diff options
Diffstat (limited to 'source')
579 files changed, 17420 insertions, 8588 deletions
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 50a8223a84c..ff31878a929 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -194,6 +194,8 @@ static GPUTexture *blf_batch_cache_texture_load(void) int offset_x = bitmap_len_landed % tex_width; int offset_y = bitmap_len_landed / tex_width; + GPU_texture_bind(gc->texture, 0); + /* TODO(germano): Update more than one row in a single call. */ while (remain) { int remain_row = tex_width - offset_x; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 77d2a973c50..800a3a426b7 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -32,15 +32,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 290 +#define BLENDER_VERSION 291 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 7 +#define BLENDER_FILE_SUBVERSION 0 /* 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 diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index 0623e0e5395..936d01d1ea4 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -70,7 +70,7 @@ void BKE_curvemapping_changed(struct CurveMapping *cumap, const bool rem_doubles void BKE_curvemapping_changed_all(struct CurveMapping *cumap); /* call before _all_ evaluation functions */ -void BKE_curvemapping_initialize(struct CurveMapping *cumap); +void BKE_curvemapping_init(struct CurveMapping *cumap); /* keep these (const CurveMap) - to help with thread safety */ /* single curve, no table check */ diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h index 877ab887138..d7880e9ec43 100644 --- a/source/blender/blenkernel/BKE_curveprofile.h +++ b/source/blender/blenkernel/BKE_curveprofile.h @@ -72,7 +72,7 @@ void BKE_curveprofile_create_samples(struct CurveProfile *profile, bool sample_straight_edges, struct CurveProfilePoint *r_samples); -void BKE_curveprofile_initialize(struct CurveProfile *profile, short segments_len); +void BKE_curveprofile_init(struct CurveProfile *profile, short segments_len); /* Called for a complete update of the widget after modifications */ enum { diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 1e5573ab014..c5221baf7d7 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -55,6 +55,7 @@ void BKE_image_free_packedfiles(struct Image *image); void BKE_image_free_views(struct Image *image); void BKE_image_free_buffers(struct Image *image); void BKE_image_free_buffers_ex(struct Image *image, bool do_lock); +void BKE_image_free_gputextures(struct Image *ima); /* call from library */ void BKE_image_free(struct Image *image); @@ -274,6 +275,10 @@ void BKE_image_free_anim_ibufs(struct Image *ima, int except_frame); /* does all images with type MOVIE or SEQUENCE */ void BKE_image_all_free_anim_ibufs(struct Main *bmain, int except_frame); +void BKE_image_free_all_gputextures(struct Main *bmain); +void BKE_image_free_anim_gputextures(struct Main *bmain); +void BKE_image_free_old_gputextures(struct Main *bmain); + bool BKE_image_memorypack(struct Image *ima); void BKE_image_packfiles(struct ReportList *reports, struct Image *ima, const char *basepath); void BKE_image_packfiles_from_mem(struct ReportList *reports, @@ -362,6 +367,30 @@ bool BKE_image_has_loaded_ibuf(struct Image *image); struct ImBuf *BKE_image_get_ibuf_with_name(struct Image *image, const char *name); struct ImBuf *BKE_image_get_first_ibuf(struct Image *image); +/* Not to be use directly. */ +struct GPUTexture *BKE_image_create_gpu_texture_from_ibuf(struct Image *image, struct ImBuf *ibuf); + +/* Get the GPUTexture for a given `Image`. + * + * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already + * available. It is also required when requesting the GPUTexture for a render result. */ +struct GPUTexture *BKE_image_get_gpu_texture(struct Image *image, + struct ImageUser *iuser, + struct ImBuf *ibuf); +struct GPUTexture *BKE_image_get_gpu_tiles(struct Image *image, + struct ImageUser *iuser, + struct ImBuf *ibuf); +struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image, + struct ImageUser *iuser, + struct ImBuf *ibuf); + +void BKE_image_update_gputexture( + struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); +void BKE_image_paint_set_mipmap(struct Main *bmain, bool mipmap); + +/* Delayed free of OpenGL buffers by main thread */ +void BKE_image_free_unused_gpu_textures(void); + struct RenderSlot *BKE_image_add_renderslot(struct Image *ima, const char *name); bool BKE_image_remove_renderslot(struct Image *ima, struct ImageUser *iuser, int slot); struct RenderSlot *BKE_image_get_renderslot(struct Image *ima, int slot); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 6a05f0c22b6..017f0f7cc53 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -141,7 +141,7 @@ void BKE_lib_override_library_main_update(struct Main *bmain); /* For now, we just use a temp main list. */ typedef struct Main OverrideLibraryStorage; -OverrideLibraryStorage *BKE_lib_override_library_operations_store_initialize(void); +OverrideLibraryStorage *BKE_lib_override_library_operations_store_init(void); struct ID *BKE_lib_override_library_operations_store_start( struct Main *bmain, OverrideLibraryStorage *override_storage, struct ID *local); void BKE_lib_override_library_operations_store_end(OverrideLibraryStorage *override_storage, diff --git a/source/blender/blenkernel/BKE_movieclip.h b/source/blender/blenkernel/BKE_movieclip.h index dbd6eb15bf2..bba01dd84d2 100644 --- a/source/blender/blenkernel/BKE_movieclip.h +++ b/source/blender/blenkernel/BKE_movieclip.h @@ -113,6 +113,11 @@ bool BKE_movieclip_put_frame_if_possible(struct MovieClip *clip, struct MovieClipUser *user, struct ImBuf *ibuf); +struct GPUTexture *BKE_movieclip_get_gpu_texture(struct MovieClip *clip, + struct MovieClipUser *cuser); + +void BKE_movieclip_free_gputexture(struct MovieClip *clip); + /* Dependency graph evaluation. */ void BKE_movieclip_eval_update(struct Depsgraph *depsgraph, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 4c55488ecd5..f1502a9b87d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1331,6 +1331,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define SIM_NODE_EMIT_PARTICLES 1009 #define SIM_NODE_TIME 1010 #define SIM_NODE_PARTICLE_ATTRIBUTE 1011 +#define SIM_NODE_AGE_REACHED_EVENT 1012 +#define SIM_NODE_KILL_PARTICLE 1013 /** \} */ @@ -1344,6 +1346,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define FN_NODE_GROUP_INSTANCE_ID 1203 #define FN_NODE_COMBINE_STRINGS 1204 #define FN_NODE_OBJECT_TRANSFORMS 1205 +#define FN_NODE_RANDOM_FLOAT 1206 /** \} */ diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h index 6ce2e13cf18..2472e3dbad3 100644 --- a/source/blender/blenkernel/BKE_ocean.h +++ b/source/blender/blenkernel/BKE_ocean.h @@ -70,8 +70,10 @@ typedef struct OceanCache { struct Ocean *BKE_ocean_add(void); void BKE_ocean_free_data(struct Ocean *oc); void BKE_ocean_free(struct Ocean *oc); -bool BKE_ocean_ensure(struct OceanModifierData *omd); -void BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData const *omd); +bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution); +void BKE_ocean_init_from_modifier(struct Ocean *ocean, + struct OceanModifierData const *omd, + const int resolution); void BKE_ocean_init(struct Ocean *o, int M, diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index de18ec6b792..e2b8a4d72e6 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -260,10 +260,20 @@ typedef struct SculptPoseIKChain { /* Cloth Brush */ typedef struct SculptClothLengthConstraint { - int v1; - int v2; + /* 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 + * deformed. Element b could be another vertex of the same mesh or any other position (arbitrary + * point, position for a previous state). In that case, elem_index_a and elem_index_b should be + * the same to avoid affecting two different vertices when solving the constraints. + * *elem_position points to the position which is owned by the element. */ + int elem_index_a; + float *elem_position_a; + + int elem_index_b; + float *elem_position_b; float length; + float strength; } SculptClothLengthConstraint; typedef struct SculptClothSimulation { @@ -273,6 +283,11 @@ typedef struct SculptClothSimulation { int capacity_length_constraints; float *length_constraint_tweak; + /* Position anchors for deformation brushes. These positions are modified by the brush and the + * final positions of the simulated vertices are updated with constraints that use these points + * as targets. */ + float (*deformation_pos)[3]; + float mass; float damping; diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 91d9b710823..02495496ee2 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -580,7 +580,7 @@ void psys_particle_on_dm(struct Mesh *mesh_final, /* particle_system.c */ void distribute_particles(struct ParticleSimulationData *sim, int from); -void initialize_particle(struct ParticleSimulationData *sim, struct ParticleData *pa); +void init_particle(struct ParticleSimulationData *sim, struct ParticleData *pa); void psys_calc_dmcache(struct Object *ob, struct Mesh *mesh_final, struct Mesh *mesh_original, diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh index 721132560e3..884e4c00766 100644 --- a/source/blender/blenkernel/BKE_persistent_data_handle.hh +++ b/source/blender/blenkernel/BKE_persistent_data_handle.hh @@ -85,45 +85,45 @@ class PersistentObjectHandle : public PersistentIDHandle { class PersistentDataHandleMap { private: - Map<int32_t, const ID *> id_by_handle_; - Map<const ID *, int32_t> handle_by_id_; + Map<int32_t, ID *> id_by_handle_; + Map<ID *, int32_t> handle_by_id_; public: - void add(int32_t handle, const ID &id) + void add(int32_t handle, ID &id) { BLI_assert(handle >= 0); handle_by_id_.add(&id, handle); id_by_handle_.add(handle, &id); } - PersistentIDHandle lookup(const ID *id) const + PersistentIDHandle lookup(ID *id) const { const int handle = handle_by_id_.lookup_default(id, -1); return PersistentIDHandle(handle); } - PersistentObjectHandle lookup(const Object *object) const + PersistentObjectHandle lookup(Object *object) const { - const int handle = handle_by_id_.lookup_default((const ID *)object, -1); + const int handle = handle_by_id_.lookup_default((ID *)object, -1); return PersistentObjectHandle(handle); } - const ID *lookup(const PersistentIDHandle &handle) const + ID *lookup(const PersistentIDHandle &handle) const { - const ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr); + ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr); return id; } - const Object *lookup(const PersistentObjectHandle &handle) const + Object *lookup(const PersistentObjectHandle &handle) const { - const ID *id = this->lookup((const PersistentIDHandle &)handle); + ID *id = this->lookup((const PersistentIDHandle &)handle); if (id == nullptr) { return nullptr; } if (GS(id->name) != ID_OB) { return nullptr; } - return (const Object *)id; + return (Object *)id; } }; diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 13716ddb5c6..98f52a29b5c 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -73,7 +73,7 @@ typedef struct SpaceType { /* Initial allocation, after this WM will call init() too. Some editors need * area and scene data (e.g. frame range) to set their initial scrolling. */ - struct SpaceLink *(*new)(const struct ScrArea *area, const struct Scene *scene); + struct SpaceLink *(*create)(const struct ScrArea *area, const struct Scene *scene); /* not free spacelink itself */ void (*free)(struct SpaceLink *sl); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 60a0bee801d..fd6885440fa 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -628,6 +628,10 @@ void BKE_sequencer_color_balance_apply(struct StripColorBalance *cb, void BKE_sequencer_all_free_anim_ibufs(struct Scene *scene, int cfra); bool BKE_sequencer_check_scene_recursion(struct Scene *scene, struct ReportList *reports); bool BKE_sequencer_render_loop_check(struct Sequence *seq_main, struct Sequence *seq); +void BKE_sequencer_flag_for_removal(struct Scene *scene, + struct ListBase *seqbase, + struct Sequence *seq); +void BKE_sequencer_remove_flagged_sequences(struct Scene *scene, struct ListBase *seqbase); /* A debug and development function which checks whether sequences have unique UUIDs. * Errors will be reported to the console. */ diff --git a/source/blender/blenkernel/BKE_simulation.h b/source/blender/blenkernel/BKE_simulation.h index 5aa71b6381d..6cbe77e8de3 100644 --- a/source/blender/blenkernel/BKE_simulation.h +++ b/source/blender/blenkernel/BKE_simulation.h @@ -32,6 +32,7 @@ void *BKE_simulation_add(struct Main *bmain, const char *name); void BKE_simulation_data_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Simulation *simulation); +void BKE_simulation_update_dependencies(struct Simulation *simulation, struct Main *bmain); SimulationState *BKE_simulation_state_add(Simulation *simulation, const char *type, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index d702da55ea8..ada341ff570 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -100,6 +100,7 @@ set(SRC intern/context.c intern/crazyspace.c intern/curve.c + intern/curve_bevel.c intern/curve_decimate.c intern/curve_deform.c intern/curveprofile.c @@ -134,6 +135,7 @@ set(SRC intern/idtype.c intern/image.c intern/image_gen.c + intern/image_gpu.c intern/image_save.c intern/ipo.c intern/kelvinlet.c diff --git a/source/blender/blenkernel/intern/CCGSubSurf.c b/source/blender/blenkernel/intern/CCGSubSurf.c index e035d5cbb68..8d8301362b3 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf.c +++ b/source/blender/blenkernel/intern/CCGSubSurf.c @@ -32,8 +32,6 @@ #include "CCGSubSurf.h" #include "CCGSubSurf_intern.h" -#include "GPU_glew.h" - /***/ int BKE_ccg_gridsize(int level) diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index b40cc4e8b9f..0a84d77e61f 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -80,11 +80,9 @@ //#define USE_MODIFIER_VALIDATE #ifdef USE_MODIFIER_VALIDATE -# define ASSERT_IS_VALID_DM(dm) (BLI_assert((dm == NULL) || (DM_is_valid(dm) == true))) # define ASSERT_IS_VALID_MESH(mesh) \ (BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true))) #else -# define ASSERT_IS_VALID_DM(dm) # define ASSERT_IS_VALID_MESH(mesh) #endif diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index ea5a4bd99d1..5b5e32f1d81 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1150,7 +1150,7 @@ static int nlaevalchan_validate_index(NlaEvalChannel *nec, int index) return 0; } -/* Initialise default values for NlaEvalChannel from the property data. */ +/* Initialize default values for NlaEvalChannel from the property data. */ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values) { PointerRNA *ptr = &nec->key.ptr; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 985be4ac99f..631ce4edd20 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2582,7 +2582,7 @@ void BKE_pose_where_is(struct Depsgraph *depsgraph, Scene *scene, Object *ob) } /* 2a. construct the IK tree (standard IK) */ - BIK_initialize_tree(depsgraph, scene, ob, ctime); + BIK_init_tree(depsgraph, scene, ob, ctime); /* 2b. construct the Spline IK trees * - this is not integrated as an IK plugin, since it should be able diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index d66991aed70..97c717572bc 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -658,7 +658,7 @@ void BKE_pose_eval_init_ik(struct Depsgraph *depsgraph, Scene *scene, Object *ob return; } /* construct the IK tree (standard IK) */ - BIK_initialize_tree(depsgraph, scene, object, ctime); + BIK_init_tree(depsgraph, scene, object, ctime); /* construct the Spline IK trees * - this is not integrated as an IK plugin, since it should be able * to function in conjunction with standard IK. */ diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 7223187831e..39fbea66637 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -557,7 +557,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INK); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INK; @@ -594,7 +594,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INKNOISE); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INKNOISE; @@ -631,7 +631,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_MARKER); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_MARKER; @@ -667,12 +667,12 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) /* Curve. */ custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_CHISEL_SENSIVITY); custom_curve = brush->gpencil_settings->curve_strength; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); + BKE_curvemapping_init(custom_curve); brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_CHISEL_STRENGTH); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_CHISEL; @@ -2274,7 +2274,7 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool int half = side / 2; int i, j; - BKE_curvemapping_initialize(br->curve); + BKE_curvemapping_init(br->curve); texcache = BKE_brush_gen_texture_cache(br, half, secondary); im->rect_float = MEM_callocN(sizeof(float) * side * side, "radial control rect"); im->x = im->y = side; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index da9dab36044..9ad6ae84c5c 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -61,6 +61,8 @@ static void cache_file_init_data(ID *id) BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(cache_file, id)); cache_file->scale = 1.0f; + cache_file->velocity_unit = CACHEFILE_VELOCITY_UNIT_SECOND; + BLI_strncpy(cache_file->velocity_name, ".velocities", sizeof(cache_file->velocity_name)); } static void cache_file_copy_data(Main *UNUSED(bmain), diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index 4f4eb8f9f9d..fc326ffb390 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1202,7 +1202,7 @@ int BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap) return 0; } -void BKE_curvemapping_initialize(CurveMapping *cumap) +void BKE_curvemapping_init(CurveMapping *cumap) { int a; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 0627d2005d5..e126fb7f632 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -223,7 +223,7 @@ void BKE_curve_init(Curve *cu, const short curve_type) cu->vfont->id.us += 4; cu->str = MEM_malloc_arrayN(12, sizeof(unsigned char), "str"); BLI_strncpy(cu->str, "Text", 12); - cu->len = cu->len_wchar = cu->pos = 4; + cu->len = cu->len_char32 = cu->pos = 4; cu->strinfo = MEM_calloc_arrayN(12, sizeof(CharInfo), "strinfo new"); cu->totbox = cu->actbox = 1; cu->tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "textbox"); @@ -1726,205 +1726,6 @@ static void forward_diff_bezier_cotangent(const float p0[3], } } -/* ***************** BEVEL ****************** */ - -void BKE_curve_bevel_make(Object *ob, ListBase *disp) -{ - DispList *dl, *dlnew; - Curve *bevcu, *cu; - float *fp, facx, facy, angle, dangle; - int nr, a; - - cu = ob->data; - BLI_listbase_clear(disp); - - /* if a font object is being edited, then do nothing */ - // XXX if ( ob == obedit && ob->type == OB_FONT ) return; - - if (cu->bevobj) { - if (cu->bevobj->type != OB_CURVE) { - return; - } - - bevcu = cu->bevobj->data; - if (bevcu->ext1 == 0.0f && bevcu->ext2 == 0.0f) { - ListBase bevdisp = {NULL, NULL}; - facx = cu->bevobj->scale[0]; - facy = cu->bevobj->scale[1]; - - if (cu->bevobj->runtime.curve_cache) { - dl = cu->bevobj->runtime.curve_cache->disp.first; - } - else { - BLI_assert(cu->bevobj->runtime.curve_cache != NULL); - dl = NULL; - } - - while (dl) { - if (ELEM(dl->type, DL_POLY, DL_SEGM)) { - dlnew = MEM_mallocN(sizeof(DispList), "makebevelcurve1"); - *dlnew = *dl; - dlnew->verts = MEM_malloc_arrayN( - dl->parts * dl->nr, 3 * sizeof(float), "makebevelcurve1"); - memcpy(dlnew->verts, dl->verts, 3 * sizeof(float) * dl->parts * dl->nr); - - if (dlnew->type == DL_SEGM) { - dlnew->flag |= (DL_FRONT_CURVE | DL_BACK_CURVE); - } - - BLI_addtail(disp, dlnew); - fp = dlnew->verts; - nr = dlnew->parts * dlnew->nr; - while (nr--) { - fp[2] = fp[1] * facy; - fp[1] = -fp[0] * facx; - fp[0] = 0.0; - fp += 3; - } - } - dl = dl->next; - } - - BKE_displist_free(&bevdisp); - } - } - else if (cu->ext1 == 0.0f && cu->ext2 == 0.0f) { - /* pass */ - } - else if (cu->ext2 == 0.0f) { - dl = MEM_callocN(sizeof(DispList), "makebevelcurve2"); - dl->verts = MEM_malloc_arrayN(2, sizeof(float[3]), "makebevelcurve2"); - BLI_addtail(disp, dl); - dl->type = DL_SEGM; - dl->parts = 1; - dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; - dl->nr = 2; - - fp = dl->verts; - fp[0] = fp[1] = 0.0; - fp[2] = -cu->ext1; - fp[3] = fp[4] = 0.0; - fp[5] = cu->ext1; - } - else if ((cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) { - /* We make a full round bevel in that case. */ - - nr = 4 + 2 * cu->bevresol; - - dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve p1"); - BLI_addtail(disp, dl); - dl->type = DL_POLY; - dl->parts = 1; - dl->flag = DL_BACK_CURVE; - dl->nr = nr; - - /* a circle */ - fp = dl->verts; - dangle = (2.0f * (float)M_PI / (nr)); - angle = -(nr - 1) * dangle; - - for (a = 0; a < nr; a++) { - fp[0] = 0.0; - fp[1] = (cosf(angle) * (cu->ext2)); - fp[2] = (sinf(angle) * (cu->ext2)) - cu->ext1; - angle += dangle; - fp += 3; - } - } - else { - /* The general case for nonzero extrusion or an incomplete loop. */ - dl = MEM_callocN(sizeof(DispList), "makebevelcurve"); - if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { - /* The full loop. */ - nr = 4 * cu->bevresol + 6; - dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; - } - else if ((cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) { - /* Half the loop. */ - nr = 2 * (cu->bevresol + 1) + ((cu->ext1 == 0.0f) ? 1 : 2); - dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; - } - else { - /* One quarter of the loop (just front or back). */ - nr = (cu->ext1 == 0.0f) ? cu->bevresol + 2 : cu->bevresol + 3; - dl->flag = (cu->flag & CU_FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE; - } - - dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), "makebevelcurve"); - BLI_addtail(disp, dl); - /* Use a different type depending on whether the loop is complete or not. */ - dl->type = ((cu->flag & (CU_FRONT | CU_BACK)) == 0) ? DL_POLY : DL_SEGM; - dl->parts = 1; - dl->nr = nr; - - fp = dl->verts; - dangle = (float)M_PI_2 / (cu->bevresol + 1); - angle = 0.0; - - /* Build the back section. */ - if (cu->flag & CU_BACK || !(cu->flag & CU_FRONT)) { - angle = (float)M_PI_2 * 3.0f; - for (a = 0; a < cu->bevresol + 2; a++) { - fp[0] = 0.0; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; - angle += dangle; - fp += 3; - } - if ((cu->ext1 != 0.0f) && !(cu->flag & CU_FRONT) && (cu->flag & CU_BACK)) { - /* Add the extrusion if we're only building the back. */ - fp[0] = 0.0; - fp[1] = cu->ext2; - fp[2] = cu->ext1; - } - } - - /* Build the front section. */ - if (cu->flag & CU_FRONT || !(cu->flag & CU_BACK)) { - if ((cu->ext1 != 0.0f) && !(cu->flag & CU_BACK) && (cu->flag & CU_FRONT)) { - /* Add the extrusion if we're only building the back. */ - fp[0] = 0.0; - fp[1] = cu->ext2; - fp[2] = -cu->ext1; - fp += 3; - } - /* Don't duplicate the last back vertex. */ - angle = (cu->ext1 == 0.0f && (cu->flag & CU_BACK)) ? dangle : 0; - int front_len = (cu->ext1 == 0.0f && ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT))) ? - cu->bevresol + 1 : - cu->bevresol + 2; - for (a = 0; a < front_len; a++) { - fp[0] = 0.0; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; - angle += dangle; - fp += 3; - } - } - - /* Build the other half only if we're building the full loop. */ - if (!(cu->flag & (CU_FRONT | CU_BACK))) { - for (a = 0; a < cu->bevresol + 1; a++) { - fp[0] = 0.0; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; - angle += dangle; - fp += 3; - } - - angle = (float)M_PI; - for (a = 0; a < cu->bevresol + 1; a++) { - fp[0] = 0.0; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; - angle += dangle; - fp += 3; - } - } - } -} - static int cu_isectLL(const float v1[3], const float v2[3], const float v3[3], @@ -5441,7 +5242,7 @@ void BKE_curve_material_index_remove(Curve *cu, int index) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr && info->mat_nr >= index) { info->mat_nr--; } @@ -5465,7 +5266,7 @@ bool BKE_curve_material_index_used(Curve *cu, int index) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr == index) { return true; } @@ -5491,7 +5292,7 @@ void BKE_curve_material_index_clear(Curve *cu) if (curvetype == OB_FONT) { struct CharInfo *info = cu->strinfo; int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { info->mat_nr = 0; } } @@ -5513,7 +5314,7 @@ bool BKE_curve_material_index_validate(Curve *cu) CharInfo *info = cu->strinfo; const int max_idx = max_ii(0, cu->totcol); /* OB_FONT use 1 as first mat index, not 0!!! */ int i; - for (i = cu->len_wchar - 1; i >= 0; i--, info++) { + for (i = cu->len_char32 - 1; i >= 0; i--, info++) { if (info->mat_nr > max_idx) { info->mat_nr = 0; is_valid = false; @@ -5561,7 +5362,7 @@ void BKE_curve_material_remap(Curve *cu, const unsigned int *remap, unsigned int } else { strinfo = cu->strinfo; - charinfo_len = cu->len_wchar; + charinfo_len = cu->len_char32; } for (i = 0; i <= charinfo_len; i++) { diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c new file mode 100644 index 00000000000..7f23f0215cc --- /dev/null +++ b/source/blender/blenkernel/intern/curve_bevel.c @@ -0,0 +1,272 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + * + * Handle curve object data bevel options, + * both extruding + */ + +#include <string.h> + +#include "BLI_listbase.h" +#include "BLI_math_base.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BKE_curve.h" +#include "BKE_displist.h" + +typedef enum CurveBevelFillType { + BACK = 0, + FRONT, + HALF, + FULL, +} CurveBevelFillType; + +static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve) +{ + if (!(curve->flag & (CU_FRONT | CU_BACK))) { + return FULL; + } + if ((curve->flag & CU_FRONT) && (curve->flag & CU_BACK)) { + return HALF; + } + + return (curve->flag & CU_FRONT) ? FRONT : BACK; +} + +static void curve_bevel_make_extrude_and_fill(Curve *cu, + ListBase *disp, + const bool use_extrude, + const CurveBevelFillType fill_type) +{ + DispList *dl = MEM_callocN(sizeof(DispList), __func__); + + int nr; + if (fill_type == FULL) { + /* The full loop. */ + nr = 4 * cu->bevresol + 6; + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + } + else if (fill_type == HALF) { + /* Half the loop. */ + nr = 2 * (cu->bevresol + 1) + (use_extrude ? 2 : 1); + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + } + else { + /* One quarter of the loop (just front or back). */ + nr = use_extrude ? cu->bevresol + 3 : cu->bevresol + 2; + dl->flag = (fill_type == FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE; + } + + dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), __func__); + BLI_addtail(disp, dl); + /* Use a different type depending on whether the loop is complete or not. */ + dl->type = (fill_type == FULL) ? DL_POLY : DL_SEGM; + dl->parts = 1; + dl->nr = nr; + + float *fp = dl->verts; + const float dangle = (float)M_PI_2 / (cu->bevresol + 1); + float angle = 0.0f; + + /* Build the back section. */ + if (ELEM(fill_type, BACK, HALF, FULL)) { + angle = (float)M_PI_2 * 3.0f; + for (int i = 0; i < cu->bevresol + 2; i++) { + fp[0] = 0.0f; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; + angle += dangle; + fp += 3; + } + if (use_extrude && fill_type == BACK) { + /* Add the extrusion if we're only building the back. */ + fp[0] = 0.0f; + fp[1] = cu->ext2; + fp[2] = cu->ext1; + } + } + + /* Build the front section. */ + if (ELEM(fill_type, FRONT, HALF, FULL)) { + if (use_extrude && fill_type == FRONT) { + /* Add the extrusion if we're only building the front. */ + fp[0] = 0.0f; + fp[1] = cu->ext2; + fp[2] = -cu->ext1; + fp += 3; + } + /* Don't duplicate the last back vertex. */ + angle = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? dangle : 0; + int front_len = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? cu->bevresol + 1 : + cu->bevresol + 2; + for (int i = 0; i < front_len; i++) { + fp[0] = 0.0f; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + angle += dangle; + fp += 3; + } + } + + /* Build the other half only if we're building the full loop. */ + if (fill_type == FULL) { + for (int i = 0; i < cu->bevresol + 1; i++) { + fp[0] = 0.0f; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; + angle += dangle; + fp += 3; + } + + angle = (float)M_PI; + for (int i = 0; i < cu->bevresol + 1; i++) { + fp[0] = 0.0f; + fp[1] = (float)(cosf(angle) * (cu->ext2)); + fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; + angle += dangle; + fp += 3; + } + } +} + +static void curve_bevel_make_full_circle(Curve *cu, ListBase *disp) +{ + const int nr = 4 + 2 * cu->bevresol; + + DispList *dl = MEM_callocN(sizeof(DispList), __func__); + dl->verts = MEM_malloc_arrayN(nr, sizeof(float[3]), __func__); + BLI_addtail(disp, dl); + dl->type = DL_POLY; + dl->parts = 1; + dl->flag = DL_BACK_CURVE; + dl->nr = nr; + + float *fp = dl->verts; + const float dangle = (2.0f * (float)M_PI / (nr)); + float angle = -(nr - 1) * dangle; + + for (int i = 0; i < nr; i++) { + fp[0] = 0.0; + fp[1] = (cosf(angle) * (cu->ext2)); + fp[2] = (sinf(angle) * (cu->ext2)) - cu->ext1; + angle += dangle; + fp += 3; + } +} + +static void curve_bevel_make_only_extrude(Curve *cu, ListBase *disp) +{ + DispList *dl = MEM_callocN(sizeof(DispList), __func__); + dl->verts = MEM_malloc_arrayN(2, sizeof(float[3]), __func__); + BLI_addtail(disp, dl); + dl->type = DL_SEGM; + dl->parts = 1; + dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; + dl->nr = 2; + + float *fp = dl->verts; + fp[0] = fp[1] = 0.0; + fp[2] = -cu->ext1; + fp[3] = fp[4] = 0.0; + fp[5] = cu->ext1; +} + +static void curve_bevel_make_from_object(Curve *cu, ListBase *disp) +{ + if (cu->bevobj->type != OB_CURVE) { + return; + } + + Curve *bevcu = cu->bevobj->data; + if (bevcu->ext1 == 0.0f && bevcu->ext2 == 0.0f) { + ListBase bevdisp = {NULL, NULL}; + float facx = cu->bevobj->scale[0]; + float facy = cu->bevobj->scale[1]; + + DispList *dl; + if (cu->bevobj->runtime.curve_cache) { + dl = cu->bevobj->runtime.curve_cache->disp.first; + } + else { + BLI_assert(cu->bevobj->runtime.curve_cache != NULL); + dl = NULL; + } + + while (dl) { + if (ELEM(dl->type, DL_POLY, DL_SEGM)) { + DispList *dlnew = MEM_mallocN(sizeof(DispList), __func__); + *dlnew = *dl; + dlnew->verts = MEM_malloc_arrayN(dl->parts * dl->nr, 3 * sizeof(float), __func__); + memcpy(dlnew->verts, dl->verts, 3 * sizeof(float) * dl->parts * dl->nr); + + if (dlnew->type == DL_SEGM) { + dlnew->flag |= (DL_FRONT_CURVE | DL_BACK_CURVE); + } + + BLI_addtail(disp, dlnew); + float *fp = dlnew->verts; + int nr = dlnew->parts * dlnew->nr; + while (nr--) { + fp[2] = fp[1] * facy; + fp[1] = -fp[0] * facx; + fp[0] = 0.0; + fp += 3; + } + } + dl = dl->next; + } + + BKE_displist_free(&bevdisp); + } +} + +void BKE_curve_bevel_make(Object *ob, ListBase *disp) +{ + Curve *curve = ob->data; + + const bool use_extrude = curve->ext1 != 0.0f; + const bool use_bevel = curve->ext2 != 0.0f; + + BLI_listbase_clear(disp); + + if (curve->bevobj) { + curve_bevel_make_from_object(curve, disp); + } + else if (!(use_extrude || use_bevel)) { + /* Pass. */ + } + else if (use_extrude && !use_bevel) { + curve_bevel_make_only_extrude(curve, disp); + } + else { + CurveBevelFillType fill_type = curve_bevel_get_fill_type(curve); + + if (!use_extrude && fill_type == FULL) { + curve_bevel_make_full_circle(curve, disp); + } + else { + /* The general case for nonzero extrusion or an incomplete loop. */ + curve_bevel_make_extrude_and_fill(curve, disp, use_extrude, fill_type); + } + } +} diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c index 6919d4fa10f..0a41529aac1 100644 --- a/source/blender/blenkernel/intern/curveprofile.c +++ b/source/blender/blenkernel/intern/curveprofile.c @@ -886,7 +886,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, */ static void curveprofile_make_table(CurveProfile *profile) { - int n_samples = PROF_N_TABLE(profile->path_len); + int n_samples = PROF_TABLE_LEN(profile->path_len); CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1), "high-res table"); @@ -1040,7 +1040,7 @@ void BKE_curveprofile_update(CurveProfile *profile, const int update_flags) * Also sets the number of segments used for the display preview of the locations * of the sampled points. */ -void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len) +void BKE_curveprofile_init(CurveProfile *profile, short segments_len) { if (segments_len != profile->segments_len) { profile->flag |= PROF_DIRTY_PRESET; @@ -1055,11 +1055,11 @@ void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len) * Gives the distance to the next point in the widgets sampled table, in other words the length * of the \a 'i' edge of the table. * - * \note Requires curveprofile_initialize or #BKE_curveprofile_update call before to fill table. + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. */ static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i) { - BLI_assert(i < PROF_N_TABLE(profile->path_len)); + BLI_assert(i < PROF_TABLE_LEN(profile->path_len)); return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); } @@ -1067,12 +1067,12 @@ static float curveprofile_distance_to_next_table_point(const CurveProfile *profi /** * Calculates the total length of the profile from the curves sampled in the table. * - * \note Requires curveprofile_initialize or #BKE_curveprofile_update call before to fill table. + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. */ float BKE_curveprofile_total_length(const CurveProfile *profile) { float total_length = 0; - for (int i = 0; i < PROF_N_TABLE(profile->path_len) - 1; i++) { + for (int i = 0; i < PROF_TABLE_LEN(profile->path_len) - 1; i++) { total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); } return total_length; @@ -1082,7 +1082,7 @@ float BKE_curveprofile_total_length(const CurveProfile *profile) * Samples evenly spaced positions along the curve profile's table (generated from path). Fills * an entire table at once for a speedup if all of the results are going to be used anyway. * - * \note Requires curveprofile_initialize or #BKE_curveprofile_update call before to fill table. + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. * \note Working, but would conflict with "Sample Straight Edges" option, so this is unused for * now. */ @@ -1145,7 +1145,7 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, * Travels down (length_portion * path) length and returns the position at that point. * * \param length_portion: The portion (0 to 1) of the path's full length to sample at. - * \note Requires curveprofile_initialize or #BKE_curveprofile_update call before to fill table. + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. */ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float length_portion, @@ -1160,7 +1160,7 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float length_travelled = 0.0f; while (length_travelled < requested_length) { /* Check if we reached the last point before the final one. */ - if (i == PROF_N_TABLE(profile->path_len) - 2) { + if (i == PROF_TABLE_LEN(profile->path_len) - 2) { break; } float new_length = curveprofile_distance_to_next_table_point(profile, i); diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 22d4af6fa39..7bf11d86a63 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -2643,11 +2643,13 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, } if (alloctype == CD_DUPLICATE && layerdata) { - if (typeInfo->copy) { - typeInfo->copy(layerdata, newlayerdata, totelem); - } - else { - memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size); + if (totelem > 0) { + if (typeInfo->copy) { + typeInfo->copy(layerdata, newlayerdata, totelem); + } + else { + memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size); + } } } else if (alloctype == CD_DEFAULT) { diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c index 6fcaf84d4ca..897fc7e692b 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.c +++ b/source/blender/blenkernel/intern/editmesh_tangent.c @@ -274,8 +274,8 @@ static void emDM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), vo /** * \see #BKE_mesh_calc_loop_tangent, same logic but used arrays instead of #BMesh data. * - * \note This function is not so normal, its using `bm->ldata` as input, - * but output's to `dm->loopData`. + * \note This function is not so normal, its using #BMesh.ldata as input, + * but output's to #Mesh.ldata. * This is done because #CD_TANGENT is cache data used only for drawing. */ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c index 10d804f437e..87cb77930f5 100644 --- a/source/blender/blenkernel/intern/fcurve_driver.c +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -21,12 +21,6 @@ * \ingroup bke */ -// #include <float.h> -// #include <math.h> -// #include <stddef.h> -// #include <stdio.h> -// #include <string.h> - #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" @@ -66,17 +60,19 @@ static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER; static CLG_LogRef LOG = {"bke.fcurve"}; -/* Driver Variables --------------------------- */ +/* -------------------------------------------------------------------- */ +/** \name Driver Variables + * \{ */ /* TypeInfo for Driver Variables (dvti) */ typedef struct DriverVarTypeInfo { - /* evaluation callback */ + /* Evaluation callback. */ float (*get_value)(ChannelDriver *driver, DriverVar *dvar); - /* allocation of target slots */ - int num_targets; /* number of target slots required */ - const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ - short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ + /* Allocation of target slots. */ + int num_targets; /* Number of target slots required. */ + const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots. */ + short target_flags[MAX_DRIVER_TARGETS]; /* Flags defining the requirements for each slot. */ } DriverVarTypeInfo; /* Macro to begin definitions */ @@ -85,7 +81,11 @@ typedef struct DriverVarTypeInfo { /* Macro to end definitions */ #define END_DVAR_TYPEDEF } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Target Utilities + * \{ */ static ID *dtar_id_ensure_proxy_from(ID *id) { @@ -107,14 +107,14 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) int index = -1; float value = 0.0f; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dtar)) { return 0.0f; } id = dtar_id_ensure_proxy_from(dtar->id); - /* error check for missing pointer... */ + /* Error check for missing pointer. */ if (id == NULL) { if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); @@ -125,12 +125,12 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) return 0.0f; } - /* get RNA-pointer for the ID-block given in target */ + /* Get RNA-pointer for the ID-block given in target. */ RNA_id_pointer_create(id, &id_ptr); - /* get property to read from, and get value as appropriate */ + /* Get property to read from, and get value as appropriate. */ if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* path couldn't be resolved */ + /* Path couldn't be resolved. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: cannot resolve target for %s -> %s", @@ -144,9 +144,9 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } if (RNA_property_array_check(prop)) { - /* array */ + /* Array. */ if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) { - /* out of bounds */ + /* Out of bounds. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", @@ -175,7 +175,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } } else { - /* not an array */ + /* Not an array. */ switch (RNA_property_type(prop)) { case PROP_BOOLEAN: value = (float)RNA_property_boolean_get(&ptr, prop); @@ -194,7 +194,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) } } - /* if we're still here, we should be ok... */ + /* If we're still here, we should be ok. */ dtar->flag &= ~DTAR_FLAG_INVALID; return value; } @@ -214,14 +214,14 @@ bool driver_get_variable_property(ChannelDriver *driver, ID *id; int index = -1; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dtar)) { return false; } id = dtar_id_ensure_proxy_from(dtar->id); - /* error check for missing pointer... */ + /* Error check for missing pointer. */ if (id == NULL) { if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); @@ -232,19 +232,19 @@ bool driver_get_variable_property(ChannelDriver *driver, return false; } - /* get RNA-pointer for the ID-block given in target */ + /* Get RNA-pointer for the ID-block given in target. */ RNA_id_pointer_create(id, &id_ptr); - /* get property to read from, and get value as appropriate */ + /* Get property to read from, and get value as appropriate. */ if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') { ptr = PointerRNA_NULL; - prop = NULL; /* ok */ + prop = NULL; /* OK. */ } else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* ok */ + /* OK. */ } else { - /* path couldn't be resolved */ + /* Path couldn't be resolved. */ if (G.debug & G_DEBUG) { CLOG_ERROR(&LOG, "Driver Evaluation Error: cannot resolve target for %s -> %s", @@ -265,7 +265,7 @@ bool driver_get_variable_property(ChannelDriver *driver, *r_prop = prop; *r_index = index; - /* if we're still here, we should be ok... */ + /* If we're still here, we should be ok. */ dtar->flag &= ~DTAR_FLAG_INVALID; return true; } @@ -277,14 +277,14 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - /* check if this target has valid data */ + /* Check if this target has valid data. */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ + /* Invalid target, so will not have enough targets. */ driver->flag |= DRIVER_FLAG_INVALID; dtar->flag |= DTAR_FLAG_INVALID; } else { - /* target seems to be OK now... */ + /* Target seems to be OK now. */ dtar->flag &= ~DTAR_FLAG_INVALID; valid_targets++; } @@ -294,21 +294,25 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) return valid_targets; } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Variable Utilities + * \{ */ -/* evaluate 'single prop' driver variable */ +/* Evaluate 'single prop' driver variable. */ static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar) { - /* just evaluate the first target slot */ + /* Just evaluate the first target slot. */ return dtar_get_prop_val(driver, &dvar->targets[0]); } -/* evaluate 'rotation difference' driver variable */ +/* Evaluate 'rotation difference' driver variable. */ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) { short valid_targets = driver_check_valid_targets(driver, dvar); - /* make sure we have enough valid targets to use - all or nothing for now... */ + /* Make sure we have enough valid targets to use - all or nothing for now. */ if (driver_check_valid_targets(driver, dvar) != 2) { if (G.debug & G_DEBUG) { CLOG_WARN(&LOG, @@ -324,31 +328,31 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) /* NOTE: for now, these are all just worldspace */ for (int i = 0; i < 2; i++) { - /* get pointer to loc values to store in */ + /* Get pointer to loc values to store in. */ DriverTarget *dtar = &dvar->targets[i]; Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); bPoseChannel *pchan; - /* after the checks above, the targets should be valid here... */ + /* After the checks above, the targets should be valid here. */ BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - /* try to get posechannel */ + /* Try to get pose-channel. */ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - /* check if object or bone */ + /* Check if object or bone. */ if (pchan) { - /* bone */ + /* Bone. */ mat[i] = pchan->pose_mat; } else { - /* object */ + /* Object. */ mat[i] = ob->obmat; } } float q1[4], q2[4], quat[4], angle; - /* use the final posed locations */ + /* Use the final posed locations. */ mat4_to_quat(q1, mat[0]); mat4_to_quat(q2, mat[1]); @@ -360,15 +364,18 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle); } -/* evaluate 'location difference' driver variable */ -/* TODO: this needs to take into account space conversions... */ +/** + * Evaluate 'location difference' driver variable. + * + * TODO: this needs to take into account space conversions. + */ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) { float loc1[3] = {0.0f, 0.0f, 0.0f}; float loc2[3] = {0.0f, 0.0f, 0.0f}; short valid_targets = driver_check_valid_targets(driver, dvar); - /* make sure we have enough valid targets to use - all or nothing for now... */ + /* Make sure we have enough valid targets to use - all or nothing for now. */ if (valid_targets < dvar->num_targets) { if (G.debug & G_DEBUG) { CLOG_WARN(&LOG, @@ -381,72 +388,72 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) } /* SECOND PASS: get two location values */ - /* NOTE: for now, these are all just worldspace */ + /* NOTE: for now, these are all just world-space */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - /* get pointer to loc values to store in */ + /* Get pointer to loc values to store in. */ Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); bPoseChannel *pchan; float tmp_loc[3]; - /* after the checks above, the targets should be valid here... */ + /* After the checks above, the targets should be valid here. */ BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - /* try to get posechannel */ + /* Try to get pose-channel. */ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - /* check if object or bone */ + /* Check if object or bone. */ if (pchan) { - /* bone */ + /* Bone. */ if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { float mat[4][4]; - /* extract transform just like how the constraints do it! */ + /* Extract transform just like how the constraints do it! */ copy_m4_m4(mat, pchan->pose_mat); BKE_constraint_mat_convertspace( ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); - /* ... and from that, we get our transform */ + /* ... and from that, we get our transform. */ copy_v3_v3(tmp_loc, mat[3]); } else { - /* transform space (use transform values directly) */ + /* Transform space (use transform values directly). */ copy_v3_v3(tmp_loc, pchan->loc); } } else { - /* convert to worldspace */ + /* Convert to worldspace. */ copy_v3_v3(tmp_loc, pchan->pose_head); mul_m4_v3(ob->obmat, tmp_loc); } } else { - /* object */ + /* Object. */ if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* XXX: this should practically be the same as transform space... */ + /* XXX: this should practically be the same as transform space. */ float mat[4][4]; - /* extract transform just like how the constraints do it! */ + /* Extract transform just like how the constraints do it! */ copy_m4_m4(mat, ob->obmat); BKE_constraint_mat_convertspace( ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); - /* ... and from that, we get our transform */ + /* ... and from that, we get our transform. */ copy_v3_v3(tmp_loc, mat[3]); } else { - /* transform space (use transform values directly) */ + /* Transform space (use transform values directly). */ copy_v3_v3(tmp_loc, ob->loc); } } else { - /* worldspace */ + /* World-space. */ copy_v3_v3(tmp_loc, ob->obmat[3]); } } - /* copy the location to the right place */ + /* Copy the location to the right place. */ if (tarIndex) { copy_v3_v3(loc2, tmp_loc); } @@ -456,13 +463,14 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) } DRIVER_TARGETS_LOOPER_END; - /* if we're still here, there should now be two targets to use, - * so just take the length of the vector between these points - */ + /* If we're still here, there should now be two targets to use, + * so just take the length of the vector between these points. */ return len_v3v3(loc1, loc2); } -/* evaluate 'transform channel' driver variable */ +/** + * Evaluate 'transform channel' driver variable. + */ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) { DriverTarget *dtar = &dvar->targets[0]; @@ -473,15 +481,15 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) bool use_eulers = false; short rot_order = ROT_MODE_EUL; - /* check if this target has valid data */ + /* Check if this target has valid data. */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ + /* Invalid target, so will not have enough targets. */ driver->flag |= DRIVER_FLAG_INVALID; dtar->flag |= DTAR_FLAG_INVALID; return 0.0f; } else { - /* target should be valid now */ + /* Target should be valid now. */ dtar->flag &= ~DTAR_FLAG_INVALID; } @@ -495,7 +503,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) * but #DTAR_FLAG_LOCAL_CONSTS is for all the common "corrective-shapes-for-limbs" situations. */ if (pchan) { - /* bone */ + /* Bone. */ if (pchan->rotmode > 0) { copy_v3_v3(oldEul, pchan->eul); rot_order = pchan->rotmode; @@ -504,16 +512,15 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ + /* Just like how the constraints do it! */ copy_m4_m4(mat, pchan->pose_mat); BKE_constraint_mat_convertspace( ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); } else { - /* specially calculate local matrix, since chan_mat is not valid + /* Specially calculate local matrix, since chan_mat is not valid * since it stores delta transform of pose_mat so that deforms work - * so it cannot be used here for "transform" space - */ + * so it cannot be used here for "transform" space. */ BKE_pchan_to_mat4(pchan, mat); } } @@ -523,7 +530,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) } } else { - /* object */ + /* Object. */ if (ob->rotmode > 0) { copy_v3_v3(oldEul, ob->rot); rot_order = ob->rotmode; @@ -532,25 +539,25 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) if (dtar->flag & DTAR_FLAG_LOCALSPACE) { if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ + /* Just like how the constraints do it! */ copy_m4_m4(mat, ob->obmat); BKE_constraint_mat_convertspace( ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); } else { - /* transforms to matrix */ + /* Transforms to matrix. */ BKE_object_to_mat4(ob, mat); } } else { - /* worldspace matrix - just the good-old one */ + /* World-space matrix - just the good-old one. */ copy_m4_m4(mat, ob->obmat); } } - /* check which transform */ + /* Check which transform. */ if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) { - /* not valid channel */ + /* Not valid channel. */ return 0.0f; } else if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) { @@ -567,7 +574,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]); } else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) { - /* extract rotation as eulers (if needed) + /* Extract rotation as eulers (if needed) * - definitely if rotation order isn't eulers already * - if eulers, then we have 2 options: * a) decompose transform matrix as required, then try to make eulers from @@ -596,7 +603,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) return quat[channel]; } else { - /* extract location and choose right axis */ + /* Extract location and choose right axis. */ return mat[3][dtar->transChan]; } } @@ -666,41 +673,45 @@ void BKE_driver_target_matrix_to_rot_channels( } } -/* ......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Variable Type Info + * \{ */ /* Table of Driver Variable Type Info Data */ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = { - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */ - 1, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* Eval callback. */ + 1, /* Number of targets used. */ {"Property"}, /* UI names for targets */ - {0} /* flags */ + {0} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */ - 2, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* Eval callback. */ + 2, /* Number of targets used. */ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, - DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */ - 2, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* Eval callback. */ + 2, /* Number of targets used. */ {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, - DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */ - 1, /* number of targets used */ + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* Eval callback. */ + 1, /* Number of targets used. */ {"Object/Bone"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* Flags. */ END_DVAR_TYPEDEF, }; /* Get driver variable typeinfo */ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) { - /* check if valid type */ + /* Check if valid type. */ if ((type >= 0) && (type < MAX_DVAR_TYPES)) { return &dvar_types[type]; } @@ -709,40 +720,44 @@ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) } } -/* Driver API --------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver API + * \{ */ /* Perform actual freeing driver variable and remove it from the given list */ void driver_free_variable(ListBase *variables, DriverVar *dvar) { - /* sanity checks */ + /* Sanity checks. */ if (dvar == NULL) { return; } - /* free target vars + /* Free target vars: * - need to go over all of them, not just up to the ones that are used * currently, since there may be some lingering RNA paths from * previous users needing freeing */ DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* free RNA path if applicable */ + /* Free RNA path if applicable. */ if (dtar->rna_path) { MEM_freeN(dtar->rna_path); } } DRIVER_TARGETS_LOOPER_END; - /* remove the variable from the driver */ + /* Remove the variable from the driver. */ BLI_freelinkN(variables, dvar); } /* Free the driver variable and do extra updates */ void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar) { - /* remove and free the driver variable */ + /* Remove and free the driver variable. */ driver_free_variable(&driver->variables, dvar); - /* since driver variables are cached, the expression needs re-compiling too */ + /* Since driver variables are cached, the expression needs re-compiling too. */ BKE_driver_invalidate_expression(driver, false, true); } @@ -753,9 +768,9 @@ void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars) BLI_duplicatelist(dst_vars, src_vars); LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) { - /* need to go over all targets so that we don't leave any dangling paths */ + /* Need to go over all targets so that we don't leave any dangling paths. */ DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* make a copy of target's rna path if available */ + /* Make a copy of target's rna path if available. */ if (dtar->rna_path) { dtar->rna_path = MEM_dupallocN(dtar->rna_path); } @@ -769,25 +784,24 @@ void driver_change_variable_type(DriverVar *dvar, int type) { const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type); - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, dvar, dvti)) { return; } - /* set the new settings */ + /* Set the new settings. */ dvar->type = type; dvar->num_targets = dvti->num_targets; - /* make changes to the targets based on the defines for these types - * NOTE: only need to make sure the ones we're using here are valid... - */ + /* Make changes to the targets based on the defines for these types. + * NOTE: only need to make sure the ones we're using here are valid. */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { short flags = dvti->target_flags[tarIndex]; - /* store the flags */ + /* Store the flags. */ dtar->flag = flags; - /* object ID types only, or idtype not yet initialized */ + /* Object ID types only, or idtype not yet initialized. */ if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) { dtar->idtype = ID_OB; } @@ -804,12 +818,12 @@ void driver_variable_name_validate(DriverVar *dvar) '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r', }; - /* sanity checks */ + /* Sanity checks. */ if (dvar == NULL) { return; } - /* clear all invalid-name flags */ + /* Clear all invalid-name flags. */ dvar->flag &= ~DVAR_ALL_INVALID_FLAGS; /* 0) Zero-length identifiers are not allowed */ @@ -870,16 +884,16 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver) { DriverVar *dvar; - /* sanity checks */ + /* Sanity checks. */ if (driver == NULL) { return NULL; } - /* make a new variable */ + /* Make a new variable. */ dvar = MEM_callocN(sizeof(DriverVar), "DriverVar"); BLI_addtail(&driver->variables, dvar); - /* give the variable a 'unique' name */ + /* Give the variable a 'unique' name. */ strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var")); BLI_uniquename(&driver->variables, dvar, @@ -888,13 +902,13 @@ DriverVar *driver_add_new_variable(ChannelDriver *driver) offsetof(DriverVar, name), sizeof(dvar->name)); - /* set the default type to 'single prop' */ + /* Set the default type to 'single prop'. */ driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP); - /* since driver variables are cached, the expression needs re-compiling too */ + /* Since driver variables are cached, the expression needs re-compiling too. */ BKE_driver_invalidate_expression(driver, false, true); - /* return the target */ + /* Return the target. */ return dvar; } @@ -904,20 +918,20 @@ void fcurve_free_driver(FCurve *fcu) ChannelDriver *driver; DriverVar *dvar, *dvarn; - /* sanity checks */ + /* Sanity checks. */ if (ELEM(NULL, fcu, fcu->driver)) { return; } driver = fcu->driver; - /* free driver targets */ + /* Free driver targets. */ for (dvar = driver->variables.first; dvar; dvar = dvarn) { dvarn = dvar->next; driver_free_variable_ex(driver, dvar); } #ifdef WITH_PYTHON - /* free compiled driver expression */ + /* Free compiled driver expression. */ if (driver->expr_comp) { BPY_DECREF(driver->expr_comp); } @@ -936,27 +950,31 @@ ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver) { ChannelDriver *ndriver; - /* sanity checks */ + /* Sanity checks. */ if (driver == NULL) { return NULL; } - /* copy all data */ + /* Copy all data. */ ndriver = MEM_dupallocN(driver); ndriver->expr_comp = NULL; ndriver->expr_simple = NULL; - /* copy variables */ + /* Copy variables. */ - /* to get rid of refs to non-copied data (that's still used on original) */ + /* To get rid of refs to non-copied data (that's still used on original). */ BLI_listbase_clear(&ndriver->variables); driver_variables_copy(&ndriver->variables, &driver->variables); - /* return the new driver */ + /* Return the new driver. */ return ndriver; } -/* Driver Expression Evaluation --------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Expression Evaluation + * \{ */ /* Index constants for the expression parameter array. */ enum { @@ -1026,7 +1044,7 @@ static bool driver_evaluate_simple_expr(ChannelDriver *driver, return true; default: - /* arriving here means a bug, not user error */ + /* Arriving here means a bug, not user error. */ CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression); return false; } @@ -1135,22 +1153,25 @@ void BKE_driver_invalidate_expression(ChannelDriver *driver, #endif } -/* Driver Evaluation -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Evaluation + * \{ */ /* Evaluate a Driver Variable to get a value that contributes to the final */ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar) { const DriverVarTypeInfo *dvti; - /* sanity check */ + /* Sanity check. */ if (ELEM(NULL, driver, dvar)) { return 0.0f; } - /* call the relevant callbacks to get the variable value + /* Call the relevant callbacks to get the variable value * using the variable type info, storing the obtained value - * in dvar->curval so that drivers can be debugged - */ + * in `dvar->curval` so that drivers can be debugged. */ dvti = get_dvar_typeinfo(dvar->type); if (dvti && dvti->get_value) { @@ -1167,25 +1188,25 @@ static void evaluate_driver_sum(ChannelDriver *driver) { DriverVar *dvar; - /* check how many variables there are first (i.e. just one?) */ + /* Check how many variables there are first (i.e. just one?). */ if (BLI_listbase_is_single(&driver->variables)) { - /* just one target, so just use that */ + /* Just one target, so just use that. */ dvar = driver->variables.first; driver->curval = driver_get_variable_value(driver, dvar); return; } - /* more than one target, so average the values of the targets */ + /* More than one target, so average the values of the targets. */ float value = 0.0f; int tot = 0; - /* loop through targets, adding (hopefully we don't get any overflow!) */ + /* Loop through targets, adding (hopefully we don't get any overflow!). */ for (dvar = driver->variables.first; dvar; dvar = dvar->next) { value += driver_get_variable_value(driver, dvar); tot++; } - /* perform operations on the total if appropriate */ + /* Perform operations on the total if appropriate. */ if (driver->type == DRIVER_TYPE_AVERAGE) { driver->curval = tot ? (value / (float)tot) : 0.0f; } @@ -1199,34 +1220,34 @@ static void evaluate_driver_min_max(ChannelDriver *driver) DriverVar *dvar; float value = 0.0f; - /* loop through the variables, getting the values and comparing them to existing ones */ + /* Loop through the variables, getting the values and comparing them to existing ones. */ for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - /* get value */ + /* Get value. */ float tmp_val = driver_get_variable_value(driver, dvar); - /* store this value if appropriate */ + /* Store this value if appropriate. */ if (dvar->prev) { - /* check if greater/smaller than the baseline */ + /* Check if greater/smaller than the baseline. */ if (driver->type == DRIVER_TYPE_MAX) { - /* max? */ + /* Max? */ if (tmp_val > value) { value = tmp_val; } } else { - /* min? */ + /* Min? */ if (tmp_val < value) { value = tmp_val; } } } else { - /* first item - make this the baseline for comparisons */ + /* First item - make this the baseline for comparisons. */ value = tmp_val; } } - /* store value in driver */ + /* Store value in driver. */ driver->curval = value; } @@ -1235,62 +1256,63 @@ static void evaluate_driver_python(PathResolvedRNA *anim_rna, ChannelDriver *driver_orig, const AnimationEvalContext *anim_eval_context) { - /* check for empty or invalid expression */ + /* Check for empty or invalid expression. */ if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) { driver->curval = 0.0f; } else if (!driver_try_evaluate_simple_expr( driver, driver_orig, &driver->curval, anim_eval_context->eval_time)) { #ifdef WITH_PYTHON - /* this evaluates the expression using Python, and returns its result: - * - on errors it reports, then returns 0.0f - */ + /* This evaluates the expression using Python, and returns its result: + * - on errors it reports, then returns 0.0f. */ BLI_mutex_lock(&python_driver_lock); driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, anim_eval_context); BLI_mutex_unlock(&python_driver_lock); -#else /* WITH_PYTHON*/ +#else /* WITH_PYTHON */ UNUSED_VARS(anim_rna, anim_eval_context); -#endif /* WITH_PYTHON*/ +#endif /* WITH_PYTHON */ } } -/* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime" - * - "evaltime" is the frame at which F-Curve is being evaluated - * - has to return a float value - * - driver_orig is where we cache Python expressions, in case of COW +/** + * Evaluate an Channel-Driver to get a 'time' value to use + * instead of `anim_eval_context->eval_time`. + * + * - `anim_eval_context->eval_time` is the frame at which F-Curve is being evaluated. + * - Has to return a float value. + * - \a driver_orig is where we cache Python expressions, in case of COW */ float evaluate_driver(PathResolvedRNA *anim_rna, ChannelDriver *driver, ChannelDriver *driver_orig, const AnimationEvalContext *anim_eval_context) { - /* check if driver can be evaluated */ + /* Check if driver can be evaluated. */ if (driver_orig->flag & DRIVER_FLAG_INVALID) { return 0.0f; } switch (driver->type) { - case DRIVER_TYPE_AVERAGE: /* average values of driver targets */ - case DRIVER_TYPE_SUM: /* sum values of driver targets */ + case DRIVER_TYPE_AVERAGE: /* Average values of driver targets. */ + case DRIVER_TYPE_SUM: /* Sum values of driver targets. */ evaluate_driver_sum(driver); break; - case DRIVER_TYPE_MIN: /* smallest value */ - case DRIVER_TYPE_MAX: /* largest value */ + case DRIVER_TYPE_MIN: /* Smallest value. */ + case DRIVER_TYPE_MAX: /* Largest value. */ evaluate_driver_min_max(driver); break; - case DRIVER_TYPE_PYTHON: /* expression */ + case DRIVER_TYPE_PYTHON: /* Expression. */ evaluate_driver_python(anim_rna, driver, driver_orig, anim_eval_context); break; default: - /* special 'hack' - just use stored value + /* Special 'hack' - just use stored value * This is currently used as the mechanism which allows animated settings to be able - * to be changed via the UI. - */ + * to be changed via the UI. */ break; } - /* return value for driver */ + /* Return value for driver. */ return driver->curval; } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 079b436a3ea..2245af31f0d 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1119,6 +1119,7 @@ static void ensure_obstaclefields(FluidDomainSettings *fds) if (fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE) { manta_ensure_guiding(fds->fluid, fds->fmd); } + manta_update_pointers(fds->fluid, fds->fmd, false); } static void update_obstacleflags(FluidDomainSettings *fds, @@ -2596,7 +2597,7 @@ static void ensure_flowsfields(FluidDomainSettings *fds) manta_smoke_ensure_fire(fds->fluid, fds->fmd); } if (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) { - /* initialize all smoke with "active_color" */ + /* Initialize all smoke with "active_color". */ manta_smoke_ensure_colors(fds->fluid, fds->fmd); } if (fds->type == FLUID_DOMAIN_TYPE_LIQUID && @@ -2605,6 +2606,7 @@ static void ensure_flowsfields(FluidDomainSettings *fds) fds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER)) { manta_liquid_ensure_sndparts(fds->fluid, fds->fmd); } + manta_update_pointers(fds->fluid, fds->fmd, false); } static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int numflowobj) @@ -2617,7 +2619,7 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n FLUID_DOMAIN_ACTIVE_HEAT | FLUID_DOMAIN_ACTIVE_FIRE); active_fields &= ~prev_flags; - /* Monitor active fields based on flow settings */ + /* Monitor active fields based on flow settings. */ for (flow_index = 0; flow_index < numflowobj; flow_index++) { Object *flow_ob = flowobjs[flow_index]; FluidModifierData *fmd2 = (FluidModifierData *)BKE_modifiers_findby_type(flow_ob, @@ -2628,6 +2630,7 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n continue; } + /* Activate specific grids if at least one flow object requires this grid. */ if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) { FluidFlowSettings *ffs = fmd2->flow; if (!ffs) { @@ -2648,17 +2651,17 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n continue; } - /* activate heat field if flow produces any heat */ - if (ffs->temperature) { + /* Activate heat field if a flow object produces any heat. */ + if (ffs->temperature != 0.0) { active_fields |= FLUID_DOMAIN_ACTIVE_HEAT; } - /* activate fuel field if flow adds any fuel */ - if (ffs->fuel_amount && - (ffs->type == FLUID_FLOW_TYPE_FIRE || ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) { + /* Activate fuel field if a flow object is of fire type. */ + if (ffs->fuel_amount != 0.0 || ffs->type == FLUID_FLOW_TYPE_FIRE || + ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE) { active_fields |= FLUID_DOMAIN_ACTIVE_FIRE; } - /* activate color field if flows add smoke with varying colors */ - if (ffs->density && + /* Activate color field if flows add smoke with varying colors. */ + if (ffs->density != 0.0 && (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) { if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) { copy_v3_v3(fds->active_color, ffs->color); @@ -2671,11 +2674,11 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n } } } - /* Monitor active fields based on domain settings */ + /* Monitor active fields based on domain settings. */ if (fds->type == FLUID_DOMAIN_TYPE_GAS && active_fields & FLUID_DOMAIN_ACTIVE_FIRE) { - /* heat is always needed for fire */ + /* Heat is always needed for fire. */ active_fields |= FLUID_DOMAIN_ACTIVE_HEAT; - /* also activate colors if domain smoke color differs from active color */ + /* Also activate colors if domain smoke color differs from active color. */ if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) { copy_v3_v3(fds->active_color, fds->flame_smoke_color); active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET; @@ -2924,8 +2927,21 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, float *velx_initial = manta_get_in_velocity_x(fds->fluid); float *vely_initial = manta_get_in_velocity_y(fds->fluid); float *velz_initial = manta_get_in_velocity_z(fds->fluid); - uint z; + float *forcex = manta_get_force_x(fds->fluid); + float *forcey = manta_get_force_y(fds->fluid); + float *forcez = manta_get_force_z(fds->fluid); + + BLI_assert(forcex && forcey && forcez); + + /* Either all or no components have to exist. */ + BLI_assert((color_r && color_g && color_b) || (!color_r && !color_g && !color_b)); + BLI_assert((color_r_in && color_g_in && color_b_in) || + (!color_r_in && !color_g_in && !color_b_in)); + BLI_assert((velx_initial && vely_initial && velz_initial) || + (!velx_initial && !vely_initial && !velz_initial)); + + uint z; /* Grid reset before writing again. */ for (z = 0; z < fds->res[0] * fds->res[1] * fds->res[2]; z++) { /* Only reset static phi on first frame, dynamic phi gets reset every time. */ @@ -2949,7 +2965,7 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, if (heat_in) { heat_in[z] = heat[z]; } - if (color_r_in) { + if (color_r_in && color_g_in && color_b_in) { color_r_in[z] = color_r[z]; color_g_in[z] = color_b[z]; color_b_in[z] = color_g[z]; @@ -2961,11 +2977,15 @@ static void update_flowsfluids(struct Depsgraph *depsgraph, if (emission_in) { emission_in[z] = 0.0f; } - if (velx_initial) { + if (velx_initial && vely_initial && velz_initial) { velx_initial[z] = 0.0f; vely_initial[z] = 0.0f; velz_initial[z] = 0.0f; } + /* Reset forces here as update_effectors() is skipped when no external forces are present. */ + forcex[z] = 0.0f; + forcey[z] = 0.0f; + forcez[z] = 0.0f; } /* Apply emission data for every flow object. */ @@ -3149,13 +3169,13 @@ static void update_effectors_task_cb(void *__restrict userdata, continue; } - /* get velocities from manta grid space and convert to blender units */ + /* Get velocities from manta grid space and convert to blender units. */ vel[0] = data->velocity_x[index]; vel[1] = data->velocity_y[index]; vel[2] = data->velocity_z[index]; mul_v3_fl(vel, fds->dx); - /* convert vel to global space */ + /* Convert vel to global space. */ mag = len_v3(vel); mul_mat3_m4_v3(fds->obmat, vel); normalize_v3(vel); @@ -3166,18 +3186,18 @@ static void update_effectors_task_cb(void *__restrict userdata, voxel_center[2] = fds->p0[2] + fds->cell_size[2] * ((float)(z + fds->res_min[2]) + 0.5f); mul_m4_v3(fds->obmat, voxel_center); - /* do effectors */ + /* Do effectors. */ pd_point_from_loc(data->scene, voxel_center, vel, index, &epoint); BKE_effectors_apply( data->effectors, NULL, fds->effector_weights, &epoint, retvel, NULL, NULL); - /* convert retvel to local space */ + /* Convert retvel to local space. */ mag = len_v3(retvel); mul_mat3_m4_v3(fds->imat, retvel); normalize_v3(retvel); mul_v3_fl(retvel, mag); - /* constrain forces to interval -1 to 1 */ + /* Constrain forces to interval -1 to 1. */ data->force_x[index] = min_ff(max_ff(-1.0f, retvel[0] * 0.2f), 1.0f); data->force_y[index] = min_ff(max_ff(-1.0f, retvel[1] * 0.2f), 1.0f); data->force_z[index] = min_ff(max_ff(-1.0f, retvel[2] * 0.2f), 1.0f); @@ -3617,14 +3637,16 @@ static int manta_step( fds->time_per_frame = time_per_frame; fds->time_total = time_total; } + /* Total time must not exceed framecount times framelength. Correct tiny errors here. */ CLAMP(fds->time_total, fds->time_total, time_total_old + fds->frame_length); + /* Compute shadow grid for gas simulations. Make sure to skip if bake job was canceled early. */ if (fds->type == FLUID_DOMAIN_TYPE_GAS && result) { manta_smoke_calc_transparency(fds, DEG_get_evaluated_view_layer(depsgraph)); } - BLI_mutex_unlock(&object_update_lock); + BLI_mutex_unlock(&object_update_lock); return result; } @@ -3716,29 +3738,35 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, int mode = fds->cache_type; /* Do not process modifier if current frame is out of cache range. */ + bool escape = false; switch (mode) { case FLUID_DOMAIN_CACHE_ALL: case FLUID_DOMAIN_CACHE_MODULAR: if (fds->cache_frame_offset > 0) { if (scene_framenr < fds->cache_frame_start || scene_framenr > fds->cache_frame_end + fds->cache_frame_offset) { - return; + escape = true; } } else { if (scene_framenr < fds->cache_frame_start + fds->cache_frame_offset || scene_framenr > fds->cache_frame_end) { - return; + escape = true; } } break; case FLUID_DOMAIN_CACHE_REPLAY: default: if (scene_framenr < fds->cache_frame_start || scene_framenr > fds->cache_frame_end) { - return; + escape = true; } break; } + /* If modifier will not be processed, update/flush pointers from (old) fluid object once more. */ + if (escape && fds->fluid) { + manta_update_pointers(fds->fluid, fmd, true); + return; + } /* Reset fluid if no fluid present. Also resets active fields. */ if (!fds->fluid) { @@ -3830,7 +3858,15 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, has_mesh = manta_has_mesh(fds->fluid, fmd, scene_framenr); has_particles = manta_has_particles(fds->fluid, fmd, scene_framenr); has_guide = manta_has_guiding(fds->fluid, fmd, scene_framenr, guide_parent); - has_config = false; + has_config = manta_read_config(fds->fluid, fmd, scene_framenr); + + /* When reading data from cache (has_config == true) ensure that active fields are allocated. + * update_flowsflags() and update_obstacleflags() will not find flow sources hidden from renders. + * See also: T72192. */ + if (has_config) { + ensure_flowsfields(fds); + ensure_obstaclefields(fds); + } bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide; baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA; @@ -3947,7 +3983,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, /* Read mesh cache. */ if (with_liquid && with_mesh) { - has_config = manta_read_config(fds->fluid, fmd, mesh_frame); + if (mesh_frame != scene_framenr) { + has_config = manta_read_config(fds->fluid, fmd, mesh_frame); + } /* Update mesh data from file is faster than via Python (manta_read_mesh()). */ has_mesh = manta_read_mesh(fds->fluid, fmd, mesh_frame); @@ -3955,7 +3993,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, /* Read particles cache. */ if (with_liquid && with_particles) { - has_config = manta_read_config(fds->fluid, fmd, particles_frame); + if (particles_frame != scene_framenr) { + has_config = manta_read_config(fds->fluid, fmd, particles_frame); + } read_partial = !baking_data && !baking_particles && next_particles; read_all = !read_partial && with_resumable_cache; @@ -3970,7 +4010,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, /* Read noise and data cache */ if (with_smoke && with_noise) { - has_config = manta_read_config(fds->fluid, fmd, noise_frame); + if (noise_frame != scene_framenr) { + has_config = manta_read_config(fds->fluid, fmd, noise_frame); + } /* Only reallocate when just reading cache or when resuming during bake. */ if (has_data && has_config && manta_needs_realloc(fds->fluid, fmd)) { @@ -3988,7 +4030,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, } /* Read data cache only */ else { - has_config = manta_read_config(fds->fluid, fmd, data_frame); + if (data_frame != scene_framenr) { + has_config = manta_read_config(fds->fluid, fmd, data_frame); + } if (with_smoke) { /* Read config and realloc fluid object if needed. */ @@ -4073,6 +4117,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, } } + /* Ensure that fluid pointers are always up to date at the end of modifier processing. */ + manta_update_pointers(fds->fluid, fmd, false); + fds->flags &= ~FLUID_DOMAIN_FILE_LOAD; fmd->time = scene_framenr; } diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index dfa5ff6975f..958acf0589b 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -797,7 +797,7 @@ static bool vfont_to_curve(Object *ob, } else { char32_t *mem_tmp; - slen = cu->len_wchar; + slen = cu->len_char32; /* Create unicode string */ mem_tmp = MEM_malloc_arrayN((slen + 1), sizeof(*mem_tmp), "convertedmem"); diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 579ebc9b9b3..14eb6bb4f4c 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -2599,10 +2599,9 @@ void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps) float color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; bGPDspoint *pt = &gps->points[0]; - color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->x)); - color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->y)); - color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints, pt->z)); - + color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints / 5, pt->x + pt->z)); + color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints + pt->x, pt->y * pt->z + pt->x)); + color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints - pt->x, pt->z * pt->x + pt->y)); for (int i = 0; i < gps->totpoints; i++) { pt = &gps->points[i]; copy_v4_v4(pt->vert_color, color); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 9365ee040c2..e331e5ae1dd 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -57,6 +57,7 @@ #include "DNA_packedFile_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_simulation_types.h" #include "DNA_world_types.h" #include "BLI_blenlib.h" @@ -89,7 +90,6 @@ #include "RE_pipeline.h" -#include "GPU_draw.h" #include "GPU_texture.h" #include "BLI_sys_types.h" // for intptr_t support @@ -392,7 +392,7 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock) ima->rr = NULL; } - GPU_free_image(ima); + BKE_image_free_gputextures(ima); LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { tile->ok = IMA_OK; @@ -3240,6 +3240,12 @@ static void image_walk_id_all_users( if (scene->nodetree && scene->use_nodes && !skip_nested_nodes) { image_walk_ntree_all_users(scene->nodetree, &scene->id, customdata, callback); } + break; + } + case ID_SIM: { + Simulation *simulation = (Simulation *)id; + image_walk_ntree_all_users(simulation->nodetree, &simulation->id, customdata, callback); + break; } default: break; @@ -3344,8 +3350,7 @@ static void image_free_tile(Image *ima, ImageTile *tile) for (int i = 0; i < TEXTARGET_COUNT; i++) { /* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other * two. */ - if (tile != ima->tiles.first && - !(ELEM(i, TEXTARGET_TEXTURE_2D_ARRAY, TEXTARGET_TEXTURE_TILE_MAPPING))) { + if (tile != ima->tiles.first && !(ELEM(i, TEXTARGET_2D_ARRAY, TEXTARGET_TILE_MAPPING))) { continue; } @@ -3622,13 +3627,13 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la for (int eye = 0; eye < 2; eye++) { /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][eye] = NULL; + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; } - if (ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] = NULL; + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; } } @@ -3937,7 +3942,7 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) #endif /* WITH_OPENEXR */ /* common stuff to do with images after loading */ -static void image_initialize_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf)) +static void image_init_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf)) { /* Preview is NULL when it has never been used as an icon before. * Never handle previews/icons outside of main thread. */ @@ -4040,11 +4045,11 @@ static ImBuf *load_sequence_single( } } else { - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); *r_assign = true; } #else - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); *r_assign = true; #endif } @@ -4149,7 +4154,7 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int e BKE_imbuf_stamp_info(ima->rr, ibuf); - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, entry); } // else printf("pass not found\n"); @@ -4213,7 +4218,7 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i ibuf = IMB_makeSingleUser(IMB_anim_absolute(ia->anim, fra, IMB_TC_RECORD_RUN, IMB_PROXY_NONE)); if (ibuf) { - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); } else { tile->ok = 0; @@ -4358,7 +4363,7 @@ static ImBuf *load_image_single(Image *ima, else #endif { - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); *r_assign = true; /* make packed file for autopack */ @@ -4472,7 +4477,7 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) if (rpass) { ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0); - image_initialize_after_load(ima, iuser, ibuf); + image_init_after_load(ima, iuser, ibuf); ibuf->rect_float = rpass->rect; ibuf->flags |= IB_rectfloat; diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c new file mode 100644 index 00000000000..22fb6dfd02a --- /dev/null +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -0,0 +1,774 @@ +/* + * 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) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_boxpack_2d.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_threads.h" + +#include "DNA_image_types.h" +#include "DNA_userdef_types.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_main.h" + +#include "GPU_extensions.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "PIL_time.h" + +/* Prototypes. */ +static void gpu_free_unused_buffers(void); +static void image_free_gpu(Image *ima, const bool immediate); + +/* -------------------------------------------------------------------- */ +/** \name UDIM gpu texture + * \{ */ + +static bool is_over_resolution_limit(int w, int h) +{ + return (w > GPU_texture_size_with_limit(w) || h > GPU_texture_size_with_limit(h)); +} + +static int smaller_power_of_2_limit(int num) +{ + return power_of_2_min_i(GPU_texture_size_with_limit(num)); +} + +static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) +{ + GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; + + if (tilearray == NULL) { + return 0; + } + + float array_w = GPU_texture_width(tilearray); + float array_h = GPU_texture_height(tilearray); + + ImageTile *last_tile = (ImageTile *)ima->tiles.last; + /* Tiles are sorted by number. */ + int max_tile = last_tile->tile_number - 1001; + + /* create image */ + int width = max_tile + 1; + float *data = (float *)MEM_callocN(width * 8 * sizeof(float), __func__); + for (int i = 0; i < width; i++) { + data[4 * i] = -1.0f; + } + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int i = tile->tile_number - 1001; + data[4 * i] = tile->runtime.tilearray_layer; + + float *tile_info = &data[4 * width + 4 * i]; + tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; + tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; + tile_info[2] = tile->runtime.tilearray_size[0] / array_w; + tile_info[3] = tile->runtime.tilearray_size[1] / array_h; + } + + GPUTexture *tex = GPU_texture_create_1d_array(width, 2, GPU_RGBA32F, data, NULL); + GPU_texture_mipmap_mode(tex, false, false); + + MEM_freeN(data); + + return tex; +} + +typedef struct PackTile { + FixedSizeBoxPack boxpack; + ImageTile *tile; + float pack_score; +} PackTile; + +static int compare_packtile(const void *a, const void *b) +{ + const PackTile *tile_a = (const PackTile *)a; + const PackTile *tile_b = (const PackTile *)b; + + return tile_a->pack_score < tile_b->pack_score; +} + +static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) +{ + int arraywidth = 0, arrayheight = 0; + ListBase boxes = {NULL}; + + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + + if (ibuf) { + PackTile *packtile = (PackTile *)MEM_callocN(sizeof(PackTile), __func__); + packtile->tile = tile; + packtile->boxpack.w = ibuf->x; + packtile->boxpack.h = ibuf->y; + + if (is_over_resolution_limit(packtile->boxpack.w, packtile->boxpack.h)) { + packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); + packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); + } + arraywidth = max_ii(arraywidth, packtile->boxpack.w); + arrayheight = max_ii(arrayheight, packtile->boxpack.h); + + /* We sort the tiles by decreasing size, with an additional penalty term + * for high aspect ratios. This improves packing efficiency. */ + float w = packtile->boxpack.w, h = packtile->boxpack.h; + packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h; + + BKE_image_release_ibuf(ima, ibuf, NULL); + BLI_addtail(&boxes, packtile); + } + } + + BLI_assert(arraywidth > 0 && arrayheight > 0); + + BLI_listbase_sort(&boxes, compare_packtile); + int arraylayers = 0; + /* Keep adding layers until all tiles are packed. */ + while (boxes.first != NULL) { + ListBase packed = {NULL}; + BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed); + BLI_assert(packed.first != NULL); + + LISTBASE_FOREACH (PackTile *, packtile, &packed) { + ImageTile *tile = packtile->tile; + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + + tileoffset[0] = packtile->boxpack.x; + tileoffset[1] = packtile->boxpack.y; + tilesize[0] = packtile->boxpack.w; + tilesize[1] = packtile->boxpack.h; + tile->runtime.tilearray_layer = arraylayers; + } + + BLI_freelistN(&packed); + arraylayers++; + } + + const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); + /* Create Texture without content. */ + GPUTexture *tex = IMB_touch_gpu_texture( + main_ibuf, arraywidth, arrayheight, arraylayers, use_high_bitdepth); + + GPU_texture_bind(tex, 0); + + /* Upload each tile one by one. */ + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int tilelayer = tile->runtime.tilearray_layer; + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + + if (tilesize[0] == 0 || tilesize[1] == 0) { + continue; + } + + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + + if (ibuf) { + const bool store_premultiplied = ibuf->rect_float ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : + (ima->alpha_mode == IMA_ALPHA_PREMUL); + IMB_update_gpu_texture_sub(tex, + ibuf, + UNPACK2(tileoffset), + tilelayer, + UNPACK2(tilesize), + use_high_bitdepth, + store_premultiplied); + } + + BKE_image_release_ibuf(ima, ibuf, NULL); + } + + if (GPU_mipmap_enabled()) { + GPU_texture_generate_mipmap(tex); + GPU_texture_mipmap_mode(tex, true, true); + if (ima) { + ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; + } + } + else { + GPU_texture_mipmap_mode(tex, false, true); + } + + GPU_texture_unbind(tex); + + return tex; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Regular gpu texture + * \{ */ + +static GPUTexture **get_image_gpu_texture_ptr(Image *ima, + eGPUTextureTarget textarget, + const int multiview_eye) +{ + const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); + BLI_assert(in_range); + + if (in_range) { + return &(ima->gputexture[textarget][multiview_eye]); + } + return NULL; +} + +static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget) +{ + switch (textarget) { + case TEXTARGET_2D_ARRAY: + return GPU_texture_create_error(2, true); + case TEXTARGET_TILE_MAPPING: + return GPU_texture_create_error(1, true); + case TEXTARGET_2D: + default: + return GPU_texture_create_error(2, false); + } +} + +static GPUTexture *image_get_gpu_texture(Image *ima, + ImageUser *iuser, + ImBuf *ibuf, + eGPUTextureTarget textarget) +{ + if (ima == NULL) { + return NULL; + } + + /* Free any unused GPU textures, since we know we are in a thread with OpenGL + * context and might as well ensure we have as much space free as possible. */ + gpu_free_unused_buffers(); + + /* currently, gpu refresh tagging is used by ima sequences */ + if (ima->gpuflag & IMA_GPU_REFRESH) { + image_free_gpu(ima, true); + ima->gpuflag &= ~IMA_GPU_REFRESH; + } + + /* Tag as in active use for garbage collector. */ + BKE_image_tag_time(ima); + + /* Test if we already have a texture. */ + GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, iuser ? iuser->multiview_eye : 0); + if (*tex) { + return *tex; + } + + /* Check if we have a valid image. If not, we return a dummy + * texture with zero bind-code so we don't keep trying. */ + ImageTile *tile = BKE_image_get_tile(ima, 0); + if (tile == NULL || tile->ok == 0) { + *tex = image_gpu_texture_error_create(textarget); + return *tex; + } + + /* check if we have a valid image buffer */ + ImBuf *ibuf_intern = ibuf; + if (ibuf_intern == NULL) { + ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL); + if (ibuf_intern == NULL) { + *tex = image_gpu_texture_error_create(textarget); + return *tex; + } + } + + if (textarget == TEXTARGET_2D_ARRAY) { + *tex = gpu_texture_create_tile_array(ima, ibuf_intern); + } + else if (textarget == TEXTARGET_TILE_MAPPING) { + *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); + } + else { + const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); + const bool store_premultiplied = ibuf_intern->rect_float ? + (ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false) : + (ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true); + + *tex = IMB_create_gpu_texture(ibuf_intern, use_high_bitdepth, store_premultiplied); + + if (GPU_mipmap_enabled()) { + GPU_texture_bind(*tex, 0); + GPU_texture_generate_mipmap(*tex); + GPU_texture_unbind(*tex); + if (ima) { + ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; + } + GPU_texture_mipmap_mode(*tex, true, true); + } + else { + GPU_texture_mipmap_mode(*tex, false, true); + } + } + + /* if `ibuf` was given, we don't own the `ibuf_intern` */ + if (ibuf == NULL) { + BKE_image_release_ibuf(ima, ibuf_intern, NULL); + } + + GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); + + return *tex; +} + +GPUTexture *BKE_image_get_gpu_texture(Image *image, ImageUser *iuser, ImBuf *ibuf) +{ + return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_2D); +} + +GPUTexture *BKE_image_get_gpu_tiles(Image *image, ImageUser *iuser, ImBuf *ibuf) +{ + return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_2D_ARRAY); +} + +GPUTexture *BKE_image_get_gpu_tilemap(Image *image, ImageUser *iuser, ImBuf *ibuf) +{ + return image_get_gpu_texture(image, iuser, ibuf, TEXTARGET_TILE_MAPPING); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delayed GPU texture free + * + * Image datablocks can be deleted by any thread, but there may not be any active OpenGL context. + * In that case we push them into a queue and free the buffers later. + * \{ */ + +static LinkNode *gpu_texture_free_queue = NULL; +static ThreadMutex gpu_texture_queue_mutex = BLI_MUTEX_INITIALIZER; + +static void gpu_free_unused_buffers(void) +{ + if (gpu_texture_free_queue == NULL) { + return; + } + + BLI_mutex_lock(&gpu_texture_queue_mutex); + + while (gpu_texture_free_queue != NULL) { + GPUTexture *tex = BLI_linklist_pop(&gpu_texture_free_queue); + GPU_texture_free(tex); + } + + BLI_mutex_unlock(&gpu_texture_queue_mutex); +} + +void BKE_image_free_unused_gpu_textures() +{ + if (BLI_thread_is_main()) { + gpu_free_unused_buffers(); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Deletion + * \{ */ + +static void image_free_gpu(Image *ima, const bool immediate) +{ + for (int eye = 0; eye < 2; eye++) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + if (ima->gputexture[i][eye] != NULL) { + if (immediate) { + GPU_texture_free(ima->gputexture[i][eye]); + } + else { + BLI_mutex_lock(&gpu_texture_queue_mutex); + BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]); + BLI_mutex_unlock(&gpu_texture_queue_mutex); + } + + ima->gputexture[i][eye] = NULL; + } + } + } + + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; +} + +void BKE_image_free_gputextures(Image *ima) +{ + image_free_gpu(ima, BLI_thread_is_main()); +} + +void BKE_image_free_all_gputextures(Main *bmain) +{ + if (bmain) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + BKE_image_free_gputextures(ima); + } + } +} + +/* same as above but only free animated images */ +void BKE_image_free_anim_gputextures(Main *bmain) +{ + if (bmain) { + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + if (BKE_image_is_animated(ima)) { + BKE_image_free_gputextures(ima); + } + } + } +} + +void BKE_image_free_old_gputextures(Main *bmain) +{ + static int lasttime = 0; + int ctime = (int)PIL_check_seconds_timer(); + + /* + * Run garbage collector once for every collecting period of time + * if textimeout is 0, that's the option to NOT run the collector + */ + if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) { + return; + } + + /* of course not! */ + if (G.is_rendering) { + return; + } + + lasttime = ctime; + + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) { + /* If it's in GL memory, deallocate and set time tag to current time + * This gives textures a "second chance" to be used before dying. */ + if (BKE_image_has_opengl_texture(ima)) { + BKE_image_free_gputextures(ima); + ima->lastused = ctime; + } + /* Otherwise, just kill the buffers */ + else { + BKE_image_free_buffers(ima); + } + } + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Paint Update + * \{ */ + +static ImBuf *update_do_scale(uchar *rect, + float *rect_float, + int *x, + int *y, + int *w, + int *h, + int limit_w, + int limit_h, + int full_w, + int full_h) +{ + /* Partial update with scaling. */ + float xratio = limit_w / (float)full_w; + float yratio = limit_h / (float)full_h; + + int part_w = *w, part_h = *h; + + /* Find sub coordinates in scaled image. Take ceiling because we will be + * losing 1 pixel due to rounding errors in x,y. */ + *x *= xratio; + *y *= yratio; + *w = (int)ceil(xratio * (*w)); + *h = (int)ceil(yratio * (*h)); + + /* ...but take back if we are over the limit! */ + if (*x + *w > limit_w) { + (*w)--; + } + if (*y + *h > limit_h) { + (*h)--; + } + + /* Scale pixels. */ + ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4); + IMB_scaleImBuf(ibuf, *w, *h); + + return ibuf; +} + +static void gpu_texture_update_scaled(GPUTexture *tex, + uchar *rect, + float *rect_float, + int full_w, + int full_h, + int x, + int y, + int layer, + const int *tile_offset, + const int *tile_size, + int w, + int h) +{ + ImBuf *ibuf; + if (layer > -1) { + ibuf = update_do_scale( + rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h); + + /* Shift to account for tile packing. */ + x += tile_offset[0]; + y += tile_offset[1]; + } + else { + /* Partial update with scaling. */ + int limit_w = smaller_power_of_2_limit(full_w); + int limit_h = smaller_power_of_2_limit(full_h); + + ibuf = update_do_scale(rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h); + } + + void *data = (ibuf->rect_float) ? (void *)(ibuf->rect_float) : (void *)(ibuf->rect); + eGPUDataFormat data_format = (ibuf->rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + + GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1); + + IMB_freeImBuf(ibuf); +} + +static void gpu_texture_update_unscaled(GPUTexture *tex, + uchar *rect, + float *rect_float, + int x, + int y, + int layer, + const int tile_offset[2], + int w, + int h, + int tex_stride, + int tex_offset) +{ + if (layer > -1) { + /* Shift to account for tile packing. */ + x += tile_offset[0]; + y += tile_offset[1]; + } + + void *data = (rect_float) ? (void *)(rect_float + tex_offset) : (void *)(rect + tex_offset); + eGPUDataFormat data_format = (rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + + /* Partial update without scaling. Stride and offset are used to copy only a + * subset of a possible larger buffer than what we are updating. */ + GPU_unpack_row_length_set(tex_stride); + + GPU_texture_update_sub(tex, data_format, data, x, y, layer, w, h, 1); + /* Restore default. */ + GPU_unpack_row_length_set(0); +} + +static void gpu_texture_update_from_ibuf( + GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) +{ + bool scaled; + if (tile != NULL) { + int *tilesize = tile->runtime.tilearray_size; + scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); + } + else { + scaled = is_over_resolution_limit(ibuf->x, ibuf->y); + } + + if (scaled) { + /* Extra padding to account for bleed from neighboring pixels. */ + const int padding = 4; + const int xmax = min_ii(x + w + padding, ibuf->x); + const int ymax = min_ii(y + h + padding, ibuf->y); + x = max_ii(x - padding, 0); + y = max_ii(y - padding, 0); + w = xmax - x; + h = ymax - y; + } + + /* Get texture data pointers. */ + float *rect_float = ibuf->rect_float; + uchar *rect = (uchar *)ibuf->rect; + int tex_stride = ibuf->x; + int tex_offset = ibuf->channels * (y * ibuf->x + x); + + if (rect_float == NULL) { + /* Byte pixels. */ + if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear( + ibuf->rect_colorspace); + + rect = (uchar *)MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__); + if (rect == NULL) { + return; + } + + tex_stride = w; + tex_offset = 0; + + /* Convert to scene linear with sRGB compression, and premultiplied for + * correct texture interpolation. */ + const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL); + IMB_colormanagement_imbuf_to_byte_texture( + rect, x, y, w, h, ibuf, compress_as_srgb, store_premultiplied); + } + } + else { + /* Float pixels. */ + const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT); + + if (ibuf->channels != 4 || scaled || !store_premultiplied) { + rect_float = (float *)MEM_mallocN(sizeof(float) * 4 * w * h, __func__); + if (rect_float == NULL) { + return; + } + + tex_stride = w; + tex_offset = 0; + + IMB_colormanagement_imbuf_to_float_texture( + rect_float, x, y, w, h, ibuf, store_premultiplied); + } + } + + GPU_texture_bind(tex, 0); + + if (scaled) { + /* Slower update where we first have to scale the input pixels. */ + if (tile != NULL) { + int *tileoffset = tile->runtime.tilearray_offset; + int *tilesize = tile->runtime.tilearray_size; + int tilelayer = tile->runtime.tilearray_layer; + gpu_texture_update_scaled( + tex, rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); + } + else { + gpu_texture_update_scaled( + tex, rect, rect_float, ibuf->x, ibuf->y, x, y, -1, NULL, NULL, w, h); + } + } + else { + /* Fast update at same resolution. */ + if (tile != NULL) { + int *tileoffset = tile->runtime.tilearray_offset; + int tilelayer = tile->runtime.tilearray_layer; + gpu_texture_update_unscaled( + tex, rect, rect_float, x, y, tilelayer, tileoffset, w, h, tex_stride, tex_offset); + } + else { + gpu_texture_update_unscaled( + tex, rect, rect_float, x, y, -1, NULL, w, h, tex_stride, tex_offset); + } + } + + /* Free buffers if needed. */ + if (rect && rect != (uchar *)ibuf->rect) { + MEM_freeN(rect); + } + if (rect_float && rect_float != ibuf->rect_float) { + MEM_freeN(rect_float); + } + + if (GPU_mipmap_enabled()) { + GPU_texture_generate_mipmap(tex); + } + else { + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + } + + GPU_texture_unbind(tex); +} + +/* Partial update of texture for texture painting. This is often much + * quicker than fully updating the texture for high resolution images. */ +void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h) +{ + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + + if ((ibuf == NULL) || (w == 0) || (h == 0)) { + /* Full reload of texture. */ + BKE_image_free_gputextures(ima); + } + + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; + /* Check if we need to update the main gputexture. */ + if (tex != NULL && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); + } + + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; + if (tex != NULL) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); + } + + BKE_image_release_ibuf(ima, ibuf, NULL); +} + +/* these two functions are called on entering and exiting texture paint mode, + * temporary disabling/enabling mipmapping on all images for quick texture + * updates with glTexSubImage2D. images that didn't change don't have to be + * re-uploaded to OpenGL */ +void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) +{ + LISTBASE_FOREACH (Image *, ima, &bmain->images) { + if (BKE_image_has_opengl_texture(ima)) { + if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { + for (int eye = 0; eye < 2; eye++) { + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { + GPUTexture *tex = ima->gputexture[a][eye]; + if (tex != NULL) { + GPU_texture_mipmap_mode(tex, mipmap, true); + } + } + } + } + } + else { + BKE_image_free_gputextures(ima); + } + } + else { + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + } + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 64649d84320..83071fd5dd3 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -221,7 +221,7 @@ ViewLayer *BKE_view_layer_add(Scene *scene, view_layer_new = view_layer_add(name); BLI_addtail(&scene->view_layers, view_layer_new); - /* Initialise layercollections */ + /* Initialize layer-collections. */ BKE_layer_collection_sync(scene, view_layer_new); layer_collection_exclude_all(view_layer_new->layer_collections.first); diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 2989c910c45..8b0517a77fb 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1515,7 +1515,7 @@ void BKE_lib_override_library_main_update(Main *bmain) */ /** Initialize an override storage. */ -OverrideLibraryStorage *BKE_lib_override_library_operations_store_initialize(void) +OverrideLibraryStorage *BKE_lib_override_library_operations_store_init(void) { return BKE_main_new(); } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 00a42b12e07..0f81d45c10f 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -412,6 +412,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return ELEM(id_type_used, ID_MA); case ID_VO: return ELEM(id_type_used, ID_MA); + case ID_SIM: + return ELEM(id_type_used, ID_OB, ID_IM); case ID_IM: case ID_VF: case ID_TXT: @@ -422,7 +424,6 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) case ID_PAL: case ID_PC: case ID_CF: - case ID_SIM: /* Those types never use/reference other IDs... */ return false; case ID_IP: diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index aa1005c663f..f42df6765c4 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -58,7 +58,7 @@ static void light_init_data(ID *id) MEMCPY_STRUCT_AFTER(la, DNA_struct_default_get(Light), id); la->curfalloff = BKE_curvemapping_add(1, 0.0f, 1.0f, 1.0f, 0.0f); - BKE_curvemapping_initialize(la->curfalloff); + BKE_curvemapping_init(la->curfalloff); } /** diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 34835fd1e35..aa72493e472 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -959,14 +959,13 @@ void BKE_object_material_array_assign(Main *bmain, const bool to_object_only) { int actcol_orig = ob->actcol; - short i; while ((ob->totcol > totcol) && BKE_object_material_slot_remove(bmain, ob)) { /* pass */ } /* now we have the right number of slots */ - for (i = 0; i < totcol; i++) { + for (int i = 0; i < totcol; i++) { if (to_object_only && ob->matbits[i] == 0) { /* If we only assign to object, and that slot uses obdata material, do nothing. */ continue; diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 4a65c6ff5e7..04e61df1173 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1866,3 +1866,84 @@ void BKE_movieclip_eval_selection_update(struct Depsgraph *depsgraph, MovieClip DEG_debug_print_eval(depsgraph, __func__, clip->id.name, clip); movieclip_selection_sync(clip, (MovieClip *)clip->id.orig_id); } + +/* -------------------------------------------------------------------- */ +/** \name GPU textures + * \{ */ + +static GPUTexture **movieclip_get_gputexture_ptr(MovieClip *clip, + MovieClipUser *cuser, + eGPUTextureTarget textarget) +{ + LISTBASE_FOREACH (MovieClip_RuntimeGPUTexture *, tex, &clip->runtime.gputextures) { + if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) { + if (tex == NULL) { + tex = (MovieClip_RuntimeGPUTexture *)MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), + __func__); + + for (int i = 0; i < TEXTARGET_COUNT; i++) { + tex->gputexture[i] = NULL; + } + + memcpy(&tex->user, cuser, sizeof(MovieClipUser)); + BLI_addtail(&clip->runtime.gputextures, tex); + } + + return &tex->gputexture[textarget]; + } + } + return NULL; +} + +GPUTexture *BKE_movieclip_get_gpu_texture(MovieClip *clip, MovieClipUser *cuser) +{ + if (clip == NULL) { + return NULL; + } + + GPUTexture **tex = movieclip_get_gputexture_ptr(clip, cuser, TEXTARGET_2D); + if (*tex) { + return *tex; + } + + /* check if we have a valid image buffer */ + ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser); + if (ibuf == NULL) { + *tex = GPU_texture_create_error(2, false); + return *tex; + } + + /* This only means RGBA16F instead of RGBA32F. */ + const bool high_bitdepth = false; + const bool store_premultiplied = ibuf->rect_float ? false : true; + *tex = IMB_create_gpu_texture(ibuf, high_bitdepth, store_premultiplied); + + /* Do not generate mips for movieclips... too slow. */ + GPU_texture_mipmap_mode(*tex, false, true); + + IMB_freeImBuf(ibuf); + + return *tex; +} + +void BKE_movieclip_free_gputexture(struct MovieClip *clip) +{ + /* number of gpu textures to keep around as cache + * We don't want to keep too many GPU textures for + * movie clips around, as they can be large.*/ + const int MOVIECLIP_NUM_GPUTEXTURES = 1; + + while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) { + MovieClip_RuntimeGPUTexture *tex = (MovieClip_RuntimeGPUTexture *)BLI_pophead( + &clip->runtime.gputextures); + for (int i = 0; i < TEXTARGET_COUNT; i++) { + /* free glsl image binding */ + if (tex->gputexture[i]) { + GPU_texture_free(tex->gputexture[i]); + tex->gputexture[i] = NULL; + } + } + MEM_freeN(tex); + } +} +/** \} */ diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 71d49dd1c19..6c10f6de855 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -241,7 +241,7 @@ static void multires_mdisps_subdivide_hidden(MDisps *md, int new_level) md->hidden = subd; } -static MDisps *multires_mdisps_initialize_hidden(Mesh *me, int level) +static MDisps *multires_mdisps_init_hidden(Mesh *me, int level) { MDisps *mdisps = CustomData_add_layer(&me->ldata, CD_MDISPS, CD_CALLOC, NULL, me->totloop); int gridsize = BKE_ccg_gridsize(level); @@ -868,7 +868,7 @@ static void multires_subdivide_legacy( mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS); if (!mdisps) { - mdisps = multires_mdisps_initialize_hidden(me, totlvl); + mdisps = multires_mdisps_init_hidden(me, totlvl); } if (mdisps->disps && !updateblock && lvl != 0) { diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 20d65e52b09..91693abd1cf 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -61,6 +61,7 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_simulation.h" #include "BLI_ghash.h" #include "BLI_threads.h" @@ -845,12 +846,12 @@ static void socket_id_user_increment(bNodeSocket *sock) switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { bNodeSocketValueObject *default_value = sock->default_value; - id_us_plus(&default_value->value->id); + id_us_plus((ID *)default_value->value); break; } case SOCK_IMAGE: { bNodeSocketValueImage *default_value = sock->default_value; - id_us_plus(&default_value->value->id); + id_us_plus((ID *)default_value->value); break; } case SOCK_FLOAT: @@ -2493,6 +2494,7 @@ ID *BKE_node_tree_find_owner_ID(Main *bmain, struct bNodeTree *ntree) &bmain->textures, &bmain->scenes, &bmain->linestyles, + &bmain->simulations, NULL}; for (int i = 0; lists[i] != NULL; i++) { @@ -3637,6 +3639,16 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup) FOREACH_NODETREE_END; } +static void ntreeUpdateSimulationDependencies(Main *main, bNodeTree *simulation_ntree) +{ + FOREACH_NODETREE_BEGIN (main, ntree, owner_id) { + if (GS(owner_id->name) == ID_SIM && ntree == simulation_ntree) { + BKE_simulation_update_dependencies((Simulation *)owner_id, main); + } + } + FOREACH_NODETREE_END; +} + void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) { bNode *node; @@ -3679,7 +3691,6 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntreeInterfaceTypeUpdate(ntree); } - /* XXX hack, should be done by depsgraph!! */ if (bmain) { ntreeUpdateAllUsers(bmain, &ntree->id); } @@ -3695,6 +3706,11 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntree_validate_links(ntree); } + if (bmain != NULL && ntree->typeinfo == ntreeType_Simulation && + (ntree->id.flag & LIB_EMBEDDED_DATA)) { + ntreeUpdateSimulationDependencies(bmain, ntree); + } + /* clear update flags */ for (node = ntree->nodes.first; node; node = node->next) { node->update = 0; @@ -4336,6 +4352,8 @@ static void registerSimulationNodes(void) register_node_type_sim_emit_particles(); register_node_type_sim_time(); register_node_type_sim_particle_attribute(); + register_node_type_sim_age_reached_event(); + register_node_type_sim_kill_particle(); } static void registerFunctionNodes(void) @@ -4346,6 +4364,7 @@ static void registerFunctionNodes(void) register_node_type_fn_group_instance_id(); register_node_type_fn_combine_strings(); register_node_type_fn_object_transforms(); + register_node_type_fn_random_float(); } void init_nodesystem(void) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index ecb2256d080..fe559d2a44e 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2409,7 +2409,7 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4]) /* ctime is now a proper var setting of Curve which gets set by Animato like any other var * that's animated, but this will only work if it actually is animated. * - * We divide the curvetime calculated in the previous step by the length of the path, + * We divide the curve-time calculated in the previous step by the length of the path, * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. */ if (cu->pathlen) { @@ -3970,15 +3970,20 @@ int BKE_object_is_modified(Scene *scene, Object *ob) return flag; } -/* Check of objects moves in time. */ -/* NOTE: This function is currently optimized for usage in combination - * with mti->canDeform, so modifiers can quickly check if their target - * objects moves (causing deformation motion blur) or not. +/** + * Check of objects moves in time. + * + * \note This function is currently optimized for usage in combination + * with modifier deformation checks (#eModifierTypeType_OnlyDeform), + * so modifiers can quickly check if their target objects moves + * (causing deformation motion blur) or not. * * This makes it possible to give some degree of false-positives here, * but it's currently an acceptable tradeoff between complexity and check * speed. In combination with checks of modifier stack and real life usage - * percentage of false-positives shouldn't be that height. + * percentage of false-positives shouldn't be that high. + * + * \note This function does not consider physics systems. */ bool BKE_object_moves_in_time(const Object *object, bool recurse_parent) { diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index f498e147110..e0aea3a2910 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -37,6 +37,7 @@ #include "DNA_collection_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_vfont_types.h" @@ -367,17 +368,13 @@ static void vertex_dupli(const VertexDupliData *vdd, DupliObject *dob; float obmat[4][4], space_mat[4][4]; - /* obmat is transform to vertex */ - get_duplivert_transform(co, no, vdd->use_rotation, inst_ob->trackflag, inst_ob->upflag, obmat); + /* space_mat is transform to vertex */ + get_duplivert_transform( + co, no, vdd->use_rotation, inst_ob->trackflag, inst_ob->upflag, space_mat); /* make offset relative to inst_ob using relative child transform */ - mul_mat3_m4_v3((float(*)[4])vdd->child_imat, obmat[3]); + mul_mat3_m4_v3((float(*)[4])vdd->child_imat, space_mat[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 - */ - mul_m4_m4m4(space_mat, obmat, inst_ob->imat); + mul_m4_m4m4(obmat, inst_ob->obmat, space_mat); dob = make_dupli(vdd->ctx, vdd->inst_ob, obmat, index); @@ -523,7 +520,7 @@ static void make_duplis_font(const DupliContext *ctx) /* Safety check even if it might fail badly when called for original object. */ const bool is_eval_curve = DEG_is_evaluated_id(&cu->id); - /* advance matching BLI_strncpy_wchar_from_utf8 */ + /* 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... @@ -573,6 +570,63 @@ static const DupliGenerator gen_dupli_verts_font = { make_duplis_font /* make_duplis */ }; +/* OB_DUPLIVERTS - PointCloud */ +static void make_child_duplis_pointcloud(const DupliContext *ctx, + void *UNUSED(userdata), + Object *child) +{ + const Object *parent = ctx->object; + const PointCloud *pointcloud = parent->data; + const float(*co)[3] = pointcloud->co; + const float *radius = pointcloud->radius; + const float(*rotation)[4] = NULL; /* TODO: add optional rotation attribute. */ + const float(*orco)[3] = NULL; /* TODO: add optional texture coordinate attribute. */ + + /* Relative transform from parent to child space. */ + float child_imat[4][4]; + mul_m4_m4m4(child_imat, child->imat, parent->obmat); + + for (int i = 0; i < pointcloud->totpoint; i++) { + /* Transform matrix from point position, radius and rotation. */ + float quat[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + float size[3] = {1.0f, 1.0f, 1.0f}; + if (radius) { + copy_v3_fl(size, radius[i]); + } + if (rotation) { + copy_v4_v4(quat, rotation[i]); + } + + float space_mat[4][4]; + loc_quat_size_to_mat4(space_mat, co[i], quat, size); + + /* Make offset relative to child object using relative child transform, + * and apply object matrix after local vertex transform. */ + mul_mat3_m4_v3(child_imat, space_mat[3]); + + /* Create dupli object. */ + float obmat[4][4]; + mul_m4_m4m4(obmat, child->obmat, space_mat); + DupliObject *dob = make_dupli(ctx, child, obmat, i); + if (orco) { + copy_v3_v3(dob->orco, orco[i]); + } + + /* Recursion. */ + make_recursive_duplis(ctx, child, space_mat, i); + } +} + +static void make_duplis_pointcloud(const DupliContext *ctx) +{ + make_child_duplis(ctx, NULL, make_child_duplis_pointcloud); +} + +static const DupliGenerator gen_dupli_verts_pointcloud = { + OB_DUPLIVERTS, /* type */ + make_duplis_pointcloud /* make_duplis */ +}; + /* OB_DUPLIFACES */ typedef struct FaceDupliData { Mesh *me_eval; @@ -1105,6 +1159,9 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) else if (ctx->object->type == OB_FONT) { return &gen_dupli_verts_font; } + else if (ctx->object->type == OB_POINTCLOUD) { + return &gen_dupli_verts_pointcloud; + } } else if (transflag & OB_DUPLIFACES) { if (ctx->object->type == OB_MESH) { diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index f94ef946851..198ff5a0540 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -753,18 +753,26 @@ struct Ocean *BKE_ocean_add(void) return oc; } -bool BKE_ocean_ensure(struct OceanModifierData *omd) +bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution) { if (omd->ocean) { - return false; + /* Check that the ocean has the same resolution than we want now. */ + if (omd->ocean->_M == resolution * resolution) { + return false; + } + else { + BKE_ocean_free(omd->ocean); + } } omd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(omd->ocean, omd); + BKE_ocean_init_from_modifier(omd->ocean, omd, resolution); return true; } -void BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData const *omd) +void BKE_ocean_init_from_modifier(struct Ocean *ocean, + struct OceanModifierData const *omd, + const int resolution) { short do_heightfield, do_chop, do_normals, do_jacobian; @@ -774,9 +782,10 @@ void BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData do_jacobian = (omd->flag & MOD_OCEAN_GENERATE_FOAM); BKE_ocean_free_data(ocean); + BKE_ocean_init(ocean, - omd->resolution * omd->resolution, - omd->resolution * omd->resolution, + resolution * resolution, + resolution * resolution, omd->spatial_size, omd->spatial_size, omd->wind_velocity, @@ -1607,7 +1616,8 @@ void BKE_ocean_bake(struct Ocean *UNUSED(o), } void BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean), - struct OceanModifierData const *UNUSED(omd)) + struct OceanModifierData const *UNUSED(omd), + int UNUSED(resolution)) { } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 0ba5ec43318..15d5b0cbf53 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1691,7 +1691,6 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object) CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert); BKE_mesh_update_customdata_pointers(orig_me, true); DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY); - return; } void BKE_sculpt_update_object_for_edit( diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index a003daf1042..1df5cda0ce5 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -3871,7 +3871,7 @@ void BKE_particlesettings_clump_curve_init(ParticleSettings *part) cumap->cm[0].curve[1].x = 1.0f; cumap->cm[0].curve[1].y = 1.0f; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); part->clumpcurve = cumap; } @@ -3885,7 +3885,7 @@ void BKE_particlesettings_rough_curve_init(ParticleSettings *part) cumap->cm[0].curve[1].x = 1.0f; cumap->cm[0].curve[1].y = 1.0f; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); part->roughcurve = cumap; } @@ -3899,7 +3899,7 @@ void BKE_particlesettings_twist_curve_init(ParticleSettings *part) cumap->cm[0].curve[1].x = 1.0f; cumap->cm[0].curve[1].y = 1.0f; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); part->twistcurve = cumap; } diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index bec9cbbad79..6bfbb4b9d00 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -570,7 +570,7 @@ void psys_thread_context_free(ParticleThreadContext *ctx) } } -static void initialize_particle_texture(ParticleSimulationData *sim, ParticleData *pa, int p) +static void init_particle_texture(ParticleSimulationData *sim, ParticleData *pa, int p) { ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; @@ -595,7 +595,7 @@ static void initialize_particle_texture(ParticleSimulationData *sim, ParticleDat } /* set particle parameters that don't change during particle's life */ -void initialize_particle(ParticleSimulationData *sim, ParticleData *pa) +void init_particle(ParticleSimulationData *sim, ParticleData *pa) { ParticleSettings *part = sim->psys->part; float birth_time = (float)(pa - sim->psys->particles) / (float)sim->psys->totpart; @@ -629,7 +629,7 @@ static void initialize_all_particles(ParticleSimulationData *sim) LOOP_PARTICLES { if (!(emit_from_volume_grid && (pa->flag & PARS_UNEXIST) != 0)) { - initialize_particle(sim, pa); + init_particle(sim, pa); } } } @@ -1092,7 +1092,7 @@ void reset_particle(ParticleSimulationData *sim, ParticleData *pa, float dtime, * We could only do it now because we'll need to know coordinate * before sampling the texture. */ - initialize_particle_texture(sim, pa, p); + init_particle_texture(sim, pa, p); if (part->phystype == PART_PHYS_BOIDS && pa->boid) { BoidParticle *bpa = pa->boid; @@ -4584,7 +4584,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_ psys->dt_frac = get_base_time_step(part); } else if ((int)cfra == startframe) { - /* Variable time step; initialise to subframes */ + /* Variable time step; initialize to sub-frames. */ psys->dt_frac = get_base_time_step(part); } else if (psys->dt_frac < MIN_TIMESTEP) { diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 4b09f7542c7..c2c5b42dbb0 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1511,7 +1511,7 @@ static int ptcache_rigidbody_write(int index, void *rb_v, void **data, int UNUSE if (ob && ob->rigidbody_object) { RigidBodyOb *rbo = ob->rigidbody_object; - if (rbo->type == RBO_TYPE_ACTIVE) { + if (rbo->type == RBO_TYPE_ACTIVE && rbo->shared->physics_object != NULL) { #ifdef WITH_BULLET RB_body_get_position(rbo->shared->physics_object, rbo->pos); RB_body_get_orientation(rbo->shared->physics_object, rbo->orn); diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 4752782eaeb..7c335a8e98c 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -466,10 +466,10 @@ static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob) return shape; } -/* Create new physics sim collision shape for object and store it, - * or remove the existing one first and replace... +/* Helper function to create physics collision shape for object. + * Returns a new collision shape. */ -static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) +static rbCollisionShape *rigidbody_validate_sim_shape_helper(RigidBodyWorld *rbw, Object *ob) { RigidBodyOb *rbo = ob->rigidbody_object; rbCollisionShape *new_shape = NULL; @@ -484,12 +484,7 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) /* sanity check */ if (rbo == NULL) { - return; - } - - /* don't create a new shape if we already have one and don't want to rebuild it */ - if (rbo->shared->physics_shape && !rebuild) { - return; + return NULL; } /* if automatically determining dimensions, use the Object's boundbox @@ -539,7 +534,7 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) break; case RB_SHAPE_CONVEXH: - /* try to emged collision margin */ + /* try to embed collision margin */ has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f); if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && has_volume) { @@ -555,18 +550,69 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) case RB_SHAPE_TRIMESH: new_shape = rigidbody_get_shape_trimesh_from_mesh(ob); break; + case RB_SHAPE_COMPOUND: + new_shape = RB_shape_new_compound(); + rbCollisionShape *childShape = NULL; + float loc[3], rot[4]; + float mat[4][4]; + /* Add children to the compound shape */ + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, childObject) { + if (childObject->parent == ob) { + childShape = rigidbody_validate_sim_shape_helper(rbw, childObject); + if (childShape) { + BKE_object_matrix_local_get(childObject, mat); + mat4_to_loc_quat(loc, rot, mat); + RB_compound_add_child_shape(new_shape, childShape, loc, rot); + } + } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + + break; } - /* use box shape if we can't fall back to old shape */ - if (new_shape == NULL && rbo->shared->physics_shape == NULL) { + /* use box shape if it failed to create new shape */ + if (new_shape == NULL) { new_shape = RB_shape_new_box(size[0], size[1], size[2]); } + if (new_shape) { + RB_shape_set_margin(new_shape, RBO_GET_MARGIN(rbo)); + } + + return new_shape; +} + +/* Create new physics sim collision shape for object and store it, + * or remove the existing one first and replace... + */ +static void rigidbody_validate_sim_shape(RigidBodyWorld *rbw, Object *ob, bool rebuild) +{ + RigidBodyOb *rbo = ob->rigidbody_object; + rbCollisionShape *new_shape = NULL; + + /* sanity check */ + if (rbo == NULL) { + return; + } + + /* don't create a new shape if we already have one and don't want to rebuild it */ + if (rbo->shared->physics_shape && !rebuild) { + return; + } + + /* Also don't create a shape if this object is parent of a compound shape */ + if (ob->parent != NULL && ob->parent->rigidbody_object != NULL && + ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) { + return; + } + + new_shape = rigidbody_validate_sim_shape_helper(rbw, ob); + /* assign new collision shape if creation was successful */ if (new_shape) { if (rbo->shared->physics_shape) { RB_shape_delete(rbo->shared->physics_shape); } rbo->shared->physics_shape = new_shape; - RB_shape_set_margin(rbo->shared->physics_shape, RBO_GET_MARGIN(rbo)); } } @@ -750,7 +796,7 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool /* FIXME we shouldn't always have to rebuild collision shapes when rebuilding objects, * but it's needed for constraints to update correctly. */ if (rbo->shared->physics_shape == NULL || rebuild) { - rigidbody_validate_sim_shape(ob, true); + rigidbody_validate_sim_shape(rbw, ob, true); } if (rbo->shared->physics_object) { @@ -760,6 +806,12 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool /* remove rigid body if it already exists before creating a new one */ if (rbo->shared->physics_object) { RB_body_delete(rbo->shared->physics_object); + rbo->shared->physics_object = NULL; + } + /* Don't create rigid body object if the parent is a compound shape */ + if (ob->parent != NULL && ob->parent->rigidbody_object != NULL && + ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) { + return; } mat4_to_loc_quat(loc, rot, ob->obmat); @@ -793,7 +845,7 @@ static void rigidbody_validate_sim_object(RigidBodyWorld *rbw, Object *ob, bool rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED); } - if (rbw && rbw->shared->physics_world) { + if (rbw && rbw->shared->physics_world && rbo->shared->physics_object) { RB_dworld_add_body(rbw->shared->physics_world, rbo->shared->physics_object, rbo->col_groups); } } @@ -1179,9 +1231,12 @@ RigidBodyOb *BKE_rigidbody_create_object(Scene *scene, Object *ob, short type) * - object must exist * - cannot add rigid body if it already exists */ - if (ob == NULL || (ob->rigidbody_object != NULL)) { + if (ob == NULL) { return NULL; } + if (ob->rigidbody_object != NULL) { + return ob->rigidbody_object; + } /* create new settings data, and link it up */ rbo = MEM_callocN(sizeof(RigidBodyOb), "RigidBodyOb"); @@ -1530,7 +1585,11 @@ static void rigidbody_update_ob_array(RigidBodyWorld *rbw) int n = 0; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { (void)object; - n++; + /* Ignore if this object is the direct child of an object with a compound shape */ + if (object->parent == NULL || object->parent->rigidbody_object == NULL || + object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) { + n++; + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; @@ -1541,8 +1600,12 @@ static void rigidbody_update_ob_array(RigidBodyWorld *rbw) int i = 0; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { - rbw->objects[i] = object; - i++; + /* Ignore if this object is the direct child of an object with a compound shape */ + if (object->parent == NULL || object->parent->rigidbody_object == NULL || + object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) { + rbw->objects[i] = object; + i++; + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } @@ -1754,11 +1817,13 @@ static void rigidbody_update_simulation(Depsgraph *depsgraph, /* refresh shape... */ if (rbo->flag & RBO_FLAG_NEEDS_RESHAPE) { /* mesh/shape data changed, so force shape refresh */ - rigidbody_validate_sim_shape(ob, true); + rigidbody_validate_sim_shape(rbw, ob, true); /* now tell RB sim about it */ /* XXX: we assume that this can only get applied for active/passive shapes * that will be included as rigidbodies. */ - RB_body_set_collision_shape(rbo->shared->physics_object, rbo->shared->physics_shape); + if (rbo->shared->physics_object != NULL && rbo->shared->physics_shape != NULL) { + RB_body_set_collision_shape(rbo->shared->physics_object, rbo->shared->physics_shape); + } } } rbo->flag &= ~(RBO_FLAG_NEEDS_VALIDATE | RBO_FLAG_NEEDS_RESHAPE); @@ -1817,7 +1882,8 @@ static void rigidbody_update_simulation_post_step(Depsgraph *depsgraph, RigidBod Base *base = BKE_view_layer_base_find(view_layer, ob); RigidBodyOb *rbo = ob->rigidbody_object; /* Reset kinematic state for transformed objects. */ - if (rbo && base && (base->flag & BASE_SELECTED) && (G.moving & G_TRANSFORM_OBJ)) { + if (rbo && base && (base->flag & BASE_SELECTED) && (G.moving & G_TRANSFORM_OBJ) && + rbo->shared->physics_object) { RB_body_set_kinematic_state(rbo->shared->physics_object, rbo->flag & RBO_FLAG_KINEMATIC || rbo->flag & RBO_FLAG_DISABLED); RB_body_set_mass(rbo->shared->physics_object, RBO_GET_MASS(rbo)); @@ -1840,8 +1906,13 @@ 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) { + if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE || + obCompoundParent) { return; } @@ -1963,7 +2034,11 @@ void BKE_rigidbody_rebuild_world(Depsgraph *depsgraph, Scene *scene, float ctime int n = 0; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { (void)object; - n++; + /* Ignore if this object is the direct child of an object with a compound shape */ + if (object->parent == NULL || object->parent->rigidbody_object == NULL || + object->parent->rigidbody_object->shape != RB_SHAPE_COMPOUND) { + n++; + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 5ae2f4b9005..fdec29dd43e 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -127,7 +127,7 @@ static void scene_init_data(ID *id) mblur_shutter_curve = &scene->r.mblur_shutter_curve; BKE_curvemapping_set_defaults(mblur_shutter_curve, 1, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(mblur_shutter_curve); + BKE_curvemapping_init(mblur_shutter_curve); BKE_curvemap_reset(mblur_shutter_curve->cm, &mblur_shutter_curve->clipr, CURVE_PRESET_MAX, @@ -140,13 +140,13 @@ static void scene_init_data(ID *id) /* grease pencil multiframe falloff curve */ scene->toolsettings->gp_sculpt.cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_falloff_curve = scene->toolsettings->gp_sculpt.cur_falloff; - BKE_curvemapping_initialize(gp_falloff_curve); + BKE_curvemapping_init(gp_falloff_curve); BKE_curvemap_reset( gp_falloff_curve->cm, &gp_falloff_curve->clipr, CURVE_PRESET_GAUSS, CURVEMAP_SLOPE_POSITIVE); scene->toolsettings->gp_sculpt.cur_primitive = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_primitive_curve = scene->toolsettings->gp_sculpt.cur_primitive; - BKE_curvemapping_initialize(gp_primitive_curve); + BKE_curvemapping_init(gp_primitive_curve); BKE_curvemap_reset(gp_primitive_curve->cm, &gp_primitive_curve->clipr, CURVE_PRESET_BELL, diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c index a630170d6d5..0bf7fffb833 100644 --- a/source/blender/blenkernel/intern/seqmodifier.c +++ b/source/blender/blenkernel/intern/seqmodifier.c @@ -396,7 +396,7 @@ static void curves_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *m float black[3] = {0.0f, 0.0f, 0.0f}; float white[3] = {1.0f, 1.0f, 1.0f}; - BKE_curvemapping_initialize(&cmd->curve_mapping); + BKE_curvemapping_init(&cmd->curve_mapping); BKE_curvemapping_premultiply(&cmd->curve_mapping, 0); BKE_curvemapping_set_black_white(&cmd->curve_mapping, black, white); @@ -525,7 +525,7 @@ static void hue_correct_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImB { HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd; - BKE_curvemapping_initialize(&hcmd->curve_mapping); + BKE_curvemapping_init(&hcmd->curve_mapping); modifier_apply_threaded(ibuf, mask, hue_correct_apply_threaded, &hcmd->curve_mapping); } diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 5481cfe8193..b0a8f709399 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -6082,6 +6082,64 @@ bool BKE_sequencer_render_loop_check(Sequence *seq_main, Sequence *seq) return false; } +static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq) +{ + LISTBASE_FOREACH (Sequence *, user_seq, seqbase) { + /* Look in metas for usage of seq. */ + if (user_seq->type == SEQ_TYPE_META) { + sequencer_flag_users_for_removal(scene, &user_seq->seqbase, seq); + } + + /* Clear seq from modifiers. */ + SequenceModifierData *smd; + for (smd = user_seq->modifiers.first; smd; smd = smd->next) { + if (smd->mask_sequence == seq) { + smd->mask_sequence = NULL; + } + } + + /* Remove effects, that use seq. */ + if ((user_seq->seq1 && user_seq->seq1 == seq) || (user_seq->seq2 && user_seq->seq2 == seq) || + (user_seq->seq3 && user_seq->seq3 == seq)) { + user_seq->flag |= SEQ_FLAG_DELETE; + /* Strips can be used as mask even if not in same seqbase. */ + sequencer_flag_users_for_removal(scene, &scene->ed->seqbase, user_seq); + } + } +} + +/* Flag seq and its users (effects) for removal. */ +void BKE_sequencer_flag_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq) +{ + if (seq == NULL || (seq->flag & SEQ_FLAG_DELETE) != 0) { + return; + } + + /* Flag and remove meta children. */ + if (seq->type == SEQ_TYPE_META) { + LISTBASE_FOREACH (Sequence *, meta_child, &seq->seqbase) { + BKE_sequencer_flag_for_removal(scene, &seq->seqbase, meta_child); + } + } + + seq->flag |= SEQ_FLAG_DELETE; + sequencer_flag_users_for_removal(scene, seqbase, seq); +} + +/* Remove all flagged sequences, return true if sequence is removed. */ +void BKE_sequencer_remove_flagged_sequences(Scene *scene, ListBase *seqbase) +{ + LISTBASE_FOREACH_MUTABLE (Sequence *, seq, seqbase) { + if (seq->flag & SEQ_FLAG_DELETE) { + if (seq->type == SEQ_TYPE_META) { + BKE_sequencer_remove_flagged_sequences(scene, &seq->seqbase); + } + BLI_remlink(seqbase, seq); + BKE_sequence_free(scene, seq, true); + } + } +} + void BKE_sequencer_check_uuids_unique_and_report(const Scene *scene) { if (scene->ed == NULL) { diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 95340e4e29c..c0fc8fcb464 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -112,8 +112,8 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons BKE_simulation_state_copy_data(state_src, state_dst); } - BLI_duplicatelist(&simulation_dst->persistent_data_handles, - &simulation_src->persistent_data_handles); + BLI_listbase_clear(&simulation_dst->dependencies); + BLI_duplicatelist(&simulation_dst->dependencies, &simulation_src->dependencies); } static void simulation_free_data(ID *id) @@ -130,7 +130,7 @@ static void simulation_free_data(ID *id) BKE_simulation_state_remove_all(simulation); - BLI_freelistN(&simulation->persistent_data_handles); + BLI_freelistN(&simulation->dependencies); } static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) @@ -140,9 +140,8 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree); } - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BKE_LIB_FOREACHID_PROCESS_ID(data, handle_item->id, IDWALK_CB_USER); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BKE_LIB_FOREACHID_PROCESS_ID(data, dependency->id, IDWALK_CB_USER); } } @@ -284,6 +283,14 @@ void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation * blender::sim::update_simulation_in_depsgraph(depsgraph, scene, simulation); } +void BKE_simulation_update_dependencies(Simulation *simulation, Main *bmain) +{ + bool dependencies_changed = blender::sim::update_simulation_dependencies(simulation); + if (dependencies_changed) { + DEG_relations_tag_update(bmain); + } +} + using StateTypeMap = blender::Map<std::string, std::unique_ptr<SimulationStateType>>; template<typename T> diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index bc1b79f62c5..c992990e0a0 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -1618,7 +1618,7 @@ static int prev_adjacent_edge_point_index(const SubdivCCG *subdiv_ccg, const int return point_index - 1; } -/* When the point index corresponds to a grid corner, returs the point index which corresponds to +/* When the point index corresponds to a grid corner, returns the point index which corresponds to * the corner of the adjacent grid, as the adjacent edge has two separate points for each grid * corner at the middle of the edge. */ static int adjacent_grid_corner_point_index_on_edge(const SubdivCCG *subdiv_ccg, @@ -1650,7 +1650,7 @@ static void neighbor_coords_edge_get(const SubdivCCG *subdiv_ccg, if (include_duplicates) { num_duplicates += num_adjacent_faces - 1; if (is_corner) { - /* When the coord is a grid corner, add an extra duplicate per adajacent grid in all adjacent + /* When the coord is a grid corner, add an extra duplicate per adjacent grid in all adjacent * faces to the edge. */ num_duplicates += num_adjacent_faces; } diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c index e09e92588c6..b9c6bb83157 100644 --- a/source/blender/blenkernel/intern/tracking_stabilize.c +++ b/source/blender/blenkernel/intern/tracking_stabilize.c @@ -215,7 +215,7 @@ static void use_values_from_fcurves(StabContext *ctx, bool toggle) /* Prepare per call private working area. * Used for access to possibly animated values: retrieve available F-curves. */ -static StabContext *initialize_stabilization_working_context(MovieClip *clip) +static StabContext *init_stabilization_working_context(MovieClip *clip) { StabContext *ctx = MEM_callocN(sizeof(StabContext), "2D stabilization animation runtime data"); ctx->clip = clip; @@ -841,14 +841,14 @@ static int establish_track_initialization_order(StabContext *ctx, TrackInitOrder * * NOTE: when done, this track is marked as initialized */ -static void initialize_track_for_stabilization(StabContext *ctx, - MovieTrackingTrack *track, - int reference_frame, - float aspect, - const float average_translation[2], - const float pivot[2], - const float average_angle, - const float average_scale_step) +static void init_track_for_stabilization(StabContext *ctx, + MovieTrackingTrack *track, + int reference_frame, + float aspect, + const float average_translation[2], + const float pivot[2], + const float average_angle, + const float average_scale_step) { float pos[2], angle, len; TrackStabilizationBase *local_data = access_stabilization_baseline_data(ctx, track); @@ -876,7 +876,7 @@ static void initialize_track_for_stabilization(StabContext *ctx, local_data->is_init_for_stabilization = true; } -static void initialize_all_tracks(StabContext *ctx, float aspect) +static void init_all_tracks(StabContext *ctx, float aspect) { size_t track_len = 0; MovieClip *clip = ctx->clip; @@ -936,14 +936,14 @@ static void initialize_all_tracks(StabContext *ctx, float aspect) &average_angle, &average_scale_step); } - initialize_track_for_stabilization(ctx, - track, - reference_frame, - aspect, - average_translation, - pivot, - average_angle, - average_scale_step); + init_track_for_stabilization(ctx, + track, + reference_frame, + aspect, + average_translation, + pivot, + average_angle, + average_scale_step); } cleanup: @@ -1257,9 +1257,9 @@ static float calculate_autoscale_factor(StabContext *ctx, int size, float aspect */ static StabContext *init_stabilizer(MovieClip *clip, int size, float aspect) { - StabContext *ctx = initialize_stabilization_working_context(clip); + StabContext *ctx = init_stabilization_working_context(clip); BLI_assert(ctx != NULL); - initialize_all_tracks(ctx, aspect); + init_all_tracks(ctx, aspect); if (ctx->stab->flag & TRACKING_AUTOSCALE) { ctx->stab->scale = 1.0; ctx->stab->scale = calculate_autoscale_factor(ctx, size, aspect); diff --git a/source/blender/blenlib/BLI_dial_2d.h b/source/blender/blenlib/BLI_dial_2d.h index a39543720e6..83467881eb5 100644 --- a/source/blender/blenlib/BLI_dial_2d.h +++ b/source/blender/blenlib/BLI_dial_2d.h @@ -38,7 +38,7 @@ * float angle; * Dial *dial; * - * dial = BLI_dial_initialize(start_position, threshold); + * dial = BLI_dial_init(start_position, threshold); * * angle = BLI_dial_angle(dial, current_position); * @@ -52,7 +52,7 @@ extern "C" { typedef struct Dial Dial; -Dial *BLI_dial_initialize(const float start_position[2], float threshold); +Dial *BLI_dial_init(const float start_position[2], float threshold); float BLI_dial_angle(Dial *dial, const float current_position[2]); diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index b2633985ac7..6acf4716525 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -63,11 +63,12 @@ struct float3 { return {a.x + b.x, a.y + b.y, a.z + b.z}; } - void operator+=(const float3 &b) + float3 &operator+=(const float3 &b) { this->x += b.x; this->y += b.y; this->z += b.z; + return *this; } friend float3 operator-(const float3 &a, const float3 &b) @@ -80,25 +81,28 @@ struct float3 { return {-a.x, -a.y, -a.z}; } - void operator-=(const float3 &b) + float3 &operator-=(const float3 &b) { this->x -= b.x; this->y -= b.y; this->z -= b.z; + return *this; } - void operator*=(float scalar) + float3 &operator*=(float scalar) { this->x *= scalar; this->y *= scalar; this->z *= scalar; + return *this; } - void operator*=(const float3 &other) + float3 &operator*=(const float3 &other) { this->x *= other.x; this->y *= other.y; this->z *= other.z; + return *this; } friend float3 operator*(const float3 &a, const float3 &b) @@ -143,6 +147,17 @@ struct float3 { return normalize_v3(*this); } + /** + * Normalizes the vector in place. + */ + void normalize() + { + normalize_v3(*this); + } + + /** + * Returns a normalized vector. The original vector is not changed. + */ float3 normalized() const { float3 result; diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 185cffd13ac..b4f12f17cc2 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -71,8 +71,8 @@ struct float4x4 { float4x4 inverted() const { - float result[4][4]; - invert_m4_m4(result, values); + float4x4 result; + invert_m4_m4(result.values, values); return result; } @@ -86,6 +86,18 @@ struct float4x4 { return this->inverted(); } + float4x4 transposed() const + { + float4x4 result; + transpose_m4_m4(result.values, values); + return result; + } + + float4x4 inverted_transposed_affine() const + { + return this->inverted_affine().transposed(); + } + struct float3x3_ref { const float4x4 &data; diff --git a/source/blender/blenlib/BLI_multi_value_map.hh b/source/blender/blenlib/BLI_multi_value_map.hh new file mode 100644 index 00000000000..c20c4ef9677 --- /dev/null +++ b/source/blender/blenlib/BLI_multi_value_map.hh @@ -0,0 +1,134 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BLI_MULTI_VALUE_MAP_HH__ +#define __BLI_MULTI_VALUE_MAP_HH__ + +/** \file + * \ingroup bli + * + * A `blender::MultiValueMap<Key, Value>` is an unordered associative container that stores + * key-value pairs. It is different from `blender::Map` in that it can store multiple values for + * the same key. The list of values that corresponds to a specific key can contain duplicates + * and their order is maintained. + * + * This data structure is different from a `std::multi_map`, because multi_map can store the same + * key more than once and MultiValueMap can't. + * + * Currently, this class exists mainly for convenience. There are no performance benefits over + * using Map<Key, Vector<Value>>. In the future, a better implementation for this data structure + * can be developed. + */ + +#include "BLI_map.hh" +#include "BLI_vector.hh" + +namespace blender { + +template<typename Key, typename Value> class MultiValueMap { + private: + using MapType = Map<Key, Vector<Value>>; + MapType map_; + + public: + /** + * Add a new value for the given key. If the map contains the key already, the value will be + * appended to the list of corresponding values. + */ + void add(const Key &key, const Value &value) + { + this->add_as(key, value); + } + void add(const Key &key, Value &&value) + { + this->add_as(key, std::move(value)); + } + void add(Key &&key, const Value &value) + { + this->add_as(std::move(key), value); + } + void add(Key &&key, Value &&value) + { + this->add_as(std::move(key), std::move(value)); + } + template<typename ForwardKey, typename ForwardValue> + void add_as(ForwardKey &&key, ForwardValue &&value) + { + Vector<Value> &vector = map_.lookup_or_add_default_as(std::forward<ForwardKey>(key)); + vector.append(std::forward<ForwardValue>(value)); + } + + /** + * Add all given values to the key. + */ + void add_multiple(const Key &key, Span<Value> values) + { + this->add_multiple_as(key, values); + } + void add_multiple(Key &&key, Span<Value> values) + { + this->add_multiple_as(std::move(key), values); + } + template<typename ForwardKey> void add_multiple_as(ForwardKey &&key, Span<Value> values) + { + Vector<Value> &vector = map_.lookup_or_add_default_as(std::forward<ForwardKey>(key)); + vector.extend(values); + } + + /** + * Get a span to all the values that are stored for the given key. + */ + Span<Value> lookup(const Key &key) const + { + return this->lookup_as(key); + } + template<typename ForwardKey> Span<Value> lookup_as(const ForwardKey &key) const + { + const Vector<Value> *vector = map_.lookup_ptr_as(key); + if (vector != nullptr) { + return vector->as_span(); + } + return {}; + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::ItemIterator items() const + { + return map_.items(); + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::KeyIterator keys() const + { + return map_.keys(); + } + + /** + * Note: This signature will change when the implementation changes. + */ + typename MapType::ValueIterator values() const + { + return map_.values(); + } +}; + +} // namespace blender + +#endif /* __BLI_MULTI_VALUE_MAP_HH__ */ diff --git a/source/blender/blenlib/BLI_rand.hh b/source/blender/blenlib/BLI_rand.hh index 612ac0bbe19..7a98ee0f2bb 100644 --- a/source/blender/blenlib/BLI_rand.hh +++ b/source/blender/blenlib/BLI_rand.hh @@ -63,6 +63,15 @@ class RandomNumberGenerator { } /** + * \return Random value (0..N), but never N. + */ + int32_t get_int32(int32_t max_exclusive) + { + BLI_assert(max_exclusive > 0); + return this->get_int32() % max_exclusive; + } + + /** * \return Random value (0..1), but never 1.0. */ double get_double() @@ -78,6 +87,35 @@ class RandomNumberGenerator { return (float)this->get_int32() / 0x80000000; } + template<typename T> void shuffle(MutableSpan<T> values) + { + /* Cannot shuffle arrays of this size yet. */ + BLI_assert(values.size() <= INT32_MAX); + + for (int i = values.size() - 1; i >= 2; i--) { + int j = this->get_int32(i); + if (i != j) { + std::swap(values[i], values[j]); + } + } + } + + /** + * Compute uniformly distributed barycentric coordinates. + */ + float3 get_barycentric_coordinates() + { + float rand1 = this->get_float(); + float rand2 = this->get_float(); + + if (rand1 + rand2 > 1.0f) { + rand1 = 1.0f - rand1; + rand2 = 1.0f - rand2; + } + + return float3(rand1, rand2, 1.0f - rand1 - rand2); + } + float2 get_unit_float2(); float3 get_unit_float3(); float2 get_triangle_sample(float2 v1, float2 v2, float2 v3); diff --git a/source/blender/blenlib/BLI_resource_collector.hh b/source/blender/blenlib/BLI_resource_collector.hh index 10d610da618..e1be87d8af2 100644 --- a/source/blender/blenlib/BLI_resource_collector.hh +++ b/source/blender/blenlib/BLI_resource_collector.hh @@ -79,6 +79,12 @@ class ResourceCollector : NonCopyable, NonMovable { */ template<typename T> void add(destruct_ptr<T> resource, const char *name) { + /* There is no need to keep track of such types. */ + if (std::is_trivially_destructible_v<T>) { + resource.release(); + return; + } + BLI_assert(resource.get() != nullptr); this->add( resource.release(), diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 2d875fe73be..81b86f647f6 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -87,7 +87,7 @@ namespace blender { */ template<typename T> class Span { private: - const T *start_ = nullptr; + const T *data_ = nullptr; int64_t size_ = 0; public: @@ -96,13 +96,13 @@ template<typename T> class Span { */ Span() = default; - Span(const T *start, int64_t size) : start_(start), size_(size) + Span(const T *start, int64_t size) : data_(start), size_(size) { BLI_assert(size >= 0); } template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(const U *start, int64_t size) : start_((const T *)start), size_(size) + Span(const U *start, int64_t size) : data_((const T *)start), size_(size) { BLI_assert(size >= 0); } @@ -136,7 +136,7 @@ template<typename T> class Span { * Span<Derived *> -> Span<Base *> */ template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> - Span(Span<U> array) : start_((T *)array.data()), size_(array.size()) + Span(Span<U> array) : data_((T *)array.data()), size_(array.size()) { } @@ -149,7 +149,7 @@ template<typename T> class Span { BLI_assert(start >= 0); BLI_assert(size >= 0); BLI_assert(start + size <= this->size() || size == 0); - return Span(start_ + start, size); + return Span(data_ + start, size); } Span slice(IndexRange range) const @@ -207,17 +207,17 @@ template<typename T> class Span { */ const T *data() const { - return start_; + return data_; } const T *begin() const { - return start_; + return data_; } const T *end() const { - return start_ + size_; + return data_ + size_; } /** @@ -228,7 +228,7 @@ template<typename T> class Span { { BLI_assert(index >= 0); BLI_assert(index < size_); - return start_[index]; + return data_[index]; } /** @@ -300,7 +300,7 @@ template<typename T> class Span { const T &first() const { BLI_assert(size_ > 0); - return start_[0]; + return data_[0]; } /** @@ -310,7 +310,7 @@ template<typename T> class Span { const T &last() const { BLI_assert(size_ > 0); - return start_[size_ - 1]; + return data_[size_ - 1]; } /** @@ -320,7 +320,7 @@ template<typename T> class Span { T get(int64_t index, const T &fallback) const { if (index < size_ && index >= 0) { - return start_[index]; + return data_[index]; } return fallback; } @@ -336,9 +336,9 @@ template<typename T> class Span { BLI_assert(size_ < 1000); for (int64_t i = 0; i < size_; i++) { - const T &value = start_[i]; + const T &value = data_[i]; for (int64_t j = i + 1; j < size_; j++) { - if (value == start_[j]) { + if (value == data_[j]) { return true; } } @@ -358,7 +358,7 @@ template<typename T> class Span { BLI_assert(size_ < 1000); for (int64_t i = 0; i < size_; i++) { - const T &value = start_[i]; + const T &value = data_[i]; if (other.contains(value)) { return true; } @@ -383,7 +383,7 @@ template<typename T> class Span { int64_t first_index_try(const T &search_value) const { for (int64_t i = 0; i < size_; i++) { - if (start_[i] == search_value) { + if (data_[i] == search_value) { return i; } } @@ -406,7 +406,7 @@ template<typename T> class Span { { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); - return Span<NewT>(reinterpret_cast<const NewT *>(start_), new_size); + return Span<NewT>(reinterpret_cast<const NewT *>(data_), new_size); } /** @@ -439,13 +439,13 @@ template<typename T> class Span { */ template<typename T> class MutableSpan { private: - T *start_; + T *data_; int64_t size_; public: MutableSpan() = default; - MutableSpan(T *start, const int64_t size) : start_(start), size_(size) + MutableSpan(T *start, const int64_t size) : data_(start), size_(size) { } @@ -459,7 +459,7 @@ template<typename T> class MutableSpan { operator Span<T>() const { - return Span<T>(start_, size_); + return Span<T>(data_, size_); } /** @@ -475,7 +475,7 @@ template<typename T> class MutableSpan { */ void fill(const T &value) { - initialized_fill_n(start_, size_, value); + initialized_fill_n(data_, size_, value); } /** @@ -486,7 +486,7 @@ template<typename T> class MutableSpan { { for (int64_t i : indices) { BLI_assert(i < size_); - start_[i] = value; + data_[i] = value; } } @@ -496,23 +496,23 @@ template<typename T> class MutableSpan { */ T *data() const { - return start_; + return data_; } T *begin() const { - return start_; + return data_; } T *end() const { - return start_ + size_; + return data_ + size_; } T &operator[](const int64_t index) const { BLI_assert(index < this->size()); - return start_[index]; + return data_[index]; } /** @@ -522,7 +522,7 @@ template<typename T> class MutableSpan { MutableSpan slice(const int64_t start, const int64_t length) const { BLI_assert(start + length <= this->size()); - return MutableSpan(start_ + start, length); + return MutableSpan(data_ + start, length); } /** @@ -571,7 +571,7 @@ template<typename T> class MutableSpan { */ Span<T> as_span() const { - return Span<T>(start_, size_); + return Span<T>(data_, size_); } /** @@ -590,7 +590,7 @@ template<typename T> class MutableSpan { T &last() const { BLI_assert(size_ > 0); - return start_[size_ - 1]; + return data_[size_ - 1]; } /** @@ -609,13 +609,24 @@ template<typename T> class MutableSpan { } /** + * Copy all values from another span into this span. This invokes undefined behavior when the + * destination contains uninitialized data and T is not trivially copy constructible. + * The size of both spans is expected to be the same. + */ + void copy_from(Span<T> values) + { + BLI_assert(size_ == values.size()); + initialized_copy_n(values.data(), size_, data_); + } + + /** * Returns a new span to the same underlying memory buffer. No conversions are done. */ template<typename NewT> MutableSpan<NewT> cast() const { BLI_assert((size_ * sizeof(T)) % sizeof(NewT) == 0); int64_t new_size = size_ * sizeof(T) / sizeof(NewT); - return MutableSpan<NewT>(reinterpret_cast<NewT *>(start_), new_size); + return MutableSpan<NewT>(reinterpret_cast<NewT *>(data_), new_size); } }; diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index bc35e969e6a..2699f2498ac 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -755,6 +755,43 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); /** \} */ /* -------------------------------------------------------------------- */ +/** \name C++ Macros + * \{ */ + +#ifdef __cplusplus + +/* Useful to port C code using enums to C++ where enums are strongly typed. + * To use after the enum declaration. */ +# 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); \ + } \ + inline constexpr _enum_type operator&(_enum_type a, _enum_type b) \ + { \ + return a = 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)); \ + } \ + inline _enum_type &operator|=(_enum_type &a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) | b); \ + } \ + inline _enum_type &operator&=(_enum_type &a, _enum_type b) \ + { \ + return a = static_cast<_enum_type>(static_cast<int>(a) & b); \ + } + +#else +/* Output nothing. */ +# define ENUM_OPERATORS(_type) +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Misc Macros * \{ */ diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 1bb674093bb..7eac511bf4a 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -561,7 +561,7 @@ class Vector { /** * Return a reference to the last element in the vector. - * This will assert when the vector is empty. + * This invokes undefined behavior when the vector is empty. */ const T &last() const { diff --git a/source/blender/blenlib/BLI_vector_adaptor.hh b/source/blender/blenlib/BLI_vector_adaptor.hh new file mode 100644 index 00000000000..cadffc0b445 --- /dev/null +++ b/source/blender/blenlib/BLI_vector_adaptor.hh @@ -0,0 +1,105 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BLI_VECTOR_ADAPTOR_HH__ +#define __BLI_VECTOR_ADAPTOR_HH__ + +/** \file + * \ingroup bli + * + * A `blender::VectorAdaptor` is a container with a fixed maximum size and does not own the + * underlying memory. When an adaptor is constructed, you have to provide it with an uninitialized + * array that will be filled when elements are added to the vector. The vector adaptor is not able + * to grow. Therefore, it is undefined behavior to add more elements than fit into the provided + * buffer. + */ + +#include "BLI_span.hh" + +namespace blender { + +template<typename T> class VectorAdaptor { + private: + T *begin_; + T *end_; + T *capacity_end_; + + public: + VectorAdaptor() : begin_(nullptr), end_(nullptr), capacity_end_(nullptr) + { + } + + VectorAdaptor(T *data, int64_t capacity, int64_t size = 0) + : begin_(data), end_(data + size), capacity_end_(data + capacity) + { + } + + VectorAdaptor(MutableSpan<T> span) : VectorAdaptor(span.data(), span.size(), 0) + { + } + + void append(const T &value) + { + BLI_assert(end_ < capacity_end_); + new (end_) T(value); + end_++; + } + + void append(T &&value) + { + BLI_assert(end_ < capacity_end_); + new (end_) T(std::move(value)); + end_++; + } + + void append_n_times(const T &value, int64_t n) + { + BLI_assert(end_ + n <= capacity_end_); + uninitialized_fill_n(end_, n, value); + end_ += n; + } + + void extend(Span<T> values) + { + BLI_assert(end_ + values.size() <= capacity_end_); + uninitialized_copy_n(values.data(), values.size(), end_); + end_ += values.size(); + } + + int64_t capacity() const + { + return capacity_end_ - begin_; + } + + int64_t size() const + { + return end_ - begin_; + } + + bool is_empty() const + { + return begin_ == end_; + } + + bool is_full() const + { + return end_ == capacity_end_; + } +}; + +} // namespace blender + +#endif /* __BLI_VECTOR_ADAPTOR_HH__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index d7b279c9bbf..8f760a928bc 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -269,6 +269,7 @@ set(SRC BLI_utility_mixins.hh BLI_uvproject.h BLI_vector.hh + BLI_vector_adaptor.hh BLI_vector_set.hh BLI_vector_set_slots.hh BLI_vfontdata.h @@ -342,3 +343,29 @@ set_source_files_properties( ) blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_GTESTS) + set(TEST_SRC + tests/BLI_array_test.cc + tests/BLI_disjoint_set_test.cc + tests/BLI_edgehash_test.cc + tests/BLI_index_mask_test.cc + tests/BLI_index_range_test.cc + tests/BLI_linear_allocator_test.cc + tests/BLI_map_test.cc + tests/BLI_math_base_safe_test.cc + tests/BLI_memory_utils_test.cc + tests/BLI_multi_value_map_test.cc + tests/BLI_set_test.cc + tests/BLI_span_test.cc + tests/BLI_stack_cxx_test.cc + tests/BLI_string_ref_test.cc + tests/BLI_vector_set_test.cc + tests/BLI_vector_test.cc + ) + set(TEST_LIB + bf_blenlib + ) + include(GTestTesting) + blender_add_test_lib(bf_bli_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") +endif() diff --git a/source/blender/blenlib/intern/BLI_dial_2d.c b/source/blender/blenlib/intern/BLI_dial_2d.c index c6d28e20f35..7363233d573 100644 --- a/source/blender/blenlib/intern/BLI_dial_2d.c +++ b/source/blender/blenlib/intern/BLI_dial_2d.c @@ -45,7 +45,7 @@ struct Dial { bool initialized; }; -Dial *BLI_dial_initialize(const float start_position[2], float threshold) +Dial *BLI_dial_init(const float start_position[2], float threshold) { Dial *dial = MEM_callocN(sizeof(Dial), "dial"); diff --git a/source/blender/blenlib/tests/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc new file mode 100644 index 00000000000..7348a6f93f3 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_array_test.cc @@ -0,0 +1,176 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_array.hh" +#include "BLI_strict_flags.h" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(array, DefaultConstructor) +{ + Array<int> array; + EXPECT_EQ(array.size(), 0); + EXPECT_TRUE(array.is_empty()); +} + +TEST(array, SizeConstructor) +{ + Array<int> array(5); + EXPECT_EQ(array.size(), 5); + EXPECT_FALSE(array.is_empty()); +} + +TEST(array, FillConstructor) +{ + Array<int> array(5, 8); + EXPECT_EQ(array.size(), 5); + EXPECT_EQ(array[0], 8); + EXPECT_EQ(array[1], 8); + EXPECT_EQ(array[2], 8); + EXPECT_EQ(array[3], 8); + EXPECT_EQ(array[4], 8); +} + +TEST(array, InitializerListConstructor) +{ + Array<int> array = {4, 5, 6, 7}; + EXPECT_EQ(array.size(), 4); + EXPECT_EQ(array[0], 4); + EXPECT_EQ(array[1], 5); + EXPECT_EQ(array[2], 6); + EXPECT_EQ(array[3], 7); +} + +TEST(array, SpanConstructor) +{ + int stackarray[4] = {6, 7, 8, 9}; + Span<int> span(stackarray, ARRAY_SIZE(stackarray)); + Array<int> array(span); + EXPECT_EQ(array.size(), 4); + EXPECT_EQ(array[0], 6); + EXPECT_EQ(array[1], 7); + EXPECT_EQ(array[2], 8); + EXPECT_EQ(array[3], 9); +} + +TEST(array, CopyConstructor) +{ + Array<int> array = {5, 6, 7, 8}; + Array<int> new_array(array); + + EXPECT_EQ(array.size(), 4); + EXPECT_EQ(new_array.size(), 4); + EXPECT_NE(array.data(), new_array.data()); + EXPECT_EQ(new_array[0], 5); + EXPECT_EQ(new_array[1], 6); + EXPECT_EQ(new_array[2], 7); + EXPECT_EQ(new_array[3], 8); +} + +TEST(array, MoveConstructor) +{ + Array<int> array = {5, 6, 7, 8}; + Array<int> new_array(std::move(array)); + + EXPECT_EQ(array.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(new_array.size(), 4); + EXPECT_EQ(new_array[0], 5); + EXPECT_EQ(new_array[1], 6); + EXPECT_EQ(new_array[2], 7); + EXPECT_EQ(new_array[3], 8); +} + +TEST(array, CopyAssignment) +{ + Array<int> array = {1, 2, 3}; + Array<int> new_array = {4}; + EXPECT_EQ(new_array.size(), 1); + new_array = array; + EXPECT_EQ(new_array.size(), 3); + EXPECT_EQ(array.size(), 3); + EXPECT_NE(array.data(), new_array.data()); + EXPECT_EQ(new_array[0], 1); + EXPECT_EQ(new_array[1], 2); + EXPECT_EQ(new_array[2], 3); +} + +TEST(array, MoveAssignment) +{ + Array<int> array = {1, 2, 3}; + Array<int> new_array = {4}; + EXPECT_EQ(new_array.size(), 1); + new_array = std::move(array); + EXPECT_EQ(new_array.size(), 3); + EXPECT_EQ(array.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(new_array[0], 1); + EXPECT_EQ(new_array[1], 2); + EXPECT_EQ(new_array[2], 3); +} + +/** + * Tests that the trivially constructible types are not zero-initialized. We do not want that for + * performance reasons. + */ +TEST(array, TrivialTypeSizeConstructor) +{ + Array<char, 1> *array = new Array<char, 1>(1); + char *ptr = &(*array)[0]; + array->~Array(); + + const char magic = 42; + *ptr = magic; + EXPECT_EQ(*ptr, magic); + + new (array) Array<char, 1>(1); + EXPECT_EQ((*array)[0], magic); + EXPECT_EQ(*ptr, magic); + delete array; +} + +struct ConstructibleType { + char value; + + ConstructibleType() + { + value = 42; + } +}; + +TEST(array, NoInitializationSizeConstructor) +{ + using MyArray = Array<ConstructibleType>; + + TypedBuffer<MyArray> buffer; + memset((void *)&buffer, 100, sizeof(MyArray)); + + /* Doing this to avoid some compiler optimization. */ + for (int64_t i : IndexRange(sizeof(MyArray))) { + EXPECT_EQ(((char *)buffer.ptr())[i], 100); + } + + { + MyArray &array = *new (buffer) MyArray(1, NoInitialization()); + EXPECT_EQ(array[0].value, 100); + array.clear_without_destruct(); + array.~Array(); + } + { + MyArray &array = *new (buffer) MyArray(1); + EXPECT_EQ(array[0].value, 42); + array.~Array(); + } +} + +TEST(array, Fill) +{ + Array<int> array(5); + array.fill(3); + EXPECT_EQ(array.size(), 5u); + EXPECT_EQ(array[0], 3); + EXPECT_EQ(array[1], 3); + EXPECT_EQ(array[2], 3); + EXPECT_EQ(array[3], 3); + EXPECT_EQ(array[4], 3); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_disjoint_set_test.cc b/source/blender/blenlib/tests/BLI_disjoint_set_test.cc new file mode 100644 index 00000000000..f30ee610b2a --- /dev/null +++ b/source/blender/blenlib/tests/BLI_disjoint_set_test.cc @@ -0,0 +1,36 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_disjoint_set.hh" +#include "BLI_strict_flags.h" + +#include "testing/testing.h" + +namespace blender::tests { + +TEST(disjoint_set, Test) +{ + DisjointSet disjoint_set(6); + EXPECT_FALSE(disjoint_set.in_same_set(1, 2)); + EXPECT_FALSE(disjoint_set.in_same_set(5, 3)); + EXPECT_TRUE(disjoint_set.in_same_set(2, 2)); + EXPECT_EQ(disjoint_set.find_root(3), 3); + + disjoint_set.join(1, 2); + + EXPECT_TRUE(disjoint_set.in_same_set(1, 2)); + EXPECT_FALSE(disjoint_set.in_same_set(0, 1)); + + disjoint_set.join(3, 4); + + EXPECT_FALSE(disjoint_set.in_same_set(2, 3)); + EXPECT_TRUE(disjoint_set.in_same_set(3, 4)); + + disjoint_set.join(1, 4); + + EXPECT_TRUE(disjoint_set.in_same_set(1, 4)); + EXPECT_TRUE(disjoint_set.in_same_set(1, 3)); + EXPECT_TRUE(disjoint_set.in_same_set(2, 4)); + EXPECT_FALSE(disjoint_set.in_same_set(0, 4)); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_edgehash_test.cc b/source/blender/blenlib/tests/BLI_edgehash_test.cc new file mode 100644 index 00000000000..7106033df36 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_edgehash_test.cc @@ -0,0 +1,408 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" +#include <algorithm> +#include <random> +#include <vector> + +#include "BLI_edgehash.h" +#include "BLI_utildefines.h" + +#define VALUE_1 POINTER_FROM_INT(1) +#define VALUE_2 POINTER_FROM_INT(2) +#define VALUE_3 POINTER_FROM_INT(3) + +TEST(edgehash, InsertIncreasesLength) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + ASSERT_EQ(BLI_edgehash_len(eh), 0); + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, ReinsertNewIncreasesLength) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + ASSERT_EQ(BLI_edgehash_len(eh), 0); + BLI_edgehash_reinsert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, ReinsertExistingDoesNotIncreaseLength) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + ASSERT_EQ(BLI_edgehash_len(eh), 0); + BLI_edgehash_reinsert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + BLI_edgehash_reinsert(eh, 1, 2, VALUE_2); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + BLI_edgehash_reinsert(eh, 2, 1, VALUE_2); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, ReinsertCanChangeValue) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_lookup(eh, 1, 2), VALUE_1); + BLI_edgehash_reinsert(eh, 2, 1, VALUE_2); + ASSERT_EQ(BLI_edgehash_lookup(eh, 1, 2), VALUE_2); + BLI_edgehash_reinsert(eh, 1, 2, VALUE_3); + ASSERT_EQ(BLI_edgehash_lookup(eh, 2, 1), VALUE_3); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, LookupExisting) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_lookup(eh, 1, 2), VALUE_1); + ASSERT_EQ(BLI_edgehash_lookup(eh, 2, 1), VALUE_1); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, LookupNonExisting) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + ASSERT_EQ(BLI_edgehash_lookup(eh, 1, 2), nullptr); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, LookupNonExistingWithDefault) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + ASSERT_EQ(BLI_edgehash_lookup_default(eh, 1, 2, VALUE_1), VALUE_1); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, LookupExistingWithDefault) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_lookup_default(eh, 1, 2, VALUE_2), VALUE_1); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, LookupPExisting) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + void *value = VALUE_1; + BLI_edgehash_insert(eh, 1, 2, value); + void **value_p = BLI_edgehash_lookup_p(eh, 1, 2); + ASSERT_EQ(*value_p, VALUE_1); + *value_p = VALUE_2; + ASSERT_EQ(BLI_edgehash_lookup(eh, 1, 2), VALUE_2); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, LookupPNonExisting) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + ASSERT_EQ(BLI_edgehash_lookup_p(eh, 1, 2), nullptr); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, EnsurePNonExisting) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + void **value_p; + bool existed = BLI_edgehash_ensure_p(eh, 1, 2, &value_p); + ASSERT_FALSE(existed); + *value_p = VALUE_1; + ASSERT_EQ(BLI_edgehash_lookup(eh, 1, 2), VALUE_1); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, EnsurePExisting) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + void **value_p; + bool existed = BLI_edgehash_ensure_p(eh, 1, 2, &value_p); + ASSERT_TRUE(existed); + ASSERT_EQ(*value_p, VALUE_1); + *value_p = VALUE_2; + ASSERT_EQ(BLI_edgehash_lookup(eh, 1, 2), VALUE_2); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, RemoveExistingDecreasesLength) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + bool has_been_removed = BLI_edgehash_remove(eh, 1, 2, nullptr); + ASSERT_EQ(BLI_edgehash_len(eh), 0); + ASSERT_TRUE(has_been_removed); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, RemoveNonExistingDoesNotDecreaseLength) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + bool has_been_removed = BLI_edgehash_remove(eh, 4, 5, nullptr); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + ASSERT_FALSE(has_been_removed); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, PopKeyTwice) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_popkey(eh, 1, 2), VALUE_1); + ASSERT_EQ(BLI_edgehash_popkey(eh, 1, 2), nullptr); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, LookupInvertedIndices) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_EQ(BLI_edgehash_lookup(eh, 2, 1), VALUE_1); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, HasKeyExisting) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + ASSERT_TRUE(BLI_edgehash_haskey(eh, 1, 2)); + ASSERT_TRUE(BLI_edgehash_haskey(eh, 2, 1)); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, HasKeyNonExisting) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + ASSERT_FALSE(BLI_edgehash_haskey(eh, 1, 2)); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, ClearSetsLengthToZero) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + BLI_edgehash_insert(eh, 1, 2, VALUE_2); + ASSERT_EQ(BLI_edgehash_len(eh), 2); + BLI_edgehash_clear(eh, nullptr); + ASSERT_EQ(BLI_edgehash_len(eh), 0); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, IteratorFindsAllValues) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + BLI_edgehash_insert(eh, 1, 3, VALUE_2); + BLI_edgehash_insert(eh, 1, 4, VALUE_3); + + EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh); + auto a = BLI_edgehashIterator_getValue(ehi); + BLI_edgehashIterator_step(ehi); + auto b = BLI_edgehashIterator_getValue(ehi); + BLI_edgehashIterator_step(ehi); + auto c = BLI_edgehashIterator_getValue(ehi); + BLI_edgehashIterator_step(ehi); + + ASSERT_NE(a, b); + ASSERT_NE(b, c); + ASSERT_NE(a, c); + ASSERT_TRUE(ELEM(a, VALUE_1, VALUE_2, VALUE_3)); + ASSERT_TRUE(ELEM(b, VALUE_1, VALUE_2, VALUE_3)); + ASSERT_TRUE(ELEM(c, VALUE_1, VALUE_2, VALUE_3)); + + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, IterateIsDone) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + BLI_edgehash_insert(eh, 1, 3, VALUE_2); + BLI_edgehash_insert(eh, 1, 4, VALUE_3); + + EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh); + ASSERT_FALSE(BLI_edgehashIterator_isDone(ehi)); + BLI_edgehashIterator_step(ehi); + ASSERT_FALSE(BLI_edgehashIterator_isDone(ehi)); + BLI_edgehashIterator_step(ehi); + ASSERT_FALSE(BLI_edgehashIterator_isDone(ehi)); + BLI_edgehashIterator_step(ehi); + ASSERT_TRUE(BLI_edgehashIterator_isDone(ehi)); + + BLI_edgehashIterator_free(ehi); + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgehash, DoubleRemove) +{ + EdgeHash *eh = BLI_edgehash_new(__func__); + + BLI_edgehash_insert(eh, 1, 2, VALUE_1); + BLI_edgehash_insert(eh, 1, 3, VALUE_2); + BLI_edgehash_insert(eh, 1, 4, VALUE_3); + ASSERT_EQ(BLI_edgehash_len(eh), 3); + + BLI_edgehash_remove(eh, 1, 2, nullptr); + BLI_edgehash_remove(eh, 1, 3, nullptr); + ASSERT_EQ(BLI_edgehash_len(eh), 1); + + BLI_edgehash_free(eh, nullptr); +} + +struct Edge { + uint v1, v2; +}; + +TEST(edgehash, StressTest) +{ + std::srand(0); + int amount = 10000; + + std::vector<Edge> edges; + for (int i = 0; i < amount; i++) { + edges.push_back({(uint)i, amount + (uint)std::rand() % 12345}); + } + + EdgeHash *eh = BLI_edgehash_new(__func__); + + /* first insert all the edges */ + for (int i = 0; i < edges.size(); i++) { + BLI_edgehash_insert(eh, edges[i].v1, edges[i].v2, POINTER_FROM_INT(i)); + } + + std::vector<Edge> shuffled = edges; + std::shuffle(shuffled.begin(), shuffled.end(), std::default_random_engine()); + + /* then remove half of them */ + int remove_until = shuffled.size() / 2; + for (int i = 0; i < remove_until; i++) { + BLI_edgehash_remove(eh, shuffled[i].v2, shuffled[i].v1, nullptr); + } + + ASSERT_EQ(BLI_edgehash_len(eh), edges.size() - remove_until); + + /* check if the right ones have been removed */ + for (int i = 0; i < shuffled.size(); i++) { + bool haskey = BLI_edgehash_haskey(eh, shuffled[i].v1, shuffled[i].v2); + if (i < remove_until) { + ASSERT_FALSE(haskey); + } + else { + ASSERT_TRUE(haskey); + } + } + + /* reinsert all edges */ + for (int i = 0; i < edges.size(); i++) { + BLI_edgehash_reinsert(eh, edges[i].v1, edges[i].v2, POINTER_FROM_INT(i)); + } + + ASSERT_EQ(BLI_edgehash_len(eh), edges.size()); + + /* pop all edges */ + for (int i = 0; i < edges.size(); i++) { + int value = POINTER_AS_INT(BLI_edgehash_popkey(eh, edges[i].v1, edges[i].v2)); + ASSERT_EQ(i, value); + } + + ASSERT_EQ(BLI_edgehash_len(eh), 0); + + BLI_edgehash_free(eh, nullptr); +} + +TEST(edgeset, AddNonExistingIncreasesLength) +{ + EdgeSet *es = BLI_edgeset_new(__func__); + + ASSERT_EQ(BLI_edgeset_len(es), 0); + BLI_edgeset_add(es, 1, 2); + ASSERT_EQ(BLI_edgeset_len(es), 1); + BLI_edgeset_add(es, 1, 3); + ASSERT_EQ(BLI_edgeset_len(es), 2); + BLI_edgeset_add(es, 1, 4); + ASSERT_EQ(BLI_edgeset_len(es), 3); + + BLI_edgeset_free(es); +} + +TEST(edgeset, AddExistingDoesNotIncreaseLength) +{ + EdgeSet *es = BLI_edgeset_new(__func__); + + ASSERT_EQ(BLI_edgeset_len(es), 0); + BLI_edgeset_add(es, 1, 2); + ASSERT_EQ(BLI_edgeset_len(es), 1); + BLI_edgeset_add(es, 2, 1); + ASSERT_EQ(BLI_edgeset_len(es), 1); + BLI_edgeset_add(es, 1, 2); + ASSERT_EQ(BLI_edgeset_len(es), 1); + + BLI_edgeset_free(es); +} + +TEST(edgeset, HasKeyNonExisting) +{ + EdgeSet *es = BLI_edgeset_new(__func__); + + ASSERT_FALSE(BLI_edgeset_haskey(es, 1, 2)); + + BLI_edgeset_free(es); +} + +TEST(edgeset, HasKeyExisting) +{ + EdgeSet *es = BLI_edgeset_new(__func__); + + BLI_edgeset_insert(es, 1, 2); + ASSERT_TRUE(BLI_edgeset_haskey(es, 1, 2)); + + BLI_edgeset_free(es); +} diff --git a/source/blender/blenlib/tests/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc new file mode 100644 index 00000000000..4d6060e51c9 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc @@ -0,0 +1,43 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_index_mask.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(index_mask, DefaultConstructor) +{ + IndexMask mask; + EXPECT_EQ(mask.min_array_size(), 0); + EXPECT_EQ(mask.size(), 0); +} + +TEST(index_mask, ArrayConstructor) +{ + [](IndexMask mask) { + EXPECT_EQ(mask.size(), 4); + EXPECT_EQ(mask.min_array_size(), 8); + EXPECT_FALSE(mask.is_range()); + EXPECT_EQ(mask[0], 3); + EXPECT_EQ(mask[1], 5); + EXPECT_EQ(mask[2], 6); + EXPECT_EQ(mask[3], 7); + }({3, 5, 6, 7}); +} + +TEST(index_mask, RangeConstructor) +{ + IndexMask mask = IndexRange(3, 5); + EXPECT_EQ(mask.size(), 5); + EXPECT_EQ(mask.min_array_size(), 8); + EXPECT_EQ(mask.last(), 7); + EXPECT_TRUE(mask.is_range()); + EXPECT_EQ(mask.as_range().first(), 3); + EXPECT_EQ(mask.as_range().last(), 7); + Span<int64_t> indices = mask.indices(); + EXPECT_EQ(indices[0], 3); + EXPECT_EQ(indices[1], 4); + EXPECT_EQ(indices[2], 5); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc new file mode 100644 index 00000000000..d472ded0f18 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -0,0 +1,143 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_index_range.hh" +#include "BLI_strict_flags.h" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(index_range, DefaultConstructor) +{ + IndexRange range; + EXPECT_EQ(range.size(), 0); + + Vector<int64_t> vector; + for (int64_t value : range) { + vector.append(value); + } + EXPECT_EQ(vector.size(), 0); +} + +TEST(index_range, SingleElementRange) +{ + IndexRange range(4, 1); + EXPECT_EQ(range.size(), 1); + EXPECT_EQ(*range.begin(), 4); + + Vector<int64_t> vector; + for (int64_t value : range) { + vector.append(value); + } + + EXPECT_EQ(vector.size(), 1); + EXPECT_EQ(vector[0], 4); +} + +TEST(index_range, MultipleElementRange) +{ + IndexRange range(6, 4); + EXPECT_EQ(range.size(), 4); + + Vector<int64_t> vector; + for (int64_t value : range) { + vector.append(value); + } + + EXPECT_EQ(vector.size(), 4); + for (int i = 0; i < 4; i++) { + EXPECT_EQ(vector[i], i + 6); + } +} + +TEST(index_range, SubscriptOperator) +{ + IndexRange range(5, 5); + EXPECT_EQ(range[0], 5); + EXPECT_EQ(range[1], 6); + EXPECT_EQ(range[2], 7); +} + +TEST(index_range, Before) +{ + IndexRange range = IndexRange(5, 5).before(3); + EXPECT_EQ(range[0], 2); + EXPECT_EQ(range[1], 3); + EXPECT_EQ(range[2], 4); + EXPECT_EQ(range.size(), 3); +} + +TEST(index_range, After) +{ + IndexRange range = IndexRange(5, 5).after(4); + EXPECT_EQ(range[0], 10); + EXPECT_EQ(range[1], 11); + EXPECT_EQ(range[2], 12); + EXPECT_EQ(range[3], 13); + EXPECT_EQ(range.size(), 4); +} + +TEST(index_range, Contains) +{ + IndexRange range = IndexRange(5, 3); + EXPECT_TRUE(range.contains(5)); + EXPECT_TRUE(range.contains(6)); + EXPECT_TRUE(range.contains(7)); + EXPECT_FALSE(range.contains(4)); + EXPECT_FALSE(range.contains(8)); +} + +TEST(index_range, First) +{ + IndexRange range = IndexRange(5, 3); + EXPECT_EQ(range.first(), 5); +} + +TEST(index_range, Last) +{ + IndexRange range = IndexRange(5, 3); + EXPECT_EQ(range.last(), 7); +} + +TEST(index_range, OneAfterEnd) +{ + IndexRange range = IndexRange(5, 3); + EXPECT_EQ(range.one_after_last(), 8); +} + +TEST(index_range, Start) +{ + IndexRange range = IndexRange(6, 2); + EXPECT_EQ(range.start(), 6); +} + +TEST(index_range, Slice) +{ + IndexRange range = IndexRange(5, 15); + IndexRange slice = range.slice(2, 6); + EXPECT_EQ(slice.size(), 6); + EXPECT_EQ(slice.first(), 7); + EXPECT_EQ(slice.last(), 12); +} + +TEST(index_range, SliceRange) +{ + IndexRange range = IndexRange(5, 15); + IndexRange slice = range.slice(IndexRange(3, 5)); + EXPECT_EQ(slice.size(), 5); + EXPECT_EQ(slice.first(), 8); + EXPECT_EQ(slice.last(), 12); +} + +TEST(index_range, AsSpan) +{ + IndexRange range = IndexRange(4, 6); + Span<int64_t> span = range.as_span(); + EXPECT_EQ(span.size(), 6); + EXPECT_EQ(span[0], 4); + EXPECT_EQ(span[1], 5); + EXPECT_EQ(span[2], 6); + EXPECT_EQ(span[3], 7); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc new file mode 100644 index 00000000000..44b70d1f55d --- /dev/null +++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc @@ -0,0 +1,118 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_linear_allocator.hh" +#include "BLI_strict_flags.h" +#include "testing/testing.h" + +namespace blender::tests { + +static bool is_aligned(void *ptr, uint alignment) +{ + BLI_assert(is_power_of_2_i((int)alignment)); + return (POINTER_AS_UINT(ptr) & (alignment - 1)) == 0; +} + +TEST(linear_allocator, AllocationAlignment) +{ + LinearAllocator<> allocator; + + EXPECT_TRUE(is_aligned(allocator.allocate(10, 4), 4)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 4), 4)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 4), 4)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 8), 8)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 4), 4)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 16), 16)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 4), 4)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 64), 64)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 64), 64)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 8), 8)); + EXPECT_TRUE(is_aligned(allocator.allocate(10, 128), 128)); +} + +TEST(linear_allocator, PackedAllocation) +{ + LinearAllocator<> allocator; + blender::AlignedBuffer<256, 32> buffer; + allocator.provide_buffer(buffer); + + uintptr_t ptr1 = (uintptr_t)allocator.allocate(10, 4); /* 0 - 10 */ + uintptr_t ptr2 = (uintptr_t)allocator.allocate(10, 4); /* 12 - 22 */ + uintptr_t ptr3 = (uintptr_t)allocator.allocate(8, 32); /* 32 - 40 */ + uintptr_t ptr4 = (uintptr_t)allocator.allocate(16, 8); /* 40 - 56 */ + uintptr_t ptr5 = (uintptr_t)allocator.allocate(1, 8); /* 56 - 57 */ + uintptr_t ptr6 = (uintptr_t)allocator.allocate(1, 4); /* 60 - 61 */ + uintptr_t ptr7 = (uintptr_t)allocator.allocate(1, 1); /* 61 - 62 */ + + EXPECT_EQ(ptr2 - ptr1, 12); /* 12 - 0 = 12 */ + EXPECT_EQ(ptr3 - ptr2, 20); /* 32 - 12 = 20 */ + EXPECT_EQ(ptr4 - ptr3, 8); /* 40 - 32 = 8 */ + EXPECT_EQ(ptr5 - ptr4, 16); /* 56 - 40 = 16 */ + EXPECT_EQ(ptr6 - ptr5, 4); /* 60 - 56 = 4 */ + EXPECT_EQ(ptr7 - ptr6, 1); /* 61 - 60 = 1 */ +} + +TEST(linear_allocator, CopyString) +{ + LinearAllocator<> allocator; + blender::AlignedBuffer<256, 1> buffer; + allocator.provide_buffer(buffer); + + StringRefNull ref1 = allocator.copy_string("Hello"); + StringRefNull ref2 = allocator.copy_string("World"); + + EXPECT_EQ(ref1, "Hello"); + EXPECT_EQ(ref2, "World"); + EXPECT_EQ(ref2.data() - ref1.data(), 6); +} + +TEST(linear_allocator, AllocateArray) +{ + LinearAllocator<> allocator; + + MutableSpan<int> span = allocator.allocate_array<int>(5); + EXPECT_EQ(span.size(), 5); +} + +TEST(linear_allocator, Construct) +{ + LinearAllocator<> allocator; + + std::array<int, 5> values = {1, 2, 3, 4, 5}; + Vector<int> *vector = allocator.construct<Vector<int>>(values); + EXPECT_EQ(vector->size(), 5); + EXPECT_EQ((*vector)[3], 4); + vector->~Vector(); +} + +TEST(linear_allocator, ConstructElementsAndPointerArray) +{ + LinearAllocator<> allocator; + + std::array<int, 7> values = {1, 2, 3, 4, 5, 6, 7}; + Span<Vector<int> *> vectors = allocator.construct_elements_and_pointer_array<Vector<int>>( + 5, values); + + EXPECT_EQ(vectors.size(), 5); + EXPECT_EQ(vectors[3]->size(), 7); + EXPECT_EQ((*vectors[2])[5], 6); + + for (Vector<int> *vector : vectors) { + vector->~Vector(); + } +} + +TEST(linear_allocator, ConstructArrayCopy) +{ + LinearAllocator<> allocator; + + Vector<int> values = {1, 2, 3}; + MutableSpan<int> span1 = allocator.construct_array_copy(values.as_span()); + MutableSpan<int> span2 = allocator.construct_array_copy(values.as_span()); + EXPECT_NE(span1.data(), span2.data()); + EXPECT_EQ(span1.size(), 3); + EXPECT_EQ(span2.size(), 3); + EXPECT_EQ(span1[1], 2); + EXPECT_EQ(span2[2], 3); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc new file mode 100644 index 00000000000..fe7b0f01279 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_map_test.cc @@ -0,0 +1,590 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_map.hh" +#include "BLI_rand.h" +#include "BLI_set.hh" +#include "BLI_strict_flags.h" +#include "BLI_timeit.hh" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(map, DefaultConstructor) +{ + Map<int, float> map; + EXPECT_EQ(map.size(), 0); + EXPECT_TRUE(map.is_empty()); +} + +TEST(map, AddIncreasesSize) +{ + Map<int, float> map; + EXPECT_EQ(map.size(), 0); + EXPECT_TRUE(map.is_empty()); + map.add(2, 5.0f); + EXPECT_EQ(map.size(), 1); + EXPECT_FALSE(map.is_empty()); + map.add(6, 2.0f); + EXPECT_EQ(map.size(), 2); + EXPECT_FALSE(map.is_empty()); +} + +TEST(map, Contains) +{ + Map<int, float> map; + EXPECT_FALSE(map.contains(4)); + map.add(5, 6.0f); + EXPECT_FALSE(map.contains(4)); + map.add(4, 2.0f); + EXPECT_TRUE(map.contains(4)); +} + +TEST(map, LookupExisting) +{ + Map<int, float> map; + map.add(2, 6.0f); + map.add(4, 1.0f); + EXPECT_EQ(map.lookup(2), 6.0f); + EXPECT_EQ(map.lookup(4), 1.0f); +} + +TEST(map, LookupNotExisting) +{ + Map<int, float> map; + map.add(2, 4.0f); + map.add(1, 1.0f); + EXPECT_EQ(map.lookup_ptr(0), nullptr); + EXPECT_EQ(map.lookup_ptr(5), nullptr); +} + +TEST(map, AddMany) +{ + Map<int, int> map; + for (int i = 0; i < 100; i++) { + map.add(i * 30, i); + map.add(i * 31, i); + } +} + +TEST(map, PopItem) +{ + Map<int, float> map; + map.add(2, 3.0f); + map.add(1, 9.0f); + EXPECT_TRUE(map.contains(2)); + EXPECT_TRUE(map.contains(1)); + + EXPECT_EQ(map.pop(1), 9.0f); + EXPECT_TRUE(map.contains(2)); + EXPECT_FALSE(map.contains(1)); + + EXPECT_EQ(map.pop(2), 3.0f); + EXPECT_FALSE(map.contains(2)); + EXPECT_FALSE(map.contains(1)); +} + +TEST(map, PopTry) +{ + Map<int, int> map; + map.add(1, 5); + map.add(2, 7); + EXPECT_EQ(map.size(), 2); + std::optional<int> value = map.pop_try(4); + EXPECT_EQ(map.size(), 2); + EXPECT_FALSE(value.has_value()); + value = map.pop_try(2); + EXPECT_EQ(map.size(), 1); + EXPECT_TRUE(value.has_value()); + EXPECT_EQ(*value, 7); + EXPECT_EQ(*map.pop_try(1), 5); + EXPECT_EQ(map.size(), 0); +} + +TEST(map, PopDefault) +{ + Map<int, int> map; + map.add(1, 4); + map.add(2, 7); + map.add(3, 8); + EXPECT_EQ(map.size(), 3); + EXPECT_EQ(map.pop_default(4, 10), 10); + EXPECT_EQ(map.size(), 3); + EXPECT_EQ(map.pop_default(1, 10), 4); + EXPECT_EQ(map.size(), 2); + EXPECT_EQ(map.pop_default(2, 20), 7); + EXPECT_EQ(map.size(), 1); + EXPECT_EQ(map.pop_default(2, 20), 20); + EXPECT_EQ(map.size(), 1); + EXPECT_EQ(map.pop_default(3, 0), 8); + EXPECT_EQ(map.size(), 0); +} + +TEST(map, PopItemMany) +{ + Map<int, int> map; + for (int i = 0; i < 100; i++) { + map.add_new(i, i); + } + for (int i = 25; i < 80; i++) { + EXPECT_EQ(map.pop(i), i); + } + for (int i = 0; i < 100; i++) { + EXPECT_EQ(map.contains(i), i < 25 || i >= 80); + } +} + +TEST(map, ValueIterator) +{ + Map<int, float> map; + map.add(3, 5.0f); + map.add(1, 2.0f); + map.add(7, -2.0f); + + blender::Set<float> values; + + int iterations = 0; + for (float value : map.values()) { + values.add(value); + iterations++; + } + + EXPECT_EQ(iterations, 3); + EXPECT_TRUE(values.contains(5.0f)); + EXPECT_TRUE(values.contains(-2.0f)); + EXPECT_TRUE(values.contains(2.0f)); +} + +TEST(map, KeyIterator) +{ + Map<int, float> map; + map.add(6, 3.0f); + map.add(2, 4.0f); + map.add(1, 3.0f); + + blender::Set<int> keys; + + int iterations = 0; + for (int key : map.keys()) { + keys.add(key); + iterations++; + } + + EXPECT_EQ(iterations, 3); + EXPECT_TRUE(keys.contains(1)); + EXPECT_TRUE(keys.contains(2)); + EXPECT_TRUE(keys.contains(6)); +} + +TEST(map, ItemIterator) +{ + Map<int, float> map; + map.add(5, 3.0f); + map.add(2, 9.0f); + map.add(1, 0.0f); + + blender::Set<int> keys; + blender::Set<float> values; + + int iterations = 0; + const Map<int, float> &const_map = map; + for (auto item : const_map.items()) { + keys.add(item.key); + values.add(item.value); + iterations++; + } + + EXPECT_EQ(iterations, 3); + EXPECT_TRUE(keys.contains(5)); + EXPECT_TRUE(keys.contains(2)); + EXPECT_TRUE(keys.contains(1)); + EXPECT_TRUE(values.contains(3.0f)); + EXPECT_TRUE(values.contains(9.0f)); + EXPECT_TRUE(values.contains(0.0f)); +} + +TEST(map, MutableValueIterator) +{ + Map<int, int> map; + map.add(3, 6); + map.add(2, 1); + + for (int &value : map.values()) { + value += 10; + } + + EXPECT_EQ(map.lookup(3), 16); + EXPECT_EQ(map.lookup(2), 11); +} + +TEST(map, MutableItemIterator) +{ + Map<int, int> map; + map.add(3, 6); + map.add(2, 1); + + for (auto item : map.items()) { + item.value += item.key; + } + + EXPECT_EQ(map.lookup(3), 9.0f); + EXPECT_EQ(map.lookup(2), 3.0f); +} + +TEST(map, MutableItemToItemConversion) +{ + Map<int, int> map; + map.add(3, 6); + map.add(2, 1); + + Vector<int> keys, values; + for (Map<int, int>::Item item : map.items()) { + keys.append(item.key); + values.append(item.value); + } + + EXPECT_EQ(keys.size(), 2); + EXPECT_EQ(values.size(), 2); + EXPECT_TRUE(keys.contains(3)); + EXPECT_TRUE(keys.contains(2)); + EXPECT_TRUE(values.contains(6)); + EXPECT_TRUE(values.contains(1)); +} + +static float return_42() +{ + return 42.0f; +} + +TEST(map, LookupOrAddCB_SeparateFunction) +{ + Map<int, float> map; + EXPECT_EQ(map.lookup_or_add_cb(0, return_42), 42.0f); + EXPECT_EQ(map.lookup(0), 42); + + map.keys(); +} + +TEST(map, LookupOrAddCB_Lambdas) +{ + Map<int, float> map; + auto lambda1 = []() { return 11.0f; }; + EXPECT_EQ(map.lookup_or_add_cb(0, lambda1), 11.0f); + auto lambda2 = []() { return 20.0f; }; + EXPECT_EQ(map.lookup_or_add_cb(1, lambda2), 20.0f); + + EXPECT_EQ(map.lookup_or_add_cb(0, lambda2), 11.0f); + EXPECT_EQ(map.lookup_or_add_cb(1, lambda1), 20.0f); +} + +TEST(map, AddOrModify) +{ + Map<int, float> map; + auto create_func = [](float *value) { + *value = 10.0f; + return true; + }; + auto modify_func = [](float *value) { + *value += 5; + return false; + }; + EXPECT_TRUE(map.add_or_modify(1, create_func, modify_func)); + EXPECT_EQ(map.lookup(1), 10.0f); + EXPECT_FALSE(map.add_or_modify(1, create_func, modify_func)); + EXPECT_EQ(map.lookup(1), 15.0f); +} + +TEST(map, AddOverwrite) +{ + Map<int, float> map; + EXPECT_FALSE(map.contains(3)); + EXPECT_TRUE(map.add_overwrite(3, 6.0f)); + EXPECT_EQ(map.lookup(3), 6.0f); + EXPECT_FALSE(map.add_overwrite(3, 7.0f)); + EXPECT_EQ(map.lookup(3), 7.0f); + EXPECT_FALSE(map.add(3, 8.0f)); + EXPECT_EQ(map.lookup(3), 7.0f); +} + +TEST(map, LookupOrAddDefault) +{ + Map<int, float> map; + map.lookup_or_add_default(3) = 6; + EXPECT_EQ(map.lookup(3), 6); + map.lookup_or_add_default(5) = 2; + EXPECT_EQ(map.lookup(5), 2); + map.lookup_or_add_default(3) += 4; + EXPECT_EQ(map.lookup(3), 10); +} + +TEST(map, LookupOrAdd) +{ + Map<int, int> map; + EXPECT_EQ(map.lookup_or_add(6, 4), 4); + EXPECT_EQ(map.lookup_or_add(6, 5), 4); + map.lookup_or_add(6, 4) += 10; + EXPECT_EQ(map.lookup(6), 14); +} + +TEST(map, MoveConstructorSmall) +{ + Map<int, float> map1; + map1.add(1, 2.0f); + map1.add(4, 1.0f); + Map<int, float> map2(std::move(map1)); + EXPECT_EQ(map2.size(), 2); + EXPECT_EQ(map2.lookup(1), 2.0f); + EXPECT_EQ(map2.lookup(4), 1.0f); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(map1.lookup_ptr(4), nullptr); +} + +TEST(map, MoveConstructorLarge) +{ + Map<int, int> map1; + for (int i = 0; i < 100; i++) { + map1.add_new(i, i); + } + Map<int, int> map2(std::move(map1)); + EXPECT_EQ(map2.size(), 100); + EXPECT_EQ(map2.lookup(1), 1); + EXPECT_EQ(map2.lookup(4), 4); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(map1.lookup_ptr(4), nullptr); +} + +TEST(map, MoveAssignment) +{ + Map<int, float> map1; + map1.add(1, 2.0f); + map1.add(4, 1.0f); + Map<int, float> map2; + map2 = std::move(map1); + EXPECT_EQ(map2.size(), 2); + EXPECT_EQ(map2.lookup(1), 2.0f); + EXPECT_EQ(map2.lookup(4), 1.0f); + EXPECT_EQ(map1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(map1.lookup_ptr(4), nullptr); +} + +TEST(map, CopyAssignment) +{ + Map<int, float> map1; + map1.add(1, 2.0f); + map1.add(4, 1.0f); + Map<int, float> map2; + map2 = map1; + EXPECT_EQ(map2.size(), 2); + EXPECT_EQ(map2.lookup(1), 2.0f); + EXPECT_EQ(map2.lookup(4), 1.0f); + EXPECT_EQ(map1.size(), 2); + EXPECT_EQ(*map1.lookup_ptr(4), 1.0f); +} + +TEST(map, Clear) +{ + Map<int, float> map; + map.add(1, 1.0f); + map.add(2, 5.0f); + + EXPECT_EQ(map.size(), 2); + EXPECT_TRUE(map.contains(1)); + EXPECT_TRUE(map.contains(2)); + + map.clear(); + + EXPECT_EQ(map.size(), 0); + EXPECT_FALSE(map.contains(1)); + EXPECT_FALSE(map.contains(2)); +} + +TEST(map, UniquePtrValue) +{ + auto value1 = std::unique_ptr<int>(new int()); + auto value2 = std::unique_ptr<int>(new int()); + auto value3 = std::unique_ptr<int>(new int()); + + int *value1_ptr = value1.get(); + + Map<int, std::unique_ptr<int>> map; + map.add_new(1, std::move(value1)); + map.add(2, std::move(value2)); + map.add_overwrite(3, std::move(value3)); + map.lookup_or_add_cb(4, []() { return std::unique_ptr<int>(new int()); }); + map.add_new(5, std::unique_ptr<int>(new int())); + map.add(6, std::unique_ptr<int>(new int())); + map.add_overwrite(7, std::unique_ptr<int>(new int())); + map.lookup_or_add(8, std::unique_ptr<int>(new int())); + map.pop_default(9, std::unique_ptr<int>(new int())); + + EXPECT_EQ(map.lookup(1).get(), value1_ptr); + EXPECT_EQ(map.lookup_ptr(100), nullptr); +} + +TEST(map, Remove) +{ + Map<int, int> map; + map.add(2, 4); + EXPECT_EQ(map.size(), 1); + EXPECT_FALSE(map.remove(3)); + EXPECT_EQ(map.size(), 1); + EXPECT_TRUE(map.remove(2)); + EXPECT_EQ(map.size(), 0); +} + +TEST(map, PointerKeys) +{ + char a, b, c, d; + + Map<char *, int> map; + EXPECT_TRUE(map.add(&a, 5)); + EXPECT_FALSE(map.add(&a, 4)); + map.add_new(&b, 1); + map.add_new(&c, 1); + EXPECT_EQ(map.size(), 3); + EXPECT_TRUE(map.remove(&b)); + EXPECT_TRUE(map.add(&b, 8)); + EXPECT_FALSE(map.remove(&d)); + EXPECT_TRUE(map.remove(&a)); + EXPECT_TRUE(map.remove(&b)); + EXPECT_TRUE(map.remove(&c)); + EXPECT_TRUE(map.is_empty()); +} + +TEST(map, ConstKeysAndValues) +{ + Map<const std::string, const std::string> map; + map.reserve(10); + map.add("45", "643"); + EXPECT_TRUE(map.contains("45")); + EXPECT_FALSE(map.contains("54")); +} + +TEST(map, ForeachItem) +{ + Map<int, int> map; + map.add(3, 4); + map.add(1, 8); + + Vector<int> keys; + Vector<int> values; + map.foreach_item([&](int key, int value) { + keys.append(key); + values.append(value); + }); + + EXPECT_EQ(keys.size(), 2); + EXPECT_EQ(values.size(), 2); + EXPECT_EQ(keys.first_index_of(3), values.first_index_of(4)); + EXPECT_EQ(keys.first_index_of(1), values.first_index_of(8)); +} + +/** + * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. + */ +#if 0 +template<typename MapT> +BLI_NOINLINE void benchmark_random_ints(StringRef name, int amount, int factor) +{ + RNG *rng = BLI_rng_new(0); + Vector<int> values; + for (int i = 0; i < amount; i++) { + values.append(BLI_rng_get_int(rng) * factor); + } + BLI_rng_free(rng); + + MapT map; + { + SCOPED_TIMER(name + " Add"); + for (int value : values) { + map.add(value, value); + } + } + int count = 0; + { + SCOPED_TIMER(name + " Contains"); + for (int value : values) { + count += map.contains(value); + } + } + { + SCOPED_TIMER(name + " Remove"); + for (int value : values) { + count += map.remove(value); + } + } + + /* Print the value for simple error checking and to avoid some compiler optimizations. */ + std::cout << "Count: " << count << "\n"; +} + +TEST(map, Benchmark) +{ + for (int i = 0; i < 3; i++) { + benchmark_random_ints<blender::Map<int, int>>("blender::Map ", 1000000, 1); + benchmark_random_ints<blender::StdUnorderedMapWrapper<int, int>>("std::unordered_map", 1000000, 1); + } + std::cout << "\n"; + for (int i = 0; i < 3; i++) { + uint32_t factor = (3 << 10); + benchmark_random_ints<blender::Map<int, int>>("blender::Map ", 1000000, factor); + benchmark_random_ints<blender::StdUnorderedMapWrapper<int, int>>( + "std::unordered_map", 1000000, factor); + } +} + +/** + * Timer 'blender::Map Add' took 61.7616 ms + * Timer 'blender::Map Contains' took 18.4989 ms + * Timer 'blender::Map Remove' took 20.5864 ms + * Count: 1999755 + * Timer 'std::unordered_map Add' took 188.674 ms + * Timer 'std::unordered_map Contains' took 44.3741 ms + * Timer 'std::unordered_map Remove' took 169.52 ms + * Count: 1999755 + * Timer 'blender::Map Add' took 37.9196 ms + * Timer 'blender::Map Contains' took 16.7361 ms + * Timer 'blender::Map Remove' took 20.9568 ms + * Count: 1999755 + * Timer 'std::unordered_map Add' took 166.09 ms + * Timer 'std::unordered_map Contains' took 40.6133 ms + * Timer 'std::unordered_map Remove' took 142.85 ms + * Count: 1999755 + * Timer 'blender::Map Add' took 37.3053 ms + * Timer 'blender::Map Contains' took 16.6731 ms + * Timer 'blender::Map Remove' took 18.8304 ms + * Count: 1999755 + * Timer 'std::unordered_map Add' took 170.964 ms + * Timer 'std::unordered_map Contains' took 38.1824 ms + * Timer 'std::unordered_map Remove' took 140.263 ms + * Count: 1999755 + * + * Timer 'blender::Map Add' took 50.1131 ms + * Timer 'blender::Map Contains' took 25.0491 ms + * Timer 'blender::Map Remove' took 32.4225 ms + * Count: 1889920 + * Timer 'std::unordered_map Add' took 150.129 ms + * Timer 'std::unordered_map Contains' took 34.6999 ms + * Timer 'std::unordered_map Remove' took 120.907 ms + * Count: 1889920 + * Timer 'blender::Map Add' took 50.4438 ms + * Timer 'blender::Map Contains' took 25.2677 ms + * Timer 'blender::Map Remove' took 32.3047 ms + * Count: 1889920 + * Timer 'std::unordered_map Add' took 144.015 ms + * Timer 'std::unordered_map Contains' took 36.3387 ms + * Timer 'std::unordered_map Remove' took 119.109 ms + * Count: 1889920 + * Timer 'blender::Map Add' took 48.6995 ms + * Timer 'blender::Map Contains' took 25.1846 ms + * Timer 'blender::Map Remove' took 33.0283 ms + * Count: 1889920 + * Timer 'std::unordered_map Add' took 143.494 ms + * Timer 'std::unordered_map Contains' took 34.8905 ms + * Timer 'std::unordered_map Remove' took 122.739 ms + * Count: 1889920 + */ + +#endif /* Benchmark */ + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_math_base_safe_test.cc b/source/blender/blenlib/tests/BLI_math_base_safe_test.cc new file mode 100644 index 00000000000..2e3e083cf92 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_math_base_safe_test.cc @@ -0,0 +1,37 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_math_base_safe.h" + +TEST(math_base, SafePowf) +{ + EXPECT_FLOAT_EQ(safe_powf(4.0f, 3.0f), 64.0f); + EXPECT_FLOAT_EQ(safe_powf(3.2f, 5.6f), 674.2793796f); + EXPECT_FLOAT_EQ(safe_powf(4.0f, -2.0f), 0.0625f); + EXPECT_FLOAT_EQ(safe_powf(6.0f, -3.2f), 0.003235311f); + EXPECT_FLOAT_EQ(safe_powf(-4.0f, 6), 4096.0f); + EXPECT_FLOAT_EQ(safe_powf(-3.0f, 5.5), 0.0f); + EXPECT_FLOAT_EQ(safe_powf(-2.5f, -4.0f), 0.0256f); + EXPECT_FLOAT_EQ(safe_powf(-3.7f, -4.5f), 0.0f); +} + +TEST(math_base, SafeModf) +{ + EXPECT_FLOAT_EQ(safe_modf(3.4, 2.2f), 1.2f); + EXPECT_FLOAT_EQ(safe_modf(3.4, -2.2f), 1.2f); + EXPECT_FLOAT_EQ(safe_modf(-3.4, -2.2f), -1.2f); + EXPECT_FLOAT_EQ(safe_modf(-3.4, 0.0f), 0.0f); + EXPECT_FLOAT_EQ(safe_modf(0.0f, 3.0f), 0.0f); + EXPECT_FLOAT_EQ(safe_modf(55.0f, 10.0f), 5.0f); +} + +TEST(math_base, SafeLogf) +{ + EXPECT_FLOAT_EQ(safe_logf(3.3f, 2.5f), 1.302995247f); + EXPECT_FLOAT_EQ(safe_logf(0.0f, 3.0f), 0.0f); + EXPECT_FLOAT_EQ(safe_logf(3.0f, 0.0f), 0.0f); + EXPECT_FLOAT_EQ(safe_logf(-2.0f, 4.3f), 0.0f); + EXPECT_FLOAT_EQ(safe_logf(2.0f, -4.3f), 0.0f); + EXPECT_FLOAT_EQ(safe_logf(-2.0f, -4.3f), 0.0f); +} diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc new file mode 100644 index 00000000000..f3cb02b63d7 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -0,0 +1,159 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_float3.hh" +#include "BLI_memory_utils.hh" +#include "BLI_strict_flags.h" +#include "testing/testing.h" + +namespace blender::tests { + +struct MyValue { + static inline int alive = 0; + + MyValue() + { + if (alive == 15) { + throw std::exception(); + } + + alive++; + } + + MyValue(const MyValue &UNUSED(other)) + { + if (alive == 15) { + throw std::exception(); + } + + alive++; + } + + ~MyValue() + { + alive--; + } +}; + +TEST(memory_utils, DefaultConstructN_ActuallyCallsConstructor) +{ + constexpr int amount = 10; + TypedBuffer<MyValue, amount> buffer; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, DefaultConstructN_StrongExceptionSafety) +{ + constexpr int amount = 20; + TypedBuffer<MyValue, amount> buffer; + + EXPECT_EQ(MyValue::alive, 0); + EXPECT_THROW(default_construct_n(buffer.ptr(), amount), std::exception); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedCopyN_ActuallyCopies) +{ + constexpr int amount = 5; + TypedBuffer<MyValue, amount> buffer1; + TypedBuffer<MyValue, amount> buffer2; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + uninitialized_copy_n(buffer1.ptr(), amount, buffer2.ptr()); + EXPECT_EQ(MyValue::alive, 2 * amount); + destruct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer2.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedCopyN_StrongExceptionSafety) +{ + constexpr int amount = 10; + TypedBuffer<MyValue, amount> buffer1; + TypedBuffer<MyValue, amount> buffer2; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + EXPECT_THROW(uninitialized_copy_n(buffer1.ptr(), amount, buffer2.ptr()), std::exception); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedFillN_ActuallyCopies) +{ + constexpr int amount = 10; + TypedBuffer<MyValue, amount> buffer; + + EXPECT_EQ(MyValue::alive, 0); + { + MyValue value; + EXPECT_EQ(MyValue::alive, 1); + uninitialized_fill_n(buffer.ptr(), amount, value); + EXPECT_EQ(MyValue::alive, 1 + amount); + destruct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, 1); + } + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedFillN_StrongExceptionSafety) +{ + constexpr int amount = 20; + TypedBuffer<MyValue, amount> buffer; + + EXPECT_EQ(MyValue::alive, 0); + { + MyValue value; + EXPECT_EQ(MyValue::alive, 1); + EXPECT_THROW(uninitialized_fill_n(buffer.ptr(), amount, value), std::exception); + EXPECT_EQ(MyValue::alive, 1); + } + EXPECT_EQ(MyValue::alive, 0); +} + +class TestBaseClass { + virtual void mymethod(){}; +}; + +class TestChildClass : public TestBaseClass { + void mymethod() override + { + } +}; + +static_assert(is_convertible_pointer_v<int *, int *>); +static_assert(is_convertible_pointer_v<int *, const int *>); +static_assert(is_convertible_pointer_v<int *, int *const>); +static_assert(is_convertible_pointer_v<int *, const int *const>); +static_assert(!is_convertible_pointer_v<const int *, int *>); +static_assert(!is_convertible_pointer_v<int, int *>); +static_assert(!is_convertible_pointer_v<int *, int>); +static_assert(is_convertible_pointer_v<TestBaseClass *, const TestBaseClass *>); +static_assert(!is_convertible_pointer_v<const TestBaseClass *, TestBaseClass *>); +static_assert(is_convertible_pointer_v<TestChildClass *, TestBaseClass *>); +static_assert(!is_convertible_pointer_v<TestBaseClass *, TestChildClass *>); +static_assert(is_convertible_pointer_v<const TestChildClass *, const TestBaseClass *>); +static_assert(!is_convertible_pointer_v<TestBaseClass, const TestChildClass *>); +static_assert(!is_convertible_pointer_v<float3, float *>); +static_assert(!is_convertible_pointer_v<float *, float3>); +static_assert(!is_convertible_pointer_v<int **, int *>); +static_assert(!is_convertible_pointer_v<int *, int **>); +static_assert(is_convertible_pointer_v<int **, int **>); +static_assert(is_convertible_pointer_v<const int **, const int **>); +static_assert(!is_convertible_pointer_v<const int **, int **>); +static_assert(!is_convertible_pointer_v<int *const *, int **>); +static_assert(!is_convertible_pointer_v<int *const *const, int **>); +static_assert(is_convertible_pointer_v<int **, int **const>); +static_assert(is_convertible_pointer_v<int **, int *const *>); +static_assert(is_convertible_pointer_v<int **, int const *const *>); + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_multi_value_map_test.cc b/source/blender/blenlib/tests/BLI_multi_value_map_test.cc new file mode 100644 index 00000000000..7501fbe0d87 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_multi_value_map_test.cc @@ -0,0 +1,109 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_multi_value_map.hh" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(multi_value_map, LookupNotExistant) +{ + MultiValueMap<int, int> map; + EXPECT_EQ(map.lookup(5).size(), 0); + map.add(2, 5); + EXPECT_EQ(map.lookup(5).size(), 0); +} + +TEST(multi_value_map, LookupExistant) +{ + MultiValueMap<int, int> map; + map.add(2, 4); + map.add(2, 5); + map.add(3, 6); + + EXPECT_EQ(map.lookup(2).size(), 2); + EXPECT_EQ(map.lookup(2)[0], 4); + EXPECT_EQ(map.lookup(2)[1], 5); + + EXPECT_EQ(map.lookup(3).size(), 1); + EXPECT_EQ(map.lookup(3)[0], 6); +} + +TEST(multi_value_map, AddMultiple) +{ + MultiValueMap<int, int> map; + map.add_multiple(2, {4, 5, 6}); + map.add_multiple(2, {1, 2}); + map.add_multiple(5, {7, 5, 3}); + + EXPECT_EQ(map.lookup(2).size(), 5); + EXPECT_EQ(map.lookup(2)[0], 4); + EXPECT_EQ(map.lookup(2)[1], 5); + EXPECT_EQ(map.lookup(2)[2], 6); + EXPECT_EQ(map.lookup(2)[3], 1); + EXPECT_EQ(map.lookup(2)[4], 2); + + EXPECT_EQ(map.lookup(5).size(), 3); + EXPECT_EQ(map.lookup(5)[0], 7); + EXPECT_EQ(map.lookup(5)[1], 5); + EXPECT_EQ(map.lookup(5)[2], 3); +} + +TEST(multi_value_map, Keys) +{ + MultiValueMap<int, int> map; + map.add(5, 7); + map.add(5, 7); + map.add_multiple(2, {6, 7, 8}); + + Vector<int> keys; + for (int key : map.keys()) { + keys.append(key); + } + + EXPECT_EQ(keys.size(), 2); + EXPECT_TRUE(keys.contains(5)); + EXPECT_TRUE(keys.contains(2)); +} + +TEST(multi_value_map, Values) +{ + MultiValueMap<int, int> map; + map.add(3, 5); + map.add_multiple(3, {1, 2}); + map.add(6, 1); + + Vector<Span<int>> values; + for (Span<int> value_span : map.values()) { + values.append(value_span); + } + + EXPECT_EQ(values.size(), 2); +} + +TEST(multi_value_map, Items) +{ + MultiValueMap<int, int> map; + map.add_multiple(4, {1, 2, 3}); + + for (auto &&item : map.items()) { + int key = item.key; + Span<int> values = item.value; + EXPECT_EQ(key, 4); + EXPECT_EQ(values.size(), 3); + EXPECT_EQ(values[0], 1); + EXPECT_EQ(values[1], 2); + EXPECT_EQ(values[2], 3); + } +} + +TEST(multi_value_map, UniquePtr) +{ + /* Mostly testing if it compiles here. */ + MultiValueMap<std::unique_ptr<int>, std::unique_ptr<int>> map; + map.add(std::make_unique<int>(4), std::make_unique<int>(6)); + map.add(std::make_unique<int>(4), std::make_unique<int>(7)); + EXPECT_EQ(map.lookup(std::make_unique<int>(10)).size(), 0); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc new file mode 100644 index 00000000000..7bd0b258df8 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -0,0 +1,565 @@ +/* Apache License, Version 2.0 */ + +#include <set> +#include <unordered_set> + +#include "BLI_ghash.h" +#include "BLI_rand.h" +#include "BLI_set.hh" +#include "BLI_strict_flags.h" +#include "BLI_timeit.hh" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender { +namespace tests { + +TEST(set, DefaultConstructor) +{ + Set<int> set; + EXPECT_EQ(set.size(), 0); + EXPECT_TRUE(set.is_empty()); +} + +TEST(set, ContainsNotExistant) +{ + Set<int> set; + EXPECT_FALSE(set.contains(3)); +} + +TEST(set, ContainsExistant) +{ + Set<int> set; + EXPECT_FALSE(set.contains(5)); + EXPECT_TRUE(set.is_empty()); + set.add(5); + EXPECT_TRUE(set.contains(5)); + EXPECT_FALSE(set.is_empty()); +} + +TEST(set, AddMany) +{ + Set<int> set; + for (int i = 0; i < 100; i++) { + set.add(i); + } + + for (int i = 50; i < 100; i++) { + EXPECT_TRUE(set.contains(i)); + } + for (int i = 100; i < 150; i++) { + EXPECT_FALSE(set.contains(i)); + } +} + +TEST(set, InitializerListConstructor) +{ + Set<int> set = {4, 5, 6}; + EXPECT_EQ(set.size(), 3); + EXPECT_TRUE(set.contains(4)); + EXPECT_TRUE(set.contains(5)); + EXPECT_TRUE(set.contains(6)); + EXPECT_FALSE(set.contains(2)); + EXPECT_FALSE(set.contains(3)); +} + +TEST(set, CopyConstructor) +{ + Set<int> set = {3}; + EXPECT_TRUE(set.contains(3)); + EXPECT_FALSE(set.contains(4)); + + Set<int> set2(set); + set2.add(4); + EXPECT_TRUE(set2.contains(3)); + EXPECT_TRUE(set2.contains(4)); + + EXPECT_FALSE(set.contains(4)); +} + +TEST(set, MoveConstructor) +{ + Set<int> set = {1, 2, 3}; + EXPECT_EQ(set.size(), 3); + Set<int> set2(std::move(set)); + EXPECT_EQ(set.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(set2.size(), 3); +} + +TEST(set, CopyAssignment) +{ + Set<int> set = {3}; + EXPECT_TRUE(set.contains(3)); + EXPECT_FALSE(set.contains(4)); + + Set<int> set2; + set2 = set; + set2.add(4); + EXPECT_TRUE(set2.contains(3)); + EXPECT_TRUE(set2.contains(4)); + + EXPECT_FALSE(set.contains(4)); +} + +TEST(set, MoveAssignment) +{ + Set<int> set = {1, 2, 3}; + EXPECT_EQ(set.size(), 3); + Set<int> set2; + set2 = std::move(set); + EXPECT_EQ(set.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(set2.size(), 3); +} + +TEST(set, RemoveContained) +{ + Set<int> set = {3, 4, 5}; + EXPECT_TRUE(set.contains(3)); + EXPECT_TRUE(set.contains(4)); + EXPECT_TRUE(set.contains(5)); + set.remove_contained(4); + EXPECT_TRUE(set.contains(3)); + EXPECT_FALSE(set.contains(4)); + EXPECT_TRUE(set.contains(5)); + set.remove_contained(3); + EXPECT_FALSE(set.contains(3)); + EXPECT_FALSE(set.contains(4)); + EXPECT_TRUE(set.contains(5)); + set.remove_contained(5); + EXPECT_FALSE(set.contains(3)); + EXPECT_FALSE(set.contains(4)); + EXPECT_FALSE(set.contains(5)); +} + +TEST(set, RemoveContainedMany) +{ + Set<int> set; + for (int i = 0; i < 1000; i++) { + set.add(i); + } + for (int i = 100; i < 1000; i++) { + set.remove_contained(i); + } + for (int i = 900; i < 1000; i++) { + set.add(i); + } + + for (int i = 0; i < 1000; i++) { + if (i < 100 || i >= 900) { + EXPECT_TRUE(set.contains(i)); + } + else { + EXPECT_FALSE(set.contains(i)); + } + } +} + +TEST(set, Intersects) +{ + Set<int> a = {3, 4, 5, 6}; + Set<int> b = {1, 2, 5}; + EXPECT_TRUE(Set<int>::Intersects(a, b)); + EXPECT_FALSE(Set<int>::Disjoint(a, b)); +} + +TEST(set, Disjoint) +{ + Set<int> a = {5, 6, 7, 8}; + Set<int> b = {2, 3, 4, 9}; + EXPECT_FALSE(Set<int>::Intersects(a, b)); + EXPECT_TRUE(Set<int>::Disjoint(a, b)); +} + +TEST(set, AddMultiple) +{ + Set<int> a; + a.add_multiple({5, 7}); + EXPECT_TRUE(a.contains(5)); + EXPECT_TRUE(a.contains(7)); + EXPECT_FALSE(a.contains(4)); + a.add_multiple({2, 4, 7}); + EXPECT_TRUE(a.contains(4)); + EXPECT_TRUE(a.contains(2)); + EXPECT_EQ(a.size(), 4); +} + +TEST(set, AddMultipleNew) +{ + Set<int> a; + a.add_multiple_new({5, 6}); + EXPECT_TRUE(a.contains(5)); + EXPECT_TRUE(a.contains(6)); +} + +TEST(set, Iterator) +{ + Set<int> set = {1, 3, 2, 5, 4}; + blender::Vector<int> vec; + for (int value : set) { + vec.append(value); + } + EXPECT_EQ(vec.size(), 5); + EXPECT_TRUE(vec.contains(1)); + EXPECT_TRUE(vec.contains(3)); + EXPECT_TRUE(vec.contains(2)); + EXPECT_TRUE(vec.contains(5)); + EXPECT_TRUE(vec.contains(4)); +} + +TEST(set, OftenAddRemoveContained) +{ + Set<int> set; + for (int i = 0; i < 100; i++) { + set.add(42); + EXPECT_EQ(set.size(), 1); + set.remove_contained(42); + EXPECT_EQ(set.size(), 0); + } +} + +TEST(set, UniquePtrValues) +{ + Set<std::unique_ptr<int>> set; + set.add_new(std::unique_ptr<int>(new int())); + auto value1 = std::unique_ptr<int>(new int()); + set.add_new(std::move(value1)); + set.add(std::unique_ptr<int>(new int())); + + EXPECT_EQ(set.size(), 3); +} + +TEST(set, Clear) +{ + Set<int> set = {3, 4, 6, 7}; + EXPECT_EQ(set.size(), 4); + set.clear(); + EXPECT_EQ(set.size(), 0); +} + +TEST(set, StringSet) +{ + Set<std::string> set; + set.add("hello"); + set.add("world"); + EXPECT_EQ(set.size(), 2); + EXPECT_TRUE(set.contains("hello")); + EXPECT_TRUE(set.contains("world")); + EXPECT_FALSE(set.contains("world2")); +} + +TEST(set, PointerSet) +{ + int a, b, c; + Set<int *> set; + set.add(&a); + set.add(&b); + EXPECT_EQ(set.size(), 2); + EXPECT_TRUE(set.contains(&a)); + EXPECT_TRUE(set.contains(&b)); + EXPECT_FALSE(set.contains(&c)); +} + +TEST(set, Remove) +{ + Set<int> set = {1, 2, 3, 4, 5, 6}; + EXPECT_EQ(set.size(), 6); + EXPECT_TRUE(set.remove(2)); + EXPECT_EQ(set.size(), 5); + EXPECT_FALSE(set.contains(2)); + EXPECT_FALSE(set.remove(2)); + EXPECT_EQ(set.size(), 5); + EXPECT_TRUE(set.remove(5)); + EXPECT_EQ(set.size(), 4); +} + +struct Type1 { + uint32_t value; +}; + +struct Type2 { + uint32_t value; +}; + +static bool operator==(const Type1 &a, const Type1 &b) +{ + return a.value == b.value; +} +static bool operator==(const Type2 &a, const Type1 &b) +{ + return a.value == b.value; +} + +} // namespace tests + +/* This has to be defined in ::blender namespace. */ +template<> struct DefaultHash<tests::Type1> { + uint32_t operator()(const tests::Type1 &value) const + { + return value.value; + } + + uint32_t operator()(const tests::Type2 &value) const + { + return value.value; + } +}; + +namespace tests { + +TEST(set, ContainsAs) +{ + Set<Type1> set; + set.add(Type1{5}); + EXPECT_TRUE(set.contains_as(Type1{5})); + EXPECT_TRUE(set.contains_as(Type2{5})); + EXPECT_FALSE(set.contains_as(Type1{6})); + EXPECT_FALSE(set.contains_as(Type2{6})); +} + +TEST(set, ContainsAsString) +{ + Set<std::string> set; + set.add("test"); + EXPECT_TRUE(set.contains_as("test")); + EXPECT_TRUE(set.contains_as(StringRef("test"))); + EXPECT_FALSE(set.contains_as("string")); + EXPECT_FALSE(set.contains_as(StringRef("string"))); +} + +TEST(set, RemoveContainedAs) +{ + Set<Type1> set; + set.add(Type1{5}); + EXPECT_TRUE(set.contains_as(Type2{5})); + set.remove_contained_as(Type2{5}); + EXPECT_FALSE(set.contains_as(Type2{5})); +} + +TEST(set, RemoveAs) +{ + Set<Type1> set; + set.add(Type1{5}); + EXPECT_TRUE(set.contains_as(Type2{5})); + set.remove_as(Type2{6}); + EXPECT_TRUE(set.contains_as(Type2{5})); + set.remove_as(Type2{5}); + EXPECT_FALSE(set.contains_as(Type2{5})); + set.remove_as(Type2{5}); + EXPECT_FALSE(set.contains_as(Type2{5})); +} + +TEST(set, AddAs) +{ + Set<std::string> set; + EXPECT_TRUE(set.add_as("test")); + EXPECT_TRUE(set.add_as(StringRef("qwe"))); + EXPECT_FALSE(set.add_as(StringRef("test"))); + EXPECT_FALSE(set.add_as("qwe")); +} + +template<uint N> struct EqualityIntModN { + bool operator()(uint a, uint b) const + { + return (a % N) == (b % N); + } +}; + +template<uint N> struct HashIntModN { + uint64_t operator()(uint value) const + { + return value % N; + } +}; + +TEST(set, CustomizeHashAndEquality) +{ + Set<uint, 0, DefaultProbingStrategy, HashIntModN<10>, EqualityIntModN<10>> set; + set.add(4); + EXPECT_TRUE(set.contains(4)); + EXPECT_TRUE(set.contains(14)); + EXPECT_TRUE(set.contains(104)); + EXPECT_FALSE(set.contains(5)); + set.add(55); + EXPECT_TRUE(set.contains(5)); + EXPECT_TRUE(set.contains(14)); + set.remove(1004); + EXPECT_FALSE(set.contains(14)); +} + +TEST(set, IntrusiveIntKey) +{ + Set<int, + 2, + DefaultProbingStrategy, + DefaultHash<int>, + DefaultEquality, + IntegerSetSlot<int, 100, 200>> + set; + EXPECT_TRUE(set.add(4)); + EXPECT_TRUE(set.add(3)); + EXPECT_TRUE(set.add(11)); + EXPECT_TRUE(set.add(8)); + EXPECT_FALSE(set.add(3)); + EXPECT_FALSE(set.add(4)); + EXPECT_TRUE(set.remove(4)); + EXPECT_FALSE(set.remove(7)); + EXPECT_TRUE(set.add(4)); + EXPECT_TRUE(set.remove(4)); +} + +struct MyKeyType { + uint32_t key; + int32_t attached_data; + + uint64_t hash() const + { + return key; + } + + friend bool operator==(const MyKeyType &a, const MyKeyType &b) + { + return a.key == b.key; + } +}; + +TEST(set, LookupKey) +{ + Set<MyKeyType> set; + set.add({1, 10}); + set.add({2, 20}); + EXPECT_EQ(set.lookup_key({1, 30}).attached_data, 10); + EXPECT_EQ(set.lookup_key({2, 0}).attached_data, 20); +} + +TEST(set, LookupKeyDefault) +{ + Set<MyKeyType> set; + set.add({1, 10}); + set.add({2, 20}); + + MyKeyType fallback{5, 50}; + EXPECT_EQ(set.lookup_key_default({1, 66}, fallback).attached_data, 10); + EXPECT_EQ(set.lookup_key_default({4, 40}, fallback).attached_data, 50); +} + +TEST(set, LookupKeyPtr) +{ + Set<MyKeyType> set; + set.add({1, 10}); + set.add({2, 20}); + EXPECT_EQ(set.lookup_key_ptr({1, 50})->attached_data, 10); + EXPECT_EQ(set.lookup_key_ptr({2, 50})->attached_data, 20); + EXPECT_EQ(set.lookup_key_ptr({3, 50}), nullptr); +} + +/** + * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. + */ +#if 0 +template<typename SetT> +BLI_NOINLINE void benchmark_random_ints(StringRef name, int amount, int factor) +{ + RNG *rng = BLI_rng_new(0); + Vector<int> values; + for (int i = 0; i < amount; i++) { + values.append(BLI_rng_get_int(rng) * factor); + } + BLI_rng_free(rng); + + SetT set; + { + SCOPED_TIMER(name + " Add"); + for (int value : values) { + set.add(value); + } + } + int count = 0; + { + SCOPED_TIMER(name + " Contains"); + for (int value : values) { + count += set.contains(value); + } + } + { + SCOPED_TIMER(name + " Remove"); + for (int value : values) { + count += set.remove(value); + } + } + + /* Print the value for simple error checking and to avoid some compiler optimizations. */ + std::cout << "Count: " << count << "\n"; +} + +TEST(set, Benchmark) +{ + for (int i = 0; i < 3; i++) { + benchmark_random_ints<blender::Set<int>>("blender::Set ", 100000, 1); + benchmark_random_ints<blender::StdUnorderedSetWrapper<int>>("std::unordered_set", 100000, 1); + } + std::cout << "\n"; + for (int i = 0; i < 3; i++) { + uint32_t factor = (3 << 10); + benchmark_random_ints<blender::Set<int>>("blender::Set ", 100000, factor); + benchmark_random_ints<blender::StdUnorderedSetWrapper<int>>("std::unordered_set", 100000, factor); + } +} + +/** + * Output of the rudimentary benchmark above on my hardware. + * + * Timer 'blender::Set Add' took 5.5573 ms + * Timer 'blender::Set Contains' took 0.807384 ms + * Timer 'blender::Set Remove' took 0.953436 ms + * Count: 199998 + * Timer 'std::unordered_set Add' took 12.551 ms + * Timer 'std::unordered_set Contains' took 2.3323 ms + * Timer 'std::unordered_set Remove' took 5.07082 ms + * Count: 199998 + * Timer 'blender::Set Add' took 2.62526 ms + * Timer 'blender::Set Contains' took 0.407499 ms + * Timer 'blender::Set Remove' took 0.472981 ms + * Count: 199998 + * Timer 'std::unordered_set Add' took 6.26945 ms + * Timer 'std::unordered_set Contains' took 1.17236 ms + * Timer 'std::unordered_set Remove' took 3.77402 ms + * Count: 199998 + * Timer 'blender::Set Add' took 2.59152 ms + * Timer 'blender::Set Contains' took 0.415254 ms + * Timer 'blender::Set Remove' took 0.477559 ms + * Count: 199998 + * Timer 'std::unordered_set Add' took 6.28129 ms + * Timer 'std::unordered_set Contains' took 1.17562 ms + * Timer 'std::unordered_set Remove' took 3.77811 ms + * Count: 199998 + * + * Timer 'blender::Set Add' took 3.16514 ms + * Timer 'blender::Set Contains' took 0.732895 ms + * Timer 'blender::Set Remove' took 1.08171 ms + * Count: 198790 + * Timer 'std::unordered_set Add' took 6.57377 ms + * Timer 'std::unordered_set Contains' took 1.17008 ms + * Timer 'std::unordered_set Remove' took 3.7946 ms + * Count: 198790 + * Timer 'blender::Set Add' took 3.11439 ms + * Timer 'blender::Set Contains' took 0.740159 ms + * Timer 'blender::Set Remove' took 1.06749 ms + * Count: 198790 + * Timer 'std::unordered_set Add' took 6.35597 ms + * Timer 'std::unordered_set Contains' took 1.17713 ms + * Timer 'std::unordered_set Remove' took 3.77826 ms + * Count: 198790 + * Timer 'blender::Set Add' took 3.09876 ms + * Timer 'blender::Set Contains' took 0.742072 ms + * Timer 'blender::Set Remove' took 1.06622 ms + * Count: 198790 + * Timer 'std::unordered_set Add' took 6.4469 ms + * Timer 'std::unordered_set Contains' took 1.16515 ms + * Timer 'std::unordered_set Remove' took 3.80639 ms + * Count: 198790 + */ + +#endif /* Benchmark */ + +} // namespace tests +} // namespace blender diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc new file mode 100644 index 00000000000..587497624f4 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -0,0 +1,311 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_span.hh" +#include "BLI_strict_flags.h" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(span, FromSmallVector) +{ + Vector<int> a = {1, 2, 3}; + Span<int> a_span = a; + EXPECT_EQ(a_span.size(), 3); + EXPECT_EQ(a_span[0], 1); + EXPECT_EQ(a_span[1], 2); + EXPECT_EQ(a_span[2], 3); +} + +TEST(span, AddConstToPointer) +{ + int a = 0; + std::vector<int *> vec = {&a}; + Span<int *> span = vec; + Span<const int *> const_span = span; + EXPECT_EQ(const_span.size(), 1); +} + +TEST(span, IsReferencing) +{ + int array[] = {3, 5, 8}; + MutableSpan<int> span(array, ARRAY_SIZE(array)); + EXPECT_EQ(span.size(), 3); + EXPECT_EQ(span[1], 5); + array[1] = 10; + EXPECT_EQ(span[1], 10); +} + +TEST(span, DropBack) +{ + Vector<int> a = {4, 5, 6, 7}; + auto slice = Span<int>(a).drop_back(2); + EXPECT_EQ(slice.size(), 2); + EXPECT_EQ(slice[0], 4); + EXPECT_EQ(slice[1], 5); +} + +TEST(span, DropBackAll) +{ + Vector<int> a = {4, 5, 6, 7}; + auto slice = Span<int>(a).drop_back(a.size()); + EXPECT_EQ(slice.size(), 0); +} + +TEST(span, DropFront) +{ + Vector<int> a = {4, 5, 6, 7}; + auto slice = Span<int>(a).drop_front(1); + EXPECT_EQ(slice.size(), 3); + EXPECT_EQ(slice[0], 5); + EXPECT_EQ(slice[1], 6); + EXPECT_EQ(slice[2], 7); +} + +TEST(span, DropFrontAll) +{ + Vector<int> a = {4, 5, 6, 7}; + auto slice = Span<int>(a).drop_front(a.size()); + EXPECT_EQ(slice.size(), 0); +} + +TEST(span, TakeFront) +{ + Vector<int> a = {4, 5, 6, 7}; + auto slice = Span<int>(a).take_front(2); + EXPECT_EQ(slice.size(), 2); + EXPECT_EQ(slice[0], 4); + EXPECT_EQ(slice[1], 5); +} + +TEST(span, TakeBack) +{ + Vector<int> a = {5, 6, 7, 8}; + auto slice = Span<int>(a).take_back(2); + EXPECT_EQ(slice.size(), 2); + EXPECT_EQ(slice[0], 7); + EXPECT_EQ(slice[1], 8); +} + +TEST(span, Slice) +{ + Vector<int> a = {4, 5, 6, 7}; + auto slice = Span<int>(a).slice(1, 2); + EXPECT_EQ(slice.size(), 2); + EXPECT_EQ(slice[0], 5); + EXPECT_EQ(slice[1], 6); +} + +TEST(span, SliceEmpty) +{ + Vector<int> a = {4, 5, 6, 7}; + auto slice = Span<int>(a).slice(2, 0); + EXPECT_EQ(slice.size(), 0); +} + +TEST(span, SliceRange) +{ + Vector<int> a = {1, 2, 3, 4, 5}; + auto slice = Span<int>(a).slice(IndexRange(2, 2)); + EXPECT_EQ(slice.size(), 2); + EXPECT_EQ(slice[0], 3); + EXPECT_EQ(slice[1], 4); +} + +TEST(span, Contains) +{ + Vector<int> a = {4, 5, 6, 7}; + Span<int> a_span = a; + EXPECT_TRUE(a_span.contains(4)); + EXPECT_TRUE(a_span.contains(5)); + EXPECT_TRUE(a_span.contains(6)); + EXPECT_TRUE(a_span.contains(7)); + EXPECT_FALSE(a_span.contains(3)); + EXPECT_FALSE(a_span.contains(8)); +} + +TEST(span, Count) +{ + Vector<int> a = {2, 3, 4, 3, 3, 2, 2, 2, 2}; + Span<int> a_span = a; + EXPECT_EQ(a_span.count(1), 0); + EXPECT_EQ(a_span.count(2), 5); + EXPECT_EQ(a_span.count(3), 3); + EXPECT_EQ(a_span.count(4), 1); + EXPECT_EQ(a_span.count(5), 0); +} + +static void test_ref_from_initializer_list(Span<int> span) +{ + EXPECT_EQ(span.size(), 4); + EXPECT_EQ(span[0], 3); + EXPECT_EQ(span[1], 6); + EXPECT_EQ(span[2], 8); + EXPECT_EQ(span[3], 9); +} + +TEST(span, FromInitializerList) +{ + test_ref_from_initializer_list({3, 6, 8, 9}); +} + +TEST(span, FromVector) +{ + std::vector<int> a = {1, 2, 3, 4}; + Span<int> a_span(a); + EXPECT_EQ(a_span.size(), 4); + EXPECT_EQ(a_span[0], 1); + EXPECT_EQ(a_span[1], 2); + EXPECT_EQ(a_span[2], 3); + EXPECT_EQ(a_span[3], 4); +} + +TEST(span, FromArray) +{ + std::array<int, 2> a = {5, 6}; + Span<int> a_span(a); + EXPECT_EQ(a_span.size(), 2); + EXPECT_EQ(a_span[0], 5); + EXPECT_EQ(a_span[1], 6); +} + +TEST(span, Fill) +{ + std::array<int, 5> a = {4, 5, 6, 7, 8}; + MutableSpan<int> a_span(a); + a_span.fill(1); + EXPECT_EQ(a[0], 1); + EXPECT_EQ(a[1], 1); + EXPECT_EQ(a[2], 1); + EXPECT_EQ(a[3], 1); + EXPECT_EQ(a[4], 1); +} + +TEST(span, FillIndices) +{ + std::array<int, 5> a = {0, 0, 0, 0, 0}; + MutableSpan<int> a_span(a); + a_span.fill_indices({0, 2, 3}, 1); + EXPECT_EQ(a[0], 1); + EXPECT_EQ(a[1], 0); + EXPECT_EQ(a[2], 1); + EXPECT_EQ(a[3], 1); + EXPECT_EQ(a[4], 0); +} + +TEST(span, SizeInBytes) +{ + std::array<int, 10> a; + Span<int> a_span(a); + EXPECT_EQ(a_span.size_in_bytes(), (int64_t)sizeof(a)); + EXPECT_EQ(a_span.size_in_bytes(), 40); +} + +TEST(span, FirstLast) +{ + std::array<int, 4> a = {6, 7, 8, 9}; + Span<int> a_span(a); + EXPECT_EQ(a_span.first(), 6); + EXPECT_EQ(a_span.last(), 9); +} + +TEST(span, FirstLast_OneElement) +{ + int a = 3; + Span<int> a_span(&a, 1); + EXPECT_EQ(a_span.first(), 3); + EXPECT_EQ(a_span.last(), 3); +} + +TEST(span, Get) +{ + std::array<int, 3> a = {5, 6, 7}; + Span<int> a_span(a); + EXPECT_EQ(a_span.get(0, 42), 5); + EXPECT_EQ(a_span.get(1, 42), 6); + EXPECT_EQ(a_span.get(2, 42), 7); + EXPECT_EQ(a_span.get(3, 42), 42); + EXPECT_EQ(a_span.get(4, 42), 42); +} + +TEST(span, ContainsPtr) +{ + std::array<int, 3> a = {5, 6, 7}; + int other = 10; + Span<int> a_span(a); + EXPECT_TRUE(a_span.contains_ptr(&a[0] + 0)); + 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)); + EXPECT_FALSE(a_span.contains_ptr(&other)); +} + +TEST(span, FirstIndex) +{ + std::array<int, 5> a = {4, 5, 4, 2, 5}; + Span<int> a_span(a); + + EXPECT_EQ(a_span.first_index(4), 0); + EXPECT_EQ(a_span.first_index(5), 1); + EXPECT_EQ(a_span.first_index(2), 3); +} + +TEST(span, CastSameSize) +{ + int value = 0; + std::array<int *, 4> a = {&value, nullptr, nullptr, nullptr}; + Span<int *> a_span = a; + Span<float *> new_a_span = a_span.cast<float *>(); + + EXPECT_EQ(a_span.size(), 4); + EXPECT_EQ(new_a_span.size(), 4); + + EXPECT_EQ(a_span[0], &value); + EXPECT_EQ(new_a_span[0], (float *)&value); +} + +TEST(span, CastSmallerSize) +{ + std::array<uint32_t, 4> a = {3, 4, 5, 6}; + Span<uint32_t> a_span = a; + Span<uint16_t> new_a_span = a_span.cast<uint16_t>(); + + EXPECT_EQ(a_span.size(), 4); + EXPECT_EQ(new_a_span.size(), 8); +} + +TEST(span, CastLargerSize) +{ + std::array<uint16_t, 4> a = {4, 5, 6, 7}; + Span<uint16_t> a_span = a; + Span<uint32_t> new_a_span = a_span.cast<uint32_t>(); + + EXPECT_EQ(a_span.size(), 4); + EXPECT_EQ(new_a_span.size(), 2); +} + +TEST(span, VoidPointerSpan) +{ + int a; + float b; + double c; + + auto func1 = [](Span<void *> span) { EXPECT_EQ(span.size(), 3); }; + func1({&a, &b, &c}); +} + +TEST(span, CopyFrom) +{ + std::array<int, 4> src = {5, 6, 7, 8}; + std::array<int, 4> dst = {1, 2, 3, 4}; + + EXPECT_EQ(dst[2], 3); + MutableSpan(dst).copy_from(src); + EXPECT_EQ(dst[0], 5); + EXPECT_EQ(dst[1], 6); + EXPECT_EQ(dst[2], 7); + EXPECT_EQ(dst[3], 8); +} + +} // 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 new file mode 100644 index 00000000000..3572e751b88 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc @@ -0,0 +1,188 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_stack.hh" +#include "BLI_strict_flags.h" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(stack, DefaultConstructor) +{ + Stack<int> stack; + EXPECT_EQ(stack.size(), 0); + EXPECT_TRUE(stack.is_empty()); +} + +TEST(stack, SpanConstructor) +{ + std::array<int, 3> array = {4, 7, 2}; + Stack<int> stack(array); + EXPECT_EQ(stack.size(), 3); + EXPECT_EQ(stack.pop(), 2); + EXPECT_EQ(stack.pop(), 7); + EXPECT_EQ(stack.pop(), 4); + EXPECT_TRUE(stack.is_empty()); +} + +TEST(stack, CopyConstructor) +{ + Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7}; + Stack<int> stack2 = stack1; + EXPECT_EQ(stack1.size(), 7); + EXPECT_EQ(stack2.size(), 7); + for (int i = 7; i >= 1; i--) { + EXPECT_FALSE(stack1.is_empty()); + EXPECT_FALSE(stack2.is_empty()); + EXPECT_EQ(stack1.pop(), i); + EXPECT_EQ(stack2.pop(), i); + } + EXPECT_TRUE(stack1.is_empty()); + EXPECT_TRUE(stack2.is_empty()); +} + +TEST(stack, MoveConstructor) +{ + Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7}; + Stack<int> stack2 = std::move(stack1); + EXPECT_EQ(stack1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(stack2.size(), 7); + for (int i = 7; i >= 1; i--) { + EXPECT_EQ(stack2.pop(), i); + } +} + +TEST(stack, CopyAssignment) +{ + Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7}; + Stack<int> stack2 = {2, 3, 4, 5, 6, 7}; + stack2 = stack1; + + EXPECT_EQ(stack1.size(), 7); + EXPECT_EQ(stack2.size(), 7); + for (int i = 7; i >= 1; i--) { + EXPECT_FALSE(stack1.is_empty()); + EXPECT_FALSE(stack2.is_empty()); + EXPECT_EQ(stack1.pop(), i); + EXPECT_EQ(stack2.pop(), i); + } + EXPECT_TRUE(stack1.is_empty()); + EXPECT_TRUE(stack2.is_empty()); +} + +TEST(stack, MoveAssignment) +{ + Stack<int> stack1 = {1, 2, 3, 4, 5, 6, 7}; + Stack<int> stack2 = {5, 3, 7, 2, 2}; + stack2 = std::move(stack1); + EXPECT_EQ(stack1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(stack2.size(), 7); + for (int i = 7; i >= 1; i--) { + EXPECT_EQ(stack2.pop(), i); + } +} + +TEST(stack, Push) +{ + Stack<int> stack; + EXPECT_EQ(stack.size(), 0); + stack.push(3); + EXPECT_EQ(stack.size(), 1); + stack.push(5); + EXPECT_EQ(stack.size(), 2); +} + +TEST(stack, PushMultiple) +{ + Stack<int> stack; + EXPECT_EQ(stack.size(), 0); + stack.push_multiple({1, 2, 3}); + EXPECT_EQ(stack.size(), 3); + EXPECT_EQ(stack.pop(), 3); + EXPECT_EQ(stack.pop(), 2); + EXPECT_EQ(stack.pop(), 1); +} + +TEST(stack, PushPopMany) +{ + Stack<int> stack; + for (int i = 0; i < 1000; i++) { + stack.push(i); + EXPECT_EQ(stack.size(), static_cast<unsigned int>(i + 1)); + } + for (int i = 999; i > 50; i--) { + EXPECT_EQ(stack.pop(), i); + EXPECT_EQ(stack.size(), static_cast<unsigned int>(i)); + } + for (int i = 51; i < 5000; i++) { + stack.push(i); + EXPECT_EQ(stack.size(), static_cast<unsigned int>(i + 1)); + } + for (int i = 4999; i >= 0; i--) { + EXPECT_EQ(stack.pop(), i); + EXPECT_EQ(stack.size(), static_cast<unsigned int>(i)); + } +} + +TEST(stack, PushMultipleAfterPop) +{ + Stack<int> stack; + for (int i = 0; i < 1000; i++) { + stack.push(i); + } + for (int i = 999; i >= 0; i--) { + EXPECT_EQ(stack.pop(), i); + } + + Vector<int> values; + for (int i = 0; i < 5000; i++) { + values.append(i); + } + stack.push_multiple(values); + EXPECT_EQ(stack.size(), 5000); + + for (int i = 4999; i >= 0; i--) { + EXPECT_EQ(stack.pop(), i); + } +} + +TEST(stack, Pop) +{ + Stack<int> stack; + stack.push(4); + stack.push(6); + EXPECT_EQ(stack.pop(), 6); + EXPECT_EQ(stack.pop(), 4); +} + +TEST(stack, Peek) +{ + Stack<int> stack; + stack.push(3); + stack.push(4); + EXPECT_EQ(stack.peek(), 4); + EXPECT_EQ(stack.peek(), 4); + stack.pop(); + EXPECT_EQ(stack.peek(), 3); +} + +TEST(stack, UniquePtrValues) +{ + Stack<std::unique_ptr<int>> stack; + stack.push(std::unique_ptr<int>(new int())); + stack.push(std::unique_ptr<int>(new int())); + std::unique_ptr<int> a = stack.pop(); + std::unique_ptr<int> &b = stack.peek(); + UNUSED_VARS(a, b); +} + +TEST(stack, OveralignedValues) +{ + Stack<AlignedBuffer<1, 512>, 2> stack; + for (int i = 0; i < 100; i++) { + stack.push({}); + EXPECT_EQ((uintptr_t)&stack.peek() % 512, 0); + } +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc new file mode 100644 index 00000000000..d08c8a77455 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc @@ -0,0 +1,277 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_strict_flags.h" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(string_ref_null, DefaultConstructor) +{ + StringRefNull ref; + EXPECT_EQ(ref.size(), 0); + EXPECT_EQ(ref[0], '\0'); +} + +TEST(string_ref_null, CStringConstructor) +{ + const char *str = "Hello"; + StringRefNull ref(str); + EXPECT_EQ(ref.size(), 5); + EXPECT_EQ(ref.data(), str); +} + +TEST(string_ref_null, CStringLengthConstructor) +{ + const char *str = "Hello"; + StringRefNull ref(str, 5); + EXPECT_EQ(ref.size(), 5); + EXPECT_EQ(ref.data(), str); +} + +TEST(string_ref, DefaultConstructor) +{ + StringRef ref; + EXPECT_EQ(ref.size(), 0); +} + +TEST(string_ref, StartEndConstructor) +{ + const char *text = "hello world"; + StringRef ref(text, text + 5); + EXPECT_EQ(ref.size(), 5); + EXPECT_TRUE(ref == "hello"); + EXPECT_FALSE(ref == "hello "); +} + +TEST(string_ref, StartEndConstructorNullptr) +{ + StringRef ref(nullptr, nullptr); + EXPECT_EQ(ref.size(), 0); + EXPECT_TRUE(ref == ""); +} + +TEST(string_ref, StartEndConstructorSame) +{ + const char *text = "hello world"; + StringRef ref(text, text); + EXPECT_EQ(ref.size(), 0); + EXPECT_TRUE(ref == ""); +} + +TEST(string_ref, CStringConstructor) +{ + const char *str = "Test"; + StringRef ref(str); + EXPECT_EQ(ref.size(), 4); + EXPECT_EQ(ref.data(), str); +} + +TEST(string_ref, PointerWithLengthConstructor) +{ + const char *str = "Test"; + StringRef ref(str, 2); + EXPECT_EQ(ref.size(), 2); + EXPECT_EQ(ref.data(), str); +} + +TEST(string_ref, StdStringConstructor) +{ + std::string str = "Test"; + StringRef ref(str); + EXPECT_EQ(ref.size(), 4); + EXPECT_EQ(ref.data(), str.data()); +} + +TEST(string_ref, SubscriptOperator) +{ + StringRef ref("hello"); + EXPECT_EQ(ref.size(), 5); + EXPECT_EQ(ref[0], 'h'); + EXPECT_EQ(ref[1], 'e'); + EXPECT_EQ(ref[2], 'l'); + EXPECT_EQ(ref[3], 'l'); + EXPECT_EQ(ref[4], 'o'); +} + +TEST(string_ref, ToStdString) +{ + StringRef ref("test"); + std::string str = ref; + EXPECT_EQ(str.size(), 4); + EXPECT_EQ(str, "test"); +} + +TEST(string_ref, Print) +{ + StringRef ref("test"); + std::stringstream ss; + ss << ref; + ss << ref; + std::string str = ss.str(); + EXPECT_EQ(str.size(), 8); + EXPECT_EQ(str, "testtest"); +} + +TEST(string_ref, Add) +{ + StringRef a("qwe"); + StringRef b("asd"); + std::string result = a + b; + EXPECT_EQ(result, "qweasd"); +} + +TEST(string_ref, AddCharPtr1) +{ + StringRef ref("test"); + std::string result = ref + "qwe"; + EXPECT_EQ(result, "testqwe"); +} + +TEST(string_ref, AddCharPtr2) +{ + StringRef ref("test"); + std::string result = "qwe" + ref; + EXPECT_EQ(result, "qwetest"); +} + +TEST(string_ref, AddString1) +{ + StringRef ref("test"); + std::string result = ref + std::string("asd"); + EXPECT_EQ(result, "testasd"); +} + +TEST(string_ref, AddString2) +{ + StringRef ref("test"); + std::string result = std::string("asd") + ref; + EXPECT_EQ(result, "asdtest"); +} + +TEST(string_ref, CompareEqual) +{ + StringRef ref1("test"); + StringRef ref2("test"); + StringRef ref3("other"); + EXPECT_TRUE(ref1 == ref2); + EXPECT_FALSE(ref1 == ref3); + EXPECT_TRUE(ref1 != ref3); + EXPECT_FALSE(ref1 != ref2); +} + +TEST(string_ref, CompareEqualCharPtr1) +{ + StringRef ref("test"); + EXPECT_TRUE(ref == "test"); + EXPECT_FALSE(ref == "other"); + EXPECT_TRUE(ref != "other"); + EXPECT_FALSE(ref != "test"); +} + +TEST(string_ref, CompareEqualCharPtr2) +{ + StringRef ref("test"); + EXPECT_TRUE("test" == ref); + EXPECT_FALSE("other" == ref); + EXPECT_TRUE(ref != "other"); + EXPECT_FALSE(ref != "test"); +} + +TEST(string_ref, CompareEqualString1) +{ + StringRef ref("test"); + EXPECT_TRUE(ref == std::string("test")); + EXPECT_FALSE(ref == std::string("other")); + EXPECT_TRUE(ref != std::string("other")); + EXPECT_FALSE(ref != std::string("test")); +} + +TEST(string_ref, CompareEqualString2) +{ + StringRef ref("test"); + EXPECT_TRUE(std::string("test") == ref); + EXPECT_FALSE(std::string("other") == ref); + EXPECT_TRUE(std::string("other") != ref); + EXPECT_FALSE(std::string("test") != ref); +} + +TEST(string_ref, Iterate) +{ + StringRef ref("test"); + Vector<char> chars; + for (char c : ref) { + chars.append(c); + } + EXPECT_EQ(chars.size(), 4); + EXPECT_EQ(chars[0], 't'); + EXPECT_EQ(chars[1], 'e'); + EXPECT_EQ(chars[2], 's'); + EXPECT_EQ(chars[3], 't'); +} + +TEST(string_ref, StartsWith) +{ + StringRef ref("test"); + EXPECT_TRUE(ref.startswith("")); + EXPECT_TRUE(ref.startswith("t")); + EXPECT_TRUE(ref.startswith("te")); + EXPECT_TRUE(ref.startswith("tes")); + EXPECT_TRUE(ref.startswith("test")); + EXPECT_FALSE(ref.startswith("test ")); + EXPECT_FALSE(ref.startswith("a")); +} + +TEST(string_ref, EndsWith) +{ + StringRef ref("test"); + EXPECT_TRUE(ref.endswith("")); + EXPECT_TRUE(ref.endswith("t")); + EXPECT_TRUE(ref.endswith("st")); + EXPECT_TRUE(ref.endswith("est")); + EXPECT_TRUE(ref.endswith("test")); + EXPECT_FALSE(ref.endswith(" test")); + EXPECT_FALSE(ref.endswith("a")); +} + +TEST(string_ref, DropPrefixN) +{ + StringRef ref("test"); + StringRef ref2 = ref.drop_prefix(2); + StringRef ref3 = ref2.drop_prefix(2); + EXPECT_EQ(ref2.size(), 2); + EXPECT_EQ(ref3.size(), 0); + EXPECT_EQ(ref2, "st"); + EXPECT_EQ(ref3, ""); +} + +TEST(string_ref, DropPrefix) +{ + StringRef ref("test"); + StringRef ref2 = ref.drop_prefix("tes"); + EXPECT_EQ(ref2.size(), 1); + EXPECT_EQ(ref2, "t"); +} + +TEST(string_ref, Substr) +{ + StringRef ref("hello world"); + EXPECT_EQ(ref.substr(0, 5), "hello"); + EXPECT_EQ(ref.substr(4, 0), ""); + EXPECT_EQ(ref.substr(3, 4), "lo w"); + EXPECT_EQ(ref.substr(6, 5), "world"); +} + +TEST(string_ref, Copy) +{ + StringRef ref("hello"); + char dst[10]; + memset(dst, 0xFF, 10); + ref.copy(dst); + EXPECT_EQ(dst[5], '\0'); + EXPECT_EQ(dst[6], 0xFF); + EXPECT_EQ(ref, dst); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc new file mode 100644 index 00000000000..8f3db8d8403 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc @@ -0,0 +1,164 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_strict_flags.h" +#include "BLI_vector_set.hh" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(vector_set, DefaultConstructor) +{ + VectorSet<int> set; + EXPECT_EQ(set.size(), 0); + EXPECT_TRUE(set.is_empty()); +} + +TEST(vector_set, InitializerListConstructor_WithoutDuplicates) +{ + VectorSet<int> set = {1, 4, 5}; + EXPECT_EQ(set.size(), 3); + EXPECT_EQ(set[0], 1); + EXPECT_EQ(set[1], 4); + EXPECT_EQ(set[2], 5); +} + +TEST(vector_set, InitializerListConstructor_WithDuplicates) +{ + VectorSet<int> set = {1, 3, 3, 2, 1, 5}; + EXPECT_EQ(set.size(), 4); + EXPECT_EQ(set[0], 1); + EXPECT_EQ(set[1], 3); + EXPECT_EQ(set[2], 2); + EXPECT_EQ(set[3], 5); +} + +TEST(vector_set, Copy) +{ + VectorSet<int> set1 = {1, 2, 3}; + VectorSet<int> set2 = set1; + EXPECT_EQ(set1.size(), 3); + EXPECT_EQ(set2.size(), 3); + EXPECT_EQ(set1.index_of(2), 1); + EXPECT_EQ(set2.index_of(2), 1); +} + +TEST(vector_set, CopyAssignment) +{ + VectorSet<int> set1 = {1, 2, 3}; + VectorSet<int> set2 = {}; + set2 = set1; + EXPECT_EQ(set1.size(), 3); + EXPECT_EQ(set2.size(), 3); + EXPECT_EQ(set1.index_of(2), 1); + EXPECT_EQ(set2.index_of(2), 1); +} + +TEST(vector_set, Move) +{ + VectorSet<int> set1 = {1, 2, 3}; + VectorSet<int> set2 = std::move(set1); + EXPECT_EQ(set1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(set2.size(), 3); +} + +TEST(vector_set, MoveAssignment) +{ + VectorSet<int> set1 = {1, 2, 3}; + VectorSet<int> set2 = {}; + set2 = std::move(set1); + EXPECT_EQ(set1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(set2.size(), 3); +} + +TEST(vector_set, AddNewIncreasesSize) +{ + VectorSet<int> set; + EXPECT_TRUE(set.is_empty()); + EXPECT_EQ(set.size(), 0); + set.add(5); + EXPECT_FALSE(set.is_empty()); + EXPECT_EQ(set.size(), 1); +} + +TEST(vector_set, AddExistingDoesNotIncreaseSize) +{ + VectorSet<int> set; + EXPECT_EQ(set.size(), 0); + EXPECT_TRUE(set.add(5)); + EXPECT_EQ(set.size(), 1); + EXPECT_FALSE(set.add(5)); + EXPECT_EQ(set.size(), 1); +} + +TEST(vector_set, Index) +{ + VectorSet<int> set = {3, 6, 4}; + EXPECT_EQ(set.index_of(6), 1); + EXPECT_EQ(set.index_of(3), 0); + EXPECT_EQ(set.index_of(4), 2); +} + +TEST(vector_set, IndexTry) +{ + VectorSet<int> set = {3, 6, 4}; + EXPECT_EQ(set.index_of_try(5), -1); + EXPECT_EQ(set.index_of_try(3), 0); + EXPECT_EQ(set.index_of_try(6), 1); + EXPECT_EQ(set.index_of_try(2), -1); +} + +TEST(vector_set, RemoveContained) +{ + VectorSet<int> set = {4, 5, 6, 7}; + EXPECT_EQ(set.size(), 4); + set.remove_contained(5); + EXPECT_EQ(set.size(), 3); + EXPECT_EQ(set[0], 4); + EXPECT_EQ(set[1], 7); + EXPECT_EQ(set[2], 6); + set.remove_contained(6); + EXPECT_EQ(set.size(), 2); + EXPECT_EQ(set[0], 4); + EXPECT_EQ(set[1], 7); + set.remove_contained(4); + EXPECT_EQ(set.size(), 1); + EXPECT_EQ(set[0], 7); + set.remove_contained(7); + EXPECT_EQ(set.size(), 0); +} + +TEST(vector_set, AddMultipleTimes) +{ + VectorSet<int> set; + for (int i = 0; i < 100; i++) { + EXPECT_FALSE(set.contains(i * 13)); + set.add(i * 12); + set.add(i * 13); + EXPECT_TRUE(set.contains(i * 13)); + } +} + +TEST(vector_set, UniquePtrValue) +{ + VectorSet<std::unique_ptr<int>> set; + set.add_new(std::unique_ptr<int>(new int())); + set.add(std::unique_ptr<int>(new int())); + set.index_of_try(std::unique_ptr<int>(new int())); + std::unique_ptr<int> value = set.pop(); + UNUSED_VARS(value); +} + +TEST(vector_set, Remove) +{ + VectorSet<int> set; + EXPECT_TRUE(set.add(5)); + EXPECT_TRUE(set.contains(5)); + EXPECT_FALSE(set.remove(6)); + EXPECT_TRUE(set.contains(5)); + EXPECT_TRUE(set.remove(5)); + EXPECT_FALSE(set.contains(5)); + EXPECT_FALSE(set.remove(5)); + EXPECT_FALSE(set.contains(5)); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc new file mode 100644 index 00000000000..f72dfc5deb8 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -0,0 +1,639 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_strict_flags.h" +#include "BLI_vector.hh" +#include "testing/testing.h" +#include <forward_list> + +namespace blender::tests { + +TEST(vector, DefaultConstructor) +{ + Vector<int> vec; + EXPECT_EQ(vec.size(), 0); +} + +TEST(vector, SizeConstructor) +{ + Vector<int> vec(3); + EXPECT_EQ(vec.size(), 3); +} + +/** + * Tests that the trivially constructible types are not zero-initialized. We do not want that for + * performance reasons. + */ +TEST(vector, TrivialTypeSizeConstructor) +{ + Vector<char, 1> *vec = new Vector<char, 1>(1); + char *ptr = &(*vec)[0]; + vec->~Vector(); + + const char magic = 42; + *ptr = magic; + EXPECT_EQ(*ptr, magic); + + new (vec) Vector<char, 1>(1); + EXPECT_EQ((*vec)[0], magic); + EXPECT_EQ(*ptr, magic); + delete vec; +} + +TEST(vector, SizeValueConstructor) +{ + Vector<int> vec(4, 10); + EXPECT_EQ(vec.size(), 4); + EXPECT_EQ(vec[0], 10); + EXPECT_EQ(vec[1], 10); + EXPECT_EQ(vec[2], 10); + EXPECT_EQ(vec[3], 10); +} + +TEST(vector, InitializerListConstructor) +{ + Vector<int> vec = {1, 3, 4, 6}; + EXPECT_EQ(vec.size(), 4); + EXPECT_EQ(vec[0], 1); + EXPECT_EQ(vec[1], 3); + EXPECT_EQ(vec[2], 4); + EXPECT_EQ(vec[3], 6); +} + +TEST(vector, ConvertingConstructor) +{ + std::array<float, 5> values = {5.4f, 7.3f, -8.1f, 5.0f, 0.0f}; + Vector<int> vec = values; + EXPECT_EQ(vec.size(), 5); + EXPECT_EQ(vec[0], 5); + EXPECT_EQ(vec[1], 7); + EXPECT_EQ(vec[2], -8); + EXPECT_EQ(vec[3], 5); + EXPECT_EQ(vec[4], 0); +} + +struct TestListValue { + TestListValue *next, *prev; + int value; +}; + +TEST(vector, ListBaseConstructor) +{ + TestListValue *value1 = new TestListValue{0, 0, 4}; + TestListValue *value2 = new TestListValue{0, 0, 5}; + TestListValue *value3 = new TestListValue{0, 0, 6}; + + ListBase list = {NULL, NULL}; + BLI_addtail(&list, value1); + BLI_addtail(&list, value2); + BLI_addtail(&list, value3); + Vector<TestListValue *> vec(list); + + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec[0]->value, 4); + EXPECT_EQ(vec[1]->value, 5); + EXPECT_EQ(vec[2]->value, 6); + + delete value1; + delete value2; + delete value3; +} + +TEST(vector, ContainerConstructor) +{ + std::forward_list<int> list; + list.push_front(3); + list.push_front(1); + list.push_front(5); + + Vector<int> vec = Vector<int>::FromContainer(list); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec[0], 5); + EXPECT_EQ(vec[1], 1); + EXPECT_EQ(vec[2], 3); +} + +TEST(vector, CopyConstructor) +{ + Vector<int> vec1 = {1, 2, 3}; + Vector<int> vec2(vec1); + EXPECT_EQ(vec2.size(), 3); + EXPECT_EQ(vec2[0], 1); + EXPECT_EQ(vec2[1], 2); + EXPECT_EQ(vec2[2], 3); + + vec1[1] = 5; + EXPECT_EQ(vec1[1], 5); + EXPECT_EQ(vec2[1], 2); +} + +TEST(vector, CopyConstructor2) +{ + Vector<int, 2> vec1 = {1, 2, 3, 4}; + Vector<int, 3> vec2(vec1); + + EXPECT_EQ(vec1.size(), 4); + EXPECT_EQ(vec2.size(), 4); + EXPECT_NE(vec1.data(), vec2.data()); + EXPECT_EQ(vec2[0], 1); + EXPECT_EQ(vec2[1], 2); + EXPECT_EQ(vec2[2], 3); + EXPECT_EQ(vec2[3], 4); +} + +TEST(vector, CopyConstructor3) +{ + Vector<int, 20> vec1 = {1, 2, 3, 4}; + Vector<int, 1> vec2(vec1); + + EXPECT_EQ(vec1.size(), 4); + EXPECT_EQ(vec2.size(), 4); + EXPECT_NE(vec1.data(), vec2.data()); + EXPECT_EQ(vec2[2], 3); +} + +TEST(vector, CopyConstructor4) +{ + Vector<int, 5> vec1 = {1, 2, 3, 4}; + Vector<int, 6> vec2(vec1); + + EXPECT_EQ(vec1.size(), 4); + EXPECT_EQ(vec2.size(), 4); + EXPECT_NE(vec1.data(), vec2.data()); + EXPECT_EQ(vec2[3], 4); +} + +TEST(vector, MoveConstructor) +{ + Vector<int> vec1 = {1, 2, 3, 4}; + Vector<int> vec2(std::move(vec1)); + + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(vec2.size(), 4); + EXPECT_EQ(vec2[0], 1); + EXPECT_EQ(vec2[1], 2); + EXPECT_EQ(vec2[2], 3); + EXPECT_EQ(vec2[3], 4); +} + +TEST(vector, MoveConstructor2) +{ + Vector<int, 2> vec1 = {1, 2, 3, 4}; + Vector<int, 3> vec2(std::move(vec1)); + + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(vec2.size(), 4); + EXPECT_EQ(vec2[0], 1); + EXPECT_EQ(vec2[1], 2); + EXPECT_EQ(vec2[2], 3); + EXPECT_EQ(vec2[3], 4); +} + +TEST(vector, MoveConstructor3) +{ + Vector<int, 20> vec1 = {1, 2, 3, 4}; + Vector<int, 1> vec2(std::move(vec1)); + + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(vec2.size(), 4); + EXPECT_EQ(vec2[2], 3); +} + +TEST(vector, MoveConstructor4) +{ + Vector<int, 5> vec1 = {1, 2, 3, 4}; + Vector<int, 6> vec2(std::move(vec1)); + + EXPECT_EQ(vec1.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(vec2.size(), 4); + EXPECT_EQ(vec2[3], 4); +} + +TEST(vector, MoveAssignment) +{ + Vector<int> vec = {1, 2}; + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec[0], 1); + EXPECT_EQ(vec[1], 2); + + vec = Vector<int>({5}); + EXPECT_EQ(vec.size(), 1); + EXPECT_EQ(vec[0], 5); +} + +TEST(vector, CopyAssignment) +{ + Vector<int> vec1 = {1, 2, 3}; + Vector<int> vec2 = {4, 5}; + EXPECT_EQ(vec1.size(), 3); + EXPECT_EQ(vec2.size(), 2); + + vec2 = vec1; + EXPECT_EQ(vec2.size(), 3); + + vec1[0] = 7; + EXPECT_EQ(vec1[0], 7); + EXPECT_EQ(vec2[0], 1); +} + +TEST(vector, Append) +{ + Vector<int> vec; + vec.append(3); + vec.append(6); + vec.append(7); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec[0], 3); + EXPECT_EQ(vec[1], 6); + EXPECT_EQ(vec[2], 7); +} + +TEST(vector, AppendAndGetIndex) +{ + Vector<int> vec; + EXPECT_EQ(vec.append_and_get_index(10), 0); + EXPECT_EQ(vec.append_and_get_index(10), 1); + EXPECT_EQ(vec.append_and_get_index(10), 2); + vec.append(10); + EXPECT_EQ(vec.append_and_get_index(10), 4); +} + +TEST(vector, AppendNonDuplicates) +{ + Vector<int> vec; + vec.append_non_duplicates(4); + EXPECT_EQ(vec.size(), 1); + vec.append_non_duplicates(5); + EXPECT_EQ(vec.size(), 2); + vec.append_non_duplicates(4); + EXPECT_EQ(vec.size(), 2); +} + +TEST(vector, ExtendNonDuplicates) +{ + Vector<int> vec; + vec.extend_non_duplicates({1, 2}); + EXPECT_EQ(vec.size(), 2); + vec.extend_non_duplicates({3, 4}); + EXPECT_EQ(vec.size(), 4); + vec.extend_non_duplicates({0, 1, 2, 3}); + EXPECT_EQ(vec.size(), 5); +} + +TEST(vector, Iterator) +{ + Vector<int> vec({1, 4, 9, 16}); + int i = 1; + for (int value : vec) { + EXPECT_EQ(value, i * i); + i++; + } +} + +TEST(vector, BecomeLarge) +{ + Vector<int, 4> vec; + for (int i = 0; i < 100; i++) { + vec.append(i * 5); + } + EXPECT_EQ(vec.size(), 100); + for (int i = 0; i < 100; i++) { + EXPECT_EQ(vec[i], static_cast<int>(i * 5)); + } +} + +static Vector<int> return_by_value_helper() +{ + return Vector<int>({3, 5, 1}); +} + +TEST(vector, ReturnByValue) +{ + Vector<int> vec = return_by_value_helper(); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec[0], 3); + EXPECT_EQ(vec[1], 5); + EXPECT_EQ(vec[2], 1); +} + +TEST(vector, VectorOfVectors_Append) +{ + Vector<Vector<int>> vec; + EXPECT_EQ(vec.size(), 0); + + Vector<int> v({1, 2}); + vec.append(v); + vec.append({7, 8}); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec[0][0], 1); + EXPECT_EQ(vec[0][1], 2); + EXPECT_EQ(vec[1][0], 7); + EXPECT_EQ(vec[1][1], 8); +} + +TEST(vector, RemoveLast) +{ + Vector<int> vec = {5, 6}; + EXPECT_EQ(vec.size(), 2); + vec.remove_last(); + EXPECT_EQ(vec.size(), 1); + vec.remove_last(); + EXPECT_EQ(vec.size(), 0); +} + +TEST(vector, IsEmpty) +{ + Vector<int> vec; + EXPECT_TRUE(vec.is_empty()); + vec.append(1); + EXPECT_FALSE(vec.is_empty()); + vec.remove_last(); + EXPECT_TRUE(vec.is_empty()); +} + +TEST(vector, RemoveReorder) +{ + Vector<int> vec = {4, 5, 6, 7}; + vec.remove_and_reorder(1); + EXPECT_EQ(vec[0], 4); + EXPECT_EQ(vec[1], 7); + EXPECT_EQ(vec[2], 6); + vec.remove_and_reorder(2); + EXPECT_EQ(vec[0], 4); + EXPECT_EQ(vec[1], 7); + vec.remove_and_reorder(0); + EXPECT_EQ(vec[0], 7); + vec.remove_and_reorder(0); + EXPECT_TRUE(vec.is_empty()); +} + +TEST(vector, RemoveFirstOccurrenceAndReorder) +{ + Vector<int> vec = {4, 5, 6, 7}; + vec.remove_first_occurrence_and_reorder(5); + EXPECT_EQ(vec[0], 4); + EXPECT_EQ(vec[1], 7); + EXPECT_EQ(vec[2], 6); + vec.remove_first_occurrence_and_reorder(6); + EXPECT_EQ(vec[0], 4); + EXPECT_EQ(vec[1], 7); + vec.remove_first_occurrence_and_reorder(4); + EXPECT_EQ(vec[0], 7); + vec.remove_first_occurrence_and_reorder(7); + EXPECT_EQ(vec.size(), 0); +} + +TEST(vector, Remove) +{ + Vector<int> vec = {1, 2, 3, 4, 5, 6}; + vec.remove(3); + EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span<int>({1, 2, 3, 5, 6}).begin())); + vec.remove(0); + EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span<int>({2, 3, 5, 6}).begin())); + vec.remove(3); + EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span<int>({2, 3, 5}).begin())); + vec.remove(1); + EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span<int>({2, 5}).begin())); + vec.remove(1); + EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span<int>({2}).begin())); + vec.remove(0); + EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span<int>({}).begin())); +} + +TEST(vector, ExtendSmallVector) +{ + Vector<int> a = {2, 3, 4}; + Vector<int> b = {11, 12}; + b.extend(a); + EXPECT_EQ(b.size(), 5); + EXPECT_EQ(b[0], 11); + EXPECT_EQ(b[1], 12); + EXPECT_EQ(b[2], 2); + EXPECT_EQ(b[3], 3); + EXPECT_EQ(b[4], 4); +} + +TEST(vector, ExtendArray) +{ + int array[] = {3, 4, 5, 6}; + + Vector<int> a; + a.extend(array, 2); + + EXPECT_EQ(a.size(), 2); + EXPECT_EQ(a[0], 3); + EXPECT_EQ(a[1], 4); +} + +TEST(vector, Last) +{ + Vector<int> a{3, 5, 7}; + EXPECT_EQ(a.last(), 7); +} + +TEST(vector, AppendNTimes) +{ + Vector<int> a; + a.append_n_times(5, 3); + a.append_n_times(2, 2); + EXPECT_EQ(a.size(), 5); + EXPECT_EQ(a[0], 5); + EXPECT_EQ(a[1], 5); + EXPECT_EQ(a[2], 5); + EXPECT_EQ(a[3], 2); + EXPECT_EQ(a[4], 2); +} + +TEST(vector, UniquePtrValue) +{ + Vector<std::unique_ptr<int>> vec; + vec.append(std::unique_ptr<int>(new int())); + vec.append(std::unique_ptr<int>(new int())); + vec.append(std::unique_ptr<int>(new int())); + vec.append(std::unique_ptr<int>(new int())); + EXPECT_EQ(vec.size(), 4); + + std::unique_ptr<int> &a = vec.last(); + std::unique_ptr<int> b = vec.pop_last(); + vec.remove_and_reorder(0); + vec.remove(0); + EXPECT_EQ(vec.size(), 1); + + UNUSED_VARS(a, b); +} + +class TypeConstructMock { + public: + bool default_constructed = false; + bool copy_constructed = false; + bool move_constructed = false; + bool copy_assigned = false; + bool move_assigned = false; + + TypeConstructMock() : default_constructed(true) + { + } + + TypeConstructMock(const TypeConstructMock &UNUSED(other)) : copy_constructed(true) + { + } + + TypeConstructMock(TypeConstructMock &&UNUSED(other)) noexcept : move_constructed(true) + { + } + + TypeConstructMock &operator=(const TypeConstructMock &other) + { + if (this == &other) { + return *this; + } + + copy_assigned = true; + return *this; + } + + TypeConstructMock &operator=(TypeConstructMock &&other) noexcept + { + if (this == &other) { + return *this; + } + + move_assigned = true; + return *this; + } +}; + +TEST(vector, SizeConstructorCallsDefaultConstructor) +{ + Vector<TypeConstructMock> vec(3); + EXPECT_TRUE(vec[0].default_constructed); + EXPECT_TRUE(vec[1].default_constructed); + EXPECT_TRUE(vec[2].default_constructed); +} + +TEST(vector, SizeValueConstructorCallsCopyConstructor) +{ + Vector<TypeConstructMock> vec(3, TypeConstructMock()); + EXPECT_TRUE(vec[0].copy_constructed); + EXPECT_TRUE(vec[1].copy_constructed); + EXPECT_TRUE(vec[2].copy_constructed); +} + +TEST(vector, AppendCallsCopyConstructor) +{ + Vector<TypeConstructMock> vec; + TypeConstructMock value; + vec.append(value); + EXPECT_TRUE(vec[0].copy_constructed); +} + +TEST(vector, AppendCallsMoveConstructor) +{ + Vector<TypeConstructMock> vec; + vec.append(TypeConstructMock()); + EXPECT_TRUE(vec[0].move_constructed); +} + +TEST(vector, SmallVectorCopyCallsCopyConstructor) +{ + Vector<TypeConstructMock, 2> src(2); + Vector<TypeConstructMock, 2> dst(src); + EXPECT_TRUE(dst[0].copy_constructed); + EXPECT_TRUE(dst[1].copy_constructed); +} + +TEST(vector, LargeVectorCopyCallsCopyConstructor) +{ + Vector<TypeConstructMock, 2> src(5); + Vector<TypeConstructMock, 2> dst(src); + EXPECT_TRUE(dst[0].copy_constructed); + EXPECT_TRUE(dst[1].copy_constructed); +} + +TEST(vector, SmallVectorMoveCallsMoveConstructor) +{ + Vector<TypeConstructMock, 2> src(2); + Vector<TypeConstructMock, 2> dst(std::move(src)); + EXPECT_TRUE(dst[0].move_constructed); + EXPECT_TRUE(dst[1].move_constructed); +} + +TEST(vector, LargeVectorMoveCallsNoConstructor) +{ + Vector<TypeConstructMock, 2> src(5); + Vector<TypeConstructMock, 2> dst(std::move(src)); + + EXPECT_TRUE(dst[0].default_constructed); + EXPECT_FALSE(dst[0].move_constructed); + EXPECT_FALSE(dst[0].copy_constructed); +} + +TEST(vector, Resize) +{ + std::string long_string = "012345678901234567890123456789"; + Vector<std::string> vec; + EXPECT_EQ(vec.size(), 0); + vec.resize(2); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec[0], ""); + EXPECT_EQ(vec[1], ""); + vec.resize(5, long_string); + EXPECT_EQ(vec.size(), 5); + EXPECT_EQ(vec[0], ""); + EXPECT_EQ(vec[1], ""); + EXPECT_EQ(vec[2], long_string); + EXPECT_EQ(vec[3], long_string); + EXPECT_EQ(vec[4], long_string); + vec.resize(1); + EXPECT_EQ(vec.size(), 1); + EXPECT_EQ(vec[0], ""); +} + +TEST(vector, FirstIndexOf) +{ + Vector<int> vec = {2, 3, 5, 7, 5, 9}; + EXPECT_EQ(vec.first_index_of(2), 0); + EXPECT_EQ(vec.first_index_of(5), 2); + EXPECT_EQ(vec.first_index_of(9), 5); +} + +TEST(vector, FirstIndexTryOf) +{ + Vector<int> vec = {2, 3, 5, 7, 5, 9}; + EXPECT_EQ(vec.first_index_of_try(2), 0); + EXPECT_EQ(vec.first_index_of_try(4), -1); + EXPECT_EQ(vec.first_index_of_try(5), 2); + EXPECT_EQ(vec.first_index_of_try(9), 5); + EXPECT_EQ(vec.first_index_of_try(1), -1); +} + +TEST(vector, OveralignedValues) +{ + Vector<AlignedBuffer<1, 512>, 2> vec; + for (int i = 0; i < 100; i++) { + vec.append({}); + EXPECT_EQ((uintptr_t)&vec.last() % 512, 0); + } +} + +TEST(vector, ConstructVoidPointerVector) +{ + int a; + float b; + double c; + Vector<void *> vec = {&a, &b, &c}; + EXPECT_EQ(vec.size(), 3); +} + +TEST(vector, Fill) +{ + Vector<int> vec(5); + vec.fill(3); + EXPECT_EQ(vec.size(), 5u); + EXPECT_EQ(vec[0], 3); + EXPECT_EQ(vec[1], 3); + EXPECT_EQ(vec[2], 3); + EXPECT_EQ(vec[3], 3); + EXPECT_EQ(vec[4], 3); +} + +} // namespace blender::tests diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 9f5e474e479..6d220d1fd1d 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4162,7 +4162,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu) direct_link_animdata(reader, cu->adt); /* Protect against integer overflow vulnerability. */ - CLAMP(cu->len_wchar, 0, INT_MAX - 4); + CLAMP(cu->len_char32, 0, INT_MAX - 4); BLO_read_pointer_array(reader, (void **)&cu->mat); @@ -5518,7 +5518,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); /* initialize the curve. Maybe this could be moved to modififer logic */ - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } else if (md->type == eGpencilModifierType_Thick) { @@ -5527,7 +5527,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_thickness); if (gpmd->curve_thickness) { BKE_curvemapping_blend_read(reader, gpmd->curve_thickness); - BKE_curvemapping_initialize(gpmd->curve_thickness); + BKE_curvemapping_init(gpmd->curve_thickness); } } else if (md->type == eGpencilModifierType_Tint) { @@ -5536,7 +5536,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_intensity); if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } else if (md->type == eGpencilModifierType_Smooth) { @@ -5544,7 +5544,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_intensity); if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } else if (md->type == eGpencilModifierType_Color) { @@ -5552,7 +5552,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_intensity); if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } else if (md->type == eGpencilModifierType_Opacity) { @@ -5560,7 +5560,7 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) BLO_read_data_address(reader, &gpmd->curve_intensity); if (gpmd->curve_intensity) { BKE_curvemapping_blend_read(reader, gpmd->curve_intensity); - BKE_curvemapping_initialize(gpmd->curve_intensity); + BKE_curvemapping_init(gpmd->curve_intensity); } } } @@ -8682,9 +8682,8 @@ static void direct_link_volume(BlendDataReader *reader, Volume *volume) static void lib_link_simulation(BlendLibReader *reader, Simulation *simulation) { - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BLO_read_id_address(reader, simulation->id.lib, &handle_item->id); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BLO_read_id_address(reader, simulation->id.lib, &dependency->id); } } @@ -8703,7 +8702,7 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati } } - BLO_read_list(reader, &simulation->persistent_data_handles); + BLO_read_list(reader, &simulation->dependencies); } /** \} */ @@ -11112,9 +11111,8 @@ static void expand_simulation(BlendExpander *expander, Simulation *simulation) if (simulation->adt) { expand_animdata(expander, simulation->adt); } - LISTBASE_FOREACH ( - PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) { - BLO_expand(expander, handle_item->id); + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + BLO_expand(expander, dependency->id); } } diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 3ed59a0baa1..1432fbeb4e2 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -280,7 +280,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb) region->v2d.keepzoom |= (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_KEEPASPECT); region->v2d.keeptot = V2D_KEEPTOT_STRICT; region->v2d.minzoom = region->v2d.maxzoom = 1.0f; - // region->v2d.flag |= V2D_IS_INITIALISED; + // region->v2d.flag |= V2D_IS_INIT; break; } case SPACE_GRAPH: { @@ -297,7 +297,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb) region->v2d.max[0] = MAXFRAMEF; region->v2d.max[1] = FLT_MAX; - // region->v2d.flag |= V2D_IS_INITIALISED; + // region->v2d.flag |= V2D_IS_INIT; break; } case SPACE_NLA: { @@ -355,7 +355,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb) region->v2d.scroll |= (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES); region->v2d.scroll |= (V2D_SCROLL_LEFT | V2D_SCROLL_VERTICAL_HANDLES); region->v2d.align = V2D_ALIGN_NO_NEG_Y; - region->v2d.flag |= V2D_IS_INITIALISED; + region->v2d.flag |= V2D_IS_INIT; break; } case SPACE_NODE: { @@ -965,7 +965,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) bPoseChannel *pchan; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - /* just need to initialise rotation axis properly... */ + /* Just need to initialize rotation axis properly. */ pchan->rotAxis[1] = 1.0f; } } diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index b3bf8991c3e..f74ee9cd735 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1205,7 +1205,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) if (region->regiontype == RGN_TYPE_PREVIEW) { if (region->alignment != RGN_ALIGN_NONE) { region->flag |= RGN_FLAG_HIDDEN; - region->v2d.flag &= ~V2D_IS_INITIALISED; + region->v2d.flag &= ~V2D_IS_INIT; region->alignment = RGN_ALIGN_NONE; hide = true; @@ -2520,7 +2520,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (cu = bmain->curves.first; cu; cu = cu->id.next) { if (cu->str) { - cu->len_wchar = BLI_strlen_utf8(cu->str); + cu->len_char32 = BLI_strlen_utf8(cu->str); } } } diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 521fc4b9b82..2c4602f546b 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -1108,7 +1108,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) for (scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { CurveMapping *curve_mapping = &scene->r.mblur_shutter_curve; BKE_curvemapping_set_defaults(curve_mapping, 1, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(curve_mapping); + BKE_curvemapping_init(curve_mapping); BKE_curvemap_reset( curve_mapping->cm, &curve_mapping->clipr, CURVE_PRESET_MAX, CURVEMAP_SLOPE_POS_NEG); } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 111ac728cc3..fc3e81a2005 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -190,7 +190,7 @@ static void do_version_workspaces_create_from_screens(Main *bmain) static void do_version_area_change_space_to_space_action(ScrArea *area, const Scene *scene) { SpaceType *stype = BKE_spacetype_from_id(SPACE_ACTION); - SpaceAction *saction = (SpaceAction *)stype->new (area, scene); + SpaceAction *saction = (SpaceAction *)stype->create(area, scene); ARegion *region_channels; /* Properly free current regions */ @@ -1958,7 +1958,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; if ((gset) && (gset->cur_falloff == NULL)) { gset->cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(gset->cur_falloff); + BKE_curvemapping_init(gset->cur_falloff); BKE_curvemap_reset(gset->cur_falloff->cm, &gset->cur_falloff->clipr, CURVE_PRESET_GAUSS, @@ -3371,7 +3371,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; if ((gset) && (gset->cur_primitive == NULL)) { gset->cur_primitive = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(gset->cur_primitive); + BKE_curvemapping_init(gset->cur_primitive); BKE_curvemap_reset(gset->cur_primitive->cm, &gset->cur_primitive->clipr, CURVE_PRESET_BELL, @@ -4767,7 +4767,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; @@ -4778,7 +4778,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; @@ -4788,7 +4788,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; @@ -4798,7 +4798,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; @@ -4808,7 +4808,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (mmd->curve_intensity == NULL) { mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (mmd->curve_intensity) { - BKE_curvemapping_initialize(mmd->curve_intensity); + BKE_curvemapping_init(mmd->curve_intensity); } } break; diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index b6caa018756..016b34278cf 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -22,9 +22,11 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" #include "DNA_constraint_types.h" #include "DNA_genfile.h" #include "DNA_gpencil_modifier_types.h" @@ -427,5 +429,39 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Initialise additional velocity parameter for CacheFiles. */ + if (!DNA_struct_elem_find( + fd->filesdna, "MeshSeqCacheModifierData", "float", "velocity_scale")) { + for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_MeshSequenceCache) { + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + mcmd->velocity_scale = 1.0f; + mcmd->vertex_velocities = NULL; + mcmd->num_vertices = 0; + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "CacheFile", "char", "velocity_unit")) { + for (CacheFile *cache_file = bmain->cachefiles.first; cache_file != NULL; + cache_file = cache_file->id.next) { + BLI_strncpy(cache_file->velocity_name, ".velocities", sizeof(cache_file->velocity_name)); + cache_file->velocity_unit = CACHEFILE_VELOCITY_UNIT_SECOND; + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "OceanModifierData", "int", "viewport_resolution")) { + for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Ocean) { + OceanModifierData *omd = (OceanModifierData *)md; + omd->viewport_resolution = omd->resolution; + } + } + } + } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 7f75c0100b8..ae6d3a58091 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -106,7 +106,7 @@ static void blo_update_defaults_screen(bScreen *screen, /* Some toolbars have been saved as initialized, * we don't want them to have odd zoom-level or scrolling set, see: T47047 */ if (ELEM(region->regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) { - region->v2d.flag &= ~V2D_IS_INITIALISED; + region->v2d.flag &= ~V2D_IS_INIT; } } @@ -326,7 +326,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (ts->gp_sculpt.cur_falloff == NULL) { ts->gp_sculpt.cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_falloff_curve = ts->gp_sculpt.cur_falloff; - BKE_curvemapping_initialize(gp_falloff_curve); + BKE_curvemapping_init(gp_falloff_curve); BKE_curvemap_reset(gp_falloff_curve->cm, &gp_falloff_curve->clipr, CURVE_PRESET_GAUSS, @@ -335,7 +335,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (ts->gp_sculpt.cur_primitive == NULL) { ts->gp_sculpt.cur_primitive = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_primitive_curve = ts->gp_sculpt.cur_primitive; - BKE_curvemapping_initialize(gp_primitive_curve); + BKE_curvemapping_init(gp_primitive_curve); BKE_curvemap_reset(gp_primitive_curve->cm, &gp_primitive_curve->clipr, CURVE_PRESET_BELL, diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 44c7c35e47d..d25e340d4fc 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -2096,7 +2096,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) if (la->curfalloff == NULL) { la->curfalloff = BKE_curvemapping_add(1, 0.0f, 1.0f, 1.0f, 0.0f); - BKE_curvemapping_initialize(la->curfalloff); + BKE_curvemapping_init(la->curfalloff); } } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 50e3b375166..e2dc27d7e88 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -366,7 +366,7 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) } if (!USER_VERSION_ATLEAST(250, 0)) { /* adjust grease-pencil distances */ - userdef->gp_manhattendist = 1; + userdef->gp_manhattandist = 1; userdef->gp_euclideandist = 2; /* adjust default interpolation for new IPO-curves */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 46ac6b43c92..59f27f6cec2 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2007,7 +2007,7 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address) if (cu->vfont) { BLO_write_raw(writer, cu->len + 1, cu->str); - BLO_write_struct_array(writer, CharInfo, cu->len_wchar + 1, cu->strinfo); + BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo); BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb); } else { @@ -3862,7 +3862,7 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const } } - BLO_write_struct_list(writer, PersistentDataHandleItem, &simulation->persistent_data_handles); + BLO_write_struct_list(writer, SimulationDependency, &simulation->dependencies); } } @@ -4047,8 +4047,9 @@ static bool write_file_handle(Main *mainvar, * avoid thumbnail detecting changes because of this. */ mywrite_flush(wd); - OverrideLibraryStorage *override_storage = - wd->use_memfile ? NULL : BKE_lib_override_library_operations_store_initialize(); + OverrideLibraryStorage *override_storage = wd->use_memfile ? + NULL : + BKE_lib_override_library_operations_store_init(); #define ID_BUFFER_STATIC_SIZE 8192 /* This outer loop allows to save first data-blocks from real mainvar, diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 6c666183755..f263392bdd2 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -45,6 +45,11 @@ #include "./intern/bmesh_private.h" +// #define BEVEL_DEBUG_TIME +#ifdef BEVEL_DEBUG_TIME +# include "PIL_time.h" +#endif + #define BEVEL_EPSILON_D 1e-6 #define BEVEL_EPSILON 1e-6f #define BEVEL_EPSILON_SQ 1e-12f @@ -917,7 +922,7 @@ static void math_layer_info_init(BevelParams *bp, BMesh *bm) * segment (and its continuation into vmesh) can usually arbitrarily be * the previous face or the next face. * Or, for the center polygon of a corner, all of the faces around - * the vertex are possible choices. + * the vertex are possibleface_component choices. * If we just choose randomly, the resulting UV maps or material * assignment can look ugly/inconsistent. * Allow for the case when arguments are null. @@ -1891,6 +1896,57 @@ static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int ns } /** + * Helper for #calculate_profile that builds the 3D locations for the segments + * and the higher power of 2 segments. + */ +static void calculate_profile_segments(const Profile *profile, + const float map[4][4], + const bool use_map, + const bool reversed, + const int ns, + const double *xvals, + const double *yvals, + float *r_prof_co) +{ + /* Iterate over the vertices along the boundary arc. */ + for (int k = 0; k <= ns; k++) { + float co[3]; + if (k == 0) { + copy_v3_v3(co, profile->start); + } + else if (k == ns) { + copy_v3_v3(co, profile->end); + } + else { + if (use_map) { + float p[3] = {reversed ? (float)yvals[ns - k] : (float)xvals[k], + reversed ? (float)xvals[ns - k] : (float)yvals[k], + 0.0f}; + /* Do the 2D->3D transformation of the profile coordinates. */ + mul_v3_m4v3(co, map, p); + } + else { + interp_v3_v3v3(co, profile->start, profile->end, (float)k / (float)ns); + } + } + /* Finish the 2D->3D transformation by projecting onto the final profile plane. */ + float *prof_co_k = r_prof_co + 3 * k; + if (!is_zero_v3(profile->proj_dir)) { + float co2[3]; + add_v3_v3v3(co2, co, profile->proj_dir); + /* pro->plane_co and pro->plane_no are filled in #set_profile_params. */ + if (!isect_line_plane_v3(prof_co_k, co, co2, profile->plane_co, profile->plane_no)) { + /* Shouldn't happen. */ + copy_v3_v3(prof_co_k, co); + } + } + else { + copy_v3_v3(prof_co_k, co); + } + } +} + +/** * Calculate the actual coordinate values for bndv's profile. * This is only needed if bp->seg > 1. * Allocate the space for them if that hasn't been done already. @@ -1900,12 +1956,6 @@ static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int ns */ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, bool miter) { - int i, k, ns; - const double *xvals, *yvals; - float co[3], co2[3], p[3], map[4][4], bottom_corner[3], top_corner[3]; - float *prof_co, *prof_co_k; - float r; - bool need_2, map_ok; Profile *pro = &bndv->profile; ProfileSpacing *pro_spacing = (miter) ? &bp->pro_spacing_miter : &bp->pro_spacing; @@ -1913,89 +1963,51 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, b return; } - need_2 = bp->seg != bp->pro_spacing.seg_2; - if (!pro->prof_co) { - pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena, - ((size_t)bp->seg + 1) * 3 * sizeof(float)); + bool need_2 = bp->seg != bp->pro_spacing.seg_2; + if (pro->prof_co == NULL) { + pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena, sizeof(float) * 3 * (bp->seg + 1)); if (need_2) { pro->prof_co_2 = (float *)BLI_memarena_alloc( - bp->mem_arena, ((size_t)bp->pro_spacing.seg_2 + 1) * 3 * sizeof(float)); + bp->mem_arena, sizeof(float) * 3 * (bp->pro_spacing.seg_2 + 1)); } else { pro->prof_co_2 = pro->prof_co; } } - r = pro->super_r; - if (bp->profile_type == BEVEL_PROFILE_SUPERELLIPSE && r == PRO_LINE_R) { - map_ok = false; + + bool use_map; + float map[4][4]; + if (bp->profile_type == BEVEL_PROFILE_SUPERELLIPSE && pro->super_r == PRO_LINE_R) { + use_map = false; } else { - map_ok = make_unit_square_map(pro->start, pro->middle, pro->end, map); + use_map = make_unit_square_map(pro->start, pro->middle, pro->end, map); } - if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && map_ok) { + if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && use_map) { /* Calculate the "height" of the profile by putting the (0,0) and (1,1) corners of the * un-transformed profile through the 2D->3D map and calculating the distance between them. */ - zero_v3(p); - mul_v3_m4v3(bottom_corner, map, p); - p[0] = 1.0f; - p[1] = 1.0f; - mul_v3_m4v3(top_corner, map, p); + float bottom_corner[3] = {0.0f, 0.0f, 0.0f}; + mul_v3_m4v3(bottom_corner, map, bottom_corner); + float top_corner[3] = {1.0f, 1.0f, 0.0f}; + mul_v3_m4v3(top_corner, map, top_corner); + pro->height = len_v3v3(bottom_corner, top_corner); } - /* The first iteration is the nseg case, the second is the seg_2 case (if it's needed) .*/ - for (i = 0; i < 2; i++) { - if (i == 0) { - ns = bp->seg; - xvals = pro_spacing->xvals; - yvals = pro_spacing->yvals; - prof_co = pro->prof_co; - } - else { - if (!need_2) { - break; /* Shares coords with pro->prof_co. */ - } - ns = bp->pro_spacing.seg_2; - xvals = pro_spacing->xvals_2; - yvals = pro_spacing->yvals_2; - prof_co = pro->prof_co_2; - } - - /* Iterate over the vertices along the boundary arc. */ - for (k = 0; k <= ns; k++) { - if (k == 0) { - copy_v3_v3(co, pro->start); - } - else if (k == ns) { - copy_v3_v3(co, pro->end); - } - else { - if (map_ok) { - p[0] = reversed ? (float)yvals[ns - k] : (float)xvals[k]; - p[1] = reversed ? (float)xvals[ns - k] : (float)yvals[k]; - p[2] = 0.0f; - /* Do the 2D->3D transformation of the profile coordinates. */ - mul_v3_m4v3(co, map, p); - } - else { - interp_v3_v3v3(co, pro->start, pro->end, (float)k / (float)ns); - } - } - /* Finish the 2D->3D transformation by projecting onto the final profile plane. */ - prof_co_k = prof_co + 3 * k; /* Each coord takes up 3 spaces. */ - if (!is_zero_v3(pro->proj_dir)) { - add_v3_v3v3(co2, co, pro->proj_dir); - /* pro->plane_co and pro->plane_no are filled in "set_profile_params". */ - if (!isect_line_plane_v3(prof_co_k, co, co2, pro->plane_co, pro->plane_no)) { - /* Shouldn't happen. */ - copy_v3_v3(prof_co_k, co); - } - } - else { - copy_v3_v3(prof_co_k, co); - } - } + /* Calculate the 3D locations for the profile points */ + calculate_profile_segments( + pro, map, use_map, reversed, bp->seg, pro_spacing->xvals, pro_spacing->yvals, pro->prof_co); + /* Also calcualte for the is the seg_2 case if it's needed .*/ + if (need_2) { + calculate_profile_segments(pro, + map, + use_map, + reversed, + bp->pro_spacing.seg_2, + pro_spacing->xvals_2, + pro_spacing->yvals_2, + pro->prof_co_2); } } @@ -7143,68 +7155,64 @@ static float find_profile_fullness(BevelParams *bp) */ static void set_profile_spacing(BevelParams *bp, ProfileSpacing *pro_spacing, bool custom) { - int seg, seg_2; + int seg = bp->seg; - seg = bp->seg; - seg_2 = power_of_2_max_i(bp->seg); - if (seg > 1) { - /* Sample the seg_2 segments used for subdividing the vertex meshes. */ - if (seg_2 == 2) { - seg_2 = 4; - } - bp->pro_spacing.seg_2 = seg_2; - if (seg_2 == seg) { - pro_spacing->xvals_2 = pro_spacing->xvals; - pro_spacing->yvals_2 = pro_spacing->yvals; - } - else { - pro_spacing->xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena, - (size_t)(seg_2 + 1) * sizeof(double)); - pro_spacing->yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena, - (size_t)(seg_2 + 1) * sizeof(double)); - if (custom) { - /* Make sure the curve profile widget's sample table is full of the seg_2 samples. */ - BKE_curveprofile_initialize((CurveProfile *)bp->custom_profile, (short)seg_2); - - /* Copy segment locations into the profile spacing struct. */ - for (int i = 0; i < seg_2 + 1; i++) { - pro_spacing->xvals_2[i] = (double)bp->custom_profile->segments[i].y; - pro_spacing->yvals_2[i] = (double)bp->custom_profile->segments[i].x; - } - } - else { - find_even_superellipse_chords( - seg_2, bp->pro_super_r, pro_spacing->xvals_2, pro_spacing->yvals_2); - } - } + if (seg <= 1) { + /* Only 1 segment, we don't need any profile information. */ + pro_spacing->xvals = NULL; + pro_spacing->yvals = NULL; + pro_spacing->xvals_2 = NULL; + pro_spacing->yvals_2 = NULL; + pro_spacing->seg_2 = 0; + return; + } - /* Sample the input number of segments. */ - pro_spacing->xvals = (double *)BLI_memarena_alloc(bp->mem_arena, - (size_t)(seg + 1) * sizeof(double)); - pro_spacing->yvals = (double *)BLI_memarena_alloc(bp->mem_arena, - (size_t)(seg + 1) * sizeof(double)); + int seg_2 = max_ii(power_of_2_max_i(bp->seg), 4); + + /* Sample the seg_2 segments used during vertex mesh subdivision. */ + bp->pro_spacing.seg_2 = seg_2; + if (seg_2 == seg) { + pro_spacing->xvals_2 = pro_spacing->xvals; + pro_spacing->yvals_2 = pro_spacing->yvals; + } + else { + pro_spacing->xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena, + sizeof(double) * (seg_2 + 1)); + pro_spacing->yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena, + sizeof(double) * (seg_2 + 1)); if (custom) { - /* Make sure the curve profile's sample table is full. */ - if (bp->custom_profile->segments_len != seg || !bp->custom_profile->segments) { - BKE_curveprofile_initialize((CurveProfile *)bp->custom_profile, (short)seg); - } + /* Make sure the curve profile widget's sample table is full of the seg_2 samples. */ + BKE_curveprofile_init((CurveProfile *)bp->custom_profile, (short)seg_2); /* Copy segment locations into the profile spacing struct. */ - for (int i = 0; i < seg + 1; i++) { - pro_spacing->xvals[i] = (double)bp->custom_profile->segments[i].y; - pro_spacing->yvals[i] = (double)bp->custom_profile->segments[i].x; + for (int i = 0; i < seg_2 + 1; i++) { + pro_spacing->xvals_2[i] = (double)bp->custom_profile->segments[i].y; + pro_spacing->yvals_2[i] = (double)bp->custom_profile->segments[i].x; } } else { - find_even_superellipse_chords(seg, bp->pro_super_r, pro_spacing->xvals, pro_spacing->yvals); + find_even_superellipse_chords( + seg_2, bp->pro_super_r, pro_spacing->xvals_2, pro_spacing->yvals_2); } } - else { /* Only 1 segment, we don't need any profile information. */ - pro_spacing->xvals = NULL; - pro_spacing->yvals = NULL; - pro_spacing->xvals_2 = NULL; - pro_spacing->yvals_2 = NULL; - pro_spacing->seg_2 = 0; + + /* Sample the input number of segments. */ + pro_spacing->xvals = (double *)BLI_memarena_alloc(bp->mem_arena, sizeof(double) * (seg + 1)); + pro_spacing->yvals = (double *)BLI_memarena_alloc(bp->mem_arena, sizeof(double) * (seg + 1)); + if (custom) { + /* Make sure the curve profile's sample table is full. */ + if (bp->custom_profile->segments_len != seg || !bp->custom_profile->segments) { + BKE_curveprofile_init((CurveProfile *)bp->custom_profile, (short)seg); + } + + /* Copy segment locations into the profile spacing struct. */ + for (int i = 0; i < seg + 1; i++) { + pro_spacing->xvals[i] = (double)bp->custom_profile->segments[i].y; + pro_spacing->yvals[i] = (double)bp->custom_profile->segments[i].x; + } + } + else { + find_even_superellipse_chords(seg, bp->pro_super_r, pro_spacing->xvals, pro_spacing->yvals); } } @@ -7493,6 +7501,10 @@ void BM_mesh_bevel(BMesh *bm, bp.custom_profile = custom_profile; bp.vmesh_method = vmesh_method; +#ifdef BEVEL_DEBUG_TIME + double start_time = PIL_check_seconds_timer(); +#endif + /* Disable the miters with the cutoff vertex mesh method, the combination isn't useful anyway. */ if (bp.vmesh_method == BEVEL_VMESH_CUTOFF) { bp.miter_outer = BEVEL_MITER_SHARP; @@ -7661,4 +7673,8 @@ void BM_mesh_bevel(BMesh *bm, BLI_ghash_free(bp.face_hash, NULL, NULL); BLI_memarena_free(bp.mem_arena); } +#ifdef BEVEL_DEBUG_TIME + double end_time = PIL_check_seconds_timer(); + printf("BMESH BEVEL TIME = %.3f\n", end_time - start_time); +#endif } diff --git a/source/blender/compositor/nodes/COM_TimeNode.cpp b/source/blender/compositor/nodes/COM_TimeNode.cpp index 9722ead0716..247e0d11df6 100644 --- a/source/blender/compositor/nodes/COM_TimeNode.cpp +++ b/source/blender/compositor/nodes/COM_TimeNode.cpp @@ -49,7 +49,7 @@ void TimeNode::convertToOperations(NodeConverter &converter, fac = (context.getFramenumber() - node->custom1) / (float)(node->custom2 - node->custom1); } - BKE_curvemapping_initialize((CurveMapping *)node->storage); + BKE_curvemapping_init((CurveMapping *)node->storage); fac = BKE_curvemapping_evaluateF((CurveMapping *)node->storage, 0, fac); operation->setValue(clamp_f(fac, 0.0f, 1.0f)); converter.addOperation(operation); diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cpp b/source/blender/compositor/operations/COM_CurveBaseOperation.cpp index b18e77cf0e3..855f728f7bf 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cpp +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cpp @@ -35,7 +35,7 @@ CurveBaseOperation::~CurveBaseOperation() void CurveBaseOperation::initExecution() { - BKE_curvemapping_initialize(this->m_curveMapping); + BKE_curvemapping_init(this->m_curveMapping); } void CurveBaseOperation::deinitExecution() { diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cpp b/source/blender/compositor/operations/COM_InpaintOperation.cpp index 0967984899d..b8329b1cb29 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cpp +++ b/source/blender/compositor/operations/COM_InpaintOperation.cpp @@ -34,7 +34,7 @@ InpaintSimpleOperation::InpaintSimpleOperation() : NodeOperation() this->setComplex(true); this->m_inputImageProgram = NULL; this->m_pixelorder = NULL; - this->m_manhatten_distance = NULL; + this->m_manhattan_distance = NULL; this->m_cached_buffer = NULL; this->m_cached_buffer_ready = false; } @@ -43,7 +43,7 @@ void InpaintSimpleOperation::initExecution() this->m_inputImageProgram = this->getInputSocketReader(0); this->m_pixelorder = NULL; - this->m_manhatten_distance = NULL; + this->m_manhattan_distance = NULL; this->m_cached_buffer = NULL; this->m_cached_buffer_ready = false; @@ -85,7 +85,7 @@ int InpaintSimpleOperation::mdist(int x, int y) ASSERT_XY_RANGE(x, y); - return this->m_manhatten_distance[y * width + x]; + return this->m_manhattan_distance[y * width + x]; } bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) @@ -108,11 +108,11 @@ bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) return true; } -void InpaintSimpleOperation::calc_manhatten_distance() +void InpaintSimpleOperation::calc_manhattan_distance() { int width = this->getWidth(); int height = this->getHeight(); - short *m = this->m_manhatten_distance = (short *)MEM_mallocN(sizeof(short) * width * height, + short *m = this->m_manhattan_distance = (short *)MEM_mallocN(sizeof(short) * width * height, __func__); int *offsets; @@ -223,7 +223,7 @@ void *InpaintSimpleOperation::initializeTileData(rcti *rect) MemoryBuffer *buf = (MemoryBuffer *)this->m_inputImageProgram->initializeTileData(rect); this->m_cached_buffer = (float *)MEM_dupallocN(buf->getBuffer()); - this->calc_manhatten_distance(); + this->calc_manhattan_distance(); int curr = 0; int x, y; @@ -258,9 +258,9 @@ void InpaintSimpleOperation::deinitExecution() this->m_pixelorder = NULL; } - if (this->m_manhatten_distance) { - MEM_freeN(this->m_manhatten_distance); - this->m_manhatten_distance = NULL; + if (this->m_manhattan_distance) { + MEM_freeN(this->m_manhattan_distance); + this->m_manhattan_distance = NULL; } this->m_cached_buffer_ready = false; } diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index 2fef7c590ea..76c1ae81b28 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -34,7 +34,7 @@ class InpaintSimpleOperation : public NodeOperation { int *m_pixelorder; int m_area_size; - short *m_manhatten_distance; + short *m_manhattan_distance; public: InpaintSimpleOperation(); @@ -65,7 +65,7 @@ class InpaintSimpleOperation : public NodeOperation { rcti *output); private: - void calc_manhatten_distance(); + void calc_manhattan_distance(); void clamp_xy(int &x, int &y); float *get_pixel(int x, int y); int mdist(int x, int y); diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cpp b/source/blender/compositor/operations/COM_SunBeamsOperation.cpp index 6f47e0e190b..4a7139537c1 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cpp +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cpp @@ -172,7 +172,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { return; } - /* initialise the iteration variables */ + /* Initialize the iteration variables. */ float *buffer = init_buffer_iterator( input, source, co, dist_min, dist_max, x, y, num, v, dv, falloff_factor); zero_v3(border); diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 51fce738700..417aaa2c4c0 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -54,6 +54,11 @@ set(SRC intern/builder/deg_builder_remove_noop.cc intern/builder/deg_builder_rna.cc intern/builder/deg_builder_transitive.cc + intern/builder/pipeline.cc + intern/builder/pipeline_compositor.cc + intern/builder/pipeline_from_ids.cc + intern/builder/pipeline_render.cc + intern/builder/pipeline_view_layer.cc intern/debug/deg_debug.cc intern/debug/deg_debug_relations_graphviz.cc intern/debug/deg_debug_stats_gnuplot.cc @@ -110,6 +115,11 @@ set(SRC 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_compositor.h + intern/builder/pipeline_from_ids.h + intern/builder/pipeline_render.h + intern/builder/pipeline_view_layer.h intern/debug/deg_debug.h intern/debug/deg_time_average.h intern/eval/deg_eval.h diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index dcdcf0c05ca..e262c880421 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1126,6 +1126,12 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene) if (object->type != OB_MESH) { continue; } + 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. */ @@ -1466,6 +1472,18 @@ void DepsgraphNodeBuilder::build_light(Light *lamp) function_bind(BKE_light_eval, _1, lamp_cow)); } +void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket) +{ + build_idproperties(socket->prop); + + if (socket->type == SOCK_OBJECT) { + build_id((ID *)((bNodeSocketValueObject *)socket->default_value)->value); + } + else if (socket->type == SOCK_IMAGE) { + build_id((ID *)((bNodeSocketValueImage *)socket->default_value)->value); + } +} + void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) { if (ntree == nullptr) { @@ -1494,10 +1512,10 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } ID *id = bnode->id; @@ -1780,8 +1798,10 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation) return; } add_id_node(&simulation->id); + build_idproperties(simulation->id.properties); build_animdata(&simulation->id); build_parameters(&simulation->id); + build_nodetree(simulation->nodetree); Simulation *simulation_cow = get_cow_datablock(simulation); Scene *scene_cow = get_cow_datablock(scene_); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 256fa3450a6..40f42705a52 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -31,6 +31,7 @@ #include "DEG_depsgraph.h" +struct bNodeSocket; struct CacheFile; struct Camera; struct Collection; @@ -211,6 +212,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_camera(Camera *camera); virtual void build_light(Light *lamp); virtual void build_nodetree(bNodeTree *ntree); + virtual void build_nodetree_socket(bNodeSocket *socket); virtual void build_material(Material *ma); virtual void build_materials(Material **materials, int num_materials); virtual void build_freestyle_lineset(FreestyleLineSet *fls); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c5c509ee853..5ae71dd1792 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1698,6 +1698,22 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) if (object->type != OB_MESH) { continue; } + 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) { + /* If we are a child of a compound shape object, the transforms and sim evaluation will be + * handled by the parent compound shape object. Do not add any evaluation triggers + * for the child objects. + */ + continue; + } + OperationKey rb_transform_copy_key( &object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); /* Rigid body synchronization depends on the actual simulation. */ @@ -1747,13 +1763,18 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) /* 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); - OperationKey ob1_key( - &rbc->ob1->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - OperationKey ob2_key( - &rbc->ob2->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"); - add_relation(trans_key, ob2_key, "RigidBodyConstraint -> RBC.Object_2"); + 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"); + } /* Ensure that sim depends on this constraint's transform. */ add_relation(trans_key, rb_simulate_key, "RigidBodyConstraint Transform -> RB Simulation"); } @@ -2276,6 +2297,24 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) add_relation(lamp_parameters_key, shading_key, "Light Shading Parameters"); } +void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket) +{ + build_idproperties(socket->prop); + + if (socket->type == SOCK_OBJECT) { + Object *object = ((bNodeSocketValueObject *)socket->default_value)->value; + if (object != nullptr) { + build_object(object); + } + } + else if (socket->type == SOCK_IMAGE) { + Image *image = ((bNodeSocketValueImage *)socket->default_value)->value; + if (image != nullptr) { + build_image(image); + } + } +} + void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) { if (ntree == nullptr) { @@ -2292,10 +2331,10 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { build_idproperties(bnode->prop); LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) { - build_idproperties(socket->prop); + build_nodetree_socket(socket); } ID *id = bnode->id; @@ -2602,13 +2641,39 @@ void DepsgraphRelationBuilder::build_simulation(Simulation *simulation) if (built_map_.checkIsBuiltAndTag(simulation)) { return; } + build_idproperties(simulation->id.properties); build_animdata(&simulation->id); build_parameters(&simulation->id); - OperationKey simulation_update_key( + build_nodetree(simulation->nodetree); + build_nested_nodetree(&simulation->id, simulation->nodetree); + + OperationKey simulation_eval_key( &simulation->id, NodeType::SIMULATION, OperationCode::SIMULATION_EVAL); TimeSourceKey time_src_key; - add_relation(time_src_key, simulation_update_key, "TimeSrc -> Simulation"); + add_relation(time_src_key, simulation_eval_key, "TimeSrc -> Simulation"); + + OperationKey nodetree_key( + &simulation->nodetree->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT); + add_relation(nodetree_key, simulation_eval_key, "NodeTree -> Simulation", 0); + + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { + if (dependency->id == nullptr) { + continue; + } + build_id(dependency->id); + if (GS(dependency->id->name) == ID_OB) { + Object *object = (Object *)dependency->id; + if (dependency->flag & SIM_DEPENDS_ON_TRANSFORM) { + ComponentKey object_transform_key(&object->id, NodeType::TRANSFORM); + add_relation(object_transform_key, simulation_eval_key, "Object Transform -> Simulation"); + } + if (dependency->flag & SIM_DEPENDS_ON_GEOMETRY) { + ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY); + add_relation(object_geometry_key, simulation_eval_key, "Object Geometry -> Simulation"); + } + } + } } void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index b4b0dc71f85..04f2a3f911d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -46,6 +46,7 @@ #include "intern/node/deg_node_operation.h" struct Base; +struct bNodeSocket; struct CacheFile; struct Camera; struct Collection; @@ -275,6 +276,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_camera(Camera *camera); virtual void build_light(Light *lamp); virtual void build_nodetree(bNodeTree *ntree); + virtual void build_nodetree_socket(bNodeSocket *socket); virtual void build_material(Material *ma); virtual void build_materials(Material **materials, int num_materials); virtual void build_freestyle_lineset(FreestyleLineSet *fls); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h index c48c6489c47..d03903d508c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h @@ -96,7 +96,7 @@ class RNANodeQuery { /* Check whether prop_identifier contains rna_path_component. * - * This checks more than a substring: + * This checks more than a sub-string: * * prop_identifier contains(prop_identifier, "location") * ------------------------ ------------------------------------- diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc new file mode 100644 index 00000000000..d6893ba11d8 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline.cc @@ -0,0 +1,132 @@ +/* + * 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.h" + +#include "PIL_time.h" + +#include "BKE_global.h" + +#include "DNA_scene_types.h" + +#include "deg_builder_cycle.h" +#include "deg_builder_nodes.h" +#include "deg_builder_relations.h" +#include "deg_builder_transitive.h" + +namespace blender { +namespace deg { + +AbstractBuilderPipeline::AbstractBuilderPipeline(::Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) + : deg_graph_(reinterpret_cast<Depsgraph *>(graph)), + bmain_(bmain), + scene_(scene), + view_layer_(view_layer), + builder_cache_() +{ +} + +AbstractBuilderPipeline::~AbstractBuilderPipeline() +{ +} + +void AbstractBuilderPipeline::build() +{ + double start_time = 0.0; + if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { + start_time = PIL_check_seconds_timer(); + } + + build_step_sanity_check(); + build_step_nodes(); + build_step_relations(); + build_step_finalize(); + + if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { + printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); + } +} + +void AbstractBuilderPipeline::build_step_sanity_check() +{ + BLI_assert(BLI_findindex(&scene_->view_layers, view_layer_) != -1); + BLI_assert(deg_graph_->scene == scene_); + BLI_assert(deg_graph_->view_layer == view_layer_); +} + +void AbstractBuilderPipeline::build_step_nodes() +{ + /* Generate all the nodes in the graph first */ + unique_ptr<DepsgraphNodeBuilder> node_builder = construct_node_builder(); + node_builder->begin_build(); + build_nodes(*node_builder); + node_builder->end_build(); +} + +void AbstractBuilderPipeline::build_step_relations() +{ + /* Hook up relationships between operations - to determine evaluation order. */ + unique_ptr<DepsgraphRelationBuilder> relation_builder = construct_relation_builder(); + relation_builder->begin_build(); + build_relations(*relation_builder); + relation_builder->build_copy_on_write_relations(); + relation_builder->build_driver_relations(); +} + +void AbstractBuilderPipeline::build_step_finalize() +{ + /* Detect and solve cycles. */ + deg_graph_detect_cycles(deg_graph_); + /* Simplify the graph by removing redundant relations (to optimize + * traversal later). */ + /* TODO: it would be useful to have an option to disable this in cases where + * it is causing trouble. */ + if (G.debug_value == 799) { + deg_graph_transitive_reduction(deg_graph_); + } + /* Store pointers to commonly used valuated datablocks. */ + deg_graph_->scene_cow = (Scene *)deg_graph_->get_cow_id(°_graph_->scene->id); + /* Flush visibility layer and re-schedule nodes for update. */ + deg_graph_build_finalize(bmain_, deg_graph_); + DEG_graph_on_visible_update(bmain_, reinterpret_cast<::Depsgraph *>(deg_graph_), false); +#if 0 + if (!DEG_debug_consistency_check(deg_graph_)) { + printf("Consistency validation failed, ABORTING!\n"); + abort(); + } +#endif + /* Relations are up to date. */ + deg_graph_->need_update = false; +} + +unique_ptr<DepsgraphNodeBuilder> AbstractBuilderPipeline::construct_node_builder() +{ + return std::make_unique<DepsgraphNodeBuilder>(bmain_, deg_graph_, &builder_cache_); +} + +unique_ptr<DepsgraphRelationBuilder> AbstractBuilderPipeline::construct_relation_builder() +{ + return std::make_unique<DepsgraphRelationBuilder>(bmain_, deg_graph_, &builder_cache_); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h new file mode 100644 index 00000000000..2c9c78bb2cb --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline.h @@ -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. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "deg_builder_cache.h" + +#include "intern/depsgraph_type.h" + +struct Main; +struct Scene; +struct ViewLayer; +struct Depsgraph; + +namespace blender { +namespace deg { + +struct Depsgraph; +class DepsgraphNodeBuilder; +class DepsgraphRelationBuilder; + +/* Base class for Depsgraph Builder pipelines. + * + * Basically it runs through the following steps: + * - sanity check + * - build nodes + * - build relations + * - finalize + */ +class AbstractBuilderPipeline { + public: + AbstractBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + virtual ~AbstractBuilderPipeline(); + + void build(); + + protected: + Depsgraph *deg_graph_; + Main *bmain_; + Scene *scene_; + ViewLayer *view_layer_; + DepsgraphBuilderCache builder_cache_; + + virtual unique_ptr<DepsgraphNodeBuilder> construct_node_builder(); + virtual unique_ptr<DepsgraphRelationBuilder> construct_relation_builder(); + + virtual void build_step_sanity_check(); + void build_step_nodes(); + void build_step_relations(); + void build_step_finalize(); + + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) = 0; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) = 0; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.cc b/source/blender/depsgraph/intern/builder/pipeline_compositor.cc new file mode 100644 index 00000000000..3e56f17fc7e --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.cc @@ -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. + */ + +#include "pipeline_compositor.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +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) +{ + deg_graph_->is_render_pipeline_depsgraph = true; +} + +void CompositorBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder) +{ + node_builder.build_scene_render(scene_, view_layer_); + node_builder.build_nodetree(nodetree_); +} + +void CompositorBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder) +{ + relation_builder.build_scene_render(scene_, view_layer_); + relation_builder.build_nodetree(nodetree_); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.h b/source/blender/depsgraph/intern/builder/pipeline_compositor.h new file mode 100644 index 00000000000..892ece7c2a4 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.h @@ -0,0 +1,47 @@ +/* + * 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.h" + +struct bNodeTree; + +namespace blender { +namespace deg { + +class CompositorBuilderPipeline : public AbstractBuilderPipeline { + public: + CompositorBuilderPipeline( + ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, bNodeTree *nodetree); + + protected: + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; + + private: + bNodeTree *nodetree_; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc new file mode 100644 index 00000000000..e44f554f197 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc @@ -0,0 +1,154 @@ +/* + * 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_from_ids.h" + +#include "DNA_layer_types.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +namespace blender { +namespace deg { + +namespace { + +class DepsgraphFromIDsFilter { + public: + DepsgraphFromIDsFilter(ID **ids, const int num_ids) + { + for (int i = 0; i < num_ids; ++i) { + ids_.add(ids[i]); + } + } + + bool contains(ID *id) + { + return ids_.contains(id); + } + + protected: + Set<ID *> ids_; +}; + +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) + { + } + + virtual bool need_pull_base_into_graph(Base *base) override + { + if (!filter_.contains(&base->object->id)) { + return false; + } + return DepsgraphNodeBuilder::need_pull_base_into_graph(base); + } + + virtual void build_object_proxy_group(Object *object, bool is_visible) override + { + if (object->proxy_group == nullptr) { + return; + } + if (!filter_.contains(&object->proxy_group->id)) { + return; + } + DepsgraphNodeBuilder::build_object_proxy_group(object, is_visible); + } + + protected: + DepsgraphFromIDsFilter filter_; +}; + +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) + { + } + + virtual bool need_pull_base_into_graph(Base *base) override + { + if (!filter_.contains(&base->object->id)) { + return false; + } + return DepsgraphRelationBuilder::need_pull_base_into_graph(base); + } + + virtual void build_object_proxy_group(Object *object) override + { + if (object->proxy_group == nullptr) { + return; + } + if (!filter_.contains(&object->proxy_group->id)) { + return; + } + DepsgraphRelationBuilder::build_object_proxy_group(object); + } + + protected: + DepsgraphFromIDsFilter filter_; +}; + +} // 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) +{ +} + +unique_ptr<DepsgraphNodeBuilder> FromIDsBuilderPipeline::construct_node_builder() +{ + return std::make_unique<DepsgraphFromIDsNodeBuilder>( + bmain_, deg_graph_, &builder_cache_, ids_, num_ids_); +} + +unique_ptr<DepsgraphRelationBuilder> FromIDsBuilderPipeline::construct_relation_builder() +{ + return std::make_unique<DepsgraphFromIDsRelationBuilder>( + bmain_, deg_graph_, &builder_cache_, ids_, num_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]); + } +} + +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]); + } +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h new file mode 100644 index 00000000000..4a507f2c728 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h @@ -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. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "pipeline.h" + +namespace blender { +namespace deg { + +/* Optimized builders for dependency graph built from a given set of IDs. + * + * General notes: + * + * - We pull in all bases if their objects are in the set of IDs. This allows to have proper + * visibility and other flags assigned to the objects. + * All other bases (the ones which points to object which is outside of the set of IDs) are + * completely ignored. + * + * - Proxy groups pointing to objects which are outside of the IDs set are also ignored. + * This way we avoid high-poly character body pulled into the dependency graph when it's coming + * from a library into an animation file and the dependency graph constructed for a proxy rig. */ + +class FromIDsBuilderPipeline : public AbstractBuilderPipeline { + public: + FromIDsBuilderPipeline( + ::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer, ID **ids, int num_ids); + + protected: + virtual unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override; + virtual unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override; + + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; + + private: + ID **ids_; + const int num_ids_; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.cc b/source/blender/depsgraph/intern/builder/pipeline_render.cc new file mode 100644 index 00000000000..50a37d0d3e4 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_render.cc @@ -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. + */ + +#include "pipeline_render.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +namespace blender { +namespace deg { + +RenderBuilderPipeline::RenderBuilderPipeline(::Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) + : AbstractBuilderPipeline(graph, bmain, scene, view_layer) +{ + deg_graph_->is_render_pipeline_depsgraph = true; +} + +void RenderBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder) +{ + node_builder.build_scene_render(scene_, view_layer_); +} + +void RenderBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder) +{ + relation_builder.build_scene_render(scene_, view_layer_); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.h b/source/blender/depsgraph/intern/builder/pipeline_render.h new file mode 100644 index 00000000000..df7f9e0de68 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_render.h @@ -0,0 +1,41 @@ +/* + * 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.h" + +namespace blender { +namespace deg { + +class RenderBuilderPipeline : public AbstractBuilderPipeline { + public: + RenderBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + + protected: + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc b/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc new file mode 100644 index 00000000000..3223f17f349 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.cc @@ -0,0 +1,48 @@ +/* + * 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_view_layer.h" + +#include "intern/builder/deg_builder_nodes.h" +#include "intern/builder/deg_builder_relations.h" +#include "intern/depsgraph.h" + +namespace blender { +namespace deg { + +ViewLayerBuilderPipeline::ViewLayerBuilderPipeline(::Depsgraph *graph, + Main *bmain, + Scene *scene, + ViewLayer *view_layer) + : AbstractBuilderPipeline(graph, bmain, scene, view_layer) +{ +} + +void ViewLayerBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder) +{ + node_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY); +} + +void ViewLayerBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder) +{ + relation_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY); +} + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h new file mode 100644 index 00000000000..fbd7b98acad --- /dev/null +++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h @@ -0,0 +1,41 @@ +/* + * 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.h" + +namespace blender { +namespace deg { + +class ViewLayerBuilderPipeline : public AbstractBuilderPipeline { + public: + ViewLayerBuilderPipeline(::Depsgraph *graph, Main *bmain, Scene *scene, ViewLayer *view_layer); + + protected: + virtual void build_nodes(DepsgraphNodeBuilder &node_builder) override; + virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; +}; + +} // namespace deg +} // namespace blender diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index c11d051e663..fb933cb38f3 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -43,12 +43,11 @@ #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_debug.h" -#include "builder/deg_builder.h" -#include "builder/deg_builder_cache.h" -#include "builder/deg_builder_cycle.h" -#include "builder/deg_builder_nodes.h" #include "builder/deg_builder_relations.h" -#include "builder/deg_builder_transitive.h" +#include "builder/pipeline_compositor.h" +#include "builder/pipeline_from_ids.h" +#include "builder/pipeline_render.h" +#include "builder/pipeline_view_layer.h" #include "intern/debug/deg_debug.h" @@ -209,65 +208,14 @@ struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle) /* ******************** */ /* Graph Building API's */ -static void graph_build_finalize_common(deg::Depsgraph *deg_graph, Main *bmain) -{ - /* Detect and solve cycles. */ - deg::deg_graph_detect_cycles(deg_graph); - /* Simplify the graph by removing redundant relations (to optimize - * traversal later). */ - /* TODO: it would be useful to have an option to disable this in cases where - * it is causing trouble. */ - if (G.debug_value == 799) { - deg::deg_graph_transitive_reduction(deg_graph); - } - /* Store pointers to commonly used valuated datablocks. */ - deg_graph->scene_cow = (Scene *)deg_graph->get_cow_id(°_graph->scene->id); - /* Flush visibility layer and re-schedule nodes for update. */ - deg::deg_graph_build_finalize(bmain, deg_graph); - DEG_graph_on_visible_update(bmain, reinterpret_cast<::Depsgraph *>(deg_graph), false); -#if 0 - if (!DEG_debug_consistency_check(deg_graph)) { - printf("Consistency validation failed, ABORTING!\n"); - abort(); - } -#endif - /* Relations are up to date. */ - deg_graph->need_update = false; -} - /* 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) { - double start_time = 0.0; - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - start_time = PIL_check_seconds_timer(); - } - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - /* Perform sanity checks. */ - BLI_assert(BLI_findindex(&scene->view_layers, view_layer) != -1); - BLI_assert(deg_graph->scene == scene); - BLI_assert(deg_graph->view_layer == view_layer); - deg::DepsgraphBuilderCache builder_cache; - /* Generate all the nodes in the graph first */ - deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache); - node_builder.begin_build(); - node_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY); - node_builder.end_build(); - /* Hook up relationships between operations - to determine evaluation order. */ - deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache); - relation_builder.begin_build(); - relation_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY); - relation_builder.build_copy_on_write_relations(); - relation_builder.build_driver_relations(); - /* Finalize building. */ - graph_build_finalize_common(deg_graph, bmain); - /* Finish statistics. */ - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + deg::ViewLayerBuilderPipeline builder(graph, bmain, scene, view_layer); + builder.build(); } void DEG_graph_build_for_render_pipeline(Depsgraph *graph, @@ -275,170 +223,17 @@ void DEG_graph_build_for_render_pipeline(Depsgraph *graph, Scene *scene, ViewLayer *view_layer) { - double start_time = 0.0; - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - start_time = PIL_check_seconds_timer(); - } - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - /* Perform sanity checks. */ - BLI_assert(deg_graph->scene == scene); - deg_graph->is_render_pipeline_depsgraph = true; - deg::DepsgraphBuilderCache builder_cache; - /* Generate all the nodes in the graph first */ - deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache); - node_builder.begin_build(); - node_builder.build_scene_render(scene, view_layer); - node_builder.end_build(); - /* Hook up relationships between operations - to determine evaluation - * order. */ - deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache); - relation_builder.begin_build(); - relation_builder.build_scene_render(scene, view_layer); - relation_builder.build_copy_on_write_relations(); - relation_builder.build_driver_relations(); - /* Finalize building. */ - graph_build_finalize_common(deg_graph, bmain); - /* Finish statistics. */ - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + deg::RenderBuilderPipeline builder(graph, bmain, scene, view_layer); + builder.build(); } void DEG_graph_build_for_compositor_preview( Depsgraph *graph, Main *bmain, Scene *scene, struct ViewLayer *view_layer, bNodeTree *nodetree) { - double start_time = 0.0; - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - start_time = PIL_check_seconds_timer(); - } - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - /* Perform sanity checks. */ - BLI_assert(deg_graph->scene == scene); - deg_graph->is_render_pipeline_depsgraph = true; - deg::DepsgraphBuilderCache builder_cache; - /* Generate all the nodes in the graph first */ - deg::DepsgraphNodeBuilder node_builder(bmain, deg_graph, &builder_cache); - node_builder.begin_build(); - node_builder.build_scene_render(scene, view_layer); - node_builder.build_nodetree(nodetree); - node_builder.end_build(); - /* Hook up relationships between operations - to determine evaluation - * order. */ - deg::DepsgraphRelationBuilder relation_builder(bmain, deg_graph, &builder_cache); - relation_builder.begin_build(); - relation_builder.build_scene_render(scene, view_layer); - relation_builder.build_nodetree(nodetree); - relation_builder.build_copy_on_write_relations(); - relation_builder.build_driver_relations(); - /* Finalize building. */ - graph_build_finalize_common(deg_graph, bmain); - /* Finish statistics. */ - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + deg::CompositorBuilderPipeline builder(graph, bmain, scene, view_layer, nodetree); + builder.build(); } -/* Optimized builders for dependency graph built from a given set of IDs. - * - * General notes: - * - * - We pull in all bases if their objects are in the set of IDs. This allows to have proper - * visibility and other flags assigned to the objects. - * All other bases (the ones which points to object which is outside of the set of IDs) are - * completely ignored. - * - * - Proxy groups pointing to objects which are outside of the IDs set are also ignored. - * This way we avoid high-poly character body pulled into the dependency graph when it's coming - * from a library into an animation file and the dependency graph constructed for a proxy rig. */ - -namespace blender { -namespace deg { -namespace { - -class DepsgraphFromIDsFilter { - public: - DepsgraphFromIDsFilter(ID **ids, const int num_ids) - { - for (int i = 0; i < num_ids; ++i) { - ids_.add(ids[i]); - } - } - - bool contains(ID *id) - { - return ids_.contains(id); - } - - protected: - Set<ID *> ids_; -}; - -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) - { - } - - virtual bool need_pull_base_into_graph(Base *base) override - { - if (!filter_.contains(&base->object->id)) { - return false; - } - return DepsgraphNodeBuilder::need_pull_base_into_graph(base); - } - - virtual void build_object_proxy_group(Object *object, bool is_visible) override - { - if (object->proxy_group == nullptr) { - return; - } - if (!filter_.contains(&object->proxy_group->id)) { - return; - } - DepsgraphNodeBuilder::build_object_proxy_group(object, is_visible); - } - - protected: - DepsgraphFromIDsFilter filter_; -}; - -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) - { - } - - virtual bool need_pull_base_into_graph(Base *base) override - { - if (!filter_.contains(&base->object->id)) { - return false; - } - return DepsgraphRelationBuilder::need_pull_base_into_graph(base); - } - - virtual void build_object_proxy_group(Object *object) override - { - if (object->proxy_group == nullptr) { - return; - } - if (!filter_.contains(&object->proxy_group->id)) { - return; - } - DepsgraphRelationBuilder::build_object_proxy_group(object); - } - - protected: - DepsgraphFromIDsFilter filter_; -}; - -} // namespace -} // namespace deg -} // namespace blender - void DEG_graph_build_from_ids(Depsgraph *graph, Main *bmain, Scene *scene, @@ -446,40 +241,8 @@ void DEG_graph_build_from_ids(Depsgraph *graph, ID **ids, const int num_ids) { - double start_time = 0.0; - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - start_time = PIL_check_seconds_timer(); - } - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - /* Perform sanity checks. */ - BLI_assert(BLI_findindex(&scene->view_layers, view_layer) != -1); - BLI_assert(deg_graph->scene == scene); - BLI_assert(deg_graph->view_layer == view_layer); - deg::DepsgraphBuilderCache builder_cache; - /* Generate all the nodes in the graph first */ - deg::DepsgraphFromIDsNodeBuilder node_builder(bmain, deg_graph, &builder_cache, ids, num_ids); - node_builder.begin_build(); - node_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY); - for (int i = 0; i < num_ids; ++i) { - node_builder.build_id(ids[i]); - } - node_builder.end_build(); - /* Hook up relationships between operations - to determine evaluation order. */ - deg::DepsgraphFromIDsRelationBuilder relation_builder( - bmain, deg_graph, &builder_cache, ids, num_ids); - relation_builder.begin_build(); - relation_builder.build_view_layer(scene, view_layer, deg::DEG_ID_LINKED_DIRECTLY); - for (int i = 0; i < num_ids; ++i) { - relation_builder.build_id(ids[i]); - } - relation_builder.build_copy_on_write_relations(); - relation_builder.build_driver_relations(); - /* Finalize building. */ - graph_build_finalize_common(deg_graph, bmain); - /* Finish statistics. */ - if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) { - printf("Depsgraph built in %f seconds.\n", PIL_check_seconds_timer() - start_time); - } + deg::FromIDsBuilderPipeline builder(graph, bmain, scene, view_layer, ids, num_ids); + builder.build(); } /* Tag graph relations for update. */ diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 848275eb899..1d8fba21857 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -48,11 +48,8 @@ #include "BKE_idtype.h" #include "BKE_node.h" #include "BKE_scene.h" -#include "BKE_workspace.h" - -#define new new_ #include "BKE_screen.h" -#undef new +#include "BKE_workspace.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" @@ -786,6 +783,7 @@ void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type) DEG_graph_id_type_tag(depsgraph, ID_LA); DEG_graph_id_type_tag(depsgraph, ID_WO); DEG_graph_id_type_tag(depsgraph, ID_SCE); + DEG_graph_id_type_tag(depsgraph, ID_SIM); } const int id_type_index = BKE_idtype_idcode_to_index(id_type); deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index c1e1ed3036d..89df41944e5 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -120,6 +120,7 @@ union NestedIDHackTempStorage { Scene scene; Tex tex; World world; + Simulation simulation; }; /* Set nested owned ID pointers to nullptr. */ @@ -137,6 +138,7 @@ void nested_id_hack_discard_pointers(ID *id_cow) SPECIAL_CASE(ID_MA, Material, nodetree) SPECIAL_CASE(ID_TE, Tex, nodetree) SPECIAL_CASE(ID_WO, World, nodetree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree) SPECIAL_CASE(ID_CU, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) @@ -185,6 +187,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage SPECIAL_CASE(ID_MA, Material, nodetree, material) SPECIAL_CASE(ID_TE, Tex, nodetree, tex) SPECIAL_CASE(ID_WO, World, nodetree, world) + SPECIAL_CASE(ID_SIM, Simulation, nodetree, simulation) SPECIAL_CASE(ID_CU, Curve, key, curve) SPECIAL_CASE(ID_LT, Lattice, key, lattice) @@ -224,6 +227,7 @@ void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id) SPECIAL_CASE(ID_SCE, Scene, nodetree) SPECIAL_CASE(ID_TE, Tex, nodetree) SPECIAL_CASE(ID_WO, World, nodetree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree) SPECIAL_CASE(ID_CU, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) @@ -261,6 +265,7 @@ void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow) SPECIAL_CASE(ID_SCE, Scene, nodetree, bNodeTree) SPECIAL_CASE(ID_TE, Tex, nodetree, bNodeTree) SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree) + SPECIAL_CASE(ID_SIM, Simulation, nodetree, bNodeTree) SPECIAL_CASE(ID_CU, Curve, key, Key) SPECIAL_CASE(ID_LT, Lattice, key, Key) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index cfcd4e0c65a..f85b03dc517 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC intern/draw_color_management.c intern/draw_common.c intern/draw_debug.c + intern/draw_fluid.c intern/draw_hair.c intern/draw_instance_data.c intern/draw_manager.c @@ -186,10 +187,10 @@ set(LIB ) data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC) -data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC) -data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC) +data_to_c_simple(engines/eevee/shaders/closure_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/common_uniforms_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/common_utiltex_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lights_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC) @@ -204,8 +205,8 @@ data_to_c_simple(engines/eevee/shaders/lightprobe_grid_display_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_grid_fill_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_planar_display_vert.glsl SRC) -data_to_c_simple(engines/eevee/shaders/lit_surface_frag.glsl SRC) -data_to_c_simple(engines/eevee/shaders/lit_surface_vert.glsl SRC) +data_to_c_simple(engines/eevee/shaders/lookdev_world_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/closure_lit_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_bloom_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_dof_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/effect_dof_frag.glsl SRC) @@ -229,7 +230,6 @@ data_to_c_simple(engines/eevee/shaders/object_motion_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/prepass_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/prepass_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/shadow_accum_frag.glsl SRC) - data_to_c_simple(engines/eevee/shaders/shadow_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/shadow_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/bsdf_lut_frag.glsl SRC) @@ -240,9 +240,14 @@ data_to_c_simple(engines/eevee/shaders/octahedron_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/cubemap_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/bsdf_sampling_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/raytrace_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/renderpass_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/renderpass_postprocess_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/ltc_lib.glsl SRC) data_to_c_simple(engines/eevee/shaders/ssr_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/surface_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/surface_geom.glsl SRC) +data_to_c_simple(engines/eevee/shaders/surface_lib.glsl SRC) +data_to_c_simple(engines/eevee/shaders/surface_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/update_noise_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/volumetric_accum_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/volumetric_lib.glsl SRC) @@ -288,6 +293,8 @@ data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC) data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC) data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC) +data_to_c_simple(intern/shaders/common_math_lib.glsl SRC) +data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC) data_to_c_simple(intern/shaders/common_view_lib.glsl SRC) data_to_c_simple(intern/shaders/common_fxaa_lib.glsl SRC) data_to_c_simple(intern/shaders/common_smaa_lib.glsl SRC) @@ -397,6 +404,13 @@ data_to_c_simple(engines/overlay/shaders/xray_fade_frag.glsl SRC) list(APPEND INC ) +if(WITH_MOD_FLUID) + list(APPEND INC + ../../../intern/mantaflow/extern + ) + add_definitions(-DWITH_FLUID) +endif() + if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c index 4a3cc36ddef..05cd6426911 100644 --- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c @@ -54,24 +54,30 @@ extern char datatoc_common_view_lib_glsl[]; static void eevee_create_shader_depth_of_field(const bool use_alpha) { - char *frag = BLI_string_joinN(datatoc_common_view_lib_glsl, datatoc_effect_dof_frag_glsl); - e_data.dof_downsample_sh[use_alpha] = DRW_shader_create_fullscreen( - frag, + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.dof_downsample_sh[use_alpha] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_dof_frag_glsl, + lib, use_alpha ? "#define USE_ALPHA_DOF\n" "#define STEP_DOWNSAMPLE\n" : "#define STEP_DOWNSAMPLE\n"); - e_data.dof_scatter_sh[use_alpha] = DRW_shader_create(datatoc_effect_dof_vert_glsl, - NULL, - frag, - use_alpha ? "#define USE_ALPHA_DOF\n" - "#define STEP_SCATTER\n" : - "#define STEP_SCATTER\n"); - e_data.dof_resolve_sh[use_alpha] = DRW_shader_create_fullscreen(frag, - use_alpha ? - "#define USE_ALPHA_DOF\n" - "#define STEP_RESOLVE\n" : - "#define STEP_RESOLVE\n"); - MEM_freeN(frag); + + e_data.dof_scatter_sh[use_alpha] = DRW_shader_create_with_shaderlib( + datatoc_effect_dof_vert_glsl, + NULL, + datatoc_effect_dof_frag_glsl, + lib, + use_alpha ? "#define USE_ALPHA_DOF\n" + "#define STEP_SCATTER\n" : + "#define STEP_SCATTER\n"); + + e_data.dof_resolve_sh[use_alpha] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_dof_frag_glsl, + lib, + use_alpha ? "#define USE_ALPHA_DOF\n" + "#define STEP_RESOLVE\n" : + "#define STEP_RESOLVE\n"); } int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *UNUSED(sldata), diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index d49bb5a268c..05189e6cdc7 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -32,6 +32,8 @@ #include "DNA_world_types.h" +#include "IMB_imbuf.h" + #include "eevee_private.h" #include "eevee_engine.h" /* own include */ diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 72187dc29eb..a4c07584b86 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -326,38 +326,28 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat { DRW_PASS_CREATE(psl->probe_background, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); DRWShadingGroup *grp = NULL; + EEVEE_lookdev_cache_init(vedata, sldata, psl->probe_background, pinfo, &grp); - Scene *scene = draw_ctx->scene; - World *wo = scene->world; - - /* LookDev */ - EEVEE_lookdev_cache_init(vedata, sldata, &grp, psl->probe_background, wo, pinfo); + if (grp == NULL) { + Scene *scene = draw_ctx->scene; + World *world = (scene->world) ? scene->world : EEVEE_world_default_get(); - if (!grp && wo) { - struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, wo, VAR_WORLD_PROBE); + const int options = VAR_WORLD_BACKGROUND | VAR_WORLD_PROBE; + struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options); grp = DRW_shgroup_material_create(gpumat, psl->probe_background); DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); - /* TODO (fclem): remove those (need to clean the GLSL files). */ - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - 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, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_call(grp, geom, NULL); } - /* Fallback if shader fails or if not using nodetree. */ - if (grp == NULL) { - grp = DRW_shgroup_create(EEVEE_shaders_probe_default_sh_get(), psl->probe_background); - DRW_shgroup_uniform_vec3(grp, "color", G_draw.block.colorBackground, 1); - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); - DRW_shgroup_call(grp, geom, NULL); - } + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); + DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); } if (DRW_state_draw_support()) { diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index b044213e029..403a8e2af55 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -97,10 +97,9 @@ static void eevee_lookdev_hdri_preview_init(EEVEE_Data *vedata, EEVEE_ViewLayerD void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, - DRWShadingGroup **r_grp, DRWPass *pass, - World *UNUSED(world), - EEVEE_LightProbesInfo *pinfo) + EEVEE_LightProbesInfo *pinfo, + DRWShadingGroup **r_shgrp) { EEVEE_StorageList *stl = vedata->stl; EEVEE_TextureList *txl = vedata->txl; @@ -153,89 +152,88 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, const View3DShading *shading = &v3d->shading; StudioLight *sl = BKE_studiolight_find(shading->lookdev_light, STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE); - if (sl && (sl->flag & STUDIOLIGHT_TYPE_WORLD)) { - GPUShader *shader = probe_render ? EEVEE_shaders_default_studiolight_sh_get() : - EEVEE_shaders_background_studiolight_sh_get(); + if (sl == NULL || (sl->flag & STUDIOLIGHT_TYPE_WORLD) == 0) { + return; + } + + GPUShader *shader = probe_render ? EEVEE_shaders_studiolight_probe_sh_get() : + EEVEE_shaders_studiolight_background_sh_get(); - const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - int cube_res = scene_eval->eevee.gi_cubemap_resolution; + const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); + int cube_res = scene_eval->eevee.gi_cubemap_resolution; - /* If one of the component is missing we start from scratch. */ - if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) || - (txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) || - (g_data->light_cache && g_data->light_cache->ref_res != cube_res)) { - eevee_lookdev_lightcache_delete(vedata); - } + /* If one of the component is missing we start from scratch. */ + if ((stl->lookdev_grid_data == NULL) || (stl->lookdev_cube_data == NULL) || + (txl->lookdev_grid_tx == NULL) || (txl->lookdev_cube_tx == NULL) || + (g_data->light_cache && g_data->light_cache->ref_res != cube_res)) { + eevee_lookdev_lightcache_delete(vedata); + } - if (stl->lookdev_lightcache == NULL) { + if (stl->lookdev_lightcache == NULL) { #if defined(IRRADIANCE_SH_L2) - int grid_res = 4; + int grid_res = 4; #elif defined(IRRADIANCE_HL2) - int grid_res = 4; + int grid_res = 4; #endif - stl->lookdev_lightcache = EEVEE_lightcache_create( - 1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1}); - - /* XXX: Fix memleak. TODO find out why. */ - MEM_SAFE_FREE(stl->lookdev_cube_mips); - - /* We do this to use a special light cache for lookdev. - * This light-cache needs to be per viewport. But we need to - * have correct freeing when the viewport is closed. So we - * need to reference all textures to the txl and the memblocks - * to the stl. */ - stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data; - stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data; - stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips; - txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex; - txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex; - } - - g_data->light_cache = stl->lookdev_lightcache; - - DRWShadingGroup *grp = *r_grp = DRW_shgroup_create(shader, pass); - axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z); - DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix); - - if (probe_render) { - DRW_shgroup_uniform_float_copy( - grp, "studioLightIntensity", shading->studiolight_intensity); - BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); - DRW_shgroup_uniform_texture(grp, "image", sl->equirect_radiance_gputexture); - /* Do not fadeout when doing probe rendering, only when drawing the background */ - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); - } - else { - float background_alpha = g_data->background_alpha * shading->studiolight_background; - float studiolight_blur = powf(shading->studiolight_blur, 2.5f); - DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha); - DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur); - DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - 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); - - /* Do we need to recalc the lightprobes? */ - if (g_data->studiolight_index != sl->index || - g_data->studiolight_rot_z != shading->studiolight_rot_z || - g_data->studiolight_intensity != shading->studiolight_intensity || - g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution || - g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp || - g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) { - stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD; - g_data->studiolight_index = sl->index; - g_data->studiolight_rot_z = shading->studiolight_rot_z; - g_data->studiolight_intensity = shading->studiolight_intensity; - g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution; - g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp; - g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality; - } + stl->lookdev_lightcache = EEVEE_lightcache_create( + 1, 1, cube_res, 8, (int[3]){grid_res, grid_res, 1}); + + /* XXX: Fix memleak. TODO find out why. */ + MEM_SAFE_FREE(stl->lookdev_cube_mips); + + /* We do this to use a special light cache for lookdev. + * This light-cache needs to be per viewport. But we need to + * have correct freeing when the viewport is closed. So we + * need to reference all textures to the txl and the memblocks + * to the stl. */ + stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data; + stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data; + stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips; + txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex; + txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex; + } + + g_data->light_cache = stl->lookdev_lightcache; + + DRWShadingGroup *grp = DRW_shgroup_create(shader, pass); + axis_angle_to_mat3_single(g_data->studiolight_matrix, 'Z', shading->studiolight_rot_z); + DRW_shgroup_uniform_mat3(grp, "StudioLightMatrix", g_data->studiolight_matrix); + + if (probe_render) { + /* Avoid artifact with equirectangular mapping. */ + eGPUSamplerState state = (GPU_SAMPLER_FILTER | GPU_SAMPLER_REPEAT_S); + DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity); + BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); + DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state); + /* Do not fadeout when doing probe rendering, only when drawing the background */ + DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); + } + else { + float background_alpha = g_data->background_alpha * shading->studiolight_background; + float studiolight_blur = powf(shading->studiolight_blur, 2.5f); + DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", background_alpha); + DRW_shgroup_uniform_float_copy(grp, "studioLightBlur", studiolight_blur); + DRW_shgroup_uniform_texture(grp, "probeCubes", txl->lookdev_cube_tx); + } + + /* Common UBOs are setup latter. */ + *r_shgrp = grp; + + /* Do we need to recalc the lightprobes? */ + if (g_data->studiolight_index != sl->index || + g_data->studiolight_rot_z != shading->studiolight_rot_z || + g_data->studiolight_intensity != shading->studiolight_intensity || + g_data->studiolight_cubemap_res != scene->eevee.gi_cubemap_resolution || + g_data->studiolight_glossy_clamp != scene->eevee.gi_glossy_clamp || + g_data->studiolight_filter_quality != scene->eevee.gi_filter_quality) { + stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD; + g_data->studiolight_index = sl->index; + g_data->studiolight_rot_z = shading->studiolight_rot_z; + g_data->studiolight_intensity = shading->studiolight_intensity; + g_data->studiolight_cubemap_res = scene->eevee.gi_cubemap_resolution; + g_data->studiolight_glossy_clamp = scene->eevee.gi_glossy_clamp; + g_data->studiolight_filter_quality = scene->eevee.gi_filter_quality; } } } diff --git a/source/blender/draw/engines/eevee/eevee_lut_gen.c b/source/blender/draw/engines/eevee/eevee_lut_gen.c index 6cee05bf015..9b07a6908c3 100644 --- a/source/blender/draw/engines/eevee/eevee_lut_gen.c +++ b/source/blender/draw/engines/eevee/eevee_lut_gen.c @@ -31,6 +31,8 @@ #include "BLI_rand.h" #include "BLI_string_utils.h" +#include "eevee_private.h" + extern char datatoc_bsdf_lut_frag_glsl[]; extern char datatoc_btdf_lut_frag_glsl[]; extern char datatoc_bsdf_common_lib_glsl[]; @@ -45,15 +47,13 @@ static struct GPUTexture *create_ggx_lut_texture(int UNUSED(w), int UNUSED(h)) static float samples_len = 8192.0f; static float inv_samples_len = 1.0f / 8192.0f; - char *lib_str = BLI_string_joinN(datatoc_bsdf_common_lib_glsl, datatoc_bsdf_sampling_lib_glsl); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); - struct GPUShader *sh = DRW_shader_create_with_lib(datatoc_lightprobe_vert_glsl, - datatoc_lightprobe_geom_glsl, - datatoc_bsdf_lut_frag_glsl, - lib_str, - "#define HAMMERSLEY_SIZE 8192\n" - "#define BRDF_LUT_SIZE 64\n" - "#define NOISE_SIZE 64\n"); + struct GPUShader *sh = DRW_shader_create_with_shaderlib(datatoc_lightprobe_vert_glsl, + datatoc_lightprobe_geom_glsl, + datatoc_bsdf_lut_frag_glsl, + lib, + "#define HAMMERSLEY_SIZE 8192\n"); DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); @@ -105,16 +105,10 @@ static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h) static float a2 = 0.0f; static float inv_samples_len = 1.0f / 8192.0f; - char *frag_str = BLI_string_joinN( - datatoc_bsdf_common_lib_glsl, datatoc_bsdf_sampling_lib_glsl, datatoc_btdf_lut_frag_glsl); - - struct GPUShader *sh = DRW_shader_create_fullscreen(frag_str, - "#define HAMMERSLEY_SIZE 8192\n" - "#define BRDF_LUT_SIZE 64\n" - "#define NOISE_SIZE 64\n" - "#define LUT_SIZE 64\n"); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); - MEM_freeN(frag_str); + struct GPUShader *sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_btdf_lut_frag_glsl, lib, "#define HAMMERSLEY_SIZE 8192\n"); DRWPass *pass = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); @@ -194,4 +188,4 @@ static struct GPUTexture *create_ggx_refraction_lut_texture(int w, int h) MEM_freeN(data); return tex; -}
\ No newline at end of file +} diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 143945b637a..59e8c1407e6 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -56,37 +56,6 @@ static struct { float noise_offsets[3]; } e_data = {NULL}; /* Engine data */ -extern char datatoc_lights_lib_glsl[]; -extern char datatoc_lightprobe_lib_glsl[]; -extern char datatoc_ambient_occlusion_lib_glsl[]; -extern char datatoc_prepass_frag_glsl[]; -extern char datatoc_prepass_vert_glsl[]; -extern char datatoc_default_frag_glsl[]; -extern char datatoc_default_world_frag_glsl[]; -extern char datatoc_ltc_lib_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_irradiance_lib_glsl[]; -extern char datatoc_octahedron_lib_glsl[]; -extern char datatoc_cubemap_lib_glsl[]; -extern char datatoc_lit_surface_frag_glsl[]; -extern char datatoc_lit_surface_vert_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; -extern char datatoc_ssr_lib_glsl[]; -extern char datatoc_shadow_vert_glsl[]; -extern char datatoc_lightprobe_geom_glsl[]; -extern char datatoc_lightprobe_vert_glsl[]; -extern char datatoc_background_vert_glsl[]; -extern char datatoc_update_noise_frag_glsl[]; -extern char datatoc_volumetric_vert_glsl[]; -extern char datatoc_volumetric_geom_glsl[]; -extern char datatoc_volumetric_frag_glsl[]; -extern char datatoc_volumetric_lib_glsl[]; -extern char datatoc_gpu_shader_uniform_color_frag_glsl[]; - typedef struct EeveeMaterialCache { struct DRWShadingGroup *depth_grp; struct DRWShadingGroup *shading_grp; @@ -238,46 +207,6 @@ void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const d DRW_draw_pass(psl->update_noise_pass); } -void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float (*r_viewvecs)[4]) -{ - /* view vectors for the corners of the view frustum. - * Can be used to recreate the world space position easily */ - float view_vecs[4][4] = { - {-1.0f, -1.0f, -1.0f, 1.0f}, - {1.0f, -1.0f, -1.0f, 1.0f}, - {-1.0f, 1.0f, -1.0f, 1.0f}, - {-1.0f, -1.0f, 1.0f, 1.0f}, - }; - - /* convert the view vectors to view space */ - const bool is_persp = (winmat[3][3] == 0.0f); - for (int i = 0; i < 4; i++) { - mul_project_m4_v3(invproj, view_vecs[i]); - /* normalized trick see: - * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ - if (is_persp) { - /* Divide XY by Z. */ - mul_v2_fl(view_vecs[i], 1.0f / view_vecs[i][2]); - } - } - - /** - * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and - * view_vecs[1] is the vector going from the near-bottom-left corner to - * the far-top-right corner. - * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner - * when Z = 1, and top-left corner if Z = 1. - * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed) - * distance from the near plane to the far clip plane. - */ - copy_v4_v4(r_viewvecs[0], view_vecs[0]); - - /* we need to store the differences */ - r_viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0]; - r_viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1]; - r_viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2]; -} - void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, EEVEE_StorageList *stl, @@ -305,15 +234,6 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, } { - /* Update view_vecs */ - float invproj[4][4], winmat[4][4]; - DRW_view_winmat_get(NULL, winmat, false); - DRW_view_winmat_get(NULL, invproj, true); - - EEVEE_update_viewvecs(invproj, winmat, sldata->common_data.view_vecs); - } - - { /* Update noise Framebuffer. */ GPU_framebuffer_ensure_config( &fbl->update_noise_fb, @@ -391,39 +311,28 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { DRW_PASS_CREATE(psl->background_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); DRWShadingGroup *grp = NULL; + EEVEE_lookdev_cache_init(vedata, sldata, psl->background_ps, NULL, &grp); - Scene *scene = draw_ctx->scene; - World *wo = scene->world; - - EEVEE_lookdev_cache_init(vedata, sldata, &grp, psl->background_ps, wo, NULL); + if (grp == NULL) { + Scene *scene = draw_ctx->scene; + World *world = (scene->world) ? scene->world : EEVEE_world_default_get(); - if (!grp && wo) { - struct GPUMaterial *gpumat = EEVEE_material_get( - vedata, scene, NULL, wo, VAR_WORLD_BACKGROUND); + const int options = VAR_WORLD_BACKGROUND; + struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options); grp = DRW_shgroup_material_create(gpumat, psl->background_ps); DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); - /* TODO (fclem): remove those (need to clean the GLSL files). */ - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); - DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); - DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); - DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); - DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); - DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); - DRW_shgroup_call(grp, geom, NULL); } - /* Fallback if shader fails or if not using nodetree. */ - if (grp == NULL) { - GPUShader *sh = EEVEE_shaders_default_background_sh_get(); - grp = DRW_shgroup_create(sh, psl->background_ps); - DRW_shgroup_uniform_vec3(grp, "color", G_draw.block.colorBackground, 1); - DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); - DRW_shgroup_call(grp, geom, NULL); - } + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); + DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); + DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); } #define EEVEE_PASS_CREATE(pass, state) \ diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c index 1cedd334d67..d2f3a13eb7c 100644 --- a/source/blender/draw/engines/eevee/eevee_mist.c +++ b/source/blender/draw/engines/eevee/eevee_mist.c @@ -56,14 +56,10 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; if (e_data.mist_sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_effect_mist_frag_glsl); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); - e_data.mist_sh = DRW_shader_create_fullscreen(frag_str, "#define FIRST_PASS\n"); - - MEM_freeN(frag_str); + e_data.mist_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_mist_frag_glsl, lib, "#define FIRST_PASS\n"); } /* Create FrameBuffer. */ @@ -98,11 +94,11 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) } } else { - float near = -sldata->common_data.view_vecs[0][2]; - float range = sldata->common_data.view_vecs[1][2]; + float near = DRW_view_near_distance_get(NULL); + float far = DRW_view_far_distance_get(NULL); /* Fallback */ g_data->mist_start = near; - g_data->mist_inv_dist = 1.0f / fabsf(range); + g_data->mist_inv_dist = 1.0f / fabsf(far - near); g_data->mist_falloff = 1.0f; } diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index 586ee780f1d..0e8a2493b01 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -68,27 +68,23 @@ extern char datatoc_common_view_lib_glsl[]; static void eevee_create_shader_motion_blur(void) { - e_data.motion_blur_sh = DRW_shader_create_fullscreen( - datatoc_effect_motion_blur_frag_glsl, - "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n"); - e_data.motion_blur_object_sh = DRW_shader_create_with_lib(datatoc_object_motion_vert_glsl, - NULL, - datatoc_object_motion_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); - e_data.velocity_tiles_sh = DRW_shader_create_fullscreen( - datatoc_effect_velocity_tile_frag_glsl, - "#define TILE_GATHER\n" - "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n"); +#define TILE_SIZE_STR "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n" + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + e_data.motion_blur_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_motion_blur_frag_glsl, lib, TILE_SIZE_STR); + e_data.motion_blur_object_sh = DRW_shader_create_with_shaderlib( + datatoc_object_motion_vert_glsl, NULL, datatoc_object_motion_frag_glsl, lib, NULL); + + e_data.motion_blur_hair_sh = DRW_shader_create_with_shaderlib(datatoc_object_motion_vert_glsl, + NULL, + datatoc_object_motion_frag_glsl, + lib, + "#define HAIR\n"); + + e_data.velocity_tiles_sh = DRW_shader_create_fullscreen(datatoc_effect_velocity_tile_frag_glsl, + "#define TILE_GATHER\n" TILE_SIZE_STR); e_data.velocity_tiles_expand_sh = DRW_shader_create_fullscreen( - datatoc_effect_velocity_tile_frag_glsl, - "#define TILE_EXPANSION\n" - "#define EEVEE_VELOCITY_TILE_SIZE " STRINGIFY(EEVEE_VELOCITY_TILE_SIZE) "\n"); - - char *vert = BLI_string_joinN(datatoc_common_hair_lib_glsl, datatoc_object_motion_vert_glsl); - e_data.motion_blur_hair_sh = DRW_shader_create_with_lib( - vert, NULL, datatoc_object_motion_frag_glsl, datatoc_common_view_lib_glsl, "#define HAIR\n"); - MEM_freeN(vert); + datatoc_effect_velocity_tile_frag_glsl, "#define TILE_EXPANSION\n" TILE_SIZE_STR); } int EEVEE_motion_blur_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c index a075210967c..1929bbb9b98 100644 --- a/source/blender/draw/engines/eevee/eevee_occlusion.c +++ b/source/blender/draw/engines/eevee/eevee_occlusion.c @@ -53,17 +53,14 @@ extern char datatoc_effect_gtao_frag_glsl[]; static void eevee_create_shader_occlusion(void) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_ambient_occlusion_lib_glsl, - datatoc_effect_gtao_frag_glsl); - - e_data.gtao_sh = DRW_shader_create_fullscreen(frag_str, NULL); - e_data.gtao_layer_sh = DRW_shader_create_fullscreen(frag_str, "#define LAYERED_DEPTH\n"); - e_data.gtao_debug_sh = DRW_shader_create_fullscreen(frag_str, "#define DEBUG_AO\n"); - - MEM_freeN(frag_str); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.gtao_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_gtao_frag_glsl, lib, NULL); + e_data.gtao_layer_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_gtao_frag_glsl, lib, "#define LAYERED_DEPTH\n"); + e_data.gtao_debug_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_gtao_frag_glsl, lib, "#define DEBUG_AO\n"); } int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 81a50d36e17..c202bbbb754 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -163,7 +163,7 @@ enum { VAR_MAT_MESH = (1 << 0), VAR_MAT_VOLUME = (1 << 1), VAR_MAT_HAIR = (1 << 2), - VAR_MAT_PROBE = (1 << 3), + /* VAR_MAT_PROBE = (1 << 3), UNUSED */ VAR_MAT_BLEND = (1 << 4), VAR_MAT_LOOKDEV = (1 << 5), VAR_MAT_HOLDOUT = (1 << 6), @@ -747,7 +747,6 @@ typedef struct EEVEE_EffectsInfo { * - sizeof(bool) == sizeof(int) in GLSL so use int in C */ typedef struct EEVEE_CommonUniformBuffer { float prev_persmat[4][4]; /* mat4 */ - float view_vecs[2][4]; /* vec4[2] */ float mip_ratio[10][4]; /* vec2[10] */ /* Ambient Occlusion */ /* -- 16 byte aligned -- */ @@ -1001,7 +1000,6 @@ void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata, void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_materials_free(void); void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]); -void EEVEE_update_viewvecs(float invproj[4][4], float winmat[4][4], float (*r_viewvecs)[4]); void EEVEE_material_renderpasses_init(EEVEE_Data *vedata); void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); @@ -1057,15 +1055,14 @@ void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]); /* eevee_shaders.c */ 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_probe_filter_glossy_sh_get(void); -struct GPUShader *EEVEE_shaders_probe_default_sh_get(void); struct GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void); struct GPUShader *EEVEE_shaders_probe_filter_visibility_sh_get(void); struct GPUShader *EEVEE_shaders_probe_grid_fill_sh_get(void); struct GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void); -struct GPUShader *EEVEE_shaders_default_studiolight_sh_get(void); -struct GPUShader *EEVEE_shaders_default_background_sh_get(void); -struct GPUShader *EEVEE_shaders_background_studiolight_sh_get(void); +struct GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void); +struct GPUShader *EEVEE_shaders_studiolight_background_sh_get(void); struct GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void); struct GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void); struct GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void); @@ -1077,6 +1074,7 @@ struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo); Material *EEVEE_material_default_diffuse_get(void); Material *EEVEE_material_default_glossy_get(void); Material *EEVEE_material_default_error_get(void); +World *EEVEE_world_default_get(void); struct GPUMaterial *EEVEE_material_default_get(struct Scene *scene, Material *ma, int options); struct GPUMaterial *EEVEE_material_get( EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options); @@ -1300,10 +1298,9 @@ void EEVEE_render_update_passes(struct RenderEngine *engine, /** eevee_lookdev.c */ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, - DRWShadingGroup **grp, DRWPass *pass, - struct World *world, - EEVEE_LightProbesInfo *pinfo); + EEVEE_LightProbesInfo *pinfo, + DRWShadingGroup **r_shgrp); void EEVEE_lookdev_draw(EEVEE_Data *vedata); /** eevee_engine.c */ diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index be771d7cf42..089d8b7a287 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -199,12 +199,10 @@ void EEVEE_renderpasses_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ve EEVEE_RENDERPASSES_WITH_POST_PROCESSING) > 0; if (needs_post_processing) { if (e_data.postprocess_sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_renderpass_postprocess_frag_glsl); - e_data.postprocess_sh = DRW_shader_create_fullscreen(frag_str, NULL); - MEM_freeN(frag_str); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.postprocess_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_renderpass_postprocess_frag_glsl, lib, NULL); } DRW_PASS_CREATE(psl->renderpass_pass, DRW_STATE_WRITE_COLOR); diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c index 32d758dba4b..a1755e60c06 100644 --- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c +++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c @@ -48,30 +48,12 @@ static struct { struct GPUTexture *depth_src; } e_data = {{NULL}}; /* Engine data */ -extern char datatoc_ambient_occlusion_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_octahedron_lib_glsl[]; -extern char datatoc_cubemap_lib_glsl[]; extern char datatoc_effect_ssr_frag_glsl[]; -extern char datatoc_lightprobe_lib_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; static struct GPUShader *eevee_effects_screen_raytrace_shader_get(int options) { if (e_data.ssr_sh[options] == NULL) { - char *ssr_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_ambient_occlusion_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_raytrace_lib_glsl, - datatoc_effect_ssr_frag_glsl); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); DynStr *ds_defines = BLI_dynstr_new(); BLI_dynstr_append(ds_defines, SHADER_DEFINES); @@ -91,9 +73,9 @@ static struct GPUShader *eevee_effects_screen_raytrace_shader_get(int options) char *ssr_define_str = BLI_dynstr_get_cstring(ds_defines); BLI_dynstr_free(ds_defines); - e_data.ssr_sh[options] = DRW_shader_create_fullscreen(ssr_shader_str, ssr_define_str); + e_data.ssr_sh[options] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_ssr_frag_glsl, lib, ssr_define_str); - MEM_freeN(ssr_shader_str); MEM_freeN(ssr_define_str); } diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 09e74c84948..5f125d395d3 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -28,6 +28,8 @@ #include "BLI_dynstr.h" #include "BLI_string_utils.h" +#include "DNA_world_types.h" + #include "MEM_guardedalloc.h" #include "GPU_material.h" @@ -40,19 +42,17 @@ static const char *filter_defines = "#define HAMMERSLEY_SIZE " STRINGIFY(HAMMERSLEY_SIZE) "\n" #if defined(IRRADIANCE_SH_L2) - "#define IRRADIANCE_SH_L2\n" -#elif defined(IRRADIANCE_CUBEMAP) - "#define IRRADIANCE_CUBEMAP\n" + "#define IRRADIANCE_SH_L2\n"; #elif defined(IRRADIANCE_HL2) - "#define IRRADIANCE_HL2\n" + "#define IRRADIANCE_HL2\n"; #endif - "#define NOISE_SIZE 64\n"; static struct { + /* Lookdev */ + struct GPUShader *studiolight_probe_sh; + struct GPUShader *studiolight_background_sh; + /* Probes */ - struct GPUShader *probe_default_sh; - struct GPUShader *probe_default_studiolight_sh; - struct GPUShader *probe_background_studiolight_sh; struct GPUShader *probe_grid_display_sh; struct GPUShader *probe_cube_display_sh; struct GPUShader *probe_planar_display_sh; @@ -70,17 +70,16 @@ static struct { struct GPUShader *taa_resolve_reproject_sh; /* General purpose Shaders. */ - struct GPUShader *default_background; + struct GPUShader *lookdev_background; struct GPUShader *update_noise_sh; /* Shader strings */ - char *frag_shader_lib; - char *vert_shader_str; - char *vert_shadow_shader_str; - char *vert_background_shader_str; - char *vert_volume_shader_str; - char *geom_volume_shader_str; - char *volume_shader_lib; + char *closure_lit_lib; + char *surface_lit_frag; + char *surface_prepass_frag; + char *surface_geom_barycentric; + + DRWShaderLibrary *lib; /* LookDev Materials */ Material *glossy_mat; @@ -88,6 +87,8 @@ static struct { Material *error_mat; + World *default_world; + /* Default Material */ struct { bNodeTree *ntree; @@ -103,16 +104,39 @@ static struct { } world; } e_data = {NULL}; /* Engine data */ -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; +extern char datatoc_common_hair_lib_glsl[]; +extern char datatoc_common_math_lib_glsl[]; +extern char datatoc_common_math_geom_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; +extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; extern char datatoc_ambient_occlusion_lib_glsl[]; extern char datatoc_background_vert_glsl[]; -extern char datatoc_common_hair_lib_glsl[]; +extern char datatoc_bsdf_common_lib_glsl[]; +extern char datatoc_bsdf_lut_frag_glsl[]; +extern char datatoc_bsdf_sampling_lib_glsl[]; +extern char datatoc_btdf_lut_frag_glsl[]; +extern char datatoc_closure_lib_glsl[]; +extern char datatoc_common_uniforms_lib_glsl[]; +extern char datatoc_common_utiltex_lib_glsl[]; extern char datatoc_cubemap_lib_glsl[]; -extern char datatoc_default_world_frag_glsl[]; +extern char datatoc_default_frag_glsl[]; +extern char datatoc_lookdev_world_frag_glsl[]; +extern char datatoc_effect_bloom_frag_glsl[]; +extern char datatoc_effect_dof_frag_glsl[]; +extern char datatoc_effect_dof_vert_glsl[]; +extern char datatoc_effect_downsample_cube_frag_glsl[]; +extern char datatoc_effect_downsample_frag_glsl[]; +extern char datatoc_effect_gtao_frag_glsl[]; +extern char datatoc_effect_minmaxz_frag_glsl[]; +extern char datatoc_effect_mist_frag_glsl[]; +extern char datatoc_effect_motion_blur_frag_glsl[]; +extern char datatoc_effect_ssr_frag_glsl[]; +extern char datatoc_effect_subsurface_frag_glsl[]; +extern char datatoc_effect_temporal_aa_glsl[]; +extern char datatoc_effect_translucency_frag_glsl[]; +extern char datatoc_effect_velocity_resolve_frag_glsl[]; +extern char datatoc_effect_velocity_tile_frag_glsl[]; extern char datatoc_irradiance_lib_glsl[]; extern char datatoc_lightprobe_cube_display_frag_glsl[]; extern char datatoc_lightprobe_cube_display_vert_glsl[]; @@ -131,72 +155,111 @@ extern char datatoc_lightprobe_planar_downsample_geom_glsl[]; extern char datatoc_lightprobe_planar_downsample_vert_glsl[]; extern char datatoc_lightprobe_vert_glsl[]; extern char datatoc_lights_lib_glsl[]; -extern char datatoc_lit_surface_frag_glsl[]; -extern char datatoc_lit_surface_vert_glsl[]; +extern char datatoc_closure_lit_lib_glsl[]; extern char datatoc_ltc_lib_glsl[]; +extern char datatoc_object_motion_frag_glsl[]; +extern char datatoc_object_motion_vert_glsl[]; extern char datatoc_octahedron_lib_glsl[]; extern char datatoc_prepass_frag_glsl[]; +extern char datatoc_prepass_vert_glsl[]; extern char datatoc_raytrace_lib_glsl[]; +extern char datatoc_renderpass_lib_glsl[]; +extern char datatoc_renderpass_postprocess_frag_glsl[]; +extern char datatoc_shadow_accum_frag_glsl[]; +extern char datatoc_shadow_frag_glsl[]; extern char datatoc_shadow_vert_glsl[]; extern char datatoc_ssr_lib_glsl[]; +extern char datatoc_surface_frag_glsl[]; +extern char datatoc_surface_geom_glsl[]; +extern char datatoc_surface_lib_glsl[]; +extern char datatoc_surface_vert_glsl[]; extern char datatoc_update_noise_frag_glsl[]; +extern char datatoc_volumetric_accum_frag_glsl[]; extern char datatoc_volumetric_frag_glsl[]; extern char datatoc_volumetric_geom_glsl[]; +extern char datatoc_volumetric_integration_frag_glsl[]; extern char datatoc_volumetric_lib_glsl[]; +extern char datatoc_volumetric_resolve_frag_glsl[]; +extern char datatoc_volumetric_scatter_frag_glsl[]; extern char datatoc_volumetric_vert_glsl[]; -/* Velocity Resolve */ -extern char datatoc_effect_velocity_resolve_frag_glsl[]; - -/* Temporal Sampling */ -extern char datatoc_effect_temporal_aa_glsl[]; - /* *********** FUNCTIONS *********** */ +static void eevee_shader_library_ensure(void) +{ + if (e_data.lib == NULL) { + e_data.lib = DRW_shader_library_create(); + /* NOTE: Theses needs to be ordered by dependencies. */ + DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib); + DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib); + DRW_SHADER_LIB_ADD(e_data.lib, renderpass_lib); + DRW_SHADER_LIB_ADD(e_data.lib, bsdf_common_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_utiltex_lib); + DRW_SHADER_LIB_ADD(e_data.lib, bsdf_sampling_lib); + DRW_SHADER_LIB_ADD(e_data.lib, cubemap_lib); + DRW_SHADER_LIB_ADD(e_data.lib, raytrace_lib); + DRW_SHADER_LIB_ADD(e_data.lib, ambient_occlusion_lib); + DRW_SHADER_LIB_ADD(e_data.lib, octahedron_lib); + DRW_SHADER_LIB_ADD(e_data.lib, irradiance_lib); + DRW_SHADER_LIB_ADD(e_data.lib, lightprobe_lib); + DRW_SHADER_LIB_ADD(e_data.lib, ltc_lib); + DRW_SHADER_LIB_ADD(e_data.lib, lights_lib); + DRW_SHADER_LIB_ADD(e_data.lib, surface_lib); + DRW_SHADER_LIB_ADD(e_data.lib, volumetric_lib); + DRW_SHADER_LIB_ADD(e_data.lib, closure_lib); + DRW_SHADER_LIB_ADD(e_data.lib, ssr_lib); + + /* Add one for each Closure */ + e_data.closure_lit_lib = BLI_string_joinN(datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl, + datatoc_closure_lit_lib_glsl); + + DRW_shader_library_add_file(e_data.lib, e_data.closure_lit_lib, "closure_lit_lib.glsl"); + + e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib, + datatoc_surface_frag_glsl); + + e_data.surface_prepass_frag = DRW_shader_library_create_shader_string( + e_data.lib, datatoc_prepass_frag_glsl); + + e_data.surface_geom_barycentric = DRW_shader_library_create_shader_string( + e_data.lib, datatoc_surface_geom_glsl); + } +} + void EEVEE_shaders_lightprobe_shaders_init(void) { BLI_assert(e_data.probe_filter_glossy_sh == NULL); - char *shader_str = NULL; - - shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_lightprobe_filter_glossy_frag_glsl); - - e_data.probe_filter_glossy_sh = DRW_shader_create( - datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, shader_str, filter_defines); - - e_data.probe_default_sh = DRW_shader_create_with_lib(datatoc_background_vert_glsl, - NULL, - datatoc_default_world_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); - MEM_freeN(shader_str); + eevee_shader_library_ensure(); - shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_lightprobe_filter_diffuse_frag_glsl); + e_data.probe_filter_glossy_sh = DRW_shader_create_with_shaderlib( + datatoc_lightprobe_vert_glsl, + datatoc_lightprobe_geom_glsl, + datatoc_lightprobe_filter_glossy_frag_glsl, + e_data.lib, + filter_defines); - e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen(shader_str, filter_defines); + e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_lightprobe_filter_diffuse_frag_glsl, e_data.lib, filter_defines); - MEM_freeN(shader_str); + e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_lightprobe_filter_visibility_frag_glsl, e_data.lib, filter_defines); - shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_lightprobe_filter_visibility_frag_glsl); - - e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen(shader_str, filter_defines); - - MEM_freeN(shader_str); - - e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen(datatoc_lightprobe_grid_fill_frag_glsl, - filter_defines); + e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_lightprobe_grid_fill_frag_glsl, e_data.lib, filter_defines); e_data.probe_planar_downsample_sh = DRW_shader_create( datatoc_lightprobe_planar_downsample_vert_glsl, @@ -207,70 +270,18 @@ void EEVEE_shaders_lightprobe_shaders_init(void) void EEVEE_shaders_material_shaders_init(void) { - e_data.frag_shader_lib = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_ambient_occlusion_lib_glsl, - datatoc_raytrace_lib_glsl, - datatoc_ssr_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_irradiance_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_ltc_lib_glsl, - datatoc_lights_lib_glsl, - /* Add one for each Closure */ - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_lit_surface_frag_glsl, - datatoc_volumetric_lib_glsl); - - e_data.volume_shader_lib = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_ambient_occlusion_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_irradiance_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_ltc_lib_glsl, - datatoc_lights_lib_glsl, - datatoc_volumetric_lib_glsl, - datatoc_volumetric_frag_glsl); - - e_data.vert_shader_str = BLI_string_joinN( - datatoc_common_view_lib_glsl, datatoc_common_hair_lib_glsl, datatoc_lit_surface_vert_glsl); - - e_data.vert_shadow_shader_str = BLI_string_joinN( - datatoc_common_view_lib_glsl, datatoc_common_hair_lib_glsl, datatoc_shadow_vert_glsl); - - e_data.vert_background_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_background_vert_glsl); - - e_data.vert_volume_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_volumetric_vert_glsl); - - e_data.geom_volume_shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_volumetric_geom_glsl); + eevee_shader_library_ensure(); } -GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void) +DRWShaderLibrary *EEVEE_shader_lib_get(void) { - return e_data.probe_filter_glossy_sh; + eevee_shader_library_ensure(); + return e_data.lib; } -GPUShader *EEVEE_shaders_probe_default_sh_get(void) +GPUShader *EEVEE_shaders_probe_filter_glossy_sh_get(void) { - return e_data.probe_default_sh; + return e_data.probe_filter_glossy_sh; } GPUShader *EEVEE_shaders_probe_filter_diffuse_sh_get(void) @@ -293,59 +304,40 @@ GPUShader *EEVEE_shaders_probe_planar_downsample_sh_get(void) return e_data.probe_planar_downsample_sh; } -GPUShader *EEVEE_shaders_default_studiolight_sh_get(void) +GPUShader *EEVEE_shaders_studiolight_probe_sh_get(void) { - if (e_data.probe_default_studiolight_sh == NULL) { - e_data.probe_default_studiolight_sh = DRW_shader_create_with_lib( - datatoc_background_vert_glsl, - NULL, - datatoc_default_world_frag_glsl, - datatoc_common_view_lib_glsl, - "#define LOOKDEV\n"); - } - return e_data.probe_default_studiolight_sh; + if (e_data.studiolight_probe_sh == NULL) { + e_data.studiolight_probe_sh = DRW_shader_create_with_shaderlib(datatoc_background_vert_glsl, + NULL, + datatoc_lookdev_world_frag_glsl, + e_data.lib, + SHADER_DEFINES); + } + return e_data.studiolight_probe_sh; } -GPUShader *EEVEE_shaders_background_studiolight_sh_get(void) +GPUShader *EEVEE_shaders_studiolight_background_sh_get(void) { - if (e_data.probe_background_studiolight_sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_default_world_frag_glsl); - - e_data.probe_background_studiolight_sh = DRW_shader_create_with_lib( + if (e_data.studiolight_background_sh == NULL) { + e_data.studiolight_background_sh = DRW_shader_create_with_shaderlib( datatoc_background_vert_glsl, NULL, - frag_str, - datatoc_common_view_lib_glsl, + datatoc_lookdev_world_frag_glsl, + e_data.lib, "#define LOOKDEV_BG\n" SHADER_DEFINES); - - MEM_freeN(frag_str); } - return e_data.probe_background_studiolight_sh; + return e_data.studiolight_background_sh; } GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void) { if (e_data.probe_cube_display_sh == NULL) { - char *shader_str = BLI_string_joinN(datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_lightprobe_cube_display_frag_glsl); - - char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_lightprobe_cube_display_vert_glsl); - - e_data.probe_cube_display_sh = DRW_shader_create(vert_str, NULL, shader_str, SHADER_DEFINES); - - MEM_freeN(vert_str); - MEM_freeN(shader_str); + e_data.probe_cube_display_sh = DRW_shader_create_with_shaderlib( + datatoc_lightprobe_cube_display_vert_glsl, + NULL, + datatoc_lightprobe_cube_display_frag_glsl, + e_data.lib, + SHADER_DEFINES); } return e_data.probe_cube_display_sh; } @@ -353,22 +345,12 @@ GPUShader *EEVEE_shaders_probe_cube_display_sh_get(void) GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void) { if (e_data.probe_grid_display_sh == NULL) { - char *shader_str = BLI_string_joinN(datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_irradiance_lib_glsl, - datatoc_lightprobe_lib_glsl, - datatoc_lightprobe_grid_display_frag_glsl); - - char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_lightprobe_grid_display_vert_glsl); - - e_data.probe_grid_display_sh = DRW_shader_create(vert_str, NULL, shader_str, filter_defines); - - MEM_freeN(vert_str); - MEM_freeN(shader_str); + e_data.probe_grid_display_sh = DRW_shader_create_with_shaderlib( + datatoc_lightprobe_grid_display_vert_glsl, + NULL, + datatoc_lightprobe_grid_display_frag_glsl, + e_data.lib, + filter_defines); } return e_data.probe_grid_display_sh; } @@ -376,16 +358,12 @@ GPUShader *EEVEE_shaders_probe_grid_display_sh_get(void) GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void) { if (e_data.probe_planar_display_sh == NULL) { - char *vert_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_lightprobe_planar_display_vert_glsl); - - char *shader_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_lightprobe_planar_display_frag_glsl); - - e_data.probe_planar_display_sh = DRW_shader_create(vert_str, NULL, shader_str, NULL); - - MEM_freeN(vert_str); - MEM_freeN(shader_str); + e_data.probe_planar_display_sh = DRW_shader_create_with_shaderlib( + datatoc_lightprobe_planar_display_vert_glsl, + NULL, + datatoc_lightprobe_planar_display_frag_glsl, + e_data.lib, + NULL); } return e_data.probe_planar_display_sh; } @@ -393,34 +371,17 @@ GPUShader *EEVEE_shaders_probe_planar_display_sh_get(void) GPUShader *EEVEE_shaders_velocity_resolve_sh_get(void) { if (e_data.velocity_resolve_sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_common_uniforms_lib_glsl, - datatoc_common_view_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_effect_velocity_resolve_frag_glsl); - - e_data.velocity_resolve_sh = DRW_shader_create_fullscreen(frag_str, NULL); - - MEM_freeN(frag_str); + e_data.velocity_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_velocity_resolve_frag_glsl, e_data.lib, NULL); } return e_data.velocity_resolve_sh; } -GPUShader *EEVEE_shaders_default_background_sh_get(void) -{ - if (e_data.default_background == NULL) { - e_data.default_background = DRW_shader_create_with_lib(datatoc_background_vert_glsl, - NULL, - datatoc_default_world_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); - } - return e_data.default_background; -} - GPUShader *EEVEE_shaders_update_noise_sh_get(void) { if (e_data.update_noise_sh == NULL) { - e_data.update_noise_sh = DRW_shader_create_fullscreen(datatoc_update_noise_frag_glsl, NULL); + e_data.update_noise_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_update_noise_frag_glsl, e_data.lib, NULL); } return e_data.update_noise_sh; } @@ -437,13 +398,8 @@ GPUShader *EEVEE_shaders_taa_resolve_sh_get(EEVEE_EffectsFlag enabled_effects) sh = &e_data.taa_resolve_sh; } if (*sh == NULL) { - char *frag_str = BLI_string_joinN(datatoc_common_uniforms_lib_glsl, - datatoc_common_view_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_effect_temporal_aa_glsl); - - *sh = DRW_shader_create_fullscreen(frag_str, define); - MEM_freeN(frag_str); + *sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_temporal_aa_glsl, e_data.lib, define); } return *sh; @@ -583,6 +539,18 @@ struct bNodeTree *EEVEE_shader_default_world_nodetree(World *wo) return e_data.world.ntree; } +World *EEVEE_world_default_get(void) +{ + if (e_data.default_world == NULL) { + e_data.default_world = BKE_id_new_nomain(ID_WO, "EEVEEE default world"); + copy_v3_fl(&e_data.default_world->horr, 0.0f); + e_data.default_world->use_nodes = 0; + e_data.default_world->nodetree = NULL; + BLI_listbase_clear(&e_data.default_world->gpumaterial); + } + return e_data.default_world; +} + static char *eevee_get_defines(int options) { char *str = NULL; @@ -605,7 +573,7 @@ static char *eevee_get_defines(int options) if ((options & VAR_MAT_HAIR) != 0) { BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); } - if ((options & (VAR_MAT_PROBE | VAR_WORLD_PROBE)) != 0) { + if ((options & VAR_WORLD_PROBE) != 0) { BLI_dynstr_append(ds, "#define PROBE_CAPTURE\n"); } if ((options & VAR_MAT_HASH) != 0) { @@ -635,13 +603,13 @@ static char *eevee_get_vert(int options) char *str = NULL; if ((options & VAR_MAT_VOLUME) != 0) { - str = BLI_strdup(e_data.vert_volume_shader_str); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_vert_glsl); } else if ((options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0) { - str = BLI_strdup(e_data.vert_background_shader_str); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_background_vert_glsl); } else { - str = BLI_strdup(e_data.vert_shader_str); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_vert_glsl); } return str; @@ -652,7 +620,7 @@ static char *eevee_get_geom(int options) char *str = NULL; if ((options & VAR_MAT_VOLUME) != 0) { - str = BLI_strdup(e_data.geom_volume_shader_str); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_geom_glsl); } return str; @@ -663,18 +631,36 @@ static char *eevee_get_frag(int options) char *str = NULL; if ((options & VAR_MAT_VOLUME) != 0) { - str = BLI_strdup(e_data.volume_shader_lib); + str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_frag_glsl); } else if ((options & VAR_MAT_DEPTH) != 0) { - str = BLI_string_joinN(e_data.frag_shader_lib, datatoc_prepass_frag_glsl); + str = BLI_strdup(e_data.surface_prepass_frag); } else { - str = BLI_strdup(e_data.frag_shader_lib); + str = BLI_strdup(e_data.surface_lit_frag); } return str; } +static void eevee_material_post_eval(GPUMaterial *mat, + int options, + const char **UNUSED(vert_code), + const char **geom_code, + const char **UNUSED(frag_lib), + const char **UNUSED(defines)) +{ + const bool is_hair = (options & VAR_MAT_HAIR) != 0; + const bool is_mesh = (options & VAR_MAT_MESH) != 0; + + /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used. + * Note: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */ + if (!is_hair && is_mesh && GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) && + *geom_code == NULL) { + *geom_code = e_data.surface_geom_barycentric; + } +} + static struct GPUMaterial *eevee_material_get_ex( struct Scene *scene, Material *ma, World *wo, int options, bool deferred) { @@ -702,14 +688,16 @@ static struct GPUMaterial *eevee_material_get_ex( char *frag = eevee_get_frag(options); if (ma) { + GPUMaterialEvalCallbackFn cbfn = &eevee_material_post_eval; + bNodeTree *ntree = !is_default ? ma->nodetree : EEVEE_shader_default_surface_nodetree(ma); mat = DRW_shader_create_from_material( - scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred); + scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, cbfn); } else { bNodeTree *ntree = !is_default ? wo->nodetree : EEVEE_shader_default_world_nodetree(wo); mat = DRW_shader_create_from_world( - scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred); + scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, NULL); } MEM_SAFE_FREE(defines); @@ -764,30 +752,31 @@ struct GPUMaterial *EEVEE_material_get( void EEVEE_shaders_free(void) { - MEM_SAFE_FREE(e_data.frag_shader_lib); - MEM_SAFE_FREE(e_data.vert_shader_str); - MEM_SAFE_FREE(e_data.vert_shadow_shader_str); - MEM_SAFE_FREE(e_data.vert_background_shader_str); - MEM_SAFE_FREE(e_data.vert_volume_shader_str); - MEM_SAFE_FREE(e_data.geom_volume_shader_str); - MEM_SAFE_FREE(e_data.volume_shader_lib); - DRW_SHADER_FREE_SAFE(e_data.default_background); + MEM_SAFE_FREE(e_data.closure_lit_lib); + MEM_SAFE_FREE(e_data.surface_prepass_frag); + MEM_SAFE_FREE(e_data.surface_lit_frag); + MEM_SAFE_FREE(e_data.surface_geom_barycentric); + DRW_SHADER_FREE_SAFE(e_data.lookdev_background); DRW_SHADER_FREE_SAFE(e_data.update_noise_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_default_sh); DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh); DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh); DRW_SHADER_FREE_SAFE(e_data.probe_filter_visibility_sh); DRW_SHADER_FREE_SAFE(e_data.probe_grid_fill_sh); DRW_SHADER_FREE_SAFE(e_data.probe_planar_downsample_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_default_studiolight_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_background_studiolight_sh); + DRW_SHADER_FREE_SAFE(e_data.studiolight_probe_sh); + DRW_SHADER_FREE_SAFE(e_data.studiolight_background_sh); DRW_SHADER_FREE_SAFE(e_data.probe_grid_display_sh); DRW_SHADER_FREE_SAFE(e_data.probe_cube_display_sh); DRW_SHADER_FREE_SAFE(e_data.probe_planar_display_sh); 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); + DRW_SHADER_LIB_FREE_SAFE(e_data.lib); + if (e_data.default_world) { + BKE_id_free(NULL, e_data.default_world); + e_data.default_world = NULL; + } if (e_data.glossy_mat) { BKE_id_free(NULL, e_data.glossy_mat); e_data.glossy_mat = NULL; diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c index 8c50b26b45f..0da356b75ac 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows.c +++ b/source/blender/draw/engines/eevee/eevee_shadows.c @@ -42,11 +42,6 @@ static struct { extern char datatoc_shadow_vert_glsl[]; extern char datatoc_shadow_frag_glsl[]; extern char datatoc_shadow_accum_frag_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; -extern char datatoc_lights_lib_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; void eevee_contact_shadow_setup(const Light *la, EEVEE_Shadow *evsh) { @@ -65,23 +60,13 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata) const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); if (!e_data.shadow_sh) { - e_data.shadow_sh = DRW_shader_create_with_lib(datatoc_shadow_vert_glsl, - NULL, - datatoc_shadow_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); - } + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); - if (!e_data.shadow_accum_sh) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_raytrace_lib_glsl, - datatoc_lights_lib_glsl, - datatoc_shadow_accum_frag_glsl); + e_data.shadow_sh = DRW_shader_create_with_shaderlib( + datatoc_shadow_vert_glsl, NULL, datatoc_shadow_frag_glsl, lib, NULL); - e_data.shadow_accum_sh = DRW_shader_create_fullscreen(frag_str, SHADER_DEFINES); - MEM_freeN(frag_str); + e_data.shadow_accum_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_shadow_accum_frag_glsl, lib, SHADER_DEFINES); } if (!sldata->lights) { diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c index ef4588f4aca..637c5201afc 100644 --- a/source/blender/draw/engines/eevee/eevee_subsurface.c +++ b/source/blender/draw/engines/eevee/eevee_subsurface.c @@ -38,41 +38,19 @@ static struct { struct GPUShader *sss_sh[3]; } e_data = {{NULL}}; /* Engine data */ -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_common_uniforms_lib_glsl[]; -extern char datatoc_lights_lib_glsl[]; -extern char datatoc_raytrace_lib_glsl[]; -extern char datatoc_octahedron_lib_glsl[]; -extern char datatoc_cubemap_lib_glsl[]; -extern char datatoc_bsdf_sampling_lib_glsl[]; -extern char datatoc_bsdf_common_lib_glsl[]; extern char datatoc_effect_subsurface_frag_glsl[]; extern char datatoc_effect_translucency_frag_glsl[]; static void eevee_create_shader_subsurface(void) { - char *frag_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_effect_subsurface_frag_glsl); - - /* TODO(fclem) remove some of these dependencies. */ - char *frag_translucent_str = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_bsdf_sampling_lib_glsl, - datatoc_raytrace_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_lights_lib_glsl, - datatoc_effect_translucency_frag_glsl); - - e_data.sss_sh[0] = DRW_shader_create_fullscreen(frag_str, "#define FIRST_PASS\n"); - e_data.sss_sh[1] = DRW_shader_create_fullscreen(frag_str, "#define SECOND_PASS\n"); - e_data.sss_sh[2] = DRW_shader_create_fullscreen(frag_translucent_str, - "#define EEVEE_TRANSLUCENCY\n" SHADER_DEFINES); - - MEM_freeN(frag_translucent_str); - MEM_freeN(frag_str); + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.sss_sh[0] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_subsurface_frag_glsl, lib, "#define FIRST_PASS\n"); + e_data.sss_sh[1] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_subsurface_frag_glsl, lib, "#define SECOND_PASS\n"); + e_data.sss_sh[2] = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_effect_translucency_frag_glsl, lib, "#define EEVEE_TRANSLUCENCY\n" SHADER_DEFINES); } void EEVEE_subsurface_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *UNUSED(vedata)) diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 300022e97a9..57d5e54290e 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -44,16 +44,12 @@ #include "DEG_depsgraph_query.h" -#include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_material.h" #include "GPU_texture.h" #include "eevee_private.h" static struct { - char *volumetric_common_lib; - char *volumetric_common_lights_lib; - struct GPUShader *volumetric_clear_sh; struct GPUShader *scatter_sh; struct GPUShader *scatter_with_lights_sh; @@ -97,57 +93,48 @@ extern char datatoc_common_fullscreen_vert_glsl[]; static void eevee_create_shader_volumes(void) { - e_data.volumetric_common_lib = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_volumetric_lib_glsl); - - e_data.volumetric_common_lights_lib = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_common_uniforms_lib_glsl, - datatoc_bsdf_common_lib_glsl, - datatoc_octahedron_lib_glsl, - datatoc_cubemap_lib_glsl, - datatoc_irradiance_lib_glsl, - datatoc_lights_lib_glsl, - datatoc_volumetric_lib_glsl); - - e_data.volumetric_clear_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_frag_glsl, - e_data.volumetric_common_lib, - "#define VOLUMETRICS\n" - "#define CLEAR\n"); - e_data.scatter_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_scatter_frag_glsl, - e_data.volumetric_common_lights_lib, - SHADER_DEFINES - "#define VOLUMETRICS\n" - "#define VOLUME_SHADOW\n"); - e_data.scatter_with_lights_sh = DRW_shader_create_with_lib(datatoc_volumetric_vert_glsl, - datatoc_volumetric_geom_glsl, - datatoc_volumetric_scatter_frag_glsl, - e_data.volumetric_common_lights_lib, - SHADER_DEFINES - "#define VOLUMETRICS\n" - "#define VOLUME_LIGHTING\n" - "#define VOLUME_SHADOW\n"); - e_data.volumetric_integration_sh = DRW_shader_create_with_lib( + DRWShaderLibrary *lib = EEVEE_shader_lib_get(); + + e_data.volumetric_clear_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl, + datatoc_volumetric_geom_glsl, + datatoc_volumetric_frag_glsl, + lib, + SHADER_DEFINES + "#define VOLUMETRICS\n" + "#define CLEAR\n"); + + e_data.scatter_sh = DRW_shader_create_with_shaderlib(datatoc_volumetric_vert_glsl, + datatoc_volumetric_geom_glsl, + datatoc_volumetric_scatter_frag_glsl, + lib, + SHADER_DEFINES + "#define VOLUMETRICS\n" + "#define VOLUME_SHADOW\n"); + + e_data.scatter_with_lights_sh = DRW_shader_create_with_shaderlib( + datatoc_volumetric_vert_glsl, + datatoc_volumetric_geom_glsl, + datatoc_volumetric_scatter_frag_glsl, + lib, + SHADER_DEFINES + "#define VOLUMETRICS\n" + "#define VOLUME_LIGHTING\n" + "#define VOLUME_SHADOW\n"); + + e_data.volumetric_integration_sh = DRW_shader_create_with_shaderlib( datatoc_volumetric_vert_glsl, datatoc_volumetric_geom_glsl, datatoc_volumetric_integration_frag_glsl, - e_data.volumetric_common_lib, + lib, USE_VOLUME_OPTI ? "#extension GL_ARB_shader_image_load_store: enable\n" "#extension GL_ARB_shading_language_420pack: enable\n" - "#define USE_VOLUME_OPTI\n" : - NULL); - e_data.volumetric_resolve_sh = DRW_shader_create_with_lib(datatoc_common_fullscreen_vert_glsl, - NULL, - datatoc_volumetric_resolve_frag_glsl, - e_data.volumetric_common_lib, - NULL); - e_data.volumetric_accum_sh = DRW_shader_create_fullscreen(datatoc_volumetric_accum_frag_glsl, - NULL); + "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : + SHADER_DEFINES); + + e_data.volumetric_resolve_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_volumetric_resolve_frag_glsl, lib, SHADER_DEFINES); + e_data.volumetric_accum_sh = DRW_shader_create_fullscreen_with_shaderlib( + datatoc_volumetric_accum_frag_glsl, lib, SHADER_DEFINES); const float density[4] = {1.0f, 1.0f, 1.0f, 1.0f}; e_data.dummy_density = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, density); @@ -259,17 +246,11 @@ void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) common_data->vol_shadow_steps = 0; } - /* Update view_vecs */ - float invproj[4][4], winmat[4][4]; - DRW_view_winmat_get(NULL, winmat, false); - DRW_view_winmat_get(NULL, invproj, true); - EEVEE_update_viewvecs(invproj, winmat, sldata->common_data.view_vecs); - if (DRW_view_is_persp_get(NULL)) { float sample_distribution = scene_eval->eevee.volumetric_sample_distribution; sample_distribution = 4.0f * (max_ff(1.0f - sample_distribution, 1e-2f)); - const float clip_start = common_data->view_vecs[0][2]; + const float clip_start = DRW_view_near_distance_get(NULL); /* Negate */ float near = integration_start = min_ff(-integration_start, clip_start - 1e-4f); float far = integration_end = min_ff(-integration_end, near - 1e-4f); @@ -280,8 +261,8 @@ void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) common_data->vol_depth_param[2] = sample_distribution; } else { - const float clip_start = common_data->view_vecs[0][2]; - const float clip_end = clip_start + common_data->view_vecs[1][2]; + const float clip_start = DRW_view_near_distance_get(NULL); + const float clip_end = DRW_view_far_distance_get(NULL); integration_start = min_ff(integration_end, clip_start); integration_end = max_ff(-integration_end, clip_end); @@ -504,12 +485,7 @@ static bool eevee_volume_object_mesh_init(Scene *scene, #endif if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS) /* && show_smoke */) { - if (!(fds->flags & FLUID_DOMAIN_USE_NOISE)) { - GPU_create_smoke(fmd, 0); - } - else if (fds->flags & FLUID_DOMAIN_USE_NOISE) { - GPU_create_smoke(fmd, 1); - } + DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE); BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(fmd)); } @@ -841,16 +817,13 @@ void EEVEE_volumes_free_smoke_textures(void) /* Free Smoke Textures after rendering */ LISTBASE_FOREACH (LinkData *, link, &e_data.smoke_domains) { FluidModifierData *fmd = (FluidModifierData *)link->data; - GPU_free_smoke(fmd); + DRW_smoke_free(fmd); } BLI_freelistN(&e_data.smoke_domains); } void EEVEE_volumes_free(void) { - MEM_SAFE_FREE(e_data.volumetric_common_lib); - MEM_SAFE_FREE(e_data.volumetric_common_lights_lib); - DRW_TEXTURE_FREE_SAFE(e_data.dummy_scatter); DRW_TEXTURE_FREE_SAFE(e_data.dummy_transmit); diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl index 57b16418696..2f6f8327f58 100644 --- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(raytrace_lib.glsl) + /* Based on Practical Realtime Strategies for Accurate Indirect Occlusion * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pptx @@ -24,12 +27,6 @@ #define MAX_SEARCH_ITER 32 #define MAX_LOD 6.0 -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - uniform sampler2D horizonBuffer; /* aoSettings flags */ @@ -243,6 +240,11 @@ float gtao_multibounce(float visibility, vec3 albedo) return max(x, ((x * a + b) * x + c) * x); } +float specular_occlusion(float NV, float AO, float roughness) +{ + return saturate(pow(NV + AO, roughness) - 1.0 + AO); +} + /* Use the right occlusion */ float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out vec3 bent_normal) { diff --git a/source/blender/draw/engines/eevee/shaders/background_vert.glsl b/source/blender/draw/engines/eevee/shaders/background_vert.glsl index aff8e0857f6..ab5d9a7ebe4 100644 --- a/source/blender/draw/engines/eevee/shaders/background_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/background_vert.glsl @@ -1,17 +1,17 @@ -in vec2 pos; +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) -out vec3 viewPosition; +in vec2 pos; -#ifndef VOLUMETRICS -/* necessary for compilation*/ -out vec3 worldPosition; -out vec3 worldNormal; -out vec3 viewNormal; -#endif +RESOURCE_ID_VARYING void main() { + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + PASS_RESOURCE_ID + gl_Position = vec4(pos, 1.0, 1.0); viewPosition = vec3(pos, -1.0); @@ -22,6 +22,6 @@ void main() #endif #ifdef USE_ATTR - pass_attr(viewPosition); + pass_attr(viewPosition, NormalMatrix, ModelMatrixInverse); #endif } diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index a8b8566edec..deedde64194 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -1,487 +1,7 @@ -#define M_PI 3.14159265358979323846 /* pi */ -#define M_2PI 6.28318530717958647692 /* 2*pi */ -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#define M_1_PI 0.318309886183790671538 /* 1/pi */ -#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */ -#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */ -#define FLT_MAX 3.402823e+38 +#pragma BLENDER_REQUIRE(common_math_lib.glsl) -#define LUT_SIZE 64 - -/* Buffers */ -uniform sampler2D colorBuffer; -uniform sampler2D depthBuffer; -uniform sampler2D maxzBuffer; -uniform sampler2D minzBuffer; -uniform sampler2DArray planarDepth; - -#define cameraForward ViewMatrixInverse[2].xyz -#define cameraPos ViewMatrixInverse[3].xyz -#define cameraVec \ - ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - worldPosition) : cameraForward) -#define viewCameraVec \ - ((ProjectionMatrix[3][3] == 0.0) ? normalize(-viewPosition) : vec3(0.0, 0.0, 1.0)) - -/* ------- Structures -------- */ - -/* ------ Lights ----- */ -struct LightData { - vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */ - vec4 color_spec; /* w : Spec Intensity */ - vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */ - vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */ - vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */ - vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */ -}; - -/* convenience aliases */ -#define l_color color_spec.rgb -#define l_spec color_spec.a -#define l_position position_influence.xyz -#define l_influence position_influence.w -#define l_sizex rightvec_sizex.w -#define l_sizey upvec_sizey.w -#define l_right rightvec_sizex.xyz -#define l_up upvec_sizey.xyz -#define l_forward forwardvec_type.xyz -#define l_type forwardvec_type.w -#define l_spot_size spotdata_radius_shadow.x -#define l_spot_blend spotdata_radius_shadow.y -#define l_radius spotdata_radius_shadow.z -#define l_shadowid spotdata_radius_shadow.w - -/* ------ Shadows ----- */ -#ifndef MAX_CASCADE_NUM -# define MAX_CASCADE_NUM 4 -#endif - -struct ShadowData { - vec4 near_far_bias_id; - vec4 contact_shadow_data; -}; - -struct ShadowCubeData { - mat4 shadowmat; - vec4 position; -}; - -struct ShadowCascadeData { - mat4 shadowmat[MAX_CASCADE_NUM]; - vec4 split_start_distances; - vec4 split_end_distances; - vec4 shadow_vec_id; -}; - -/* convenience aliases */ -#define sh_near near_far_bias_id.x -#define sh_far near_far_bias_id.y -#define sh_bias near_far_bias_id.z -#define sh_data_index near_far_bias_id.w -#define sh_contact_dist contact_shadow_data.x -#define sh_contact_offset contact_shadow_data.y -#define sh_contact_spread contact_shadow_data.z -#define sh_contact_thickness contact_shadow_data.w -#define sh_shadow_vec shadow_vec_id.xyz -#define sh_tex_index shadow_vec_id.w - -/* ------ Render Passes ----- */ -layout(std140) uniform renderpass_block -{ - bool renderPassDiffuse; - bool renderPassDiffuseLight; - bool renderPassGlossy; - bool renderPassGlossyLight; - bool renderPassEmit; - bool renderPassSSSColor; - bool renderPassEnvironment; -}; - -vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light) -{ - return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0); -} - -vec3 render_pass_sss_mask(vec3 sss_color) -{ - return renderPassSSSColor ? sss_color : vec3(0.0); -} - -vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light) -{ - return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0); -} - -vec3 render_pass_emission_mask(vec3 emission_light) -{ - return renderPassEmit ? emission_light : vec3(0.0); -} - -/* ------- Convenience functions --------- */ - -vec3 mul(mat3 m, vec3 v) -{ - return m * v; -} -mat3 mul(mat3 m1, mat3 m2) -{ - return m1 * m2; -} -vec3 transform_direction(mat4 m, vec3 v) -{ - return mat3(m) * v; -} -vec3 transform_point(mat4 m, vec3 v) -{ - return (m * vec4(v, 1.0)).xyz; -} -vec3 project_point(mat4 m, vec3 v) -{ - vec4 tmp = m * vec4(v, 1.0); - return tmp.xyz / tmp.w; -} - -#define min3(a, b, c) min(a, min(b, c)) -#define min4(a, b, c, d) min(a, min3(b, c, d)) -#define min5(a, b, c, d, e) min(a, min4(b, c, d, e)) -#define min6(a, b, c, d, e, f) min(a, min5(b, c, d, e, f)) -#define min7(a, b, c, d, e, f, g) min(a, min6(b, c, d, e, f, g)) -#define min8(a, b, c, d, e, f, g, h) min(a, min7(b, c, d, e, f, g, h)) -#define min9(a, b, c, d, e, f, g, h, i) min(a, min8(b, c, d, e, f, g, h, i)) - -#define max3(a, b, c) max(a, max(b, c)) -#define max4(a, b, c, d) max(a, max3(b, c, d)) -#define max5(a, b, c, d, e) max(a, max4(b, c, d, e)) -#define max6(a, b, c, d, e, f) max(a, max5(b, c, d, e, f)) -#define max7(a, b, c, d, e, f, g) max(a, max6(b, c, d, e, f, g)) -#define max8(a, b, c, d, e, f, g, h) max(a, max7(b, c, d, e, f, g, h)) -#define max9(a, b, c, d, e, f, g, h, i) max(a, max8(b, c, d, e, f, g, h, i)) - -#define avg3(a, b, c) (a + b + c) * (1.0 / 3.0) -#define avg4(a, b, c, d) (a + b + c + d) * (1.0 / 4.0) -#define avg5(a, b, c, d, e) (a + b + c + d + e) * (1.0 / 5.0) -#define avg6(a, b, c, d, e, f) (a + b + c + d + e + f) * (1.0 / 6.0) -#define avg7(a, b, c, d, e, f, g) (a + b + c + d + e + f + g) * (1.0 / 7.0) -#define avg8(a, b, c, d, e, f, g, h) (a + b + c + d + e + f + g + h) * (1.0 / 8.0) -#define avg9(a, b, c, d, e, f, g, h, i) (a + b + c + d + e + f + g + h + i) * (1.0 / 9.0) - -float min_v2(vec2 v) -{ - return min(v.x, v.y); -} -float min_v3(vec3 v) -{ - return min(v.x, min(v.y, v.z)); -} -float min_v4(vec4 v) -{ - return min(min(v.x, v.y), min(v.z, v.w)); -} -float max_v2(vec2 v) -{ - return max(v.x, v.y); -} -float max_v3(vec3 v) -{ - return max(v.x, max(v.y, v.z)); -} -float max_v4(vec4 v) -{ - return max(max(v.x, v.y), max(v.z, v.w)); -} - -float sum(vec2 v) -{ - return dot(vec2(1.0), v); -} -float sum(vec3 v) -{ - return dot(vec3(1.0), v); -} -float sum(vec4 v) -{ - return dot(vec4(1.0), v); -} - -float avg(vec2 v) -{ - return dot(vec2(1.0 / 2.0), v); -} -float avg(vec3 v) -{ - return dot(vec3(1.0 / 3.0), v); -} -float avg(vec4 v) -{ - return dot(vec4(1.0 / 4.0), v); -} - -float saturate(float a) -{ - return clamp(a, 0.0, 1.0); -} -vec2 saturate(vec2 a) -{ - return clamp(a, 0.0, 1.0); -} -vec3 saturate(vec3 a) -{ - return clamp(a, 0.0, 1.0); -} -vec4 saturate(vec4 a) -{ - return clamp(a, 0.0, 1.0); -} - -float distance_squared(vec2 a, vec2 b) -{ - a -= b; - return dot(a, a); -} -float distance_squared(vec3 a, vec3 b) -{ - a -= b; - return dot(a, a); -} -float len_squared(vec3 a) -{ - return dot(a, a); -} - -float inverse_distance(vec3 V) -{ - return max(1 / length(V), 1e-8); -} - -vec2 mip_ratio_interp(float mip) -{ - float low_mip = floor(mip); - return mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip); -} - -/* ------- RNG ------- */ - -float wang_hash_noise(uint s) -{ - s = (s ^ 61u) ^ (s >> 16u); - s *= 9u; - s = s ^ (s >> 4u); - s *= 0x27d4eb2du; - s = s ^ (s >> 15u); - - return fract(float(s) / 4294967296.0); -} - -/* ------- Fast Math ------- */ - -/* [Drobot2014a] Low Level Optimizations for GCN */ -float fast_sqrt(float v) -{ - return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1)); -} - -vec2 fast_sqrt(vec2 v) -{ - return intBitsToFloat(0x1fbd1df5 + (floatBitsToInt(v) >> 1)); -} - -/* [Eberly2014] GPGPU Programming for Games and Science */ -float fast_acos(float v) -{ - float res = -0.156583 * abs(v) + M_PI_2; - res *= fast_sqrt(1.0 - abs(v)); - return (v >= 0) ? res : M_PI - res; -} - -vec2 fast_acos(vec2 v) -{ - vec2 res = -0.156583 * abs(v) + M_PI_2; - res *= fast_sqrt(1.0 - abs(v)); - v.x = (v.x >= 0) ? res.x : M_PI - res.x; - v.y = (v.y >= 0) ? res.y : M_PI - res.y; - return v; -} - -float point_plane_projection_dist(vec3 lineorigin, vec3 planeorigin, vec3 planenormal) -{ - return dot(planenormal, planeorigin - lineorigin); -} - -float line_plane_intersect_dist(vec3 lineorigin, - vec3 linedirection, - vec3 planeorigin, - vec3 planenormal) -{ - return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection); -} - -float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec4 plane) -{ - vec3 plane_co = plane.xyz * (-plane.w / len_squared(plane.xyz)); - vec3 h = lineorigin - plane_co; - return -dot(plane.xyz, h) / dot(plane.xyz, linedirection); -} - -vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal) -{ - float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal); - return lineorigin + linedirection * dist; -} - -vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec4 plane) -{ - float dist = line_plane_intersect_dist(lineorigin, linedirection, plane); - return lineorigin + linedirection * dist; -} - -float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin) -{ - /* aligned plane normal */ - vec3 L = planeorigin - lineorigin; - float diskdist = length(L); - vec3 planenormal = -normalize(L); - return -diskdist / dot(planenormal, linedirection); -} - -vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin) -{ - float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin); - if (dist < 0) { - /* if intersection is behind we fake the intersection to be - * really far and (hopefully) not inside the radius of interest */ - dist = 1e16; - } - return lineorigin + linedirection * dist; -} - -float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection) -{ - float a = dot(linedirection, linedirection); - float b = dot(linedirection, lineorigin); - float c = dot(lineorigin, lineorigin) - 1; - - float dist = 1e15; - float determinant = b * b - a * c; - if (determinant >= 0) { - dist = (sqrt(determinant) - b) / a; - } - - return dist; -} - -float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection) -{ - /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ - */ - vec3 firstplane = (vec3(1.0) - lineorigin) / linedirection; - vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection; - vec3 furthestplane = max(firstplane, secondplane); - - return min_v3(furthestplane); -} - -/* Return texture coordinates to sample Surface LUT */ -vec2 lut_coords(float cosTheta, float roughness) -{ - float theta = acos(cosTheta); - vec2 coords = vec2(roughness, theta / M_PI_2); - - /* scale and bias coordinates, for correct filtered lookup */ - return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; -} - -vec2 lut_coords_ltc(float cosTheta, float roughness) -{ - vec2 coords = vec2(roughness, sqrt(1.0 - cosTheta)); - - /* scale and bias coordinates, for correct filtered lookup */ - return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; -} - -/* -- Tangent Space conversion -- */ -vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B) -{ - return T * vector.x + B * vector.y + N * vector.z; -} - -vec3 world_to_tangent(vec3 vector, vec3 N, vec3 T, vec3 B) -{ - return vec3(dot(T, vector), dot(B, vector), dot(N, vector)); -} - -void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B) -{ - vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - T = normalize(cross(UpVector, N)); - B = cross(N, T); -} - -/* ---- Opengl Depth conversion ---- */ - -float linear_depth(bool is_persp, float z, float zf, float zn) -{ - if (is_persp) { - return (zn * zf) / (z * (zn - zf) + zf); - } - else { - return (z * 2.0 - 1.0) * zf; - } -} - -float buffer_depth(bool is_persp, float z, float zf, float zn) -{ - if (is_persp) { - return (zf * (zn - z)) / (z * (zn - zf)); - } - else { - return (z / (zf * 2.0)) + 0.5; - } -} - -float get_view_z_from_depth(float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - float d = 2.0 * depth - 1.0; - return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); - } - else { - return viewVecs[0].z + depth * viewVecs[1].z; - } -} - -float get_depth_from_view_z(float z) -{ - if (ProjectionMatrix[3][3] == 0.0) { - float d = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2]; - return d * 0.5 + 0.5; - } - else { - return (z - viewVecs[0].z) / viewVecs[1].z; - } -} - -vec2 get_uvs_from_view(vec3 view) -{ - vec3 ndc = project_point(ProjectionMatrix, view); - return ndc.xy * 0.5 + 0.5; -} - -vec3 get_view_space_from_depth(vec2 uvcoords, float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - return vec3(viewVecs[0].xy + uvcoords * viewVecs[1].xy, 1.0) * get_view_z_from_depth(depth); - } - else { - return viewVecs[0].xyz + vec3(uvcoords, depth) * viewVecs[1].xyz; - } -} - -vec3 get_world_space_from_depth(vec2 uvcoords, float depth) -{ - return (ViewMatrixInverse * vec4(get_view_space_from_depth(uvcoords, depth), 1.0)).xyz; -} - -vec3 get_specular_reflection_dominant_dir(vec3 N, vec3 V, float roughness) +vec3 specular_dominant_dir(vec3 N, vec3 V, float roughness) { vec3 R = -reflect(V, N); float smoothness = 1.0 - roughness; @@ -489,13 +9,6 @@ vec3 get_specular_reflection_dominant_dir(vec3 N, vec3 V, float roughness) return normalize(mix(N, R, fac)); } -float specular_occlusion(float NV, float AO, float roughness) -{ - return saturate(pow(NV + AO, roughness) - 1.0 + AO); -} - -/* --- Refraction utils --- */ - float ior_from_f0(float f0) { float f = sqrt(f0); @@ -508,7 +21,7 @@ float f0_from_ior(float eta) return A * A; } -vec3 get_specular_refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior) +vec3 refraction_dominant_dir(vec3 N, vec3 V, float roughness, float ior) { /* TODO: This a bad approximation. Better approximation should fit * the refracted vector and roughness into the best prefiltered reflection @@ -527,128 +40,6 @@ vec3 get_specular_refraction_dominant_dir(vec3 N, vec3 V, float roughness, float return R; } -float get_btdf_lut(sampler2DArray btdf_lut_tex, float NV, float roughness, float ior) -{ - const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE; - - vec3 coords; - /* Try to compensate for the low resolution and interpolation error. */ - coords.x = (ior > 1.0) ? (0.9 + lut_scale_bias_texel_size.z) + - (0.1 - lut_scale_bias_texel_size.z) * f0_from_ior(ior) : - (0.9 + lut_scale_bias_texel_size.z) * ior * ior; - coords.y = 1.0 - saturate(NV); - coords.xy *= lut_scale_bias_texel_size.x; - coords.xy += lut_scale_bias_texel_size.y; - - const float lut_lvl_ofs = 4.0; /* First texture lvl of roughness. */ - const float lut_lvl_scale = 16.0; /* How many lvl of roughness in the lut. */ - - float mip = roughness * lut_lvl_scale; - float mip_floor = floor(mip); - - coords.z = lut_lvl_ofs + mip_floor + 1.0; - float btdf_high = textureLod(btdf_lut_tex, coords, 0.0).r; - - coords.z -= 1.0; - float btdf_low = textureLod(btdf_lut_tex, coords, 0.0).r; - - float btdf = (ior == 1.0) ? 1.0 : mix(btdf_low, btdf_high, mip - coords.z); - - return btdf; -} - -/* ---- Encode / Decode Normal buffer data ---- */ -/* From http://aras-p.info/texts/CompactNormalStorage.html - * Using Method #4: Spheremap Transform */ -vec2 normal_encode(vec3 n, vec3 view) -{ - float p = sqrt(n.z * 8.0 + 8.0); - return n.xy / p + 0.5; -} - -vec3 normal_decode(vec2 enc, vec3 view) -{ - vec2 fenc = enc * 4.0 - 2.0; - float f = dot(fenc, fenc); - float g = sqrt(1.0 - f / 4.0); - vec3 n; - n.xy = fenc * g; - n.z = 1 - f / 2; - return n; -} - -/* ---- RGBM (shared multiplier) encoding ---- */ -/* From http://iwasbeingirony.blogspot.fr/2010/06/difference-between-rgbm-and-rgbd.html */ - -/* Higher RGBM_MAX_RANGE gives imprecision issues in low intensity. */ -#define RGBM_MAX_RANGE 512.0 - -vec4 rgbm_encode(vec3 rgb) -{ - float maxRGB = max_v3(rgb); - float M = maxRGB / RGBM_MAX_RANGE; - M = ceil(M * 255.0) / 255.0; - return vec4(rgb / (M * RGBM_MAX_RANGE), M); -} - -vec3 rgbm_decode(vec4 data) -{ - return data.rgb * (data.a * RGBM_MAX_RANGE); -} - -/* ---- RGBE (shared exponent) encoding ---- */ -vec4 rgbe_encode(vec3 rgb) -{ - float maxRGB = max_v3(rgb); - float fexp = ceil(log2(maxRGB)); - return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0); -} - -vec3 rgbe_decode(vec4 data) -{ - float fexp = data.a * 255.0 - 128.0; - return data.rgb * exp2(fexp); -} - -#if 1 -# define irradiance_encode rgbe_encode -# define irradiance_decode rgbe_decode -#else /* No ecoding (when using floating point format) */ -# define irradiance_encode(X) (X).rgbb -# define irradiance_decode(X) (X).rgb -#endif - -/* Irradiance Visibility Encoding */ -#if 1 -vec4 visibility_encode(vec2 accum, float range) -{ - accum /= range; - - vec4 data; - data.x = fract(accum.x); - data.y = floor(accum.x) / 255.0; - data.z = fract(accum.y); - data.w = floor(accum.y) / 255.0; - - return data; -} - -vec2 visibility_decode(vec4 data, float range) -{ - return (data.xz + data.yw * 255.0) * range; -} -#else /* No ecoding (when using floating point format) */ -vec4 visibility_encode(vec2 accum, float range) -{ - return accum.xyxy; -} - -vec2 visibility_decode(vec4 data, float range) -{ - return data.xy; -} -#endif - /* Fresnel monochromatic, perfect mirror */ float F_eta(float eta, float cos_theta) { @@ -766,265 +157,3 @@ float cone_cosine(float r) /* Jimenez 2016 in Practical Realtime Strategies for Accurate Indirect Occlusion*/ return exp2(-3.32193 * r * r); } - -/* --------- Closure ---------- */ - -#ifdef VOLUMETRICS - -struct Closure { - vec3 absorption; - vec3 scatter; - vec3 emission; - float anisotropy; -}; - -Closure nodetree_exec(void); /* Prototype */ - -# define CLOSURE_DEFAULT Closure(vec3(0.0), vec3(0.0), vec3(0.0), 0.0) - -Closure closure_mix(Closure cl1, Closure cl2, float fac) -{ - Closure cl; - cl.absorption = mix(cl1.absorption, cl2.absorption, fac); - cl.scatter = mix(cl1.scatter, cl2.scatter, fac); - cl.emission = mix(cl1.emission, cl2.emission, fac); - cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac); - return cl; -} - -Closure closure_add(Closure cl1, Closure cl2) -{ - Closure cl; - cl.absorption = cl1.absorption + cl2.absorption; - cl.scatter = cl1.scatter + cl2.scatter; - cl.emission = cl1.emission + cl2.emission; - cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */ - return cl; -} - -Closure closure_emission(vec3 rgb) -{ - Closure cl = CLOSURE_DEFAULT; - cl.emission = rgb; - return cl; -} - -#else /* VOLUMETRICS */ - -struct Closure { - vec3 radiance; - vec3 transmittance; - float holdout; -# ifdef USE_SSS - vec3 sss_irradiance; - vec3 sss_albedo; - float sss_radius; -# endif - vec4 ssr_data; - vec2 ssr_normal; - int flag; -}; - -Closure nodetree_exec(void); /* Prototype */ - -# define FLAG_TEST(flag, val) (((flag) & (val)) != 0) - -# define CLOSURE_SSR_FLAG 1 -# define CLOSURE_SSS_FLAG 2 -# define CLOSURE_HOLDOUT_FLAG 4 - -# ifdef USE_SSS -# define CLOSURE_DEFAULT \ - Closure(vec3(0.0), vec3(0.0), 0.0, vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec2(0.0), 0) -# else -# define CLOSURE_DEFAULT Closure(vec3(0.0), vec3(0.0), 0.0, vec4(0.0), vec2(0.0), 0) -# endif - -uniform int outputSsrId = 1; -uniform int outputSssId = 1; - -void closure_load_ssr_data( - vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout Closure cl) -{ - /* Still encode to avoid artifacts in the SSR pass. */ - vec3 vN = normalize(mat3(ViewMatrix) * N); - cl.ssr_normal = normal_encode(vN, viewVec); - - if (ssr_id == outputSsrId) { - cl.ssr_data = vec4(ssr_spec, roughness); - cl.flag |= CLOSURE_SSR_FLAG; - } -} - -void closure_load_sss_data( - float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl) -{ -# ifdef USE_SSS - if (sss_id == outputSssId) { - cl.sss_irradiance = sss_irradiance; - cl.sss_radius = radius; - cl.sss_albedo = sss_albedo; - cl.flag |= CLOSURE_SSS_FLAG; - cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0)); - } - else -# endif - { - cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo); - } -} - -Closure closure_mix(Closure cl1, Closure cl2, float fac) -{ - Closure cl; - cl.holdout = mix(cl1.holdout, cl2.holdout, fac); - - if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) { - fac = 1.0; - } - else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) { - fac = 0.0; - } - - cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac); - cl.radiance = mix(cl1.radiance, cl2.radiance, fac); - cl.flag = cl1.flag | cl2.flag; - cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac); - bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); - /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz).*/ - cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; - cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; - -# ifdef USE_SSS - cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac); - bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); - /* It also does not make sense to mix SSS radius or irradiance. */ - cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; - cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; -# endif - return cl; -} - -Closure closure_add(Closure cl1, Closure cl2) -{ - Closure cl; - cl.transmittance = cl1.transmittance + cl2.transmittance; - cl.radiance = cl1.radiance + cl2.radiance; - cl.holdout = cl1.holdout + cl2.holdout; - cl.flag = cl1.flag | cl2.flag; - cl.ssr_data = cl1.ssr_data + cl2.ssr_data; - bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); - /* When mixing SSR don't blend roughness and normals.*/ - cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; - cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; - -# ifdef USE_SSS - cl.sss_albedo = cl1.sss_albedo + cl2.sss_albedo; - bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); - /* It also does not make sense to mix SSS radius or irradiance. */ - cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; - cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; -# endif - return cl; -} - -Closure closure_emission(vec3 rgb) -{ - Closure cl = CLOSURE_DEFAULT; - cl.radiance = rgb; - return cl; -} - -/* Breaking this across multiple lines causes issues for some older GLSL compilers. */ -/* clang-format off */ -# if defined(MESH_SHADER) && !defined(DEPTH_SHADER) -/* clang-format on */ -# ifndef USE_ALPHA_BLEND -layout(location = 0) out vec4 outRadiance; -layout(location = 1) out vec2 ssrNormals; -layout(location = 2) out vec4 ssrData; -# ifdef USE_SSS -layout(location = 3) out vec3 sssIrradiance; -layout(location = 4) out float sssRadius; -layout(location = 5) out vec3 sssAlbedo; -# endif -# else /* USE_ALPHA_BLEND */ -/* Use dual source blending to be able to make a whole range of effects. */ -layout(location = 0, index = 0) out vec4 outRadiance; -layout(location = 0, index = 1) out vec4 outTransmittance; -# endif /* USE_ALPHA_BLEND */ - -# if defined(USE_ALPHA_BLEND) -/* Prototype because this file is included before volumetric_lib.glsl */ -void volumetric_resolve(vec2 frag_uvs, - float frag_depth, - out vec3 transmittance, - out vec3 scattering); -# endif - -# define NODETREE_EXEC -void main() -{ - Closure cl = nodetree_exec(); - - float holdout = saturate(1.0 - cl.holdout); - float transmit = saturate(avg(cl.transmittance)); - float alpha = 1.0 - transmit; - -# ifdef USE_ALPHA_BLEND - vec2 uvs = gl_FragCoord.xy * volCoordScale.zw; - vec3 vol_transmit, vol_scatter; - volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter); - - /* Removes part of the volume scattering that have - * already been added to the destination pixels. - * Since we do that using the blending pipeline we need to account for material transmittance. */ - vol_scatter -= vol_scatter * cl.transmittance; - - cl.radiance = cl.radiance * holdout * vol_transmit + vol_scatter; - outRadiance = vec4(cl.radiance, alpha * holdout); - outTransmittance = vec4(cl.transmittance, transmit) * holdout; -# else - outRadiance = vec4(cl.radiance, holdout); - ssrNormals = cl.ssr_normal; - ssrData = cl.ssr_data; -# ifdef USE_SSS - sssIrradiance = cl.sss_irradiance; - sssRadius = cl.sss_radius; - sssAlbedo = cl.sss_albedo; -# endif -# endif - - /* For Probe capture */ -# ifdef USE_SSS - float fac = float(!sssToggle); - - /* TODO(fclem) we shouldn't need this. - * Just disable USE_SSS when USE_REFRACTION is enabled. */ -# ifdef USE_REFRACTION - /* SSRefraction pass is done after the SSS pass. - * In order to not loose the diffuse light totally we - * need to merge the SSS radiance to the main radiance. */ - fac = 1.0; -# endif - - outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac; -# endif - -# ifdef LOOKDEV - gl_FragDepth = 0.0; -# endif - -# ifndef USE_ALPHA_BLEND - float alpha_div = 1.0 / max(1e-8, alpha); - outRadiance.rgb *= alpha_div; - ssrData.rgb *= alpha_div; -# ifdef USE_SSS - sssAlbedo.rgb *= alpha_div; -# endif -# endif -} - -# endif /* MESH_SHADER */ - -#endif /* VOLUMETRICS */ diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl index f05b3396428..2b2da884fde 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl @@ -1,3 +1,4 @@ +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) out vec4 FragColor; @@ -5,8 +6,8 @@ void main() { vec3 N, T, B, V; - float NV = (1.0 - (clamp(gl_FragCoord.y / BRDF_LUT_SIZE, 1e-4, 0.9999))); - float sqrtRoughness = clamp(gl_FragCoord.x / BRDF_LUT_SIZE, 1e-4, 0.9999); + float NV = (1.0 - (clamp(gl_FragCoord.y / b, 1e-4, 0.9999))); + float sqrtRoughness = clamp(gl_FragCoord.x / LUT_SIZE, 1e-4, 0.9999); float a = sqrtRoughness * sqrtRoughness; float a2 = a * a; diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl index 5f2b719095e..066ea58e2bf 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl @@ -1,6 +1,7 @@ +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + uniform sampler1D texHammersley; -uniform sampler2D texJitter; uniform float sampleCount; uniform float invSampleCount; @@ -8,8 +9,7 @@ vec2 jitternoise = vec2(0.0); #ifndef UTIL_TEX # define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) + #endif /* UTIL_TEX */ void setup_noise(void) @@ -17,6 +17,11 @@ void setup_noise(void) jitternoise = texelfetch_noise_tex(gl_FragCoord.xy).rg; /* Global variable */ } +vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B) +{ + return T * vector.x + B * vector.y + N * vector.z; +} + #ifdef HAMMERSLEY_SIZE vec3 hammersley_3d(float i, float invsamplenbr) { diff --git a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl index 1389a9763c0..d815d9d4e6b 100644 --- a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl @@ -1,3 +1,4 @@ +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) uniform float a2; @@ -7,8 +8,8 @@ void main() { vec3 N, T, B, V; - float x = gl_FragCoord.x / BRDF_LUT_SIZE; - float y = gl_FragCoord.y / BRDF_LUT_SIZE; + float x = gl_FragCoord.x / LUT_SIZE; + float y = gl_FragCoord.y / LUT_SIZE; /* There is little variation if ior > 1.0 so we * maximize LUT precision for ior < 1.0 */ x = x * 1.1; diff --git a/source/blender/draw/engines/eevee/shaders/closure_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_lib.glsl new file mode 100644 index 00000000000..e572245ace9 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/closure_lib.glsl @@ -0,0 +1,181 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(renderpass_lib.glsl) + +#ifndef VOLUMETRICS + +uniform int outputSsrId = 1; +uniform int outputSssId = 1; + +#endif + +struct Closure { +#ifdef VOLUMETRICS + vec3 absorption; + vec3 scatter; + vec3 emission; + float anisotropy; + +#else /* SURFACE */ + vec3 radiance; + vec3 transmittance; + float holdout; + vec4 ssr_data; + vec2 ssr_normal; + int flag; +# ifdef USE_SSS + vec3 sss_irradiance; + vec3 sss_albedo; + float sss_radius; +# endif + +#endif +}; + +/* Prototype */ +Closure nodetree_exec(void); + +/* clang-format off */ +/* Avoid multiline defines. */ +#ifdef VOLUMETRICS +# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), vec3(0), 0.0) +#elif !defined(USE_SSS) +# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0) +#else +# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0, vec3(0), vec3(0), 0.0) +#endif +/* clang-format on */ + +#define FLAG_TEST(flag, val) (((flag) & (val)) != 0) + +#define CLOSURE_SSR_FLAG 1 +#define CLOSURE_SSS_FLAG 2 +#define CLOSURE_HOLDOUT_FLAG 4 + +#ifdef VOLUMETRICS +Closure closure_mix(Closure cl1, Closure cl2, float fac) +{ + Closure cl; + cl.absorption = mix(cl1.absorption, cl2.absorption, fac); + cl.scatter = mix(cl1.scatter, cl2.scatter, fac); + cl.emission = mix(cl1.emission, cl2.emission, fac); + cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac); + return cl; +} + +Closure closure_add(Closure cl1, Closure cl2) +{ + Closure cl; + cl.absorption = cl1.absorption + cl2.absorption; + cl.scatter = cl1.scatter + cl2.scatter; + cl.emission = cl1.emission + cl2.emission; + cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */ + return cl; +} + +Closure closure_emission(vec3 rgb) +{ + Closure cl = CLOSURE_DEFAULT; + cl.emission = rgb; + return cl; +} + +#else /* SURFACE */ + +Closure closure_mix(Closure cl1, Closure cl2, float fac) +{ + Closure cl; + cl.holdout = mix(cl1.holdout, cl2.holdout, fac); + + if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) { + fac = 1.0; + } + else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) { + fac = 0.0; + } + + cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac); + cl.radiance = mix(cl1.radiance, cl2.radiance, fac); + cl.flag = cl1.flag | cl2.flag; + cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac); + bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); + /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz).*/ + cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; + cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; + +# ifdef USE_SSS + cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac); + bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); + /* It also does not make sense to mix SSS radius or irradiance. */ + cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; + cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; +# endif + return cl; +} + +Closure closure_add(Closure cl1, Closure cl2) +{ + Closure cl; + cl.transmittance = cl1.transmittance + cl2.transmittance; + cl.radiance = cl1.radiance + cl2.radiance; + cl.holdout = cl1.holdout + cl2.holdout; + cl.flag = cl1.flag | cl2.flag; + cl.ssr_data = cl1.ssr_data + cl2.ssr_data; + bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG); + /* When mixing SSR don't blend roughness and normals.*/ + cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w; + cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal; + +# ifdef USE_SSS + cl.sss_albedo = cl1.sss_albedo + cl2.sss_albedo; + bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG); + /* It also does not make sense to mix SSS radius or irradiance. */ + cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius; + cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance; +# endif + return cl; +} + +Closure closure_emission(vec3 rgb) +{ + Closure cl = CLOSURE_DEFAULT; + cl.radiance = rgb; + return cl; +} + +#endif + +#ifndef VOLUMETRICS + +void closure_load_ssr_data( + vec3 ssr_spec, float roughness, vec3 N, vec3 viewVec, int ssr_id, inout Closure cl) +{ + /* Still encode to avoid artifacts in the SSR pass. */ + vec3 vN = normalize(mat3(ViewMatrix) * N); + cl.ssr_normal = normal_encode(vN, viewVec); + + if (ssr_id == outputSsrId) { + cl.ssr_data = vec4(ssr_spec, roughness); + cl.flag |= CLOSURE_SSR_FLAG; + } +} + +void closure_load_sss_data( + float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl) +{ +# ifdef USE_SSS + if (sss_id == outputSssId) { + cl.sss_irradiance = sss_irradiance; + cl.sss_radius = radius; + cl.sss_albedo = sss_albedo; + cl.flag |= CLOSURE_SSS_FLAG; + cl.radiance += render_pass_diffuse_mask(sss_albedo, vec3(0)); + } + else +# endif + { + cl.radiance += render_pass_diffuse_mask(sss_albedo, sss_irradiance * sss_albedo); + } +} + +#endif diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl index bc7879763c3..bf33caf9854 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_lit_lib.glsl @@ -1,32 +1,8 @@ -#ifndef LIT_SURFACE_UNIFORM -# define LIT_SURFACE_UNIFORM - -uniform float refractionDepth; - -# ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -# endif /* UTIL_TEX */ - -in vec3 worldPosition; -in vec3 viewPosition; - -in vec3 worldNormal; -in vec3 viewNormal; - -# ifdef HAIR_SHADER -in vec3 hairTangent; /* world space */ -in float hairThickTime; -in float hairThickness; -in float hairTime; -flat in int hairStrandID; - -uniform int hairThicknessRes = 1; -# endif - -#endif /* LIT_SURFACE_UNIFORM */ +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) +#pragma BLENDER_REQUIRE(ssr_lib.glsl) /** * AUTO CONFIG @@ -209,7 +185,7 @@ void CLOSURE_NAME(vec3 N vec3 V = cameraVec; - vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0); + vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); /* ---------------------------------------------------------------- */ /* -------------------- SCENE LIGHTS LIGHTING --------------------- */ @@ -328,11 +304,11 @@ void CLOSURE_NAME(vec3 N # endif # ifdef CLOSURE_GLOSSY - vec3 spec_dir = get_specular_reflection_dominant_dir(N, V, roughnessSquared); + vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared); # endif # ifdef CLOSURE_CLEARCOAT - vec3 C_spec_dir = get_specular_reflection_dominant_dir(C_N, V, C_roughnessSquared); + vec3 C_spec_dir = specular_dominant_dir(C_N, V, C_roughnessSquared); # endif # ifdef CLOSURE_REFRACTION @@ -345,7 +321,7 @@ void CLOSURE_NAME(vec3 N line_plane_intersect( worldPosition, refr_V, worldPosition - N * refractionDepth, N) : worldPosition; - vec3 refr_dir = get_specular_refraction_dominant_dir(N, refr_V, roughness, final_ior); + vec3 refr_dir = refraction_dominant_dir(N, refr_V, roughness, final_ior); # endif # ifdef CLOSURE_REFRACTION @@ -485,7 +461,7 @@ void CLOSURE_NAME(vec3 N # endif # ifdef CLOSURE_REFRACTION - float btdf = get_btdf_lut(utilTex, NV, roughness, ior); + float btdf = get_btdf_lut(NV, roughness, ior); out_refr += refr_accum.rgb * btdf; # endif diff --git a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl index 759b4098b37..a6c9eebaff2 100644 --- a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl @@ -2,7 +2,6 @@ layout(std140) uniform common_block { mat4 pastViewProjectionMatrix; - vec4 viewVecs[2]; vec2 mipRatio[10]; /* To correct mip level texel misalignment */ /* Ambient Occlusion */ vec4 aoParameters[2]; @@ -70,3 +69,9 @@ layout(std140) uniform common_block #define ssrQuality ssrParameters.x #define ssrThickness ssrParameters.y #define ssrPixelSize ssrParameters.zw + +vec2 mip_ratio_interp(float mip) +{ + float low_mip = floor(mip); + return mix(mipRatio[int(low_mip)], mipRatio[int(low_mip + 1.0)], mip - low_mip); +} diff --git a/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl new file mode 100644 index 00000000000..95a585f0d9c --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/common_utiltex_lib.glsl @@ -0,0 +1,65 @@ + +#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Utiltex + * + * Utiltex is a sampler2DArray that stores a number of useful small utilitary textures and lookup + * tables. + * \{ */ + +uniform sampler2DArray utilTex; + +#define LUT_SIZE 64 + +#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) + +/* Return texture coordinates to sample Surface LUT */ +vec2 lut_coords(float cosTheta, float roughness) +{ + float theta = acos(cosTheta); + vec2 coords = vec2(roughness, theta / M_PI_2); + + /* scale and bias coordinates, for correct filtered lookup */ + return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; +} + +vec2 lut_coords_ltc(float cosTheta, float roughness) +{ + vec2 coords = vec2(roughness, sqrt(1.0 - cosTheta)); + + /* scale and bias coordinates, for correct filtered lookup */ + return coords * (LUT_SIZE - 1.0) / LUT_SIZE + 0.5 / LUT_SIZE; +} + +float get_btdf_lut(float NV, float roughness, float ior) +{ + const vec3 lut_scale_bias_texel_size = vec3((LUT_SIZE - 1.0), 0.5, 1.5) / LUT_SIZE; + + vec3 coords; + /* Try to compensate for the low resolution and interpolation error. */ + coords.x = (ior > 1.0) ? (0.9 + lut_scale_bias_texel_size.z) + + (0.1 - lut_scale_bias_texel_size.z) * f0_from_ior(ior) : + (0.9 + lut_scale_bias_texel_size.z) * ior * ior; + coords.y = 1.0 - saturate(NV); + coords.xy *= lut_scale_bias_texel_size.x; + coords.xy += lut_scale_bias_texel_size.y; + + const float lut_lvl_ofs = 4.0; /* First texture lvl of roughness. */ + const float lut_lvl_scale = 16.0; /* How many lvl of roughness in the lut. */ + + float mip = roughness * lut_lvl_scale; + float mip_floor = floor(mip); + + coords.z = lut_lvl_ofs + mip_floor + 1.0; + float btdf_high = textureLod(utilTex, coords, 0.0).r; + + coords.z -= 1.0; + float btdf_low = textureLod(utilTex, coords, 0.0).r; + + float btdf = (ior == 1.0) ? 1.0 : mix(btdf_low, btdf_high, mip - coords.z); + + return btdf; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/default_frag.glsl b/source/blender/draw/engines/eevee/shaders/default_frag.glsl deleted file mode 100644 index 1014b25033a..00000000000 --- a/source/blender/draw/engines/eevee/shaders/default_frag.glsl +++ /dev/null @@ -1,51 +0,0 @@ - -uniform vec3 basecol; -uniform float metallic; -uniform float specular; -uniform float roughness; - -Closure nodetree_exec(void) -{ -#ifdef HAIR_SHADER - vec3 B = normalize(cross(worldNormal, hairTangent)); - float cos_theta; - if (hairThicknessRes == 1) { - vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0); - /* Random cosine normal distribution on the hair surface. */ - cos_theta = rand.x * 2.0 - 1.0; - } - else { - /* Shade as a cylinder. */ - cos_theta = hairThickTime / hairThickness; - } - float sin_theta = sqrt(max(0.0, 1.0f - cos_theta * cos_theta)); - vec3 N = normalize(worldNormal * sin_theta + B * cos_theta); - vec3 vN = mat3(ViewMatrix) * N; -#else - vec3 N = normalize(gl_FrontFacing ? worldNormal : -worldNormal); - vec3 vN = normalize(gl_FrontFacing ? viewNormal : -viewNormal); -#endif - - vec3 dielectric = vec3(0.034) * specular * 2.0; - vec3 albedo = mix(basecol, vec3(0.0), metallic); - vec3 f0 = mix(dielectric, basecol, metallic); - vec3 f90 = mix(vec3(1.0), f0, (1.0 - specular) * metallic); - vec3 out_diff, out_spec, ssr_spec; - eevee_closure_default(N, albedo, f0, f90, 1, roughness, 1.0, true, out_diff, out_spec, ssr_spec); - - Closure cl = CLOSURE_DEFAULT; - cl.radiance = render_pass_glossy_mask(vec3(1.0), out_spec) + - render_pass_diffuse_mask(albedo, out_diff * albedo); - closure_load_ssr_data(ssr_spec, roughness, N, viewCameraVec, 1, cl); - -#ifdef LOOKDEV - gl_FragDepth = 0.0; -#endif - -#ifdef HOLDOUT - cl = CLOSURE_DEFAULT; - cl.holdout = 1.0; -#endif - - return cl; -} diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl index d56890769a7..9c1ca17f87c 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_frag.glsl @@ -1,4 +1,8 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + uniform sampler2D colorBuffer; uniform sampler2D depthBuffer; @@ -18,9 +22,6 @@ uniform vec4 bokehParams[2]; uniform vec2 nearFar; /* Near & far view depths values */ -#define M_PI 3.1415926535897932384626433832795 -#define M_2PI 6.2831853071795864769252868 - /* -------------- Utils ------------- */ /* divide by sensor size to get the normalized size */ @@ -34,11 +35,6 @@ uniform vec2 nearFar; /* Near & far view depths values */ #define weighted_sum(a, b, c, d, e) \ (a * e.x + b * e.y + c * e.z + d * e.w) / max(1e-6, dot(e, vec4(1.0))); -float max_v4(vec4 v) -{ - return max(max(v.x, v.y), max(v.z, v.w)); -} - vec4 safe_color(vec4 c) { /* Clamp to avoid black square artifacts if a pixel goes NaN. */ diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl index d83b410125a..6e35d4a54ae 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_vert.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + uniform vec4 bokehParams[2]; #define bokeh_rotation bokehParams[0].x @@ -15,8 +17,6 @@ flat out float smoothFac; flat out ivec2 edge; out vec2 particlecoord; -#define M_PI 3.1415926535897932384626433832795 - /* Scatter pass, calculate a triangle covering the CoC. */ void main() { diff --git a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl index eea0ce0aae2..47fe21928b3 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_gtao_frag.glsl @@ -1,14 +1,34 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) + /** * This shader only compute maximum horizon angles for each directions. * The final integration is done at the resolve stage with the shading normal. */ -uniform float rotationOffset; - out vec4 FragColor; -#ifdef DEBUG_AO uniform sampler2D normalBuffer; +#ifdef LAYERED_DEPTH +uniform sampler2DArray depthBufferLayered; +uniform int layer; +# define gtao_depthBuffer depthBufferLayered +# define gtao_textureLod(a, b, c) textureLod(a, vec3(b, layer), c) + +#else +uniform sampler2D depthBuffer; +# define gtao_depthBuffer depthBuffer +# define gtao_textureLod(a, b, c) textureLod(a, b, c) + +#endif + +uniform float rotationOffset; + +#ifdef DEBUG_AO void main() { @@ -34,18 +54,6 @@ void main() #else -# ifdef LAYERED_DEPTH -uniform sampler2DArray depthBufferLayered; -uniform int layer; -# define gtao_depthBuffer depthBufferLayered -# define gtao_textureLod(a, b, c) textureLod(a, vec3(b, layer), c) - -# else -# define gtao_depthBuffer depthBuffer -# define gtao_textureLod(a, b, c) textureLod(a, b, c) - -# endif - void main() { vec2 uvs = saturate(gl_FragCoord.xy / vec2(textureSize(gtao_depthBuffer, 0).xy)); diff --git a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl index edee55a07e0..7331f92ba6d 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_mist_frag.glsl @@ -1,5 +1,10 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + /* Convert depth to Mist factor */ uniform vec3 mistSettings; +uniform sampler2D depthBuffer; #define mistStart mistSettings.x #define mistInvDistance mistSettings.y diff --git a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl index fbf507a2e40..3501a4448c5 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_motion_blur_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + /* * Based on: * A Fast and Stable Feature-Aware Motion Blur Filter @@ -15,11 +17,6 @@ uniform sampler2D tileMaxBuffer; #define KERNEL 8 -/* TODO(fclem) deduplicate this code. */ -uniform sampler2DArray utilTex; -#define LUT_SIZE 64 -#define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) - uniform float depthScale; uniform ivec2 tileBufferSize; uniform vec2 viewportSize; diff --git a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl index 598cc3e5183..f8dccb7511a 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl @@ -1,4 +1,11 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(raytrace_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(ssr_lib.glsl) + /* Based on Stochastic Screen Space Reflections * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections */ @@ -131,7 +138,7 @@ void main() return; } - vec4 rand = texelFetch(utilTex, ivec3(halfres_texel % LUT_SIZE, 2), 0); + vec4 rand = texelfetch_noise_tex(halfres_texel); /* Gives *perfect* reflection for very small roughness */ if (roughness < 0.04) { diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl index e9da49c9eb9..2a53a4f119f 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl @@ -1,4 +1,9 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) + /* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ #define MAX_SSS_SAMPLES 65 @@ -14,36 +19,16 @@ uniform sampler2D sssIrradiance; uniform sampler2D sssRadius; uniform sampler2D sssAlbedo; -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - layout(location = 0) out vec4 sssRadiance; -float get_view_z_from_depth(float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - float d = 2.0 * depth - 1.0; - return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); - } - else { - return viewVecs[0].z + depth * viewVecs[1].z; - } -} - -#define LUT_SIZE 64 -#define M_PI_2 1.5707963267948966 /* pi/2 */ -#define M_2PI 6.2831853071795865 /* 2*pi */ - void main(void) { vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO precompute */ vec2 uvs = gl_FragCoord.xy * pixel_size; vec3 sss_irradiance = texture(sssIrradiance, uvs).rgb; float sss_radius = texture(sssRadius, uvs).r; - float depth_view = get_view_z_from_depth(texture(depthBuffer, uvs).r); + float depth = texture(depthBuffer, uvs).r; + float depth_view = get_view_z_from_depth(depth); float rand = texelfetch_noise_tex(gl_FragCoord.xy).r; #ifdef FIRST_PASS diff --git a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl index b44645174bd..28947e971d2 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_temporal_aa.glsl @@ -1,5 +1,11 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +uniform sampler2D colorBuffer; +uniform sampler2D depthBuffer; uniform sampler2D colorHistoryBuffer; + uniform mat4 prevViewProjectionMatrix; out vec4 FragColor; diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl index 6531ceb8dbe..c85eff92e37 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl @@ -1,11 +1,16 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(lights_lib.glsl) + in vec4 uvcoordsvar; out vec4 FragColor; +uniform sampler2D depthBuffer; uniform sampler1D sssTexProfile; uniform sampler2D sssRadius; - uniform sampler2DArray sssShadowCubes; uniform sampler2DArray sssShadowCascades; @@ -27,12 +32,6 @@ vec3 sss_profile(float s) return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb; } -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - float light_translucent_power_with_falloff(LightData ld, vec3 N, vec4 l_vector) { float power, falloff; diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl index d927fd78d30..145939cefb2 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_velocity_resolve_frag.glsl @@ -1,4 +1,8 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +uniform sampler2D depthBuffer; + uniform mat4 prevViewProjMatrix; uniform mat4 currViewProjMatrixInv; uniform mat4 nextViewProjMatrix; diff --git a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl index 0eb598521af..f52acaf6f7f 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_velocity_tile_frag.glsl @@ -148,4 +148,4 @@ void main() tileMaxVelocity = encode_velocity(max_motion); } -#endif
\ No newline at end of file +#endif diff --git a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl b/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl index 64ea87001f4..2274bf8b950 100644 --- a/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/irradiance_lib.glsl @@ -1,18 +1,71 @@ -uniform sampler2DArray irradianceGrid; +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) +#pragma BLENDER_REQUIRE(octahedron_lib.glsl) #define IRRADIANCE_LIB +/* ---------------------------------------------------------------------- */ +/** \name Structure + * \{ */ + #if defined(IRRADIANCE_SH_L2) struct IrradianceData { vec3 shcoefs[9]; }; + #else /* defined(IRRADIANCE_HL2) */ struct IrradianceData { vec3 cubesides[3]; }; + #endif +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Resources + * \{ */ + +uniform sampler2DArray irradianceGrid; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Functions + * \{ */ + +vec4 irradiance_encode(vec3 rgb) +{ + float maxRGB = max_v3(rgb); + float fexp = ceil(log2(maxRGB)); + return vec4(rgb / exp2(fexp), (fexp + 128.0) / 255.0); +} + +vec3 irradiance_decode(vec4 data) +{ + float fexp = data.a * 255.0 - 128.0; + return data.rgb * exp2(fexp); +} + +vec4 visibility_encode(vec2 accum, float range) +{ + accum /= range; + + vec4 data; + data.x = fract(accum.x); + data.y = floor(accum.x) / 255.0; + data.z = fract(accum.y); + data.w = floor(accum.y) / 255.0; + + return data; +} + +vec2 visibility_decode(vec4 data, float range) +{ + return (data.xz + data.yw * 255.0) * range; +} + IrradianceData load_irradiance_cell(int cell, vec3 N) { /* Keep in sync with diffuse_filter_probe() */ @@ -155,3 +208,5 @@ vec3 irradiance_from_cell_get(int cell, vec3 ir_dir) IrradianceData ir_data = load_irradiance_cell(cell, ir_dir); return compute_irradiance(ir_dir, ir_data); } + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl index 96fe94fc41e..b0da4274a13 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) + flat in int pid; in vec2 quadCoord; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl index db780714091..d06ad553ca4 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + /* XXX TODO fix code duplication */ struct CubeData { vec4 position_type; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl index b485511318b..bf45169ebaa 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl @@ -1,4 +1,8 @@ +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) + uniform samplerCube probeHdr; uniform int probeSize; uniform float lodFactor; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl index 00eb3c7e200..ccb77427ed2 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) + uniform samplerCube probeHdr; uniform float roughnessSquared; uniform float texelSize; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl index 5d8af21032a..8d7c58a93d5 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl @@ -1,4 +1,8 @@ +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) + uniform samplerCube probeDepth; uniform int outputSize; uniform float lodFactor; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl index f8bc1703c66..009ccf6535e 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_geom.glsl @@ -40,9 +40,6 @@ void main() for (int v = 0; v < 3; v++) { gl_Position = vPos[v]; worldPosition = x_axis[fFace] * vPos[v].x + y_axis[fFace] * vPos[v].y + maj_axes[fFace]; -#ifdef USE_ATTR - pass_attr(v); -#endif EmitVertex(); } diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl index f9bcc718a1e..dc5ec1e40f5 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl @@ -1,3 +1,5 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) flat in int cellOffset; in vec2 quadCoord; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl index 4e500db827e..6fefe1319bd 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + uniform float sphere_size; uniform int offset; uniform ivec3 grid_resolution; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl index 6c6db88139b..a2e25b83532 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl @@ -1,3 +1,12 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) +#pragma BLENDER_REQUIRE(cubemap_lib.glsl) +#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) + /* ----------- Uniforms --------- */ uniform sampler2DArray probePlanars; @@ -73,12 +82,6 @@ struct GridData { # define MAX_PLANAR 1 #endif -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - layout(std140) uniform probe_block { CubeData probes_data[MAX_PROBE]; @@ -218,7 +221,7 @@ void fallback_cubemap(vec3 N, inout vec4 spec_accum) { /* Specular probes */ - vec3 spec_dir = get_specular_reflection_dominant_dir(N, V, roughnessSquared); + vec3 spec_dir = specular_dominant_dir(N, V, roughnessSquared); #ifdef SSR_AO vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); @@ -246,7 +249,6 @@ void fallback_cubemap(vec3 N, } } -#ifdef IRRADIANCE_LIB vec3 probe_evaluate_grid(GridData gd, vec3 W, vec3 N, vec3 localpos) { localpos = localpos * 0.5 + 0.5; @@ -308,5 +310,3 @@ vec3 probe_evaluate_world_diff(vec3 N) { return irradiance_from_cell_get(0, N); } - -#endif /* IRRADIANCE_LIB */ diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl index 2807488db6c..0a53abcb04a 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + uniform sampler2DArray probePlanars; in vec3 worldPosition; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl index 65506e5c7b1..6759c060259 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_planar_display_vert.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + in vec3 pos; in int probe_id; diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl index 3b9d0a8f2bc..949e4d8f04f 100644 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl @@ -1,8 +1,76 @@ -uniform sampler2DArrayShadow shadowCubeTexture; -uniform sampler2DArrayShadow shadowCascadeTexture; +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(raytrace_lib.glsl) +#pragma BLENDER_REQUIRE(ltc_lib.glsl) + +#ifndef MAX_CASCADE_NUM +# define MAX_CASCADE_NUM 4 +#endif + +/* ---------------------------------------------------------------------- */ +/** \name Structure + * \{ */ + +struct LightData { + vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */ + vec4 color_spec; /* w : Spec Intensity */ + vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */ + vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */ + vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */ + vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */ +}; + +/* convenience aliases */ +#define l_color color_spec.rgb +#define l_spec color_spec.a +#define l_position position_influence.xyz +#define l_influence position_influence.w +#define l_sizex rightvec_sizex.w +#define l_sizey upvec_sizey.w +#define l_right rightvec_sizex.xyz +#define l_up upvec_sizey.xyz +#define l_forward forwardvec_type.xyz +#define l_type forwardvec_type.w +#define l_spot_size spotdata_radius_shadow.x +#define l_spot_blend spotdata_radius_shadow.y +#define l_radius spotdata_radius_shadow.z +#define l_shadowid spotdata_radius_shadow.w + +struct ShadowData { + vec4 near_far_bias_id; + vec4 contact_shadow_data; +}; + +struct ShadowCubeData { + mat4 shadowmat; + vec4 position; +}; + +struct ShadowCascadeData { + mat4 shadowmat[MAX_CASCADE_NUM]; + vec4 split_start_distances; + vec4 split_end_distances; + vec4 shadow_vec_id; +}; + +/* convenience aliases */ +#define sh_near near_far_bias_id.x +#define sh_far near_far_bias_id.y +#define sh_bias near_far_bias_id.z +#define sh_data_index near_far_bias_id.w +#define sh_contact_dist contact_shadow_data.x +#define sh_contact_offset contact_shadow_data.y +#define sh_contact_spread contact_shadow_data.z +#define sh_contact_thickness contact_shadow_data.w +#define sh_shadow_vec shadow_vec_id.xyz +#define sh_tex_index shadow_vec_id.w + +/** \} */ -#define LAMPS_LIB +/* ---------------------------------------------------------------------- */ +/** \name Resources + * \{ */ layout(std140) uniform shadow_block { @@ -16,6 +84,15 @@ layout(std140) uniform light_block LightData lights_data[MAX_LIGHT]; }; +uniform sampler2DArrayShadow shadowCubeTexture; +uniform sampler2DArrayShadow shadowCascadeTexture; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Shadow Functions + * \{ */ + /* type */ #define POINT 0.0 #define SUN 1.0 @@ -133,9 +210,11 @@ float sample_cascade_shadow(int shadow_id, vec3 W) #undef scube #undef scsmd -/* ----------------------------------------------------------- */ -/* --------------------- Light Functions --------------------- */ -/* ----------------------------------------------------------- */ +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Light Functions + * \{ */ /* From Frostbite PBR Course * Distance based attenuation @@ -258,7 +337,6 @@ float light_visibility(LightData ld, l_atten); } -#ifdef USE_LTC float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) { if (ld.l_type == AREA_RECT) { @@ -321,4 +399,5 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) return ltc_evaluate_disk(N, V, ltc_matrix(ltc_mat), points); } } -#endif + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/default_world_frag.glsl b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl index 8c876cf582c..9077b414026 100644 --- a/source/blender/draw/engines/eevee/shaders/default_world_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl @@ -1,20 +1,17 @@ -uniform float backgroundAlpha; -uniform vec3 color; - -out vec4 FragColor; +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) -#if defined(LOOKDEV_BG) || defined(LOOKDEV) +uniform sampler2D studioLight; +uniform float backgroundAlpha; uniform mat3 StudioLightMatrix; -uniform sampler2D image; uniform float studioLightIntensity = 1.0; uniform float studioLightBlur = 0.0; -in vec3 viewPosition; -# ifndef M_PI -# define M_PI 3.14159265358979323846 -# endif +out vec4 FragColor; vec3 background_transform_to_world(vec3 viewvec) { @@ -35,36 +32,20 @@ vec4 node_tex_environment_equirectangular(vec3 co, sampler2D ima) vec3 nco = normalize(co); float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5; float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5; - - /* Fix pole bleeding */ - float width = float(textureSize(ima, 0).x); - float texel_width = 1.0 / width; - v = clamp(v, texel_width, 1.0 - texel_width); - - /* Fix u = 0 seam */ - /* This is caused by texture filtering, since uv don't have smooth derivatives - * at u = 0 or 2PI, hardware filtering is using the smallest mipmap for certain - * texels. So we force the highest mipmap and don't do anisotropic filtering. */ return textureLod(ima, vec2(u, v), 0.0); } -#endif void main() { - vec3 background_color; + vec3 worldvec = background_transform_to_world(viewPosition); + vec3 background_color; #if defined(LOOKDEV_BG) - vec3 worldvec = background_transform_to_world(viewPosition); background_color = probe_evaluate_world_spec(worldvec, studioLightBlur).rgb; - background_color *= studioLightIntensity; - -#elif defined(LOOKDEV) - vec3 worldvec = background_transform_to_world(viewPosition); - background_color = node_tex_environment_equirectangular(StudioLightMatrix * worldvec, image).rgb; - background_color *= studioLightIntensity; - #else - background_color = color; + worldvec = StudioLightMatrix * worldvec; + background_color = node_tex_environment_equirectangular(worldvec, studioLight).rgb; + background_color *= studioLightIntensity; #endif FragColor = vec4(clamp(background_color, vec3(0.0), vec3(1e10)), 1.0) * backgroundAlpha; diff --git a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl index dbfc7ad5a71..2750d42a74a 100644 --- a/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ltc_lib.glsl @@ -8,12 +8,6 @@ #define USE_LTC -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ - /* Diffuse *clipped* sphere integral. */ float diffuse_sphere_integral(float avg_dir_z, float form_factor) { diff --git a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl index 95cd69ba310..ef96bcbaedb 100644 --- a/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/object_motion_vert.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) + uniform mat4 currModelMatrix; uniform mat4 prevModelMatrix; uniform mat4 nextModelMatrix; @@ -19,6 +22,8 @@ out vec3 nextWorldPos; void main() { + GPU_INTEL_VERTEX_SHADER_WORKAROUND + #ifdef HAIR bool is_persp = (ProjectionMatrix[3][3] == 0.0); float time, thick_time, thickness; diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl index 9acd8f998f6..34999076f9c 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl @@ -1,4 +1,14 @@ +/* Required by some nodes. */ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lit_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + #ifdef USE_ALPHA_HASH /* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire */ @@ -56,8 +66,6 @@ float hashed_alpha_threshold(vec3 co) #endif -#define NODETREE_EXEC - void main() { #if defined(USE_ALPHA_HASH) @@ -66,11 +74,9 @@ void main() float opacity = saturate(1.0 - avg(cl.transmittance)); -# if defined(USE_ALPHA_HASH) /* Hashed Alpha Testing */ if (opacity < hashed_alpha_threshold(worldPosition)) { discard; } -# endif #endif } diff --git a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl index 2c7e0aca3fb..f650e2eeb8c 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl @@ -1,16 +1,14 @@ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + #ifndef HAIR_SHADER in vec3 pos; #endif void main() { -#ifdef GPU_INTEL - /* Due to some shader compiler bug, we somewhat - * need to access gl_VertexID to make it work. even - * if it's actually dead code. */ - gl_Position.x = float(gl_VertexID); -#endif + GPU_INTEL_VERTEX_SHADER_WORKAROUND #ifdef HAIR_SHADER float time, thick_time, thickness; @@ -34,5 +32,4 @@ void main() #ifdef CLIP_PLANES gl_ClipDistance[0] = dot(vec4(worldPosition.xyz, 1.0), clipPlanes[0]); #endif - /* TODO motion vectors */ } diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl index f88cfdf3787..39db39f8756 100644 --- a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl @@ -1,3 +1,11 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) + +uniform sampler2D maxzBuffer; +uniform sampler2DArray planarDepth; + #define MAX_STEP 256 float sample_depth(vec2 uv, int index, float lod) diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl new file mode 100644 index 00000000000..36cf3cecf40 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl @@ -0,0 +1,43 @@ + +/* ---------------------------------------------------------------------- */ +/** \name Resources + * \{ */ + +layout(std140) uniform renderpass_block +{ + bool renderPassDiffuse; + bool renderPassDiffuseLight; + bool renderPassGlossy; + bool renderPassGlossyLight; + bool renderPassEmit; + bool renderPassSSSColor; + bool renderPassEnvironment; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Functions + * \{ */ + +vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light) +{ + return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0); +} + +vec3 render_pass_sss_mask(vec3 sss_color) +{ + return renderPassSSSColor ? sss_color : vec3(0.0); +} + +vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light) +{ + return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0); +} + +vec3 render_pass_emission_mask(vec3 emission_light) +{ + return renderPassEmit ? emission_light : vec3(0.0); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl index 361963d5ac3..89a411bc7cb 100644 --- a/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/renderpass_postprocess_frag.glsl @@ -1,3 +1,7 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) + #define PASS_POST_UNDEFINED 0 #define PASS_POST_ACCUMULATED_COLOR 1 #define PASS_POST_ACCUMULATED_LIGHT 2 @@ -9,6 +13,8 @@ uniform int postProcessType; uniform int currentSample; + +uniform sampler2D depthBuffer; uniform sampler2D inputBuffer; uniform sampler2D inputSecondLightBuffer; uniform sampler2D inputColorBuffer; diff --git a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl index 860ec9e1727..e0b9d4a60db 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_accum_frag.glsl @@ -1,11 +1,11 @@ -out vec4 fragColor; +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) +#pragma BLENDER_REQUIRE(lights_lib.glsl) + +uniform sampler2D depthBuffer; -#ifndef UTIL_TEX -# define UTIL_TEX -uniform sampler2DArray utilTex; -# define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0) -#endif /* UTIL_TEX */ +out vec4 fragColor; void main() { diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index c42f905cf7e..0e342938396 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -1,39 +1,23 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + in vec3 pos; in vec3 nor; -#ifdef MESH_SHADER -out vec3 worldPosition; -out vec3 viewPosition; -out vec3 worldNormal; -out vec3 viewNormal; -#endif - -#ifdef HAIR_SHADER -out vec3 hairTangent; -out float hairThickTime; -out float hairThickness; -out float hairTime; -flat out int hairStrandID; -#endif - void main() { -#ifdef GPU_INTEL - /* Due to some shader compiler bug, we somewhat - * need to access gl_VertexID to make it work. even - * if it's actually dead code. */ - gl_Position.x = float(gl_VertexID); -#endif + GPU_INTEL_VERTEX_SHADER_WORKAROUND #ifdef HAIR_SHADER hairStrandID = hair_get_strand_id(); - vec3 world_pos, binor; + vec3 pos, binor; hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), ModelMatrixInverse, ViewMatrixInverse[3].xyz, ViewMatrixInverse[2].xyz, - world_pos, + pos, hairTangent, binor, hairTime, @@ -41,6 +25,7 @@ void main() hairThickTime); worldNormal = cross(hairTangent, binor); + vec3 world_pos = pos; #else vec3 world_pos = point_object_to_world(pos); #endif @@ -57,7 +42,10 @@ void main() /* No need to normalize since this is just a rotation. */ viewNormal = normal_world_to_view(worldNormal); # ifdef USE_ATTR - pass_attr(pos); +# ifdef HAIR_SHADER + pos = hair_get_strand_pos(); +# endif + pass_attr(pos, NormalMatrix, ModelMatrixInverse); # endif #endif } diff --git a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl index 0591b247541..29495e98355 100644 --- a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl @@ -1,3 +1,10 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(raytrace_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + /* ------------ Refraction ------------ */ #define BTDF_BIAS 0.85 diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl new file mode 100644 index 00000000000..e746acfdfa3 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl @@ -0,0 +1,89 @@ + +/* Required by some nodes. */ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) + +#pragma BLENDER_REQUIRE(closure_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lit_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + +#ifdef USE_ALPHA_BLEND +/* Use dual source blending to be able to make a whole range of effects. */ +layout(location = 0, index = 0) out vec4 outRadiance; +layout(location = 0, index = 1) out vec4 outTransmittance; + +#else /* OPAQUE */ +layout(location = 0) out vec4 outRadiance; +layout(location = 1) out vec2 ssrNormals; +layout(location = 2) out vec4 ssrData; +# ifdef USE_SSS +layout(location = 3) out vec3 sssIrradiance; +layout(location = 4) out float sssRadius; +layout(location = 5) out vec3 sssAlbedo; +# endif + +#endif + +void main() +{ + Closure cl = nodetree_exec(); + + float holdout = saturate(1.0 - cl.holdout); + float transmit = saturate(avg(cl.transmittance)); + float alpha = 1.0 - transmit; + +#ifdef USE_ALPHA_BLEND + vec2 uvs = gl_FragCoord.xy * volCoordScale.zw; + vec3 vol_transmit, vol_scatter; + volumetric_resolve(uvs, gl_FragCoord.z, vol_transmit, vol_scatter); + + /* Removes part of the volume scattering that have + * already been added to the destination pixels. + * Since we do that using the blending pipeline we need to account for material transmittance. */ + vol_scatter -= vol_scatter * cl.transmittance; + + cl.radiance = cl.radiance * holdout * vol_transmit + vol_scatter; + outRadiance = vec4(cl.radiance, alpha * holdout); + outTransmittance = vec4(cl.transmittance, transmit) * holdout; +#else + outRadiance = vec4(cl.radiance, holdout); + ssrNormals = cl.ssr_normal; + ssrData = cl.ssr_data; +# ifdef USE_SSS + sssIrradiance = cl.sss_irradiance; + sssRadius = cl.sss_radius; + sssAlbedo = cl.sss_albedo; +# endif +#endif + + /* For Probe capture */ +#ifdef USE_SSS + float fac = float(!sssToggle); + + /* TODO(fclem) we shouldn't need this. + * Just disable USE_SSS when USE_REFRACTION is enabled. */ +# ifdef USE_REFRACTION + /* SSRefraction pass is done after the SSS pass. + * In order to not loose the diffuse light totally we + * need to merge the SSS radiance to the main radiance. */ + fac = 1.0; +# endif + + outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac; +#endif + +#ifndef USE_ALPHA_BLEND + float alpha_div = 1.0 / max(1e-8, alpha); + outRadiance.rgb *= alpha_div; + ssrData.rgb *= alpha_div; +# ifdef USE_SSS + sssAlbedo.rgb *= alpha_div; +# endif +#endif + +#ifdef LOOKDEV + /* Lookdev spheres are rendered in front. */ + gl_FragDepth = 0.0; +#endif +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_geom.glsl b/source/blender/draw/engines/eevee/shaders/surface_geom.glsl new file mode 100644 index 00000000000..ad437dca79a --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/surface_geom.glsl @@ -0,0 +1,46 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +RESOURCE_ID_VARYING + +/* Only used to compute barycentric coordinates. */ + +void main() +{ +#ifdef DO_BARYCENTRIC_DISTANCES + dataAttrOut.barycentricDist = calc_barycentric_distances( + dataIn[0].worldPosition, dataIn[1].worldPosition, dataIn[2].worldPosition); +#endif + + PASS_RESOURCE_ID + +#ifdef USE_ATTR + pass_attr(0); +#endif + PASS_SURFACE_INTERFACE(0); + gl_Position = gl_in[0].gl_Position; + gl_ClipDistance[0] = gl_in[0].gl_ClipDistance[0]; + EmitVertex(); + +#ifdef USE_ATTR + pass_attr(1); +#endif + PASS_SURFACE_INTERFACE(1); + gl_Position = gl_in[1].gl_Position; + gl_ClipDistance[0] = gl_in[1].gl_ClipDistance[0]; + EmitVertex(); + +#ifdef USE_ATTR + pass_attr(2); +#endif + PASS_SURFACE_INTERFACE(2); + gl_Position = gl_in[2].gl_Position; + gl_ClipDistance[0] = gl_in[2].gl_ClipDistance[0]; + EmitVertex(); + + EndPrimitive(); +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl new file mode 100644 index 00000000000..b93a3a23eff --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -0,0 +1,43 @@ +/** This describe the entire interface of the shader. */ + +/* Samplers */ +uniform sampler2D colorBuffer; +uniform sampler2D depthBuffer; + +/* Uniforms */ +uniform float refractionDepth; + +#define SURFACE_INTERFACE \ + vec3 worldPosition; \ + vec3 viewPosition; \ + vec3 worldNormal; \ + vec3 viewNormal; + +#ifdef GPU_GEOMETRY_SHADER +in ShaderStageInterface{SURFACE_INTERFACE} dataIn[]; + +out ShaderStageInterface{SURFACE_INTERFACE} dataOut; + +# define PASS_SURFACE_INTERFACE(vert) \ + dataOut.worldPosition = dataIn[vert].worldPosition; \ + dataOut.viewPosition = dataIn[vert].viewPosition; \ + dataOut.worldNormal = dataIn[vert].worldNormal; \ + dataOut.viewNormal = dataIn[vert].viewNormal; + +#else + +IN_OUT ShaderStageInterface{SURFACE_INTERFACE}; + +#endif + +#ifdef HAIR_SHADER +IN_OUT ShaderHairInterface +{ + /* world space */ + vec3 hairTangent; + float hairThickTime; + float hairThickness; + float hairTime; + flat int hairStrandID; +}; +#endif diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 1b94fc2bee1..0ad1393dd70 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -1,32 +1,20 @@ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + #ifndef HAIR_SHADER in vec3 pos; in vec3 nor; #endif -#ifdef MESH_SHADER -out vec3 worldPosition; -out vec3 viewPosition; -out vec3 worldNormal; -out vec3 viewNormal; -#endif - -#ifdef HAIR_SHADER -out vec3 hairTangent; -out float hairThickTime; -out float hairThickness; -out float hairTime; -flat out int hairStrandID; -#endif +RESOURCE_ID_VARYING void main() { -#ifdef GPU_INTEL - /* Due to some shader compiler bug, we somewhat - * need to access gl_VertexID to make it work. even - * if it's actually dead code. */ - gl_Position.x = float(gl_VertexID); -#endif + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + PASS_RESOURCE_ID #ifdef HAIR_SHADER hairStrandID = hair_get_strand_id(); @@ -63,7 +51,10 @@ void main() /* No need to normalize since this is just a rotation. */ viewNormal = normal_world_to_view(worldNormal); # ifdef USE_ATTR - pass_attr(pos); +# ifdef HAIR_SHADER + pos = hair_get_strand_pos(); +# endif + pass_attr(pos, NormalMatrix, ModelMatrixInverse); # endif #endif } diff --git a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl b/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl index 02ad2170f06..0c01c46c2ba 100644 --- a/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/update_noise_frag.glsl @@ -1,11 +1,11 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + uniform sampler2D blueNoise; uniform vec3 offsets; out vec4 FragColor; -#define M_2PI 6.28318530717958647692 - void main(void) { vec3 blue_noise = texelFetch(blueNoise, ivec2(gl_FragCoord.xy), 0).xyz; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index 312fc07054a..bac69ab0355 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -1,9 +1,10 @@ +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) +#pragma BLENDER_REQUIRE(closure_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ -#define NODETREE_EXEC - #ifdef MESH_SHADER uniform vec3 volumeOrcoLoc; uniform vec3 volumeOrcoSize; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl index 96b891c929f..30cda401284 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + #ifdef MESH_SHADER /* TODO tight slices */ layout(triangles) in; @@ -12,12 +14,16 @@ in vec4 vPos[]; flat out int slice; +RESOURCE_ID_VARYING + #ifdef MESH_SHADER /* TODO tight slices */ void main() { gl_Layer = slice = int(vPos[0].z); + PASS_RESOURCE_ID + # ifdef USE_ATTR pass_attr(0); # endif @@ -48,6 +54,8 @@ void main() { gl_Layer = slice = int(vPos[0].z); + PASS_RESOURCE_ID + # ifdef USE_ATTR pass_attr(0); # endif diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl index c3c442e7b69..f4276bd61bd 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ @@ -11,9 +13,11 @@ uniform sampler3D volumeExtinction; #ifdef USE_VOLUME_OPTI uniform layout(binding = 0, r11f_g11f_b10f) writeonly restrict image3D finalScattering_img; uniform layout(binding = 1, r11f_g11f_b10f) writeonly restrict image3D finalTransmittance_img; + vec3 finalScattering; vec3 finalTransmittance; #else + flat in int slice; layout(location = 0) out vec3 finalScattering; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl index 40eb3da42d1..9b852a57ec4 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_lib.glsl @@ -1,4 +1,8 @@ +#pragma BLENDER_REQUIRE(lights_lib.glsl) +#pragma BLENDER_REQUIRE(lightprobe_lib.glsl) +#pragma BLENDER_REQUIRE(irradiance_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ @@ -58,7 +62,6 @@ float phase_function(vec3 v, vec3 l, float g) return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0)); } -#ifdef LAMPS_LIB vec3 light_volume(LightData ld, vec4 l_vector) { float power; @@ -95,7 +98,7 @@ vec3 light_volume(LightData ld, vec4 l_vector) return tint * lum; } -# define VOLUMETRIC_SHADOW_MAX_STEP 32.0 +#define VOLUMETRIC_SHADOW_MAX_STEP 32.0 vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction) { @@ -109,7 +112,7 @@ vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction) vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D volume_extinction) { -# if defined(VOLUME_SHADOW) +#if defined(VOLUME_SHADOW) /* Heterogeneous volume shadows */ float dd = l_vector.w / volShadowSteps; vec3 L = l_vector.xyz * l_vector.w; @@ -120,27 +123,24 @@ vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D v shadow *= exp(-s_extinction * dd); } return shadow; -# else +#else return vec3(1.0); -# endif /* VOLUME_SHADOW */ +#endif /* VOLUME_SHADOW */ } -#endif -#ifdef IRRADIANCE_LIB vec3 irradiance_volumetric(vec3 wpos) { -# ifdef IRRADIANCE_HL2 +#ifdef IRRADIANCE_HL2 IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0)); vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; ir_data = load_irradiance_cell(0, vec3(-1.0)); irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2]; irradiance *= 0.16666666; /* 1/6 */ return irradiance; -# else +#else return vec3(0.0); -# endif -} #endif +} uniform sampler3D inScattering; uniform sampler3D inTransmittance; diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl index 1ff7e848c40..6ab21587c9a 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_resolve_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl index 9621fa1cc0d..806f1b5b205 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_scatter_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(volumetric_lib.glsl) + /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl index b96360febb0..b70747ecec3 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -1,6 +1,10 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + out vec4 vPos; +RESOURCE_ID_VARYING + void main() { /* Generate Triangle : less memory fetches from a VBO */ @@ -25,7 +29,9 @@ void main() vPos.z = float(t_id); vPos.w = 1.0; + PASS_RESOURCE_ID + #ifdef USE_ATTR - pass_attr(vec3(0.0)); + pass_attr(vec3(0.0), mat3(1), mat4(1)); #endif } diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 6e6b35e19ca..ed443b2c73f 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -63,7 +63,7 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_ ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); if (ibuf != NULL && ibuf->rect != NULL) { - gpu_tex = GPU_texture_from_blender(image, &iuser, ibuf, GL_TEXTURE_2D); + gpu_tex = BKE_image_get_gpu_texture(image, &iuser, ibuf); *r_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL); } BKE_image_release_ibuf(image, ibuf, lock); diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 47f05eda58e..1e766b3cc39 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -53,8 +53,6 @@ #include "ED_view3d.h" -#include "GPU_draw.h" - #include "overlay_private.h" #include "draw_common.h" @@ -1339,7 +1337,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, else { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); - if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag && (1 << 0))) { + if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag & (1 << 0))) { ListBase targets = {NULL, NULL}; bConstraintTarget *ct; @@ -1422,7 +1420,7 @@ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb, line_count /= fds->res[axis]; } - GPU_create_smoke_velocity(fmd); + DRW_smoke_ensure_velocity(fmd); GPUShader *sh = OVERLAY_shader_volume_velocity(use_needle); DRWShadingGroup *grp = DRW_shgroup_create(sh, data->psl->extra_ps[0]); @@ -1452,7 +1450,7 @@ static void OVERLAY_volume_free_smoke_textures(OVERLAY_Data *data) LinkData *link; while ((link = BLI_pophead(&data->stl->pd->smoke_domains))) { FluidModifierData *fmd = (FluidModifierData *)link->data; - GPU_free_smoke_velocity(fmd); + DRW_smoke_free_velocity(fmd); MEM_freeN(link); } } diff --git a/source/blender/draw/engines/overlay/overlay_image.c b/source/blender/draw/engines/overlay/overlay_image.c index 67132a9e0ed..d456e147c15 100644 --- a/source/blender/draw/engines/overlay/overlay_image.c +++ b/source/blender/draw/engines/overlay/overlay_image.c @@ -175,7 +175,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp } width = ibuf->x; height = ibuf->y; - tex = GPU_texture_from_blender(image, iuser, ibuf, GL_TEXTURE_2D); + tex = BKE_image_get_gpu_texture(image, iuser, ibuf); BKE_image_release_ibuf(image, ibuf, lock); iuser->scene = NULL; @@ -203,7 +203,7 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp } BKE_movieclip_user_set_frame(&bgpic->cuser, ctime); - tex = GPU_texture_from_movieclip(clip, &bgpic->cuser, GL_TEXTURE_2D); + tex = BKE_movieclip_get_gpu_texture(clip, &bgpic->cuser); if (tex == NULL) { return NULL; } @@ -232,7 +232,7 @@ static void OVERLAY_image_free_movieclips_textures(OVERLAY_Data *data) LinkData *link; while ((link = BLI_pophead(&data->stl->pd->bg_movie_clips))) { MovieClip *clip = (MovieClip *)link->data; - GPU_free_texture_movieclip(clip); + BKE_movieclip_free_gputexture(clip); MEM_freeN(link); } } @@ -383,7 +383,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) if (ima != NULL) { ImageUser iuser = *ob->iuser; camera_background_images_stereo_setup(draw_ctx->scene, draw_ctx->v3d, ima, &iuser); - tex = GPU_texture_from_blender(ima, &iuser, NULL, GL_TEXTURE_2D); + tex = BKE_image_get_gpu_texture(ima, &iuser, NULL); if (tex) { size[0] = GPU_texture_orig_width(tex); size[1] = GPU_texture_orig_height(tex); diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c index e94cc820568..25b8a953ba7 100644 --- a/source/blender/draw/engines/overlay/overlay_paint.c +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -22,6 +22,8 @@ #include "DRW_render.h" +#include "BKE_image.h" + #include "DNA_mesh_types.h" #include "DEG_depsgraph_query.h" @@ -136,7 +138,7 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata) state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); - GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, NULL, GL_TEXTURE_2D); + GPUTexture *tex = BKE_image_get_gpu_texture(imapaint->stencil, NULL, NULL); const bool mask_premult = (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL); const bool mask_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0; diff --git a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl index 0d01f67c6ea..2989e07691f 100644 --- a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + uniform sampler2D colorTex; uniform sampler2D depthTex; uniform sampler2D lineTex; diff --git a/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl index 380708795e9..0925901a9c9 100644 --- a/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl @@ -14,19 +14,6 @@ layout(depth_greater) out float gl_FragDepth; layout(location = 0) out vec4 fragColor; layout(location = 1) out vec4 lineOutput; -#define cameraPos ViewMatrixInverse[3].xyz - -float get_depth_from_view_z(float z) -{ - if (ProjectionMatrix[3][3] == 0.0) { - z = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2]; - } - else { - z = z * ProjectionMatrix[2][2] / (1.0 - ProjectionMatrix[3][2]); - } - return z * 0.5 + 0.5; -} - void main() { const float sphere_radius = 0.05; diff --git a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl index 317e9fe0447..d0b68df0625 100644 --- a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl @@ -14,8 +14,6 @@ uniform float meshSize; uniform float lineKernel = 0.0; uniform sampler2D depthBuffer; -#define cameraPos (ViewMatrixInverse[3].xyz) - uniform int gridFlag; #define STEPS_LEN 8 diff --git a/source/blender/draw/engines/overlay/shaders/grid_vert.glsl b/source/blender/draw/engines/overlay/shaders/grid_vert.glsl index 496bb011c74..dd0e771ad93 100644 --- a/source/blender/draw/engines/overlay/shaders/grid_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/grid_vert.glsl @@ -7,8 +7,6 @@ uniform float meshSize; uniform int gridFlag; -#define cameraPos (ViewMatrixInverse[3].xyz) - #define PLANE_XY (1 << 4) #define PLANE_XZ (1 << 5) #define PLANE_YZ (1 << 6) diff --git a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl index 722dbdd0b5e..d0d52c8485b 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl @@ -28,7 +28,7 @@ void cavity_compute(vec2 screenco, return; } - vec3 position = view_position_from_depth(screenco, depth, ViewVecs, ProjectionMatrix); + vec3 position = get_view_space_from_depth(screenco, depth); vec3 normal = workbench_normal_decode(texture(normalBuffer, screenco)); vec2 jitter_co = (screenco * world_data.viewport_size.xy) * world_data.cavity_jitter_scale; @@ -68,7 +68,7 @@ void cavity_compute(vec2 screenco, bool is_background = (s_depth == 1.0); /* This trick provide good edge effect even if no neighbor is found. */ s_depth = (is_background) ? depth : s_depth; - vec3 s_pos = view_position_from_depth(uvcoords, s_depth, ViewVecs, ProjectionMatrix); + vec3 s_pos = get_view_space_from_depth(uvcoords, s_depth); if (is_background) { s_pos.z -= world_data.cavity_distance; 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 c529f23265b..eb61edca6c7 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl @@ -67,31 +67,3 @@ void workbench_float_pair_decode(float data, out float v1, out float v2) v1 = float(idata & v1_mask) * (1.0 / float(v1_mask)); v2 = float(idata >> int(ROUGHNESS_BITS)) * (1.0 / float(v2_mask)); } - -vec3 view_vector_from_screen_uv(vec2 uv, vec4 viewvecs[2], mat4 proj_mat) -{ - if (proj_mat[3][3] == 0.0) { - return normalize(vec3(viewvecs[0].xy + uv * viewvecs[1].xy, 1.0)); - } - else { - return vec3(0.0, 0.0, 1.0); - } -} - -vec3 view_position_from_depth(vec2 uvcoords, float depth, vec4 viewvecs[2], mat4 proj_mat) -{ - if (proj_mat[3][3] == 0.0) { - /* Perspective */ - float d = 2.0 * depth - 1.0; - - float zview = -proj_mat[3][2] / (d + proj_mat[2][2]); - - return zview * vec3(viewvecs[0].xy + uvcoords * viewvecs[1].xy, 1.0); - } - else { - /* Orthographic */ - vec3 offset = vec3(uvcoords, depth); - - return viewvecs[0].xyz + offset * viewvecs[1].xyz; - } -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl index f3a238fd112..6e10a656fc1 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl @@ -14,7 +14,7 @@ out vec4 fragColor; void main() { /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ - vec3 I = view_vector_from_screen_uv(uvcoordsvar.st, ViewVecs, ProjectionMatrix); + vec3 I = get_view_vector_from_screen_uv(uvcoordsvar.st); vec3 N = workbench_normal_decode(texture(normalBuffer, uvcoordsvar.st)); vec4 mat_data = texture(materialBuffer, uvcoordsvar.st); 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 51007a9f246..71816f6ff6e 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,3 +1,6 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + /** * Separable Hexagonal Bokeh Blur by Colin Barré-Brisebois * https://colinbarrebrisebois.com/2017/04/18/hexagonal-bokeh-blur-revisited-part-1-basic-3-pass-version/ @@ -21,13 +24,6 @@ uniform sampler2D noiseTex; #define dof_distance dofParams.y #define dof_invsensorsize dofParams.z -#define M_PI 3.1415926535897932 /* pi */ - -float max_v4(vec4 v) -{ - return max(max(v.x, v.y), max(v.z, v.w)); -} - #define weighted_sum(a, b, c, d, e, e_sum) \ ((a)*e.x + (b)*e.y + (c)*e.z + (d)*e.w) / max(1e-6, e_sum); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl index ba8eeff1001..fd4d00d96dd 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl @@ -57,7 +57,7 @@ void main() { /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ vec2 uv_viewport = gl_FragCoord.xy * world_data.viewport_size_inv; - vec3 I = view_vector_from_screen_uv(uv_viewport, ViewVecs, ProjectionMatrix); + vec3 I = get_view_vector_from_screen_uv(uv_viewport); vec3 N = normalize(normal_interp); vec3 color = color_interp; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 6ab652cbf36..aa938d80fa3 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -1,4 +1,5 @@ +#pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl) #pragma BLENDER_REQUIRE(workbench_data_lib.glsl) @@ -33,11 +34,6 @@ float phase_function_isotropic() return 1.0 / (4.0 * M_PI); } -float max_v3(vec3 v) -{ - return max(v.x, max(v.y, v.z)); -} - float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection) { /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ @@ -194,8 +190,8 @@ void main() float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; float depth_end = min(depth, gl_FragCoord.z); - vec3 vs_ray_end = view_position_from_depth(screen_uv, depth_end, ViewVecs, ProjectionMatrix); - vec3 vs_ray_ori = view_position_from_depth(screen_uv, 0.0, ViewVecs, ProjectionMatrix); + vec3 vs_ray_end = get_view_space_from_depth(screen_uv, depth_end); + vec3 vs_ray_ori = get_view_space_from_depth(screen_uv, 0.0); vec3 vs_ray_dir = (is_persp) ? (vs_ray_end - vs_ray_ori) : vec3(0.0, 0.0, -1.0); vs_ray_dir /= abs(vs_ray_dir.z); diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 3fa2e7ef084..824c3dc5ff1 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -261,11 +261,11 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd, if (ima) { if (ima->source == IMA_SRC_TILED) { - tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D_ARRAY); - tex_tile_data = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_1D_ARRAY); + tex = BKE_image_get_gpu_tiles(ima, iuser, NULL); + tex_tile_data = BKE_image_get_gpu_tilemap(ima, iuser, NULL); } else { - tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D); + tex = BKE_image_get_gpu_texture(ima, iuser, NULL); } } diff --git a/source/blender/draw/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c index 835f10598d4..aab3cef00e6 100644 --- a/source/blender/draw/engines/workbench/workbench_shader.c +++ b/source/blender/draw/engines/workbench/workbench_shader.c @@ -28,6 +28,8 @@ #include "workbench_engine.h" #include "workbench_private.h" +extern char datatoc_common_math_lib_glsl[]; +extern char datatoc_common_math_geom_lib_glsl[]; extern char datatoc_common_hair_lib_glsl[]; extern char datatoc_common_pointcloud_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; @@ -119,6 +121,8 @@ void workbench_shader_library_ensure(void) if (e_data.lib == NULL) { e_data.lib = DRW_shader_library_create(); /* NOTE: Theses needs to be ordered by dependencies. */ + DRW_SHADER_LIB_ADD(e_data.lib, common_math_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_math_geom_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); DRW_SHADER_LIB_ADD(e_data.lib, common_pointcloud_lib); diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 8e345f8275b..d3c4d51dbd4 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -38,8 +38,6 @@ #include "BKE_volume.h" #include "BKE_volume_render.h" -#include "GPU_draw.h" - void workbench_volume_engine_init(WORKBENCH_Data *vedata) { WORKBENCH_TextureList *txl = vedata->txl; @@ -79,13 +77,10 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, wpd->volumes_do = true; if (fds->use_coba) { - GPU_create_smoke_coba_field(fmd); - } - else if (!(fds->flags & FLUID_DOMAIN_USE_NOISE)) { - GPU_create_smoke(fmd, 0); + DRW_smoke_ensure_coba_field(fmd); } - else if (fds->flags & FLUID_DOMAIN_USE_NOISE) { - GPU_create_smoke(fmd, 1); + else { + DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE); } if ((!fds->use_coba && (fds->tex_density == NULL && fds->tex_color == NULL)) || @@ -293,7 +288,7 @@ void workbench_volume_draw_finish(WORKBENCH_Data *vedata) * all viewport in a redraw at least. */ LISTBASE_FOREACH (LinkData *, link, &wpd->smoke_domains) { FluidModifierData *fmd = (FluidModifierData *)link->data; - GPU_free_smoke(fmd); + DRW_smoke_free(fmd); } BLI_freelistN(&wpd->smoke_domains); } diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 7a889d9399e..3551296cfc3 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -198,6 +198,17 @@ void DRW_uniformbuffer_free(struct GPUUniformBuffer *ubo); } while (0) /* Shaders */ + +#ifndef __GPU_MATERIAL_H__ +/* FIXME: Meh avoid including all GPUMaterial. */ +typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat, + int options, + const char **vert_code, + const char **geom_code, + const char **frag_lib, + const char **defines); +#endif + struct GPUShader *DRW_shader_create(const char *vert, const char *geom, const char *frag, @@ -237,7 +248,8 @@ struct GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, const char *geom, const char *frag_lib, const char *defines, - bool deferred); + bool deferred, + GPUMaterialEvalCallbackFn callback); struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, struct Material *ma, struct bNodeTree *ntree, @@ -248,7 +260,8 @@ struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, const char *geom, const char *frag_lib, const char *defines, - bool deferred); + bool deferred, + GPUMaterialEvalCallbackFn callback); void DRW_shader_free(struct GPUShader *shader); #define DRW_SHADER_FREE_SAFE(shader) \ do { \ diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c index 17902f27513..06cedb9f72c 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c @@ -158,6 +158,7 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * const bool has_radius = pointcloud->radius != NULL; static GPUVertFormat format = {0}; + static GPUVertFormat format_no_radius = {0}; static uint pos; if (format.attr_len == 0) { /* initialize vertex format */ @@ -167,11 +168,11 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * * If the vertex shader has more components than the array provides, the extras are given * values from the vector (0, 0, 0, 1) for the missing XYZW components. */ - int comp_len = has_radius ? 4 : 3; - pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, comp_len, GPU_FETCH_FLOAT); + pos = GPU_vertformat_attr_add(&format_no_radius, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } - cache->pos = GPU_vertbuf_create_with_format(&format); + cache->pos = GPU_vertbuf_create_with_format(has_radius ? &format : &format_no_radius); GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint); if (has_radius) { diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 81c0e97a1a8..09f901c344f 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -27,8 +27,10 @@ struct DRWPass; struct DRWShadingGroup; struct GPUMaterial; struct ModifierData; +struct FluidModifierData; struct Object; struct ParticleSystem; +struct RegionView3D; struct ViewLayer; #define UBO_FIRST_COLOR colorWire @@ -159,14 +161,14 @@ void DRW_globals_update(void); void DRW_globals_free(void); struct DRWView *DRW_view_create_with_zoffset(const struct DRWView *parent_view, - const RegionView3D *rv3d, + const struct RegionView3D *rv3d, float offset); int DRW_object_wire_theme_get(struct Object *ob, struct ViewLayer *view_layer, float **r_color); float *DRW_color_background_blend_get(int theme_id); -bool DRW_object_is_flat(Object *ob, int *r_axis); -bool DRW_object_axis_orthogonal_to_view(Object *ob, int axis); +bool DRW_object_is_flat(struct Object *ob, int *r_axis); +bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); /* draw_hair.c */ @@ -188,6 +190,16 @@ void DRW_hair_init(void); void DRW_hair_update(void); void DRW_hair_free(void); +/* draw_fluid.c */ + +/* Fluid simulation. */ +void DRW_smoke_ensure(struct FluidModifierData *fmd, int highres); +void DRW_smoke_ensure_coba_field(struct FluidModifierData *fmd); +void DRW_smoke_ensure_velocity(struct FluidModifierData *fmd); + +void DRW_smoke_free(struct FluidModifierData *fmd); +void DRW_smoke_free_velocity(struct FluidModifierData *fmd); + /* draw_common.c */ struct DRW_Global { /** If needed, contains all global/Theme colors diff --git a/source/blender/gpu/intern/gpu_draw_smoke.c b/source/blender/draw/intern/draw_fluid.c index e0b94e20574..33311dc698f 100644 --- a/source/blender/gpu/intern/gpu_draw_smoke.c +++ b/source/blender/draw/intern/draw_fluid.c @@ -35,10 +35,10 @@ #include "BKE_colorband.h" -#include "GPU_draw.h" -#include "GPU_glew.h" #include "GPU_texture.h" +#include "draw_common.h" /* Own include. */ + #ifdef WITH_FLUID # include "manta_fluid_API.h" #endif @@ -62,7 +62,8 @@ static void create_flame_spectrum_texture(float *data) # define MAX_FIRE_ALPHA 0.06f # define FULL_ON_FIRE 100 - float *spec_pixels = MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels"); + float *spec_pixels = (float *)MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), + "spec_pixels"); blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000); @@ -105,7 +106,7 @@ static void create_color_ramp(const struct ColorBand *coba, float *data) static GPUTexture *create_transfer_function(int type, const struct ColorBand *coba) { - float *data = MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__); + float *data = (float *)MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__); switch (type) { case TFUNC_FLAME_SPECTRUM: @@ -184,7 +185,7 @@ static GPUTexture *create_field_texture(FluidDomainSettings *fds) } GPUTexture *tex = GPU_texture_create_nD( - fds->res[0], fds->res[1], fds->res[2], 3, field, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); + UNPACK3(fds->res), 3, field, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); swizzle_texture_channel_single(tex); return tex; @@ -203,7 +204,7 @@ static GPUTexture *create_density_texture(FluidDomainSettings *fds, int highres) } GPUTexture *tex = GPU_texture_create_nD( - dim[0], dim[1], dim[2], 3, data, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); + UNPACK3(dim), 3, data, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); swizzle_texture_channel_single(tex); @@ -221,7 +222,7 @@ static GPUTexture *create_color_texture(FluidDomainSettings *fds, int highres) int cell_count = (highres) ? manta_noise_get_cells(fds->fluid) : fds->total_cells; int *dim = (highres) ? fds->res_noise : fds->res; - float *data = MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture"); + float *data = (float *)MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture"); if (data == NULL) { return NULL; @@ -276,7 +277,7 @@ static GPUTexture *create_flame_texture(FluidDomainSettings *fds, int highres) /** \name Public API * \{ */ -void GPU_free_smoke(FluidModifierData *fmd) +void DRW_smoke_free(FluidModifierData *fmd) { if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) { if (fmd->domain->tex_density) { @@ -316,7 +317,7 @@ void GPU_free_smoke(FluidModifierData *fmd) } } -void GPU_create_smoke_coba_field(FluidModifierData *fmd) +void DRW_smoke_ensure_coba_field(FluidModifierData *fmd) { #ifndef WITH_FLUID UNUSED_VARS(fmd); @@ -334,7 +335,7 @@ void GPU_create_smoke_coba_field(FluidModifierData *fmd) #endif } -void GPU_create_smoke(FluidModifierData *fmd, int highres) +void DRW_smoke_ensure(FluidModifierData *fmd, int highres) { #ifndef WITH_FLUID UNUSED_VARS(fmd, highres); @@ -355,9 +356,7 @@ void GPU_create_smoke(FluidModifierData *fmd, int highres) fds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL); } if (!fds->tex_shadow) { - fds->tex_shadow = GPU_texture_create_nD(fds->res[0], - fds->res[1], - fds->res[2], + fds->tex_shadow = GPU_texture_create_nD(UNPACK3(fds->res), 3, manta_smoke_get_shadow(fds->fluid), GPU_R8, @@ -370,7 +369,7 @@ void GPU_create_smoke(FluidModifierData *fmd, int highres) #endif /* WITH_FLUID */ } -void GPU_create_smoke_velocity(FluidModifierData *fmd) +void DRW_smoke_ensure_velocity(FluidModifierData *fmd) { #ifndef WITH_FLUID UNUSED_VARS(fmd); @@ -387,19 +386,16 @@ void GPU_create_smoke_velocity(FluidModifierData *fmd) } if (!fds->tex_velocity_x) { - fds->tex_velocity_x = GPU_texture_create_3d( - fds->res[0], fds->res[1], fds->res[2], GPU_R16F, vel_x, NULL); - fds->tex_velocity_y = GPU_texture_create_3d( - fds->res[0], fds->res[1], fds->res[2], GPU_R16F, vel_y, NULL); - fds->tex_velocity_z = GPU_texture_create_3d( - fds->res[0], fds->res[1], fds->res[2], GPU_R16F, vel_z, NULL); + fds->tex_velocity_x = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_x, NULL); + fds->tex_velocity_y = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_y, NULL); + fds->tex_velocity_z = GPU_texture_create_3d(UNPACK3(fds->res), GPU_R16F, vel_z, NULL); } } #endif /* WITH_FLUID */ } -/* TODO Unify with the other GPU_free_smoke. */ -void GPU_free_smoke_velocity(FluidModifierData *fmd) +/* TODO Unify with the other DRW_smoke_free. */ +void DRW_smoke_free_velocity(FluidModifierData *fmd) { if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) { if (fmd->domain->tex_velocity_x) { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index cea5dea6029..61c9bf65507 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2716,7 +2716,7 @@ void DRW_engines_free(void) void DRW_render_context_enable(Render *render) { if (G.background && DST.gl_context == NULL) { - WM_init_opengl(G_MAIN); + WM_init_opengl(); } void *re_gl_context = RE_gl_context_get(render); @@ -2835,7 +2835,7 @@ void DRW_opengl_context_disable_ex(bool restore) void DRW_opengl_context_enable(void) { if (G.background && DST.gl_context == NULL) { - WM_init_opengl(G_MAIN); + WM_init_opengl(); } DRW_opengl_context_enable_ex(true); } diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 9d8050504ab..3184d6155d2 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1292,13 +1292,10 @@ static DRWShadingGroup *drw_shgroup_material_create_ex(GPUPass *gpupass, DRWPass } static void drw_shgroup_material_texture(DRWShadingGroup *grp, - GPUMaterialTexture *tex, + GPUTexture *gputex, const char *name, - eGPUSamplerState state, - int textarget) + eGPUSamplerState state) { - GPUTexture *gputex = GPU_texture_from_blender(tex->ima, tex->iuser, NULL, textarget); - DRW_shgroup_uniform_texture_ex(grp, name, gputex, state); GPUTexture **gputex_ref = BLI_memblock_alloc(DST.vmempool->images); @@ -1314,15 +1311,16 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial LISTBASE_FOREACH (GPUMaterialTexture *, tex, &textures) { if (tex->ima) { /* Image */ + GPUTexture *gputex; if (tex->tiled_mapping_name[0]) { - drw_shgroup_material_texture( - grp, tex, tex->sampler_name, tex->sampler_state, GL_TEXTURE_2D_ARRAY); - drw_shgroup_material_texture( - grp, tex, tex->tiled_mapping_name, tex->sampler_state, GL_TEXTURE_1D_ARRAY); + gputex = BKE_image_get_gpu_tiles(tex->ima, tex->iuser, NULL); + drw_shgroup_material_texture(grp, gputex, tex->sampler_name, tex->sampler_state); + gputex = BKE_image_get_gpu_tilemap(tex->ima, tex->iuser, NULL); + drw_shgroup_material_texture(grp, gputex, tex->tiled_mapping_name, tex->sampler_state); } else { - drw_shgroup_material_texture( - grp, tex, tex->sampler_name, tex->sampler_state, GL_TEXTURE_2D); + gputex = BKE_image_get_gpu_texture(tex->ima, tex->iuser, NULL); + drw_shgroup_material_texture(grp, gputex, tex->sampler_name, tex->sampler_state); } } else if (tex->colorband) { diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index b6f51ada5a1..91cbc03e5a4 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -446,6 +446,7 @@ void DRW_state_reset(void) DRW_state_reset_ex(DRW_STATE_DEFAULT); GPU_texture_unbind_all(); + GPU_uniformbuffer_unbind_all(); /* Should stay constant during the whole rendering. */ GPU_point_size(5); @@ -669,8 +670,7 @@ BLI_INLINE void draw_geometry_bind(DRWShadingGroup *shgroup, GPUBatch *geom) DST.batch = geom; - GPU_batch_program_set_no_use( - geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader)); + GPU_batch_set_shader_no_bind(geom, shgroup->shader); geom->program_in_use = true; /* XXX hacking #GPUBatch */ @@ -773,10 +773,11 @@ static bool ubo_bindings_validate(DRWShadingGroup *shgroup) DRWPass *parent_pass = DRW_memblock_elem_from_handle(DST.vmempool->passes, &shgroup->pass_handle); - printf("Pass : %s, Shader : %s, Block : %s\n", + printf("Pass : %s, Shader : %s, Block : %s, Binding %d\n", parent_pass->name, shgroup->shader->name, - blockname); + blockname, + binding); } } # endif @@ -1106,6 +1107,7 @@ 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_shader_bind(shgroup->shader); diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 0bb20631537..fec234c5015 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -430,7 +430,8 @@ GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, const char *geom, const char *frag_lib, const char *defines, - bool deferred) + bool deferred, + GPUMaterialEvalCallbackFn callback) { GPUMaterial *mat = NULL; if (DRW_state_is_image_render() || !deferred) { @@ -450,7 +451,8 @@ GPUMaterial *DRW_shader_create_from_world(struct Scene *scene, geom, frag_lib, defines, - wo->id.name); + wo->id.name, + callback); } if (GPU_material_status(mat) == GPU_MAT_QUEUED) { @@ -470,7 +472,8 @@ GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, const char *geom, const char *frag_lib, const char *defines, - bool deferred) + bool deferred, + GPUMaterialEvalCallbackFn callback) { GPUMaterial *mat = NULL; if (DRW_state_is_image_render() || !deferred) { @@ -490,7 +493,8 @@ GPUMaterial *DRW_shader_create_from_material(struct Scene *scene, geom, frag_lib, defines, - ma->id.name); + ma->id.name, + callback); } if (GPU_material_status(mat) == GPU_MAT_QUEUED) { diff --git a/source/blender/draw/intern/draw_select_buffer.c b/source/blender/draw/intern/draw_select_buffer.c index 84c8d0f861f..ee5561e1e38 100644 --- a/source/blender/draw/intern/draw_select_buffer.c +++ b/source/blender/draw/intern/draw_select_buffer.c @@ -395,7 +395,7 @@ uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph, int center_x = width / 2; int center_y = height / 2; - /* Manhatten distance in keeping with other screen-based selection. */ + /* Manhattan distance in keeping with other screen-based selection. */ *dist = (uint)(abs(hit_x - center_x) + abs(hit_y - center_y)); /* Indices start at 1 here. */ diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index 3c470f802ec..b42700b2c7e 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -184,8 +184,7 @@ void DRW_draw_cursor(void) GPUBatch *cursor_batch = DRW_cache_cursor_get(is_aligned); GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR); - GPU_batch_program_set( - cursor_batch, GPU_shader_get_program(shader), GPU_shader_get_interface(shader)); + GPU_batch_set_shader(cursor_batch, shader); GPU_batch_draw(cursor_batch); diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index ffff631e34b..8684d82f228 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -95,7 +95,7 @@ void hair_get_interp_attrs( * For final drawing, the vertex index and the number of vertex per segment */ -#ifndef HAIR_PHASE_SUBDIV +#if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER) int hair_get_strand_id(void) { return gl_VertexID / (hairStrandsRes * hairThicknessRes); @@ -206,4 +206,24 @@ vec3 hair_get_strand_pos(void) return texelFetch(hairPointBuffer, id).point_position; } +vec2 hair_get_barycentric(void) +{ + /* To match cycles without breaking into individual segment we encode if we need to invert + * the first component into the second component. We invert if the barycentricTexCo.y + * is NOT 0.0 or 1.0. */ + int id = hair_get_base_id(); + return vec2(float((id % 2) == 1), float(((id % 4) % 3) > 0)); +} + #endif + +/* To be fed the result of hair_get_barycentric from vertex shader. */ +vec2 hair_resolve_barycentric(vec2 vert_barycentric) +{ + if (fract(vert_barycentric.y) != 0.0) { + return vec2(vert_barycentric.x, 0.0); + } + else { + return vec2(1.0 - vert_barycentric.x, 0.0); + } +} diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl index e337376d7c4..643d7e7d942 100644 --- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl @@ -116,4 +116,4 @@ vec3 normal_decode(vec2 enc, vec3 view) return n; } -/** \} */
\ No newline at end of file +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl index 36b67f2bd60..625e8bb1ff8 100644 --- a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl +++ b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl @@ -36,4 +36,4 @@ vec3 pointcloud_get_pos(void) vec3 outpos, outnor; pointcloud_get_pos_and_nor(outpos, outnor); return outpos; -}
\ No newline at end of file +} diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 095bc64a19e..a55d2cd8c1a 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -22,6 +22,16 @@ layout(std140) uniform viewBlock vec4 CameraTexCoFactors; }; +#define ViewNear (ViewVecs[0].w) +#define ViewFar (ViewVecs[1].w) + +#define cameraForward ViewMatrixInverse[2].xyz +#define cameraPos ViewMatrixInverse[3].xyz +#define cameraVec \ + ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - worldPosition) : cameraForward) +#define viewCameraVec \ + ((ProjectionMatrix[3][3] == 0.0) ? normalize(-viewPosition) : vec3(0.0, 0.0, 1.0)) + #ifdef world_clip_planes_calc_clip_distance # undef world_clip_planes_calc_clip_distance # define world_clip_planes_calc_clip_distance(p) \ @@ -104,10 +114,13 @@ uniform int resourceId; /* Use this to declare and pass the value if * the fragment shader uses the resource_id. */ -# define RESOURCE_ID_VARYING flat out int resourceIDFrag; -# define RESOURCE_ID_VARYING_GEOM flat out int resourceIDGeom; -# define PASS_RESOURCE_ID resourceIDFrag = resource_id; -# define PASS_RESOURCE_ID_GEOM resourceIDGeom = resource_id; +# ifdef USE_GEOMETRY_SHADER +# define RESOURCE_ID_VARYING flat out int resourceIDGeom; +# define PASS_RESOURCE_ID resourceIDGeom = resource_id; +# else +# define RESOURCE_ID_VARYING flat out int resourceIDFrag; +# define PASS_RESOURCE_ID resourceIDFrag = resource_id; +# endif #endif /* If used in a fragment / geometry shader, we pass @@ -118,7 +131,7 @@ uniform int resourceId; flat in int resourceIDGeom[]; # define resource_id resourceIDGeom -# define PASS_RESOURCE_ID(i) resourceIDFrag = resource_id[i]; +# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0]; #endif #ifdef GPU_FRAGMENT_SHADER @@ -171,9 +184,12 @@ uniform mat4 ModelMatrixInverse; * Note: This is only valid because we are only using the mat3 of the ViewMatrixInverse. * ViewMatrix * transpose(ModelMatrixInverse) **/ -#define normal_object_to_view(n) (mat3(ViewMatrix) * (transpose(mat3(ModelMatrixInverse)) * n)) -#define normal_object_to_world(n) (transpose(mat3(ModelMatrixInverse)) * n) -#define normal_world_to_object(n) (transpose(mat3(ModelMatrix)) * n) +#define NormalMatrix transpose(mat3(ModelMatrixInverse)) +#define NormalMatrixInverse transpose(mat3(ModelMatrix)) + +#define normal_object_to_view(n) (mat3(ViewMatrix) * (NormalMatrix * n)) +#define normal_object_to_world(n) (NormalMatrix * n) +#define normal_world_to_object(n) (NormalMatrixInverse * n) #define normal_world_to_view(n) (mat3(ViewMatrix) * n) #define normal_view_to_world(n) (mat3(ViewMatrixInverse) * n) @@ -199,3 +215,78 @@ uniform mat4 ModelMatrixInverse; #define DRW_BASE_FROM_DUPLI (1 << 2) #define DRW_BASE_FROM_SET (1 << 3) #define DRW_BASE_ACTIVE (1 << 4) + +/* ---- Opengl Depth conversion ---- */ + +float linear_depth(bool is_persp, float z, float zf, float zn) +{ + if (is_persp) { + return (zn * zf) / (z * (zn - zf) + zf); + } + else { + return (z * 2.0 - 1.0) * zf; + } +} + +float buffer_depth(bool is_persp, float z, float zf, float zn) +{ + if (is_persp) { + return (zf * (zn - z)) / (z * (zn - zf)); + } + else { + return (z / (zf * 2.0)) + 0.5; + } +} + +float get_view_z_from_depth(float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = 2.0 * depth - 1.0; + return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); + } + else { + return ViewVecs[0].z + depth * ViewVecs[1].z; + } +} + +float get_depth_from_view_z(float z) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2]; + return d * 0.5 + 0.5; + } + else { + return (z - ViewVecs[0].z) / ViewVecs[1].z; + } +} + +vec2 get_uvs_from_view(vec3 view) +{ + vec4 ndc = ProjectionMatrix * vec4(view, 1.0); + return (ndc.xy / ndc.w) * 0.5 + 0.5; +} + +vec3 get_view_space_from_depth(vec2 uvcoords, float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + return vec3(ViewVecs[0].xy + uvcoords * ViewVecs[1].xy, 1.0) * get_view_z_from_depth(depth); + } + else { + return ViewVecs[0].xyz + vec3(uvcoords, depth) * ViewVecs[1].xyz; + } +} + +vec3 get_world_space_from_depth(vec2 uvcoords, float depth) +{ + return (ViewMatrixInverse * vec4(get_view_space_from_depth(uvcoords, depth), 1.0)).xyz; +} + +vec3 get_view_vector_from_screen_uv(vec2 uv) +{ + if (ProjectionMatrix[3][3] == 0.0) { + return normalize(vec3(ViewVecs[0].xy + uv * ViewVecs[1].xy, 1.0)); + } + else { + return vec3(0.0, 0.0, 1.0); + } +} diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 50733afe6fb..8280b58c21a 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -427,7 +427,7 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac) ac->spacetype = (area) ? area->spacetype : 0; ac->regiontype = (region) ? region->regiontype : 0; - /* initialise default y-scale factor */ + /* Initialize default y-scale factor. */ animedit_get_yscale_factor(ac); /* get data context info */ diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 8df9c99896e..e60270bc3f0 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -251,7 +251,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) */ BLI_dlrbTree_init(&pso->keys); - /* initialise numeric input */ + /* Initialize numeric input. */ initNumInput(&pso->num); pso->num.idx_max = 0; /* one axis */ pso->num.val_flag[0] |= NUM_NO_NEGATIVE; @@ -1310,7 +1310,7 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ @@ -1370,7 +1370,7 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ @@ -1429,7 +1429,7 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ @@ -1489,7 +1489,7 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ @@ -1549,7 +1549,7 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven pso = op->customdata; - /* initialise percentage so that it won't pop on first mouse move */ + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); /* do common setup work */ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 91a8ea0fa3a..72ea52314b9 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1137,7 +1137,7 @@ int ED_curve_updateAnimPaths(Main *bmain, Curve *cu) /** \name Edit Mode Conversion (Make & Load) * \{ */ -static int *initialize_index_map(Object *obedit, int *r_old_totvert) +static int *init_index_map(Object *obedit, int *r_old_totvert) { Curve *curve = (Curve *)obedit->data; EditNurb *editnurb = curve->editnurb; @@ -1225,7 +1225,7 @@ static void remap_hooks_and_vertex_parents(Main *bmain, Object *obedit) if ((object->parent) && (object->parent->data == curve) && ELEM(object->partype, PARVERT1, PARVERT3)) { if (old_to_new_map == NULL) { - old_to_new_map = initialize_index_map(obedit, &old_totvert); + old_to_new_map = init_index_map(obedit, &old_totvert); } if (object->par1 < old_totvert) { @@ -1254,7 +1254,7 @@ static void remap_hooks_and_vertex_parents(Main *bmain, Object *obedit) int i, j; if (old_to_new_map == NULL) { - old_to_new_map = initialize_index_map(obedit, &old_totvert); + old_to_new_map = init_index_map(obedit, &old_totvert); } for (i = j = 0; i < hmd->totindex; i++) { diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index b759277572c..d78c543f94b 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -76,9 +76,9 @@ static int kill_selection(Object *obedit, int ins); /** \name Internal Utilities * \{ */ -static wchar_t findaccent(wchar_t char1, uint code) +static char32_t findaccent(char32_t char1, uint code) { - wchar_t new = 0; + char32_t new = 0; if (char1 == 'a') { if (code == '`') { @@ -682,7 +682,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const cu->strinfo = MEM_callocN((nchars + 4) * sizeof(CharInfo), "strinfo"); cu->len = 0; - cu->len_wchar = nchars - 1; + cu->len_char32 = nchars - 1; cu->pos = 0; s = cu->str; @@ -703,7 +703,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const } } - cu->pos = cu->len_wchar; + cu->pos = cu->len_char32; *s = '\0'; WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, obedit); @@ -1661,7 +1661,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) uintptr_t ascii = event->ascii; int alt = event->alt, shift = event->shift, ctrl = event->ctrl; int event_type = event->type, event_val = event->val; - wchar_t inserted_text[2] = {0}; + char32_t inserted_text[2] = {0}; if (RNA_struct_property_is_set(op->ptr, "text")) { return insert_text_exec(C, op); @@ -1733,7 +1733,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* store as utf8 in RNA string */ char inserted_utf8[8] = {0}; - BLI_strncpy_wchar_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8)); + BLI_str_utf32_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8)); RNA_string_set(op->ptr, "text", inserted_utf8); } @@ -1867,7 +1867,7 @@ void ED_curve_editfont_make(Object *obedit) { Curve *cu = obedit->data; EditFont *ef = cu->editfont; - int len_wchar; + int len_char32; if (ef == NULL) { ef = cu->editfont = MEM_callocN(sizeof(EditFont), "editfont"); @@ -1876,10 +1876,10 @@ void ED_curve_editfont_make(Object *obedit) ef->textbufinfo = MEM_callocN((MAXTEXT + 4) * sizeof(CharInfo), "texteditbufinfo"); } - /* Convert the original text to wchar_t */ - len_wchar = BLI_str_utf8_as_utf32(ef->textbuf, cu->str, MAXTEXT + 4); - BLI_assert(len_wchar == cu->len_wchar); - ef->len = len_wchar; + /* Convert the original text to chat32_t. */ + len_char32 = BLI_str_utf8_as_utf32(ef->textbuf, cu->str, MAXTEXT + 4); + BLI_assert(len_char32 == cu->len_char32); + ef->len = len_char32; BLI_assert(ef->len >= 0); memcpy(ef->textbufinfo, cu->strinfo, ef->len * sizeof(CharInfo)); @@ -1908,7 +1908,7 @@ void ED_curve_editfont_load(Object *obedit) MEM_freeN(cu->str); /* Calculate the actual string length in UTF-8 variable characters */ - cu->len_wchar = ef->len; + cu->len_char32 = ef->len; cu->len = BLI_str_utf32_as_utf8_len(ef->textbuf); /* Alloc memory for UTF-8 variable char length string */ @@ -1920,8 +1920,8 @@ void ED_curve_editfont_load(Object *obedit) if (cu->strinfo) { MEM_freeN(cu->strinfo); } - cu->strinfo = MEM_callocN((cu->len_wchar + 4) * sizeof(CharInfo), "texteditinfo"); - memcpy(cu->strinfo, ef->textbufinfo, cu->len_wchar * sizeof(CharInfo)); + cu->strinfo = MEM_callocN((cu->len_char32 + 4) * sizeof(CharInfo), "texteditinfo"); + memcpy(cu->strinfo, ef->textbufinfo, cu->len_char32 * sizeof(CharInfo)); /* Other vars */ cu->pos = ef->pos; diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index 71a364c60d7..033673a99a8 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -29,7 +29,6 @@ #include "ED_view3d.h" #include "GPU_batch.h" -#include "GPU_glew.h" #include "GPU_immediate.h" #include "MEM_guardedalloc.h" 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 85f84af5f14..406d66dfd8f 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -999,7 +999,7 @@ static int gizmo_cage2d_modal(bContext *C, if (data->dial == NULL) { MUL_V2_V3_M4_FINAL(test_co, data->orig_matrix_offset[3]); - data->dial = BLI_dial_initialize(test_co, FLT_EPSILON); + data->dial = BLI_dial_init(test_co, FLT_EPSILON); MUL_V2_V3_M4_FINAL(test_co, data->orig_mouse); BLI_dial_angle(data->dial, test_co); diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 6f700f6c4b8..ab83abb6b37 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -200,7 +200,7 @@ typedef struct tGPsdata { /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX (U.gp_manhattendist) +#define MIN_MANHATTAN_PX (U.gp_manhattandist) /* minimum length of new segment before new point can be added */ #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) @@ -297,7 +297,7 @@ static bool annotation_stroke_filtermval(tGPsdata *p, const float mval[2], float return false; } - if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) { + if ((dx > MIN_MANHATTAN_PX) && (dy > MIN_MANHATTAN_PX)) { return true; } @@ -603,7 +603,7 @@ static short annotation_stroke_addpoint(tGPsdata *p, /* store settings */ copy_v2_v2(&pt->x, mval); pt->pressure = pressure; - /* unused for annotations, but initialise for easier conversions to GP Object */ + /* Unused for annotations, but initialize for easier conversions to GP Object. */ pt->strength = 1.0f; /* point time */ diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 96b0296a7de..348fb614977 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -861,6 +861,154 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot) INT_MAX); } +/* ********************* Clean Duplicated Frames ************************** */ +static bool gpencil_frame_is_equal(bGPDframe *gpf_a, bGPDframe *gpf_b) +{ + if ((gpf_a == NULL) || (gpf_b == NULL)) { + return false; + } + /* If the number of strokes is different, cannot be equal. */ + int totstrokes_a = BLI_listbase_count(&gpf_a->strokes); + int totstrokes_b = BLI_listbase_count(&gpf_b->strokes); + if ((totstrokes_a == 0) || (totstrokes_b == 0) || (totstrokes_a != totstrokes_b)) { + return false; + } + /* Loop all strokes and check. */ + bGPDstroke *gps_a = gpf_a->strokes.first; + bGPDstroke *gps_b = gpf_b->strokes.first; + for (int i = 0; i < totstrokes_a; i++) { + /* If the number of points is different, cannot be equal. */ + if (gps_a->totpoints != gps_b->totpoints) { + return false; + } + /* Check other variables. */ + if (!equals_v4v4(gps_a->vert_color_fill, gps_b->vert_color_fill)) { + return false; + } + if (gps_a->thickness != gps_b->thickness) { + return false; + } + if (gps_a->mat_nr != gps_b->mat_nr) { + return false; + } + if (gps_a->caps[0] != gps_b->caps[0]) { + return false; + } + if (gps_a->caps[1] != gps_b->caps[1]) { + return false; + } + if (gps_a->hardeness != gps_b->hardeness) { + return false; + } + if (!equals_v2v2(gps_a->aspect_ratio, gps_b->aspect_ratio)) { + return false; + } + if (gps_a->uv_rotation != gps_b->uv_rotation) { + return false; + } + if (!equals_v2v2(gps_a->uv_translation, gps_b->uv_translation)) { + return false; + } + if (gps_a->uv_scale != gps_b->uv_scale) { + return false; + } + + /* Loop points and check if equals or not. */ + for (int p = 0; p < gps_a->totpoints; p++) { + bGPDspoint *pt_a = &gps_a->points[p]; + bGPDspoint *pt_b = &gps_b->points[p]; + if (!equals_v3v3(&pt_a->x, &pt_b->x)) { + return false; + } + if (pt_a->pressure != pt_b->pressure) { + return false; + } + if (pt_a->strength != pt_b->strength) { + return false; + } + if (pt_a->uv_fac != pt_b->uv_fac) { + return false; + } + if (pt_a->uv_rot != pt_b->uv_rot) { + return false; + } + if (!equals_v4v4(pt_a->vert_color, pt_b->vert_color)) { + return false; + } + } + + /* Look at next pair of strokes. */ + gps_a = gps_a->next; + gps_b = gps_b->next; + } + + return true; +} + +static int gpencil_frame_clean_duplicate_exec(bContext *C, wmOperator *op) +{ +#define SELECTED 1 + + bool changed = false; + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + const int type = RNA_enum_get(op->ptr, "type"); + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Only editable and visible layers are considered. */ + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->frames.first != NULL)) { + bGPDframe *gpf = gpl->frames.first; + + if ((type == SELECTED) && ((gpf->flag & GP_FRAME_SELECT) == 0)) { + continue; + } + + while (gpf != NULL) { + if (gpencil_frame_is_equal(gpf, gpf->next)) { + /* Remove frame. */ + BKE_gpencil_layer_frame_delete(gpl, gpf->next); + /* Tag for recalc. */ + changed = true; + } + else { + gpf = gpf->next; + } + } + } + } + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_frame_clean_duplicate(wmOperatorType *ot) +{ + static const EnumPropertyItem clean_type[] = { + {0, "ALL", 0, "All Frames", ""}, + {1, "SELECTED", 0, "Selected Frames", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Clean Duplicated Frames"; + ot->idname = "GPENCIL_OT_frame_clean_duplicate"; + ot->description = "Remove any duplicated frame"; + + /* callbacks */ + ot->exec = gpencil_frame_clean_duplicate_exec; + ot->poll = gpencil_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", clean_type, 0, "Type", ""); +} + /* *********************** Hide Layers ******************************** */ static int gpencil_hide_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 286efeeff01..9658dc04b52 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -94,6 +94,8 @@ /** \name Stroke Edit Mode Management * \{ */ +static void gpencil_flip_stroke(bGPDstroke *gps); + /* poll callback for all stroke editing operators */ static bool gpencil_stroke_edit_poll(bContext *C) { @@ -1126,6 +1128,11 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps) pt->flag |= GP_SPOINT_SELECT; } + /* Flip stroke if it was only one point to consider extrude point as last point. */ + if (gps->totpoints == 2) { + gpencil_flip_stroke(gps); + } + /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gps); @@ -1764,7 +1771,7 @@ static int gpencil_blank_frame_add_exec(bContext *C, wmOperator *op) const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); - /* Initialise datablock and an active layer if nothing exists yet */ + /* Initialize data-block and an active layer if nothing exists yet. */ if (ELEM(NULL, gpd, active_gpl)) { /* Let's just be lazy, and call the "Add New Layer" operator, * which sets everything up as required. */ @@ -4283,14 +4290,14 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_prev, dupflag); ob_dst = base_new->object; ob_dst->mode = OB_MODE_OBJECT; - /* Duplication will increment bGPdata usercount, but since we create a new greasepencil datablock - * for ob_dst (which gets its own user automatically), we have to decrement the usercount again. - */ + /* Duplication will increment #bGPdata user-count, but since we create a new grease-pencil + * data-block for ob_dst (which gets its own user automatically), + * we have to decrement the user-count again. */ gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2); id_us_min(ob_dst->data); ob_dst->data = (bGPdata *)gpd_dst; - /* loop old datablock and separate parts */ + /* Loop old data-block and separate parts. */ if ((mode == GP_SEPARATE_POINT) || (mode == GP_SEPARATE_STROKE)) { CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { gpl_dst = NULL; diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 517225e5a81..d7525c3c6a5 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -416,10 +416,10 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) /* create a image to see result of template */ if (ibuf->rect_float) { - GPU_offscreen_read_pixels(offscreen, GL_FLOAT, ibuf->rect_float); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, ibuf->rect_float); } else if (ibuf->rect) { - GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, ibuf->rect); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_UNSIGNED_BYTE, ibuf->rect); } if (ibuf->rect_float && ibuf->rect) { IMB_rect_from_float(ibuf); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index bd697dbc768..68424839ab0 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -485,6 +485,7 @@ void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); void GPENCIL_OT_frame_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot); void GPENCIL_OT_frame_clean_loose(struct wmOperatorType *ot); +void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index efee05f7da3..3892f451e18 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -600,6 +600,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_frame_duplicate); WM_operatortype_append(GPENCIL_OT_frame_clean_fill); WM_operatortype_append(GPENCIL_OT_frame_clean_loose); + WM_operatortype_append(GPENCIL_OT_frame_clean_duplicate); WM_operatortype_append(GPENCIL_OT_convert); WM_operatortype_append(GPENCIL_OT_bake_mesh_animation); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 1880045e238..9ec04bbb553 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -268,7 +268,7 @@ typedef struct tGPsdata { /* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX (U.gp_manhattendist) +#define MIN_MANHATTEN_PX (U.gp_manhattandist) /* minimum length of new segment before new point can be added */ #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) @@ -1817,15 +1817,15 @@ static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p) changed = true; } /* Be sure curves are initializated. */ - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_sensitivity); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_strength); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_jitter); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_pressure); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_strength); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_uv); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_hue); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_saturation); - BKE_curvemapping_initialize(paint->brush->gpencil_settings->curve_rand_value); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_sensitivity); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_strength); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_jitter); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_pressure); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_strength); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_uv); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_hue); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_saturation); + BKE_curvemapping_init(paint->brush->gpencil_settings->curve_rand_value); /* assign to temp tGPsdata */ p->brush = paint->brush; diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 30be9bde7ae..35449ad4d34 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -695,7 +695,6 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) && (tgpi->rv3d->persp == RV3D_CAMOB) && (!is_depth); - const bool is_vertex_stroke = GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush); if (tgpi->type == GP_STROKE_BOX) { gps->totpoints = (tgpi->tot_edges * 4 + tgpi->tot_stored_edges); @@ -741,13 +740,13 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) gpencil_session_validatebuffer(tgpi); gpencil_init_colors(tgpi); if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_primitive); + BKE_curvemapping_init(ts->gp_sculpt.cur_primitive); } if (brush_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { - BKE_curvemapping_initialize(brush_settings->curve_jitter); + BKE_curvemapping_init(brush_settings->curve_jitter); } if (brush_settings->flag & GP_BRUSH_USE_STRENGTH_PRESSURE) { - BKE_curvemapping_initialize(brush_settings->curve_strength); + BKE_curvemapping_init(brush_settings->curve_strength); } /* get an array of depths, far depths are blended */ @@ -1020,12 +1019,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) pt->time = 0.0f; pt->flag = 0; pt->uv_fac = tpt->uv_fac; - if (is_vertex_stroke) { - copy_v4_v4(pt->vert_color, tpt->vert_color); - } - else { - zero_v4(pt->vert_color); - } + ED_gpencil_point_vertex_color_set(ts, brush, pt, tpt); if (gps->dvert != NULL) { MDeformVert *dvert = &gps->dvert[i]; @@ -1091,7 +1085,7 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive gpencil_primitive_update_strokes(C, tgpi); } -/* Initialise mouse points */ +/* Initialize mouse points. */ static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event) { copy_v2fl_v2i(tgpi->mval, event->mval); diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 20eeab65623..43c8b766c52 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -444,7 +444,9 @@ typedef struct tGPSB_Grab_StrokeData { int size; } tGPSB_Grab_StrokeData; -/* initialise custom data for handling this stroke */ +/** + * Initialize custom data for handling this stroke. + */ static void gpencil_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) { tGPSB_Grab_StrokeData *data = NULL; @@ -910,13 +912,13 @@ typedef struct tGPSB_CloneBrushData { GHash *new_colors; } tGPSB_CloneBrushData; -/* Initialise "clone" brush data */ +/* Initialize "clone" brush data. */ static void gpencil_brush_clone_init(bContext *C, tGP_BrushEditData *gso) { tGPSB_CloneBrushData *data; bGPDstroke *gps; - /* init custom data */ + /* Initialize custom-data. */ gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData"); /* compute midpoint of strokes on clipboard */ @@ -1188,7 +1190,7 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) Paint *paint = &ts->gp_sculptpaint->paint; gso->brush = paint->brush; - BKE_curvemapping_initialize(gso->brush->curve); + BKE_curvemapping_init(gso->brush->curve); /* save mask */ gso->mask = ts->gpencil_selectmode_sculpt; @@ -1200,10 +1202,10 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) /* Init multi-edit falloff curve data before doing anything, * so we won't have to do it again later. */ if (gso->is_multiframe) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); } - /* initialise custom data for brushes */ + /* Initialize custom data for brushes. */ char tool = gso->brush->gpencil_sculpt_tool; switch (tool) { case GPSCULPT_TOOL_CLONE: { @@ -1229,13 +1231,13 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) op->customdata = NULL; return false; } - /* initialise customdata */ + /* Initialize custom-data. */ gpencil_brush_clone_init(C, gso); break; } case GPSCULPT_TOOL_GRAB: { - /* initialise the cache needed for this brush */ + /* Initialize the cache needed for this brush. */ gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash"); break; } @@ -1936,7 +1938,7 @@ static int gpencil_sculpt_brush_invoke(bContext *C, wmOperator *op, const wmEven gso = op->customdata; - /* initialise type-specific data (used for the entire session) */ + /* Initialize type-specific data (used for the entire session). */ char tool = gso->brush->gpencil_sculpt_tool; switch (tool) { /* Brushes requiring timer... */ diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index f77bb394567..f2ccbd6d2cf 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -1500,7 +1500,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) if (ts->gp_sculpt.cur_falloff == NULL) { ts->gp_sculpt.cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); CurveMapping *gp_falloff_curve = ts->gp_sculpt.cur_falloff; - BKE_curvemapping_initialize(gp_falloff_curve); + BKE_curvemapping_init(gp_falloff_curve); BKE_curvemap_reset(gp_falloff_curve->cm, &gp_falloff_curve->clipr, CURVE_PRESET_GAUSS, @@ -2688,7 +2688,11 @@ void ED_gpencil_tag_scene_gpencil(Scene *scene) void ED_gpencil_fill_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDstroke *gps) { - if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) { + const bool is_vertex = (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) && + (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) || + (!GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) && + (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR)); + if (is_vertex) { copy_v3_v3(gps->vert_color_fill, brush->rgb); gps->vert_color_fill[3] = brush->gpencil_settings->vertex_factor; srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill); @@ -2703,7 +2707,12 @@ void ED_gpencil_point_vertex_color_set(ToolSettings *ts, bGPDspoint *pt, tGPspoint *tpt) { - if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) { + const bool is_vertex = (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) && + (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) || + (!GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) && + (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR)); + + if (is_vertex) { if (tpt == NULL) { copy_v3_v3(pt->vert_color, brush->rgb); pt->vert_color[3] = brush->gpencil_settings->vertex_factor; @@ -2859,6 +2868,18 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph, bGPdata *gpd_eval = (bGPdata *)ob_eval->data; MaterialGPencilStyle *gp_style = material->gp_style; + const bool is_vertex_fill = + (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) && + (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) || + (!GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush) && + (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR)); + + const bool is_vertex_stroke = + (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) && + (brush->gpencil_settings->brush_draw_mode != GP_BRUSH_MODE_MATERIAL)) || + (!GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush) && + (brush->gpencil_settings->brush_draw_mode == GP_BRUSH_MODE_VERTEXCOLOR)); + int idx = gpd->runtime.sbuffer_used; tGPspoint *tpt = (tGPspoint *)gpd->runtime.sbuffer + idx; @@ -2868,14 +2889,14 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph, srgb_to_linearrgb_v4(vertex_color, vertex_color); /* Copy fill vertex color. */ - if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) { + if (is_vertex_fill) { copy_v4_v4(gpd->runtime.vert_color_fill, vertex_color); } else { copy_v4_v4(gpd->runtime.vert_color_fill, gp_style->fill_rgba); } /* Copy stroke vertex color. */ - if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) { + if (is_vertex_stroke) { copy_v4_v4(tpt->vert_color, vertex_color); } else { diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index c36bc4388d7..b0dff6589da 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -727,7 +727,7 @@ static bool gpencil_vertexpaint_brush_init(bContext *C, wmOperator *op) gso->brush = paint->brush; srgb_to_linearrgb_v3_v3(gso->linear_color, gso->brush->rgb); - BKE_curvemapping_initialize(gso->brush->curve); + BKE_curvemapping_init(gso->brush->curve); gso->is_painting = false; gso->first = true; @@ -759,7 +759,7 @@ static bool gpencil_vertexpaint_brush_init(bContext *C, wmOperator *op) /* Init multi-edit falloff curve data before doing anything, * so we won't have to do it again later. */ if (gso->is_multiframe) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); } /* Setup space conversions. */ diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index 4392ec92824..9d3e9c6ae45 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -295,7 +295,7 @@ static bool gpencil_weightpaint_brush_init(bContext *C, wmOperator *op) gso->bmain = CTX_data_main(C); gso->brush = paint->brush; - BKE_curvemapping_initialize(gso->brush->curve); + BKE_curvemapping_init(gso->brush->curve); gso->is_painting = false; gso->first = true; @@ -326,7 +326,7 @@ static bool gpencil_weightpaint_brush_init(bContext *C, wmOperator *op) /* Init multi-edit falloff curve data before doing anything, * so we won't have to do it again later. */ if (gso->is_multiframe) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); } /* Setup space conversions. */ diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index 1eb22fb34e2..cfd92bf3113 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -24,6 +24,8 @@ #ifndef __BIF_GLUTIL_H__ #define __BIF_GLUTIL_H__ +#include "GPU_texture.h" + #ifdef __cplusplus extern "C" { #endif @@ -66,9 +68,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float xzoom, float yzoom, @@ -78,9 +79,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float clip_min_x, float clip_min_y, @@ -94,9 +94,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -108,9 +107,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -133,7 +131,7 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, void ED_draw_imbuf(struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, struct ColorManagedViewSettings *view_settings, struct ColorManagedDisplaySettings *display_settings, float zoom_x, @@ -141,7 +139,7 @@ void ED_draw_imbuf(struct ImBuf *ibuf, void ED_draw_imbuf_clipping(struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, struct ColorManagedViewSettings *view_settings, struct ColorManagedDisplaySettings *display_settings, float clip_min_x, @@ -155,14 +153,14 @@ void ED_draw_imbuf_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float zoom_x, float zoom_y); void ED_draw_imbuf_ctx_clipping(const struct bContext *C, struct ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float clip_min_x, float clip_min_y, float clip_max_x, diff --git a/source/blender/editors/include/ED_buttons.h b/source/blender/editors/include/ED_buttons.h index 2eaef5e82e0..455eee8580d 100644 --- a/source/blender/editors/include/ED_buttons.h +++ b/source/blender/editors/include/ED_buttons.h @@ -27,6 +27,10 @@ extern "C" { #endif +struct SpaceProperties; + +int ED_buttons_tabs_list(struct SpaceProperties *sbuts, int *context_tabs_array); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h index 8c8f3e6f4a3..16d05a7793a 100644 --- a/source/blender/editors/include/ED_numinput.h +++ b/source/blender/editors/include/ED_numinput.h @@ -103,8 +103,12 @@ bool handleNumInput(struct bContext *C, NumInput *n, const struct wmEvent *event #define NUM_MODAL_INCREMENT_UP 18 #define NUM_MODAL_INCREMENT_DOWN 19 -bool user_string_to_number( - bContext *C, const char *str, const struct UnitSettings *unit, int type, double *r_value); +bool user_string_to_number(bContext *C, + const char *str, + const struct UnitSettings *unit, + int type, + const char *error_prefix, + double *r_value); /** \} */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 71b7d35908b..fcc70a49d36 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -73,7 +73,7 @@ void ED_region_exit(struct bContext *C, struct ARegion *region); void ED_region_remove(struct bContext *C, struct ScrArea *area, struct ARegion *region); void ED_region_pixelspace(struct ARegion *region); void ED_region_update_rect(struct ARegion *region); -void ED_region_floating_initialize(struct ARegion *region); +void ED_region_floating_init(struct ARegion *region); void ED_region_tag_redraw(struct ARegion *region); void ED_region_tag_redraw_partial(struct ARegion *region, const struct rcti *rct, bool rebuild); void ED_region_tag_redraw_cursor(struct ARegion *region); @@ -171,7 +171,7 @@ void ED_spacetypes_keymap(struct wmKeyConfig *keyconf); int ED_area_header_switchbutton(const struct bContext *C, struct uiBlock *block, int yco); /* areas */ -void ED_area_initialize(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); +void ED_area_init(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); void ED_area_exit(struct bContext *C, struct ScrArea *area); int ED_screen_area_active(const struct bContext *C); void ED_screen_global_areas_refresh(struct wmWindow *win); @@ -221,7 +221,7 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); vert_name->next) /* screens */ -void ED_screens_initialize(struct Main *bmain, struct wmWindowManager *wm); +void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm); void ED_screen_draw_edges(struct wmWindow *win); void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2); void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac); @@ -287,6 +287,12 @@ bool ED_workspace_delete(struct WorkSpace *workspace, struct bContext *C, struct wmWindowManager *wm) ATTR_NONNULL(); void ED_workspace_scene_data_sync(struct WorkSpaceInstanceHook *hook, Scene *scene) ATTR_NONNULL(); +struct WorkSpaceLayout *ED_workspace_screen_change_ensure_unused_layout( + struct Main *bmain, + struct WorkSpace *workspace, + struct WorkSpaceLayout *layout_new, + const struct WorkSpaceLayout *layout_fallback_base, + struct wmWindow *win) ATTR_NONNULL(); struct WorkSpaceLayout *ED_workspace_layout_add(struct Main *bmain, struct WorkSpace *workspace, struct wmWindow *win, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 4060efbda92..bc542084292 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -699,17 +699,6 @@ float ED_view3d_grid_view_scale(struct Scene *scene, void ED_scene_draw_fps(const struct Scene *scene, int xoffset, int *yoffset); -/* view matrix properties utilities */ -/* unused */ -#if 0 -void ED_view3d_operator_properties_viewmat(struct wmOperatorType *ot); -void ED_view3d_operator_properties_viewmat_set(struct bContext *C, struct wmOperator *op); -void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, - int *winx, - int *winy, - float persmat[4][4]); -#endif - /* render */ void ED_view3d_stop_render_preview(struct wmWindowManager *wm, struct ARegion *region); void ED_view3d_shade_update(struct Main *bmain, struct View3D *v3d, struct ScrArea *area); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index e0bd1369a51..2c42f3a5071 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -101,7 +101,7 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle; /* use for clamping popups within the screen */ #define UI_SCREEN_MARGIN 10 -/* uiBlock->dt and uiBut->dt */ +/** #uiBlock.emboss and #uiBut.emboss */ enum { UI_EMBOSS = 0, /* use widget style for drawing */ UI_EMBOSS_NONE = 1, /* Nothing, only icon and/or text */ @@ -657,7 +657,7 @@ bool UI_popup_block_name_exists(const struct bScreen *screen, const char *name); uiBlock *UI_block_begin(const struct bContext *C, struct ARegion *region, const char *name, - short dt); + char emboss); void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], int r_xy[2]); void UI_block_end(const struct bContext *C, uiBlock *block); void UI_block_draw(const struct bContext *C, struct uiBlock *block); @@ -671,7 +671,7 @@ enum { }; void UI_block_theme_style_set(uiBlock *block, char theme_style); char UI_block_emboss_get(uiBlock *block); -void UI_block_emboss_set(uiBlock *block, char dt); +void UI_block_emboss_set(uiBlock *block, char emboss); void UI_block_free(const struct bContext *C, uiBlock *block); void UI_blocklist_free(const struct bContext *C, struct ListBase *lb); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index adb2e0e3b23..5dfecacf81b 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -52,7 +52,6 @@ #include "BKE_screen.h" #include "BKE_unit.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_state.h" @@ -1487,7 +1486,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) continue; } } - else if (but->dt != UI_EMBOSS_PULLDOWN) { + else if (but->emboss != UI_EMBOSS_PULLDOWN) { continue; } @@ -2801,25 +2800,39 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size) return str; } -static bool ui_set_but_string_eval_num_unit(bContext *C, - uiBut *but, - const char *str, - double *r_value) +/** + * Report a generic error prefix when evaluating a string with #BPY_execute_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") + +static bool ui_number_from_string_units( + bContext *C, const char *str, const int unit_type, const UnitSettings *unit, double *r_value) { + return user_string_to_number(C, str, unit, unit_type, UI_NUMBER_EVAL_ERROR_PREFIX, r_value); +} + +static bool ui_number_from_string_units_with_but(bContext *C, + const char *str, + const uiBut *but, + double *r_value) +{ + const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); const UnitSettings *unit = but->block->unit; - int type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); - return user_string_to_number(C, str, unit, type, r_value); + return ui_number_from_string_units(C, str, unit_type, unit, r_value); } static bool ui_number_from_string(bContext *C, const char *str, double *r_value) { + bool ok; #ifdef WITH_PYTHON - return BPY_execute_string_as_number(C, NULL, str, true, r_value); + ok = BPY_execute_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value); #else UNUSED_VARS(C); *r_value = atof(str); - return true; + ok = true; #endif + return ok; } static bool ui_number_from_string_factor(bContext *C, const char *str, double *r_value) @@ -2853,7 +2866,7 @@ static bool ui_number_from_string_percentage(bContext *C, const char *str, doubl return ui_number_from_string(C, str, r_value); } -bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double *r_value) +bool ui_but_string_eval_number(bContext *C, const uiBut *but, const char *str, double *r_value) { if (str[0] == '\0') { *r_value = 0.0; @@ -2867,7 +2880,7 @@ bool ui_but_string_set_eval_num(bContext *C, uiBut *but, const char *str, double if (ui_but_is_float(but)) { if (ui_but_is_unit(but)) { - return ui_set_but_string_eval_num_unit(C, but, str, r_value); + return ui_number_from_string_units_with_but(C, str, but, r_value); } if (subtype == PROP_FACTOR) { return ui_number_from_string_factor(C, str, r_value); @@ -3007,7 +3020,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) /* number editing */ double value; - if (ui_but_string_set_eval_num(C, but, str, &value) == false) { + if (ui_but_string_eval_number(C, but, str, &value) == false) { WM_report_banner_show(); return false; } @@ -3371,7 +3384,7 @@ void UI_block_region_set(uiBlock *block, ARegion *region) block->oldblock = oldblock; } -uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, short dt) +uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, char emboss) { uiBlock *block; wmWindow *window; @@ -3382,7 +3395,7 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh block = MEM_callocN(sizeof(uiBlock), "uiBlock"); block->active = 1; - block->dt = dt; + block->emboss = emboss; block->evil_C = (void *)C; /* XXX */ if (scn) { @@ -3421,12 +3434,12 @@ uiBlock *UI_block_begin(const bContext *C, ARegion *region, const char *name, sh char UI_block_emboss_get(uiBlock *block) { - return block->dt; + return block->emboss; } -void UI_block_emboss_set(uiBlock *block, char dt) +void UI_block_emboss_set(uiBlock *block, char emboss) { - block->dt = dt; + block->emboss = emboss; } void UI_block_theme_style_set(uiBlock *block, char theme_style) @@ -3806,7 +3819,7 @@ static uiBut *ui_def_but(uiBlock *block, but->tip = tip; but->disabled_info = block->lockstr; - but->dt = block->dt; + but->emboss = block->emboss; but->pie_dir = UI_RADIAL_NONE; but->block = block; /* pointer back, used for frontbuffer status, and picker */ @@ -4339,7 +4352,7 @@ static uiBut *ui_def_but_rna(uiBlock *block, } if (type == UI_BTYPE_MENU) { - if (but->dt == UI_EMBOSS_PULLDOWN) { + if (but->emboss == UI_EMBOSS_PULLDOWN) { ui_but_submenu_enable(block, but); } } diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index a08c5c45b6f..aaa5e1c0cf1 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -407,7 +407,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, true, &expr_result)) { + if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { STRNCPY(drawstr, expr_result); MEM_freeN(expr_result); } diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index cc5d21c3df3..df11a78e657 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -773,9 +773,8 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region), (float)rect->ymin, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, ibuf->rect, 1.0f, 1.0f, @@ -2257,12 +2256,12 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* 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; - uint tot_points = (uint)PROF_N_TABLE(profile->path_len) + 1 + add_left_tri + add_bottom_tri; + uint tot_points = (uint)PROF_TABLE_LEN(profile->path_len) + 1 + add_left_tri + add_bottom_tri; 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"); - for (i = 0; i < (uint)PROF_N_TABLE(profile->path_len); + for (i = 0; i < (uint)PROF_TABLE_LEN(profile->path_len); i++) { /* Only add the points from the table here. */ table_coords[i][0] = pts[i].x; table_coords[i][1] = pts[i].y; @@ -2545,9 +2544,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), rect.ymin + 1, drawibuf->x, drawibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_LINEAR, + GPU_RGBA8, + true, drawibuf->rect, 1.0f, 1.0f, diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 93b052b3b69..5da82b5be9c 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -39,8 +39,6 @@ #include "RNA_access.h" -#include "GPU_glew.h" - #include "UI_interface.h" #include "IMB_colormanagement.h" diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index b94224f3604..4944e29fcec 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2364,7 +2364,7 @@ static void ui_but_paste_numeric_value(bContext *C, { double value; - if (ui_but_string_set_eval_num(C, but, buf_paste, &value)) { + if (ui_but_string_eval_number(C, but, buf_paste, &value)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); data->value = value; ui_but_string_set(C, but, buf_paste); @@ -4387,7 +4387,7 @@ static int ui_do_but_TEX( if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && (!UI_but_is_utf8(but))) { /* pass - allow filesel, enter to execute */ } - else if (but->dt == UI_EMBOSS_NONE && !event->ctrl) { + else if (but->emboss == UI_EMBOSS_NONE && !event->ctrl) { /* pass */ } else { @@ -7126,7 +7126,7 @@ static int ui_do_but_CURVEPROFILE( dist_min_sq = square_f(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */ /* Loop through the path's high resolution table and find what's near the click. */ - for (int i = 1; i <= PROF_N_TABLE(profile->path_len); i++) { + for (int i = 1; i <= PROF_TABLE_LEN(profile->path_len); i++) { copy_v2_v2(f_xy_prev, f_xy); BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[i].x); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 586f5e07997..a7b7bad2fe6 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1517,18 +1517,8 @@ static void icon_draw_rect(float x, immUniform1f("factor", desaturate); } - immDrawPixelsTex(&state, - draw_x, - draw_y, - draw_w, - draw_h, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - rect, - 1.0f, - 1.0f, - col); + immDrawPixelsTex( + &state, draw_x, draw_y, draw_w, draw_h, GPU_RGBA8, false, rect, 1.0f, 1.0f, col); if (ima) { IMB_freeImBuf(ima); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 3b39e7d2161..92efa27f916 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -233,8 +233,8 @@ struct uiBut { const char *disabled_info; BIFIconID icon; - /** drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied from the block */ - char dt; + /** emboss: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied from the #uiBlock.emboss */ + char emboss; /** direction in a pie menu, used for collision detection (RadialDirection) */ signed char pie_dir; /** could be made into a single flag */ @@ -405,8 +405,8 @@ struct uiBlock { char direction; /** UI_BLOCK_THEME_STYLE_* */ char theme_style; - /** drawtype: UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied to buttons */ - char dt; + /** UI_EMBOSS, UI_EMBOSS_NONE ... etc, copied to #uiBut.emboss */ + char emboss; bool auto_open; char _pad[5]; double auto_open_last; @@ -517,10 +517,10 @@ extern void ui_but_string_get(uiBut *but, char *str, const size_t maxlen) ATTR_N extern char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size); extern void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen) ATTR_NONNULL(); extern bool ui_but_string_set(struct bContext *C, uiBut *but, const char *str) ATTR_NONNULL(); -extern bool ui_but_string_set_eval_num(struct bContext *C, - uiBut *but, - const char *str, - double *value) ATTR_NONNULL(); +extern bool ui_but_string_eval_number(struct bContext *C, + const uiBut *but, + const char *str, + double *value) ATTR_NONNULL(); extern int ui_but_string_get_max_length(uiBut *but); /* Clear & exit the active button's string. */ extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL(); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index b707aaa0ee9..e82a42d9ad7 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -674,7 +674,7 @@ static void ui_item_array(uiLayout *layout, /* show checkboxes for rna on a non-emboss block (menu for eg) */ if (type == PROP_BOOLEAN && - ELEM(layout->root->block->dt, UI_EMBOSS_NONE, UI_EMBOSS_PULLDOWN)) { + ELEM(layout->root->block->emboss, UI_EMBOSS_NONE, UI_EMBOSS_PULLDOWN)) { boolarr = MEM_callocN(sizeof(bool) * len, __func__); RNA_property_boolean_get_array(ptr, prop, boolarr); } @@ -2330,7 +2330,7 @@ void uiItemFullR(uiLayout *layout, /* Mark non-embossed textfields inside a listbox. */ if (but && (block->flag & UI_BLOCK_LIST_ITEM) && (but->type == UI_BTYPE_TEXT) && - (but->dt & UI_EMBOSS_NONE)) { + (but->emboss & UI_EMBOSS_NONE)) { UI_but_flag_enable(but, UI_BUT_LIST_ITEM); } @@ -3828,7 +3828,7 @@ static void ui_litem_layout_radial(uiLayout *litem) bitem->but->rect.xmax += 1.5f * UI_UNIT_X; /* enable drawing as pie item if supported by widget */ if (ui_item_is_radial_drawable(bitem)) { - bitem->but->dt = UI_EMBOSS_RADIAL; + bitem->but->emboss = UI_EMBOSS_RADIAL; bitem->but->drawflag |= UI_BUT_ICON_LEFT; } } @@ -5036,7 +5036,7 @@ float uiLayoutGetUnitsY(uiLayout *layout) int uiLayoutGetEmboss(uiLayout *layout) { if (layout->emboss == UI_EMBOSS_UNDEFINED) { - return layout->root->block->dt; + return layout->root->block->emboss; } return layout->emboss; } @@ -5411,7 +5411,7 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but) } if (layout->emboss != UI_EMBOSS_UNDEFINED) { - but->dt = layout->emboss; + but->emboss = layout->emboss; } } diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 4634f4b95c9..10b219202e5 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -90,7 +90,7 @@ bool ui_but_is_interactive(const uiBut *but, const bool labeledit) if (but->flag & UI_SCROLLED) { return false; } - if ((but->type == UI_BTYPE_TEXT) && (but->dt == UI_EMBOSS_NONE) && !labeledit) { + if ((but->type == UI_BTYPE_TEXT) && (but->emboss == UI_EMBOSS_NONE) && !labeledit) { return false; } if ((but->type == UI_BTYPE_LISTROW) && labeledit) { diff --git a/source/blender/editors/interface/interface_region_hud.c b/source/blender/editors/interface/interface_region_hud.c index 1f8af7b9e6e..1773a7b3057 100644 --- a/source/blender/editors/interface/interface_region_hud.c +++ b/source/blender/editors/interface/interface_region_hud.c @@ -367,7 +367,7 @@ void ED_area_type_hud_ensure(bContext *C, ScrArea *area) ED_area_update_region_sizes(wm, win, area); } - ED_region_floating_initialize(region); + ED_region_floating_init(region); ED_region_tag_redraw(region); /* Reset zoom level (not well supported). */ diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 2ad7e517c60..13c85952f52 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -759,7 +759,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, ui_popup_block_scrolltest(block); /* adds subwindow */ - ED_region_floating_initialize(region); + ED_region_floating_init(region); /* get winmat now that we actually have the subwindow */ wmGetProjectionMatrix(block->winmat, ®ion->winrct); diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index a9e87f4cc07..80155e3e871 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -819,7 +819,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but } /* adds subwindow */ - ED_region_floating_initialize(region); + ED_region_floating_init(region); /* notify change and redraw */ ED_region_tag_redraw(region); diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 46814e11b9e..41b41cb3d75 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -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, true, &expr_result)) { + else if (BPY_execute_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, true, &expr_result)) { + else if (BPY_execute_string_as_string(C, expr_imports, expr, __func__, &expr_result)) { if (STREQ(expr_result, ".")) { MEM_freeN(expr_result); expr_result = NULL; @@ -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, true, &expr_result)) { + else if (BPY_execute_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) { @@ -659,7 +659,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* pass */ } else if (BPY_execute_string_as_string_and_size( - C, expr_imports, expr, true, &expr_result, &expr_result_len)) { + 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, true, &expr_result)) { + else if (BPY_execute_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) { if (expr_result != 0) { { uiTooltipField *field = text_field_add(data, @@ -1386,7 +1386,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, } /* adds subwindow */ - ED_region_floating_initialize(region); + ED_region_floating_init(region); /* notify change and redraw */ ED_region_tag_redraw(region); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index d3487b635ce..fcafa88a806 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -7348,6 +7348,9 @@ void uiTemplateCacheFile(uiLayout *layout, uiItemR(row, &fileptr, "scale", 0, IFACE_("Manual Scale"), ICON_NONE); } + uiItemR(layout, &fileptr, "velocity_name", 0, NULL, ICON_NONE); + uiItemR(layout, &fileptr, "velocity_unit", 0, NULL, ICON_NONE); + /* TODO: unused for now, so no need to expose. */ #if 0 row = uiLayoutRow(layout, false); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 2bc2ed5f5e8..a339006bd08 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1356,7 +1356,7 @@ static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect) static int ui_but_draw_menu_icon(const uiBut *but) { - return (but->flag & UI_BUT_ICON_SUBMENU) && (but->dt == UI_EMBOSS_PULLDOWN); + return (but->flag & UI_BUT_ICON_SUBMENU) && (but->emboss == UI_EMBOSS_PULLDOWN); } /* icons have been standardized... and this call draws in untransformed coordinates */ @@ -1417,7 +1417,7 @@ static void widget_draw_icon( but->str && but->str[0] == '\0') { xs = rect->xmin + 2.0f * ofs; } - else if (but->dt == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) { + else if (but->emboss == UI_EMBOSS_NONE || but->type == UI_BTYPE_LABEL) { xs = rect->xmin + 2.0f * ofs; } else { @@ -2375,7 +2375,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, /* pass (even if its a menu toolbar) */ } else if (ui_block_is_pie_menu(but->block)) { - if (but->dt == UI_EMBOSS_RADIAL) { + if (but->emboss == UI_EMBOSS_RADIAL) { rect->xmin += 0.3f * U.widget_unit; } } @@ -4502,7 +4502,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu uiWidgetType *wt = NULL; /* handle menus separately */ - if (but->dt == UI_EMBOSS_PULLDOWN) { + if (but->emboss == UI_EMBOSS_PULLDOWN) { switch (but->type) { case UI_BTYPE_LABEL: widget_draw_text_icon(&style->widgetlabel, &tui->wcol_menu_back, but, rect); @@ -4515,7 +4515,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu break; } } - else if (but->dt == UI_EMBOSS_NONE) { + else if (but->emboss == UI_EMBOSS_NONE) { /* "nothing" */ switch (but->type) { case UI_BTYPE_LABEL: @@ -4526,11 +4526,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu break; } } - else if (but->dt == UI_EMBOSS_RADIAL) { + else if (but->emboss == UI_EMBOSS_RADIAL) { wt = widget_type(UI_WTYPE_MENU_ITEM_RADIAL); } else { - BLI_assert(but->dt == UI_EMBOSS); + BLI_assert(but->emboss == UI_EMBOSS); switch (but->type) { case UI_BTYPE_LABEL: @@ -4779,7 +4779,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu } if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { - if (but->dt != UI_EMBOSS_PULLDOWN) { + if (but->emboss != UI_EMBOSS_PULLDOWN) { disabled = true; } } diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 0cbf73280a3..3efed43e08c 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -242,7 +242,7 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) bool tot_changed = false, do_init; const uiStyle *style = UI_style_get(); - do_init = (v2d->flag & V2D_IS_INITIALISED) == 0; + do_init = (v2d->flag & V2D_IS_INIT) == 0; /* see eView2D_CommonViewTypes in UI_view2d.h for available view presets */ switch (type) { @@ -374,8 +374,8 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) break; } - /* set initialized flag so that View2D doesn't get reinitialised next time again */ - v2d->flag |= V2D_IS_INITIALISED; + /* set initialized flag so that View2D doesn't get reinitialized next time again */ + v2d->flag |= V2D_IS_INIT; /* store view size */ v2d->winx = winx; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 64cacd44e3d..d62058699d9 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -56,7 +56,7 @@ static bool view2d_poll(bContext *C) { ARegion *region = CTX_wm_region(C); - return (region != NULL) && (region->v2d.flag & V2D_IS_INITIALISED); + return (region != NULL) && (region->v2d.flag & V2D_IS_INIT); } /** \} */ diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index 3786ed2789c..12ce358a501 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -753,8 +753,7 @@ 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_RED, GL_FLOAT, GL_NEAREST, buffer, 1.0f, 1.0f, NULL); + immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GL_R16F, false, buffer, 1.0f, 1.0f, NULL); GPU_matrix_pop(); diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 61b40dd3e60..dd4b8146154 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -1158,12 +1158,12 @@ void MESH_OT_bevel(wmOperatorType *ot) PROFILE_HARD_MIN, 1.0f); - prop = RNA_def_enum(ot->srna, - "affect", - prop_affect_items, - BEVEL_AFFECT_EDGES, - "Affect", - "Affect Edges or Vertices"); + RNA_def_enum(ot->srna, + "affect", + prop_affect_items, + BEVEL_AFFECT_EDGES, + "Affect", + "Affect Edges or Vertices"); RNA_def_boolean(ot->srna, "clamp_overlap", diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 347f6806d13..1f411617c68 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1628,9 +1628,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) plane_from_point_normal_v3(plane, v1, plane); } - /* First use bvh tree to find faces, knife edges, and knife verts that might + /* First use BVH tree to find faces, knife edges, and knife verts that might * intersect the cut plane with rays v1-v3 and v2-v4. - * This deduplicates the candidates before doing more expensive intersection tests. */ + * This de-duplicates the candidates before doing more expensive intersection tests. */ tree = BKE_bmbvh_tree_get(kcd->bmbvh); results = BLI_bvhtree_intersect_plane(tree, plane, &tot); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 1ea25353598..d2e9b57e950 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -205,7 +205,7 @@ static BMElem *edbm_select_id_bm_elem_get(Base **bases, const uint sel_id, uint /* -------------------------------------------------------------------- */ /** \name Find Nearest Vert/Edge/Face * - * \note Screen-space manhatten distances are used here, + * \note Screen-space manhattan distances are used here, * since its faster and good enough for the purpose of selection. * * \note \a dist_bias is used so we can bias against selected items. @@ -415,7 +415,7 @@ struct NearestEdgeUserData_Hit { int index; BMEdge *edge; - /* edges only, un-biased manhatten distance to which ever edge we pick + /* edges only, un-biased manhattan distance to which ever edge we pick * (not used for choosing) */ float dist_center; }; diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 9c364d41e24..4270f1cbc72 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1237,7 +1237,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) break; } - /* if this is a new object, initialise default stuff (colors, etc.) */ + /* If this is a new object, initialize default stuff (colors, etc.) */ if (newob) { /* set default viewport color to black */ copy_v3_fl(ob->color, 0.0f); diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index baa24ab2f4e..ae1aae27b7f 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -60,8 +60,6 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" -#include "GPU_draw.h" /* GPU_free_image */ - #include "WM_api.h" #include "WM_types.h" @@ -530,7 +528,7 @@ static void multiresbake_freejob(void *bkv) /* delete here, since this delete will be called from main thread */ for (link = data->images.first; link; link = link->next) { Image *ima = (Image *)link->data; - GPU_free_image(ima); + BKE_image_free_gputextures(ima); } MEM_freeN(data->ob_image.array); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index c4cb21a67f3..cb92fab3cb0 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -68,8 +68,6 @@ #include "ED_screen.h" #include "ED_uvedit.h" -#include "GPU_draw.h" - #include "object_intern.h" /* prototypes */ @@ -308,7 +306,7 @@ static void refresh_images(BakeImages *bake_images) Image *ima = bake_images->data[i].image; LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { if (tile->ok == IMA_OK_LOADED) { - GPU_free_image(ima); + BKE_image_free_gputextures(ima); DEG_id_tag_update(&ima->id, 0); break; } @@ -675,7 +673,7 @@ static void build_image_lookup(Main *bmain, Object *ob, BakeImages *bake_images) /* * returns the total number of pixels */ -static size_t initialize_internal_images(BakeImages *bake_images, ReportList *reports) +static size_t init_internal_images(BakeImages *bake_images, ReportList *reports) { int i; size_t tot_size = 0; @@ -830,7 +828,7 @@ static int bake(Render *re, build_image_lookup(bmain, ob_low, &bake_images); if (is_save_internal) { - num_pixels = initialize_internal_images(&bake_images, reports); + num_pixels = init_internal_images(&bake_images, reports); if (num_pixels == 0) { goto cleanup; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index e93bd8bd94e..517e791e8fa 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -2813,7 +2813,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) /* make a copy of ocean to use for baking - threadsafety */ ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(ocean, omd); + BKE_ocean_init_from_modifier(ocean, omd, omd->resolution); #if 0 BKE_ocean_bake(ocean, och); diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 8981851394d..28f58a34814 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -72,7 +72,6 @@ #include "RNA_define.h" #include "RNA_enum_types.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 749c6cd640e..82a1139c860 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -4346,7 +4346,7 @@ static int brush_add(const bContext *C, PEData *data, short number) } pa->size = 1.0f; - initialize_particle(&sim, pa); + init_particle(&sim, pa); reset_particle(&sim, pa, 0.0, 1.0); point->flag |= PEP_EDIT_RECALC; if (pe_x_mirror(ob)) { diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 673df69bf9b..d279958df8a 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -79,7 +79,6 @@ #include "RNA_define.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "render_intern.h" @@ -351,7 +350,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R G.f &= ~G_FLAG_RENDER_VIEWPORT; gp_rect = MEM_mallocN(sizex * sizey * sizeof(uchar) * 4, "offscreen rect"); - GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect); + GPU_offscreen_read_pixels(oglrender->ofs, GPU_DATA_UNSIGNED_BYTE, gp_rect); for (i = 0; i < sizex * sizey * 4; i += 4) { blend_color_mix_byte(&render_rect[i], &render_rect[i], &gp_rect[i]); @@ -963,7 +962,7 @@ static void screen_opengl_render_cancel(bContext *C, wmOperator *op) } /* share between invoke and exec */ -static bool screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) +static bool screen_opengl_render_anim_init(bContext *C, wmOperator *op) { /* initialize animation */ OGLRender *oglrender; @@ -1257,7 +1256,7 @@ static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEven } if (anim) { - if (!screen_opengl_render_anim_initialize(C, op)) { + if (!screen_opengl_render_anim_init(C, op)) { return OPERATOR_CANCELLED; } } @@ -1293,7 +1292,7 @@ static int screen_opengl_render_exec(bContext *C, wmOperator *op) bool ret = true; - if (!screen_opengl_render_anim_initialize(C, op)) { + if (!screen_opengl_render_anim_init(C, op)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 19e4f652963..85fc6927063 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -82,7 +82,6 @@ #include "BIF_glutil.h" -#include "GPU_glew.h" #include "GPU_shader.h" #include "RE_engine.h" @@ -632,18 +631,8 @@ static bool ed_preview_draw_rect(ScrArea *area, int split, int first, rcti *rect } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTex(&state, - fx, - fy, - rres.rectx, - rres.recty, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - rect_byte, - 1.0f, - 1.0f, - NULL); + immDrawPixelsTex( + &state, fx, fy, rres.rectx, rres.recty, GPU_RGBA8, false, rect_byte, 1.0f, 1.0f, NULL); MEM_freeN(rect_byte); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index f87f631c643..9616b8114ba 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -813,7 +813,7 @@ void ED_workspace_status_text(bContext *C, const char *str) /* ************************************************************ */ -static void area_azone_initialize(wmWindow *win, const bScreen *screen, ScrArea *area) +static void area_azone_init(wmWindow *win, const bScreen *screen, ScrArea *area) { AZone *az; @@ -883,7 +883,7 @@ static void area_azone_initialize(wmWindow *win, const bScreen *screen, ScrArea } } -static void fullscreen_azone_initialize(ScrArea *area, ARegion *region) +static void fullscreen_azone_init(ScrArea *area, ARegion *region) { AZone *az; @@ -1007,10 +1007,10 @@ static bool region_azone_edge_poll(const ARegion *region, const bool is_fullscre return true; } -static void region_azone_edge_initialize(ScrArea *area, - ARegion *region, - AZEdge edge, - const bool is_fullscreen) +static void region_azone_edge_init(ScrArea *area, + ARegion *region, + AZEdge edge, + const bool is_fullscreen) { AZone *az = NULL; const bool is_hidden = (region->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)); @@ -1033,9 +1033,9 @@ static void region_azone_edge_initialize(ScrArea *area, } } -static void region_azone_scrollbar_initialize(ScrArea *area, - ARegion *region, - AZScrollDirection direction) +static void region_azone_scrollbar_init(ScrArea *area, + ARegion *region, + AZScrollDirection direction) { rcti scroller_vert = (direction == AZ_SCROLL_VERT) ? region->v2d.vert : region->v2d.hor; AZone *az = MEM_callocN(sizeof(*az), __func__); @@ -1061,16 +1061,16 @@ static void region_azone_scrollbar_initialize(ScrArea *area, BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } -static void region_azones_scrollbars_initialize(ScrArea *area, ARegion *region) +static void region_azones_scrollbars_init(ScrArea *area, ARegion *region) { const View2D *v2d = ®ion->v2d; if ((v2d->scroll & V2D_SCROLL_VERTICAL) && ((v2d->scroll & V2D_SCROLL_VERTICAL_HANDLES) == 0)) { - region_azone_scrollbar_initialize(area, region, AZ_SCROLL_VERT); + region_azone_scrollbar_init(area, region, AZ_SCROLL_VERT); } if ((v2d->scroll & V2D_SCROLL_HORIZONTAL) && ((v2d->scroll & V2D_SCROLL_HORIZONTAL_HANDLES) == 0)) { - region_azone_scrollbar_initialize(area, region, AZ_SCROLL_HOR); + region_azone_scrollbar_init(area, region, AZ_SCROLL_HOR); } } @@ -1083,16 +1083,16 @@ static void region_azones_add_edge(ScrArea *area, /* edge code (t b l r) is along which area edge azone will be drawn */ if (alignment == RGN_ALIGN_TOP) { - region_azone_edge_initialize(area, region, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); + region_azone_edge_init(area, region, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); } else if (alignment == RGN_ALIGN_BOTTOM) { - region_azone_edge_initialize(area, region, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); + region_azone_edge_init(area, region, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); } else if (alignment == RGN_ALIGN_RIGHT) { - region_azone_edge_initialize(area, region, AE_LEFT_TO_TOPRIGHT, is_fullscreen); + region_azone_edge_init(area, region, AE_LEFT_TO_TOPRIGHT, is_fullscreen); } else if (alignment == RGN_ALIGN_LEFT) { - region_azone_edge_initialize(area, region, AE_RIGHT_TO_TOPLEFT, is_fullscreen); + region_azone_edge_init(area, region, AE_RIGHT_TO_TOPLEFT, is_fullscreen); } } @@ -1116,10 +1116,10 @@ static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *reg } if (is_fullscreen) { - fullscreen_azone_initialize(area, region); + fullscreen_azone_init(area, region); } - region_azones_scrollbars_initialize(area, region); + region_azones_scrollbars_init(area, region); } /* dir is direction to check, not the splitting edge direction! */ @@ -1828,7 +1828,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar region_rect_recursive(area, area->regionbase.first, &rect, &overlap_rect, 0); /* Dynamically sized regions may have changed region sizes, so we have to force azone update. */ - area_azone_initialize(win, screen, area); + area_azone_init(win, screen, area); LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { region_subwindow(region); @@ -1847,7 +1847,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar } /* called in screen_refresh, or screens_init, also area size changes */ -void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *area) +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); @@ -1890,7 +1890,7 @@ void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *area) } /* clear all azones, add the area triangle widgets */ - area_azone_initialize(win, screen, area); + area_azone_init(win, screen, area); /* region windows, default and own handlers */ for (region = area->regionbase.first; region; region = region->next) { @@ -1944,7 +1944,7 @@ void ED_region_update_rect(ARegion *region) } /* externally called for floating regions like menus */ -void ED_region_floating_initialize(ARegion *region) +void ED_region_floating_init(ARegion *region) { BLI_assert(region->alignment == RGN_ALIGN_FLOAT); @@ -1980,7 +1980,7 @@ void ED_region_visibility_change_update(bContext *C, ScrArea *area, ARegion *reg WM_event_remove_handlers(C, ®ion->handlers); } - ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), area); + ED_area_init(CTX_wm_manager(C), CTX_wm_window(C), area); ED_area_tag_redraw(area); } @@ -2066,8 +2066,8 @@ void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2) ED_area_data_copy(tmp, sa1, false); ED_area_data_copy(sa1, sa2, true); ED_area_data_copy(sa2, tmp, true); - ED_area_initialize(CTX_wm_manager(C), win, sa1); - ED_area_initialize(CTX_wm_manager(C), win, sa2); + ED_area_init(CTX_wm_manager(C), win, sa1); + ED_area_init(CTX_wm_manager(C), win, sa2); BKE_screen_area_free(tmp); MEM_freeN(tmp); @@ -2126,7 +2126,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi area->spacetype = type; area->type = st; - /* If st->new may be called, don't use context until then. The + /* If st->create may be called, don't use context until then. The * area->type->context() callback has changed but data may be invalid * (e.g. with properties editor) until space-data is properly created */ @@ -2166,7 +2166,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi if (st) { /* Don't get scene from context here which may depend on space-data. */ Scene *scene = WM_window_get_active_scene(win); - sl = st->new (area, scene); + sl = st->create(area, scene); BLI_addhead(&area->spacedata, sl); /* swap regions */ @@ -2204,7 +2204,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi } } - ED_area_initialize(CTX_wm_manager(C), win, area); + ED_area_init(CTX_wm_manager(C), win, area); /* tell WM to refresh, cursor types etc */ WM_event_add_mousemove(win); diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 5c3b1944164..07a122c7094 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -95,9 +95,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -115,21 +114,30 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y)); float white[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - if (type != GL_FLOAT) { - BLI_assert(type == GL_UNSIGNED_BYTE); - type = GL_UNSIGNED_BYTE; + if (ELEM(gpu_format, GPU_RGBA8, GPU_RGBA16F)) { + components = 4; + } + else if (ELEM(gpu_format, GPU_RGB16F)) { + components = 3; + } + else if (ELEM(gpu_format, GPU_R8, GPU_R16F)) { + components = 1; + } + else { + BLI_assert(!"Incompatible format passed to immDrawPixels"); + return; } - eGPUTextureFormat gpu_format = (type == GL_FLOAT) ? GPU_RGBA16F : GPU_RGBA8; - eGPUDataFormat gpu_data = (type == GL_FLOAT) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; - GPUTexture *texture = GPU_texture_create_nD( - tex_w, tex_h, 0, 2, NULL, gpu_format, gpu_data, 0, false, NULL); + const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F); + eGPUDataFormat gpu_data = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + size_t stride = components * ((use_float_data) ? sizeof(float) : sizeof(uchar)); - /* TODO replace GL_NEAREST/LINEAR in callers. */ - GPU_texture_filter_mode(texture, (zoomfilter == GL_LINEAR)); - GPU_texture_wrap_mode(texture, false, true); + GPUTexture *tex = GPU_texture_create_2d(tex_w, tex_h, gpu_format, NULL, NULL); - GPU_texture_bind(texture, 0); + GPU_texture_filter_mode(tex, use_filter); + GPU_texture_wrap_mode(tex, false, true); + + GPU_texture_bind(tex, 0); /* setup seamless 2=on, 0=off */ seamless = ((tex_w < img_w || tex_h < img_h) && tex_w > 2 && tex_h > 2) ? 2 : 0; @@ -140,20 +148,6 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, nsubparts_x = (img_w + (offset_x - 1)) / (offset_x); nsubparts_y = (img_h + (offset_y - 1)) / (offset_y); - if (format == GL_RGBA) { - components = 4; - } - else if (format == GL_RGB) { - components = 3; - } - else if (format == GL_RED) { - components = 1; - } - else { - BLI_assert(!"Incompatible format passed to glaDrawPixelsTexScaled"); - return; - } - /* optional */ /* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since * it does not need color. @@ -199,26 +193,32 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, { int src_y = subpart_y * offset_y; int src_x = subpart_x * offset_x; - size_t stride = components * ((type == GL_FLOAT) ? sizeof(float) : sizeof(uchar)); #define DATA(_y, _x) ((char *)rect + stride * ((size_t)(_y)*img_w + (_x))) { void *data = DATA(src_y, src_x); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, type, data); + GPU_texture_update_sub(tex, gpu_data, data, 0, 0, 0, subpart_w, subpart_h, 0); } /* Add an extra border of pixels so linear interpolation looks ok * at edges of full image. */ if (subpart_w < tex_w) { void *data = DATA(src_y, src_x + subpart_w - 1); - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, type, data); + int offset[2] = {subpart_w, 0}; + int extent[2] = {1, subpart_h}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } if (subpart_h < tex_h) { void *data = DATA(src_y + subpart_h - 1, src_x); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, type, data); + int offset[2] = {0, subpart_h}; + int extent[2] = {subpart_w, 1}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } + if (subpart_w < tex_w && subpart_h < tex_h) { void *data = DATA(src_y + subpart_h - 1, src_x + subpart_w - 1); - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, type, data); + int offset[2] = {subpart_w, subpart_h}; + int extent[2] = {1, 1}; + GPU_texture_update_sub(tex, gpu_data, data, UNPACK2(offset), 0, UNPACK2(extent), 0); } #undef DATA } @@ -253,8 +253,8 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, immUnbindProgram(); } - GPU_texture_unbind(texture); - GPU_texture_free(texture); + GPU_texture_unbind(tex); + GPU_texture_free(tex); /* Restore default. */ GPU_unpack_row_length_set(0); @@ -265,9 +265,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float scaleX, float scaleY, @@ -280,9 +279,8 @@ void immDrawPixelsTexScaled(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, scaleX, scaleY, @@ -300,9 +298,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float xzoom, float yzoom, @@ -313,9 +310,8 @@ void immDrawPixelsTex(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, 1.0f, 1.0f, @@ -333,9 +329,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, float y, int img_w, int img_h, - int format, - int type, - int zoomfilter, + eGPUTextureFormat gpu_format, + bool use_filter, void *rect, float clip_min_x, float clip_min_y, @@ -350,9 +345,8 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, y, img_w, img_h, - format, - type, - zoomfilter, + gpu_format, + use_filter, rect, 1.0f, 1.0f, @@ -371,7 +365,7 @@ void immDrawPixelsTex_clipping(IMMDrawPixelsTexState *state, void ED_draw_imbuf_clipping(ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, ColorManagedViewSettings *view_settings, ColorManagedDisplaySettings *display_settings, float clip_min_x, @@ -421,13 +415,13 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, if (ok) { if (ibuf->rect_float) { - int format = 0; + eGPUTextureFormat format = 0; if (ibuf->channels == 3) { - format = GL_RGB; + format = GPU_RGB16F; } else if (ibuf->channels == 4) { - format = GL_RGBA; + format = GPU_RGBA16F; } else { BLI_assert(!"Incompatible number of channels for GLSL display"); @@ -440,8 +434,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, ibuf->x, ibuf->y, format, - GL_FLOAT, - zoomfilter, + use_filter, ibuf->rect_float, clip_min_x, clip_min_y, @@ -459,9 +452,8 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - zoomfilter, + GPU_RGBA8, + use_filter, ibuf->rect, clip_min_x, clip_min_y, @@ -493,9 +485,8 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - zoomfilter, + GPU_RGBA8, + use_filter, display_buffer, clip_min_x, clip_min_y, @@ -513,7 +504,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, void ED_draw_imbuf(ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, ColorManagedViewSettings *view_settings, ColorManagedDisplaySettings *display_settings, float zoom_x, @@ -522,7 +513,7 @@ void ED_draw_imbuf(ImBuf *ibuf, ED_draw_imbuf_clipping(ibuf, x, y, - zoomfilter, + use_filter, view_settings, display_settings, 0.0f, @@ -537,7 +528,7 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, ImBuf *ibuf, float x, float y, - int zoomfilter, + bool use_filter, float clip_min_x, float clip_min_y, float clip_max_x, @@ -553,7 +544,7 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, ED_draw_imbuf_clipping(ibuf, x, y, - zoomfilter, + use_filter, view_settings, display_settings, clip_min_x, @@ -565,9 +556,9 @@ void ED_draw_imbuf_ctx_clipping(const bContext *C, } void ED_draw_imbuf_ctx( - const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter, float zoom_x, float zoom_y) + const bContext *C, ImBuf *ibuf, float x, float y, bool use_filter, float zoom_x, float zoom_y) { - ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y); + ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, use_filter, 0.0f, 0.0f, 0.0f, 0.0f, zoom_x, zoom_y); } int ED_draw_imbuf_method(ImBuf *ibuf) diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 7983ac889ef..c17a34f97b9 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -475,7 +475,6 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult CTX_data_id_pointer_set(result, &obpose->id); } return 1; - return -1; /* found but not available */ } if (CTX_data_equals(member, "sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 1608e842376..40a452a5363 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -619,7 +619,7 @@ void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uin screen_preview_draw(screen, size_x, size_y); - GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, r_rect); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_UNSIGNED_BYTE, r_rect); GPU_offscreen_unbind(offscreen, true); GPU_offscreen_free(offscreen); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index b6f210d7f13..62720d8ca37 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -516,7 +516,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) ED_screen_areas_iter (win, screen, area) { /* set spacetype and region callbacks, calls init() */ /* sets subwindows for regions, adds handlers */ - ED_area_initialize(wm, win, area); + ED_area_init(wm, win, area); } /* wake up animtimer */ @@ -536,7 +536,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) } /* file read, set all screens, ... */ -void ED_screens_initialize(Main *bmain, wmWindowManager *wm) +void ED_screens_init(Main *bmain, wmWindowManager *wm) { wmWindow *win; @@ -897,7 +897,7 @@ static void screen_global_area_refresh(wmWindow *win, else { area = screen_area_create_with_geometry(&win->global_areas, rect, space_type); SpaceType *stype = BKE_spacetype_from_id(space_type); - SpaceLink *slink = stype->new (area, WM_window_get_active_scene(win)); + SpaceLink *slink = stype->create(area, WM_window_get_active_scene(win)); area->regionbase = slink->regionbase; @@ -985,39 +985,14 @@ void ED_screen_global_areas_refresh(wmWindow *win) /* -------------------------------------------------------------------- */ /* Screen changing */ -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) { - if ((screen_iter != screen) && ELEM(screen_iter->state, SCREENMAXIMIZED, SCREENFULL)) { - ScrArea *area = screen_iter->areabase.first; - if (area && area->full == screen) { - return screen_iter; - } - } - } - - return screen; -} - /** * \return the screen to activate. * \warning The returned screen may not always equal \a screen_new! */ -bScreen *screen_change_prepare( +void screen_change_prepare( bScreen *screen_old, bScreen *screen_new, Main *bmain, bContext *C, wmWindow *win) { - /* validate screen, it's called with notifier reference */ - if (BLI_findindex(&bmain->screens, screen_new) == -1) { - return NULL; - } - - screen_new = screen_fullscreen_find_associated_normal_screen(bmain, screen_new); - - /* check for valid winid */ - if (!(screen_new->winid == 0 || screen_new->winid == win->winid)) { - return NULL; - } + BLI_assert(BLI_findindex(&bmain->screens, screen_new) != -1); if (screen_old != screen_new) { wmTimer *wt = screen_old->animtimer; @@ -1038,11 +1013,7 @@ bScreen *screen_change_prepare( if (wt) { screen_new->animtimer = wt; } - - return screen_new; } - - return NULL; } void screen_change_update(bContext *C, wmWindow *win, bScreen *screen) @@ -1075,12 +1046,20 @@ bool ED_screen_change(bContext *C, bScreen *screen) { Main *bmain = CTX_data_main(C); wmWindow *win = CTX_wm_window(C); + WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); + WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, screen); bScreen *screen_old = CTX_wm_screen(C); - bScreen *screen_new = screen_change_prepare(screen_old, screen, bmain, C, win); - if (screen_new) { - WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook); - WM_window_set_active_screen(win, workspace, screen); + /* Get the actual layout/screen to be activated (guaranteed to be unused, even if that means + * having to duplicate an existing one). */ + WorkSpaceLayout *layout_new = ED_workspace_screen_change_ensure_unused_layout( + bmain, workspace, layout, layout, win); + bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new); + + screen_change_prepare(screen_old, screen_new, bmain, C, win); + + if (screen_old != screen_new) { + WM_window_set_active_screen(win, workspace, screen_new); screen_change_update(C, win, screen_new); return true; diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 2d42313d528..b96890c7db3 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -50,11 +50,11 @@ bScreen *screen_add(struct Main *bmain, const char *name, const rcti *rect); void screen_data_copy(bScreen *to, bScreen *from); void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new); void screen_change_update(struct bContext *C, wmWindow *win, bScreen *screen); -bScreen *screen_change_prepare(bScreen *screen_old, - bScreen *screen_new, - struct Main *bmain, - struct bContext *C, - wmWindow *win); +void screen_change_prepare(bScreen *screen_old, + bScreen *screen_new, + struct Main *bmain, + struct bContext *C, + wmWindow *win); ScrArea *area_split( const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge); int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index fde1498bc5e..f4d36a15d30 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -5184,7 +5184,7 @@ static void region_blend_end(bContext *C, ARegion *region, const bool is_running else { if (rgi->hidden) { rgi->region->flag |= rgi->hidden; - ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), rgi->area); + ED_area_init(CTX_wm_manager(C), CTX_wm_window(C), rgi->area); } /* area decoration needs redraw in end */ ED_area_tag_redraw(rgi->area); @@ -5215,7 +5215,7 @@ void ED_region_visibility_change_update_animated(bContext *C, ScrArea *area, ARe /* blend in, reinitialize regions because it got unhidden */ if (rgi->hidden == 0) { - ED_area_initialize(wm, win, area); + ED_area_init(wm, win, area); } else { WM_event_remove_handlers(C, ®ion->handlers); diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index 478a0adfd9a..b20dc80d158 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -88,22 +88,15 @@ static void workspace_change_update(WorkSpace *workspace_new, #endif } -static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) -{ - /* return false to stop the iterator if we've found a layout that can be activated */ - return workspace_layout_set_poll(layout) ? false : true; -} - static WorkSpaceLayout *workspace_change_get_new_layout(Main *bmain, WorkSpace *workspace_new, wmWindow *win) { - /* ED_workspace_duplicate may have stored a layout to activate - * once the workspace gets activated. */ WorkSpaceLayout *layout_old = WM_window_get_active_layout(win); WorkSpaceLayout *layout_new; - bScreen *screen_new; + /* ED_workspace_duplicate may have stored a layout to activate + * once the workspace gets activated. */ if (win->workspace_hook->temp_workspace_store) { layout_new = win->workspace_hook->temp_layout_store; } @@ -113,20 +106,9 @@ static WorkSpaceLayout *workspace_change_get_new_layout(Main *bmain, layout_new = workspace_new->layouts.first; } } - screen_new = BKE_workspace_layout_screen_get(layout_new); - - if (screen_new->winid) { - /* screen is already used, try to find a free one */ - WorkSpaceLayout *layout_temp = BKE_workspace_layout_iter_circular( - workspace_new, layout_new, workspace_change_find_new_layout_cb, NULL, false); - if (!layout_temp) { - /* fallback solution: duplicate layout from old workspace */ - layout_temp = ED_workspace_layout_duplicate(bmain, workspace_new, layout_old, win); - } - layout_new = layout_temp; - } - return layout_new; + return ED_workspace_screen_change_ensure_unused_layout( + bmain, workspace_new, layout_new, layout_old, win); } /** @@ -153,10 +135,7 @@ bool ED_workspace_change(WorkSpace *workspace_new, bContext *C, wmWindowManager return false; } - screen_new = screen_change_prepare(screen_old, screen_new, bmain, C, win); - if (BKE_workspace_layout_screen_get(layout_new) != screen_new) { - layout_new = BKE_workspace_layout_find(workspace_new, screen_new); - } + screen_change_prepare(screen_old, screen_new, bmain, C, win); if (screen_new == NULL) { return false; diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c index 0af81e0db21..8a36cffa1f1 100644 --- a/source/blender/editors/screen/workspace_layout_edit.c +++ b/source/blender/editors/screen/workspace_layout_edit.c @@ -160,6 +160,66 @@ bool ED_workspace_layout_delete(WorkSpace *workspace, WorkSpaceLayout *layout_ol return false; } +static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) +{ + /* return false to stop the iterator if we've found a layout that can be activated */ + return workspace_layout_set_poll(layout) ? false : true; +} + +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) { + if ((screen_iter != screen) && ELEM(screen_iter->state, SCREENMAXIMIZED, SCREENFULL)) { + ScrArea *area = screen_iter->areabase.first; + if (area && area->full == screen) { + return screen_iter; + } + } + } + + return screen; +} + +static bool screen_is_used_by_other_window(const wmWindow *win, const bScreen *screen) +{ + return BKE_screen_is_used(screen) && (screen->winid != win->winid); +} + +/** + * Make sure there is a non-fullscreen layout to switch to that is not used yet by an other window. + * Needed for workspace or screen switching to ensure valid screens. + * + * \param layout_fallback_base: As last resort, this layout is duplicated and returned. + */ +WorkSpaceLayout *ED_workspace_screen_change_ensure_unused_layout( + Main *bmain, + WorkSpace *workspace, + WorkSpaceLayout *layout_new, + const WorkSpaceLayout *layout_fallback_base, + wmWindow *win) +{ + WorkSpaceLayout *layout_temp = layout_new; + bScreen *screen_temp = BKE_workspace_layout_screen_get(layout_new); + + screen_temp = screen_fullscreen_find_associated_normal_screen(bmain, screen_temp); + layout_temp = BKE_workspace_layout_find(workspace, screen_temp); + + if (screen_is_used_by_other_window(win, screen_temp)) { + /* Screen is already used, try to find a free one. */ + layout_temp = BKE_workspace_layout_iter_circular( + workspace, layout_new, workspace_change_find_new_layout_cb, NULL, false); + screen_temp = layout_temp ? BKE_workspace_layout_screen_get(layout_temp) : NULL; + + if (!layout_temp || screen_is_used_by_other_window(win, screen_temp)) { + /* Fallback solution: duplicate layout. */ + layout_temp = ED_workspace_layout_duplicate(bmain, workspace, layout_fallback_base, win); + } + } + + return layout_temp; +} + static bool workspace_layout_cycle_iter_cb(const WorkSpaceLayout *layout, void *UNUSED(arg)) { /* return false to stop iterator when we have found a layout to activate */ diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index d0082769575..9398e4443f3 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -56,7 +56,6 @@ #include "DEG_depsgraph.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" @@ -456,7 +455,7 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) } buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex"); - BKE_curvemapping_initialize(br->curve); + BKE_curvemapping_init(br->curve); LoadTexData data = { .br = br, diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 7ee3d991eb7..34c9cac67c8 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -74,7 +74,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_state.h" @@ -182,7 +181,7 @@ void imapaint_image_update( int h = imapaintpartial.y2 - imapaintpartial.y1; if (w && h) { /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h); + BKE_image_update_gputexture(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h); } } } @@ -1164,9 +1163,9 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint); if (U.glreslimit != 0) { - GPU_free_images(bmain); + BKE_image_free_all_gputextures(bmain); } - GPU_paint_set_mipmap(bmain, 0); + BKE_image_paint_set_mipmap(bmain, 0); toggle_paint_cursor(scene, true); @@ -1189,9 +1188,9 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob) ob->mode &= ~OB_MODE_TEXTURE_PAINT; if (U.glreslimit != 0) { - GPU_free_images(bmain); + BKE_image_free_all_gputextures(bmain); } - GPU_paint_set_mipmap(bmain, 1); + BKE_image_paint_set_mipmap(bmain, 1); toggle_paint_cursor(scene, false); Mesh *me = BKE_mesh_from_object(ob); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index a7f09390a3d..6d588d1450b 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -57,8 +57,6 @@ #include "UI_view2d.h" -#include "GPU_draw.h" - #include "paint_intern.h" /* Brush Painting for 2D image editor */ @@ -1784,7 +1782,7 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final) if (final) { if (s->image && !(s->sima && s->sima->lock)) { - GPU_free_image(s->image); + BKE_image_free_gputextures(s->image); } /* compositor listener deals with updating */ diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 5af3a3f4241..af2762889e8 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -100,8 +100,6 @@ #include "RNA_define.h" #include "RNA_enum_types.h" -#include "GPU_draw.h" - #include "IMB_colormanagement.h" //#include "bmesh_tools.h" @@ -6134,7 +6132,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) project_image_refresh_tagged(&ps); for (a = 0; a < ps.image_tot; a++) { - GPU_free_image(ps.projImages[a].ima); + BKE_image_free_gputextures(ps.projImages[a].ima); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima); } @@ -6175,7 +6173,7 @@ static bool texture_paint_image_from_view_poll(bContext *C) CTX_wm_operator_poll_msg_set(C, "No 3D viewport found to create image from"); return false; } - if (G.background || !GPU_is_initialized()) { + if (G.background || !GPU_is_init()) { return false; } return true; diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 0d4e957c77b..feadeb28cd5 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -341,6 +341,7 @@ typedef enum { void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot); void PAINT_OT_mask_lasso_gesture(struct wmOperatorType *ot); +void PAINT_OT_mask_box_gesture(struct wmOperatorType *ot); /* paint_curve.c */ void PAINTCURVE_OT_new(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 6e0402fc6e0..c255cbcaaab 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -25,6 +25,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_vec_types.h" #include "BLI_bitmap_draw_2d.h" #include "BLI_lasso_2d.h" @@ -290,27 +291,32 @@ static void mask_box_select_task_cb(void *__restrict userdata, } } -bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *rect, bool select) +static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) { + ViewContext vc; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Sculpt *sd = vc->scene->toolsettings->sculpt; + 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; - PaintMaskFloodMode mode; + 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; - mode = PAINT_MASK_FLOOD_VALUE; - float value = select ? 1.0f : 0.0f; + const PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); + const float value = RNA_float_get(op->ptr, "value"); + + rcti rect; + WM_operator_properties_border_to_rcti(op, &rect); /* Transform the clip planes in object space. */ - ED_view3d_clipping_calc(&bb, clip_planes, vc->region, vc->obact, rect); + ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &rect); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); pbvh = ob->sculpt->pbvh; @@ -589,3 +595,33 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) 0.0f, 1.0f); } + +void PAINT_OT_mask_box_gesture(wmOperatorType *ot) +{ + ot->name = "Mask Box Gesture"; + ot->idname = "PAINT_OT_mask_box_gesture"; + ot->description = "Add mask within the box as you move the brush"; + + ot->invoke = WM_gesture_box_invoke; + ot->modal = WM_gesture_box_modal; + ot->exec = paint_mask_gesture_box_exec; + + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* Properties. */ + WM_operator_properties_border(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); +} diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index d65f158174f..0fe6e259d8d 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -1356,6 +1356,7 @@ void ED_operatortypes_paint(void) /* paint masking */ WM_operatortype_append(PAINT_OT_mask_flood_fill); WM_operatortype_append(PAINT_OT_mask_lasso_gesture); + WM_operatortype_append(PAINT_OT_mask_box_gesture); } void ED_keymap_paint(wmKeyConfig *keyconf) diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index caecc7b708a..b9361726826 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -920,9 +920,9 @@ PaintStroke *paint_stroke_new(bContext *C, ups->average_stroke_counter = 0; /* initialize here to avoid initialization conflict with threaded strokes */ - BKE_curvemapping_initialize(br->curve); + BKE_curvemapping_init(br->curve); if (p->flags & PAINT_USE_CAVITY_MASK) { - BKE_curvemapping_initialize(p->cavity_curve); + BKE_curvemapping_init(p->cavity_curve); } BKE_paint_set_overlay_override(br->overlay_flags); diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index 6965946d2ce..83d76c166ce 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -811,7 +811,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) VPaint *wp = ts->wpaint; struct Brush *brush = BKE_paint_brush(&wp->paint); - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); data.brush = brush; data.weightpaint = BKE_brush_weight_get(scene, brush); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index e838d87aeff..cde4039d20f 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -196,7 +196,7 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) } } -static const float *sculpt_vertex_persistent_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index) { if (ss->persistent_base) { return ss->persistent_base[index].co; @@ -204,7 +204,7 @@ static const float *sculpt_vertex_persistent_co_get(SculptSession *ss, int index return SCULPT_vertex_co_get(ss, index); } -static void sculpt_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]) { if (ss->persistent_base) { copy_v3_v3(no, ss->persistent_base[index].no); @@ -2966,7 +2966,7 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise * initialize before threads so they can do curve mapping. */ - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ SculptThreadedTaskData data = { @@ -3043,7 +3043,7 @@ static void do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise * initialize before threads so they can do curve mapping. */ - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ SculptThreadedTaskData data = { @@ -3273,7 +3273,7 @@ static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t return; } - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); SculptThreadedTaskData data = { .sd = sd, @@ -4341,9 +4341,9 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, float normal[3]; if (use_persistent_base) { - sculpt_vertex_persistent_normal_get(ss, vi, normal); + SCULPT_vertex_persistent_normal_get(ss, vi, normal); mul_v3_fl(normal, brush->height); - madd_v3_v3v3fl(final_co, sculpt_vertex_persistent_co_get(ss, vi), normal, *disp_factor); + madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor); } else { normal_short_to_float_v3(normal, orig_data.no); @@ -6390,7 +6390,7 @@ static void sculpt_update_cache_invariants( brush = br; cache->saved_smooth_size = BKE_brush_size_get(scene, brush); BKE_brush_size_set(scene, brush, size); - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); } } } @@ -6458,7 +6458,7 @@ static void sculpt_update_cache_invariants( #define PIXEL_INPUT_THRESHHOLD 5 if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { - cache->dial = BLI_dial_initialize(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); + cache->dial = BLI_dial_init(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); } #undef PIXEL_INPUT_THRESHHOLD diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 4232be91034..51b2d50e73b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -85,7 +85,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" @@ -101,6 +100,28 @@ #include <stdlib.h> #include <string.h> +static float cloth_brush_simulation_falloff_get(const Brush *brush, + const float radius, + const float location[3], + const float co[3]) +{ + const float distance = len_v3v3(location, co); + const float limit = radius + (radius * brush->cloth_sim_limit); + const float falloff = radius + (radius * brush->cloth_sim_limit * brush->cloth_sim_falloff); + + if (distance > limit) { + /* Outiside the limits. */ + return 0.0f; + } + if (distance < falloff) { + /* Before the falloff area. */ + return 1.0f; + } + /* Do a smoothstep transition inside the falloff area. */ + float p = 1.0f - ((distance - falloff) / (limit - falloff)); + return 3.0f * p * p - 2.0f * p * p * p; +} + #define CLOTH_LENGTH_CONSTRAINTS_BLOCK 100000 #define CLOTH_SIMULATION_ITERATIONS 5 #define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024 @@ -113,19 +134,8 @@ static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_s return BLI_edgeset_haskey(cloth_sim->created_length_constraints, v1, v2); } -static void cloth_brush_add_length_constraint(SculptSession *ss, - SculptClothSimulation *cloth_sim, - const int v1, - const int v2) +static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim) { - cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v1 = v1; - cloth_sim->length_constraints[cloth_sim->tot_length_constraints].v2 = v2; - cloth_sim->length_constraints[cloth_sim->tot_length_constraints].length = len_v3v3( - SCULPT_vertex_co_get(ss, v1), SCULPT_vertex_co_get(ss, v2)); - - cloth_sim->tot_length_constraints++; - - /* Reallocation if the array capacity is exceeded. */ if (cloth_sim->tot_length_constraints >= cloth_sim->capacity_length_constraints) { cloth_sim->capacity_length_constraints += CLOTH_LENGTH_CONSTRAINTS_BLOCK; cloth_sim->length_constraints = MEM_reallocN_id(cloth_sim->length_constraints, @@ -133,23 +143,115 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, sizeof(SculptClothLengthConstraint), "length constraints"); } +} + +static void cloth_brush_add_length_constraint(SculptSession *ss, + SculptClothSimulation *cloth_sim, + const int v1, + const int v2, + const bool use_persistent) +{ + SculptClothLengthConstraint *length_constraint = + &cloth_sim->length_constraints[cloth_sim->tot_length_constraints]; + + length_constraint->elem_index_a = v1; + length_constraint->elem_index_b = v2; + + length_constraint->elem_position_a = cloth_sim->pos[v1]; + length_constraint->elem_position_b = cloth_sim->pos[v2]; + + if (use_persistent) { + length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), + SCULPT_vertex_persistent_co_get(ss, v2)); + } + else { + length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1), + SCULPT_vertex_co_get(ss, v2)); + } + length_constraint->strength = 1.0f; + + cloth_sim->tot_length_constraints++; + + /* Reallocation if the array capacity is exceeded. */ + cloth_brush_reallocate_constraints(cloth_sim); /* Add the constraint to the GSet to avoid creating it again. */ BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2); } +static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim, + const int v, + const float strength) +{ + SculptClothLengthConstraint *length_constraint = + &cloth_sim->length_constraints[cloth_sim->tot_length_constraints]; + + length_constraint->elem_index_a = v; + length_constraint->elem_index_b = v; + + length_constraint->elem_position_a = cloth_sim->pos[v]; + length_constraint->elem_position_b = cloth_sim->init_pos[v]; + + length_constraint->length = 0.0f; + length_constraint->strength = strength; + + cloth_sim->tot_length_constraints++; + + /* Reallocation if the array capacity is exceeded. */ + cloth_brush_reallocate_constraints(cloth_sim); +} + +static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_sim, + const int v, + const float strength) +{ + SculptClothLengthConstraint *length_constraint = + &cloth_sim->length_constraints[cloth_sim->tot_length_constraints]; + + length_constraint->elem_index_a = v; + length_constraint->elem_index_b = v; + + length_constraint->elem_position_a = cloth_sim->pos[v]; + length_constraint->elem_position_b = cloth_sim->deformation_pos[v]; + + length_constraint->length = 0.0f; + length_constraint->strength = strength; + + cloth_sim->tot_length_constraints++; + + /* Reallocation if the array capacity is exceeded. */ + cloth_brush_reallocate_constraints(cloth_sim); +} + static void do_cloth_brush_build_constraints_task_cb_ex( void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; PBVHVertexIter vd; + const bool pin_simulation_boundary = ss->cache != NULL && brush != NULL && + brush->flag2 & BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY; + + const bool use_persistent = brush != NULL && brush->flag & BRUSH_PERSISTENT; + + /* Brush can be NULL in tools that use the solver without relying of constraints with deformation + * positions. */ + const bool cloth_is_deform_brush = ss->cache != NULL && brush != NULL && + SCULPT_is_cloth_deform_brush(brush); + float radius_squared = 0.0f; + if (cloth_is_deform_brush) { + radius_squared = ss->cache->initial_radius * ss->cache->initial_radius; + } + + const float cloth_sim_radius_squared = data->cloth_sim_radius * data->cloth_sim_radius; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (len_squared_v3v3(vd.co, data->cloth_sim_initial_location) < - data->cloth_sim_radius * data->cloth_sim_radius) { + const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location); + if (len_squared < cloth_sim_radius_squared) { SculptVertexNeighborIter ni; int build_indices[CLOTH_MAX_CONSTRAINTS_PER_VERTEX]; @@ -162,6 +264,11 @@ static void do_cloth_brush_build_constraints_task_cb_ex( } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + if (brush->cloth_constraint_softbody_strength > 0.0f) { + cloth_brush_add_softbody_constraint( + data->cloth_sim, vd.index, brush->cloth_constraint_softbody_strength); + } + /* As we don't know the order of the neighbor vertices, we create all possible combinations * between the neighbor and the original vertex as length constraints. */ /* This results on a pattern that contains structural, shear and bending constraints for all @@ -172,35 +279,29 @@ static void do_cloth_brush_build_constraints_task_cb_ex( if (c_i != c_j && !cloth_brush_sim_has_length_constraint( data->cloth_sim, build_indices[c_i], build_indices[c_j])) { cloth_brush_add_length_constraint( - ss, data->cloth_sim, build_indices[c_i], build_indices[c_j]); + ss, data->cloth_sim, build_indices[c_i], build_indices[c_j], use_persistent); } } } } - } - BKE_pbvh_vertex_iter_end; -} -static float cloth_brush_simulation_falloff_get(const Brush *brush, - const float radius, - const float location[3], - const float co[3]) -{ - const float distance = len_v3v3(location, co); - const float limit = radius + (radius * brush->cloth_sim_limit); - const float falloff = radius + (radius * brush->cloth_sim_limit * brush->cloth_sim_falloff); + 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 (distance > limit) { - /* Outiside the limits. */ - return 0.0f; - } - if (distance < falloff) { - /* Before the falloff area. */ - return 1.0f; + 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. */ + if (sim_falloff < 1.0f) { + /* Create constraints with more strength the closer the vertex is to the simulation + * boundary. */ + cloth_brush_add_softbody_constraint(data->cloth_sim, vd.index, 1.0f - sim_falloff); + } + } } - /* Do a smoothstep transition inside the falloff area. */ - float p = 1.0f - ((distance - falloff) / (limit - falloff)); - return 3.0f * p * p - 2.0f * p * p * p; + BKE_pbvh_vertex_iter_end; } static void cloth_brush_apply_force_to_vertex(SculptSession *UNUSED(ss), @@ -323,9 +424,10 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(force, offset, -fade); break; case BRUSH_CLOTH_DEFORM_GRAB: - /* Grab writes the positions in the simulation directly without applying forces. */ - madd_v3_v3v3fl( - cloth_sim->pos[vd.index], orig_data.co, ss->cache->grab_delta_symmetry, fade); + madd_v3_v3v3fl(cloth_sim->deformation_pos[vd.index], + orig_data.co, + ss->cache->grab_delta_symmetry, + fade); zero_v3(force); break; case BRUSH_CLOTH_DEFORM_PINCH_POINT: @@ -367,6 +469,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, } static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, + Brush *brush, const float cloth_mass, const float cloth_damping) { @@ -380,12 +483,20 @@ static SculptClothSimulation *cloth_brush_simulation_create(SculptSession *ss, "cloth length constraints"); cloth_sim->capacity_length_constraints = CLOTH_LENGTH_CONSTRAINTS_BLOCK; - cloth_sim->acceleration = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim acceleration"); - cloth_sim->pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim pos"); - cloth_sim->prev_pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim prev pos"); - cloth_sim->init_pos = MEM_callocN(sizeof(float) * 3 * totverts, "cloth sim init pos"); - cloth_sim->length_constraint_tweak = MEM_callocN(sizeof(float) * totverts, - "cloth sim length tweak"); + cloth_sim->acceleration = MEM_calloc_arrayN( + totverts, 3 * sizeof(float), "cloth sim acceleration"); + cloth_sim->pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim pos"); + cloth_sim->prev_pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "cloth sim prev pos"); + cloth_sim->init_pos = MEM_calloc_arrayN(totverts, 3 * sizeof(float), "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, 3 * sizeof(float), "cloth sim deformation positions"); + } cloth_sim->mass = cloth_mass; cloth_sim->damping = cloth_damping; @@ -484,11 +595,11 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, for (int i = 0; i < cloth_sim->tot_length_constraints; i++) { const SculptClothLengthConstraint *constraint = &cloth_sim->length_constraints[i]; - const int v1 = constraint->v1; - const int v2 = constraint->v2; + const int v1 = constraint->elem_index_a; + const int v2 = constraint->elem_index_b; float v1_to_v2[3]; - sub_v3_v3v3(v1_to_v2, cloth_sim->pos[v2], cloth_sim->pos[v1]); + sub_v3_v3v3(v1_to_v2, constraint->elem_position_b, constraint->elem_position_a); const float current_distance = len_v3(v1_to_v2); float correction_vector[3]; float correction_vector_half[3]; @@ -524,8 +635,14 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, cloth_sim->init_pos[v2]) : 1.0f; - madd_v3_v3fl(cloth_sim->pos[v1], correction_vector_half, 1.0f * mask_v1 * sim_factor_v1); - madd_v3_v3fl(cloth_sim->pos[v2], correction_vector_half, -1.0f * mask_v2 * sim_factor_v2); + madd_v3_v3fl(cloth_sim->pos[v1], + correction_vector_half, + 1.0f * mask_v1 * sim_factor_v1 * constraint->strength); + if (v1 != v2) { + madd_v3_v3fl(cloth_sim->pos[v2], + correction_vector_half, + -1.0f * mask_v2 * sim_factor_v2 * constraint->strength); + } } } } @@ -577,7 +694,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod .mat = imat, }; - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Init the grab delta. */ copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); @@ -649,10 +766,16 @@ 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) { ss->cache->cloth_sim = cloth_brush_simulation_create( - ss, brush->cloth_mass, brush->cloth_damping); + ss, brush, brush->cloth_mass, brush->cloth_damping); + + const bool is_cloth_deform_brush = SCULPT_is_cloth_deform_brush(brush); + for (int i = 0; i < totverts; i++) { copy_v3_v3(ss->cache->cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); copy_v3_v3(ss->cache->cloth_sim->init_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)); + } } } @@ -684,6 +807,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) MEM_SAFE_FREE(cloth_sim->acceleration); MEM_SAFE_FREE(cloth_sim->length_constraints); 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); } @@ -913,7 +1037,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass"); const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping"); - ss->filter_cache->cloth_sim = cloth_brush_simulation_create(ss, cloth_mass, cloth_damping); + ss->filter_cache->cloth_sim = cloth_brush_simulation_create(ss, NULL, cloth_mass, cloth_damping); copy_v3_v3(ss->filter_cache->cloth_sim_pinch_point, SCULPT_active_vertex_co_get(ss)); const int totverts = SCULPT_vertex_count_get(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 1940b007cb0..5cc32be331e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -192,7 +192,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ SculptThreadedTaskData data = { diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index ef0897ce70e..912dfd808b0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -80,7 +80,7 @@ typedef enum eSculptColorFilterTypes { COLOR_FILTER_SMOOTH, } eSculptColorFilterTypes; -EnumPropertyItem prop_color_filter_types[] = { +static EnumPropertyItem prop_color_filter_types[] = { {COLOR_FILTER_FILL, "FILL", 0, "Fill", "Fill with a specific color"}, {COLOR_FILTER_HUE, "HUE", 0, "Hue", "Change hue"}, {COLOR_FILTER_SATURATION, "SATURATION", 0, "Saturation", "Change saturation"}, diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index f834ab7b179..47e6fb55d81 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -98,6 +98,9 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]); float SCULPT_vertex_mask_get(struct SculptSession *ss, int index); 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]); + #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 typedef struct SculptVertexNeighborIter { /* Storage */ diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index b52036d753c..bc3f8537289 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -47,7 +47,6 @@ #include "paint_intern.h" #include "sculpt_intern.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index f01a914fdd3..00a59949130 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -255,7 +255,7 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode return; } - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); float area_no[3]; float mat[4][4]; @@ -468,7 +468,7 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode } } - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); SculptThreadedTaskData data = { .sd = sd, diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index a338b5346af..976006b3958 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -1000,7 +1000,7 @@ void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br static void sculpt_pose_do_translate_deform(SculptSession *ss, Brush *brush) { SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); pose_solve_translate_chain(ik_chain, ss->cache->grab_delta); } @@ -1041,7 +1041,7 @@ static void sculpt_pose_do_twist_deform(SculptSession *ss, Brush *brush) /* Calculate the maximum roll. 0.02 radians per pixel works fine. */ float roll = (ss->cache->initial_mouse[0] - ss->cache->mouse[0]) * ss->cache->bstrength * 0.02f; - BKE_curvemapping_initialize(brush->curve); + BKE_curvemapping_init(brush->curve); pose_solve_roll_chain(ik_chain, brush, roll); } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 4b3df2dfea2..be509f4aed6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -488,7 +488,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm op->customdata = data; - BKE_curvemapping_initialize(ts->uvsculpt->paint.brush->curve); + BKE_curvemapping_init(ts->uvsculpt->paint.brush->curve); if (data) { int counter = 0, i; diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 079cee290ae..db55eff8284 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -60,7 +60,7 @@ /* ******************** default callbacks for action space ***************** */ -static SpaceLink *action_new(const ScrArea *area, const Scene *scene) +static SpaceLink *action_create(const ScrArea *area, const Scene *scene) { SpaceAction *saction; ARegion *region; @@ -863,7 +863,7 @@ void ED_spacetype_action(void) st->spaceid = SPACE_ACTION; strncpy(st->name, "Action", BKE_ST_MAXNAME); - st->new = action_new; + st->create = action_create; st->free = action_free; st->init = action_init; st->duplicate = action_duplicate; diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 3ae203b563b..1656a76e2d4 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -280,7 +280,7 @@ void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) void ED_spacetype_xxx(void); /* allocate and init some vars */ -static SpaceLink *xxx_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *xxx_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { return NULL; } @@ -324,7 +324,7 @@ void ED_spacetype_xxx(void) st.spaceid = SPACE_VIEW3D; - st.new = xxx_new; + st.create = xxx_create; st.free = xxx_free; st.init = xxx_init; st.duplicate = xxx_duplicate; diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 16256f6c97e..5885d3dcbb0 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -101,7 +101,7 @@ static PointerRNA *get_pointer_type(ButsContextPath *path, StructRNA *type) /************************* Creating the Path ************************/ -static int buttons_context_path_scene(ButsContextPath *path) +static bool buttons_context_path_scene(ButsContextPath *path) { PointerRNA *ptr = &path->ptr[path->len - 1]; @@ -162,14 +162,14 @@ static int buttons_context_path_world(ButsContextPath *path) return 0; } -static int buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window) +static bool buttons_context_path_linestyle(ButsContextPath *path, wmWindow *window) { FreestyleLineStyle *linestyle; PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a (pinned) linestyle, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_FreestyleLineStyle)) { - return 1; + return true; } /* if we have a view layer, use the lineset's linestyle */ if (buttons_context_path_view_layer(path, window)) { @@ -178,24 +178,24 @@ static int buttons_context_path_linestyle(ButsContextPath *path, wmWindow *windo if (linestyle) { RNA_id_pointer_create(&linestyle->id, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to a linestyle possible */ - return 0; + return false; } -static int buttons_context_path_object(ButsContextPath *path) +static bool buttons_context_path_object(ButsContextPath *path) { PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a (pinned) object, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Object)) { - return 1; + return true; } if (!RNA_struct_is_a(ptr->type, &RNA_ViewLayer)) { - return 0; + return false; } ViewLayer *view_layer = ptr->data; @@ -205,58 +205,58 @@ static int buttons_context_path_object(ButsContextPath *path) RNA_id_pointer_create(&ob->id, &path->ptr[path->len]); path->len++; - return 1; + return true; } /* no path to a object possible */ - return 0; + return false; } -static int buttons_context_path_data(ButsContextPath *path, int type) +static bool buttons_context_path_data(ButsContextPath *path, int type) { Object *ob; PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a data, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Mesh) && (type == -1 || type == OB_MESH)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Curve) && (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Camera) && (type == -1 || type == OB_CAMERA)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Light) && (type == -1 || type == OB_LAMP)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) { - return 1; + return true; } if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) { - return 1; + return true; } /* try to get an object in the path, no pinning supported here */ if (buttons_context_path_object(path)) { @@ -266,15 +266,15 @@ static int buttons_context_path_data(ButsContextPath *path, int type) RNA_id_pointer_create(ob->data, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to data possible */ - return 0; + return false; } -static int buttons_context_path_modifier(ButsContextPath *path) +static bool buttons_context_path_modifier(ButsContextPath *path) { Object *ob; @@ -291,14 +291,14 @@ static int buttons_context_path_modifier(ButsContextPath *path) OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) { - return 1; + return true; } } - return 0; + return false; } -static int buttons_context_path_shaderfx(ButsContextPath *path) +static bool buttons_context_path_shaderfx(ButsContextPath *path) { Object *ob; @@ -306,14 +306,14 @@ static int buttons_context_path_shaderfx(ButsContextPath *path) ob = path->ptr[path->len - 1].data; if (ob && ELEM(ob->type, OB_GPENCIL)) { - return 1; + return true; } } - return 0; + return false; } -static int buttons_context_path_material(ButsContextPath *path) +static bool buttons_context_path_material(ButsContextPath *path) { Object *ob; PointerRNA *ptr = &path->ptr[path->len - 1]; @@ -321,7 +321,7 @@ static int buttons_context_path_material(ButsContextPath *path) /* if we already have a (pinned) material, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Material)) { - return 1; + return true; } /* if we have an object, use the object material slot */ if (buttons_context_path_object(path)) { @@ -331,15 +331,15 @@ static int buttons_context_path_material(ButsContextPath *path) ma = BKE_object_material_get(ob, ob->actcol); RNA_id_pointer_create(&ma->id, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to a material possible */ - return 0; + return false; } -static int buttons_context_path_bone(ButsContextPath *path) +static bool buttons_context_path_bone(ButsContextPath *path) { bArmature *arm; EditBone *edbo; @@ -353,29 +353,29 @@ static int buttons_context_path_bone(ButsContextPath *path) edbo = arm->act_edbone; RNA_pointer_create(&arm->id, &RNA_EditBone, edbo, &path->ptr[path->len]); path->len++; - return 1; + return true; } } else { if (arm->act_bone) { RNA_pointer_create(&arm->id, &RNA_Bone, arm->act_bone, &path->ptr[path->len]); path->len++; - return 1; + return true; } } } /* no path to a bone possible */ - return 0; + return false; } -static int buttons_context_path_pose_bone(ButsContextPath *path) +static bool buttons_context_path_pose_bone(ButsContextPath *path) { PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a (pinned) PoseBone, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_PoseBone)) { - return 1; + return true; } /* if we have an armature, get the active bone */ @@ -384,7 +384,7 @@ static int buttons_context_path_pose_bone(ButsContextPath *path) bArmature *arm = ob->data; /* path->ptr[path->len-1].data - works too */ if (ob->type != OB_ARMATURE || arm->edbo) { - return 0; + return false; } if (arm->act_bone) { @@ -392,16 +392,16 @@ static int buttons_context_path_pose_bone(ButsContextPath *path) if (pchan) { RNA_pointer_create(&ob->id, &RNA_PoseBone, pchan, &path->ptr[path->len]); path->len++; - return 1; + return true; } } } /* no path to a bone possible */ - return 0; + return false; } -static int buttons_context_path_particle(ButsContextPath *path) +static bool buttons_context_path_particle(ButsContextPath *path) { Object *ob; ParticleSystem *psys; @@ -409,7 +409,7 @@ static int buttons_context_path_particle(ButsContextPath *path) /* if we already have (pinned) particle settings, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_ParticleSettings)) { - return 1; + return true; } /* if we have an object, get the active particle system */ if (buttons_context_path_object(path)) { @@ -420,15 +420,15 @@ static int buttons_context_path_particle(ButsContextPath *path) RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to a particle system possible */ - return 0; + return false; } -static int buttons_context_path_brush(const bContext *C, ButsContextPath *path) +static bool buttons_context_path_brush(const bContext *C, ButsContextPath *path) { Scene *scene; Brush *br = NULL; @@ -436,7 +436,7 @@ static int buttons_context_path_brush(const bContext *C, ButsContextPath *path) /* if we already have a (pinned) brush, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Brush)) { - return 1; + return true; } /* if we have a scene, use the toolsettings brushes */ if (buttons_context_path_scene(path)) { @@ -452,32 +452,32 @@ static int buttons_context_path_brush(const bContext *C, ButsContextPath *path) RNA_id_pointer_create((ID *)br, &path->ptr[path->len]); path->len++; - return 1; + return true; } } /* no path to a brush possible */ - return 0; + return false; } -static int buttons_context_path_texture(const bContext *C, - ButsContextPath *path, - ButsContextTexture *ct) +static bool buttons_context_path_texture(const bContext *C, + ButsContextPath *path, + ButsContextTexture *ct) { PointerRNA *ptr = &path->ptr[path->len - 1]; ID *id; if (!ct) { - return 0; + return false; } /* if we already have a (pinned) texture, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Texture)) { - return 1; + return true; } if (!ct->user) { - return 0; + return false; } id = ct->user->id; @@ -502,7 +502,7 @@ static int buttons_context_path_texture(const bContext *C, path->len++; } - return 1; + return true; } #ifdef WITH_FREESTYLE @@ -531,7 +531,7 @@ static bool buttons_context_linestyle_pinnable(const bContext *C, ViewLayer *vie } #endif -static int buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int flag) +static bool buttons_context_path(const bContext *C, ButsContextPath *path, int mainb, int flag) { /* Note we don't use CTX_data here, instead we get it from the window. * Otherwise there is a loop reading the context that we are setting. */ @@ -626,27 +626,27 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma found = buttons_context_path_pose_bone(path); break; default: - found = 0; + found = false; break; } return found; } -static int buttons_shading_context(const bContext *C, int mainb) +static bool buttons_shading_context(const bContext *C, int mainb) { wmWindow *window = CTX_wm_window(C); ViewLayer *view_layer = WM_window_get_active_view_layer(window); Object *ob = OBACT(view_layer); if (ELEM(mainb, BCONTEXT_MATERIAL, BCONTEXT_WORLD, BCONTEXT_TEXTURE)) { - return 1; + return true; } if (mainb == BCONTEXT_DATA && ob && ELEM(ob->type, OB_LAMP, OB_CAMERA)) { - return 1; + return true; } - return 0; + return false; } static int buttons_shading_new_context(const bContext *C, int flag) diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index 733f344fbc6..a062b178fc8 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -52,7 +52,9 @@ #include "buttons_intern.h" /* own include */ -/********************** context_menu operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Context Menu Operator + * \{ */ static int context_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) { @@ -67,17 +69,21 @@ static int context_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven void BUTTONS_OT_context_menu(wmOperatorType *ot) { - /* identifiers */ + /* Identifiers. */ ot->name = "Context Menu"; ot->description = "Display properties editor context_menu"; ot->idname = "BUTTONS_OT_context_menu"; - /* api callbacks */ + /* Callbacks. */ ot->invoke = context_menu_invoke; ot->poll = ED_operator_buttons_active; } -/********************** filebrowse operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name File Browse Operator + * \{ */ typedef struct FileBrowseOp { PointerRNA ptr; @@ -101,7 +107,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) str = RNA_string_get_alloc(op->ptr, path_prop, NULL, 0); - /* add slash for directories, important for some properties */ + /* Add slash for directories, important for some properties. */ if (RNA_property_subtype(fbo->prop) == PROP_DIRPATH) { const bool is_relative = RNA_boolean_get(op->ptr, "relative_path"); id = fbo->ptr.owner_id; @@ -110,7 +116,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) BLI_path_abs(path, id ? ID_BLEND_PATH(bmain, id) : BKE_main_blendfile_path(bmain)); if (BLI_is_dir(path)) { - /* do this first so '//' isnt converted to '//\' on windows */ + /* Do this first so '//' isnt converted to '//\' on windows. */ BLI_path_slash_ensure(path); if (is_relative) { BLI_strncpy(path, str, FILE_MAX); @@ -139,7 +145,7 @@ static int file_browse_exec(bContext *C, wmOperator *op) ED_undo_push(C, undostr); } - /* special, annoying exception, filesel on redo panel [#26618] */ + /* Special annoying exception, filesel on redo panel [#26618]. */ { wmOperator *redo_op = WM_operator_last_redo(C); if (redo_op) { @@ -187,8 +193,8 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) str = RNA_property_string_get_alloc(&ptr, prop, NULL, 0, NULL); - /* useful yet irritating feature, Shift+Click to open the file - * Alt+Click to browse a folder in the OS's browser */ + /* Useful yet irritating feature, Shift+Click to open the file + * Alt+Click to browse a folder in the OS's browser. */ if (event->shift || event->alt) { wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true); PointerRNA props_ptr; @@ -219,13 +225,13 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) fbo->is_userdef = is_userdef; op->customdata = fbo; - /* normally ED_fileselect_get_params would handle this but we need to because of stupid - * user-prefs exception - campbell */ + /* Normally ED_fileselect_get_params would handle this but we need to because of stupid + * user-prefs exception. - campbell */ if ((prop_relpath = RNA_struct_find_property(op->ptr, "relative_path"))) { if (!RNA_property_is_set(op->ptr, prop_relpath)) { bool is_relative = (U.flag & USER_RELPATHS) != 0; - /* while we want to follow the defaults, + /* While we want to follow the defaults, * we better not switch existing paths relative/absolute state. */ if (str[0]) { is_relative = BLI_path_is_rel(str); @@ -235,7 +241,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) is_relative = false; } - /* annoying exception!, if we're dealing with the user prefs, default relative to be off */ + /* Annoying exception!, if we're dealing with the user prefs, default relative to be off. */ RNA_property_boolean_set(op->ptr, prop_relpath, is_relative); } } @@ -250,21 +256,21 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) void BUTTONS_OT_file_browse(wmOperatorType *ot) { - /* identifiers */ + /* Identifiers. */ ot->name = "Accept"; ot->description = "Open a file browser, Hold Shift to open the file, Alt to browse containing directory"; ot->idname = "BUTTONS_OT_file_browse"; - /* api callbacks */ + /* Callbacks. */ ot->invoke = file_browse_invoke; ot->exec = file_browse_exec; ot->cancel = file_browse_cancel; - /* conditional undo based on button flag */ + /* Conditional undo based on button flag. */ ot->flag = 0; - /* properties */ + /* Properties. */ WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, @@ -274,7 +280,7 @@ void BUTTONS_OT_file_browse(wmOperatorType *ot) FILE_SORT_ALPHA); } -/* second operator, only difference from BUTTONS_OT_file_browse is WM_FILESEL_DIRECTORY */ +/* Second operator, only difference from BUTTONS_OT_file_browse is WM_FILESEL_DIRECTORY. */ void BUTTONS_OT_directory_browse(wmOperatorType *ot) { /* identifiers */ @@ -300,3 +306,5 @@ void BUTTONS_OT_directory_browse(wmOperatorType *ot) FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); } + +/** \} */ diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index ac59bb245f3..dc34e56dc92 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -35,6 +35,7 @@ #include "BKE_screen.h" #include "BKE_shader_fx.h" +#include "ED_buttons.h" #include "ED_screen.h" #include "ED_space_api.h" #include "ED_view3d.h" /* To draw toolbar UI. */ @@ -49,13 +50,11 @@ #include "UI_resources.h" -#include "GPU_glew.h" - #include "buttons_intern.h" /* own include */ /* ******************** default callbacks for buttons space ***************** */ -static SpaceLink *buttons_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *buttons_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceProperties *sbuts; @@ -139,6 +138,98 @@ static void buttons_main_region_init(wmWindowManager *wm, ARegion *region) WM_event_add_keymap_handler(®ion->handlers, keymap); } +/** + * Fills an array with the tab context values for the properties editor. -1 signals a separator. + * + * \return The total number of items in the array returned. + */ +int ED_buttons_tabs_list(SpaceProperties *sbuts, int *context_tabs_array) +{ + int length = 0; + if (sbuts->pathflag & (1 << BCONTEXT_TOOL)) { + context_tabs_array[length] = BCONTEXT_TOOL; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_RENDER)) { + context_tabs_array[length] = BCONTEXT_RENDER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_OUTPUT)) { + context_tabs_array[length] = BCONTEXT_OUTPUT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) { + context_tabs_array[length] = BCONTEXT_VIEW_LAYER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) { + context_tabs_array[length] = BCONTEXT_SCENE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_WORLD)) { + context_tabs_array[length] = BCONTEXT_WORLD; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) { + context_tabs_array[length] = BCONTEXT_OBJECT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) { + context_tabs_array[length] = BCONTEXT_MODIFIER; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) { + context_tabs_array[length] = BCONTEXT_SHADERFX; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) { + context_tabs_array[length] = BCONTEXT_PARTICLE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) { + context_tabs_array[length] = BCONTEXT_PHYSICS; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) { + context_tabs_array[length] = BCONTEXT_CONSTRAINT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_DATA)) { + context_tabs_array[length] = BCONTEXT_DATA; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_BONE)) { + context_tabs_array[length] = BCONTEXT_BONE; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_BONE_CONSTRAINT)) { + context_tabs_array[length] = BCONTEXT_BONE_CONSTRAINT; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_MATERIAL)) { + context_tabs_array[length] = BCONTEXT_MATERIAL; + length++; + } + if (length != 0) { + context_tabs_array[length] = -1; + length++; + } + if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) { + context_tabs_array[length] = BCONTEXT_TEXTURE; + length++; + } + + return length; +} + static void buttons_main_region_layout_properties(const bContext *C, SpaceProperties *sbuts, ARegion *region) @@ -618,7 +709,7 @@ void ED_spacetype_buttons(void) st->spaceid = SPACE_PROPERTIES; strncpy(st->name, "Buttons", BKE_ST_MAXNAME); - st->new = buttons_new; + st->create = buttons_create; st->free = buttons_free; st->init = buttons_init; st->duplicate = buttons_duplicate; diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index d33f624063a..1d510d2989c 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -322,7 +322,7 @@ static void draw_movieclip_buffer(const bContext *C, float zoomy) { MovieClip *clip = ED_space_clip_get_clip(sc); - int filter = GL_LINEAR; + bool use_filter = true; int x, y; /* find window pixel coordinates of origin */ @@ -340,10 +340,10 @@ static void draw_movieclip_buffer(const bContext *C, /* non-scaled proxy shouldn't use filtering */ if ((clip->flag & MCLIP_USE_PROXY) == 0 || ELEM(sc->user.render_size, MCLIP_PROXY_RENDER_SIZE_FULL, MCLIP_PROXY_RENDER_SIZE_100)) { - filter = GL_NEAREST; + use_filter = false; } - ED_draw_imbuf_ctx(C, ibuf, x, y, filter, zoomx * width / ibuf->x, zoomy * height / ibuf->y); + 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); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 9c251fb619a..d27b80efd40 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -56,7 +56,6 @@ #include "IMB_imbuf.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "WM_api.h" @@ -238,7 +237,7 @@ static void clip_area_sync_frame_from_scene(ScrArea *area, Scene *scene) /* ******************** default callbacks for clip space ***************** */ -static SpaceLink *clip_new(const ScrArea *area, const Scene *scene) +static SpaceLink *clip_create(const ScrArea *area, const Scene *scene) { ARegion *region; SpaceClip *sc; @@ -685,7 +684,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (main_visible) { if (region_main && (region_main->flag & RGN_FLAG_HIDDEN)) { region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } @@ -697,7 +696,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_main && !(region_main->flag & RGN_FLAG_HIDDEN)) { region_main->flag |= RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_main->handlers); view_changed = true; } @@ -710,7 +709,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (properties_visible) { if (region_properties && (region_properties->flag & RGN_FLAG_HIDDEN)) { region_properties->flag &= ~RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INITIALISED; + region_properties->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } if (region_properties && region_properties->alignment != RGN_ALIGN_RIGHT) { @@ -721,7 +720,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_properties && !(region_properties->flag & RGN_FLAG_HIDDEN)) { region_properties->flag |= RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INITIALISED; + region_properties->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_properties->handlers); view_changed = true; } @@ -734,7 +733,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (tools_visible) { if (region_tools && (region_tools->flag & RGN_FLAG_HIDDEN)) { region_tools->flag &= ~RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INITIALISED; + region_tools->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } if (region_tools && region_tools->alignment != RGN_ALIGN_LEFT) { @@ -745,7 +744,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_tools && !(region_tools->flag & RGN_FLAG_HIDDEN)) { region_tools->flag |= RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INITIALISED; + region_tools->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_tools->handlers); view_changed = true; } @@ -758,7 +757,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (preview_visible) { if (region_preview && (region_preview->flag & RGN_FLAG_HIDDEN)) { region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; region_preview->v2d.cur = region_preview->v2d.tot; view_changed = true; } @@ -770,7 +769,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_preview && !(region_preview->flag & RGN_FLAG_HIDDEN)) { region_preview->flag |= RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_preview->handlers); view_changed = true; } @@ -783,7 +782,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) if (channels_visible) { if (region_channels && (region_channels->flag & RGN_FLAG_HIDDEN)) { region_channels->flag &= ~RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INITIALISED; + region_channels->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } if (region_channels && region_channels->alignment != RGN_ALIGN_LEFT) { @@ -794,7 +793,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) else { if (region_channels && !(region_channels->flag & RGN_FLAG_HIDDEN)) { region_channels->flag |= RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INITIALISED; + region_channels->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_channels->handlers); view_changed = true; } @@ -805,7 +804,7 @@ static void clip_refresh(const bContext *C, ScrArea *area) } if (view_changed) { - ED_area_initialize(wm, window, area); + ED_area_init(wm, window, area); ED_area_tag_redraw(area); } @@ -1352,7 +1351,7 @@ void ED_spacetype_clip(void) st->spaceid = SPACE_CLIP; strncpy(st->name, "Clip", BKE_ST_MAXNAME); - st->new = clip_new; + st->create = clip_create; st->free = clip_free; st->init = clip_init; st->duplicate = clip_duplicate; diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 3c62aeb1759..3a0125356f7 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -46,7 +46,7 @@ /* ******************** default callbacks for console space ***************** */ -static SpaceLink *console_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *console_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceConsole *sconsole; @@ -312,7 +312,7 @@ void ED_spacetype_console(void) st->spaceid = SPACE_CONSOLE; strncpy(st->name, "Console", BKE_ST_MAXNAME); - st->new = console_new; + st->create = console_create; st->free = console_free; st->init = console_init; st->duplicate = console_duplicate; diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 8d14664c0fa..68e9f76abc5 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -300,9 +300,8 @@ static void file_draw_preview(uiBlock *block, (float)yco, imb->x, imb->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, imb->rect, scale, scale, diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 43939b9ff54..f520f91b89b 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -91,7 +91,7 @@ static ARegion *file_tool_props_region_ensure(ScrArea *area, ARegion *region_pre /* ******************** default callbacks for file space ***************** */ -static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *file_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceFile *sfile; @@ -235,7 +235,7 @@ static void file_ensure_valid_region_state(bContext *C, ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI); ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS); ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE); - bool needs_init = false; /* To avoid multiple ED_area_initialize() calls. */ + bool needs_init = false; /* To avoid multiple ED_area_init() calls. */ /* If there's an file-operation, ensure we have the option and execute region */ if (sfile->op && (region_props == NULL)) { @@ -261,7 +261,7 @@ static void file_ensure_valid_region_state(bContext *C, } if (needs_init) { - ED_area_initialize(wm, win, area); + ED_area_init(wm, win, area); } } @@ -693,7 +693,7 @@ void ED_spacetype_file(void) st->spaceid = SPACE_FILE; strncpy(st->name, "File", BKE_ST_MAXNAME); - st->new = file_new; + st->create = file_create; st->free = file_free; st->init = file_init; st->exit = file_exit; diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 68fdef54a53..90fe95c6818 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1487,7 +1487,7 @@ static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent dgo->area = CTX_wm_area(C); dgo->region = CTX_wm_region(C); - /* initialise percentage so that it will have the correct value before the first mouse move. */ + /* Initialize percentage so that it will have the correct value before the first mouse move. */ decimate_mouse_update_percentage(dgo, op, event); decimate_draw_status_header(op, dgo); diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index b1d995a7a0b..a4f76384cc6 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -64,7 +64,7 @@ /* ******************** default callbacks for ipo space ***************** */ -static SpaceLink *graph_new(const ScrArea *UNUSED(area), const Scene *scene) +static SpaceLink *graph_create(const ScrArea *UNUSED(area), const Scene *scene) { ARegion *region; SpaceGraph *sipo; @@ -838,7 +838,7 @@ void ED_spacetype_ipo(void) st->spaceid = SPACE_GRAPH; strncpy(st->name, "Graph", BKE_ST_MAXNAME); - st->new = graph_new; + st->create = graph_create; st->free = graph_free; st->init = graph_init; st->duplicate = graph_duplicate; diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index a7fa7709c51..1038011e480 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -468,20 +468,19 @@ static void sima_draw_zbuf_pixels( float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; /* Slowwww */ - int *recti = MEM_mallocN(rectx * recty * sizeof(int), "temp"); + float *rectf = MEM_mallocN(rectx * recty * sizeof(float), "temp"); for (int a = rectx * recty - 1; a >= 0; a--) { /* zbuffer values are signed, so we need to shift color range */ - recti[a] = rect[a] * 0.5f + 0.5f; + rectf[a] = rect[a] * 0.5f + 0.5f; } 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, x1, y1, rectx, recty, GL_RED, GL_INT, GL_NEAREST, recti, zoomx, zoomy, NULL); + immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL); - MEM_freeN(recti); + MEM_freeN(rectf); } static void sima_draw_zbuffloat_pixels(Scene *scene, @@ -526,8 +525,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_RED, GL_FLOAT, GL_NEAREST, rectf, zoomx, zoomy, NULL); + immDrawPixelsTex(&state, x1, y1, rectx, recty, GL_R16F, false, rectf, zoomx, zoomy, NULL); MEM_freeN(rectf); } @@ -612,8 +610,7 @@ static void draw_image_buffer(const bContext *C, /* If RGBA display with color management */ if ((sima_flag & (SI_SHOW_R | SI_SHOW_G | SI_SHOW_B | SI_SHOW_ALPHA)) == 0) { - ED_draw_imbuf_ctx_clipping( - C, ibuf, x, y, GL_NEAREST, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy); + ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, false, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy); } else { float shuffle[4] = {0.0f, 0.0f, 0.0f, 0.0f}; @@ -649,9 +646,8 @@ static void draw_image_buffer(const bContext *C, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, display_buffer, 0, 0, @@ -780,18 +776,8 @@ static void draw_image_paint_helpers( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTex(&state, - x, - y, - ibuf->x, - ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, - display_buffer, - zoomx, - zoomy, - col); + immDrawPixelsTex( + &state, x, y, ibuf->x, ibuf->y, GPU_RGBA8, false, display_buffer, zoomx, zoomy, col); GPU_blend(false); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 4e410d35df0..1a98ec0e7c1 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -69,7 +69,6 @@ #include "DEG_depsgraph.h" -#include "GPU_draw.h" #include "GPU_immediate.h" #include "GPU_state.h" @@ -2769,7 +2768,7 @@ static int image_invert_exec(bContext *C, wmOperator *op) ED_image_undo_push_end(); /* force GPU reupload, all image is invalid */ - GPU_free_image(ima); + BKE_image_free_gputextures(ima); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -2860,7 +2859,7 @@ static int image_scale_exec(bContext *C, wmOperator *op) ED_image_undo_push_end(); /* force GPU reupload, all image is invalid */ - GPU_free_image(ima); + BKE_image_free_gputextures(ima); DEG_id_tag_update(&ima->id, 0); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -3715,7 +3714,7 @@ static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout) uiItemR(col[1], ptr, "float", 0, NULL, ICON_NONE); } -static void initialize_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile) +static void tile_fill_init(PointerRNA *ptr, Image *ima, ImageTile *tile) { ImageUser iuser; BKE_imageuser_default(&iuser); @@ -3828,7 +3827,7 @@ static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev } ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index); - initialize_fill_tile(op->ptr, ima, tile); + tile_fill_init(op->ptr, ima, tile); RNA_int_set(op->ptr, "number", next_number); RNA_int_set(op->ptr, "count", 1); @@ -3974,7 +3973,7 @@ static int tile_fill_exec(bContext *C, wmOperator *op) static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - initialize_fill_tile(op->ptr, CTX_data_edit_image(C), NULL); + tile_fill_init(op->ptr, CTX_data_edit_image(C), NULL); return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X); } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index e0c44c3a0ba..27b84307f7d 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -60,8 +60,6 @@ #include "ED_undo.h" #include "ED_util.h" -#include "GPU_draw.h" - #include "WM_api.h" static CLG_LogRef LOG = {"ed.image.undo"}; @@ -295,7 +293,8 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) SWAP(uint *, ptile->rect.uint, tmpibuf->rect); } - GPU_free_image(image); /* force OpenGL reload (maybe partial update will operate better?) */ + BKE_image_free_gputextures( + image); /* force OpenGL reload (maybe partial update will operate better?) */ if (ibuf->rect_float) { ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ } @@ -570,7 +569,7 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init) if (changed) { BKE_image_mark_dirty(image, ibuf); - GPU_free_image(image); /* force OpenGL reload */ + BKE_image_free_gputextures(image); /* force OpenGL reload */ if (ibuf->rect_float) { ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index d7d85112497..11af71586a5 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -117,7 +117,7 @@ static void image_user_refresh_scene(const bContext *C, SpaceImage *sima) /* ******************** default callbacks for image space ***************** */ -static SpaceLink *image_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceImage *simage; @@ -1093,7 +1093,7 @@ void ED_spacetype_image(void) st->spaceid = SPACE_IMAGE; strncpy(st->name, "Image", BKE_ST_MAXNAME); - st->new = image_new; + st->create = image_create; st->free = image_free; st->init = image_init; st->duplicate = image_duplicate; diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index 836830916ed..b9153ec0cbd 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -53,7 +53,7 @@ /* ******************** default callbacks for info space ***************** */ -static SpaceLink *info_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *info_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceInfo *sinfo; @@ -287,7 +287,7 @@ void ED_spacetype_info(void) st->spaceid = SPACE_INFO; strncpy(st->name, "Info", BKE_ST_MAXNAME); - st->new = info_new; + st->create = info_create; st->free = info_free; st->init = info_init; st->duplicate = info_duplicate; diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index b09536e0621..7bbfe451eed 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -57,7 +57,7 @@ /* ******************** default callbacks for nla space ***************** */ -static SpaceLink *nla_new(const ScrArea *area, const Scene *scene) +static SpaceLink *nla_create(const ScrArea *area, const Scene *scene) { ARegion *region; SpaceNla *snla; @@ -608,7 +608,7 @@ void ED_spacetype_nla(void) st->spaceid = SPACE_NLA; strncpy(st->name, "NLA", BKE_ST_MAXNAME); - st->new = nla_new; + st->create = nla_create; st->free = nla_free; st->init = nla_init; st->duplicate = nla_duplicate; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 01883f1c086..207f67aed1b 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3678,9 +3678,8 @@ void draw_nodespace_back_pix(const bContext *C, y, ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, display_buffer, snode->zoom, snode->zoom, @@ -3693,12 +3692,12 @@ void draw_nodespace_back_pix(const bContext *C, GPU_blend_set_func_separate( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - ED_draw_imbuf_ctx(C, ibuf, x, y, GL_NEAREST, snode->zoom, snode->zoom); + ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom); GPU_blend(false); } else { - ED_draw_imbuf_ctx(C, ibuf, x, y, GL_NEAREST, snode->zoom, snode->zoom); + ED_draw_imbuf_ctx(C, ibuf, x, y, false, snode->zoom, snode->zoom); } if (cache_handle) { diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 22b549cbd5d..52bd4d68649 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -947,9 +947,8 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv) draw_rect.ymin, preview->xsize, preview->ysize, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_LINEAR, + GPU_RGBA8, + true, preview->rect, scale, scale, diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 7af64e75656..3c927dbf25f 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -1697,6 +1697,8 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) } } + do_tag_update |= ED_node_is_simulation(snode); + snode_notify(C, snode); if (do_tag_update) { snode_dag_update(C, snode); @@ -1739,6 +1741,8 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) } } + do_tag_update |= ED_node_is_simulation(snode); + ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 3e898b7d400..9110d82fb84 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -670,6 +670,8 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) } ntree->is_updating = false; + do_tag_update |= ED_node_is_simulation(snode); + ntreeUpdateTree(bmain, ntree); snode_notify(C, snode); if (do_tag_update) { @@ -1070,6 +1072,8 @@ static int cut_links_exec(bContext *C, wmOperator *op) } } + do_tag_update |= ED_node_is_simulation(snode); + if (found) { ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index d4adad3fc25..6d570001347 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -244,7 +244,7 @@ void snode_group_offset(SpaceNode *snode, float *x, float *y) /* ******************** default callbacks for node space ***************** */ -static SpaceLink *node_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *node_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceNode *snode; @@ -954,7 +954,7 @@ void ED_spacetype_node(void) st->spaceid = SPACE_NODE; strncpy(st->name, "Node", BKE_ST_MAXNAME); - st->new = node_new; + st->create = node_create; st->free = node_free; st->init = node_init; st->duplicate = node_duplicate; diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index aa1663dff01..8a78211f145 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -294,7 +294,7 @@ static void outliner_header_region_listener(wmWindow *UNUSED(win), /* ******************** default callbacks for outliner space ***************** */ -static SpaceLink *outliner_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *outliner_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceOutliner *soutliner; @@ -407,7 +407,7 @@ void ED_spacetype_outliner(void) st->spaceid = SPACE_OUTLINER; strncpy(st->name, "Outliner", BKE_ST_MAXNAME); - st->new = outliner_new; + st->create = outliner_create; st->free = outliner_free; st->init = outliner_init; st->duplicate = outliner_duplicate; diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c index 343f35421a4..4d0c2b658c6 100644 --- a/source/blender/editors/space_script/space_script.c +++ b/source/blender/editors/space_script/space_script.c @@ -51,7 +51,7 @@ /* ******************** default callbacks for script space ***************** */ -static SpaceLink *script_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *script_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceScript *sscript; @@ -179,7 +179,7 @@ void ED_spacetype_script(void) st->spaceid = SPACE_SCRIPT; strncpy(st->name, "Script", BKE_ST_MAXNAME); - st->new = script_new; + st->create = script_create; st->free = script_free; st->init = script_init; st->duplicate = script_duplicate; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 995e980aba0..7863d2b724d 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1599,7 +1599,7 @@ static void sequencer_draw_display_buffer(const bContext *C, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); } - /* Format needs to be created prior to any immBindProgram call. + /* Format needs to be created prior to any immBindShader call. * Do it here because OCIO binds it's own shader. */ eGPUTextureFormat format; eGPUDataFormat data; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index b2d0362602e..99d4c2d9f1a 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -666,74 +666,6 @@ int seq_effect_find_selected(Scene *scene, /** \name Delete Utilities * \{ */ -static Sequence *del_seq_find_replace_recurs(Scene *scene, Sequence *seq) -{ - Sequence *seq1, *seq2, *seq3; - - /* Try to find a replacement input sequence, and flag for later deletion if - * no replacement can be found. */ - - if (!seq) { - return NULL; - } - if (!(seq->type & SEQ_TYPE_EFFECT)) { - return ((seq->flag & SELECT) ? NULL : seq); - } - if (!(seq->flag & SELECT)) { - /* Try to find replacement for effect inputs. */ - seq1 = del_seq_find_replace_recurs(scene, seq->seq1); - seq2 = del_seq_find_replace_recurs(scene, seq->seq2); - seq3 = del_seq_find_replace_recurs(scene, seq->seq3); - - if (seq1 == seq->seq1 && seq2 == seq->seq2 && seq3 == seq->seq3) { - /* Pass. */ - } - else if (seq1 || seq2 || seq3) { - seq->seq1 = (seq1) ? seq1 : (seq2) ? seq2 : seq3; - seq->seq2 = (seq2) ? seq2 : (seq1) ? seq1 : seq3; - seq->seq3 = (seq3) ? seq3 : (seq1) ? seq1 : seq2; - - BKE_sequencer_update_changed_seq_and_deps(scene, seq, 1, 1); - } - else { - seq->flag |= SELECT; /* Mark for delete. */ - } - } - - if (seq->flag & SELECT) { - if ((seq1 = del_seq_find_replace_recurs(scene, seq->seq1))) { - return seq1; - } - if ((seq2 = del_seq_find_replace_recurs(scene, seq->seq2))) { - return seq2; - } - if ((seq3 = del_seq_find_replace_recurs(scene, seq->seq3))) { - return seq3; - } - return NULL; - } - return seq; -} - -static void del_seq_clear_modifiers_recurs(Scene *scene, Sequence *deleting_sequence) -{ - Editing *ed = BKE_sequencer_editing_get(scene, false); - Sequence *current_sequence; - - SEQP_BEGIN (ed, current_sequence) { - if (!(current_sequence->flag & SELECT) && current_sequence != deleting_sequence) { - SequenceModifierData *smd; - - for (smd = current_sequence->modifiers.first; smd; smd = smd->next) { - if (smd->mask_sequence == deleting_sequence) { - smd->mask_sequence = NULL; - } - } - } - } - SEQ_END; -} - static void recurs_del_seq_flag(Scene *scene, ListBase *lb, short flag, short deleteall) { Sequence *seq, *seqn; @@ -2586,61 +2518,20 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); Sequence *seq; - MetaStack *ms; - bool nothing_selected = true; BKE_sequencer_prefetch_stop(scene); - seq = BKE_sequencer_active_get(scene); - if (seq && seq->flag & SELECT) { /* Avoid a loop since this is likely to be selected. */ - nothing_selected = false; - } - else { - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->flag & SELECT) { - nothing_selected = false; - break; - } - } - } - - if (nothing_selected) { - return OPERATOR_FINISHED; - } - - /* For effects and modifiers, try to find a replacement input. */ - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (!(seq->flag & SELECT)) { - if ((seq->type & SEQ_TYPE_EFFECT)) { - del_seq_find_replace_recurs(scene, seq); - } - } - else { - del_seq_clear_modifiers_recurs(scene, seq); + SEQP_BEGIN (scene->ed, seq) { + if (seq->flag & SELECT) { + BKE_sequencer_flag_for_removal(scene, ed->seqbasep, seq); } } - - /* Delete all selected strips. */ - recurs_del_seq_flag(scene, ed->seqbasep, SELECT, 0); - - /* Update lengths, etc. */ - seq = ed->seqbasep->first; - while (seq) { - BKE_sequence_calc(scene, seq); - seq = seq->next; - } - - /* Free parent metas. */ - ms = ed->metastack.last; - while (ms) { - BKE_sequence_calc(scene, ms->parseq); - ms = ms->prev; - } + SEQ_END; + BKE_sequencer_remove_flagged_sequences(scene, ed->seqbasep); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - return OPERATOR_FINISHED; } @@ -3036,8 +2927,8 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) } /* This moves strips from meta to parent, sating within same edit and no new strips are - * allocated. If the UUID was unique already (as it should) it will stay unique. Nn need to - * re-generate the UUIDs.*/ + * allocated. If the UUID was unique already (as it should) it will stay unique. + * No need to re-generate the UUIDs. */ BLI_movelisttolist(ed->seqbasep, &last_seq->seqbase); BLI_listbase_clear(&last_seq->seqbase); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 926752c6488..b8bb3e4d43b 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -86,7 +86,7 @@ static ARegion *sequencer_find_region(ScrArea *area, short type) /* ******************** default callbacks for sequencer space ***************** */ -static SpaceLink *sequencer_new(const ScrArea *UNUSED(area), const Scene *scene) +static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *scene) { ARegion *region; SpaceSeq *sseq; @@ -231,12 +231,12 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) case SEQ_VIEW_SEQUENCE: if (region_main && (region_main->flag & RGN_FLAG_HIDDEN)) { region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; view_changed = true; } if (region_preview && !(region_preview->flag & RGN_FLAG_HIDDEN)) { region_preview->flag |= RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_preview->handlers); view_changed = true; } @@ -252,13 +252,13 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) case SEQ_VIEW_PREVIEW: if (region_main && !(region_main->flag & RGN_FLAG_HIDDEN)) { region_main->flag |= RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; WM_event_remove_handlers((bContext *)C, ®ion_main->handlers); view_changed = true; } if (region_preview && (region_preview->flag & RGN_FLAG_HIDDEN)) { region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; region_preview->v2d.cur = region_preview->v2d.tot; view_changed = true; } @@ -281,13 +281,13 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) * 'full window' views before, though... Better than nothing. */ if (region_main->flag & RGN_FLAG_HIDDEN) { region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INITIALISED; + region_main->v2d.flag &= ~V2D_IS_INIT; region_preview->sizey = (int)(height - region_main->sizey); view_changed = true; } if (region_preview->flag & RGN_FLAG_HIDDEN) { region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INITIALISED; + region_preview->v2d.flag &= ~V2D_IS_INIT; region_preview->v2d.cur = region_preview->v2d.tot; region_main->sizey = (int)(height - region_preview->sizey); view_changed = true; @@ -312,7 +312,7 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) } if (view_changed) { - ED_area_initialize(wm, window, area); + ED_area_init(wm, window, area); ED_area_tag_redraw(area); } } @@ -852,7 +852,7 @@ void ED_spacetype_sequencer(void) st->spaceid = SPACE_SEQ; strncpy(st->name, "Sequencer", BKE_ST_MAXNAME); - st->new = sequencer_new; + st->create = sequencer_create; st->free = sequencer_free; st->init = sequencer_init; st->duplicate = sequencer_duplicate; diff --git a/source/blender/editors/space_statusbar/space_statusbar.c b/source/blender/editors/space_statusbar/space_statusbar.c index 34d7f8b0216..ae56b111360 100644 --- a/source/blender/editors/space_statusbar/space_statusbar.c +++ b/source/blender/editors/space_statusbar/space_statusbar.c @@ -42,7 +42,7 @@ /* ******************** default callbacks for statusbar space ******************** */ -static SpaceLink *statusbar_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *statusbar_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceStatusBar *sstatusbar; @@ -158,7 +158,7 @@ void ED_spacetype_statusbar(void) st->spaceid = SPACE_STATUSBAR; strncpy(st->name, "Status Bar", BKE_ST_MAXNAME); - st->new = statusbar_new; + st->create = statusbar_create; st->free = statusbar_free; st->init = statusbar_init; st->duplicate = statusbar_duplicate; diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index a2af99ee9f9..f6d00ec94bf 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -53,7 +53,7 @@ /* ******************** default callbacks for text space ***************** */ -static SpaceLink *text_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *text_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceText *stext; @@ -445,7 +445,7 @@ void ED_spacetype_text(void) st->spaceid = SPACE_TEXT; strncpy(st->name, "Text", BKE_ST_MAXNAME); - st->new = text_new; + st->create = text_create; st->free = text_free; st->init = text_init; st->duplicate = text_duplicate; diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index d06c567988d..dc357cdd355 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -52,7 +52,7 @@ /* ******************** default callbacks for topbar space ***************** */ -static SpaceLink *topbar_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) +static SpaceLink *topbar_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) { ARegion *region; SpaceTopBar *stopbar; @@ -250,7 +250,7 @@ void ED_spacetype_topbar(void) st->spaceid = SPACE_TOPBAR; strncpy(st->name, "Top Bar", BKE_ST_MAXNAME); - st->new = topbar_new; + st->create = topbar_create; st->free = topbar_free; st->init = topbar_init; st->duplicate = topbar_duplicate; diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c index 9eae722d5c8..0242bb4fe24 100644 --- a/source/blender/editors/space_userpref/space_userpref.c +++ b/source/blender/editors/space_userpref/space_userpref.c @@ -45,7 +45,7 @@ /* ******************** default callbacks for userpref space ***************** */ -static SpaceLink *userpref_new(const ScrArea *area, const Scene *UNUSED(scene)) +static SpaceLink *userpref_create(const ScrArea *area, const Scene *UNUSED(scene)) { ARegion *region; SpaceUserPref *spref; @@ -115,7 +115,7 @@ static void userpref_main_region_init(wmWindowManager *wm, ARegion *region) { /* do not use here, the properties changed in userprefs do a system-wide refresh, * then scroller jumps back */ - /* region->v2d.flag &= ~V2D_IS_INITIALISED; */ + /* region->v2d.flag &= ~V2D_IS_INIT; */ region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE; @@ -235,7 +235,7 @@ void ED_spacetype_userpref(void) st->spaceid = SPACE_USERPREF; strncpy(st->name, "Userpref", BKE_ST_MAXNAME); - st->new = userpref_new; + st->create = userpref_create; st->free = userpref_free; st->init = userpref_init; st->duplicate = userpref_duplicate; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index c88303daa16..e5ba27cef07 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -260,7 +260,7 @@ void ED_view3d_shade_update(Main *bmain, View3D *v3d, ScrArea *area) /* ******************** default callbacks for view3d space ***************** */ -static SpaceLink *view3d_new(const ScrArea *UNUSED(area), const Scene *scene) +static SpaceLink *view3d_create(const ScrArea *UNUSED(area), const Scene *scene) { ARegion *region; View3D *v3d; @@ -616,11 +616,12 @@ static void view3d_lightcache_update(bContext *C) return; } - WM_operator_properties_create(&op_ptr, "SCENE_OT_light_cache_bake"); + wmOperatorType *ot = WM_operatortype_find("SCENE_OT_light_cache_bake", true); + WM_operator_properties_create_ptr(&op_ptr, ot); RNA_int_set(&op_ptr, "delay", 200); RNA_enum_set_identifier(C, &op_ptr, "subset", "DIRTY"); - WM_operator_name_call(C, "SCENE_OT_light_cache_bake", WM_OP_INVOKE_DEFAULT, &op_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr); WM_operator_properties_free(&op_ptr); } @@ -1610,7 +1611,7 @@ void ED_spacetype_view3d(void) st->spaceid = SPACE_VIEW3D; strncpy(st->name, "View3D", BKE_ST_MAXNAME); - st->new = view3d_new; + st->create = view3d_create; st->free = view3d_free; st->init = view3d_init; st->listener = space_view3d_listener; diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 2e170126574..d78c58c0c64 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1474,6 +1474,7 @@ static void v3d_editmetaball_buts(uiLayout *layout, Object *ob) uiLayout *col; if (!mball || !(mball->lastelem)) { + uiItemL(layout, IFACE_("Nothing selected"), ICON_NONE); return; } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index c062a8de361..cf132a1a0c0 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -36,6 +36,7 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_global.h" +#include "BKE_image.h" #include "BKE_key.h" #include "BKE_layer.h" #include "BKE_main.h" @@ -74,7 +75,6 @@ #include "GPU_batch.h" #include "GPU_batch_presets.h" -#include "GPU_draw.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" @@ -1617,7 +1617,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *region) view3d_draw_view(C, region); DRW_cache_free_old_batches(bmain); - GPU_free_images_old(bmain); + BKE_image_free_old_gputextures(bmain); GPU_pass_cache_garbage_collect(); /* XXX This is in order to draw UI batches with the DRW @@ -1707,7 +1707,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, { /* free images which can have changed on frame-change * warning! can be slow so only free animated images - campbell */ - GPU_free_images_anim(G.main); /* XXX :((( */ + BKE_image_free_anim_gputextures(G.main); /* XXX :((( */ } GPU_matrix_push_projection(); @@ -1954,10 +1954,10 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, NULL); if (ibuf->rect_float) { - GPU_offscreen_read_pixels(ofs, GL_FLOAT, ibuf->rect_float); + GPU_offscreen_read_pixels(ofs, GPU_DATA_FLOAT, ibuf->rect_float); } else if (ibuf->rect) { - GPU_offscreen_read_pixels(ofs, GL_UNSIGNED_BYTE, ibuf->rect); + GPU_offscreen_read_pixels(ofs, GPU_DATA_UNSIGNED_BYTE, ibuf->rect); } /* unbind */ diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 64447015bdc..9490c807989 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -97,7 +97,6 @@ #include "UI_interface.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "DEG_depsgraph.h" @@ -3336,12 +3335,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) FOREACH_OBJECT_IN_MODE_END; } else { /* No edit-mode, unified for bones and objects. */ - if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) { - /* XXX, this is not selection, could be it's own operator. */ - changed_multi = ED_sculpt_mask_box_select( - C, &vc, &rect, sel_op == SEL_OP_ADD ? true : false); - } - else if (vc.obact && BKE_paint_select_face_test(vc.obact)) { + if (vc.obact && BKE_paint_select_face_test(vc.obact)) { changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op); } else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) { diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 66efa5b5de3..ff9673a4262 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -50,7 +50,6 @@ #include "UI_resources.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_state.h" diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 2bda04ad811..bbd18b4bdaf 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -81,6 +81,7 @@ typedef struct TransSnap { bool snap_self; bool peel; bool snap_spatial_grid; + bool use_backface_culling; char status; /* Snapped Element Type (currently for objects only). */ char snapElem; diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index 0eb12aeabed..84885dd8c49 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -104,7 +104,7 @@ void createTransGPencil(bContext *C, TransInfo *t) /* initialize falloff curve */ if (is_multiedit) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + BKE_curvemapping_init(ts->gp_sculpt.cur_falloff); } /* First Pass: Count the number of data-points required for the strokes, diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index ad426713719..6f447997fb0 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1033,8 +1033,10 @@ static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t), # define FACE_SUBSTITUTE_INDEX INT_MIN -/* Search for a neighboring face with area and preferably without selected vertex. - * Used to replace arealess faces in customdata correction. */ +/** + * Search for a neighboring face with area and preferably without selected vertex. + * Used to replace area-less faces in custom-data correction. + */ static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f) { BMFace *best_face = NULL; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 2943c3cb8ea..a700dd320b7 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -37,6 +37,7 @@ #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_object.h" +#include "BKE_scene.h" #include "BKE_sequencer.h" #include "RNA_access.h" @@ -100,6 +101,24 @@ int BIF_snappingSupported(Object *obedit) } #endif +static bool snap_use_backface_culling(const TransInfo *t) +{ + BLI_assert(t->spacetype == SPACE_VIEW3D); + View3D *v3d = t->view; + if ((v3d->shading.type == OB_SOLID) && (v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)) { + return true; + } + if (v3d->shading.type == OB_RENDER && + (t->scene->display.shading.flag & V3D_SHADING_BACKFACE_CULLING) && + BKE_scene_uses_blender_workbench(t->scene)) { + return true; + } + if (t->settings->snap_flag & SCE_SNAP_BACKFACE_CULLING) { + return true; + } + return false; +} + bool validSnap(const TransInfo *t) { return (t->tsnap.status & (POINT_INIT | TARGET_INIT)) == (POINT_INIT | TARGET_INIT) || @@ -315,8 +334,7 @@ void applyProject(TransInfo *t) .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, .use_occlusion_test = false, - .use_backface_culling = (t->scene->toolsettings->snap_flag & - SCE_SNAP_BACKFACE_CULLING) != 0, + .use_backface_culling = t->tsnap.use_backface_culling, }, mval_fl, NULL, @@ -601,6 +619,7 @@ static void initSnappingMode(TransInfo *t) if (t->spacetype == SPACE_VIEW3D) { if (t->tsnap.object_context == NULL) { + t->tsnap.use_backface_culling = snap_use_backface_culling(t); t->tsnap.object_context = ED_transform_snap_object_context_create_view3d( t->scene, 0, t->region, t->view); @@ -1120,13 +1139,12 @@ short snapObjectsTransform( return ED_transform_snap_object_project_view3d_ex( t->tsnap.object_context, t->depsgraph, - t->scene->toolsettings->snap_mode, + t->settings->snap_mode, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, - .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE, - .use_backface_culling = (t->scene->toolsettings->snap_flag & - SCE_SNAP_BACKFACE_CULLING) != 0, + .use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE, + .use_backface_culling = t->tsnap.use_backface_culling, }, mval, target, diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 3c747a29361..384da6fb931 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -26,6 +26,8 @@ #include "BLI_string_utf8.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_scene.h" #include "BKE_unit.h" @@ -277,8 +279,12 @@ static bool editstr_insert_at_cursor(NumInput *n, const char *buf, const int buf return true; } -bool user_string_to_number( - bContext *C, const char *str, const UnitSettings *unit, int type, double *r_value) +bool user_string_to_number(bContext *C, + const char *str, + const UnitSettings *unit, + int type, + const char *error_prefix, + double *r_value) { #ifdef WITH_PYTHON double unit_scale = BKE_scene_unit_scale(unit, type, 1.0); @@ -288,10 +294,10 @@ bool user_string_to_number( 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, true, r_value); + return BPY_execute_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value); } - int success = BPY_execute_string_as_number(C, NULL, str, true, r_value); + int success = BPY_execute_string_as_number(C, NULL, str, error_prefix, r_value); *r_value *= bUnit_PreferredInputUnitScalar(unit, type); *r_value /= unit_scale; return success; @@ -573,7 +579,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) Scene *sce = CTX_data_scene(C); double val; - int success = user_string_to_number(C, n->str, &sce->unit, n->unit_type[idx], &val); + int success = user_string_to_number( + C, n->str, &sce->unit, n->unit_type[idx], IFACE_("Numeric input evaluation"), &val); if (success) { n->val[idx] = (float)val; diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index abbb0aa330c..5ccc6e29026 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -117,6 +117,7 @@ void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); void UV_OT_rip(struct wmOperatorType *ot); void UV_OT_stitch(struct wmOperatorType *ot); +void UV_OT_smart_project(struct wmOperatorType *ot); /* uvedit_path.c */ void UV_OT_shortest_path_pick(struct wmOperatorType *ot); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 532061e3dc1..956c094c19b 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -2094,6 +2094,7 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_reset); WM_operatortype_append(UV_OT_sphere_project); WM_operatortype_append(UV_OT_unwrap); + WM_operatortype_append(UV_OT_smart_project); WM_operatortype_append(UV_OT_reveal); WM_operatortype_append(UV_OT_hide); diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 6fcfb0e0bfc..2f30ef1caa6 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -35,7 +35,10 @@ #include "DNA_scene_types.h" #include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_linklist.h" #include "BLI_math.h" +#include "BLI_memarena.h" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BLI_uvproject.h" @@ -147,7 +150,13 @@ typedef struct UnwrapOptions { /** Connectivity based on UV coordinates instead of seams. */ bool topology_from_uvs; /** Only affect selected faces. */ - bool only_selected; + bool only_selected_faces; + /** + * Only affect selected UV's. + * \note Disable this for operations that don't run in the image-window. + * Unwrapping from the 3D view for example, where only 'only_selected_faces' should be used. + */ + bool only_selected_uvs; /** Fill holes to better preserve shape. */ bool fill_holes; /** Correct for mapped image texture aspect ratio. */ @@ -299,7 +308,7 @@ static ParamHandle *construct_param_handle(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || - (options->only_selected && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { + (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } @@ -307,10 +316,12 @@ static ParamHandle *construct_param_handle(const Scene *scene, bool is_loopsel = false; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - is_loopsel = true; - break; + if (options->only_selected_uvs && + (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { + continue; } + is_loopsel = true; + break; } if (is_loopsel == false) { continue; @@ -382,7 +393,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || - (options->only_selected && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { + (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } @@ -390,10 +401,12 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, bool is_loopsel = false; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - is_loopsel = true; - break; + if (options->only_selected_uvs && + (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) { + continue; } + is_loopsel = true; + break; } if (is_loopsel == false) { continue; @@ -571,7 +584,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, } else { if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN) || - (options->only_selected && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) { + (options->only_selected_faces && !BM_elem_flag_test(origFace, BM_ELEM_SELECT))) { continue; } } @@ -671,7 +684,8 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = true, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .correct_aspect = true, }; @@ -933,7 +947,8 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm) { const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = false, }; @@ -975,7 +990,8 @@ static int pack_islands_exec(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = true, }; @@ -1040,7 +1056,8 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) const UnwrapOptions options = { .topology_from_uvs = true, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = false, .correct_aspect = true, }; @@ -1114,7 +1131,8 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit) const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0, .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0, }; @@ -1471,18 +1489,21 @@ static void correct_uv_aspect(Object *ob, BMEditMesh *em) /** \name UV Map Clip & Correct * \{ */ -static void uv_map_clip_correct_properties(wmOperatorType *ot) +static void uv_map_clip_correct_properties_ex(wmOperatorType *ot, bool clip_to_bounds) { RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UVs taking image aspect ratio into account"); - RNA_def_boolean(ot->srna, - "clip_to_bounds", - 0, - "Clip to Bounds", - "Clip UV coordinates to bounds after unwrapping"); + /* Optional, since not all unwrapping types need to be clipped. */ + if (clip_to_bounds) { + RNA_def_boolean(ot->srna, + "clip_to_bounds", + 0, + "Clip to Bounds", + "Clip UV coordinates to bounds after unwrapping"); + } RNA_def_boolean(ot->srna, "scale_to_bounds", 0, @@ -1490,6 +1511,11 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) "Scale UV coordinates to bounds after unwrapping"); } +static void uv_map_clip_correct_properties(wmOperatorType *ot) +{ + uv_map_clip_correct_properties_ex(ot, true); +} + static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op) { BMFace *efa; @@ -1498,7 +1524,8 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper MLoopUV *luv; float dx, dy, min[2], max[2]; const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); - const bool clip_to_bounds = RNA_boolean_get(op->ptr, "clip_to_bounds"); + const bool clip_to_bounds = (RNA_struct_find_property(op->ptr, "clip_to_bounds") && + RNA_boolean_get(op->ptr, "clip_to_bounds")); const bool scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds"); INIT_MINMAX2(min, max); @@ -1635,7 +1662,8 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len if (scene->toolsettings->edge_mode_live_unwrap) { const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = false, + .only_selected_faces = false, + .only_selected_uvs = true, .fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0, .correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0, }; @@ -1670,7 +1698,8 @@ static int unwrap_exec(bContext *C, wmOperator *op) const UnwrapOptions options = { .topology_from_uvs = false, - .only_selected = true, + .only_selected_faces = true, + .only_selected_uvs = true, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), .correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"), }; @@ -1829,6 +1858,365 @@ void UV_OT_unwrap(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Smart UV Project Operator + * \{ */ + +/* Ignore all areas below this, as the UV's get zeroed. */ +static const float smart_uv_project_area_ignore = 1e-12f; + +typedef struct ThickFace { + float area; + BMFace *efa; +} ThickFace; + +static int smart_uv_project_thickface_area_cmp_fn(const void *tf_a_p, const void *tf_b_p) +{ + + const ThickFace *tf_a = (ThickFace *)tf_a_p; + const ThickFace *tf_b = (ThickFace *)tf_b_p; + + /* Ignore the area of small faces. + * Also, order checks so `!isfinite(...)` values are counted as zero area. */ + if (!((tf_a->area > smart_uv_project_area_ignore) || + (tf_b->area > smart_uv_project_area_ignore))) { + return 0; + } + + if (tf_a->area < tf_b->area) { + return 1; + } + else if (tf_a->area > tf_b->area) { + return -1; + } + else { + return 0; + } +} + +static uint smart_uv_project_calculate_project_normals(const ThickFace *thick_faces, + const uint thick_faces_len, + BMesh *bm, + const float project_angle_limit_half_cos, + const float project_angle_limit_cos, + const float area_weight, + float (**r_project_normal_array)[3]) +{ + if (UNLIKELY(thick_faces_len == 0)) { + *r_project_normal_array = NULL; + return 0; + } + + const float *project_normal = thick_faces[0].efa->no; + + const ThickFace **project_thick_faces = NULL; + BLI_array_declare(project_thick_faces); + + float(*project_normal_array)[3] = NULL; + BLI_array_declare(project_normal_array); + + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); + + while (true) { + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) { + continue; + } + + if (dot_v3v3(thick_faces[f_index].efa->no, project_normal) > project_angle_limit_half_cos) { + BLI_array_append(project_thick_faces, &thick_faces[f_index]); + BM_elem_flag_set(thick_faces[f_index].efa, BM_ELEM_TAG, true); + } + } + + float average_normal[3] = {0.0f, 0.0f, 0.0f}; + + if (area_weight <= 0.0f) { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + add_v3_v3(average_normal, tf->efa->no); + } + } + else if (area_weight >= 1.0f) { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + madd_v3_v3fl(average_normal, tf->efa->no, tf->area); + } + } + else { + for (int f_proj_index = 0; f_proj_index < BLI_array_len(project_thick_faces); + f_proj_index++) { + const ThickFace *tf = project_thick_faces[f_proj_index]; + const float area_blend = (tf->area * area_weight) + (1.0f - area_weight); + madd_v3_v3fl(average_normal, tf->efa->no, area_blend); + } + } + + /* Avoid NAN. */ + if (normalize_v3(average_normal) != 0.0f) { + float(*normal)[3] = BLI_array_append_ret(project_normal_array); + copy_v3_v3(*normal, average_normal); + } + + /* Find the most unique angle that points away from other normals. */ + float anble_best = 1.0f; + uint angle_best_index = 0; + + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + if (BM_elem_flag_test(thick_faces[f_index].efa, BM_ELEM_TAG)) { + continue; + } + + float angle_test = -1.0f; + for (int p_index = 0; p_index < BLI_array_len(project_normal_array); p_index++) { + angle_test = max_ff(angle_test, + dot_v3v3(project_normal_array[p_index], thick_faces[f_index].efa->no)); + } + + if (angle_test < anble_best) { + anble_best = angle_test; + angle_best_index = f_index; + } + } + + if (anble_best < project_angle_limit_cos) { + project_normal = thick_faces[angle_best_index].efa->no; + BLI_array_clear(project_thick_faces); + BLI_array_append(project_thick_faces, &thick_faces[angle_best_index]); + BM_elem_flag_enable(thick_faces[angle_best_index].efa, BM_ELEM_TAG); + } + else { + if (BLI_array_len(project_normal_array) >= 1) { + break; + } + } + } + + BLI_array_free(project_thick_faces); + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); + + *r_project_normal_array = project_normal_array; + return BLI_array_len(project_normal_array); +} + +static int smart_project_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + /* May be NULL. */ + View3D *v3d = CTX_wm_view3d(C); + + const float project_angle_limit = RNA_float_get(op->ptr, "angle_limit"); + const float island_margin = RNA_float_get(op->ptr, "island_margin"); + const float area_weight = RNA_float_get(op->ptr, "area_weight"); + + const float project_angle_limit_cos = cosf(project_angle_limit); + const float project_angle_limit_half_cos = cosf(project_angle_limit / 2); + + /* Memory arena for list links (cleared for each object). */ + MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, v3d, &objects_len); + + Object **objects_changed = MEM_mallocN(sizeof(*objects_changed) * objects_len, __func__); + uint object_changed_len = 0; + + BMFace *efa; + BMIter iter; + uint ob_index; + + for (ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + bool changed = false; + + const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + ThickFace *thick_faces = MEM_mallocN(sizeof(*thick_faces) * em->bm->totface, __func__); + + uint thick_faces_len = 0; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } + thick_faces[thick_faces_len].area = BM_face_calc_area(efa); + thick_faces[thick_faces_len].efa = efa; + thick_faces_len++; + } + + qsort(thick_faces, thick_faces_len, sizeof(ThickFace), smart_uv_project_thickface_area_cmp_fn); + + /* Remove all zero area faces. */ + while ((thick_faces_len > 0) && + !(thick_faces[thick_faces_len - 1].area > smart_uv_project_area_ignore)) { + + /* Zero UV's so they don't overlap with other faces being unwrapped. */ + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, thick_faces[thick_faces_len - 1].efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + zero_v2(luv->uv); + changed = true; + } + + thick_faces_len -= 1; + } + + float(*project_normal_array)[3] = NULL; + int project_normals_len = smart_uv_project_calculate_project_normals( + thick_faces, + thick_faces_len, + em->bm, + project_angle_limit_half_cos, + project_angle_limit_cos, + area_weight, + &project_normal_array); + + if (project_normals_len == 0) { + MEM_freeN(thick_faces); + BLI_assert(project_normal_array == NULL); + continue; + } + + /* After finding projection vectors, we find the uv positions. */ + LinkNode **thickface_project_groups = MEM_callocN( + sizeof(*thickface_project_groups) * project_normals_len, __func__); + + BLI_memarena_clear(arena); + + for (int f_index = thick_faces_len - 1; f_index >= 0; f_index--) { + const float *f_normal = thick_faces[f_index].efa->no; + + float angle_best = dot_v3v3(f_normal, project_normal_array[0]); + uint angle_best_index = 0; + + for (int p_index = 1; p_index < project_normals_len; p_index++) { + const float angle_test = dot_v3v3(f_normal, project_normal_array[p_index]); + if (angle_test > angle_best) { + angle_best = angle_test; + angle_best_index = p_index; + } + } + + BLI_linklist_prepend_arena( + &thickface_project_groups[angle_best_index], &thick_faces[f_index], arena); + } + + for (int p_index = 0; p_index < project_normals_len; p_index++) { + if (thickface_project_groups[p_index] == NULL) { + continue; + } + + float axis_mat[3][3]; + axis_dominant_v3_to_m3_negate(axis_mat, project_normal_array[p_index]); + + for (LinkNode *list = thickface_project_groups[p_index]; list; list = list->next) { + ThickFace *tf = list->link; + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, tf->efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + mul_v2_m3v3(luv->uv, axis_mat, l->v->co); + } + changed = true; + } + } + + MEM_freeN(thick_faces); + MEM_freeN(project_normal_array); + + /* No need to free the lists in 'thickface_project_groups' values as the 'arena' is used. */ + MEM_freeN(thickface_project_groups); + + if (changed) { + objects_changed[object_changed_len] = objects[ob_index]; + object_changed_len += 1; + } + } + + BLI_memarena_free(arena); + + MEM_freeN(objects); + + /* Pack islands & Stretch to UV bounds */ + if (object_changed_len > 0) { + + scene->toolsettings->uvcalc_margin = island_margin; + const UnwrapOptions options = { + .topology_from_uvs = true, + .only_selected_faces = true, + .only_selected_uvs = false, + .fill_holes = true, + .correct_aspect = false, + }; + + /* Depsgraph refresh functions are called here. */ + uvedit_pack_islands_multi(scene, objects_changed, object_changed_len, &options, true, false); + uv_map_clip_correct_multi(objects_changed, object_changed_len, op); + } + + MEM_freeN(objects_changed); + + return OPERATOR_FINISHED; +} + +void UV_OT_smart_project(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Smart UV Project"; + ot->idname = "UV_OT_smart_project"; + ot->description = "Projection unwraps the selected faces of mesh objects"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = smart_project_exec; + ot->poll = ED_operator_uvmap; + ot->invoke = WM_operator_props_popup_confirm; + + /* properties */ + prop = RNA_def_float_rotation(ot->srna, + "angle_limit", + 0, + NULL, + DEG2RADF(0.0f), + DEG2RADF(90.0f), + "Angle Limit", + "Lower for more projection groups, higher for less distortion", + DEG2RADF(0.0f), + DEG2RADF(89.0f)); + RNA_def_property_float_default(prop, DEG2RADF(66.0f)); + + RNA_def_float(ot->srna, + "island_margin", + 0.0f, + 0.0f, + 1.0f, + "Island Margin", + "Margin to reduce bleed from adjacent islands", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "area_weight", + 0.0f, + 0.0f, + 1.0f, + "Area Weight", + "Weight projections vector by faces with larger areas", + 0.0f, + 1.0f); + + uv_map_clip_correct_properties_ex(ot, false); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Project UV From View Operator * \{ */ diff --git a/source/blender/freestyle/FRS_freestyle.h b/source/blender/freestyle/FRS_freestyle.h index 876f40b211f..71d7dd03ef3 100644 --- a/source/blender/freestyle/FRS_freestyle.h +++ b/source/blender/freestyle/FRS_freestyle.h @@ -43,7 +43,7 @@ struct FreestyleGlobals { extern struct FreestyleGlobals g_freestyle; /* Rendering */ -void FRS_initialize(void); +void FRS_init(void); void FRS_set_context(struct bContext *C); int FRS_is_freestyle_enabled(struct ViewLayer *view_layer); void FRS_init_stroke_renderer(struct Render *re); diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index 51ac281e330..2b43e913b3d 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -100,7 +100,7 @@ static bCallbackFuncStore load_post_callback_funcstore = { // Initialization //======================================================= -void FRS_initialize() +void FRS_init() { if (freestyle_is_initialized) { return; diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index 33078e6ba6a..9796dcda964 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -264,7 +264,7 @@ static PyObject *Freestyle_evaluateCurveMappingF(PyObject * /*self*/, PyObject * return NULL; } cumap = (CurveMapping *)py_srna->ptr.data; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); /* disable extrapolation if enabled */ if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE)) { cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; diff --git a/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp b/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp index 2c226e330d7..3c2f022adb6 100644 --- a/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp +++ b/source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp @@ -108,7 +108,7 @@ static int FrsMaterial_init(BPy_FrsMaterial *self, PyObject *args, PyObject *kwd self->m = new FrsMaterial(*m); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O&O&O&O&O&fi", diff --git a/source/blender/freestyle/intern/python/BPy_Id.cpp b/source/blender/freestyle/intern/python/BPy_Id.cpp index 4f61ba847f6..eb0eb661e3d 100644 --- a/source/blender/freestyle/intern/python/BPy_Id.cpp +++ b/source/blender/freestyle/intern/python/BPy_Id.cpp @@ -75,7 +75,7 @@ static int Id_init(BPy_Id *self, PyObject *args, PyObject *kwds) if (PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_1, &Id_Type, &brother)) { self->id = new Id(*(((BPy_Id *)brother)->id)); } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "|ii", (char **)kwlist_2, &first, &second)) { self->id = new Id(first, second); } diff --git a/source/blender/freestyle/intern/python/BPy_Operators.cpp b/source/blender/freestyle/intern/python/BPy_Operators.cpp index 510e823ba55..56f95b8ecbb 100644 --- a/source/blender/freestyle/intern/python/BPy_Operators.cpp +++ b/source/blender/freestyle/intern/python/BPy_Operators.cpp @@ -365,8 +365,8 @@ static PyObject *Operators_sequential_split(BPy_Operators * /*self*/, return NULL; } } - else if (PyErr_Clear(), - (f = 0.0f), + else if ((void)PyErr_Clear(), + (void)(f = 0.0f), PyArg_ParseTupleAndKeywords( args, kwds, "O!|f", (char **)kwlist_2, &UnaryPredicate0D_Type, &obj1, &f)) { if (!((BPy_UnaryPredicate0D *)obj1)->up0D) { @@ -484,8 +484,8 @@ static PyObject *Operators_recursive_split(BPy_Operators * /*self*/, return NULL; } } - else if (PyErr_Clear(), - (f = 0.0f), + else if ((void)PyErr_Clear(), + (void)(f = 0.0f), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O!|f", diff --git a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp index 5f5407e82e3..214385f74ad 100644 --- a/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp +++ b/source/blender/freestyle/intern/python/BPy_StrokeAttribute.cpp @@ -110,7 +110,7 @@ static int StrokeAttribute_init(BPy_StrokeAttribute *self, PyObject *args, PyObj self->sa = new StrokeAttribute(*(((BPy_StrokeAttribute *)obj1)->sa)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!f", @@ -123,7 +123,7 @@ static int StrokeAttribute_init(BPy_StrokeAttribute *self, PyObject *args, PyObj self->sa = new StrokeAttribute( *(((BPy_StrokeAttribute *)obj1)->sa), *(((BPy_StrokeAttribute *)obj2)->sa), t); } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "ffffff", diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp index f59ae15ae01..2adcae13e6d 100644 --- a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp +++ b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp @@ -88,7 +88,7 @@ static int ViewShape_init(BPy_ViewShape *self, PyObject *args, PyObject *kwds) self->py_ss = ((BPy_ViewShape *)obj)->py_ss; } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &SShape_Type, &obj)) { BPy_SShape *py_ss = (BPy_SShape *)obj; self->vs = new ViewShape(py_ss->ss); diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp index 6af80c4bfd9..81dd79ff270 100644 --- a/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp +++ b/source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp @@ -95,7 +95,7 @@ static int CurvePoint_init(BPy_CurvePoint *self, PyObject *args, PyObject *kwds) self->cp = new CurvePoint(*(((BPy_CurvePoint *)obj1)->cp)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!f", @@ -107,7 +107,7 @@ static int CurvePoint_init(BPy_CurvePoint *self, PyObject *args, PyObject *kwds) &t2d)) { self->cp = new CurvePoint(((BPy_SVertex *)obj1)->sv, ((BPy_SVertex *)obj2)->sv, t2d); } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!f", diff --git a/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp index 71a87c2c01e..c01f1f17000 100644 --- a/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp +++ b/source/blender/freestyle/intern/python/Interface0D/BPy_SVertex.cpp @@ -72,7 +72,7 @@ static int SVertex_init(BPy_SVertex *self, PyObject *args, PyObject *kwds) self->sv = new SVertex(*(((BPy_SVertex *)obj)->sv)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "O&O!", (char **)kwlist_2, convert_v3, v, &Id_Type, &obj)) { Vec3r point_3d(v[0], v[1], v[2]); diff --git a/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp b/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp index b4c08714af0..519bd72db3b 100644 --- a/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp +++ b/source/blender/freestyle/intern/python/Interface0D/CurvePoint/BPy_StrokeVertex.cpp @@ -107,7 +107,7 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k self->sv = new StrokeVertex(*(((BPy_StrokeVertex *)obj1)->sv)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!f", @@ -129,7 +129,7 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k } self->sv = new StrokeVertex(sv1, sv2, t3d); } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "O!", (char **)kwlist_3, &CurvePoint_Type, &obj1)) { CurvePoint *cp = ((BPy_CurvePoint *)obj1)->cp; @@ -139,8 +139,8 @@ static int StrokeVertex_init(BPy_StrokeVertex *self, PyObject *args, PyObject *k } self->sv = new StrokeVertex(cp); } - else if (PyErr_Clear(), - (obj2 = 0), + else if ((void)PyErr_Clear(), + (void)(obj2 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!", diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp index 8a09e7722ea..187ab94360a 100644 --- a/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/BPy_FEdge.cpp @@ -81,7 +81,7 @@ static int FEdge_init(BPy_FEdge *self, PyObject *args, PyObject *kwds) self->fe = new FEdge(*(((BPy_FEdge *)obj1)->fe)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp index fd434f9c4ef..788dfa78992 100644 --- a/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/BPy_FrsCurve.cpp @@ -72,7 +72,7 @@ static int FrsCurve_init(BPy_FrsCurve *self, PyObject *args, PyObject *kwds) self->c = new Curve(*(((BPy_FrsCurve *)obj)->c)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &Id_Type, &obj)) { self->c = new Curve(*(((BPy_Id *)obj)->id)); } diff --git a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp index 3a25ddb0252..b31efe1f923 100644 --- a/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/BPy_Stroke.cpp @@ -157,7 +157,8 @@ static PyObject *Stroke_resample(BPy_Stroke *self, PyObject *args, PyObject *kwd return NULL; } } - else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) { + else if ((void)PyErr_Clear(), + PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) { if (self->s->Resample(f) < 0) { PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex interval) failed"); return NULL; diff --git a/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp b/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp index 285100193d3..9cdc344081e 100644 --- a/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/Curve/BPy_Chain.cpp @@ -71,7 +71,7 @@ static int Chain_init(BPy_Chain *self, PyObject *args, PyObject *kwds) self->c = new Chain(*(((BPy_Chain *)obj)->c)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist_2, &Id_Type, &obj)) { self->c = new Chain(*(((BPy_Id *)obj)->id)); } diff --git a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp index 725daa80b5e..5db75c84608 100644 --- a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSharp.cpp @@ -75,7 +75,7 @@ static int FEdgeSharp_init(BPy_FEdgeSharp *self, PyObject *args, PyObject *kwds) self->fes = new FEdgeSharp(*(((BPy_FEdgeSharp *)obj1)->fes)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", diff --git a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp index 65d9dcbe01f..3fb739b18db 100644 --- a/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp +++ b/source/blender/freestyle/intern/python/Interface1D/FEdge/BPy_FEdgeSmooth.cpp @@ -73,7 +73,7 @@ static int FEdgeSmooth_init(BPy_FEdgeSmooth *self, PyObject *args, PyObject *kwd self->fes = new FEdgeSmooth(*(((BPy_FEdgeSmooth *)obj1)->fes)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp index 74ae7809284..90e751333b9 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp @@ -82,8 +82,8 @@ static int AdjacencyIterator_init(BPy_AdjacencyIterator *self, PyObject *args, P self->at_start = ((BPy_AdjacencyIterator *)obj1)->at_start; } } - else if (PyErr_Clear(), - (obj2 = obj3 = 0), + else if ((void)PyErr_Clear(), + (void)(obj2 = obj3 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp index 164e1646934..1703fc2bddb 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainPredicateIterator.cpp @@ -114,8 +114,8 @@ static int ChainPredicateIterator_init(BPy_ChainPredicateIterator *self, Py_INCREF(self->upred); Py_INCREF(self->bpred); } - else if (PyErr_Clear(), - (obj3 = obj4 = obj5 = obj6 = 0), + else if ((void)PyErr_Clear(), + (void)(obj3 = obj4 = obj5 = obj6 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|O!O!O&O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp index 401959be0c0..d8ad82d667c 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainSilhouetteIterator.cpp @@ -91,8 +91,8 @@ static int ChainSilhouetteIterator_init(BPy_ChainSilhouetteIterator *self, args, kwds, "O!", (char **)kwlist_1, &ChainSilhouetteIterator_Type, &obj1)) { self->cs_it = new ChainSilhouetteIterator(*(((BPy_ChainSilhouetteIterator *)obj1)->cs_it)); } - else if (PyErr_Clear(), - (obj1 = obj2 = obj3 = 0), + else if ((void)PyErr_Clear(), + (void)(obj1 = obj2 = obj3 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "|O!O&O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp index b6d841c5b64..dbd6e8dd09d 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_ChainingIterator.cpp @@ -91,8 +91,8 @@ static int ChainingIterator___init__(BPy_ChainingIterator *self, PyObject *args, args, kwds, "O!", (char **)kwlist_1, &ChainingIterator_Type, &obj1)) { self->c_it = new ChainingIterator(*(((BPy_ChainingIterator *)obj1)->c_it)); } - else if (PyErr_Clear(), - (obj1 = obj2 = obj3 = obj4 = 0), + else if ((void)PyErr_Clear(), + (void)(obj1 = obj2 = obj3 = obj4 = 0), PyArg_ParseTupleAndKeywords(args, kwds, "|O!O!O&O!", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp index 6ea61a060cb..6c496b0308b 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_CurvePointIterator.cpp @@ -75,7 +75,8 @@ static int CurvePointIterator_init(BPy_CurvePointIterator *self, PyObject *args, *(((BPy_CurvePointIterator *)brother)->cp_it)); } } - else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &step)) { + else if ((void)PyErr_Clear(), + PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &step)) { self->cp_it = new CurveInternal::CurvePointIterator(step); } else { diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp index 0dbef9f325c..f0d6acf461b 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_Interface0DIterator.cpp @@ -81,14 +81,14 @@ static int Interface0DIterator_init(BPy_Interface0DIterator *self, PyObject *arg self->at_start = true; self->reversed = false; } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "O!", (char **)kwlist_2, &Interface1D_Type, &inter)) { self->if0D_it = new Interface0DIterator(((BPy_Interface1D *)inter)->if1D->verticesBegin()); self->at_start = true; self->reversed = false; } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "O!", (char **)kwlist_3, &Interface0DIterator_Type, &brother)) { self->if0D_it = new Interface0DIterator(*(((BPy_Interface0DIterator *)brother)->if0D_it)); diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp index dd738b97473..4a5927ff6eb 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_SVertexIterator.cpp @@ -82,7 +82,7 @@ static int SVertexIterator_init(BPy_SVertexIterator *self, PyObject *args, PyObj self->sv_it = new ViewEdgeInternal::SVertexIterator(*(((BPy_SVertexIterator *)obj1)->sv_it)); } } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O!O!f", diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp index 84f57f1fe31..df03ecba96f 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_StrokeVertexIterator.cpp @@ -74,7 +74,7 @@ static int StrokeVertexIterator_init(BPy_StrokeVertexIterator *self, self->at_start = ((BPy_StrokeVertexIterator *)brother)->at_start; } - else if (PyErr_Clear(), + else if ((void)PyErr_Clear(), PyArg_ParseTupleAndKeywords( args, kwds, "|O!", (char **)kwlist_2, &Stroke_Type, &stroke)) { if (!stroke) { diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp index c8a978784a4..3d0ed5d5a4d 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_ViewEdgeIterator.cpp @@ -78,8 +78,8 @@ static int ViewEdgeIterator_init(BPy_ViewEdgeIterator *self, PyObject *args, PyO args, kwds, "O!", (char **)kwlist_1, &ViewEdgeIterator_Type, &obj1)) { self->ve_it = new ViewEdgeInternal::ViewEdgeIterator(*(((BPy_ViewEdgeIterator *)obj1)->ve_it)); } - else if (PyErr_Clear(), - (obj1 = obj2 = 0), + else if ((void)PyErr_Clear(), + (void)(obj1 = obj2 = 0), PyArg_ParseTupleAndKeywords( args, kwds, "|O&O!", (char **)kwlist_2, check_begin, &obj1, &PyBool_Type, &obj2)) { ViewEdge *begin = (!obj1 || obj1 == Py_None) ? NULL : ((BPy_ViewEdge *)obj1)->ve; diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 2686275e898..ad29dbe6668 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -57,3 +57,20 @@ set(LIB ) blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_GTESTS) + set(TEST_SRC + tests/FN_array_spans_test.cc + tests/FN_attributes_ref_test.cc + tests/FN_cpp_type_test.cc + tests/FN_generic_vector_array_test.cc + tests/FN_multi_function_network_test.cc + tests/FN_multi_function_test.cc + tests/FN_spans_test.cc + ) + set (TEST_LIB + bf_functions + ) + include(GTestTesting) + blender_add_test_lib(bf_functions_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") +endif() diff --git a/source/blender/functions/FN_array_spans.hh b/source/blender/functions/FN_array_spans.hh index c362fef3630..5f976711e06 100644 --- a/source/blender/functions/FN_array_spans.hh +++ b/source/blender/functions/FN_array_spans.hh @@ -158,7 +158,7 @@ class GVArraySpan : public VArraySpanBase<void> { this->type_ = &array.type(); this->virtual_size_ = virtual_size; this->category_ = VArraySpanCategory::SingleArray; - this->data_.single_array.start = array.buffer(); + this->data_.single_array.start = array.data(); this->data_.single_array.size = array.size(); } diff --git a/source/blender/functions/FN_attributes_ref.hh b/source/blender/functions/FN_attributes_ref.hh index ed14676731e..c694f11b7a7 100644 --- a/source/blender/functions/FN_attributes_ref.hh +++ b/source/blender/functions/FN_attributes_ref.hh @@ -50,12 +50,12 @@ class AttributesInfoBuilder : NonCopyable, NonMovable { AttributesInfoBuilder() = default; ~AttributesInfoBuilder(); - template<typename T> void add(StringRef name, const T &default_value) + template<typename T> bool add(StringRef name, const T &default_value) { - this->add(name, CPPType::get<T>(), (const void *)&default_value); + return this->add(name, CPPType::get<T>(), (const void *)&default_value); } - void add(StringRef name, const CPPType &type, const void *default_value = nullptr); + bool add(StringRef name, const CPPType &type, const void *default_value = nullptr); }; /** diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 594890e353a..531a9073784 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -371,7 +371,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_initialized_n(const void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -380,7 +380,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_initialized_indices(const void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -406,7 +406,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_uninitialized_n(const void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -415,7 +415,7 @@ class CPPType : NonCopyable, NonMovable { void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -441,7 +441,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_initialized_n(void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -450,7 +450,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_initialized_indices(void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); @@ -476,7 +476,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_uninitialized_n(void *src, void *dst, int64_t n) const { - BLI_assert(src != dst); + BLI_assert(n == 0 || src != dst); BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); @@ -485,7 +485,7 @@ class CPPType : NonCopyable, NonMovable { void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask mask) const { - BLI_assert(src != dst); + BLI_assert(mask.size() == 0 || src != dst); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 93d7b47af83..ac4dca33cf0 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -50,52 +50,56 @@ class MFParamsBuilder { MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size); - template<typename T> void add_readonly_single_input(const T *value) + template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(GVSpan::FromSingle(CPPType::get<T>(), value, min_array_size_)); + this->add_readonly_single_input(GVSpan::FromSingle(CPPType::get<T>(), value, min_array_size_), + expected_name); } - void add_readonly_single_input(GVSpan ref) + void add_readonly_single_input(GVSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForSingleInput(ref.type())); + this->assert_current_param_type(MFParamType::ForSingleInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); virtual_spans_.append(ref); } - void add_readonly_vector_input(GVArraySpan ref) + void add_readonly_vector_input(GVArraySpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForVectorInput(ref.type())); + this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); virtual_array_spans_.append(ref); } - template<typename T> void add_uninitialized_single_output(T *value) + template<typename T> void add_uninitialized_single_output(T *value, StringRef expected_name = "") { - this->add_uninitialized_single_output(GMutableSpan(CPPType::get<T>(), value, 1)); + this->add_uninitialized_single_output(GMutableSpan(CPPType::get<T>(), value, 1), + expected_name); } - void add_uninitialized_single_output(GMutableSpan ref) + void add_uninitialized_single_output(GMutableSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type())); + this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); mutable_spans_.append(ref); } - void add_vector_output(GVectorArray &vector_array) + void add_vector_output(GVectorArray &vector_array, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type())); + this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type()), + expected_name); BLI_assert(vector_array.size() >= min_array_size_); vector_arrays_.append(&vector_array); } - void add_single_mutable(GMutableSpan ref) + void add_single_mutable(GMutableSpan ref, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type())); + this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); mutable_spans_.append(ref); } - void add_vector_mutable(GVectorArray &vector_array) + void add_vector_mutable(GVectorArray &vector_array, StringRef expected_name = "") { - this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type())); + this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type()), + expected_name); BLI_assert(vector_array.size() >= min_array_size_); vector_arrays_.append(&vector_array); } @@ -119,11 +123,17 @@ class MFParamsBuilder { } private: - void assert_current_param_type(MFParamType param_type) + void assert_current_param_type(MFParamType param_type, StringRef expected_name = "") { - UNUSED_VARS_NDEBUG(param_type); + UNUSED_VARS_NDEBUG(param_type, expected_name); #ifdef DEBUG int param_index = this->current_param_index(); + + if (expected_name != "") { + StringRef actual_name = signature_->param_names[param_index]; + BLI_assert(actual_name == expected_name); + } + MFParamType expected_type = signature_->param_types[param_index]; BLI_assert(expected_type == param_type); #endif diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index ba79dddff16..3c09d1c961c 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -32,10 +32,9 @@ namespace blender::fn { struct MFSignature { std::string function_name; - /* Use RawAllocator so that a MultiFunction can have static storage duration. */ - RawVector<std::string> param_names; - RawVector<MFParamType> param_types; - RawVector<int> param_data_indices; + Vector<std::string> param_names; + Vector<MFParamType> param_types; + Vector<int> param_data_indices; bool depends_on_context = false; int data_index(int param_index) const diff --git a/source/blender/functions/FN_spans.hh b/source/blender/functions/FN_spans.hh index d8b381199cc..62fa7c8ed4b 100644 --- a/source/blender/functions/FN_spans.hh +++ b/source/blender/functions/FN_spans.hh @@ -51,12 +51,12 @@ namespace blender::fn { class GSpan { private: const CPPType *type_; - const void *buffer_; + const void *data_; int64_t size_; public: GSpan(const CPPType &type, const void *buffer, int64_t size) - : type_(&type), buffer_(buffer), size_(size) + : type_(&type), data_(buffer), size_(size) { BLI_assert(size >= 0); BLI_assert(buffer != nullptr || size == 0); @@ -87,21 +87,21 @@ class GSpan { return size_; } - const void *buffer() const + const void *data() const { - return buffer_; + return data_; } const void *operator[](int64_t index) const { BLI_assert(index < size_); - return POINTER_OFFSET(buffer_, type_->size() * index); + return POINTER_OFFSET(data_, type_->size() * index); } template<typename T> Span<T> typed() const { BLI_assert(type_->is<T>()); - return Span<T>((const T *)buffer_, size_); + return Span<T>((const T *)data_, size_); } }; @@ -112,12 +112,12 @@ class GSpan { class GMutableSpan { private: const CPPType *type_; - void *buffer_; + void *data_; int64_t size_; public: GMutableSpan(const CPPType &type, void *buffer, int64_t size) - : type_(&type), buffer_(buffer), size_(size) + : type_(&type), data_(buffer), size_(size) { BLI_assert(size >= 0); BLI_assert(buffer != nullptr || size == 0); @@ -136,7 +136,7 @@ class GMutableSpan { operator GSpan() const { - return GSpan(*type_, buffer_, size_); + return GSpan(*type_, data_, size_); } const CPPType &type() const @@ -154,21 +154,21 @@ class GMutableSpan { return size_; } - void *buffer() + void *data() { - return buffer_; + return data_; } void *operator[](int64_t index) { BLI_assert(index < size_); - return POINTER_OFFSET(buffer_, type_->size() * index); + return POINTER_OFFSET(data_, type_->size() * index); } template<typename T> MutableSpan<T> typed() { BLI_assert(type_->is<T>()); - return MutableSpan<T>((T *)buffer_, size_); + return MutableSpan<T>((T *)data_, size_); } }; @@ -209,6 +209,20 @@ template<typename T> struct VSpanBase { return false; } + bool is_full_array() const + { + switch (category_) { + case VSpanCategory::Single: + return virtual_size_ == 1; + case VSpanCategory::FullArray: + return true; + case VSpanCategory::FullPointerArray: + return virtual_size_ <= 1; + } + BLI_assert(false); + return false; + } + bool is_empty() const { return this->virtual_size_ == 0; @@ -285,6 +299,22 @@ template<typename T> class VSpan : public VSpanBase<T> { BLI_assert(false); return *this->data_.single.data; } + + const T &as_single_element() const + { + BLI_assert(this->is_single_element()); + return (*this)[0]; + } + + Span<T> as_full_array() const + { + BLI_assert(this->is_full_array()); + if (this->virtual_size_ == 0) { + return Span<T>(); + } + const T *data = &(*this)[0]; + return Span<T>(data, this->virtual_size_); + } }; /** @@ -311,7 +341,7 @@ class GVSpan : public VSpanBase<void> { this->type_ = &values.type(); this->virtual_size_ = values.size(); this->category_ = VSpanCategory::FullArray; - this->data_.full_array.data = values.buffer(); + this->data_.full_array.data = values.data(); } GVSpan(GMutableSpan values) : GVSpan(GSpan(values)) @@ -395,6 +425,16 @@ class GVSpan : public VSpanBase<void> { return (*this)[0]; } + GSpan as_full_array() const + { + BLI_assert(this->is_full_array()); + if (this->virtual_size_ == 0) { + return GSpan(*this->type_); + } + const void *data = (*this)[0]; + return GSpan(*this->type_, data, this->virtual_size_); + } + void materialize_to_uninitialized(void *dst) const { this->materialize_to_uninitialized(IndexRange(virtual_size_), dst); diff --git a/source/blender/functions/intern/attributes_ref.cc b/source/blender/functions/intern/attributes_ref.cc index 7bfcc69671a..8f7f41be079 100644 --- a/source/blender/functions/intern/attributes_ref.cc +++ b/source/blender/functions/intern/attributes_ref.cc @@ -25,8 +25,12 @@ AttributesInfoBuilder::~AttributesInfoBuilder() } } -void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value) +bool AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void *default_value) { + if (name.size() == 0) { + std::cout << "Warning: Tried to add an attribute with empty name.\n"; + return false; + } if (names_.add_as(name)) { types_.append(&type); @@ -36,10 +40,15 @@ void AttributesInfoBuilder::add(StringRef name, const CPPType &type, const void void *dst = allocator_.allocate(type.size(), type.alignment()); type.copy_to_uninitialized(default_value, dst); defaults_.append(dst); + return true; } else { - /* The same name can be added more than once as long as the type is always the same. */ - BLI_assert(types_[names_.index_of_as(name)] == &type); + const CPPType &stored_type = *types_[names_.index_of_as(name)]; + if (stored_type != type) { + std::cout << "Warning: Tried to add an attribute twice with different types (" << name + << ": " << stored_type.name() << ", " << type.name() << ").\n"; + } + return false; } } diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc index 06084247e66..c9e8b88ba03 100644 --- a/source/blender/functions/intern/multi_function_builder.cc +++ b/source/blender/functions/intern/multi_function_builder.cc @@ -34,7 +34,7 @@ void CustomMF_GenericConstant::call(IndexMask mask, MFContext UNUSED(context)) const { GMutableSpan output = params.uninitialized_single_output(0); - type_.fill_uninitialized_indices(value_, output.buffer(), mask); + type_.fill_uninitialized_indices(value_, output.data(), mask); } uint64_t CustomMF_GenericConstant::hash() const @@ -111,7 +111,7 @@ void CustomMF_DefaultOutput::call(IndexMask mask, MFParams params, MFContext UNU if (param_type.data_type().is_single()) { GMutableSpan span = params.uninitialized_single_output(param_index); const CPPType &type = span.type(); - type.fill_uninitialized_indices(type.default_value(), span.buffer(), mask); + type.fill_uninitialized_indices(type.default_value(), span.data(), mask); } } } diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index 58577e31c42..25e983d2eeb 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -390,7 +390,7 @@ BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs( case MFDataType::Single: { GVSpan values = storage.get_single_input__full(*socket); GMutableSpan output_values = params.uninitialized_single_output(param_index); - values.materialize_to_uninitialized(storage.mask(), output_values.buffer()); + values.materialize_to_uninitialized(storage.mask(), output_values.data()); break; } case MFDataType::Vector: { @@ -524,11 +524,11 @@ MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage() GMutableSpan span = value->span; const CPPType &type = span.type(); if (value->is_single_allocated) { - type.destruct(span.buffer()); + type.destruct(span.data()); } else { - type.destruct_indices(span.buffer(), mask_); - MEM_freeN(span.buffer()); + type.destruct_indices(span.data(), mask_); + MEM_freeN(span.data()); } } else if (any_value->type == ValueType::OwnVector) { @@ -634,11 +634,11 @@ void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket GMutableSpan span = value->span; const CPPType &type = span.type(); if (value->is_single_allocated) { - type.destruct(span.buffer()); + type.destruct(span.data()); } else { - type.destruct_indices(span.buffer(), mask_); - MEM_freeN(span.buffer()); + type.destruct_indices(span.data(), mask_); + MEM_freeN(span.data()); } value_per_output_id_[origin.id()] = nullptr; } @@ -791,7 +791,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS BLI_assert(to_any_value->type == ValueType::OutputSingle); GMutableSpan span = ((OutputSingleValue *)to_any_value)->span; GVSpan virtual_span = this->get_single_input__full(input); - virtual_span.materialize_to_uninitialized(mask_, span.buffer()); + virtual_span.materialize_to_uninitialized(mask_, span.data()); return span; } @@ -808,7 +808,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS GVSpan virtual_span = this->get_single_input__full(input); void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); GMutableSpan new_array_ref(type, new_buffer, min_array_size_); - virtual_span.materialize_to_uninitialized(mask_, new_array_ref.buffer()); + virtual_span.materialize_to_uninitialized(mask_, new_array_ref.data()); OwnSingleValue *new_value = allocator_.construct<OwnSingleValue>( new_array_ref, to.targets().size(), false); @@ -953,7 +953,7 @@ GVSpan MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &s if (any_value->type == ValueType::OwnSingle) { OwnSingleValue *value = (OwnSingleValue *)any_value; if (value->is_single_allocated) { - return GVSpan::FromSingle(value->span.type(), value->span.buffer(), min_array_size_); + return GVSpan::FromSingle(value->span.type(), value->span.data(), min_array_size_); } else { return value->span; diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc index f1e047f01a1..af1e77aa355 100644 --- a/source/blender/functions/intern/multi_function_network_optimization.cc +++ b/source/blender/functions/intern/multi_function_network_optimization.cc @@ -28,6 +28,7 @@ #include "BLI_disjoint_set.hh" #include "BLI_ghash.h" #include "BLI_map.hh" +#include "BLI_multi_value_map.hh" #include "BLI_rand.h" #include "BLI_stack.hh" @@ -265,7 +266,7 @@ static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction & case MFDataType::Single: { const CPPType &cpp_type = data_type.single_type(); GMutableSpan array = params.computed_array(param_index); - void *buffer = array.buffer(); + void *buffer = array.data(); resources.add(buffer, array.type().destruct_cb(), AT); constant_fn = &resources.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer); @@ -403,15 +404,15 @@ static Array<uint64_t> compute_node_hashes(MFNetwork &network) return node_hashes; } -static Map<uint64_t, Vector<MFNode *, 1>> group_nodes_by_hash(MFNetwork &network, - Span<uint64_t> node_hashes) +static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network, + Span<uint64_t> node_hashes) { - Map<uint64_t, Vector<MFNode *, 1>> nodes_by_hash; + MultiValueMap<uint64_t, MFNode *> nodes_by_hash; for (int id : IndexRange(network.node_id_amount())) { MFNode *node = network.node_or_null_by_id(id); if (node != nullptr) { uint64_t node_hash = node_hashes[id]; - nodes_by_hash.lookup_or_add_default(node_hash).append(node); + nodes_by_hash.add(node_hash, node); } } return nodes_by_hash; @@ -456,7 +457,7 @@ static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const } static void relink_duplicate_nodes(MFNetwork &network, - Map<uint64_t, Vector<MFNode *, 1>> &nodes_by_hash) + MultiValueMap<uint64_t, MFNode *> &nodes_by_hash) { DisjointSet same_node_cache{network.node_id_amount()}; @@ -494,7 +495,7 @@ static void relink_duplicate_nodes(MFNetwork &network, void common_subnetwork_elimination(MFNetwork &network) { Array<uint64_t> node_hashes = compute_node_hashes(network); - Map<uint64_t, Vector<MFNode *, 1>> nodes_by_hash = group_nodes_by_hash(network, node_hashes); + MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes); relink_duplicate_nodes(network, nodes_by_hash); } diff --git a/source/blender/functions/tests/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc new file mode 100644 index 00000000000..9a632b58be8 --- /dev/null +++ b/source/blender/functions/tests/FN_array_spans_test.cc @@ -0,0 +1,132 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_array_spans.hh" +#include "FN_generic_vector_array.hh" + +#include "BLI_array.hh" + +namespace blender::fn::tests { + +TEST(virtual_array_span, EmptyConstructor) +{ + VArraySpan<int> span; + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); + + GVArraySpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 0); +} + +TEST(virtual_array_span, SingleArrayConstructor) +{ + std::array<int, 4> values = {3, 4, 5, 6}; + VArraySpan<int> span{Span<int>(values), 3}; + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0].size(), 4); + EXPECT_EQ(span[1].size(), 4); + EXPECT_EQ(span[2].size(), 4); + EXPECT_EQ(span[0][0], 3); + EXPECT_EQ(span[0][1], 4); + EXPECT_EQ(span[0][2], 5); + EXPECT_EQ(span[0][3], 6); + EXPECT_EQ(span[1][3], 6); + EXPECT_EQ(span[2][1], 4); + + GVArraySpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0].size(), 4); + EXPECT_EQ(converted[1].size(), 4); + EXPECT_EQ(converted[1][2], &values[2]); +} + +TEST(virtual_array_span, MultipleArrayConstructor) +{ + std::array<int, 4> values0 = {1, 2, 3, 4}; + 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()}; + + VArraySpan<int> span{starts, sizes}; + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0].size(), 4); + EXPECT_EQ(span[1].size(), 2); + EXPECT_EQ(span[2].size(), 1); + EXPECT_EQ(&span[0][0], values0.data()); + EXPECT_EQ(&span[1][0], values1.data()); + EXPECT_EQ(&span[2][0], values2.data()); + EXPECT_EQ(span[2][0], 8); + EXPECT_EQ(span[1][1], 7); + + GVArraySpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0].size(), 4); + EXPECT_EQ(converted[1].size(), 2); + EXPECT_EQ(converted[2].size(), 1); + EXPECT_EQ(converted[0][0], values0.data()); + EXPECT_EQ(converted[1][1], values1.data() + 1); +} + +TEST(generic_virtual_array_span, TypeConstructor) +{ + GVArraySpan span{CPPType::get<int32_t>()}; + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); + + VArraySpan converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 0); +} + +TEST(generic_virtual_array_span, GSpanConstructor) +{ + std::array<std::string, 3> values = {"hello", "world", "test"}; + GVArraySpan span{GSpan(CPPType::get<std::string>(), values.data(), 3), 5}; + EXPECT_EQ(span.size(), 5); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0][0], values.data()); + EXPECT_EQ(span[1][0], values.data()); + EXPECT_EQ(span[4][0], values.data()); + EXPECT_EQ(span[0].size(), 3); + EXPECT_EQ(span[2].size(), 3); + EXPECT_EQ(*(std::string *)span[3][1], "world"); + + VArraySpan converted = span.typed<std::string>(); + EXPECT_EQ(converted.size(), 5); + EXPECT_EQ(converted[0][0], "hello"); + EXPECT_EQ(converted[1][0], "hello"); + EXPECT_EQ(converted[4][0], "hello"); + EXPECT_EQ(converted[0].size(), 3); + EXPECT_EQ(converted[2].size(), 3); +} + +TEST(generic_virtual_array_span, IsSingleArray1) +{ + Array<int> values = {5, 6, 7}; + GVArraySpan span{GSpan(values.as_span()), 4}; + EXPECT_TRUE(span.is_single_array()); + + VArraySpan converted = span.typed<int>(); + EXPECT_TRUE(converted.is_single_array()); +} + +TEST(generic_virtual_array_span, IsSingleArray2) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 3}; + GVectorArrayRef<int> vectors_ref = vectors; + vectors_ref.append(1, 4); + + GVArraySpan span = vectors; + EXPECT_FALSE(span.is_single_array()); + + VArraySpan converted = span.typed<int>(); + EXPECT_FALSE(converted.is_single_array()); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_attributes_ref_test.cc b/source/blender/functions/tests/FN_attributes_ref_test.cc new file mode 100644 index 00000000000..3a5e4743c1e --- /dev/null +++ b/source/blender/functions/tests/FN_attributes_ref_test.cc @@ -0,0 +1,97 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_float3.hh" +#include "FN_attributes_ref.hh" + +#include "testing/testing.h" + +namespace blender::fn::tests { + +TEST(attributes_info, BuildEmpty) +{ + AttributesInfoBuilder info_builder; + AttributesInfo info{info_builder}; + + EXPECT_EQ(info.size(), 0); +} + +TEST(attributes_info, AddSameNameTwice) +{ + AttributesInfoBuilder info_builder; + info_builder.add<int>("A", 4); + info_builder.add<int>("A", 5); + AttributesInfo info{info_builder}; + EXPECT_EQ(info.size(), 1); + EXPECT_TRUE(info.has_attribute("A", CPPType::get<int>())); + EXPECT_FALSE(info.has_attribute("B", CPPType::get<int>())); + EXPECT_FALSE(info.has_attribute("A", CPPType::get<float>())); + EXPECT_EQ(info.default_of<int>("A"), 4); + EXPECT_EQ(info.name_of(0), "A"); + EXPECT_EQ(info.index_range().start(), 0); + EXPECT_EQ(info.index_range().one_after_last(), 1); +} + +TEST(attributes_info, BuildWithDefaultString) +{ + AttributesInfoBuilder info_builder; + info_builder.add("A", CPPType::get<std::string>()); + AttributesInfo info{info_builder}; + EXPECT_EQ(info.default_of<std::string>("A"), ""); +} + +TEST(attributes_info, BuildWithGivenDefault) +{ + AttributesInfoBuilder info_builder; + info_builder.add<std::string>("A", "hello world"); + AttributesInfo info{info_builder}; + const void *default_value = info.default_of("A"); + EXPECT_EQ(*(const std::string *)default_value, "hello world"); + EXPECT_EQ(info.type_of("A"), CPPType::get<std::string>()); +} + +TEST(mutable_attributes_ref, ComplexTest) +{ + AttributesInfoBuilder info_builder; + info_builder.add<float3>("Position", {0, 0, 10}); + info_builder.add<uint>("ID", 0); + info_builder.add<float>("Size", 0.5f); + info_builder.add<std::string>("Name", "<no name>"); + AttributesInfo info{info_builder}; + + int amount = 5; + Array<float3> positions(amount); + Array<uint> ids(amount, 0); + Array<float> sizes(amount); + Array<std::string> names(amount); + + Array<void *> buffers = {positions.data(), ids.data(), sizes.data(), names.data()}; + MutableAttributesRef attributes{info, buffers, IndexRange(1, 3)}; + EXPECT_EQ(attributes.size(), 3); + EXPECT_EQ(attributes.info().size(), 4); + EXPECT_EQ(attributes.get("Position").data(), positions.data() + 1); + EXPECT_EQ(attributes.get("ID").data(), ids.data() + 1); + EXPECT_EQ(attributes.get("Size").data(), sizes.data() + 1); + EXPECT_EQ(attributes.get("Name").data(), names.data() + 1); + + EXPECT_EQ(attributes.get("ID").size(), 3); + EXPECT_EQ(attributes.get<uint>("ID").size(), 3); + + EXPECT_EQ(ids[2], 0); + MutableSpan<uint> ids_span = attributes.get<uint>("ID"); + ids_span[1] = 42; + EXPECT_EQ(ids[2], 42); + + EXPECT_FALSE(attributes.try_get<int>("not existant").has_value()); + EXPECT_FALSE(attributes.try_get<int>("Position").has_value()); + EXPECT_TRUE(attributes.try_get<float3>("Position").has_value()); + EXPECT_FALSE(attributes.try_get("not existant", CPPType::get<int>()).has_value()); + EXPECT_FALSE(attributes.try_get("Position", CPPType::get<int>()).has_value()); + EXPECT_TRUE(attributes.try_get("Position", CPPType::get<float3>()).has_value()); + + MutableAttributesRef sliced = attributes.slice(IndexRange(1, 2)); + EXPECT_EQ(sliced.size(), 2); + sliced.get<uint>("ID")[0] = 100; + EXPECT_EQ(ids[2], 100); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_cpp_type_test.cc b/source/blender/functions/tests/FN_cpp_type_test.cc new file mode 100644 index 00000000000..29368b251cc --- /dev/null +++ b/source/blender/functions/tests/FN_cpp_type_test.cc @@ -0,0 +1,325 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_cpp_type.hh" + +namespace blender::fn::tests { + +static const int default_constructed_value = 1; +static const int copy_constructed_value = 2; +static const int move_constructed_value = 3; +static const int copy_constructed_from_value = 4; +static const int move_constructed_from_value = 5; +static const int copy_assigned_value = 6; +static const int copy_assigned_from_value = 7; +static const int move_assigned_value = 8; +static const int move_assigned_from_value = 9; +static const int destructed_value = 10; + +struct TestType { + mutable volatile int value; + + TestType() + { + value = default_constructed_value; + } + + ~TestType() + { + value = destructed_value; + } + + TestType(const TestType &other) + { + value = copy_constructed_value; + other.value = copy_constructed_from_value; + } + + TestType(TestType &&other) noexcept + { + value = move_constructed_value; + other.value = move_constructed_from_value; + } + + TestType &operator=(const TestType &other) + { + value = copy_assigned_value; + other.value = copy_assigned_from_value; + return *this; + } + + TestType &operator=(TestType &&other) noexcept + { + value = move_assigned_value; + other.value = move_assigned_from_value; + return *this; + } + + friend std::ostream &operator<<(std::ostream &stream, const TestType &value) + { + stream << value.value; + return stream; + } + + friend bool operator==(const TestType &UNUSED(a), const TestType &UNUSED(b)) + { + return false; + } + + uint64_t hash() const + { + return 0; + } +}; + +} // namespace blender::fn::tests + +MAKE_CPP_TYPE(TestType, blender::fn::tests::TestType) + +namespace blender::fn::tests { + +const CPPType &CPPType_TestType = CPPType::get<TestType>(); + +TEST(cpp_type, Size) +{ + EXPECT_EQ(CPPType_TestType.size(), sizeof(TestType)); +} + +TEST(cpp_type, Alignment) +{ + EXPECT_EQ(CPPType_TestType.alignment(), alignof(TestType)); +} + +TEST(cpp_type, Is) +{ + EXPECT_TRUE(CPPType_TestType.is<TestType>()); + EXPECT_FALSE(CPPType_TestType.is<int>()); +} + +TEST(cpp_type, DefaultConstruction) +{ + int buffer[10] = {0}; + CPPType_TestType.construct_default((void *)buffer); + EXPECT_EQ(buffer[0], default_constructed_value); + EXPECT_EQ(buffer[1], 0); + CPPType_TestType.construct_default_n((void *)buffer, 3); + EXPECT_EQ(buffer[0], default_constructed_value); + EXPECT_EQ(buffer[1], default_constructed_value); + EXPECT_EQ(buffer[2], default_constructed_value); + EXPECT_EQ(buffer[3], 0); + CPPType_TestType.construct_default_indices((void *)buffer, {2, 5, 7}); + EXPECT_EQ(buffer[2], default_constructed_value); + EXPECT_EQ(buffer[4], 0); + EXPECT_EQ(buffer[5], default_constructed_value); + EXPECT_EQ(buffer[6], 0); + EXPECT_EQ(buffer[7], default_constructed_value); + EXPECT_EQ(buffer[8], 0); +} + +TEST(cpp_type, Destruct) +{ + int buffer[10] = {0}; + CPPType_TestType.destruct((void *)buffer); + EXPECT_EQ(buffer[0], destructed_value); + EXPECT_EQ(buffer[1], 0); + CPPType_TestType.destruct_n((void *)buffer, 3); + EXPECT_EQ(buffer[0], destructed_value); + EXPECT_EQ(buffer[1], destructed_value); + EXPECT_EQ(buffer[2], destructed_value); + EXPECT_EQ(buffer[3], 0); + CPPType_TestType.destruct_indices((void *)buffer, {2, 5, 7}); + EXPECT_EQ(buffer[2], destructed_value); + EXPECT_EQ(buffer[4], 0); + EXPECT_EQ(buffer[5], destructed_value); + EXPECT_EQ(buffer[6], 0); + EXPECT_EQ(buffer[7], destructed_value); + EXPECT_EQ(buffer[8], 0); +} + +TEST(cpp_type, CopyToUninitialized) +{ + int buffer1[10] = {0}; + int buffer2[10] = {0}; + CPPType_TestType.copy_to_uninitialized((void *)buffer1, (void *)buffer2); + EXPECT_EQ(buffer1[0], copy_constructed_from_value); + EXPECT_EQ(buffer2[0], copy_constructed_value); + CPPType_TestType.copy_to_uninitialized_n((void *)buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1[0], copy_constructed_from_value); + EXPECT_EQ(buffer2[0], copy_constructed_value); + EXPECT_EQ(buffer1[1], copy_constructed_from_value); + EXPECT_EQ(buffer2[1], copy_constructed_value); + EXPECT_EQ(buffer1[2], copy_constructed_from_value); + EXPECT_EQ(buffer2[2], copy_constructed_value); + EXPECT_EQ(buffer1[3], 0); + EXPECT_EQ(buffer2[3], 0); + CPPType_TestType.copy_to_uninitialized_indices((void *)buffer1, (void *)buffer2, {2, 5, 7}); + EXPECT_EQ(buffer1[2], copy_constructed_from_value); + EXPECT_EQ(buffer2[2], copy_constructed_value); + EXPECT_EQ(buffer1[4], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer1[5], copy_constructed_from_value); + EXPECT_EQ(buffer2[5], copy_constructed_value); + EXPECT_EQ(buffer1[6], 0); + EXPECT_EQ(buffer2[6], 0); + EXPECT_EQ(buffer1[7], copy_constructed_from_value); + EXPECT_EQ(buffer2[7], copy_constructed_value); + EXPECT_EQ(buffer1[8], 0); + EXPECT_EQ(buffer2[8], 0); +} + +TEST(cpp_type, CopyToInitialized) +{ + int buffer1[10] = {0}; + int buffer2[10] = {0}; + CPPType_TestType.copy_to_initialized((void *)buffer1, (void *)buffer2); + EXPECT_EQ(buffer1[0], copy_assigned_from_value); + EXPECT_EQ(buffer2[0], copy_assigned_value); + CPPType_TestType.copy_to_initialized_n((void *)buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1[0], copy_assigned_from_value); + EXPECT_EQ(buffer2[0], copy_assigned_value); + EXPECT_EQ(buffer1[1], copy_assigned_from_value); + EXPECT_EQ(buffer2[1], copy_assigned_value); + EXPECT_EQ(buffer1[2], copy_assigned_from_value); + EXPECT_EQ(buffer2[2], copy_assigned_value); + EXPECT_EQ(buffer1[3], 0); + EXPECT_EQ(buffer2[3], 0); + CPPType_TestType.copy_to_initialized_indices((void *)buffer1, (void *)buffer2, {2, 5, 7}); + EXPECT_EQ(buffer1[2], copy_assigned_from_value); + EXPECT_EQ(buffer2[2], copy_assigned_value); + EXPECT_EQ(buffer1[4], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer1[5], copy_assigned_from_value); + EXPECT_EQ(buffer2[5], copy_assigned_value); + EXPECT_EQ(buffer1[6], 0); + EXPECT_EQ(buffer2[6], 0); + EXPECT_EQ(buffer1[7], copy_assigned_from_value); + EXPECT_EQ(buffer2[7], copy_assigned_value); + EXPECT_EQ(buffer1[8], 0); + EXPECT_EQ(buffer2[8], 0); +} + +TEST(cpp_type, RelocateToUninitialized) +{ + int buffer1[10] = {0}; + int buffer2[10] = {0}; + CPPType_TestType.relocate_to_uninitialized((void *)buffer1, (void *)buffer2); + EXPECT_EQ(buffer1[0], destructed_value); + EXPECT_EQ(buffer2[0], move_constructed_value); + CPPType_TestType.relocate_to_uninitialized_n((void *)buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1[0], destructed_value); + EXPECT_EQ(buffer2[0], move_constructed_value); + EXPECT_EQ(buffer1[1], destructed_value); + EXPECT_EQ(buffer2[1], move_constructed_value); + EXPECT_EQ(buffer1[2], destructed_value); + EXPECT_EQ(buffer2[2], move_constructed_value); + EXPECT_EQ(buffer1[3], 0); + EXPECT_EQ(buffer2[3], 0); + CPPType_TestType.relocate_to_uninitialized_indices((void *)buffer1, (void *)buffer2, {2, 5, 7}); + EXPECT_EQ(buffer1[2], destructed_value); + EXPECT_EQ(buffer2[2], move_constructed_value); + EXPECT_EQ(buffer1[4], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer1[5], destructed_value); + EXPECT_EQ(buffer2[5], move_constructed_value); + EXPECT_EQ(buffer1[6], 0); + EXPECT_EQ(buffer2[6], 0); + EXPECT_EQ(buffer1[7], destructed_value); + EXPECT_EQ(buffer2[7], move_constructed_value); + EXPECT_EQ(buffer1[8], 0); + EXPECT_EQ(buffer2[8], 0); +} + +TEST(cpp_type, RelocateToInitialized) +{ + int buffer1[10] = {0}; + int buffer2[10] = {0}; + CPPType_TestType.relocate_to_initialized((void *)buffer1, (void *)buffer2); + EXPECT_EQ(buffer1[0], destructed_value); + EXPECT_EQ(buffer2[0], move_assigned_value); + CPPType_TestType.relocate_to_initialized_n((void *)buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1[0], destructed_value); + EXPECT_EQ(buffer2[0], move_assigned_value); + EXPECT_EQ(buffer1[1], destructed_value); + EXPECT_EQ(buffer2[1], move_assigned_value); + EXPECT_EQ(buffer1[2], destructed_value); + EXPECT_EQ(buffer2[2], move_assigned_value); + EXPECT_EQ(buffer1[3], 0); + EXPECT_EQ(buffer2[3], 0); + CPPType_TestType.relocate_to_initialized_indices((void *)buffer1, (void *)buffer2, {2, 5, 7}); + EXPECT_EQ(buffer1[2], destructed_value); + EXPECT_EQ(buffer2[2], move_assigned_value); + EXPECT_EQ(buffer1[4], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer1[5], destructed_value); + EXPECT_EQ(buffer2[5], move_assigned_value); + EXPECT_EQ(buffer1[6], 0); + EXPECT_EQ(buffer2[6], 0); + EXPECT_EQ(buffer1[7], destructed_value); + EXPECT_EQ(buffer2[7], move_assigned_value); + EXPECT_EQ(buffer1[8], 0); + EXPECT_EQ(buffer2[8], 0); +} + +TEST(cpp_type, FillInitialized) +{ + int buffer1 = 0; + int buffer2[10] = {0}; + CPPType_TestType.fill_initialized((void *)&buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1, copy_assigned_from_value); + EXPECT_EQ(buffer2[0], copy_assigned_value); + EXPECT_EQ(buffer2[1], copy_assigned_value); + EXPECT_EQ(buffer2[2], copy_assigned_value); + EXPECT_EQ(buffer2[3], 0); + + buffer1 = 0; + CPPType_TestType.fill_initialized_indices((void *)&buffer1, (void *)buffer2, {1, 6, 8}); + EXPECT_EQ(buffer1, copy_assigned_from_value); + EXPECT_EQ(buffer2[0], copy_assigned_value); + EXPECT_EQ(buffer2[1], copy_assigned_value); + EXPECT_EQ(buffer2[2], copy_assigned_value); + EXPECT_EQ(buffer2[3], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer2[5], 0); + EXPECT_EQ(buffer2[6], copy_assigned_value); + EXPECT_EQ(buffer2[7], 0); + EXPECT_EQ(buffer2[8], copy_assigned_value); + EXPECT_EQ(buffer2[9], 0); +} + +TEST(cpp_type, FillUninitialized) +{ + int buffer1 = 0; + int buffer2[10] = {0}; + CPPType_TestType.fill_uninitialized((void *)&buffer1, (void *)buffer2, 3); + EXPECT_EQ(buffer1, copy_constructed_from_value); + EXPECT_EQ(buffer2[0], copy_constructed_value); + EXPECT_EQ(buffer2[1], copy_constructed_value); + EXPECT_EQ(buffer2[2], copy_constructed_value); + EXPECT_EQ(buffer2[3], 0); + + buffer1 = 0; + CPPType_TestType.fill_uninitialized_indices((void *)&buffer1, (void *)buffer2, {1, 6, 8}); + EXPECT_EQ(buffer1, copy_constructed_from_value); + EXPECT_EQ(buffer2[0], copy_constructed_value); + EXPECT_EQ(buffer2[1], copy_constructed_value); + EXPECT_EQ(buffer2[2], copy_constructed_value); + EXPECT_EQ(buffer2[3], 0); + EXPECT_EQ(buffer2[4], 0); + EXPECT_EQ(buffer2[5], 0); + EXPECT_EQ(buffer2[6], copy_constructed_value); + EXPECT_EQ(buffer2[7], 0); + EXPECT_EQ(buffer2[8], copy_constructed_value); + EXPECT_EQ(buffer2[9], 0); +} + +TEST(cpp_type, DebugPrint) +{ + int value = 42; + std::stringstream ss; + CPPType::get<int32_t>().debug_print((void *)&value, ss); + std::string text = ss.str(); + EXPECT_EQ(text, "42"); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_generic_vector_array_test.cc b/source/blender/functions/tests/FN_generic_vector_array_test.cc new file mode 100644 index 00000000000..77ec05f12dc --- /dev/null +++ b/source/blender/functions/tests/FN_generic_vector_array_test.cc @@ -0,0 +1,101 @@ +/* Apache License, Version 2.0 */ + +#include "FN_generic_vector_array.hh" + +#include "testing/testing.h" + +namespace blender::fn::tests { + +TEST(generic_vector_array, Constructor) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 3}; + EXPECT_EQ(vectors.size(), 3); + EXPECT_EQ(vectors.lengths().size(), 3); + EXPECT_EQ(vectors.starts().size(), 3); + EXPECT_EQ(vectors.lengths()[0], 0); + EXPECT_EQ(vectors.lengths()[1], 0); + EXPECT_EQ(vectors.lengths()[2], 0); + EXPECT_EQ(vectors.type(), CPPType::get<int32_t>()); +} + +TEST(generic_vector_array, Append) +{ + GVectorArray vectors{CPPType::get<std::string>(), 3}; + std::string value = "hello"; + vectors.append(0, &value); + value = "world"; + vectors.append(0, &value); + vectors.append(2, &value); + + EXPECT_EQ(vectors.lengths()[0], 2); + EXPECT_EQ(vectors.lengths()[1], 0); + EXPECT_EQ(vectors.lengths()[2], 1); + EXPECT_EQ(vectors[0].size(), 2); + EXPECT_EQ(vectors[0].typed<std::string>()[0], "hello"); + EXPECT_EQ(vectors[0].typed<std::string>()[1], "world"); + EXPECT_EQ(vectors[2].typed<std::string>()[0], "world"); +} + +TEST(generic_vector_array, AsArraySpan) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 3}; + int value = 3; + vectors.append(0, &value); + vectors.append(0, &value); + value = 5; + vectors.append(2, &value); + vectors.append(2, &value); + vectors.append(2, &value); + + GVArraySpan span = vectors; + EXPECT_EQ(span.type(), CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 3); + EXPECT_EQ(span[0].size(), 2); + EXPECT_EQ(span[1].size(), 0); + EXPECT_EQ(span[2].size(), 3); + EXPECT_EQ(span[0].typed<int>()[1], 3); + EXPECT_EQ(span[2].typed<int>()[0], 5); +} + +TEST(generic_vector_array, TypedRef) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 4}; + GVectorArrayRef<int> ref = vectors.typed<int>(); + ref.append(0, 2); + ref.append(0, 6); + ref.append(0, 7); + ref.append(2, 1); + ref.append(2, 1); + ref.append(3, 5); + ref.append(3, 6); + + EXPECT_EQ(ref[0].size(), 3); + EXPECT_EQ(vectors[0].size(), 3); + EXPECT_EQ(ref[0][0], 2); + EXPECT_EQ(ref[0][1], 6); + EXPECT_EQ(ref[0][2], 7); + EXPECT_EQ(ref[1].size(), 0); + EXPECT_EQ(ref[2][0], 1); + EXPECT_EQ(ref[2][1], 1); + EXPECT_EQ(ref[3][0], 5); + EXPECT_EQ(ref[3][1], 6); +} + +TEST(generic_vector_array, Extend) +{ + GVectorArray vectors{CPPType::get<int32_t>(), 3}; + GVectorArrayRef<int> ref = vectors; + + ref.extend(1, {5, 6, 7}); + ref.extend(0, {3}); + + EXPECT_EQ(vectors[0].size(), 1); + EXPECT_EQ(vectors[1].size(), 3); + EXPECT_EQ(vectors[2].size(), 0); + EXPECT_EQ(ref[1][0], 5); + EXPECT_EQ(ref[1][1], 6); + EXPECT_EQ(ref[1][2], 7); + EXPECT_EQ(ref[0][0], 3); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc new file mode 100644 index 00000000000..53290e9170c --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_network_test.cc @@ -0,0 +1,253 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_network.hh" +#include "FN_multi_function_network_evaluation.hh" + +namespace blender::fn::tests { + +TEST(multi_function_network, Test1) +{ + CustomMF_SI_SO<int, int> add_10_fn("add 10", [](int value) { return value + 10; }); + CustomMF_SI_SI_SO<int, int, int> multiply_fn("multiply", [](int a, int b) { return a * b; }); + + MFNetwork network; + + MFNode &node1 = network.add_function(add_10_fn); + MFNode &node2 = network.add_function(multiply_fn); + MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle<int>()); + MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle<int>()); + network.add_link(node1.output(0), node2.input(0)); + network.add_link(node1.output(0), node2.input(1)); + network.add_link(node2.output(0), output_socket); + network.add_link(input_socket, node1.input(0)); + + MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}}; + + { + Array<int> values = {4, 6, 1, 2, 0}; + Array<int> results(values.size(), 0); + + MFParamsBuilder params(network_fn, values.size()); + params.add_readonly_single_input(values.as_span()); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + + network_fn.call({0, 2, 3, 4}, params, context); + + EXPECT_EQ(results[0], 14 * 14); + EXPECT_EQ(results[1], 0); + EXPECT_EQ(results[2], 11 * 11); + EXPECT_EQ(results[3], 12 * 12); + EXPECT_EQ(results[4], 10 * 10); + } + { + int value = 3; + Array<int> results(5, 0); + + MFParamsBuilder params(network_fn, results.size()); + params.add_readonly_single_input(&value); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + + network_fn.call({1, 2, 4}, params, context); + + EXPECT_EQ(results[0], 0); + EXPECT_EQ(results[1], 13 * 13); + EXPECT_EQ(results[2], 13 * 13); + EXPECT_EQ(results[3], 0); + EXPECT_EQ(results[4], 13 * 13); + } +} + +class ConcatVectorsFunction : public MultiFunction { + public: + ConcatVectorsFunction() + { + MFSignatureBuilder signature = this->get_builder("Concat Vectors"); + signature.vector_mutable<int>("A"); + signature.vector_input<int>("B"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArrayRef<int> a = params.vector_mutable<int>(0); + VArraySpan<int> b = params.readonly_vector_input<int>(1); + + for (int64_t i : mask) { + a.extend(i, b[i]); + } + } +}; + +class AppendFunction : public MultiFunction { + public: + AppendFunction() + { + MFSignatureBuilder signature = this->get_builder("Append"); + signature.vector_mutable<int>("Vector"); + signature.single_input<int>("Value"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArrayRef<int> vectors = params.vector_mutable<int>(0); + VSpan<int> values = params.readonly_single_input<int>(1); + + for (int64_t i : mask) { + vectors.append(i, values[i]); + } + } +}; + +class SumVectorFunction : public MultiFunction { + public: + SumVectorFunction() + { + MFSignatureBuilder signature = this->get_builder("Sum Vector"); + signature.vector_input<int>("Vector"); + signature.single_output<int>("Sum"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VArraySpan<int> vectors = params.readonly_vector_input<int>(0); + MutableSpan<int> sums = params.uninitialized_single_output<int>(1); + + for (int64_t i : mask) { + int sum = 0; + VSpan<int> vector = vectors[i]; + for (int j = 0; j < vector.size(); j++) { + sum += vector[j]; + } + sums[i] = sum; + } + } +}; + +class CreateRangeFunction : public MultiFunction { + public: + CreateRangeFunction() + { + MFSignatureBuilder builder = this->get_builder("Create Range"); + builder.single_input<int>("Size"); + builder.vector_output<int>("Range"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<int> sizes = params.readonly_single_input<int>(0, "Size"); + GVectorArrayRef<int> ranges = params.vector_output<int>(1, "Range"); + + for (int64_t i : mask) { + int size = sizes[i]; + for (int j : IndexRange(size)) { + ranges.append(i, j); + } + } + } +}; + +TEST(multi_function_network, Test2) +{ + CustomMF_SI_SO<int, int> add_3_fn("add 3", [](int value) { return value + 3; }); + + ConcatVectorsFunction concat_vectors_fn; + AppendFunction append_fn; + SumVectorFunction sum_fn; + CreateRangeFunction create_range_fn; + + MFNetwork network; + + MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector<int>()); + MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle<int>()); + MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector<int>()); + MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle<int>()); + + MFNode &node1 = network.add_function(add_3_fn); + MFNode &node2 = network.add_function(create_range_fn); + MFNode &node3 = network.add_function(concat_vectors_fn); + MFNode &node4 = network.add_function(sum_fn); + MFNode &node5 = network.add_function(append_fn); + MFNode &node6 = network.add_function(sum_fn); + + network.add_link(input2, node1.input(0)); + network.add_link(node1.output(0), node2.input(0)); + network.add_link(node2.output(0), node3.input(1)); + network.add_link(input1, node3.input(0)); + network.add_link(input1, node4.input(0)); + network.add_link(node4.output(0), node5.input(1)); + network.add_link(node3.output(0), node5.input(0)); + network.add_link(node5.output(0), node6.input(0)); + network.add_link(node3.output(0), output1); + network.add_link(node6.output(0), output2); + + // std::cout << network.to_dot() << "\n\n"; + + MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}}; + + { + Array<int> input_value_1 = {3, 6}; + int input_value_2 = 4; + + GVectorArray output_value_1(CPPType::get<int32_t>(), 5); + Array<int> output_value_2(5, -1); + + MFParamsBuilder params(network_fn, 5); + params.add_readonly_vector_input(GVArraySpan(input_value_1.as_span(), 5)); + params.add_readonly_single_input(&input_value_2); + params.add_vector_output(output_value_1); + params.add_uninitialized_single_output(output_value_2.as_mutable_span()); + + MFContextBuilder context; + + network_fn.call({1, 2, 4}, params, context); + + EXPECT_EQ(output_value_1[0].size(), 0); + EXPECT_EQ(output_value_1[1].size(), 9); + EXPECT_EQ(output_value_1[2].size(), 9); + EXPECT_EQ(output_value_1[3].size(), 0); + EXPECT_EQ(output_value_1[4].size(), 9); + + EXPECT_EQ(output_value_2[0], -1); + EXPECT_EQ(output_value_2[1], 39); + EXPECT_EQ(output_value_2[2], 39); + EXPECT_EQ(output_value_2[3], -1); + EXPECT_EQ(output_value_2[4], 39); + } + { + GVectorArray input_value_1(CPPType::get<int32_t>(), 3); + GVectorArrayRef<int> input_value_ref_1 = input_value_1; + input_value_ref_1.extend(0, {3, 4, 5}); + input_value_ref_1.extend(1, {1, 2}); + + Array<int> input_value_2 = {4, 2, 3}; + + GVectorArray output_value_1(CPPType::get<int32_t>(), 3); + Array<int> output_value_2(3, -1); + + MFParamsBuilder params(network_fn, 3); + params.add_readonly_vector_input(input_value_1); + params.add_readonly_single_input(input_value_2.as_span()); + params.add_vector_output(output_value_1); + params.add_uninitialized_single_output(output_value_2.as_mutable_span()); + + MFContextBuilder context; + + network_fn.call({0, 1, 2}, params, context); + + EXPECT_EQ(output_value_1[0].size(), 10); + EXPECT_EQ(output_value_1[1].size(), 7); + EXPECT_EQ(output_value_1[2].size(), 6); + + EXPECT_EQ(output_value_2[0], 45); + EXPECT_EQ(output_value_2[1], 16); + EXPECT_EQ(output_value_2[2], 15); + } +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc new file mode 100644 index 00000000000..6acb6e22b01 --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -0,0 +1,385 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_multi_function.hh" +#include "FN_multi_function_builder.hh" + +namespace blender::fn::tests { + +class AddFunction : public MultiFunction { + public: + AddFunction() + { + MFSignatureBuilder builder = this->get_builder("Add"); + builder.single_input<int>("A"); + builder.single_input<int>("B"); + builder.single_output<int>("Result"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<int> a = params.readonly_single_input<int>(0, "A"); + VSpan<int> b = params.readonly_single_input<int>(1, "B"); + MutableSpan<int> result = params.uninitialized_single_output<int>(2, "Result"); + + for (int64_t i : mask) { + result[i] = a[i] + b[i]; + } + } +}; + +TEST(multi_function, AddFunction) +{ + AddFunction fn; + + Array<int> input1 = {4, 5, 6}; + Array<int> input2 = {10, 20, 30}; + Array<int> output(3, -1); + + MFParamsBuilder params(fn, 3); + params.add_readonly_single_input(input1.as_span()); + params.add_readonly_single_input(input2.as_span()); + params.add_uninitialized_single_output(output.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 2}, params, context); + + EXPECT_EQ(output[0], 14); + EXPECT_EQ(output[1], -1); + EXPECT_EQ(output[2], 36); +} + +class AddPrefixFunction : public MultiFunction { + public: + AddPrefixFunction() + { + MFSignatureBuilder builder = this->get_builder("Add Prefix"); + builder.single_input<std::string>("Prefix"); + builder.single_mutable<std::string>("Strings"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<std::string> prefixes = params.readonly_single_input<std::string>(0, "Prefix"); + MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings"); + + for (int64_t i : mask) { + strings[i] = prefixes[i] + strings[i]; + } + } +}; + +TEST(multi_function, AddPrefixFunction) +{ + AddPrefixFunction fn; + + Array<std::string> strings = { + "Hello", + "World", + "This is a test", + "Another much longer string to trigger an allocation", + }; + + std::string prefix = "AB"; + + MFParamsBuilder params(fn, strings.size()); + params.add_readonly_single_input(&prefix); + params.add_single_mutable(strings.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 2, 3}, params, context); + + EXPECT_EQ(strings[0], "ABHello"); + EXPECT_EQ(strings[1], "World"); + EXPECT_EQ(strings[2], "ABThis is a test"); + EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation"); +} + +class CreateRangeFunction : public MultiFunction { + public: + CreateRangeFunction() + { + MFSignatureBuilder builder = this->get_builder("Create Range"); + builder.single_input<uint>("Size"); + builder.vector_output<uint>("Range"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<uint> sizes = params.readonly_single_input<uint>(0, "Size"); + GVectorArrayRef<uint> ranges = params.vector_output<uint>(1, "Range"); + + for (int64_t i : mask) { + uint size = sizes[i]; + for (uint j : IndexRange(size)) { + ranges.append(i, j); + } + } + } +}; + +TEST(multi_function, CreateRangeFunction) +{ + CreateRangeFunction fn; + + GVectorArray ranges(CPPType::get<int32_t>(), 5); + GVectorArrayRef<uint> ranges_ref(ranges); + Array<uint> sizes = {3, 0, 6, 1, 4}; + + MFParamsBuilder params(fn, ranges.size()); + params.add_readonly_single_input(sizes.as_span()); + params.add_vector_output(ranges); + + MFContextBuilder context; + + fn.call({0, 1, 2, 3}, params, context); + + EXPECT_EQ(ranges_ref[0].size(), 3); + EXPECT_EQ(ranges_ref[1].size(), 0); + EXPECT_EQ(ranges_ref[2].size(), 6); + EXPECT_EQ(ranges_ref[3].size(), 1); + EXPECT_EQ(ranges_ref[4].size(), 0); + + EXPECT_EQ(ranges_ref[0][0], 0); + EXPECT_EQ(ranges_ref[0][1], 1); + EXPECT_EQ(ranges_ref[0][2], 2); + EXPECT_EQ(ranges_ref[2][0], 0); + EXPECT_EQ(ranges_ref[2][1], 1); +} + +class GenericAppendFunction : public MultiFunction { + public: + GenericAppendFunction(const CPPType &type) + { + MFSignatureBuilder builder = this->get_builder("Append"); + builder.vector_mutable("Vector", type); + builder.single_input("Value", type); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray &vectors = params.vector_mutable(0, "Vector"); + GVSpan values = params.readonly_single_input(1, "Value"); + + for (int64_t i : mask) { + vectors.append(i, values[i]); + } + } +}; + +TEST(multi_function, GenericAppendFunction) +{ + GenericAppendFunction fn(CPPType::get<int32_t>()); + + GVectorArray vectors(CPPType::get<int32_t>(), 4); + GVectorArrayRef<int> vectors_ref(vectors); + vectors_ref.append(0, 1); + vectors_ref.append(0, 2); + vectors_ref.append(2, 6); + Array<int> values = {5, 7, 3, 1}; + + MFParamsBuilder params(fn, vectors.size()); + params.add_vector_mutable(vectors); + params.add_readonly_single_input(values.as_span()); + + MFContextBuilder context; + + fn.call(IndexRange(vectors.size()), params, context); + + EXPECT_EQ(vectors_ref[0].size(), 3); + EXPECT_EQ(vectors_ref[1].size(), 1); + EXPECT_EQ(vectors_ref[2].size(), 2); + EXPECT_EQ(vectors_ref[3].size(), 1); + + EXPECT_EQ(vectors_ref[0][0], 1); + EXPECT_EQ(vectors_ref[0][1], 2); + EXPECT_EQ(vectors_ref[0][2], 5); + EXPECT_EQ(vectors_ref[1][0], 7); + EXPECT_EQ(vectors_ref[2][0], 6); + EXPECT_EQ(vectors_ref[2][1], 3); + EXPECT_EQ(vectors_ref[3][0], 1); +} + +TEST(multi_function, CustomMF_SI_SO) +{ + CustomMF_SI_SO<std::string, uint> fn("strlen", + [](const std::string &str) { return str.size(); }); + + Array<std::string> strings = {"hello", "world", "test", "another test"}; + Array<uint> sizes(strings.size(), 0); + + MFParamsBuilder params(fn, strings.size()); + params.add_readonly_single_input(strings.as_span()); + params.add_uninitialized_single_output(sizes.as_mutable_span()); + + MFContextBuilder context; + + fn.call(IndexRange(strings.size()), params, context); + + EXPECT_EQ(sizes[0], 5); + EXPECT_EQ(sizes[1], 5); + EXPECT_EQ(sizes[2], 4); + EXPECT_EQ(sizes[3], 12); +} + +TEST(multi_function, CustomMF_SI_SI_SO) +{ + CustomMF_SI_SI_SO<int, int, int> fn("mul", [](int a, int b) { return a * b; }); + + Array<int> values_a = {4, 6, 8, 9}; + int value_b = 10; + Array<int> outputs(values_a.size(), -1); + + MFParamsBuilder params(fn, values_a.size()); + params.add_readonly_single_input(values_a.as_span()); + params.add_readonly_single_input(&value_b); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 1, 3}, params, context); + + EXPECT_EQ(outputs[0], 40); + EXPECT_EQ(outputs[1], 60); + EXPECT_EQ(outputs[2], -1); + EXPECT_EQ(outputs[3], 90); +} + +TEST(multi_function, CustomMF_SI_SI_SI_SO) +{ + CustomMF_SI_SI_SI_SO<int, std::string, bool, uint> fn{ + "custom", + [](int a, const std::string &b, bool c) { return (uint)((uint)a + b.size() + (uint)c); }}; + + Array<int> values_a = {5, 7, 3, 8}; + Array<std::string> values_b = {"hello", "world", "another", "test"}; + Array<bool> values_c = {true, false, false, true}; + Array<uint> outputs(values_a.size(), 0); + + MFParamsBuilder params(fn, values_a.size()); + params.add_readonly_single_input(values_a.as_span()); + params.add_readonly_single_input(values_b.as_span()); + params.add_readonly_single_input(values_c.as_span()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({1, 2, 3}, params, context); + + EXPECT_EQ(outputs[0], 0); + EXPECT_EQ(outputs[1], 12); + EXPECT_EQ(outputs[2], 10); + EXPECT_EQ(outputs[3], 13); +} + +TEST(multi_function, CustomMF_SM) +{ + CustomMF_SM<std::string> fn("AddSuffix", [](std::string &value) { value += " test"; }); + + Array<std::string> values = {"a", "b", "c", "d", "e"}; + + MFParamsBuilder params(fn, values.size()); + params.add_single_mutable(values.as_mutable_span()); + + MFContextBuilder context; + + fn.call({1, 2, 3}, params, context); + + EXPECT_EQ(values[0], "a"); + EXPECT_EQ(values[1], "b test"); + EXPECT_EQ(values[2], "c test"); + EXPECT_EQ(values[3], "d test"); + EXPECT_EQ(values[4], "e"); +} + +TEST(multi_function, CustomMF_Constant) +{ + CustomMF_Constant<int> fn{42}; + + Array<int> outputs(4, 0); + + MFParamsBuilder params(fn, outputs.size()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 2, 3}, params, context); + + EXPECT_EQ(outputs[0], 42); + EXPECT_EQ(outputs[1], 0); + EXPECT_EQ(outputs[2], 42); + EXPECT_EQ(outputs[3], 42); +} + +TEST(multi_function, CustomMF_GenericConstant) +{ + int value = 42; + CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value}; + EXPECT_EQ(fn.param_name(0), "42"); + + Array<int> outputs(4, 0); + + MFParamsBuilder params(fn, outputs.size()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 1, 2}, params, context); + + EXPECT_EQ(outputs[0], 42); + EXPECT_EQ(outputs[1], 42); + EXPECT_EQ(outputs[2], 42); + EXPECT_EQ(outputs[3], 0); +} + +TEST(multi_function, CustomMF_GenericConstantArray) +{ + std::array<int, 4> values = {3, 4, 5, 6}; + CustomMF_GenericConstantArray fn{GSpan(Span(values))}; + EXPECT_EQ(fn.param_name(0), "[3, 4, 5, 6, ]"); + + GVectorArray g_vector_array{CPPType::get<int32_t>(), 4}; + GVectorArrayRef<int> vector_array = g_vector_array; + + MFParamsBuilder params(fn, g_vector_array.size()); + params.add_vector_output(g_vector_array); + + MFContextBuilder context; + + fn.call({1, 2, 3}, params, context); + + EXPECT_EQ(vector_array[0].size(), 0); + EXPECT_EQ(vector_array[1].size(), 4); + EXPECT_EQ(vector_array[2].size(), 4); + EXPECT_EQ(vector_array[3].size(), 4); + for (int i = 1; i < 4; i++) { + EXPECT_EQ(vector_array[i][0], 3); + EXPECT_EQ(vector_array[i][1], 4); + EXPECT_EQ(vector_array[i][2], 5); + EXPECT_EQ(vector_array[i][3], 6); + } +} + +TEST(multi_function, CustomMF_Convert) +{ + CustomMF_Convert<float, int> fn; + + Array<float> inputs = {5.4f, 7.1f, 9.0f}; + Array<int> outputs(inputs.size(), 0); + + MFParamsBuilder params(fn, inputs.size()); + params.add_readonly_single_input(inputs.as_span()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + fn.call({0, 2}, params, context); + + EXPECT_EQ(outputs[0], 5); + EXPECT_EQ(outputs[1], 0); + EXPECT_EQ(outputs[2], 9); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_spans_test.cc b/source/blender/functions/tests/FN_spans_test.cc new file mode 100644 index 00000000000..fbcf1fda71e --- /dev/null +++ b/source/blender/functions/tests/FN_spans_test.cc @@ -0,0 +1,222 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_spans.hh" + +namespace blender::fn::tests { + +TEST(generic_span, TypeConstructor) +{ + GSpan span(CPPType::get<float>()); + EXPECT_EQ(span.size(), 0); + EXPECT_EQ(span.typed<float>().size(), 0); + EXPECT_TRUE(span.is_empty()); +} + +TEST(generic_span, BufferAndSizeConstructor) +{ + int values[4] = {6, 7, 3, 2}; + void *buffer = (void *)values; + GSpan span(CPPType::get<int32_t>(), buffer, 4); + EXPECT_EQ(span.size(), 4); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span.typed<int>().size(), 4); + EXPECT_EQ(span[0], &values[0]); + EXPECT_EQ(span[1], &values[1]); + EXPECT_EQ(span[2], &values[2]); + EXPECT_EQ(span[3], &values[3]); +} + +TEST(generic_mutable_span, TypeConstructor) +{ + GMutableSpan span(CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); +} + +TEST(generic_mutable_span, BufferAndSizeConstructor) +{ + int values[4] = {4, 7, 3, 5}; + void *buffer = (void *)values; + GMutableSpan span(CPPType::get<int32_t>(), buffer, 4); + EXPECT_EQ(span.size(), 4); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span.typed<int>().size(), 4); + EXPECT_EQ(values[2], 3); + *(int *)span[2] = 10; + EXPECT_EQ(values[2], 10); + span.typed<int>()[2] = 20; + EXPECT_EQ(values[2], 20); +} + +TEST(virtual_span, EmptyConstructor) +{ + VSpan<int> span; + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); + EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); + + GVSpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 0); +} + +TEST(virtual_span, SpanConstructor) +{ + std::array<int, 5> values = {7, 3, 8, 6, 4}; + Span<int> span = values; + VSpan<int> virtual_span = span; + EXPECT_EQ(virtual_span.size(), 5); + EXPECT_FALSE(virtual_span.is_empty()); + EXPECT_EQ(virtual_span[0], 7); + EXPECT_EQ(virtual_span[2], 8); + EXPECT_EQ(virtual_span[3], 6); + EXPECT_FALSE(virtual_span.is_single_element()); + EXPECT_TRUE(virtual_span.is_full_array()); + + GVSpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 5); +} + +TEST(virtual_span, PointerSpanConstructor) +{ + int x0 = 3; + int x1 = 6; + int x2 = 7; + std::array<const int *, 3> pointers = {&x0, &x2, &x1}; + VSpan<int> span = Span<const int *>(pointers); + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0], 3); + EXPECT_EQ(span[1], 7); + EXPECT_EQ(span[2], 6); + EXPECT_EQ(&span[1], &x2); + EXPECT_FALSE(span.is_single_element()); + EXPECT_FALSE(span.is_full_array()); + + GVSpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0], &x0); + EXPECT_EQ(converted[1], &x2); + EXPECT_EQ(converted[2], &x1); +} + +TEST(virtual_span, SingleConstructor) +{ + int value = 5; + VSpan<int> span = VSpan<int>::FromSingle(&value, 3); + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0], 5); + EXPECT_EQ(span[1], 5); + EXPECT_EQ(span[2], 5); + EXPECT_EQ(&span[0], &value); + EXPECT_EQ(&span[1], &value); + EXPECT_EQ(&span[2], &value); + EXPECT_TRUE(span.is_single_element()); + EXPECT_FALSE(span.is_full_array()); + + GVSpan converted(span); + EXPECT_EQ(converted.type(), CPPType::get<int>()); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0], &value); + EXPECT_EQ(converted[1], &value); + EXPECT_EQ(converted[2], &value); +} + +TEST(generic_virtual_span, TypeConstructor) +{ + GVSpan span(CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 0); + EXPECT_TRUE(span.is_empty()); + EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); + + VSpan<int> converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 0); +} + +TEST(generic_virtual_span, GenericSpanConstructor) +{ + int values[4] = {3, 4, 5, 6}; + GVSpan span{GSpan(CPPType::get<int32_t>(), values, 4)}; + EXPECT_EQ(span.size(), 4); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0], &values[0]); + EXPECT_EQ(span[1], &values[1]); + EXPECT_EQ(span[2], &values[2]); + EXPECT_EQ(span[3], &values[3]); + EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); + + int materialized[4] = {0}; + span.materialize_to_uninitialized(materialized); + EXPECT_EQ(materialized[0], 3); + EXPECT_EQ(materialized[1], 4); + EXPECT_EQ(materialized[2], 5); + EXPECT_EQ(materialized[3], 6); + + VSpan<int> converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 4); + EXPECT_EQ(converted[0], 3); + EXPECT_EQ(converted[1], 4); + EXPECT_EQ(converted[2], 5); + EXPECT_EQ(converted[3], 6); +} + +TEST(generic_virtual_span, SpanConstructor) +{ + std::array<int, 3> values = {6, 7, 8}; + GVSpan span{Span<int>(values)}; + EXPECT_EQ(span.type(), CPPType::get<int32_t>()); + EXPECT_EQ(span.size(), 3); + EXPECT_EQ(span[0], &values[0]); + EXPECT_EQ(span[1], &values[1]); + EXPECT_EQ(span[2], &values[2]); + EXPECT_FALSE(span.is_single_element()); + EXPECT_TRUE(span.is_full_array()); + + int materialized[3] = {0}; + span.materialize_to_uninitialized(materialized); + EXPECT_EQ(materialized[0], 6); + EXPECT_EQ(materialized[1], 7); + EXPECT_EQ(materialized[2], 8); + + VSpan<int> converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0], 6); + EXPECT_EQ(converted[1], 7); + EXPECT_EQ(converted[2], 8); +} + +TEST(generic_virtual_span, SingleConstructor) +{ + int value = 5; + GVSpan span = GVSpan::FromSingle(CPPType::get<int32_t>(), &value, 3); + EXPECT_EQ(span.size(), 3); + EXPECT_FALSE(span.is_empty()); + EXPECT_EQ(span[0], &value); + EXPECT_EQ(span[1], &value); + EXPECT_EQ(span[2], &value); + EXPECT_TRUE(span.is_single_element()); + EXPECT_EQ(span.as_single_element(), &value); + EXPECT_FALSE(span.is_full_array()); + + int materialized[3] = {0}; + span.materialize_to_uninitialized({1, 2}, materialized); + EXPECT_EQ(materialized[0], 0); + EXPECT_EQ(materialized[1], 5); + EXPECT_EQ(materialized[2], 5); + + VSpan<int> converted = span.typed<int>(); + EXPECT_EQ(converted.size(), 3); + EXPECT_EQ(converted[0], 5); + EXPECT_EQ(converted[1], 5); + EXPECT_EQ(converted[2], 5); +} + +} // namespace blender::fn::tests diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c index 03137a5cf23..21a8962b131 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c @@ -69,7 +69,7 @@ static void initData(GpencilModifierData *md) gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c index 4761dc878c0..10f775ee340 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c @@ -94,7 +94,7 @@ static void initData(GpencilModifierData *md) gpmd->falloff_type = eGPHook_Falloff_Smooth; gpmd->curfalloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curfalloff) { - BKE_curvemapping_initialize(gpmd->curfalloff); + BKE_curvemapping_init(gpmd->curfalloff); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index 0d8a5f7914e..67c26bf2584 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -79,7 +79,7 @@ static void initData(GpencilModifierData *md) if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; BKE_curvemap_reset(curve->cm, &curve->clipr, CURVE_PRESET_BELL, CURVEMAP_SLOPE_POSITIVE); - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c index 9cc3712e8f4..75f929e979e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -105,19 +105,23 @@ static void deformStroke(GpencilModifierData *md, bGPDspoint *pt = &gps->points[i]; MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; - /* verify vertex group */ + /* Verify vertex group. */ const float weight = get_modifier_point_weight( dvert, (mmd->flag & GP_OFFSET_INVERT_VGROUP) != 0, def_nr); if (weight < 0.0f) { continue; } - /* calculate matrix */ + /* Calculate matrix. */ mul_v3_v3fl(loc, mmd->loc, weight); mul_v3_v3fl(rot, mmd->rot, weight); mul_v3_v3fl(scale, mmd->scale, weight); add_v3_fl(scale, 1.0); loc_eul_size_to_mat4(mat, loc, rot, scale); + /* Apply scale to thickness. */ + float unit_scale = (scale[0] + scale[1] + scale[2]) / 3.0f; + pt->pressure *= unit_scale; + mul_m4_v3(mat, &pt->x); } /* Calc geometry data. */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index 34142709c18..efa58cc4ae0 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -70,7 +70,7 @@ static void initData(GpencilModifierData *md) gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c index 557a305d731..e3511d9645e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c @@ -66,7 +66,7 @@ static void initData(GpencilModifierData *md) gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index 4fa47a592ba..68547614776 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -65,7 +65,7 @@ static void initData(GpencilModifierData *md) gpmd->material = NULL; gpmd->curve_thickness = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_thickness) { - BKE_curvemapping_initialize(gpmd->curve_thickness); + BKE_curvemapping_init(gpmd->curve_thickness); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index da7d33839f1..9d10fcbe49b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -96,7 +96,7 @@ static void initData(GpencilModifierData *md) gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); if (gpmd->curve_intensity) { CurveMapping *curve = gpmd->curve_intensity; - BKE_curvemapping_initialize(curve); + BKE_curvemapping_init(curve); } } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4f90482d16e..3ea18f72166 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -52,38 +52,37 @@ set(INC_SYS ) set(SRC - intern/gpu_attr_binding.c - intern/gpu_batch.c + intern/gpu_attr_binding.cc + intern/gpu_batch.cc intern/gpu_batch_presets.c intern/gpu_batch_utils.c intern/gpu_buffers.c intern/gpu_codegen.c - intern/gpu_context.cpp - intern/gpu_debug.c - intern/gpu_draw.c - intern/gpu_draw_smoke.c - intern/gpu_element.c - intern/gpu_extensions.c - intern/gpu_framebuffer.c - intern/gpu_immediate.c + intern/gpu_context.cc + intern/gpu_debug.cc + intern/gpu_element.cc + intern/gpu_extensions.cc + intern/gpu_framebuffer.cc + intern/gpu_immediate.cc intern/gpu_immediate_util.c intern/gpu_init_exit.c intern/gpu_material.c intern/gpu_material_library.c - intern/gpu_matrix.c + intern/gpu_matrix.cc intern/gpu_node_graph.c - intern/gpu_platform.c + intern/gpu_platform.cc intern/gpu_primitive.c intern/gpu_select.c intern/gpu_select_pick.c intern/gpu_select_sample_query.c - intern/gpu_shader.c - intern/gpu_shader_interface.c - intern/gpu_state.c - intern/gpu_texture.c - intern/gpu_uniformbuffer.c - intern/gpu_vertex_buffer.c - intern/gpu_vertex_format.c + intern/gpu_shader.cc + intern/gpu_shader_builtin.c + intern/gpu_shader_interface.cc + intern/gpu_state.cc + intern/gpu_texture.cc + intern/gpu_uniformbuffer.cc + intern/gpu_vertex_buffer.cc + intern/gpu_vertex_format.cc intern/gpu_viewport.c GPU_attr_binding.h @@ -94,7 +93,6 @@ set(SRC GPU_common.h GPU_context.h GPU_debug.h - GPU_draw.h GPU_element.h GPU_extensions.h GPU_framebuffer.h @@ -216,6 +214,8 @@ data_to_c_simple(shaders/gpu_shader_text_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_keyframe_diamond_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_keyframe_diamond_frag.glsl SRC) +data_to_c_simple(shaders/gpu_shader_codegen_lib.glsl SRC) + data_to_c_simple(shaders/gpu_shader_geometry.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_add_shader.glsl SRC) diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 5f55b512695..85d9c037b38 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -127,9 +127,8 @@ 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_program_set_no_use(GPUBatch *, uint32_t program, const GPUShaderInterface *); -void GPU_batch_program_set(GPUBatch *, uint32_t program, const GPUShaderInterface *); -void GPU_batch_program_set_shader(GPUBatch *, GPUShader *shader); +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, diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h index 8928581ee08..fc6ca791664 100644 --- a/source/blender/gpu/GPU_debug.h +++ b/source/blender/gpu/GPU_debug.h @@ -24,8 +24,6 @@ #ifndef __GPU_DEBUG_H__ #define __GPU_DEBUG_H__ -#include "GPU_glew.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h deleted file mode 100644 index b364bd0ef95..00000000000 --- a/source/blender/gpu/GPU_draw.h +++ /dev/null @@ -1,95 +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 - */ - -#ifndef __GPU_DRAW_H__ -#define __GPU_DRAW_H__ - -#include "BLI_utildefines.h" -#include "DNA_object_enums.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct FluidModifierData; -struct ImBuf; -struct Image; -struct ImageUser; -struct Main; - -/* OpenGL drawing functions related to shading. */ - -/* Mipmap settings - * - these will free textures on changes */ - -void GPU_set_mipmap(struct Main *bmain, bool mipmap); -bool GPU_get_mipmap(void); -void GPU_set_linear_mipmap(bool linear); -bool GPU_get_linear_mipmap(void); -void GPU_paint_set_mipmap(struct Main *bmain, bool mipmap); - -/* Anisotropic filtering settings - * - these will free textures on changes */ -void GPU_set_anisotropic(float value); -float GPU_get_anisotropic(void); - -/* Image updates and free - * - these deal with images bound as opengl textures */ - -void GPU_paint_update_image( - struct Image *ima, struct ImageUser *iuser, int x, int y, int w, int h); -void GPU_create_gl_tex(unsigned int *bind, - unsigned int *rect, - float *frect, - int rectw, - int recth, - int textarget, - bool mipmap, - bool half_float, - bool use_srgb, - struct Image *ima); -void GPU_create_gl_tex_compressed(unsigned int *bind, - int textarget, - struct Image *ima, - struct ImBuf *ibuf); -bool GPU_upload_dxt_texture(struct ImBuf *ibuf, bool use_srgb); -void GPU_free_image(struct Image *ima); -void GPU_free_images(struct Main *bmain); -void GPU_free_images_anim(struct Main *bmain); -void GPU_free_images_old(struct Main *bmain); - -/* gpu_draw_smoke.c */ -void GPU_free_smoke(struct FluidModifierData *fmd); -void GPU_free_smoke_velocity(struct FluidModifierData *fmd); -void GPU_create_smoke(struct FluidModifierData *fmd, int highres); -void GPU_create_smoke_coba_field(struct FluidModifierData *fmd); -void GPU_create_smoke_velocity(struct FluidModifierData *fmd); - -/* Delayed free of OpenGL buffers by main thread */ -void GPU_free_unused_buffers(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index 263deeaf28d..0d8954dc16c 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -52,6 +52,8 @@ bool GPU_context_local_shaders_workaround(void); bool GPU_texture_copy_workaround(void); bool GPU_crappy_amd_driver(void); +int GPU_texture_size_with_limit(int res); + bool GPU_mem_stats_supported(void); void GPU_mem_stats_get(int *totalmem, int *freemem); diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 0a43b2c58a0..db531cdef9b 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -32,7 +32,7 @@ extern "C" { typedef struct GPUAttachment { struct GPUTexture *tex; - int mip, layer; + int layer, mip; } GPUAttachment; typedef enum eGPUFrameBufferBits { @@ -120,35 +120,35 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *confi #define GPU_ATTACHMENT_NONE \ { \ - .tex = NULL, .layer = -1, .mip = 0, \ + NULL, -1, 0, \ } #define GPU_ATTACHMENT_LEAVE \ { \ - .tex = NULL, .layer = -1, .mip = -1, \ + NULL, -1, -1, \ } #define GPU_ATTACHMENT_TEXTURE(_tex) \ { \ - .tex = _tex, .layer = -1, .mip = 0, \ + _tex, -1, 0, \ } #define GPU_ATTACHMENT_TEXTURE_MIP(_tex, _mip) \ { \ - .tex = _tex, .layer = -1, .mip = _mip, \ + _tex, -1, _mip, \ } #define GPU_ATTACHMENT_TEXTURE_LAYER(_tex, _layer) \ { \ - .tex = _tex, .layer = _layer, .mip = 0, \ + _tex, _layer, 0, \ } #define GPU_ATTACHMENT_TEXTURE_LAYER_MIP(_tex, _layer, _mip) \ { \ - .tex = _tex, .layer = _layer, .mip = _mip, \ + _tex, _layer, _mip, \ } #define GPU_ATTACHMENT_TEXTURE_CUBEFACE(_tex, _face) \ { \ - .tex = _tex, .layer = _face, .mip = 0, \ + _tex, _face, 0, \ } #define GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(_tex, _face, _mip) \ { \ - .tex = _tex, .layer = _face, .mip = _mip, \ + _tex, _face, _mip, \ } /* Framebuffer operations */ @@ -212,7 +212,7 @@ GPUOffScreen *GPU_offscreen_create( void GPU_offscreen_free(GPUOffScreen *ofs); void GPU_offscreen_bind(GPUOffScreen *ofs, bool save); void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore); -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels); +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels); void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y); int GPU_offscreen_width(const GPUOffScreen *ofs); int GPU_offscreen_height(const GPUOffScreen *ofs); diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h index 698c4585d20..869c1fff29d 100644 --- a/source/blender/gpu/GPU_immediate.h +++ b/source/blender/gpu/GPU_immediate.h @@ -42,7 +42,7 @@ extern "C" { GPUVertFormat *immVertexFormat(void); /** Every immBegin must have a program bound first. */ -void immBindProgram(uint32_t program, const GPUShaderInterface *); +void immBindShader(GPUShader *shader); /** Call after your last immEnd, or before binding another program. */ void immUnbindProgram(void); @@ -134,7 +134,7 @@ void immUniformColor3ubvAlpha(const unsigned char rgb[3], unsigned char a); void immUniformColor4ubv(const unsigned char rgba[4]); /** - * Extend #immBindProgram to use Blender’s library of built-in shader programs. + * Extend #immBindShader to use Blender’s library of built-in shader programs. * Use #immUnbindProgram() when done. */ void immBindBuiltinProgram(eGPUBuiltinShader shader_id); diff --git a/source/blender/gpu/GPU_init_exit.h b/source/blender/gpu/GPU_init_exit.h index 3e30a1ddcf5..58b9505cdb1 100644 --- a/source/blender/gpu/GPU_init_exit.h +++ b/source/blender/gpu/GPU_init_exit.h @@ -32,7 +32,7 @@ extern "C" { void GPU_init(void); void GPU_exit(void); -bool GPU_is_initialized(void); +bool GPU_is_init(void); #ifdef __cplusplus } diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index c372bfaf218..7512fac6410 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -110,6 +110,7 @@ typedef enum eGPUMatFlag { GPU_MATFLAG_GLOSSY = (1 << 1), GPU_MATFLAG_REFRACT = (1 << 2), GPU_MATFLAG_SSS = (1 << 3), + GPU_MATFLAG_BARYCENTRIC = (1 << 4), } eGPUMatFlag; typedef enum eGPUBlendMode { @@ -137,6 +138,13 @@ typedef enum eGPUMaterialStatus { GPU_MAT_SUCCESS, } eGPUMaterialStatus; +typedef void (*GPUMaterialEvalCallbackFn)(GPUMaterial *mat, + int options, + const char **vert_code, + const char **geom_code, + const char **frag_lib, + const char **defines); + GPUNodeLink *GPU_constant(const float *num); GPUNodeLink *GPU_uniform(const float *num); GPUNodeLink *GPU_attribute(GPUMaterial *mat, CustomDataType type, const char *name); @@ -190,7 +198,8 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene, const char *geom_code, const char *frag_lib, const char *defines, - const char *name); + const char *name, + GPUMaterialEvalCallbackFn callback); void GPU_material_compile(GPUMaterial *mat); void GPU_material_free(struct ListBase *gpumaterial); diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h index f199a748cb5..104d5ef0ddc 100644 --- a/source/blender/gpu/GPU_platform.h +++ b/source/blender/gpu/GPU_platform.h @@ -25,10 +25,7 @@ #define __GPU_PLATFORM_H__ #include "BLI_sys_types.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "BLI_utildefines.h" /* GPU platform support */ @@ -43,6 +40,8 @@ typedef enum eGPUDeviceType { GPU_DEVICE_ANY = (0xff), } eGPUDeviceType; +ENUM_OPERATORS(eGPUDeviceType) + typedef enum eGPUOSType { GPU_OS_WIN = (1 << 8), GPU_OS_MAC = (1 << 9), @@ -63,6 +62,10 @@ typedef enum eGPUSupportLevel { GPU_SUPPORT_LEVEL_UNSUPPORTED, } eGPUSupportLevel; +#ifdef __cplusplus +extern "C" { +#endif + bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver); eGPUSupportLevel GPU_platform_support_level(void); const char *GPU_platform_support_level_key(void); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index a19ed2d84fd..b51a63a1c1f 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -87,8 +87,6 @@ void GPU_shader_transform_feedback_disable(GPUShader *shader); int GPU_shader_get_program(GPUShader *shader); -void *GPU_shader_get_interface(GPUShader *shader); - void GPU_shader_set_srgb_uniform(const struct GPUShaderInterface *interface); int GPU_shader_get_uniform(GPUShader *shader, const char *name); diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 8d50330ac93..a71b44507ba 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -87,6 +87,7 @@ bool GPU_depth_mask_get(void); void GPU_stencil_mask(uint stencil); void GPU_unpack_row_length_set(uint len); void GPU_clip_distances(int enabled_len); +bool GPU_mipmap_enabled(void); void GPU_flush(void); void GPU_finish(void); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index de08391b40c..1fbcfd41dec 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -25,6 +25,7 @@ #define __GPU_TEXTURE_H__ #include "BLI_utildefines.h" + #include "GPU_state.h" struct GPUVertBuf; @@ -43,7 +44,6 @@ typedef struct GPUTexture GPUTexture; * - Internally used by textures. * - All states are created at startup to avoid runtime costs. */ - typedef enum eGPUSamplerState { GPU_SAMPLER_FILTER = (1 << 0), GPU_SAMPLER_MIPMAP = (1 << 1), @@ -57,6 +57,8 @@ typedef enum eGPUSamplerState { GPU_SAMPLER_MAX = (1 << 8), } eGPUSamplerState; +ENUM_OPERATORS(eGPUSamplerState) + #ifdef __cplusplus extern "C" { #endif @@ -121,7 +123,6 @@ typedef enum eGPUTextureFormat { #if 0 GPU_RGB10_A2, GPU_RGB10_A2UI, - GPU_SRGB8_A8, #endif GPU_R11F_G11F_B10F, GPU_DEPTH32F_STENCIL8, @@ -150,7 +151,13 @@ typedef enum eGPUTextureFormat { GPU_R8_SNORM, #endif -/* Special formats texture only */ + /* Special formats texture only */ + GPU_SRGB8_A8_DXT1, + GPU_SRGB8_A8_DXT3, + GPU_SRGB8_A8_DXT5, + GPU_RGBA8_DXT1, + GPU_RGBA8_DXT3, + GPU_RGBA8_DXT5, #if 0 GPU_SRGB8, GPU_RGB9_E5, @@ -223,17 +230,10 @@ GPUTexture *GPU_texture_create_cube_array( GPUTexture *GPU_texture_create_from_vertbuf(struct GPUVertBuf *vert); GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat data_type, const uint buffer); -GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode); -GPUTexture *GPU_texture_from_blender(struct Image *ima, - struct ImageUser *iuser, - struct ImBuf *ibuf, - int textarget); +GPUTexture *GPU_texture_create_compressed( + int w, int h, int miplen, eGPUTextureFormat format, const void *data); -/* movie clip drawing */ -GPUTexture *GPU_texture_from_movieclip(struct MovieClip *clip, - struct MovieClipUser *cuser, - int textarget); -void GPU_free_texture_movieclip(struct MovieClip *clip); +GPUTexture *GPU_texture_create_error(int dimension, bool array); void GPU_texture_add_mipmap(GPUTexture *tex, eGPUDataFormat gpu_data_format, @@ -269,6 +269,7 @@ void GPU_texture_unbind_all(void); void GPU_texture_copy(GPUTexture *dst, GPUTexture *src); void GPU_texture_generate_mipmap(GPUTexture *tex); +void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso); void GPU_texture_compare_mode(GPUTexture *tex, bool use_compare); void GPU_texture_filter_mode(GPUTexture *tex, bool use_filter); void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter); diff --git a/source/blender/gpu/GPU_uniformbuffer.h b/source/blender/gpu/GPU_uniformbuffer.h index b221ae035d3..6862c1d960d 100644 --- a/source/blender/gpu/GPU_uniformbuffer.h +++ b/source/blender/gpu/GPU_uniformbuffer.h @@ -42,8 +42,7 @@ void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_); void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number); void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo); - -int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo); +void GPU_uniformbuffer_unbind_all(void); bool GPU_uniformbuffer_is_empty(GPUUniformBuffer *ubo); bool GPU_uniformbuffer_is_dirty(GPUUniformBuffer *ubo); diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index f9bdf726930..d1693852c1a 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -59,10 +59,10 @@ typedef struct GPUVertBuf { /** 0 indicates not yet allocated. */ uint32_t vbo_id; /** Usage hint for GL optimisation. */ - uint usage : 2; + GPUUsageType usage; /** Data has been touched and need to be reuploaded to GPU. */ - uint dirty : 1; - unsigned char *data; /* NULL indicates data in VRAM (unmapped) */ + bool dirty; + uchar *data; /* NULL indicates data in VRAM (unmapped) */ } GPUVertBuf; GPUVertBuf *GPU_vertbuf_create(GPUUsageType); diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index 34bfbb27823..cf0f52e3950 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -42,7 +42,7 @@ extern "C" { #define GPU_MAX_SAFE_ATTR_NAME 12 typedef enum { - GPU_COMP_I8, + GPU_COMP_I8 = 0, GPU_COMP_U8, GPU_COMP_I16, GPU_COMP_U16, @@ -52,17 +52,21 @@ typedef enum { GPU_COMP_F32, GPU_COMP_I10, + /* Warning! adjust GPUVertAttr if changing. */ } GPUVertCompType; typedef enum { - GPU_FETCH_FLOAT, + GPU_FETCH_FLOAT = 0, GPU_FETCH_INT, GPU_FETCH_INT_TO_FLOAT_UNIT, /* 127 (ubyte) -> 0.5 (and so on for other int types) */ GPU_FETCH_INT_TO_FLOAT, /* 127 (any int type) -> 127.0 */ + /* Warning! adjust GPUVertAttr if changing. */ } GPUVertFetchMode; typedef struct GPUVertAttr { + /* GPUVertFetchMode */ uint fetch_mode : 2; + /* GPUVertCompType */ uint comp_type : 3; /* 1 to 4 or 8 or 12 or 16 */ uint comp_len : 5; @@ -72,8 +76,6 @@ typedef struct GPUVertAttr { uint offset : 11; /* up to GPU_VERT_ATTR_MAX_NAMES */ uint name_len : 3; - uint gl_comp_type; - /* -- 8 Bytes -- */ uchar names[GPU_VERT_ATTR_MAX_NAMES]; } GPUVertAttr; diff --git a/source/blender/gpu/intern/gpu_attr_binding.c b/source/blender/gpu/intern/gpu_attr_binding.cc index 6cb60884620..6cb60884620 100644 --- a/source/blender/gpu/intern/gpu_attr_binding.c +++ b/source/blender/gpu/intern/gpu_attr_binding.cc diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/intern/gpu_attr_binding_private.h index 301ec3333dd..7df403a3ea5 100644 --- a/source/blender/gpu/intern/gpu_attr_binding_private.h +++ b/source/blender/gpu/intern/gpu_attr_binding_private.h @@ -29,6 +29,11 @@ #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); void get_attr_locations(const GPUVertFormat *format, @@ -36,4 +41,8 @@ void get_attr_locations(const GPUVertFormat *format, const GPUShaderInterface *shaderface); uint read_attr_location(const GPUAttrBinding *binding, uint a_idx); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_ATTR_BINDING_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_batch.c b/source/blender/gpu/intern/gpu_batch.cc index 5f77f13c135..9f9adcacfa6 100644 --- a/source/blender/gpu/intern/gpu_batch.c +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -37,6 +37,7 @@ #include "gpu_context_private.h" #include "gpu_primitive_private.h" #include "gpu_shader_private.h" +#include "gpu_vertex_format_private.h" #include <limits.h> #include <stdlib.h> @@ -89,7 +90,7 @@ GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type, GPUIndexBuf *elem, uint owns_flag) { - GPUBatch *batch = MEM_callocN(sizeof(GPUBatch), "GPUBatch"); + GPUBatch *batch = (GPUBatch *)MEM_callocN(sizeof(GPUBatch), "GPUBatch"); GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag); return batch; } @@ -327,10 +328,10 @@ static GLuint batch_vao_get(GPUBatch *batch) } /* 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 = MEM_callocN( + batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN( batch->dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces"); - batch->dynamic_vaos.vao_ids = MEM_callocN(batch->dynamic_vaos.count * sizeof(GLuint), - "dyn vaos ids"); + batch->dynamic_vaos.vao_ids = (GLuint *)MEM_callocN( + batch->dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids"); } } @@ -346,11 +347,11 @@ static GLuint batch_vao_get(GPUBatch *batch) /* 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 = MEM_recallocN((void *)batch->dynamic_vaos.interfaces, - sizeof(GPUShaderInterface *) * - batch->dynamic_vaos.count); - batch->dynamic_vaos.vao_ids = MEM_recallocN(batch->dynamic_vaos.vao_ids, - sizeof(GLuint) * batch->dynamic_vaos.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(); @@ -370,22 +371,20 @@ static GLuint batch_vao_get(GPUBatch *batch) return new_vao; } -void GPU_batch_program_set_no_use(GPUBatch *batch, - uint32_t program, - const GPUShaderInterface *shaderface) +void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader) { #if TRUST_NO_ONE - assert(glIsProgram(program)); + assert(glIsProgram(shader->program)); assert(batch->program_in_use == 0); #endif - batch->interface = shaderface; - batch->program = program; + batch->interface = shader->interface; + batch->program = shader->program; batch->vao_id = batch_vao_get(batch); } -void GPU_batch_program_set(GPUBatch *batch, uint32_t program, const GPUShaderInterface *shaderface) +void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader) { - GPU_batch_program_set_no_use(batch, program, shaderface); + GPU_batch_set_shader_no_bind(batch, shader); GPU_batch_program_use_begin(batch); /* hack! to make Batch_Uniform* simpler */ } @@ -440,6 +439,7 @@ static void create_bindings(GPUVertBuf *verts, } 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); @@ -452,19 +452,13 @@ static void create_bindings(GPUVertBuf *verts, *attr_mask &= ~(1 << input->location); if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) { -#if TRUST_NO_ONE - assert(a->fetch_mode == GPU_FETCH_FLOAT); - assert(a->gl_comp_type == GL_FLOAT); -#endif + 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, - a->gl_comp_type, - GL_FALSE, - stride, - (const GLubyte *)pointer + i * 16); + glVertexAttribPointer( + input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16); } } else { @@ -474,15 +468,13 @@ static void create_bindings(GPUVertBuf *verts, switch (a->fetch_mode) { case GPU_FETCH_FLOAT: case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer( - input->location, a->comp_len, a->gl_comp_type, GL_FALSE, stride, pointer); + 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, a->gl_comp_type, GL_TRUE, stride, pointer); + glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer); break; case GPU_FETCH_INT: - glVertexAttribIPointer(input->location, a->comp_len, a->gl_comp_type, stride, pointer); + glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); break; } } @@ -839,7 +831,7 @@ struct GPUDrawList { GPUDrawList *GPU_draw_list_create(int length) { - GPUDrawList *list = MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); + 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) { @@ -848,7 +840,7 @@ GPUDrawList *GPU_draw_list_create(int length) glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW); } else { - list->commands = MEM_mallocN(list->buffer_size, "GPUDrawList data"); + list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data"); } return list; } @@ -880,7 +872,7 @@ void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch) list->cmd_offset = 0; } GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; - list->commands = glMapBufferRange( + list->commands = (GPUDrawCommand *)glMapBufferRange( GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags); } } @@ -989,17 +981,12 @@ void GPU_draw_list_submit(GPUDrawList *list) /** \name Utilities * \{ */ -void GPU_batch_program_set_shader(GPUBatch *batch, GPUShader *shader) -{ - GPU_batch_program_set(batch, shader->program, shader->interface); -} - void GPU_batch_program_set_builtin_with_config(GPUBatch *batch, eGPUBuiltinShader shader_id, eGPUShaderConfig sh_cfg) { GPUShader *shader = GPU_shader_get_builtin_shader_with_config(shader_id, sh_cfg); - GPU_batch_program_set(batch, shader->program, shader->interface); + GPU_batch_set_shader(batch, shader); } void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id) @@ -1012,10 +999,7 @@ void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id) * DO NOT DRAW WITH THE BATCH BEFORE CALLING immUnbindProgram. */ void GPU_batch_program_set_imm_shader(GPUBatch *batch) { - GLuint program; - GPUShaderInterface *interface; - immGetProgram(&program, &interface); - GPU_batch_program_set(batch, program, interface); + GPU_batch_set_shader(batch, immGetShader()); } /** \} */ diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 0a1d6f560b6..e15e4e08159 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -41,7 +41,6 @@ #include "BKE_material.h" #include "GPU_extensions.h" -#include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_uniformbuffer.h" @@ -56,8 +55,8 @@ #include <stdarg.h> #include <string.h> +extern char datatoc_gpu_shader_codegen_lib_glsl[]; extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; /* -------------------- GPUPass Cache ------------------ */ /** @@ -282,18 +281,15 @@ static const char *gpu_builtin_name(eGPUBuiltin builtin) static void codegen_set_unique_ids(GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *input; - GPUOutput *output; int id = 1; - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { /* set id for unique names of uniform variables */ input->id = id++; } - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { /* set id for unique names of tmp variables storing output */ output->id = id++; } @@ -307,17 +303,10 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *input; const char *name; int builtins = 0; ListBase ubo_inputs = {NULL, NULL}; - /* Attributes */ - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - BLI_dynstr_appendf(ds, "in %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); - } - /* Textures */ LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) { if (tex->colorband) { @@ -339,8 +328,9 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, } /* Print other uniforms */ - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_BUILTIN) { /* only define each builtin uniform/varying once */ if (!(builtins & input->builtin)) { @@ -382,8 +372,8 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME); LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) { - input = link->data; - BLI_dynstr_appendf(ds, "\t%s unf%d;\n", gpu_data_type_to_string(input->type), input->id); + GPUInput *input = (GPUInput *)(link->data); + BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id); } BLI_dynstr_append(ds, "};\n"); BLI_freelistN(&ubo_inputs); @@ -396,34 +386,26 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph) { - GPUNode *node; - GPUOutput *output; - - for (node = graph->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { /* declare temporary variables for node output storage */ - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { if (output->type == GPU_CLOSURE) { - BLI_dynstr_appendf(ds, "\tClosure tmp%d;\n", output->id); + BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id); } else { - BLI_dynstr_appendf(ds, "\t%s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); + BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); } } } - BLI_dynstr_append(ds, "\n"); } static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *finaloutput) { - GPUNode *node; - GPUInput *input; - GPUOutput *output; - - for (node = graph->nodes.first; node; node = node->next) { - BLI_dynstr_appendf(ds, "\t%s(", node->name); + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + BLI_dynstr_appendf(ds, " %s(", node->name); - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_TEX) { BLI_dynstr_append(ds, input->texture->sampler_name); } @@ -504,7 +486,7 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f BLI_dynstr_append(ds, ", "); } - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { BLI_dynstr_appendf(ds, "tmp%d", output->id); if (output->next) { BLI_dynstr_append(ds, ", "); @@ -514,21 +496,24 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f BLI_dynstr_append(ds, ");\n"); } - BLI_dynstr_appendf(ds, "\n\treturn tmp%d", finaloutput->id); - BLI_dynstr_append(ds, ";\n"); + BLI_dynstr_appendf(ds, "\n return tmp%d;\n", finaloutput->id); } -static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) +static char *code_generate_fragment(GPUMaterial *material, + GPUNodeGraph *graph, + const char *interface_str) { DynStr *ds = BLI_dynstr_new(); char *code; int builtins; -#if 0 - BLI_dynstr_append(ds, FUNCTION_PROTOTYPES); -#endif - codegen_set_unique_ids(graph); + + /* Attributes, Shader stage interface. */ + if (interface_str) { + BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str); + } + builtins = codegen_process_uniforms_functions(material, ds, graph); if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) { @@ -536,73 +521,61 @@ static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) } if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "in vec2 barycentricTexCo;\n"); - } - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "flat in vec3 barycentricDist;\n"); + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); } BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n"); if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, - "\tvec2 barytexco = vec2((fract(barycentricTexCo.y) != 0.0)\n" - "\t ? barycentricTexCo.x\n" - "\t : 1.0 - barycentricTexCo.x,\n" - "\t 0.0);\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tvec2 barytexco = barycentricTexCo;\n"); - BLI_dynstr_append(ds, "#endif\n"); + BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n"); } /* TODO(fclem) get rid of that. */ if (builtins & GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define viewmat ViewMatrix\n"); + BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n"); } if (builtins & GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, "\t#define camtexfac CameraTexCoFactors\n"); + BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n"); } if (builtins & GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "\t#define objmat ModelMatrix\n"); + BLI_dynstr_append(ds, " #define objmat ModelMatrix\n"); } if (builtins & GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "\t#define objinv ModelMatrixInverse\n"); + BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n"); } if (builtins & GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define viewinv ViewMatrixInverse\n"); + BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n"); } if (builtins & GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define localtoviewmat (ViewMatrix * ModelMatrix)\n"); + BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n"); } if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) { BLI_dynstr_append(ds, - "\t#define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); + " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); } if (builtins & GPU_VIEW_NORMAL) { BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tvec3 n;\n"); - BLI_dynstr_append(ds, "\tworld_normals_get(n);\n"); - BLI_dynstr_append(ds, "\tvec3 facingnormal = transform_direction(ViewMatrix, n);\n"); + BLI_dynstr_append(ds, " vec3 n;\n"); + BLI_dynstr_append(ds, " world_normals_get(n);\n"); + BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n"); BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); + BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); BLI_dynstr_append(ds, "#endif\n"); } if (builtins & GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, "\tvec3 facingwnormal;\n"); + BLI_dynstr_append(ds, " vec3 facingwnormal;\n"); if (builtins & GPU_VIEW_NORMAL) { BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tfacingwnormal = n;\n"); + BLI_dynstr_append(ds, " facingwnormal = n;\n"); BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tworld_normals_get(facingwnormal);\n"); + BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); BLI_dynstr_append(ds, "#endif\n"); } else { - BLI_dynstr_append(ds, "\tworld_normals_get(facingwnormal);\n"); + BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); } } if (builtins & GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, "\t#define viewposition viewPosition\n"); + BLI_dynstr_append(ds, " #define viewposition viewPosition\n"); } codegen_declare_tmps(ds, graph); @@ -610,21 +583,6 @@ static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) BLI_dynstr_append(ds, "}\n"); - /* XXX This cannot go into gpu_shader_material.glsl because main() - * would be parsed and generate error */ - /* Old glsl mode compat. */ - /* TODO(fclem) This is only used by world shader now. get rid of it? */ - BLI_dynstr_append(ds, "#ifndef NODETREE_EXEC\n"); - BLI_dynstr_append(ds, "out vec4 fragColor;\n"); - BLI_dynstr_append(ds, "void main()\n"); - BLI_dynstr_append(ds, "{\n"); - BLI_dynstr_append(ds, "\tClosure cl = nodetree_exec();\n"); - BLI_dynstr_append(ds, - "\tfragColor = vec4(cl.radiance, " - "saturate(1.0 - avg(cl.transmittance)));\n"); - BLI_dynstr_append(ds, "}\n"); - BLI_dynstr_append(ds, "#endif\n\n"); - /* create shader */ code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -659,23 +617,48 @@ static const char *attr_prefix_get(CustomDataType type) } } -static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bool use_geom) +/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */ +static char *code_generate_interface(GPUNodeGraph *graph, int builtins) { + if (BLI_listbase_is_empty(&graph->attributes) && + (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) { + return NULL; + } + DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; - char *code; - int builtins = 0; - /* Hairs uv and col attributes are passed by bufferTextures. */ - BLI_dynstr_append(ds, - "#ifdef HAIR_SHADER\n" - "#define DEFINE_ATTR(type, attr) uniform samplerBuffer attr\n" - "#else\n" - "#define DEFINE_ATTR(type, attr) in type attr\n" - "#endif\n"); + BLI_dynstr_append(ds, "\n"); + + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { + BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + } + if (builtins & GPU_BARYCENTRIC_TEXCO) { + BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n"); + } + if (builtins & GPU_BARYCENTRIC_DIST) { + BLI_dynstr_append(ds, "vec3 barycentricDist;\n"); + } + + char *code = BLI_dynstr_get_cstring(ds); + + BLI_dynstr_free(ds); + return code; +} + +static char *code_generate_vertex(GPUNodeGraph *graph, + const char *interface_str, + const char *vert_code, + int builtins) +{ + DynStr *ds = BLI_dynstr_new(); + + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); + + /* Inputs */ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { + const char *type_str = gpu_data_type_to_string(attr->gputype); + const char *prefix = attr_prefix_get(attr->type); /* XXX FIXME : see notes in mesh_render_data_create() */ /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ if (attr->type == CD_ORCO) { @@ -684,188 +667,58 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } else if (attr->name[0] == '\0') { - BLI_dynstr_appendf(ds, - "DEFINE_ATTR(%s, %s);\n", - gpu_data_type_to_string(attr->gputype), - attr_prefix_get(attr->type)); - BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, attr_prefix_get(attr->type)); + BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix); + BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix); } else { char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - BLI_dynstr_appendf(ds, - "DEFINE_ATTR(%s, %s%s);\n", - gpu_data_type_to_string(attr->gputype), - attr_prefix_get(attr->type), - attr_safe_name); - BLI_dynstr_appendf( - ds, "#define att%d %s%s\n", attr->id, attr_prefix_get(attr->type), attr_safe_name); + BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name); + BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name); } - BLI_dynstr_appendf(ds, - "out %s var%d%s;\n", - gpu_data_type_to_string(attr->gputype), - attr->id, - use_geom ? "g" : ""); - } - - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_appendf(ds, "out vec2 barycentricTexCo%s;\n", use_geom ? "g" : ""); - BLI_dynstr_append(ds, "#endif\n"); } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "out vec3 barycentricPosg;\n"); + /* Outputs interface */ + if (interface_str) { + BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str); } - BLI_dynstr_append(ds, "\n#define USE_ATTR\n"); - - /* Prototype, defined later (this is because of matrices definition). */ - BLI_dynstr_append(ds, "void pass_attr(in vec3 position);\n"); - - BLI_dynstr_append(ds, "\n"); - - if (use_geom) { - /* XXX HACK: Eevee specific. */ - char *vert_new, *vert_new2; - vert_new = BLI_str_replaceN(vert_code, "worldPosition", "worldPositiong"); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "viewPosition", "viewPositiong"); - MEM_freeN(vert_new2); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "worldNormal", "worldNormalg"); - MEM_freeN(vert_new2); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "viewNormal", "viewNormalg"); - MEM_freeN(vert_new2); - - BLI_dynstr_append(ds, vert_new); - - MEM_freeN(vert_new); - } - else { - BLI_dynstr_append(ds, vert_code); - } + /* Prototype. Needed for hair functions. */ + BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n"); + BLI_dynstr_append(ds, "#define USE_ATTR\n\n"); + BLI_dynstr_append(ds, vert_code); BLI_dynstr_append(ds, "\n"); - BLI_dynstr_append(ds, use_geom ? "RESOURCE_ID_VARYING_GEOM\n" : "RESOURCE_ID_VARYING\n"); - - /* Prototype because defined later. */ - BLI_dynstr_append(ds, - "vec2 hair_get_customdata_vec2(const samplerBuffer);\n" - "vec3 hair_get_customdata_vec3(const samplerBuffer);\n" - "vec4 hair_get_customdata_vec4(const samplerBuffer);\n" - "vec3 hair_get_strand_pos(void);\n" - "int hair_get_base_id(void);\n" - "\n"); - - BLI_dynstr_append(ds, "void pass_attr(in vec3 position) {\n"); - - BLI_dynstr_append(ds, use_geom ? "\tPASS_RESOURCE_ID_GEOM\n" : "\tPASS_RESOURCE_ID\n"); - - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - /* To match cycles without breaking into individual segment we encode if we need to invert - * the first component into the second component. We invert if the barycentricTexCo.y - * is NOT 0.0 or 1.0. */ - BLI_dynstr_append(ds, "\tint _base_id = hair_get_base_id();\n"); - BLI_dynstr_appendf( - ds, "\tbarycentricTexCo%s.x = float((_base_id %% 2) == 1);\n", use_geom ? "g" : ""); - BLI_dynstr_appendf( - ds, "\tbarycentricTexCo%s.y = float(((_base_id %% 4) %% 3) > 0);\n", use_geom ? "g" : ""); - } - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "\tbarycentricPosg = position;\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - if (attr->type == CD_TANGENT) { - /* Not supported by hairs */ - BLI_dynstr_appendf(ds, "\tvar%d%s = vec4(0.0);\n", attr->id, use_geom ? "g" : ""); - } - else if (attr->type == CD_ORCO) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = OrcoTexCoFactors[0].xyz + (ModelMatrixInverse * " - "vec4(hair_get_strand_pos(), 1.0)).xyz * OrcoTexCoFactors[1].xyz;\n", - attr->id, - use_geom ? "g" : ""); - /* TODO: fix ORCO with modifiers. */ - } - else { - BLI_dynstr_appendf(ds, - "\tvar%d%s = hair_get_customdata_%s(att%d);\n", - attr->id, - use_geom ? "g" : "", - gpu_data_type_to_string(attr->gputype), - attr->id); - } - } - - BLI_dynstr_append(ds, "#else /* MESH_SHADER */\n"); + BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n"); /* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID * for MESH_SHADER because of indexed drawing. In this case a * geometry shader is needed. */ - + if (builtins & GPU_BARYCENTRIC_TEXCO) { + BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n"); + } if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "\tbarycentricPosg = (ModelMatrix * vec4(position, 1.0)).xyz;\n"); + BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n"); } LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { if (attr->type == CD_TANGENT) { /* silly exception */ - BLI_dynstr_appendf(ds, - "\tvar%d%s.xyz = transpose(mat3(ModelMatrixInverse)) * att%d.xyz;\n", - attr->id, - use_geom ? "g" : "", - attr->id); - BLI_dynstr_appendf(ds, "\tvar%d%s.w = att%d.w;\n", attr->id, use_geom ? "g" : "", attr->id); - /* Normalize only if vector is not null. */ - BLI_dynstr_appendf(ds, - "\tfloat lvar%d = dot(var%d%s.xyz, var%d%s.xyz);\n", - attr->id, - attr->id, - use_geom ? "g" : "", - attr->id, - use_geom ? "g" : ""); - BLI_dynstr_appendf(ds, - "\tvar%d%s.xyz *= (lvar%d > 0.0) ? inversesqrt(lvar%d) : 1.0;\n", - attr->id, - use_geom ? "g" : "", - attr->id, - attr->id); + BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id); } else if (attr->type == CD_ORCO) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = OrcoTexCoFactors[0].xyz + position *" - " OrcoTexCoFactors[1].xyz;\n", - attr->id, - use_geom ? "g" : ""); - /* See mesh_create_loop_orco() for explanation. */ - BLI_dynstr_appendf(ds, - "\tif (orco.w == 0.0) { var%d%s = orco.xyz * 0.5 + 0.5; }\n", - attr->id, - use_geom ? "g" : ""); + BLI_dynstr_appendf( + ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id); } else { - BLI_dynstr_appendf(ds, "\tvar%d%s = att%d;\n", attr->id, use_geom ? "g" : "", attr->id); + const char *type_str = gpu_data_type_to_string(attr->gputype); + BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id); } } - BLI_dynstr_append(ds, "#endif /* HAIR_SHADER */\n"); BLI_dynstr_append(ds, "}\n"); - code = BLI_dynstr_get_cstring(ds); + char *code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -879,146 +732,46 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo } static char *code_generate_geometry(GPUNodeGraph *graph, + const char *interface_str, const char *geom_code, - const char *defines) + int builtins) { - DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; - char *code; - int builtins = 0; - - /* XXX we should not make specific eevee cases here. */ - bool is_hair_shader = (strstr(defines, "HAIR_SHADER") != NULL); - - /* Create prototype because attributes cannot be declared before layout. */ - BLI_dynstr_append(ds, "void pass_attr(in int vert);\n"); - BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2);\n"); - BLI_dynstr_append(ds, "#define USE_ATTR\n"); - - /* Generate varying declarations. */ - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - BLI_dynstr_appendf(ds, "in %s var%dg[];\n", gpu_data_type_to_string(attr->gputype), attr->id); - BLI_dynstr_appendf(ds, "out %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + if (!geom_code) { + return NULL; } - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "in vec2 barycentricTexCog[];\n"); - BLI_dynstr_append(ds, "#endif\n"); - - BLI_dynstr_append(ds, "out vec2 barycentricTexCo;\n"); - } + DynStr *ds = BLI_dynstr_new(); - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "in vec3 barycentricPosg[];\n"); - BLI_dynstr_append(ds, "flat out vec3 barycentricDist;\n"); + /* Attributes, Shader interface; */ + if (interface_str) { + BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str); + BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str); } - if (geom_code == NULL) { - /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used. - * Note: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */ - if ((builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0 || is_hair_shader) { - /* Early out */ - BLI_dynstr_free(ds); - return NULL; - } - else { - /* Force geom shader usage */ - /* TODO put in external file. */ - BLI_dynstr_append(ds, "layout(triangles) in;\n"); - BLI_dynstr_append(ds, "layout(triangle_strip, max_vertices=3) out;\n"); - - BLI_dynstr_append(ds, "in vec3 worldPositiong[];\n"); - BLI_dynstr_append(ds, "in vec3 viewPositiong[];\n"); - BLI_dynstr_append(ds, "in vec3 worldNormalg[];\n"); - BLI_dynstr_append(ds, "in vec3 viewNormalg[];\n"); - - BLI_dynstr_append(ds, "out vec3 worldPosition;\n"); - BLI_dynstr_append(ds, "out vec3 viewPosition;\n"); - BLI_dynstr_append(ds, "out vec3 worldNormal;\n"); - BLI_dynstr_append(ds, "out vec3 viewNormal;\n"); - - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - - BLI_dynstr_append(ds, "void main(){\n"); - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, - "\tcalc_barycentric_distances(barycentricPosg[0], barycentricPosg[1], " - "barycentricPosg[2]);\n"); - } - - for (int i = 0; i < 3; i++) { - BLI_dynstr_appendf(ds, "\tgl_Position = gl_in[%d].gl_Position;\n", i); - BLI_dynstr_appendf(ds, "\tgl_ClipDistance[0] = gl_in[%d].gl_ClipDistance[0];\n", i); - BLI_dynstr_appendf(ds, "\tpass_attr(%d);\n", i); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); - } - BLI_dynstr_append(ds, "}\n"); - } - } - else { - BLI_dynstr_append(ds, geom_code); - } + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2) {\n"); - BLI_dynstr_append(ds, "\tvec3 edge21 = pos2 - pos1;\n"); - BLI_dynstr_append(ds, "\tvec3 edge10 = pos1 - pos0;\n"); - BLI_dynstr_append(ds, "\tvec3 edge02 = pos0 - pos2;\n"); - BLI_dynstr_append(ds, "\tvec3 d21 = normalize(edge21);\n"); - BLI_dynstr_append(ds, "\tvec3 d10 = normalize(edge10);\n"); - BLI_dynstr_append(ds, "\tvec3 d02 = normalize(edge02);\n"); - - BLI_dynstr_append(ds, "\tfloat d = dot(d21, edge02);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.x = sqrt(dot(edge02, edge02) - d * d);\n"); - BLI_dynstr_append(ds, "\td = dot(d02, edge10);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.y = sqrt(dot(edge10, edge10) - d * d);\n"); - BLI_dynstr_append(ds, "\td = dot(d10, edge21);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.z = sqrt(dot(edge21, edge21) - d * d);\n"); - BLI_dynstr_append(ds, "}\n"); + /* geom_code should do something with this, but may not. */ + BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n"); } - BLI_dynstr_append(ds, "RESOURCE_ID_VARYING\n"); - /* Generate varying assignments. */ - BLI_dynstr_append(ds, "void pass_attr(in int vert) {\n"); - - BLI_dynstr_append(ds, "\tPASS_RESOURCE_ID(vert)\n"); - - /* XXX HACK: Eevee specific. */ - if (geom_code == NULL) { - BLI_dynstr_append(ds, "\tworldPosition = worldPositiong[vert];\n"); - BLI_dynstr_append(ds, "\tviewPosition = viewPositiong[vert];\n"); - BLI_dynstr_append(ds, "\tworldNormal = worldNormalg[vert];\n"); - BLI_dynstr_append(ds, "\tviewNormal = viewNormalg[vert];\n"); - } + BLI_dynstr_append(ds, "#define USE_ATTR\n"); + BLI_dynstr_append(ds, "void pass_attr(const int vert) {\n"); if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo = barycentricTexCog[vert];\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo.x = float((vert % 3) == 0);\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo.y = float((vert % 3) == 1);\n"); - BLI_dynstr_append(ds, "#endif\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, "\tvar%d = var%dg[vert];\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"); + BLI_dynstr_append(ds, "}\n\n"); - code = BLI_dynstr_get_cstring(ds); + BLI_dynstr_append(ds, geom_code); + + char *code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); return code; @@ -1048,8 +801,17 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, * generated VBOs are ready to accept the future shader. */ gpu_node_graph_prune_unused(graph); + int builtins = 0; + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + if (input->source == GPU_SOURCE_BUILTIN) { + builtins |= input->builtin; + } + } + } /* generate code */ - char *fragmentgen = code_generate_fragment(material, graph); + char *interface_str = code_generate_interface(graph, builtins); + char *fragmentgen = code_generate_fragment(material, graph, interface_str); /* Cache lookup: Reuse shaders already compiled */ uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes); @@ -1057,6 +819,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) { /* No collision, just return the pass. */ + MEM_SAFE_FREE(interface_str); MEM_freeN(fragmentgen); if (!gpu_pass_is_valid(pass_hash)) { /* Shader has already been created but failed to compile. */ @@ -1071,10 +834,11 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, GSet *used_libraries = gpu_material_used_libraries(material); char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib); - char *geometrycode = code_generate_geometry(graph, geom_code, defines); - char *vertexcode = code_generate_vertex(graph, vert_code, (geometrycode != NULL)); + char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins); + char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins); char *fragmentcode = BLI_strdupcat(tmp, fragmentgen); + MEM_SAFE_FREE(interface_str); MEM_freeN(fragmentgen); MEM_freeN(tmp); diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index ce20f495ba3..18ff4c3241d 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -26,6 +26,10 @@ #ifndef __GPU_CODEGEN_H__ #define __GPU_CODEGEN_H__ +#ifdef __cplusplus +extern "C" { +#endif + struct GPUMaterial; struct GPUNodeGraph; struct GPUOutput; @@ -68,4 +72,8 @@ void GPU_pass_release(GPUPass *pass); void gpu_codegen_init(void); void gpu_codegen_exit(void); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_CODEGEN_H__ */ diff --git a/source/blender/gpu/intern/gpu_context.cpp b/source/blender/gpu/intern/gpu_context.cc index 0b9104e5349..0b9104e5349 100644 --- a/source/blender/gpu/intern/gpu_context.cpp +++ b/source/blender/gpu/intern/gpu_context.cc diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.cc index f7d6236071d..f7d6236071d 100644 --- a/source/blender/gpu/intern/gpu_debug.c +++ b/source/blender/gpu/intern/gpu_debug.cc diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c deleted file mode 100644 index e5f49555265..00000000000 --- a/source/blender/gpu/intern/gpu_draw.c +++ /dev/null @@ -1,1469 +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 - * - * Utility functions for dealing with OpenGL texture & material context, - * mipmap generation and light objects. - * - * These are some obscure rendering functions shared between the game engine (not anymore) - * and the blender, in this module to avoid duplication - * and abstract them away from the rest a bit. - */ - -#include <string.h> - -#include "BLI_blenlib.h" -#include "BLI_boxpack_2d.h" -#include "BLI_linklist.h" -#include "BLI_math.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" - -#include "DNA_image_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_userdef_types.h" - -#include "MEM_guardedalloc.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_main.h" -#include "BKE_movieclip.h" - -#include "GPU_draw.h" -#include "GPU_extensions.h" -#include "GPU_glew.h" -#include "GPU_matrix.h" -#include "GPU_platform.h" -#include "GPU_texture.h" - -#include "PIL_time.h" - -static void gpu_free_image(Image *ima, const bool immediate); -static void gpu_free_unused_buffers(void); - -//* Checking powers of two for images since OpenGL ES requires it */ -#ifdef WITH_DDS -static bool is_power_of_2_resolution(int w, int h) -{ - return is_power_of_2_i(w) && is_power_of_2_i(h); -} -#endif - -static bool is_over_resolution_limit(GLenum textarget, int w, int h) -{ - int size = (textarget == GL_TEXTURE_CUBE_MAP) ? GPU_max_cube_map_size() : GPU_max_texture_size(); - int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; - - return (w > reslimit || h > reslimit); -} - -static int smaller_power_of_2_limit(int num) -{ - int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, GPU_max_texture_size()) : - GPU_max_texture_size(); - /* take texture clamping into account */ - if (num > reslimit) { - return reslimit; - } - - return power_of_2_min_i(num); -} - -/* Current OpenGL state caching for GPU_set_tpage */ - -static struct GPUTextureState { - /* also controls min/mag filtering */ - bool domipmap; - /* only use when 'domipmap' is set */ - bool linearmipmap; - /* store this so that new images created while texture painting won't be set to mipmapped */ - bool texpaint; - - float anisotropic; -} GTS = {1, 0, 0, 1.0f}; - -/* Mipmap settings */ - -void GPU_set_mipmap(Main *bmain, bool mipmap) -{ - if (GTS.domipmap != mipmap) { - GPU_free_images(bmain); - GTS.domipmap = mipmap; - } -} - -void GPU_set_linear_mipmap(bool linear) -{ - if (GTS.linearmipmap != linear) { - GTS.linearmipmap = linear; - } -} - -bool GPU_get_mipmap(void) -{ - return GTS.domipmap && !GTS.texpaint; -} - -bool GPU_get_linear_mipmap(void) -{ - return GTS.linearmipmap; -} - -static GLenum gpu_get_mipmap_filter(bool mag) -{ - /* linearmipmap is off by default *when mipmapping is off, - * use unfiltered display */ - if (mag) { - if (GTS.domipmap) { - return GL_LINEAR; - } - else { - return GL_NEAREST; - } - } - else { - if (GTS.domipmap) { - if (GTS.linearmipmap) { - return GL_LINEAR_MIPMAP_LINEAR; - } - else { - return GL_LINEAR_MIPMAP_NEAREST; - } - } - else { - return GL_NEAREST; - } - } -} - -/* Anisotropic filtering settings */ -void GPU_set_anisotropic(float value) -{ - if (GTS.anisotropic != value) { - GPU_samplers_free(); - - /* Clamp value to the maximum value the graphics card supports */ - const float max = GPU_max_texture_anisotropy(); - if (value > max) { - value = max; - } - - GTS.anisotropic = value; - - GPU_samplers_init(); - } -} - -float GPU_get_anisotropic(void) -{ - return GTS.anisotropic; -} - -/* Set OpenGL state for an MTFace */ - -static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget, const int multiview_eye) -{ - if (textarget == GL_TEXTURE_2D) { - return &(ima->gputexture[TEXTARGET_TEXTURE_2D][multiview_eye]); - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &(ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP][multiview_eye]); - } - else if (textarget == GL_TEXTURE_2D_ARRAY) { - return &(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]); - } - else if (textarget == GL_TEXTURE_1D_ARRAY) { - return &(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][multiview_eye]); - } - - return NULL; -} - -static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) -{ - GPUTexture *tilearray = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]; - - if (tilearray == NULL) { - return 0; - } - - float array_w = GPU_texture_width(tilearray); - float array_h = GPU_texture_height(tilearray); - - ImageTile *last_tile = ima->tiles.last; - /* Tiles are sorted by number. */ - int max_tile = last_tile->tile_number - 1001; - - /* create image */ - int bindcode; - glGenTextures(1, (GLuint *)&bindcode); - glBindTexture(GL_TEXTURE_1D_ARRAY, bindcode); - - int width = max_tile + 1; - float *data = MEM_callocN(width * 8 * sizeof(float), __func__); - for (int i = 0; i < width; i++) { - data[4 * i] = -1.0f; - } - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int i = tile->tile_number - 1001; - data[4 * i] = tile->runtime.tilearray_layer; - - float *tile_info = &data[4 * width + 4 * i]; - tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; - tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; - tile_info[2] = tile->runtime.tilearray_size[0] / array_w; - tile_info[3] = tile->runtime.tilearray_size[1] / array_h; - } - - glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA32F, width, 2, 0, GL_RGBA, GL_FLOAT, data); - MEM_freeN(data); - - glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glBindTexture(GL_TEXTURE_1D_ARRAY, 0); - - return bindcode; -} - -typedef struct PackTile { - FixedSizeBoxPack boxpack; - ImageTile *tile; - float pack_score; -} PackTile; - -static int compare_packtile(const void *a, const void *b) -{ - const PackTile *tile_a = a; - const PackTile *tile_b = b; - - return tile_a->pack_score < tile_b->pack_score; -} - -static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) -{ - int arraywidth = 0, arrayheight = 0; - - ListBase boxes = {NULL}; - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - ImageUser iuser; - BKE_imageuser_default(&iuser); - iuser.tile = tile->tile_number; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - - if (ibuf) { - PackTile *packtile = MEM_callocN(sizeof(PackTile), __func__); - packtile->tile = tile; - packtile->boxpack.w = ibuf->x; - packtile->boxpack.h = ibuf->y; - - if (is_over_resolution_limit( - GL_TEXTURE_2D_ARRAY, packtile->boxpack.w, packtile->boxpack.h)) { - packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); - packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); - } - arraywidth = max_ii(arraywidth, packtile->boxpack.w); - arrayheight = max_ii(arrayheight, packtile->boxpack.h); - - /* We sort the tiles by decreasing size, with an additional penalty term - * for high aspect ratios. This improves packing efficiency. */ - float w = packtile->boxpack.w, h = packtile->boxpack.h; - packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h; - - BKE_image_release_ibuf(ima, ibuf, NULL); - BLI_addtail(&boxes, packtile); - } - } - - BLI_assert(arraywidth > 0 && arrayheight > 0); - - BLI_listbase_sort(&boxes, compare_packtile); - int arraylayers = 0; - /* Keep adding layers until all tiles are packed. */ - while (boxes.first != NULL) { - ListBase packed = {NULL}; - BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed); - BLI_assert(packed.first != NULL); - - LISTBASE_FOREACH (PackTile *, packtile, &packed) { - ImageTile *tile = packtile->tile; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - - tileoffset[0] = packtile->boxpack.x; - tileoffset[1] = packtile->boxpack.y; - tilesize[0] = packtile->boxpack.w; - tilesize[1] = packtile->boxpack.h; - tile->runtime.tilearray_layer = arraylayers; - } - - BLI_freelistN(&packed); - arraylayers++; - } - - /* create image */ - int bindcode; - glGenTextures(1, (GLuint *)&bindcode); - glBindTexture(GL_TEXTURE_2D_ARRAY, bindcode); - - GLenum data_type, internal_format; - if (main_ibuf->rect_float) { - data_type = GL_FLOAT; - internal_format = (!(main_ibuf->flags & IB_halffloat) && (ima->flag & IMA_HIGH_BITDEPTH)) ? - GL_RGBA32F : - GL_RGBA16F; - } - else { - data_type = GL_UNSIGNED_BYTE; - internal_format = GL_RGBA8; - if (!IMB_colormanagement_space_is_data(main_ibuf->rect_colorspace) && - !IMB_colormanagement_space_is_scene_linear(main_ibuf->rect_colorspace)) { - internal_format = GL_SRGB8_ALPHA8; - } - } - - glTexImage3D(GL_TEXTURE_2D_ARRAY, - 0, - internal_format, - arraywidth, - arrayheight, - arraylayers, - 0, - GL_RGBA, - data_type, - NULL); - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int tilelayer = tile->runtime.tilearray_layer; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - - if (tilesize[0] == 0 || tilesize[1] == 0) { - continue; - } - - ImageUser iuser; - BKE_imageuser_default(&iuser); - iuser.tile = tile->tile_number; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - - if (ibuf) { - bool needs_scale = (ibuf->x != tilesize[0] || ibuf->y != tilesize[1]); - - ImBuf *scale_ibuf = NULL; - if (ibuf->rect_float) { - float *rect_float = ibuf->rect_float; - - const bool store_premultiplied = ima->alpha_mode != IMA_ALPHA_STRAIGHT; - if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); - IMB_colormanagement_imbuf_to_float_texture( - rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); - } - - float *pixeldata = rect_float; - if (needs_scale) { - scale_ibuf = IMB_allocFromBuffer(NULL, rect_float, ibuf->x, ibuf->y, 4); - IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); - pixeldata = scale_ibuf->rect_float; - } - - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - tileoffset[0], - tileoffset[1], - tilelayer, - tilesize[0], - tilesize[1], - 1, - GL_RGBA, - GL_FLOAT, - pixeldata); - - if (rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - } - else { - unsigned int *rect = ibuf->rect; - - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); - IMB_colormanagement_imbuf_to_byte_texture((uchar *)rect, - 0, - 0, - ibuf->x, - ibuf->y, - ibuf, - internal_format == GL_SRGB8_ALPHA8, - ima->alpha_mode == IMA_ALPHA_PREMUL); - } - - unsigned int *pixeldata = rect; - if (needs_scale) { - scale_ibuf = IMB_allocFromBuffer(rect, NULL, ibuf->x, ibuf->y, 4); - IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); - pixeldata = scale_ibuf->rect; - } - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - tileoffset[0], - tileoffset[1], - tilelayer, - tilesize[0], - tilesize[1], - 1, - GL_RGBA, - GL_UNSIGNED_BYTE, - pixeldata); - - if (rect != ibuf->rect) { - MEM_freeN(rect); - } - } - if (scale_ibuf != NULL) { - IMB_freeImBuf(scale_ibuf); - } - } - - BKE_image_release_ibuf(ima, ibuf, NULL); - } - - if (GPU_get_mipmap()) { - glGenerateMipmap(GL_TEXTURE_2D_ARRAY); - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - - return bindcode; -} - -static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) -{ - uint bindcode = 0; - const bool mipmap = GPU_get_mipmap(); - const bool half_float = (ibuf->flags & IB_halffloat) != 0; - -#ifdef WITH_DDS - if (ibuf->ftype == IMB_FTYPE_DDS) { - /* DDS is loaded directly in compressed form. */ - GPU_create_gl_tex_compressed(&bindcode, textarget, ima, ibuf); - return bindcode; - } -#endif - - /* Regular uncompressed texture. */ - float *rect_float = ibuf->rect_float; - uchar *rect = (uchar *)ibuf->rect; - bool compress_as_srgb = false; - - if (rect_float == NULL) { - /* Byte image is in original colorspace from the file. If the file is sRGB - * scene linear, or non-color data no conversion is needed. Otherwise we - * compress as scene linear + sRGB transfer function to avoid precision loss - * in common cases. - * - * We must also convert to premultiplied for correct texture interpolation - * and consistency with float images. */ - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace); - - rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); - if (rect == NULL) { - return bindcode; - } - - /* Texture storage of images is defined by the alpha mode of the image. The - * downside of this is that there can be artifacts near alpha edges. However, - * this allows us to use sRGB texture formats and preserves color values in - * zero alpha areas, and appears generally closer to what game engines that we - * want to be compatible with do. */ - const bool store_premultiplied = ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true; - IMB_colormanagement_imbuf_to_byte_texture( - rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); - } - } - else { - /* Float image is already in scene linear colorspace or non-color data by - * convention, no colorspace conversion needed. But we do require 4 channels - * currently. */ - const bool store_premultiplied = ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false; - - if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); - if (rect_float == NULL) { - return bindcode; - } - IMB_colormanagement_imbuf_to_float_texture( - rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); - } - } - - /* Create OpenGL texture. */ - GPU_create_gl_tex(&bindcode, - (uint *)rect, - rect_float, - ibuf->x, - ibuf->y, - textarget, - mipmap, - half_float, - compress_as_srgb, - ima); - - /* Free buffers if needed. */ - if (rect && rect != (uchar *)ibuf->rect) { - MEM_freeN(rect); - } - if (rect_float && rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - - return bindcode; -} - -static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip, - MovieClipUser *cuser, - GLenum textarget) -{ - MovieClip_RuntimeGPUTexture *tex; - for (tex = clip->runtime.gputextures.first; tex; tex = tex->next) { - if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) { - break; - } - } - - if (tex == NULL) { - tex = MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), __func__); - - for (int i = 0; i < TEXTARGET_COUNT; i++) { - tex->gputexture[i] = NULL; - } - - memcpy(&tex->user, cuser, sizeof(MovieClipUser)); - BLI_addtail(&clip->runtime.gputextures, tex); - } - - if (textarget == GL_TEXTURE_2D) { - return &tex->gputexture[TEXTARGET_TEXTURE_2D]; - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &tex->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; - } - - return NULL; -} - -static ImBuf *update_do_scale(uchar *rect, - float *rect_float, - int *x, - int *y, - int *w, - int *h, - int limit_w, - int limit_h, - int full_w, - int full_h) -{ - /* Partial update with scaling. */ - float xratio = limit_w / (float)full_w; - float yratio = limit_h / (float)full_h; - - int part_w = *w, part_h = *h; - - /* Find sub coordinates in scaled image. Take ceiling because we will be - * losing 1 pixel due to rounding errors in x,y. */ - *x *= xratio; - *y *= yratio; - *w = (int)ceil(xratio * (*w)); - *h = (int)ceil(yratio * (*h)); - - /* ...but take back if we are over the limit! */ - if (*x + *w > limit_w) { - (*w)--; - } - if (*y + *h > limit_h) { - (*h)--; - } - - /* Scale pixels. */ - ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4); - IMB_scaleImBuf(ibuf, *w, *h); - - return ibuf; -} - -static void gpu_texture_update_scaled_array(uchar *rect, - float *rect_float, - int full_w, - int full_h, - int x, - int y, - int layer, - const int *tile_offset, - const int *tile_size, - int w, - int h) -{ - ImBuf *ibuf = update_do_scale( - rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h); - - /* Shift to account for tile packing. */ - x += tile_offset[0]; - y += tile_offset[1]; - - if (ibuf->rect_float) { - glTexSubImage3D( - GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_FLOAT, ibuf->rect_float); - } - else { - glTexSubImage3D( - GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); - } - - IMB_freeImBuf(ibuf); -} - -static void gpu_texture_update_scaled( - uchar *rect, float *rect_float, int full_w, int full_h, int x, int y, int w, int h) -{ - /* Partial update with scaling. */ - int limit_w = smaller_power_of_2_limit(full_w); - int limit_h = smaller_power_of_2_limit(full_h); - - ImBuf *ibuf = update_do_scale( - rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h); - - if (ibuf->rect_float) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, ibuf->rect_float); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); - } - - IMB_freeImBuf(ibuf); -} - -static void gpu_texture_update_unscaled(uchar *rect, - float *rect_float, - int x, - int y, - int layer, - int w, - int h, - GLint tex_stride, - GLint tex_offset) -{ - /* Partial update without scaling. Stride and offset are used to copy only a - * subset of a possible larger buffer than what we are updating. */ - GPU_unpack_row_length_set(tex_stride); - - if (layer >= 0) { - if (rect_float == NULL) { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - x, - y, - layer, - w, - h, - 1, - GL_RGBA, - GL_UNSIGNED_BYTE, - rect + tex_offset); - } - else { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - x, - y, - layer, - w, - h, - 1, - GL_RGBA, - GL_FLOAT, - rect_float + tex_offset); - } - } - else { - if (rect_float == NULL) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset); - } - } - - /* Restore default. */ - GPU_unpack_row_length_set(0); -} - -static void gpu_texture_update_from_ibuf( - GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) -{ - /* Partial update of texture for texture painting. This is often much - * quicker than fully updating the texture for high resolution images. */ - GPU_texture_bind(tex, 0); - - bool scaled; - if (tile != NULL) { - int *tilesize = tile->runtime.tilearray_size; - scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); - } - else { - scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y); - } - - if (scaled) { - /* Extra padding to account for bleed from neighboring pixels. */ - const int padding = 4; - const int xmax = min_ii(x + w + padding, ibuf->x); - const int ymax = min_ii(y + h + padding, ibuf->y); - x = max_ii(x - padding, 0); - y = max_ii(y - padding, 0); - w = xmax - x; - h = ymax - y; - } - - /* Get texture data pointers. */ - float *rect_float = ibuf->rect_float; - uchar *rect = (uchar *)ibuf->rect; - GLint tex_stride = ibuf->x; - GLint tex_offset = ibuf->channels * (y * ibuf->x + x); - - if (rect_float == NULL) { - /* Byte pixels. */ - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear( - ibuf->rect_colorspace); - - rect = MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__); - if (rect == NULL) { - return; - } - - tex_stride = w; - tex_offset = 0; - - /* Convert to scene linear with sRGB compression, and premultiplied for - * correct texture interpolation. */ - const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL); - IMB_colormanagement_imbuf_to_byte_texture( - rect, x, y, w, h, ibuf, compress_as_srgb, store_premultiplied); - } - } - else { - /* Float pixels. */ - const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT); - - if (ibuf->channels != 4 || scaled || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * w * h, __func__); - if (rect_float == NULL) { - return; - } - - tex_stride = w; - tex_offset = 0; - - IMB_colormanagement_imbuf_to_float_texture( - rect_float, x, y, w, h, ibuf, store_premultiplied); - } - } - - if (scaled) { - /* Slower update where we first have to scale the input pixels. */ - if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - int tilelayer = tile->runtime.tilearray_layer; - gpu_texture_update_scaled_array( - rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); - } - else { - gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h); - } - } - else { - /* Fast update at same resolution. */ - if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int tilelayer = tile->runtime.tilearray_layer; - gpu_texture_update_unscaled(rect, - rect_float, - x + tileoffset[0], - y + tileoffset[1], - tilelayer, - w, - h, - tex_stride, - tex_offset); - } - else { - gpu_texture_update_unscaled(rect, rect_float, x, y, -1, w, h, tex_stride, tex_offset); - } - } - - /* Free buffers if needed. */ - if (rect && rect != (uchar *)ibuf->rect) { - MEM_freeN(rect); - } - if (rect_float && rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - - if (GPU_get_mipmap()) { - glGenerateMipmap((tile != NULL) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D); - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - - GPU_texture_unbind(tex); -} - -/* Get the GPUTexture for a given `Image`. - * - * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already - * available. It is also required when requesting the GPUTexture for a render result. */ -GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, int textarget) -{ -#ifndef GPU_STANDALONE - if (ima == NULL) { - return NULL; - } - - /* Free any unused GPU textures, since we know we are in a thread with OpenGL - * context and might as well ensure we have as much space free as possible. */ - gpu_free_unused_buffers(); - - /* currently, gpu refresh tagging is used by ima sequences */ - if (ima->gpuflag & IMA_GPU_REFRESH) { - gpu_free_image(ima, true); - ima->gpuflag &= ~IMA_GPU_REFRESH; - } - - /* Tag as in active use for garbage collector. */ - BKE_image_tag_time(ima); - - /* Test if we already have a texture. */ - GPUTexture **tex = gpu_get_image_gputexture(ima, textarget, iuser ? iuser->multiview_eye : 0); - if (*tex) { - return *tex; - } - - /* Check if we have a valid image. If not, we return a dummy - * texture with zero bindcode so we don't keep trying. */ - uint bindcode = 0; - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (tile == NULL || tile->ok == 0) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - - /* check if we have a valid image buffer */ - ImBuf *ibuf_intern = ibuf; - if (ibuf_intern == NULL) { - ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf_intern == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - } - - if (textarget == GL_TEXTURE_2D_ARRAY) { - bindcode = gpu_texture_create_tile_array(ima, ibuf_intern); - } - else if (textarget == GL_TEXTURE_1D_ARRAY) { - bindcode = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); - } - else { - bindcode = gpu_texture_create_from_ibuf(ima, ibuf_intern, textarget); - } - - /* if `ibuf` was given, we don't own the `ibuf_intern` */ - if (ibuf == NULL) { - BKE_image_release_ibuf(ima, ibuf_intern, NULL); - } - - *tex = GPU_texture_from_bindcode(textarget, bindcode); - - GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); - - if (textarget == GL_TEXTURE_1D_ARRAY) { - /* Special for tile mapping. */ - GPU_texture_mipmap_mode(*tex, false, false); - } - - return *tex; -#endif - return NULL; -} - -GPUTexture *GPU_texture_from_movieclip(MovieClip *clip, MovieClipUser *cuser, int textarget) -{ -#ifndef GPU_STANDALONE - if (clip == NULL) { - return NULL; - } - - GPUTexture **tex = gpu_get_movieclip_gputexture(clip, cuser, textarget); - if (*tex) { - return *tex; - } - - /* check if we have a valid image buffer */ - uint bindcode = 0; - ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser); - if (ibuf == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - - bindcode = gpu_texture_create_from_ibuf(NULL, ibuf, textarget); - IMB_freeImBuf(ibuf); - - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; -#else - return NULL; -#endif -} - -void GPU_free_texture_movieclip(struct MovieClip *clip) -{ - /* number of gpu textures to keep around as cache - * We don't want to keep too many GPU textures for - * movie clips around, as they can be large.*/ - const int MOVIECLIP_NUM_GPUTEXTURES = 1; - - while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) { - MovieClip_RuntimeGPUTexture *tex = BLI_pophead(&clip->runtime.gputextures); - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* free glsl image binding */ - if (tex->gputexture[i]) { - GPU_texture_free(tex->gputexture[i]); - tex->gputexture[i] = NULL; - } - } - MEM_freeN(tex); - } -} - -static void **gpu_gen_cube_map(uint *rect, float *frect, int rectw, int recth) -{ - size_t block_size = frect ? sizeof(float[4]) : sizeof(uchar[4]); - void **sides = NULL; - int h = recth / 2; - int w = rectw / 3; - - if (w != h) { - return sides; - } - - /* PosX, NegX, PosY, NegY, PosZ, NegZ */ - sides = MEM_mallocN(sizeof(void *) * 6, ""); - for (int i = 0; i < 6; i++) { - sides[i] = MEM_mallocN(block_size * w * h, ""); - } - - /* divide image into six parts */ - /* ______________________ - * | | | | - * | NegX | NegY | PosX | - * |______|______|______| - * | | | | - * | NegZ | PosZ | PosY | - * |______|______|______| - */ - if (frect) { - float(*frectb)[4] = (float(*)[4])frect; - float(**fsides)[4] = (float(**)[4])sides; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - memcpy(&fsides[0][x * h + y], &frectb[(recth - y - 1) * rectw + 2 * w + x], block_size); - memcpy(&fsides[1][x * h + y], &frectb[(y + h) * rectw + w - 1 - x], block_size); - memcpy( - &fsides[3][y * w + x], &frectb[(recth - y - 1) * rectw + 2 * w - 1 - x], block_size); - memcpy(&fsides[5][y * w + x], &frectb[(h - y - 1) * rectw + w - 1 - x], block_size); - } - memcpy(&fsides[2][y * w], frectb[y * rectw + 2 * w], block_size * w); - memcpy(&fsides[4][y * w], frectb[y * rectw + w], block_size * w); - } - } - else { - uint **isides = (uint **)sides; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - isides[0][x * h + y] = rect[(recth - y - 1) * rectw + 2 * w + x]; - isides[1][x * h + y] = rect[(y + h) * rectw + w - 1 - x]; - isides[3][y * w + x] = rect[(recth - y - 1) * rectw + 2 * w - 1 - x]; - isides[5][y * w + x] = rect[(h - y - 1) * rectw + w - 1 - x]; - } - memcpy(&isides[2][y * w], &rect[y * rectw + 2 * w], block_size * w); - memcpy(&isides[4][y * w], &rect[y * rectw + w], block_size * w); - } - } - - return sides; -} - -static void gpu_del_cube_map(void **cube_map) -{ - int i; - if (cube_map == NULL) { - return; - } - for (i = 0; i < 6; i++) { - MEM_freeN(cube_map[i]); - } - MEM_freeN(cube_map); -} - -/* Image *ima can be NULL */ -void GPU_create_gl_tex(uint *bind, - uint *rect, - float *frect, - int rectw, - int recth, - int textarget, - bool mipmap, - bool half_float, - bool use_srgb, - Image *ima) -{ - ImBuf *ibuf = NULL; - - if (textarget == GL_TEXTURE_2D && is_over_resolution_limit(textarget, rectw, recth)) { - int tpx = rectw; - int tpy = recth; - rectw = smaller_power_of_2_limit(rectw); - recth = smaller_power_of_2_limit(recth); - - if (frect) { - ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy, 4); - IMB_scaleImBuf(ibuf, rectw, recth); - - frect = ibuf->rect_float; - } - else { - ibuf = IMB_allocFromBuffer(rect, NULL, tpx, tpy, 4); - IMB_scaleImBuf(ibuf, rectw, recth); - - rect = ibuf->rect; - } - } - - /* create image */ - glGenTextures(1, (GLuint *)bind); - glBindTexture(textarget, *bind); - - GLenum float_format = (!half_float && (ima && (ima->flag & IMA_HIGH_BITDEPTH))) ? GL_RGBA32F : - GL_RGBA16F; - GLenum internal_format = (frect) ? float_format : (use_srgb) ? GL_SRGB8_ALPHA8 : GL_RGBA8; - - if (textarget == GL_TEXTURE_2D) { - if (frect) { - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect); - } - else { - glTexImage2D( - GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect); - } - - if (GPU_get_mipmap() && mipmap) { - glGenerateMipmap(GL_TEXTURE_2D); - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - int w = rectw / 3, h = recth / 2; - - if (h == w && is_power_of_2_i(h) && !is_over_resolution_limit(textarget, h, w)) { - void **cube_map = gpu_gen_cube_map(rect, frect, rectw, recth); - GLenum type = frect ? GL_FLOAT : GL_UNSIGNED_BYTE; - - if (cube_map) { - for (int i = 0; i < 6; i++) { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, - 0, - internal_format, - w, - h, - 0, - GL_RGBA, - type, - cube_map[i]); - } - } - - if (GPU_get_mipmap() && mipmap) { - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - - gpu_del_cube_map(cube_map); - } - else { - printf("Incorrect envmap size\n"); - } - } - - glBindTexture(textarget, 0); - - if (ibuf) { - IMB_freeImBuf(ibuf); - } -} - -/** - * GPU_upload_dxt_texture() assumes that the texture is already bound and ready to go. - * This is so the viewport and the BGE can share some code. - * Returns false if the provided ImBuf doesn't have a supported DXT compression format - */ -bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb) -{ -#ifdef WITH_DDS - GLint format = 0; - int blocksize, height, width, i, size, offset = 0; - - width = ibuf->x; - height = ibuf->y; - - if (GLEW_EXT_texture_compression_s3tc) { - if (ibuf->dds_data.fourcc == FOURCC_DXT1) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - } - else if (ibuf->dds_data.fourcc == FOURCC_DXT3) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - } - else if (ibuf->dds_data.fourcc == FOURCC_DXT5) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - } - } - - if (format == 0) { - fprintf(stderr, "Unable to find a suitable DXT compression, falling back to uncompressed\n"); - return false; - } - - if (!is_power_of_2_resolution(width, height)) { - fprintf( - stderr, - "Unable to load non-power-of-two DXT image resolution, falling back to uncompressed\n"); - return false; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - - /* Reset to opengl Defaults. (Untested, might not be needed) */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - - blocksize = (ibuf->dds_data.fourcc == FOURCC_DXT1) ? 8 : 16; - for (i = 0; i < ibuf->dds_data.nummipmaps && (width || height); i++) { - if (width == 0) { - width = 1; - } - if (height == 0) { - height = 1; - } - - size = ((width + 3) / 4) * ((height + 3) / 4) * blocksize; - - glCompressedTexImage2D( - GL_TEXTURE_2D, i, format, width, height, 0, size, ibuf->dds_data.data + offset); - - offset += size; - width >>= 1; - height >>= 1; - } - /* Restore Blender default. */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - /* set number of mipmap levels we have, needed in case they don't go down to 1x1 */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1); - - return true; -#else - UNUSED_VARS(ibuf, use_srgb); - return false; -#endif -} - -void GPU_create_gl_tex_compressed(unsigned int *bind, int textarget, Image *ima, ImBuf *ibuf) -{ - /* For DDS we only support data, scene linear and sRGB. Converting to - * different colorspace would break the compression. */ - const bool use_srgb = !(IMB_colormanagement_space_is_data(ibuf->rect_colorspace) || - IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); - const bool mipmap = GPU_get_mipmap(); - const bool half_float = (ibuf->flags & IB_halffloat) != 0; - -#ifndef WITH_DDS - (void)ibuf; - /* Fall back to uncompressed if DDS isn't enabled */ - GPU_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); -#else - glGenTextures(1, (GLuint *)bind); - glBindTexture(textarget, *bind); - - if (textarget == GL_TEXTURE_2D && GPU_upload_dxt_texture(ibuf, use_srgb) == 0) { - glDeleteTextures(1, (GLuint *)bind); - GPU_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); - } - - glBindTexture(textarget, 0); -#endif -} - -/* these two functions are called on entering and exiting texture paint mode, - * temporary disabling/enabling mipmapping on all images for quick texture - * updates with glTexSubImage2D. images that didn't change don't have to be - * re-uploaded to OpenGL */ -void GPU_paint_set_mipmap(Main *bmain, bool mipmap) -{ -#ifndef GPU_STANDALONE - if (!GTS.domipmap) { - return; - } - - GTS.texpaint = !mipmap; - - if (mipmap) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_has_opengl_texture(ima)) { - if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); - } - } - } - } - } - else { - GPU_free_image(ima); - } - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - } - } - else { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_has_opengl_texture(ima)) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); - } - } - } - } - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - } - } -#endif /* GPU_STANDALONE */ -} - -void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h) -{ -#ifndef GPU_STANDALONE - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - - if ((ibuf == NULL) || (w == 0) || (h == 0)) { - /* Full reload of texture. */ - GPU_free_image(ima); - } - - GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0]; - /* Check if we need to update the main gputexture. */ - if (tex != NULL && tile == ima->tiles.first) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); - } - - /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][0]; - if (tex != NULL) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); - } - - BKE_image_release_ibuf(ima, ibuf, NULL); -#endif -} - -/* Delayed GPU texture free. Image datablocks can be deleted by any thread, - * but there may not be any active OpenGL context. In that case we push them - * into a queue and free the buffers later. */ -static LinkNode *gpu_texture_free_queue = NULL; -static ThreadMutex gpu_texture_queue_mutex = BLI_MUTEX_INITIALIZER; - -static void gpu_free_unused_buffers() -{ - if (gpu_texture_free_queue == NULL) { - return; - } - - BLI_mutex_lock(&gpu_texture_queue_mutex); - - if (gpu_texture_free_queue != NULL) { - for (LinkNode *node = gpu_texture_free_queue; node; node = node->next) { - GPUTexture *tex = node->link; - GPU_texture_free(tex); - } - - BLI_linklist_free(gpu_texture_free_queue, NULL); - gpu_texture_free_queue = NULL; - } - - BLI_mutex_unlock(&gpu_texture_queue_mutex); -} - -static void gpu_free_image(Image *ima, const bool immediate) -{ - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye] != NULL) { - if (immediate) { - GPU_texture_free(ima->gputexture[i][eye]); - } - else { - BLI_mutex_lock(&gpu_texture_queue_mutex); - BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]); - BLI_mutex_unlock(&gpu_texture_queue_mutex); - } - - ima->gputexture[i][eye] = NULL; - } - } - } - - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; -} - -void GPU_free_unused_buffers() -{ - if (BLI_thread_is_main()) { - gpu_free_unused_buffers(); - } -} - -void GPU_free_image(Image *ima) -{ - gpu_free_image(ima, BLI_thread_is_main()); -} - -void GPU_free_images(Main *bmain) -{ - if (bmain) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - GPU_free_image(ima); - } - } -} - -/* same as above but only free animated images */ -void GPU_free_images_anim(Main *bmain) -{ - if (bmain) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_is_animated(ima)) { - GPU_free_image(ima); - } - } - } -} - -void GPU_free_images_old(Main *bmain) -{ - static int lasttime = 0; - int ctime = (int)PIL_check_seconds_timer(); - - /* - * Run garbage collector once for every collecting period of time - * if textimeout is 0, that's the option to NOT run the collector - */ - if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) { - return; - } - - /* of course not! */ - if (G.is_rendering) { - return; - } - - lasttime = ctime; - - Image *ima = bmain->images.first; - while (ima) { - if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) { - /* If it's in GL memory, deallocate and set time tag to current time - * This gives textures a "second chance" to be used before dying. */ - if (BKE_image_has_opengl_texture(ima)) { - GPU_free_image(ima); - ima->lastused = ctime; - } - /* Otherwise, just kill the buffers */ - else { - BKE_image_free_buffers(ima); - } - } - ima = ima->id.next; - } -} diff --git a/source/blender/gpu/intern/gpu_element.c b/source/blender/gpu/intern/gpu_element.cc index 036588b4a48..9f104ab3fec 100644 --- a/source/blender/gpu/intern/gpu_element.c +++ b/source/blender/gpu/intern/gpu_element.cc @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "GPU_element.h" +#include "GPU_glew.h" #include "gpu_context_private.h" @@ -37,21 +38,18 @@ static GLenum convert_index_type_to_gl(GPUIndexBufType type) { - static const GLenum table[] = { - [GPU_INDEX_U16] = GL_UNSIGNED_SHORT, - [GPU_INDEX_U32] = GL_UNSIGNED_INT, - }; - return table[type]; +#if GPU_TRACK_INDEX_RANGE + return (type == GPU_INDEX_U32) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; +#else + return GL_UNSIGNED_INT; +#endif } uint GPU_indexbuf_size_get(const GPUIndexBuf *elem) { #if GPU_TRACK_INDEX_RANGE - static const uint table[] = { - [GPU_INDEX_U16] = sizeof(GLushort), - [GPU_INDEX_U32] = sizeof(GLuint), - }; - return elem->index_len * table[elem->index_type]; + return elem->index_len * + ((elem->index_type == GPU_INDEX_U32) ? sizeof(GLuint) : sizeof(GLshort)); #else return elem->index_len * sizeof(GLuint); #endif @@ -86,7 +84,7 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder, builder->max_index_len = index_len; builder->index_len = 0; // start empty builder->prim_type = prim_type; - builder->data = MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); + builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); } void GPU_indexbuf_init(GPUIndexBufBuilder *builder, @@ -241,7 +239,7 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem) GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length) { - GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); GPU_indexbuf_create_subrange_in_place(elem, elem_src, start, length); return elem; } @@ -331,7 +329,7 @@ static void squeeze_indices_short(GPUIndexBufBuilder *builder, GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder) { - GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); GPU_indexbuf_build_in_place(builder, elem); return elem; } diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.cc index 9aa3becef1d..32c1bf6e2d3 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.cc @@ -31,6 +31,8 @@ #include "BKE_global.h" #include "MEM_guardedalloc.h" +#include "DNA_userdef_types.h" + #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_glew.h" @@ -104,7 +106,8 @@ static struct GPUGlobal { static void gpu_detect_mip_render_workaround(void) { int cube_size = 2; - float *source_pix = MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, __func__); + float *source_pix = (float *)MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, + __func__); float clear_color[4] = {1.0f, 0.5f, 0.0f, 0.0f}; GPUTexture *tex = GPU_texture_create_cube(cube_size, GPU_RGBA16F, source_pix, NULL); @@ -123,7 +126,7 @@ static void gpu_detect_mip_render_workaround(void) GPU_framebuffer_restore(); GPU_framebuffer_free(fb); - float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 1); + float *data = (float *)GPU_texture_read(tex, GPU_DATA_FLOAT, 1); GG.mip_render_workaround = !equals_v4v4(clear_color, data); MEM_freeN(data); @@ -238,6 +241,13 @@ bool GPU_crappy_amd_driver(void) return GG.broken_amd_driver; } +int GPU_texture_size_with_limit(int res) +{ + int size = GPU_max_texture_size(); + int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; + return min_ii(reslimit, res); +} + void gpu_extensions_init(void) { /* during 2.8 development each platform has its own OpenGL minimum requirements @@ -311,7 +321,7 @@ void gpu_extensions_init(void) if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL)) { /* Limit this fix to older hardware with GL < 4.5. This means Broadwell GPUs are * covered since they only support GL 4.4 on windows. - * This fixes some issues with workbench antialiasing on Win + Intel GPU. (see T76273) */ + * This fixes some issues with workbench anti-aliasing on Win + Intel GPU. (see T76273) */ if (!GLEW_VERSION_4_5) { GG.texture_copy_workaround = true; } diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.cc index 77abb786117..056a449ac6d 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -28,7 +28,6 @@ #include "BLI_utildefines.h" #include "GPU_batch.h" -#include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_shader.h" @@ -53,6 +52,10 @@ typedef enum { 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) @@ -74,17 +77,25 @@ struct GPUFrameBuffer { static GLenum convert_attachment_type_to_gl(GPUAttachmentType type) { - static const GLenum table[] = { - [GPU_FB_DEPTH_ATTACHMENT] = GL_DEPTH_ATTACHMENT, - [GPU_FB_DEPTH_STENCIL_ATTACHMENT] = GL_DEPTH_STENCIL_ATTACHMENT, - [GPU_FB_COLOR_ATTACHMENT0] = GL_COLOR_ATTACHMENT0, - [GPU_FB_COLOR_ATTACHMENT1] = GL_COLOR_ATTACHMENT1, - [GPU_FB_COLOR_ATTACHMENT2] = GL_COLOR_ATTACHMENT2, - [GPU_FB_COLOR_ATTACHMENT3] = GL_COLOR_ATTACHMENT3, - [GPU_FB_COLOR_ATTACHMENT4] = GL_COLOR_ATTACHMENT4, - [GPU_FB_COLOR_ATTACHMENT5] = GL_COLOR_ATTACHMENT5, - }; - return table[type]; +#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; + } } static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) @@ -98,7 +109,7 @@ static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) case GPU_DEPTH32F_STENCIL8: return GPU_FB_DEPTH_STENCIL_ATTACHMENT; default: - return GPU_FB_COLOR_ATTACHMENT0 + slot; + return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); } } @@ -198,7 +209,7 @@ GPUFrameBuffer *GPU_framebuffer_create(void) { /* We generate the FB object later at first use in order to * create the framebuffer in the right opengl context. */ - return MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); + return (GPUFrameBuffer *)MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); } static void gpu_framebuffer_init(GPUFrameBuffer *fb) @@ -210,7 +221,8 @@ static void gpu_framebuffer_init(GPUFrameBuffer *fb) void GPU_framebuffer_free(GPUFrameBuffer *fb) { - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + 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); } @@ -304,7 +316,7 @@ void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, in void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex) { - GPUAttachmentType type = GPU_texture_detach_framebuffer(tex, fb); + GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, fb); GPU_framebuffer_texture_detach_slot(fb, tex, type); } @@ -387,8 +399,8 @@ static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb) BLI_assert(GPU_framebuffer_active_get() == fb); /* Update attachments */ - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { - + 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); @@ -438,7 +450,8 @@ static void gpu_framebuffer_update_attachments_and_fill_empty_slots(GPUFrameBuff BLI_assert(GPU_framebuffer_active_get() == fb); /* Update attachments */ - for (GPUAttachmentType type = GPU_FB_MAX_ATTACHEMENT; type--;) { + 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) { @@ -623,8 +636,9 @@ void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0; - for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) { + 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]); } @@ -702,7 +716,8 @@ void GPU_framebuffer_read_color(GPUFrameBuffer *fb, void *data) { CHECK_FRAMEBUFFER_IS_BOUND(fb); - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, data); + gpu_framebuffer_read_color_ex( + x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data); } /* read_slot and write_slot are only used for color buffers. */ @@ -815,7 +830,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, current_dim[0] = max_ii(current_dim[0] / 2, 1); current_dim[1] = max_ii(current_dim[1] / 2, 1); - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + 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 @@ -844,7 +860,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, } } - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + 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; @@ -885,9 +902,11 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) for (int i = 0; i < MAX_CTX_FB_LEN; i++) { if (ofs->framebuffers[i].fb == NULL) { ofs->framebuffers[i].ctx = ctx; - GPU_framebuffer_ensure_config( - &ofs->framebuffers[i].fb, - {GPU_ATTACHMENT_TEXTURE(ofs->depth), GPU_ATTACHMENT_TEXTURE(ofs->color)}); + GPU_framebuffer_ensure_config(&ofs->framebuffers[i].fb, + { + GPU_ATTACHMENT_TEXTURE(ofs->depth), + GPU_ATTACHMENT_TEXTURE(ofs->color), + }); } if (ofs->framebuffers[i].ctx == ctx) { @@ -916,9 +935,7 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) GPUOffScreen *GPU_offscreen_create( int width, int height, bool depth, bool high_bitdepth, char err_out[256]) { - GPUOffScreen *ofs; - - ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); + GPUOffScreen *ofs = (GPUOffScreen *)MEM_callocN(sizeof(GPUOffScreen), __func__); /* Sometimes areas can have 0 height or width and this will * create a 1D texture which we don't want. */ @@ -975,7 +992,7 @@ void GPU_offscreen_free(GPUOffScreen *ofs) void GPU_offscreen_bind(GPUOffScreen *ofs, bool save) { if (save) { - gpuPushAttr(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT); + gpuPushAttr((eGPUAttrMask)(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT)); GPUFrameBuffer *fb = GPU_framebuffer_active_get(); gpuPushFrameBuffer(fb); } @@ -1023,14 +1040,15 @@ void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default()); } -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels) { const int w = GPU_texture_width(ofs->color); const int h = GPU_texture_height(ofs->color); - BLI_assert(type == GL_UNSIGNED_BYTE || type == GL_FLOAT); + 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, type, pixels); + glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels); } int GPU_offscreen_width(const GPUOffScreen *ofs) @@ -1077,8 +1095,7 @@ void GPU_clear(eGPUFrameBufferBits flags) void GPU_frontbuffer_read_pixels( int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) { - glReadBuffer(GL_FRONT); - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, data); + gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data); } /* For stereo rendering. */ diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.cc index 4f5cb3fcc91..ac3e653c9ff 100644 --- a/source/blender/gpu/intern/gpu_immediate.c +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -29,6 +29,7 @@ #include "GPU_attr_binding.h" #include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_texture.h" #include "gpu_attr_binding_private.h" @@ -40,10 +41,6 @@ #include <stdlib.h> #include <string.h> -/* necessary functions from matrix API */ -extern void GPU_matrix_bind(const GPUShaderInterface *); -extern bool GPU_matrix_dirty_get(void); - typedef struct ImmediateDrawBuffer { GLuint vbo_id; GLubyte *buffer_data; @@ -75,7 +72,7 @@ typedef struct { GLuint vao_id; - GLuint bound_program; + GPUShader *bound_program; const GPUShaderInterface *shader_interface; GPUAttrBinding attr_binding; uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */ @@ -146,48 +143,47 @@ GPUVertFormat *immVertexFormat(void) return &imm.vertex_format; } -void immBindProgram(GLuint program, const GPUShaderInterface *shaderface) +void immBindShader(GPUShader *shader) { #if TRUST_NO_ONE - assert(imm.bound_program == 0); - assert(glIsProgram(program)); + assert(imm.bound_program == NULL); + assert(glIsProgram(shader->program)); #endif - imm.bound_program = program; - imm.shader_interface = shaderface; + imm.bound_program = shader; + imm.shader_interface = shader->interface; if (!imm.vertex_format.packed) { VertexFormat_pack(&imm.vertex_format); } - glUseProgram(program); - get_attr_locations(&imm.vertex_format, &imm.attr_binding, shaderface); - GPU_matrix_bind(shaderface); - GPU_shader_set_srgb_uniform(shaderface); + 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); } void immBindBuiltinProgram(eGPUBuiltinShader shader_id) { GPUShader *shader = GPU_shader_get_builtin_shader(shader_id); - immBindProgram(shader->program, shader->interface); + immBindShader(shader); } void immUnbindProgram(void) { #if TRUST_NO_ONE - assert(imm.bound_program != 0); + assert(imm.bound_program != NULL); #endif #if PROGRAM_NO_OPTI glUseProgram(0); #endif - imm.bound_program = 0; + imm.bound_program = NULL; } /* XXX do not use it. Special hack to use OCIO with batch API. */ -void immGetProgram(GLuint *program, GPUShaderInterface **shaderface) +GPUShader *immGetShader(void) { - *program = imm.bound_program; - *shaderface = (GPUShaderInterface *)imm.shader_interface; + return imm.bound_program; } #if TRUST_NO_ONE @@ -281,7 +277,7 @@ void immBegin(GPUPrimType prim_type, uint vertex_len) } #endif - active_buffer->buffer_data = glMapBufferRange( + active_buffer->buffer_data = (GLubyte *)glMapBufferRange( GL_ARRAY_BUFFER, active_buffer->buffer_offset, bytes_needed, @@ -367,17 +363,18 @@ static void immDrawSetup(void) 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, a->gl_comp_type, GL_FALSE, stride, pointer); + glVertexAttribPointer(loc, a->comp_len, type, GL_FALSE, stride, pointer); break; case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_TRUE, stride, pointer); + glVertexAttribPointer(loc, a->comp_len, type, GL_TRUE, stride, pointer); break; case GPU_FETCH_INT: - glVertexAttribIPointer(loc, a->comp_len, a->gl_comp_type, stride, pointer); + glVertexAttribIPointer(loc, a->comp_len, type, stride, pointer); } } @@ -425,7 +422,7 @@ void immEnd(void) GPU_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_len); /* TODO: resize only if vertex count is much smaller */ } - GPU_batch_program_set(imm.batch, imm.bound_program, imm.shader_interface); + 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 */ } diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index 54ddb9351b9..c5061ec9ba3 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -95,7 +95,7 @@ void GPU_exit(void) initialized = false; } -bool GPU_is_initialized(void) +bool GPU_is_init(void) { return initialized; } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index c65c1046b8f..f3477b6f3a4 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -85,7 +85,7 @@ struct GPUMaterial { bool has_surface_output; /* Only used by Eevee to know which bsdf are used. */ - int flag; + eGPUMatFlag flag; /* Used by 2.8 pipeline */ GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */ @@ -659,7 +659,8 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, const char *geom_code, const char *frag_lib, const char *defines, - const char *name) + const char *name, + GPUMaterialEvalCallbackFn callback) { LinkData *link; bool has_volume_output, has_surface_output; @@ -696,6 +697,9 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, mat->has_volume_output = has_volume_output; if (mat->graph.outlink) { + if (callback) { + callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines); + } /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */ if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { defines = BLI_string_joinN(defines, diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.cc index 669bf56b726..c15bb1fba19 100644 --- a/source/blender/gpu/intern/gpu_matrix.c +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -37,8 +37,6 @@ #include "MEM_guardedalloc.h" -#define DEBUG_MATRIX_BIND 0 - #define MATRIX_STACK_DEPTH 32 typedef float Mat4[4][4]; @@ -79,7 +77,7 @@ GPUMatrixState *GPU_matrix_state_create(void) } \ } - GPUMatrixState *state = MEM_mallocN(sizeof(*state), __func__); + GPUMatrixState *state = (GPUMatrixState *)MEM_mallocN(sizeof(*state), __func__); const MatrixStack identity_stack = {{MATRIX_4X4_IDENTITY}, 0}; state->model_view_stack = state->projection_stack = identity_stack; @@ -662,51 +660,32 @@ void GPU_matrix_bind(const GPUShaderInterface *shaderface) 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); + /* XXX(fclem) this works but this assumes shader is unused inside GPU_shader_uniform_vector. */ + GPUShader *sh = NULL; if (MV != -1) { -#if DEBUG_MATRIX_BIND - puts("setting MV matrix"); -#endif - - glUniformMatrix4fv(MV, 1, GL_FALSE, (const float *)GPU_matrix_model_view_get(NULL)); + GPU_shader_uniform_vector(sh, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL)); } - if (P != -1) { -#if DEBUG_MATRIX_BIND - puts("setting P matrix"); -#endif - - glUniformMatrix4fv(P, 1, GL_FALSE, (const float *)GPU_matrix_projection_get(NULL)); + GPU_shader_uniform_vector(sh, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL)); } - if (MVP != -1) { -#if DEBUG_MATRIX_BIND - puts("setting MVP matrix"); -#endif - - glUniformMatrix4fv( - MVP, 1, GL_FALSE, (const float *)GPU_matrix_model_view_projection_get(NULL)); + GPU_shader_uniform_vector( + sh, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL)); } - if (N != -1) { -#if DEBUG_MATRIX_BIND - puts("setting normal matrix"); -#endif - - glUniformMatrix3fv(N, 1, GL_FALSE, (const float *)GPU_matrix_normal_get(NULL)); + GPU_shader_uniform_vector(sh, 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); - glUniformMatrix4fv(MV_inv, 1, GL_FALSE, (const float *)m); + GPU_shader_uniform_vector(sh, MV_inv, 16, 1, (const float *)m); } - if (P_inv != -1) { Mat4 m; GPU_matrix_projection_get(m); invert_m4(m); - glUniformMatrix4fv(P_inv, 1, GL_FALSE, (const float *)m); + GPU_shader_uniform_vector(sh, P_inv, 16, 1, (const float *)m); } gpu_matrix_state_active_set_dirty(false); diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index bf59b720cff..a133dd1ccdc 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -29,7 +29,6 @@ #include "DNA_customdata_types.h" #include "DNA_listBase.h" -#include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" diff --git a/source/blender/gpu/intern/gpu_platform.c b/source/blender/gpu/intern/gpu_platform.cc index 5cabde61bc3..5cabde61bc3 100644 --- a/source/blender/gpu/intern/gpu_platform.c +++ b/source/blender/gpu/intern/gpu_platform.cc diff --git a/source/blender/gpu/intern/gpu_primitive.c b/source/blender/gpu/intern/gpu_primitive.c index 2285c1ab95b..3b11b38db87 100644 --- a/source/blender/gpu/intern/gpu_primitive.c +++ b/source/blender/gpu/intern/gpu_primitive.c @@ -26,35 +26,6 @@ #include "GPU_primitive.h" #include "gpu_primitive_private.h" -GPUPrimClass GPU_primtype_class(GPUPrimType prim_type) -{ - static const GPUPrimClass classes[] = { - [GPU_PRIM_POINTS] = GPU_PRIM_CLASS_POINT, - [GPU_PRIM_LINES] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_STRIP] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_LOOP] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_TRIS] = GPU_PRIM_CLASS_SURFACE, - [GPU_PRIM_TRI_STRIP] = GPU_PRIM_CLASS_SURFACE, - [GPU_PRIM_TRI_FAN] = GPU_PRIM_CLASS_SURFACE, - - [GPU_PRIM_LINES_ADJ] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_STRIP_ADJ] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_TRIS_ADJ] = GPU_PRIM_CLASS_SURFACE, - - [GPU_PRIM_NONE] = GPU_PRIM_CLASS_NONE, - }; - - return classes[prim_type]; -} - -bool GPU_primtype_belongs_to_class(GPUPrimType prim_type, GPUPrimClass prim_class) -{ - if (prim_class == GPU_PRIM_CLASS_NONE && prim_type == GPU_PRIM_NONE) { - return true; - } - return prim_class & GPU_primtype_class(prim_type); -} - GLenum convert_prim_type_to_gl(GPUPrimType prim_type) { #if TRUST_NO_ONE diff --git a/source/blender/gpu/intern/gpu_primitive_private.h b/source/blender/gpu/intern/gpu_primitive_private.h index abefa6abd20..b7d7b262128 100644 --- a/source/blender/gpu/intern/gpu_primitive_private.h +++ b/source/blender/gpu/intern/gpu_primitive_private.h @@ -26,6 +26,15 @@ #ifndef __GPU_PRIMITIVE_PRIVATE_H__ #define __GPU_PRIMITIVE_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO(fclem) move to OGL backend */ GLenum convert_prim_type_to_gl(GPUPrimType); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_PRIMITIVE_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h index 7846bff87f4..ed729dd399c 100644 --- a/source/blender/gpu/intern/gpu_private.h +++ b/source/blender/gpu/intern/gpu_private.h @@ -21,6 +21,10 @@ #ifndef __GPU_PRIVATE_H__ #define __GPU_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + /* call this before running any of the functions below */ void gpu_platform_init(void); void gpu_platform_exit(void); @@ -41,4 +45,8 @@ void gpu_framebuffer_module_exit(void); void gpu_pbvh_init(void); void gpu_pbvh_exit(void); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 5766a176a96..c069cbe012f 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -26,7 +26,6 @@ #include <stdlib.h> #include <string.h> -#include "GPU_glew.h" #include "GPU_select.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index c6d8545527c..3025b5d66da 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -27,7 +27,6 @@ #include <stdlib.h> #include <string.h> -#include "GPU_draw.h" #include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_select.h" diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc new file mode 100644 index 00000000000..2b54e733bf5 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -0,0 +1,839 @@ +/* + * 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 + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math_base.h" +#include "BLI_math_vector.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "BKE_appdir.h" +#include "BKE_global.h" + +#include "DNA_space_types.h" + +#include "GPU_extensions.h" +#include "GPU_matrix.h" +#include "GPU_platform.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_uniformbuffer.h" + +#include "gpu_shader_private.h" + +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 + +#ifndef NDEBUG +static uint g_shaderid = 0; +#endif + +/* -------------------------------------------------------------------- */ +/** \name Convenience functions + * \{ */ + +static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) +{ + 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++; + } + + fprintf(stderr, "%s", c); + } + } + + fprintf(stderr, "%s\n", log); +} + +static const char *gpu_shader_version(void) +{ + return "#version 330\n"; +} + +static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) +{ + /* 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"); + } + 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"); + } +} + +static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) +{ + /* 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"); + } + } + else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#define GPU_NVIDIA\n"); + } + else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#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"); + } + else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { + strcat(defines, "#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); + } + + 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; + } + + glDeleteProgram(program); + return NULL; +} + +GPUShader *GPU_shader_create_ex(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines, + const eGPUShaderTFBType tf_type, + const char **tf_names, + 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; + } + + gpu_shader_standard_defines(standard_defines); + gpu_shader_standard_extensions(standard_extensions); + + 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 (defines) { + source[num_source++] = 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); + + 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; + } + } + + 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"; + } + if (defines) { + source[num_source++] = defines; + } + if (libcode) { + source[num_source++] = libcode; + } + source[num_source++] = fragcode; + + gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT); + + 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; + } + } + + 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 (defines) { + source[num_source++] = 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); + + 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; + } + } + + if (tf_names != NULL) { + glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); + /* Primitive type must be setup */ + BLI_assert(tf_type != GPU_SHADER_TFB_NONE); + shader->feedback_transform_type = 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); + return NULL; + } + + glUseProgram(shader->program); + shader->interface = GPU_shaderinterface_create(shader->program); + + return shader; +} + +#undef DEBUG_SHADER_GEOMETRY +#undef DEBUG_SHADER_FRAGMENT +#undef DEBUG_SHADER_VERTEX +#undef DEBUG_SHADER_NONE + +void GPU_shader_free(GPUShader *shader) +{ +#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); + + if (shader->vertex) { + glDeleteShader(shader->vertex); + } + if (shader->geometry) { + glDeleteShader(shader->geometry); + } + if (shader->fragment) { + glDeleteShader(shader->fragment); + } + if (shader->program) { + glDeleteProgram(shader->program); + } + + if (shader->interface) { + GPU_shaderinterface_discard(shader->interface); + } + + MEM_freeN(shader); +} + +static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc) +{ + bool is_alloc = false; + if (str_arr == NULL) { + *r_is_alloc = false; + return NULL; + } + /* Skip empty strings (avoid alloc if we can). */ + while (str_arr[0] && str_arr[0][0] == '\0') { + str_arr++; + } + int i; + for (i = 0; str_arr[i]; i++) { + if (i != 0 && str_arr[i][0] != '\0') { + is_alloc = true; + } + } + *r_is_alloc = is_alloc; + if (is_alloc) { + return BLI_string_join_arrayN(str_arr, i); + } + else { + return str_arr[0]; + } +} + +/** + * Use via #GPU_shader_create_from_arrays macro (avoids passing in param). + * + * Similar to #DRW_shader_create_with_lib with the ability to include libs for each type of shader. + * + * It has the advantage that each item can be conditionally included + * without having to build the string inline, then free it. + * + * \param params: NULL terminated arrays of strings. + * + * Example: + * \code{.c} + * sh = GPU_shader_create_from_arrays({ + * .vert = (const char *[]){shader_lib_glsl, shader_vert_glsl, NULL}, + * .geom = (const char *[]){shader_geom_glsl, NULL}, + * .frag = (const char *[]){shader_frag_glsl, NULL}, + * .defs = (const char *[]){"#define DEFINE\n", test ? "#define OTHER_DEFINE\n" : "", NULL}, + * }); + * \endcode + */ +struct GPUShader *GPU_shader_create_from_arrays_impl( + const struct GPU_ShaderCreateFromArray_Params *params) +{ + struct { + const char *str; + bool is_alloc; + } str_dst[4] = {{0}}; + const char **str_src[4] = {params->vert, params->frag, params->geom, params->defs}; + + for (int i = 0; i < ARRAY_SIZE(str_src); i++) { + str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc); + } + + GPUShader *sh = GPU_shader_create( + str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__); + + for (int i = 0; i < ARRAY_SIZE(str_dst); i++) { + if (str_dst[i].is_alloc) { + MEM_freeN((void *)str_dst[i].str); + } + } + return sh; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Binding + * \{ */ + +void GPU_shader_bind(GPUShader *shader) +{ + BLI_assert(shader && shader->program); + + glUseProgram(shader->program); + GPU_matrix_bind(shader->interface); + GPU_shader_set_srgb_uniform(shader->interface); +} + +void GPU_shader_unbind(void) +{ + glUseProgram(0); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform feedback + * \{ */ + +bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id) +{ + 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; + } +} + +void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) +{ + glEndTransformFeedback(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms / Resource location + * \{ */ + +int GPU_shader_get_uniform(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, 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)); +} + +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)); +} + +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); + 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); + 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); + 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); + return attr ? attr->location : -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Getters + * \{ */ + +/* Clement : Temp */ +int GPU_shader_get_program(GPUShader *shader) +{ + return (int)shader->program; +} + +char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len) +{ + BLI_assert(GLEW_ARB_get_program_binary); + char *r_binary; + int binary_len = 0; + + 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); + + if (r_binary_len) { + *r_binary_len = binary_len; + } + + return r_binary; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms setters + * \{ */ + +void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) +{ + if (location == -1) { + return; + } + + glUniform1f(location, value); +} + +void GPU_shader_uniform_vector( + GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) +{ + if (location == -1 || value == NULL) { + return; + } + + 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_int(GPUShader *UNUSED(shader), int location, int value) +{ + if (location == -1) { + return; + } + + glUniform1i(location, value); +} + +void GPU_shader_uniform_vector_int( + GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) +{ + if (location == -1) { + return; + } + + 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; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name sRGB Rendering Workaround + * + * The viewport overlay frame-buffer is sRGB and will expect shaders to output display referred + * Linear colors. But other frame-buffers (i.e: the area frame-buffers) are not sRGB and require + * the shader output color to be in sRGB space + * (assumed display encoded color-space as the time of writing). + * For this reason we have a uniform to switch the transform on and off depending on the current + * frame-buffer color-space. + * \{ */ + +static int g_shader_builtin_srgb_transform = 0; + +void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface) +{ + int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM); + if (loc != -1) { + glUniform1i(loc, g_shader_builtin_srgb_transform); + } +} + +void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear) +{ + g_shader_builtin_srgb_transform = use_srgb_to_linear; +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 9ea798e5669..9c0692b76e2 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -153,11 +153,6 @@ const struct GPUShaderConfigData GPU_shader_cfg_data[GPU_SHADER_CFG_LEN] = { /* cache of built-in shaders (each is created on first use) */ static GPUShader *builtin_shaders[GPU_SHADER_CFG_LEN][GPU_SHADER_BUILTIN_LEN] = {{NULL}}; -static int g_shader_builtin_srgb_transform = 0; - -#ifndef NDEBUG -static uint g_shaderid = 0; -#endif typedef struct { const char *vert; @@ -168,727 +163,6 @@ typedef struct { const char *defs; } GPUShaderStages; -static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) -{ - 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++; - } - - fprintf(stderr, "%s", c); - } - } - - fprintf(stderr, "%s\n", log); -} - -static const char *gpu_shader_version(void) -{ - return "#version 330\n"; -} - -static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) -{ - /* 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"); - } - 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"); - } -} - -static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) -{ - /* 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"); - } - } - else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_NVIDIA\n"); - } - else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#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"); - } - else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { - strcat(defines, "#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"); - } -} - -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); - } - - 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 = 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; - } - - glDeleteProgram(program); - return NULL; -} - -#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); -} - -GPUShader *GPU_shader_create_ex(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - const eGPUShaderTFBType tf_type, - const char **tf_names, - 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 = 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; - } - - gpu_shader_standard_defines(standard_defines); - gpu_shader_standard_extensions(standard_extensions); - - if (vertexcode) { - const char *source[6]; - /* 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 (defines) { - source[num_source++] = 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); - - 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; - } - } - - if (fragcode) { - const char *source[7]; - 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 (defines) { - source[num_source++] = defines; - } - if (libcode) { - source[num_source++] = libcode; - } - source[num_source++] = fragcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT); - - 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; - } - } - - 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 (defines) { - source[num_source++] = 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); - - 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; - } - } - - if (tf_names != NULL) { - glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); - /* Primitive type must be setup */ - BLI_assert(tf_type != GPU_SHADER_TFB_NONE); - shader->feedback_transform_type = 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 (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); - return NULL; - } - - glUseProgram(shader->program); - shader->interface = GPU_shaderinterface_create(shader->program); - - return shader; -} - -#undef DEBUG_SHADER_GEOMETRY -#undef DEBUG_SHADER_FRAGMENT -#undef DEBUG_SHADER_VERTEX -#undef DEBUG_SHADER_NONE - -static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc) -{ - bool is_alloc = false; - if (str_arr == NULL) { - *r_is_alloc = false; - return NULL; - } - /* Skip empty strings (avoid alloc if we can). */ - while (str_arr[0] && str_arr[0][0] == '\0') { - str_arr++; - } - int i; - for (i = 0; str_arr[i]; i++) { - if (i != 0 && str_arr[i][0] != '\0') { - is_alloc = true; - } - } - *r_is_alloc = is_alloc; - if (is_alloc) { - return BLI_string_join_arrayN(str_arr, i); - } - else { - return str_arr[0]; - } -} - -/** - * Use via #GPU_shader_create_from_arrays macro (avoids passing in param). - * - * Similar to #DRW_shader_create_with_lib with the ability to include libs for each type of shader. - * - * It has the advantage that each item can be conditionally included - * without having to build the string inline, then free it. - * - * \param params: NULL terminated arrays of strings. - * - * Example: - * \code{.c} - * sh = GPU_shader_create_from_arrays({ - * .vert = (const char *[]){shader_lib_glsl, shader_vert_glsl, NULL}, - * .geom = (const char *[]){shader_geom_glsl, NULL}, - * .frag = (const char *[]){shader_frag_glsl, NULL}, - * .defs = (const char *[]){"#define DEFINE\n", test ? "#define OTHER_DEFINE\n" : "", NULL}, - * }); - * \endcode - */ -struct GPUShader *GPU_shader_create_from_arrays_impl( - const struct GPU_ShaderCreateFromArray_Params *params) -{ - struct { - const char *str; - bool is_alloc; - } str_dst[4] = {{0}}; - const char **str_src[4] = {params->vert, params->frag, params->geom, params->defs}; - - for (int i = 0; i < ARRAY_SIZE(str_src); i++) { - str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc); - } - - GPUShader *sh = GPU_shader_create( - str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__); - - for (int i = 0; i < ARRAY_SIZE(str_dst); i++) { - if (str_dst[i].is_alloc) { - MEM_freeN((void *)str_dst[i].str); - } - } - return sh; -} - -void GPU_shader_bind(GPUShader *shader) -{ - BLI_assert(shader && shader->program); - - glUseProgram(shader->program); - GPU_matrix_bind(shader->interface); - GPU_shader_set_srgb_uniform(shader->interface); -} - -void GPU_shader_unbind(void) -{ - glUseProgram(0); -} - -bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id) -{ - 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; - } -} - -void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) -{ - glEndTransformFeedback(); -} - -void GPU_shader_free(GPUShader *shader) -{ -#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); - - if (shader->vertex) { - glDeleteShader(shader->vertex); - } - if (shader->geometry) { - glDeleteShader(shader->geometry); - } - if (shader->fragment) { - glDeleteShader(shader->fragment); - } - if (shader->program) { - glDeleteProgram(shader->program); - } - - if (shader->interface) { - GPU_shaderinterface_discard(shader->interface); - } - - MEM_freeN(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); - 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, builtin); -} - -int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) -{ - BLI_assert(shader && shader->program); - return GPU_shaderinterface_block_builtin(shader->interface, builtin); -} - -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); - 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); - 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); - return tex ? tex->binding : -1; -} - -void *GPU_shader_get_interface(GPUShader *shader) -{ - return shader->interface; -} - -/* Clement : Temp */ -int GPU_shader_get_program(GPUShader *shader) -{ - return (int)shader->program; -} - -void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) -{ - if (location == -1) { - return; - } - - glUniform1f(location, value); -} - -void GPU_shader_uniform_vector( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) -{ - if (location == -1 || value == NULL) { - return; - } - - 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_vector_int( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) -{ - if (location == -1) { - return; - } - - 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_int(GPUShader *UNUSED(shader), int location, int value) -{ - if (location == -1) { - return; - } - - glUniform1i(location, value); -} - -void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface) -{ - int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM); - if (loc != -1) { - glUniform1i(loc, g_shader_builtin_srgb_transform); - } -} - -int GPU_shader_get_attribute(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name); - return attr ? attr->location : -1; -} - -char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len) -{ - BLI_assert(GLEW_ARB_get_program_binary); - char *r_binary; - int binary_len = 0; - - glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len); - r_binary = MEM_mallocN(binary_len, __func__); - glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary); - - if (r_binary_len) { - *r_binary_len = binary_len; - } - - return r_binary; -} - -void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear) -{ - g_shader_builtin_srgb_transform = use_srgb_to_linear; -} - static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { [GPU_SHADER_TEXT] = { @@ -1242,6 +516,7 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, return *sh_p; } + GPUShader *GPU_shader_get_builtin_shader(eGPUBuiltinShader shader) { return GPU_shader_get_builtin_shader_with_config(shader, GPU_SHADER_CFG_DEFAULT); diff --git a/source/blender/gpu/intern/gpu_shader_interface.c b/source/blender/gpu/intern/gpu_shader_interface.cc index 9d9f98c6bb0..4586b1ce814 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.c +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -47,47 +47,66 @@ static const char *BuiltinUniform_name(GPUUniformBuiltin u) { - static const char *names[] = { - [GPU_UNIFORM_MODEL] = "ModelMatrix", - [GPU_UNIFORM_VIEW] = "ViewMatrix", - [GPU_UNIFORM_MODELVIEW] = "ModelViewMatrix", - [GPU_UNIFORM_PROJECTION] = "ProjectionMatrix", - [GPU_UNIFORM_VIEWPROJECTION] = "ViewProjectionMatrix", - [GPU_UNIFORM_MVP] = "ModelViewProjectionMatrix", - - [GPU_UNIFORM_MODEL_INV] = "ModelMatrixInverse", - [GPU_UNIFORM_VIEW_INV] = "ViewMatrixInverse", - [GPU_UNIFORM_MODELVIEW_INV] = "ModelViewMatrixInverse", - [GPU_UNIFORM_PROJECTION_INV] = "ProjectionMatrixInverse", - [GPU_UNIFORM_VIEWPROJECTION_INV] = "ViewProjectionMatrixInverse", - - [GPU_UNIFORM_NORMAL] = "NormalMatrix", - [GPU_UNIFORM_ORCO] = "OrcoTexCoFactors", - [GPU_UNIFORM_CLIPPLANES] = "WorldClipPlanes", - - [GPU_UNIFORM_COLOR] = "color", - [GPU_UNIFORM_BASE_INSTANCE] = "baseInstance", - [GPU_UNIFORM_RESOURCE_CHUNK] = "resourceChunk", - [GPU_UNIFORM_RESOURCE_ID] = "resourceId", - [GPU_UNIFORM_SRGB_TRANSFORM] = "srgbTarget", - - [GPU_NUM_UNIFORMS] = NULL, - }; - - return names[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; + } } static const char *BuiltinUniformBlock_name(GPUUniformBlockBuiltin u) { - static const char *names[] = { - [GPU_UNIFORM_BLOCK_VIEW] = "viewBlock", - [GPU_UNIFORM_BLOCK_MODEL] = "modelBlock", - [GPU_UNIFORM_BLOCK_INFO] = "infoBlock", - - [GPU_NUM_UNIFORM_BLOCKS] = NULL, - }; - - return names[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; + } } GPU_INLINE bool match(const char *a, const char *b) @@ -273,7 +292,7 @@ GPUShaderInterface *GPU_shaderinterface_create(int32_t program) /* 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 = MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); + 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); @@ -291,16 +310,18 @@ GPUShaderInterface *GPU_shaderinterface_create(int32_t program) int input_tot_len = attr_len + ubo_len + uniform_len; size_t interface_size = sizeof(GPUShaderInterface) + sizeof(GPUShaderInput) * input_tot_len; - GPUShaderInterface *shaderface = MEM_callocN(interface_size, "GPUShaderInterface"); + 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 = MEM_mallocN(name_buffer_len, "name_buffer"); + 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 = MEM_mallocN(sizeof(GPUShaderInput) * input_tmp_len, "name_buffer"); + GPUShaderInput *inputs_tmp = (GPUShaderInput *)MEM_mallocN( + sizeof(GPUShaderInput) * input_tmp_len, "name_buffer"); /* Attributes */ shaderface->enabled_attr_mask = 0; @@ -366,27 +387,29 @@ GPUShaderInterface *GPU_shaderinterface_create(int32_t program) sort_input_list(inputs, inputs_tmp, shaderface->uniform_len); /* Builtin Uniforms */ - for (GPUUniformBuiltin u = 0; u < GPU_NUM_UNIFORMS; u++) { + 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 (GPUUniformBlockBuiltin u = 0; u < GPU_NUM_UNIFORM_BLOCKS; u++) { + 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 = MEM_callocN(shaderface->batches_len * sizeof(GPUBatch *), - "GPUShaderInterface batches"); + 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 = MEM_reallocN(shaderface->name_buffer, name_buffer_offset); + shaderface->name_buffer = (char *)MEM_reallocN(shaderface->name_buffer, name_buffer_offset); } #if DEBUG_SHADER_INTERFACE @@ -501,8 +524,8 @@ void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *shaderface, GPUBatch /* Not enough place, realloc the array. */ i = shaderface->batches_len; shaderface->batches_len += GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = MEM_recallocN(shaderface->batches, - sizeof(GPUBatch *) * shaderface->batches_len); + shaderface->batches = (GPUBatch **)MEM_recallocN(shaderface->batches, + sizeof(GPUBatch *) * shaderface->batches_len); } shaderface->batches[i] = batch; } diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/gpu/intern/gpu_shader_private.h index e4443e79a8d..2c52568ac25 100644 --- a/source/blender/gpu/intern/gpu_shader_private.h +++ b/source/blender/gpu/intern/gpu_shader_private.h @@ -21,9 +21,12 @@ #ifndef __GPU_SHADER_PRIVATE_H__ #define __GPU_SHADER_PRIVATE_H__ -#include "GPU_glew.h" #include "GPU_shader_interface.h" +#ifdef __cplusplus +extern "C" { +#endif + struct GPUShader { /** Handle for full program (links shader stages below). */ GLuint program; @@ -45,6 +48,10 @@ struct GPUShader { }; /* XXX do not use it. Special hack to use OCIO with batch API. */ -void immGetProgram(GLuint *program, GPUShaderInterface **shaderface); +GPUShader *immGetShader(void); + +#ifdef __cplusplus +} +#endif #endif /* __GPU_SHADER_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_state.c b/source/blender/gpu/intern/gpu_state.cc index bd7aff9772b..794c7a3eb97 100644 --- a/source/blender/gpu/intern/gpu_state.c +++ b/source/blender/gpu/intern/gpu_state.cc @@ -268,6 +268,12 @@ void GPU_clip_distances(int distances_new) distances_enabled = distances_new; } +bool GPU_mipmap_enabled(void) +{ + /* TODO(fclem) this used to be a userdef option. */ + return true; +} + /** \name GPU Push/Pop State * \{ */ @@ -276,33 +282,18 @@ void GPU_clip_distances(int distances_new) typedef struct { eGPUAttrMask mask; - /* GL_ENABLE_BIT */ + /* GL_BLEND_BIT */ uint is_blend : 1; - uint is_cull_face : 1; - uint is_depth_test : 1; - /* uint is_lighting : 1; */ /* UNUSED */ - uint is_line_smooth : 1; - uint is_color_logic_op : 1; - uint is_multisample : 1; - uint is_polygon_offset_line : 1; - uint is_polygon_offset_fill : 1; - uint is_polygon_smooth : 1; - uint is_sample_alpha_to_coverage : 1; - uint is_scissor_test : 1; - uint is_stencil_test : 1; - uint is_framebuffer_srgb : 1; - - bool is_clip_plane[6]; /* GL_DEPTH_BUFFER_BIT */ - /* uint is_depth_test : 1; */ + 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; */ + uint is_scissor_test : 1; /* GL_VIEWPORT_BIT */ int viewport[4]; @@ -315,7 +306,8 @@ typedef struct { } GPUAttrStack; static GPUAttrStack state = { - .top = 0, + {}, + 0, }; #define AttrStack state @@ -338,26 +330,6 @@ void gpuPushAttr(eGPUAttrMask mask) glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attr.depth_write_mask); } - if ((mask & GPU_ENABLE_BIT) != 0) { - Attr.is_blend = glIsEnabled(GL_BLEND); - - for (int i = 0; i < 6; i++) { - Attr.is_clip_plane[i] = glIsEnabled(GL_CLIP_PLANE0 + i); - } - - Attr.is_cull_face = glIsEnabled(GL_CULL_FACE); - Attr.is_depth_test = glIsEnabled(GL_DEPTH_TEST); - Attr.is_line_smooth = glIsEnabled(GL_LINE_SMOOTH); - Attr.is_color_logic_op = glIsEnabled(GL_COLOR_LOGIC_OP); - Attr.is_multisample = glIsEnabled(GL_MULTISAMPLE); - Attr.is_polygon_offset_line = glIsEnabled(GL_POLYGON_OFFSET_LINE); - Attr.is_polygon_offset_fill = glIsEnabled(GL_POLYGON_OFFSET_FILL); - Attr.is_polygon_smooth = glIsEnabled(GL_POLYGON_SMOOTH); - Attr.is_sample_alpha_to_coverage = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE); - Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - Attr.is_stencil_test = glIsEnabled(GL_STENCIL_TEST); - } - if ((mask & GPU_SCISSOR_BIT) != 0) { Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attr.scissor_box); @@ -366,7 +338,6 @@ void gpuPushAttr(eGPUAttrMask mask) if ((mask & GPU_VIEWPORT_BIT) != 0) { glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attr.near_far); glGetIntegerv(GL_VIEWPORT, (GLint *)&Attr.viewport); - Attr.is_framebuffer_srgb = glIsEnabled(GL_FRAMEBUFFER_SRGB); } if ((mask & GPU_BLEND_BIT) != 0) { @@ -401,30 +372,9 @@ void gpuPopAttr(void) glDepthMask(Attr.depth_write_mask); } - if ((mask & GPU_ENABLE_BIT) != 0) { - restore_mask(GL_BLEND, Attr.is_blend); - - for (int i = 0; i < 6; i++) { - restore_mask(GL_CLIP_PLANE0 + i, Attr.is_clip_plane[i]); - } - - restore_mask(GL_CULL_FACE, Attr.is_cull_face); - restore_mask(GL_DEPTH_TEST, Attr.is_depth_test); - restore_mask(GL_LINE_SMOOTH, Attr.is_line_smooth); - restore_mask(GL_COLOR_LOGIC_OP, Attr.is_color_logic_op); - restore_mask(GL_MULTISAMPLE, Attr.is_multisample); - restore_mask(GL_POLYGON_OFFSET_LINE, Attr.is_polygon_offset_line); - restore_mask(GL_POLYGON_OFFSET_FILL, Attr.is_polygon_offset_fill); - restore_mask(GL_POLYGON_SMOOTH, Attr.is_polygon_smooth); - restore_mask(GL_SAMPLE_ALPHA_TO_COVERAGE, Attr.is_sample_alpha_to_coverage); - restore_mask(GL_SCISSOR_TEST, Attr.is_scissor_test); - restore_mask(GL_STENCIL_TEST, Attr.is_stencil_test); - } - 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]); - restore_mask(GL_FRAMEBUFFER_SRGB, Attr.is_framebuffer_srgb); } if ((mask & GPU_SCISSOR_BIT) != 0) { diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.cc index f8d38b489e8..014f82f83de 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "DNA_image_types.h" +#include "DNA_userdef_types.h" #include "BLI_blenlib.h" #include "BLI_math_base.h" @@ -36,7 +37,6 @@ #include "GPU_batch.h" #include "GPU_context.h" #include "GPU_debug.h" -#include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_glew.h" @@ -45,6 +45,16 @@ #include "gpu_context_private.h" +#define WARN_NOT_BOUND(_tex) \ + { \ + if (_tex->number == -1) { \ + fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ + BLI_assert(0); \ + return; \ + } \ + } \ + ((void)0) + static struct GPUTextureGlobal { /** Texture used in place of invalid textures (not loaded correctly, missing). */ GPUTexture *invalid_tex_1D; @@ -71,6 +81,8 @@ typedef enum eGPUTextureFormatFlag { GPU_FORMAT_ARRAY = (1 << 14), } eGPUTextureFormatFlag; +ENUM_OPERATORS(eGPUTextureFormatFlag) + /* GPUTexture */ struct GPUTexture { int w, h, d; /* width/height/depth */ @@ -103,7 +115,7 @@ static void gpu_texture_framebuffer_ensure(GPUTexture *tex); /* ------ Memory Management ------- */ /* Records every texture allocation / free * to estimate the Texture Pool Memory consumption */ -static uint memory_usage; +static uint memory_usage = 0; static uint gpu_texture_memory_footprint_compute(GPUTexture *tex) { @@ -161,54 +173,58 @@ uint GPU_texture_memory_usage_get(void) static const char *gl_enum_to_str(GLenum e) { -#define ENUM_TO_STRING(e) [GL_##e] = STRINGIFY_ARG(e) - static const char *enum_strings[] = { - ENUM_TO_STRING(TEXTURE_CUBE_MAP), - ENUM_TO_STRING(TEXTURE_CUBE_MAP_ARRAY), - ENUM_TO_STRING(TEXTURE_2D), - ENUM_TO_STRING(TEXTURE_2D_ARRAY), - ENUM_TO_STRING(TEXTURE_1D), - ENUM_TO_STRING(TEXTURE_1D_ARRAY), - ENUM_TO_STRING(TEXTURE_3D), - ENUM_TO_STRING(TEXTURE_2D_MULTISAMPLE), - ENUM_TO_STRING(RGBA32F), - ENUM_TO_STRING(RGBA16F), - ENUM_TO_STRING(RGBA16UI), - ENUM_TO_STRING(RGBA16I), - ENUM_TO_STRING(RGBA16), - ENUM_TO_STRING(RGBA8UI), - ENUM_TO_STRING(RGBA8I), - ENUM_TO_STRING(RGBA8), - ENUM_TO_STRING(RGB16F), - ENUM_TO_STRING(RG32F), - ENUM_TO_STRING(RG16F), - ENUM_TO_STRING(RG16UI), - ENUM_TO_STRING(RG16I), - ENUM_TO_STRING(RG16), - ENUM_TO_STRING(RG8UI), - ENUM_TO_STRING(RG8I), - ENUM_TO_STRING(RG8), - ENUM_TO_STRING(R8UI), - ENUM_TO_STRING(R8I), - ENUM_TO_STRING(R8), - ENUM_TO_STRING(R32F), - ENUM_TO_STRING(R32UI), - ENUM_TO_STRING(R32I), - ENUM_TO_STRING(R16F), - ENUM_TO_STRING(R16UI), - ENUM_TO_STRING(R16I), - ENUM_TO_STRING(R16), - ENUM_TO_STRING(R11F_G11F_B10F), - ENUM_TO_STRING(SRGB8_ALPHA8), - ENUM_TO_STRING(DEPTH24_STENCIL8), - ENUM_TO_STRING(DEPTH32F_STENCIL8), - ENUM_TO_STRING(DEPTH_COMPONENT32F), - ENUM_TO_STRING(DEPTH_COMPONENT24), - ENUM_TO_STRING(DEPTH_COMPONENT16), +#define ENUM_TO_STRING(e) \ + case GL_##e: { \ + return STRINGIFY_ARG(e); \ + } + + switch (e) { + ENUM_TO_STRING(TEXTURE_CUBE_MAP); + ENUM_TO_STRING(TEXTURE_CUBE_MAP_ARRAY); + ENUM_TO_STRING(TEXTURE_2D); + ENUM_TO_STRING(TEXTURE_2D_ARRAY); + ENUM_TO_STRING(TEXTURE_1D); + ENUM_TO_STRING(TEXTURE_1D_ARRAY); + ENUM_TO_STRING(TEXTURE_3D); + ENUM_TO_STRING(TEXTURE_2D_MULTISAMPLE); + ENUM_TO_STRING(RGBA32F); + ENUM_TO_STRING(RGBA16F); + ENUM_TO_STRING(RGBA16UI); + ENUM_TO_STRING(RGBA16I); + ENUM_TO_STRING(RGBA16); + ENUM_TO_STRING(RGBA8UI); + ENUM_TO_STRING(RGBA8I); + ENUM_TO_STRING(RGBA8); + ENUM_TO_STRING(RGB16F); + ENUM_TO_STRING(RG32F); + ENUM_TO_STRING(RG16F); + ENUM_TO_STRING(RG16UI); + ENUM_TO_STRING(RG16I); + ENUM_TO_STRING(RG16); + ENUM_TO_STRING(RG8UI); + ENUM_TO_STRING(RG8I); + ENUM_TO_STRING(RG8); + ENUM_TO_STRING(R8UI); + ENUM_TO_STRING(R8I); + ENUM_TO_STRING(R8); + ENUM_TO_STRING(R32F); + ENUM_TO_STRING(R32UI); + ENUM_TO_STRING(R32I); + ENUM_TO_STRING(R16F); + ENUM_TO_STRING(R16UI); + ENUM_TO_STRING(R16I); + ENUM_TO_STRING(R16); + ENUM_TO_STRING(R11F_G11F_B10F); + ENUM_TO_STRING(SRGB8_ALPHA8); + ENUM_TO_STRING(DEPTH24_STENCIL8); + ENUM_TO_STRING(DEPTH32F_STENCIL8); + ENUM_TO_STRING(DEPTH_COMPONENT32F); + ENUM_TO_STRING(DEPTH_COMPONENT24); + ENUM_TO_STRING(DEPTH_COMPONENT16); + default: + return "Unkown enum"; }; #undef ENUM_TO_STRING - - return enum_strings[e]; } static int gpu_get_component_count(eGPUTextureFormat format) @@ -423,6 +439,13 @@ static uint gpu_get_bytesize(eGPUTextureFormat data_type) case GPU_R8: case GPU_R8UI: return 1; + case GPU_SRGB8_A8_DXT1: + case GPU_SRGB8_A8_DXT3: + case GPU_SRGB8_A8_DXT5: + case GPU_RGBA8_DXT1: + case GPU_RGBA8_DXT3: + case GPU_RGBA8_DXT5: + return 1; /* Incorrect but actual size is fractional. */ default: BLI_assert(!"Texture format incorrect or unsupported\n"); return 0; @@ -508,7 +531,18 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format) case GPU_RGB16F: return GL_RGB16F; /* Special formats texture only */ - /* ** Add Format here */ + case GPU_SRGB8_A8_DXT1: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + case GPU_SRGB8_A8_DXT3: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + case GPU_SRGB8_A8_DXT5: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case GPU_RGBA8_DXT1: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case GPU_RGBA8_DXT3: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case GPU_RGBA8_DXT5: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; /* Depth Formats */ case GPU_DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT32F; @@ -522,99 +556,6 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format) } } -static eGPUTextureFormat gl_internalformat_to_gpu_format(const GLint glformat) -{ - /* You can add any of the available type to this list - * For available types see GPU_texture.h */ - switch (glformat) { - /* Formats texture & renderbuffer */ - case GL_RGBA8UI: - return GPU_RGBA8UI; - case GL_RGBA8I: - return GPU_RGBA8I; - case GL_RGBA8: - return GPU_RGBA8; - case GL_RGBA32UI: - return GPU_RGBA32UI; - case GL_RGBA32I: - return GPU_RGBA32I; - case GL_RGBA32F: - return GPU_RGBA32F; - case GL_RGBA16UI: - return GPU_RGBA16UI; - case GL_RGBA16I: - return GPU_RGBA16I; - case GL_RGBA16F: - return GPU_RGBA16F; - case GL_RGBA16: - return GPU_RGBA16; - case GL_RG8UI: - return GPU_RG8UI; - case GL_RG8I: - return GPU_RG8I; - case GL_RG8: - return GPU_RG8; - case GL_RG32UI: - return GPU_RG32UI; - case GL_RG32I: - return GPU_RG32I; - case GL_RG32F: - return GPU_RG32F; - case GL_RG16UI: - return GPU_RG16UI; - case GL_RG16I: - return GPU_RG16I; - case GL_RG16F: - return GPU_RGBA32F; - case GL_RG16: - return GPU_RG16; - case GL_R8UI: - return GPU_R8UI; - case GL_R8I: - return GPU_R8I; - case GL_R8: - return GPU_R8; - case GL_R32UI: - return GPU_R32UI; - case GL_R32I: - return GPU_R32I; - case GL_R32F: - return GPU_R32F; - case GL_R16UI: - return GPU_R16UI; - case GL_R16I: - return GPU_R16I; - case GL_R16F: - return GPU_R16F; - case GL_R16: - return GPU_R16; - /* Special formats texture & renderbuffer */ - case GL_R11F_G11F_B10F: - return GPU_R11F_G11F_B10F; - case GL_DEPTH32F_STENCIL8: - return GPU_DEPTH32F_STENCIL8; - case GL_DEPTH24_STENCIL8: - return GPU_DEPTH24_STENCIL8; - case GL_SRGB8_ALPHA8: - return GPU_SRGB8_A8; - /* Texture only format */ - case GL_RGB16F: - return GPU_RGB16F; - /* Special formats texture only */ - /* ** Add Format here */ - /* Depth Formats */ - case GL_DEPTH_COMPONENT32F: - return GPU_DEPTH_COMPONENT32F; - case GL_DEPTH_COMPONENT24: - return GPU_DEPTH_COMPONENT24; - case GL_DEPTH_COMPONENT16: - return GPU_DEPTH_COMPONENT16; - default: - BLI_assert(!"Internal format incorrect or unsupported\n"); - } - return -1; -} - static GLenum gpu_get_gl_datatype(eGPUDataFormat format) { switch (format) { @@ -640,8 +581,8 @@ static float *GPU_texture_rescale_3d( GPUTexture *tex, int w, int h, int d, int channels, const float *fpixels) { const uint xf = w / tex->w, yf = h / tex->h, zf = d / tex->d; - float *nfpixels = MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d, - "GPUTexture Rescaled 3Dtex"); + float *nfpixels = (float *)MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d, + "GPUTexture Rescaled 3Dtex"); if (nfpixels) { GPU_print_error_debug("You need to scale a 3D texture, feel the pain!"); @@ -828,7 +769,7 @@ GPUTexture *GPU_texture_create_nD(int w, tex_format = GPU_DEPTH32F_STENCIL8; } - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->w = w; tex->h = h; tex->d = d; @@ -837,7 +778,7 @@ GPUTexture *GPU_texture_create_nD(int w, tex->format = tex_format; tex->components = gpu_get_component_count(tex_format); tex->mipmaps = 0; - tex->format_flag = 0; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); tex->number = -1; if (n == 2) { @@ -921,7 +862,7 @@ GPUTexture *GPU_texture_create_nD(int w, data_type, tex->components, can_rescale, - pixels, + (float *)pixels, &rescaled_pixels); if (G.debug & G_DEBUG_GPU || !valid) { @@ -952,7 +893,7 @@ GPUTexture *GPU_texture_create_nD(int w, gpu_texture_memory_footprint_add(tex); /* Upload Texture */ - const float *pix = (rescaled_pixels) ? rescaled_pixels : pixels; + const void *pix = (rescaled_pixels) ? rescaled_pixels : pixels; if (tex->target == GL_TEXTURE_2D || tex->target == GL_TEXTURE_2D_MULTISAMPLE || tex->target == GL_TEXTURE_1D_ARRAY) { @@ -1002,7 +943,7 @@ GPUTexture *GPU_texture_cube_create(int w, eGPUDataFormat gpu_data_format, char err_out[256]) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->w = w; tex->h = w; tex->d = d; @@ -1126,11 +1067,11 @@ GPUTexture *GPU_texture_cube_create(int w, /* Special buffer textures. tex_format must be compatible with the buffer content. */ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint buffer) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->refcount = 1; tex->format = tex_format; tex->components = gpu_get_component_count(tex_format); - tex->format_flag = 0; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); tex->target_base = tex->target = GL_TEXTURE_BUFFER; tex->mipmaps = 0; tex->number = -1; @@ -1176,44 +1117,87 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint return tex; } -GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) +static GLenum convert_target_to_gl(int dimension, bool is_array) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->bindcode = bindcode; + switch (dimension) { + case 1: + return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY; + case 2: + return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY; + case 3: + return GL_TEXTURE_3D; + default: + BLI_assert(0); + return GL_TEXTURE_2D; + } +} + +/* Create an error texture that will bind an invalid texture (pink) at draw time. */ +GPUTexture *GPU_texture_create_error(int dimension, bool is_array) +{ + GLenum textarget = convert_target_to_gl(dimension, is_array); + + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); + tex->bindcode = 0; tex->refcount = 1; tex->target = textarget; tex->target_base = textarget; tex->samples = 0; - tex->sampler_state = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO; - if (GPU_get_mipmap()) { - tex->sampler_state |= (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER); - } + tex->sampler_state = GPU_SAMPLER_DEFAULT; tex->number = -1; - if (!glIsTexture(tex->bindcode)) { - GPU_print_error_debug("Blender Texture Not Loaded"); + GPU_print_error_debug("Blender Texture Not Loaded"); + return tex; +} + +/* DDS texture loading. Return NULL if support is not available. */ +GPUTexture *GPU_texture_create_compressed( + int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data) +{ + if (!GLEW_EXT_texture_compression_s3tc) { + return NULL; } - else { - GLint w, h, gl_format; - GLenum gettarget; - gettarget = (textarget == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : textarget; - - glBindTexture(textarget, tex->bindcode); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_HEIGHT, &h); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_INTERNAL_FORMAT, &gl_format); - tex->w = w; - tex->h = h; - tex->format = gl_internalformat_to_gpu_format(gl_format); - tex->components = gpu_get_component_count(tex->format); - glBindTexture(textarget, 0); - - /* Depending on how this bindcode was obtained, the memory used here could - * already have been computed. - * But that is not the case currently. */ - gpu_texture_memory_footprint_add(tex); + + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); + tex->w = w; + tex->h = h; + tex->refcount = 1; + tex->target = tex->target_base = GL_TEXTURE_2D; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); + tex->components = gpu_get_component_count(tex_format); + tex->mipmaps = miplen - 1; + tex->sampler_state = GPU_SAMPLER_DEFAULT; + tex->number = -1; + + GLenum internalformat = gpu_format_to_gl_internalformat(tex_format); + + glGenTextures(1, &tex->bindcode); + glBindTexture(tex->target, tex->bindcode); + + /* Reset to opengl Defaults. (Untested, might not be needed) */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int blocksize = (ELEM(tex_format, GPU_RGBA8_DXT1, GPU_SRGB8_A8_DXT1)) ? 8 : 16; + + size_t ofs = 0; + for (int mip = 0; mip < miplen && (w || h); mip++, w >>= 1, h >>= 1) { + w = max_ii(1, w); + h = max_ii(1, h); + size_t size = ((w + 3) / 4) * ((h + 3) / 4) * blocksize; + + glCompressedTexImage2D(tex->target, mip, internalformat, w, h, 0, size, (uchar *)data + ofs); + + ofs += size; } + /* Restore Blender default. */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, tex->mipmaps); + glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glBindTexture(tex->target, 0); + return tex; } @@ -1468,7 +1452,8 @@ void GPU_texture_update_sub(GPUTexture *tex, GLenum data_format = gpu_get_gl_dataformat(tex->format, &tex->format_flag); GLenum data_type = gpu_get_gl_datatype(gpu_data_format); - glBindTexture(tex->target, tex->bindcode); + WARN_NOT_BOUND(tex); + switch (tex->target) { case GL_TEXTURE_1D: glTexSubImage1D(tex->target, 0, offset_x, width, data_format, data_type, pixels); @@ -1496,8 +1481,6 @@ void GPU_texture_update_sub(GPUTexture *tex, default: BLI_assert(!"tex->target mode not supported"); } - - glBindTexture(tex->target, 0); } void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat gpu_data_format, int miplvl) @@ -1791,16 +1774,6 @@ void GPU_texture_unbind_all(void) glActiveTexture(GL_TEXTURE0); } -#define WARN_NOT_BOUND(_tex) \ - { \ - if (_tex->number == -1) { \ - fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ - BLI_assert(0); \ - return; \ - } \ - } \ - ((void)0) - void GPU_texture_generate_mipmap(GPUTexture *tex) { WARN_NOT_BOUND(tex); @@ -1944,6 +1917,14 @@ void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter) SET_FLAG_FROM_TEST(tex->sampler_state, use_filter, GPU_SAMPLER_FILTER); } +void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso) +{ + /* Stencil and integer format does not support filtering. */ + BLI_assert(!(use_aniso) || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex))); + + SET_FLAG_FROM_TEST(tex->sampler_state, use_aniso, GPU_SAMPLER_ANISO); +} + void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp) { SET_FLAG_FROM_TEST(tex->sampler_state, use_repeat, GPU_SAMPLER_REPEAT); @@ -2158,7 +2139,7 @@ void GPU_samplers_init(void) { glGenSamplers(GPU_SAMPLER_MAX, GG.samplers); for (int i = 0; i < GPU_SAMPLER_MAX; i++) { - eGPUSamplerState state = i; + eGPUSamplerState state = static_cast<eGPUSamplerState>(i); GLenum clamp_type = (state & GPU_SAMPLER_CLAMP_BORDER) ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE; GLenum wrap_s = (state & GPU_SAMPLER_REPEAT_S) ? GL_REPEAT : clamp_type; GLenum wrap_t = (state & GPU_SAMPLER_REPEAT_T) ? GL_REPEAT : clamp_type; @@ -2168,8 +2149,9 @@ void GPU_samplers_init(void) ((state & GPU_SAMPLER_MIPMAP) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) : ((state & GPU_SAMPLER_MIPMAP) ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST); 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)) ? - GPU_get_anisotropic() : + U.anisotropic_filter : 1.0f; glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s); diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.c b/source/blender/gpu/intern/gpu_uniformbuffer.cc index 130e8fe7da1..846ab6c8560 100644 --- a/source/blender/gpu/intern/gpu_uniformbuffer.c +++ b/source/blender/gpu/intern/gpu_uniformbuffer.cc @@ -25,6 +25,7 @@ #include <string.h> #include "BLI_blenlib.h" +#include "BLI_math_base.h" #include "gpu_context_private.h" #include "gpu_node_graph.h" @@ -34,217 +35,63 @@ #include "GPU_material.h" #include "GPU_uniformbuffer.h" -typedef enum eGPUUniformBufferFlag { - GPU_UBO_FLAG_INITIALIZED = (1 << 0), - GPU_UBO_FLAG_DIRTY = (1 << 1), -} eGPUUniformBufferFlag; - -typedef enum eGPUUniformBufferType { - GPU_UBO_STATIC = 0, - GPU_UBO_DYNAMIC = 1, -} eGPUUniformBufferType; - -struct GPUUniformBuffer { - int size; /* in bytes */ - GLuint bindcode; /* opengl identifier for UBO */ - int bindpoint; /* current binding point */ - eGPUUniformBufferType type; -}; - -#define GPUUniformBufferStatic GPUUniformBuffer - -typedef struct GPUUniformBufferDynamic { - GPUUniformBuffer buffer; - void *data; /* Continuous memory block to copy to GPU. */ - char flag; -} GPUUniformBufferDynamic; - -/* Prototypes */ -static eGPUType get_padded_gpu_type(struct LinkData *link); -static void gpu_uniformbuffer_inputs_sort(struct 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. */ -#define MAX_UBO_GPU_TYPE GPU_MAT4 - -static void gpu_uniformbuffer_initialize(GPUUniformBuffer *ubo, const void *data) -{ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferData(GL_UNIFORM_BUFFER, ubo->size, data, GL_DYNAMIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); -} +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]) { /* Make sure that UBO is padded to size of vec4 */ BLI_assert((size % 16) == 0); - GPUUniformBuffer *ubo = MEM_callocN(sizeof(GPUUniformBufferStatic), "GPUUniformBufferStatic"); - ubo->size = size; - ubo->bindpoint = -1; - - /* Generate Buffer object */ - ubo->bindcode = GPU_buf_alloc(); - - if (!ubo->bindcode) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO create failed", 256); - } - GPU_uniformbuffer_free(ubo); - return NULL; - } - - if (ubo->size > GPU_max_ubo_size()) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); - } - GPU_uniformbuffer_free(ubo); - return NULL; - } - - gpu_uniformbuffer_initialize(ubo, data); - return ubo; -} - -/** - * 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]) -{ - /* There is no point on creating an UBO if there is no arguments. */ - if (BLI_listbase_is_empty(inputs)) { - return NULL; - } - - GPUUniformBufferDynamic *ubo = MEM_callocN(sizeof(GPUUniformBufferDynamic), - "GPUUniformBufferDynamic"); - ubo->buffer.type = GPU_UBO_DYNAMIC; - ubo->buffer.bindpoint = -1; - ubo->flag = GPU_UBO_FLAG_DIRTY; - - /* Generate Buffer object. */ - ubo->buffer.bindcode = GPU_buf_alloc(); - - if (!ubo->buffer.bindcode) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO create failed", 256); - } - GPU_uniformbuffer_free(&ubo->buffer); - return NULL; - } - - if (ubo->buffer.size > GPU_max_ubo_size()) { + if (size > GPU_max_ubo_size()) { if (err_out) { BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); } - GPU_uniformbuffer_free(&ubo->buffer); return NULL; } - /* Make sure we comply to the ubo alignment requirements. */ - gpu_uniformbuffer_inputs_sort(inputs); - - LISTBASE_FOREACH (LinkData *, link, inputs) { - const eGPUType gputype = get_padded_gpu_type(link); - ubo->buffer.size += gputype * sizeof(float); - } - - /* Round up to size of vec4 */ - ubo->buffer.size = ((ubo->buffer.size + 15) / 16) * 16; - - /* Allocate the data. */ - ubo->data = MEM_mallocN(ubo->buffer.size, __func__); + GPUUniformBuffer *ubo = (GPUUniformBuffer *)MEM_mallocN(sizeof(GPUUniformBuffer), __func__); + ubo->size = size; + ubo->data = NULL; + ubo->bindcode = 0; + ubo->bindpoint = -1; - /* Now that we know the total ubo size we can start populating it. */ - float *offset = ubo->data; - LISTBASE_FOREACH (LinkData *, link, inputs) { - GPUInput *input = link->data; - memcpy(offset, input->vec, input->type * sizeof(float)); - offset += get_padded_gpu_type(link); + /* Direct init. */ + if (data != NULL) { + GPU_uniformbuffer_update(ubo, data); } - /* Note since we may create the UBOs in the CPU in a different thread than the main drawing one, - * we don't create the UBO in the GPU here. This will happen when we first bind the UBO. - */ - - return &ubo->buffer; -} - -/** - * Free the data - */ -static void gpu_uniformbuffer_dynamic_free(GPUUniformBuffer *ubo_) -{ - BLI_assert(ubo_->type == GPU_UBO_DYNAMIC); - GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_; - - ubo->buffer.size = 0; - if (ubo->data) { - MEM_freeN(ubo->data); - } + return ubo; } void GPU_uniformbuffer_free(GPUUniformBuffer *ubo) { - if (ubo->type == GPU_UBO_DYNAMIC) { - gpu_uniformbuffer_dynamic_free(ubo); - } - + MEM_SAFE_FREE(ubo->data); GPU_buf_free(ubo->bindcode); MEM_freeN(ubo); } -static void gpu_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) -{ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); - glBindBuffer(GL_UNIFORM_BUFFER, 0); -} - -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) -{ - BLI_assert(ubo->type == GPU_UBO_STATIC); - gpu_uniformbuffer_update(ubo, data); -} - -/** - * We need to recalculate the internal data, and re-generate it - * from its populated items. - */ -void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_) -{ - BLI_assert(ubo_->type == GPU_UBO_DYNAMIC); - GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_; - - if (ubo->flag & GPU_UBO_FLAG_INITIALIZED) { - gpu_uniformbuffer_update(ubo_, ubo->data); - } - else { - ubo->flag |= GPU_UBO_FLAG_INITIALIZED; - gpu_uniformbuffer_initialize(ubo_, ubo->data); - } - - ubo->flag &= ~GPU_UBO_FLAG_DIRTY; -} - /** * We need to pad some data types (vec3) on the C side * To match the GPU expected memory block alignment. */ static eGPUType get_padded_gpu_type(LinkData *link) { - GPUInput *input = link->data; + GPUInput *input = (GPUInput *)link->data; eGPUType gputype = input->type; - /* Unless the vec3 is followed by a float we need to treat it as a vec4. */ if (gputype == GPU_VEC3 && (link->next != NULL) && (((GPUInput *)link->next->data)->type != GPU_FLOAT)) { gputype = GPU_VEC4; } - return gputype; } @@ -254,8 +101,9 @@ static eGPUType get_padded_gpu_type(LinkData *link) */ static int inputs_cmp(const void *a, const void *b) { - const LinkData *link_a = a, *link_b = b; - const GPUInput *input_a = link_a->data, *input_b = link_b->data; + const LinkData *link_a = (const LinkData *)a, *link_b = (const LinkData *)b; + const GPUInput *input_a = (const GPUInput *)link_a->data; + const GPUInput *input_b = (const GPUInput *)link_b->data; return input_a->type < input_b->type ? 1 : 0; } @@ -265,15 +113,19 @@ static int inputs_cmp(const void *a, const void *b) */ static void gpu_uniformbuffer_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. */ +#define MAX_UBO_GPU_TYPE GPU_MAT4 + /* Order them as mat4, vec4, vec3, vec2, float. */ BLI_listbase_sort(inputs, inputs_cmp); /* Creates a lookup table for the different types; */ LinkData *inputs_lookup[MAX_UBO_GPU_TYPE + 1] = {NULL}; - eGPUType cur_type = MAX_UBO_GPU_TYPE + 1; + eGPUType cur_type = static_cast<eGPUType>(MAX_UBO_GPU_TYPE + 1); LISTBASE_FOREACH (LinkData *, link, inputs) { - GPUInput *input = link->data; + GPUInput *input = (GPUInput *)link->data; if (input->type == GPU_MAT3) { /* Alignment for mat3 is not handled currently, so not supported */ @@ -319,6 +171,74 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) link = link_next; } +#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]) +{ + /* 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); + } + /* 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__); + + /* Now that we know the total ubo size we can start populating it. */ + float *offset = (float *)data; + LISTBASE_FOREACH (LinkData *, link, inputs) { + GPUInput *input = (GPUInput *)link->data; + 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; + } + + glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); + glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW); +} + +void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) +{ + if (ubo->bindcode == 0) { + gpu_uniformbuffer_init(ubo); + } + + glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); + glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); } void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) @@ -328,28 +248,30 @@ void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) return; } - if (ubo->type == GPU_UBO_DYNAMIC) { - GPUUniformBufferDynamic *ubo_dynamic = (GPUUniformBufferDynamic *)ubo; - if (ubo_dynamic->flag & GPU_UBO_FLAG_DIRTY) { - GPU_uniformbuffer_dynamic_update(ubo); - } + if (ubo->bindcode == 0) { + gpu_uniformbuffer_init(ubo); } - if (ubo->bindcode != 0) { - glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); + if (ubo->data != NULL) { + GPU_uniformbuffer_update(ubo, ubo->data); + MEM_SAFE_FREE(ubo->data); } + glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); ubo->bindpoint = number; } void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo) { - ubo->bindpoint = -1; +#ifndef NDEBUG + glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0); +#endif + ubo->bindpoint = 0; } -int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo) +void GPU_uniformbuffer_unbind_all(void) { - return ubo->bindpoint; + for (int i = 0; i < GPU_max_ubo_binds(); i++) { + glBindBufferBase(GL_UNIFORM_BUFFER, i, 0); + } } - -#undef MAX_UBO_GPU_TYPE diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.c b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 3b4d469542c..eda6d1c7300 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.c +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -39,17 +39,22 @@ static uint vbo_memory_usage; static GLenum convert_usage_type_to_gl(GPUUsageType type) { - static const GLenum table[] = { - [GPU_USAGE_STREAM] = GL_STREAM_DRAW, - [GPU_USAGE_STATIC] = GL_STATIC_DRAW, - [GPU_USAGE_DYNAMIC] = GL_DYNAMIC_DRAW, - }; - return table[type]; + switch (type) { + case GPU_USAGE_STREAM: + return GL_STREAM_DRAW; + case GPU_USAGE_DYNAMIC: + return GL_DYNAMIC_DRAW; + case GPU_USAGE_STATIC: + return GL_STATIC_DRAW; + default: + BLI_assert(0); + return GL_STATIC_DRAW; + } } GPUVertBuf *GPU_vertbuf_create(GPUUsageType usage) { - GPUVertBuf *verts = MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf"); + GPUVertBuf *verts = (GPUVertBuf *)MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf"); GPU_vertbuf_init(verts, usage); return verts; } @@ -109,7 +114,7 @@ GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts) } if (verts->data) { - verts_dst->data = MEM_dupallocN(verts->data); + verts_dst->data = (uchar *)MEM_dupallocN(verts->data); } return verts_dst; } @@ -161,7 +166,7 @@ void GPU_vertbuf_data_alloc(GPUVertBuf *verts, uint v_len) #endif verts->dirty = true; verts->vertex_len = verts->vertex_alloc = v_len; - verts->data = MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), "GPUVertBuf data"); + verts->data = (uchar *)MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), __func__); } /* resize buffer keeping existing data */ @@ -178,7 +183,7 @@ void GPU_vertbuf_data_resize(GPUVertBuf *verts, uint v_len) #endif verts->dirty = true; verts->vertex_len = verts->vertex_alloc = v_len; - verts->data = MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts)); + verts->data = (uchar *)MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts)); } /* Set vertex count but does not change allocation. diff --git a/source/blender/gpu/intern/gpu_vertex_format.c b/source/blender/gpu/intern/gpu_vertex_format.cc index 585a22277b2..a59a6a468ce 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.c +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -63,21 +63,29 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src) memcpy(dest, src, sizeof(GPUVertFormat)); } -static GLenum convert_comp_type_to_gl(GPUVertCompType type) +GLenum convert_comp_type_to_gl(GPUVertCompType type) { - static const GLenum table[] = { - [GPU_COMP_I8] = GL_BYTE, - [GPU_COMP_U8] = GL_UNSIGNED_BYTE, - [GPU_COMP_I16] = GL_SHORT, - [GPU_COMP_U16] = GL_UNSIGNED_SHORT, - [GPU_COMP_I32] = GL_INT, - [GPU_COMP_U32] = GL_UNSIGNED_INT, - - [GPU_COMP_F32] = GL_FLOAT, - - [GPU_COMP_I10] = GL_INT_2_10_10_10_REV, - }; - return table[type]; + switch (type) { + case GPU_COMP_I8: + return GL_BYTE; + case GPU_COMP_U8: + return GL_UNSIGNED_BYTE; + case GPU_COMP_I16: + return GL_SHORT; + case GPU_COMP_U16: + return GL_UNSIGNED_SHORT; + case GPU_COMP_I32: + return GL_INT; + case GPU_COMP_U32: + return GL_UNSIGNED_INT; + case GPU_COMP_F32: + return GL_FLOAT; + case GPU_COMP_I10: + return GL_INT_2_10_10_10_REV; + default: + BLI_assert(0); + return GL_FLOAT; + } } static uint comp_sz(GPUVertCompType type) @@ -94,7 +102,7 @@ static uint attr_sz(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - return a->comp_len * comp_sz(a->comp_type); + return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type)); } static uint attr_align(const GPUVertAttr *a) @@ -102,7 +110,7 @@ static uint attr_align(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - uint c = comp_sz(a->comp_type); + uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type)); if (a->comp_len == 3 && c <= 2) { return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */ } @@ -185,7 +193,6 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, attr->names[attr->name_len++] = copy_attr_name(format, name); attr->comp_type = comp_type; - attr->gl_comp_type = convert_comp_type_to_gl(comp_type); attr->comp_len = (comp_type == GPU_COMP_I10) ? 4 : comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */ @@ -279,7 +286,7 @@ void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr_id, const char * /* Encode 8 original bytes into 11 safe bytes. */ static void safe_bytes(char out[11], const char data[8]) { - char safe_chars[63] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + char safe_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; uint64_t in = *(uint64_t *)data; for (int i = 0; i < 11; i++) { @@ -368,14 +375,6 @@ static void show_pack(uint a_idx, uint sz, uint pad) void VertexFormat_pack(GPUVertFormat *format) { - /* For now, attributes are packed in the order they were added, - * making sure each attribute is naturally aligned (add padding where necessary) - * Later we can implement more efficient packing w/ reordering - * (keep attribute ID order, adjust their offsets to reorder in buffer). */ - - /* TODO: realloc just enough to hold the final combo string. And just enough to - * hold used attributes, not all 16. */ - GPUVertAttr *a0 = &format->attrs[0]; a0->offset = 0; uint offset = a0->sz; @@ -512,7 +511,6 @@ void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader) attr->sz = attr->comp_len * 4; attr->fetch_mode = fetch_mode; attr->comp_type = comp_type; - attr->gl_comp_type = convert_comp_type_to_gl(comp_type); attr += 1; } } diff --git a/source/blender/gpu/intern/gpu_vertex_format_private.h b/source/blender/gpu/intern/gpu_vertex_format_private.h index a850d17a1dd..7b560a35bd0 100644 --- a/source/blender/gpu/intern/gpu_vertex_format_private.h +++ b/source/blender/gpu/intern/gpu_vertex_format_private.h @@ -26,8 +26,17 @@ #ifndef __GPU_VERTEX_FORMAT_PRIVATE_H__ #define __GPU_VERTEX_FORMAT_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + void VertexFormat_pack(GPUVertFormat *format); uint padding(uint offset, uint alignment); uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len); +GLenum convert_comp_type_to_gl(GPUVertCompType type); + +#ifdef __cplusplus +} +#endif #endif /* __GPU_VERTEX_FORMAT_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 4d31366f53f..ed5297f0a4a 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -38,7 +38,6 @@ #include "DNA_vec_types.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl new file mode 100644 index 00000000000..f7bf3d33361 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -0,0 +1,87 @@ + +vec3 calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2) +{ + vec3 edge21 = pos2 - pos1; + vec3 edge10 = pos1 - pos0; + vec3 edge02 = pos0 - pos2; + vec3 d21 = normalize(edge21); + vec3 d10 = normalize(edge10); + vec3 d02 = normalize(edge02); + + vec3 dists; + float d = dot(d21, edge02); + dists.x = sqrt(dot(edge02, edge02) - d * d); + d = dot(d02, edge10); + dists.y = sqrt(dot(edge10, edge10) - d * d); + d = dot(d10, edge21); + dists.z = sqrt(dot(edge21, edge21) - d * d); + return dists; +} + +vec2 calc_barycentric_co(int vertid) +{ + vec2 bary; + bary.x = float((vertid % 3) == 0); + bary.y = float((vertid % 3) == 1); + return bary; +} + +#ifdef HAIR_SHADER + +/* Hairs uv and col attributes are passed by bufferTextures. */ +# define DEFINE_ATTR(type, attr) uniform samplerBuffer attr +# define GET_ATTR(type, attr) hair_get_customdata_##type(attr) + +# define barycentric_get() hair_get_barycentric() +# define barycentric_resolve(bary) hair_resolve_barycentric(bary) + +vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const samplerBuffer orco_samp) +{ + /* TODO: fix ORCO with modifiers. */ + vec3 orco = (modelmatinv * vec4(local_pos, 1.0)).xyz; + return orco_madd[0].xyz + orco * orco_madd[1].xyz; +} + +vec4 tangent_get(const samplerBuffer attr, mat3 normalmat) +{ + /* Unsupported */ + return vec4(0.0); +} + +#else /* MESH_SHADER */ + +# define DEFINE_ATTR(type, attr) in type attr +# define GET_ATTR(type, attr) attr + +/* Calculated in geom shader later with calc_barycentric_co. */ +# define barycentric_get() vec2(0) +# define barycentric_resolve(bary) bary + +vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco) +{ + /* If the object does not have any deformation, the orco layer calculation is done on the fly + * using the orco_madd factors. + * We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex + * attrib (which is [0,0,0,1]). */ + if (orco.w == 0.0) { + return orco.xyz * 0.5 + 0.5; + } + else { + return orco_madd[0].xyz + local_pos * orco_madd[1].xyz; + } +} + +vec4 tangent_get(vec4 attr, mat3 normalmat) +{ + vec4 tangent; + tangent.xyz = normalmat * attr.xyz; + tangent.w = attr.w; + float len_sqr = dot(tangent.xyz, tangent.xyz); + /* Normalize only if vector is not null. */ + if (len_sqr > 0.0) { + tangent.xyz *= inversesqrt(len_sqr); + } + return tangent; +} + +#endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl index d6d6fbab971..eea8d19efce 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl @@ -3,7 +3,7 @@ void node_ambient_occlusion( vec4 color, float distance, vec3 normal, out vec4 result_color, out float result_ao) { vec3 bent_normal; - vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0); + vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); result_ao = occlusion_compute(normalize(normal), viewPosition, 1.0, rand, bent_normal); result_color = result_ao * color; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 3b23ac976ae..6330daa4391 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -1,3 +1,15 @@ + +float wang_hash_noise(uint s) +{ + s = (s ^ 61u) ^ (s >> 16u); + s *= 9u; + s = s ^ (s >> 4u); + s *= 0x27d4eb2du; + s = s ^ (s >> 15u); + + return fract(float(s) / 4294967296.0); +} + void node_hair_info(out float is_strand, out float intercept, out float thickness, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl index f9691beee6f..d33465fa846 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl @@ -6,7 +6,7 @@ void world_normals_get(out vec3 N) vec3 B = normalize(cross(worldNormal, hairTangent)); float cos_theta; if (hairThicknessRes == 1) { - vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0); + vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy); /* Random cosine normal distribution on the hair surface. */ cos_theta = rand.x * 2.0 - 1.0; } diff --git a/source/blender/ikplugin/BIK_api.h b/source/blender/ikplugin/BIK_api.h index 2c2053b47a6..38a48864cc0 100644 --- a/source/blender/ikplugin/BIK_api.h +++ b/source/blender/ikplugin/BIK_api.h @@ -36,10 +36,10 @@ struct bConstraint; struct bPose; struct bPoseChannel; -void BIK_initialize_tree(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - float ctime); +void BIK_init_tree(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + float ctime); void BIK_execute_tree(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, diff --git a/source/blender/ikplugin/intern/ikplugin_api.c b/source/blender/ikplugin/intern/ikplugin_api.c index 5e683d36408..233150a77aa 100644 --- a/source/blender/ikplugin/intern/ikplugin_api.c +++ b/source/blender/ikplugin/intern/ikplugin_api.c @@ -80,7 +80,7 @@ static IKPlugin *get_plugin(bPose *pose) /*----------------------------------------*/ /* Plugin API */ -void BIK_initialize_tree(struct Depsgraph *depsgraph, Scene *scene, Object *ob, float ctime) +void BIK_init_tree(struct Depsgraph *depsgraph, Scene *scene, Object *ob, float ctime) { IKPlugin *plugin = get_plugin(ob->pose); diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index cad0be659ec..cf637a06405 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ../blenkernel ../blenlib ../blenloader + ../gpu ../makesdna ../makesrna ../../../intern/guardedalloc @@ -63,6 +64,7 @@ set(SRC intern/thumbs_blend.c intern/thumbs_font.c intern/util.c + intern/util_gpu.c intern/writeimage.c IMB_colormanagement.h diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 478297e61b2..ed56268e436 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -90,6 +90,12 @@ struct Stereo3dFormat; /** * + * \attention defined in GPU_texture.h + */ +struct GPUTexture; + +/** + * * \attention Defined in allocimbuf.c */ void IMB_init(void); @@ -729,6 +735,25 @@ const char *IMB_ffmpeg_last_error(void); /** * + * \attention defined in util_gpu.c + */ +struct GPUTexture *IMB_create_gpu_texture(struct ImBuf *ibuf, + bool use_high_bitdepth, + bool use_premult); +struct GPUTexture *IMB_touch_gpu_texture( + struct ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth); +void IMB_update_gpu_texture_sub(struct GPUTexture *tex, + struct ImBuf *ibuf, + int x, + int y, + int z, + int w, + int h, + bool use_high_bitdepth, + bool use_premult); + +/** + * * \attention defined in stereoimbuf.c */ void IMB_stereo3d_write_dimensions(const char mode, diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index c9b3db39976..136d191c6a0 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -3958,7 +3958,7 @@ static void curve_mapping_to_ocio_settings(CurveMapping *curve_mapping, { int i; - BKE_curvemapping_initialize(curve_mapping); + BKE_curvemapping_init(curve_mapping); BKE_curvemapping_premultiply(curve_mapping, false); BKE_curvemapping_table_RGBA( curve_mapping, &curve_mapping_settings->lut, &curve_mapping_settings->lut_size); diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index 5154f50c7e8..f81c005bf06 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -651,7 +651,7 @@ BLI_INLINE int DOWNSAMPLE_FLOAT_TO_16BIT(const float _val) #define COMP_24_CS 1041666 /*Maximum size per color component for 2K & 4K @ 24fps*/ #define COMP_48_CS 520833 /*Maximum size per color component for 2K @ 48fps*/ -static int initialise_4K_poc(opj_poc_t *POC, int numres) +static int init_4K_poc(opj_poc_t *POC, int numres) { POC[0].tile = 1; POC[0].resno0 = 0; @@ -750,7 +750,7 @@ static void cinema_setup_encoder(opj_cparameters_t *parameters, else { parameters->cp_rsiz = DCP_CINEMA2K; } - parameters->numpocs = initialise_4K_poc(parameters->POC, parameters->numresolution); + parameters->numpocs = init_4K_poc(parameters->POC, parameters->numresolution); break; case OPJ_OFF: /* do nothing */ diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index 5569e119b95..247122065de 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -669,17 +669,17 @@ static void imb_stereo3d_squeeze_rect( /*************************** preparing to call the write functions **************************/ -static void imb_stereo3d_data_initialize(Stereo3DData *s3d_data, - const bool is_float, - const size_t x, - const size_t y, - const size_t channels, - int *rect_left, - int *rect_right, - int *rect_stereo, - float *rectf_left, - float *rectf_right, - float *rectf_stereo) +static void imb_stereo3d_data_init(Stereo3DData *s3d_data, + const bool is_float, + const size_t x, + const size_t y, + const size_t channels, + int *rect_left, + int *rect_right, + int *rect_stereo, + float *rectf_left, + float *rectf_right, + float *rectf_stereo) { s3d_data->is_float = is_float; s3d_data->x = x; @@ -709,7 +709,7 @@ int *IMB_stereo3d_from_rect(ImageFormatData *im_format, im_format->stereo3d_format.display_mode, false, x, y, &width, &height); r_rect = MEM_mallocN(channels * sizeof(int) * width * height, __func__); - imb_stereo3d_data_initialize( + imb_stereo3d_data_init( &s3d_data, is_float, x, y, channels, rect_left, rect_right, r_rect, NULL, NULL, NULL); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_rect(r_rect, &im_format->stereo3d_format, x, y, channels); @@ -733,7 +733,7 @@ float *IMB_stereo3d_from_rectf(ImageFormatData *im_format, im_format->stereo3d_format.display_mode, false, x, y, &width, &height); r_rectf = MEM_mallocN(channels * sizeof(float) * width * height, __func__); - imb_stereo3d_data_initialize( + imb_stereo3d_data_init( &s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, r_rectf); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_rectf(r_rectf, &im_format->stereo3d_format, x, y, channels); @@ -759,17 +759,17 @@ ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *i ibuf_stereo->flags = ibuf_left->flags; - imb_stereo3d_data_initialize(&s3d_data, - is_float, - ibuf_left->x, - ibuf_left->y, - 4, - (int *)ibuf_left->rect, - (int *)ibuf_right->rect, - (int *)ibuf_stereo->rect, - ibuf_left->rect_float, - ibuf_right->rect_float, - ibuf_stereo->rect_float); + imb_stereo3d_data_init(&s3d_data, + is_float, + ibuf_left->x, + ibuf_left->y, + 4, + (int *)ibuf_left->rect, + (int *)ibuf_right->rect, + (int *)ibuf_stereo->rect, + ibuf_left->rect_float, + ibuf_right->rect_float, + ibuf_stereo->rect_float); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_ImBuf(ibuf_stereo, &im_format->stereo3d_format, ibuf_left->x, ibuf_left->y); @@ -1286,17 +1286,17 @@ void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d, &height); imb_stereo3d_unsqueeze_ImBuf(ibuf_stereo3d, s3d, width, height); - imb_stereo3d_data_initialize(&s3d_data, - is_float, - ibuf_left->x, - ibuf_left->y, - 4, - (int *)ibuf_left->rect, - (int *)ibuf_right->rect, - (int *)ibuf_stereo3d->rect, - ibuf_left->rect_float, - ibuf_right->rect_float, - ibuf_stereo3d->rect_float); + imb_stereo3d_data_init(&s3d_data, + is_float, + ibuf_left->x, + ibuf_left->y, + 4, + (int *)ibuf_left->rect, + (int *)ibuf_right->rect, + (int *)ibuf_stereo3d->rect, + ibuf_left->rect_float, + ibuf_right->rect_float, + ibuf_stereo3d->rect_float); imb_stereo3d_read_doit(&s3d_data, s3d); @@ -1310,17 +1310,17 @@ void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d, addzbufImBuf(ibuf_right); } - imb_stereo3d_data_initialize(&s3d_data, - is_float, - ibuf_left->x, - ibuf_left->y, - 1, - (int *)ibuf_left->zbuf, - (int *)ibuf_right->zbuf, - (int *)ibuf_stereo3d->zbuf, - ibuf_left->zbuf_float, - ibuf_right->zbuf_float, - ibuf_stereo3d->zbuf_float); + imb_stereo3d_data_init(&s3d_data, + is_float, + ibuf_left->x, + ibuf_left->y, + 1, + (int *)ibuf_left->zbuf, + (int *)ibuf_right->zbuf, + (int *)ibuf_stereo3d->zbuf, + ibuf_left->zbuf_float, + ibuf_right->zbuf_float, + ibuf_stereo3d->zbuf_float); imb_stereo3d_read_doit(&s3d_data, s3d); } diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c new file mode 100644 index 00000000000..52628f5aaad --- /dev/null +++ b/source/blender/imbuf/intern/util_gpu.c @@ -0,0 +1,261 @@ +/* + * 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) 2001-2002 by NaN Holding BV. + * All rights reserved. + * util.c + */ + +/** \file + * \ingroup imbuf + */ + +#include "imbuf.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "MEM_guardedalloc.h" + +#include "BKE_global.h" + +#include "GPU_extensions.h" +#include "GPU_texture.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +/* gpu ibuf utils */ + +static void imb_gpu_get_format(const ImBuf *ibuf, + bool high_bitdepth, + eGPUDataFormat *r_data_format, + eGPUTextureFormat *r_texture_format) +{ + const bool float_rect = (ibuf->rect_float != NULL); + const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); + high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth); + + *r_data_format = (float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + + if (float_rect) { + *r_texture_format = high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F; + } + else { + *r_texture_format = use_srgb ? GPU_SRGB8_A8 : GPU_RGBA8; + } +} + +/* Return false if no suitable format was found. */ +#ifdef WITH_DDS +static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format) +{ + /* For DDS we only support data, scene linear and sRGB. Converting to + * different colorspace would break the compression. */ + const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); + + if (ibuf->dds_data.fourcc == FOURCC_DXT1) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1; + } + else if (ibuf->dds_data.fourcc == FOURCC_DXT3) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3; + } + else if (ibuf->dds_data.fourcc == FOURCC_DXT5) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5; + } + else { + return false; + } + return true; +} +#endif + +/** + * Apply colormanagement and scale buffer if needed. + * *r_freedata is set to true if the returned buffer need to be manually freed. + **/ +static void *imb_gpu_get_data(const ImBuf *ibuf, + const bool do_rescale, + const int rescale_size[2], + const bool compress_as_srgb, + const bool store_premultiplied, + bool *r_freedata) +{ + const bool is_float_rect = (ibuf->rect_float != NULL); + void *data_rect = (is_float_rect) ? (void *)ibuf->rect_float : (void *)ibuf->rect; + + if (is_float_rect) { + /* Float image is already in scene linear colorspace or non-color data by + * convention, no colorspace conversion needed. But we do require 4 channels + * currently. */ + if (ibuf->channels != 4 || !store_premultiplied) { + data_rect = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); + *r_freedata = true; + + if (data_rect == NULL) { + return NULL; + } + + IMB_colormanagement_imbuf_to_float_texture( + (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); + } + } + else { + /* Byte image is in original colorspace from the file. If the file is sRGB + * scene linear, or non-color data no conversion is needed. Otherwise we + * compress as scene linear + sRGB transfer function to avoid precision loss + * in common cases. + * + * We must also convert to premultiplied for correct texture interpolation + * and consistency with float images. */ + if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + data_rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); + *r_freedata = true; + + if (data_rect == NULL) { + return NULL; + } + + /* Texture storage of images is defined by the alpha mode of the image. The + * downside of this is that there can be artifacts near alpha edges. However, + * this allows us to use sRGB texture formats and preserves color values in + * zero alpha areas, and appears generally closer to what game engines that we + * want to be compatible with do. */ + IMB_colormanagement_imbuf_to_byte_texture( + (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); + } + } + + if (do_rescale) { + uint *rect = (is_float_rect) ? NULL : (uint *)data_rect; + float *rect_float = (is_float_rect) ? (float *)data_rect : NULL; + + ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4); + IMB_scaleImBuf(scale_ibuf, UNPACK2(rescale_size)); + + data_rect = (is_float_rect) ? (void *)scale_ibuf->rect_float : (void *)scale_ibuf->rect; + *r_freedata = true; + /* Steal the rescaled buffer to avoid double free. */ + scale_ibuf->rect_float = NULL; + scale_ibuf->rect = NULL; + IMB_freeImBuf(scale_ibuf); + } + return data_rect; +} + +/* The ibuf is only here to detect the storage type. The produced texture will have undefined + * content. It will need to be populated by using IMB_update_gpu_texture_sub(). */ +GPUTexture *IMB_touch_gpu_texture(ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth) +{ + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); + + GPUTexture *tex = GPU_texture_create_nD( + w, h, layers, 2, NULL, tex_format, data_format, 0, false, NULL); + + GPU_texture_anisotropic_filter(tex, true); + return tex; +} + +/* Will update a GPUTexture using the content of the ImBuf. Only one layer will be updated. + * Will resize the ibuf if needed. + * z is the layer to update. Unused if the texture is 2D. */ +void IMB_update_gpu_texture_sub(GPUTexture *tex, + ImBuf *ibuf, + int x, + int y, + int z, + int w, + int h, + bool use_high_bitdepth, + bool use_premult) +{ + const bool do_rescale = (ibuf->x != w || ibuf->y != h); + int size[2] = {w, h}; + + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); + + const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); + bool freebuf = false; + + void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf); + + /* Update Texture. */ + GPU_texture_update_sub(tex, data_format, data, x, y, z, w, h, 1); + + if (freebuf) { + MEM_freeN(data); + } +} + +GPUTexture *IMB_create_gpu_texture(ImBuf *ibuf, bool use_high_bitdepth, bool use_premult) +{ + GPUTexture *tex = NULL; + int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)}; + bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]); + +#ifdef WITH_DDS + if (ibuf->ftype == IMB_FTYPE_DDS) { + eGPUTextureFormat compressed_format; + if (!IMB_gpu_get_compressed_format(ibuf, &compressed_format)) { + fprintf(stderr, "Unable to find a suitable DXT compression,"); + } + else if (do_rescale) { + fprintf(stderr, "Unable to load DXT image resolution,"); + } + else if (!is_power_of_2_i(ibuf->x) || !is_power_of_2_i(ibuf->y)) { + fprintf(stderr, "Unable to load non-power-of-two DXT image resolution,"); + } + else { + tex = GPU_texture_create_compressed( + ibuf->x, ibuf->y, ibuf->dds_data.nummipmaps, compressed_format, ibuf->dds_data.data); + + if (tex != NULL) { + return tex; + } + else { + fprintf(stderr, "ST3C support not found,"); + } + } + /* Fallback to uncompressed texture. */ + fprintf(stderr, " falling back to uncompressed.\n"); + } +#endif + + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); + + const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); + bool freebuf = false; + + void *data = imb_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf); + + /* Create Texture. */ + tex = GPU_texture_create_nD(UNPACK2(size), 0, 2, data, tex_format, data_format, 0, false, NULL); + + GPU_texture_anisotropic_filter(tex, true); + + if (freebuf) { + MEM_freeN(data); + } + + return tex; +} diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index ddf75aa3258..67f8aeb0a67 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -128,6 +128,16 @@ struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *han struct Object *object, const char *object_path); +bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name); + +/* r_vertex_velocities should point to a preallocated array of num_vertices floats */ +int ABC_read_velocity_cache(struct CacheReader *reader, + const char *velocity_name, + float time, + float fps, + int num_vertices, + float *r_vertex_velocities); + #ifdef __cplusplus } #endif diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.cc b/source/blender/io/alembic/intern/abc_axis_conversion.cc index cebab1f2e41..396c8fdb28b 100644 --- a/source/blender/io/alembic/intern/abc_axis_conversion.cc +++ b/source/blender/io/alembic/intern/abc_axis_conversion.cc @@ -170,4 +170,4 @@ void create_transform_matrix(Object *obj, } // namespace alembic } // namespace io -} // namespace blender
\ No newline at end of file +} // namespace blender diff --git a/source/blender/io/alembic/intern/abc_axis_conversion.h b/source/blender/io/alembic/intern/abc_axis_conversion.h index 9a19e9116be..645d9fc783b 100644 --- a/source/blender/io/alembic/intern/abc_axis_conversion.h +++ b/source/blender/io/alembic/intern/abc_axis_conversion.h @@ -100,4 +100,4 @@ void create_transform_matrix(Object *obj, } // namespace alembic } // namespace io -} // namespace blender
\ No newline at end of file +} // namespace blender diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index 7cde2d4fe73..eba7f64db02 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -22,6 +22,7 @@ #include <Alembic/AbcMaterial/IMaterial.h> +#include "abc_axis_conversion.h" #include "abc_reader_archive.h" #include "abc_reader_camera.h" #include "abc_reader_curves.h" @@ -47,18 +48,13 @@ #include "BKE_lib_id.h" #include "BKE_object.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" #include "ED_undo.h" -/* SpaceType struct has a member called 'new' which obviously conflicts with C++ - * so temporarily redefining the new keyword to make it compile. */ -#define new extern_new -#include "BKE_screen.h" -#undef new - #include "BLI_compiler_compat.h" #include "BLI_fileops.h" #include "BLI_ghash.h" @@ -70,7 +66,10 @@ #include "WM_api.h" #include "WM_types.h" +using Alembic::Abc::IV3fArrayProperty; using Alembic::Abc::ObjectHeader; +using Alembic::Abc::PropertyHeader; +using Alembic::Abc::V3fArraySamplePtr; using Alembic::AbcGeom::ICamera; using Alembic::AbcGeom::ICurves; using Alembic::AbcGeom::IFaceSet; @@ -79,9 +78,11 @@ using Alembic::AbcGeom::INuPatch; using Alembic::AbcGeom::IObject; using Alembic::AbcGeom::IPoints; using Alembic::AbcGeom::IPolyMesh; +using Alembic::AbcGeom::IPolyMeshSchema; using Alembic::AbcGeom::ISampleSelector; using Alembic::AbcGeom::ISubD; using Alembic::AbcGeom::IXform; +using Alembic::AbcGeom::kWrapExisting; using Alembic::AbcGeom::MetaData; using Alembic::AbcMaterial::IMaterial; @@ -859,3 +860,136 @@ CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, return reinterpret_cast<CacheReader *>(abc_reader); } + +/* ************************************************************************** */ + +static const PropertyHeader *get_property_header(const IPolyMeshSchema &schema, const char *name) +{ + const PropertyHeader *prop_header = schema.getPropertyHeader(name); + + if (prop_header) { + return prop_header; + } + + ICompoundProperty prop = schema.getArbGeomParams(); + + if (!has_property(prop, name)) { + return nullptr; + } + + return prop.getPropertyHeader(name); +} + +bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name) +{ + AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); + + if (!abc_reader) { + return false; + } + + IObject iobject = abc_reader->iobject(); + + if (!iobject.valid()) { + return false; + } + + const ObjectHeader &header = iobject.getHeader(); + + if (!IPolyMesh::matches(header)) { + return false; + } + + IPolyMesh mesh(iobject, kWrapExisting); + IPolyMeshSchema schema = mesh.getSchema(); + + const PropertyHeader *prop_header = get_property_header(schema, name); + + if (!prop_header) { + return false; + } + + return IV3fArrayProperty::matches(prop_header->getMetaData()); +} + +static V3fArraySamplePtr get_velocity_prop(const IPolyMeshSchema &schema, + const ISampleSelector &iss, + const std::string &name) +{ + const PropertyHeader *prop_header = schema.getPropertyHeader(name); + + if (prop_header) { + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0); + return velocity_prop.getValue(iss); + } + + ICompoundProperty prop = schema.getArbGeomParams(); + + if (!has_property(prop, name)) { + return V3fArraySamplePtr(); + } + + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0); + + if (velocity_prop) { + return velocity_prop.getValue(iss); + } + + return V3fArraySamplePtr(); +} + +int ABC_read_velocity_cache(CacheReader *reader, + const char *velocity_name, + const float time, + float velocity_scale, + int num_vertices, + float *r_vertex_velocities) +{ + AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); + + if (!abc_reader) { + return -1; + } + + IObject iobject = abc_reader->iobject(); + + if (!iobject.valid()) { + return -1; + } + + const ObjectHeader &header = iobject.getHeader(); + + if (!IPolyMesh::matches(header)) { + return -1; + } + + IPolyMesh mesh(iobject, kWrapExisting); + IPolyMeshSchema schema = mesh.getSchema(); + ISampleSelector sample_sel(time); + const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel); + + V3fArraySamplePtr velocities = get_velocity_prop(schema, sample_sel, velocity_name); + + if (!velocities) { + return -1; + } + + float vel[3]; + + int num_velocity_vectors = static_cast<int>(velocities->size()); + + if (num_velocity_vectors != num_vertices) { + return -1; + } + + for (size_t i = 0; i < velocities->size(); ++i) { + const Imath::V3f &vel_in = (*velocities)[i]; + copy_zup_from_yup(vel, vel_in.getValue()); + + mul_v3_fl(vel, velocity_scale); + + copy_v3_v3(r_vertex_velocities + i * 3, vel); + } + + return num_vertices; +} diff --git a/source/blender/io/common/intern/dupli_parent_finder.hh b/source/blender/io/common/intern/dupli_parent_finder.hh index e7e628665ee..de7db1785dd 100644 --- a/source/blender/io/common/intern/dupli_parent_finder.hh +++ b/source/blender/io/common/intern/dupli_parent_finder.hh @@ -59,4 +59,4 @@ class DupliParentFinder final { } // namespace blender::io -#endif
\ No newline at end of file +#endif diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 30babcf07dd..000b61fb39b 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -118,7 +118,8 @@ typedef struct BrushGpencilSettings { int sculpt_mode_flag; /** Preset type (used to reset brushes - internal). */ short preset_type; - char _pad3[2]; + /** Brush preselected mode (Active/Material/Vertexcolor). */ + short brush_draw_mode; /** Randomness for Hue. */ float random_hue; @@ -258,6 +259,13 @@ typedef enum eGP_BrushEraserMode { GP_BRUSH_ERASER_STROKE = 2, } eGP_BrushEraserMode; +/* BrushGpencilSettings->brush_draw_mode */ +typedef enum eGP_BrushMode { + GP_BRUSH_MODE_ACTIVE = 0, + GP_BRUSH_MODE_MATERIAL = 1, + GP_BRUSH_MODE_VERTEXCOLOR = 2, +} eGP_BrushMode; + /* BrushGpencilSettings default brush icons */ typedef enum eGP_BrushIcons { GP_BRUSH_ICON_PENCIL = 1, @@ -500,7 +508,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; @@ -572,6 +580,8 @@ typedef struct Brush { float cloth_sim_limit; float cloth_sim_falloff; + float cloth_constraint_softbody_strength; + /* smooth */ int smooth_deform_type; float surface_smooth_shape_preservation; @@ -716,6 +726,7 @@ typedef enum eBrushFlags2 { BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW = (1 << 1), BRUSH_POSE_IK_ANCHORED = (1 << 2), BRUSH_USE_CONNECTED_ONLY = (1 << 3), + BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY = (1 << 4), } eBrushFlags2; typedef enum { diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h index 1175c7f0dc0..376041e0cce 100644 --- a/source/blender/makesdna/DNA_cachefile_types.h +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -53,6 +53,13 @@ typedef struct AlembicObjectPath { char path[4096]; } AlembicObjectPath; +/* CacheFile::velocity_unit + * Determines what temporal unit is used to interpret velocity vectors for motion blur effects. */ +enum { + CACHEFILE_VELOCITY_UNIT_FRAME, + CACHEFILE_VELOCITY_UNIT_SECOND, +}; + typedef struct CacheFile { ID id; struct AnimData *adt; @@ -78,7 +85,11 @@ typedef struct CacheFile { short flag; short draw_flag; /* UNUSED */ - char _pad[4]; + char _pad[3]; + + char velocity_unit; + /* Name of the velocity property in the Alembic file. */ + char velocity_name[64]; /* Runtime */ struct AbcArchiveHandle *handle; diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index b2902407a15..9f724973b6c 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -274,9 +274,12 @@ typedef struct Curve { int selstart, selend; /* text data */ - /** Number of characters (strinfo). */ - int len_wchar; - /** Number of bytes (str - utf8). */ + /** + * Number of characters (unicode code-points) + * This is the length of #Curve.strinfo and the result of `BLI_strlen_utf8(cu->str)`. + */ + int len_char32; + /** Number of bytes: `strlen(Curve.str)`. */ int len; char *str; struct EditFont *editfont; diff --git a/source/blender/makesdna/DNA_curveprofile_types.h b/source/blender/makesdna/DNA_curveprofile_types.h index ca00f783905..7af8c3cf3ca 100644 --- a/source/blender/makesdna/DNA_curveprofile_types.h +++ b/source/blender/makesdna/DNA_curveprofile_types.h @@ -31,7 +31,7 @@ /** Number of table points per control point. */ #define PROF_RESOL 16 /** Dynamic size of widget's high resolution table. Input should be profile->totpoint. */ -#define PROF_N_TABLE(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1) +#define PROF_TABLE_LEN(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1) /** * Each control point that makes up the profile. diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 0ffb6c8a76a..70512c5094b 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -117,13 +117,13 @@ typedef struct ImageTile { #define IMA_NEED_FRAME_RECALC (1 << 3) #define IMA_SHOW_STEREO (1 << 4) -enum { - TEXTARGET_TEXTURE_2D = 0, - TEXTARGET_TEXTURE_CUBE_MAP = 1, - TEXTARGET_TEXTURE_2D_ARRAY = 2, - TEXTARGET_TEXTURE_TILE_MAPPING = 3, - TEXTARGET_COUNT = 4, -}; +/* Used to get the correct gpu texture from an Image datablock. */ +typedef enum eGPUTextureTarget { + TEXTARGET_2D = 0, + TEXTARGET_2D_ARRAY, + TEXTARGET_TILE_MAPPING, + TEXTARGET_COUNT, +} eGPUTextureTarget; typedef struct Image { ID id; @@ -133,8 +133,8 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; - /** Not written in file 4 = TEXTARGET_COUNT, 2 = stereo eyes. */ - struct GPUTexture *gputexture[4][2]; + /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */ + struct GPUTexture *gputexture[3][2]; /* sources from: */ ListBase anims; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 8c564bda3d0..2669ca37132 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1277,7 +1277,11 @@ typedef struct OceanModifierData { struct Ocean *ocean; struct OceanCache *oceancache; + /** Render resolution. */ int resolution; + /** Viewport resolution for the non-render case. */ + int viewport_resolution; + int spatial_size; float wind_velocity; @@ -1294,8 +1298,6 @@ typedef struct OceanModifierData { float foam_coverage; float time; - char _pad1[4]; - /* Spectrum being used. */ int spectrum; @@ -2052,6 +2054,10 @@ enum { MOD_NORMALEDIT_MIX_MUL = 3, }; +typedef struct MeshCacheVertexVelocity { + float vel[3]; +} MeshCacheVertexVelocity; + typedef struct MeshSeqCacheModifierData { ModifierData modifier; @@ -2060,11 +2066,31 @@ typedef struct MeshSeqCacheModifierData { char object_path[1024]; char read_flag; - char _pad[7]; + char _pad[3]; + + float velocity_scale; /* Runtime. */ struct CacheReader *reader; char reader_object_path[1024]; + + /* Vertex velocities read from the cache. The velocities are not automatically read during + * modifier execution, and therefore have to manually be read when needed. This is only used + * through the RNA for now. */ + struct MeshCacheVertexVelocity *vertex_velocities; + + /* The number of vertices of the Alembic mesh, set when the modifier is executed. */ + int num_vertices; + + /* Time (in frames or seconds) between two velocity samples. Automatically computed to + * scale the velocity vectors at render time for generating proper motion blur data. */ + float velocity_delta; + + /* Caches the scene time (in seconds) used to lookup data in the Alembic archive when the + * modifier was last executed. Used to access Alembic samples through the RNA. */ + float last_lookup_time; + + int _pad1; } MeshSeqCacheModifierData; /* MeshSeqCacheModifierData.read_flag */ diff --git a/source/blender/makesdna/DNA_movieclip_types.h b/source/blender/makesdna/DNA_movieclip_types.h index d750a7f3148..063e5d8af09 100644 --- a/source/blender/makesdna/DNA_movieclip_types.h +++ b/source/blender/makesdna/DNA_movieclip_types.h @@ -64,8 +64,8 @@ typedef struct MovieClipProxy { typedef struct MovieClip_RuntimeGPUTexture { void *next, *prev; MovieClipUser user; - /** Not written in file 4 = TEXTARGET_COUNT. */ - struct GPUTexture *gputexture[4]; + /** Not written in file 3 = TEXTARGET_COUNT. */ + struct GPUTexture *gputexture[3]; } MovieClip_RuntimeGPUTexture; typedef struct MovieClip_Runtime { diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h index 7ad50dc04de..facf4c6636f 100644 --- a/source/blender/makesdna/DNA_rigidbody_types.h +++ b/source/blender/makesdna/DNA_rigidbody_types.h @@ -214,7 +214,7 @@ typedef enum eRigidBody_Shape { RB_SHAPE_TRIMESH = 6, /* concave mesh approximated using primitives */ - // RB_SHAPE_COMPOUND, + RB_SHAPE_COMPOUND = 7, } eRigidBody_Shape; typedef enum eRigidBody_MeshSource { diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h index 5bb0e50e089..afc59b422c5 100644 --- a/source/blender/makesdna/DNA_simulation_types.h +++ b/source/blender/makesdna/DNA_simulation_types.h @@ -30,16 +30,21 @@ typedef struct Simulation { struct bNodeTree *nodetree; - int flag; + uint32_t flag; + + /** This is the frame in scene time, that the states correspond to. */ float current_frame; + + /** Time since the start of the simulation in simulation time (which might differ from scene + * time). */ float current_simulation_time; char _pad[4]; /** List containing SimulationState objects. */ struct ListBase states; - /** List containing PersistentDataHandleItem objects. */ - struct ListBase persistent_data_handles; + /** List containing SimulationDependency objects. */ + struct ListBase dependencies; } Simulation; typedef struct SimulationState { @@ -54,8 +59,8 @@ typedef struct ParticleSimulationState { SimulationState head; /** Contains the state of the particles at time Simulation->current_frame. */ - int tot_particles; - int next_particle_id; + int32_t tot_particles; + int32_t next_particle_id; struct CustomData attributes; } ParticleSimulationState; @@ -66,20 +71,27 @@ typedef struct ParticleMeshEmitterSimulationState { char _pad[4]; } ParticleMeshEmitterSimulationState; -/** Stores a mapping between an integer handle and a corresponding ID data block. */ -typedef struct PersistentDataHandleItem { - struct PersistentDataHandleItem *next; - struct PersistentDataHandleItem *prev; +/** Stores a reference to data that the simulation depends on. This is partially derived from the + * simulation node tree. */ +typedef struct SimulationDependency { + struct SimulationDependency *next; + struct SimulationDependency *prev; struct ID *id; - int handle; - char _pad[4]; -} PersistentDataHandleItem; + int32_t handle; + uint32_t flag; +} SimulationDependency; /* Simulation.flag */ enum { SIM_DS_EXPAND = (1 << 0), }; +/* SimulationDependency.flag */ +enum { + SIM_DEPENDS_ON_TRANSFORM = (1 << 0), + SIM_DEPENDS_ON_GEOMETRY = (1 << 1), +}; + #define SIM_TYPE_NAME_PARTICLE_SIMULATION "Particle Simulation" #define SIM_TYPE_NAME_PARTICLE_MESH_EMITTER "Particle Mesh Emitter" diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e6f6ce1e208..d50c0055499 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -733,7 +733,7 @@ typedef struct UserDef { char _pad1[2]; int undomemory; float gpu_viewport_quality DNA_DEPRECATED; - short gp_manhattendist, gp_euclideandist, gp_eraser; + short gp_manhattandist, gp_euclideandist, gp_eraser; /** #eGP_UserdefSettings. */ short gp_settings; char _pad13[4]; diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h index 8734a73c07a..30eafb34ba6 100644 --- a/source/blender/makesdna/DNA_view2d_types.h +++ b/source/blender/makesdna/DNA_view2d_types.h @@ -137,7 +137,7 @@ enum { /* apply pixel offsets on y-axis when setting view matrices */ V2D_PIXELOFS_Y = (1 << 3), /* view settings need to be set still... */ - V2D_IS_INITIALISED = (1 << 10), + V2D_IS_INIT = (1 << 10), }; /* scroller flags for View2D (v2d->scroll) */ diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index a07b8f81d96..668818b17bb 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -198,8 +198,8 @@ typedef struct wmWindowManager { /* wmWindowManager.initialized */ enum { - WM_WINDOW_IS_INITIALIZED = (1 << 0), - WM_KEYCONFIG_IS_INITIALIZED = (1 << 1), + WM_WINDOW_IS_INIT = (1 << 0), + WM_KEYCONFIG_IS_INIT = (1 << 1), }; /* wmWindowManager.outliner_sync_select_dirty */ diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index f2cf72843bd..a73fc747f84 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -67,6 +67,7 @@ DNA_STRUCT_RENAME_ELEM(Bone, scaleOut, scale_out_x) DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_f, hardeness) DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_s, aspect_ratio) DNA_STRUCT_RENAME_ELEM(Camera, YF_dofdist, dof_distance) +DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Camera, clipend, clip_end) DNA_STRUCT_RENAME_ELEM(Camera, clipsta, clip_start) DNA_STRUCT_RENAME_ELEM(Collection, dupli_ofs, instance_offset) @@ -90,6 +91,7 @@ DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) DNA_STRUCT_RENAME_ELEM(Text, name, filepath) DNA_STRUCT_RENAME_ELEM(ThemeSpace, scrubbing_background, time_scrub_background) DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) +DNA_STRUCT_RENAME_ELEM(UserDef, gp_manhattendist, gp_manhattandist) DNA_STRUCT_RENAME_ELEM(VFont, name, filepath) DNA_STRUCT_RENAME_ELEM(View3D, far, clip_end) DNA_STRUCT_RENAME_ELEM(View3D, near, clip_start) diff --git a/source/blender/makesdna/intern/dna_utils.c b/source/blender/makesdna/intern/dna_utils.c index 97f4785374a..3cf5c52a4c6 100644 --- a/source/blender/makesdna/intern/dna_utils.c +++ b/source/blender/makesdna/intern/dna_utils.c @@ -235,7 +235,9 @@ void DNA_alias_maps(enum eDNA_RenameDir version_dir, GHash **r_struct_map, GHash if (version_dir == DNA_RENAME_STATIC_FROM_ALIAS) { const char *renames[][2] = { - {"int8_t", "char"}, /* Note that a char is always unsigned in Blender. */ + /* Disable 'int8_t' until we support 'signed char', since changing negative + * values to a different type isn't supported and will change the value. */ + /* {"int8_t", "char"}, */ {"uint8_t", "uchar"}, {"int16_t", "short"}, {"uint16_t", "ushort"}, diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 9d2fecb123b..026274e5b22 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -1531,15 +1531,23 @@ int main(int argc, char **argv) #endif /* if 0 */ -/* even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit, - * use int or int64_t instead. +/** + * Disable types: + * + * - 'long': even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit, + * use int, int32_t or int64_t instead. + * - 'int8_t': as DNA doesn't yet support 'signed char' types, + * all char types are assumed to be unsigned. + * We should be able to support this, it's just not something which has been added yet. + * * Only valid use would be as a runtime variable if an API expected a long, - * but so far we dont have this happening. */ + * but so far we don't have this happening. + */ #ifdef __GNUC__ # pragma GCC poison long +# pragma GCC poison int8_t #endif -#include "DNA_session_uuid_types.h" #include "DNA_ID.h" #include "DNA_action_types.h" #include "DNA_anim_types.h" @@ -1594,6 +1602,7 @@ int main(int argc, char **argv) #include "DNA_screen_types.h" #include "DNA_sdna_types.h" #include "DNA_sequence_types.h" +#include "DNA_session_uuid_types.h" #include "DNA_shader_fx_types.h" #include "DNA_simulation_types.h" #include "DNA_sound_types.h" diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 045d098bf6a..6169b72a7fa 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -5146,7 +5146,7 @@ int main(int argc, char **argv) { int return_status = 0; - MEM_initialize_memleak_detection(); + MEM_init_memleak_detection(); MEM_set_error_callback(mem_error_cb); CLG_init(); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 84f9ec749cb..efc712544ca 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -243,6 +243,12 @@ static EnumPropertyItem rna_enum_gpencil_fill_draw_modes_items[] = { {GP_FILL_DMODE_CONTROL, "CONTROL", 0, "Edit Lines", "Use edit lines as fill boundary limits"}, {0, NULL, 0, NULL, NULL}}; +static EnumPropertyItem rna_enum_gpencil_brush_modes_items[] = { + {GP_BRUSH_MODE_ACTIVE, "ACTIVE", 0, "Active", "Use current mode"}, + {GP_BRUSH_MODE_MATERIAL, "MATERIAL", 0, "Material", "Use always material mode"}, + {GP_BRUSH_MODE_VERTEXCOLOR, "VERTEXCOLOR", 0, "Vertex Color", "Use always Vertex Color mode"}, + {0, NULL, 0, NULL, NULL}}; + static EnumPropertyItem rna_enum_gpencil_brush_paint_icons_items[] = { {GP_BRUSH_ICON_PENCIL, "PENCIL", ICON_GPBRUSH_PENCIL, "Pencil", ""}, {GP_BRUSH_ICON_PEN, "PEN", ICON_GPBRUSH_PEN, "Pen", ""}, @@ -359,7 +365,7 @@ static bool rna_BrushCapabilities_has_overlay_get(PointerRNA *ptr) static bool rna_BrushCapabilitiesSculpt_has_persistence_get(PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; - return br->sculpt_tool == SCULPT_TOOL_LAYER; + return ELEM(br->sculpt_tool, SCULPT_TOOL_LAYER, SCULPT_TOOL_CLOTH); } static bool rna_BrushCapabilitiesSculpt_has_pinch_factor_get(PointerRNA *ptr) @@ -1640,6 +1646,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Mode to draw boundary limits"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "brush_draw_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "brush_draw_mode"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_modes_items); + RNA_def_property_ui_text(prop, "Mode", "Preselected mode when using this brush"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "trim", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_TRIM_STROKE); RNA_def_property_boolean_default(prop, false); @@ -2556,6 +2568,15 @@ static void rna_def_brush(BlenderRNA *brna) "Area to apply deformation falloff to the effects of the simulation"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "cloth_constraint_softbody_strength", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "cloth_constraint_softbody_strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text( + prop, + "Soft Body Influence", + "How much the simulation preserves the original shape, acting as a soft body"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "hardness", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "hardness"); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -2772,6 +2793,15 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Connected Only", "Affect only topologically connected elements"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_cloth_pin_simulation_boundary", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CLOTH_PIN_SIMULATION_BOUNDARY); + RNA_def_property_ui_text( + prop, + "Pin Simulation Boundary", + "Lock the position of the vertices in the simulation falloff area to avoid artifacts and " + "create a softer transitionwith with unnafected areas"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "invert_to_scrape_fill", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_INVERT_TO_SCRAPE_FILL); RNA_def_property_ui_text(prop, diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index f9275ef1993..c25cea1b4b3 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -174,6 +174,32 @@ static void rna_def_cachefile(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Object Paths", "Paths of the objects inside the Alembic archive"); + /* ----------------- Alembic Velocity Attribute ----------------- */ + + prop = RNA_def_property(srna, "velocity_name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, + "Velocity Attribute", + "Name of the Alembic attribute used for generating motion blur data"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + static const EnumPropertyItem velocity_unit_items[] = { + {CACHEFILE_VELOCITY_UNIT_SECOND, "SECOND", 0, "Second", ""}, + {CACHEFILE_VELOCITY_UNIT_FRAME, "FRAME", 0, "Frame", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "velocity_unit", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "velocity_unit"); + RNA_def_property_enum_items(prop, velocity_unit_items); + RNA_def_property_ui_text( + prop, + "Velocity Unit", + "Define how the velocity vectors are interpreted with regard to time, 'frame' means " + "the delta time is 1 frame, 'second' means the delta time is 1 / FPS"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_define_lib_overridable(false); rna_def_cachefile_object_paths(brna, prop); diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 56ad8e2677b..60b6cc40792 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -702,7 +702,7 @@ static float rna_CurveMapping_evaluateF(struct CurveMapping *cumap, static void rna_CurveMap_initialize(struct CurveMapping *cumap) { - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); } #else diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 771235c85aa..8a3186ea7fe 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -570,7 +570,7 @@ static void rna_Curve_body_set(PointerRNA *ptr, const char *value) Curve *cu = (Curve *)ptr->owner_id; - cu->len_wchar = len_chars; + cu->len_char32 = len_chars; cu->len = len_bytes; cu->pos = len_chars; @@ -1206,7 +1206,7 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "body_format", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "strinfo", "len_wchar"); + RNA_def_property_collection_sdna(prop, NULL, "strinfo", "len_char32"); RNA_def_property_struct_type(prop, "TextCharacterFormat"); RNA_def_property_ui_text(prop, "Character Info", "Stores the style of each character"); diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c index ce91fc79085..ee1c659fcd5 100644 --- a/source/blender/makesrna/intern/rna_curveprofile.c +++ b/source/blender/makesrna/intern/rna_curveprofile.c @@ -146,7 +146,7 @@ static void rna_CurveProfile_evaluate(struct CurveProfile *profile, static void rna_CurveProfile_initialize(struct CurveProfile *profile, int segments_len) { - BKE_curveprofile_initialize(profile, (short)segments_len); + BKE_curveprofile_init(profile, (short)segments_len); } static void rna_CurveProfile_update(struct CurveProfile *profile) diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 47e0333edb1..feb26f8c775 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -2052,6 +2052,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, "Start", "Frame on which the simulation starts. This is the first frame that will be baked"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); prop = RNA_def_property(srna, "cache_frame_end", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "cache_frame_end"); @@ -2061,6 +2062,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, "End", "Frame on which the simulation stops. This is the last frame that will be baked"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); prop = RNA_def_property(srna, "cache_frame_offset", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "cache_frame_offset"); @@ -2070,6 +2072,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "Offset", "Frame offset that is used when loading the simulation from the cache. It is not considered " "when baking the simulation, only when loading it"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); prop = RNA_def_property(srna, "cache_frame_pause_data", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "cache_frame_pause_data"); @@ -2093,6 +2096,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, NULL, "rna_Fluid_cachetype_mesh_set", "rna_Fluid_cachetype_mesh_itemf"); RNA_def_property_ui_text( prop, "File Format", "Select the file format to be used for caching surface data"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_meshcache_reset"); prop = RNA_def_property(srna, "cache_data_format", PROP_ENUM, PROP_NONE); @@ -2102,6 +2106,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, NULL, "rna_Fluid_cachetype_data_set", "rna_Fluid_cachetype_volume_itemf"); RNA_def_property_ui_text( prop, "File Format", "Select the file format to be used for caching volumetric data"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); prop = RNA_def_property(srna, "cache_particle_format", PROP_ENUM, PROP_NONE); @@ -2111,6 +2116,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, NULL, "rna_Fluid_cachetype_particle_set", "rna_Fluid_cachetype_particle_itemf"); RNA_def_property_ui_text( prop, "File Format", "Select the file format to be used for caching particle data"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_particlescache_reset"); prop = RNA_def_property(srna, "cache_noise_format", PROP_ENUM, PROP_NONE); @@ -2120,6 +2126,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) prop, NULL, "rna_Fluid_cachetype_noise_set", "rna_Fluid_cachetype_volume_itemf"); RNA_def_property_ui_text( prop, "File Format", "Select the file format to be used for caching noise data"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_noisecache_reset"); prop = RNA_def_property(srna, "cache_type", PROP_ENUM, PROP_NONE); @@ -2127,6 +2134,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, cache_types); RNA_def_property_enum_funcs(prop, NULL, "rna_Fluid_cachetype_set", NULL); RNA_def_property_ui_text(prop, "Type", "Change the cache type of the simulation"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_domain_data_reset"); prop = RNA_def_property(srna, "cache_resumable", PROP_BOOLEAN, PROP_NONE); @@ -2137,6 +2145,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "Additional data will be saved so that the bake jobs can be resumed after pausing. Because " "more data will be written to disk it is recommended to avoid enabling this option when " "baking at high resolutions"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); prop = RNA_def_property(srna, "cache_directory", PROP_STRING, PROP_DIRPATH); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 887bded8540..1c43815d3a2 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -67,7 +67,6 @@ static const EnumPropertyItem image_source_items[] = { # include "BKE_global.h" -# include "GPU_draw.h" # include "GPU_texture.h" # include "IMB_imbuf.h" @@ -200,7 +199,7 @@ static void rna_Image_gpu_texture_update(Main *UNUSED(bmain), Image *ima = (Image *)ptr->owner_id; if (!G.background) { - GPU_free_image(ima); + BKE_image_free_gputextures(ima); } WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id); @@ -398,7 +397,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0]; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } @@ -516,7 +515,7 @@ static void rna_Image_pixels_set(PointerRNA *ptr, const float *values) ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID | IB_MIPMAP_INVALID; BKE_image_mark_dirty(ima, ibuf); if (!G.background) { - GPU_free_image(ima); + BKE_image_free_gputextures(ima); } WM_main_add_notifier(NC_IMAGE | ND_DISPLAY, &ima->id); } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 41c0e724234..6f876923e52 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -50,8 +50,6 @@ # include "DNA_image_types.h" # include "DNA_scene_types.h" -# include "GPU_glew.h" - # include "MEM_guardedalloc.h" static void rna_ImagePackedFile_save(ImagePackedFile *imapf, Main *bmain, ReportList *reports) @@ -222,23 +220,24 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame) BKE_imageuser_default(&iuser); iuser.framenr = frame; - GPUTexture *tex = GPU_texture_from_blender(image, &iuser, NULL, GL_TEXTURE_2D); + GPUTexture *tex = BKE_image_get_gpu_texture(image, &iuser, NULL); if (tex == NULL) { BKE_reportf(reports, RPT_ERROR, "Failed to load image texture '%s'", image->id.name + 2); - return (int)GL_INVALID_OPERATION; + /* TODO(fclem) this error code makes no sense for vulkan. */ + return 0x0502; /* GL_INVALID_OPERATION */ } - return GL_NO_ERROR; + return 0; /* GL_NO_ERROR */ } static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame) { - int error = GL_NO_ERROR; + int error = 0; /* GL_NO_ERROR */ BKE_image_tag_time(image); - if (image->gputexture[TEXTARGET_TEXTURE_2D] == NULL) { + if (image->gputexture[TEXTARGET_2D][0] == NULL) { error = rna_Image_gl_load(image, reports, frame); } @@ -247,7 +246,7 @@ static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame) static void rna_Image_gl_free(Image *image) { - GPU_free_image(image); + BKE_image_free_gputextures(image); /* remove the nocollect flag, image is available for garbage collection again */ image->flag &= ~IMA_NOCOLLECT; diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 86f05c350f3..a891194550f 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1163,7 +1163,7 @@ static void rna_BevelModifier_update_segments(Main *bmain, Scene *scene, Pointer BevelModifierData *bmd = (BevelModifierData *)ptr->data; if (RNA_enum_get(ptr, "profile_type") == MOD_BEVEL_PROFILE_CUSTOM) { short segments = (short)RNA_int_get(ptr, "segments"); - BKE_curveprofile_initialize(bmd->custom_profile, segments); + BKE_curveprofile_init(bmd->custom_profile, segments); } rna_Modifier_update(bmain, scene, ptr); } @@ -1699,6 +1699,51 @@ static bool rna_Modifier_show_expanded_get(PointerRNA *ptr) return md->ui_expand_flag & (1 << 0); } +static int rna_MeshSequenceCacheModifier_has_velocity_get(PointerRNA *ptr) +{ +# ifdef WITH_ALEMBIC + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; + return ABC_has_vec3_array_property_named(mcmd->reader, mcmd->cache_file->velocity_name); +# else + return false; + UNUSED_VARS(ptr); +# endif +} + +static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr) +{ +# ifdef WITH_ALEMBIC + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; + + if (mcmd->num_vertices == 0) { + return 0; + } + + if (mcmd->vertex_velocities) { + MEM_freeN(mcmd->vertex_velocities); + } + + mcmd->vertex_velocities = MEM_mallocN(sizeof(MeshCacheVertexVelocity) * mcmd->num_vertices, + "Mesh Cache Velocities"); + + int num_read = ABC_read_velocity_cache(mcmd->reader, + mcmd->cache_file->velocity_name, + mcmd->last_lookup_time, + mcmd->velocity_scale * mcmd->velocity_delta, + mcmd->num_vertices, + (float *)mcmd->vertex_velocities); + + if (num_read == -1 || num_read != mcmd->num_vertices) { + return false; + } + + return true; +# else + return false; + UNUSED_VARS(ptr); +# endif +} + #else /* NOTE: *MUST* return subdivision_type property. */ @@ -5667,7 +5712,17 @@ static void rna_def_modifier_ocean(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 1, 1024); RNA_def_property_ui_range(prop, 1, 32, 1, -1); - RNA_def_property_ui_text(prop, "Resolution", "Resolution of the generated surface"); + RNA_def_property_ui_text( + prop, "Render Resolution", "Resolution of the generated surface for rendering and baking"); + RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update"); + + prop = RNA_def_property(srna, "viewport_resolution", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "viewport_resolution"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_range(prop, 1, 1024); + RNA_def_property_ui_range(prop, 1, 32, 1, -1); + RNA_def_property_ui_text( + prop, "Viewport Resolution", "Viewport resolution of the generated surface"); RNA_def_property_update(prop, 0, "rna_OceanModifier_init_update"); prop = RNA_def_property(srna, "spatial_size", PROP_INT, PROP_NONE); @@ -6066,6 +6121,22 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) RNA_define_lib_overridable(false); } +static void rna_def_mesh_cache_velocities(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "MeshCacheVertexVelocity", NULL); + RNA_def_struct_ui_text(srna, "Mesh Cache Velocity", "Velocity attribute of an Alembic mesh"); + RNA_def_struct_ui_icon(srna, ICON_VERTEXSEL); + + prop = RNA_def_property(srna, "velocity", PROP_FLOAT, PROP_VELOCITY); + RNA_def_property_array(prop, 3); + RNA_def_property_float_sdna(prop, NULL, "vel"); + RNA_def_property_ui_text(prop, "Velocity", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); +} + static void rna_def_modifier_meshseqcache(BlenderRNA *brna) { StructRNA *srna; @@ -6108,6 +6179,35 @@ static void rna_def_modifier_meshseqcache(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "velocity_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "velocity_scale"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text( + prop, + "Velocity Scale", + "Multiplier used to control the magnitude of the velocity vectors for time effects"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + /* -------------------------- Velocity Vectors -------------------------- */ + + prop = RNA_def_property(srna, "vertex_velocities", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "vertex_velocities", "num_vertices"); + RNA_def_property_struct_type(prop, "MeshCacheVertexVelocity"); + RNA_def_property_ui_text( + prop, "Fluid Mesh Vertices", "Vertices of the fluid mesh generated by simulation"); + + rna_def_mesh_cache_velocities(brna); + + prop = RNA_def_property(srna, "has_velocity", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Has Velocity Cache", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshSequenceCacheModifier_has_velocity_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "read_velocity", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Read Velocity Cache", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshSequenceCacheModifier_read_velocity_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_define_lib_overridable(false); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 3eb2c15c053..95ea9eced1a 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -38,6 +38,7 @@ #include "BKE_animsys.h" #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_simulation.h" #include "BKE_texture.h" #include "RNA_access.h" @@ -2848,6 +2849,14 @@ static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA * } } +static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C, PointerRNA *ptr) +{ + rna_NodeSocketStandard_value_update(C, ptr); + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + Main *bmain = CTX_data_main(C); + ntreeUpdateTree(bmain, ntree); +} + /* ******** Node Types ******** */ static void rna_NodeInternalSocketTemplate_name_get(PointerRNA *ptr, char *value) @@ -8868,7 +8877,8 @@ static void rna_def_node_socket_object(BlenderRNA *brna, RNA_def_property_pointer_sdna(prop, NULL, "value"); RNA_def_property_struct_type(prop, "Object"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); /* socket interface */ @@ -8902,7 +8912,8 @@ static void rna_def_node_socket_image(BlenderRNA *brna, RNA_def_property_pointer_sdna(prop, NULL, "value"); RNA_def_property_struct_type(prop, "Image"); RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); /* socket interface */ diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 84b83bee089..08ca3f16b6d 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -193,6 +193,12 @@ static EnumPropertyItem instance_items_nogroup[] = { INSTANCE_ITEMS_SHARED, {0, NULL, 0, NULL, NULL}, }; + +static EnumPropertyItem instance_items_pointcloud[] = { + {0, "NONE", 0, "None", ""}, + {OB_DUPLIVERTS, "POINTS", 0, "Points", "Instantiate child objects on all points"}, + {0, NULL, 0, NULL, NULL}, +}; #endif #undef INSTANCE_ITEMS_SHARED #undef INSTANCE_ITEM_COLLECTION @@ -707,6 +713,9 @@ static const EnumPropertyItem *rna_Object_instance_type_itemf(bContext *UNUSED(C if (ob->type == OB_EMPTY) { item = instance_items; } + else if (ob->type == OB_POINTCLOUD) { + item = instance_items_pointcloud; + } else { item = instance_items_nogroup; } diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c index 325c4e3caa9..450d148d8a3 100644 --- a/source/blender/makesrna/intern/rna_rigidbody.c +++ b/source/blender/makesrna/intern/rna_rigidbody.c @@ -75,6 +75,11 @@ const EnumPropertyItem rna_enum_rigidbody_object_shape_items[] = { "Mesh", "Mesh consisting of triangles only, allowing for more detailed interactions than convex " "hulls"}, + {RB_SHAPE_COMPOUND, + "COMPOUND", + ICON_MESH_DATA, + "Compound Parent", + "Combines all of its direct rigid body children into one rigid object."}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index 24c4818694f..fb2a60db0fd 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -231,7 +231,7 @@ static int rna_Area_ui_type_get(PointerRNA *ptr) * the area type is changing. * So manually do the lookup in those cases, but do not actually change area->type * since that prevents a proper exit when the area type is changing. - * Logic copied from `ED_area_initialize()`.*/ + * Logic copied from `ED_area_init()`.*/ SpaceType *type = area->type; if (type == NULL || area_changing) { type = BKE_spacetype_from_id(area_type); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 4157747455d..59cedf8fcb8 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -370,13 +370,14 @@ static void rna_Sequences_remove( Sequence *seq = seq_ptr->data; Scene *scene = (Scene *)id; - if (BLI_remlink_safe(&ed->seqbase, seq) == false) { + if (BLI_findindex(&ed->seqbase, seq) == -1) { BKE_reportf( reports, RPT_ERROR, "Sequence '%s' not in scene '%s'", seq->name + 2, scene->id.name + 2); return; } - BKE_sequence_free(scene, seq, true); + BKE_sequencer_flag_for_removal(scene, &ed->seqbase, seq); + BKE_sequencer_remove_flagged_sequences(scene, &ed->seqbase); RNA_POINTER_INVALIDATE(seq_ptr); DEG_relations_tag_update(bmain); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 494fcec4c31..155f5ab3043 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1776,87 +1776,28 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(bContext *UNUSE { SpaceProperties *sbuts = (SpaceProperties *)(ptr->data); EnumPropertyItem *item = NULL; - int totitem = 0; - - if (sbuts->pathflag & (1 << BCONTEXT_TOOL)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TOOL); - } - - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } - - if (sbuts->pathflag & (1 << BCONTEXT_RENDER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_RENDER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_OUTPUT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OUTPUT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_VIEW_LAYER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_VIEW_LAYER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_SCENE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SCENE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_WORLD)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_WORLD); - } - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } - - if (sbuts->pathflag & (1 << BCONTEXT_OBJECT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OBJECT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MODIFIER); - } - - if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SHADERFX); - } - - if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PARTICLE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PHYSICS); - } - - if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_CONSTRAINT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_DATA)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_DATA); - (item + totitem - 1)->icon = sbuts->dataicon; - } - - if (sbuts->pathflag & (1 << BCONTEXT_BONE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_BONE); - } - - if (sbuts->pathflag & (1 << BCONTEXT_BONE_CONSTRAINT)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_BONE_CONSTRAINT); - } - - if (sbuts->pathflag & (1 << BCONTEXT_MATERIAL)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MATERIAL); - } + /* We use 32 tabs maximum here so a flag for each can fit into a 32 bit integer flag. + * A theoretical maximum would be BCONTEXT_TOT * 2, with every tab displayed and a spacer + * in every other item. But this size is currently limited by the size of integer + * supported by RNA enums. */ + int context_tabs_array[32]; + int totitem = ED_buttons_tabs_list(sbuts, context_tabs_array); + BLI_assert(totitem <= ARRAY_SIZE(context_tabs_array)); + + int totitem_added = 0; + for (int i = 0; i < totitem; i++) { + if (context_tabs_array[i] == -1) { + RNA_enum_item_add_separator(&item, &totitem_added); + continue; + } - if (totitem) { - RNA_enum_item_add_separator(&item, &totitem); - } + RNA_enum_items_add_value(&item, &totitem_added, buttons_context_items, context_tabs_array[i]); - if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) { - RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TEXTURE); + /* Add the object data icon dynamically for the data tab. */ + if (context_tabs_array[i] == BCONTEXT_DATA) { + (item + totitem_added - 1)->icon = sbuts->dataicon; + } } RNA_enum_item_end(&item, &totitem); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 00bbff5cf51..0567b22d23f 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -179,6 +179,7 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "BKE_blender.h" # include "BKE_global.h" # include "BKE_idprop.h" +# include "BKE_image.h" # include "BKE_main.h" # include "BKE_mesh_runtime.h" # include "BKE_paint.h" @@ -187,9 +188,9 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "DEG_depsgraph.h" -# include "GPU_draw.h" # include "GPU_extensions.h" # include "GPU_select.h" +# include "GPU_texture.h" # include "BLF_api.h" @@ -363,13 +364,14 @@ static void rna_userdef_load_ui_update(Main *UNUSED(bmain), Scene *UNUSED(scene) static void rna_userdef_anisotropic_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - GPU_set_anisotropic(U.anisotropic_filter); + GPU_samplers_free(); + GPU_samplers_init(); rna_userdef_update(bmain, scene, ptr); } static void rna_userdef_gl_texture_limit_update(Main *bmain, Scene *scene, PointerRNA *ptr) { - GPU_free_images(bmain); + BKE_image_free_all_gputextures(bmain); rna_userdef_update(bmain, scene, ptr); } @@ -4593,7 +4595,7 @@ static void rna_def_userdef_view(BlenderRNA *brna) "no matter opening direction"); static const EnumPropertyItem header_align_items[] = { - {0, "NONE", 0, "Default", "Keep existing header alignment"}, + {0, "NONE", 0, "Keep Existing", "Keep existing header alignment"}, {USER_HEADER_FROM_PREF, "TOP", 0, "Top", "Top aligned on load"}, {USER_HEADER_FROM_PREF | USER_HEADER_BOTTOM, "BOTTOM", @@ -4988,7 +4990,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna) /* grease pencil */ prop = RNA_def_property(srna, "grease_pencil_manhattan_distance", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "gp_manhattendist"); + RNA_def_property_int_sdna(prop, NULL, "gp_manhattandist"); RNA_def_property_range(prop, 0, 100); RNA_def_property_ui_text(prop, "Grease Pencil Manhattan Distance", diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 083348dfb26..746f9e56c06 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -304,7 +304,7 @@ static void deformVerts_do(HookModifierData *hmd, } if (hmd->curfalloff) { - BKE_curvemapping_initialize(hmd->curfalloff); + BKE_curvemapping_init(hmd->curfalloff); } /* Generic data needed for applying per-vertex calculations (initialize all members) */ diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 93fb7749392..7b09d3c470d 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -44,12 +44,7 @@ #include "BKE_lib_query.h" #include "BKE_mesh.h" #include "BKE_modifier.h" - -/* SpaceType struct has a member called 'new' which obviously conflicts with C++ - * so temporarily redefining the new keyword to make it compile. */ -#define new extern_new #include "BKE_screen.h" -#undef new #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 8f6676dd0b2..5513e6b4971 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -33,6 +33,8 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "MEM_guardedalloc.h" + #include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_lib_query.h" @@ -65,6 +67,9 @@ static void initData(ModifierData *md) mcmd->cache_file = NULL; mcmd->object_path[0] = '\0'; mcmd->read_flag = MOD_MESHSEQ_READ_ALL; + mcmd->velocity_scale = 1.0f; + mcmd->vertex_velocities = NULL; + mcmd->num_vertices = 0; mcmd->reader = NULL; mcmd->reader_object_path[0] = '\0'; @@ -91,6 +96,10 @@ static void freeData(ModifierData *md) mcmd->reader_object_path[0] = '\0'; BKE_cachefile_reader_free(mcmd->cache_file, &mcmd->reader); } + + if (mcmd->vertex_velocities) { + MEM_freeN(mcmd->vertex_velocities); + } } static bool isDisabled(const struct Scene *UNUSED(scene), @@ -154,6 +163,17 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * Mesh *result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag); + mcmd->velocity_delta = 1.0f; + if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_SECOND) { + mcmd->velocity_delta /= FPS; + } + + mcmd->last_lookup_time = time; + + if (result != NULL) { + mcmd->num_vertices = result->totvert; + } + if (err_str) { BKE_modifier_set_error(md, "%s", err_str); } @@ -221,6 +241,8 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(layout, &ptr, "read_data", UI_ITEM_R_EXPAND, NULL, ICON_NONE); } + uiItemR(layout, &ptr, "velocity_scale", 0, NULL, ICON_NONE); + modifier_panel_end(layout, &ptr); } diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 6374f081581..7b31886a220 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -59,7 +59,7 @@ #include "MOD_ui_common.h" #ifdef WITH_OCEANSIM -static void init_cache_data(Object *ob, struct OceanModifierData *omd) +static void init_cache_data(Object *ob, struct OceanModifierData *omd, const int resolution) { const char *relbase = BKE_modifier_path_relbase_from_global(ob); @@ -71,7 +71,7 @@ static void init_cache_data(Object *ob, struct OceanModifierData *omd) omd->chop_amount, omd->foam_coverage, omd->foam_fade, - omd->resolution); + resolution); } static void simulate_ocean_modifier(struct OceanModifierData *omd) @@ -87,7 +87,11 @@ static void initData(ModifierData *md) #ifdef WITH_OCEANSIM OceanModifierData *omd = (OceanModifierData *)md; + /* Render resolution */ omd->resolution = 7; + /* Display resolution for the non-render case */ + omd->viewport_resolution = 7; + omd->spatial_size = 50; omd->wave_alignment = 0.0; @@ -126,7 +130,7 @@ static void initData(ModifierData *md) omd->spraylayername[0] = '\0'; /* layer name empty by default */ omd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(omd->ocean, omd); + BKE_ocean_init_from_modifier(omd->ocean, omd, omd->viewport_resolution); simulate_ocean_modifier(omd); #else /* WITH_OCEANSIM */ /* unused */ @@ -164,7 +168,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla tomd->oceancache = NULL; tomd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(tomd->ocean, tomd); + BKE_ocean_init_from_modifier(tomd->ocean, tomd, tomd->viewport_resolution); simulate_ocean_modifier(tomd); #else /* WITH_OCEANSIM */ /* unused */ @@ -288,7 +292,7 @@ static void generate_ocean_geometry_uvs(void *__restrict userdata, } } -static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig) +static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, const int resolution) { Mesh *result; @@ -297,10 +301,10 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig) int num_verts; int num_polys; - const bool use_threading = omd->resolution > 4; + const bool use_threading = resolution > 4; - gogd.rx = omd->resolution * omd->resolution; - gogd.ry = omd->resolution * omd->resolution; + gogd.rx = resolution * resolution; + gogd.ry = resolution * resolution; gogd.res_x = gogd.rx * omd->repeat_x; gogd.res_y = gogd.ry * omd->repeat_y; @@ -362,6 +366,9 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes Mesh *result = NULL; OceanResult ocr; + const int resolution = (ctx->flag & MOD_APPLY_RENDER) ? omd->resolution : + omd->viewport_resolution; + MVert *mverts; int cfra_for_cache; @@ -383,7 +390,7 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes /* do ocean simulation */ if (omd->cached == true) { if (!omd->oceancache) { - init_cache_data(ob, omd); + init_cache_data(ob, omd, resolution); } BKE_ocean_simulate_cache(omd->oceancache, cfra_scene); } @@ -393,12 +400,12 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes * This function is only called on an original object when applying the modifier * using the 'Apply Modifier' button, and thus it is not called frequently for * simulation. */ - allocated_ocean |= BKE_ocean_ensure(omd); + allocated_ocean |= BKE_ocean_ensure(omd, resolution); simulate_ocean_modifier(omd); } if (omd->geometry_mode == MOD_OCEAN_GEOM_GENERATE) { - result = generate_ocean_geometry(omd, mesh); + result = generate_ocean_geometry(omd, mesh, resolution); BKE_mesh_ensure_normals(result); } else if (omd->geometry_mode == MOD_OCEAN_GEOM_DISPLACE) { @@ -558,7 +565,10 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(sub, &ptr, "repeat_x", 0, IFACE_("Repeat X"), ICON_NONE); uiItemR(sub, &ptr, "repeat_y", 0, IFACE_("Y"), ICON_NONE); } - uiItemR(col, &ptr, "resolution", 0, NULL, ICON_NONE); + + sub = uiLayoutColumn(col, true); + uiItemR(sub, &ptr, "viewport_resolution", 0, IFACE_("Resolution Viewport"), ICON_NONE); + uiItemR(sub, &ptr, "resolution", 0, IFACE_("Render"), ICON_NONE); uiItemR(col, &ptr, "time", 0, NULL, ICON_NONE); diff --git a/source/blender/modifiers/intern/MOD_simulation.cc b/source/blender/modifiers/intern/MOD_simulation.cc index d9cc9840e08..92ad02ae34a 100644 --- a/source/blender/modifiers/intern/MOD_simulation.cc +++ b/source/blender/modifiers/intern/MOD_simulation.cc @@ -46,16 +46,11 @@ #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_pointcloud.h" +#include "BKE_screen.h" #include "BKE_simulation.h" #include "BLO_read_write.h" -/* SpaceType struct has a member called 'new' which obviously conflicts with C++ - * so temporarily redefining the new keyword to make it compile. */ -#define new extern_new -#include "BKE_screen.h" -#undef new - #include "UI_interface.h" #include "UI_resources.h" @@ -114,10 +109,12 @@ static PointCloud *modifyPointCloud(ModifierData *md, const float3 *positions = (const float3 *)CustomData_get_layer_named( &state->attributes, CD_PROP_FLOAT3, "Position"); + const float *radii = (const float *)CustomData_get_layer_named( + &state->attributes, CD_PROP_FLOAT, "Radius"); memcpy(pointcloud->co, positions, sizeof(float3) * state->tot_particles); for (int i = 0; i < state->tot_particles; i++) { - pointcloud->radius[i] = 0.03f; + pointcloud->radius[i] = radii[i]; } return pointcloud; @@ -131,6 +128,9 @@ static void panel_draw(const bContext *C, Panel *panel) PointerRNA ob_ptr; modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, &ptr, "simulation", 0, NULL, ICON_NONE); uiItemR(layout, &ptr, "data_path", 0, NULL, ICON_NONE); diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index cbe774e91da..2657e3d894a 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -236,7 +236,7 @@ static void warpModifier_do(WarpModifierData *wmd, } if (wmd->curfalloff) { - BKE_curvemapping_initialize(wmd->curfalloff); + BKE_curvemapping_init(wmd->curfalloff); } invert_m4_m4(obinv, ob->obmat); diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index 54d3aa46344..4ee1f9f669a 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -83,7 +83,7 @@ void weightvg_do_map( } if (cmap && falloff_type == MOD_WVG_MAPPING_CURVE) { - BKE_curvemapping_initialize(cmap); + BKE_curvemapping_init(cmap); } /* Map each weight (vertex) to its new value, accordingly to the chosen mode. */ @@ -382,4 +382,4 @@ void weightvg_ui_common(const bContext *C, PointerRNA *ob_ptr, PointerRNA *ptr, } } } -}
\ No newline at end of file +} diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 8039856172a..6bb4f3dc1b5 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -72,7 +72,7 @@ static void initData(ModifierData *md) wmd->default_weight = 0.0f; wmd->cmap_curve = BKE_curvemapping_add(1, 0.0, 0.0, 1.0, 1.0); - BKE_curvemapping_initialize(wmd->cmap_curve); + BKE_curvemapping_init(wmd->cmap_curve); wmd->rem_threshold = 0.01f; wmd->add_threshold = 0.01f; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 80720f5206a..33b95d50cc0 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -136,6 +136,7 @@ set(SRC function/nodes/node_fn_float_compare.cc function/nodes/node_fn_group_instance_id.cc function/nodes/node_fn_object_transforms.cc + function/nodes/node_fn_random_float.cc function/nodes/node_fn_switch.cc function/node_function_util.cc @@ -160,7 +161,7 @@ set(SRC shader/nodes/node_shader_bsdf_velvet.c shader/nodes/node_shader_bump.c shader/nodes/node_shader_camera.c - shader/nodes/node_shader_clamp.c + shader/nodes/node_shader_clamp.cc shader/nodes/node_shader_common.c shader/nodes/node_shader_curves.c shader/nodes/node_shader_displacement.c @@ -232,10 +233,12 @@ set(SRC shader/node_shader_tree.c shader/node_shader_util.c + simulation/nodes/node_sim_age_reached_event.cc simulation/nodes/node_sim_common.cc simulation/nodes/node_sim_emit_particles.cc simulation/nodes/node_sim_execute_condition.cc simulation/nodes/node_sim_force.cc + simulation/nodes/node_sim_kill_particle.cc simulation/nodes/node_sim_multi_execute.cc simulation/nodes/node_sim_particle_attribute.cc simulation/nodes/node_sim_particle_birth_event.cc @@ -278,6 +281,7 @@ set(SRC intern/node_common.c intern/node_exec.c intern/node_socket.cc + intern/node_tree_dependencies.cc intern/node_tree_multi_function.cc intern/node_tree_ref.cc intern/node_util.c @@ -292,6 +296,7 @@ set(SRC NOD_composite.h NOD_derived_node_tree.hh NOD_function.h + NOD_node_tree_dependencies.hh NOD_node_tree_multi_function.hh NOD_node_tree_ref.hh NOD_shader.h diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index d79bd9031b8..b39c9fd1a23 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -181,7 +181,7 @@ class DerivedNodeTree : NonCopyable, NonMovable { Vector<DInputSocket *> input_sockets_; Vector<DOutputSocket *> output_sockets_; - Map<const bNodeType *, Vector<DNode *>> nodes_by_type_; + MultiValueMap<const bNodeType *, DNode *> nodes_by_type_; public: DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs); @@ -483,13 +483,7 @@ inline Span<const DNode *> DerivedNodeTree::nodes_by_type(StringRefNull idname) inline Span<const DNode *> DerivedNodeTree::nodes_by_type(const bNodeType *nodetype) const { - const Vector<DNode *> *nodes = nodes_by_type_.lookup_ptr(nodetype); - if (nodes == nullptr) { - return {}; - } - else { - return *nodes; - } + return nodes_by_type_.lookup(nodetype); } inline Span<const DSocket *> DerivedNodeTree::sockets() const diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 4c05da694f7..1d22e9c6811 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -27,6 +27,7 @@ void register_node_type_fn_switch(void); void register_node_type_fn_group_instance_id(void); void register_node_type_fn_combine_strings(void); void register_node_type_fn_object_transforms(void); +void register_node_type_fn_random_float(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_node_tree_dependencies.hh b/source/blender/nodes/NOD_node_tree_dependencies.hh new file mode 100644 index 00000000000..ca7059caa5f --- /dev/null +++ b/source/blender/nodes/NOD_node_tree_dependencies.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. + */ + +#ifndef __NOD_NODE_TREE_DEPENDENCIES_H__ +#define __NOD_NODE_TREE_DEPENDENCIES_H__ + +#include "BLI_vector_set.hh" + +#include "DNA_ID.h" +#include "DNA_object_types.h" + +struct bNodeTree; + +namespace blender::nodes { + +class NodeTreeDependencies { + private: + VectorSet<Object *> transform_deps_; + VectorSet<Object *> geometry_deps_; + VectorSet<ID *> id_deps_; + + public: + void add_transform_dependency(Object *object) + { + if (object == nullptr) { + return; + } + transform_deps_.add(object); + id_deps_.add(&object->id); + } + + void add_geometry_dependency(Object *object) + { + if (object == nullptr) { + return; + } + geometry_deps_.add(object); + id_deps_.add(&object->id); + } + + bool depends_on(ID *id) const + { + return id_deps_.contains(id); + } + + Span<Object *> transform_dependencies() + { + return transform_deps_; + } + + Span<Object *> geometry_dependencies() + { + return geometry_deps_; + } + + Span<ID *> id_dependencies() + { + return id_deps_; + } +}; + +NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree); + +} // namespace blender::nodes + +#endif /* __NOD_NODE_TREE_DEPENDENCIES_H__ */ diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index ebf5709ef50..13656c2e940 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -47,6 +47,7 @@ #include "BLI_array.hh" #include "BLI_linear_allocator.hh" #include "BLI_map.hh" +#include "BLI_multi_value_map.hh" #include "BLI_string_ref.hh" #include "BLI_timeit.hh" #include "BLI_utility_mixins.hh" @@ -162,7 +163,7 @@ class NodeTreeRef : NonCopyable, NonMovable { Vector<SocketRef *> sockets_by_id_; Vector<InputSocketRef *> input_sockets_; Vector<OutputSocketRef *> output_sockets_; - Map<const bNodeType *, Vector<NodeRef *>> nodes_by_type_; + MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_; public: NodeTreeRef(bNodeTree *btree); @@ -411,13 +412,7 @@ inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) co inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const { - const Vector<NodeRef *> *nodes = nodes_by_type_.lookup_ptr(nodetype); - if (nodes == nullptr) { - return {}; - } - else { - return *nodes; - } + return nodes_by_type_.lookup(nodetype); } inline Span<const SocketRef *> NodeTreeRef::sockets() const diff --git a/source/blender/nodes/NOD_simulation.h b/source/blender/nodes/NOD_simulation.h index 2947d38fe83..fe3f73517ec 100644 --- a/source/blender/nodes/NOD_simulation.h +++ b/source/blender/nodes/NOD_simulation.h @@ -39,6 +39,8 @@ void register_node_type_sim_particle_mesh_collision_event(void); void register_node_type_sim_emit_particles(void); void register_node_type_sim_time(void); void register_node_type_sim_particle_attribute(void); +void register_node_type_sim_age_reached_event(void); +void register_node_type_sim_kill_particle(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 31ce3f81450..7922a73902c 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -270,6 +270,8 @@ DefNode(SimulationNode, SIM_NODE_PARTICLE_MESH_COLLISION_EVENT, 0, "PARTIC DefNode(SimulationNode, SIM_NODE_EMIT_PARTICLES, 0, "EMIT_PARTICLES", EmitParticles, "Emit Particles", "") DefNode(SimulationNode, SIM_NODE_TIME, def_sim_time, "TIME", Time, "Time", "") DefNode(SimulationNode, SIM_NODE_PARTICLE_ATTRIBUTE, def_sim_particle_attribute, "PARTICLE_ATTRIBUTE", ParticleAttribute, "Particle Attribute", "") +DefNode(SimulationNode, SIM_NODE_AGE_REACHED_EVENT, 0, "AGE_REACHED_EVENT", AgeReachedEvent, "Age Reached Event", "") +DefNode(SimulationNode, SIM_NODE_KILL_PARTICLE, 0, "KILL_PARTICLE", KillParticle, "Kill Particle", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") @@ -277,7 +279,7 @@ DefNode(FunctionNode, FN_NODE_SWITCH, def_fn_switch, "SWITCH", DefNode(FunctionNode, FN_NODE_GROUP_INSTANCE_ID, 0, "GROUP_INSTANCE_ID", GroupInstanceID, "Group Instance ID", "") DefNode(FunctionNode, FN_NODE_COMBINE_STRINGS, 0, "COMBINE_STRINGS", CombineStrings, "Combine Strings", "") DefNode(FunctionNode, FN_NODE_OBJECT_TRANSFORMS, 0, "OBJECT_TRANSFORMS", ObjectTransforms, "Object Transforms", "") - +DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") /* undefine macros */ diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc new file mode 100644 index 00000000000..2ee0830637a --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#include "node_function_util.hh" + +#include "BLI_hash.h" + +static bNodeSocketTemplate fn_node_random_float_in[] = { + {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_INT, N_("Seed")}, + {-1, ""}, +}; + +static bNodeSocketTemplate fn_node_random_float_out[] = { + {SOCK_FLOAT, N_("Value")}, + {-1, ""}, +}; + +class RandomFloatFunction : public blender::fn::MultiFunction { + private: + uint32_t function_seed_; + + public: + RandomFloatFunction(uint32_t function_seed) : function_seed_(function_seed) + { + blender::fn::MFSignatureBuilder signature = this->get_builder("Random float"); + signature.single_input<float>("Min"); + signature.single_input<float>("Max"); + signature.single_input<int>("Seed"); + signature.single_output<float>("Value"); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + blender::fn::VSpan<float> min_values = params.readonly_single_input<float>(0, "Min"); + blender::fn::VSpan<float> max_values = params.readonly_single_input<float>(1, "Max"); + blender::fn::VSpan<int> seeds = params.readonly_single_input<int>(2, "Seed"); + blender::MutableSpan<float> values = params.uninitialized_single_output<float>(3, "Value"); + + for (int64_t i : mask) { + const float min_value = min_values[i]; + const float max_value = max_values[i]; + const int seed = seeds[i]; + const float value = BLI_hash_int_01((uint32_t)seed ^ function_seed_); + values[i] = value * (max_value - min_value) + min_value; + } + } +}; + +static void fn_node_random_float_expand_in_mf_network( + blender::nodes::NodeMFNetworkBuilder &builder) +{ + uint32_t function_seed = 1746872341u; + const blender::nodes::DNode &node = builder.dnode(); + const blender::DefaultHash<blender::StringRefNull> hasher; + function_seed = 33 * function_seed + hasher(node.name()); + for (const blender::nodes::DParentNode *parent = node.parent(); parent != nullptr; + parent = parent->parent()) { + function_seed = 33 * function_seed + hasher(parent->node_ref().name()); + } + + builder.construct_and_set_matching_fn<RandomFloatFunction>(function_seed); +} + +void register_node_type_fn_random_float() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); + node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); + ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index b7c78cb1499..5414ea9208a 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -321,7 +321,7 @@ BLI_NOINLINE void DerivedNodeTree::store_in_this_and_init_ids( node->id_ = node_index; const bNodeType *nodetype = node->node_ref_->bnode()->typeinfo; - nodes_by_type_.lookup_or_add_default(nodetype).append(node); + nodes_by_type_.add(nodetype, node); for (DInputSocket *socket : node->inputs_) { socket->id_ = sockets_by_id_.append_and_get_index(socket); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 04d86f5b44e..0dfae7424cb 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -634,10 +634,10 @@ static bNodeSocketType *make_socket_type_string() class ObjectSocketMultiFunction : public blender::fn::MultiFunction { private: - const Object *object_; + Object *object_; public: - ObjectSocketMultiFunction(const Object *object) : object_(object) + ObjectSocketMultiFunction(Object *object) : object_(object) { blender::fn::MFSignatureBuilder signature = this->get_builder("Object Socket"); signature.depends_on_context(); @@ -679,7 +679,7 @@ static bNodeSocketType *make_socket_type_object() return blender::fn::MFDataType::ForSingle<blender::bke::PersistentObjectHandle>(); }; socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) { - const Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value; + Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value; builder.construct_generator_fn<ObjectSocketMultiFunction>(object); }; return socktype; diff --git a/source/blender/nodes/intern/node_tree_dependencies.cc b/source/blender/nodes/intern/node_tree_dependencies.cc new file mode 100644 index 00000000000..efe75a10f7e --- /dev/null +++ b/source/blender/nodes/intern/node_tree_dependencies.cc @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#include "NOD_node_tree_dependencies.hh" + +#include "DNA_node_types.h" + +#include "BKE_node.h" + +namespace blender::nodes { + +static void add_dependencies_of_node_tree(bNodeTree &ntree, NodeTreeDependencies &r_dependencies) +{ + /* TODO: Do a bit more sophisticated parsing to see which dependencies are really required. */ + LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->type == SOCK_OBJECT) { + Object *object = ((bNodeSocketValueObject *)socket->default_value)->value; + if (object != nullptr) { + r_dependencies.add_transform_dependency(object); + if (object->type == OB_MESH) { + r_dependencies.add_geometry_dependency(object); + } + } + } + } + + if (node->type == NODE_GROUP) { + bNodeTree *group = (bNodeTree *)node->id; + if (group != nullptr) { + add_dependencies_of_node_tree(*group, r_dependencies); + } + } + } +} + +NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree) +{ + NodeTreeDependencies dependencies; + add_dependencies_of_node_tree(ntree, dependencies); + return dependencies; +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 186ca750f10..47669bc5ca2 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -79,7 +79,7 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) for (NodeRef *node : nodes_by_id_) { const bNodeType *nodetype = node->bnode_->typeinfo; - nodes_by_type_.lookup_or_add_default(nodetype).append(node); + nodes_by_type_.add(nodetype, node); } } diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 9efbdc079e6..b2309abe32e 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -73,7 +73,7 @@ void *node_initexec_curves(bNodeExecContext *UNUSED(context), bNode *node, bNodeInstanceKey UNUSED(key)) { - BKE_curvemapping_initialize(node->storage); + BKE_curvemapping_init(node->storage); return NULL; /* unused return */ } diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.c b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 808f9686f0a..1077f616a62 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.c +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -50,6 +50,30 @@ static int gpu_shader_clamp(GPUMaterial *mat, GPU_stack_link(mat, node, "clamp_range", in, out); } +static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +{ + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{ + "Clamp (Min Max)", + [](float value, float min, float max) { return std::min(std::max(value, min), max); }}; + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> range_fn{ + "Clamp (Range)", [](float value, float a, float b) { + if (a < b) { + return clamp_f(value, a, b); + } + else { + return clamp_f(value, b, a); + } + }}; + + int clamp_type = builder.bnode().custom1; + if (clamp_type == NODE_CLAMP_MINMAX) { + builder.set_matching_fn(minmax_fn); + } + else { + builder.set_matching_fn(range_fn); + } +} + void register_node_type_sh_clamp(void) { static bNodeType ntype; @@ -58,6 +82,7 @@ void register_node_type_sh_clamp(void) node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); + ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.c index 68f252cb092..06aaf9e74cb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.c +++ b/source/blender/nodes/shader/nodes/node_shader_curves.c @@ -168,7 +168,7 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, CurveMapping *cumap = node->storage; - BKE_curvemapping_initialize(cumap); + BKE_curvemapping_init(cumap); BKE_curvemapping_table_RGBA(cumap, &array, &size); GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.c b/source/blender/nodes/shader/nodes/node_shader_geometry.c index deb0ee9037c..30e376f8e09 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geometry.c +++ b/source/blender/nodes/shader/nodes/node_shader_geometry.c @@ -45,6 +45,9 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; GPUNodeLink *bary_link = (!out[5].hasoutput) ? GPU_constant(val) : GPU_builtin(GPU_BARYCENTRIC_TEXCO); + if (out[5].hasoutput) { + GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); + } /* Opti: don't request orco if not needed. */ GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) : GPU_attribute(mat, CD_ORCO, ""); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c index 0cf4b51f307..38e79ebe94d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c @@ -19,8 +19,6 @@ #include "../node_shader_util.h" -#include "GPU_draw.h" - /* **************** OUTPUT ******************** */ static bNodeSocketTemplate sh_node_tex_environment_in[] = { @@ -59,7 +57,8 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, NodeTexImage *tex_original = node_original->storage; ImageUser *iuser = &tex_original->iuser; eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; - if (GPU_get_mipmap()) { + /* TODO(fclem) For now assume mipmap is always enabled. */ + if (true) { sampler |= GPU_SAMPLER_MIPMAP; } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index cbda72cd228..1a78d2f5bf2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -19,8 +19,6 @@ #include "../node_shader_util.h" -#include "GPU_draw.h" - /* **************** OUTPUT ******************** */ static bNodeSocketTemplate sh_node_tex_image_in[] = { @@ -95,7 +93,8 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, if (tex->interpolation != SHD_INTERP_CLOSEST) { sampler_state |= GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; - sampler_state |= GPU_get_mipmap() ? GPU_SAMPLER_MIPMAP : 0; + /* TODO(fclem) For now assume mipmap is always enabled. */ + sampler_state |= true ? GPU_SAMPLER_MIPMAP : 0; } const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART); diff --git a/source/blender/nodes/shader/nodes/node_shader_wireframe.c b/source/blender/nodes/shader/nodes/node_shader_wireframe.c index e1da1cd34e4..d1ed13e1ffd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_wireframe.c +++ b/source/blender/nodes/shader/nodes/node_shader_wireframe.c @@ -36,6 +36,7 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { + GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); /* node->custom1 is use_pixel_size */ if (node->custom1) { return GPU_stack_link( diff --git a/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc b/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc new file mode 100644 index 00000000000..add8c4eba4d --- /dev/null +++ b/source/blender/nodes/simulation/nodes/node_sim_age_reached_event.cc @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#include "node_simulation_util.h" + +static bNodeSocketTemplate sim_node_age_reached_event_in[] = { + {SOCK_FLOAT, N_("Age"), 3, 0, 0, 0, 0, 10000000}, + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate sim_node_age_reached_event_out[] = { + {SOCK_EVENTS, N_("Event")}, + {-1, ""}, +}; + +void register_node_type_sim_age_reached_event() +{ + static bNodeType ntype; + + sim_node_type_base(&ntype, SIM_NODE_AGE_REACHED_EVENT, "Age Reached Event", 0, 0); + node_type_socket_templates( + &ntype, sim_node_age_reached_event_in, sim_node_age_reached_event_out); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc b/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc new file mode 100644 index 00000000000..793b40d9365 --- /dev/null +++ b/source/blender/nodes/simulation/nodes/node_sim_kill_particle.cc @@ -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. + */ + +#include "node_simulation_util.h" + +static bNodeSocketTemplate sim_node_kill_particle_in[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate sim_node_kill_particle_out[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, + {-1, ""}, +}; + +void register_node_type_sim_kill_particle() +{ + static bNodeType ntype; + + sim_node_type_base(&ntype, SIM_NODE_KILL_PARTICLE, "Kill Particle", 0, 0); + node_type_socket_templates(&ntype, sim_node_kill_particle_in, sim_node_kill_particle_out); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc index 2de7be2d3eb..5e1a6c35d52 100644 --- a/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc +++ b/source/blender/nodes/simulation/nodes/node_sim_particle_mesh_emitter.cc @@ -20,7 +20,8 @@ static bNodeSocketTemplate sim_node_particle_mesh_emitter_in[] = { {SOCK_OBJECT, N_("Object")}, - {SOCK_FLOAT, N_("Rate"), 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_FLOAT, N_("Rate"), 100.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_CONTROL_FLOW, N_("Execute")}, {-1, ""}, }; diff --git a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc index 8696dbe340c..8f5c6818cb4 100644 --- a/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc +++ b/source/blender/nodes/simulation/nodes/node_sim_set_particle_attribute.cc @@ -18,6 +18,7 @@ #include "node_simulation_util.h" static bNodeSocketTemplate sim_node_set_particle_attribute_in[] = { + {SOCK_CONTROL_FLOW, N_("Execute")}, {SOCK_STRING, N_("Name")}, {SOCK_FLOAT, N_("Float"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, {SOCK_INT, N_("Int"), 0, 0, 0, 0, -10000, 10000}, @@ -38,7 +39,7 @@ static void sim_node_set_particle_attribute_update(bNodeTree *UNUSED(ntree), bNo { int index = 0; LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (index >= 1) { + if (index >= 2) { nodeSetSocketAvailability(sock, sock->type == node->custom1); } index++; diff --git a/source/blender/nodes/texture/nodes/node_texture_curves.c b/source/blender/nodes/texture/nodes/node_texture_curves.c index d42985ba041..70f7e731720 100644 --- a/source/blender/nodes/texture/nodes/node_texture_curves.c +++ b/source/blender/nodes/texture/nodes/node_texture_curves.c @@ -39,7 +39,7 @@ static void time_colorfn( fac = (p->cfra - node->custom1) / (float)(node->custom2 - node->custom1); } - BKE_curvemapping_initialize(node->storage); + BKE_curvemapping_init(node->storage); fac = BKE_curvemapping_evaluateF(node->storage, 0, fac); out[0] = CLAMPIS(fac, 0.0f, 1.0f); } diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 5c6e0b0a308..049b919b4a4 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -82,23 +82,23 @@ bool BPY_execute_text(struct bContext *C, bool BPY_execute_string_as_number(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, double *r_value); bool BPY_execute_string_as_intptr(struct bContext *C, const char *imports[], const char *expr, - const bool verbose, + 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 bool verbose, + 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 bool verbose, + const char *report_prefix, char **r_value); bool BPY_execute_string_ex(struct bContext *C, diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 9c84a4bb824..406dbdafe22 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -634,7 +634,7 @@ error_cleanup: PyObject *PyC_ExceptionBuffer_Simple(void) { - PyObject *string_io_buf; + PyObject *string_io_buf = NULL; PyObject *error_type, *error_value, *error_traceback; @@ -648,7 +648,19 @@ PyObject *PyC_ExceptionBuffer_Simple(void) return NULL; } - string_io_buf = PyObject_Str(error_value); + if (PyErr_GivenExceptionMatches(error_type, PyExc_SyntaxError)) { + /* Special exception for syntax errors, + * in these cases the full error is verbose and not very useful, + * just use the initial text so we know what the error is. */ + if (PyTuple_CheckExact(error_value) && PyTuple_GET_SIZE(error_value) >= 1) { + string_io_buf = PyObject_Str(PyTuple_GET_ITEM(error_value, 0)); + } + } + + if (string_io_buf == NULL) { + string_io_buf = PyObject_Str(error_value); + } + /* Python does this too */ if (UNLIKELY(string_io_buf == NULL)) { string_io_buf = PyUnicode_FromFormat("<unprintable %s object>", Py_TYPE(error_value)->tp_name); @@ -804,8 +816,12 @@ void PyC_MainModule_Restore(PyObject *main_mod) Py_XDECREF(main_mod); } -/* Must be called before Py_Initialize, - * expects output of BKE_appdir_folder_id(BLENDER_PYTHON, NULL). */ +/** + * - Must be called before #Py_Initialize. + * - Expects output of `BKE_appdir_folder_id(BLENDER_PYTHON, NULL)`. + * - Note that the `PYTHONPATH` environment variable isn't reliable, see T31506. + Use #Py_SetPythonHome instead. + */ void PyC_SetHomePath(const char *py_path_bundle) { if (py_path_bundle == NULL) { @@ -824,24 +840,14 @@ void PyC_SetHomePath(const char *py_path_bundle) /* OSX allow file/directory names to contain : character (represented as / in the Finder) * but current Python lib (release 3.1.1) doesn't handle these correctly */ if (strchr(py_path_bundle, ':')) { - printf( - "Warning : Blender application is located in a path containing : or / chars\ - \nThis may make python import function fail\n"); - } -# endif - -# if 0 /* disable for now [#31506] - campbell */ -# ifdef _WIN32 - /* cmake/MSVC debug build crashes without this, why only - * in this case is unknown.. */ - { - /*BLI_setenv("PYTHONPATH", py_path_bundle)*/; + fprintf(stderr, + "Warning! Blender application is located in a path containing ':' or '/' chars\n" + "This may make python import function fail\n"); } -# endif # endif { - static wchar_t py_path_bundle_wchar[1024]; + wchar_t py_path_bundle_wchar[1024]; /* Can't use this, on linux gives bug: #23018, * TODO: try LANG="en_US.UTF-8" /usr/bin/blender, suggested 2008 */ diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c index 1379c0e557a..da7674eb7f9 100644 --- a/source/blender/python/gpu/gpu_py_api.c +++ b/source/blender/python/gpu/gpu_py_api.c @@ -43,9 +43,9 @@ /** \name Utils to invalidate functions * \{ */ -bool bpygpu_is_initialized_or_error(void) +bool bpygpu_is_init_or_error(void) { - if (!GPU_is_initialized()) { + if (!GPU_is_init()) { PyErr_SetString(PyExc_SystemError, "GPU functions for drawing are not available in background mode"); diff --git a/source/blender/python/gpu/gpu_py_api.h b/source/blender/python/gpu/gpu_py_api.h index e278bb63a49..916b1ac6e49 100644 --- a/source/blender/python/gpu/gpu_py_api.h +++ b/source/blender/python/gpu/gpu_py_api.h @@ -25,14 +25,14 @@ int bpygpu_ParsePrimType(PyObject *o, void *p); PyObject *BPyInit_gpu(void); -bool bpygpu_is_initialized_or_error(void); +bool bpygpu_is_init_or_error(void); #define BPYGPU_IS_INIT_OR_ERROR_OBJ \ - if (UNLIKELY(!bpygpu_is_initialized_or_error())) { \ + if (UNLIKELY(!bpygpu_is_init_or_error())) { \ return NULL; \ } \ ((void)0) #define BPYGPU_IS_INIT_OR_ERROR_INT \ - if (UNLIKELY(!bpygpu_is_initialized_or_error())) { \ + if (UNLIKELY(!bpygpu_is_init_or_error())) { \ return -1; \ } \ ((void)0) diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c index b3df991cf12..f9fe1654db0 100644 --- a/source/blender/python/gpu/gpu_py_batch.c +++ b/source/blender/python/gpu/gpu_py_batch.c @@ -184,8 +184,7 @@ static PyObject *bpygpu_Batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_sh } GPUShader *shader = py_shader->shader; - GPU_batch_program_set( - self->batch, GPU_shader_get_program(shader), GPU_shader_get_interface(shader)); + GPU_batch_set_shader(self->batch, shader); #ifdef USE_GPU_PY_REFERENCES /* Remove existing user (if any), hold new user. */ @@ -229,9 +228,7 @@ static PyObject *bpygpu_Batch_draw(BPyGPUBatch *self, PyObject *args) } } else if (self->batch->program != GPU_shader_get_program(py_program->shader)) { - GPU_batch_program_set(self->batch, - GPU_shader_get_program(py_program->shader), - GPU_shader_get_interface(py_program->shader)); + GPU_batch_set_shader(self->batch, py_program->shader); } GPU_batch_draw(self->batch); diff --git a/source/blender/python/intern/bpy_capi_utils.c b/source/blender/python/intern/bpy_capi_utils.c index 89ef2f40a30..27eea80f1f6 100644 --- a/source/blender/python/intern/bpy_capi_utils.c +++ b/source/blender/python/intern/bpy_capi_utils.c @@ -97,7 +97,10 @@ void BPy_reports_write_stdout(const ReportList *reports, const char *header) } } -bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const bool use_location) +bool BPy_errors_to_report_ex(ReportList *reports, + const char *error_prefix, + const bool use_full, + const bool use_location) { PyObject *pystring; @@ -124,40 +127,38 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo return 0; } + if (error_prefix == NULL) { + /* Not very helpful, better than nothing. */ + error_prefix = "Python"; + } + if (use_location) { const char *filename; int lineno; - PyObject *pystring_format; /* workaround, see below */ - const char *cstring; - PyC_FileAndNum(&filename, &lineno); if (filename == NULL) { filename = "<unknown location>"; } -#if 0 /* ARG!. workaround for a bug in blenders use of vsnprintf */ BKE_reportf(reports, RPT_ERROR, - "%s\nlocation: %s:%d\n", + TIP_("%s: %s\nlocation: %s:%d\n"), + error_prefix, _PyUnicode_AsString(pystring), filename, lineno); -#else - pystring_format = PyUnicode_FromFormat( - TIP_("%s\nlocation: %s:%d\n"), _PyUnicode_AsString(pystring), filename, lineno); - - cstring = _PyUnicode_AsString(pystring_format); - BKE_report(reports, RPT_ERROR, cstring); - - /* not exactly needed. just for testing */ - fprintf(stderr, TIP_("%s\nlocation: %s:%d\n"), cstring, filename, lineno); - Py_DECREF(pystring_format); /* workaround */ -#endif + /* Not exactly needed. Useful for developers tracking down issues. */ + fprintf(stderr, + TIP_("%s: %s\nlocation: %s:%d\n"), + error_prefix, + _PyUnicode_AsString(pystring), + filename, + lineno); } else { - BKE_report(reports, RPT_ERROR, _PyUnicode_AsString(pystring)); + BKE_reportf(reports, RPT_ERROR, "%s: %s", error_prefix, _PyUnicode_AsString(pystring)); } Py_DECREF(pystring); @@ -166,5 +167,5 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo bool BPy_errors_to_report(ReportList *reports) { - return BPy_errors_to_report_ex(reports, true, true); + return BPy_errors_to_report_ex(reports, NULL, true, true); } diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h index 46206f134ba..11409397028 100644 --- a/source/blender/python/intern/bpy_capi_utils.h +++ b/source/blender/python/intern/bpy_capi_utils.h @@ -43,8 +43,10 @@ char *BPy_enum_as_string(const struct EnumPropertyItem *item); short BPy_reports_to_error(struct ReportList *reports, PyObject *exception, const bool clear); void BPy_reports_write_stdout(const struct ReportList *reports, const char *header); bool BPy_errors_to_report_ex(struct ReportList *reports, + const char *error_prefix, const bool use_full, const bool use_location); +bool BPy_errors_to_report_brief_with_prefix(struct ReportList *reports, const char *error_prefix); bool BPy_errors_to_report(struct ReportList *reports); /* TODO - find a better solution! */ diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 5e2162c9e2d..d48ad2b197c 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -411,8 +411,10 @@ static PyObject *bpy_pydriver_depsgraph_as_pyobject(struct Depsgraph *depsgraph) return pyrna_struct_CreatePyObject(&depsgraph_ptr); } -/* Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated - * datablocks, and the current view layer and scene. See T75553. */ +/** + * Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated + * data-blocks, and the current view layer and scene. See T75553. + */ static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars, struct Depsgraph *depsgraph) { @@ -428,17 +430,18 @@ static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars, } } -/* This evals py driver expressions, 'expr' is a Python expression that - * should evaluate to a float number, which is returned. +/** + * This evaluates Python driver expressions, `driver_orig->expression` + * is a Python expression that should evaluate to a float number, which is returned. * * (old)note: PyGILState_Ensure() isn't always called because python can call * the bake operator which intern starts a thread which calls scene update - * which does a driver update. to avoid a deadlock check PyC_IsInterpreterActive() - * if PyGILState_Ensure() is needed - see [#27683] + * which does a driver update. to avoid a deadlock check #PyC_IsInterpreterActive() + * if #PyGILState_Ensure() is needed, see T27683. * - * (new)note: checking if python is running is not threadsafe [#28114] + * (new)note: checking if python is running is not thread-safe T28114 * now release the GIL on python operator execution instead, using - * PyEval_SaveThread() / PyEval_RestoreThread() so we don't lock up blender. + * #PyEval_SaveThread() / #PyEval_RestoreThread() so we don't lock up blender. * * For copy-on-write we always cache expressions and write errors in the * original driver, otherwise these would get freed while editing. Due to diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index a880d2cd285..585119a0eae 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -258,11 +258,13 @@ void BPY_python_start(int argc, const char **argv) PyThreadState *py_tstate = NULL; const char *py_path_bundle = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, NULL); - /* not essential but nice to set our name */ - static wchar_t program_path_wchar[FILE_MAX]; /* python holds a reference */ - BLI_strncpy_wchar_from_utf8( - program_path_wchar, BKE_appdir_program_path(), ARRAY_SIZE(program_path_wchar)); - Py_SetProgramName(program_path_wchar); + /* Not essential but nice to set our name. */ + { + const char *program_path = BKE_appdir_program_path(); + wchar_t program_path_wchar[FILE_MAX]; + BLI_strncpy_wchar_from_utf8(program_path_wchar, program_path, ARRAY_SIZE(program_path_wchar)); + Py_SetProgramName(program_path_wchar); + } /* must run before python initializes */ PyImport_ExtendInittab(bpy_internal_modules); @@ -615,8 +617,11 @@ void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr) /** * \return success */ -bool BPY_execute_string_as_number( - bContext *C, const char *imports[], const char *expr, const bool verbose, double *r_value) +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; @@ -635,8 +640,8 @@ bool BPY_execute_string_as_number( ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); } else { PyErr_Clear(); @@ -654,7 +659,7 @@ bool BPY_execute_string_as_number( bool BPY_execute_string_as_string_and_size(bContext *C, const char *imports[], const char *expr, - const bool verbose, + const char *report_prefix, char **r_value, size_t *r_value_size) { @@ -672,8 +677,8 @@ bool BPY_execute_string_as_string_and_size(bContext *C, ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix); } else { PyErr_Clear(); @@ -685,12 +690,15 @@ bool BPY_execute_string_as_string_and_size(bContext *C, return ok; } -bool BPY_execute_string_as_string( - bContext *C, const char *imports[], const char *expr, const bool verbose, char **r_value) +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, verbose, r_value, &value_dummy_size); + C, imports, expr, report_prefix, r_value, &value_dummy_size); } /** @@ -698,8 +706,11 @@ bool BPY_execute_string_as_string( * * \return success */ -bool BPY_execute_string_as_intptr( - bContext *C, const char *imports[], const char *expr, const bool verbose, intptr_t *r_value) +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; @@ -715,8 +726,8 @@ bool BPY_execute_string_as_intptr( ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value); if (ok == false) { - if (verbose) { - BPy_errors_to_report_ex(CTX_wm_reports(C), false, false); + if (report_prefix != NULL) { + BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false); } else { PyErr_Clear(); @@ -910,8 +921,11 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * /* TODO, reloading the module isn't functional at the moment. */ static void bpy_module_free(void *mod); + +/* Defined in 'creator.c' when building as a Python module. */ extern int main_python_enter(int argc, const char **argv); extern void main_python_exit(void); + static struct PyModuleDef bpy_proxy_def = { PyModuleDef_HEAD_INIT, "bpy", /* m_name */ diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index ade80898131..fa53ad522a6 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1879,14 +1879,14 @@ const char *RE_GetActiveRenderView(Render *re) } /* evaluating scene options for general Blender render */ -static int render_initialize_from_main(Render *re, - const RenderData *rd, - Main *bmain, - Scene *scene, - ViewLayer *single_layer, - Object *camera_override, - int anim, - int anim_init) +static int render_init_from_main(Render *re, + const RenderData *rd, + Main *bmain, + Scene *scene, + ViewLayer *single_layer, + Object *camera_override, + int anim, + int anim_init) { int winx, winy; rcti disprect; @@ -2004,8 +2004,7 @@ void RE_RenderFrame(Render *re, scene->r.cfra = frame; - if (render_initialize_from_main( - re, &scene->r, bmain, scene, single_layer, camera_override, 0, 0)) { + if (render_init_from_main(re, &scene->r, bmain, scene, single_layer, camera_override, 0, 0)) { const RenderData rd = scene->r; MEM_reset_peak_memory(); @@ -2058,7 +2057,7 @@ void RE_RenderFrame(Render *re, void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, int render) { re->result_ok = 0; - if (render_initialize_from_main(re, &scene->r, bmain, scene, NULL, NULL, 0, 0)) { + if (render_init_from_main(re, &scene->r, bmain, scene, NULL, NULL, 0, 0)) { if (render) { do_render_3d(re); } @@ -2422,7 +2421,7 @@ void RE_RenderAnim(Render *re, (rd.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)); /* do not fully call for each frame, it initializes & pops output window */ - if (!render_initialize_from_main(re, &rd, bmain, scene, single_layer, camera_override, 0, 1)) { + if (!render_init_from_main(re, &rd, bmain, scene, single_layer, camera_override, 0, 1)) { return; } @@ -2501,7 +2500,7 @@ void RE_RenderAnim(Render *re, render_update_depsgraph(re); /* only border now, todo: camera lens. (ton) */ - render_initialize_from_main(re, &rd, bmain, scene, single_layer, camera_override, 1, 0); + render_init_from_main(re, &rd, bmain, scene, single_layer, camera_override, 1, 0); if (nfra != scene->r.cfra) { /* Skip this frame, but could update for physics and particles system. */ diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c index 8daad33b477..c49cf6203e0 100644 --- a/source/blender/render/intern/source/pointdensity.c +++ b/source/blender/render/intern/source/pointdensity.c @@ -557,7 +557,7 @@ static float density_falloff(PointDensityRangeData *pdr, int index, float square } if (pdr->density_curve && dist != 0.0f) { - BKE_curvemapping_initialize(pdr->density_curve); + BKE_curvemapping_init(pdr->density_curve); density = BKE_curvemapping_evaluateF(pdr->density_curve, 0, density / dist) * dist; } diff --git a/source/blender/simulation/CMakeLists.txt b/source/blender/simulation/CMakeLists.txt index 243b056db74..cbc6ee65303 100644 --- a/source/blender/simulation/CMakeLists.txt +++ b/source/blender/simulation/CMakeLists.txt @@ -43,7 +43,9 @@ set(SRC intern/implicit_eigen.cpp intern/particle_allocator.cc 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_update.cc @@ -52,7 +54,9 @@ set(SRC intern/implicit.h intern/particle_allocator.hh intern/particle_function.hh + intern/particle_mesh_emitter.hh intern/simulation_collect_influences.hh + intern/simulation_solver_influences.hh intern/simulation_solver.hh intern/time_interval.hh diff --git a/source/blender/simulation/SIM_simulation_update.hh b/source/blender/simulation/SIM_simulation_update.hh index 40b62bfb58a..efdfde8a4de 100644 --- a/source/blender/simulation/SIM_simulation_update.hh +++ b/source/blender/simulation/SIM_simulation_update.hh @@ -27,6 +27,8 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, Scene *scene_cow, Simulation *simulation_cow); -} +bool update_simulation_dependencies(Simulation *simulation); + +} // namespace blender::sim #endif /* __SIM_SIMULATION_UPDATE_HH__ */ diff --git a/source/blender/simulation/intern/particle_allocator.cc b/source/blender/simulation/intern/particle_allocator.cc index eb1e998e63a..e47a6354d81 100644 --- a/source/blender/simulation/intern/particle_allocator.cc +++ b/source/blender/simulation/intern/particle_allocator.cc @@ -16,6 +16,8 @@ #include "particle_allocator.hh" +#include "BLI_rand.hh" + namespace blender::sim { AttributesAllocator::~AttributesAllocator() @@ -67,8 +69,15 @@ fn::MutableAttributesRef ParticleAllocator::allocate(int size) ids[pindex] = start_id + pindex; } } + else if (name == "Hash") { + MutableSpan<int> hashes = attributes.get<int>("Hash"); + RandomNumberGenerator rng(hash_seed_ ^ (uint32_t)next_id_); + for (int pindex : IndexRange(size)) { + hashes[pindex] = (int)rng.get_uint32(); + } + } else { - type.fill_uninitialized(info.default_of(i), attributes.get(i).buffer(), size); + type.fill_uninitialized(info.default_of(i), attributes.get(i).data(), size); } } return attributes; diff --git a/source/blender/simulation/intern/particle_allocator.hh b/source/blender/simulation/intern/particle_allocator.hh index 1e7578a75ed..1c412508fe6 100644 --- a/source/blender/simulation/intern/particle_allocator.hh +++ b/source/blender/simulation/intern/particle_allocator.hh @@ -70,10 +70,11 @@ class ParticleAllocator : NonCopyable, NonMovable { private: AttributesAllocator attributes_allocator_; std::atomic<int> next_id_; + uint32_t hash_seed_; public: - ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id) - : attributes_allocator_(attributes_info), next_id_(next_id) + ParticleAllocator(const fn::AttributesInfo &attributes_info, int next_id, uint32_t hash_seed) + : attributes_allocator_(attributes_info), next_id_(next_id), hash_seed_(hash_seed) { } diff --git a/source/blender/simulation/intern/particle_function.cc b/source/blender/simulation/intern/particle_function.cc index 935ef7983d9..035e6d50e21 100644 --- a/source/blender/simulation/intern/particle_function.cc +++ b/source/blender/simulation/intern/particle_function.cc @@ -49,19 +49,17 @@ ParticleFunction::ParticleFunction(const fn::MultiFunction *global_fn, } } -ParticleFunctionEvaluator::ParticleFunctionEvaluator( - const ParticleFunction &particle_fn, - const SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context) +ParticleFunctionEvaluator::ParticleFunctionEvaluator(const ParticleFunction &particle_fn, + const SimulationSolveContext &solve_context, + const ParticleChunkContext &particles) : particle_fn_(particle_fn), solve_context_(solve_context), - particle_chunk_context_(particle_chunk_context), - mask_(particle_chunk_context_.index_mask()), + particles_(particles), + mask_(particles_.index_mask), outputs_(particle_fn_.output_types_.size(), nullptr) { - global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map()); - per_particle_context_.add_global_context("PersistentDataHandleMap", - &solve_context_.handle_map()); + global_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map); + per_particle_context_.add_global_context("PersistentDataHandleMap", &solve_context_.handle_map); } ParticleFunctionEvaluator::~ParticleFunctionEvaluator() @@ -92,8 +90,10 @@ void ParticleFunctionEvaluator::compute() fn::GVSpan ParticleFunctionEvaluator::get(int output_index, StringRef expected_name) const { #ifdef DEBUG - StringRef real_name = particle_fn_.output_names_[output_index]; - BLI_assert(expected_name == real_name); + if (expected_name != "") { + StringRef real_name = particle_fn_.output_names_[output_index]; + BLI_assert(expected_name == real_name); + } BLI_assert(is_computed_); #endif UNUSED_VARS_NDEBUG(expected_name); @@ -116,8 +116,9 @@ void ParticleFunctionEvaluator::compute_globals() fn::MFParamsBuilder params(*particle_fn_.global_fn_, mask_.min_array_size()); /* Add input parameters. */ + ParticleFunctionInputContext input_context{solve_context_, particles_}; for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) { - input->add_input(particle_chunk_context_.attributes(), params, resources_); + input->add_input(input_context, params, resources_); } /* Add output parameters. */ @@ -143,8 +144,9 @@ void ParticleFunctionEvaluator::compute_per_particle() fn::MFParamsBuilder params(*particle_fn_.per_particle_fn_, mask_.min_array_size()); /* Add input parameters. */ + ParticleFunctionInputContext input_context{solve_context_, particles_}; for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) { - input->add_input(particle_chunk_context_.attributes(), params, resources_); + input->add_input(input_context, params, resources_); } /* Add output parameters. */ diff --git a/source/blender/simulation/intern/particle_function.hh b/source/blender/simulation/intern/particle_function.hh index eec4a700383..c9b35640b47 100644 --- a/source/blender/simulation/intern/particle_function.hh +++ b/source/blender/simulation/intern/particle_function.hh @@ -26,10 +26,15 @@ namespace blender::sim { +struct ParticleFunctionInputContext { + const SimulationSolveContext &solve_context; + const ParticleChunkContext &particles; +}; + class ParticleFunctionInput { public: virtual ~ParticleFunctionInput() = default; - virtual void add_input(fn::AttributesRef attributes, + virtual void add_input(ParticleFunctionInputContext &context, fn::MFParamsBuilder ¶ms, ResourceCollector &resources) const = 0; }; @@ -61,7 +66,7 @@ class ParticleFunctionEvaluator { ResourceCollector resources_; const ParticleFunction &particle_fn_; const SimulationSolveContext &solve_context_; - const ParticleChunkContext &particle_chunk_context_; + const ParticleChunkContext &particles_; IndexMask mask_; fn::MFContextBuilder global_context_; fn::MFContextBuilder per_particle_context_; @@ -71,13 +76,13 @@ class ParticleFunctionEvaluator { public: ParticleFunctionEvaluator(const ParticleFunction &particle_fn, const SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context); + const ParticleChunkContext &particles); ~ParticleFunctionEvaluator(); void compute(); - fn::GVSpan get(int output_index, StringRef expected_name) const; + fn::GVSpan get(int output_index, StringRef expected_name = "") const; - template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name) const + template<typename T> fn::VSpan<T> get(int output_index, StringRef expected_name = "") const { return this->get(output_index, expected_name).typed<T>(); } diff --git a/source/blender/simulation/intern/particle_mesh_emitter.cc b/source/blender/simulation/intern/particle_mesh_emitter.cc new file mode 100644 index 00000000000..26541d550eb --- /dev/null +++ b/source/blender/simulation/intern/particle_mesh_emitter.cc @@ -0,0 +1,362 @@ +/* + * 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. + */ + +#include "particle_mesh_emitter.hh" + +#include "BLI_float4x4.hh" +#include "BLI_rand.hh" +#include "BLI_vector_adaptor.hh" + +#include "BKE_mesh_runtime.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +namespace blender::sim { + +ParticleMeshEmitter::~ParticleMeshEmitter() = default; + +struct EmitterSettings { + Object *object; + float rate; +}; + +static BLI_NOINLINE void compute_birth_times(float rate, + TimeInterval emit_interval, + ParticleMeshEmitterSimulationState &state, + Vector<float> &r_birth_times) +{ + const float time_between_particles = 1.0f / rate; + int counter = 0; + while (true) { + counter++; + const float time_offset = counter * time_between_particles; + const float birth_time = state.last_birth_time + time_offset; + if (birth_time > emit_interval.stop()) { + break; + } + if (birth_time <= emit_interval.start()) { + continue; + } + r_birth_times.append(birth_time); + } +} + +static BLI_NOINLINE Span<MLoopTri> get_mesh_triangles(Mesh &mesh) +{ + const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(&mesh); + int amount = BKE_mesh_runtime_looptri_len(&mesh); + return Span(triangles, amount); +} + +static BLI_NOINLINE void compute_triangle_areas(Mesh &mesh, + Span<MLoopTri> triangles, + MutableSpan<float> r_areas) +{ + assert_same_size(triangles, r_areas); + + for (int i : triangles.index_range()) { + const MLoopTri &tri = triangles[i]; + + const float3 v1 = mesh.mvert[mesh.mloop[tri.tri[0]].v].co; + const float3 v2 = mesh.mvert[mesh.mloop[tri.tri[1]].v].co; + const float3 v3 = mesh.mvert[mesh.mloop[tri.tri[2]].v].co; + + const float area = area_tri_v3(v1, v2, v3); + r_areas[i] = area; + } +} + +static BLI_NOINLINE void compute_triangle_weights(Mesh &mesh, + Span<MLoopTri> triangles, + MutableSpan<float> r_weights) +{ + assert_same_size(triangles, r_weights); + compute_triangle_areas(mesh, triangles, r_weights); +} + +static BLI_NOINLINE void compute_cumulative_distribution(Span<float> weights, + MutableSpan<float> r_cumulative_weights) +{ + BLI_assert(weights.size() + 1 == r_cumulative_weights.size()); + + r_cumulative_weights[0] = 0; + for (int i : weights.index_range()) { + r_cumulative_weights[i + 1] = r_cumulative_weights[i] + weights[i]; + } +} + +static void sample_cumulative_distribution_recursive(RandomNumberGenerator &rng, + int amount, + int start, + int one_after_end, + Span<float> cumulative_weights, + VectorAdaptor<int> &r_sampled_indices) +{ + BLI_assert(start <= one_after_end); + const int size = one_after_end - start; + if (size == 0) { + BLI_assert(amount == 0); + } + else if (amount == 0) { + return; + } + else if (size == 1) { + r_sampled_indices.append_n_times(start, amount); + } + else { + const int middle = start + size / 2; + const float left_weight = cumulative_weights[middle] - cumulative_weights[start]; + const float right_weight = cumulative_weights[one_after_end] - cumulative_weights[middle]; + BLI_assert(left_weight >= 0.0f && right_weight >= 0.0f); + const float weight_sum = left_weight + right_weight; + BLI_assert(weight_sum > 0.0f); + + const float left_factor = left_weight / weight_sum; + const float right_factor = right_weight / weight_sum; + + int left_amount = amount * left_factor; + int right_amount = amount * right_factor; + + if (left_amount + right_amount < amount) { + BLI_assert(left_amount + right_amount + 1 == amount); + const float weight_per_item = weight_sum / amount; + const float total_remaining_weight = weight_sum - + (left_amount + right_amount) * weight_per_item; + const float left_remaining_weight = left_weight - left_amount * weight_per_item; + const float left_remaining_factor = left_remaining_weight / total_remaining_weight; + if (rng.get_float() < left_remaining_factor) { + left_amount++; + } + else { + right_amount++; + } + } + + sample_cumulative_distribution_recursive( + rng, left_amount, start, middle, cumulative_weights, r_sampled_indices); + sample_cumulative_distribution_recursive( + rng, right_amount, middle, one_after_end, cumulative_weights, r_sampled_indices); + } +} + +static BLI_NOINLINE void sample_cumulative_distribution(RandomNumberGenerator &rng, + Span<float> cumulative_weights, + MutableSpan<int> r_samples) +{ + VectorAdaptor<int> sampled_indices(r_samples); + sample_cumulative_distribution_recursive(rng, + r_samples.size(), + 0, + cumulative_weights.size() - 1, + cumulative_weights, + sampled_indices); + BLI_assert(sampled_indices.is_full()); +} + +static BLI_NOINLINE bool sample_weighted_buckets(RandomNumberGenerator &rng, + Span<float> weights, + MutableSpan<int> r_samples) +{ + Array<float> cumulative_weights(weights.size() + 1); + compute_cumulative_distribution(weights, cumulative_weights); + + if (r_samples.size() > 0 && cumulative_weights.as_span().last() == 0.0f) { + /* All weights are zero. */ + return false; + } + + sample_cumulative_distribution(rng, cumulative_weights, r_samples); + return true; +} + +static BLI_NOINLINE void sample_looptris(RandomNumberGenerator &rng, + Mesh &mesh, + Span<MLoopTri> triangles, + Span<int> triangles_to_sample, + MutableSpan<float3> r_sampled_positions, + MutableSpan<float3> r_sampled_normals) +{ + assert_same_size(triangles_to_sample, r_sampled_positions, r_sampled_normals); + + MLoop *loops = mesh.mloop; + MVert *verts = mesh.mvert; + + for (uint i : triangles_to_sample.index_range()) { + const uint triangle_index = triangles_to_sample[i]; + const MLoopTri &triangle = triangles[triangle_index]; + + const float3 v1 = verts[loops[triangle.tri[0]].v].co; + const float3 v2 = verts[loops[triangle.tri[1]].v].co; + const float3 v3 = verts[loops[triangle.tri[2]].v].co; + + const float3 bary_coords = rng.get_barycentric_coordinates(); + + float3 position; + interp_v3_v3v3v3(position, v1, v2, v3, bary_coords); + + float3 normal; + normal_tri_v3(normal, v1, v2, v3); + + r_sampled_positions[i] = position; + r_sampled_normals[i] = normal; + } +} + +static BLI_NOINLINE bool compute_new_particle_attributes(ParticleEmitterContext &context, + EmitterSettings &settings, + ParticleMeshEmitterSimulationState &state, + Vector<float3> &r_positions, + Vector<float3> &r_velocities, + Vector<float> &r_birth_times) +{ + if (settings.object == nullptr) { + return false; + } + if (settings.rate <= 0.000001f) { + return false; + } + if (settings.object->type != OB_MESH) { + return false; + } + Mesh &mesh = *(Mesh *)settings.object->data; + if (mesh.totvert == 0) { + return false; + } + + const float start_time = context.emit_interval.start(); + const uint32_t seed = DefaultHash<StringRef>{}(state.head.name); + RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed}; + + compute_birth_times(settings.rate, context.emit_interval, state, r_birth_times); + const int particle_amount = r_birth_times.size(); + if (particle_amount == 0) { + return false; + } + + const float last_birth_time = r_birth_times.last(); + rng.shuffle(r_birth_times.as_mutable_span()); + + Span<MLoopTri> triangles = get_mesh_triangles(mesh); + if (triangles.is_empty()) { + return false; + } + + Array<float> triangle_weights(triangles.size()); + compute_triangle_weights(mesh, triangles, triangle_weights); + + Array<int> triangles_to_sample(particle_amount); + if (!sample_weighted_buckets(rng, triangle_weights, triangles_to_sample)) { + return false; + } + + r_positions.resize(particle_amount); + r_velocities.resize(particle_amount); + sample_looptris(rng, mesh, triangles, triangles_to_sample, r_positions, r_velocities); + + if (context.solve_context.dependency_animations.is_object_transform_changing(*settings.object)) { + Array<float4x4> local_to_world_matrices(particle_amount); + context.solve_context.dependency_animations.get_object_transforms( + *settings.object, r_birth_times, local_to_world_matrices); + + for (int i : IndexRange(particle_amount)) { + const float4x4 &position_to_world = local_to_world_matrices[i]; + const float4x4 normal_to_world = position_to_world.inverted_transposed_affine(); + r_positions[i] = position_to_world * r_positions[i]; + r_velocities[i] = normal_to_world * r_velocities[i]; + } + } + else { + const float4x4 position_to_world = settings.object->obmat; + const float4x4 normal_to_world = position_to_world.inverted_transposed_affine(); + for (int i : IndexRange(particle_amount)) { + r_positions[i] = position_to_world * r_positions[i]; + r_velocities[i] = normal_to_world * r_velocities[i]; + } + } + + for (int i : IndexRange(particle_amount)) { + r_velocities[i].normalize(); + } + + state.last_birth_time = last_birth_time; + return true; +} + +static BLI_NOINLINE EmitterSettings compute_settings(const fn::MultiFunction &inputs_fn, + ParticleEmitterContext &context) +{ + EmitterSettings parameters; + + fn::MFContextBuilder mf_context; + mf_context.add_global_context("PersistentDataHandleMap", &context.solve_context.handle_map); + + fn::MFParamsBuilder mf_params{inputs_fn, 1}; + bke::PersistentObjectHandle object_handle; + mf_params.add_uninitialized_single_output(&object_handle, "Object"); + mf_params.add_uninitialized_single_output(¶meters.rate, "Rate"); + + inputs_fn.call(IndexRange(1), mf_params, mf_context); + + parameters.object = context.solve_context.handle_map.lookup(object_handle); + return parameters; +} + +void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const +{ + auto *state = context.lookup_state<ParticleMeshEmitterSimulationState>(own_state_name_); + if (state == nullptr) { + return; + } + + EmitterSettings settings = compute_settings(inputs_fn_, context); + + Vector<float3> new_positions; + Vector<float3> new_velocities; + Vector<float> new_birth_times; + + if (!compute_new_particle_attributes( + context, settings, *state, new_positions, new_velocities, new_birth_times)) { + return; + } + + for (StringRef name : particle_names_) { + ParticleAllocator *allocator = context.try_get_particle_allocator(name); + if (allocator == nullptr) { + continue; + } + + int amount = new_positions.size(); + fn::MutableAttributesRef attributes = allocator->allocate(amount); + + attributes.get<float3>("Position").copy_from(new_positions); + attributes.get<float3>("Velocity").copy_from(new_velocities); + attributes.get<float>("Birth Time").copy_from(new_birth_times); + + if (action_ != nullptr) { + ParticleChunkContext particles{ + *context.solve_context.state_map.lookup<ParticleSimulationState>(name), + IndexRange(amount), + attributes, + nullptr}; + ParticleActionContext action_context{context.solve_context, particles}; + action_->execute(action_context); + } + } +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/particle_mesh_emitter.hh b/source/blender/simulation/intern/particle_mesh_emitter.hh new file mode 100644 index 00000000000..724d79c1aec --- /dev/null +++ b/source/blender/simulation/intern/particle_mesh_emitter.hh @@ -0,0 +1,52 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SIM_PARTICLE_MESH_EMITTER_HH__ +#define __SIM_PARTICLE_MESH_EMITTER_HH__ + +#include "simulation_solver_influences.hh" + +#include "FN_multi_function.hh" + +namespace blender::sim { + +class ParticleMeshEmitter final : public ParticleEmitter { + private: + std::string own_state_name_; + Array<std::string> particle_names_; + const fn::MultiFunction &inputs_fn_; + const ParticleAction *action_; + + public: + ParticleMeshEmitter(std::string own_state_name, + Array<std::string> particle_names, + const fn::MultiFunction &inputs_fn, + const ParticleAction *action) + : own_state_name_(std::move(own_state_name)), + particle_names_(particle_names), + inputs_fn_(inputs_fn), + action_(action) + { + } + + ~ParticleMeshEmitter(); + + void emit(ParticleEmitterContext &context) const override; +}; + +} // namespace blender::sim + +#endif /* __SIM_PARTICLE_MESH_EMITTER_HH__ */ diff --git a/source/blender/simulation/intern/simulation_collect_influences.cc b/source/blender/simulation/intern/simulation_collect_influences.cc index 764e587d157..818415e5d88 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.cc +++ b/source/blender/simulation/intern/simulation_collect_influences.cc @@ -16,6 +16,7 @@ #include "simulation_collect_influences.hh" #include "particle_function.hh" +#include "particle_mesh_emitter.hh" #include "FN_attributes_ref.hh" #include "FN_multi_function_network_evaluation.hh" @@ -23,38 +24,125 @@ #include "NOD_node_tree_multi_function.hh" +#include "DEG_depsgraph_query.h" + +#include "BLI_hash.h" #include "BLI_rand.hh" +#include "BLI_set.hh" namespace blender::sim { +using fn::GVSpan; +using fn::MFContextBuilder; +using fn::MFDataType; +using fn::MFDummyNode; +using fn::MFFunctionNode; +using fn::MFInputSocket; +using fn::MFNetwork; +using fn::MFNetworkEvaluator; +using fn::MFNode; +using fn::MFOutputSocket; +using fn::MFParamsBuilder; +using fn::MFParamType; +using fn::MultiFunction; +using fn::VSpan; +using nodes::DerivedNodeTree; +using nodes::DInputSocket; +using nodes::DNode; +using nodes::DOutputSocket; +using nodes::DParentNode; +using nodes::MFNetworkTreeMap; +using nodes::NodeTreeRefMap; + struct DummyDataSources { - Map<const fn::MFOutputSocket *, std::string> particle_attributes; + Map<const MFOutputSocket *, std::string> particle_attributes; + Set<const MFOutputSocket *> simulation_time; + Set<const MFOutputSocket *> scene_time; }; extern "C" { void WM_clipboard_text_set(const char *buf, bool selection); } -static std::string dnode_to_path(const nodes::DNode &dnode) +static std::string dnode_to_path(const DNode &dnode) { std::string path; - for (const nodes::DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { + for (const DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { path = parent->node_ref().name() + "/" + path; } path = path + dnode.name(); return path; } -static Span<const nodes::DNode *> get_particle_simulation_nodes(const nodes::DerivedNodeTree &tree) +struct CollectContext : NonCopyable, NonMovable { + SimulationInfluences &influences; + RequiredStates &required_states; + ResourceCollector &resources; + MFNetworkTreeMap &network_map; + MFNetwork &network; + const DerivedNodeTree &tree; + + DummyDataSources data_sources; + Span<const DNode *> particle_simulation_nodes; + Map<const DNode *, std::string> node_paths; + + CollectContext(SimulationInfluences &influences, + RequiredStates &required_states, + ResourceCollector &resources, + MFNetworkTreeMap &network_map) + : influences(influences), + required_states(required_states), + resources(resources), + network_map(network_map), + network(network_map.network()), + tree(network_map.tree()) + { + particle_simulation_nodes = tree.nodes_by_type("SimulationNodeParticleSimulation"); + } +}; + +static const ParticleAction *create_particle_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names); + +static const ParticleAction *create_particle_action(CollectContext &context, + const DInputSocket &dsocket, + Span<StringRefNull> particle_names) +{ + BLI_assert(dsocket.bsocket()->type == SOCK_CONTROL_FLOW); + if (dsocket.linked_sockets().size() != 1) { + return nullptr; + } + return create_particle_action(context, *dsocket.linked_sockets()[0], particle_names); +} + +static StringRefNull get_identifier(CollectContext &context, const DNode &dnode) +{ + return context.node_paths.lookup_or_add_cb(&dnode, [&]() { return dnode_to_path(dnode); }); +} + +static Span<const DNode *> nodes_by_type(CollectContext &context, StringRefNull idname) { - return tree.nodes_by_type("SimulationNodeParticleSimulation"); + return context.tree.nodes_by_type(idname); +} + +static Array<StringRefNull> find_linked_particle_simulations(CollectContext &context, + const DOutputSocket &output_socket) +{ + VectorSet<StringRefNull> names; + for (const DInputSocket *target_socket : output_socket.linked_sockets()) { + if (target_socket->node().idname() == "SimulationNodeParticleSimulation") { + names.add(get_identifier(context, target_socket->node())); + } + } + return names.as_span(); } /* Returns true on success. */ -static bool compute_global_inputs(nodes::MFNetworkTreeMap &network_map, +static bool compute_global_inputs(MFNetworkTreeMap &network_map, ResourceCollector &resources, - Span<const fn::MFInputSocket *> sockets, - MutableSpan<fn::GMutableSpan> r_results) + Span<const MFInputSocket *> sockets, + MutableSpan<GMutableSpan> r_results) { int amount = sockets.size(); if (amount == 0) { @@ -65,28 +153,28 @@ static bool compute_global_inputs(nodes::MFNetworkTreeMap &network_map, return false; } - fn::MFNetworkEvaluator network_fn{{}, sockets}; - fn::MFParamsBuilder params{network_fn, 1}; + MFNetworkEvaluator network_fn{{}, sockets}; + MFParamsBuilder params{network_fn, 1}; for (int param_index : network_fn.param_indices()) { - fn::MFParamType param_type = network_fn.param_type(param_index); - BLI_assert(param_type.category() == fn::MFParamType::Category::SingleOutput); /* For now. */ - const fn::CPPType &type = param_type.data_type().single_type(); + MFParamType param_type = network_fn.param_type(param_index); + BLI_assert(param_type.category() == MFParamType::Category::SingleOutput); /* For now. */ + const CPPType &type = param_type.data_type().single_type(); void *buffer = resources.linear_allocator().allocate(type.size(), type.alignment()); resources.add(buffer, type.destruct_cb(), AT); - fn::GMutableSpan span{type, buffer, 1}; + GMutableSpan span{type, buffer, 1}; r_results[param_index] = span; params.add_uninitialized_single_output(span); } - fn::MFContextBuilder context; + MFContextBuilder context; network_fn.call(IndexRange(1), params, context); return true; } static std::optional<Array<std::string>> compute_global_string_inputs( - nodes::MFNetworkTreeMap &network_map, Span<const fn::MFInputSocket *> sockets) + MFNetworkTreeMap &network_map, Span<const MFInputSocket *> sockets) { ResourceCollector local_resources; - Array<fn::GMutableSpan> computed_values(sockets.size(), NoInitialization()); + Array<GMutableSpan> computed_values(sockets.size(), NoInitialization()); if (!compute_global_inputs(network_map, local_resources, sockets, computed_values)) { return {}; } @@ -98,107 +186,177 @@ static std::optional<Array<std::string>> compute_global_string_inputs( return strings; } -static void find_and_deduplicate_particle_attribute_nodes(nodes::MFNetworkTreeMap &network_map, - DummyDataSources &r_data_sources) +/** + * This will find all the particle attribute input nodes. Then it will compute the attribute names + * by evaluating the network (those names should not depend on per particle data). In the end, + * input nodes that access the same attribute are combined. + */ +static void prepare_particle_attribute_nodes(CollectContext &context) { - fn::MFNetwork &network = network_map.network(); - const nodes::DerivedNodeTree &tree = network_map.tree(); - - Span<const nodes::DNode *> attribute_dnodes = tree.nodes_by_type( - "SimulationNodeParticleAttribute"); + Span<const DNode *> attribute_dnodes = nodes_by_type(context, "SimulationNodeParticleAttribute"); - Vector<fn::MFInputSocket *> name_sockets; - for (const nodes::DNode *dnode : attribute_dnodes) { - fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); + Vector<MFInputSocket *> name_sockets; + for (const DNode *dnode : attribute_dnodes) { + MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode->input(0)); name_sockets.append(&name_socket); } - std::optional<Array<std::string>> attribute_names = compute_global_string_inputs(network_map, - name_sockets); + std::optional<Array<std::string>> attribute_names = compute_global_string_inputs( + context.network_map, name_sockets); if (!attribute_names.has_value()) { return; } - Map<std::pair<std::string, fn::MFDataType>, Vector<fn::MFNode *>> - attribute_nodes_by_name_and_type; + MultiValueMap<std::pair<std::string, MFDataType>, MFNode *> attribute_nodes_by_name_and_type; for (int i : attribute_names->index_range()) { - attribute_nodes_by_name_and_type - .lookup_or_add_default( - {(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()}) - .append(&name_sockets[i]->node()); + attribute_nodes_by_name_and_type.add( + {(*attribute_names)[i], name_sockets[i]->node().output(0).data_type()}, + &name_sockets[i]->node()); } - Map<const fn::MFOutputSocket *, std::string> attribute_inputs; + Map<const MFOutputSocket *, std::string> attribute_inputs; for (auto item : attribute_nodes_by_name_and_type.items()) { StringRef attribute_name = item.key.first; - fn::MFDataType data_type = item.key.second; - Span<fn::MFNode *> nodes = item.value; + MFDataType data_type = item.key.second; + Span<MFNode *> nodes = item.value; - fn::MFOutputSocket &new_attribute_socket = network.add_input( + MFOutputSocket &new_attribute_socket = context.network.add_input( "Attribute '" + attribute_name + "'", data_type); - for (fn::MFNode *node : nodes) { - network.relink(node->output(0), new_attribute_socket); + for (MFNode *node : nodes) { + context.network.relink(node->output(0), new_attribute_socket); } - network.remove(nodes); + context.network.remove(nodes); - r_data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name); + context.data_sources.particle_attributes.add_new(&new_attribute_socket, attribute_name); + } +} + +static void prepare_time_input_nodes(CollectContext &context) +{ + Span<const DNode *> time_input_dnodes = nodes_by_type(context, "SimulationNodeTime"); + Vector<const DNode *> simulation_time_inputs; + Vector<const DNode *> scene_time_inputs; + for (const DNode *dnode : time_input_dnodes) { + NodeSimInputTimeType type = (NodeSimInputTimeType)dnode->node_ref().bnode()->custom1; + switch (type) { + case NODE_SIM_INPUT_SIMULATION_TIME: { + simulation_time_inputs.append(dnode); + break; + } + case NODE_SIM_INPUT_SCENE_TIME: { + scene_time_inputs.append(dnode); + break; + } + } + } + + if (simulation_time_inputs.size() > 0) { + MFOutputSocket &new_socket = context.network.add_input("Simulation Time", + MFDataType::ForSingle<float>()); + for (const DNode *dnode : simulation_time_inputs) { + MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0)); + context.network.relink(old_socket, new_socket); + context.network.remove(old_socket.node()); + } + context.data_sources.simulation_time.add(&new_socket); + } + if (scene_time_inputs.size() > 0) { + MFOutputSocket &new_socket = context.network.add_input("Scene Time", + MFDataType::ForSingle<float>()); + for (const DNode *dnode : scene_time_inputs) { + MFOutputSocket &old_socket = context.network_map.lookup_dummy(dnode->output(0)); + context.network.relink(old_socket, new_socket); + context.network.remove(old_socket.node()); + } + context.data_sources.scene_time.add(&new_socket); } } class ParticleAttributeInput : public ParticleFunctionInput { private: std::string attribute_name_; - const fn::CPPType &attribute_type_; + const CPPType &attribute_type_; public: - ParticleAttributeInput(std::string attribute_name, const fn::CPPType &attribute_type) + ParticleAttributeInput(std::string attribute_name, const CPPType &attribute_type) : attribute_name_(std::move(attribute_name)), attribute_type_(attribute_type) { } - void add_input(fn::AttributesRef attributes, - fn::MFParamsBuilder ¶ms, + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, ResourceCollector &UNUSED(resources)) const override { - std::optional<fn::GSpan> span = attributes.try_get(attribute_name_, attribute_type_); + std::optional<GSpan> span = context.particles.attributes.try_get(attribute_name_, + attribute_type_); if (span.has_value()) { params.add_readonly_single_input(*span); } else { - params.add_readonly_single_input(fn::GVSpan::FromDefault(attribute_type_)); + params.add_readonly_single_input(GVSpan::FromDefault(attribute_type_)); } } }; +class SceneTimeInput : public ParticleFunctionInput { + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, + ResourceCollector &resources) const override + { + const float time = DEG_get_ctime(&context.solve_context.depsgraph); + float *time_ptr = &resources.construct<float>(AT, time); + params.add_readonly_single_input(time_ptr); + } +}; + +class SimulationTimeInput : public ParticleFunctionInput { + void add_input(ParticleFunctionInputContext &context, + MFParamsBuilder ¶ms, + ResourceCollector &resources) const override + { + /* TODO: Vary this per particle. */ + const float time = context.solve_context.solve_interval.stop(); + float *time_ptr = &resources.construct<float>(AT, time); + params.add_readonly_single_input(time_ptr); + } +}; + static const ParticleFunction *create_particle_function_for_inputs( - Span<const fn::MFInputSocket *> sockets_to_compute, - ResourceCollector &resources, - DummyDataSources &data_sources) + CollectContext &context, Span<const MFInputSocket *> sockets_to_compute) { BLI_assert(sockets_to_compute.size() >= 1); - const fn::MFNetwork &network = sockets_to_compute[0]->node().network(); + const MFNetwork &network = sockets_to_compute[0]->node().network(); - VectorSet<const fn::MFOutputSocket *> dummy_deps; - VectorSet<const fn::MFInputSocket *> unlinked_input_deps; + VectorSet<const MFOutputSocket *> dummy_deps; + VectorSet<const MFInputSocket *> unlinked_input_deps; network.find_dependencies(sockets_to_compute, dummy_deps, unlinked_input_deps); BLI_assert(unlinked_input_deps.size() == 0); Vector<const ParticleFunctionInput *> per_particle_inputs; - for (const fn::MFOutputSocket *socket : dummy_deps) { - const std::string *attribute_name = data_sources.particle_attributes.lookup_ptr(socket); - if (attribute_name == nullptr) { - return nullptr; + for (const MFOutputSocket *socket : dummy_deps) { + if (context.data_sources.particle_attributes.contains(socket)) { + const std::string *attribute_name = context.data_sources.particle_attributes.lookup_ptr( + socket); + if (attribute_name == nullptr) { + return nullptr; + } + per_particle_inputs.append(&context.resources.construct<ParticleAttributeInput>( + AT, *attribute_name, socket->data_type().single_type())); + } + else if (context.data_sources.scene_time.contains(socket)) { + per_particle_inputs.append(&context.resources.construct<SceneTimeInput>(AT)); + } + else if (context.data_sources.simulation_time.contains(socket)) { + per_particle_inputs.append(&context.resources.construct<SimulationTimeInput>(AT)); } - per_particle_inputs.append(&resources.construct<ParticleAttributeInput>( - AT, *attribute_name, socket->data_type().single_type())); } - const fn::MultiFunction &per_particle_fn = resources.construct<fn::MFNetworkEvaluator>( + const MultiFunction &per_particle_fn = context.resources.construct<MFNetworkEvaluator>( AT, dummy_deps.as_span(), sockets_to_compute); Array<bool> output_is_global(sockets_to_compute.size(), false); - const ParticleFunction &particle_fn = resources.construct<ParticleFunction>( + const ParticleFunction &particle_fn = context.resources.construct<ParticleFunction>( AT, nullptr, &per_particle_fn, @@ -209,6 +367,17 @@ static const ParticleFunction *create_particle_function_for_inputs( return &particle_fn; } +static const ParticleFunction *create_particle_function_for_inputs( + CollectContext &context, Span<const DInputSocket *> dsockets_to_compute) +{ + Vector<const MFInputSocket *> sockets_to_compute; + for (const DInputSocket *dsocket : dsockets_to_compute) { + const MFInputSocket &socket = context.network_map.lookup_dummy(*dsocket); + sockets_to_compute.append(&socket); + } + return create_particle_function_for_inputs(context, sockets_to_compute); +} + class ParticleFunctionForce : public ParticleForce { private: const ParticleFunction &particle_fn_; @@ -220,13 +389,12 @@ class ParticleFunctionForce : public ParticleForce { void add_force(ParticleForceContext &context) const override { - IndexMask mask = context.particle_chunk().index_mask(); - MutableSpan<float3> r_combined_force = context.force_dst(); + IndexMask mask = context.particles.index_mask; + MutableSpan<float3> r_combined_force = context.force_dst; - ParticleFunctionEvaluator evaluator{ - particle_fn_, context.solve_context(), context.particle_chunk()}; + ParticleFunctionEvaluator evaluator{particle_fn_, context.solve_context, context.particles}; evaluator.compute(); - fn::VSpan<float3> forces = evaluator.get<float3>(0, "Force"); + VSpan<float3> forces = evaluator.get<float3>(0, "Force"); for (int64_t i : mask) { r_combined_force[i] += forces[i]; @@ -234,218 +402,473 @@ class ParticleFunctionForce : public ParticleForce { } }; -static Vector<const ParticleForce *> create_forces_for_particle_simulation( - const nodes::DNode &simulation_node, - nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - DummyDataSources &data_sources) +static void create_forces_for_particle_simulation(CollectContext &context, + const DNode &simulation_node) { Vector<const ParticleForce *> forces; - for (const nodes::DOutputSocket *origin_socket : - simulation_node.input(2, "Forces").linked_sockets()) { - const nodes::DNode &origin_node = origin_socket->node(); + for (const DOutputSocket *origin_socket : simulation_node.input(2, "Forces").linked_sockets()) { + const DNode &origin_node = origin_socket->node(); if (origin_node.idname() != "SimulationNodeForce") { continue; } - const fn::MFInputSocket &force_socket = network_map.lookup_dummy( - origin_node.input(0, "Force")); - const ParticleFunction *particle_fn = create_particle_function_for_inputs( - {&force_socket}, resources, data_sources); + context, {&origin_node.input(0, "Force")}); if (particle_fn == nullptr) { continue; } - const ParticleForce &force = resources.construct<ParticleFunctionForce>(AT, *particle_fn); + const ParticleForce &force = context.resources.construct<ParticleFunctionForce>(AT, + *particle_fn); forces.append(&force); } - return forces; + + StringRef particle_name = get_identifier(context, simulation_node); + context.influences.particle_forces.add_multiple_as(particle_name, forces); +} + +static void collect_forces(CollectContext &context) +{ + for (const DNode *dnode : context.particle_simulation_nodes) { + create_forces_for_particle_simulation(context, *dnode); + } +} + +static ParticleEmitter *create_particle_emitter(CollectContext &context, const DNode &dnode) +{ + Array<StringRefNull> names = find_linked_particle_simulations(context, dnode.output(0)); + if (names.size() == 0) { + return nullptr; + } + + Array<const MFInputSocket *> input_sockets{2}; + for (int i : input_sockets.index_range()) { + input_sockets[i] = &context.network_map.lookup_dummy(dnode.input(i)); + } + + if (context.network.have_dummy_or_unlinked_dependencies(input_sockets)) { + return nullptr; + } + + MultiFunction &inputs_fn = context.resources.construct<MFNetworkEvaluator>( + AT, Span<const MFOutputSocket *>(), input_sockets.as_span()); + + const ParticleAction *birth_action = create_particle_action( + context, dnode.input(2, "Execute"), names); + + StringRefNull own_state_name = get_identifier(context, dnode); + context.required_states.add(own_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER); + ParticleEmitter &emitter = context.resources.construct<ParticleMeshEmitter>( + AT, own_state_name, names.as_span(), inputs_fn, birth_action); + return &emitter; +} + +static void collect_emitters(CollectContext &context) +{ + for (const DNode *dnode : nodes_by_type(context, "SimulationNodeParticleMeshEmitter")) { + ParticleEmitter *emitter = create_particle_emitter(context, *dnode); + if (emitter != nullptr) { + context.influences.particle_emitters.append(emitter); + } + } } -static void collect_forces(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - DummyDataSources &data_sources, - SimulationInfluences &r_influences) +static void collect_birth_events(CollectContext &context) { - for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { - std::string name = dnode_to_path(*dnode); - Vector<const ParticleForce *> forces = create_forces_for_particle_simulation( - *dnode, network_map, resources, data_sources); - r_influences.particle_forces.add_new(std::move(name), std::move(forces)); + for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleBirthEvent")) { + const DInputSocket &execute_input = event_dnode->input(0); + if (execute_input.linked_sockets().size() != 1) { + continue; + } + + Array<StringRefNull> particle_names = find_linked_particle_simulations(context, + event_dnode->output(0)); + + const DOutputSocket &execute_source = *execute_input.linked_sockets()[0]; + const ParticleAction *action = create_particle_action(context, execute_source, particle_names); + if (action == nullptr) { + continue; + } + + for (StringRefNull particle_name : particle_names) { + context.influences.particle_birth_actions.add_as(particle_name, action); + } + } +} + +static void collect_time_step_events(CollectContext &context) +{ + for (const DNode *event_dnode : nodes_by_type(context, "SimulationNodeParticleTimeStepEvent")) { + const DInputSocket &execute_input = event_dnode->input(0); + Array<StringRefNull> particle_names = find_linked_particle_simulations(context, + event_dnode->output(0)); + + const ParticleAction *action = create_particle_action(context, execute_input, particle_names); + if (action == nullptr) { + continue; + } + + NodeSimParticleTimeStepEventType type = + (NodeSimParticleTimeStepEventType)event_dnode->node_ref().bnode()->custom1; + if (type == NODE_PARTICLE_TIME_STEP_EVENT_BEGIN) { + for (StringRefNull particle_name : particle_names) { + context.influences.particle_time_step_begin_actions.add_as(particle_name, action); + } + } + else { + for (StringRefNull particle_name : particle_names) { + context.influences.particle_time_step_end_actions.add_as(particle_name, action); + } + } } } -class MyBasicEmitter : public ParticleEmitter { +class SequenceParticleAction : public ParticleAction { private: - Array<std::string> names_; - std::string my_state_; - const fn::MultiFunction &inputs_fn_; - uint32_t seed_; + Vector<const ParticleAction *> actions_; public: - MyBasicEmitter(Array<std::string> names, - std::string my_state, - const fn::MultiFunction &inputs_fn, - uint32_t seed) - : names_(std::move(names)), - my_state_(std::move(my_state)), - inputs_fn_(inputs_fn), - seed_(seed) + SequenceParticleAction(Span<const ParticleAction *> actions) : actions_(std::move(actions)) { } - void emit(ParticleEmitterContext &context) const override + void execute(ParticleActionContext &context) const override { - auto *state = context.solve_context().state_map().lookup<ParticleMeshEmitterSimulationState>( - my_state_); - if (state == nullptr) { - return; + for (const ParticleAction *action : actions_) { + action->execute(context); } + } +}; - fn::MFContextBuilder mf_context; - mf_context.add_global_context("PersistentDataHandleMap", - &context.solve_context().handle_map()); +class SetParticleAttributeAction : public ParticleAction { + private: + std::string attribute_name_; + const CPPType &cpp_type_; + const ParticleFunction &inputs_fn_; - fn::MFParamsBuilder mf_params{inputs_fn_, 1}; - bke::PersistentObjectHandle object_handle; - float rate; - mf_params.add_uninitialized_single_output(&object_handle); - mf_params.add_uninitialized_single_output(&rate); - inputs_fn_.call(IndexRange(1), mf_params, mf_context); + public: + SetParticleAttributeAction(std::string attribute_name, + const CPPType &cpp_type, + const ParticleFunction &inputs_fn) + : attribute_name_(std::move(attribute_name)), cpp_type_(cpp_type), inputs_fn_(inputs_fn) + { + } - const Object *object = context.solve_context().handle_map().lookup(object_handle); - if (object == nullptr) { + void execute(ParticleActionContext &context) const override + { + std::optional<GMutableSpan> attribute_array = context.particles.attributes.try_get( + attribute_name_, cpp_type_); + if (!attribute_array.has_value()) { return; } - Vector<float3> new_positions; - Vector<float3> new_velocities; - Vector<float> new_birth_times; - - TimeInterval time_interval = context.simulation_time_interval(); - float start_time = time_interval.start(); - RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed_}; + ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles}; + evaluator.compute(); + GVSpan values = evaluator.get(0); - const float time_between_particles = 1.0f / rate; - while (state->last_birth_time + time_between_particles < time_interval.end()) { - new_positions.append(rng.get_unit_float3() * 0.3 + float3(object->loc)); - new_velocities.append(rng.get_unit_float3()); - const float birth_time = state->last_birth_time + time_between_particles; - new_birth_times.append(birth_time); - state->last_birth_time = birth_time; + if (values.is_single_element()) { + cpp_type_.fill_initialized_indices( + values.as_single_element(), attribute_array->data(), context.particles.index_mask); + } + else { + GSpan value_array = values.as_full_array(); + cpp_type_.copy_to_initialized_indices( + value_array.data(), attribute_array->data(), context.particles.index_mask); } - for (StringRef name : names_) { - ParticleAllocator *allocator = context.try_get_particle_allocator(name); - if (allocator == nullptr) { - return; - } - - int amount = new_positions.size(); - fn::MutableAttributesRef attributes = allocator->allocate(amount); - - initialized_copy_n(new_positions.data(), amount, attributes.get<float3>("Position").data()); - initialized_copy_n(new_velocities.data(), amount, attributes.get<float3>("Velocity").data()); - initialized_copy_n( - new_birth_times.data(), amount, attributes.get<float>("Birth Time").data()); + if (attribute_name_ == "Velocity") { + context.particles.update_diffs_after_velocity_change(); } } }; -static Vector<const nodes::DNode *> find_linked_particle_simulations( - const nodes::DOutputSocket &output_socket) +static const ParticleAction *concatenate_actions(CollectContext &context, + Span<const ParticleAction *> actions) { - Vector<const nodes::DNode *> simulation_nodes; - for (const nodes::DInputSocket *target_socket : output_socket.linked_sockets()) { - if (target_socket->node().idname() == "SimulationNodeParticleSimulation") { - simulation_nodes.append(&target_socket->node()); + Vector<const ParticleAction *> non_null_actions; + for (const ParticleAction *action : actions) { + if (action != nullptr) { + non_null_actions.append(action); } } - return simulation_nodes; + if (non_null_actions.size() == 0) { + return nullptr; + } + if (non_null_actions.size() == 1) { + return non_null_actions[0]; + } + return &context.resources.construct<SequenceParticleAction>(AT, std::move(non_null_actions)); } -static ParticleEmitter *create_particle_emitter(const nodes::DNode &dnode, - ResourceCollector &resources, - nodes::MFNetworkTreeMap &network_map, - RequiredStates &r_required_states) +static const ParticleAction *create_set_particle_attribute_action( + CollectContext &context, const DOutputSocket &dsocket, Span<StringRefNull> particle_names) { - Vector<const nodes::DNode *> simulation_dnodes = find_linked_particle_simulations( - dnode.output(0)); - if (simulation_dnodes.size() == 0) { - return nullptr; + const DNode &dnode = dsocket.node(); + + const ParticleAction *previous_action = create_particle_action( + context, dnode.input(0), particle_names); + + MFInputSocket &name_socket = context.network_map.lookup_dummy(dnode.input(1)); + MFInputSocket &value_socket = name_socket.node().input(1); + std::optional<Array<std::string>> names = compute_global_string_inputs(context.network_map, + {&name_socket}); + if (!names.has_value()) { + return previous_action; } - Array<std::string> names{simulation_dnodes.size()}; - for (int i : simulation_dnodes.index_range()) { - names[i] = dnode_to_path(*simulation_dnodes[i]); + std::string attribute_name = (*names)[0]; + if (attribute_name.empty()) { + return previous_action; } + const CPPType &attribute_type = value_socket.data_type().single_type(); - Array<const fn::MFInputSocket *> input_sockets{dnode.inputs().size()}; - for (int i : input_sockets.index_range()) { - input_sockets[i] = &network_map.lookup_dummy(dnode.input(i)); + const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context, + {&value_socket}); + if (inputs_fn == nullptr) { + return previous_action; } - if (network_map.network().have_dummy_or_unlinked_dependencies(input_sockets)) { - return nullptr; + for (StringRef particle_name : particle_names) { + context.influences.particle_attributes_builder.lookup_as(particle_name) + ->add(attribute_name, attribute_type); } - fn::MultiFunction &inputs_fn = resources.construct<fn::MFNetworkEvaluator>( - AT, Span<const fn::MFOutputSocket *>(), input_sockets.as_span()); + ParticleAction &this_action = context.resources.construct<SetParticleAttributeAction>( + AT, attribute_name, attribute_type, *inputs_fn); - std::string my_state_name = dnode_to_path(dnode); - r_required_states.add(my_state_name, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER); - uint32_t seed = DefaultHash<std::string>{}(my_state_name); - ParticleEmitter &emitter = resources.construct<MyBasicEmitter>( - AT, std::move(names), std::move(my_state_name), inputs_fn, seed); - return &emitter; + return concatenate_actions(context, {previous_action, &this_action}); } -static void collect_emitters(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - SimulationInfluences &r_influences, - RequiredStates &r_required_states) +class ParticleConditionAction : public ParticleAction { + private: + const ParticleFunction &inputs_fn_; + const ParticleAction *action_true_; + const ParticleAction *action_false_; + + public: + ParticleConditionAction(const ParticleFunction &inputs_fn, + const ParticleAction *action_true, + const ParticleAction *action_false) + : inputs_fn_(inputs_fn), action_true_(action_true), action_false_(action_false) + { + } + + void execute(ParticleActionContext &context) const override + { + ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles}; + evaluator.compute(); + VSpan<bool> conditions = evaluator.get<bool>(0, "Condition"); + + if (conditions.is_single_element()) { + const bool condition = conditions.as_single_element(); + if (condition) { + if (action_true_ != nullptr) { + action_true_->execute(context); + } + } + else { + if (action_false_ != nullptr) { + action_false_->execute(context); + } + } + } + else { + Span<bool> conditions_array = conditions.as_full_array(); + + Vector<int64_t> true_indices; + Vector<int64_t> false_indices; + for (int i : context.particles.index_mask) { + if (conditions_array[i]) { + true_indices.append(i); + } + else { + false_indices.append(i); + } + } + + if (action_true_ != nullptr) { + ParticleChunkContext chunk_context{context.particles.state, + true_indices.as_span(), + context.particles.attributes, + context.particles.integration}; + ParticleActionContext action_context{context.solve_context, chunk_context}; + action_true_->execute(action_context); + } + if (action_false_ != nullptr) { + ParticleChunkContext chunk_context{context.particles.state, + false_indices.as_span(), + context.particles.attributes, + context.particles.integration}; + ParticleActionContext action_context{context.solve_context, chunk_context}; + action_false_->execute(action_context); + } + } + } +}; + +static const ParticleAction *create_particle_condition_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names) { - for (const nodes::DNode *dnode : - network_map.tree().nodes_by_type("SimulationNodeParticleMeshEmitter")) { - ParticleEmitter *emitter = create_particle_emitter( - *dnode, resources, network_map, r_required_states); - if (emitter != nullptr) { - r_influences.particle_emitters.append(emitter); + const DNode &dnode = dsocket.node(); + + const ParticleFunction *inputs_fn = create_particle_function_for_inputs( + context, {&dnode.input(0, "Condition")}); + if (inputs_fn == nullptr) { + return nullptr; + } + + const ParticleAction *true_action = create_particle_action( + context, dnode.input(1), particle_names); + const ParticleAction *false_action = create_particle_action( + context, dnode.input(2), particle_names); + + if (true_action == nullptr && false_action == nullptr) { + return nullptr; + } + return &context.resources.construct<ParticleConditionAction>( + AT, *inputs_fn, true_action, false_action); +} + +class KillParticleAction : public ParticleAction { + public: + void execute(ParticleActionContext &context) const override + { + MutableSpan<int> dead_states = context.particles.attributes.get<int>("Dead"); + for (int i : context.particles.index_mask) { + dead_states[i] = true; } } +}; + +static const ParticleAction *create_particle_action(CollectContext &context, + const DOutputSocket &dsocket, + Span<StringRefNull> particle_names) +{ + const DNode &dnode = dsocket.node(); + StringRef idname = dnode.idname(); + if (idname == "SimulationNodeSetParticleAttribute") { + return create_set_particle_attribute_action(context, dsocket, particle_names); + } + if (idname == "SimulationNodeExecuteCondition") { + return create_particle_condition_action(context, dsocket, particle_names); + } + if (idname == "SimulationNodeKillParticle") { + return &context.resources.construct<KillParticleAction>(AT); + } + return nullptr; } -static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network_map, - ResourceCollector &resources, - SimulationInfluences &r_influences) +static void initialize_particle_attribute_builders(CollectContext &context) { - for (const nodes::DNode *dnode : get_particle_simulation_nodes(network_map.tree())) { - std::string name = dnode_to_path(*dnode); - fn::AttributesInfoBuilder &builder = resources.construct<fn::AttributesInfoBuilder>(AT); - builder.add<float3>("Position", {0, 0, 0}); - builder.add<float3>("Velocity", {0, 0, 0}); - builder.add<int>("ID", 0); + for (const DNode *dnode : context.particle_simulation_nodes) { + StringRef name = get_identifier(context, *dnode); + AttributesInfoBuilder &attributes_builder = context.resources.construct<AttributesInfoBuilder>( + AT); + attributes_builder.add<float3>("Position", {0, 0, 0}); + attributes_builder.add<float3>("Velocity", {0, 0, 0}); + attributes_builder.add<int>("ID", 0); /* TODO: Use bool property, but need to add CD_PROP_BOOL first. */ - builder.add<int>("Dead", 0); - builder.add<float>("Birth Time", 0.0f); - r_influences.particle_attributes_builder.add_new(std::move(name), &builder); + attributes_builder.add<int>("Dead", 0); + /* TODO: Use uint32_t, but we don't have a corresponding custom property type. */ + attributes_builder.add<int>("Hash", 0); + attributes_builder.add<float>("Birth Time", 0.0f); + attributes_builder.add<float>("Radius", 0.02f); + context.influences.particle_attributes_builder.add_new(name, &attributes_builder); } } -static void find_used_data_blocks(const nodes::DerivedNodeTree &tree, - SimulationInfluences &r_influences) +static void optimize_function_network(CollectContext &context) +{ + fn::mf_network_optimization::constant_folding(context.network, context.resources); + fn::mf_network_optimization::common_subnetwork_elimination(context.network); + fn::mf_network_optimization::dead_node_removal(context.network); + // WM_clipboard_text_set(network.to_dot().c_str(), false); +} + +class AgeReachedEvent : public ParticleEvent { + private: + std::string attribute_name_; + const ParticleFunction &inputs_fn_; + const ParticleAction &action_; + + public: + AgeReachedEvent(std::string attribute_name, + const ParticleFunction &inputs_fn, + const ParticleAction &action) + : attribute_name_(std::move(attribute_name)), inputs_fn_(inputs_fn), action_(action) + { + } + + void filter(ParticleEventFilterContext &context) const override + { + Span<float> birth_times = context.particles.attributes.get<float>("Birth Time"); + std::optional<Span<int>> has_been_triggered = context.particles.attributes.try_get<int>( + attribute_name_); + if (!has_been_triggered.has_value()) { + return; + } + + ParticleFunctionEvaluator evaluator{inputs_fn_, context.solve_context, context.particles}; + evaluator.compute(); + VSpan<float> trigger_ages = evaluator.get<float>(0, "Age"); + + const float end_time = context.particles.integration->end_time; + for (int i : context.particles.index_mask) { + if ((*has_been_triggered)[i]) { + continue; + } + const float trigger_age = trigger_ages[i]; + const float birth_time = birth_times[i]; + const float trigger_time = birth_time + trigger_age; + if (trigger_time > end_time) { + continue; + } + + const float duration = context.particles.integration->durations[i]; + TimeInterval interval(end_time - duration, duration); + const float time_factor = interval.safe_factor_at_time(trigger_time); + + context.factor_dst[i] = std::max<float>(0.0f, time_factor); + } + } + + void execute(ParticleActionContext &context) const override + { + MutableSpan<int> has_been_triggered = context.particles.attributes.get<int>(attribute_name_); + for (int i : context.particles.index_mask) { + has_been_triggered[i] = 1; + } + action_.execute(context); + } +}; + +static void collect_age_reached_events(CollectContext &context) { - const bNodeSocketType *socktype = nodeSocketTypeFind("NodeSocketObject"); - BLI_assert(socktype != nullptr); - - for (const nodes::DInputSocket *dsocket : tree.input_sockets()) { - const bNodeSocket *bsocket = dsocket->bsocket(); - if (bsocket->typeinfo == socktype) { - Object *value = ((const bNodeSocketValueObject *)bsocket->default_value)->value; - if (value != nullptr) { - r_influences.used_data_blocks.add(&value->id); + for (const DNode *dnode : nodes_by_type(context, "SimulationNodeAgeReachedEvent")) { + const DInputSocket &age_input = dnode->input(0, "Age"); + const DInputSocket &execute_input = dnode->input(1, "Execute"); + Array<StringRefNull> particle_names = find_linked_particle_simulations(context, + dnode->output(0)); + const ParticleAction *action = create_particle_action(context, execute_input, particle_names); + if (action == nullptr) { + continue; + } + const ParticleFunction *inputs_fn = create_particle_function_for_inputs(context, {&age_input}); + if (inputs_fn == nullptr) { + continue; + } + + std::string attribute_name = get_identifier(context, *dnode); + const ParticleEvent &event = context.resources.construct<AgeReachedEvent>( + AT, attribute_name, *inputs_fn, *action); + for (StringRefNull particle_name : particle_names) { + const bool added_attribute = context.influences.particle_attributes_builder + .lookup_as(particle_name) + ->add<int>(attribute_name, 0); + if (added_attribute) { + context.influences.particle_events.add_as(particle_name, &event); } } } @@ -456,30 +879,29 @@ void collect_simulation_influences(Simulation &simulation, SimulationInfluences &r_influences, RequiredStates &r_required_states) { - nodes::NodeTreeRefMap tree_refs; - const nodes::DerivedNodeTree tree{simulation.nodetree, tree_refs}; + NodeTreeRefMap tree_refs; + const DerivedNodeTree tree{simulation.nodetree, tree_refs}; - fn::MFNetwork &network = resources.construct<fn::MFNetwork>(AT); - nodes::MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); + MFNetwork &network = resources.construct<MFNetwork>(AT); + MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); - prepare_particle_attribute_builders(network_map, resources, r_influences); + CollectContext context{r_influences, r_required_states, resources, network_map}; + initialize_particle_attribute_builders(context); - DummyDataSources data_sources; - find_and_deduplicate_particle_attribute_nodes(network_map, data_sources); + prepare_particle_attribute_nodes(context); + prepare_time_input_nodes(context); - fn::mf_network_optimization::constant_folding(network, resources); - fn::mf_network_optimization::common_subnetwork_elimination(network); - fn::mf_network_optimization::dead_node_removal(network); - // WM_clipboard_text_set(network.to_dot().c_str(), false); + collect_forces(context); + collect_emitters(context); + collect_birth_events(context); + collect_time_step_events(context); + collect_age_reached_events(context); - collect_forces(network_map, resources, data_sources, r_influences); - collect_emitters(network_map, resources, r_influences, r_required_states); + optimize_function_network(context); - for (const nodes::DNode *dnode : get_particle_simulation_nodes(tree)) { - r_required_states.add(dnode_to_path(*dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION); + for (const DNode *dnode : context.particle_simulation_nodes) { + r_required_states.add(get_identifier(context, *dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION); } - - find_used_data_blocks(tree, r_influences); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_collect_influences.hh b/source/blender/simulation/intern/simulation_collect_influences.hh index 42cbea6977e..caf5a8c4ffa 100644 --- a/source/blender/simulation/intern/simulation_collect_influences.hh +++ b/source/blender/simulation/intern/simulation_collect_influences.hh @@ -21,7 +21,7 @@ #include "BLI_resource_collector.hh" -#include "simulation_solver.hh" +#include "simulation_solver_influences.hh" namespace blender::sim { diff --git a/source/blender/simulation/intern/simulation_solver.cc b/source/blender/simulation/intern/simulation_solver.cc index ee7a8d40035..d53ccd2bd49 100644 --- a/source/blender/simulation/intern/simulation_solver.cc +++ b/source/blender/simulation/intern/simulation_solver.cc @@ -17,23 +17,16 @@ #include "simulation_solver.hh" #include "BKE_customdata.h" -#include "BKE_lib_id.h" #include "BKE_persistent_data_handle.hh" #include "BLI_rand.hh" #include "BLI_set.hh" -namespace blender::sim { - -ParticleForce::~ParticleForce() -{ -} +#include "DEG_depsgraph_query.h" -ParticleEmitter::~ParticleEmitter() -{ -} +namespace blender::sim { -static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) +static CustomDataType cpp_to_custom_data_type(const CPPType &type) { if (type.is<float3>()) { return CD_PROP_FLOAT3; @@ -48,18 +41,18 @@ static CustomDataType cpp_to_custom_data_type(const fn::CPPType &type) return CD_PROP_FLOAT; } -static const fn::CPPType &custom_to_cpp_data_type(CustomDataType type) +static const CPPType &custom_to_cpp_data_type(CustomDataType type) { switch (type) { case CD_PROP_FLOAT3: - return fn::CPPType::get<float3>(); + return CPPType::get<float3>(); case CD_PROP_FLOAT: - return fn::CPPType::get<float>(); + return CPPType::get<float>(); case CD_PROP_INT32: - return fn::CPPType::get<int32_t>(); + return CPPType::get<int32_t>(); default: BLI_assert(false); - return fn::CPPType::get<float>(); + return CPPType::get<float>(); } } @@ -67,33 +60,33 @@ class CustomDataAttributesRef { private: Array<void *> buffers_; int64_t size_; - const fn::AttributesInfo &info_; + const AttributesInfo &info_; public: - CustomDataAttributesRef(CustomData &custom_data, int64_t size, const fn::AttributesInfo &info) + CustomDataAttributesRef(CustomData &custom_data, int64_t size, const AttributesInfo &info) : buffers_(info.size(), nullptr), size_(size), info_(info) { for (int attribute_index : info.index_range()) { StringRefNull name = info.name_of(attribute_index); - const fn::CPPType &cpp_type = info.type_of(attribute_index); + const CPPType &cpp_type = info.type_of(attribute_index); CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); void *data = CustomData_get_layer_named(&custom_data, custom_type, name.c_str()); buffers_[attribute_index] = data; } } - operator fn::MutableAttributesRef() + operator MutableAttributesRef() { - return fn::MutableAttributesRef(info_, buffers_, size_); + return MutableAttributesRef(info_, buffers_, size_); } - operator fn::AttributesRef() const + operator AttributesRef() const { - return fn::AttributesRef(info_, buffers_, size_); + return AttributesRef(info_, buffers_, size_); } }; -static void ensure_attributes_exist(ParticleSimulationState *state, const fn::AttributesInfo &info) +static void ensure_attributes_exist(ParticleSimulationState *state, const AttributesInfo &info) { bool found_layer_to_remove; do { @@ -101,7 +94,7 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At for (int layer_index = 0; layer_index < state->attributes.totlayer; layer_index++) { CustomDataLayer *layer = &state->attributes.layers[layer_index]; BLI_assert(layer->name != nullptr); - const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); + const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer->type); StringRefNull name = layer->name; if (!info.has_attribute(name, cpp_type)) { found_layer_to_remove = true; @@ -113,7 +106,7 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At for (int attribute_index : info.index_range()) { StringRefNull attribute_name = info.name_of(attribute_index); - const fn::CPPType &cpp_type = info.type_of(attribute_index); + const CPPType &cpp_type = info.type_of(attribute_index); CustomDataType custom_type = cpp_to_custom_data_type(cpp_type); if (CustomData_get_layer_named(&state->attributes, custom_type, attribute_name.c_str()) == nullptr) { @@ -128,50 +121,241 @@ static void ensure_attributes_exist(ParticleSimulationState *state, const fn::At } } -BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context, - ParticleSimulationState &state, - const fn::AttributesInfo &attributes_info) +BLI_NOINLINE static void apply_remaining_diffs(ParticleChunkContext &context) { - CustomDataAttributesRef custom_data_attributes{ - state.attributes, state.tot_particles, attributes_info}; - fn::MutableAttributesRef attributes = custom_data_attributes; + BLI_assert(context.integration != nullptr); + MutableSpan<float3> positions = context.attributes.get<float3>("Position"); + MutableSpan<float3> velocities = context.attributes.get<float3>("Velocity"); + + for (int i : context.index_mask) { + positions[i] += context.integration->position_diffs[i]; + velocities[i] += context.integration->velocity_diffs[i]; + } +} - Array<float3> force_vectors{state.tot_particles, {0, 0, 0}}; - const Vector<const ParticleForce *> *forces = - solve_context.influences().particle_forces.lookup_ptr(state.head.name); +BLI_NOINLINE static void find_next_event_per_particle( + SimulationSolveContext &solve_context, + ParticleChunkContext &particles, + Span<const ParticleEvent *> events, + MutableSpan<int> r_next_event_indices, + MutableSpan<float> r_time_factors_to_next_event) +{ + r_next_event_indices.fill_indices(particles.index_mask, -1); + r_time_factors_to_next_event.fill_indices(particles.index_mask, 1.0f); + + Array<float> time_factors(particles.index_mask.min_array_size()); + for (int event_index : events.index_range()) { + time_factors.fill(-1.0f); + ParticleEventFilterContext event_context{solve_context, particles, time_factors}; + const ParticleEvent &event = *events[event_index]; + event.filter(event_context); + + for (int i : particles.index_mask) { + const float time_factor = time_factors[i]; + const float previously_smallest_time_factor = r_time_factors_to_next_event[i]; + if (time_factor >= 0.0f && time_factor <= previously_smallest_time_factor) { + r_time_factors_to_next_event[i] = time_factor; + r_next_event_indices[i] = event_index; + } + } + } +} - if (forces != nullptr) { - ParticleChunkContext particle_chunk_context{IndexMask(state.tot_particles), attributes}; - ParticleForceContext particle_force_context{ - solve_context, particle_chunk_context, force_vectors}; +BLI_NOINLINE static void forward_particles_to_next_event_or_end( + ParticleChunkContext &particles, Span<float> time_factors_to_next_event) +{ + MutableSpan<float3> positions = particles.attributes.get<float3>("Position"); + MutableSpan<float3> velocities = particles.attributes.get<float3>("Velocity"); + + MutableSpan<float3> position_diffs = particles.integration->position_diffs; + MutableSpan<float3> velocity_diffs = particles.integration->velocity_diffs; + MutableSpan<float> durations = particles.integration->durations; + + for (int i : particles.index_mask) { + const float time_factor = time_factors_to_next_event[i]; + positions[i] += position_diffs[i] * time_factor; + velocities[i] += velocity_diffs[i] * time_factor; + + const float remaining_time_factor = 1.0f - time_factor; + position_diffs[i] *= remaining_time_factor; + velocity_diffs[i] *= remaining_time_factor; + durations[i] *= remaining_time_factor; + } +} - for (const ParticleForce *force : *forces) { - force->add_force(particle_force_context); +BLI_NOINLINE static void group_particles_by_event( + IndexMask mask, + Span<int> next_event_indices, + MutableSpan<Vector<int64_t>> r_particles_per_event) +{ + for (int i : mask) { + int event_index = next_event_indices[i]; + if (event_index >= 0) { + r_particles_per_event[event_index].append(i); } } +} - MutableSpan<float3> positions = attributes.get<float3>("Position"); - MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); - MutableSpan<float> birth_times = attributes.get<float>("Birth Time"); - MutableSpan<int> dead_states = attributes.get<int>("Dead"); - float end_time = solve_context.solve_interval().end(); - float time_step = solve_context.solve_interval().duration(); - for (int i : positions.index_range()) { - velocities[i] += force_vectors[i] * time_step; - positions[i] += velocities[i] * time_step; - - if (end_time - birth_times[i] > 2) { - dead_states[i] = true; +BLI_NOINLINE static void execute_events(SimulationSolveContext &solve_context, + ParticleChunkContext &all_particles, + Span<const ParticleEvent *> events, + Span<Vector<int64_t>> particles_per_event) +{ + for (int event_index : events.index_range()) { + Span<int64_t> pindices = particles_per_event[event_index]; + if (pindices.is_empty()) { + continue; + } + + const ParticleEvent &event = *events[event_index]; + ParticleChunkContext particles{ + all_particles.state, pindices, all_particles.attributes, all_particles.integration}; + ParticleActionContext action_context{solve_context, particles}; + event.execute(action_context); + } +} + +BLI_NOINLINE static void find_unfinished_particles(IndexMask index_mask, + Span<float> time_factors_to_next_event, + Vector<int64_t> &r_unfinished_pindices) +{ + for (int i : index_mask) { + float time_factor = time_factors_to_next_event[i]; + if (time_factor < 1.0f) { + r_unfinished_pindices.append(i); + } + } +} + +BLI_NOINLINE static void simulate_to_next_event(SimulationSolveContext &solve_context, + ParticleChunkContext &particles, + Span<const ParticleEvent *> events, + Vector<int64_t> &r_unfinished_pindices) +{ + int array_size = particles.index_mask.min_array_size(); + Array<int> next_event_indices(array_size); + Array<float> time_factors_to_next_event(array_size); + + find_next_event_per_particle( + solve_context, particles, events, next_event_indices, time_factors_to_next_event); + + forward_particles_to_next_event_or_end(particles, time_factors_to_next_event); + + Array<Vector<int64_t>> particles_per_event(events.size()); + group_particles_by_event(particles.index_mask, next_event_indices, particles_per_event); + + execute_events(solve_context, particles, events, particles_per_event); + find_unfinished_particles( + particles.index_mask, time_factors_to_next_event, r_unfinished_pindices); +} + +BLI_NOINLINE static void simulate_with_max_n_events(SimulationSolveContext &solve_context, + ParticleSimulationState &state, + ParticleChunkContext &particles, + int max_events) +{ + Span<const ParticleEvent *> events = solve_context.influences.particle_events.lookup_as( + state.head.name); + if (events.size() == 0) { + apply_remaining_diffs(particles); + return; + } + + Vector<int64_t> unfininished_pindices = particles.index_mask.indices(); + for (int iteration : IndexRange(max_events)) { + UNUSED_VARS(iteration); + if (unfininished_pindices.is_empty()) { + break; } + + Vector<int64_t> new_unfinished_pindices; + ParticleChunkContext remaining_particles{particles.state, + unfininished_pindices.as_span(), + particles.attributes, + particles.integration}; + simulate_to_next_event(solve_context, remaining_particles, events, new_unfinished_pindices); + unfininished_pindices = std::move(new_unfinished_pindices); + } + + if (!unfininished_pindices.is_empty()) { + ParticleChunkContext remaining_particles{particles.state, + unfininished_pindices.as_span(), + particles.attributes, + particles.integration}; + apply_remaining_diffs(remaining_particles); } } +BLI_NOINLINE static void simulate_particle_chunk(SimulationSolveContext &solve_context, + ParticleSimulationState &state, + MutableAttributesRef attributes, + MutableSpan<float> remaining_durations, + float end_time) +{ + int particle_amount = attributes.size(); + + Span<const ParticleAction *> begin_actions = + solve_context.influences.particle_time_step_begin_actions.lookup_as(state.head.name); + for (const ParticleAction *action : begin_actions) { + ParticleChunkContext particles{state, IndexMask(particle_amount), attributes}; + ParticleActionContext action_context{solve_context, particles}; + action->execute(action_context); + } + + Array<float3> force_vectors{particle_amount, {0, 0, 0}}; + Span<const ParticleForce *> forces = solve_context.influences.particle_forces.lookup_as( + state.head.name); + for (const ParticleForce *force : forces) { + ParticleChunkContext particles{state, IndexMask(particle_amount), attributes}; + ParticleForceContext particle_force_context{solve_context, particles, force_vectors}; + force->add_force(particle_force_context); + } + + MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); + + Array<float3> position_diffs(particle_amount); + Array<float3> velocity_diffs(particle_amount); + for (int i : IndexRange(particle_amount)) { + const float time_step = remaining_durations[i]; + velocity_diffs[i] = force_vectors[i] * time_step; + position_diffs[i] = (velocities[i] + velocity_diffs[i] / 2.0f) * time_step; + } + + ParticleChunkIntegrationContext integration_context = { + position_diffs, velocity_diffs, remaining_durations, end_time}; + ParticleChunkContext particle_chunk_context{ + state, IndexMask(particle_amount), attributes, &integration_context}; + + simulate_with_max_n_events(solve_context, state, particle_chunk_context, 10); + + Span<const ParticleAction *> end_actions = + solve_context.influences.particle_time_step_end_actions.lookup_as(state.head.name); + for (const ParticleAction *action : end_actions) { + ParticleChunkContext particles{state, IndexMask(particle_amount), attributes}; + ParticleActionContext action_context{solve_context, particles}; + action->execute(action_context); + } +} + +BLI_NOINLINE static void simulate_existing_particles(SimulationSolveContext &solve_context, + ParticleSimulationState &state, + const AttributesInfo &attributes_info) +{ + CustomDataAttributesRef custom_data_attributes{ + state.attributes, state.tot_particles, attributes_info}; + MutableAttributesRef attributes = custom_data_attributes; + + Array<float> remaining_durations(state.tot_particles, solve_context.solve_interval.duration()); + simulate_particle_chunk( + solve_context, state, attributes, remaining_durations, solve_context.solve_interval.stop()); +} + BLI_NOINLINE static void run_emitters(SimulationSolveContext &solve_context, ParticleAllocators &particle_allocators) { - for (const ParticleEmitter *emitter : solve_context.influences().particle_emitters) { + for (const ParticleEmitter *emitter : solve_context.influences.particle_emitters) { ParticleEmitterContext emitter_context{ - solve_context, particle_allocators, solve_context.solve_interval()}; + solve_context, particle_allocators, solve_context.solve_interval}; emitter->emit(emitter_context); } } @@ -181,10 +365,10 @@ BLI_NOINLINE static int count_particles_after_time_step(ParticleSimulationState { CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, allocator.attributes_info()}; - fn::MutableAttributesRef attributes = custom_data_attributes; + MutableAttributesRef attributes = custom_data_attributes; int new_particle_amount = attributes.get<int>("Dead").count(0); - for (fn::MutableAttributesRef emitted_attributes : allocator.get_allocations()) { + for (MutableAttributesRef emitted_attributes : allocator.get_allocations()) { new_particle_amount += emitted_attributes.get<int>("Dead").count(0); } @@ -199,7 +383,7 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta CustomDataAttributesRef custom_data_attributes{ state.attributes, state.tot_particles, allocator.attributes_info()}; - Vector<fn::MutableAttributesRef> particle_sources; + Vector<MutableAttributesRef> particle_sources; particle_sources.append(custom_data_attributes); particle_sources.extend(allocator.get_allocations()); @@ -211,16 +395,16 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta dead_layer = &layer; continue; } - const fn::CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type); - fn::GMutableSpan new_buffer{ + const CPPType &cpp_type = custom_to_cpp_data_type((CustomDataType)layer.type); + GMutableSpan new_buffer{ cpp_type, MEM_mallocN_aligned(new_particle_amount * cpp_type.size(), cpp_type.alignment(), AT), new_particle_amount}; int current = 0; - for (fn::MutableAttributesRef attributes : particle_sources) { + for (MutableAttributesRef attributes : particle_sources) { Span<int> dead_states = attributes.get<int>("Dead"); - fn::GSpan source_buffer = attributes.get(name); + GSpan source_buffer = attributes.get(name); BLI_assert(source_buffer.type() == cpp_type); for (int i : attributes.index_range()) { if (dead_states[i] == 0) { @@ -233,7 +417,7 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta if (layer.data != nullptr) { MEM_freeN(layer.data); } - layer.data = new_buffer.buffer(); + layer.data = new_buffer.data(); } BLI_assert(dead_layer != nullptr); @@ -246,56 +430,10 @@ BLI_NOINLINE static void remove_dead_and_add_new_particles(ParticleSimulationSta state.next_particle_id += allocator.total_allocated(); } -static void update_persistent_data_handles(Simulation &simulation, - const VectorSet<ID *> &used_data_blocks) -{ - Set<ID *> contained_ids; - Set<int> used_handles; - - /* Remove handles that have been invalidated. */ - LISTBASE_FOREACH_MUTABLE ( - PersistentDataHandleItem *, handle_item, &simulation.persistent_data_handles) { - if (handle_item->id == nullptr) { - BLI_remlink(&simulation.persistent_data_handles, handle_item); - continue; - } - if (!used_data_blocks.contains(handle_item->id)) { - id_us_min(handle_item->id); - BLI_remlink(&simulation.persistent_data_handles, handle_item); - MEM_freeN(handle_item); - continue; - } - contained_ids.add_new(handle_item->id); - used_handles.add_new(handle_item->handle); - } - - /* Add new handles that are not in the list yet. */ - int next_handle = 0; - for (ID *id : used_data_blocks) { - if (contained_ids.contains(id)) { - continue; - } - - /* Find the next available handle. */ - while (used_handles.contains(next_handle)) { - next_handle++; - } - used_handles.add_new(next_handle); - - PersistentDataHandleItem *handle_item = (PersistentDataHandleItem *)MEM_callocN( - sizeof(*handle_item), AT); - /* Cannot store const pointers in DNA. */ - id_us_plus(id); - handle_item->id = id; - handle_item->handle = next_handle; - - BLI_addtail(&simulation.persistent_data_handles, handle_item); - } -} - void initialize_simulation_states(Simulation &simulation, Depsgraph &UNUSED(depsgraph), - const SimulationInfluences &UNUSED(influences)) + const SimulationInfluences &UNUSED(influences), + const bke::PersistentDataHandleMap &UNUSED(handle_map)) { simulation.current_simulation_time = 0.0f; } @@ -303,15 +441,10 @@ void initialize_simulation_states(Simulation &simulation, void solve_simulation_time_step(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map, + const DependencyAnimations &dependency_animations, float time_step) { - update_persistent_data_handles(simulation, influences.used_data_blocks); - - bke::PersistentDataHandleMap handle_map; - LISTBASE_FOREACH (PersistentDataHandleItem *, handle, &simulation.persistent_data_handles) { - handle_map.add(handle->handle, *handle->id); - } - SimulationStateMap state_map; LISTBASE_FOREACH (SimulationState *, state, &simulation.states) { state_map.add(state); @@ -322,30 +455,32 @@ void solve_simulation_time_step(Simulation &simulation, influences, TimeInterval(simulation.current_simulation_time, time_step), state_map, - handle_map}; - TimeInterval simulation_time_interval{simulation.current_simulation_time, time_step}; + handle_map, + dependency_animations}; Span<ParticleSimulationState *> particle_simulation_states = state_map.lookup<ParticleSimulationState>(); - Map<std::string, std::unique_ptr<fn::AttributesInfo>> attribute_infos; + Map<std::string, std::unique_ptr<AttributesInfo>> attribute_infos; Map<std::string, std::unique_ptr<ParticleAllocator>> particle_allocators_map; for (ParticleSimulationState *state : particle_simulation_states) { - const fn::AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( + const AttributesInfoBuilder &builder = *influences.particle_attributes_builder.lookup_as( state->head.name); - auto info = std::make_unique<fn::AttributesInfo>(builder); + auto info = std::make_unique<AttributesInfo>(builder); ensure_attributes_exist(state, *info); + uint32_t hash_seed = DefaultHash<StringRef>{}(state->head.name); particle_allocators_map.add_new( - state->head.name, std::make_unique<ParticleAllocator>(*info, state->next_particle_id)); + state->head.name, + std::make_unique<ParticleAllocator>(*info, state->next_particle_id, hash_seed)); attribute_infos.add_new(state->head.name, std::move(info)); } ParticleAllocators particle_allocators{particle_allocators_map}; for (ParticleSimulationState *state : particle_simulation_states) { - const fn::AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); + const AttributesInfo &attributes_info = *attribute_infos.lookup_as(state->head.name); simulate_existing_particles(solve_context, *state, attributes_info); } @@ -353,10 +488,35 @@ void solve_simulation_time_step(Simulation &simulation, for (ParticleSimulationState *state : particle_simulation_states) { ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name); + + for (MutableAttributesRef attributes : allocator.get_allocations()) { + Span<const ParticleAction *> actions = influences.particle_birth_actions.lookup_as( + state->head.name); + for (const ParticleAction *action : actions) { + ParticleChunkContext chunk_context{*state, IndexRange(attributes.size()), attributes}; + ParticleActionContext action_context{solve_context, chunk_context}; + action->execute(action_context); + } + } + } + + for (ParticleSimulationState *state : particle_simulation_states) { + ParticleAllocator &allocator = *particle_allocators.try_get_allocator(state->head.name); + + for (MutableAttributesRef attributes : allocator.get_allocations()) { + Array<float> remaining_durations(attributes.size()); + Span<float> birth_times = attributes.get<float>("Birth Time"); + const float end_time = solve_context.solve_interval.stop(); + for (int i : attributes.index_range()) { + remaining_durations[i] = end_time - birth_times[i]; + } + simulate_particle_chunk(solve_context, *state, attributes, remaining_durations, end_time); + } + remove_dead_and_add_new_particles(*state, allocator); } - simulation.current_simulation_time = simulation_time_interval.end(); + simulation.current_simulation_time = solve_context.solve_interval.stop(); } } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver.hh b/source/blender/simulation/intern/simulation_solver.hh index 2dc0921ad9e..9d30ea87731 100644 --- a/source/blender/simulation/intern/simulation_solver.hh +++ b/source/blender/simulation/intern/simulation_solver.hh @@ -17,259 +17,22 @@ #ifndef __SIM_SIMULATION_SOLVER_HH__ #define __SIM_SIMULATION_SOLVER_HH__ -#include "BLI_float3.hh" -#include "BLI_span.hh" - -#include "DNA_simulation_types.h" - -#include "FN_attributes_ref.hh" - -#include "BKE_persistent_data_handle.hh" -#include "BKE_simulation.h" - -#include "particle_allocator.hh" -#include "time_interval.hh" +#include "simulation_collect_influences.hh" struct Depsgraph; namespace blender::sim { -class ParticleEmitterContext; -class ParticleForceContext; - -class ParticleEmitter { - public: - virtual ~ParticleEmitter(); - virtual void emit(ParticleEmitterContext &context) const = 0; -}; - -class ParticleForce { - public: - virtual ~ParticleForce(); - virtual void add_force(ParticleForceContext &context) const = 0; -}; - -struct SimulationInfluences { - Map<std::string, Vector<const ParticleForce *>> particle_forces; - Map<std::string, fn::AttributesInfoBuilder *> particle_attributes_builder; - Vector<const ParticleEmitter *> particle_emitters; - VectorSet<ID *> used_data_blocks; -}; - -class SimulationStateMap { - private: - Map<StringRefNull, SimulationState *> states_by_name_; - Map<StringRefNull, Vector<SimulationState *>> states_by_type_; - - public: - void add(SimulationState *state) - { - states_by_name_.add_new(state->name, state); - states_by_type_.lookup_or_add_default(state->type).append(state); - } - - template<typename StateType> StateType *lookup(StringRef name) const - { - const char *type = BKE_simulation_get_state_type_name<StateType>(); - return (StateType *)this->lookup_name_type(name, type); - } - - template<typename StateType> Span<StateType *> lookup() const - { - const char *type = BKE_simulation_get_state_type_name<StateType>(); - return this->lookup_type(type).cast<StateType *>(); - } - - SimulationState *lookup_name_type(StringRef name, StringRef type) const - { - SimulationState *state = states_by_name_.lookup_default_as(name, nullptr); - if (state == nullptr) { - return nullptr; - } - if (state->type == type) { - return state; - } - return nullptr; - } - - Span<SimulationState *> lookup_type(StringRef type) const - { - const Vector<SimulationState *> *states = states_by_type_.lookup_ptr_as(type); - if (states == nullptr) { - return {}; - } - else { - return states->as_span(); - } - } -}; - -class SimulationSolveContext { - private: - Simulation &simulation_; - Depsgraph &depsgraph_; - const SimulationInfluences &influences_; - TimeInterval solve_interval_; - const SimulationStateMap &state_map_; - const bke::PersistentDataHandleMap &id_handle_map_; - - public: - SimulationSolveContext(Simulation &simulation, - Depsgraph &depsgraph, - const SimulationInfluences &influences, - TimeInterval solve_interval, - const SimulationStateMap &state_map, - const bke::PersistentDataHandleMap &handle_map) - : simulation_(simulation), - depsgraph_(depsgraph), - influences_(influences), - solve_interval_(solve_interval), - state_map_(state_map), - id_handle_map_(handle_map) - { - } - - TimeInterval solve_interval() const - { - return solve_interval_; - } - - const SimulationInfluences &influences() const - { - return influences_; - } - - const bke::PersistentDataHandleMap &handle_map() const - { - return id_handle_map_; - } - - const SimulationStateMap &state_map() const - { - return state_map_; - } -}; - -class ParticleAllocators { - private: - Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators_; - - public: - ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators) - : allocators_(allocators) - { - } - - ParticleAllocator *try_get_allocator(StringRef particle_simulation_name) - { - auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name); - if (ptr != nullptr) { - return ptr->get(); - } - else { - return nullptr; - } - } -}; - -class ParticleChunkContext { - private: - IndexMask index_mask_; - fn::MutableAttributesRef attributes_; - - public: - ParticleChunkContext(IndexMask index_mask, fn::MutableAttributesRef attributes) - : index_mask_(index_mask), attributes_(attributes) - { - } - - IndexMask index_mask() const - { - return index_mask_; - } - - fn::MutableAttributesRef attributes() - { - return attributes_; - } - - fn::AttributesRef attributes() const - { - return attributes_; - } -}; - -class ParticleEmitterContext { - private: - SimulationSolveContext &solve_context_; - ParticleAllocators &particle_allocators_; - TimeInterval simulation_time_interval_; - - public: - ParticleEmitterContext(SimulationSolveContext &solve_context, - ParticleAllocators &particle_allocators, - TimeInterval simulation_time_interval) - : solve_context_(solve_context), - particle_allocators_(particle_allocators), - simulation_time_interval_(simulation_time_interval) - { - } - - SimulationSolveContext &solve_context() - { - return solve_context_; - } - - ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) - { - return particle_allocators_.try_get_allocator(particle_simulation_name); - } - - TimeInterval simulation_time_interval() const - { - return simulation_time_interval_; - } -}; - -class ParticleForceContext { - private: - SimulationSolveContext &solve_context_; - const ParticleChunkContext &particle_chunk_context_; - MutableSpan<float3> force_dst_; - - public: - ParticleForceContext(SimulationSolveContext &solve_context, - const ParticleChunkContext &particle_chunk_context, - MutableSpan<float3> force_dst) - : solve_context_(solve_context), - particle_chunk_context_(particle_chunk_context), - force_dst_(force_dst) - { - } - - SimulationSolveContext &solve_context() - { - return solve_context_; - } - - const ParticleChunkContext &particle_chunk() const - { - return particle_chunk_context_; - } - - MutableSpan<float3> force_dst() - { - return force_dst_; - } -}; - void initialize_simulation_states(Simulation &simulation, Depsgraph &depsgraph, - const SimulationInfluences &influences); + const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map); void solve_simulation_time_step(Simulation &simulation, Depsgraph &depsgraph, const SimulationInfluences &influences, + const bke::PersistentDataHandleMap &handle_map, + const DependencyAnimations &dependency_animations, float time_step); } // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.cc b/source/blender/simulation/intern/simulation_solver_influences.cc new file mode 100644 index 00000000000..3485d7c7bfb --- /dev/null +++ b/source/blender/simulation/intern/simulation_solver_influences.cc @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#include "simulation_solver_influences.hh" + +#include "DNA_object_types.h" + +namespace blender::sim { + +ParticleForce::~ParticleForce() +{ +} + +ParticleEmitter::~ParticleEmitter() +{ +} + +ParticleAction::~ParticleAction() +{ +} + +ParticleEvent::~ParticleEvent() +{ +} + +DependencyAnimations::~DependencyAnimations() +{ +} + +bool DependencyAnimations::is_object_transform_changing(Object &UNUSED(object)) const +{ + return false; +} + +void DependencyAnimations::get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const +{ + assert_same_size(simulation_times, r_transforms); + float4x4 world_matrix = object.obmat; + r_transforms.fill(world_matrix); +} + +} // namespace blender::sim diff --git a/source/blender/simulation/intern/simulation_solver_influences.hh b/source/blender/simulation/intern/simulation_solver_influences.hh new file mode 100644 index 00000000000..f7b8affd88d --- /dev/null +++ b/source/blender/simulation/intern/simulation_solver_influences.hh @@ -0,0 +1,237 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ +#define __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ + +#include "BLI_float3.hh" +#include "BLI_float4x4.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_span.hh" + +#include "DNA_simulation_types.h" + +#include "FN_attributes_ref.hh" + +#include "BKE_persistent_data_handle.hh" +#include "BKE_simulation.h" + +#include "particle_allocator.hh" +#include "time_interval.hh" + +namespace blender::sim { + +using fn::AttributesInfo; +using fn::AttributesInfoBuilder; +using fn::AttributesRef; +using fn::CPPType; +using fn::GMutableSpan; +using fn::GSpan; +using fn::MutableAttributesRef; + +struct ParticleEmitterContext; +struct ParticleForceContext; +struct ParticleActionContext; +struct ParticleEventFilterContext; + +class ParticleEmitter { + public: + virtual ~ParticleEmitter(); + virtual void emit(ParticleEmitterContext &context) const = 0; +}; + +class ParticleForce { + public: + virtual ~ParticleForce(); + virtual void add_force(ParticleForceContext &context) const = 0; +}; + +class ParticleAction { + public: + virtual ~ParticleAction(); + virtual void execute(ParticleActionContext &context) const = 0; +}; + +class ParticleEvent { + public: + virtual ~ParticleEvent(); + virtual void filter(ParticleEventFilterContext &context) const = 0; + virtual void execute(ParticleActionContext &context) const = 0; +}; + +struct SimulationInfluences { + MultiValueMap<std::string, const ParticleForce *> particle_forces; + MultiValueMap<std::string, const ParticleAction *> particle_birth_actions; + MultiValueMap<std::string, const ParticleAction *> particle_time_step_begin_actions; + MultiValueMap<std::string, const ParticleAction *> particle_time_step_end_actions; + MultiValueMap<std::string, const ParticleEvent *> particle_events; + Map<std::string, AttributesInfoBuilder *> particle_attributes_builder; + Vector<const ParticleEmitter *> particle_emitters; +}; + +class SimulationStateMap { + private: + Map<StringRefNull, SimulationState *> states_by_name_; + MultiValueMap<StringRefNull, SimulationState *> states_by_type_; + + public: + void add(SimulationState *state) + { + states_by_name_.add_new(state->name, state); + states_by_type_.add(state->type, state); + } + + template<typename StateType> StateType *lookup(StringRef name) const + { + const char *type = BKE_simulation_get_state_type_name<StateType>(); + return (StateType *)this->lookup_name_type(name, type); + } + + template<typename StateType> Span<StateType *> lookup() const + { + const char *type = BKE_simulation_get_state_type_name<StateType>(); + return this->lookup_type(type).cast<StateType *>(); + } + + SimulationState *lookup_name_type(StringRef name, StringRef type) const + { + SimulationState *state = states_by_name_.lookup_default_as(name, nullptr); + if (state == nullptr) { + return nullptr; + } + if (state->type == type) { + return state; + } + return nullptr; + } + + Span<SimulationState *> lookup_type(StringRef type) const + { + return states_by_type_.lookup_as(type); + } +}; + +class DependencyAnimations { + public: + ~DependencyAnimations(); + + virtual bool is_object_transform_changing(Object &object) const; + virtual void get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const; +}; + +struct SimulationSolveContext { + Simulation &simulation; + Depsgraph &depsgraph; + const SimulationInfluences &influences; + TimeInterval solve_interval; + const SimulationStateMap &state_map; + const bke::PersistentDataHandleMap &handle_map; + const DependencyAnimations &dependency_animations; +}; + +class ParticleAllocators { + private: + Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators_; + + public: + ParticleAllocators(Map<std::string, std::unique_ptr<ParticleAllocator>> &allocators) + : allocators_(allocators) + { + } + + ParticleAllocator *try_get_allocator(StringRef particle_simulation_name) + { + auto *ptr = allocators_.lookup_ptr_as(particle_simulation_name); + if (ptr != nullptr) { + return ptr->get(); + } + else { + return nullptr; + } + } +}; + +struct ParticleChunkIntegrationContext { + MutableSpan<float3> position_diffs; + MutableSpan<float3> velocity_diffs; + MutableSpan<float> durations; + float end_time; +}; + +struct ParticleChunkContext { + ParticleSimulationState &state; + IndexMask index_mask; + MutableAttributesRef attributes; + ParticleChunkIntegrationContext *integration = nullptr; + + void update_diffs_after_velocity_change() + { + if (integration == nullptr) { + return; + } + + Span<float> remaining_durations = integration->durations; + MutableSpan<float3> position_diffs = integration->position_diffs; + Span<float3> velocities = attributes.get<float3>("Velocity"); + + for (int i : index_mask) { + const float duration = remaining_durations[i]; + /* This is certainly not a perfect way to "re-integrate" the velocity, but it should be good + * enough for most use cases. Changing the velocity in an instant is not physically correct + * anyway. */ + position_diffs[i] = velocities[i] * duration; + } + } +}; + +struct ParticleEmitterContext { + SimulationSolveContext &solve_context; + ParticleAllocators &particle_allocators; + TimeInterval emit_interval; + + template<typename StateType> StateType *lookup_state(StringRef name) + { + return solve_context.state_map.lookup<StateType>(name); + } + + ParticleAllocator *try_get_particle_allocator(StringRef particle_simulation_name) + { + return particle_allocators.try_get_allocator(particle_simulation_name); + } +}; + +struct ParticleForceContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particles; + MutableSpan<float3> force_dst; +}; + +struct ParticleActionContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particles; +}; + +struct ParticleEventFilterContext { + SimulationSolveContext &solve_context; + ParticleChunkContext &particles; + MutableSpan<float> factor_dst; +}; + +} // namespace blender::sim + +#endif /* __SIM_SIMULATION_SOLVER_INFLUENCES_HH__ */ diff --git a/source/blender/simulation/intern/simulation_update.cc b/source/blender/simulation/intern/simulation_update.cc index 09219e0238f..32b582977d0 100644 --- a/source/blender/simulation/intern/simulation_update.cc +++ b/source/blender/simulation/intern/simulation_update.cc @@ -17,8 +17,11 @@ #include "SIM_simulation_update.hh" #include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_object.h" #include "BKE_simulation.h" +#include "DNA_modifier_types.h" #include "DNA_scene_types.h" #include "DNA_simulation_types.h" @@ -29,8 +32,11 @@ #include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_rand.h" +#include "BLI_set.hh" #include "BLI_vector.hh" +#include "NOD_node_tree_dependencies.hh" + #include "particle_function.hh" #include "simulation_collect_influences.hh" #include "simulation_solver.hh" @@ -88,6 +94,131 @@ static void update_simulation_state_list(Simulation *simulation, add_missing_states(simulation, required_states); } +class SampledDependencyAnimations : public DependencyAnimations { + private: + TimeInterval simulation_time_interval_; + MultiValueMap<Object *, float4x4> object_transforms_cache_; + + public: + SampledDependencyAnimations(TimeInterval simulation_time_interval) + : simulation_time_interval_(simulation_time_interval) + { + } + + void add_object_transforms(Object &object, Span<float4x4> transforms) + { + object_transforms_cache_.add_multiple(&object, transforms); + } + + bool is_object_transform_changing(Object &object) const + { + return object_transforms_cache_.lookup(&object).size() >= 2; + } + + void get_object_transforms(Object &object, + Span<float> simulation_times, + MutableSpan<float4x4> r_transforms) const + { + assert_same_size(simulation_times, r_transforms); + Span<float4x4> cached_transforms = object_transforms_cache_.lookup(&object); + if (cached_transforms.size() == 0) { + r_transforms.fill(object.obmat); + return; + } + if (cached_transforms.size() == 1) { + r_transforms.fill(cached_transforms[0]); + return; + } + + for (int i : simulation_times.index_range()) { + const float simulation_time = simulation_times[i]; + if (simulation_time <= simulation_time_interval_.start()) { + r_transforms[i] = cached_transforms.first(); + continue; + } + if (simulation_time >= simulation_time_interval_.stop()) { + r_transforms[i] = cached_transforms.last(); + continue; + } + const float factor = simulation_time_interval_.factor_at_time(simulation_time); + BLI_assert(factor > 0.0f && factor < 1.0f); + const float scaled_factor = factor * (cached_transforms.size() - 1); + const int lower_sample = (int)scaled_factor; + const int upper_sample = lower_sample + 1; + const float mix_factor = scaled_factor - lower_sample; + r_transforms[i] = float4x4::interpolate( + cached_transforms[lower_sample], cached_transforms[upper_sample], mix_factor); + } + } +}; + +static void sample_object_transforms(Object &object, + Depsgraph &depsgraph, + Scene &scene, + TimeInterval scene_frame_interval, + MutableSpan<float4x4> r_transforms) +{ + if (r_transforms.size() == 0) { + return; + } + if (r_transforms.size() == 1) { + r_transforms[0] = object.obmat; + return; + } + + Array<float> frames(r_transforms.size()); + scene_frame_interval.compute_uniform_samples(frames); + + for (int i : frames.index_range()) { + float frame = frames[i]; + const int recursion_depth = 5; + BKE_object_modifier_update_subframe( + &depsgraph, &scene, &object, false, recursion_depth, frame, eModifierType_None); + r_transforms[i] = object.obmat; + } +} + +template<typename T> static bool all_values_equal(Span<T> values) +{ + if (values.size() == 0) { + return true; + } + for (const T &value : values.drop_front(1)) { + if (value != values[0]) { + return false; + } + } + return true; +} + +static void prepare_dependency_animations(Depsgraph &depsgraph, + Scene &scene, + Simulation &simulation, + TimeInterval scene_frame_interval, + SampledDependencyAnimations &r_dependency_animations) +{ + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation.dependencies) { + ID *id_cow = DEG_get_evaluated_id(&depsgraph, dependency->id); + if (id_cow == nullptr) { + continue; + } + if (GS(id_cow->name) != ID_OB) { + continue; + } + Object &object_cow = *(Object *)id_cow; + constexpr int sample_count = 10; + Array<float4x4, sample_count> transforms(sample_count); + sample_object_transforms(object_cow, depsgraph, scene, scene_frame_interval, transforms); + + /* If all samples are the same, only store one. */ + Span<float4x4> transforms_to_use = (all_values_equal(transforms.as_span())) ? + transforms.as_span().take_front(1) : + transforms.as_span(); + + r_dependency_animations.add_object_transforms(object_cow, transforms_to_use); + } +} + void update_simulation_in_depsgraph(Depsgraph *depsgraph, Scene *scene_cow, Simulation *simulation_cow) @@ -108,13 +239,20 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, SimulationInfluences influences; RequiredStates required_states; - /* TODO: Use simulation_cow, but need to add depsgraph relations before that. */ - collect_simulation_influences(*simulation_orig, resources, influences, required_states); + collect_simulation_influences(*simulation_cow, resources, influences, required_states); + + bke::PersistentDataHandleMap handle_map; + LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation_orig->dependencies) { + ID *id_cow = DEG_get_evaluated_id(depsgraph, dependency->id); + if (id_cow != nullptr) { + handle_map.add(dependency->handle, *id_cow); + } + } if (current_frame == 1) { reinitialize_empty_simulation_states(simulation_orig, required_states); - initialize_simulation_states(*simulation_orig, *depsgraph, influences); + initialize_simulation_states(*simulation_orig, *depsgraph, influences, handle_map); simulation_orig->current_frame = 1; copy_states_to_cow(simulation_orig, simulation_cow); @@ -122,12 +260,97 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph, else if (current_frame == simulation_orig->current_frame + 1) { update_simulation_state_list(simulation_orig, required_states); - float time_step = 1.0f / 24.0f; - solve_simulation_time_step(*simulation_orig, *depsgraph, influences, time_step); + const float fps = scene_cow->r.frs_sec / scene_cow->r.frs_sec_base; + const float time_step = 1.0f / fps; + TimeInterval scene_frame_interval(current_frame - 1, 1); + TimeInterval simulation_time_interval(simulation_orig->current_simulation_time, time_step); + SampledDependencyAnimations dependency_animations{simulation_time_interval}; + prepare_dependency_animations( + *depsgraph, *scene_cow, *simulation_orig, scene_frame_interval, dependency_animations); + + solve_simulation_time_step( + *simulation_orig, *depsgraph, influences, handle_map, dependency_animations, time_step); simulation_orig->current_frame = current_frame; copy_states_to_cow(simulation_orig, simulation_cow); } } +/* Returns true when dependencies have changed. */ +bool update_simulation_dependencies(Simulation *simulation) +{ + nodes::NodeTreeDependencies dependencies = nodes::find_node_tree_dependencies( + *simulation->nodetree); + + ListBase *dependency_list = &simulation->dependencies; + + bool dependencies_changed = false; + + Map<ID *, SimulationDependency *> dependency_by_id; + Map<SimulationDependency *, int> old_flag_by_dependency; + Set<int> used_handles; + + /* Remove unused handle items and clear flags that are reinitialized later. */ + LISTBASE_FOREACH_MUTABLE (SimulationDependency *, dependency, dependency_list) { + if (dependencies.depends_on(dependency->id)) { + dependency_by_id.add_new(dependency->id, dependency); + used_handles.add_new(dependency->handle); + old_flag_by_dependency.add_new(dependency, dependency->flag); + dependency->flag &= ~(SIM_DEPENDS_ON_TRANSFORM | SIM_DEPENDS_ON_GEOMETRY); + } + else { + if (dependency->id != nullptr) { + id_us_min(dependency->id); + } + BLI_remlink(dependency_list, dependency); + MEM_freeN(dependency); + dependencies_changed = true; + } + } + + /* Add handle items for new id dependencies. */ + int next_handle = 0; + for (ID *id : dependencies.id_dependencies()) { + dependency_by_id.lookup_or_add_cb(id, [&]() { + while (used_handles.contains(next_handle)) { + next_handle++; + } + used_handles.add_new(next_handle); + + SimulationDependency *dependency = (SimulationDependency *)MEM_callocN(sizeof(*dependency), + AT); + id_us_plus(id); + dependency->id = id; + dependency->handle = next_handle; + BLI_addtail(dependency_list, dependency); + + return dependency; + }); + } + + /* Set appropriate dependency flags. */ + for (Object *object : dependencies.transform_dependencies()) { + SimulationDependency *dependency = dependency_by_id.lookup(&object->id); + dependency->flag |= SIM_DEPENDS_ON_TRANSFORM; + } + for (Object *object : dependencies.geometry_dependencies()) { + SimulationDependency *dependency = dependency_by_id.lookup(&object->id); + dependency->flag |= SIM_DEPENDS_ON_GEOMETRY; + } + + if (!dependencies_changed) { + /* Check if any flags have changed. */ + LISTBASE_FOREACH (SimulationDependency *, dependency, dependency_list) { + uint32_t old_flag = old_flag_by_dependency.lookup_default(dependency, 0); + uint32_t new_flag = dependency->flag; + if (old_flag != new_flag) { + dependencies_changed = true; + break; + } + } + } + + return dependencies_changed; +} + } // namespace blender::sim diff --git a/source/blender/simulation/intern/time_interval.hh b/source/blender/simulation/intern/time_interval.hh index 49600dd10de..5996dc5d25b 100644 --- a/source/blender/simulation/intern/time_interval.hh +++ b/source/blender/simulation/intern/time_interval.hh @@ -22,7 +22,7 @@ namespace blender::sim { /** - * The start time is inclusive and the end time is exclusive. The duration is zero, the interval + * The start time is exclusive and the end time is inclusive. If the duration is zero, the interval * describes a single point in time. */ class TimeInterval { @@ -41,7 +41,7 @@ class TimeInterval { return start_; } - float end() const + float stop() const { return start_ + duration_; } @@ -50,6 +50,42 @@ class TimeInterval { { return duration_; } + + float time_at_factor(float factor) const + { + return start_ + factor * duration_; + } + + float factor_at_time(float time) const + { + BLI_assert(duration_ > 0.0f); + return (time - start_) / duration_; + } + + float safe_factor_at_time(float time) const + { + if (duration_ > 0.0f) { + return this->factor_at_time(time); + } + return 0.0f; + } + + void compute_uniform_samples(MutableSpan<float> r_samples) const + { + int64_t amount = r_samples.size(); + if (amount == 0) { + return; + } + if (amount == 1) { + r_samples[0] = this->time_at_factor(0.5f); + return; + } + + const float step = duration_ / (float)(amount - 1); + for (int64_t i : r_samples.index_range()) { + r_samples[i] = start_ + i * step; + } + } }; } // namespace blender::sim diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 3d4c84805f9..c0b2643d78d 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -99,7 +99,7 @@ void WM_main(struct bContext *C) ATTR_NORETURN; void WM_init_splash(struct bContext *C); -void WM_init_opengl(struct Main *bmain); +void WM_init_opengl(void); void WM_check(struct bContext *C); void WM_reinit_gizmomap_all(struct Main *bmain); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c index e687af15982..90d9ba45637 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c @@ -29,7 +29,6 @@ #include "BKE_context.h" #include "GPU_batch.h" -#include "GPU_glew.h" #include "RNA_access.h" #include "RNA_define.h" diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 396b59ba6e2..2038a82c9fd 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -38,7 +38,6 @@ #include "ED_select_utils.h" #include "ED_view3d.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_state.h" @@ -582,7 +581,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); rcti rect; /* Almost certainly overkill, but allow for many custom gizmos. */ - GLuint buffer[MAXPICKBUF]; + uint buffer[MAXPICKBUF]; short hits; BLI_rcti_init_pt_radius(&rect, co, hotspot); @@ -625,7 +624,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin); - GLuint *buf_iter = buffer; + uint *buf_iter = buffer; int hit_found = -1; float dot_best = FLT_MAX; @@ -649,7 +648,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, return hit_found; } else { - const GLuint *hit_near = GPU_select_buffer_near(buffer, hits); + const uint *hit_near = GPU_select_buffer_near(buffer, hits); return hit_near ? hit_near[3] : -1; } } diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 2112477e62a..43c08a2b980 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -293,7 +293,7 @@ void WM_keyconfig_init(bContext *C) /* initialize only after python init is done, for keymaps that * use python operators */ - if (CTX_py_init_get(C) && (wm->initialized & WM_KEYCONFIG_IS_INITIALIZED) == 0) { + if (CTX_py_init_get(C) && (wm->initialized & WM_KEYCONFIG_IS_INIT) == 0) { /* create default key config, only initialize once, * it's persistent across sessions */ if (!(wm->defaultconf->flag & KEYCONF_INIT_DEFAULT)) { @@ -308,7 +308,7 @@ void WM_keyconfig_init(bContext *C) WM_keyconfig_update_tag(NULL, NULL); WM_keyconfig_update(wm); - wm->initialized |= WM_KEYCONFIG_IS_INITIALIZED; + wm->initialized |= WM_KEYCONFIG_IS_INIT; } } @@ -334,7 +334,7 @@ void WM_check(bContext *C) if (!G.background) { /* case: fileread */ - if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { + if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) { WM_keyconfig_init(C); WM_autosave_init(wm); } @@ -345,9 +345,9 @@ void WM_check(bContext *C) /* case: fileread */ /* note: this runs in bg mode to set the screen context cb */ - if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { - ED_screens_initialize(bmain, wm); - wm->initialized |= WM_WINDOW_IS_INITIALIZED; + if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) { + ED_screens_init(bmain, wm); + wm->initialized |= WM_WINDOW_IS_INIT; } } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index ad3fc7a1302..5f13adcb971 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -39,7 +39,6 @@ #include "BKE_context.h" #include "BKE_idtype.h" -#include "GPU_glew.h" #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_viewport.h" @@ -424,9 +423,8 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) y, drag->imb->x, drag->imb->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, drag->imb->rect, drag->scale, drag->scale, diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 16454e473cc..4db6eb6af91 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -51,7 +51,6 @@ #include "ED_screen.h" #include "ED_view3d.h" -#include "GPU_draw.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_state.h" @@ -999,7 +998,7 @@ void wm_draw_update(bContext *C) wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win; - GPU_free_unused_buffers(); + BKE_image_free_unused_gpu_textures(); for (win = wm->windows.first; win; win = win->next) { #ifdef WIN32 diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index f431a6f431b..f39fc0aecf2 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -258,7 +258,7 @@ static void wm_window_match_keep_current_wm(const bContext *C, bScreen *screen = NULL; /* match oldwm to new dbase, only old files */ - wm->initialized &= ~WM_WINDOW_IS_INITIALIZED; + wm->initialized &= ~WM_WINDOW_IS_INIT; /* when loading without UI, no matching needed */ if (load_ui && (screen = CTX_wm_screen(C))) { diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index eee93fc9459..5a66cbd10eb 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -362,18 +362,8 @@ static void draw_filled_lasso(wmGesture *gt) GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex(&state, - rect.xmin, - rect.ymin, - w, - h, - GL_RED, - GL_UNSIGNED_BYTE, - GL_NEAREST, - pixel_buf, - 1.0f, - 1.0f, - NULL); + immDrawPixelsTex( + &state, rect.xmin, rect.ymin, w, h, GL_R8, false, pixel_buf, 1.0f, 1.0f, NULL); GPU_shader_unbind(); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 945d5fd42e4..85694dec9c8 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -59,6 +59,7 @@ #include "BKE_font.h" #include "BKE_global.h" #include "BKE_icons.h" +#include "BKE_image.h" #include "BKE_keyconfig.h" #include "BKE_lib_remap.h" #include "BKE_main.h" @@ -120,7 +121,6 @@ #include "UI_interface.h" #include "UI_resources.h" -#include "GPU_draw.h" #include "GPU_init_exit.h" #include "GPU_material.h" @@ -171,7 +171,7 @@ void WM_init_state_start_with_console_set(bool value) */ static bool opengl_is_init = false; -void WM_init_opengl(Main *bmain) +void WM_init_opengl(void) { /* must be called only once */ BLI_assert(opengl_is_init == false); @@ -185,9 +185,6 @@ void WM_init_opengl(Main *bmain) DRW_opengl_context_create(); GPU_init(); - GPU_set_mipmap(bmain, true); - GPU_set_linear_mipmap(true); - GPU_set_anisotropic(U.anisotropic_filter); GPU_pass_cache_init(); @@ -315,7 +312,7 @@ void WM_init(bContext *C, int argc, const char **argv) /* sets 3D mouse deadzone */ WM_ndof_deadzone_set(U.ndof_deadzone); #endif - WM_init_opengl(G_MAIN); + WM_init_opengl(); if (!WM_platform_support_perform_checks()) { exit(-1); @@ -578,7 +575,7 @@ void WM_exit_ex(bContext *C, const bool do_python) BKE_subdiv_exit(); if (opengl_is_init) { - GPU_free_unused_buffers(); + BKE_image_free_unused_gpu_textures(); } BKE_blender_free(); /* blender.c, does entire library and spacetypes */ diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index d7102a1e8af..f0ad5f1e9c6 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -363,7 +363,7 @@ void WM_keyconfig_set_active(wmWindowManager *wm, const char *idname) WM_keyconfig_update(wm); BLI_strncpy(U.keyconfigstr, idname, sizeof(U.keyconfigstr)); - if (wm->initialized & WM_KEYCONFIG_IS_INITIALIZED) { + if (wm->initialized & WM_KEYCONFIG_IS_INIT) { U.runtime.is_dirty = true; } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index d1f65b6271b..72cc5a3d7dd 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2322,7 +2322,7 @@ static void radial_control_paint_curve(uint pos, Brush *br, float radius, int li GPU_line_width(2.0f); immUniformColor4f(0.8f, 0.8f, 0.8f, 0.85f); float step = (radius * 2.0f) / (float)line_segments; - BKE_curvemapping_initialize(br->curve); + BKE_curvemapping_init(br->curve); immBegin(GPU_PRIM_LINES, line_segments * 2); for (int i = 0; i < line_segments; i++) { float h1 = BKE_brush_curve_strength_clamped(br, fabsf((i * step) - radius), radius); @@ -2968,7 +2968,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even if (rc->subtype == PROP_ANGLE) { float initial_position[2] = {UNPACK2(rc->initial_mouse)}; float current_position[2] = {UNPACK2(rc->slow_mouse)}; - rc->dial = BLI_dial_initialize(initial_position, 0.0f); + rc->dial = BLI_dial_init(initial_position, 0.0f); /* immediately set the position to get a an initial direction */ BLI_dial_angle(rc->dial, current_position); } @@ -3917,6 +3917,7 @@ static void gesture_box_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "CLIP_OT_select_box"); WM_modalkeymap_assign(keymap, "CLIP_OT_graph_select_box"); WM_modalkeymap_assign(keymap, "MASK_OT_select_box"); + WM_modalkeymap_assign(keymap, "PAINT_OT_mask_box_gesture"); WM_modalkeymap_assign(keymap, "VIEW2D_OT_zoom_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_clip_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_render_border"); diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 74e1c495a00..d0a70596957 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -335,9 +335,8 @@ static void playanim_toscreen( offs_y + (ps->draw_flip[1] ? span_y : 0.0f), ibuf->x, ibuf->y, - GL_RGBA, - GL_UNSIGNED_BYTE, - GL_NEAREST, + GPU_RGBA8, + false, ibuf->rect, ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x), ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y), diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c index 477579ed620..0cb76404258 100644 --- a/source/blender/windowmanager/intern/wm_subwindow.c +++ b/source/blender/windowmanager/intern/wm_subwindow.c @@ -29,7 +29,6 @@ #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" -#include "GPU_glew.h" #include "GPU_matrix.h" #include "GPU_viewport.h" diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index e444c5b2109..fc04323333b 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1206,7 +1206,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* Ghost now can call this function for life resizes, * but it should return if WM didn't initialize yet. * Can happen on file read (especially full size window). */ - if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) { + if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) { return 1; } if (!ghostwin) { diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h index a675647f57a..9b6bd66c0e5 100644 --- a/source/blender/windowmanager/wm_draw.h +++ b/source/blender/windowmanager/wm_draw.h @@ -24,8 +24,6 @@ #ifndef __WM_DRAW_H__ #define __WM_DRAW_H__ -#include "GPU_glew.h" - struct GPUOffScreen; struct GPUTexture; struct GPUViewport; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 8e7101ddcd5..c564f74b771 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -41,8 +41,8 @@ #include "wm_window.h" #include "wm_xr_intern.h" -wmSurface *g_xr_surface = NULL; -CLG_LogRef LOG = {"wm.xr"}; +static wmSurface *g_xr_surface = NULL; +static CLG_LogRef LOG = {"wm.xr"}; /* -------------------------------------------------------------------- */ diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index aed2a9350bb..8784d9f1744 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -694,6 +694,23 @@ elseif(WIN32) ) endif() + if(WITH_GMP) + install( + FILES ${LIBDIR}/gmp/lib/libgmp-10.dll + DESTINATION "." + ) + install( + FILES ${LIBDIR}/gmp/lib/libgmpxx.dll + DESTINATION "." + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + ) + install( + FILES ${LIBDIR}/gmp/lib/libgmpxx_d.dll + DESTINATION "." + CONFIGURATIONS Debug + ) + endif() + if(WITH_WINDOWS_PDB) if(WITH_WINDOWS_STRIPPED_PDB) # Icky hack for older cmake from https://stackoverflow.com/a/21198501 diff --git a/source/creator/creator.c b/source/creator/creator.c index 9268ed15923..3d76d832d9f 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -110,25 +110,10 @@ #include "creator_intern.h" /* own include */ /* Local Function prototypes. */ -#ifdef WITH_PYTHON_MODULE -int main_python_enter(int argc, const char **argv); -void main_python_exit(void); -#endif -#ifdef WITH_USD -/** - * Workaround to make it possible to pass a path at runtime to USD. - * - * USD requires some JSON files, and it uses a static constructor to determine the possible - * file-system paths to find those files. This made it impossible for Blender to pass a path to the - * USD library at runtime, as the constructor would run before Blender's main() function. We have - * patched USD (see usd.diff) to avoid that particular static constructor, and have an - * initialization function instead. - * - * This function is implemented in the USD source code, `pxr/base/lib/plug/initConfig.cpp`. - */ -void usd_initialise_plugin_path(const char *datafiles_usd_path); -#endif +/* -------------------------------------------------------------------- */ +/** \name Local Application State + * \{ */ /* written to by 'creator_args.c' */ struct ApplicationState app_state = { @@ -143,6 +128,8 @@ struct ApplicationState app_state = { }, }; +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Application Level Callbacks * @@ -199,11 +186,19 @@ static void callback_clg_fatal(void *fp) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Main Function +/** \name Blender as a Stand-Alone Python Module (bpy) + * + * While not officially supported, this can be useful for Python developers. + * See: https://wiki.blender.org/wiki/Building_Blender/Other/BlenderAsPyModule * \{ */ #ifdef WITH_PYTHON_MODULE -/* allow python module to call main */ + +/* Called in `bpy_interface.c` when building as a Python module. */ +int main_python_enter(int argc, const char **argv); +void main_python_exit(void); + +/* Rename the 'main' function, allowing Python initialization to call it. */ # define main main_python_enter static void *evil_C = NULL; @@ -211,8 +206,15 @@ static void *evil_C = NULL; /* Environment is not available in macOS shared libraries. */ # include <crt_externs.h> char **environ = NULL; -# endif -#endif +# endif /* __APPLE__ */ + +#endif /* WITH_PYTHON_MODULE */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Function + * \{ */ /** * Blender's main function responsibilities are: @@ -295,7 +297,7 @@ int main(int argc, break; } } - MEM_initialize_memleak_detection(); + MEM_init_memleak_detection(); } #ifdef BUILD_DATE @@ -441,10 +443,21 @@ int main(int argc, BKE_materials_init(); #ifdef WITH_USD + /* Workaround to make it possible to pass a path at runtime to USD. + * + * USD requires some JSON files, and it uses a static constructor to determine the possible + * file-system paths to find those files. This made it impossible for Blender to pass a path to + * the USD library at runtime, as the constructor would run before Blender's main() function. + * We have patched USD (see usd.diff) to avoid that particular static constructor, and have an + * initialization function instead. + * + * This function is implemented in the USD source code, `pxr/base/lib/plug/initConfig.cpp`. */ + extern void usd_initialise_plugin_path(const char *datafiles_usd_path); + /* 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. */ usd_initialise_plugin_path(BKE_appdir_folder_id(BLENDER_DATAFILES, "usd")); -#endif +#endif /* WITH_USD */ if (G.background == 0) { #ifndef WITH_PYTHON_MODULE @@ -488,7 +501,7 @@ int main(int argc, #ifdef WITH_FREESTYLE /* Initialize Freestyle. */ - FRS_initialize(); + FRS_init(); FRS_set_context(C); #endif diff --git a/source/tools b/source/tools -Subproject 6a252de776d0b9dca3167c30a7621a4f1e9bc91 +Subproject 896c5f78952adb2d091d28c65086d46992dabda |