diff options
Diffstat (limited to 'source/blender/blenkernel')
41 files changed, 3079 insertions, 1340 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index eb7078213da..2a4394ee096 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ * \note Use #STRINGIFY() rather than defining with quotes. */ #define BLENDER_VERSION 283 -#define BLENDER_SUBVERSION 8 +#define BLENDER_SUBVERSION 9 /** Several breakages with 280, e.g. collections vs layers. */ #define BLENDER_MINVERSION 280 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index aebe04f3a7a..f93003dc423 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -117,7 +117,7 @@ void BKE_camera_view_frame(const struct Scene *scene, float r_vec[4][3]); bool BKE_camera_view_frame_fit_to_scene(struct Depsgraph *depsgraph, - struct Scene *scene, + const struct Scene *scene, struct Object *camera_ob, float r_co[3], float *r_scale); @@ -130,33 +130,34 @@ bool BKE_camera_view_frame_fit_to_coords(const struct Depsgraph *depsgraph, /* Camera multi-view API */ -struct Object *BKE_camera_multiview_render(struct Scene *scene, +struct Object *BKE_camera_multiview_render(const struct Scene *scene, struct Object *camera, const char *viewname); -void BKE_camera_multiview_view_matrix(struct RenderData *rd, +void BKE_camera_multiview_view_matrix(const struct RenderData *rd, const struct Object *camera, const bool is_left, float r_viewmat[4][4]); -void BKE_camera_multiview_model_matrix(struct RenderData *rd, +void BKE_camera_multiview_model_matrix(const struct RenderData *rd, const struct Object *camera, const char *viewname, float r_modelmat[4][4]); -void BKE_camera_multiview_model_matrix_scaled(struct RenderData *rd, +void BKE_camera_multiview_model_matrix_scaled(const struct RenderData *rd, const struct Object *camera, const char *viewname, float r_modelmat[4][4]); -void BKE_camera_multiview_window_matrix(struct RenderData *rd, +void BKE_camera_multiview_window_matrix(const struct RenderData *rd, const struct Object *camera, const char *viewname, float r_winmat[4][4]); -float BKE_camera_multiview_shift_x(struct RenderData *rd, +float BKE_camera_multiview_shift_x(const struct RenderData *rd, const struct Object *camera, const char *viewname); -void BKE_camera_multiview_params(struct RenderData *rd, +void BKE_camera_multiview_params(const struct RenderData *rd, struct CameraParams *params, const struct Object *camera, const char *viewname); -bool BKE_camera_multiview_spherical_stereo(struct RenderData *rd, const struct Object *camera); +bool BKE_camera_multiview_spherical_stereo(const struct RenderData *rd, + const struct Object *camera); /* Camera background image API */ struct CameraBGImage *BKE_camera_background_image_new(struct Camera *cam); diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index d5fb55af06c..96837f380d4 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -33,10 +33,10 @@ extern "C" { struct ClothModifierData; struct CollisionModifierData; struct Depsgraph; +struct GHash; struct Mesh; struct Object; struct Scene; -struct GHash; #define DO_INLINE MALWAYS_INLINE diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 3284896795b..5d23029df9e 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -34,8 +34,10 @@ struct CurveMapping; struct Depsgraph; struct GHash; struct ListBase; +struct MDeformVert; struct Main; struct Material; +struct MaterialGPencilStyle; struct Object; struct Scene; struct SpaceImage; @@ -47,8 +49,6 @@ struct bGPDlayer_Mask; struct bGPDspoint; struct bGPDstroke; struct bGPdata; -struct MaterialGPencilStyle; -struct MDeformVert; #define GPENCIL_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)) #define GPENCIL_SIMPLIFY_ONPLAY(playing) \ @@ -193,6 +193,7 @@ void BKE_gpencil_layer_mask_remove_ref(struct bGPdata *gpd, const char *name); struct bGPDlayer_Mask *BKE_gpencil_layer_mask_named_get(struct bGPDlayer *gpl, const char *name); void BKE_gpencil_layer_mask_sort(struct bGPdata *gpd, struct bGPDlayer *gpl); void BKE_gpencil_layer_mask_sort_all(struct bGPdata *gpd); +void BKE_gpencil_layer_frames_sort(struct bGPDlayer *gpl, bool *r_has_duplicate_frames); /* Brush */ struct Material *BKE_gpencil_brush_material_get(struct Brush *brush); diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 9562ace3c51..e6e82900f6d 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -70,7 +70,7 @@ typedef struct IDTypeInfo { * Bitflag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's * FILTER_ID_XX enums. */ - int id_filter; + int64_t id_filter; /** * Define the position of this data-block type in the virtual list of all data in a Main that is diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 524bd3318f6..4ce740a1f5a 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -68,7 +68,7 @@ void BKE_render_result_stamp_info(struct Scene *scene, * Fills in the static stamp data (i.e. everything except things that can change per frame). * The caller is responsible for freeing the allocated memory. */ -struct StampData *BKE_stamp_info_from_scene_static(struct Scene *scene); +struct StampData *BKE_stamp_info_from_scene_static(const struct Scene *scene); bool BKE_stamp_is_known_field(const char *field_name); void BKE_imbuf_stamp_info(struct RenderResult *rr, struct ImBuf *ibuf); void BKE_stamp_info_from_imbuf(struct RenderResult *rr, struct ImBuf *ibuf); diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index c663ae76564..4ee255d4e61 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -98,16 +98,15 @@ void multiresModifier_del_levels(struct MultiresModifierData *mmd, struct Scene *scene, struct Object *object, int direction); -void multiresModifier_base_apply(struct MultiresModifierData *mmd, - struct Scene *scene, - struct Object *ob); -void multiresModifier_subdivide(struct MultiresModifierData *mmd, - struct Scene *scene, - struct Object *ob, - int updateblock, - int simple); -void multiresModifier_sync_levels_ex(struct Scene *scene, - struct Object *ob_dst, +void multiresModifier_base_apply(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd); +void multiresModifier_subdivide_legacy(struct MultiresModifierData *mmd, + struct Scene *scene, + struct Object *ob, + int updateblock, + int simple); +void multiresModifier_sync_levels_ex(struct Object *ob_dst, struct MultiresModifierData *mmd_src, struct MultiresModifierData *mmd_dst); @@ -131,6 +130,10 @@ int multires_mdisp_corners(struct MDisps *s); /* update multires data after topology changing */ void multires_topology_changed(struct Mesh *me); +void multires_ensure_external_read(struct Mesh *mesh, int top_level); +void multiresModifier_ensure_external_read(struct Mesh *mesh, + const struct MultiresModifierData *mmd); + /**** interpolation stuff ****/ void old_mdisps_bilinear(float out[3], float (*disps)[3], const int st, float u, float v); int mdisp_rot_face_to_crn(struct MVert *mvert, @@ -145,18 +148,32 @@ int mdisp_rot_face_to_crn(struct MVert *mvert, /* Reshaping, define in multires_reshape.c */ +bool multiresModifier_reshapeFromVertcos(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd, + const float (*vert_coords)[3], + const int num_vert_coords); bool multiresModifier_reshapeFromObject(struct Depsgraph *depsgraph, struct MultiresModifierData *mmd, struct Object *dst, struct Object *src); bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph, - struct MultiresModifierData *mmd, struct Object *ob, - struct ModifierData *md); + struct MultiresModifierData *mmd, + struct ModifierData *deform_md); bool multiresModifier_reshapeFromCCG(const int tot_level, struct Mesh *coarse_mesh, struct SubdivCCG *subdiv_ccg); +/* Subdivide multires displacement once. */ +void multiresModifier_subdivide(struct Object *object, struct MultiresModifierData *mmd); + +/* Subdivide displacement to the given level. + * If level is lower than the current top level nothing happens. */ +void multiresModifier_subdivide_to_level(struct Object *object, + struct MultiresModifierData *mmd, + const int top_level); + /* Subdivision integration, defined in multires_subdiv.c */ struct SubdivSettings; diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index a8ebd32ad4d..886591d7728 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -76,9 +76,7 @@ bool BKE_object_shaderfx_use_time(struct Object *ob, struct ShaderFxData *md); bool BKE_object_support_modifier_type_check(const struct Object *ob, int modifier_type); -void BKE_object_link_modifiers(struct Scene *scene, - struct Object *ob_dst, - const struct Object *ob_src); +void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_src); void BKE_object_free_modifiers(struct Object *ob, const int flag); void BKE_object_free_shaderfx(struct Object *ob, const int flag); diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 9fd732027d1..881f3356a86 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -56,12 +56,12 @@ struct SubdivCCG; struct SubdivCCG; struct Tex; struct ToolSettings; -struct tPaletteColorHSV; struct UnifiedPaintSettings; struct View3D; struct ViewLayer; struct bContext; struct bToolRef; +struct tPaletteColorHSV; enum eOverlayFlags; diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 8018178fcee..f78c7b66bb4 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -78,7 +78,7 @@ void BKE_scene_remove_rigidbody_object(struct Main *bmain, const bool free_us); bool BKE_scene_object_find(struct Scene *scene, struct Object *ob); -struct Object *BKE_scene_object_find_by_name(struct Scene *scene, const char *name); +struct Object *BKE_scene_object_find_by_name(const struct Scene *scene, const char *name); /* Scene base iteration function. * Define struct here, so no need to bother with alloc/free it. @@ -118,8 +118,8 @@ struct Object *BKE_scene_camera_switch_find(struct Scene *scene); // DURIAN_CAM #endif bool BKE_scene_camera_switch_update(struct Scene *scene); -char *BKE_scene_find_marker_name(struct Scene *scene, int frame); -char *BKE_scene_find_last_marker_name(struct Scene *scene, int frame); +const char *BKE_scene_find_marker_name(const struct Scene *scene, int frame); +const char *BKE_scene_find_last_marker_name(const struct Scene *scene, int frame); int BKE_scene_frame_snap_by_seconds(struct Scene *scene, double interval_in_seconds, int cfra); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 2a45d89bad4..31951cc101a 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -393,7 +393,7 @@ void BKE_sequence_single_fix(struct Sequence *seq); bool BKE_sequence_test_overlap(struct ListBase *seqbasep, struct Sequence *test); void BKE_sequence_translate(struct Scene *scene, struct Sequence *seq, int delta); void BKE_sequence_sound_init(struct Scene *scene, struct Sequence *seq); -struct Sequence *BKE_sequencer_foreground_frame_get(struct Scene *scene, int frame); +const struct Sequence *BKE_sequencer_foreground_frame_get(const struct Scene *scene, int frame); struct ListBase *BKE_sequence_seqbase(struct ListBase *seqbase, struct Sequence *seq); struct Sequence *BKE_sequence_metastrip(ListBase *seqbase /* = ed->seqbase */, struct Sequence *meta /* = NULL */, diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index e6d931276bf..1298f25fcc4 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -105,7 +105,10 @@ typedef struct SoundInfo { bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *sound_info); #if defined(WITH_AUDASPACE) -AUD_Device *BKE_sound_mixdown(struct Scene *scene, AUD_DeviceSpecs specs, int start, float volume); +AUD_Device *BKE_sound_mixdown(const struct Scene *scene, + AUD_DeviceSpecs specs, + int start, + float volume); #endif void BKE_sound_reset_scene_runtime(struct Scene *scene); diff --git a/source/blender/blenkernel/BKE_writeavi.h b/source/blender/blenkernel/BKE_writeavi.h index 7fc740a4a9b..79605e99306 100644 --- a/source/blender/blenkernel/BKE_writeavi.h +++ b/source/blender/blenkernel/BKE_writeavi.h @@ -36,7 +36,7 @@ struct Scene; typedef struct bMovieHandle { int (*start_movie)(void *context_v, - struct Scene *scene, + const struct Scene *scene, struct RenderData *rd, int rectx, int recty, @@ -55,14 +55,20 @@ typedef struct bMovieHandle { void (*end_movie)(void *context_v); /* Optional function. */ - void (*get_movie_path)(char *string, struct RenderData *rd, bool preview, const char *suffix); + void (*get_movie_path)(char *string, + const struct RenderData *rd, + bool preview, + const char *suffix); void *(*context_create)(void); void (*context_free)(void *context_v); } bMovieHandle; bMovieHandle *BKE_movie_handle_get(const char imtype); -void BKE_movie_filepath_get(char *string, struct RenderData *rd, bool preview, const char *suffix); +void BKE_movie_filepath_get(char *string, + const struct RenderData *rd, + bool preview, + const char *suffix); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index 9da28c849d9..467e6ab7242 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -62,7 +62,7 @@ struct ReportList; struct Scene; int BKE_ffmpeg_start(void *context_v, - struct Scene *scene, + const struct Scene *scene, struct RenderData *rd, int rectx, int recty, @@ -80,14 +80,14 @@ int BKE_ffmpeg_append(void *context_v, const char *suffix, struct ReportList *reports); void BKE_ffmpeg_filepath_get(char *string, - struct RenderData *rd, + const struct RenderData *rd, bool preview, const char *suffix); void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset); void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf); void BKE_ffmpeg_codec_settings_verify(struct RenderData *rd); -bool BKE_ffmpeg_alpha_channel_is_supported(struct RenderData *rd); +bool BKE_ffmpeg_alpha_channel_is_supported(const struct RenderData *rd); int BKE_ffmpeg_property_add_string(struct RenderData *rd, const char *type, const char *str); void BKE_ffmpeg_property_del(struct RenderData *rd, void *type, void *prop_); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 112933e40be..923108240dd 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -168,7 +168,12 @@ set(SRC intern/modifier.c intern/movieclip.c intern/multires.c - intern/multires_reshape_legacy.c + intern/multires_reshape.c + intern/multires_reshape_apply_base.c + intern/multires_reshape_ccg.c + intern/multires_reshape_smooth.c + intern/multires_reshape_util.c + intern/multires_reshape_vertcos.c intern/multires_subdiv.c intern/nla.c intern/node.c @@ -378,6 +383,7 @@ set(SRC intern/data_transfer_intern.h intern/lib_intern.h intern/multires_inline.h + intern/multires_reshape.h intern/pbvh_intern.h intern/subdiv_converter.h intern/subdiv_inline.h diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index d1a6ebb222c..1a2d8290609 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -666,7 +666,7 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, /* don't move the camera, just yield the fit location */ /* r_scale only valid/useful for ortho cameras */ bool BKE_camera_view_frame_fit_to_scene( - Depsgraph *depsgraph, Scene *scene, Object *camera_ob, float r_co[3], float *r_scale) + Depsgraph *depsgraph, const Scene *scene, Object *camera_ob, float r_co[3], float *r_scale) { CameraParams params; CameraViewFrameData data_cb; @@ -813,7 +813,7 @@ static void camera_stereo3d_model_matrix(const Object *camera, } /* the view matrix is used by the viewport drawing, it is basically the inverted model matrix */ -void BKE_camera_multiview_view_matrix(RenderData *rd, +void BKE_camera_multiview_view_matrix(const RenderData *rd, const Object *camera, const bool is_left, float r_viewmat[4][4]) @@ -832,7 +832,7 @@ static bool camera_is_left(const char *viewname) return true; } -void BKE_camera_multiview_model_matrix(RenderData *rd, +void BKE_camera_multiview_model_matrix(const RenderData *rd, const Object *camera, const char *viewname, float r_modelmat[4][4]) @@ -841,7 +841,7 @@ void BKE_camera_multiview_model_matrix(RenderData *rd, normalize_m4(r_modelmat); } -void BKE_camera_multiview_model_matrix_scaled(RenderData *rd, +void BKE_camera_multiview_model_matrix_scaled(const RenderData *rd, const Object *camera, const char *viewname, float r_modelmat[4][4]) @@ -860,7 +860,7 @@ void BKE_camera_multiview_model_matrix_scaled(RenderData *rd, } } -void BKE_camera_multiview_window_matrix(RenderData *rd, +void BKE_camera_multiview_window_matrix(const RenderData *rd, const Object *camera, const char *viewname, float r_winmat[4][4]) @@ -879,7 +879,7 @@ void BKE_camera_multiview_window_matrix(RenderData *rd, copy_m4_m4(r_winmat, params.winmat); } -bool BKE_camera_multiview_spherical_stereo(RenderData *rd, const Object *camera) +bool BKE_camera_multiview_spherical_stereo(const RenderData *rd, const Object *camera) { Camera *cam; const bool is_multiview = (rd && rd->scemode & R_MULTIVIEW) != 0; @@ -903,9 +903,8 @@ bool BKE_camera_multiview_spherical_stereo(RenderData *rd, const Object *camera) return false; } -static Object *camera_multiview_advanced(Scene *scene, Object *camera, const char *suffix) +static Object *camera_multiview_advanced(const Scene *scene, Object *camera, const char *suffix) { - SceneRenderView *srv; char name[MAX_NAME]; const char *camera_name = camera->id.name + 2; const int len_name = strlen(camera_name); @@ -914,7 +913,7 @@ static Object *camera_multiview_advanced(Scene *scene, Object *camera, const cha name[0] = '\0'; /* we need to take the better match, thus the len_suffix_max test */ - for (srv = scene->r.views.first; srv; srv = srv->next) { + for (const SceneRenderView *srv = scene->r.views.first; srv; srv = srv->next) { const int len_suffix = strlen(srv->suffix); if ((len_suffix < len_suffix_max) || (len_name < len_suffix)) { @@ -938,7 +937,7 @@ static Object *camera_multiview_advanced(Scene *scene, Object *camera, const cha } /* returns the camera to be used for render */ -Object *BKE_camera_multiview_render(Scene *scene, Object *camera, const char *viewname) +Object *BKE_camera_multiview_render(const Scene *scene, Object *camera, const char *viewname) { const bool is_multiview = (camera != NULL) && (scene->r.scemode & R_MULTIVIEW) != 0; @@ -993,7 +992,9 @@ static float camera_stereo3d_shift_x(const Object *camera, const char *viewname) return shift; } -float BKE_camera_multiview_shift_x(RenderData *rd, const Object *camera, const char *viewname) +float BKE_camera_multiview_shift_x(const RenderData *rd, + const Object *camera, + const char *viewname) { const bool is_multiview = (rd && rd->scemode & R_MULTIVIEW) != 0; Camera *data = camera->data; @@ -1011,7 +1012,7 @@ float BKE_camera_multiview_shift_x(RenderData *rd, const Object *camera, const c } } -void BKE_camera_multiview_params(RenderData *rd, +void BKE_camera_multiview_params(const RenderData *rd, CameraParams *params, const Object *camera, const char *viewname) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index f38810322a8..3a86e3084d8 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -4822,7 +4822,7 @@ static bConstraintTypeInfo CTI_FOLLOWTRACK = { followtrack_evaluate, /* evaluate */ }; -/* ----------- Camre Solver ------------- */ +/* ----------- Camera Solver ------------- */ static void camerasolver_new_data(void *cdata) { diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 9919a0d7385..366137b5fa6 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -705,8 +705,7 @@ static void bb_allocateData(FluidObjectBB *bb, bool use_velocity, bool use_influ } bb->distances = MEM_malloc_arrayN(bb->total_cells, sizeof(float), "fluid_bb_distances"); - /* Initialize to infinity. */ - memset(bb->distances, 0x7f7f7f7f, sizeof(float) * bb->total_cells); + copy_vn_fl(bb->distances, bb->total_cells, FLT_MAX); bb->valid = true; } @@ -3272,12 +3271,12 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *mds, Mesh *orgmesh, Obj } me = BKE_mesh_new_nomain(num_verts, 0, 0, num_faces * 3, num_faces); - mverts = me->mvert; - mpolys = me->mpoly; - mloops = me->mloop; if (!me) { return NULL; } + mverts = me->mvert; + mpolys = me->mpoly; + mloops = me->mloop; /* Get size (dimension) but considering scaling scaling. */ copy_v3_v3(cell_size_scaled, mds->cell_size); @@ -3674,8 +3673,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, /* Reset fluid if no fluid present (obviously) * or if timeline gets reset to startframe */ - if (!mds->fluid || is_startframe) { + if (!mds->fluid) { BKE_fluid_modifier_reset_ex(mmd, false); + BKE_fluid_modifier_init(mmd, depsgraph, ob, scene, me); } /* Guiding parent res pointer needs initialization */ @@ -3687,8 +3687,6 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, } } - BKE_fluid_modifier_init(mmd, depsgraph, ob, scene, me); - /* ensure that time parameters are initialized correctly before every step */ float fps = scene->r.frs_sec / scene->r.frs_sec_base; mds->frame_length = DT_DEFAULT * (25.0f / fps) * mds->time_scale; @@ -3743,10 +3741,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, baking_mesh = mds->cache_flag & FLUID_DOMAIN_BAKING_MESH; baking_particles = mds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES; baking_guide = mds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE; - bake_outdated = mds->cache_flag & - (FLUID_DOMAIN_OUTDATED_DATA | FLUID_DOMAIN_OUTDATED_NOISE | - FLUID_DOMAIN_OUTDATED_NOISE | FLUID_DOMAIN_OUTDATED_MESH | - FLUID_DOMAIN_OUTDATED_PARTICLES | FLUID_DOMAIN_OUTDATED_GUIDE); + bake_outdated = mds->cache_flag & (FLUID_DOMAIN_OUTDATED_DATA | FLUID_DOMAIN_OUTDATED_NOISE | + FLUID_DOMAIN_OUTDATED_MESH | FLUID_DOMAIN_OUTDATED_PARTICLES | + FLUID_DOMAIN_OUTDATED_GUIDE); bool resume_data, resume_noise, resume_mesh, resume_particles, resume_guide; resume_data = (!is_startframe) && (mds->cache_frame_pause_data == scene_framenr); @@ -4897,7 +4894,6 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *mmd) mmd->flow->type = FLUID_FLOW_TYPE_SMOKE; mmd->flow->behavior = FLUID_FLOW_BEHAVIOR_GEOMETRY; - mmd->flow->type = FLUID_FLOW_TYPE_SMOKE; mmd->flow->flags = FLUID_FLOW_ABSOLUTE | FLUID_FLOW_USE_PART_SIZE | FLUID_FLOW_USE_INFLOW; } else if (mmd->type & MOD_FLUID_TYPE_EFFEC) { diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 6cd2d36c188..c0b40721ccc 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -1120,6 +1120,32 @@ void BKE_gpencil_layer_mask_sort_all(bGPdata *gpd) } } +static int gpencil_cb_cmp_frame(void *thunk, const void *a, const void *b) +{ + const bGPDframe *frame_a = a; + const bGPDframe *frame_b = b; + + if (frame_a->framenum < frame_b->framenum) { + return -1; + } + if (frame_a->framenum > frame_b->framenum) { + return 1; + } + if (thunk != NULL) { + *((bool *)thunk) = true; + } + /* Sort selected last. */ + if ((frame_a->flag & GP_FRAME_SELECT) && ((frame_b->flag & GP_FRAME_SELECT) == 0)) { + return 1; + } + return 0; +} + +void BKE_gpencil_layer_frames_sort(struct bGPDlayer *gpl, bool *r_has_duplicate_frames) +{ + BLI_listbase_sort_r(&gpl->frames, gpencil_cb_cmp_frame, r_has_duplicate_frames); +} + /* get the active gp-layer for editing */ bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd) { @@ -3909,6 +3935,9 @@ void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig /* Assign original point pointer. */ for (int i = 0; i < gps_orig->totpoints; i++) { + if (i > gps_eval->totpoints - 1) { + break; + } bGPDspoint *pt_eval = &gps_eval->points[i]; pt_eval->runtime.pt_orig = &gps_orig->points[i]; pt_eval->runtime.idx_orig = i; diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 4220408749a..22ec1fe1b98 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -145,8 +145,10 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c BLI_duplicatelist(&image_dst->tiles, &image_src->tiles); - for (int i = 0; i < TEXTARGET_COUNT; i++) { - image_dst->gputexture[i] = NULL; + for (int eye = 0; eye < 2; eye++) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + image_dst->gputexture[i][eye] = NULL; + } } if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) { @@ -529,9 +531,11 @@ bool BKE_image_scale(Image *image, int width, int height) bool BKE_image_has_opengl_texture(Image *ima) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i] != NULL) { - return true; + for (int eye = 0; eye < 2; eye++) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + if (ima->gputexture[i][eye] != NULL) { + return true; + } } } return false; @@ -1813,7 +1817,7 @@ typedef struct StampData { * \param use_dynamic: Also include data that can change on a per-frame basis. */ static void stampdata( - Scene *scene, Object *camera, StampData *stamp_data, int do_prefix, bool use_dynamic) + const Scene *scene, Object *camera, StampData *stamp_data, int do_prefix, bool use_dynamic) { char text[256]; struct tm *tl; @@ -1935,7 +1939,7 @@ static void stampdata( } if (use_dynamic && scene->r.stamp & R_STAMP_SEQSTRIP) { - Sequence *seq = BKE_sequencer_foreground_frame_get(scene, scene->r.cfra); + const Sequence *seq = BKE_sequencer_foreground_frame_get(scene, scene->r.cfra); if (seq) { STRNCPY(text, seq->name + 2); @@ -2479,7 +2483,7 @@ void BKE_render_result_stamp_info(Scene *scene, } } -struct StampData *BKE_stamp_info_from_scene_static(Scene *scene) +struct StampData *BKE_stamp_info_from_scene_static(const Scene *scene) { struct StampData *stamp_data; @@ -3318,9 +3322,11 @@ static void image_free_tile(Image *ima, ImageTile *tile) continue; } - if (ima->gputexture[i] != NULL) { - GPU_texture_free(ima->gputexture[i]); - ima->gputexture[i] = NULL; + for (int eye = 0; eye < 2; eye++) { + if (ima->gputexture[i][eye] != NULL) { + GPU_texture_free(ima->gputexture[i][eye]); + ima->gputexture[i][eye] = NULL; + } } } @@ -3587,14 +3593,16 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la BLI_strncpy(tile->label, label, sizeof(tile->label)); } - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY]); - ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY] = NULL; - } - if (ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING]); - ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING] = NULL; + 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_TEXTURE_TILE_MAPPING][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][eye] = NULL; + } } return tile; diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index c342185d0b8..27d4b8dd047 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -162,7 +162,8 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) { bNodeTree *ntree = NULL; Key *key = NULL; - const bool id_in_mainlist = (id->tag & LIB_TAG_NO_MAIN) == 0; + const bool id_in_mainlist = (id->tag & LIB_TAG_NO_MAIN) == 0 && + (id->flag & LIB_EMBEDDED_DATA) == 0; lib_id_library_local_paths(bmain, id->lib, id); diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index f0bab4c0aa2..ce8fcc4eeb9 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -1420,6 +1420,8 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, const CustomData_MeshMasks *mask, bool take_ownership) { + BLI_assert(mesh_src->id.tag & LIB_TAG_NO_MAIN); + /* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */ /* TODO(Sybren): the above claim came from 2.7x derived-mesh code (DM_to_mesh); * check whether it is still true with Mesh */ @@ -1571,6 +1573,8 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb) { + BLI_assert(mesh_src->id.tag & LIB_TAG_NO_MAIN); + int a, totvert = mesh_src->totvert; float *fp; MVert *mvert; diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 7b655b2d8fc..e14803e4193 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -3576,6 +3576,7 @@ void BKE_mesh_convert_mfaces_to_mpolys_ex(ID *id, if (id) { /* ensure external data is transferred */ + /* TODO(sergey): Use multiresModifier_ensure_external_read(). */ CustomData_external_read(fdata, id, CD_MASK_MDISPS, totface_i); } diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index f3d65f584d1..4e97d0fc05c 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -57,6 +57,8 @@ #include "DEG_depsgraph_query.h" +#include "multires_reshape.h" + #include <math.h> #include <string.h> @@ -645,7 +647,7 @@ static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl) GridPaintMask *gpm; multires_set_tot_mdisps(me, mmd->totlvl); - CustomData_external_read(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop); + multiresModifier_ensure_external_read(me, mmd); mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS); gpm = CustomData_get_layer(&me->ldata, CD_GRID_PAINT_MASK); @@ -711,7 +713,7 @@ void multiresModifier_del_levels(MultiresModifierData *mmd, MDisps *mdisps; multires_set_tot_mdisps(me, mmd->totlvl); - CustomData_external_read(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop); + multiresModifier_ensure_external_read(me, mmd); mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS); multires_force_sculpt_rebuild(ob); @@ -789,158 +791,7 @@ static DerivedMesh *subsurf_dm_create_local(Scene *scene, return subsurf_make_derived_from_derived(dm, &smd, scene, NULL, flags); } -/* assumes no is normalized; return value's sign is negative if v is on - * the other side of the plane */ -static float v3_dist_from_plane(float v[3], float center[3], float no[3]) -{ - float s[3]; - sub_v3_v3v3(s, v, center); - return dot_v3v3(s, no); -} - -void multiresModifier_base_apply(MultiresModifierData *mmd, Scene *scene, Object *ob) -{ - DerivedMesh *cddm, *dispdm, *origdm; - Mesh *me; - const MeshElemMap *pmap; - float(*origco)[3]; - int i, j, k, offset, totlvl; - - multires_force_sculpt_rebuild(ob); - - me = BKE_mesh_from_object(ob); - totlvl = mmd->totlvl; - - /* nothing to do */ - if (!totlvl) { - return; - } - - /* XXX - probably not necessary to regenerate the cddm so much? */ - - /* generate highest level with displacements */ - cddm = CDDM_from_mesh(me); - DM_set_only_copy(cddm, &CD_MASK_BAREMESH); - dispdm = multires_dm_create_local( - scene, ob, cddm, totlvl, totlvl, 0, 0, MULTIRES_IGNORE_SIMPLIFY); - cddm->release(cddm); - - /* copy the new locations of the base verts into the mesh */ - offset = dispdm->getNumVerts(dispdm) - me->totvert; - for (i = 0; i < me->totvert; i++) { - dispdm->getVertCo(dispdm, offset + i, me->mvert[i].co); - } - - /* heuristic to produce a better-fitting base mesh */ - - cddm = CDDM_from_mesh(me); - pmap = cddm->getPolyMap(ob, cddm); - origco = MEM_calloc_arrayN(me->totvert, 3 * sizeof(float), "multires apply base origco"); - for (i = 0; i < me->totvert; i++) { - copy_v3_v3(origco[i], me->mvert[i].co); - } - - for (i = 0; i < me->totvert; i++) { - float avg_no[3] = {0, 0, 0}, center[3] = {0, 0, 0}, push[3]; - float dist; - int tot = 0; - - /* don't adjust verts not used by at least one poly */ - if (!pmap[i].count) { - continue; - } - - /* find center */ - for (j = 0; j < pmap[i].count; j++) { - const MPoly *p = &me->mpoly[pmap[i].indices[j]]; - - /* this double counts, not sure if that's bad or good */ - for (k = 0; k < p->totloop; k++) { - int vndx = me->mloop[p->loopstart + k].v; - if (vndx != i) { - add_v3_v3(center, origco[vndx]); - tot++; - } - } - } - mul_v3_fl(center, 1.0f / tot); - - /* find normal */ - for (j = 0; j < pmap[i].count; j++) { - const MPoly *p = &me->mpoly[pmap[i].indices[j]]; - MPoly fake_poly; - MLoop *fake_loops; - float(*fake_co)[3]; - float no[3]; - - /* set up poly, loops, and coords in order to call - * BKE_mesh_calc_poly_normal_coords() */ - fake_poly.totloop = p->totloop; - fake_poly.loopstart = 0; - fake_loops = MEM_malloc_arrayN(p->totloop, sizeof(MLoop), "fake_loops"); - fake_co = MEM_malloc_arrayN(p->totloop, 3 * sizeof(float), "fake_co"); - - for (k = 0; k < p->totloop; k++) { - int vndx = me->mloop[p->loopstart + k].v; - - fake_loops[k].v = k; - - if (vndx == i) { - copy_v3_v3(fake_co[k], center); - } - else { - copy_v3_v3(fake_co[k], origco[vndx]); - } - } - - BKE_mesh_calc_poly_normal_coords(&fake_poly, fake_loops, (const float(*)[3])fake_co, no); - MEM_freeN(fake_loops); - MEM_freeN(fake_co); - - add_v3_v3(avg_no, no); - } - normalize_v3(avg_no); - - /* push vertex away from the plane */ - dist = v3_dist_from_plane(me->mvert[i].co, center, avg_no); - copy_v3_v3(push, avg_no); - mul_v3_fl(push, dist); - add_v3_v3(me->mvert[i].co, push); - } - - MEM_freeN(origco); - cddm->release(cddm); - - /* Vertices were moved around, need to update normals after all the vertices are updated - * Probably this is possible to do in the loop above, but this is rather tricky because - * we don't know all needed vertices' coordinates there yet. - */ - BKE_mesh_calc_normals(me); - - /* subdivide the mesh to highest level without displacements */ - cddm = CDDM_from_mesh(me); - DM_set_only_copy(cddm, &CD_MASK_BAREMESH); - origdm = subsurf_dm_create_local(scene, - ob, - cddm, - totlvl, - 0, - 0, - mmd->uv_smooth == SUBSURF_UV_SMOOTH_NONE, - 0, - false, - SUBSURF_IGNORE_SIMPLIFY); - cddm->release(cddm); - - /* calc disps */ - multiresModifier_disp_run( - dispdm, me, NULL, CALC_DISPLACEMENTS, origdm->getGridData(origdm), totlvl); - - origdm->release(origdm); - dispdm->release(dispdm); -} - -static void multires_subdivide( +static void multires_subdivide_legacy( MultiresModifierData *mmd, Scene *scene, Object *ob, int totlvl, int updateblock, int simple) { Mesh *me = ob->data; @@ -1038,10 +889,10 @@ static void multires_subdivide( multires_set_tot_level(ob, mmd, totlvl); } -void multiresModifier_subdivide( +void multiresModifier_subdivide_legacy( MultiresModifierData *mmd, Scene *scene, Object *ob, int updateblock, int simple) { - multires_subdivide(mmd, scene, ob, mmd->totlvl + 1, updateblock, simple); + multires_subdivide_legacy(mmd, scene, ob, mmd->totlvl + 1, updateblock, simple); } static void grid_tangent(const CCGKey *key, int x, int y, int axis, CCGElem *grid, float t[3]) @@ -1293,7 +1144,7 @@ void multires_modifier_update_mdisps(struct DerivedMesh *dm, Scene *scene) me = ccgdm->multires.ob->data; mmd = ccgdm->multires.mmd; multires_set_tot_mdisps(me, mmd->totlvl); - CustomData_external_read(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop); + multiresModifier_ensure_external_read(me, mmd); mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS); if (mdisps) { @@ -1521,7 +1372,7 @@ DerivedMesh *multires_make_derived_from_derived( } multires_set_tot_mdisps(me, mmd->totlvl); - CustomData_external_read(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop); + multiresModifier_ensure_external_read(me, mmd); /*run displacement*/ multiresModifier_disp_run(result, ob->data, dm, APPLY_DISPLACEMENTS, subGridData, mmd->totlvl); @@ -2277,7 +2128,8 @@ void multires_load_old(Object *ob, Mesh *me) multires_load_old_vcols(me); multires_load_old_face_flags(me); - /* multiresModifier_subdivide (actually, multires_subdivide) expects polys, not tessfaces! */ + /* multiresModifier_subdivide_legacy (actually, multires_subdivide_legacy) expects polys, not + * tessfaces! */ BKE_mesh_convert_mfaces_to_mpolys(me); /* Add a multires modifier to the object */ @@ -2289,7 +2141,7 @@ void multires_load_old(Object *ob, Mesh *me) BLI_insertlinkbefore(&ob->modifiers, md, mmd); for (i = 0; i < me->mr->level_count - 1; i++) { - multiresModifier_subdivide(mmd, NULL, ob, 1, 0); + multiresModifier_subdivide_legacy(mmd, NULL, ob, 1, 0); } mmd->lvl = mmd->totlvl; @@ -2314,8 +2166,7 @@ void multires_load_old(Object *ob, Mesh *me) /* If 'ob_src' and 'ob_dst' both have multires modifiers, synchronize them * such that 'ob_dst' has the same total number of levels as 'ob_src'. */ -void multiresModifier_sync_levels_ex(Scene *scene, - Object *ob_dst, +void multiresModifier_sync_levels_ex(Object *ob_dst, MultiresModifierData *mmd_src, MultiresModifierData *mmd_dst) { @@ -2324,7 +2175,7 @@ void multiresModifier_sync_levels_ex(Scene *scene, } if (mmd_src->totlvl > mmd_dst->totlvl) { - multires_subdivide(mmd_dst, scene, ob_dst, mmd_src->totlvl, false, mmd_dst->simple); + multiresModifier_subdivide_to_level(ob_dst, mmd_dst, mmd_src->totlvl); } else { multires_del_higher(mmd_dst, ob_dst, mmd_src->totlvl); @@ -2346,7 +2197,7 @@ static void multires_sync_levels(Scene *scene, Object *ob_src, Object *ob_dst) } if (mmd_src && mmd_dst) { - multiresModifier_sync_levels_ex(scene, ob_dst, mmd_src, mmd_dst); + multiresModifier_sync_levels_ex(ob_dst, mmd_src, mmd_dst); } } @@ -2373,7 +2224,7 @@ static void multires_apply_smat(struct Depsgraph *UNUSED(depsgraph), } /* Make sure layer present. */ Mesh *mesh = (Mesh *)object->data; - CustomData_external_read(&mesh->ldata, &mesh->id, CD_MASK_MDISPS, mesh->totloop); + multiresModifier_ensure_external_read(mesh, mmd); if (!CustomData_get_layer(&mesh->ldata, CD_MDISPS)) { return; } @@ -2467,6 +2318,44 @@ void multires_topology_changed(Mesh *me) } } +/* Makes sure data from an external file is fully read. + * + * Since the multires data files only contain displacement vectors without knowledge about + * subdivision level some extra work is needed. Namely make is to all displacement grids have + * proper level and number of displacement vectors set. */ +void multires_ensure_external_read(struct Mesh *mesh, int top_level) +{ + if (!CustomData_external_test(&mesh->ldata, CD_MDISPS)) { + return; + } + + MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS); + if (mdisps == NULL) { + mdisps = CustomData_add_layer(&mesh->ldata, CD_MDISPS, CD_DEFAULT, NULL, mesh->totloop); + } + + const int totloop = mesh->totloop; + + for (int i = 0; i < totloop; ++i) { + if (mdisps[i].level != top_level) { + MEM_SAFE_FREE(mdisps[i].disps); + } + + /* NOTE: CustomData_external_read will take care of allocation of displacement vectors if + * they are missing. */ + + const int totdisp = multires_grid_tot[top_level]; + mdisps[i].totdisp = totdisp; + mdisps[i].level = top_level; + } + + CustomData_external_read(&mesh->ldata, &mesh->id, CD_MASK_MDISPS, mesh->totloop); +} +void multiresModifier_ensure_external_read(struct Mesh *mesh, const MultiresModifierData *mmd) +{ + multires_ensure_external_read(mesh, mmd->totlvl); +} + /***************** Multires interpolation stuff *****************/ /* Find per-corner coordinate with given per-face UV coord */ diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c new file mode 100644 index 00000000000..a29398b24a0 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -0,0 +1,236 @@ +/* + * 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 bke + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BLI_math_vector.h" + +#include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_subdiv.h" + +#include "DEG_depsgraph_query.h" + +#include "multires_reshape.h" + +/* ================================================================================================ + * Reshape from object. + */ + +bool multiresModifier_reshapeFromVertcos(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd, + const float (*vert_coords)[3], + const int num_vert_coords) +{ + MultiresReshapeContext reshape_context; + if (!multires_reshape_context_create_from_object(&reshape_context, depsgraph, object, mmd)) { + return false; + } + multires_reshape_store_original_grids(&reshape_context); + multires_reshape_ensure_grids(object->data, reshape_context.top.level); + if (!multires_reshape_assign_final_coords_from_vertcos( + &reshape_context, vert_coords, num_vert_coords)) { + multires_reshape_context_free(&reshape_context); + return false; + } + multires_reshape_smooth_object_grids_with_details(&reshape_context); + multires_reshape_object_grids_to_tangent_displacement(&reshape_context); + multires_reshape_context_free(&reshape_context); + return true; +} + +/* Returns truth on success, false otherwise. + * + * This function might fail in cases like source and destination not having + * matched amount of vertices. */ +bool multiresModifier_reshapeFromObject(struct Depsgraph *depsgraph, + struct MultiresModifierData *mmd, + struct Object *dst, + struct Object *src) +{ + struct Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + struct Object *src_eval = DEG_get_evaluated_object(depsgraph, src); + Mesh *src_mesh_eval = mesh_get_eval_final(depsgraph, scene_eval, src_eval, &CD_MASK_BAREMESH); + + int num_deformed_verts; + float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(src_mesh_eval, &num_deformed_verts); + + const bool result = multiresModifier_reshapeFromVertcos( + depsgraph, dst, mmd, deformed_verts, num_deformed_verts); + + MEM_freeN(deformed_verts); + + return result; +} + +/* ================================================================================================ + * Reshape from modifier. + */ + +bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd, + struct ModifierData *deform_md) +{ + MultiresModifierData highest_mmd = *mmd; + highest_mmd.sculptlvl = highest_mmd.totlvl; + highest_mmd.lvl = highest_mmd.totlvl; + highest_mmd.renderlvl = highest_mmd.totlvl; + + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + + /* Create mesh for the multires, ignoring any further modifiers (leading + * deformation modifiers will be applied though). */ + Mesh *multires_mesh = BKE_multires_create_mesh(depsgraph, scene_eval, &highest_mmd, object); + int num_deformed_verts; + float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(multires_mesh, &num_deformed_verts); + + /* Apply deformation modifier on the multires, */ + const ModifierEvalContext modifier_ctx = { + .depsgraph = depsgraph, + .object = object, + .flag = MOD_APPLY_USECACHE | MOD_APPLY_IGNORE_SIMPLIFY, + }; + modwrap_deformVerts( + deform_md, &modifier_ctx, multires_mesh, deformed_verts, multires_mesh->totvert); + BKE_id_free(NULL, multires_mesh); + + /* Reshaping */ + bool result = multiresModifier_reshapeFromVertcos( + depsgraph, object, &highest_mmd, deformed_verts, num_deformed_verts); + + /* Cleanup */ + MEM_freeN(deformed_verts); + + return result; +} + +/* ================================================================================================ + * Reshape from grids. + */ + +bool multiresModifier_reshapeFromCCG(const int tot_level, + Mesh *coarse_mesh, + struct SubdivCCG *subdiv_ccg) +{ + MultiresReshapeContext reshape_context; + if (!multires_reshape_context_create_from_ccg( + &reshape_context, subdiv_ccg, coarse_mesh, tot_level)) { + return false; + } + + multires_ensure_external_read(coarse_mesh, reshape_context.top.level); + + multires_reshape_store_original_grids(&reshape_context); + multires_reshape_ensure_grids(coarse_mesh, reshape_context.top.level); + if (!multires_reshape_assign_final_coords_from_ccg(&reshape_context, subdiv_ccg)) { + multires_reshape_context_free(&reshape_context); + return false; + } + multires_reshape_smooth_object_grids_with_details(&reshape_context); + multires_reshape_object_grids_to_tangent_displacement(&reshape_context); + multires_reshape_context_free(&reshape_context); + return true; +} + +/* ================================================================================================ + * Subdivision. + */ + +void multiresModifier_subdivide(Object *object, MultiresModifierData *mmd) +{ + const int top_level = mmd->totlvl + 1; + multiresModifier_subdivide_to_level(object, mmd, top_level); +} + +void multiresModifier_subdivide_to_level(struct Object *object, + struct MultiresModifierData *mmd, + const int top_level) +{ + if (top_level <= mmd->totlvl) { + return; + } + + Mesh *coarse_mesh = object->data; + MultiresReshapeContext reshape_context; + + /* There was no multires at all, all displacement is at 0. Can simply make sure all mdisps grids + * are allocated at a proper level and return. */ + const bool has_mdisps = CustomData_has_layer(&coarse_mesh->ldata, CD_MDISPS); + if (!has_mdisps) { + CustomData_add_layer(&coarse_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, coarse_mesh->totloop); + } + if (!has_mdisps || top_level == 1) { + multires_reshape_ensure_grids(coarse_mesh, top_level); + multires_set_tot_level(object, mmd, top_level); + return; + } + + multires_flush_sculpt_updates(object); + + if (!multires_reshape_context_create_from_subdivide(&reshape_context, object, mmd, top_level)) { + return; + } + multires_reshape_store_original_grids(&reshape_context); + multires_reshape_ensure_grids(coarse_mesh, reshape_context.top.level); + multires_reshape_assign_final_coords_from_orig_mdisps(&reshape_context); + multires_reshape_smooth_object_grids(&reshape_context); + multires_reshape_object_grids_to_tangent_displacement(&reshape_context); + multires_reshape_context_free(&reshape_context); + + multires_set_tot_level(object, mmd, top_level); +} + +/* ================================================================================================ + * Apply base. + */ + +void multiresModifier_base_apply(struct Depsgraph *depsgraph, + Object *object, + MultiresModifierData *mmd) +{ + multires_force_sculpt_rebuild(object); + + MultiresReshapeContext reshape_context; + if (!multires_reshape_context_create_from_object(&reshape_context, depsgraph, object, mmd)) { + return; + } + + multires_reshape_assign_final_coords_from_mdisps(&reshape_context); + multires_reshape_apply_base_update_mesh_coords(&reshape_context); + multires_reshape_apply_base_refit_base_mesh(&reshape_context); + multires_reshape_apply_base_refine_subdiv(&reshape_context); + multires_reshape_object_grids_to_tangent_displacement(&reshape_context); + + multires_reshape_context_free(&reshape_context); +} diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h new file mode 100644 index 00000000000..9029d19ae88 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_reshape.h @@ -0,0 +1,327 @@ +/* + * 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 bke + */ + +#ifndef __BKE_INTERN_MULTIRES_RESHAPE_H__ +#define __BKE_INTERN_MULTIRES_RESHAPE_H__ + +#include "BLI_sys_types.h" + +struct Depsgraph; +struct GridPaintMask; +struct MDisps; +struct Mesh; +struct MultiresModifierData; +struct Object; +struct Subdiv; +struct SubdivCCG; + +typedef struct MultiresReshapeContext { + /* Base mesh from original object. + * NOTE: Does NOT include any leading modifiers in it. */ + struct Mesh *base_mesh; + + /* Subdivision surface created for multires modifier. + * + * The coarse mesh of this subdivision surface is a base mesh with all deformation modifiers + * leading multires applied on it. */ + struct Subdiv *subdiv; + bool need_free_subdiv; + + struct { + /* Level at which displacement is being assigned to. + * It will be propagated up from this level to top.level. */ + int level; + + /* Grid size for reshape.level. */ + int grid_size; + } reshape; + + struct { + /* Top level of the displacement grids. + * The displacement will be propagated up to this level. */ + int level; + + /* Grid size for top.level. */ + int grid_size; + } top; + + struct { + /* Copy of original displacement and painting masks. */ + struct MDisps *mdisps; + struct GridPaintMask *grid_paint_masks; + } orig; + + /* Number of grids which are required for base_mesh. */ + int num_grids; + + /* Destination displacement and mask. + * Points to a custom data on a destination mesh. */ + struct MDisps *mdisps; + struct GridPaintMask *grid_paint_masks; + + /* Indexed by face index, gives first grid index of the face. */ + int *face_start_grid_index; + + /* Indexed by grid index, contains face (poly) index in the base mesh from which the grid has + * been created (in other words, index of a poly which contains loop corresponding to the grid + * index). */ + int *grid_to_face_index; + + /* Indexed by ptex face index, gives first grid index of the ptex face. + * + * For non-quad base faces ptex face is created for every face corner, so it's similar to a + * grid in this case. In this case start grid index will be the only one for this ptex face. + * + * For quad base faces there is a single ptex face but 4 grids. So in this case there will be + * 4 grids for the ptex, starting at a value stored in this mapping. */ + int *ptex_start_grid_index; + + /* Indexed by base face index, returns first ptex face index corresponding + * to that base face. */ + int *face_ptex_offset; +} MultiresReshapeContext; + +/** + * Coordinate which identifies element of a grid. + * This is directly related on how #CD_MDISPS stores displacement. + */ +typedef struct GridCoord { + int grid_index; + float u, v; +} GridCoord; + +/** + * Coordinate within ptex, which is what OpenSubdiv API operates on. + */ +typedef struct PTexCoord { + int ptex_face_index; + float u, v; +} PTexCoord; + +/** + * Element of a grid data stored in the destination mesh. + * This is where reshaped coordinates and mask values will be written to. + */ +typedef struct ReshapeGridElement { + float *displacement; + float *mask; +} ReshapeGridElement; + +typedef struct ReshapeConstGridElement { + float displacement[3]; + float mask; +} ReshapeConstGridElement; + +/* -------------------------------------------------------------------- */ +/** \name Construct/destruct reshape context. + * \{ */ + +/* Create subdivision surface descriptor which is configured for surface evaluation at a given + * multires modifier. */ +struct Subdiv *multires_reshape_create_subdiv(struct Depsgraph *depsgraph, + struct Object *object, + const struct MultiresModifierData *mmd); + +bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context, + struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd); + +bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_context, + struct SubdivCCG *subdiv_ccg, + struct Mesh *base_mesh, + int top_level); + +bool multires_reshape_context_create_from_subdivide(MultiresReshapeContext *reshape_context, + struct Object *object, + struct MultiresModifierData *mmd, + int top_level); + +void multires_reshape_context_free(MultiresReshapeContext *reshape_context); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helper accessors. + * \{ */ + +/* For the given grid index get index of face it was created for. */ +int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context, + int grid_index); + +/* For the given grid index get corner of a face it was created for. */ +int multires_reshape_grid_to_corner(const MultiresReshapeContext *reshape_context, int grid_index); + +bool multires_reshape_is_quad_face(const MultiresReshapeContext *reshape_context, int face_index); + +/* For the given grid index get index of corresponding ptex face. */ +int multires_reshape_grid_to_ptex_index(const MultiresReshapeContext *reshape_context, + int grid_index); + +/* Convert normalized coordinate within a grid to a normalized coordinate within a ptex face. */ +PTexCoord multires_reshape_grid_coord_to_ptex(const MultiresReshapeContext *reshape_context, + const GridCoord *grid_coord); + +/* Convert a normalized coordinate within a ptex face to a normalized coordinate within a grid. */ +GridCoord multires_reshape_ptex_coord_to_grid(const MultiresReshapeContext *reshape_context, + const PTexCoord *ptex_coord); + +/* Calculate tangent matrix which converts displacement to a object vector. + * Is calculated for the given surface derivatives at a given base face corner. */ +void multires_reshape_tangent_matrix_for_corner(const MultiresReshapeContext *reshape_context, + const int face_index, + const int corner, + const float dPdu[3], + const float dPdv[3], + float r_tangent_matrix[3][3]); + +/* Get grid elements which are to be reshaped at a given or ptex coordinate. + * The data is coming from final custom mdata layers. */ +ReshapeGridElement multires_reshape_grid_element_for_grid_coord( + const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord); +ReshapeGridElement multires_reshape_grid_element_for_ptex_coord( + const MultiresReshapeContext *reshape_context, const PTexCoord *ptex_coord); + +/* Get original grid element for the given coordinate. */ +ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord( + const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sample limit surface of the base mesh. + * \{ */ + +/* Evaluate limit surface created from base mesh. + * This is the limit surface which defines tangent space for MDisps. */ +void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *reshape_context, + const GridCoord *grid_coord, + float r_P[3], + float r_tangent_matrix[3][3]); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom data preparation. + * \{ */ + +/* Make sure custom data is allocated for the given level. */ +void multires_reshape_ensure_grids(struct Mesh *mesh, const int level); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Functions specific to reshaping from a set of vertices in a object position. + * \{ */ + +/* Returns truth if all coordinates were assigned. + * + * False will be returned if the number of vertex coordinates did not match required number of + * vertices at a reshape level. */ +bool multires_reshape_assign_final_coords_from_vertcos( + const MultiresReshapeContext *reshape_context, + const float (*vert_coords)[3], + const int num_vert_coords); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Functions specific to reshaping from CCG. + * \{ */ + +/* NOTE: Displacement grids to be at least at a reshape level. + * + * Return truth if all coordinates have been updated. */ +bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext *reshape_context, + struct SubdivCCG *subdiv_ccg); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Functions specific to reshaping from MDISPS. + * \{ */ + +/* Reads and writes to the current mesh CD_MDISPS. */ +void multires_reshape_assign_final_coords_from_mdisps( + const MultiresReshapeContext *reshape_context); + +/* Reads from original CD_MIDTSPS, writes to the current mesh CD_MDISPS. */ +void multires_reshape_assign_final_coords_from_orig_mdisps( + const MultiresReshapeContext *reshape_context); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Displacement smooth. + * \{ */ + +/* Operates on a displacement grids (CD_MDISPS) which contains object space coordinates stored for + * the reshape level. + * + * The result is grids which are defining mesh with a smooth surface and details starting from + * reshape level up to top level added back from original displacement grids. */ +void multires_reshape_smooth_object_grids_with_details( + const MultiresReshapeContext *reshape_context); + +/* Operates on a displacement grids (CD_MDISPS) which contains object space-coordinates stored for + * the reshape level. + * + * Makes it so surface on top level looks smooth. Details are not preserved + */ +void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Displacement, space conversion. + * \{ */ + +/* Store original grid data, so then it's possible to calculate delta from it and add + * high-frequency content on top of reshaped grids. */ +void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context); + +void multires_reshape_object_grids_to_tangent_displacement( + const MultiresReshapeContext *reshape_context); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Apply base. + * \{ */ + +/* Update mesh coordinates to the final positions of displacement in object space. + * This is effectively desired position of base mesh vertices after canceling out displacement. + * + * NOTE: Expects that mesh's CD_MDISPS has been set to object space positions. */ +void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *reshape_context); + +/* Perform better fitting of the base mesh so its subdivided version brings vertices to their + * desired locations. */ +void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape_context); + +/* Refine subdivision surface to the new positions of the base mesh. */ +void multires_reshape_apply_base_refine_subdiv(MultiresReshapeContext *reshape_context); + +/** \} */ + +#endif /* __BKE_INTERN_MULTIRES_RESHAPE_H__ */ diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.c b/source/blender/blenkernel/intern/multires_reshape_apply_base.c new file mode 100644 index 00000000000..e05b5bb3179 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.c @@ -0,0 +1,158 @@ +/* + * 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 bke + */ + +#include "multires_reshape.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math_vector.h" + +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_subdiv_eval.h" + +void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *reshape_context) +{ + Mesh *base_mesh = reshape_context->base_mesh; + const int grid_size = reshape_context->top.grid_size; + const int grid_index = grid_size * grid_size - 1; + for (int i = 0; i < base_mesh->totloop; ++i) { + MDisps *displacement_grid = &reshape_context->mdisps[i]; + const MLoop *loop = &base_mesh->mloop[i]; + MVert *vert = &base_mesh->mvert[loop->v]; + copy_v3_v3(vert->co, displacement_grid->disps[grid_index]); + } +} + +/* Assumes no is normalized; return value's sign is negative if v is on the other side of the + * plane. */ +static float v3_dist_from_plane(float v[3], float center[3], float no[3]) +{ + float s[3]; + sub_v3_v3v3(s, v, center); + return dot_v3v3(s, no); +} + +void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape_context) +{ + Mesh *base_mesh = reshape_context->base_mesh; + + MeshElemMap *pmap; + int *pmap_mem; + BKE_mesh_vert_poly_map_create(&pmap, + &pmap_mem, + base_mesh->mpoly, + base_mesh->mloop, + base_mesh->totvert, + base_mesh->totpoly, + base_mesh->totloop); + + float(*origco)[3] = MEM_calloc_arrayN( + base_mesh->totvert, 3 * sizeof(float), "multires apply base origco"); + for (int i = 0; i < base_mesh->totvert; i++) { + copy_v3_v3(origco[i], base_mesh->mvert[i].co); + } + + for (int i = 0; i < base_mesh->totvert; i++) { + float avg_no[3] = {0, 0, 0}, center[3] = {0, 0, 0}, push[3]; + + /* Don't adjust vertices not used by at least one poly. */ + if (!pmap[i].count) { + continue; + } + + /* Find center. */ + int tot = 0; + for (int j = 0; j < pmap[i].count; j++) { + const MPoly *p = &base_mesh->mpoly[pmap[i].indices[j]]; + + /* This double counts, not sure if that's bad or good. */ + for (int k = 0; k < p->totloop; k++) { + const int vndx = base_mesh->mloop[p->loopstart + k].v; + if (vndx != i) { + add_v3_v3(center, origco[vndx]); + tot++; + } + } + } + mul_v3_fl(center, 1.0f / tot); + + /* Find normal. */ + for (int j = 0; j < pmap[i].count; j++) { + const MPoly *p = &base_mesh->mpoly[pmap[i].indices[j]]; + MPoly fake_poly; + MLoop *fake_loops; + float(*fake_co)[3]; + float no[3]; + + /* Set up poly, loops, and coords in order to call BKE_mesh_calc_poly_normal_coords(). */ + fake_poly.totloop = p->totloop; + fake_poly.loopstart = 0; + fake_loops = MEM_malloc_arrayN(p->totloop, sizeof(MLoop), "fake_loops"); + fake_co = MEM_malloc_arrayN(p->totloop, 3 * sizeof(float), "fake_co"); + + for (int k = 0; k < p->totloop; k++) { + const int vndx = base_mesh->mloop[p->loopstart + k].v; + + fake_loops[k].v = k; + + if (vndx == i) { + copy_v3_v3(fake_co[k], center); + } + else { + copy_v3_v3(fake_co[k], origco[vndx]); + } + } + + BKE_mesh_calc_poly_normal_coords(&fake_poly, fake_loops, (const float(*)[3])fake_co, no); + MEM_freeN(fake_loops); + MEM_freeN(fake_co); + + add_v3_v3(avg_no, no); + } + normalize_v3(avg_no); + + /* Push vertex away from the plane. */ + const float dist = v3_dist_from_plane(base_mesh->mvert[i].co, center, avg_no); + copy_v3_v3(push, avg_no); + mul_v3_fl(push, dist); + add_v3_v3(base_mesh->mvert[i].co, push); + } + + MEM_freeN(origco); + MEM_freeN(pmap); + MEM_freeN(pmap_mem); + + /* Vertices were moved around, need to update normals after all the vertices are updated + * Probably this is possible to do in the loop above, but this is rather tricky because + * we don't know all needed vertices' coordinates there yet. */ + BKE_mesh_calc_normals(base_mesh); +} + +void multires_reshape_apply_base_refine_subdiv(MultiresReshapeContext *reshape_context) +{ + BKE_subdiv_eval_update_from_mesh(reshape_context->subdiv, reshape_context->base_mesh, NULL); +} diff --git a/source/blender/blenkernel/intern/multires_reshape_ccg.c b/source/blender/blenkernel/intern/multires_reshape_ccg.c new file mode 100644 index 00000000000..1f8c782ed46 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_reshape_ccg.c @@ -0,0 +1,72 @@ +/* + * 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 bke + */ + +#include "multires_reshape.h" + +#include <string.h> + +#include "BLI_utildefines.h" + +#include "BKE_ccg.h" +#include "BKE_subdiv_ccg.h" + +bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext *reshape_context, + struct SubdivCCG *subdiv_ccg) +{ + CCGKey reshape_level_key; + BKE_subdiv_ccg_key(&reshape_level_key, subdiv_ccg, reshape_context->reshape.level); + + const int reshape_grid_size = reshape_context->reshape.grid_size; + const float reshape_grid_size_1_inv = 1.0f / (((float)reshape_grid_size) - 1.0f); + + int num_grids = subdiv_ccg->num_grids; + for (int grid_index = 0; grid_index < num_grids; ++grid_index) { + CCGElem *ccg_grid = subdiv_ccg->grids[grid_index]; + for (int y = 0; y < reshape_grid_size; ++y) { + const float v = (float)y * reshape_grid_size_1_inv; + for (int x = 0; x < reshape_grid_size; ++x) { + const float u = (float)x * reshape_grid_size_1_inv; + + GridCoord grid_coord; + grid_coord.grid_index = grid_index; + grid_coord.u = u; + grid_coord.v = v; + + ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord( + reshape_context, &grid_coord); + + BLI_assert(grid_element.displacement != NULL); + memcpy(grid_element.displacement, + CCG_grid_elem_co(&reshape_level_key, ccg_grid, x, y), + sizeof(float) * 3); + + if (reshape_level_key.has_mask) { + BLI_assert(grid_element.mask != NULL); + *grid_element.mask = *CCG_grid_elem_mask(&reshape_level_key, ccg_grid, x, y); + } + } + } + } + + return true; +} diff --git a/source/blender/blenkernel/intern/multires_reshape_legacy.c b/source/blender/blenkernel/intern/multires_reshape_legacy.c deleted file mode 100644 index 0a8248b25f1..00000000000 --- a/source/blender/blenkernel/intern/multires_reshape_legacy.c +++ /dev/null @@ -1,1056 +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) 2018 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup bke - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_scene_types.h" - -#include "BLI_utildefines.h" -#include "BLI_math_vector.h" -#include "BLI_task.h" - -#include "BKE_ccg.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_modifier.h" -#include "BKE_multires.h" -#include "BKE_subdiv.h" -#include "BKE_subdiv_ccg.h" -#include "BKE_subdiv_eval.h" -#include "BKE_subdiv_foreach.h" -#include "BKE_subdiv_mesh.h" - -#include "DEG_depsgraph_query.h" - -static void multires_reshape_init_mmd(MultiresModifierData *reshape_mmd, - const MultiresModifierData *mmd) -{ - *reshape_mmd = *mmd; -} - -static void multires_reshape_init_mmd_top_level(MultiresModifierData *reshape_mmd, - const MultiresModifierData *mmd) -{ - *reshape_mmd = *mmd; - reshape_mmd->lvl = reshape_mmd->totlvl; -} - -/* ============================================================================= - * General reshape implementation, reused by all particular cases. - */ - -typedef struct MultiresReshapeContext { - Subdiv *subdiv; - const Mesh *coarse_mesh; - MDisps *mdisps; - GridPaintMask *grid_paint_mask; - int top_grid_size; - int top_level; - /* Indexed by coarse face index, returns first ptex face index corresponding - * to that coarse face. */ - int *face_ptex_offset; -} MultiresReshapeContext; - -static void multires_reshape_allocate_displacement_grid(MDisps *displacement_grid, const int level) -{ - const int grid_size = BKE_subdiv_grid_size_from_level(level); - const int grid_area = grid_size * grid_size; - float(*disps)[3] = MEM_calloc_arrayN(grid_area, 3 * sizeof(float), "multires disps"); - if (displacement_grid->disps != NULL) { - MEM_freeN(displacement_grid->disps); - } - displacement_grid->disps = disps; - displacement_grid->totdisp = grid_area; - displacement_grid->level = level; -} - -static void multires_reshape_ensure_displacement_grid(MDisps *displacement_grid, const int level) -{ - if (displacement_grid->disps != NULL && displacement_grid->level == level) { - return; - } - multires_reshape_allocate_displacement_grid(displacement_grid, level); -} - -static void multires_reshape_ensure_displacement_grids(Mesh *mesh, const int grid_level) -{ - const int num_grids = mesh->totloop; - MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS); - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - multires_reshape_ensure_displacement_grid(&mdisps[grid_index], grid_level); - } -} - -static void multires_reshape_ensure_mask_grids(Mesh *mesh, const int grid_level) -{ - GridPaintMask *grid_paint_masks = CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK); - if (grid_paint_masks == NULL) { - return; - } - const int num_grids = mesh->totloop; - const int grid_size = BKE_subdiv_grid_size_from_level(grid_level); - const int grid_area = grid_size * grid_size; - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - GridPaintMask *grid_paint_mask = &grid_paint_masks[grid_index]; - if (grid_paint_mask->level == grid_level) { - continue; - } - grid_paint_mask->level = grid_level; - if (grid_paint_mask->data) { - MEM_freeN(grid_paint_mask->data); - } - grid_paint_mask->data = MEM_calloc_arrayN(grid_area, sizeof(float), "gpm.data"); - } -} - -static void multires_reshape_ensure_grids(Mesh *mesh, const int grid_level) -{ - multires_reshape_ensure_displacement_grids(mesh, grid_level); - multires_reshape_ensure_mask_grids(mesh, grid_level); -} - -/* Convert normalized coordinate within a grid to a normalized coordinate within - * a ptex face. */ -static void multires_reshape_corner_coord_to_ptex(const MPoly *coarse_poly, - const int corner, - const float corner_u, - const float corner_v, - float *r_ptex_face_u, - float *r_ptex_face_v) -{ - if (coarse_poly->totloop == 4) { - float grid_u, grid_v; - BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v); - BKE_subdiv_rotate_grid_to_quad(corner, grid_u, grid_v, r_ptex_face_u, r_ptex_face_v); - } - else { - *r_ptex_face_u = corner_u; - *r_ptex_face_v = corner_v; - } -} - -/* NOTE: The tangent vectors are measured in ptex face normalized coordinates, - * which is different from grid tangent. */ -static void multires_reshape_sample_surface(Subdiv *subdiv, - const MPoly *coarse_poly, - const int corner, - const float corner_u, - const float corner_v, - const int ptex_face_index, - float r_P[3], - float r_dPdu[3], - float r_dPdv[3]) -{ - float ptex_face_u, ptex_face_v; - multires_reshape_corner_coord_to_ptex( - coarse_poly, corner, corner_u, corner_v, &ptex_face_u, &ptex_face_v); - BKE_subdiv_eval_limit_point_and_derivatives( - subdiv, ptex_face_index, ptex_face_u, ptex_face_v, r_P, r_dPdu, r_dPdv); -} - -static void multires_reshape_tangent_matrix_for_corner(const MPoly *coarse_poly, - const int coarse_corner, - const float dPdu[3], - const float dPdv[3], - float r_tangent_matrix[3][3]) -{ - /* For a quad faces we would need to flip the tangent, since they will use - * use different coordinates within displacement grid comparent to ptex - * face. */ - const bool is_quad = (coarse_poly->totloop == 4); - const int tangent_corner = is_quad ? coarse_corner : 0; - BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, tangent_corner); -} - -static void multires_reshape_vertex_from_final_data(MultiresReshapeContext *ctx, - const int ptex_face_index, - const float corner_u, - const float corner_v, - const int coarse_poly_index, - const int coarse_corner, - const float final_P[3], - const float final_mask) -{ - Subdiv *subdiv = ctx->subdiv; - const int grid_size = ctx->top_grid_size; - const Mesh *coarse_mesh = ctx->coarse_mesh; - const MPoly *coarse_mpoly = coarse_mesh->mpoly; - const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index]; - const int loop_index = coarse_poly->loopstart + coarse_corner; - /* Evaluate limit surface. */ - float P[3], dPdu[3], dPdv[3]; - multires_reshape_sample_surface( - subdiv, coarse_poly, coarse_corner, corner_u, corner_v, ptex_face_index, P, dPdu, dPdv); - /* Construct tangent matrix which matches orientation of the current - * displacement grid. */ - float tangent_matrix[3][3], inv_tangent_matrix[3][3]; - multires_reshape_tangent_matrix_for_corner( - coarse_poly, coarse_corner, dPdu, dPdv, tangent_matrix); - invert_m3_m3(inv_tangent_matrix, tangent_matrix); - /* Convert object coordinate to a tangent space of displacement grid. */ - float D[3]; - sub_v3_v3v3(D, final_P, P); - float tangent_D[3]; - mul_v3_m3v3(tangent_D, inv_tangent_matrix, D); - /* Calculate index of element within the grid. */ - float grid_u, grid_v; - BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v); - const int grid_x = (grid_u * (grid_size - 1) + 0.5f); - const int grid_y = (grid_v * (grid_size - 1) + 0.5f); - const int index = grid_y * grid_size + grid_x; - /* Write tangent displacement. */ - MDisps *displacement_grid = &ctx->mdisps[loop_index]; - copy_v3_v3(displacement_grid->disps[index], tangent_D); - /* Write mask grid. */ - if (ctx->grid_paint_mask != NULL) { - GridPaintMask *grid_paint_mask = &ctx->grid_paint_mask[loop_index]; - BLI_assert(grid_paint_mask->level == displacement_grid->level); - grid_paint_mask->data[index] = final_mask; - } -} - -/* ============================================================================= - * Helpers to propagate displacement to higher levels. - */ - -typedef struct MultiresPropagateData { - /* Number of displacement grids. */ - int num_grids; - /* Resolution level up to which displacement is known. */ - int reshape_level; - /* Resolution up to which propagation is happening, affecting all the - * levels in [reshape_level + 1, top_level]. */ - int top_level; - /* Grid sizes at the corresponding levels. */ - int reshape_grid_size; - int top_grid_size; - /* Keys to access CCG at different levels. */ - CCGKey reshape_level_key; - CCGKey top_level_key; - /* Original grid data, before any updates for reshape. - * Contains data at the reshape_level resolution level. */ - CCGElem **orig_grids_data; - /* Custom data layers from a coarse mesh. */ - MDisps *mdisps; - GridPaintMask *grid_paint_mask; -} MultiresPropagateData; - -static CCGElem **allocate_grids(CCGKey *key, int num_grids) -{ - CCGElem **grids = MEM_calloc_arrayN(num_grids, sizeof(CCGElem *), "reshape grids*"); - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - grids[grid_index] = MEM_calloc_arrayN( - key->elem_size, key->grid_area, "reshape orig_grids_data elems"); - } - return grids; -} - -static void free_grids(CCGElem **grids, int num_grids) -{ - if (grids == NULL) { - return; - } - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - MEM_freeN(grids[grid_index]); - } - MEM_freeN(grids); -} - -/* Initialize element sizes and offsets. */ -static void multires_reshape_init_key_layers(CCGKey *key, const MultiresPropagateData *data) -{ - key->elem_size = 3 * sizeof(float); - if (data->grid_paint_mask != NULL) { - key->mask_offset = 3 * sizeof(float); - key->elem_size += sizeof(float); - key->has_mask = true; - } - else { - key->mask_offset = -1; - key->has_mask = false; - } - /* We never have normals in original grids. */ - key->normal_offset = -1; - key->has_normals = false; -} - -/* Initialize key used to access reshape grids at given level. */ -static void multires_reshape_init_level_key(CCGKey *key, - const MultiresPropagateData *data, - const int level) -{ - key->level = level; - /* Init layers. */ - multires_reshape_init_key_layers(key, data); - /* By default, only 3 floats for coordinate, */ - key->grid_size = BKE_subdiv_grid_size_from_level(key->level); - key->grid_area = key->grid_size * key->grid_size; - key->grid_bytes = key->elem_size * key->grid_area; -} - -static void multires_reshape_store_original_grids(MultiresPropagateData *data) -{ - const int num_grids = data->num_grids; - /* Original data to be backed up. */ - const MDisps *mdisps = data->mdisps; - const GridPaintMask *grid_paint_mask = data->grid_paint_mask; - /* Allocate grids for backup. */ - CCGKey *orig_key = &data->reshape_level_key; - CCGElem **orig_grids_data = allocate_grids(orig_key, num_grids); - /* Fill in grids. */ - const int orig_grid_size = data->reshape_grid_size; - const int top_grid_size = data->top_grid_size; - const int skip = (top_grid_size - 1) / (orig_grid_size - 1); - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - CCGElem *orig_grid = orig_grids_data[grid_index]; - for (int y = 0; y < orig_grid_size; y++) { - const int top_y = y * skip; - for (int x = 0; x < orig_grid_size; x++) { - const int top_x = x * skip; - const int top_index = top_y * top_grid_size + top_x; - memcpy(CCG_grid_elem_co(orig_key, orig_grid, x, y), - mdisps[grid_index].disps[top_index], - sizeof(float) * 3); - if (orig_key->has_mask) { - *CCG_grid_elem_mask( - orig_key, orig_grid, x, y) = grid_paint_mask[grid_index].data[top_index]; - } - } - } - } - /* Store in the context. */ - data->orig_grids_data = orig_grids_data; -} - -static void multires_reshape_propagate_prepare(MultiresPropagateData *data, - Mesh *coarse_mesh, - const int reshape_level, - const int top_level) -{ - BLI_assert(reshape_level <= top_level); - memset(data, 0, sizeof(*data)); - data->num_grids = coarse_mesh->totloop; - data->reshape_level = reshape_level; - data->top_level = top_level; - if (reshape_level == top_level) { - /* Nothing to do, reshape will happen on the whole grid content. */ - return; - } - data->mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS); - data->grid_paint_mask = CustomData_get_layer(&coarse_mesh->ldata, CD_GRID_PAINT_MASK); - data->top_grid_size = BKE_subdiv_grid_size_from_level(top_level); - data->reshape_grid_size = BKE_subdiv_grid_size_from_level(reshape_level); - /* Initialize keys to access CCG at different levels. */ - multires_reshape_init_level_key(&data->reshape_level_key, data, data->reshape_level); - multires_reshape_init_level_key(&data->top_level_key, data, data->top_level); - /* Make a copy of grids before reshaping, so we can calculate deltas - * later on. */ - multires_reshape_store_original_grids(data); -} - -static void multires_reshape_propagate_prepare_from_mmd(MultiresPropagateData *data, - struct Depsgraph *depsgraph, - Object *object, - const MultiresModifierData *mmd, - const int top_level, - const bool use_render_params) -{ - /* TODO(sergey): Find mode reliable way of getting current level. */ - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Mesh *mesh = object->data; - const int level = multires_get_level(scene_eval, object, mmd, use_render_params, true); - multires_reshape_propagate_prepare(data, mesh, level, top_level); -} - -/* Calculate delta of changed reshape level data layers. Delta goes to a - * grids at top level (meaning, the result grids are only partially filled - * in). */ -static void multires_reshape_calculate_delta(MultiresPropagateData *data, - CCGElem **delta_grids_data) -{ - const int num_grids = data->num_grids; - /* At this point those custom data layers has updated data for the - * level we are propagating from. */ - const MDisps *mdisps = data->mdisps; - const GridPaintMask *grid_paint_mask = data->grid_paint_mask; - CCGKey *reshape_key = &data->reshape_level_key; - CCGKey *delta_level_key = &data->top_level_key; - /* Calculate delta. */ - const int top_grid_size = data->top_grid_size; - const int reshape_grid_size = data->reshape_grid_size; - const int delta_grid_size = data->top_grid_size; - const int skip = (top_grid_size - 1) / (reshape_grid_size - 1); - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - /*const*/ CCGElem *orig_grid = data->orig_grids_data[grid_index]; - CCGElem *delta_grid = delta_grids_data[grid_index]; - for (int y = 0; y < reshape_grid_size; y++) { - const int top_y = y * skip; - for (int x = 0; x < reshape_grid_size; x++) { - const int top_x = x * skip; - const int top_index = top_y * delta_grid_size + top_x; - sub_v3_v3v3(CCG_grid_elem_co(delta_level_key, delta_grid, top_x, top_y), - mdisps[grid_index].disps[top_index], - CCG_grid_elem_co(reshape_key, orig_grid, x, y)); - if (delta_level_key->has_mask) { - const float old_mask_value = *CCG_grid_elem_mask(reshape_key, orig_grid, x, y); - const float new_mask_value = grid_paint_mask[grid_index].data[top_index]; - *CCG_grid_elem_mask(delta_level_key, delta_grid, top_x, top_y) = new_mask_value - - old_mask_value; - } - } - } - } -} - -/* Makes it so delta is propagated onto all the higher levels, but is also - * that this delta is smoothed in a way that it does not cause artifacts on - * boundaries. */ - -typedef struct MultiresPropagateCornerData { - float coord_delta[3]; - float mask_delta; -} MultiresPropagateCornerData; - -BLI_INLINE void multires_reshape_propagate_init_patch_corners( - MultiresPropagateData *data, - CCGElem *delta_grid, - const int patch_x, - const int patch_y, - MultiresPropagateCornerData r_corners[4]) -{ - CCGKey *delta_level_key = &data->top_level_key; - const int orig_grid_size = data->reshape_grid_size; - const int top_grid_size = data->top_grid_size; - const int skip = (top_grid_size - 1) / (orig_grid_size - 1); - const int x = patch_x * skip; - const int y = patch_y * skip; - /* Store coordinate deltas. */ - copy_v3_v3(r_corners[0].coord_delta, CCG_grid_elem_co(delta_level_key, delta_grid, x, y)); - copy_v3_v3(r_corners[1].coord_delta, CCG_grid_elem_co(delta_level_key, delta_grid, x + skip, y)); - copy_v3_v3(r_corners[2].coord_delta, CCG_grid_elem_co(delta_level_key, delta_grid, x, y + skip)); - copy_v3_v3(r_corners[3].coord_delta, - CCG_grid_elem_co(delta_level_key, delta_grid, x + skip, y + skip)); - if (delta_level_key->has_mask) { - r_corners[0].mask_delta = *CCG_grid_elem_mask(delta_level_key, delta_grid, x, y); - r_corners[1].mask_delta = *CCG_grid_elem_mask(delta_level_key, delta_grid, x + skip, y); - r_corners[2].mask_delta = *CCG_grid_elem_mask(delta_level_key, delta_grid, x, y + skip); - r_corners[3].mask_delta = *CCG_grid_elem_mask(delta_level_key, delta_grid, x + skip, y + skip); - } -} - -BLI_INLINE void multires_reshape_propagate_interpolate_coord( - float delta[3], const MultiresPropagateCornerData corners[4], const float weights[4]) -{ - interp_v3_v3v3v3v3(delta, - corners[0].coord_delta, - corners[1].coord_delta, - corners[2].coord_delta, - corners[3].coord_delta, - weights); -} - -BLI_INLINE float multires_reshape_propagate_interpolate_mask( - const MultiresPropagateCornerData corners[4], const float weights[4]) -{ - return corners[0].mask_delta * weights[0] + corners[1].mask_delta * weights[1] + - corners[2].mask_delta * weights[2] + corners[3].mask_delta * weights[3]; -} - -BLI_INLINE void multires_reshape_propagate_and_smooth_delta_grid_patch(MultiresPropagateData *data, - CCGElem *delta_grid, - const int patch_x, - const int patch_y) -{ - CCGKey *delta_level_key = &data->top_level_key; - const int orig_grid_size = data->reshape_grid_size; - const int top_grid_size = data->top_grid_size; - const int skip = (top_grid_size - 1) / (orig_grid_size - 1); - const float skip_inv = 1.0f / (float)skip; - MultiresPropagateCornerData corners[4]; - multires_reshape_propagate_init_patch_corners(data, delta_grid, patch_x, patch_y, corners); - const int start_x = patch_x * skip; - const int start_y = patch_y * skip; - for (int y = 0; y <= skip; y++) { - const float v = (float)y * skip_inv; - const int final_y = start_y + y; - for (int x = 0; x <= skip; x++) { - const float u = (float)x * skip_inv; - const int final_x = start_x + x; - const float linear_weights[4] = { - (1.0f - u) * (1.0f - v), u * (1.0f - v), (1.0f - u) * v, u * v}; - multires_reshape_propagate_interpolate_coord( - CCG_grid_elem_co(delta_level_key, delta_grid, final_x, final_y), - corners, - linear_weights); - if (delta_level_key->has_mask) { - float *mask = CCG_grid_elem_mask(delta_level_key, delta_grid, final_x, final_y); - *mask = multires_reshape_propagate_interpolate_mask(corners, linear_weights); - } - } - } -} - -BLI_INLINE void multires_reshape_propagate_and_smooth_delta_grid(MultiresPropagateData *data, - CCGElem *delta_grid) -{ - const int orig_grid_size = data->reshape_grid_size; - for (int patch_y = 0; patch_y < orig_grid_size - 1; patch_y++) { - for (int patch_x = 0; patch_x < orig_grid_size - 1; patch_x++) { - multires_reshape_propagate_and_smooth_delta_grid_patch(data, delta_grid, patch_x, patch_y); - } - } -} - -/* Entry point to propagate+smooth. */ -static void multires_reshape_propagate_and_smooth_delta(MultiresPropagateData *data, - CCGElem **delta_grids_data) -{ - const int num_grids = data->num_grids; - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - CCGElem *delta_grid = delta_grids_data[grid_index]; - multires_reshape_propagate_and_smooth_delta_grid(data, delta_grid); - } -} - -/* Apply smoothed deltas on the actual data layers. */ -static void multires_reshape_propagate_apply_delta(MultiresPropagateData *data, - CCGElem **delta_grids_data) -{ - const int num_grids = data->num_grids; - /* At this point those custom data layers has updated data for the - * level we are propagating from. */ - MDisps *mdisps = data->mdisps; - GridPaintMask *grid_paint_mask = data->grid_paint_mask; - CCGKey *orig_key = &data->reshape_level_key; - CCGKey *delta_level_key = &data->top_level_key; - CCGElem **orig_grids_data = data->orig_grids_data; - const int orig_grid_size = data->reshape_grid_size; - const int top_grid_size = data->top_grid_size; - const int skip = (top_grid_size - 1) / (orig_grid_size - 1); - /* Restore grid values at the reshape level. Those values are to be changed - * to the accommodate for the smooth delta. */ - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - CCGElem *orig_grid = orig_grids_data[grid_index]; - for (int y = 0; y < orig_grid_size; y++) { - const int top_y = y * skip; - for (int x = 0; x < orig_grid_size; x++) { - const int top_x = x * skip; - const int top_index = top_y * top_grid_size + top_x; - copy_v3_v3(mdisps[grid_index].disps[top_index], - CCG_grid_elem_co(orig_key, orig_grid, x, y)); - if (grid_paint_mask != NULL) { - grid_paint_mask[grid_index].data[top_index] = *CCG_grid_elem_mask( - orig_key, orig_grid, x, y); - } - } - } - } - /* Add smoothed delta to all the levels. */ - for (int grid_index = 0; grid_index < num_grids; grid_index++) { - CCGElem *delta_grid = delta_grids_data[grid_index]; - for (int y = 0; y < top_grid_size; y++) { - for (int x = 0; x < top_grid_size; x++) { - const int top_index = y * top_grid_size + x; - add_v3_v3(mdisps[grid_index].disps[top_index], - CCG_grid_elem_co(delta_level_key, delta_grid, x, y)); - if (delta_level_key->has_mask) { - grid_paint_mask[grid_index].data[top_index] += *CCG_grid_elem_mask( - delta_level_key, delta_grid, x, y); - } - } - } - } -} - -static void multires_reshape_propagate(MultiresPropagateData *data) -{ - if (data->reshape_level == data->top_level) { - return; - } - const int num_grids = data->num_grids; - /* Calculate delta made at the reshape level. */ - CCGKey *delta_level_key = &data->top_level_key; - CCGElem **delta_grids_data = allocate_grids(delta_level_key, num_grids); - multires_reshape_calculate_delta(data, delta_grids_data); - /* Propagate deltas to the higher levels. */ - multires_reshape_propagate_and_smooth_delta(data, delta_grids_data); - /* Finally, apply smoothed deltas. */ - multires_reshape_propagate_apply_delta(data, delta_grids_data); - /* Cleanup. */ - free_grids(delta_grids_data, num_grids); -} - -static void multires_reshape_propagate_free(MultiresPropagateData *data) -{ - free_grids(data->orig_grids_data, data->num_grids); -} - -/* ============================================================================= - * Reshape from deformed vertex coordinates. - */ - -typedef struct MultiresReshapeFromDeformedVertsContext { - MultiresReshapeContext reshape_ctx; - const float (*deformed_verts)[3]; - int num_deformed_verts; -} MultiresReshapeFromDeformedVertsContext; - -static bool multires_reshape_topology_info(const SubdivForeachContext *foreach_context, - const int num_vertices, - const int UNUSED(num_edges), - const int UNUSED(num_loops), - const int UNUSED(num_polygons)) -{ - MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data; - if (num_vertices != ctx->num_deformed_verts) { - return false; - } - return true; -} - -/* Will run reshaping for all grid elements which are adjacent to the given - * one. This is the way to ensure continuity of displacement stored in the - * grids across the inner boundaries of the grids. */ -static void multires_reshape_neighour_boundary_vertices(MultiresReshapeContext *ctx, - const int UNUSED(ptex_face_index), - const float corner_u, - const float corner_v, - const int coarse_poly_index, - const int coarse_corner, - const float final_P[3], - const float final_mask) -{ - const Mesh *coarse_mesh = ctx->coarse_mesh; - const MPoly *coarse_mpoly = coarse_mesh->mpoly; - const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index]; - const int num_corners = coarse_poly->totloop; - const int start_ptex_face_index = ctx->face_ptex_offset[coarse_poly_index]; - const bool is_quad = (coarse_poly->totloop == 4); - if (corner_u == 1.0f && corner_v == 1.0f) { - for (int current_corner = 0; current_corner < num_corners; current_corner++) { - if (current_corner == coarse_corner) { - continue; - } - const int current_ptex_face_index = is_quad ? start_ptex_face_index : - start_ptex_face_index + current_corner; - multires_reshape_vertex_from_final_data(ctx, - current_ptex_face_index, - 1.0f, - 1.0f, - coarse_poly_index, - current_corner, - final_P, - final_mask); - } - } - else if (corner_u == 1.0f) { - const float next_corner_index = (coarse_corner + 1) % num_corners; - const float next_corner_u = corner_v; - const float next_corner_v = 1.0f; - const int next_ptex_face_index = is_quad ? start_ptex_face_index : - start_ptex_face_index + next_corner_index; - multires_reshape_vertex_from_final_data(ctx, - next_ptex_face_index, - next_corner_u, - next_corner_v, - coarse_poly_index, - next_corner_index, - final_P, - final_mask); - } - else if (corner_v == 1.0f) { - const float prev_corner_index = (coarse_corner + num_corners - 1) % num_corners; - const float prev_corner_u = 1.0f; - const float prev_corner_v = corner_u; - const int prev_ptex_face_index = is_quad ? start_ptex_face_index : - start_ptex_face_index + prev_corner_index; - multires_reshape_vertex_from_final_data(ctx, - prev_ptex_face_index, - prev_corner_u, - prev_corner_v, - coarse_poly_index, - prev_corner_index, - final_P, - final_mask); - } -} - -static void multires_reshape_vertex(MultiresReshapeFromDeformedVertsContext *ctx, - const int ptex_face_index, - const float u, - const float v, - const int coarse_poly_index, - const int coarse_corner, - const int subdiv_vertex_index) -{ - const float *final_P = ctx->deformed_verts[subdiv_vertex_index]; - const Mesh *coarse_mesh = ctx->reshape_ctx.coarse_mesh; - const MPoly *coarse_mpoly = coarse_mesh->mpoly; - const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index]; - const bool is_quad = (coarse_poly->totloop == 4); - float corner_u, corner_v; - int actual_coarse_corner; - if (is_quad) { - actual_coarse_corner = BKE_subdiv_rotate_quad_to_corner(u, v, &corner_u, &corner_v); - } - else { - actual_coarse_corner = coarse_corner; - corner_u = u; - corner_v = v; - } - multires_reshape_vertex_from_final_data(&ctx->reshape_ctx, - ptex_face_index, - corner_u, - corner_v, - coarse_poly_index, - actual_coarse_corner, - final_P, - 0.0f); - multires_reshape_neighour_boundary_vertices(&ctx->reshape_ctx, - ptex_face_index, - corner_u, - corner_v, - coarse_poly_index, - actual_coarse_corner, - final_P, - 0.0f); -} - -static void multires_reshape_vertex_inner(const SubdivForeachContext *foreach_context, - void *UNUSED(tls_v), - const int ptex_face_index, - const float u, - const float v, - const int coarse_poly_index, - const int coarse_corner, - const int subdiv_vertex_index) -{ - MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data; - multires_reshape_vertex( - ctx, ptex_face_index, u, v, coarse_poly_index, coarse_corner, subdiv_vertex_index); -} - -static void multires_reshape_vertex_every_corner( - const struct SubdivForeachContext *foreach_context, - void *UNUSED(tls_v), - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_vertex_index), - const int coarse_poly_index, - const int coarse_corner, - const int subdiv_vertex_index) -{ - MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data; - multires_reshape_vertex( - ctx, ptex_face_index, u, v, coarse_poly_index, coarse_corner, subdiv_vertex_index); -} - -static void multires_reshape_vertex_every_edge(const struct SubdivForeachContext *foreach_context, - void *UNUSED(tls_v), - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_edge_index), - const int coarse_poly_index, - const int coarse_corner, - const int subdiv_vertex_index) -{ - MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data; - multires_reshape_vertex( - ctx, ptex_face_index, u, v, coarse_poly_index, coarse_corner, subdiv_vertex_index); -} - -static Subdiv *multires_create_subdiv_for_reshape(struct Depsgraph *depsgraph, - /*const*/ Object *object, - const MultiresModifierData *mmd) -{ - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *object_eval = DEG_get_evaluated_object(depsgraph, object); - Mesh *deformed_mesh = mesh_get_eval_deform( - depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH); - SubdivSettings subdiv_settings; - BKE_multires_subdiv_settings_init(&subdiv_settings, mmd); - Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, deformed_mesh); - if (!BKE_subdiv_eval_update_from_mesh(subdiv, deformed_mesh, NULL)) { - BKE_subdiv_free(subdiv); - return NULL; - } - return subdiv; -} - -static bool multires_reshape_from_vertcos(struct Depsgraph *depsgraph, - Object *object, - const MultiresModifierData *mmd, - const float (*deformed_verts)[3], - const int num_deformed_verts, - const bool use_render_params) -{ - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Mesh *coarse_mesh = object->data; - MDisps *mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS); - /* Pick maximum between multires level and displacement level. - * This is because mesh can be used by objects with multires at different - * levels. - * - * TODO(sergey): At this point it should be possible to always use - * mdisps->level. */ - const int top_level = max_ii(mmd->totlvl, mdisps->level); - /* Make sure displacement grids are ready. */ - multires_reshape_ensure_grids(coarse_mesh, top_level); - /* Initialize subdivision surface. */ - Subdiv *subdiv = multires_create_subdiv_for_reshape(depsgraph, object, mmd); - if (subdiv == NULL) { - return false; - } - /* Construct context. */ - MultiresReshapeFromDeformedVertsContext reshape_deformed_verts_ctx = { - .reshape_ctx = - { - .subdiv = subdiv, - .coarse_mesh = coarse_mesh, - .mdisps = mdisps, - .grid_paint_mask = NULL, - .top_grid_size = BKE_subdiv_grid_size_from_level(top_level), - .top_level = top_level, - .face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv), - }, - .deformed_verts = deformed_verts, - .num_deformed_verts = num_deformed_verts, - }; - SubdivForeachContext foreach_context = { - .topology_info = multires_reshape_topology_info, - .vertex_inner = multires_reshape_vertex_inner, - .vertex_every_edge = multires_reshape_vertex_every_edge, - .vertex_every_corner = multires_reshape_vertex_every_corner, - .user_data = &reshape_deformed_verts_ctx, - }; - /* Initialize mesh rasterization settings. */ - SubdivToMeshSettings mesh_settings; - BKE_multires_subdiv_mesh_settings_init( - &mesh_settings, scene_eval, object, mmd, use_render_params, true); - /* Initialize propagation to higher levels. */ - MultiresPropagateData propagate_data; - multires_reshape_propagate_prepare_from_mmd( - &propagate_data, depsgraph, object, mmd, top_level, use_render_params); - /* Run all the callbacks. */ - BKE_subdiv_foreach_subdiv_geometry(subdiv, &foreach_context, &mesh_settings, coarse_mesh); - BKE_subdiv_free(subdiv); - /* Update higher levels if needed. */ - multires_reshape_propagate(&propagate_data); - multires_reshape_propagate_free(&propagate_data); - return true; -} - -/* ============================================================================= - * Reshape from object. - */ - -/* Returns truth on success, false otherwise. - * - * This function might fail in cases like source and destination not having - * matched amount of vertices. */ -bool multiresModifier_reshapeFromObject(struct Depsgraph *depsgraph, - MultiresModifierData *mmd, - Object *dst, - Object *src) -{ - /* Would be cool to support this eventually, but it is very tricky to match - * vertices order even for meshes, when mixing meshes and other objects it's - * even more tricky. */ - if (src->type != OB_MESH) { - return false; - } - MultiresModifierData reshape_mmd; - multires_reshape_init_mmd(&reshape_mmd, mmd); - /* Get evaluated vertices locations to reshape to. */ - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *src_eval = DEG_get_evaluated_object(depsgraph, src); - Mesh *src_mesh_eval = mesh_get_eval_final(depsgraph, scene_eval, src_eval, &CD_MASK_BAREMESH); - int num_deformed_verts; - float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(src_mesh_eval, &num_deformed_verts); - bool result = multires_reshape_from_vertcos( - depsgraph, dst, &reshape_mmd, deformed_verts, num_deformed_verts, false); - MEM_freeN(deformed_verts); - return result; -} - -/* ============================================================================= - * Reshape from modifier. - */ - -bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph, - MultiresModifierData *mmd, - Object *object, - ModifierData *md) -{ - MultiresModifierData highest_mmd; - /* It is possible that the current subdivision level of multires is lower - * that it's maximum possible one (i.e., viewport is set to a lower level - * for the performance purposes). But even then, we want all the multires - * levels to be reshaped. Most accurate way to do so is to ignore all - * simplifications and calculate deformation modifier for the highest - * possible multires level. - * Alternative would be propagate displacement from current level to a - * higher ones, but that is likely to cause artifacts. */ - multires_reshape_init_mmd_top_level(&highest_mmd, mmd); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - /* Perform sanity checks and early output. */ - if (multires_get_level(scene_eval, object, &highest_mmd, false, true) == 0) { - return false; - } - /* Create mesh for the multires, ignoring any further modifiers (leading - * deformation modifiers will be applied though). */ - Mesh *multires_mesh = BKE_multires_create_mesh(depsgraph, scene_eval, &highest_mmd, object); - int num_deformed_verts; - float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(multires_mesh, &num_deformed_verts); - /* Apply deformation modifier on the multires, */ - const ModifierEvalContext modifier_ctx = { - .depsgraph = depsgraph, - .object = object, - .flag = MOD_APPLY_USECACHE | MOD_APPLY_IGNORE_SIMPLIFY, - }; - modwrap_deformVerts(md, &modifier_ctx, multires_mesh, deformed_verts, multires_mesh->totvert); - BKE_id_free(NULL, multires_mesh); - /* Reshaping */ - bool result = multires_reshape_from_vertcos( - depsgraph, object, &highest_mmd, deformed_verts, num_deformed_verts, false); - /* Cleanup */ - MEM_freeN(deformed_verts); - return result; -} - -/* ============================================================================= - * Reshape from grids. - */ - -typedef struct ReshapeFromCCGTaskData { - MultiresReshapeContext reshape_ctx; - const CCGKey *key; - /*const*/ CCGElem **grids; -} ReshapeFromCCGTaskData; - -static void reshape_from_ccg_task(void *__restrict userdata, - const int coarse_poly_index, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - ReshapeFromCCGTaskData *data = userdata; - const CCGKey *key = data->key; - /*const*/ CCGElem **grids = data->grids; - const Mesh *coarse_mesh = data->reshape_ctx.coarse_mesh; - const MPoly *coarse_mpoly = coarse_mesh->mpoly; - const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index]; - const int key_grid_size = key->grid_size; - const int key_grid_size_1 = key_grid_size - 1; - const int resolution = key_grid_size; - const float resolution_1_inv = 1.0f / (float)(resolution - 1); - const int start_ptex_face_index = data->reshape_ctx.face_ptex_offset[coarse_poly_index]; - const bool is_quad = (coarse_poly->totloop == 4); - for (int corner = 0; corner < coarse_poly->totloop; corner++) { - for (int y = 0; y < resolution; y++) { - const float corner_v = y * resolution_1_inv; - for (int x = 0; x < resolution; x++) { - const float corner_u = x * resolution_1_inv; - /* Quad faces consists of a single ptex face. */ - const int ptex_face_index = is_quad ? start_ptex_face_index : - start_ptex_face_index + corner; - float grid_u, grid_v; - BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v); - /*const*/ CCGElem *grid = grids[coarse_poly->loopstart + corner]; - /*const*/ CCGElem *grid_element = CCG_grid_elem( - key, grid, key_grid_size_1 * grid_u, key_grid_size_1 * grid_v); - const float *final_P = CCG_elem_co(key, grid_element); - float final_mask = 0.0f; - if (key->has_mask) { - final_mask = *CCG_elem_mask(key, grid_element); - } - multires_reshape_vertex_from_final_data(&data->reshape_ctx, - ptex_face_index, - corner_u, - corner_v, - coarse_poly_index, - corner, - final_P, - final_mask); - } - } - } -} - -bool multiresModifier_reshapeFromCCG(const int tot_level, Mesh *coarse_mesh, SubdivCCG *subdiv_ccg) -{ - CCGKey key; - BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); - /* Sanity checks. */ - if (coarse_mesh->totloop != subdiv_ccg->num_grids) { - /* Grids are supposed to eb created for each face-cornder (aka loop). */ - return false; - } - MDisps *mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS); - if (mdisps == NULL) { - /* Multires displacement has been removed before current changes were - * applies to all the levels. */ - return false; - } - GridPaintMask *grid_paint_mask = CustomData_get_layer(&coarse_mesh->ldata, CD_GRID_PAINT_MASK); - Subdiv *subdiv = subdiv_ccg->subdiv; - /* Pick maximum between multires level and displacement level. - * This is because mesh can be used by objects with multires at different - * levels. - * - * TODO(sergey): At this point it should be possible to always use - * mdisps->level. */ - const int top_level = max_ii(tot_level, mdisps->level); - /* Make sure displacement grids are ready. */ - multires_reshape_ensure_grids(coarse_mesh, top_level); - /* Construct context. */ - ReshapeFromCCGTaskData data = { - .reshape_ctx = - { - .subdiv = subdiv, - .coarse_mesh = coarse_mesh, - .mdisps = mdisps, - .grid_paint_mask = grid_paint_mask, - .top_grid_size = BKE_subdiv_grid_size_from_level(top_level), - .top_level = top_level, - .face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv), - }, - .key = &key, - .grids = subdiv_ccg->grids, - }; - /* Initialize propagation to higher levels. */ - MultiresPropagateData propagate_data; - multires_reshape_propagate_prepare(&propagate_data, coarse_mesh, key.level, top_level); - /* Threaded grids iteration. */ - TaskParallelSettings parallel_range_settings; - BLI_parallel_range_settings_defaults(¶llel_range_settings); - BLI_task_parallel_range( - 0, coarse_mesh->totpoly, &data, reshape_from_ccg_task, ¶llel_range_settings); - /* Update higher levels if needed. */ - multires_reshape_propagate(&propagate_data); - multires_reshape_propagate_free(&propagate_data); - return true; -} diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c new file mode 100644 index 00000000000..0a9a17d7bd8 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c @@ -0,0 +1,1064 @@ +/* + * 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 bke + */ + +#include "multires_reshape.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_task.h" + +#include "BKE_multires.h" +#include "BKE_subdiv.h" +#include "BKE_subdiv_eval.h" +#include "BKE_subdiv_foreach.h" +#include "BKE_subdiv_mesh.h" + +#include "opensubdiv_converter_capi.h" +#include "opensubdiv_evaluator_capi.h" +#include "opensubdiv_topology_refiner_capi.h" + +#include "subdiv_converter.h" + +typedef struct SurfacePoint { + float P[3]; + float tangent_matrix[3][3]; +} SurfacePoint; + +typedef struct SurfaceGrid { + SurfacePoint *points; +} SurfaceGrid; + +typedef struct Vertex { + /* All grid coordinates which the vertex corresponding to. + * For a vertices which are created from inner points of grids there is always one coordinate. */ + int num_grid_coords; + GridCoord *grid_coords; + + bool is_infinite_sharp; +} Vertex; + +typedef struct Corner { + const Vertex *vertex; + int grid_index; +} Corner; + +typedef struct Dace { + int start_corner_index; + int num_corners; +} Dace; + +typedef struct MultiresReshapeSmoothContext { + const MultiresReshapeContext *reshape_context; + + // Geometry at a reshape multires level. + struct { + int num_vertices; + Vertex *vertices; + + int num_corners; + Corner *corners; + + int num_faces; + Dace *faces; + } geometry; + + /* Subdivision surface created for geometry at a reshape level. */ + Subdiv *reshape_subdiv; + + SurfaceGrid *base_surface_grids; +} MultiresReshapeSmoothContext; + +/* ================================================================================================ + * Masks. + */ + +/* Interpolate mask grid at a reshape level. + * Will return 0 if there is no masks custom data layer. */ +static float interpolate_masks_grid(const MultiresReshapeSmoothContext *reshape_smooth_context, + const GridCoord *grid_coord) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + if (reshape_context->grid_paint_masks == NULL) { + return 0.0f; + } + + const GridPaintMask *grid = &reshape_context->orig.grid_paint_masks[grid_coord->grid_index]; + const int grid_size = BKE_subdiv_grid_size_from_level(grid->level); + const int grid_size_1 = grid_size - 1; + const float grid_size_1_inv = 1.0f / (float)(grid_size_1); + + const float x_f = grid_coord->u * grid_size_1; + const float y_f = grid_coord->v * grid_size_1; + + const int x_i = x_f; + const int y_i = y_f; + const int x_n_i = (x_i == grid_size - 1) ? (x_i) : (x_i + 1); + const int y_n_i = (y_i == grid_size - 1) ? (y_i) : (y_i + 1); + + const int corners[4][2] = {{x_i, y_i}, {x_n_i, y_i}, {x_n_i, y_n_i}, {x_i, y_n_i}}; + float mask_elements[4]; + for (int i = 0; i < 4; ++i) { + GridCoord corner_grid_coord; + corner_grid_coord.grid_index = grid_coord->grid_index; + corner_grid_coord.u = corners[i][0] * grid_size_1_inv; + corner_grid_coord.v = corners[i][1] * grid_size_1_inv; + + ReshapeConstGridElement element = multires_reshape_orig_grid_element_for_grid_coord( + reshape_context, &corner_grid_coord); + mask_elements[i] = element.mask; + } + + const float u = x_f - x_i; + const float v = y_f - y_i; + const float weights[4] = {(1.0f - u) * (1.0f - v), u * (1.0f - v), (1.0f - u) * v, u * v}; + + return mask_elements[0] * weights[0] + mask_elements[1] * weights[1] + + mask_elements[2] * weights[2] + mask_elements[3] * weights[3]; +} + +/* ================================================================================================ + * Surface. + */ + +static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_smooth_context) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + const int num_grids = reshape_context->num_grids; + const int grid_size = reshape_context->top.grid_size; + const int grid_area = grid_size * grid_size; + + SurfaceGrid *surface_grid = MEM_malloc_arrayN(num_grids, sizeof(SurfaceGrid), "delta grids"); + + for (int grid_index = 0; grid_index < num_grids; ++grid_index) { + surface_grid[grid_index].points = MEM_calloc_arrayN( + sizeof(SurfacePoint), grid_area, "delta grid dispalcement"); + } + + reshape_smooth_context->base_surface_grids = surface_grid; +} + +static void base_surface_grids_free(MultiresReshapeSmoothContext *reshape_smooth_context) +{ + if (reshape_smooth_context->base_surface_grids == NULL) { + return; + } + + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + const int num_grids = reshape_context->num_grids; + for (int grid_index = 0; grid_index < num_grids; ++grid_index) { + MEM_freeN(reshape_smooth_context->base_surface_grids[grid_index].points); + } + MEM_freeN(reshape_smooth_context->base_surface_grids); +} + +static SurfacePoint *base_surface_grids_read( + const MultiresReshapeSmoothContext *reshape_smooth_context, const GridCoord *grid_coord) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + const int grid_index = grid_coord->grid_index; + const int grid_size = reshape_context->top.grid_size; + const int grid_x = lround(grid_coord->u * (grid_size - 1)); + const int grid_y = lround(grid_coord->v * (grid_size - 1)); + const int grid_element_index = grid_y * grid_size + grid_x; + + SurfaceGrid *surface_grid = &reshape_smooth_context->base_surface_grids[grid_index]; + return &surface_grid->points[grid_element_index]; +} + +static void base_surface_grids_write(const MultiresReshapeSmoothContext *reshape_smooth_context, + const GridCoord *grid_coord, + float P[3], + float tangent_matrix[3][3]) +{ + SurfacePoint *point = base_surface_grids_read(reshape_smooth_context, grid_coord); + copy_v3_v3(point->P, P); + copy_m3_m3(point->tangent_matrix, tangent_matrix); +} + +/* ================================================================================================ + * Evaluation of subdivision surface at a reshape level. + */ + +typedef void (*ForeachTopLevelGridCoordCallback)( + const MultiresReshapeSmoothContext *reshape_smooth_context, + const PTexCoord *ptex_coord, + const GridCoord *grid_coord, + void *userdata_v); + +typedef struct ForeachTopLevelGridCoordTaskData { + const MultiresReshapeSmoothContext *reshape_smooth_context; + + int inner_grid_size; + float inner_grid_size_1_inv; + + ForeachTopLevelGridCoordCallback callback; + void *callback_userdata_v; +} ForeachHighLevelCoordTaskData; + +/* Find grid index which given face was created for. */ +static int get_face_grid_index(const MultiresReshapeSmoothContext *reshape_smooth_context, + const Dace *face) +{ + const Corner *first_corner = &reshape_smooth_context->geometry.corners[face->start_corner_index]; + const int grid_index = first_corner->grid_index; + +#ifndef NDEBUG + for (int face_corner = 0; face_corner < face->num_corners; ++face_corner) { + const int corner_index = face->start_corner_index + face_corner; + const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index]; + BLI_assert(corner->grid_index == grid_index); + } +#endif + + return grid_index; +} + +static GridCoord *vertex_grid_coord_with_grid_index(const Vertex *vertex, const int grid_index) +{ + for (int i = 0; i < vertex->num_grid_coords; ++i) { + if (vertex->grid_coords[i].grid_index == grid_index) { + return &vertex->grid_coords[i]; + } + } + return NULL; +} + +/* Get grid coordinates which correspond to corners of the given face. + * All the grid coordinates will be from the same grid index. */ +static void grid_coords_from_face_vertices( + const MultiresReshapeSmoothContext *reshape_smooth_context, + const Dace *face, + const GridCoord *grid_coords[]) +{ + BLI_assert(face->num_corners == 4); + + const int grid_index = get_face_grid_index(reshape_smooth_context, face); + BLI_assert(grid_index != -1); + + for (int i = 0; i < face->num_corners; ++i) { + const int corner_index = face->start_corner_index + i; + const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index]; + grid_coords[i] = vertex_grid_coord_with_grid_index(corner->vertex, grid_index); + BLI_assert(grid_coords[i] != NULL); + } +} + +static float lerp(float t, float a, float b) +{ + return (a + t * (b - a)); +} + +static void interpolate_grid_coord(GridCoord *result, + const GridCoord *face_grid_coords[4], + const float u, + const float v) +{ + /* + * v + * ^ + * | (3) -------- (2) + * | | | + * | | | + * | | | + * | | | + * | (0) -------- (1) + * *--------------------------> u + */ + + const float u01 = lerp(u, face_grid_coords[0]->u, face_grid_coords[1]->u); + const float u32 = lerp(u, face_grid_coords[3]->u, face_grid_coords[2]->u); + + const float v03 = lerp(v, face_grid_coords[0]->v, face_grid_coords[3]->v); + const float v12 = lerp(v, face_grid_coords[1]->v, face_grid_coords[2]->v); + + result->grid_index = face_grid_coords[0]->grid_index; + result->u = lerp(v, u01, u32); + result->v = lerp(u, v03, v12); +} + +static void foreach_toplevel_grid_coord_task(void *__restrict userdata_v, + const int face_index, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + ForeachHighLevelCoordTaskData *data = userdata_v; + + const MultiresReshapeSmoothContext *reshape_smooth_context = data->reshape_smooth_context; + const int inner_grid_size = data->inner_grid_size; + const float inner_grid_size_1_inv = data->inner_grid_size_1_inv; + + const Dace *face = &reshape_smooth_context->geometry.faces[face_index]; + const GridCoord *face_grid_coords[4]; + grid_coords_from_face_vertices(reshape_smooth_context, face, face_grid_coords); + + for (int y = 0; y < inner_grid_size; ++y) { + const float ptex_v = (float)y * inner_grid_size_1_inv; + for (int x = 0; x < inner_grid_size; ++x) { + const float ptex_u = (float)x * inner_grid_size_1_inv; + + PTexCoord ptex_coord; + ptex_coord.ptex_face_index = face_index; + ptex_coord.u = ptex_u; + ptex_coord.v = ptex_v; + + GridCoord grid_coord; + interpolate_grid_coord(&grid_coord, face_grid_coords, ptex_u, ptex_v); + + data->callback(reshape_smooth_context, &ptex_coord, &grid_coord, data->callback_userdata_v); + } + } +} + +static void foreach_toplevel_grid_coord(const MultiresReshapeSmoothContext *reshape_smooth_context, + ForeachTopLevelGridCoordCallback callback, + void *callback_userdata_v) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + const int level_difference = (reshape_context->top.level - reshape_context->reshape.level); + + ForeachHighLevelCoordTaskData data; + data.reshape_smooth_context = reshape_smooth_context; + data.inner_grid_size = (1 << level_difference) + 1; + data.inner_grid_size_1_inv = 1.0f / (float)(data.inner_grid_size - 1); + data.callback = callback; + data.callback_userdata_v = callback_userdata_v; + + TaskParallelSettings parallel_range_settings; + BLI_parallel_range_settings_defaults(¶llel_range_settings); + parallel_range_settings.min_iter_per_thread = 1; + + const int num_faces = reshape_smooth_context->geometry.num_faces; + BLI_task_parallel_range( + 0, num_faces, &data, foreach_toplevel_grid_coord_task, ¶llel_range_settings); +} + +/* ================================================================================================ + * Generation of a topology information for OpenSubdiv converter. + * + * Calculates vertices, their coordinates in the original grids, and connections of them so then + * it's easy to create OpenSubdiv's topology refiner. */ + +static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context, + const MultiresReshapeContext *reshape_context) +{ + reshape_smooth_context->reshape_context = reshape_context; + + reshape_smooth_context->geometry.num_vertices = 0; + reshape_smooth_context->geometry.vertices = NULL; + reshape_smooth_context->geometry.num_corners = 0; + reshape_smooth_context->geometry.corners = NULL; + + reshape_smooth_context->reshape_subdiv = NULL; + reshape_smooth_context->base_surface_grids = NULL; +} + +static void context_free_geometry(MultiresReshapeSmoothContext *reshape_smooth_context) +{ + if (reshape_smooth_context->geometry.vertices != NULL) { + for (int i = 0; i < reshape_smooth_context->geometry.num_vertices; ++i) { + MEM_SAFE_FREE(reshape_smooth_context->geometry.vertices[i].grid_coords); + } + } + MEM_SAFE_FREE(reshape_smooth_context->geometry.vertices); + MEM_SAFE_FREE(reshape_smooth_context->geometry.corners); + MEM_SAFE_FREE(reshape_smooth_context->geometry.faces); +} + +static void context_free_subdiv(MultiresReshapeSmoothContext *reshape_smooth_context) +{ + if (reshape_smooth_context->reshape_subdiv == NULL) { + return; + } + BKE_subdiv_free(reshape_smooth_context->reshape_subdiv); +} + +static void context_free(MultiresReshapeSmoothContext *reshape_smooth_context) +{ + context_free_geometry(reshape_smooth_context); + context_free_subdiv(reshape_smooth_context); + base_surface_grids_free(reshape_smooth_context); +} + +static bool foreach_topology_info(const SubdivForeachContext *foreach_context, + const int num_vertices, + const int UNUSED(num_edges), + const int num_loops, + const int num_polygons) +{ + MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; + + /* NOTE: Calloc so the counters are re-set to 0 "for free". */ + reshape_smooth_context->geometry.num_vertices = num_vertices; + reshape_smooth_context->geometry.vertices = MEM_calloc_arrayN( + sizeof(Vertex), num_vertices, "smooth vertices"); + + reshape_smooth_context->geometry.num_corners = num_loops; + reshape_smooth_context->geometry.corners = MEM_malloc_arrayN( + sizeof(Corner), num_loops, "smooth corners"); + + reshape_smooth_context->geometry.num_faces = num_polygons; + reshape_smooth_context->geometry.faces = MEM_malloc_arrayN( + sizeof(Dace), num_polygons, "smooth faces"); + + return true; +} + +static void foreach_single_vertex(const SubdivForeachContext *foreach_context, + const GridCoord *grid_coord, + const int subdiv_vertex_index) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; + + BLI_assert(subdiv_vertex_index < reshape_smooth_context->geometry.num_vertices); + + Vertex *vertex = &reshape_smooth_context->geometry.vertices[subdiv_vertex_index]; + + vertex->grid_coords = MEM_reallocN(vertex->grid_coords, + sizeof(Vertex) * (vertex->num_grid_coords + 1)); + vertex->grid_coords[vertex->num_grid_coords] = *grid_coord; + ++vertex->num_grid_coords; +} + +/* TODO(sergey): De-duplicate with similar function in multires_reshape_vertcos.c */ +static void foreach_vertex(const SubdivForeachContext *foreach_context, + const PTexCoord *ptex_coord, + const int subdiv_vertex_index) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + const GridCoord grid_coord = multires_reshape_ptex_coord_to_grid(reshape_context, ptex_coord); + const int face_index = multires_reshape_grid_to_face_index(reshape_context, + grid_coord.grid_index); + + const Mesh *base_mesh = reshape_context->base_mesh; + const MPoly *base_poly = &base_mesh->mpoly[face_index]; + const int num_corners = base_poly->totloop; + const int start_grid_index = reshape_context->face_start_grid_index[face_index]; + const int corner = grid_coord.grid_index - start_grid_index; + + if (grid_coord.u == 0.0f && grid_coord.v == 0.0f) { + for (int current_corner = 0; current_corner < num_corners; ++current_corner) { + GridCoord corner_grid_coord = grid_coord; + corner_grid_coord.grid_index = start_grid_index + current_corner; + foreach_single_vertex(foreach_context, &corner_grid_coord, subdiv_vertex_index); + } + return; + } + + foreach_single_vertex(foreach_context, &grid_coord, subdiv_vertex_index); + + if (grid_coord.u == 0.0f) { + GridCoord prev_grid_coord; + prev_grid_coord.grid_index = start_grid_index + ((corner + num_corners - 1) % num_corners); + prev_grid_coord.u = grid_coord.v; + prev_grid_coord.v = 0.0f; + + foreach_single_vertex(foreach_context, &prev_grid_coord, subdiv_vertex_index); + } + + if (grid_coord.v == 0.0f) { + GridCoord next_grid_coord; + next_grid_coord.grid_index = start_grid_index + ((corner + 1) % num_corners); + next_grid_coord.u = 0.0f; + next_grid_coord.v = grid_coord.u; + + foreach_single_vertex(foreach_context, &next_grid_coord, subdiv_vertex_index); + } +} + +static void foreach_vertex_inner(const struct SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int ptex_face_index, + const float ptex_face_u, + const float ptex_face_v, + const int UNUSED(coarse_poly_index), + const int UNUSED(coarse_corner), + const int subdiv_vertex_index) +{ + const PTexCoord ptex_coord = { + .ptex_face_index = ptex_face_index, + .u = ptex_face_u, + .v = ptex_face_v, + }; + foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index); +} + +static void foreach_vertex_every_corner(const struct SubdivForeachContext *foreach_context, + void *UNUSED(tls_v), + const int ptex_face_index, + const float ptex_face_u, + const float ptex_face_v, + const int UNUSED(coarse_vertex_index), + const int UNUSED(coarse_face_index), + const int UNUSED(coarse_face_corner), + const int subdiv_vertex_index) +{ + const PTexCoord ptex_coord = { + .ptex_face_index = ptex_face_index, + .u = ptex_face_u, + .v = ptex_face_v, + }; + foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index); +} + +static void foreach_vertex_every_edge(const struct SubdivForeachContext *foreach_context, + void *UNUSED(tls_v), + const int ptex_face_index, + const float ptex_face_u, + const float ptex_face_v, + const int UNUSED(coarse_edge_index), + const int UNUSED(coarse_face_index), + const int UNUSED(coarse_face_corner), + const int subdiv_vertex_index) +{ + const PTexCoord ptex_coord = { + .ptex_face_index = ptex_face_index, + .u = ptex_face_u, + .v = ptex_face_v, + }; + foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index); +} + +static void foreach_loop(const struct SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int UNUSED(ptex_face_index), + const float UNUSED(ptex_face_u), + const float UNUSED(ptex_face_v), + const int UNUSED(coarse_loop_index), + const int coarse_poly_index, + const int coarse_corner, + const int subdiv_loop_index, + const int subdiv_vertex_index, + const int UNUSED(subdiv_edge_index)) +{ + MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + BLI_assert(subdiv_loop_index < reshape_smooth_context->geometry.num_corners); + + Corner *corner = &reshape_smooth_context->geometry.corners[subdiv_loop_index]; + corner->vertex = &reshape_smooth_context->geometry.vertices[subdiv_vertex_index]; + + const int first_grid_index = reshape_context->face_start_grid_index[coarse_poly_index]; + corner->grid_index = first_grid_index + coarse_corner; +} + +static void foreach_poly(const SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int UNUSED(coarse_poly_index), + const int subdiv_poly_index, + const int start_loop_index, + const int num_loops) +{ + MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; + + BLI_assert(subdiv_poly_index < reshape_smooth_context->geometry.num_faces); + + Dace *face = &reshape_smooth_context->geometry.faces[subdiv_poly_index]; + face->start_corner_index = start_loop_index; + face->num_corners = num_loops; +} + +static void foreach_vertex_of_loose_edge(const struct SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int UNUSED(coarse_edge_index), + const float UNUSED(u), + const int vertex_index) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; + Vertex *vertex = &reshape_smooth_context->geometry.vertices[vertex_index]; + + if (vertex->num_grid_coords != 0) { + vertex->is_infinite_sharp = true; + } +} + +static void geometry_create(MultiresReshapeSmoothContext *reshape_smooth_context) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + SubdivForeachContext foreach_context = { + .topology_info = foreach_topology_info, + .vertex_inner = foreach_vertex_inner, + .vertex_every_corner = foreach_vertex_every_corner, + .vertex_every_edge = foreach_vertex_every_edge, + .loop = foreach_loop, + .poly = foreach_poly, + .vertex_of_loose_edge = foreach_vertex_of_loose_edge, + .user_data = reshape_smooth_context, + }; + + SubdivToMeshSettings mesh_settings; + mesh_settings.resolution = (1 << reshape_context->reshape.level) + 1; + mesh_settings.use_optimal_display = false; + + /* TODO(sergey): Tell the foreach() to ignore loose vertices. */ + BKE_subdiv_foreach_subdiv_geometry( + reshape_context->subdiv, &foreach_context, &mesh_settings, reshape_context->base_mesh); +} + +/* ================================================================================================ + * Generation of OpenSubdiv evaluator for topology created form reshape level. + */ + +static OpenSubdiv_SchemeType get_scheme_type(const OpenSubdiv_Converter *UNUSED(converter)) +{ + return OSD_SCHEME_CATMARK; +} + +static OpenSubdiv_VtxBoundaryInterpolation get_vtx_boundary_interpolation( + const struct OpenSubdiv_Converter *converter) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data; + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + const SubdivSettings *settings = &reshape_context->subdiv->settings; + + return BKE_subdiv_converter_vtx_boundary_interpolation_from_settings(settings); +} + +static OpenSubdiv_FVarLinearInterpolation get_fvar_linear_interpolation( + const OpenSubdiv_Converter *converter) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data; + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + const SubdivSettings *settings = &reshape_context->subdiv->settings; + + return BKE_subdiv_converter_fvar_linear_from_settings(settings); +} + +static bool specifies_full_topology(const OpenSubdiv_Converter *UNUSED(converter)) +{ + return false; +} + +static int get_num_faces(const OpenSubdiv_Converter *converter) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data; + + return reshape_smooth_context->geometry.num_faces; +} + +static int get_num_vertices(const OpenSubdiv_Converter *converter) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data; + + return reshape_smooth_context->geometry.num_vertices; +} + +static int get_num_face_vertices(const OpenSubdiv_Converter *converter, int face_index) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data; + const Dace *face = &reshape_smooth_context->geometry.faces[face_index]; + + return face->num_corners; +} + +static void get_face_vertices(const OpenSubdiv_Converter *converter, + int face_index, + int *face_vertices) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data; + const Dace *face = &reshape_smooth_context->geometry.faces[face_index]; + + for (int i = 0; i < face->num_corners; ++i) { + const int corner_index = face->start_corner_index + i; + const Corner *corner = &reshape_smooth_context->geometry.corners[corner_index]; + face_vertices[i] = corner->vertex - reshape_smooth_context->geometry.vertices; + } +} + +static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter, int vertex_index) +{ + const MultiresReshapeSmoothContext *reshape_smooth_context = converter->user_data; + Vertex *vertex = &reshape_smooth_context->geometry.vertices[vertex_index]; + + return vertex->is_infinite_sharp; +} + +static void converter_init(const MultiresReshapeSmoothContext *reshape_smooth_context, + OpenSubdiv_Converter *converter) +{ + converter->getSchemeType = get_scheme_type; + converter->getVtxBoundaryInterpolation = get_vtx_boundary_interpolation; + converter->getFVarLinearInterpolation = get_fvar_linear_interpolation; + converter->specifiesFullTopology = specifies_full_topology; + + converter->getNumFaces = get_num_faces; + converter->getNumEdges = NULL; + converter->getNumVertices = get_num_vertices; + + converter->getNumFaceVertices = get_num_face_vertices; + converter->getFaceVertices = get_face_vertices; + converter->getFaceEdges = NULL; + + converter->getEdgeVertices = NULL; + converter->getNumEdgeFaces = NULL; + converter->getEdgeFaces = NULL; + converter->getEdgeSharpness = NULL; + + converter->getNumVertexEdges = NULL; + converter->getVertexEdges = NULL; + converter->getNumVertexFaces = NULL; + converter->getVertexFaces = NULL; + converter->isInfiniteSharpVertex = is_infinite_sharp_vertex; + converter->getVertexSharpness = NULL; + + converter->getNumUVLayers = NULL; + converter->precalcUVLayer = NULL; + converter->finishUVLayer = NULL; + converter->getNumUVCoordinates = NULL; + converter->getFaceCornerUVIndex = NULL; + + converter->freeUserData = NULL; + + converter->user_data = (void *)reshape_smooth_context; +} + +/* Create subdiv descriptor created for topology at a reshape level, */ +static void reshape_subdiv_create(MultiresReshapeSmoothContext *reshape_smooth_context) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + const SubdivSettings *settings = &reshape_context->subdiv->settings; + + OpenSubdiv_Converter converter; + converter_init(reshape_smooth_context, &converter); + + Subdiv *reshape_subdiv = BKE_subdiv_new_from_converter(settings, &converter); + BKE_subdiv_eval_begin(reshape_subdiv); + + reshape_smooth_context->reshape_subdiv = reshape_subdiv; + + BKE_subdiv_converter_free(&converter); +} + +/* Callback to provide coarse position for subdivision surface topology at a reshape level. */ +typedef void(ReshapeSubdivCoarsePositionCb)( + const MultiresReshapeSmoothContext *reshape_smooth_context, + const Vertex *vertex, + float r_P[3]); + +/* Refine subdivision surface topology at a reshape level for new coarse verticies positions. */ +static void reshape_subdiv_refine(const MultiresReshapeSmoothContext *reshape_smooth_context, + ReshapeSubdivCoarsePositionCb coarse_position_cb) +{ + Subdiv *reshape_subdiv = reshape_smooth_context->reshape_subdiv; + + /* TODO(sergey): For non-trivial coarse_position_cb we should multi-thread this loop. */ + + const int num_vertices = reshape_smooth_context->geometry.num_vertices; + for (int i = 0; i < num_vertices; ++i) { + const Vertex *vertex = &reshape_smooth_context->geometry.vertices[i]; + float P[3]; + coarse_position_cb(reshape_smooth_context, vertex, P); + reshape_subdiv->evaluator->setCoarsePositions(reshape_subdiv->evaluator, P, i, 1); + } + reshape_subdiv->evaluator->refine(reshape_subdiv->evaluator); +} + +BLI_INLINE const GridCoord *reshape_subdiv_refine_vertex_grid_coord(const Vertex *vertex) +{ + if (vertex->num_grid_coords == 0) { + /* This is a loose vertex, the coordinate is not important. */ + /* TODO(sergey): Once the subdiv_foreach() supports properly ignoring loose elements this + * should become an assert instead. */ + return NULL; + } + /* NOTE: All grid coordinates will point to the same object position, so can be simple and use + * first grid coordinate. */ + return &vertex->grid_coords[0]; +} + +/* Version of reshape_subdiv_refine() which uses coarse position from original grids. */ +static void reshape_subdiv_refine_orig_P( + const MultiresReshapeSmoothContext *reshape_smooth_context, const Vertex *vertex, float r_P[3]) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + const GridCoord *grid_coord = reshape_subdiv_refine_vertex_grid_coord(vertex); + + /* Check whether this is a loose vertex. */ + if (grid_coord == NULL) { + zero_v3(r_P); + return; + } + + float limit_P[3]; + float tangent_matrix[3][3]; + multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, limit_P, tangent_matrix); + + const ReshapeConstGridElement orig_grid_element = + multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord); + + float D[3]; + mul_v3_m3v3(D, tangent_matrix, orig_grid_element.displacement); + + add_v3_v3v3(r_P, limit_P, D); +} +static void reshape_subdiv_refine_orig(const MultiresReshapeSmoothContext *reshape_smooth_context) +{ + reshape_subdiv_refine(reshape_smooth_context, reshape_subdiv_refine_orig_P); +} + +/* Version of reshape_subdiv_refine() which uses coarse position from final grids. */ +static void reshape_subdiv_refine_final_P( + const MultiresReshapeSmoothContext *reshape_smooth_context, const Vertex *vertex, float r_P[3]) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + const GridCoord *grid_coord = reshape_subdiv_refine_vertex_grid_coord(vertex); + + /* Check whether this is a loose vertex. */ + if (grid_coord == NULL) { + zero_v3(r_P); + return; + } + + const ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord( + reshape_context, grid_coord); + + /* NOTE: At this point in reshape/propagate pipeline grid displacement is actually storing object + * vertices coordinates. */ + copy_v3_v3(r_P, grid_element.displacement); +} +static void reshape_subdiv_refine_final(const MultiresReshapeSmoothContext *reshape_smooth_context) +{ + reshape_subdiv_refine(reshape_smooth_context, reshape_subdiv_refine_final_P); +} + +static void reshape_subdiv_evaluate_limit_at_grid( + const MultiresReshapeSmoothContext *reshape_smooth_context, + const PTexCoord *ptex_coord, + const GridCoord *grid_coord, + float limit_P[3], + float r_tangent_matrix[3][3]) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + float dPdu[3], dPdv[3]; + BKE_subdiv_eval_limit_point_and_derivatives(reshape_smooth_context->reshape_subdiv, + ptex_coord->ptex_face_index, + ptex_coord->u, + ptex_coord->v, + limit_P, + dPdu, + dPdv); + + const int corner = multires_reshape_grid_to_corner(reshape_context, grid_coord->grid_index); + BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, corner); +} + +/* ================================================================================================ + * Evaluation of base surface. + */ + +static void evaluate_base_surface_grids_callback( + const MultiresReshapeSmoothContext *reshape_smooth_context, + const PTexCoord *ptex_coord, + const GridCoord *grid_coord, + void *UNUSED(userdata_v)) +{ + float limit_P[3]; + float tangent_matrix[3][3]; + reshape_subdiv_evaluate_limit_at_grid( + reshape_smooth_context, ptex_coord, grid_coord, limit_P, tangent_matrix); + + base_surface_grids_write(reshape_smooth_context, grid_coord, limit_P, tangent_matrix); +} + +static void evaluate_base_surface_grids(const MultiresReshapeSmoothContext *reshape_smooth_context) +{ + foreach_toplevel_grid_coord(reshape_smooth_context, evaluate_base_surface_grids_callback, NULL); +} + +/* ================================================================================================ + * Evaluation of new surface. + */ + +/* Evaluate final position of the original (pre-sculpt-edit) point position at a given grid + * coordinate. */ +static void evaluate_final_original_point( + const MultiresReshapeSmoothContext *reshape_smooth_context, + const GridCoord *grid_coord, + float r_orig_final_P[3]) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + /* Element of an original MDISPS grid) */ + const ReshapeConstGridElement orig_grid_element = + multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord); + + /* Limit surface of the base mesh. */ + float base_mesh_limit_P[3]; + float base_mesh_tangent_matrix[3][3]; + multires_reshape_evaluate_limit_at_grid( + reshape_context, grid_coord, base_mesh_limit_P, base_mesh_tangent_matrix); + + /* Convert original displacement from tangent space to object space. */ + float orig_displacement[3]; + mul_v3_m3v3(orig_displacement, base_mesh_tangent_matrix, orig_grid_element.displacement); + + /* Final point = limit surface + displacement. */ + add_v3_v3v3(r_orig_final_P, base_mesh_limit_P, orig_displacement); +} + +static void evaluate_higher_grid_positions_with_details_callback( + const MultiresReshapeSmoothContext *reshape_smooth_context, + const PTexCoord *ptex_coord, + const GridCoord *grid_coord, + void *UNUSED(userdata_v)) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + + /* Position of the original veretx at top level. */ + float orig_final_P[3]; + evaluate_final_original_point(reshape_smooth_context, grid_coord, orig_final_P); + + /* Original surface point on sculpt level (sculpt level before edits in sculpt mode). */ + const SurfacePoint *orig_sculpt_point = base_surface_grids_read(reshape_smooth_context, + grid_coord); + + /* Difference between original top level and original sculpt level in object space. */ + float original_detail_delta[3]; + sub_v3_v3v3(original_detail_delta, orig_final_P, orig_sculpt_point->P); + + /* Difference between original top level and original sculpt level in tangent space of original + * sculpt level. */ + float original_detail_delta_tangent[3]; + float original_sculpt_tangent_matrix_inv[3][3]; + invert_m3_m3(original_sculpt_tangent_matrix_inv, orig_sculpt_point->tangent_matrix); + mul_v3_m3v3( + original_detail_delta_tangent, original_sculpt_tangent_matrix_inv, original_detail_delta); + + /* Limit surface of smoothed (subdivided) edited sculpt level. */ + float smooth_limit_P[3]; + float smooth_tangent_matrix[3][3]; + reshape_subdiv_evaluate_limit_at_grid( + reshape_smooth_context, ptex_coord, grid_coord, smooth_limit_P, smooth_tangent_matrix); + + /* Add original detail to the smoothed surface. */ + float smooth_delta[3]; + mul_v3_m3v3(smooth_delta, smooth_tangent_matrix, original_detail_delta_tangent); + + /* Grid element of the result. + * + * NOTE: Displacement is storing object space coordinate. */ + ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context, + grid_coord); + + add_v3_v3v3(grid_element.displacement, smooth_limit_P, smooth_delta); +} +static void evaluate_higher_grid_positions_with_details( + const MultiresReshapeSmoothContext *reshape_smooth_context) +{ + foreach_toplevel_grid_coord( + reshape_smooth_context, evaluate_higher_grid_positions_with_details_callback, NULL); +} + +static void evaluate_higher_grid_positions_callback( + const MultiresReshapeSmoothContext *reshape_smooth_context, + const PTexCoord *ptex_coord, + const GridCoord *grid_coord, + void *UNUSED(userdata_v)) +{ + const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; + Subdiv *reshape_subdiv = reshape_smooth_context->reshape_subdiv; + + ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context, + grid_coord); + + /* Surface. */ + + float P[3]; + BKE_subdiv_eval_limit_point( + reshape_subdiv, ptex_coord->ptex_face_index, ptex_coord->u, ptex_coord->v, P); + + copy_v3_v3(grid_element.displacement, P); + + /* Masks. */ + if (grid_element.mask != NULL) { + *grid_element.mask = interpolate_masks_grid(reshape_smooth_context, grid_coord); + } +} + +static void evaluate_higher_grid_positions( + const MultiresReshapeSmoothContext *reshape_smooth_context) +{ + foreach_toplevel_grid_coord( + reshape_smooth_context, evaluate_higher_grid_positions_callback, NULL); +} +/* ================================================================================================ + * Entry point. + */ + +void multires_reshape_smooth_object_grids_with_details( + const MultiresReshapeContext *reshape_context) +{ + const int level_difference = (reshape_context->top.level - reshape_context->reshape.level); + if (level_difference == 0) { + /* Early output. */ + return; + } + + MultiresReshapeSmoothContext reshape_smooth_context; + context_init(&reshape_smooth_context, reshape_context); + + geometry_create(&reshape_smooth_context); + + reshape_subdiv_create(&reshape_smooth_context); + + base_surface_grids_allocate(&reshape_smooth_context); + reshape_subdiv_refine_orig(&reshape_smooth_context); + evaluate_base_surface_grids(&reshape_smooth_context); + + reshape_subdiv_refine_final(&reshape_smooth_context); + evaluate_higher_grid_positions_with_details(&reshape_smooth_context); + + context_free(&reshape_smooth_context); +} + +void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context) +{ + const int level_difference = (reshape_context->top.level - reshape_context->reshape.level); + if (level_difference == 0) { + /* Early output. */ + return; + } + + MultiresReshapeSmoothContext reshape_smooth_context; + context_init(&reshape_smooth_context, reshape_context); + + geometry_create(&reshape_smooth_context); + + reshape_subdiv_create(&reshape_smooth_context); + + reshape_subdiv_refine_final(&reshape_smooth_context); + evaluate_higher_grid_positions(&reshape_smooth_context); + + context_free(&reshape_smooth_context); +} diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c new file mode 100644 index 00000000000..f9271e37672 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -0,0 +1,739 @@ +/* + * 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 bke + */ + +#include "multires_reshape.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BLI_task.h" + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_multires.h" +#include "BKE_subdiv.h" +#include "BKE_subdiv_eval.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subdiv_foreach.h" +#include "BKE_subdiv_mesh.h" + +#include "DEG_depsgraph_query.h" + +/* -------------------------------------------------------------------- */ +/** \name Construct/destruct reshape context. + * \{ */ + +/* Create subdivision surface descriptor which is configured for surface evaluation at a given + * multires modifier. */ +Subdiv *multires_reshape_create_subdiv(Depsgraph *depsgraph, + /*const*/ Object *object, + const MultiresModifierData *mmd) +{ + Mesh *coarse_mesh; + + if (depsgraph != NULL) { + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *object_eval = DEG_get_evaluated_object(depsgraph, object); + coarse_mesh = mesh_get_eval_deform(depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH); + } + else { + coarse_mesh = (Mesh *)object->data; + } + + SubdivSettings subdiv_settings; + BKE_multires_subdiv_settings_init(&subdiv_settings, mmd); + Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, coarse_mesh); + if (!BKE_subdiv_eval_update_from_mesh(subdiv, coarse_mesh, NULL)) { + BKE_subdiv_free(subdiv); + return NULL; + } + return subdiv; +} + +static void context_zero(MultiresReshapeContext *reshape_context) +{ + memset(reshape_context, 0, sizeof(*reshape_context)); +} + +static void context_init_lookup(MultiresReshapeContext *reshape_context) +{ + const Mesh *base_mesh = reshape_context->base_mesh; + const MPoly *mpoly = base_mesh->mpoly; + const int num_faces = base_mesh->totpoly; + + reshape_context->face_start_grid_index = MEM_malloc_arrayN( + sizeof(int), num_faces, "face_start_grid_index"); + int num_grids = 0; + int num_ptex_faces = 0; + for (int face_index = 0; face_index < num_faces; ++face_index) { + const int num_corners = mpoly[face_index].totloop; + reshape_context->face_start_grid_index[face_index] = num_grids; + num_grids += num_corners; + num_ptex_faces += (num_corners == 4) ? 1 : num_corners; + } + + reshape_context->grid_to_face_index = MEM_malloc_arrayN( + sizeof(int), num_grids, "grid_to_face_index"); + reshape_context->ptex_start_grid_index = MEM_malloc_arrayN( + sizeof(int), num_ptex_faces, "ptex_start_grid_index"); + for (int face_index = 0, grid_index = 0, ptex_index = 0; face_index < num_faces; ++face_index) { + const int num_corners = mpoly[face_index].totloop; + const int num_face_ptex_faces = (num_corners == 4) ? 1 : num_corners; + for (int i = 0; i < num_face_ptex_faces; ++i) { + reshape_context->ptex_start_grid_index[ptex_index + i] = grid_index + i; + } + for (int corner = 0; corner < num_corners; ++corner, ++grid_index) { + reshape_context->grid_to_face_index[grid_index] = face_index; + } + ptex_index += num_face_ptex_faces; + } + + /* Store number of grids, which will be used for sanity checks. */ + reshape_context->num_grids = num_grids; +} + +static void context_init_grid_pointers(MultiresReshapeContext *reshape_context) +{ + Mesh *base_mesh = reshape_context->base_mesh; + reshape_context->mdisps = CustomData_get_layer(&base_mesh->ldata, CD_MDISPS); + reshape_context->grid_paint_masks = CustomData_get_layer(&base_mesh->ldata, CD_GRID_PAINT_MASK); +} + +static void context_init_commoon(MultiresReshapeContext *reshape_context) +{ + BLI_assert(reshape_context->subdiv != NULL); + BLI_assert(reshape_context->base_mesh != NULL); + + reshape_context->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(reshape_context->subdiv), + + context_init_lookup(reshape_context); + context_init_grid_pointers(reshape_context); +} + +static bool context_is_valid(MultiresReshapeContext *reshape_context) +{ + if (reshape_context->mdisps == NULL) { + /* Multires displacement has been removed before current changes were applies. */ + return false; + } + return true; +} + +static bool context_verify_or_free(MultiresReshapeContext *reshape_context) +{ + const bool is_valid = context_is_valid(reshape_context); + if (!is_valid) { + multires_reshape_context_free(reshape_context); + } + return is_valid; +} + +bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context, + Depsgraph *depsgraph, + Object *object, + MultiresModifierData *mmd) +{ + context_zero(reshape_context); + + const bool use_render_params = false; + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Mesh *base_mesh = (Mesh *)object->data; + + reshape_context->base_mesh = base_mesh; + + reshape_context->subdiv = multires_reshape_create_subdiv(depsgraph, object, mmd); + reshape_context->need_free_subdiv = true; + + reshape_context->reshape.level = multires_get_level( + scene_eval, object, mmd, use_render_params, true); + reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level( + reshape_context->reshape.level); + + reshape_context->top.level = mmd->totlvl; + reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level); + + context_init_commoon(reshape_context); + + return context_verify_or_free(reshape_context); +} + +bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_context, + SubdivCCG *subdiv_ccg, + Mesh *base_mesh, + int top_level) +{ + context_zero(reshape_context); + + reshape_context->base_mesh = base_mesh; + + reshape_context->subdiv = subdiv_ccg->subdiv; + reshape_context->need_free_subdiv = false; + + reshape_context->reshape.level = subdiv_ccg->level; + reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level( + reshape_context->reshape.level); + + reshape_context->top.level = top_level; + reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level); + + context_init_commoon(reshape_context); + + return context_verify_or_free(reshape_context); +} + +bool multires_reshape_context_create_from_subdivide(MultiresReshapeContext *reshape_context, + struct Object *object, + struct MultiresModifierData *mmd, + int top_level) +{ + context_zero(reshape_context); + + Mesh *base_mesh = (Mesh *)object->data; + + reshape_context->base_mesh = base_mesh; + + reshape_context->subdiv = multires_reshape_create_subdiv(NULL, object, mmd); + reshape_context->need_free_subdiv = true; + + reshape_context->reshape.level = mmd->totlvl; + reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level( + reshape_context->reshape.level); + + reshape_context->top.level = top_level; + reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level); + + context_init_commoon(reshape_context); + + return context_verify_or_free(reshape_context); +} + +static void free_original_grids(MultiresReshapeContext *reshape_context) +{ + MDisps *orig_mdisps = reshape_context->orig.mdisps; + GridPaintMask *orig_grid_paint_masks = reshape_context->orig.grid_paint_masks; + + if (orig_mdisps == NULL && orig_grid_paint_masks == NULL) { + return; + } + + const int num_grids = reshape_context->num_grids; + for (int grid_index = 0; grid_index < num_grids; grid_index++) { + if (orig_mdisps != NULL) { + MDisps *orig_grid = &orig_mdisps[grid_index]; + MEM_SAFE_FREE(orig_grid->disps); + } + if (orig_grid_paint_masks != NULL) { + GridPaintMask *orig_paint_mask_grid = &orig_grid_paint_masks[grid_index]; + MEM_SAFE_FREE(orig_paint_mask_grid->data); + } + } + + MEM_SAFE_FREE(orig_mdisps); + MEM_SAFE_FREE(orig_grid_paint_masks); +} + +void multires_reshape_context_free(MultiresReshapeContext *reshape_context) +{ + if (reshape_context->need_free_subdiv) { + BKE_subdiv_free(reshape_context->subdiv); + } + + free_original_grids(reshape_context); + + MEM_freeN(reshape_context->face_start_grid_index); + MEM_freeN(reshape_context->ptex_start_grid_index); + MEM_freeN(reshape_context->grid_to_face_index); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helper accessors. + * \{ */ + +/* For the given grid index get index of face it was created for. */ +int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context, + int grid_index) +{ + BLI_assert(grid_index >= 0); + BLI_assert(grid_index < reshape_context->num_grids); + + /* TODO(sergey): Optimization: when SubdivCCG is known we can calculate face index using + * SubdivCCG::grid_faces and SubdivCCG::faces, saving memory used by grid_to_face_index. */ + + return reshape_context->grid_to_face_index[grid_index]; +} + +/* For the given grid index get corner of a face it was created for. */ +int multires_reshape_grid_to_corner(const MultiresReshapeContext *reshape_context, int grid_index) +{ + BLI_assert(grid_index >= 0); + BLI_assert(grid_index < reshape_context->num_grids); + + /* TODO(sergey): Optimization: when SubdivCCG is known we can calculate face index using + * SubdivCCG::grid_faces and SubdivCCG::faces, saving memory used by grid_to_face_index. */ + + const int face_index = multires_reshape_grid_to_face_index(reshape_context, grid_index); + return grid_index - reshape_context->face_start_grid_index[face_index]; +} + +bool multires_reshape_is_quad_face(const MultiresReshapeContext *reshape_context, int face_index) +{ + const MPoly *coarse_poly = &reshape_context->base_mesh->mpoly[face_index]; + return (coarse_poly->totloop == 4); +} + +/* For the given grid index get index of corresponding ptex face. */ +int multires_reshape_grid_to_ptex_index(const MultiresReshapeContext *reshape_context, + int grid_index) +{ + const int face_index = multires_reshape_grid_to_face_index(reshape_context, grid_index); + const int corner = multires_reshape_grid_to_corner(reshape_context, grid_index); + const bool is_quad = multires_reshape_is_quad_face(reshape_context, face_index); + return reshape_context->face_ptex_offset[face_index] + (is_quad ? 0 : corner); +} + +/* Convert normalized coordinate within a grid to a normalized coordinate within a ptex face. */ +PTexCoord multires_reshape_grid_coord_to_ptex(const MultiresReshapeContext *reshape_context, + const GridCoord *grid_coord) +{ + PTexCoord ptex_coord; + + ptex_coord.ptex_face_index = multires_reshape_grid_to_ptex_index(reshape_context, + grid_coord->grid_index); + + float corner_u, corner_v; + BKE_subdiv_grid_uv_to_ptex_face_uv(grid_coord->u, grid_coord->v, &corner_u, &corner_v); + + const int face_index = multires_reshape_grid_to_face_index(reshape_context, + grid_coord->grid_index); + const int corner = multires_reshape_grid_to_corner(reshape_context, grid_coord->grid_index); + if (multires_reshape_is_quad_face(reshape_context, face_index)) { + float grid_u, grid_v; + BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v); + BKE_subdiv_rotate_grid_to_quad(corner, grid_u, grid_v, &ptex_coord.u, &ptex_coord.v); + } + else { + ptex_coord.u = corner_u; + ptex_coord.v = corner_v; + } + + return ptex_coord; +} + +/* Convert a normalized coordinate within a ptex face to a normalized coordinate within a grid. */ +GridCoord multires_reshape_ptex_coord_to_grid(const MultiresReshapeContext *reshape_context, + const PTexCoord *ptex_coord) +{ + GridCoord grid_coord; + + const int start_grid_index = reshape_context->ptex_start_grid_index[ptex_coord->ptex_face_index]; + const int face_index = reshape_context->grid_to_face_index[start_grid_index]; + + int corner_delta; + if (multires_reshape_is_quad_face(reshape_context, face_index)) { + corner_delta = BKE_subdiv_rotate_quad_to_corner( + ptex_coord->u, ptex_coord->v, &grid_coord.u, &grid_coord.v); + } + else { + corner_delta = 0; + grid_coord.u = ptex_coord->u; + grid_coord.v = ptex_coord->v; + } + grid_coord.grid_index = start_grid_index + corner_delta; + + BKE_subdiv_ptex_face_uv_to_grid_uv(grid_coord.u, grid_coord.v, &grid_coord.u, &grid_coord.v); + + return grid_coord; +} + +void multires_reshape_tangent_matrix_for_corner(const MultiresReshapeContext *reshape_context, + const int face_index, + const int corner, + const float dPdu[3], + const float dPdv[3], + float r_tangent_matrix[3][3]) +{ + /* For a quad faces we would need to flip the tangent, since they will use + * use different coordinates within displacement grid compared to the ptex face. */ + const bool is_quad = multires_reshape_is_quad_face(reshape_context, face_index); + const int tangent_corner = is_quad ? corner : 0; + BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, tangent_corner); +} + +ReshapeGridElement multires_reshape_grid_element_for_grid_coord( + const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord) +{ + ReshapeGridElement grid_element = {NULL, NULL}; + + const int grid_size = reshape_context->top.grid_size; + const int grid_x = lround(grid_coord->u * (grid_size - 1)); + const int grid_y = lround(grid_coord->v * (grid_size - 1)); + const int grid_element_index = grid_y * grid_size + grid_x; + + if (reshape_context->mdisps != NULL) { + MDisps *displacement_grid = &reshape_context->mdisps[grid_coord->grid_index]; + grid_element.displacement = displacement_grid->disps[grid_element_index]; + } + + if (reshape_context->grid_paint_masks != NULL) { + GridPaintMask *grid_paint_mask = &reshape_context->grid_paint_masks[grid_coord->grid_index]; + grid_element.mask = &grid_paint_mask->data[grid_element_index]; + } + + return grid_element; +} + +ReshapeGridElement multires_reshape_grid_element_for_ptex_coord( + const MultiresReshapeContext *reshape_context, const PTexCoord *ptex_coord) +{ + GridCoord grid_coord = multires_reshape_ptex_coord_to_grid(reshape_context, ptex_coord); + return multires_reshape_grid_element_for_grid_coord(reshape_context, &grid_coord); +} + +ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord( + const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord) +{ + ReshapeConstGridElement grid_element = {{0.0f, 0.0f, 0.0f}, 0.0f}; + + const MDisps *mdisps = reshape_context->orig.mdisps; + if (mdisps != NULL) { + const MDisps *displacement_grid = &mdisps[grid_coord->grid_index]; + if (displacement_grid->disps != NULL) { + const int grid_size = BKE_subdiv_grid_size_from_level(displacement_grid->level); + const int grid_x = lround(grid_coord->u * (grid_size - 1)); + const int grid_y = lround(grid_coord->v * (grid_size - 1)); + const int grid_element_index = grid_y * grid_size + grid_x; + copy_v3_v3(grid_element.displacement, displacement_grid->disps[grid_element_index]); + } + } + + const GridPaintMask *grid_paint_masks = reshape_context->orig.grid_paint_masks; + if (grid_paint_masks != NULL) { + const GridPaintMask *paint_mask_grid = &grid_paint_masks[grid_coord->grid_index]; + if (paint_mask_grid->data != NULL) { + const int grid_size = BKE_subdiv_grid_size_from_level(paint_mask_grid->level); + const int grid_x = lround(grid_coord->u * (grid_size - 1)); + const int grid_y = lround(grid_coord->v * (grid_size - 1)); + const int grid_element_index = grid_y * grid_size + grid_x; + grid_element.mask = paint_mask_grid->data[grid_element_index]; + } + } + + return grid_element; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sample limit surface of the base mesh. + * \{ */ + +void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *reshape_context, + const GridCoord *grid_coord, + float r_P[3], + float r_tangent_matrix[3][3]) +{ + float dPdu[3], dPdv[3]; + const PTexCoord ptex_coord = multires_reshape_grid_coord_to_ptex(reshape_context, grid_coord); + Subdiv *subdiv = reshape_context->subdiv; + BKE_subdiv_eval_limit_point_and_derivatives( + subdiv, ptex_coord.ptex_face_index, ptex_coord.u, ptex_coord.v, r_P, dPdu, dPdv); + + const int face_index = multires_reshape_grid_to_face_index(reshape_context, + grid_coord->grid_index); + const int corner = multires_reshape_grid_to_corner(reshape_context, grid_coord->grid_index); + multires_reshape_tangent_matrix_for_corner( + reshape_context, face_index, corner, dPdu, dPdv, r_tangent_matrix); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom data preparation. + * \{ */ + +static void allocate_displacement_grid(MDisps *displacement_grid, const int level) +{ + const int grid_size = BKE_subdiv_grid_size_from_level(level); + const int grid_area = grid_size * grid_size; + float(*disps)[3] = MEM_calloc_arrayN(grid_area, 3 * sizeof(float), "multires disps"); + if (displacement_grid->disps != NULL) { + MEM_freeN(displacement_grid->disps); + } + /* TODO(sergey): Preserve data on the old level. */ + displacement_grid->disps = disps; + displacement_grid->totdisp = grid_area; + displacement_grid->level = level; +} + +static void ensure_displacement_grid(MDisps *displacement_grid, const int level) +{ + if (displacement_grid->disps != NULL && displacement_grid->level >= level) { + return; + } + allocate_displacement_grid(displacement_grid, level); +} + +static void ensure_displacement_grids(Mesh *mesh, const int grid_level) +{ + const int num_grids = mesh->totloop; + MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS); + for (int grid_index = 0; grid_index < num_grids; grid_index++) { + ensure_displacement_grid(&mdisps[grid_index], grid_level); + } +} + +static void ensure_mask_grids(Mesh *mesh, const int level) +{ + GridPaintMask *grid_paint_masks = CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK); + if (grid_paint_masks == NULL) { + return; + } + const int num_grids = mesh->totloop; + const int grid_size = BKE_subdiv_grid_size_from_level(level); + const int grid_area = grid_size * grid_size; + for (int grid_index = 0; grid_index < num_grids; grid_index++) { + GridPaintMask *grid_paint_mask = &grid_paint_masks[grid_index]; + if (grid_paint_mask->level >= level) { + continue; + } + grid_paint_mask->level = level; + if (grid_paint_mask->data) { + MEM_freeN(grid_paint_mask->data); + } + /* TODO(sergey): Preserve data on the old level. */ + grid_paint_mask->data = MEM_calloc_arrayN(grid_area, sizeof(float), "gpm.data"); + } +} + +void multires_reshape_ensure_grids(Mesh *mesh, const int level) +{ + ensure_displacement_grids(mesh, level); + ensure_mask_grids(mesh, level); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Displacement, space conversion. + * \{ */ + +void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context) +{ + const MDisps *mdisps = reshape_context->mdisps; + const GridPaintMask *grid_paint_masks = reshape_context->grid_paint_masks; + + MDisps *orig_mdisps = MEM_dupallocN(mdisps); + GridPaintMask *orig_grid_paint_masks = NULL; + if (grid_paint_masks != NULL) { + orig_grid_paint_masks = MEM_dupallocN(grid_paint_masks); + } + + const int num_grids = reshape_context->num_grids; + for (int grid_index = 0; grid_index < num_grids; grid_index++) { + MDisps *orig_grid = &orig_mdisps[grid_index]; + /* Ignore possibly invalid/non-allocated original grids. They will be replaced with 0 original + * data when accessed during reshape process. + * Reshape process will ensure all grids are on top level, but that happens on separate set of + * grids which eventually replaces original one. */ + if (orig_grid->disps != NULL) { + orig_grid->disps = MEM_dupallocN(orig_grid->disps); + } + if (orig_grid_paint_masks != NULL) { + GridPaintMask *orig_paint_mask_grid = &orig_grid_paint_masks[grid_index]; + if (orig_paint_mask_grid->data != NULL) { + orig_paint_mask_grid->data = MEM_dupallocN(orig_paint_mask_grid->data); + } + } + } + + reshape_context->orig.mdisps = orig_mdisps; + reshape_context->orig.grid_paint_masks = orig_grid_paint_masks; +} + +typedef void (*ForeachGridCoordinateCallback)(const MultiresReshapeContext *reshape_context, + const GridCoord *grid_coord, + void *userdata_v); + +typedef struct ForeachGridCoordinateTaskData { + const MultiresReshapeContext *reshape_context; + + int grid_size; + float grid_size_1_inv; + + ForeachGridCoordinateCallback callback; + void *callback_userdata_v; +} ForeachGridCoordinateTaskData; + +static void foreach_grid_face_coordinate_task(void *__restrict userdata_v, + const int face_index, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + ForeachGridCoordinateTaskData *data = userdata_v; + + const MultiresReshapeContext *reshape_context = data->reshape_context; + + const Mesh *base_mesh = data->reshape_context->base_mesh; + const MPoly *mpoly = base_mesh->mpoly; + const int grid_size = data->grid_size; + const float grid_size_1_inv = 1.0f / (((float)grid_size) - 1.0f); + + const int num_corners = mpoly[face_index].totloop; + int grid_index = reshape_context->face_start_grid_index[face_index]; + for (int corner = 0; corner < num_corners; ++corner, ++grid_index) { + for (int y = 0; y < grid_size; ++y) { + const float v = (float)y * grid_size_1_inv; + for (int x = 0; x < grid_size; ++x) { + const float u = (float)x * grid_size_1_inv; + + GridCoord grid_coord; + grid_coord.grid_index = grid_index; + grid_coord.u = u; + grid_coord.v = v; + + data->callback(data->reshape_context, &grid_coord, data->callback_userdata_v); + } + } + } +} + +/* Run given callback for every grid coordinate at a given level. */ +static void foreach_grid_coordinate(const MultiresReshapeContext *reshape_context, + const int level, + ForeachGridCoordinateCallback callback, + void *userdata_v) +{ + ForeachGridCoordinateTaskData data; + data.reshape_context = reshape_context; + data.grid_size = BKE_subdiv_grid_size_from_level(level); + data.grid_size_1_inv = 1.0f / (((float)data.grid_size) - 1.0f); + data.callback = callback; + data.callback_userdata_v = userdata_v; + + TaskParallelSettings parallel_range_settings; + BLI_parallel_range_settings_defaults(¶llel_range_settings); + parallel_range_settings.min_iter_per_thread = 1; + + const Mesh *base_mesh = reshape_context->base_mesh; + const int num_faces = base_mesh->totpoly; + BLI_task_parallel_range( + 0, num_faces, &data, foreach_grid_face_coordinate_task, ¶llel_range_settings); +} + +static void object_grid_element_to_tangent_displacement( + const MultiresReshapeContext *reshape_context, + const GridCoord *grid_coord, + void *UNUSED(userdata_v)) +{ + float P[3]; + float tangent_matrix[3][3]; + multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, P, tangent_matrix); + + float inv_tangent_matrix[3][3]; + invert_m3_m3(inv_tangent_matrix, tangent_matrix); + + ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context, + grid_coord); + + float D[3]; + sub_v3_v3v3(D, grid_element.displacement, P); + + float tangent_D[3]; + mul_v3_m3v3(tangent_D, inv_tangent_matrix, D); + + copy_v3_v3(grid_element.displacement, tangent_D); +} + +void multires_reshape_object_grids_to_tangent_displacement( + const MultiresReshapeContext *reshape_context) +{ + foreach_grid_coordinate(reshape_context, + reshape_context->top.level, + object_grid_element_to_tangent_displacement, + NULL); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name MDISPS. + * \{ */ + +/* TODO(sergey): Make foreach_grid_coordinate more accessible and move this functionality to + * own file. */ + +static void assign_final_coords_from_mdisps(const MultiresReshapeContext *reshape_context, + const GridCoord *grid_coord, + void *UNUSED(userdata_v)) +{ + float P[3]; + float tangent_matrix[3][3]; + multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, P, tangent_matrix); + + ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context, + grid_coord); + float D[3]; + mul_v3_m3v3(D, tangent_matrix, grid_element.displacement); + + add_v3_v3v3(grid_element.displacement, P, D); +} + +void multires_reshape_assign_final_coords_from_mdisps( + const MultiresReshapeContext *reshape_context) +{ + foreach_grid_coordinate( + reshape_context, reshape_context->top.level, assign_final_coords_from_mdisps, NULL); +} + +static void assign_final_coords_from_orig_mdisps(const MultiresReshapeContext *reshape_context, + const GridCoord *grid_coord, + void *UNUSED(userdata_v)) +{ + float P[3]; + float tangent_matrix[3][3]; + multires_reshape_evaluate_limit_at_grid(reshape_context, grid_coord, P, tangent_matrix); + + const ReshapeConstGridElement orig_grid_element = + multires_reshape_orig_grid_element_for_grid_coord(reshape_context, grid_coord); + + float D[3]; + mul_v3_m3v3(D, tangent_matrix, orig_grid_element.displacement); + + ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord(reshape_context, + grid_coord); + add_v3_v3v3(grid_element.displacement, P, D); +} + +void multires_reshape_assign_final_coords_from_orig_mdisps( + const MultiresReshapeContext *reshape_context) +{ + foreach_grid_coordinate( + reshape_context, reshape_context->top.level, assign_final_coords_from_orig_mdisps, NULL); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/multires_reshape_vertcos.c b/source/blender/blenkernel/intern/multires_reshape_vertcos.c new file mode 100644 index 00000000000..5aff0b3caa2 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_reshape_vertcos.c @@ -0,0 +1,212 @@ +/* + * 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 bke + */ + +#include "multires_reshape.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math_vector.h" + +#include "BKE_subdiv_foreach.h" +#include "BKE_subdiv_mesh.h" + +typedef struct MultiresReshapeAssignVertcosContext { + const MultiresReshapeContext *reshape_context; + + const float (*vert_coords)[3]; + const int num_vert_coords; +} MultiresReshapeAssignVertcosContext; + +/** + * Set single displacement grid value at a reshape level to a corresponding vertex coordinate. + * This function will be called for every side of a boundary grid points for inner coordinates. + */ +static void multires_reshape_vertcos_foreach_single_vertex( + const SubdivForeachContext *foreach_context, + const GridCoord *grid_coord, + const int subdiv_vertex_index) +{ + MultiresReshapeAssignVertcosContext *reshape_vertcos_context = foreach_context->user_data; + const float *coordinate = reshape_vertcos_context->vert_coords[subdiv_vertex_index]; + + ReshapeGridElement grid_element = multires_reshape_grid_element_for_grid_coord( + reshape_vertcos_context->reshape_context, grid_coord); + BLI_assert(grid_element.displacement != NULL); + copy_v3_v3(grid_element.displacement, coordinate); +} + +/* TODO(sergey): De-duplicate with similar function in multires_reshape_smooth.c */ +static void multires_reshape_vertcos_foreach_vertex(const SubdivForeachContext *foreach_context, + const PTexCoord *ptex_coord, + const int subdiv_vertex_index) +{ + const MultiresReshapeAssignVertcosContext *reshape_vertcos_context = foreach_context->user_data; + const MultiresReshapeContext *reshape_context = reshape_vertcos_context->reshape_context; + + const GridCoord grid_coord = multires_reshape_ptex_coord_to_grid(reshape_context, ptex_coord); + const int face_index = multires_reshape_grid_to_face_index(reshape_context, + grid_coord.grid_index); + + const Mesh *base_mesh = reshape_context->base_mesh; + const MPoly *base_poly = &base_mesh->mpoly[face_index]; + const int num_corners = base_poly->totloop; + const int start_grid_index = reshape_context->face_start_grid_index[face_index]; + const int corner = grid_coord.grid_index - start_grid_index; + + if (grid_coord.u == 0.0f && grid_coord.v == 0.0f) { + for (int current_corner = 0; current_corner < num_corners; ++current_corner) { + GridCoord corner_grid_coord = grid_coord; + corner_grid_coord.grid_index = start_grid_index + current_corner; + multires_reshape_vertcos_foreach_single_vertex( + foreach_context, &corner_grid_coord, subdiv_vertex_index); + } + return; + } + + multires_reshape_vertcos_foreach_single_vertex( + foreach_context, &grid_coord, subdiv_vertex_index); + + if (grid_coord.u == 0.0f) { + GridCoord prev_grid_coord; + prev_grid_coord.grid_index = start_grid_index + ((corner + num_corners - 1) % num_corners); + prev_grid_coord.u = grid_coord.v; + prev_grid_coord.v = 0.0f; + + multires_reshape_vertcos_foreach_single_vertex( + foreach_context, &prev_grid_coord, subdiv_vertex_index); + } + + if (grid_coord.v == 0.0f) { + GridCoord next_grid_coord; + next_grid_coord.grid_index = start_grid_index + ((corner + 1) % num_corners); + next_grid_coord.u = 0.0f; + next_grid_coord.v = grid_coord.u; + + multires_reshape_vertcos_foreach_single_vertex( + foreach_context, &next_grid_coord, subdiv_vertex_index); + } +} + +/* SubdivForeachContext::topology_info() */ +static bool multires_reshape_vertcos_foreach_topology_info( + const SubdivForeachContext *foreach_context, + const int num_vertices, + const int UNUSED(num_edges), + const int UNUSED(num_loops), + const int UNUSED(num_polygons)) +{ + MultiresReshapeAssignVertcosContext *reshape_vertcos_context = foreach_context->user_data; + if (num_vertices != reshape_vertcos_context->num_vert_coords) { + return false; + } + return true; +} + +/* SubdivForeachContext::vertex_inner() */ +static void multires_reshape_vertcos_foreach_vertex_inner( + const SubdivForeachContext *foreach_context, + void *UNUSED(tls_v), + const int ptex_face_index, + const float ptex_face_u, + const float ptex_face_v, + const int UNUSED(coarse_face_index), + const int UNUSED(coarse_face_corner), + const int subdiv_vertex_index) +{ + const PTexCoord ptex_coord = { + .ptex_face_index = ptex_face_index, + .u = ptex_face_u, + .v = ptex_face_v, + }; + multires_reshape_vertcos_foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index); +} + +/* SubdivForeachContext::vertex_every_corner() */ +static void multires_reshape_vertcos_foreach_vertex_every_corner( + const struct SubdivForeachContext *foreach_context, + void *UNUSED(tls_v), + const int ptex_face_index, + const float ptex_face_u, + const float ptex_face_v, + const int UNUSED(coarse_vertex_index), + const int UNUSED(coarse_face_index), + const int UNUSED(coarse_face_corner), + const int subdiv_vertex_index) +{ + const PTexCoord ptex_coord = { + .ptex_face_index = ptex_face_index, + .u = ptex_face_u, + .v = ptex_face_v, + }; + multires_reshape_vertcos_foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index); +} + +/* SubdivForeachContext::vertex_every_edge() */ +static void multires_reshape_vertcos_foreach_vertex_every_edge( + const struct SubdivForeachContext *foreach_context, + void *UNUSED(tls_v), + const int ptex_face_index, + const float ptex_face_u, + const float ptex_face_v, + const int UNUSED(coarse_edge_index), + const int UNUSED(coarse_face_index), + const int UNUSED(coarse_face_corner), + const int subdiv_vertex_index) +{ + const PTexCoord ptex_coord = { + .ptex_face_index = ptex_face_index, + .u = ptex_face_u, + .v = ptex_face_v, + }; + multires_reshape_vertcos_foreach_vertex(foreach_context, &ptex_coord, subdiv_vertex_index); +} + +/* Set displacement grids values at a reshape level to a object coordinates of the the given + * source. */ +bool multires_reshape_assign_final_coords_from_vertcos( + const MultiresReshapeContext *reshape_context, + const float (*vert_coords)[3], + const int num_vert_coords) +{ + MultiresReshapeAssignVertcosContext reshape_vertcos_context = { + .reshape_context = reshape_context, + .vert_coords = vert_coords, + .num_vert_coords = num_vert_coords, + }; + + SubdivForeachContext foreach_context = { + .topology_info = multires_reshape_vertcos_foreach_topology_info, + .vertex_inner = multires_reshape_vertcos_foreach_vertex_inner, + .vertex_every_edge = multires_reshape_vertcos_foreach_vertex_every_edge, + .vertex_every_corner = multires_reshape_vertcos_foreach_vertex_every_corner, + .user_data = &reshape_vertcos_context, + }; + + SubdivToMeshSettings mesh_settings; + mesh_settings.resolution = (1 << reshape_context->reshape.level) + 1; + mesh_settings.use_optimal_display = false; + + return BKE_subdiv_foreach_subdiv_geometry( + reshape_context->subdiv, &foreach_context, &mesh_settings, reshape_context->base_mesh); +} diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 41f7531ed85..d7247518f9e 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -524,7 +524,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) return true; } -void BKE_object_link_modifiers(Scene *scene, struct Object *ob_dst, const struct Object *ob_src) +void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_src) { ModifierData *md; BKE_object_free_modifiers(ob_dst, 0); @@ -562,7 +562,7 @@ void BKE_object_link_modifiers(Scene *scene, struct Object *ob_dst, const struct if (md->type == eModifierType_Multires) { /* Has to be done after mod creation, but *before* we actually copy its settings! */ multiresModifier_sync_levels_ex( - scene, ob_dst, (MultiresModifierData *)md, (MultiresModifierData *)nmd); + ob_dst, (MultiresModifierData *)md, (MultiresModifierData *)nmd); } modifier_copyData(md, nmd); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 6ca14ec9197..98638b6c865 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -740,7 +740,7 @@ bool BKE_scene_object_find(Scene *scene, Object *ob) return false; } -Object *BKE_scene_object_find_by_name(Scene *scene, const char *name) +Object *BKE_scene_object_find_by_name(const Scene *scene, const char *name) { for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { @@ -999,10 +999,10 @@ bool BKE_scene_camera_switch_update(Scene *scene) return false; } -char *BKE_scene_find_marker_name(Scene *scene, int frame) +const char *BKE_scene_find_marker_name(const Scene *scene, int frame) { - ListBase *markers = &scene->markers; - TimeMarker *m1, *m2; + const ListBase *markers = &scene->markers; + const TimeMarker *m1, *m2; /* search through markers for match */ for (m1 = markers->first, m2 = markers->last; m1 && m2; m1 = m1->next, m2 = m2->prev) { @@ -1024,9 +1024,9 @@ char *BKE_scene_find_marker_name(Scene *scene, int frame) /* return the current marker for this frame, * we can have more than 1 marker per frame, this just returns the first :/ */ -char *BKE_scene_find_last_marker_name(Scene *scene, int frame) +const char *BKE_scene_find_last_marker_name(const Scene *scene, int frame) { - TimeMarker *marker, *best_marker = NULL; + const TimeMarker *marker, *best_marker = NULL; int best_frame = -MAXFRAME * 2; for (marker = scene->markers.first; marker; marker = marker->next) { if (marker->frame == frame) { diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 0846c6c90a6..aa4066b643f 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -682,7 +682,6 @@ void BKE_sequencer_cache_put( key->nfra = cfra - seq->start; key->type = type; key->cost = cost; - key->cache_owner = cache; key->link_prev = NULL; key->link_next = NULL; key->is_temp_cache = true; diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index ea53726a94d..827cccc6bd6 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -4603,10 +4603,10 @@ void BKE_sequence_sound_init(Scene *scene, Sequence *seq) } } -Sequence *BKE_sequencer_foreground_frame_get(Scene *scene, int frame) +const Sequence *BKE_sequencer_foreground_frame_get(const Scene *scene, int frame) { - Editing *ed = BKE_sequencer_editing_get(scene, false); - Sequence *seq, *best_seq = NULL; + const Editing *ed = scene->ed; + const Sequence *seq, *best_seq = NULL; int best_machine = -1; if (!ed) { diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index df7597b7f01..47b02654104 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -135,7 +135,7 @@ static int sound_cfra; static char **audio_device_names = NULL; #endif -BLI_INLINE void sound_verify_evaluated_id(ID *id) +BLI_INLINE void sound_verify_evaluated_id(const ID *id) { UNUSED_VARS_NDEBUG(id); /* This is a bit tricky and not quite reliable, but good enough check. @@ -504,7 +504,7 @@ void BKE_sound_load(Main *bmain, bSound *sound) sound_load_audio(bmain, sound, true); } -AUD_Device *BKE_sound_mixdown(Scene *scene, AUD_DeviceSpecs specs, int start, float volume) +AUD_Device *BKE_sound_mixdown(const Scene *scene, AUD_DeviceSpecs specs, int start, float volume) { sound_verify_evaluated_id(&scene->id); return AUD_openMixdownDevice(specs, scene->sound_scene, volume, start / FPS); diff --git a/source/blender/blenkernel/intern/subdiv_displacement_multires.c b/source/blender/blenkernel/intern/subdiv_displacement_multires.c index 1f78cf4eb3b..50b2b3c7c46 100644 --- a/source/blender/blenkernel/intern/subdiv_displacement_multires.c +++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.c @@ -47,6 +47,7 @@ typedef struct MultiresDisplacementData { int grid_size; /* Mesh is used to read external displacement. */ Mesh *mesh; + const MultiresModifierData *mmd; const MPoly *mpoly; const MDisps *mdisps; /* Indexed by ptex face index, contains polygon/corner which corresponds @@ -328,9 +329,7 @@ static int displacement_get_face_corner(MultiresDisplacementData *data, static void initialize(SubdivDisplacement *displacement) { MultiresDisplacementData *data = displacement->user_data; - Mesh *mesh = data->mesh; - /* Make sure external displacement is read. */ - CustomData_external_read(&mesh->ldata, &mesh->id, CD_MASK_MDISPS, mesh->totloop); + multiresModifier_ensure_external_read(data->mesh, data->mmd); data->is_initialized = true; } @@ -421,6 +420,7 @@ static void displacement_init_data(SubdivDisplacement *displacement, data->subdiv = subdiv; data->grid_size = BKE_subdiv_grid_size_from_level(mmd->totlvl); data->mesh = mesh; + data->mmd = mmd; data->mpoly = mesh->mpoly; data->mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS); data->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv); diff --git a/source/blender/blenkernel/intern/subdiv_foreach.c b/source/blender/blenkernel/intern/subdiv_foreach.c index 0f56fc5ec21..347e2843423 100644 --- a/source/blender/blenkernel/intern/subdiv_foreach.c +++ b/source/blender/blenkernel/intern/subdiv_foreach.c @@ -1772,6 +1772,21 @@ static void subdiv_foreach_single_geometry_vertices(SubdivForeachTaskContext *ct } } +static void subdiv_foreach_mark_non_loose_geometry(SubdivForeachTaskContext *ctx) +{ + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + const MLoop *coarse_mloop = coarse_mesh->mloop; + for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) { + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *loop = &coarse_mloop[coarse_poly->loopstart + corner]; + BLI_BITMAP_ENABLE(ctx->coarse_edges_used_map, loop->e); + BLI_BITMAP_ENABLE(ctx->coarse_vertices_used_map, loop->v); + } + } +} + static void subdiv_foreach_single_thread_tasks(SubdivForeachTaskContext *ctx) { /* NOTE: In theory, we can try to skip allocation of TLS here, but in @@ -1785,6 +1800,15 @@ static void subdiv_foreach_single_thread_tasks(SubdivForeachTaskContext *ctx) /* Run callbacks which are supposed to be run once per shared geometry. */ subdiv_foreach_single_geometry_vertices(ctx, tls); subdiv_foreach_tls_free(ctx, tls); + + const SubdivForeachContext *foreach_context = ctx->foreach_context; + const bool is_loose_geometry_tagged = (foreach_context->vertex_every_edge != NULL && + foreach_context->vertex_every_corner != NULL); + const bool is_loose_geometry_tags_needed = (foreach_context->vertex_loose != NULL || + foreach_context->vertex_of_loose_edge != NULL); + if (is_loose_geometry_tagged && is_loose_geometry_tags_needed) { + subdiv_foreach_mark_non_loose_geometry(ctx); + } } static void subdiv_foreach_task(void *__restrict userdata, diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index 19425a0d80b..4635db98514 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -43,7 +43,7 @@ /* ********************** general blender movie support ***************************** */ static int start_stub(void *UNUSED(context_v), - Scene *UNUSED(scene), + const Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty), @@ -85,7 +85,7 @@ static void context_free_stub(void *UNUSED(context_v)) /* callbacks */ static int start_avi(void *context_v, - Scene *scene, + const Scene *scene, RenderData *rd, int rectx, int recty, @@ -102,7 +102,7 @@ static int append_avi(void *context_v, int recty, const char *suffix, ReportList *reports); -static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix); +static void filepath_avi(char *string, const RenderData *rd, bool preview, const char *suffix); static void *context_create_avi(void); static void context_free_avi(void *context_v); #endif /* WITH_AVI */ @@ -158,7 +158,7 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) #ifdef WITH_AVI -static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix) +static void filepath_avi(char *string, const RenderData *rd, bool preview, const char *suffix) { int sfra, efra; @@ -196,7 +196,7 @@ static void filepath_avi(char *string, RenderData *rd, bool preview, const char } static int start_avi(void *context_v, - Scene *UNUSED(scene), + const Scene *UNUSED(scene), RenderData *rd, int rectx, int recty, @@ -316,7 +316,7 @@ static void context_free_avi(void *context_v) #endif /* WITH_AVI */ /* similar to BKE_image_path_from_imformat() */ -void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix) +void BKE_movie_filepath_get(char *string, const RenderData *rd, bool preview, const char *suffix) { bMovieHandle *mh = BKE_movie_handle_get(rd->im_format.imtype); if (mh && mh->get_movie_path) { diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index deb9592b4f9..7e5d99617e4 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -114,8 +114,11 @@ typedef struct FFMpegContext { static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value); static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value); static void ffmpeg_set_expert_options(RenderData *rd); -static void ffmpeg_filepath_get( - FFMpegContext *context, char *string, struct RenderData *rd, bool preview, const char *suffix); +static void ffmpeg_filepath_get(FFMpegContext *context, + char *string, + const struct RenderData *rd, + bool preview, + const char *suffix); /* Delete a picture buffer */ @@ -338,7 +341,7 @@ static const char **get_file_extensions(int format) /* Write a frame to the output file */ static int write_video_frame( - FFMpegContext *context, RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) + FFMpegContext *context, const RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) { int got_output; int ret, success = 1; @@ -1217,7 +1220,7 @@ static void flush_ffmpeg(FFMpegContext *context) /* Get the output filename-- similar to the other output formats */ static void ffmpeg_filepath_get( - FFMpegContext *context, char *string, RenderData *rd, bool preview, const char *suffix) + FFMpegContext *context, char *string, const RenderData *rd, bool preview, const char *suffix) { char autosplit[20]; @@ -1282,13 +1285,13 @@ static void ffmpeg_filepath_get( BLI_path_suffix(string, FILE_MAX, suffix, ""); } -void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix) +void BKE_ffmpeg_filepath_get(char *string, const RenderData *rd, bool preview, const char *suffix) { ffmpeg_filepath_get(NULL, string, rd, preview, suffix); } int BKE_ffmpeg_start(void *context_v, - struct Scene *scene, + const struct Scene *scene, RenderData *rd, int rectx, int recty, @@ -1846,7 +1849,7 @@ void BKE_ffmpeg_codec_settings_verify(RenderData *rd) ffmpeg_set_expert_options(rd); } -bool BKE_ffmpeg_alpha_channel_is_supported(RenderData *rd) +bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd) { int codec = rd->ffcodecdata.codec; |