diff options
author | Antony Riakiotakis <kalast@gmail.com> | 2015-04-07 15:19:16 +0300 |
---|---|---|
committer | Antony Riakiotakis <kalast@gmail.com> | 2015-04-07 15:19:16 +0300 |
commit | e5a47562eb55b6434b8a141f896c1ee0f89d4b25 (patch) | |
tree | 918b07a4d067ff1c11e3760b3c494f645a8e4fb5 /source | |
parent | 7ffdbecdbf6f0f8f14dd5d5636401512a6e643cc (diff) | |
parent | d60ff6c112b1c5d3819676109b0080a316850eb0 (diff) |
Merge branch 'master' into gooseberry
Conflicts:
source/blender/blenkernel/intern/image.c
source/blender/blenkernel/intern/object_dupli.c
source/blender/blenkernel/intern/sequencer.c
source/blender/blenloader/intern/versioning_270.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/editors/space_view3d/view3d_ops.c
source/blender/makesdna/DNA_sequence_types.h
source/blender/makesdna/DNA_view3d_types.h
source/blender/windowmanager/CMakeLists.txt
source/blender/windowmanager/intern/wm_operators.c
source/blenderplayer/bad_level_call_stubs/stubs.c
Diffstat (limited to 'source')
308 files changed, 14925 insertions, 4901 deletions
diff --git a/source/blender/blenfont/BLF_translation.h b/source/blender/blenfont/BLF_translation.h index 0ce60c96611..ad1c29192d9 100644 --- a/source/blender/blenfont/BLF_translation.h +++ b/source/blender/blenfont/BLF_translation.h @@ -181,8 +181,7 @@ const char *BLF_translate_do_new_dataname(const char *msgctxt, const char *msgid #define BLF_I18NCONTEXT_ID_MASK "Mask" /* Helper for bpy.app.i18n object... */ -typedef struct -{ +typedef struct { const char *c_id; const char *py_id; const char *value; diff --git a/source/blender/blenkernel/BKE_anim.h b/source/blender/blenkernel/BKE_anim.h index 07af4af39c2..08ebe036f19 100644 --- a/source/blender/blenkernel/BKE_anim.h +++ b/source/blender/blenkernel/BKE_anim.h @@ -117,7 +117,7 @@ typedef struct DupliApplyData { DupliExtraData *extra; } DupliApplyData; -DupliApplyData *duplilist_apply(struct Object *ob, struct ListBase *duplilist); +DupliApplyData *duplilist_apply(struct Object *ob, struct Scene *scene, struct ListBase *duplilist); void duplilist_restore(struct ListBase *duplilist, DupliApplyData *apply_data); void duplilist_free_apply_data(DupliApplyData *apply_data); diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 0acfd40a110..a8e430c78df 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -56,22 +56,22 @@ bool id_type_can_have_animdata(struct ID *id); struct AnimData *BKE_animdata_from_id(struct ID *id); /* Add AnimData to the given ID-block */ -struct AnimData *BKE_id_add_animdata(struct ID *id); +struct AnimData *BKE_animdata_add_id(struct ID *id); /* Set active action used by AnimData from the given ID-block */ bool BKE_animdata_set_action(struct ReportList *reports, struct ID *id, struct bAction *act); /* Free AnimData */ -void BKE_free_animdata(struct ID *id); +void BKE_animdata_free(struct ID *id); /* Copy AnimData */ -struct AnimData *BKE_copy_animdata(struct AnimData *adt, const bool do_action); +struct AnimData *BKE_animdata_copy(struct AnimData *adt, const bool do_action); /* Copy AnimData */ -bool BKE_copy_animdata_id(struct ID *id_to, struct ID *id_from, const bool do_action); +bool BKE_animdata_copy_id(struct ID *id_to, struct ID *id_from, const bool do_action); /* Copy AnimData Actions */ -void BKE_copy_animdata_id_action(struct ID *id); +void BKE_animdata_copy_id_action(struct ID *id); /* Merge copies of data from source AnimData block */ typedef enum eAnimData_MergeCopy_Modes { @@ -91,7 +91,7 @@ void BKE_animdata_merge_copy(struct ID *dst_id, struct ID *src_id, eAnimData_Mer void BKE_animdata_make_local(struct AnimData *adt); /* Re-Assign ID's */ -void BKE_relink_animdata(struct AnimData *adt); +void BKE_animdata_relink(struct AnimData *adt); /* ************************************* */ /* KeyingSets API */ @@ -134,7 +134,7 @@ void BKE_animdata_fix_paths_rename(struct ID *owner_id, struct AnimData *adt, st bool verify_paths); /* Fix all the paths for the entire database... */ -void BKE_all_animdata_fix_paths_rename(ID *ref_id, const char *prefix, const char *oldName, const char *newName); +void BKE_animdata_fix_paths_rename_all(ID *ref_id, const char *prefix, const char *oldName, const char *newName); /* Fix the path after removing elements that are not ID (e.g., node) */ void BKE_animdata_fix_paths_remove(struct ID *id, const char *path); diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index e4d3fe4868b..86576f9a11b 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -42,7 +42,7 @@ extern "C" { * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 274 -#define BLENDER_SUBVERSION 2 +#define BLENDER_SUBVERSION 4 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 5 diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 4849c66df1b..aacb7a4066b 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -136,6 +136,14 @@ bool BKE_camera_view_frame_fit_to_coords( void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings); +/* Camera multi-view API */ + +struct Object *BKE_camera_multiview_render(struct Scene *scene, struct Object *camera, const char *viewname); +void BKE_camera_multiview_view_matrix(struct RenderData *rd, struct Object *camera, const bool is_left, float r_viewmat[4][4]); +void BKE_camera_multiview_model_matrix(struct RenderData *rd, struct Object *camera, const char *viewname, float r_modelmat[4][4]); +float BKE_camera_multiview_shift_x(struct RenderData *rd, struct Object *camera, const char *viewname); +void BKE_camera_multiview_params(struct RenderData *rd, struct CameraParams *params, struct Object *camera, const char *viewname); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 3b7ba24d20b..ad191968a40 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -45,34 +45,34 @@ struct Object; struct ImageFormatData; struct ImagePool; struct Main; +struct ReportList; #define IMA_MAX_SPACE 64 void BKE_images_init(void); void BKE_images_exit(void); +void BKE_image_free_packedfiles(struct Image *image); +void BKE_image_free_views(struct Image *image); void BKE_image_free_buffers(struct Image *image); /* call from library */ void BKE_image_free(struct Image *image); void BKE_imbuf_stamp_info(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf); -void BKE_image_stamp_buf( - struct Scene *scene, struct Object *camera, - unsigned char *rect, float *rectf, int width, int height, int channels); +void BKE_image_stamp_buf(struct Scene *scene, struct Object *camera, unsigned char *rect, float *rectf, int width, int height, int channels); bool BKE_imbuf_alpha_test(struct ImBuf *ibuf); int BKE_imbuf_write_stamp(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf); +void BKE_imbuf_write_prepare(struct ImBuf *ibuf, struct ImageFormatData *imf); int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf); int BKE_imbuf_write_as(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf, const bool is_copy); - void BKE_image_path_from_imformat( char *string, const char *base, const char *relbase, int frame, - const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames); + const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames, const char *suffix); void BKE_image_path_from_imtype( char *string, const char *base, const char *relbase, int frame, - const char imtype, const bool use_ext, const bool use_frames); - -bool BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); -bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype); + const char imtype, const bool use_ext, const bool use_frames, const char *suffix); +int BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); +int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype); char BKE_image_ftype_to_imtype(const int ftype); int BKE_image_imtype_to_ftype(const char imtype); @@ -103,6 +103,7 @@ void BKE_image_tag_time(struct Image *ima); /* ImageUser is in Texture, in Nodes, Background Image, Image Window, .... */ /* should be used in conjunction with an ID * to Image. */ struct ImageUser; +struct RenderData; struct RenderPass; struct RenderResult; @@ -172,11 +173,12 @@ struct Image *BKE_image_load_exists(const char *filepath); /* adds image, adds ibuf, generates color or pattern */ struct Image *BKE_image_add_generated( - struct Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4]); + struct Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d); /* adds image from imbuf, owns imbuf */ struct Image *BKE_image_add_from_imbuf(struct ImBuf *ibuf); /* for reload, refresh, pack */ +void BKE_image_init_imageuser(struct Image *ima, struct ImageUser *iuser); void BKE_image_signal(struct Image *ima, struct ImageUser *iuser, int signal); void BKE_image_walk_all_users(const struct Main *mainp, void *customdata, @@ -184,6 +186,8 @@ void BKE_image_walk_all_users(const struct Main *mainp, void *customdata, /* ensures an Image exists for viewing nodes or render */ struct Image *BKE_image_verify_viewer(int type, const char *name); +/* ensures the view node cache is compatible with the scene views */ +void BKE_image_verify_viewer_views(const struct RenderData *rd, struct Image *ima, struct ImageUser *iuser); /* called on frame change or before render */ void BKE_image_user_frame_calc(struct ImageUser *iuser, int cfra, int fieldnr); @@ -195,13 +199,23 @@ void BKE_image_update_frame(const struct Main *bmain, int cfra); /* sets index offset for multilayer files */ struct RenderPass *BKE_image_multilayer_index(struct RenderResult *rr, struct ImageUser *iuser); +/* sets index offset for multiview files */ +void BKE_image_multiview_index(struct Image *ima, struct ImageUser *iuser); + /* for multilayer images as well as for render-viewer */ +bool BKE_image_is_multilayer(struct Image *ima); struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima); void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima); +/* for multilayer images as well as for singlelayer */ +bool BKE_image_is_openexr(struct Image *ima); + /* for multiple slot render, call this before render */ void BKE_image_backup_render(struct Scene *scene, struct Image *ima); - + +/* for singlelayer openexr saving */ +bool BKE_image_save_openexr_multiview(struct Image *ima, struct ImBuf *ibuf, const char *filepath, const int flags); + /* goes over all textures that use images */ void BKE_image_free_all_textures(void); @@ -212,6 +226,7 @@ void BKE_image_free_anim_ibufs(struct Image *ima, int except_frame); void BKE_image_all_free_anim_ibufs(int except_frame); void BKE_image_memorypack(struct Image *ima); +void BKE_image_packfiles(struct ReportList *reports, struct Image *ima, const char *basepath); /* prints memory statistics for images */ void BKE_image_print_memlist(void); @@ -243,7 +258,8 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame); /* Guess offset for the first frame in the sequence */ int BKE_image_sequence_guess_offset(struct Image *image); - +bool BKE_image_has_anim(struct Image *image); +bool BKE_image_has_packedfile(struct Image *image); bool BKE_image_is_animated(struct Image *image); bool BKE_image_is_dirty(struct Image *image); void BKE_image_file_format_set(struct Image *image, int ftype); diff --git a/source/blender/blenkernel/BKE_mball.h b/source/blender/blenkernel/BKE_mball.h index 70f932f3292..321cbbbd708 100644 --- a/source/blender/blenkernel/BKE_mball.h +++ b/source/blender/blenkernel/BKE_mball.h @@ -45,8 +45,6 @@ struct MetaBall *BKE_mball_copy(struct MetaBall *mb); void BKE_mball_make_local(struct MetaBall *mb); -void BKE_mball_cubeTable_free(void); - bool BKE_mball_is_basis_for(struct Object *ob1, struct Object *ob2); bool BKE_mball_is_basis(struct Object *ob); struct Object *BKE_mball_basis_find(struct Scene *scene, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_mball_tessellate.h b/source/blender/blenkernel/BKE_mball_tessellate.h index a3d3e1934b7..361f31b704c 100644 --- a/source/blender/blenkernel/BKE_mball_tessellate.h +++ b/source/blender/blenkernel/BKE_mball_tessellate.h @@ -31,4 +31,6 @@ void BKE_mball_polygonize( struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob, struct ListBase *dispbase); +void BKE_mball_cubeTable_free(void); + #endif /* __BKE_MBALL_TESSELLATE_H__ */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ce5e73ff1b1..a481b2ab17f 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -507,8 +507,7 @@ const struct ListBase *BKE_node_clipboard_get_links(void); int BKE_node_clipboard_get_type(void); /* Node Instance Hash */ -typedef struct bNodeInstanceHash -{ +typedef struct bNodeInstanceHash { GHash *ghash; /* XXX should be made a direct member, GHash allocation needs to support it */ } bNodeInstanceHash; @@ -942,6 +941,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria #define CMP_NODE_MAP_RANGE 319 #define CMP_NODE_PLANETRACKDEFORM 320 #define CMP_NODE_CORNERPIN 321 +#define CMP_NODE_SWITCH_VIEW 322 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -976,7 +976,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria /* API */ void ntreeCompositExecTree(struct Scene *scene, struct bNodeTree *ntree, struct RenderData *rd, int rendering, int do_previews, - const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); + const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings, + const char *view_name); void ntreeCompositTagRender(struct Scene *sce); int ntreeCompositTagAnimated(struct bNodeTree *ntree); void ntreeCompositTagGenerators(struct bNodeTree *ntree); diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h index b750d8b283a..9b1b937febf 100644 --- a/source/blender/blenkernel/BKE_ocean.h +++ b/source/blender/blenkernel/BKE_ocean.h @@ -74,13 +74,14 @@ typedef struct OceanCache { #define OCEAN_CACHING 1 #define OCEAN_CACHED 2 -struct Ocean *BKE_add_ocean(void); -void BKE_free_ocean_data(struct Ocean *oc); -void BKE_free_ocean(struct Ocean *oc); +struct Ocean *BKE_ocean_add(void); +void BKE_ocean_free_data(struct Ocean *oc); +void BKE_ocean_free(struct Ocean *oc); -void BKE_init_ocean(struct Ocean *o, int M, int N, float Lx, float Lz, float V, float l, float A, float w, float damp, - float alignment, float depth, float time, short do_height_field, short do_chop, short do_normals, short do_jacobian, int seed); -void BKE_simulate_ocean(struct Ocean *o, float t, float scale, float chop_amount); +void BKE_ocean_init( + struct Ocean *o, int M, int N, float Lx, float Lz, float V, float l, float A, float w, float damp, + float alignment, float depth, float time, short do_height_field, short do_chop, short do_normals, short do_jacobian, int seed); +void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount); /* sampling the ocean surface */ float BKE_ocean_jminus_to_foam(float jminus, float coverage); @@ -92,16 +93,17 @@ void BKE_ocean_eval_ij(struct Ocean *oc, struct OceanResult *ocr, int i, int j) /* ocean cache handling */ -struct OceanCache *BKE_init_ocean_cache(const char *bakepath, const char *relbase, - int start, int end, float wave_scale, - float chop_amount, float foam_coverage, float foam_fade, int resolution); -void BKE_simulate_ocean_cache(struct OceanCache *och, int frame); +struct OceanCache *BKE_ocean_init_cache( + const char *bakepath, const char *relbase, + int start, int end, float wave_scale, + float chop_amount, float foam_coverage, float foam_fade, int resolution); +void BKE_ocean_simulate_cache(struct OceanCache *och, int frame); -void BKE_bake_ocean(struct Ocean *o, struct OceanCache *och, void (*update_cb)(void *, float progress, int *cancel), void *update_cb_data); +void BKE_ocean_bake(struct Ocean *o, struct OceanCache *och, void (*update_cb)(void *, float progress, int *cancel), void *update_cb_data); void BKE_ocean_cache_eval_uv(struct OceanCache *och, struct OceanResult *ocr, int f, float u, float v); void BKE_ocean_cache_eval_ij(struct OceanCache *och, struct OceanResult *ocr, int f, int i, int j); -void BKE_free_ocean_cache(struct OceanCache *och); +void BKE_ocean_free_cache(struct OceanCache *och); #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_treehash.h b/source/blender/blenkernel/BKE_outliner_treehash.h index 16ab02c0965..454edb40c4e 100644 --- a/source/blender/blenkernel/BKE_treehash.h +++ b/source/blender/blenkernel/BKE_outliner_treehash.h @@ -19,10 +19,10 @@ * * ***** END GPL LICENSE BLOCK ***** */ -#ifndef __BKE_TREEHASH_H__ -#define __BKE_TREEHASH_H__ +#ifndef __BKE_OUTLINER_TREEHASH_H__ +#define __BKE_OUTLINER_TREEHASH_H__ -/** \file BKE_treehash.h +/** \file BKE_outliner_treehash.h * \ingroup bke */ @@ -31,21 +31,21 @@ struct BLI_mempool; struct TreeStoreElem; /* create and fill hashtable with treestore elements */ -void *BKE_treehash_create_from_treestore(struct BLI_mempool *treestore); +void *BKE_outliner_treehash_create_from_treestore(struct BLI_mempool *treestore); /* full rebuild for already allocated hashtable */ -void *BKE_treehash_rebuild_from_treestore(void *treehash, struct BLI_mempool *treestore); +void *BKE_outliner_treehash_rebuild_from_treestore(void *treehash, struct BLI_mempool *treestore); /* full rebuild for already allocated hashtable */ -void BKE_treehash_add_element(void *treehash, struct TreeStoreElem *elem); +void BKE_outliner_treehash_add_element(void *treehash, struct TreeStoreElem *elem); /* find first unused element with specific type, nr and id */ -struct TreeStoreElem *BKE_treehash_lookup_unused(void *treehash, short type, short nr, struct ID *id); +struct TreeStoreElem *BKE_outliner_treehash_lookup_unused(void *treehash, short type, short nr, struct ID *id); /* find user or unused element with specific type, nr and id */ -struct TreeStoreElem *BKE_treehash_lookup_any(void *treehash, short type, short nr, struct ID *id); +struct TreeStoreElem *BKE_outliner_treehash_lookup_any(void *treehash, short type, short nr, struct ID *id); /* free treehash structure */ -void BKE_treehash_free(void *treehash); +void BKE_outliner_treehash_free(void *treehash); #endif diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index d5cd1fd5d3e..4d713449b17 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -194,8 +194,8 @@ typedef struct SculptSession { struct StrokeCache *cache; } SculptSession; -void BKE_free_sculptsession(struct Object *ob); -void BKE_free_sculptsession_deformMats(struct SculptSession *ss); +void BKE_sculptsession_free(struct Object *ob); +void BKE_sculptsession_free_deformMats(struct SculptSession *ss); void BKE_sculptsession_bm_to_me(struct Object *ob, bool reorder); void BKE_sculptsession_bm_to_me_for_render(struct Object *object); void BKE_sculpt_update_mesh_elements(struct Scene *scene, struct Sculpt *sd, struct Object *ob, diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 27c89dd3a09..79778d504a5 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -70,6 +70,7 @@ void BKE_scene_free(struct Scene *sce); struct Scene *BKE_scene_add(struct Main *bmain, const char *name); /* base functions */ +struct Base *BKE_scene_base_find_by_name(struct Scene *scene, const char *name); struct Base *BKE_scene_base_find(struct Scene *scene, struct Object *ob); struct Base *BKE_scene_base_add(struct Scene *sce, struct Object *ob); void BKE_scene_base_unlink(struct Scene *sce, struct Base *base); @@ -122,6 +123,9 @@ void BKE_scene_update_for_newframe_ex(struct EvaluationContext *eval_ctx, struct struct SceneRenderLayer *BKE_scene_add_render_layer(struct Scene *sce, const char *name); bool BKE_scene_remove_render_layer(struct Main *main, struct Scene *scene, struct SceneRenderLayer *srl); +struct SceneRenderView *BKE_scene_add_render_view(struct Scene *sce, const char *name); +bool BKE_scene_remove_render_view(struct Scene *scene, struct SceneRenderView *srv); + /* render profile */ int get_render_subsurf_level(const struct RenderData *r, int level); int get_render_child_particle_number(const struct RenderData *r, int num); @@ -142,6 +146,23 @@ int BKE_render_num_threads(const struct RenderData *r); double BKE_scene_unit_scale(const struct UnitSettings *unit, const int unit_type, double value); +/* multiview */ +bool BKE_scene_multiview_is_stereo3d(const struct RenderData *rd); +bool BKE_scene_multiview_is_render_view_active(const struct RenderData *rd, const struct SceneRenderView *srv); +bool BKE_scene_multiview_is_render_view_first(const struct RenderData *rd, const char *viewname); +bool BKE_scene_multiview_is_render_view_last(const struct RenderData *rd, const char *viewname); +size_t BKE_scene_multiview_num_views_get(const struct RenderData *rd); +struct SceneRenderView *BKE_scene_multiview_render_view_findindex(const struct RenderData *rd, const int view_id); +const char *BKE_scene_multiview_render_view_name_get(const struct RenderData *rd, const int view_id); +size_t BKE_scene_multiview_view_id_get(const struct RenderData *rd, const char *viewname); +void BKE_scene_multiview_filepath_get(struct SceneRenderView *srv, const char *filepath, char *r_filepath); +void BKE_scene_multiview_view_filepath_get(const struct RenderData *rd, const char *filepath, const char *view, char *r_filepath); +const char *BKE_scene_multiview_view_suffix_get(const struct RenderData *rd, const char *viewname); +const char *BKE_scene_multiview_view_id_suffix_get(const struct RenderData *rd, const size_t view_id); +void BKE_scene_multiview_view_prefix_get(struct Scene *scene, const char *name, char *rprefix, char **rext); +void BKE_scene_multiview_videos_dimensions_get(const struct RenderData *rd, const size_t width, const size_t height, size_t *r_width, size_t *r_height); +size_t BKE_scene_multiview_num_videos_get(const struct RenderData *rd); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 494caa25a7c..0e3fb5c29a9 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -41,6 +41,7 @@ struct Mask; struct Scene; struct Sequence; struct SequenceModifierData; +struct Stereo3dFormat; struct StripElem; struct bSound; @@ -99,6 +100,7 @@ typedef struct SeqRenderData { float motion_blur_shutter; bool skip_cache; bool is_proxy_render; + size_t view_id; } SeqRenderData; void BKE_sequencer_new_render_data( @@ -223,6 +225,7 @@ void BKE_sequencer_base_clipboard_pointers_store(struct ListBase *seqbase); void BKE_sequencer_base_clipboard_pointers_restore(struct ListBase *seqbase, struct Main *bmain); void BKE_sequence_free(struct Scene *scene, struct Sequence *seq); +void BKE_sequence_free_anim(struct Sequence *seq); const char *BKE_sequence_give_name(struct Sequence *seq); ListBase *BKE_sequence_seqbase_get(struct Sequence *seq, int *r_offset); void BKE_sequence_calc(struct Scene *scene, struct Sequence *seq); @@ -236,7 +239,7 @@ struct StripElem *BKE_sequencer_give_stripelem(struct Sequence *seq, int cfra); void BKE_sequencer_update_changed_seq_and_deps(struct Scene *scene, struct Sequence *changed_seq, int len_change, int ibuf_change); bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context, struct Sequence *seq, float cfra); -struct SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list); +void BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list, ListBase *queue); void BKE_sequencer_proxy_rebuild(struct SeqIndexBuildContext *context, short *stop, short *do_update, float *progress); void BKE_sequencer_proxy_rebuild_finish(struct SeqIndexBuildContext *context, bool stop); @@ -356,6 +359,10 @@ typedef struct SeqLoadInfo { int len; /* only for image strips */ char path[1024]; /* 1024 = FILE_MAX */ + /* multiview */ + char views_format; + struct Stereo3dFormat *stereo3d_format; + /* return values */ char name[64]; struct Sequence *seq_sound; /* for movie's */ @@ -400,7 +407,7 @@ struct Sequence *BKE_sequencer_add_sound_strip(struct bContext *C, ListBase *seq struct Sequence *BKE_sequencer_add_movie_strip(struct bContext *C, ListBase *seqbasep, struct SeqLoadInfo *seq_load); /* view3d draw callback, run when not in background view */ -typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, bool, bool, bool, int, char[256]); +typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, bool, bool, bool, int, const char *, char[256]); extern SequencerDrawView sequencer_view3d_cb; /* copy/paste */ diff --git a/source/blender/blenkernel/BKE_writeavi.h b/source/blender/blenkernel/BKE_writeavi.h index bc06be0295a..ca295c51f5d 100644 --- a/source/blender/blenkernel/BKE_writeavi.h +++ b/source/blender/blenkernel/BKE_writeavi.h @@ -43,16 +43,20 @@ struct ReportList; struct Scene; typedef struct bMovieHandle { - int (*start_movie)(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); - int (*append_movie)(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); - void (*end_movie)(void); - int (*get_next_frame)(struct RenderData *rd, struct ReportList *reports); /* optional */ - void (*get_movie_path)(char *string, struct RenderData *rd, bool preview); /* optional */ + int (*start_movie)(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, + struct ReportList *reports, bool preview, const char *suffix); + int (*append_movie)(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, struct ReportList *reports); + void (*end_movie)(void *context_v); + int (*get_next_frame)(void *context_v, struct RenderData *rd, struct ReportList *reports); /* optional */ + void (*get_movie_path)(char *string, struct RenderData *rd, bool preview, const char *suffix); /* optional */ + 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); +void BKE_movie_filepath_get(char *string, struct RenderData *rd, bool preview, const char *suffix); +void BKE_context_create(bMovieHandle *mh); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index 951fa501f9c..a40c31022e3 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -68,11 +68,11 @@ struct RenderData; struct ReportList; struct Scene; -int BKE_ffmpeg_start(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); -void BKE_ffmpeg_end(void); -int BKE_ffmpeg_append(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); -void BKE_ffmpeg_filepath_get(char *string, struct RenderData *rd, bool preview); +int BKE_ffmpeg_start(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview, const char *suffix); +void BKE_ffmpeg_end(void *context_v); +int BKE_ffmpeg_append(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, struct ReportList *reports); +void BKE_ffmpeg_filepath_get(char *string, 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); @@ -82,6 +82,9 @@ bool BKE_ffmpeg_alpha_channel_is_supported(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_); +void *BKE_ffmpeg_context_create(void); +void BKE_ffmpeg_context_free(void *context_v); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_writeframeserver.h b/source/blender/blenkernel/BKE_writeframeserver.h index b7d601ea769..0837e9bce79 100644 --- a/source/blender/blenkernel/BKE_writeframeserver.h +++ b/source/blender/blenkernel/BKE_writeframeserver.h @@ -40,11 +40,16 @@ struct RenderData; struct ReportList; struct Scene; -int BKE_frameserver_start(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); -void BKE_frameserver_end(void); -int BKE_frameserver_append(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); -int BKE_frameserver_loop(struct RenderData *rd, struct ReportList *reports); +int BKE_frameserver_start( + void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, + struct ReportList *reports, bool preview, const char *suffix); +void BKE_frameserver_end(void *context_v); +int BKE_frameserver_append( + void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, struct ReportList *reports); +int BKE_frameserver_loop(void *context_v, struct RenderData *rd, struct ReportList *reports); +void *BKE_frameserver_context_create(void); +void BKE_frameserver_context_free(void *context_v); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 636a4cda039..fb87aa870d5 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -138,6 +138,7 @@ set(SRC intern/object_deform.c intern/object_dupli.c intern/ocean.c + intern/outliner_treehash.c intern/packedFile.c intern/paint.c intern/particle.c @@ -177,7 +178,6 @@ set(SRC intern/tracking_solver.c intern/tracking_stabilize.c intern/tracking_util.c - intern/treehash.c intern/unit.c intern/world.c intern/writeavi.c @@ -217,6 +217,8 @@ set(SRC BKE_depsgraph.h BKE_displist.h BKE_dynamicpaint.h + BKE_editmesh.h + BKE_editmesh_bvh.h BKE_effect.h BKE_facemap.h BKE_fcurve.h @@ -253,6 +255,7 @@ set(SRC BKE_object.h BKE_object_deform.h BKE_ocean.h + BKE_outliner_treehash.h BKE_packedFile.h BKE_paint.h BKE_particle.h @@ -274,12 +277,9 @@ set(SRC BKE_strands.h BKE_subsurf.h BKE_suggestions.h - BKE_editmesh.h - BKE_editmesh_bvh.h BKE_text.h BKE_texture.h BKE_tracking.h - BKE_treehash.h BKE_unit.h BKE_utildefines.h BKE_world.h diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index b4c396d38d2..3c98d11dbdb 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -134,7 +134,7 @@ AnimData *BKE_animdata_from_id(ID *id) * the AnimData pointer is stored immediately after the given ID-block in the struct, * as per IdAdtTemplate. Also note that */ -AnimData *BKE_id_add_animdata(ID *id) +AnimData *BKE_animdata_add_id(ID *id) { /* Only some ID-blocks have this info for now, so we cast the * types that do to be of type IdAdtTemplate, and add the AnimData @@ -216,7 +216,7 @@ bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act) /* Freeing -------------------------------------------- */ /* Free AnimData used by the nominated ID-block, and clear ID-block's AnimData pointer */ -void BKE_free_animdata(ID *id) +void BKE_animdata_free(ID *id) { /* Only some ID-blocks have this info for now, so we cast the * types that do to be of type IdAdtTemplate @@ -253,7 +253,7 @@ void BKE_free_animdata(ID *id) /* Copying -------------------------------------------- */ /* Make a copy of the given AnimData - to be used when copying datablocks */ -AnimData *BKE_copy_animdata(AnimData *adt, const bool do_action) +AnimData *BKE_animdata_copy(AnimData *adt, const bool do_action) { AnimData *dadt; @@ -285,25 +285,25 @@ AnimData *BKE_copy_animdata(AnimData *adt, const bool do_action) return dadt; } -bool BKE_copy_animdata_id(ID *id_to, ID *id_from, const bool do_action) +bool BKE_animdata_copy_id(ID *id_to, ID *id_from, const bool do_action) { AnimData *adt; if ((id_to && id_from) && (GS(id_to->name) != GS(id_from->name))) return false; - BKE_free_animdata(id_to); + BKE_animdata_free(id_to); adt = BKE_animdata_from_id(id_from); if (adt) { IdAdtTemplate *iat = (IdAdtTemplate *)id_to; - iat->adt = BKE_copy_animdata(adt, do_action); + iat->adt = BKE_animdata_copy(adt, do_action); } return true; } -void BKE_copy_animdata_id_action(ID *id) +void BKE_animdata_copy_id_action(ID *id) { AnimData *adt = BKE_animdata_from_id(id); if (adt) { @@ -426,7 +426,7 @@ void BKE_animdata_make_local(AnimData *adt) /* When duplicating data (i.e. objects), drivers referring to the original data will * get updated to point to the duplicated data (if drivers belong to the new data) */ -void BKE_relink_animdata(AnimData *adt) +void BKE_animdata_relink(AnimData *adt) { /* sanity check */ if (adt == NULL) @@ -571,7 +571,7 @@ void BKE_animdata_separate_by_basepath(ID *srcID, ID *dstID, ListBase *basepaths /* get animdata from src, and create for destination (if needed) */ srcAdt = BKE_animdata_from_id(srcID); - dstAdt = BKE_id_add_animdata(dstID); + dstAdt = BKE_animdata_add_id(dstID); if (ELEM(NULL, srcAdt, dstAdt)) { if (G.debug & G_DEBUG) @@ -1167,7 +1167,7 @@ void BKE_animdata_main_cb(Main *mainptr, ID_AnimData_Edit_Callback func, void *u * i.e. pose.bones["Bone"] */ /* TODO: use BKE_animdata_main_cb for looping over all data */ -void BKE_all_animdata_fix_paths_rename(ID *ref_id, const char *prefix, const char *oldName, const char *newName) +void BKE_animdata_fix_paths_rename_all(ID *ref_id, const char *prefix, const char *oldName, const char *newName) { Main *mainptr = G.main; ID *id; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index f775b67eedb..dc11bcb00aa 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -127,7 +127,7 @@ void BKE_armature_free(bArmature *arm) /* free animation data */ if (arm->adt) { - BKE_free_animdata(&arm->id); + BKE_animdata_free(&arm->id); arm->adt = NULL; } } @@ -2238,7 +2238,7 @@ static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *o } /* compute scale factor for xz axes from this value */ - final_scale = sqrt(bulge); + final_scale = sqrtf(bulge); } else { /* no scaling, so scale factor is simple */ diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index e5160fe7984..955bd97fa06 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -425,10 +425,10 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int { Image *ima; ima = (Image *)id; - if (ima->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { + if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { if (rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data)) { - if (!ima->packedfile) { + if (!BKE_image_has_packedfile(ima)) { BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); BKE_image_walk_all_users(bmain, ima, bpath_traverse_image_user_cb); } diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 886df16d0ee..fec6542ac47 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -30,16 +30,19 @@ */ #include <stdlib.h> +#include <stddef.h> #include "DNA_camera_types.h" #include "DNA_lamp_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_view3d_types.h" +#include "DNA_ID.h" #include "BLI_math.h" -#include "BLI_utildefines.h" #include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" #include "BKE_animsys.h" #include "BKE_camera.h" @@ -47,6 +50,7 @@ #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "GPU_compositing.h" @@ -71,6 +75,10 @@ void *BKE_camera_add(Main *bmain, const char *name) GPU_fx_compositor_init_dof_settings(&cam->gpu_dof); + /* stereoscopy 3d */ + cam->stereo.interocular_distance = 0.065f; + cam->stereo.convergence_distance = 30.f * 0.065f; + return cam; } @@ -138,7 +146,7 @@ void BKE_camera_make_local(Camera *cam) void BKE_camera_free(Camera *ca) { - BKE_free_animdata((ID *)ca); + BKE_animdata_free((ID *)ca); } /******************************** Camera Usage *******************************/ @@ -695,6 +703,250 @@ bool BKE_camera_view_frame_fit_to_coords( return camera_frame_fit_calc_from_data(¶ms, &data_cb, r_co, r_scale); } +/******************* multiview matrix functions ***********************/ + +static void camera_model_matrix(Object *camera, float r_modelmat[4][4]) +{ + copy_m4_m4(r_modelmat, camera->obmat); +} + +static void camera_stereo3d_model_matrix(Object *camera, const bool is_left, float r_modelmat[4][4]) +{ + Camera *data = (Camera *)camera->data; + float interocular_distance, convergence_distance; + short convergence_mode, pivot; + float sizemat[4][4]; + + float fac = 1.0f; + float fac_signed; + + interocular_distance = data->stereo.interocular_distance; + convergence_distance = data->stereo.convergence_distance; + convergence_mode = data->stereo.convergence_mode; + pivot = data->stereo.pivot; + + if (((pivot == CAM_S3D_PIVOT_LEFT) && is_left) || + ((pivot == CAM_S3D_PIVOT_RIGHT) && !is_left)) + { + return camera_model_matrix(camera, r_modelmat); + } + else { + float size[3]; + mat4_to_size(size, camera->obmat); + size_to_mat4(sizemat, size); + } + + if (pivot == CAM_S3D_PIVOT_CENTER) + fac = 0.5f; + + fac_signed = is_left ? fac : -fac; + + /* rotation */ + if (convergence_mode == CAM_S3D_TOE) { + float angle; + float angle_sin, angle_cos; + float toeinmat[4][4]; + float rotmat[4][4]; + + unit_m4(rotmat); + + if (pivot == CAM_S3D_PIVOT_CENTER) { + fac = -fac; + fac_signed = -fac_signed; + } + + angle = atanf((interocular_distance * 0.5f) / convergence_distance) / fac; + + angle_cos = cosf(angle * fac_signed); + angle_sin = sinf(angle * fac_signed); + + rotmat[0][0] = angle_cos; + rotmat[2][0] = -angle_sin; + rotmat[0][2] = angle_sin; + rotmat[2][2] = angle_cos; + + if (pivot == CAM_S3D_PIVOT_CENTER) { + /* set the rotation */ + copy_m4_m4(toeinmat, rotmat); + /* set the translation */ + toeinmat[3][0] = interocular_distance * fac_signed; + + /* transform */ + normalize_m4_m4(r_modelmat, camera->obmat); + mul_m4_m4m4(r_modelmat, r_modelmat, toeinmat); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } + else { /* CAM_S3D_PIVOT_LEFT, CAM_S3D_PIVOT_RIGHT */ + /* rotate perpendicular to the interocular line */ + normalize_m4_m4(r_modelmat, camera->obmat); + mul_m4_m4m4(r_modelmat, r_modelmat, rotmat); + + /* translate along the interocular line */ + unit_m4(toeinmat); + toeinmat[3][0] = -interocular_distance * fac_signed; + mul_m4_m4m4(r_modelmat, r_modelmat, toeinmat); + + /* rotate to toe-in angle */ + mul_m4_m4m4(r_modelmat, r_modelmat, rotmat); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } + } + else { + normalize_m4_m4(r_modelmat, camera->obmat); + + /* translate - no rotation in CAM_S3D_OFFAXIS, CAM_S3D_PARALLEL */ + translate_m4(r_modelmat, -interocular_distance * fac_signed, 0.0f, 0.0f); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } +} + +/* the view matrix is used by the viewport drawing, it is basically the inverted model matrix */ +void BKE_camera_multiview_view_matrix(RenderData *rd, Object *camera, const bool is_left, float r_viewmat[4][4]) +{ + BKE_camera_multiview_model_matrix(rd, camera, is_left ? STEREO_LEFT_NAME : STEREO_RIGHT_NAME, r_viewmat); + invert_m4(r_viewmat); +} + +/* left is the default */ +static bool camera_is_left(const char *viewname) +{ + if (viewname && viewname[0] != '\0') { + return !STREQ(viewname, STEREO_RIGHT_NAME); + } + return true; +} + +void BKE_camera_multiview_model_matrix(RenderData *rd, Object *camera, const char *viewname, float r_modelmat[4][4]) +{ + const bool is_multiview = (rd && rd->scemode & R_MULTIVIEW) != 0; + + if (!is_multiview) { + camera_model_matrix(camera, r_modelmat); + } + else if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + camera_model_matrix(camera, r_modelmat); + } + else { /* SCE_VIEWS_SETUP_BASIC */ + const bool is_left = camera_is_left(viewname); + camera_stereo3d_model_matrix(camera, is_left, r_modelmat); + } + normalize_m4(r_modelmat); +} + +static Object *camera_multiview_advanced(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); + + name[0] = '\0'; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + const int len_suffix = strlen(srv->suffix); + + if (len_name < len_suffix) + continue; + + if (STREQ(camera_name + (len_name - len_suffix), srv->suffix)) { + BLI_snprintf(name, sizeof(name), "%.*s%s", (len_name - len_suffix), camera_name, suffix); + break; + } + } + + if (name[0] != '\0') { + Base *base = BKE_scene_base_find_by_name(scene, name); + if (base) { + return base->object; + } + } + + return camera; +} + +/* returns the camera to be used for render */ +Object *BKE_camera_multiview_render(Scene *scene, Object *camera, const char *viewname) +{ + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + + if (!is_multiview) { + return camera; + } + else if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + return camera; + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + const char *suffix = BKE_scene_multiview_view_suffix_get(&scene->r, viewname); + return camera_multiview_advanced(scene, camera, suffix); + } +} + +static float camera_stereo3d_shift_x(Object *camera, const char *viewname) +{ + Camera *data = camera->data; + float shift = data->shiftx; + float interocular_distance, convergence_distance; + short convergence_mode, pivot; + bool is_left = true; + + float fac = 1.0f; + float fac_signed; + + if (viewname && viewname[0]) { + is_left = STREQ(viewname, STEREO_LEFT_NAME); + } + + interocular_distance = data->stereo.interocular_distance; + convergence_distance = data->stereo.convergence_distance; + convergence_mode = data->stereo.convergence_mode; + pivot = data->stereo.pivot; + + if (((pivot == CAM_S3D_PIVOT_LEFT) && is_left) || + ((pivot == CAM_S3D_PIVOT_RIGHT) && !is_left)) + { + return shift; + } + + if (pivot == CAM_S3D_PIVOT_CENTER) + fac = 0.5f; + + fac_signed = is_left ? fac : -fac; + + /* Note: in viewport, parallel renders as offaxis, but in render it does parallel */ + if (ELEM(convergence_mode, CAM_S3D_OFFAXIS, CAM_S3D_PARALLEL)) { + shift += ((interocular_distance / data->sensor_x) * (data->lens / convergence_distance)) * fac_signed; + } + + return shift; +} + +float BKE_camera_multiview_shift_x(RenderData *rd, Object *camera, const char *viewname) +{ + const bool is_multiview = (rd && rd->scemode & R_MULTIVIEW) != 0; + Camera *data = camera->data; + + if (!is_multiview) { + return data->shiftx; + } + else if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + return data->shiftx; + } + else { /* SCE_VIEWS_SETUP_BASIC */ + return camera_stereo3d_shift_x(camera, viewname); + } +} + +void BKE_camera_multiview_params(RenderData *rd, CameraParams *params, Object *camera, const char *viewname) +{ + params->shiftx = BKE_camera_multiview_shift_x(rd, camera, viewname); +} + void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings) { if (camera->type == OB_CAMERA) { diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 016a4bb8b22..31f733d7a81 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -2545,16 +2545,16 @@ DerivedMesh *CDDM_merge_verts(DerivedMesh *dm, const int *vtargetmap, const int const unsigned int v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1; const unsigned int v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2; if (LIKELY(v1 != v2)) { - void **eh_p = BLI_edgehash_lookup_p(ehash, v1, v2); + void **val_p; - if (eh_p) { - newe[i] = GET_INT_FROM_POINTER(*eh_p); + if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) { + newe[i] = GET_INT_FROM_POINTER(*val_p); } else { STACK_PUSH(olde, i); STACK_PUSH(medge, *med); newe[i] = c; - BLI_edgehash_insert(ehash, v1, v2, SET_INT_IN_POINTER(c)); + *val_p = SET_INT_IN_POINTER(c); c++; } } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 2b886ef9be9..e096506a95a 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -147,7 +147,7 @@ void BKE_curve_free(Curve *cu) BKE_curve_editNurb_free(cu); BKE_curve_unlink(cu); - BKE_free_animdata((ID *)cu); + BKE_animdata_free((ID *)cu); if (cu->mat) MEM_freeN(cu->mat); diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index 350b19b0250..72133cf6460 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -982,7 +982,7 @@ void BKE_defvert_extract_vgroup_to_vertweights( } } else { - fill_vn_fl(r_weights, invert_vgroup ? 1.0f : 0.0f, num_verts); + fill_vn_fl(r_weights, num_verts, invert_vgroup ? 1.0f : 0.0f); } } @@ -1008,7 +1008,7 @@ void BKE_defvert_extract_vgroup_to_edgeweights( MEM_freeN(tmp_weights); } else { - fill_vn_fl(r_weights, 0.0f, num_edges); + fill_vn_fl(r_weights, num_edges, 0.0f); } } @@ -1031,7 +1031,7 @@ void BKE_defvert_extract_vgroup_to_loopweights( MEM_freeN(tmp_weights); } else { - fill_vn_fl(r_weights, 0.0f, num_loops); + fill_vn_fl(r_weights, num_loops, 0.0f); } } @@ -1060,7 +1060,7 @@ void BKE_defvert_extract_vgroup_to_polyweights( MEM_freeN(tmp_weights); } else { - fill_vn_fl(r_weights, 0.0f, num_polys); + fill_vn_fl(r_weights, num_polys, 0.0f); } } diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index dd2155505fb..17d87394118 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -119,7 +119,7 @@ void BKE_gpencil_free(bGPdata *gpd) /* free animation data */ if (gpd->adt) { - BKE_free_animdata(&gpd->id); + BKE_animdata_free(&gpd->id); gpd->adt = NULL; } } diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index 8bda957f187..d3225f3fa35 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -311,16 +311,14 @@ Icon *BKE_icon_get(int icon_id) void BKE_icon_set(int icon_id, struct Icon *icon) { - Icon *old_icon = NULL; + void **val_p; - old_icon = BLI_ghash_lookup(gIcons, SET_INT_IN_POINTER(icon_id)); - - if (old_icon) { + if (BLI_ghash_ensure_p(gIcons, SET_INT_IN_POINTER(icon_id), &val_p)) { printf("BKE_icon_set: Internal error, icon already set: %d\n", icon_id); return; } - BLI_ghash_insert(gIcons, SET_INT_IN_POINTER(icon_id), icon); + *val_p = icon; } void BKE_icon_delete(struct ID *id) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 9be09e99bcc..f852d886ae9 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -99,6 +99,12 @@ static SpinLock image_spin; +/* prototypes */ +static size_t image_num_files(struct Image *ima); +static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r); +static void image_update_views_format(Image *ima, ImageUser *iuser); +static void image_add_view(Image *ima, const char *viewname, const char *filepath); + /* max int, to indicate we don't store sequences in ibuf */ #define IMA_NO_INDEX 0x7FEFEFEF @@ -257,6 +263,46 @@ static void image_free_cached_frames(Image *image) } } +static void image_free_packedfiles(Image *ima) +{ + while (ima->packedfiles.last) { + ImagePackedFile *imapf = ima->packedfiles.last; + if (imapf->packedfile) { + freePackedFile(imapf->packedfile); + } + BLI_remlink(&ima->packedfiles, imapf); + MEM_freeN(imapf); + } +} + +void BKE_image_free_packedfiles(Image *ima) +{ + image_free_packedfiles(ima); +} + +static void image_free_views(Image *ima) +{ + BLI_freelistN(&ima->views); +} + +void BKE_image_free_views(Image *image) +{ + image_free_views(image); +} + +static void image_free_anims(Image *ima) +{ + while (ima->anims.last) { + ImageAnim *ia = ima->anims.last; + if (ia->anim) { + IMB_free_anim(ia->anim); + ia->anim = NULL; + } + BLI_remlink(&ima->anims, ia); + MEM_freeN(ia); + } +} + /** * Simply free the image data from memory, * on display the image can load again (except for render buffers). @@ -265,8 +311,7 @@ void BKE_image_free_buffers(Image *ima) { image_free_cached_frames(ima); - if (ima->anim) IMB_free_anim(ima->anim); - ima->anim = NULL; + image_free_anims(ima); if (ima->rr) { RE_FreeRenderResult(ima->rr); @@ -290,10 +335,9 @@ void BKE_image_free(Image *ima) int a; BKE_image_free_buffers(ima); - if (ima->packedfile) { - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - } + + image_free_packedfiles(ima); + BKE_icon_delete(&ima->id); ima->id.icon_id = 0; @@ -305,6 +349,9 @@ void BKE_image_free(Image *ima) ima->renders[a] = NULL; } } + + image_free_views(ima); + MEM_freeN(ima->stereo3d_format); } /* only image block itself */ @@ -328,7 +375,9 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ ima->flag |= IMA_VIEW_AS_RENDER; BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); + ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); } + return ima; } @@ -359,6 +408,22 @@ static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame) } } +static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) +{ + const ImagePackedFile *imapf_src; + + BLI_listbase_clear(lb_dst); + for (imapf_src = lb_src->first; imapf_src; imapf_src = imapf_src->next) { + ImagePackedFile *imapf_dst = MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)"); + BLI_strncpy(imapf_dst->filepath, imapf_src->filepath, sizeof(imapf_dst->filepath)); + + if (imapf_src->packedfile) + imapf_dst->packedfile = dupPackedFile(imapf_src->packedfile); + + BLI_addtail(lb_dst, imapf_dst); + } +} + /* empty image block, of similar type and filename */ Image *BKE_image_copy(Main *bmain, Image *ima) { @@ -381,8 +446,10 @@ Image *BKE_image_copy(Main *bmain, Image *ima) BKE_color_managed_colorspace_settings_copy(&nima->colorspace_settings, &ima->colorspace_settings); - if (ima->packedfile) - nima->packedfile = dupPackedFile(ima->packedfile); + copy_image_packedfiles(&nima->packedfiles, &ima->packedfiles); + + nima->stereo3d_format = MEM_dupallocN(ima->stereo3d_format); + BLI_duplicatelist(&nima->views, &ima->views); if (ima->id.lib) { BKE_id_lib_local_paths(bmain, ima->id.lib, &nima->id); @@ -686,7 +753,9 @@ Image *BKE_image_load_exists_ex(const char *filepath, bool *r_exists) BLI_path_abs(strtest, ID_BLEND_PATH(G.main, &ima->id)); if (BLI_path_cmp(strtest, str) == 0) { - if (ima->anim == NULL || ima->id.us == 0) { + if ((BKE_image_has_anim(ima) == false) || + (ima->id.us == 0)) + { ima->id.us++; /* officially should not, it doesn't link here! */ if (ima->ok == 0) ima->ok = IMA_OK; @@ -774,13 +843,14 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char } /* adds new image block, creates ImBuf and initializes color */ -Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4]) +Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d) { /* on save, type is changed to FILE in editsima.c */ Image *ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); if (ima) { - ImBuf *ibuf; + size_t view_id; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; /* BLI_strncpy(ima->name, name, FILE_MAX); */ /* don't do this, this writes in ain invalid filepath! */ ima->gen_x = width; @@ -790,13 +860,21 @@ Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int hei ima->gen_depth = depth; copy_v4_v4(ima->gen_color, color); - ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + for (view_id = 0; view_id < 2; view_id++) { + ImBuf *ibuf; + ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings); + image_assign_ibuf(ima, ibuf, stereo3d ? view_id : IMA_NO_INDEX, 0); - /* image_assign_ibuf puts buffer to the cache, which increments user counter. */ - IMB_freeImBuf(ibuf); + /* image_assign_ibuf puts buffer to the cache, which increments user counter. */ + IMB_freeImBuf(ibuf); + if (!stereo3d) break; + + image_add_view(ima, names[view_id], ""); + } ima->ok = IMA_OK_LOADED; + if (stereo3d) + ima->flag |= IMA_IS_STEREO | IMA_IS_MULTIVIEW; } return ima; @@ -821,17 +899,76 @@ Image *BKE_image_add_from_imbuf(ImBuf *ibuf) return ima; } +/* packs rects from memory as PNG + * convert multiview images to R_IMF_VIEWS_INDIVIDUAL + */ +static void image_memorypack_multiview(Image *ima) +{ + ImageView *iv; + size_t i; + + image_free_packedfiles(ima); + + for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) { + ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, i, 0); + + ibuf->ftype = PNG; + ibuf->planes = R_IMF_PLANES_RGBA; + + /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ + if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; + BLI_path_suffix(iv->filepath, FILE_MAX, suffix[i], ""); + } + + IMB_saveiff(ibuf, iv->filepath, IB_rect | IB_mem); + + if (ibuf->encodedbuffer == NULL) { + printf("memory save for pack error\n"); + IMB_freeImBuf(ibuf); + image_free_packedfiles(ima); + return; + } + else { + ImagePackedFile *imapf; + PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile"); + + pf->data = ibuf->encodedbuffer; + pf->size = ibuf->encodedsize; + + imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile"); + BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath)); + imapf->packedfile = pf; + BLI_addtail(&ima->packedfiles, imapf); + + ibuf->encodedbuffer = NULL; + ibuf->encodedsize = 0; + ibuf->userflags &= ~IB_BITMAPDIRTY; + } + IMB_freeImBuf(ibuf); + } + + if (ima->source == IMA_SRC_GENERATED) { + ima->source = IMA_SRC_FILE; + ima->type = IMA_TYPE_IMAGE; + } + ima->views_format = R_IMF_VIEWS_INDIVIDUAL; +} + /* packs rect from memory as PNG */ void BKE_image_memorypack(Image *ima) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ImBuf *ibuf; + + if ((ima->flag & IMA_IS_MULTIVIEW)) + return image_memorypack_multiview(ima); + + ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); if (ibuf == NULL) return; - if (ima->packedfile) { - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - } + + image_free_packedfiles(ima); ibuf->ftype = PNG; ibuf->planes = R_IMF_PLANES_RGBA; @@ -841,11 +978,17 @@ void BKE_image_memorypack(Image *ima) printf("memory save for pack error\n"); } else { + ImagePackedFile *imapf; PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile"); pf->data = ibuf->encodedbuffer; pf->size = ibuf->encodedsize; - ima->packedfile = pf; + + imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile"); + BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath)); + imapf->packedfile = pf; + BLI_addtail(&ima->packedfiles, imapf); + ibuf->encodedbuffer = NULL; ibuf->encodedsize = 0; ibuf->userflags &= ~IB_BITMAPDIRTY; @@ -859,6 +1002,28 @@ void BKE_image_memorypack(Image *ima) IMB_freeImBuf(ibuf); } +void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath) +{ + const size_t totfiles = image_num_files(ima); + + if (totfiles == 1) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file"); + BLI_addtail(&ima->packedfiles, imapf); + imapf->packedfile = newPackedFile(reports, ima->name, basepath); + BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath)); + } + else { + ImageView *iv; + for (iv = ima->views.first; iv; iv = iv->next) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file"); + BLI_addtail(&ima->packedfiles, imapf); + + imapf->packedfile = newPackedFile(reports, iv->filepath, basepath); + BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath)); + } + } +} + void BKE_image_tag_time(Image *ima) { ima->lastused = PIL_check_seconds_timer_i(); @@ -1259,7 +1424,7 @@ char BKE_imtype_from_arg(const char *imtype_arg) else return R_IMF_IMTYPE_INVALID; } -static bool image_path_ensure_ext(char *string, const char imtype, const ImageFormatData *im_format) +static bool do_add_image_extension(char *string, const char imtype, const ImageFormatData *im_format) { const char *extension = NULL; const char *extension_test; @@ -1311,7 +1476,7 @@ static bool image_path_ensure_ext(char *string, const char imtype, const ImageFo } #endif #ifdef WITH_OPENEXR - else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + else if (imtype == R_IMF_IMTYPE_OPENEXR || imtype == R_IMF_IMTYPE_MULTILAYER) { if (!BLI_testextensie(string, extension_test = ".exr")) extension = extension_test; } @@ -1369,14 +1534,14 @@ static bool image_path_ensure_ext(char *string, const char imtype, const ImageFo } } -bool BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) +int BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) { - return image_path_ensure_ext(string, im_format->imtype, im_format); + return do_add_image_extension(string, im_format->imtype, im_format); } -bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) +int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) { - return image_path_ensure_ext(string, imtype, NULL); + return do_add_image_extension(string, imtype, NULL); } void BKE_imformat_defaults(ImageFormatData *im_format) @@ -1908,14 +2073,12 @@ bool BKE_imbuf_alpha_test(ImBuf *ibuf) /* note: imf->planes is ignored here, its assumed the image channels * are already set */ -int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) +void BKE_imbuf_write_prepare(ImBuf *ibuf, ImageFormatData *imf) { char imtype = imf->imtype; char compress = imf->compress; char quality = imf->quality; - int ok; - if (imtype == R_IMF_IMTYPE_IRIS) { ibuf->ftype = IMAGIC; } @@ -1952,7 +2115,7 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) } #endif #ifdef WITH_OPENEXR - else if (imtype == R_IMF_IMTYPE_OPENEXR || imtype == R_IMF_IMTYPE_MULTILAYER) { + else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { ibuf->ftype = OPENEXR; if (imf->depth == R_IMF_CHAN_DEPTH_16) ibuf->ftype |= OPENEXR_HALF; @@ -2036,6 +2199,13 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) if (quality < 10) quality = 90; ibuf->ftype = JPG | quality; } +} + +int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) +{ + int ok; + + BKE_imbuf_write_prepare(ibuf, imf); BLI_make_existing_file(name); @@ -2079,9 +2249,10 @@ int BKE_imbuf_write_stamp(Scene *scene, struct Object *camera, ImBuf *ibuf, cons } -static void image_path_makepicstring( +static void do_makepicstring( char *string, const char *base, const char *relbase, int frame, const char imtype, - const ImageFormatData *im_format, const short use_ext, const short use_frames) + const ImageFormatData *im_format, const short use_ext, const short use_frames, + const char *suffix) { if (string == NULL) return; BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ @@ -2090,22 +2261,25 @@ static void image_path_makepicstring( if (use_frames) BLI_path_frame(string, frame, 4); + if (suffix) + BLI_path_suffix(string, FILE_MAX, suffix, ""); + if (use_ext) - image_path_ensure_ext(string, imtype, im_format); + do_add_image_extension(string, imtype, im_format); } void BKE_image_path_from_imformat( char *string, const char *base, const char *relbase, int frame, - const ImageFormatData *im_format, const bool use_ext, const bool use_frames) + const ImageFormatData *im_format, const bool use_ext, const bool use_frames, const char *suffix) { - image_path_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames); + do_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames, suffix); } void BKE_image_path_from_imtype( char *string, const char *base, const char *relbase, int frame, - const char imtype, const bool use_ext, const bool use_frames) + const char imtype, const bool use_ext, const bool use_frames, const char *view) { - image_path_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames); + do_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames, view); } struct anim *openanim_noload(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]) @@ -2181,6 +2355,59 @@ Image *BKE_image_verify_viewer(int type, const char *name) return ima; } +static void image_viewer_create_views(const RenderData *rd, Image *ima) +{ + SceneRenderView *srv; + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + image_add_view(ima, srv->name, ""); + } +} + +/* Reset the image cache and views when the Viewer Nodes views don't match the scene views */ +void BKE_image_verify_viewer_views(const RenderData *rd, Image *ima, ImageUser *iuser) +{ + bool do_reset; + + BLI_lock_thread(LOCK_DRAW_IMAGE); + + if (BKE_scene_multiview_is_stereo3d(rd)) { + ima->flag |= IMA_IS_STEREO; + ima->flag |= IMA_IS_MULTIVIEW; + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + iuser->flag &= ~IMA_SHOW_STEREO; + } + + /* see if all scene render views are in the image view list */ + do_reset = (BKE_scene_multiview_num_views_get(rd) != BLI_listbase_count(&ima->views)); + if (!do_reset) { + SceneRenderView *srv; + ImageView *iv; + + for (iv = ima->views.first; iv; iv = iv->next) { + srv = BLI_findstring(&rd->views, iv->name, offsetof(SceneRenderView, name)); + if ((srv == NULL) || (BKE_scene_multiview_is_render_view_active(rd, srv) == false)) { + do_reset = true; + break; + } + } + } + + if (do_reset) { + image_free_cached_frames(ima); + BKE_image_free_views(ima); + + /* add new views */ + image_viewer_create_views(rd, ima); + } + + BLI_unlock_thread(LOCK_DRAW_IMAGE); +} + void BKE_image_walk_all_users(const Main *mainp, void *customdata, void callback(Image *ima, ImageUser *iuser, void *customdata)) { @@ -2238,6 +2465,33 @@ static void image_tag_frame_recalc(Image *ima, ImageUser *iuser, void *customdat } } +static void image_init_imageuser(Image *ima, ImageUser *iuser) +{ + RenderResult *rr = ima->rr; + + iuser->multi_index = 0; + iuser->layer = iuser->pass = iuser->view = 0; + iuser->passtype = SCE_PASS_COMBINED; + + if (rr) { + RenderLayer *rl = rr->layers.first; + + if (rl) { + RenderPass *rp = rl->passes.first; + + if (rp) + iuser->passtype = rp->passtype; + } + + BKE_image_multilayer_index(rr, iuser); + } +} + +void BKE_image_init_imageuser(Image *ima, ImageUser *iuser) +{ + return image_init_imageuser(ima, iuser); +} + void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) { if (ima == NULL) @@ -2248,8 +2502,13 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) switch (signal) { case IMA_SIGNAL_FREE: BKE_image_free_buffers(ima); - if (iuser) + + if (iuser) { iuser->ok = 1; + if (iuser->scene) { + image_update_views_format(ima, iuser); + } + } break; case IMA_SIGNAL_SRC_CHANGE: if (ima->type == IMA_TYPE_UV_TEST) @@ -2301,23 +2560,41 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) case IMA_SIGNAL_RELOAD: /* try to repack file */ - if (ima->packedfile) { - PackedFile *pf; - pf = newPackedFile(NULL, ima->name, ID_BLEND_PATH(G.main, &ima->id)); - if (pf) { - freePackedFile(ima->packedfile); - ima->packedfile = pf; - BKE_image_free_buffers(ima); + if (BKE_image_has_packedfile(ima)) { + const size_t totfiles = image_num_files(ima); + + if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) { + /* in case there are new available files to be loaded */ + image_free_packedfiles(ima); + BKE_image_packfiles(NULL, ima, ID_BLEND_PATH(G.main, &ima->id)); } else { - printf("ERROR: Image not available. Keeping packed image\n"); + ImagePackedFile *imapf; + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + PackedFile *pf; + pf = newPackedFile(NULL, imapf->filepath, ID_BLEND_PATH(G.main, &ima->id)); + if (pf) { + freePackedFile(imapf->packedfile); + imapf->packedfile = pf; + } + else { + printf("ERROR: Image \"%s\" not available. Keeping packed image\n", imapf->filepath); + } + } } + + if (BKE_image_has_packedfile(ima)) + BKE_image_free_buffers(ima); } else BKE_image_free_buffers(ima); - if (iuser) + if (iuser) { iuser->ok = 1; + if (iuser->scene) { + image_update_views_format(ima, iuser); + } + } break; case IMA_SIGNAL_USER_NEW_IMAGE: @@ -2325,8 +2602,7 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) iuser->ok = 1; if (ima->source == IMA_SRC_FILE || ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_MULTILAYER) { - iuser->multi_index = 0; - iuser->layer = iuser->pass = 0; + image_init_imageuser(ima, iuser); } } } @@ -2368,21 +2644,34 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) return NULL; if (iuser) { - short index = 0, rl_index = 0, rp_index; + short index = 0, rv_index, rl_index = 0, rp_index; + bool is_stereo = (iuser->flag & IMA_SHOW_STEREO) && RE_RenderResult_is_stereo(rr); + + rv_index = is_stereo ? iuser->multiview_eye : iuser->view; for (rl = rr->layers.first; rl; rl = rl->next, rl_index++) { rp_index = 0; - for (rpass = rl->passes.first; rpass; rpass = rpass->next, index++, rp_index++) - if (iuser->layer == rl_index && iuser->pass == rp_index) + + for (rpass = rl->passes.first; rpass; rpass = rpass->next, index++, rp_index++) { + if (iuser->layer == rl_index && + iuser->passtype == rpass->passtype && + rv_index == rpass->view_id) + { break; + } + } if (rpass) break; } - if (rpass) + if (rpass) { iuser->multi_index = index; - else + iuser->pass = rp_index; + } + else { iuser->multi_index = 0; + iuser->pass = 0; + } } if (rpass == NULL) { rl = rr->layers.first; @@ -2393,19 +2682,80 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) return rpass; } +void BKE_image_multiview_index(Image *ima, ImageUser *iuser) +{ + if (iuser) { + bool is_stereo = (ima->flag & IMA_IS_STEREO) && (iuser->flag & IMA_SHOW_STEREO); + if (is_stereo) { + iuser->multi_index = iuser->multiview_eye; + } + else { + if ((iuser->view < 0) || (iuser->view >= BLI_listbase_count_ex(&ima->views, iuser->view + 1))) { + iuser->multi_index = iuser->view = 0; + } + else { + iuser->multi_index = iuser->view; + } + } + } +} + +/* if layer or pass changes, we need an index for the imbufs list */ +/* note it is called for rendered results, but it doesnt use the index! */ +/* and because rendered results use fake layer/passes, don't correct for wrong indices here */ +bool BKE_image_is_multilayer(Image *ima) +{ + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) { + if (ima->type == IMA_TYPE_MULTILAYER) { + return true; + } + } + else if (ima->source == IMA_SRC_VIEWER) { + if (ima->type == IMA_TYPE_R_RESULT) { + return true; + } + } + return false; +} + +static void image_init_multilayer_multiview_flag(Image *ima, RenderResult *rr) +{ + if (rr) { + if (RE_RenderResult_is_stereo(rr)) { + ima->flag |= IMA_IS_STEREO; + ima->flag |= IMA_IS_MULTIVIEW; + } + else { + ima->flag &= ~IMA_IS_STEREO; + if (BLI_listbase_count_ex(&rr->views, 2) > 1) + ima->flag |= IMA_IS_MULTIVIEW; + else + ima->flag &= ~IMA_IS_MULTIVIEW; + } + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + } +} + RenderResult *BKE_image_acquire_renderresult(Scene *scene, Image *ima) { + RenderResult *rr = NULL; if (ima->rr) { - return ima->rr; + rr = ima->rr; } else if (ima->type == IMA_TYPE_R_RESULT) { if (ima->render_slot == ima->last_render_slot) - return RE_AcquireResultRead(RE_GetRender(scene->id.name)); + rr = RE_AcquireResultRead(RE_GetRender(scene->id.name)); else - return ima->renders[ima->render_slot]; + rr = ima->renders[ima->render_slot]; + + /* set proper multiview flag */ + image_init_multilayer_multiview_flag(ima, rr); } - else - return NULL; + + return rr; } void BKE_image_release_renderresult(Scene *scene, Image *ima) @@ -2419,6 +2769,18 @@ void BKE_image_release_renderresult(Scene *scene, Image *ima) } } +bool BKE_image_is_openexr(struct Image *ima) +{ +#ifdef WITH_OPENEXR + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) { + return BLI_testextensie(ima->name, ".exr"); + } +#else + UNUSED_VARS(ima); +#endif + return false; +} + void BKE_image_backup_render(Scene *scene, Image *ima) { /* called right before rendering, ima->renders contains render @@ -2439,8 +2801,155 @@ void BKE_image_backup_render(Scene *scene, Image *ima) ima->last_render_slot = slot; } +/**************************** multiview save openexr *********************************/ +#ifdef WITH_OPENEXR +static const char *image_get_view_cb(void *base, const size_t view_id) +{ + Image *ima = base; + ImageView *iv = BLI_findlink(&ima->views, view_id); + return iv ? iv->name : ""; +} +#endif /* WITH_OPENEXR */ + +#ifdef WITH_OPENEXR +static ImBuf *image_get_buffer_cb(void *base, const size_t view_id) +{ + Image *ima = base; + ImageUser iuser = {0}; + + iuser.view = view_id; + iuser.ok = 1; + + BKE_image_multiview_index(ima, &iuser); + + return image_acquire_ibuf(ima, &iuser, NULL); +} +#endif /* WITH_OPENEXR */ + +bool BKE_image_save_openexr_multiview(Image *ima, ImBuf *ibuf, const char *filepath, const int flags) +{ +#ifdef WITH_OPENEXR + char name[FILE_MAX]; + bool ok; + + BLI_strncpy(name, filepath, sizeof(name)); + BLI_path_abs(name, G.main->name); + + ibuf->userdata = ima; + ok = IMB_exr_multiview_save(ibuf, name, flags, BLI_listbase_count(&ima->views), image_get_view_cb, image_get_buffer_cb); + ibuf->userdata = NULL; + + return ok; +#else + UNUSED_VARS(ima, ibuf, filepath, flags); + return false; +#endif +} + +/**************************** multiview load openexr *********************************/ + +static void image_add_view(Image *ima, const char *viewname, const char *filepath) +{ + ImageView *iv; + + iv = MEM_mallocN(sizeof(ImageView), "Viewer Image View"); + BLI_strncpy(iv->name, viewname, sizeof(iv->name)); + BLI_strncpy(iv->filepath, filepath, sizeof(iv->filepath)); + + /* For stereo drawing we need to ensure: + * STEREO_LEFT_NAME == STEREO_LEFT_ID and + * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */ + + if (STREQ(viewname, STEREO_LEFT_NAME)) { + BLI_addhead(&ima->views, iv); + } + else if (STREQ(viewname, STEREO_RIGHT_NAME)) { + ImageView *left_iv = BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)); + + if (left_iv == NULL) { + BLI_addhead(&ima->views, iv); + } + else { + BLI_insertlinkafter(&ima->views, left_iv, iv); + } + } + else { + BLI_addtail(&ima->views, iv); + } +} + +#ifdef WITH_OPENEXR +static void image_add_view_cb(void *base, const char *str) +{ + Image *ima = base; + image_add_view(ima, str, ima->name); +} + +static void image_add_buffer_cb(void *base, const char *str, ImBuf *ibuf, const int frame) +{ + Image *ima = base; + size_t id; + bool predivide = (ima->alpha_mode == IMA_ALPHA_PREMUL); + const char *colorspace = ima->colorspace_settings.name; + const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + + if (ibuf == NULL) + return; + + id = BLI_findstringindex(&ima->views, str, offsetof(ImageView, name)); + + if (id == -1) + return; + + if (ibuf->channels >= 3) + IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, + colorspace, to_colorspace, predivide); + + image_assign_ibuf(ima, ibuf, id, frame); + IMB_freeImBuf(ibuf); +} +#endif /* WITH_OPENEXR */ + +#ifdef WITH_OPENEXR +static void image_update_multiview_flags(Image *ima) +{ + if (BLI_listbase_count_ex(&ima->views, 2) > 1) { + ima->flag |= IMA_IS_MULTIVIEW; + + if (BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)) && + BLI_findstring(&ima->views, STEREO_RIGHT_NAME, offsetof(ImageView, name))) + { + ima->flag |= IMA_IS_STEREO; + } + else { + ima->flag &= ~IMA_IS_STEREO; + } + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + } +} +#endif /* WITH_OPENEXR */ + /* after imbuf load, openexr type can return with a exrhandle open */ /* in that case we have to build a render-result */ +#ifdef WITH_OPENEXR +static void image_create_multiview(Image *ima, ImBuf *ibuf, const int frame) +{ + image_free_views(ima); + + IMB_exr_multiview_convert(ibuf->userdata, ima, image_add_view_cb, image_add_buffer_cb, frame); + + image_update_multiview_flags(ima); + + IMB_exr_close(ibuf->userdata); +} +#endif /* WITH_OPENEXR */ + +/* after imbuf load, openexr type can return with a exrhandle open */ +/* in that case we have to build a render-result */ +#ifdef WITH_OPENEXR static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) { const char *colorspace = ima->colorspace_settings.name; @@ -2448,14 +2957,16 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) ima->rr = RE_MultilayerConvert(ibuf->userdata, colorspace, predivide, ibuf->x, ibuf->y); -#ifdef WITH_OPENEXR IMB_exr_close(ibuf->userdata); -#endif ibuf->userdata = NULL; if (ima->rr) ima->rr->framenr = framenr; + + /* set proper multiview flag */ + image_init_multilayer_multiview_flag(ima, ima->rr); } +#endif /* WITH_OPENEXR */ /* common stuff to do with images after loading */ static void image_initialize_after_load(Image *ima, ImBuf *ibuf) @@ -2488,18 +2999,41 @@ static int imbuf_alpha_flags_for_image(Image *ima) return flag; } -static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) +/* the number of files will vary according to the stereo format */ +static size_t image_num_files(Image *ima) +{ + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + + if (!is_multiview) { + return 1; + } + else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + /* R_IMF_VIEWS_INDIVIDUAL */ + else { + return BLI_listbase_count(&ima->views); + } +} + +static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, const size_t view_id, bool *r_assign) { struct ImBuf *ibuf; char name[FILE_MAX]; int flag; + ImageUser iuser_t; /* XXX temp stuff? */ if (ima->lastframe != frame) ima->tpageflag |= IMA_TPAGE_REFRESH; ima->lastframe = frame; - BKE_image_user_file_path(iuser, ima, name); + + if (iuser) + iuser_t = *iuser; + + iuser_t.view = view_id; + BKE_image_user_file_path(&iuser_t, ima, name); flag = IB_rect | IB_multilayer; flag |= imbuf_alpha_flags_for_image(ima); @@ -2520,25 +3054,78 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) #ifdef WITH_OPENEXR /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ if (ibuf->ftype == OPENEXR && ibuf->userdata) { - image_create_multilayer(ima, ibuf, frame); - ima->type = IMA_TYPE_MULTILAYER; - IMB_freeImBuf(ibuf); - ibuf = NULL; + /* handle singlelayer multiview case assign ibuf based on available views */ + if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) { + image_create_multiview(ima, ibuf, frame); + IMB_freeImBuf(ibuf); + ibuf = NULL; + } + else if (IMB_exr_has_multilayer(ibuf->userdata)) { + /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ + image_create_multilayer(ima, ibuf, frame); + ima->type = IMA_TYPE_MULTILAYER; + IMB_freeImBuf(ibuf); + ibuf = NULL; + } } else { image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); + *r_assign = true; } #else image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); + *r_assign = true; #endif } - else - ima->ok = 0; - if (iuser) - iuser->ok = ima->ok; + return ibuf; +} + +static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) +{ + struct ImBuf *ibuf = NULL; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + bool assign = false; + + if (!is_multiview) { + ibuf = load_sequence_single(ima, iuser, frame, 0, &assign); + if (assign) { + image_assign_ibuf(ima, ibuf, 0, frame); + } + } + else { + size_t i; + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) + ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i, &assign); + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; + + if (assign) { + for (i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, frame); + } + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } return ibuf; } @@ -2595,46 +3182,52 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f return ibuf; } - -static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) +static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const size_t view_id) { struct ImBuf *ibuf = NULL; + ImageAnim *ia; - ima->lastframe = frame; + ia = BLI_findlink(&ima->anims, view_id); - if (ima->anim == NULL) { + if (ia->anim == NULL) { char str[FILE_MAX]; int flags = IB_rect; + ImageUser iuser_t; + if (ima->flag & IMA_DEINTERLACE) { flags |= IB_animdeinterlace; } - BKE_image_user_file_path(iuser, ima, str); + if (iuser) + iuser_t = *iuser; + + iuser_t.view = view_id; + + BKE_image_user_file_path(&iuser_t, ima, str); /* FIXME: make several stream accessible in image editor, too*/ - ima->anim = openanim(str, IB_rect, 0, ima->colorspace_settings.name); + ia->anim = openanim(str, flags, 0, ima->colorspace_settings.name); /* let's initialize this user */ - if (ima->anim && iuser && iuser->frames == 0) - iuser->frames = IMB_anim_get_duration(ima->anim, + if (ia->anim && iuser && iuser->frames == 0) + iuser->frames = IMB_anim_get_duration(ia->anim, IMB_TC_RECORD_RUN); } - if (ima->anim) { - int dur = IMB_anim_get_duration(ima->anim, + if (ia->anim) { + int dur = IMB_anim_get_duration(ia->anim, IMB_TC_RECORD_RUN); int fra = frame - 1; if (fra < 0) fra = 0; if (fra > (dur - 1)) fra = dur - 1; ibuf = IMB_makeSingleUser( - IMB_anim_absolute(ima->anim, fra, + IMB_anim_absolute(ia->anim, fra, IMB_TC_RECORD_RUN, IMB_PROXY_NONE)); if (ibuf) { image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); } else ima->ok = 0; @@ -2642,67 +3235,223 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) else ima->ok = 0; + return ibuf; +} + +static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) +{ + struct ImBuf *ibuf = NULL; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + size_t i; + + if (totfiles != BLI_listbase_count_ex(&ima->anims, totfiles + 1)) { + image_free_anims(ima); + + for (i = 0; i < totfiles; i++) { + /* allocate the ImageAnim */ + ImageAnim *ia = MEM_callocN(sizeof(ImageAnim), "Image Anim"); + BLI_addtail(&ima->anims, ia); + } + } + + if (!is_multiview) { + ibuf = load_movie_single(ima, iuser, frame, 0); + image_assign_ibuf(ima, ibuf, 0, frame); + } + else { + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views (movie) Imbufs"); + + for (i = 0; i < totfiles; i++) { + ibuf_arr[i] = load_movie_single(ima, iuser, frame, i); + } + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i]) { + image_assign_ibuf(ima, ibuf_arr[i], i, frame); + } + else { + ima->ok = 0; + } + } + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } + if (iuser) iuser->ok = ima->ok; return ibuf; } -/* warning, 'iuser' can be NULL */ -static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) +static ImBuf *load_image_single( + Image *ima, ImageUser *iuser, int cfra, + const size_t view_id, + const bool has_packed, + bool *r_assign) { - struct ImBuf *ibuf; - char str[FILE_MAX]; - int assign = 0, flag; - - /* always ensure clean ima */ - BKE_image_free_buffers(ima); + char filepath[FILE_MAX]; + struct ImBuf *ibuf = NULL; + int flag; /* is there a PackedFile with this image ? */ - if (ima->packedfile) { + if (has_packed) { + ImagePackedFile *imapf; + flag = IB_rect | IB_multilayer; flag |= imbuf_alpha_flags_for_image(ima); - ibuf = IMB_ibImageFromMemory((unsigned char *)ima->packedfile->data, ima->packedfile->size, flag, - ima->colorspace_settings.name, "<packed data>"); + imapf = BLI_findlink(&ima->packedfiles, view_id); + ibuf = IMB_ibImageFromMemory( + (unsigned char *)imapf->packedfile->data, imapf->packedfile->size, flag, + ima->colorspace_settings.name, "<packed data>"); } else { + ImageUser iuser_t; + flag = IB_rect | IB_multilayer | IB_metadata; flag |= imbuf_alpha_flags_for_image(ima); - /* get the right string */ + /* get the correct filepath */ BKE_image_user_frame_calc(iuser, cfra, 0); - BKE_image_user_file_path(iuser, ima, str); + + if (iuser) + iuser_t = *iuser; + else + iuser_t.framenr = ima->lastframe; + + iuser_t.view = view_id; + + BKE_image_user_file_path(&iuser_t, ima, filepath); /* read ibuf */ - ibuf = IMB_loadiffname(str, flag, ima->colorspace_settings.name); + ibuf = IMB_loadiffname(filepath, flag, ima->colorspace_settings.name); } if (ibuf) { - /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ +#ifdef WITH_OPENEXR if (ibuf->ftype == OPENEXR && ibuf->userdata) { - image_create_multilayer(ima, ibuf, cfra); - ima->type = IMA_TYPE_MULTILAYER; - IMB_freeImBuf(ibuf); - ibuf = NULL; + if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) { + /* handle singlelayer multiview case assign ibuf based on available views */ + image_create_multiview(ima, ibuf, cfra); + IMB_freeImBuf(ibuf); + ibuf = NULL; + } + else if (IMB_exr_has_multilayer(ibuf->userdata)) { + /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ + image_create_multilayer(ima, ibuf, cfra); + ima->type = IMA_TYPE_MULTILAYER; + IMB_freeImBuf(ibuf); + ibuf = NULL; + } } else { image_initialize_after_load(ima, ibuf); - assign = 1; + *r_assign = true; /* check if the image is a font image... */ detectBitmapFont(ibuf); /* make packed file for autopack */ - if ((ima->packedfile == NULL) && (G.fileflags & G_AUTOPACK)) - ima->packedfile = newPackedFile(NULL, str, ID_BLEND_PATH(G.main, &ima->id)); + if ((has_packed == false) && (G.fileflags & G_AUTOPACK)) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packefile"); + BLI_addtail(&ima->packedfiles, imapf); + + BLI_strncpy(imapf->filepath, filepath, sizeof(imapf->filepath)); + imapf->packedfile = newPackedFile(NULL, filepath, ID_BLEND_PATH(G.main, &ima->id)); + } } +#else + image_initialize_after_load(ima, ibuf); + *r_assign = true; +#endif } - else + else { ima->ok = 0; + } - if (assign) - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + return ibuf; +} + +/* warning, 'iuser' can be NULL + * note: Image->views was already populated (in image_update_views_format) + */ +static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) +{ + struct ImBuf *ibuf = NULL; + bool assign = false; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + bool has_packed = BKE_image_has_packedfile(ima); + + /* always ensure clean ima */ + BKE_image_free_buffers(ima); + + /* this should never happen, but just playing safe */ + if (has_packed) { + if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) { + image_free_packedfiles(ima); + has_packed = false; + } + } + + if (!is_multiview) { + ibuf = load_image_single(ima, iuser, cfra, 0, has_packed, &assign); + if (assign) { + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + } + } + else { + size_t i; + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + BLI_assert(totviews > 0); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) + ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed, &assign); + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + /* return the original requested ImBuf */ + i = iuser && iuser->multi_index < totviews ? iuser->multi_index : 0; + ibuf = ibuf_arr[i]; + + if (assign) { + for (i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, 0); + } + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } if (iuser) iuser->ok = ima->ok; @@ -2756,9 +3505,10 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ float *rectf, *rectz; unsigned int *rect; float dither; - int channels, layer, pass; + int channels, layer, passtype; ImBuf *ibuf; int from_render = (ima->render_slot == ima->last_render_slot); + int actview; bool byte_buffer_in_display_space = false; if (!(iuser && iuser->scene)) @@ -2772,14 +3522,18 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ channels = 4; layer = iuser->layer; - pass = iuser->pass; + passtype = iuser->passtype; + actview = iuser->view; + + if ((ima->flag & IMA_IS_STEREO) && (iuser->flag & IMA_SHOW_STEREO)) + actview = iuser->multiview_eye; if (from_render) { - RE_AcquireResultImage(re, &rres); + RE_AcquireResultImage(re, &rres, actview); } else if (ima->renders[ima->render_slot]) { rres = *(ima->renders[ima->render_slot]); - rres.have_combined = rres.rectf != NULL; + rres.have_combined = RE_RenderViewGetRectf(&rres, actview) != NULL; } else memset(&rres, 0, sizeof(RenderResult)); @@ -2819,24 +3573,18 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ if (rl) { RenderPass *rpass; - /* there's no combined pass, is in renderlayer itself */ - if (pass == 0) { - rectf = rl->rectf; - if (rectf == NULL) { - /* Happens when Save Buffers is enabled. - * Use display buffer stored in the render layer. - */ - rect = (unsigned int *) rl->display_buffer; - byte_buffer_in_display_space = true; + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + if (passtype == rpass->passtype && + actview == rpass->view_id) + { + break; } } - else { - rpass = BLI_findlink(&rl->passes, pass - 1); - if (rpass) { - channels = rpass->channels; - rectf = rpass->rect; - dither = 0.0f; /* don't dither passes */ - } + + if (rpass) { + channels = rpass->channels; + rectf = rpass->rect; + dither = 0.0f; /* don't dither passes */ } for (rpass = rl->passes.first; rpass; rpass = rpass->next) @@ -2925,9 +3673,31 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ return ibuf; } +static size_t image_get_multiview_index(Image *ima, ImageUser *iuser) +{ + const bool is_multilayer = BKE_image_is_multilayer(ima); + const bool is_backdrop = (ima->source == IMA_SRC_VIEWER) && (ima->type == IMA_TYPE_COMPOSITE) && (iuser == NULL); + + if (is_multilayer) { + return iuser ? iuser->multi_index : IMA_NO_INDEX; + } + else if (is_backdrop) { + if ((ima->flag & IMA_IS_STEREO)) { + /* backdrop hackaround (since there is no iuser */ + return ima->eye; + } + } + else if ((ima->flag & IMA_IS_MULTIVIEW)) { + return iuser ? iuser->multi_index : 0; + } + + return IMA_NO_INDEX; +} + static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame, int *r_index) { int frame = 0, index = 0; + index = image_get_multiview_index(ima, iuser); /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { @@ -2939,7 +3709,6 @@ static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame } else if (ima->type == IMA_TYPE_MULTILAYER) { frame = iuser ? iuser->framenr : ima->lastframe; - index = iuser ? iuser->multi_index : IMA_NO_INDEX; } } @@ -2958,10 +3727,12 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, ImBuf *ibuf = NULL; int frame = 0, index = 0; + index = image_get_multiview_index(ima, iuser); + /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); /* XXX temp stuff? */ if (ima->lastframe != frame) ima->tpageflag |= IMA_TPAGE_REFRESH; @@ -2970,7 +3741,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); /* XXX temp stuff? */ if (ima->lastframe != frame) { @@ -2990,18 +3761,17 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, } else if (ima->type == IMA_TYPE_MULTILAYER) { frame = iuser ? iuser->framenr : ima->lastframe; - index = iuser ? iuser->multi_index : IMA_NO_INDEX; ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); } } else if (ima->source == IMA_SRC_FILE) { if (ima->type == IMA_TYPE_IMAGE) - ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); else if (ima->type == IMA_TYPE_MULTILAYER) - ibuf = image_get_cached_ibuf_for_index_frame(ima, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); } else if (ima->source == IMA_SRC_GENERATED) { - ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); } else if (ima->source == IMA_SRC_VIEWER) { /* always verify entirely, not that this shouldn't happen @@ -3085,7 +3855,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) if (ima->gen_depth == 0) ima->gen_depth = 24; ibuf = add_ibuf_size(ima->gen_x, ima->gen_y, ima->name, ima->gen_depth, (ima->gen_flag & IMA_GEN_FLOAT) != 0, ima->gen_type, ima->gen_color, &ima->colorspace_settings); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + image_assign_ibuf(ima, ibuf, index, 0); ima->ok = IMA_OK_LOADED; } else if (ima->source == IMA_SRC_VIEWER) { @@ -3103,13 +3873,13 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) /* XXX anim play for viewer nodes not yet supported */ frame = 0; // XXX iuser ? iuser->framenr : 0; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); if (!ibuf) { /* Composite Viewer, all handled in compositor */ /* fake ibuf, will be filled in compositor */ - ibuf = IMB_allocImBuf(256, 256, 32, IB_rect); - image_assign_ibuf(ima, ibuf, 0, frame); + ibuf = IMB_allocImBuf(256, 256, 32, IB_rect | IB_rectfloat); + image_assign_ibuf(ima, ibuf, index, frame); } } } @@ -3409,7 +4179,13 @@ void BKE_image_update_frame(const Main *bmain, int cfra) void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) { - BLI_strncpy(filepath, ima->name, FILE_MAX); + if ((ima->flag & IMA_IS_MULTIVIEW) && (ima->rr == NULL)) { + ImageView *iv = BLI_findlink(&ima->views, iuser->view); + BLI_strncpy(filepath, iv->filepath, FILE_MAX); + } + else { + BLI_strncpy(filepath, ima->name, FILE_MAX); + } if (ima->source == IMA_SRC_SEQUENCE) { char head[FILE_MAX], tail[FILE_MAX]; @@ -3545,6 +4321,16 @@ int BKE_image_sequence_guess_offset(Image *image) return atoi(num); } +bool BKE_image_has_anim(Image *ima) +{ + return (BLI_listbase_is_empty(&ima->anims) == false); +} + +bool BKE_image_has_packedfile(Image *ima) +{ + return (BLI_listbase_is_empty(&ima->packedfiles) == false); +} + /** * Checks the image buffer changes (not keyframed values) * @@ -3675,3 +4461,88 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) return ibuf; } + +static void image_update_views_format(Image *ima, ImageUser *iuser) +{ + SceneRenderView *srv; + ImageView *iv; + Scene *scene = iuser->scene; + const bool is_multiview = ((scene->r.scemode & R_MULTIVIEW) != 0) && + ((ima->flag & IMA_USE_VIEWS) != 0); + + /* reset the image views */ + BKE_image_free_views(ima); + + if (!is_multiview) { + goto monoview; + } + else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + size_t i; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + ima->flag |= IMA_IS_MULTIVIEW; + ima->flag |= IMA_IS_STEREO; + + for (i = 0; i < 2; i++) { + image_add_view(ima, names[i], ima->name); + } + return; + } + else { + /* R_IMF_VIEWS_INDIVIDUAL */ + char prefix[FILE_MAX] = {'\0'}; + char *name = ima->name; + char *ext = NULL; + + BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext); + + if (prefix[0] == '\0') { + goto monoview; + } + + /* create all the image views */ + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + char filepath[FILE_MAX]; + BLI_snprintf(filepath, sizeof(filepath), "%s%s%s", prefix, srv->suffix, ext); + image_add_view(ima, srv->name, filepath); + } + } + + /* check if the files are all available */ + iv = ima->views.last; + while (iv) { + int file; + char str[FILE_MAX]; + + BLI_strncpy(str, iv->filepath, sizeof(str)); + BLI_path_abs(str, G.main->name); + + /* exists? */ + file = BLI_open(str, O_BINARY | O_RDONLY, 0); + if (file == -1) { + ImageView *iv_del = iv; + iv = iv->prev; + BLI_remlink(&ima->views, iv_del); + MEM_freeN(iv_del); + } + else { + iv = iv->prev; + } + close(file); + } + + /* all good */ + if (BLI_listbase_count_ex(&ima->views, 2) > 1) { + ima->flag |= IMA_IS_MULTIVIEW; + if (BKE_scene_multiview_is_stereo3d(&scene->r)) + ima->flag |= IMA_IS_STEREO; + } + else { +monoview: + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + BKE_image_free_views(ima); + } + } +} diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 09ce9484a69..6d37f3ae006 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -1712,7 +1712,7 @@ void do_versions_ipos_to_animato(Main *main) /* check if object has any animation data */ if (ob->nlastrips.first) { /* Add AnimData block */ - BKE_id_add_animdata(id); + BKE_animdata_add_id(id); /* IPO first to take into any non-NLA'd Object Animation */ if (ob->ipo) { @@ -1735,7 +1735,7 @@ void do_versions_ipos_to_animato(Main *main) } else if ((ob->ipo) || (ob->action)) { /* Add AnimData block */ - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); /* Action first - so that Action name get conserved */ if (ob->action) { @@ -1776,7 +1776,7 @@ void do_versions_ipos_to_animato(Main *main) /* check PoseChannels for constraints with local data */ if (ob->pose) { /* Verify if there's AnimData block */ - BKE_id_add_animdata(id); + BKE_animdata_add_id(id); for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { for (con = pchan->constraints.first; con; con = con->next) { @@ -1802,7 +1802,7 @@ void do_versions_ipos_to_animato(Main *main) */ if (con->ipo) { /* Verify if there's AnimData block, just in case */ - BKE_id_add_animdata(id); + BKE_animdata_add_id(id); /* although this was the constraint's local IPO, we still need to provide con * so that drivers can be added properly... @@ -1819,7 +1819,7 @@ void do_versions_ipos_to_animato(Main *main) /* check constraint channels - we need to remove them anyway... */ if (ob->constraintChannels.first) { /* Verify if there's AnimData block */ - BKE_id_add_animdata(id); + BKE_animdata_add_id(id); for (conchan = ob->constraintChannels.first; conchan; conchan = conchann) { /* get pointer to next Constraint Channel */ @@ -1857,7 +1857,7 @@ void do_versions_ipos_to_animato(Main *main) */ if (key->ipo) { /* Add AnimData block */ - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); /* Convert Shapekey data... */ ipo_to_animdata(id, key->ipo, NULL, NULL, NULL); @@ -1879,7 +1879,7 @@ void do_versions_ipos_to_animato(Main *main) /* we're only interested in the IPO */ if (ma->ipo) { /* Add AnimData block */ - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); /* Convert Material data... */ ipo_to_animdata(id, ma->ipo, NULL, NULL, NULL); @@ -1901,7 +1901,7 @@ void do_versions_ipos_to_animato(Main *main) /* we're only interested in the IPO */ if (wo->ipo) { /* Add AnimData block */ - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); /* Convert World data... */ ipo_to_animdata(id, wo->ipo, NULL, NULL, NULL); @@ -1921,7 +1921,7 @@ void do_versions_ipos_to_animato(Main *main) if (ed && ed->seqbasep) { Sequence *seq; - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); SEQ_BEGIN(ed, seq) { @@ -1977,7 +1977,7 @@ void do_versions_ipos_to_animato(Main *main) /* we're only interested in the IPO */ if (te->ipo) { /* Add AnimData block */ - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); /* Convert Texture data... */ ipo_to_animdata(id, te->ipo, NULL, NULL, NULL); @@ -1999,7 +1999,7 @@ void do_versions_ipos_to_animato(Main *main) /* we're only interested in the IPO */ if (ca->ipo) { /* Add AnimData block */ - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); /* Convert Camera data... */ ipo_to_animdata(id, ca->ipo, NULL, NULL, NULL); @@ -2021,7 +2021,7 @@ void do_versions_ipos_to_animato(Main *main) /* we're only interested in the IPO */ if (la->ipo) { /* Add AnimData block */ - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); /* Convert Lamp data... */ ipo_to_animdata(id, la->ipo, NULL, NULL, NULL); @@ -2043,7 +2043,7 @@ void do_versions_ipos_to_animato(Main *main) /* we're only interested in the IPO */ if (cu->ipo) { /* Add AnimData block */ - AnimData *adt = BKE_id_add_animdata(id); + AnimData *adt = BKE_animdata_add_id(id); /* Convert Curve data... */ ipo_to_animdata(id, cu->ipo, NULL, NULL, NULL); diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index f598be0298a..862c47b18a2 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -83,7 +83,7 @@ void BKE_key_free(Key *key) { KeyBlock *kb; - BKE_free_animdata((ID *)key); + BKE_animdata_free((ID *)key); while ((kb = BLI_pophead(&key->block))) { if (kb->data) diff --git a/source/blender/blenkernel/intern/lamp.c b/source/blender/blenkernel/intern/lamp.c index 93b9c22566d..96b0b95adf3 100644 --- a/source/blender/blenkernel/intern/lamp.c +++ b/source/blender/blenkernel/intern/lamp.c @@ -221,7 +221,7 @@ void BKE_lamp_free(Lamp *la) if (mtex) MEM_freeN(mtex); } - BKE_free_animdata((ID *)la); + BKE_animdata_free((ID *)la); curvemapping_free(la->curfalloff); diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 650c768cf9a..5001d7390f8 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -307,7 +307,7 @@ void BKE_lattice_free(Lattice *lt) /* free animation data */ if (lt->adt) { - BKE_free_animdata(<->id); + BKE_animdata_free(<->id); lt->adt = NULL; } } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index a77ab86029b..da0649d5313 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -439,7 +439,7 @@ bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop) if (RNA_property_editable(ptr, prop)) { if (id_copy(id, &newid, false) && newid) { /* copy animation actions too */ - BKE_copy_animdata_id_action(id); + BKE_animdata_copy_id_action(id); /* us is 1 by convention, but RNA_property_pointer_set * will also increment it, so set it to zero */ newid->us = 0; @@ -798,7 +798,7 @@ static void id_copy_animdata(ID *id, const bool do_action) if (adt) { IdAdtTemplate *iat = (IdAdtTemplate *)id; - iat->adt = BKE_copy_animdata(iat->adt, do_action); /* could be set to false, need to investigate */ + iat->adt = BKE_animdata_copy(iat->adt, do_action); /* could be set to false, need to investigate */ } } diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 28b7ceded67..40db411ef4c 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -135,7 +135,7 @@ void BKE_linestyle_free(FreestyleLineStyle *linestyle) MEM_freeN(linestyle->nodetree); } - BKE_free_animdata(&linestyle->id); + BKE_animdata_free(&linestyle->id); while ((m = (LineStyleModifier *)linestyle->color_modifiers.first)) BKE_linestyle_color_modifier_remove(linestyle, m); while ((m = (LineStyleModifier *)linestyle->alpha_modifiers.first)) diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 9b682f101ad..b5b7f3d06aa 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -99,7 +99,7 @@ void BKE_material_free_ex(Material *ma, bool do_id_user) if (ma->ramp_col) MEM_freeN(ma->ramp_col); if (ma->ramp_spec) MEM_freeN(ma->ramp_spec); - BKE_free_animdata((ID *)ma); + BKE_animdata_free((ID *)ma); if (ma->preview) BKE_previewimg_free(&ma->preview); diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 25031183545..8728e6f98d5 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -83,7 +83,7 @@ void BKE_mball_free(MetaBall *mb) BKE_mball_unlink(mb); if (mb->adt) { - BKE_free_animdata((ID *)mb); + BKE_animdata_free((ID *)mb); mb->adt = NULL; } if (mb->mat) MEM_freeN(mb->mat); diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 04024943310..080a8cead7b 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -44,26 +44,21 @@ #include "BLI_path_util.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_memarena.h" #include "BKE_global.h" #include "BKE_depsgraph.h" #include "BKE_scene.h" #include "BKE_displist.h" -#include "BKE_mball.h" #include "BKE_mball_tessellate.h" /* own include */ -/* Data types */ +#include "BLI_strict_flags.h" -typedef struct vertex { /* surface vertex */ - float co[3]; /* position and surface normal */ - float no[3]; -} VERTEX; +/* experimental (faster) normal calculation */ +// #define USE_ACCUM_NORMAL -typedef struct vertices { /* list of vertices in polygonization */ - int count, max; /* # vertices, max # allowed */ - VERTEX *ptr; /* dynamically allocated */ -} VERTICES; +/* Data types */ typedef struct corner { /* corner of a cube */ int i, j, k; /* (i, j, k) is index within lattice */ @@ -102,74 +97,161 @@ typedef struct intlists { /* list of list of integers */ struct intlists *next; /* remaining elements */ } INTLISTS; -/* dividing scene using octal tree makes polygonisation faster */ -typedef struct ml_pointer { - struct ml_pointer *next, *prev; - struct MetaElem *ml; -} ml_pointer; - -typedef struct octal_node { - struct octal_node *nodes[8];/* children of current node */ - struct octal_node *parent; /* parent of current node */ - struct ListBase elems; /* ListBase of MetaElem pointers (ml_pointer) */ - float x_min, y_min, z_min; /* 1st border point */ - float x_max, y_max, z_max; /* 7th border point */ - float x, y, z; /* center of node */ - int pos, neg; /* number of positive and negative MetaElements in the node */ - int count; /* number of MetaElems, which belongs to the node */ -} octal_node; - -typedef struct octal_tree { - struct octal_node *first; /* first node */ - int pos, neg; /* number of positive and negative MetaElements in the scene */ - short depth; /* number of scene subdivision */ -} octal_tree; - -struct pgn_elements { - struct pgn_elements *next, *prev; - char *data; -}; +typedef struct Box { /* an AABB with pointer to metalelem */ + float min[3], max[3]; + const MetaElem *ml; +} Box; + +typedef struct MetaballBVHNode { /* BVH node */ + Box bb[2]; /* AABB of children */ + struct MetaballBVHNode *child[2]; +} MetaballBVHNode; + +typedef struct process { /* parameters, storage */ + float thresh, size; /* mball threshold, single cube size */ + float delta; /* small delta for calculating normals */ + unsigned int converge_res; /* converge procedure resolution (more = slower) */ + + MetaElem **mainb; /* array of all metaelems */ + unsigned int totelem, mem; /* number of metaelems */ + + MetaballBVHNode metaball_bvh; /* The simplest bvh */ + Box allbb; /* Bounding box of all metaelems */ -typedef struct process { /* parameters, function, storage */ - /* ** old G_mb contents ** */ - float thresh; - int totelem; - MetaElem **mainb; - octal_tree *metaball_tree; - - /* ** old process contents ** */ - - /* what happens here? floats, I think. */ - /* float (*function)(void); */ /* implicit surface function */ - float (*function)(struct process *, float, float, float); - float size, delta; /* cube size, normal delta */ - int bounds; /* cube range within lattice */ - CUBES *cubes; /* active cubes */ - VERTICES vertices; /* surface vertices */ + MetaballBVHNode **bvh_queue; /* Queue used during bvh traversal */ + unsigned int bvh_queue_size; + + CUBES *cubes; /* stack of cubes waiting for polygonization */ CENTERLIST **centers; /* cube center hash table */ CORNER **corners; /* corner value hash table */ EDGELIST **edges; /* edge and vertex id hash table */ - /* Runtime things */ - int *indices; - int totindex, curindex; + int (*indices)[4]; /* output indices */ + unsigned int totindex; /* size of memory allocated for indices */ + unsigned int curindex; /* number of currently added indices */ + + float (*co)[3], (*no)[3]; /* surface vertices - positions and normals */ + unsigned int totvertex; /* memory size */ + unsigned int curvertex; /* currently added vertices */ - int pgn_offset; - struct pgn_elements *pgn_current; - ListBase pgn_list; + /* memory allocation from common pool */ + MemArena *pgn_elements; } PROCESS; /* Forward declarations */ -static int vertid(PROCESS *process, const CORNER *c1, const CORNER *c2, MetaBall *mb); -static int setcenter(PROCESS *process, CENTERLIST *table[], const int i, const int j, const int k); -static CORNER *setcorner(PROCESS *process, int i, int j, int k); -static void converge(PROCESS *process, const float p1[3], const float p2[3], float v1, float v2, - float p[3], MetaBall *mb, int f); +static int vertid(PROCESS *process, const CORNER *c1, const CORNER *c2); +static void add_cube(PROCESS *process, int i, int j, int k); +static void make_face(PROCESS *process, int i1, int i2, int i3, int i4); +static void converge(PROCESS *process, const CORNER *c1, const CORNER *c2, float r_p[3]); + +/* ******************* SIMPLE BVH ********************* */ + +static void make_box_union(const BoundBox *a, const Box *b, Box *r_out) +{ + r_out->min[0] = min_ff(a->vec[0][0], b->min[0]); + r_out->min[1] = min_ff(a->vec[0][1], b->min[1]); + r_out->min[2] = min_ff(a->vec[0][2], b->min[2]); + + r_out->max[0] = max_ff(a->vec[6][0], b->max[0]); + r_out->max[1] = max_ff(a->vec[6][1], b->max[1]); + r_out->max[2] = max_ff(a->vec[6][2], b->max[2]); +} + +static void make_box_from_metaelem(Box *r, const MetaElem *ml) +{ + copy_v3_v3(r->max, ml->bb->vec[6]); + copy_v3_v3(r->min, ml->bb->vec[0]); + r->ml = ml; +} + +/** + * Partitions part of mainb array [start, end) along axis s. Returns i, + * where centroids of elements in the [start, i) segment lie "on the right side" of div, + * and elements in the [i, end) segment lie "on the left" + */ +static unsigned int partition_mainb(MetaElem **mainb, unsigned int start, unsigned int end, unsigned int s, float div) +{ + unsigned int i = start, j = end - 1; + div *= 2.0f; + + while (1) { + while (i < j && div > (mainb[i]->bb->vec[6][s] + mainb[i]->bb->vec[0][s])) i++; + while (j > i && div < (mainb[j]->bb->vec[6][s] + mainb[j]->bb->vec[0][s])) j--; + + if (i >= j) + break; + + SWAP(MetaElem *, mainb[i], mainb[j]); + i++; + j--; + } + + if (i == start) { + i++; + } + + return i; +} + +/** + * Recursively builds a BVH, dividing elements along the middle of the longest axis of allbox. + */ +static void build_bvh_spatial( + PROCESS *process, MetaballBVHNode *node, + unsigned int start, unsigned int end, const Box *allbox) +{ + unsigned int part, j, s; + float dim[3], div; + + /* Maximum bvh queue size is number of nodes which are made, equals calls to this function. */ + process->bvh_queue_size++; + dim[0] = allbox->max[0] - allbox->min[0]; + dim[1] = allbox->max[1] - allbox->min[1]; + dim[2] = allbox->max[2] - allbox->min[2]; + + s = 0; + if (dim[1] > dim[0] && dim[1] > dim[2]) s = 1; + else if (dim[2] > dim[1] && dim[2] > dim[0]) s = 2; + + div = allbox->min[s] + (dim[s] / 2.0f); + + part = partition_mainb(process->mainb, start, end, s, div); + + make_box_from_metaelem(&node->bb[0], process->mainb[start]); + node->child[0] = NULL; + + if (part > start + 1) { + for (j = start; j < part; j++) { + make_box_union(process->mainb[j]->bb, &node->bb[0], &node->bb[0]); + } + + node->child[0] = BLI_memarena_alloc(process->pgn_elements, sizeof(MetaballBVHNode)); + build_bvh_spatial(process, node->child[0], start, part, &node->bb[0]); + } + + node->child[1] = NULL; + if (part < end) { + make_box_from_metaelem(&node->bb[1], process->mainb[part]); + + if (part < end - 1) { + for (j = part; j < end; j++) { + make_box_union(process->mainb[j]->bb, &node->bb[1], &node->bb[1]); + } + + node->child[1] = BLI_memarena_alloc(process->pgn_elements, sizeof(MetaballBVHNode)); + build_bvh_spatial(process, node->child[1], part, end, &node->bb[1]); + } + } + else { + INIT_MINMAX(node->bb[1].min, node->bb[1].max); + } +} /* ******************** ARITH ************************* */ -/* BASED AT CODE (but mostly rewritten) : +/** + * BASED AT CODE (but mostly rewritten) : * C code from the article * "An Implicit Surface Polygonizer" * by Jules Bloomenthal, jbloom@beauty.gmu.edu @@ -178,9 +260,8 @@ static void converge(PROCESS *process, const float p1[3], const float p2[3], flo * Authored by Jules Bloomenthal, Xerox PARC. * Copyright (c) Xerox Corporation, 1991. All rights reserved. * Permission is granted to reproduce, use and distribute this code for - * any and all purposes, provided that this notice appears in all copies. */ - -#define RES 12 /* # converge iterations */ + * any and all purposes, provided that this notice appears in all copies. + */ #define L 0 /* left direction: -x, -i */ #define R 1 /* right direction: +x, +i */ @@ -197,8 +278,10 @@ static void converge(PROCESS *process, const float p1[3], const float p2[3], flo #define RTN 6 /* right top near corner */ #define RTF 7 /* right top far corner */ -/* the LBN corner of cube (i, j, k), corresponds with location - * (i-0.5)*size, (j-0.5)*size, (k-0.5)*size) */ +/** + * the LBN corner of cube (i, j, k), corresponds with location + * (i-0.5)*size, (j-0.5)*size, (k-0.5)*size) + */ #define HASHBIT (5) #define HASHSIZE (size_t)(1 << (3 * HASHBIT)) /*! < hash table size (32768) */ @@ -206,19 +289,19 @@ static void converge(PROCESS *process, const float p1[3], const float p2[3], flo #define HASH(i, j, k) ((((( (i) & 31) << 5) | ( (j) & 31)) << 5) | ( (k) & 31) ) #define MB_BIT(i, bit) (((i) >> (bit)) & 1) -#define FLIP(i, bit) ((i) ^ 1 << (bit)) /* flip the given bit of i */ +// #define FLIP(i, bit) ((i) ^ 1 << (bit)) /* flip the given bit of i */ +/* ******************** DENSITY COPMPUTATION ********************* */ -/* **************** POLYGONIZATION ************************ */ - -static void calc_mballco(MetaElem *ml, float vec[3]) -{ - if (ml->mat) { - mul_m4_v3((float (*)[4])ml->mat, vec); - } -} - -static float densfunc(MetaElem *ball, float x, float y, float z) +/** + * Computes density from given metaball at given position. + * Metaball equation is: ``(1 - r^2 / R^2)^3 * s`` + * + * r = distance from center + * R = metaball radius + * s - metaball stiffness + */ +static float densfunc(const MetaElem *ball, float x, float y, float z) { float dist2; float dvec[3] = {x, y, z}; @@ -229,37 +312,26 @@ static float densfunc(MetaElem *ball, float x, float y, float z) case MB_BALL: /* do nothing */ break; - case MB_TUBE: - if (dvec[0] > ball->expx) dvec[0] -= ball->expx; - else if (dvec[0] < -ball->expx) dvec[0] += ball->expx; - else dvec[0] = 0.0; - break; + case MB_CUBE: + if (dvec[2] > ball->expz) dvec[2] -= ball->expz; + else if (dvec[2] < -ball->expz) dvec[2] += ball->expz; + else dvec[2] = 0.0; + /* fall through */ case MB_PLANE: - if (dvec[0] > ball->expx) dvec[0] -= ball->expx; - else if (dvec[0] < -ball->expx) dvec[0] += ball->expx; - else dvec[0] = 0.0; if (dvec[1] > ball->expy) dvec[1] -= ball->expy; else if (dvec[1] < -ball->expy) dvec[1] += ball->expy; else dvec[1] = 0.0; + /* fall through */ + case MB_TUBE: + if (dvec[0] > ball->expx) dvec[0] -= ball->expx; + else if (dvec[0] < -ball->expx) dvec[0] += ball->expx; + else dvec[0] = 0.0; break; case MB_ELIPSOID: dvec[0] /= ball->expx; dvec[1] /= ball->expy; dvec[2] /= ball->expz; break; - case MB_CUBE: - if (dvec[0] > ball->expx) dvec[0] -= ball->expx; - else if (dvec[0] < -ball->expx) dvec[0] += ball->expx; - else dvec[0] = 0.0; - - if (dvec[1] > ball->expy) dvec[1] -= ball->expy; - else if (dvec[1] < -ball->expy) dvec[1] += ball->expy; - else dvec[1] = 0.0; - - if (dvec[2] > ball->expz) dvec[2] -= ball->expz; - else if (dvec[2] < -ball->expz) dvec[2] += ball->expz; - else dvec[2] = 0.0; - break; /* *** deprecated, could be removed?, do-versioned at least *** */ case MB_TUBEX: @@ -277,208 +349,107 @@ static float densfunc(MetaElem *ball, float x, float y, float z) else if (dvec[2] < -ball->len) dvec[2] += ball->len; else dvec[2] = 0.0; break; - /* *** end deprecated *** */ + /* *** end deprecated *** */ } - dist2 = 1.0f - (len_squared_v3(dvec) / ball->rad2); + /* ball->rad2 is inverse of squared rad */ + dist2 = 1.0f - (len_squared_v3(dvec) * ball->rad2); - if ((ball->flag & MB_NEGATIVE) == 0) { - return (dist2 < 0.0f) ? -0.5f : (ball->s * dist2 * dist2 * dist2) - 0.5f; - } - else { - return (dist2 < 0.0f) ? 0.5f : 0.5f - (ball->s * dist2 * dist2 * dist2); - } -} - -static octal_node *find_metaball_octal_node(octal_node *node, float x, float y, float z, short depth) -{ - if (!depth) return node; - - if (z < node->z) { - if (y < node->y) { - if (x < node->x) { - if (node->nodes[0]) - return find_metaball_octal_node(node->nodes[0], x, y, z, depth--); - else - return node; - } - else { - if (node->nodes[1]) - return find_metaball_octal_node(node->nodes[1], x, y, z, depth--); - else - return node; - } - } - else { - if (x < node->x) { - if (node->nodes[3]) - return find_metaball_octal_node(node->nodes[3], x, y, z, depth--); - else - return node; - } - else { - if (node->nodes[2]) - return find_metaball_octal_node(node->nodes[2], x, y, z, depth--); - else - return node; - } - } - } - else { - if (y < node->y) { - if (x < node->x) { - if (node->nodes[4]) - return find_metaball_octal_node(node->nodes[4], x, y, z, depth--); - else - return node; - } - else { - if (node->nodes[5]) - return find_metaball_octal_node(node->nodes[5], x, y, z, depth--); - else - return node; - } - } - else { - if (x < node->x) { - if (node->nodes[7]) - return find_metaball_octal_node(node->nodes[7], x, y, z, depth--); - else - return node; - } - else { - if (node->nodes[6]) - return find_metaball_octal_node(node->nodes[6], x, y, z, depth--); - else - return node; - } - } - } - - /* all cases accounted for */ - BLI_assert(0); + /* ball->s is negative if metaball is negative */ + return (dist2 < 0.0f) ? 0.0f : (ball->s * dist2 * dist2 * dist2); } +/** + * Computes density at given position form all metaballs which contain this point in their box. + * Traverses BVH using a queue. + */ static float metaball(PROCESS *process, float x, float y, float z) -/* float x, y, z; */ { - octal_tree *metaball_tree = process->metaball_tree; - struct octal_node *node; - struct ml_pointer *ml_p; - float dens = 0; - int a; - - if (process->totelem > 1) { - node = find_metaball_octal_node(metaball_tree->first, x, y, z, metaball_tree->depth); - if (node) { - for (ml_p = node->elems.first; ml_p; ml_p = ml_p->next) { - dens += densfunc(ml_p->ml, x, y, z); - } - - dens += -0.5f * (metaball_tree->pos - node->pos); - dens += 0.5f * (metaball_tree->neg - node->neg); - } - else { - for (a = 0; a < process->totelem; a++) { - dens += densfunc(process->mainb[a], x, y, z); + int i; + float dens = 0.0f; + unsigned int front = 0, back = 0; + MetaballBVHNode *node; + + process->bvh_queue[front++] = &process->metaball_bvh; + + while (front != back) { + node = process->bvh_queue[back++]; + + for (i = 0; i < 2; i++) { + if ((node->bb[i].min[0] <= x) && (node->bb[i].max[0] >= x) && + (node->bb[i].min[1] <= y) && (node->bb[i].max[1] >= y) && + (node->bb[i].min[2] <= z) && (node->bb[i].max[2] >= z)) + { + if (node->child[i]) process->bvh_queue[front++] = node->child[i]; + else dens += densfunc(node->bb[i].ml, x, y, z); } } } - else { - dens += densfunc(process->mainb[0], x, y, z); - } return process->thresh - dens; } -/* ******************************************** */ - -static void accum_mballfaces(PROCESS *process, int i1, int i2, int i3, int i4) +/** + * Adds face to indices, expands memory if needed. + */ +static void make_face(PROCESS *process, int i1, int i2, int i3, int i4) { - int *newi, *cur; - /* static int i = 0; I would like to delete altogether, but I don't dare to, yet */ - - if (process->totindex == process->curindex) { - process->totindex += 256; - newi = MEM_mallocN(4 * sizeof(int) * process->totindex, "vertindex"); - - if (process->indices) { - memcpy(newi, process->indices, 4 * sizeof(int) * (process->totindex - 256)); - MEM_freeN(process->indices); - } - process->indices = newi; + int *cur; + +#ifdef USE_ACCUM_NORMAL + float n[3]; +#endif + + if (UNLIKELY(process->totindex == process->curindex)) { + process->totindex += 4096; + process->indices = MEM_reallocN(process->indices, sizeof(int[4]) * process->totindex); } - - cur = process->indices + 4 * process->curindex; + + cur = process->indices[process->curindex++]; /* displists now support array drawing, we treat tri's as fake quad */ - + cur[0] = i1; cur[1] = i2; cur[2] = i3; - if (i4 == 0) + + if (i4 == 0) { cur[3] = i3; - else + } + else { cur[3] = i4; - - process->curindex++; - -} - -/* ******************* MEMORY MANAGEMENT *********************** */ -static void *new_pgn_element(PROCESS *process, int size) -{ - /* during polygonize 1000s of elements are allocated - * and never freed in between. Freeing only done at the end. - */ - int blocksize = 16384; - void *adr; - - if (size > 10000 || size == 0) { - printf("incorrect use of new_pgn_element\n"); } - else if (size == -1) { - struct pgn_elements *cur = process->pgn_list.first; - while (cur) { - MEM_freeN(cur->data); - cur = cur->next; - } - BLI_freelistN(&process->pgn_list); - - return NULL; + +#ifdef USE_ACCUM_NORMAL + if (i4 == 0) { + normal_tri_v3(n, process->co[i1], process->co[i2], process->co[i3]); + accumulate_vertex_normals( + process->no[i1], process->no[i2], process->no[i3], NULL, n, + process->co[i1], process->co[i2], process->co[i3], NULL); } - - size = 4 * ( (size + 3) / 4); - - if (process->pgn_current) { - if (size + process->pgn_offset < blocksize) { - adr = (void *) (process->pgn_current->data + process->pgn_offset); - process->pgn_offset += size; - return adr; - } + else { + normal_quad_v3(n, process->co[i1], process->co[i2], process->co[i3], process->co[i4]); + accumulate_vertex_normals( + process->no[i1], process->no[i2], process->no[i3], process->no[i4], n, + process->co[i1], process->co[i2], process->co[i3], process->co[i4]); } - - process->pgn_current = MEM_callocN(sizeof(struct pgn_elements), "newpgn"); - process->pgn_current->data = MEM_callocN(blocksize, "newpgn"); - BLI_addtail(&process->pgn_list, process->pgn_current); - - process->pgn_offset = size; - return process->pgn_current->data; +#endif + } +/* Frees allocated memory */ static void freepolygonize(PROCESS *process) { - MEM_freeN(process->corners); - MEM_freeN(process->edges); - MEM_freeN(process->centers); - - new_pgn_element(process, -1); - - if (process->vertices.ptr) { - MEM_freeN(process->vertices.ptr); - } + if (process->corners) MEM_freeN(process->corners); + if (process->edges) MEM_freeN(process->edges); + if (process->centers) MEM_freeN(process->centers); + if (process->mainb) MEM_freeN(process->mainb); + if (process->bvh_queue) MEM_freeN(process->bvh_queue); + if (process->pgn_elements) BLI_memarena_free(process->pgn_elements); } +/* **************** POLYGONIZATION ************************ */ + /**** Cubical Polygonization (optional) ****/ #define LB 0 /* left bottom edge */ @@ -495,6 +466,7 @@ static void freepolygonize(PROCESS *process) #define TF 11 /* top far edge */ static INTLISTS *cubetable[256]; +static char faces[256]; /* edge: LB, LT, LN, LF, RB, RT, RN, RF, BN, BF, TN, TF */ static int corner1[12] = { @@ -504,77 +476,89 @@ static int corner2[12] = { LBF, LTF, LTN, LTF, RBF, RTF, RTN, RTF, RBN, RBF, RTN, RTF }; static int leftface[12] = { - B, L, L, F, R, T, N, R, N, B, T, F + B, L, L, F, R, T, N, R, N, B, T, F }; /* face on left when going corner1 to corner2 */ static int rightface[12] = { - L, T, N, L, B, R, R, F, B, F, N, T + L, T, N, L, B, R, R, F, B, F, N, T }; /* face on right when going corner1 to corner2 */ - -/* docube: triangulate the cube directly, without decomposition */ - -static void docube(PROCESS *process, CUBE *cube, MetaBall *mb) +/** + * triangulate the cube directly, without decomposition + */ +static void docube(PROCESS *process, CUBE *cube) { INTLISTS *polys; CORNER *c1, *c2; int i, index = 0, count, indexar[8]; - + + /* Determine which case cube falls into. */ for (i = 0; i < 8; i++) { if (cube->corners[i]->value > 0.0f) { index += (1 << i); } } - + + /* Using faces[] table, adds neighbouring cube if surface intersects face in this direction. */ + if (MB_BIT(faces[index], 0)) add_cube(process, cube->i - 1, cube->j, cube->k); + if (MB_BIT(faces[index], 1)) add_cube(process, cube->i + 1, cube->j, cube->k); + if (MB_BIT(faces[index], 2)) add_cube(process, cube->i, cube->j - 1, cube->k); + if (MB_BIT(faces[index], 3)) add_cube(process, cube->i, cube->j + 1, cube->k); + if (MB_BIT(faces[index], 4)) add_cube(process, cube->i, cube->j, cube->k - 1); + if (MB_BIT(faces[index], 5)) add_cube(process, cube->i, cube->j, cube->k + 1); + + /* Using cubetable[], determines polygons for output. */ for (polys = cubetable[index]; polys; polys = polys->next) { INTLIST *edges; - + count = 0; - + /* Sets needed vertex id's lying on the edges. */ for (edges = polys->list; edges; edges = edges->next) { c1 = cube->corners[corner1[edges->i]]; c2 = cube->corners[corner2[edges->i]]; - - indexar[count] = vertid(process, c1, c2, mb); + + indexar[count] = vertid(process, c1, c2); count++; } + + /* Adds faces to output. */ if (count > 2) { switch (count) { case 3: - accum_mballfaces(process, indexar[2], indexar[1], indexar[0], 0); + make_face(process, indexar[2], indexar[1], indexar[0], 0); break; case 4: - if (indexar[0] == 0) accum_mballfaces(process, indexar[0], indexar[3], indexar[2], indexar[1]); - else accum_mballfaces(process, indexar[3], indexar[2], indexar[1], indexar[0]); + if (indexar[0] == 0) make_face(process, indexar[0], indexar[3], indexar[2], indexar[1]); + else make_face(process, indexar[3], indexar[2], indexar[1], indexar[0]); break; case 5: - if (indexar[0] == 0) accum_mballfaces(process, indexar[0], indexar[3], indexar[2], indexar[1]); - else accum_mballfaces(process, indexar[3], indexar[2], indexar[1], indexar[0]); - - accum_mballfaces(process, indexar[4], indexar[3], indexar[0], 0); + if (indexar[0] == 0) make_face(process, indexar[0], indexar[3], indexar[2], indexar[1]); + else make_face(process, indexar[3], indexar[2], indexar[1], indexar[0]); + + make_face(process, indexar[4], indexar[3], indexar[0], 0); break; case 6: if (indexar[0] == 0) { - accum_mballfaces(process, indexar[0], indexar[3], indexar[2], indexar[1]); - accum_mballfaces(process, indexar[0], indexar[5], indexar[4], indexar[3]); + make_face(process, indexar[0], indexar[3], indexar[2], indexar[1]); + make_face(process, indexar[0], indexar[5], indexar[4], indexar[3]); } else { - accum_mballfaces(process, indexar[3], indexar[2], indexar[1], indexar[0]); - accum_mballfaces(process, indexar[5], indexar[4], indexar[3], indexar[0]); + make_face(process, indexar[3], indexar[2], indexar[1], indexar[0]); + make_face(process, indexar[5], indexar[4], indexar[3], indexar[0]); } break; case 7: if (indexar[0] == 0) { - accum_mballfaces(process, indexar[0], indexar[3], indexar[2], indexar[1]); - accum_mballfaces(process, indexar[0], indexar[5], indexar[4], indexar[3]); + make_face(process, indexar[0], indexar[3], indexar[2], indexar[1]); + make_face(process, indexar[0], indexar[5], indexar[4], indexar[3]); } else { - accum_mballfaces(process, indexar[3], indexar[2], indexar[1], indexar[0]); - accum_mballfaces(process, indexar[5], indexar[4], indexar[3], indexar[0]); + make_face(process, indexar[3], indexar[2], indexar[1], indexar[0]); + make_face(process, indexar[5], indexar[4], indexar[3], indexar[0]); } - - accum_mballfaces(process, indexar[6], indexar[5], indexar[0], 0); + + make_face(process, indexar[6], indexar[5], indexar[0], 0); break; } @@ -582,63 +566,10 @@ static void docube(PROCESS *process, CUBE *cube, MetaBall *mb) } } - -/* testface: given cube at lattice (i, j, k), and four corners of face, - * if surface crosses face, compute other four corners of adjacent cube - * and add new cube to cube stack */ - -static void testface(PROCESS *process, int i, int j, int k, CUBE *old, int bit, int c1, int c2, int c3, int c4) -{ - CUBE newc; - CUBES *oldcubes = process->cubes; - CORNER *corn1, *corn2, *corn3, *corn4; - int n, pos; - - corn1 = old->corners[c1]; - corn2 = old->corners[c2]; - corn3 = old->corners[c3]; - corn4 = old->corners[c4]; - - pos = corn1->value > 0.0f ? 1 : 0; - - /* test if no surface crossing */ - if ( (corn2->value > 0) == pos && (corn3->value > 0) == pos && (corn4->value > 0) == pos) return; - /* test if cube out of bounds */ - /*if ( abs(i) > p->bounds || abs(j) > p->bounds || abs(k) > p->bounds) return;*/ - /* test if already visited (always as last) */ - if (setcenter(process, process->centers, i, j, k)) { - return; - } - - /* create new cube and add cube to top of stack: */ - process->cubes = (CUBES *) new_pgn_element(process, sizeof(CUBES)); - process->cubes->next = oldcubes; - - newc.i = i; - newc.j = j; - newc.k = k; - for (n = 0; n < 8; n++) newc.corners[n] = NULL; - - newc.corners[FLIP(c1, bit)] = corn1; - newc.corners[FLIP(c2, bit)] = corn2; - newc.corners[FLIP(c3, bit)] = corn3; - newc.corners[FLIP(c4, bit)] = corn4; - - if (newc.corners[0] == NULL) newc.corners[0] = setcorner(process, i, j, k); - if (newc.corners[1] == NULL) newc.corners[1] = setcorner(process, i, j, k + 1); - if (newc.corners[2] == NULL) newc.corners[2] = setcorner(process, i, j + 1, k); - if (newc.corners[3] == NULL) newc.corners[3] = setcorner(process, i, j + 1, k + 1); - if (newc.corners[4] == NULL) newc.corners[4] = setcorner(process, i + 1, j, k); - if (newc.corners[5] == NULL) newc.corners[5] = setcorner(process, i + 1, j, k + 1); - if (newc.corners[6] == NULL) newc.corners[6] = setcorner(process, i + 1, j + 1, k); - if (newc.corners[7] == NULL) newc.corners[7] = setcorner(process, i + 1, j + 1, k + 1); - - process->cubes->cube = newc; -} - -/* setcorner: return corner with the given lattice location - * set (and cache) its function value */ - +/** + * return corner with the given lattice location + * set (and cache) its function value + */ static CORNER *setcorner(PROCESS *process, int i, int j, int k) { /* for speed, do corner value caching here */ @@ -648,32 +579,33 @@ static CORNER *setcorner(PROCESS *process, int i, int j, int k) /* does corner exist? */ index = HASH(i, j, k); c = process->corners[index]; - + for (; c != NULL; c = c->next) { if (c->i == i && c->j == j && c->k == k) { return c; } } - c = (CORNER *) new_pgn_element(process, sizeof(CORNER)); + c = BLI_memarena_alloc(process->pgn_elements, sizeof(CORNER)); - c->i = i; + c->i = i; c->co[0] = ((float)i - 0.5f) * process->size; - c->j = j; + c->j = j; c->co[1] = ((float)j - 0.5f) * process->size; - c->k = k; + c->k = k; c->co[2] = ((float)k - 0.5f) * process->size; - c->value = process->function(process, c->co[0], c->co[1], c->co[2]); - + + c->value = metaball(process, c->co[0], c->co[1], c->co[2]); + c->next = process->corners[index]; process->corners[index] = c; - + return c; } - -/* nextcwedge: return next clockwise edge from given edge around given face */ - +/** + * return next clockwise edge from given edge around given face + */ static int nextcwedge(int edge, int face) { switch (edge) { @@ -705,18 +637,18 @@ static int nextcwedge(int edge, int face) return 0; } - -/* otherface: return face adjoining edge that is not the given face */ - +/** + * \return the face adjoining edge that is not the given face + */ static int otherface(int edge, int face) { int other = leftface[edge]; return face == other ? rightface[edge] : other; } - -/* makecubetable: create the 256 entry table for cubical polygonization */ - +/** + * create the 256 entry table for cubical polygonization + */ static void makecubetable(void) { static bool is_done = false; @@ -731,22 +663,22 @@ static void makecubetable(void) for (e = 0; e < 12; e++) if (!done[e] && (pos[corner1[e]] != pos[corner2[e]])) { INTLIST *ints = NULL; - INTLISTS *lists = (INTLISTS *) MEM_callocN(sizeof(INTLISTS), "mball_intlist"); + INTLISTS *lists = MEM_callocN(sizeof(INTLISTS), "mball_intlist"); int start = e, edge = e; - + /* get face that is to right of edge from pos to neg corner: */ int face = pos[corner1[e]] ? rightface[e] : leftface[e]; - + while (1) { edge = nextcwedge(edge, face); done[edge] = 1; if (pos[corner1[edge]] != pos[corner2[edge]]) { INTLIST *tmp = ints; - - ints = (INTLIST *) MEM_callocN(sizeof(INTLIST), "mball_intlist"); + + ints = MEM_callocN(sizeof(INTLIST), "mball_intlist"); ints->i = edge; ints->next = tmp; /* add edge to head of list */ - + if (edge == start) break; face = otherface(edge, face); } @@ -756,6 +688,23 @@ static void makecubetable(void) cubetable[i] = lists; } } + + for (i = 0; i < 256; i++) { + INTLISTS *polys; + faces[i] = 0; + for (polys = cubetable[i]; polys; polys = polys->next) { + INTLIST *edges; + + for (edges = polys->list; edges; edges = edges->next) { + if (edges->i == LB || edges->i == LT || edges->i == LN || edges->i == LF) faces[i] |= 1 << L; + if (edges->i == RB || edges->i == RT || edges->i == RN || edges->i == RF) faces[i] |= 1 << R; + if (edges->i == LB || edges->i == RB || edges->i == BN || edges->i == BF) faces[i] |= 1 << B; + if (edges->i == LT || edges->i == RT || edges->i == TN || edges->i == TF) faces[i] |= 1 << T; + if (edges->i == LN || edges->i == RN || edges->i == BN || edges->i == TN) faces[i] |= 1 << N; + if (edges->i == LF || edges->i == RF || edges->i == BF || edges->i == TF) faces[i] |= 1 << F; + } + } + } } void BKE_mball_cubeTable_free(void) @@ -768,14 +717,14 @@ void BKE_mball_cubeTable_free(void) lists = cubetable[i]; while (lists) { nlists = lists->next; - + ints = lists->list; while (ints) { nints = ints->next; MEM_freeN(ints); ints = nints; } - + MEM_freeN(lists); lists = nlists; } @@ -785,9 +734,9 @@ void BKE_mball_cubeTable_free(void) /**** Storage ****/ -/* setcenter: set (i, j, k) entry of table[] - * return 1 if already set; otherwise, set and return 0 */ - +/** + * Inserts cube at lattice i, j, k into hash table, marking it as "done" + */ static int setcenter(PROCESS *process, CENTERLIST *table[], const int i, const int j, const int k) { int index; @@ -799,30 +748,29 @@ static int setcenter(PROCESS *process, CENTERLIST *table[], const int i, const i for (l = q; l != NULL; l = l->next) { if (l->i == i && l->j == j && l->k == k) return 1; } - - newc = (CENTERLIST *) new_pgn_element(process, sizeof(CENTERLIST)); - newc->i = i; - newc->j = j; - newc->k = k; + + newc = BLI_memarena_alloc(process->pgn_elements, sizeof(CENTERLIST)); + newc->i = i; + newc->j = j; + newc->k = k; newc->next = q; table[index] = newc; - + return 0; } - -/* setedge: set vertex id for edge */ - -static void setedge(PROCESS *process, - EDGELIST *table[], - int i1, int j1, - int k1, int i2, - int j2, int k2, - int vid) +/** + * Sets vid of vertex lying on given edge. + */ +static void setedge( + PROCESS *process, + int i1, int j1, int k1, + int i2, int j2, int k2, + int vid) { - unsigned int index; + int index; EDGELIST *newe; - + if (i1 > i2 || (i1 == i2 && (j1 > j2 || (j1 == j2 && k1 > k2)))) { int t = i1; i1 = i2; @@ -835,27 +783,28 @@ static void setedge(PROCESS *process, k2 = t; } index = HASH(i1, j1, k1) + HASH(i2, j2, k2); - newe = (EDGELIST *) new_pgn_element(process, sizeof(EDGELIST)); - newe->i1 = i1; - newe->j1 = j1; + newe = BLI_memarena_alloc(process->pgn_elements, sizeof(EDGELIST)); + + newe->i1 = i1; + newe->j1 = j1; newe->k1 = k1; - newe->i2 = i2; - newe->j2 = j2; + newe->i2 = i2; + newe->j2 = j2; newe->k2 = k2; newe->vid = vid; - newe->next = table[index]; - table[index] = newe; + newe->next = process->edges[index]; + process->edges[index] = newe; } - -/* getedge: return vertex id for edge; return -1 if not set */ - +/** + * \return vertex id for edge; return -1 if not set + */ static int getedge(EDGELIST *table[], int i1, int j1, int k1, int i2, int j2, int k2) { EDGELIST *q; - + if (i1 > i2 || (i1 == i2 && (j1 > j2 || (j1 == j2 && k1 > k2)))) { int t = i1; i1 = i2; @@ -878,62 +827,52 @@ static int getedge(EDGELIST *table[], return -1; } - -/**** Vertices ****/ - -#undef R - - - -/* vertid: return index for vertex on edge: - * c1->value and c2->value are presumed of different sign - * return saved index if any; else compute vertex and save */ - -/* addtovertices: add v to sequence of vertices */ - -static void addtovertices(VERTICES *vertices, VERTEX v) +/** + * Adds a vertex, expands memory if needed. + */ +static void addtovertices(PROCESS *process, const float v[3], const float no[3]) { - if (vertices->count == vertices->max) { - int i; - VERTEX *newv; - vertices->max = vertices->count == 0 ? 10 : 2 * vertices->count; - newv = (VERTEX *) MEM_callocN(vertices->max * sizeof(VERTEX), "addtovertices"); - - for (i = 0; i < vertices->count; i++) newv[i] = vertices->ptr[i]; - - if (vertices->ptr != NULL) MEM_freeN(vertices->ptr); - vertices->ptr = newv; + if (process->curvertex == process->totvertex) { + process->totvertex += 4096; + process->co = MEM_reallocN(process->co, process->totvertex * sizeof(float[3])); + process->no = MEM_reallocN(process->no, process->totvertex * sizeof(float[3])); } - vertices->ptr[vertices->count++] = v; -} -/* vnormal: compute unit length surface normal at point */ + copy_v3_v3(process->co[process->curvertex], v); + copy_v3_v3(process->no[process->curvertex], no); + process->curvertex++; +} + +#ifndef USE_ACCUM_NORMAL +/** + * Computes normal from density field at given point. + * + * \note Doesn't do normalization! + */ static void vnormal(PROCESS *process, const float point[3], float r_no[3]) { - const float delta = 0.2f * process->delta; - const float f = process->function(process, point[0], point[1], point[2]); + const float delta = process->delta; + const float f = metaball(process, point[0], point[1], point[2]); - r_no[0] = process->function(process, point[0] + delta, point[1], point[2]) - f; - r_no[1] = process->function(process, point[0], point[1] + delta, point[2]) - f; - r_no[2] = process->function(process, point[0], point[1], point[2] + delta) - f; + r_no[0] = metaball(process, point[0] + delta, point[1], point[2]) - f; + r_no[1] = metaball(process, point[0], point[1] + delta, point[2]) - f; + r_no[2] = metaball(process, point[0], point[1], point[2] + delta) - f; -#if 1 - normalize_v3(r_no); -#else +#if 0 f = normalize_v3(r_no); - + if (0) { float tvec[3]; - + delta *= 2.0f; - + f = process->function(process, point[0], point[1], point[2]); - + tvec[0] = process->function(process, point[0] + delta, point[1], point[2]) - f; tvec[1] = process->function(process, point[0], point[1] + delta, point[2]) - f; tvec[2] = process->function(process, point[0], point[1], point[2] + delta) - f; - + if (normalize_v3(tvec) != 0.0f) { add_v3_v3(r_no, tvec); normalize_v3(r_no); @@ -941,355 +880,242 @@ static void vnormal(PROCESS *process, const float point[3], float r_no[3]) } #endif } +#endif /* USE_ACCUM_NORMAL */ - -static int vertid(PROCESS *process, const CORNER *c1, const CORNER *c2, MetaBall *mb) +/** + * \return the id of vertex between two corners. + * + * If it wasn't previously computed, does #converge() and adds vertex to process. + */ +static int vertid(PROCESS *process, const CORNER *c1, const CORNER *c2) { - VERTEX v; + float v[3], no[3]; int vid = getedge(process->edges, c1->i, c1->j, c1->k, c2->i, c2->j, c2->k); - if (vid != -1) { - return vid; /* previously computed */ - } + if (vid != -1) return vid; /* previously computed */ - converge(process, c1->co, c2->co, c1->value, c2->value, v.co, mb, 1); /* position */ - vnormal(process, v.co, v.no); + converge(process, c1, c2, v); /* position */ + +#ifdef USE_ACCUM_NORMAL + zero_v3(no); +#else + vnormal(process, v, no); +#endif + + addtovertices(process, v, no); /* save vertex */ + vid = (int)process->curvertex - 1; + setedge(process, c1->i, c1->j, c1->k, c2->i, c2->j, c2->k, vid); - addtovertices(&process->vertices, v); /* save vertex */ - vid = process->vertices.count - 1; - setedge(process, process->edges, c1->i, c1->j, c1->k, c2->i, c2->j, c2->k, vid); - return vid; } - -/* converge: from two points of differing sign, converge to zero crossing */ -/* watch it: p1 and p2 are used to calculate */ -static void converge(PROCESS *process, const float p1[3], const float p2[3], float v1, float v2, - float p[3], MetaBall *mb, int f) +/** + * Given two corners, computes approximation of surface intersection point between them. + * In case of small threshold, do bisection. + */ +static void converge(PROCESS *process, const CORNER *c1, const CORNER *c2, float r_p[3]) { - int i = 0; - float pos[3], neg[3]; - float positive = 0.0f, negative = 0.0f; - float dvec[3]; - - if (v1 < 0) { - copy_v3_v3(pos, p2); - copy_v3_v3(neg, p1); - positive = v2; - negative = v1; + float tmp, dens; + unsigned int i; + float c1_value, c1_co[3]; + float c2_value, c2_co[3]; + + if (c1->value < c2->value) { + c1_value = c2->value; + copy_v3_v3(c1_co, c2->co); + c2_value = c1->value; + copy_v3_v3(c2_co, c1->co); } else { - copy_v3_v3(pos, p1); - copy_v3_v3(neg, p2); - positive = v1; - negative = v2; + c1_value = c1->value; + copy_v3_v3(c1_co, c1->co); + c2_value = c2->value; + copy_v3_v3(c2_co, c2->co); } - sub_v3_v3v3(dvec, pos, neg); -/* Approximation by linear interpolation is faster then binary subdivision, - * but it results sometimes (mb->thresh < 0.2) into the strange results */ - if ((mb->thresh > 0.2f) && (f == 1)) { - if ((dvec[1] == 0.0f) && (dvec[2] == 0.0f)) { - p[0] = neg[0] - negative * dvec[0] / (positive - negative); - p[1] = neg[1]; - p[2] = neg[2]; - return; - } - if ((dvec[0] == 0.0f) && (dvec[2] == 0.0f)) { - p[0] = neg[0]; - p[1] = neg[1] - negative * dvec[1] / (positive - negative); - p[2] = neg[2]; - return; - } - if ((dvec[0] == 0.0f) && (dvec[1] == 0.0f)) { - p[0] = neg[0]; - p[1] = neg[1]; - p[2] = neg[2] - negative * dvec[2] / (positive - negative); - return; - } - } + for (i = 0; i < process->converge_res; i++) { + interp_v3_v3v3(r_p, c1_co, c2_co, 0.5f); + dens = metaball(process, r_p[0], r_p[1], r_p[2]); - if ((dvec[1] == 0.0f) && (dvec[2] == 0.0f)) { - p[1] = neg[1]; - p[2] = neg[2]; - while (1) { - if (i++ == RES) return; - p[0] = 0.5f * (pos[0] + neg[0]); - if ((process->function(process, p[0], p[1], p[2])) > 0.0f) pos[0] = p[0]; - else neg[0] = p[0]; + if (dens > 0.0f) { + c1_value = dens; + copy_v3_v3(c1_co, r_p); } - } - - if ((dvec[0] == 0.0f) && (dvec[2] == 0.0f)) { - p[0] = neg[0]; - p[2] = neg[2]; - while (1) { - if (i++ == RES) return; - p[1] = 0.5f * (pos[1] + neg[1]); - if ((process->function(process, p[0], p[1], p[2])) > 0.0f) pos[1] = p[1]; - else neg[1] = p[1]; + else { + c2_value = dens; + copy_v3_v3(c2_co, r_p); } } - if ((dvec[0] == 0.0f) && (dvec[1] == 0.0f)) { - p[0] = neg[0]; - p[1] = neg[1]; - while (1) { - if (i++ == RES) return; - p[2] = 0.5f * (pos[2] + neg[2]); - if ((process->function(process, p[0], p[1], p[2])) > 0.0f) pos[2] = p[2]; - else neg[2] = p[2]; - } - } + tmp = -c1_value / (c2_value - c1_value); + interp_v3_v3v3(r_p, c1_co, c2_co, tmp); +} - /* This is necessary to find start point */ - while (1) { - mid_v3_v3v3(&p[0], pos, neg); +/** + * Adds cube at given lattice position to cube stack of process. + */ +static void add_cube(PROCESS *process, int i, int j, int k) +{ + CUBES *ncube; + int n; - if (i++ == RES) { - return; - } + /* test if cube has been found before */ + if (setcenter(process, process->centers, i, j, k) == 0) { + /* push cube on stack: */ + ncube = BLI_memarena_alloc(process->pgn_elements, sizeof(CUBES)); + ncube->next = process->cubes; + process->cubes = ncube; - if ((process->function(process, p[0], p[1], p[2])) > 0.0f) { - copy_v3_v3(pos, &p[0]); - } - else { - copy_v3_v3(neg, &p[0]); - } + ncube->cube.i = i; + ncube->cube.j = j; + ncube->cube.k = k; + + /* set corners of initial cube: */ + for (n = 0; n < 8; n++) + ncube->cube.corners[n] = setcorner(process, i + MB_BIT(n, 2), j + MB_BIT(n, 1), k + MB_BIT(n, 0)); } } -/* ************************************** */ -static void add_cube(PROCESS *process, int i, int j, int k, int count) +static void next_lattice(int r[3], const float pos[3], const float size) { - CUBES *ncube; - int n; - int a, b, c; - - /* hmmm, not only one, but eight cube will be added on the stack - * ... */ - for (a = i - 1; a < i + count; a++) - for (b = j - 1; b < j + count; b++) - for (c = k - 1; c < k + count; c++) { - /* test if cube has been found before */ - if (setcenter(process, process->centers, a, b, c) == 0) { - /* push cube on stack: */ - ncube = (CUBES *) new_pgn_element(process, sizeof(CUBES)); - ncube->next = process->cubes; - process->cubes = ncube; - - ncube->cube.i = a; - ncube->cube.j = b; - ncube->cube.k = c; - - /* set corners of initial cube: */ - for (n = 0; n < 8; n++) - ncube->cube.corners[n] = setcorner(process, a + MB_BIT(n, 2), b + MB_BIT(n, 1), c + MB_BIT(n, 0)); - } - } + r[0] = (int)ceil((pos[0] / size) + 0.5f); + r[1] = (int)ceil((pos[1] / size) + 0.5f); + r[2] = (int)ceil((pos[2] / size) + 0.5f); } - - -static void find_first_points(PROCESS *process, MetaBall *mb, int a) +static void prev_lattice(int r[3], const float pos[3], const float size) { - MetaElem *ml; - float f; - - ml = process->mainb[a]; - f = 1.0f - (mb->thresh / ml->s); - - /* Skip, when Stiffness of MetaElement is too small ... MetaElement can't be - * visible alone ... but still can influence others MetaElements :-) */ - if (f > 0.0f) { - float IN[3] = {0.0f}, OUT[3] = {0.0f}, in[3] = {0.0f}, out[3]; - int i, j, k, c_i, c_j, c_k; - int index[3] = {1, 0, -1}; - float in_v /*, out_v*/; - float workp[3]; - float dvec[3]; - float tmp_v, workp_v, max_len_sq, nx, ny, nz, max_dim; - - calc_mballco(ml, in); - in_v = process->function(process, in[0], in[1], in[2]); - - for (i = 0; i < 3; i++) { - switch (ml->type) { - case MB_BALL: - OUT[0] = out[0] = IN[0] + index[i] * ml->rad; - break; - case MB_TUBE: - case MB_PLANE: - case MB_ELIPSOID: - case MB_CUBE: - OUT[0] = out[0] = IN[0] + index[i] * (ml->expx + ml->rad); - break; - } + next_lattice(r, pos, size); + r[0]--; r[1]--; r[2]--; +} +static void closest_latice(int r[3], const float pos[3], const float size) +{ + r[0] = (int)floorf(pos[0] / size + 1.0f); + r[1] = (int)floorf(pos[1] / size + 1.0f); + r[2] = (int)floorf(pos[2] / size + 1.0f); +} - for (j = 0; j < 3; j++) { - switch (ml->type) { - case MB_BALL: - OUT[1] = out[1] = IN[1] + index[j] * ml->rad; - break; - case MB_TUBE: - case MB_PLANE: - case MB_ELIPSOID: - case MB_CUBE: - OUT[1] = out[1] = IN[1] + index[j] * (ml->expy + ml->rad); - break; +/** + * Find at most 26 cubes to start polygonization from. + */ +static void find_first_points(PROCESS *process, const unsigned int em) +{ + const MetaElem *ml; + int center[3], lbn[3], rtf[3], it[3], dir[3], add[3]; + float tmp[3], a, b; + + ml = process->mainb[em]; + + mid_v3_v3v3(tmp, ml->bb->vec[0], ml->bb->vec[6]); + closest_latice(center, tmp, process->size); + prev_lattice(lbn, ml->bb->vec[0], process->size); + next_lattice(rtf, ml->bb->vec[6], process->size); + + for (dir[0] = -1; dir[0] <= 1; dir[0]++) { + for (dir[1] = -1; dir[1] <= 1; dir[1]++) { + for (dir[2] = -1; dir[2] <= 1; dir[2]++) { + if (dir[0] == 0 && dir[1] == 0 && dir[2] == 0) { + continue; } - - for (k = 0; k < 3; k++) { - out[0] = OUT[0]; - out[1] = OUT[1]; - switch (ml->type) { - case MB_BALL: - case MB_TUBE: - case MB_PLANE: - out[2] = IN[2] + index[k] * ml->rad; - break; - case MB_ELIPSOID: - case MB_CUBE: - out[2] = IN[2] + index[k] * (ml->expz + ml->rad); - break; - } - - calc_mballco(ml, out); - - /*out_v = process->function(out[0], out[1], out[2]);*/ /*UNUSED*/ - - /* find "first points" on Implicit Surface of MetaElemnt ml */ - copy_v3_v3(workp, in); - workp_v = in_v; - max_len_sq = len_squared_v3v3(out, in); - - nx = fabsf((out[0] - in[0]) / process->size); - ny = fabsf((out[1] - in[1]) / process->size); - nz = fabsf((out[2] - in[2]) / process->size); - - max_dim = max_fff(nx, ny, nz); - if (max_dim != 0.0f) { - float len_sq = 0.0f; - - dvec[0] = (out[0] - in[0]) / max_dim; - dvec[1] = (out[1] - in[1]) / max_dim; - dvec[2] = (out[2] - in[2]) / max_dim; - - while (len_sq <= max_len_sq) { - add_v3_v3(workp, dvec); - - /* compute value of implicite function */ - tmp_v = process->function(process, workp[0], workp[1], workp[2]); - /* add cube to the stack, when value of implicite function crosses zero value */ - if ((tmp_v < 0.0f && workp_v >= 0.0f) || (tmp_v > 0.0f && workp_v <= 0.0f)) { - - /* indexes of CUBE, which includes "first point" */ - c_i = (int)floor(workp[0] / process->size); - c_j = (int)floor(workp[1] / process->size); - c_k = (int)floor(workp[2] / process->size); - - /* add CUBE (with indexes c_i, c_j, c_k) to the stack, - * this cube includes found point of Implicit Surface */ - if ((ml->flag & MB_NEGATIVE) == 0) { - add_cube(process, c_i, c_j, c_k, 1); - } - else { - add_cube(process, c_i, c_j, c_k, 2); - } - } - len_sq = len_squared_v3v3(workp, in); - workp_v = tmp_v; - } + copy_v3_v3_int(it, center); + + b = setcorner(process, it[0], it[1], it[2])->value; + do { + it[0] += dir[0]; + it[1] += dir[1]; + it[2] += dir[2]; + a = b; + b = setcorner(process, it[0], it[1], it[2])->value; + + if (a * b < 0.0f) { + add[0] = it[0] - dir[0]; + add[1] = it[1] - dir[1]; + add[2] = it[2] - dir[2]; + DO_MIN(it, add); + add_cube(process, add[0], add[1], add[2]); + break; } - } + } while ((it[0] > lbn[0]) && (it[1] > lbn[1]) && (it[2] > lbn[2]) && + (it[0] < rtf[0]) && (it[1] < rtf[1]) && (it[2] < rtf[2])); } } } } -static void polygonize(PROCESS *process, MetaBall *mb) +/** + * The main polygonization proc. + * Allocates memory, makes cubetable, + * finds starting surface points + * and processes cubes on the stack until none left. + */ +static void polygonize(PROCESS *process) { CUBE c; - int a; - - process->vertices.count = process->vertices.max = 0; - process->vertices.ptr = NULL; + unsigned int i; - /* allocate hash tables and build cube polygon table: */ process->centers = MEM_callocN(HASHSIZE * sizeof(CENTERLIST *), "mbproc->centers"); process->corners = MEM_callocN(HASHSIZE * sizeof(CORNER *), "mbproc->corners"); process->edges = MEM_callocN(2 * HASHSIZE * sizeof(EDGELIST *), "mbproc->edges"); - makecubetable(); + process->bvh_queue = MEM_callocN(sizeof(MetaballBVHNode *) * process->bvh_queue_size, "Metaball BVH Queue"); - for (a = 0; a < process->totelem; a++) { + makecubetable(); - /* try to find 8 points on the surface for each MetaElem */ - find_first_points(process, mb, a); + for (i = 0; i < process->totelem; i++) { + find_first_points(process, i); } - /* polygonize all MetaElems of current MetaBall */ - while (process->cubes != NULL) { /* process active cubes till none left */ + while (process->cubes != NULL) { c = process->cubes->cube; - - /* polygonize the cube directly: */ - docube(process, &c, mb); - - /* pop current cube from stack */ process->cubes = process->cubes->next; - - /* test six face directions, maybe add to stack: */ - testface(process, c.i - 1, c.j, c.k, &c, 2, LBN, LBF, LTN, LTF); - testface(process, c.i + 1, c.j, c.k, &c, 2, RBN, RBF, RTN, RTF); - testface(process, c.i, c.j - 1, c.k, &c, 1, LBN, LBF, RBN, RBF); - testface(process, c.i, c.j + 1, c.k, &c, 1, LTN, LTF, RTN, RTF); - testface(process, c.i, c.j, c.k - 1, &c, 0, LBN, LTN, RBN, RTN); - testface(process, c.i, c.j, c.k + 1, &c, 0, LBF, LTF, RBF, RTF); + + docube(process, &c); } } -static float init_meta(EvaluationContext *eval_ctx, PROCESS *process, Scene *scene, Object *ob) /* return totsize */ +/** + * Iterates over ALL objects in the scene and all of its sets, including + * making all duplis(not only metas). Copies metas to mainb array. + * Computes bounding boxes for building BVH. */ +static void init_meta(EvaluationContext *eval_ctx, PROCESS *process, Scene *scene, Object *ob) { Scene *sce_iter = scene; Base *base; Object *bob; MetaBall *mb; - MetaElem *ml; - float size, totsize, obinv[4][4], obmat[4][4], vec[3]; - //float max = 0.0f; - int a, obnr, zero_size = 0; + const MetaElem *ml; + float obinv[4][4], obmat[4][4]; + unsigned int i; + int obnr, zero_size = 0; char obname[MAX_ID_NAME]; SceneBaseIter iter; copy_m4_m4(obmat, ob->obmat); /* to cope with duplicators from BKE_scene_base_iter_next */ invert_m4_m4(obinv, ob->obmat); - a = 0; - + BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); - + /* make main array */ BKE_scene_base_iter_next(eval_ctx, &iter, &sce_iter, 0, NULL, NULL); while (BKE_scene_base_iter_next(eval_ctx, &iter, &sce_iter, 1, &base, &bob)) { - if (bob->type == OB_MBALL) { zero_size = 0; ml = NULL; if (bob == ob && (base->flag & OB_FROMDUPLI) == 0) { mb = ob->data; - + if (mb->editelems) ml = mb->editelems->first; else ml = mb->elems.first; } else { char name[MAX_ID_NAME]; int nr; - + BLI_split_name_num(name, &nr, bob->id.name + 2, '.'); if (STREQ(obname, name)) { mb = bob->data; - + if (mb->editelems) ml = mb->editelems->first; else ml = mb->elems.first; } @@ -1312,562 +1138,124 @@ static float init_meta(EvaluationContext *eval_ctx, PROCESS *process, Scene *sce } if (zero_size) { - unsigned int ml_count = 0; while (ml) { - ml_count++; ml = ml->next; } - process->totelem -= ml_count; } else { while (ml) { if (!(ml->flag & MB_HIDE)) { - int i; - float temp1[4][4], temp2[4][4], temp3[4][4]; - float (*mat)[4] = NULL, (*imat)[4] = NULL; - float max_x, max_y, max_z, min_x, min_y, min_z; + float pos[4][4], rot[4][4]; float expx, expy, expz; + float tempmin[3], tempmax[3]; - max_x = max_y = max_z = -3.4e38; - min_x = min_y = min_z = 3.4e38; - - /* too big stiffness seems only ugly due to linear interpolation - * no need to have possibility for too big stiffness */ - if (ml->s > 10.0f) ml->s = 10.0f; - - /* Rotation of MetaElem is stored in quat */ - quat_to_mat4(temp3, ml->quat); - - /* Translation of MetaElem */ - unit_m4(temp2); - temp2[3][0] = ml->x; - temp2[3][1] = ml->y; - temp2[3][2] = ml->z; - - mul_m4_m4m4(temp1, temp2, temp3); + MetaElem *new_ml; /* make a copy because of duplicates */ - process->mainb[a] = new_pgn_element(process, sizeof(MetaElem)); - *(process->mainb[a]) = *ml; - process->mainb[a]->bb = new_pgn_element(process, sizeof(BoundBox)); + new_ml = BLI_memarena_alloc(process->pgn_elements, sizeof(MetaElem)); + *(new_ml) = *ml; + new_ml->bb = BLI_memarena_alloc(process->pgn_elements, sizeof(BoundBox)); + new_ml->mat = BLI_memarena_alloc(process->pgn_elements, 4 * 4 * sizeof(float)); + new_ml->imat = BLI_memarena_alloc(process->pgn_elements, 4 * 4 * sizeof(float)); - mat = new_pgn_element(process, 4 * 4 * sizeof(float)); - imat = new_pgn_element(process, 4 * 4 * sizeof(float)); - - /* mat is the matrix to transform from mball into the basis-mball */ - invert_m4_m4(obinv, obmat); - mul_m4_m4m4(temp2, obinv, bob->obmat); - /* MetaBall transformation */ - mul_m4_m4m4(mat, temp2, temp1); - - invert_m4_m4(imat, mat); + /* too big stiffness seems only ugly due to linear interpolation + * no need to have possibility for too big stiffness */ + if (ml->s > 10.0f) new_ml->s = 10.0f; + else new_ml->s = ml->s; - process->mainb[a]->rad2 = ml->rad * ml->rad; + /* if metaball is negative, set stiffness negative */ + if (new_ml->flag & MB_NEGATIVE) new_ml->s = -new_ml->s; - process->mainb[a]->mat = (float *) mat; - process->mainb[a]->imat = (float *) imat; + /* Translation of MetaElem */ + unit_m4(pos); + pos[3][0] = ml->x; + pos[3][1] = ml->y; + pos[3][2] = ml->z; - if (!MB_TYPE_SIZE_SQUARED(ml->type)) { - expx = ml->expx; - expy = ml->expy; - expz = ml->expz; - } - else { - expx = ml->expx * ml->expx; - expy = ml->expy * ml->expy; - expz = ml->expz * ml->expz; + /* Rotation of MetaElem is stored in quat */ + quat_to_mat4(rot, ml->quat); + + /* basis object space -> world -> ml object space -> position -> rotation -> ml local space */ + mul_m4_series((float(*)[4])new_ml->mat, obinv, bob->obmat, pos, rot); + /* ml local space -> basis object space */ + invert_m4_m4((float(*)[4])new_ml->imat, (float(*)[4])new_ml->mat); + + /* rad2 is inverse of squared radius */ + new_ml->rad2 = 1 / (ml->rad * ml->rad); + + /* initial dimensions = radius */ + expx = ml->rad; + expy = ml->rad; + expz = ml->rad; + + switch (ml->type) { + case MB_BALL: + break; + case MB_CUBE: /* cube is "expanded" by expz, expy and expx */ + expz += ml->expz; + /* fall through */ + case MB_PLANE: /* plane is "expanded" by expy and expx */ + expy += ml->expy; + /* fall through */ + case MB_TUBE: /* tube is "expanded" by expx */ + expx += ml->expx; + break; + case MB_ELIPSOID: /* ellipsoid is "stretched" by exp* */ + expx *= ml->expx; + expy *= ml->expy; + expz *= ml->expz; + break; } /* untransformed Bounding Box of MetaElem */ /* TODO, its possible the elem type has been changed and the exp* values can use a fallback */ - copy_v3_fl3(process->mainb[a]->bb->vec[0], -expx, -expy, -expz); /* 0 */ - copy_v3_fl3(process->mainb[a]->bb->vec[1], +expx, -expy, -expz); /* 1 */ - copy_v3_fl3(process->mainb[a]->bb->vec[2], +expx, +expy, -expz); /* 2 */ - copy_v3_fl3(process->mainb[a]->bb->vec[3], -expx, +expy, -expz); /* 3 */ - copy_v3_fl3(process->mainb[a]->bb->vec[4], -expx, -expy, +expz); /* 4 */ - copy_v3_fl3(process->mainb[a]->bb->vec[5], +expx, -expy, +expz); /* 5 */ - copy_v3_fl3(process->mainb[a]->bb->vec[6], +expx, +expy, +expz); /* 6 */ - copy_v3_fl3(process->mainb[a]->bb->vec[7], -expx, +expy, +expz); /* 7 */ + copy_v3_fl3(new_ml->bb->vec[0], -expx, -expy, -expz); /* 0 */ + copy_v3_fl3(new_ml->bb->vec[1], +expx, -expy, -expz); /* 1 */ + copy_v3_fl3(new_ml->bb->vec[2], +expx, +expy, -expz); /* 2 */ + copy_v3_fl3(new_ml->bb->vec[3], -expx, +expy, -expz); /* 3 */ + copy_v3_fl3(new_ml->bb->vec[4], -expx, -expy, +expz); /* 4 */ + copy_v3_fl3(new_ml->bb->vec[5], +expx, -expy, +expz); /* 5 */ + copy_v3_fl3(new_ml->bb->vec[6], +expx, +expy, +expz); /* 6 */ + copy_v3_fl3(new_ml->bb->vec[7], -expx, +expy, +expz); /* 7 */ /* transformation of Metalem bb */ for (i = 0; i < 8; i++) - mul_m4_v3((float (*)[4])mat, process->mainb[a]->bb->vec[i]); + mul_m4_v3((float(*)[4])new_ml->mat, new_ml->bb->vec[i]); /* find max and min of transformed bb */ + INIT_MINMAX(tempmin, tempmax); for (i = 0; i < 8; i++) { - /* find maximums */ - if (process->mainb[a]->bb->vec[i][0] > max_x) max_x = process->mainb[a]->bb->vec[i][0]; - if (process->mainb[a]->bb->vec[i][1] > max_y) max_y = process->mainb[a]->bb->vec[i][1]; - if (process->mainb[a]->bb->vec[i][2] > max_z) max_z = process->mainb[a]->bb->vec[i][2]; - /* find minimums */ - if (process->mainb[a]->bb->vec[i][0] < min_x) min_x = process->mainb[a]->bb->vec[i][0]; - if (process->mainb[a]->bb->vec[i][1] < min_y) min_y = process->mainb[a]->bb->vec[i][1]; - if (process->mainb[a]->bb->vec[i][2] < min_z) min_z = process->mainb[a]->bb->vec[i][2]; + DO_MINMAX(new_ml->bb->vec[i], tempmin, tempmax); } - - /* create "new" bb, only point 0 and 6, which are - * necessary for octal tree filling */ - process->mainb[a]->bb->vec[0][0] = min_x - ml->rad; - process->mainb[a]->bb->vec[0][1] = min_y - ml->rad; - process->mainb[a]->bb->vec[0][2] = min_z - ml->rad; - - process->mainb[a]->bb->vec[6][0] = max_x + ml->rad; - process->mainb[a]->bb->vec[6][1] = max_y + ml->rad; - process->mainb[a]->bb->vec[6][2] = max_z + ml->rad; - - a++; - } - ml = ml->next; - } - } - } - } - - - /* totsize (= 'manhattan' radius) */ - totsize = 0.0; - for (a = 0; a < process->totelem; a++) { - - vec[0] = process->mainb[a]->x + process->mainb[a]->rad + process->mainb[a]->expx; - vec[1] = process->mainb[a]->y + process->mainb[a]->rad + process->mainb[a]->expy; - vec[2] = process->mainb[a]->z + process->mainb[a]->rad + process->mainb[a]->expz; - - calc_mballco(process->mainb[a], vec); - - size = fabsf(vec[0]); - if (size > totsize) totsize = size; - size = fabsf(vec[1]); - if (size > totsize) totsize = size; - size = fabsf(vec[2]); - if (size > totsize) totsize = size; - - vec[0] = process->mainb[a]->x - process->mainb[a]->rad; - vec[1] = process->mainb[a]->y - process->mainb[a]->rad; - vec[2] = process->mainb[a]->z - process->mainb[a]->rad; - - calc_mballco(process->mainb[a], vec); - - size = fabsf(vec[0]); - if (size > totsize) totsize = size; - size = fabsf(vec[1]); - if (size > totsize) totsize = size; - size = fabsf(vec[2]); - if (size > totsize) totsize = size; - } - - for (a = 0; a < process->totelem; a++) { - process->thresh += densfunc(process->mainb[a], 2.0f * totsize, 2.0f * totsize, 2.0f * totsize); - } - - return totsize; -} - -/* if MetaElem lies in node, then node includes MetaElem pointer (ml_p) - * pointing at MetaElem (ml) - */ -static void fill_metaball_octal_node(octal_node *node, MetaElem *ml, short i) -{ - ml_pointer *ml_p; - - ml_p = MEM_mallocN(sizeof(ml_pointer), "ml_pointer"); - ml_p->ml = ml; - BLI_addtail(&(node->nodes[i]->elems), ml_p); - node->count++; - - if ((ml->flag & MB_NEGATIVE) == 0) { - node->nodes[i]->pos++; - } - else { - node->nodes[i]->neg++; - } -} -/* Node is subdivided as is illustrated on the following figure: - * - * +------+------+ - * / / /| - * +------+------+ | - * / / /| + - * +------+------+ |/| - * | | | + | - * | | |/| + - * +------+------+ |/ - * | | | + - * | | |/ - * +------+------+ - * - */ -static void subdivide_metaball_octal_node(octal_node *node, float size_x, float size_y, float size_z, short depth) -{ - MetaElem *ml; - ml_pointer *ml_p; - float x, y, z; - int a, i; - - /* create new nodes */ - for (a = 0; a < 8; a++) { - node->nodes[a] = MEM_mallocN(sizeof(octal_node), "octal_node"); - for (i = 0; i < 8; i++) - node->nodes[a]->nodes[i] = NULL; - node->nodes[a]->parent = node; - BLI_listbase_clear(&node->nodes[a]->elems); - node->nodes[a]->count = 0; - node->nodes[a]->neg = 0; - node->nodes[a]->pos = 0; - } + /* set only point 0 and 6 - AABB of Metaelem */ + copy_v3_v3(new_ml->bb->vec[0], tempmin); + copy_v3_v3(new_ml->bb->vec[6], tempmax); - size_x /= 2; - size_y /= 2; - size_z /= 2; - - /* center of node */ - node->x = x = node->x_min + size_x; - node->y = y = node->y_min + size_y; - node->z = z = node->z_min + size_z; - - /* setting up of border points of new nodes */ - node->nodes[0]->x_min = node->x_min; - node->nodes[0]->y_min = node->y_min; - node->nodes[0]->z_min = node->z_min; - node->nodes[0]->x = node->nodes[0]->x_min + size_x / 2; - node->nodes[0]->y = node->nodes[0]->y_min + size_y / 2; - node->nodes[0]->z = node->nodes[0]->z_min + size_z / 2; - - node->nodes[1]->x_min = x; - node->nodes[1]->y_min = node->y_min; - node->nodes[1]->z_min = node->z_min; - node->nodes[1]->x = node->nodes[1]->x_min + size_x / 2; - node->nodes[1]->y = node->nodes[1]->y_min + size_y / 2; - node->nodes[1]->z = node->nodes[1]->z_min + size_z / 2; - - node->nodes[2]->x_min = x; - node->nodes[2]->y_min = y; - node->nodes[2]->z_min = node->z_min; - node->nodes[2]->x = node->nodes[2]->x_min + size_x / 2; - node->nodes[2]->y = node->nodes[2]->y_min + size_y / 2; - node->nodes[2]->z = node->nodes[2]->z_min + size_z / 2; - - node->nodes[3]->x_min = node->x_min; - node->nodes[3]->y_min = y; - node->nodes[3]->z_min = node->z_min; - node->nodes[3]->x = node->nodes[3]->x_min + size_x / 2; - node->nodes[3]->y = node->nodes[3]->y_min + size_y / 2; - node->nodes[3]->z = node->nodes[3]->z_min + size_z / 2; - - node->nodes[4]->x_min = node->x_min; - node->nodes[4]->y_min = node->y_min; - node->nodes[4]->z_min = z; - node->nodes[4]->x = node->nodes[4]->x_min + size_x / 2; - node->nodes[4]->y = node->nodes[4]->y_min + size_y / 2; - node->nodes[4]->z = node->nodes[4]->z_min + size_z / 2; - - node->nodes[5]->x_min = x; - node->nodes[5]->y_min = node->y_min; - node->nodes[5]->z_min = z; - node->nodes[5]->x = node->nodes[5]->x_min + size_x / 2; - node->nodes[5]->y = node->nodes[5]->y_min + size_y / 2; - node->nodes[5]->z = node->nodes[5]->z_min + size_z / 2; - - node->nodes[6]->x_min = x; - node->nodes[6]->y_min = y; - node->nodes[6]->z_min = z; - node->nodes[6]->x = node->nodes[6]->x_min + size_x / 2; - node->nodes[6]->y = node->nodes[6]->y_min + size_y / 2; - node->nodes[6]->z = node->nodes[6]->z_min + size_z / 2; - - node->nodes[7]->x_min = node->x_min; - node->nodes[7]->y_min = y; - node->nodes[7]->z_min = z; - node->nodes[7]->x = node->nodes[7]->x_min + size_x / 2; - node->nodes[7]->y = node->nodes[7]->y_min + size_y / 2; - node->nodes[7]->z = node->nodes[7]->z_min + size_z / 2; - - ml_p = node->elems.first; - - /* setting up references of MetaElems for new nodes */ - while (ml_p) { - ml = ml_p->ml; - if (ml->bb->vec[0][2] < z) { - if (ml->bb->vec[0][1] < y) { - /* vec[0][0] lies in first octant */ - if (ml->bb->vec[0][0] < x) { - /* ml belongs to the (0)1st node */ - fill_metaball_octal_node(node, ml, 0); - - /* ml belongs to the (3)4th node */ - if (ml->bb->vec[6][1] >= y) { - fill_metaball_octal_node(node, ml, 3); - - /* ml belongs to the (7)8th node */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 7); - } - } - - /* ml belongs to the (1)2nd node */ - if (ml->bb->vec[6][0] >= x) { - fill_metaball_octal_node(node, ml, 1); - - /* ml belongs to the (5)6th node */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 5); - } - } + /* add new_ml to mainb[] */ + if (process->totelem == process->mem) { + MetaElem **newelem; + process->mem = process->mem * 2 + 10; + newelem = MEM_mallocN(sizeof(MetaElem *) * process->mem, "metaballs"); - /* ml belongs to the (2)3th node */ - if ((ml->bb->vec[6][0] >= x) && (ml->bb->vec[6][1] >= y)) { - fill_metaball_octal_node(node, ml, 2); - - /* ml belong to the (6)7th node */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 6); + memcpy(newelem, process->mainb, sizeof(MetaElem *) * process->totelem); + if (process->mainb) MEM_freeN(process->mainb); + process->mainb = newelem; } - - } - - /* ml belongs to the (4)5th node too */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 4); - } - - - - } - /* vec[0][0] is in the (1)second octant */ - else { - /* ml belong to the (1)2nd node */ - fill_metaball_octal_node(node, ml, 1); - - /* ml belongs to the (2)3th node */ - if (ml->bb->vec[6][1] >= y) { - fill_metaball_octal_node(node, ml, 2); - - /* ml belongs to the (6)7th node */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 6); - } - - } - - /* ml belongs to the (5)6th node */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 5); - } - } - } - else { - /* vec[0][0] is in the (3)4th octant */ - if (ml->bb->vec[0][0] < x) { - /* ml belongs to the (3)4nd node */ - fill_metaball_octal_node(node, ml, 3); - - /* ml belongs to the (7)8th node */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 7); - } - - - /* ml belongs to the (2)3th node */ - if (ml->bb->vec[6][0] >= x) { - fill_metaball_octal_node(node, ml, 2); - - /* ml belongs to the (6)7th node */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 6); - } - } - } - - } - - /* vec[0][0] is in the (2)3th octant */ - if ((ml->bb->vec[0][0] >= x) && (ml->bb->vec[0][1] >= y)) { - /* ml belongs to the (2)3th node */ - fill_metaball_octal_node(node, ml, 2); - - /* ml belongs to the (6)7th node */ - if (ml->bb->vec[6][2] >= z) { - fill_metaball_octal_node(node, ml, 6); - } - } - } - else { - if (ml->bb->vec[0][1] < y) { - /* vec[0][0] lies in (4)5th octant */ - if (ml->bb->vec[0][0] < x) { - /* ml belongs to the (4)5th node */ - fill_metaball_octal_node(node, ml, 4); - - if (ml->bb->vec[6][0] >= x) { - fill_metaball_octal_node(node, ml, 5); - } - - if (ml->bb->vec[6][1] >= y) { - fill_metaball_octal_node(node, ml, 7); - } - - if ((ml->bb->vec[6][0] >= x) && (ml->bb->vec[6][1] >= y)) { - fill_metaball_octal_node(node, ml, 6); - } - } - /* vec[0][0] lies in (5)6th octant */ - else { - fill_metaball_octal_node(node, ml, 5); - - if (ml->bb->vec[6][1] >= y) { - fill_metaball_octal_node(node, ml, 6); - } - } - } - else { - /* vec[0][0] lies in (7)8th octant */ - if (ml->bb->vec[0][0] < x) { - fill_metaball_octal_node(node, ml, 7); - - if (ml->bb->vec[6][0] >= x) { - fill_metaball_octal_node(node, ml, 6); + process->mainb[process->totelem++] = new_ml; } + ml = ml->next; } - } - - /* vec[0][0] lies in (6)7th octant */ - if ((ml->bb->vec[0][0] >= x) && (ml->bb->vec[0][1] >= y)) { - fill_metaball_octal_node(node, ml, 6); - } - } - ml_p = ml_p->next; - } - - /* free references of MetaElems for curent node (it is not needed anymore) */ - BLI_freelistN(&node->elems); - - depth--; - - if (depth > 0) { - for (a = 0; a < 8; a++) { - if (node->nodes[a]->count > 0) /* if node is not empty, then it is subdivided */ - subdivide_metaball_octal_node(node->nodes[a], size_x, size_y, size_z, depth); - } - } -} - -/* free all octal nodes recursively */ -static void free_metaball_octal_node(octal_node *node) -{ - int a; - for (a = 0; a < 8; a++) { - if (node->nodes[a] != NULL) free_metaball_octal_node(node->nodes[a]); - } - BLI_freelistN(&node->elems); - MEM_freeN(node); -} - -/* If scene include more than one MetaElem, then octree is used */ -static void init_metaball_octal_tree(PROCESS *process, int depth) -{ - struct octal_node *node; - ml_pointer *ml_p; - float size[3]; - int a; - - process->metaball_tree = MEM_mallocN(sizeof(octal_tree), "metaball_octal_tree"); - process->metaball_tree->first = node = MEM_mallocN(sizeof(octal_node), "metaball_octal_node"); - /* maximal depth of octree */ - process->metaball_tree->depth = depth; - - process->metaball_tree->neg = node->neg = 0; - process->metaball_tree->pos = node->pos = 0; - - BLI_listbase_clear(&node->elems); - node->count = 0; - - for (a = 0; a < 8; a++) - node->nodes[a] = NULL; - - node->x_min = node->y_min = node->z_min = FLT_MAX; - node->x_max = node->y_max = node->z_max = -FLT_MAX; - - /* size of octal tree scene */ - for (a = 0; a < process->totelem; a++) { - if (process->mainb[a]->bb->vec[0][0] < node->x_min) node->x_min = process->mainb[a]->bb->vec[0][0]; - if (process->mainb[a]->bb->vec[0][1] < node->y_min) node->y_min = process->mainb[a]->bb->vec[0][1]; - if (process->mainb[a]->bb->vec[0][2] < node->z_min) node->z_min = process->mainb[a]->bb->vec[0][2]; - - if (process->mainb[a]->bb->vec[6][0] > node->x_max) node->x_max = process->mainb[a]->bb->vec[6][0]; - if (process->mainb[a]->bb->vec[6][1] > node->y_max) node->y_max = process->mainb[a]->bb->vec[6][1]; - if (process->mainb[a]->bb->vec[6][2] > node->z_max) node->z_max = process->mainb[a]->bb->vec[6][2]; - - ml_p = MEM_mallocN(sizeof(ml_pointer), "ml_pointer"); - ml_p->ml = process->mainb[a]; - BLI_addtail(&node->elems, ml_p); - - if ((process->mainb[a]->flag & MB_NEGATIVE) == 0) { - /* number of positive MetaElem in scene */ - process->metaball_tree->pos++; - } - else { - /* number of negative MetaElem in scene */ - process->metaball_tree->neg++; } } - /* size of first node */ - size[0] = node->x_max - node->x_min; - size[1] = node->y_max - node->y_min; - size[2] = node->z_max - node->z_min; - - /* first node is subdivided recursively */ - subdivide_metaball_octal_node(node, size[0], size[1], size[2], process->metaball_tree->depth); -} - -static void mball_count(EvaluationContext *eval_ctx, PROCESS *process, Scene *scene, Object *basis) -{ - Scene *sce_iter = scene; - Base *base; - Object *ob, *bob = basis; - MetaElem *ml = NULL; - int basisnr, obnr; - char basisname[MAX_ID_NAME], obname[MAX_ID_NAME]; - SceneBaseIter iter; - - BLI_split_name_num(basisname, &basisnr, basis->id.name + 2, '.'); - process->totelem = 0; - - BKE_scene_base_iter_next(eval_ctx, &iter, &sce_iter, 0, NULL, NULL); - while (BKE_scene_base_iter_next(eval_ctx, &iter, &sce_iter, 1, &base, &ob)) { - if (ob->type == OB_MBALL) { - if (ob == bob) { - MetaBall *mb = ob->data; - - /* if bob object is in edit mode, then dynamic list of all MetaElems - * is stored in editelems */ - if (mb->editelems) ml = mb->editelems->first; - /* if bob object is in object mode */ - else ml = mb->elems.first; - } - else { - BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); - - /* object ob has to be in same "group" ... it means, that it has to have - * same base of its name */ - if (STREQ(obname, basisname)) { - MetaBall *mb = ob->data; - - /* if object is in edit mode, then dynamic list of all MetaElems - * is stored in editelems */ - if (mb->editelems) ml = mb->editelems->first; - /* if bob object is in object mode */ - else ml = mb->elems.first; - } - } - - for ( ; ml; ml = ml->next) { - if (!(ml->flag & MB_HIDE)) { - process->totelem++; - } - } - } + /* compute AABB of all Metaelems */ + if (process->totelem > 0) { + copy_v3_v3(process->allbb.min, process->mainb[0]->bb->vec[0]); + copy_v3_v3(process->allbb.max, process->mainb[0]->bb->vec[6]); + for (i = 1; i < process->totelem; i++) + make_box_union(process->mainb[i]->bb, &process->allbb, &process->allbb); } } @@ -1875,102 +1263,66 @@ void BKE_mball_polygonize(EvaluationContext *eval_ctx, Scene *scene, Object *ob, { MetaBall *mb; DispList *dl; - int a, nr_cubes; - float *co, *no, totsize, width; + unsigned int a; PROCESS process = {0}; mb = ob->data; - mball_count(eval_ctx, &process, scene, ob); - - if (process.totelem == 0) return; - if ((eval_ctx->mode != DAG_EVAL_RENDER) && (mb->flag == MB_UPDATE_NEVER)) return; - if ((G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) && mb->flag == MB_UPDATE_FAST) return; - process.thresh = mb->thresh; - /* total number of MetaElems (totelem) is precomputed in find_basis_mball() function */ - process.mainb = MEM_mallocN(sizeof(void *) * process.totelem, "mainb"); - - /* initialize all mainb (MetaElems) */ - totsize = init_meta(eval_ctx, &process, scene, ob); - - /* if scene includes more than one MetaElem, then octal tree optimization is used */ - if ((process.totelem > 1) && (process.totelem <= 64)) init_metaball_octal_tree(&process, 1); - if ((process.totelem > 64) && (process.totelem <= 128)) init_metaball_octal_tree(&process, 2); - if ((process.totelem > 128) && (process.totelem <= 512)) init_metaball_octal_tree(&process, 3); - if ((process.totelem > 512) && (process.totelem <= 1024)) init_metaball_octal_tree(&process, 4); - if (process.totelem > 1024) init_metaball_octal_tree(&process, 5); - - /* don't polygonize metaballs with too high resolution (base mball to small) - * note: Eps was 0.0001f but this was giving problems for blood animation for durian, using 0.00001f */ - if (process.metaball_tree) { - if (ob->size[0] <= 0.00001f * (process.metaball_tree->first->x_max - process.metaball_tree->first->x_min) || - ob->size[1] <= 0.00001f * (process.metaball_tree->first->y_max - process.metaball_tree->first->y_min) || - ob->size[2] <= 0.00001f * (process.metaball_tree->first->z_max - process.metaball_tree->first->z_min)) - { - new_pgn_element(&process, -1); /* free values created by init_meta */ - - MEM_freeN(process.mainb); - - /* free tree */ - free_metaball_octal_node(process.metaball_tree->first); - MEM_freeN(process.metaball_tree); + if (process.thresh < 0.001f) process.converge_res = 16; + else if (process.thresh < 0.01f) process.converge_res = 8; + else if (process.thresh < 0.1f) process.converge_res = 4; + else process.converge_res = 2; - return; - } - } + if ((eval_ctx->mode != DAG_EVAL_RENDER) && (mb->flag == MB_UPDATE_NEVER)) return; + if ((G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) && mb->flag == MB_UPDATE_FAST) return; - /* width is size per polygonize cube */ if (eval_ctx->mode == DAG_EVAL_RENDER) { - width = mb->rendersize; + process.size = mb->rendersize; } else { - width = mb->wiresize; + process.size = mb->wiresize; if ((G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) && mb->flag == MB_UPDATE_HALFRES) { - width *= 2; + process.size *= 2.0f; } } - /* nr_cubes is just for safety, minimum is totsize */ - nr_cubes = (int)(0.5f + totsize / width); - - /* init process */ - process.function = metaball; - process.size = width; - process.bounds = nr_cubes; - process.cubes = NULL; - process.delta = width / (float)(RES * RES); - - polygonize(&process, mb); - - MEM_freeN(process.mainb); - - /* free octal tree */ - if (process.totelem > 1) { - free_metaball_octal_node(process.metaball_tree->first); - MEM_freeN(process.metaball_tree); - process.metaball_tree = NULL; - } - if (process.curindex) { - VERTEX *ptr = process.vertices.ptr; + process.delta = process.size * 0.001f; - dl = MEM_callocN(sizeof(DispList), "mbaldisp"); - BLI_addtail(dispbase, dl); - dl->type = DL_INDEX4; - dl->nr = process.vertices.count; - dl->parts = process.curindex; + process.pgn_elements = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "Metaball memarena"); - dl->index = process.indices; - process.indices = NULL; + /* initialize all mainb (MetaElems) */ + init_meta(eval_ctx, &process, scene, ob); - a = process.vertices.count; - dl->verts = co = MEM_mallocN(sizeof(float[3]) * a, "mballverts"); - dl->nors = no = MEM_mallocN(sizeof(float[3]) * a, "mballnors"); + if (process.totelem > 0) { + build_bvh_spatial(&process, &process.metaball_bvh, 0, process.totelem, &process.allbb); - for (a = 0; a < process.vertices.count; ptr++, a++, no += 3, co += 3) { - copy_v3_v3(co, ptr->co); - copy_v3_v3(no, ptr->no); + /* don't polygonize metaballs with too high resolution (base mball to small) + * note: Eps was 0.0001f but this was giving problems for blood animation for durian, using 0.00001f */ + if (ob->size[0] > 0.00001f * (process.allbb.max[0] - process.allbb.min[0]) || + ob->size[1] > 0.00001f * (process.allbb.max[1] - process.allbb.min[1]) || + ob->size[2] > 0.00001f * (process.allbb.max[2] - process.allbb.min[2])) + { + polygonize(&process); + + /* add resulting surface to displist */ + if (process.curindex) { + dl = MEM_callocN(sizeof(DispList), "mballdisp"); + BLI_addtail(dispbase, dl); + dl->type = DL_INDEX4; + dl->nr = (int)process.curvertex; + dl->parts = (int)process.curindex; + + dl->index = (int *)process.indices; + + for (a = 0; a < process.curvertex; a++) { + normalize_v3(process.no[a]); + } + + dl->verts = (float *)process.co; + dl->nors = (float *)process.no; + } } } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 8b943c2ddd8..43db4b75e65 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -463,7 +463,7 @@ void BKE_mesh_free(Mesh *me, int unlink) CustomData_free(&me->pdata, me->totpoly); if (me->adt) { - BKE_free_animdata(&me->id); + BKE_animdata_free(&me->id); me->adt = NULL; } diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index 7de993e1099..f758bcdf2f5 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -1475,8 +1475,9 @@ void BKE_mesh_calc_edges(Mesh *mesh, bool update, const bool select) int j, v_prev = (l + (mp->totloop - 1))->v; for (j = 0; j < mp->totloop; j++, l++) { if (v_prev != l->v) { - if (!BLI_edgehash_haskey(eh, v_prev, l->v)) { - BLI_edgehash_insert(eh, v_prev, l->v, NULL); + void **val_p; + if (!BLI_edgehash_ensure_p(eh, v_prev, l->v, &val_p)) { + *val_p = NULL; } } v_prev = l->v; diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 73f75f4f96d..1aeb7bed86c 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1168,7 +1168,7 @@ static void free_buffers(MovieClip *clip) clip->anim = NULL; } - BKE_free_animdata((ID *) clip); + BKE_animdata_free((ID *) clip); } void BKE_movieclip_clear_cache(MovieClip *clip) diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 9a04312aaab..c87f378d5ae 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -1584,6 +1584,12 @@ bool BKE_nla_action_stash(AnimData *adt) nlt = add_nlatrack(adt, prev_track); BLI_assert(nlt != NULL); + /* we need to ensure that if there wasn't any previous instance, it must go to tbe bottom of the stack */ + if (prev_track == NULL) { + BLI_remlink(&adt->nla_tracks, nlt); + BLI_addhead(&adt->nla_tracks, nlt); + } + BLI_strncpy(nlt->name, STASH_TRACK_NAME, sizeof(nlt->name)); BLI_uniquename(&adt->nla_tracks, nlt, STASH_TRACK_NAME, '.', offsetof(NlaTrack, name), sizeof(nlt->name)); diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 10411d7bb4d..073fc1b3741 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -1733,7 +1733,7 @@ void ntreeFreeTree_ex(bNodeTree *ntree, const bool do_id_user) /* unregister associated RNA types */ ntreeInterfaceTypeFree(ntree); - BKE_free_animdata((ID *)ntree); + BKE_animdata_free((ID *)ntree); id_us_min((ID *)ntree->gpd); @@ -3475,6 +3475,7 @@ static void registerCompositNodes(void) register_node_type_cmp_bokehimage(); register_node_type_cmp_bokehblur(); register_node_type_cmp_switch(); + register_node_type_cmp_switch_view(); register_node_type_cmp_pixelate(); register_node_type_cmp_mask(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index f3acf627d19..4845f669f75 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -417,7 +417,7 @@ void BKE_object_free_ex(Object *ob, bool do_id_user) ob->iuser = NULL; if (ob->bb) MEM_freeN(ob->bb); ob->bb = NULL; - if (ob->adt) BKE_free_animdata((ID *)ob); + if (ob->adt) BKE_animdata_free((ID *)ob); if (ob->poselib) ob->poselib->id.us--; if (ob->gpd) ((ID *)ob->gpd)->us--; if (ob->defbase.first) @@ -445,7 +445,7 @@ void BKE_object_free_ex(Object *ob, bool do_id_user) if (ob->bsoft) bsbFree(ob->bsoft); if (ob->gpulamp.first) GPU_lamp_free(ob); - BKE_free_sculptsession(ob); + BKE_sculptsession_free(ob); if (ob->pc_ids.first) BLI_freelistN(&ob->pc_ids); @@ -1745,7 +1745,7 @@ void BKE_object_copy_proxy_drivers(Object *ob, Object *target) /* add new animdata block */ if (!ob->adt) - ob->adt = BKE_id_add_animdata(&ob->id); + ob->adt = BKE_animdata_add_id(&ob->id); /* make a copy of all the drivers (for now), then correct any links that need fixing */ free_fcurves(&ob->adt->drivers); @@ -3297,7 +3297,7 @@ void BKE_object_sculpt_modifiers_changed(Object *ob) ss->pbvh = NULL; } - BKE_free_sculptsession_deformMats(ob->sculpt); + BKE_sculptsession_free_deformMats(ob->sculpt); } else { PBVHNode **nodes; @@ -3784,7 +3784,7 @@ void BKE_object_relink(Object *ob) modifiers_foreachIDLink(ob, copy_object__forwardModifierLinks, NULL); if (ob->adt) - BKE_relink_animdata(ob->adt); + BKE_animdata_relink(ob->adt); if (ob->rigidbody_constraint) BKE_rigidbody_relink_constraint(ob->rigidbody_constraint); diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index 9dccda9039a..7ec0a8de96c 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -1722,7 +1722,7 @@ DupliObjectData *BKE_dupli_cache_iter_get(DupliCacheIterator *iter) /* ------------------------------------------------------------------------- */ -DupliApplyData *duplilist_apply(Object *ob, ListBase *duplilist) +DupliApplyData *duplilist_apply(Object *ob, Scene *scene, ListBase *duplilist) { DupliApplyData *apply_data = NULL; int num_objects = BLI_listbase_count(duplilist); @@ -1738,6 +1738,13 @@ DupliApplyData *duplilist_apply(Object *ob, ListBase *duplilist) for (dob = duplilist->first, i = 0; dob; dob = dob->next, ++i) { /* copy obmat from duplis */ copy_m4_m4(apply_data->extra[i].obmat, dob->ob->obmat); + + /* make sure derivedmesh is calculated once, before drawing */ + if (scene && !(dob->ob->transflag & OB_DUPLICALCDERIVED) && dob->ob->type == OB_MESH) { + mesh_get_derived_final(scene, dob->ob, scene->customdata_mask); + dob->ob->transflag |= OB_DUPLICALCDERIVED; + } + copy_m4_m4(dob->ob->obmat, dob->mat); /* copy layers from the main duplicator object */ @@ -1758,6 +1765,7 @@ void duplilist_restore(ListBase *duplilist, DupliApplyData *apply_data) */ for (dob = duplilist->last, i = apply_data->num_objects - 1; dob; dob = dob->prev, --i) { copy_m4_m4(dob->ob->obmat, apply_data->extra[i].obmat); + dob->ob->transflag &= ~OB_DUPLICALCDERIVED; dob->ob->lay = apply_data->extra[i].lay; } diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 12e82d3a34f..b89b5ab4bbc 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -494,7 +494,7 @@ void BKE_ocean_eval_ij(struct Ocean *oc, struct OceanResult *ocr, int i, int j) BLI_rw_mutex_unlock(&oc->oceanmutex); } -void BKE_simulate_ocean(struct Ocean *o, float t, float scale, float chop_amount) +void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount) { int i, j; @@ -732,7 +732,7 @@ static void set_height_normalize_factor(struct Ocean *oc) oc->normalize_factor = 1.0; - BKE_simulate_ocean(oc, 0.0, 1.0, 0); + BKE_ocean_simulate(oc, 0.0, 1.0, 0); BLI_rw_mutex_lock(&oc->oceanmutex, THREAD_LOCK_READ); @@ -754,7 +754,7 @@ static void set_height_normalize_factor(struct Ocean *oc) oc->normalize_factor = res; } -struct Ocean *BKE_add_ocean(void) +struct Ocean *BKE_ocean_add(void) { Ocean *oc = MEM_callocN(sizeof(Ocean), "ocean sim data"); @@ -763,7 +763,7 @@ struct Ocean *BKE_add_ocean(void) return oc; } -void BKE_init_ocean(struct Ocean *o, int M, int N, float Lx, float Lz, float V, float l, float A, float w, float damp, +void BKE_ocean_init(struct Ocean *o, int M, int N, float Lx, float Lz, float V, float l, float A, float w, float damp, float alignment, float depth, float time, short do_height_field, short do_chop, short do_normals, short do_jacobian, int seed) { @@ -900,7 +900,7 @@ void BKE_init_ocean(struct Ocean *o, int M, int N, float Lx, float Lz, float V, BLI_rng_free(rng); } -void BKE_free_ocean_data(struct Ocean *oc) +void BKE_ocean_free_data(struct Ocean *oc) { if (!oc) return; @@ -962,11 +962,11 @@ void BKE_free_ocean_data(struct Ocean *oc) BLI_rw_mutex_unlock(&oc->oceanmutex); } -void BKE_free_ocean(struct Ocean *oc) +void BKE_ocean_free(struct Ocean *oc) { if (!oc) return; - BKE_free_ocean_data(oc); + BKE_ocean_free_data(oc); BLI_rw_mutex_end(&oc->oceanmutex); MEM_freeN(oc); @@ -1002,7 +1002,7 @@ static void cache_filename(char *string, const char *path, const char *relbase, BLI_join_dirfile(cachepath, sizeof(cachepath), path, fname); - BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true); + BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true, ""); } /* silly functions but useful to inline when the args do a lot of indirections */ @@ -1021,7 +1021,7 @@ MINLINE void value_to_rgba_unit_alpha(float r_rgba[4], const float value) r_rgba[3] = 1.0f; } -void BKE_free_ocean_cache(struct OceanCache *och) +void BKE_ocean_free_cache(struct OceanCache *och) { int i, f = 0; @@ -1111,7 +1111,7 @@ void BKE_ocean_cache_eval_ij(struct OceanCache *och, struct OceanResult *ocr, in } } -struct OceanCache *BKE_init_ocean_cache(const char *bakepath, const char *relbase, int start, int end, float wave_scale, +struct OceanCache *BKE_ocean_init_cache(const char *bakepath, const char *relbase, int start, int end, float wave_scale, float chop_amount, float foam_coverage, float foam_fade, int resolution) { OceanCache *och = MEM_callocN(sizeof(OceanCache), "ocean cache data"); @@ -1138,7 +1138,7 @@ struct OceanCache *BKE_init_ocean_cache(const char *bakepath, const char *relbas return och; } -void BKE_simulate_ocean_cache(struct OceanCache *och, int frame) +void BKE_ocean_simulate_cache(struct OceanCache *och, int frame) { char string[FILE_MAX]; int f = frame; @@ -1182,7 +1182,7 @@ void BKE_simulate_ocean_cache(struct OceanCache *och, int frame) } -void BKE_bake_ocean(struct Ocean *o, struct OceanCache *och, void (*update_cb)(void *, float progress, int *cancel), +void BKE_ocean_bake(struct Ocean *o, struct OceanCache *och, void (*update_cb)(void *, float progress, int *cancel), void *update_cb_data) { /* note: some of these values remain uninitialized unless certain options @@ -1221,7 +1221,7 @@ void BKE_bake_ocean(struct Ocean *o, struct OceanCache *och, void (*update_cb)(v ibuf_disp = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat); ibuf_normal = IMB_allocImBuf(res_x, res_y, 32, IB_rectfloat); - BKE_simulate_ocean(o, och->time[i], och->wave_scale, och->chop_amount); + BKE_ocean_simulate(o, och->time[i], och->wave_scale, och->chop_amount); /* add new foam */ for (y = 0; y < res_y; y++) { @@ -1371,29 +1371,29 @@ void BKE_ocean_eval_ij(struct Ocean *UNUSED(oc), struct OceanResult *UNUSED(ocr) { } -void BKE_simulate_ocean(struct Ocean *UNUSED(o), float UNUSED(t), float UNUSED(scale), float UNUSED(chop_amount)) +void BKE_ocean_simulate(struct Ocean *UNUSED(o), float UNUSED(t), float UNUSED(scale), float UNUSED(chop_amount)) { } -struct Ocean *BKE_add_ocean(void) +struct Ocean *BKE_ocean_add(void) { Ocean *oc = MEM_callocN(sizeof(Ocean), "ocean sim data"); return oc; } -void BKE_init_ocean(struct Ocean *UNUSED(o), int UNUSED(M), int UNUSED(N), float UNUSED(Lx), float UNUSED(Lz), +void BKE_ocean_init(struct Ocean *UNUSED(o), int UNUSED(M), int UNUSED(N), float UNUSED(Lx), float UNUSED(Lz), float UNUSED(V), float UNUSED(l), float UNUSED(A), float UNUSED(w), float UNUSED(damp), float UNUSED(alignment), float UNUSED(depth), float UNUSED(time), short UNUSED(do_height_field), short UNUSED(do_chop), short UNUSED(do_normals), short UNUSED(do_jacobian), int UNUSED(seed)) { } -void BKE_free_ocean_data(struct Ocean *UNUSED(oc)) +void BKE_ocean_free_data(struct Ocean *UNUSED(oc)) { } -void BKE_free_ocean(struct Ocean *oc) +void BKE_ocean_free(struct Ocean *oc) { if (!oc) return; MEM_freeN(oc); @@ -1403,7 +1403,7 @@ void BKE_free_ocean(struct Ocean *oc) /* ********* Baking/Caching ********* */ -void BKE_free_ocean_cache(struct OceanCache *och) +void BKE_ocean_free_cache(struct OceanCache *och) { if (!och) return; @@ -1420,7 +1420,7 @@ void BKE_ocean_cache_eval_ij(struct OceanCache *UNUSED(och), struct OceanResult { } -OceanCache *BKE_init_ocean_cache(const char *UNUSED(bakepath), const char *UNUSED(relbase), int UNUSED(start), +OceanCache *BKE_ocean_init_cache(const char *UNUSED(bakepath), const char *UNUSED(relbase), int UNUSED(start), int UNUSED(end), float UNUSED(wave_scale), float UNUSED(chop_amount), float UNUSED(foam_coverage), float UNUSED(foam_fade), int UNUSED(resolution)) { @@ -1429,11 +1429,11 @@ OceanCache *BKE_init_ocean_cache(const char *UNUSED(bakepath), const char *UNUSE return och; } -void BKE_simulate_ocean_cache(struct OceanCache *UNUSED(och), int UNUSED(frame)) +void BKE_ocean_simulate_cache(struct OceanCache *UNUSED(och), int UNUSED(frame)) { } -void BKE_bake_ocean(struct Ocean *UNUSED(o), struct OceanCache *UNUSED(och), +void BKE_ocean_bake(struct Ocean *UNUSED(o), struct OceanCache *UNUSED(och), void (*update_cb)(void *, float progress, int *cancel), void *UNUSED(update_cb_data)) { /* unused */ diff --git a/source/blender/blenkernel/intern/treehash.c b/source/blender/blenkernel/intern/outliner_treehash.c index 866502c4ae1..21664bfd285 100644 --- a/source/blender/blenkernel/intern/treehash.c +++ b/source/blender/blenkernel/intern/outliner_treehash.c @@ -20,7 +20,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file treehash.c +/** \file outliner_treehash.c * \ingroup bke * * Tree hash for the outliner space. @@ -28,7 +28,7 @@ #include <stdlib.h> -#include "BKE_treehash.h" +#include "BKE_outliner_treehash.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -38,8 +38,7 @@ #include "MEM_guardedalloc.h" -typedef struct TseGroup -{ +typedef struct TseGroup { TreeStoreElem **elems; int size; int allocated; @@ -104,11 +103,11 @@ static void fill_treehash(void *treehash, BLI_mempool *treestore) BLI_mempool_iter iter; BLI_mempool_iternew(treestore, &iter); while ((tselem = BLI_mempool_iterstep(&iter))) { - BKE_treehash_add_element(treehash, tselem); + BKE_outliner_treehash_add_element(treehash, tselem); } } -void *BKE_treehash_create_from_treestore(BLI_mempool *treestore) +void *BKE_outliner_treehash_create_from_treestore(BLI_mempool *treestore) { GHash *treehash = BLI_ghash_new_ex(tse_hash, tse_cmp, "treehash", BLI_mempool_count(treestore)); fill_treehash(treehash, treestore); @@ -120,24 +119,26 @@ static void free_treehash_group(void *key) tse_group_free(key); } -void *BKE_treehash_rebuild_from_treestore(void *treehash, BLI_mempool *treestore) +void *BKE_outliner_treehash_rebuild_from_treestore(void *treehash, BLI_mempool *treestore) { BLI_ghash_clear_ex(treehash, NULL, free_treehash_group, BLI_mempool_count(treestore)); fill_treehash(treehash, treestore); return treehash; } -void BKE_treehash_add_element(void *treehash, TreeStoreElem *elem) +void BKE_outliner_treehash_add_element(void *treehash, TreeStoreElem *elem) { - TseGroup *group = BLI_ghash_lookup(treehash, elem); - if (!group) { - group = tse_group_create(); - BLI_ghash_insert(treehash, elem, group); + TseGroup *group; + void **val_p; + + if (!BLI_ghash_ensure_p(treehash, elem, &val_p)) { + *val_p = tse_group_create(); } + group = *val_p; tse_group_add(group, elem); } -static TseGroup *BKE_treehash_lookup_group(GHash *th, short type, short nr, struct ID *id) +static TseGroup *BKE_outliner_treehash_lookup_group(GHash *th, short type, short nr, struct ID *id) { TreeStoreElem tse_template; tse_template.type = type; @@ -146,9 +147,9 @@ static TseGroup *BKE_treehash_lookup_group(GHash *th, short type, short nr, stru return BLI_ghash_lookup(th, &tse_template); } -TreeStoreElem *BKE_treehash_lookup_unused(void *treehash, short type, short nr, struct ID *id) +TreeStoreElem *BKE_outliner_treehash_lookup_unused(void *treehash, short type, short nr, struct ID *id) { - TseGroup *group = BKE_treehash_lookup_group(treehash, type, nr, id); + TseGroup *group = BKE_outliner_treehash_lookup_group(treehash, type, nr, id); if (group) { int i; for (i = 0; i < group->size; i++) { @@ -160,13 +161,13 @@ TreeStoreElem *BKE_treehash_lookup_unused(void *treehash, short type, short nr, return NULL; } -TreeStoreElem *BKE_treehash_lookup_any(void *treehash, short type, short nr, struct ID *id) +TreeStoreElem *BKE_outliner_treehash_lookup_any(void *treehash, short type, short nr, struct ID *id) { - TseGroup *group = BKE_treehash_lookup_group(treehash, type, nr, id); + TseGroup *group = BKE_outliner_treehash_lookup_group(treehash, type, nr, id); return group ? group->elems[0] : NULL; } -void BKE_treehash_free(void *treehash) +void BKE_outliner_treehash_free(void *treehash) { BLI_ghash_free(treehash, NULL, free_treehash_group); } diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 9b429b356b4..61e39d0ad47 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -128,8 +128,8 @@ int countPackedFiles(Main *bmain) /* let's check if there are packed files... */ for (ima = bmain->image.first; ima; ima = ima->id.next) - if (ima->packedfile) - count++; + if (BKE_image_has_packedfile(ima)) + count ++; for (vf = bmain->vfont.first; vf; vf = vf->id.next) if (vf->packedfile) @@ -232,9 +232,9 @@ void packAll(Main *bmain, ReportList *reports) int tot = 0; for (ima = bmain->image.first; ima; ima = ima->id.next) { - if (ima->packedfile == NULL && ima->id.lib == NULL) { + if (BKE_image_has_packedfile(ima) == false && ima->id.lib == NULL) { if (ima->source == IMA_SRC_FILE) { - ima->packedfile = newPackedFile(reports, ima->name, ID_BLEND_PATH(bmain, &ima->id)); + BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id)); tot ++; } else if (BKE_image_is_animated(ima)) { @@ -564,23 +564,47 @@ int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how) int unpackImage(ReportList *reports, Image *ima, int how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; int ret_value = RET_ERROR; - + if (ima != NULL && ima->name[0]) { - unpack_generate_paths(ima->name, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname)); - newname = unpackFile(reports, absname, localname, ima->packedfile, how); - if (newname != NULL) { - ret_value = RET_OK; - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - BLI_strncpy(ima->name, newname, sizeof(ima->name)); - MEM_freeN(newname); - BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); + while (ima->packedfiles.last) { + char localname[FILE_MAX], absname[FILE_MAX]; + char *newname; + ImagePackedFile *imapf = ima->packedfiles.last; + + unpack_generate_paths(imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname)); + newname = unpackFile(reports, absname, localname, imapf->packedfile, how); + + if (newname != NULL) { + ImageView *iv; + + ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK; + freePackedFile(imapf->packedfile); + imapf->packedfile = NULL; + + /* update the new corresponding view filepath */ + iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath)); + if (iv) { + BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath)); + } + + /* keep the new name in the image for non-pack specific reasons */ + BLI_strncpy(ima->name, newname, sizeof(imapf->filepath)); + MEM_freeN(newname); + } + else { + ret_value = RET_ERROR; + } + + BLI_remlink(&ima->packedfiles, imapf); + MEM_freeN(imapf); } } - + + if (ret_value == RET_OK) { + BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); + } + return(ret_value); } @@ -636,7 +660,7 @@ void unpackAll(Main *bmain, ReportList *reports, int how) bSound *sound; for (ima = bmain->image.first; ima; ima = ima->id.next) - if (ima->packedfile) + if (BKE_image_has_packedfile(ima)) unpackImage(reports, ima, how); for (vf = bmain->vfont.first; vf; vf = vf->id.next) @@ -655,7 +679,7 @@ bool BKE_pack_check(ID *id) case ID_IM: { Image *ima = (Image *)id; - return ima->packedfile != NULL; + return BKE_image_has_packedfile(ima); } case ID_VF: { @@ -683,7 +707,7 @@ void BKE_unpack_id(Main *bmain, ID *id, ReportList *reports, int how) case ID_IM: { Image *ima = (Image *)id; - if (ima->packedfile) { + if (BKE_image_has_packedfile(ima)) { unpackImage(reports, ima, how); } break; diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 07b3893c545..a5b608703d1 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -559,7 +559,7 @@ void paint_calculate_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, cons } } -void BKE_free_sculptsession_deformMats(SculptSession *ss) +void BKE_sculptsession_free_deformMats(SculptSession *ss) { if (ss->orig_cos) MEM_freeN(ss->orig_cos); if (ss->deform_cos) MEM_freeN(ss->deform_cos); @@ -626,7 +626,7 @@ void BKE_sculptsession_bm_to_me_for_render(Object *object) } } -void BKE_free_sculptsession(Object *ob) +void BKE_sculptsession_free(Object *ob) { if (ob && ob->sculpt) { SculptSession *ss = ob->sculpt; @@ -804,7 +804,7 @@ void BKE_sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob, if (!ss->orig_cos) { int a; - BKE_free_sculptsession_deformMats(ss); + BKE_sculptsession_free_deformMats(ss); ss->orig_cos = (ss->kb) ? BKE_keyblock_convert_to_vertcos(ob, ss->kb) : BKE_mesh_vertexCos_get(me, NULL); @@ -817,7 +817,7 @@ void BKE_sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob, } } else { - BKE_free_sculptsession_deformMats(ss); + BKE_sculptsession_free_deformMats(ss); } if (ss->kb != NULL && ss->deform_cos == NULL) { diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 4d78ebeebeb..dab2714b49c 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -286,7 +286,7 @@ bool psys_check_enabled(Object *ob, ParticleSystem *psys) return 0; psmd = psys_get_modifier(ob, psys); - if (psys->renderdata || G.is_rendering) { + if (psys->renderdata) { if (!(psmd->modifier.mode & eModifierMode_Render)) return 0; } @@ -407,7 +407,7 @@ void BKE_particlesettings_free(ParticleSettings *part) { MTex *mtex; int a; - BKE_free_animdata(&part->id); + BKE_animdata_free(&part->id); if (part->clumpcurve) curvemapping_free(part->clumpcurve); @@ -1611,7 +1611,7 @@ void psys_find_parents(ParticleSimulationData *sim) int from = PART_FROM_FACE; totparent = (int)(totchild * part->parents * 0.3f); - if ((sim->psys->renderdata || G.is_rendering) && part->child_nbr && part->ren_child_nbr) + if (sim->psys->renderdata && part->child_nbr && part->ren_child_nbr) totparent *= (float)part->child_nbr / (float)part->ren_child_nbr; /* hard limit, workaround for it being ignored above */ @@ -1665,7 +1665,7 @@ static bool psys_thread_context_init_path(ParticleThreadContext *ctx, ParticleSi if (totchild && part->childtype == PART_CHILD_FACES) { totparent = (int)(totchild * part->parents * 0.3f); - if ((psys->renderdata || G.is_rendering) && part->child_nbr && part->ren_child_nbr) + if (psys->renderdata && part->child_nbr && part->ren_child_nbr) totparent *= (float)part->child_nbr / (float)part->ren_child_nbr; /* part->parents could still be 0 so we can't test with totparent */ @@ -1956,6 +1956,7 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp if (i >= ctx->totparent) { pa = &psys->particles[cpa->parent]; /* this is now threadsafe, virtual parents are calculated before rest of children */ + BLI_assert(cpa->parent < psys->totchildcache); par = cache[cpa->parent]; } } @@ -2003,6 +2004,7 @@ static void exec_child_path_cache(TaskPool *UNUSED(pool), void *taskdata, int UN cpa = psys->child + task->begin; for (i = task->begin; i < task->end; ++i, ++cpa) { + BLI_assert(i < psys->totchildcache); psys_thread_create_path(task, cpa, cache[i], i); } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 9f1e7e002ac..1ea22683ad8 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -93,6 +93,7 @@ #include "PIL_time.h" #include "IMB_colormanagement.h" +#include "IMB_imbuf.h" #include "bmesh.h" @@ -158,14 +159,16 @@ Scene *BKE_scene_copy(Scene *sce, int type) Base *base, *obase; if (type == SCE_COPY_EMPTY) { - ListBase lb; + ListBase rl, rv; /* XXX. main should become an arg */ scen = BKE_scene_add(G.main, sce->id.name + 2); - lb = scen->r.layers; + rl = scen->r.layers; + rv = scen->r.views; scen->r = sce->r; - scen->r.layers = lb; + scen->r.layers = rl; scen->r.actlay = 0; + scen->r.views = rv; scen->unit = sce->unit; scen->physics_settings = sce->physics_settings; scen->gm = sce->gm; @@ -198,6 +201,7 @@ Scene *BKE_scene_copy(Scene *sce, int type) BLI_duplicatelist(&(scen->markers), &(sce->markers)); BLI_duplicatelist(&(scen->transform_spaces), &(sce->transform_spaces)); BLI_duplicatelist(&(scen->r.layers), &(sce->r.layers)); + BLI_duplicatelist(&(scen->r.views), &(sce->r.views)); BKE_keyingsets_copy(&(scen->keyingsets), &(sce->keyingsets)); if (sce->nodetree) { @@ -225,7 +229,7 @@ Scene *BKE_scene_copy(Scene *sce, int type) sizeof(scen->sequencer_colorspace_settings.name)); /* copy action and remove animation used by sequencer */ - BKE_copy_animdata_id_action(&scen->id); + BKE_animdata_copy_id_action(&scen->id); if (type != SCE_COPY_FULL) remove_sequencer_fcurves(scen); @@ -301,7 +305,7 @@ Scene *BKE_scene_copy(Scene *sce, int type) if (scen->world) { id_us_plus((ID *)scen->world); scen->world = BKE_world_copy(scen->world); - BKE_copy_animdata_id_action((ID *)scen->world); + BKE_animdata_copy_id_action((ID *)scen->world); } if (sce->ed) { @@ -362,7 +366,7 @@ void BKE_scene_free(Scene *sce) BLI_freelistN(&sce->base); BKE_sequencer_editing_free(sce); - BKE_free_animdata((ID *)sce); + BKE_animdata_free((ID *)sce); BKE_keyingsets_free(&sce->keyingsets); if (sce->rigidbody_world) @@ -391,6 +395,7 @@ void BKE_scene_free(Scene *sce) BLI_freelistN(&sce->markers); BLI_freelistN(&sce->transform_spaces); BLI_freelistN(&sce->r.layers); + BLI_freelistN(&sce->r.views); if (sce->toolsettings) { if (sce->toolsettings->vpaint) { @@ -438,6 +443,7 @@ Scene *BKE_scene_add(Main *bmain, const char *name) ParticleEditSettings *pset; int a; const char *colorspace_name; + SceneRenderView *srv; sce = BKE_libblock_alloc(bmain, ID_SCE, name); sce->lay = sce->layact = 1; @@ -629,7 +635,16 @@ Scene *BKE_scene_add(Main *bmain, const char *name) /* note; in header_info.c the scene copy happens..., if you add more to renderdata it has to be checked there */ BKE_scene_add_render_layer(sce, NULL); - + + /* multiview - stereo */ + BKE_scene_add_render_view(sce, STEREO_LEFT_NAME); + srv = sce->r.views.first; + BLI_strncpy(srv->suffix, STEREO_LEFT_SUFFIX, sizeof(srv->suffix)); + + BKE_scene_add_render_view(sce, STEREO_RIGHT_NAME); + srv = sce->r.views.last; + BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix)); + /* game data */ sce->gm.stereoflag = STEREO_NOSTEREO; sce->gm.stereomode = STEREO_ANAGLYPH; @@ -702,6 +717,19 @@ Scene *BKE_scene_add(Main *bmain, const char *name) return sce; } +Base *BKE_scene_base_find_by_name(struct Scene *scene, const char *name) +{ + Base *base; + + for (base = scene->base.first; base; base = base->next) { + if (STREQ(base->object->id.name + 2, name)) { + break; + } + } + + return base; +} + Base *BKE_scene_base_find(Scene *scene, Object *ob) { return BLI_findptr(&scene->base, ob, offsetof(Base, object)); @@ -1896,6 +1924,42 @@ bool BKE_scene_remove_render_layer(Main *bmain, Scene *scene, SceneRenderLayer * return true; } +/* return default view */ +SceneRenderView *BKE_scene_add_render_view(Scene *sce, const char *name) +{ + SceneRenderView *srv; + + if (!name) + name = DATA_("RenderView"); + + srv = MEM_callocN(sizeof(SceneRenderView), "new render view"); + BLI_strncpy(srv->name, name, sizeof(srv->name)); + BLI_uniquename(&sce->r.views, srv, DATA_("RenderView"), '.', offsetof(SceneRenderView, name), sizeof(srv->name)); + BLI_addtail(&sce->r.views, srv); + + return srv; +} + +bool BKE_scene_remove_render_view(Scene *scene, SceneRenderView *srv) +{ + const int act = BLI_findindex(&scene->r.views, srv); + + if (act == -1) { + return false; + } + else if (scene->r.views.first == scene->r.views.last) { + /* ensure 1 view is kept */ + return false; + } + + BLI_remlink(&scene->r.views, srv); + MEM_freeN(srv); + + scene->r.actview = 0; + + return true; +} + /* render simplification */ int get_render_subsurf_level(const RenderData *r, int lvl) @@ -2066,3 +2130,281 @@ double BKE_scene_unit_scale(const UnitSettings *unit, const int unit_type, doubl return value; } } + +/******************** multiview *************************/ + +size_t BKE_scene_multiview_num_views_get(const RenderData *rd) +{ + SceneRenderView *srv; + size_t totviews = 0; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return 1; + + if (rd->views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + if (BLI_findstring(&rd->views, STEREO_LEFT_NAME, offsetof(SceneRenderView, name))) { + totviews++; + } + + if (BLI_findstring(&rd->views, STEREO_RIGHT_NAME, offsetof(SceneRenderView, name))) { + totviews++; + } + } + else { + for (srv = rd->views.first; srv; srv = srv->next) { + if ((srv->viewflag & SCE_VIEW_DISABLE) == 0) { + totviews++; + } + } + } + return totviews; +} + +bool BKE_scene_multiview_is_stereo3d(const RenderData *rd) +{ + SceneRenderView *srv[2]; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return false; + + srv[0] = (SceneRenderView *)BLI_findstring(&rd->views, STEREO_LEFT_NAME, offsetof(SceneRenderView, name)); + srv[1] = (SceneRenderView *)BLI_findstring(&rd->views, STEREO_RIGHT_NAME, offsetof(SceneRenderView, name)); + + return (srv[0] && ((srv[0]->viewflag & SCE_VIEW_DISABLE) == 0) && + srv[1] && ((srv[1]->viewflag & SCE_VIEW_DISABLE) == 0)); +} + +/* return whether to render this SceneRenderView */ +bool BKE_scene_multiview_is_render_view_active(const RenderData *rd, const SceneRenderView *srv) +{ + if (srv == NULL) + return false; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return false; + + if ((srv->viewflag & SCE_VIEW_DISABLE)) + return false; + + if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) + return true; + + /* SCE_VIEWS_SETUP_BASIC */ + if (STREQ(srv->name, STEREO_LEFT_NAME) || + STREQ(srv->name, STEREO_RIGHT_NAME)) + { + return true; + } + + return false; +} + +/* return true if viewname is the first or if the name is NULL or not found */ +bool BKE_scene_multiview_is_render_view_first(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return true; + + if ((!viewname) || (!viewname[0])) + return true; + + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + return STREQ(viewname, srv->name); + } + } + + return true; +} + +/* return true if viewname is the last or if the name is NULL or not found */ +bool BKE_scene_multiview_is_render_view_last(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return true; + + if ((!viewname) || (!viewname[0])) + return true; + + for (srv = rd->views.last; srv; srv = srv->prev) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + return STREQ(viewname, srv->name); + } + } + + return true; +} + +SceneRenderView *BKE_scene_multiview_render_view_findindex(const RenderData *rd, const int view_id) +{ + SceneRenderView *srv; + size_t nr; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return NULL; + + nr = 0; + for (srv = rd->views.first, nr = 0; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (nr++ == view_id) + return srv; + } + } + return srv; +} + +const char *BKE_scene_multiview_render_view_name_get(const RenderData *rd, const int view_id) +{ + SceneRenderView *srv = BKE_scene_multiview_render_view_findindex(rd, view_id); + + if (srv) + return srv->name; + else + return ""; +} + +size_t BKE_scene_multiview_view_id_get(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + size_t nr; + + if ((!rd) || ((rd->scemode & R_MULTIVIEW) == 0)) + return 0; + + if ((!viewname) || (!viewname[0])) + return 0; + + nr = 0; + for (srv = rd->views.first, nr = 0; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (STREQ(viewname, srv->name)) { + return nr; + } + else { + nr += 1; + } + } + } + + return 0; +} + +void BKE_scene_multiview_filepath_get( + SceneRenderView *srv, const char *filepath, + char *r_filepath) +{ + BLI_strncpy(r_filepath, filepath, FILE_MAX); + BLI_path_suffix(r_filepath, FILE_MAX, srv->suffix, ""); +} + +/** + * When multiview is not used the filepath is as usual (e.g., ``Image.jpg``). + * When multiview is on, even if only one view is enabled the view is incorporated + * into the file name (e.g., ``Image_L.jpg``). That allows for the user to re-render + * individual views. + */ +void BKE_scene_multiview_view_filepath_get( + const RenderData *rd, const char *filepath, const char *viewname, + char *r_filepath) +{ + SceneRenderView *srv; + char suffix[FILE_MAX]; + + srv = BLI_findstring(&rd->views, viewname, offsetof(SceneRenderView, name)); + if (srv) + BLI_strncpy(suffix, srv->suffix, sizeof(suffix)); + else + BLI_strncpy(suffix, viewname, sizeof(suffix)); + + BLI_strncpy(r_filepath, filepath, FILE_MAX); + BLI_path_suffix(r_filepath, FILE_MAX, suffix, ""); +} + +const char *BKE_scene_multiview_view_suffix_get(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((viewname == NULL) || (viewname[0] == '\0')) + return viewname; + + srv = BLI_findstring(&rd->views, viewname, offsetof(SceneRenderView, name)); + if (srv) + return srv->suffix; + else + return viewname; +} + +const char *BKE_scene_multiview_view_id_suffix_get(const RenderData *rd, const size_t view_id) +{ + if ((rd->scemode & R_MULTIVIEW) == 0) { + return ""; + } + else { + const char *viewname = BKE_scene_multiview_render_view_name_get(rd, view_id); + return BKE_scene_multiview_view_suffix_get(rd, viewname); + } +} + +void BKE_scene_multiview_view_prefix_get(Scene *scene, const char *name, char *rprefix, char **rext) +{ + SceneRenderView *srv; + size_t index_act; + char *suf_act; + const char delims[] = {'.', '\0'}; + + rprefix[0] = '\0'; + + /* begin of extension */ + index_act = BLI_str_rpartition(name, delims, rext, &suf_act); + BLI_assert(index_act > 0); + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + size_t len = strlen(srv->suffix); + if (STREQLEN(*rext - len, srv->suffix, len)) { + BLI_strncpy(rprefix, name, strlen(name) - strlen(*rext) - len + 1); + break; + } + } + } +} + +void BKE_scene_multiview_videos_dimensions_get( + const RenderData *rd, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + if ((rd->scemode & R_MULTIVIEW) && + rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D) + { + IMB_stereo3d_write_dimensions( + rd->im_format.stereo3d_format.display_mode, + (rd->im_format.stereo3d_format.flag & S3D_SQUEEZED_FRAME) != 0, + width, height, + r_width, r_height); + } + else { + *r_width = width; + *r_height = height; + } +} + +size_t BKE_scene_multiview_num_videos_get(const RenderData *rd) +{ + if (BKE_imtype_is_movie(rd->im_format.imtype) == false) + return 0; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return 1; + + if (rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + else { + /* R_IMF_VIEWS_INDIVIDUAL */ + return BKE_scene_multiview_num_views_get(rd); + } +} diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 05d6bf136a4..69ba7618981 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -35,6 +35,7 @@ #include "MEM_guardedalloc.h" #include "DNA_sequence_types.h" +#include "DNA_scene_types.h" #include "IMB_moviecache.h" #include "IMB_imbuf.h" @@ -43,6 +44,7 @@ #include "BLI_listbase.h" #include "BKE_sequencer.h" +#include "BKE_scene.h" typedef struct SeqCacheKey { struct Sequence *seq; @@ -79,7 +81,9 @@ static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b) (a->bmain != b->bmain) || (a->scene != b->scene) || (a->motion_blur_shutter != b->motion_blur_shutter) || - (a->motion_blur_samples != b->motion_blur_samples)); + (a->motion_blur_samples != b->motion_blur_samples) || + (a->scene->r.views_format != b->scene->r.views_format) || + (a->view_id != b->view_id)); } static unsigned int seq_hash_render_data(const SeqRenderData *a) @@ -91,6 +95,7 @@ static unsigned int seq_hash_render_data(const SeqRenderData *a) rval ^= ((intptr_t) a->scene) << 6; rval ^= (int)(a->motion_blur_shutter * 100.0f) << 10; rval ^= a->motion_blur_samples << 24; + rval ^= ((a->scene->r.views_format * 2) + a->view_id) << 32; return rval; } diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 0cf51c7409f..a1d645c8e28 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -53,6 +53,12 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#ifdef WIN32 +# include "BLI_winstuff.h" +#else +# include <unistd.h> +#endif + #include "BLF_translation.h" #include "BKE_animsys.h" @@ -89,6 +95,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ListBase *seq static ImBuf *seq_render_strip(const SeqRenderData *context, Sequence *seq, float cfra); static void seq_free_animdata(Scene *scene, Sequence *seq); static ImBuf *seq_render_mask(const SeqRenderData *context, Mask *mask, float nr, bool make_float); +static size_t seq_num_files(Scene *scene, char views_format, const bool is_multiview); +static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const size_t view_id); /* **** XXX ******** */ #define SELECT 1 @@ -181,10 +189,7 @@ static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cach if (seq->strip) seq_free_strip(seq->strip); - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } + BKE_sequence_free_anim(seq); if (seq->type & SEQ_TYPE_EFFECT) { struct SeqEffectHandle sh = BKE_sequence_get_effect(seq); @@ -196,6 +201,10 @@ static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cach ((ID *)seq->sound)->us--; } + if (seq->stereo3d_format) { + MEM_freeN(seq->stereo3d_format); + } + /* clipboard has no scene and will never have a sound handle or be active * same goes to sequences copy for proxy rebuild job */ @@ -240,6 +249,22 @@ void BKE_sequence_free(Scene *scene, Sequence *seq) BKE_sequence_free_ex(scene, seq, true); } +/* Function to free imbuf and anim data on changes */ +void BKE_sequence_free_anim(Sequence *seq) +{ + while (seq->anims.last) { + StripAnim *sanim = seq->anims.last; + BLI_remlink(&seq->anims, sanim); + + if (sanim->anim) { + IMB_free_anim(sanim->anim); + sanim->anim = NULL; + } + + MEM_freeN(sanim); + } +} + /* cache must be freed before calling this function * since it leaves the seqbase in an invalid state */ static void seq_free_sequence_recurse(Scene *scene, Sequence *seq) @@ -537,6 +562,7 @@ void BKE_sequencer_new_render_data( r_context->motion_blur_shutter = 0; r_context->skip_cache = false; r_context->is_proxy_render = false; + r_context->view_id = 0; } /* ************************* iterator ************************** */ @@ -771,10 +797,17 @@ void BKE_sequence_calc(Scene *scene, Sequence *seq) } } +static void seq_multiview_name(Scene *scene, const size_t view_id, const char *prefix, + const char *ext, char *r_path, size_t r_size) +{ + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + BLI_snprintf(r_path, r_size, "%s%s%s", prefix, suffix, ext); +} + /* note: caller should run BKE_sequence_calc(scene, seq) after */ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_range) { - char str[FILE_MAX]; + char path[FILE_MAX]; int prev_startdisp = 0, prev_enddisp = 0; /* note: don't rename the strip, will break animation curves */ @@ -807,22 +840,67 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r break; } case SEQ_TYPE_MOVIE: - BLI_join_dirfile(str, sizeof(str), seq->strip->dir, + { + StripAnim *sanim; + bool is_multiview_loaded = false; + const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (scene->r.scemode & R_MULTIVIEW) != 0; + + BLI_join_dirfile(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); - BLI_path_abs(str, G.main->name); + BLI_path_abs(path, G.main->name); + + BKE_sequence_free_anim(seq); + + if (is_multiview && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + char prefix[FILE_MAX]; + char *ext = NULL; + size_t totfiles = seq_num_files(scene, seq->views_format, true); + int i = 0; - if (seq->anim) IMB_free_anim(seq->anim); + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); - seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + struct anim *anim; + char str[FILE_MAX]; - if (!seq->anim) { + seq_multiview_name(scene, i, prefix, ext, str, FILE_MAX); + anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + seq_anim_add_suffix(scene, anim, i); + + if (anim) { + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim; + } + } + is_multiview_loaded = true; + } + } + + if (is_multiview_loaded == false) { + struct anim *anim; + anim = openanim(path, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + if (anim) { + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim; + } + } + + /* use the first video as reference for everything */ + sanim = seq->anims.first; + + if ((!sanim) || (!sanim->anim)) { return; } - seq->len = IMB_anim_get_duration(seq->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); - - seq->anim_preseek = IMB_anim_get_preseek(seq->anim); + seq->len = IMB_anim_get_duration(sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); + + seq->anim_preseek = IMB_anim_get_preseek(sanim->anim); seq->len -= seq->anim_startofs; seq->len -= seq->anim_endofs; @@ -830,6 +908,7 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r seq->len = 0; } break; + } case SEQ_TYPE_MOVIECLIP: if (seq->clip == NULL) return; @@ -1324,6 +1403,7 @@ typedef struct SeqIndexBuildContext { int size_flags; int quality; bool overwrite; + size_t view_id; Main *bmain; Scene *scene; @@ -1363,44 +1443,59 @@ static double seq_rendersize_to_scale_factor(int size) return 0.25; } -static void seq_open_anim_file(Editing *ed, Sequence *seq, bool openfile) +/* the number of files will vary according to the stereo format */ +static size_t seq_num_files(Scene *scene, char views_format, const bool is_multiview) +{ + if (!is_multiview) { + return 1; + } + else if (views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + /* R_IMF_VIEWS_INDIVIDUAL */ + else { + return BKE_scene_multiview_num_views_get(&scene->r); + } +} + +static void seq_proxy_index_dir_set(struct anim *anim, const char *base_dir) +{ + char dir[FILE_MAX]; + char fname[FILE_MAXFILE]; + + IMB_anim_get_fname(anim, fname, FILE_MAXFILE); + BLI_strncpy(dir, base_dir, sizeof(dir)); + BLI_path_append(dir, sizeof(dir), fname); + IMB_anim_set_index_dir(anim, dir); +} + +static void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile) { + char dir[FILE_MAX]; char name[FILE_MAX]; StripProxy *proxy; + bool use_proxy; + bool is_multiview_loaded = false; + Editing *ed = scene->ed; + const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0; - if (seq->anim != NULL) { + if ((seq->anims.first != NULL) && (((StripAnim *)seq->anims.first)->anim != NULL)) { return; } + /* reset all the previously created anims */ + BKE_sequence_free_anim(seq); + BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(name, G.main->name); - - if (openfile) { - seq->anim = openanim(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); - } - else { - seq->anim = openanim_noload(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); - } - - if (seq->anim == NULL) { - return; - } proxy = seq->strip->proxy; - if (proxy == NULL) { - return; - } - - if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) || - (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE)) - { - char dir[FILE_MAX]; - char fname[FILE_MAXFILE]; + use_proxy = proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 || + (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE)); + if (use_proxy) { if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { if (ed->proxy_dir[0] == 0) BLI_strncpy(dir, "//BL_proxy", sizeof(dir)); @@ -1410,21 +1505,93 @@ static void seq_open_anim_file(Editing *ed, Sequence *seq, bool openfile) else { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } + BLI_path_abs(dir, G.main->name); + } - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); - BLI_path_append(dir, sizeof(dir), fname); + if (is_multiview && seq->views_format == R_IMF_VIEWS_INDIVIDUAL) { + size_t totfiles = seq_num_files(scene, seq->views_format, true); + char prefix[FILE_MAX]; + char *ext = NULL; + int i; - BLI_path_abs(dir, G.main->name); + BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext); + + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i); + char str[FILE_MAX]; + StripAnim *sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + + BLI_addtail(&seq->anims, sanim); - IMB_anim_set_index_dir(seq->anim, dir); + BLI_snprintf(str, sizeof(str), "%s%s%s", prefix, suffix, ext); + + if (openfile) { + sanim->anim = openanim( + str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + + seq_anim_add_suffix(scene, sanim->anim, i); + + if (sanim->anim == NULL) { + if (openfile) { + sanim->anim = openanim( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + + /* no individual view files - monoscopic, stereo 3d or exr multiview */ + totfiles = 1; + } + + if (sanim->anim && use_proxy) { + seq_proxy_index_dir_set(sanim->anim, dir); + } + } + is_multiview_loaded = true; + } } -} + if (is_multiview_loaded == false) { + StripAnim *sanim; + + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + + if (openfile) { + sanim->anim = openanim( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } -static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render_size, char *name) + if (sanim->anim && use_proxy) { + seq_proxy_index_dir_set(sanim->anim, dir); + } + } +} + +static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render_size, char *name, const size_t view_id) { int frameno; char dir[PROXY_MAXFILE]; + StripAnim *sanim; + char suffix[24] = {'\0'}; StripProxy *proxy = seq->strip->proxy; if (!proxy) { @@ -1439,23 +1606,31 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render * have both, a directory full of jpeg files and proxy avis, so * sorry folks, please rebuild your proxies... */ - if (seq->anim && ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { + sanim = BLI_findlink(&seq->anims, view_id); + + if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { char fname[FILE_MAXFILE]; if (ed->proxy_dir[0] == 0) BLI_strncpy(dir, "//BL_proxy", sizeof(dir)); else BLI_strncpy(dir, ed->proxy_dir, sizeof(dir)); - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); + + if (sanim && sanim->anim) { + IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE); + } + else if (seq->type == SEQ_TYPE_IMAGE) { + fname[0] = 0; + } BLI_path_append(dir, sizeof(dir), fname); BLI_path_abs(name, G.main->name); } else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } - else if (seq->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { + else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { char fname[FILE_MAXFILE]; BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); + IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE); BLI_path_append(dir, sizeof(dir), fname); } else if (seq->type == SEQ_TYPE_IMAGE) { @@ -1465,12 +1640,16 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render return false; } - if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && seq->anim && + if (view_id > 0) + BLI_snprintf(suffix, sizeof(suffix), "_%zu", view_id); + + if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && sanim && sanim->anim && ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) { BLI_join_dirfile(name, PROXY_MAXFILE, dir, proxy->file); BLI_path_abs(name, G.main->name); + BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", name, suffix); return true; } @@ -1478,13 +1657,13 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render /* generate a separate proxy directory for each preview size */ if (seq->type == SEQ_TYPE_IMAGE) { - BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy", dir, render_size, - BKE_sequencer_give_stripelem(seq, cfra)->name); + BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy%s", dir, render_size, + BKE_sequencer_give_stripelem(seq, cfra)->name, suffix); frameno = 1; } else { frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; - BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####", dir, render_size); + BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, render_size, suffix); } BLI_path_abs(name, G.main->name); @@ -1503,6 +1682,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c int render_size = context->preview_render_size; StripProxy *proxy = seq->strip->proxy; Editing *ed = context->scene->ed; + StripAnim *sanim; if (!(seq->flag & SEQ_USE_PROXY)) { return NULL; @@ -1523,7 +1703,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) { int frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; if (proxy->anim == NULL) { - if (seq_proxy_get_fname(ed, seq, cfra, render_size, name) == 0) { + if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) { return NULL; } @@ -1533,14 +1713,15 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c return NULL; } - seq_open_anim_file(context->scene->ed, seq, true); + seq_open_anim_file(context->scene, seq, true); + sanim = seq->anims.first; - frameno = IMB_anim_index_get_frame_index(seq->anim, proxy->tc, frameno); + frameno = IMB_anim_index_get_frame_index(sanim ? sanim->anim : NULL, seq->strip->proxy->tc, frameno); return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE); } - if (seq_proxy_get_fname(ed, seq, cfra, render_size, name) == 0) { + if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) { return NULL; } @@ -1567,7 +1748,7 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i ImBuf *ibuf_tmp, *ibuf; Editing *ed = context->scene->ed; - if (!seq_proxy_get_fname(ed, seq, cfra, proxy_render_size, name)) { + if (!seq_proxy_get_fname(ed, seq, cfra, proxy_render_size, name, context->view_id)) { return; } @@ -1608,44 +1789,130 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i IMB_freeImBuf(ibuf); } -SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list) +/* returns whether the file this context would read from even exist, if not, don't create the context +*/ +static bool seq_proxy_multiview_context_invalid(Sequence *seq, Scene *scene, const size_t view_id) +{ + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return false; + + if ((seq->type == SEQ_TYPE_IMAGE) && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + static char prefix[FILE_MAX]; + static char *ext = NULL; + char str[FILE_MAX]; + + if (view_id == 0) { + char path[FILE_MAX]; + BLI_join_dirfile(path, sizeof(path), seq->strip->dir, + seq->strip->stripdata->name); + BLI_path_abs(path, G.main->name); + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); + } + + if (prefix[0] == '\0') + return view_id != 0; + + seq_multiview_name(scene, view_id, prefix, ext, str, FILE_MAX); + + if (BLI_access(str, R_OK) == 0) + return false; + else + return view_id != 0; + } + return false; +} + +/** This returns the maximum possible number of required contexts +*/ +static size_t seq_proxy_context_count(Sequence *seq, Scene *scene) +{ + size_t num_views = 1; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return 1; + + switch (seq->type) { + case SEQ_TYPE_MOVIE: + { + num_views = BLI_listbase_count(&seq->anims); + break; + } + case SEQ_TYPE_IMAGE: + { + switch (seq->views_format) { + case R_IMF_VIEWS_INDIVIDUAL: + num_views = BKE_scene_multiview_num_views_get(&scene->r); + break; + case R_IMF_VIEWS_STEREO_3D: + num_views = 2; + break; + case R_IMF_VIEWS_MULTIVIEW: + /* not supported at the moment */ + /* pass through */ + default: + num_views = 1; + } + break; + } + } + + return num_views; +} + +void BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list, ListBase *queue) { SeqIndexBuildContext *context; Sequence *nseq; + LinkData *link; + size_t i; + size_t num_files; if (!seq->strip || !seq->strip->proxy) { - return NULL; + return; } if (!(seq->flag & SEQ_USE_PROXY)) { - return NULL; + return; } - context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context"); + num_files = seq_proxy_context_count(seq, scene); + + for (i = 0; i < num_files; i++) { + if (seq_proxy_multiview_context_invalid(seq, scene, i)) + continue; - nseq = BKE_sequence_dupli_recursive(scene, scene, seq, 0); + context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context"); - context->tc_flags = nseq->strip->proxy->build_tc_flags; - context->size_flags = nseq->strip->proxy->build_size_flags; - context->quality = nseq->strip->proxy->quality; - context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0; + nseq = BKE_sequence_dupli_recursive(scene, scene, seq, 0); - context->bmain = bmain; - context->scene = scene; - context->orig_seq = seq; - context->seq = nseq; + context->tc_flags = nseq->strip->proxy->build_tc_flags; + context->size_flags = nseq->strip->proxy->build_size_flags; + context->quality = nseq->strip->proxy->quality; + context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0; - if (nseq->type == SEQ_TYPE_MOVIE) { - seq_open_anim_file(scene->ed, nseq, true); + context->bmain = bmain; + context->scene = scene; + context->orig_seq = seq; + context->seq = nseq; - if (nseq->anim) { - context->index_context = IMB_anim_index_rebuild_context(nseq->anim, - context->tc_flags, context->size_flags, context->quality, - context->overwrite, file_list); + context->view_id = i; /* only for images */ + + link = BLI_genericNodeN(context); + BLI_addtail(queue, link); + + if (nseq->type == SEQ_TYPE_MOVIE) { + StripAnim *sanim; + + seq_open_anim_file(scene, nseq, true); + sanim = BLI_findlink(&nseq->anims, i); + + if (sanim->anim) { + context->index_context = IMB_anim_index_rebuild_context(sanim->anim, + context->tc_flags, context->size_flags, context->quality, + context->overwrite, file_list); + } } } - - return context; } void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, short *do_update, float *progress) @@ -1684,6 +1951,7 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho render_context.skip_cache = true; render_context.is_proxy_render = true; + render_context.view_id = context->view_id; for (cfra = seq->startdisp + seq->startstill; cfra < seq->enddisp - seq->endstill; cfra++) { if (context->size_flags & IMB_PROXY_25) { @@ -1710,8 +1978,14 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho void BKE_sequencer_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop) { if (context->index_context) { - IMB_close_anim_proxies(context->seq->anim); - IMB_close_anim_proxies(context->orig_seq->anim); + StripAnim *sanim; + + for (sanim = context->seq->anims.first; sanim; sanim = sanim->next) + IMB_close_anim_proxies(sanim->anim); + + for (sanim = context->orig_seq->anims.first; sanim; sanim = sanim->next) + IMB_close_anim_proxies(sanim->anim); + IMB_anim_index_rebuild_finish(context->index_context, stop); } @@ -2458,6 +2732,234 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, Sequenc return out; } +static ImBuf *seq_render_image_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) +{ + ImBuf *ibuf = NULL; + char name[FILE_MAX]; + bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (context->scene->r.scemode & R_MULTIVIEW) != 0; + StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra); + int flag; + + if (s_elem) { + BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); + BLI_path_abs(name, G.main->name); + } + + flag = IB_rect; + if (seq->alpha_mode == SEQ_ALPHA_PREMUL) + flag |= IB_alphamode_premul; + + if (!s_elem) { + /* don't do anything */ + } + else if (is_multiview) { + size_t totfiles = seq_num_files(context->scene, seq->views_format, true); + size_t totviews; + struct ImBuf **ibufs_arr; + char prefix[FILE_MAX]; + char *ext = NULL; + int i; + + if (totfiles > 1) { + BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext); + if (prefix[0] == '\0') { + goto monoview_image; + } + } + + totviews = BKE_scene_multiview_num_views_get(&context->scene->r); + ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) { + + if (prefix[0] == '\0') { + ibufs_arr[i] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name); + } + else { + char str[FILE_MAX]; + seq_multiview_name(context->scene, i, prefix, ext, str, FILE_MAX); + ibufs_arr[i] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name); + } + + if (ibufs_arr[i]) { + /* we don't need both (speed reasons)! */ + if (ibufs_arr[i]->rect_float && ibufs_arr[i]->rect) + imb_freerectImBuf(ibufs_arr[i]); + } + } + + if (seq->views_format == R_IMF_VIEWS_STEREO_3D && ibufs_arr[0]) + IMB_ImBufFromStereo3d(seq->stereo3d_format, ibufs_arr[0], &ibufs_arr[0], &ibufs_arr[1]); + + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i]) { + SeqRenderData localcontext = *context; + localcontext.view_id = i; + + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); + + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]); + } + } + } + + /* return the original requested ImBuf */ + ibuf = ibufs_arr[context->view_id]; + if (ibuf) { + s_elem->orig_width = ibufs_arr[0]->x; + s_elem->orig_height = ibufs_arr[0]->y; + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i] != ibuf) { + IMB_freeImBuf(ibufs_arr[i]); + } + } + + MEM_freeN(ibufs_arr); + } + else { +monoview_image: + if ((ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) { + /* we don't need both (speed reasons)! */ + if (ibuf->rect_float && ibuf->rect) + imb_freerectImBuf(ibuf); + + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + + s_elem->orig_width = ibuf->x; + s_elem->orig_height = ibuf->y; + } + } + + return ibuf; +} + +static ImBuf *seq_render_movie_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) +{ + ImBuf *ibuf = NULL; + StripAnim *sanim; + bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (context->scene->r.scemode & R_MULTIVIEW) != 0; + + /* load all the videos */ + seq_open_anim_file(context->scene, seq, false); + + if (is_multiview) { + ImBuf **ibuf_arr; + size_t totviews; + size_t totfiles = seq_num_files(context->scene, seq->views_format, true); + int i; + + if (totfiles != BLI_listbase_count_ex(&seq->anims, totfiles + 1)) + goto monoview_movie; + + totviews = BKE_scene_multiview_num_views_get(&context->scene->r); + ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + + for (i = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, i++) { + if (sanim->anim) { + IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); + IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); + + ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + proxy_size); + + /* fetching for requested proxy size failed, try fetching the original instead */ + if (!ibuf_arr[i] && proxy_size != IMB_PROXY_NONE) { + ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); + } + if (ibuf_arr[i]) { + /* we don't need both (speed reasons)! */ + if (ibuf_arr[i]->rect_float && ibuf_arr[i]->rect) + imb_freerectImBuf(ibuf_arr[i]); + } + } + } + + if (seq->views_format == R_IMF_VIEWS_STEREO_3D) { + if (ibuf_arr[0]) { + IMB_ImBufFromStereo3d(seq->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + } + else { + /* probably proxy hasn't been created yet */ + MEM_freeN(ibuf_arr); + return NULL; + } + } + + for (i = 0; i < totviews; i++) { + SeqRenderData localcontext = *context; + localcontext.view_id = i; + + if (ibuf_arr[i]) { + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[i], false); + } + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibuf_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibuf_arr[i]); + } + } + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[context->view_id]; + if (ibuf) { + seq->strip->stripdata->orig_width = ibuf->x; + seq->strip->stripdata->orig_height = ibuf->y; + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + MEM_freeN(ibuf_arr); + } + else { +monoview_movie: + sanim = seq->anims.first; + if (sanim && sanim->anim) { + IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); + IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); + + ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + proxy_size); + + /* fetching for requested proxy size failed, try fetching the original instead */ + if (!ibuf && proxy_size != IMB_PROXY_NONE) { + ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); + } + if (ibuf) { + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + + /* we don't need both (speed reasons)! */ + if (ibuf->rect_float && ibuf->rect) { + imb_freerectImBuf(ibuf); + } + + seq->strip->stripdata->orig_width = ibuf->x; + seq->strip->stripdata->orig_height = ibuf->y; + } + } + } + return ibuf; +} + static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence *seq, float nr) { ImBuf *ibuf = NULL; @@ -2589,7 +3091,7 @@ static ImBuf *seq_render_mask_strip(const SeqRenderData *context, Sequence *seq, return seq_render_mask(context, seq->mask, nr, make_float); } -static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq, float nr) +static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) { ImBuf *ibuf = NULL; float frame; @@ -2687,6 +3189,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq char err_out[256] = "unknown"; int width = (scene->r.xsch * scene->r.size) / 100; int height = (scene->r.ysch * scene->r.size) / 100; + const char *viewname = BKE_scene_multiview_render_view_name_get(&scene->r, context->view_id); /* for old scened this can be uninitialized, * should probably be added to do_versions at some point if the functionality stays */ @@ -2698,14 +3201,18 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq ibuf = sequencer_view3d_cb(scene, camera, width, height, IB_rect, context->scene->r.seq_prev_type, (context->scene->r.seq_flag & R_SEQ_SOLID_TEX) != 0, - use_gpencil, true, scene->r.alphamode, err_out); + use_gpencil, true, scene->r.alphamode, viewname, err_out); if (ibuf == NULL) { fprintf(stderr, "seq_render_scene_strip failed to get opengl buffer: %s\n", err_out); } } else { Render *re = RE_GetRender(scene->id.name); - RenderResult rres; + size_t totviews = BKE_scene_multiview_num_views_get(&scene->r); + int i; + ImBuf **ibufs_arr; + + ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); /* XXX: this if can be removed when sequence preview rendering uses the job system * @@ -2725,27 +3232,51 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq /* restore previous state after it was toggled on & off by RE_BlenderFrame */ G.is_rendering = is_rendering; } - - RE_AcquireResultImage(re, &rres); - - if (rres.rectf) { - ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat); - memcpy(ibuf->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty); - if (rres.rectz) { - addzbuffloatImBuf(ibuf); - memcpy(ibuf->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty); + + for (i = 0; i < totviews; i++) { + SeqRenderData localcontext = *context; + RenderResult rres; + + localcontext.view_id = i; + + RE_AcquireResultImage(re, &rres, i); + + if (rres.rectf) { + ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat); + memcpy(ibufs_arr[i]->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty); + + if (rres.rectz) { + addzbuffloatImBuf(ibufs_arr[i]); + memcpy(ibufs_arr[i]->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty); + } + + /* float buffers in the sequencer are not linear */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); + } + else if (rres.rect32) { + ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); + memcpy(ibufs_arr[i]->rect, rres.rect32, 4 * rres.rectx * rres.recty); } - /* float buffers in the sequencer are not linear */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]); + } + + RE_ReleaseResultImage(re); } - else if (rres.rect32) { - ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); - memcpy(ibuf->rect, rres.rect32, 4 * rres.rectx * rres.recty); + + /* return the original requested ImBuf */ + ibuf = ibufs_arr[context->view_id]; + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i] != ibuf) { + IMB_freeImBuf(ibufs_arr[i]); + } } - - RE_ReleaseResultImage(re); - + MEM_freeN(ibufs_arr); + // BIF_end_render_callbacks(); } @@ -2806,7 +3337,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s float nr = give_stripelem_index(seq, cfra); int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT : seq->type; bool use_preprocess = BKE_sequencer_input_have_to_preprocess(context, seq, cfra); - char name[FILE_MAX]; switch (type) { case SEQ_TYPE_META: @@ -2825,7 +3355,7 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s } else { /* scene can be NULL after deletions */ - ibuf = seq_render_scene_strip(context, seq, nr); + ibuf = seq_render_scene_strip(context, seq, nr, cfra); /* Scene strips update all animation, so we need to restore original state.*/ BKE_animsys_evaluate_all_animation(context->bmain, context->scene, cfra); @@ -2870,68 +3400,14 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s case SEQ_TYPE_IMAGE: { - StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra); - int flag; - - if (s_elem) { - BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); - BLI_path_abs(name, G.main->name); - } - - flag = IB_rect; - if (seq->alpha_mode == SEQ_ALPHA_PREMUL) - flag |= IB_alphamode_premul; - - if (s_elem && (ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) { - /* we don't need both (speed reasons)! */ - if (ibuf->rect_float && ibuf->rect) - imb_freerectImBuf(ibuf); - - /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); - - copy_to_ibuf_still(context, seq, nr, ibuf); - - s_elem->orig_width = ibuf->x; - s_elem->orig_height = ibuf->y; - } + ibuf = seq_render_image_strip(context, seq, nr, cfra); + copy_to_ibuf_still(context, seq, nr, ibuf); break; } case SEQ_TYPE_MOVIE: { - seq_open_anim_file(context->scene->ed, seq, false); - - if (seq->anim) { - IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); - IMB_anim_set_preseek(seq->anim, seq->anim_preseek); - - ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - proxy_size); - - /* fetching for requested proxy size failed, try fetching the original instead - if (!ibuf && proxy_size != IMB_PROXY_NONE) { - IMB_Proxy_Size proxy_sizes = IMB_anim_proxy_get_existing(seq->anim); - while (!(proxy_size & proxy_sizes) && proxy_size > 0) { - proxy_size >>= 1; - } - ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - proxy_size); - }*/ - if (ibuf) { - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); - - /* we don't need both (speed reasons)! */ - if (ibuf->rect_float && ibuf->rect) { - imb_freerectImBuf(ibuf); - } - - seq->strip->stripdata->orig_width = ibuf->x; - seq->strip->stripdata->orig_height = ibuf->y; - } - } + ibuf = seq_render_movie_strip(context, seq, nr, cfra); copy_to_ibuf_still(context, seq, nr, ibuf); break; } @@ -3427,16 +3903,6 @@ ImBuf *BKE_sequencer_give_ibuf_threaded(const SeqRenderData *context, float cfra return e ? e->ibuf : NULL; } -/* Functions to free imbuf and anim data on changes */ - -static void free_anim_seq(Sequence *seq) -{ - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } -} - /* check whether sequence cur depends on seq */ bool BKE_sequence_check_depend(Sequence *seq, Sequence *cur) { @@ -3488,15 +3954,11 @@ static void sequence_invalidate_cache(Scene *scene, Sequence *seq, bool invalida /* invalidate cache for current sequence */ if (invalidate_self) { - if (seq->anim) { - /* Animation structure holds some buffers inside, - * so for proper cache invalidation we need to - * re-open the animation. - */ - IMB_free_anim(seq->anim); - seq->anim = NULL; - } - + /* Animation structure holds some buffers inside, + * so for proper cache invalidation we need to + * re-open the animation. + */ + BKE_sequence_free_anim(seq); BKE_sequencer_cache_cleanup_sequence(seq); } @@ -3543,7 +4005,7 @@ void BKE_sequencer_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render) if (seq->strip) { if (seq->type == SEQ_TYPE_MOVIE) { - free_anim_seq(seq); + BKE_sequence_free_anim(seq); } if (seq->type == SEQ_TYPE_SPEED) { BKE_sequence_effect_speed_rebuild_map(scene, seq, true); @@ -3590,7 +4052,7 @@ static bool update_changed_seq_recurs(Scene *scene, Sequence *seq, Sequence *cha if (free_imbuf) { if (ibuf_change) { if (seq->type == SEQ_TYPE_MOVIE) - free_anim_seq(seq); + BKE_sequence_free_anim(seq); if (seq->type == SEQ_TYPE_SPEED) { BKE_sequence_effect_speed_rebuild_map(scene, seq, true); } @@ -4478,6 +4940,8 @@ Sequence *BKE_sequence_alloc(ListBase *lb, int cfra, int machine) seq->pitch = 1.0f; seq->scene_sound = NULL; + seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); + return seq; } @@ -4535,6 +4999,12 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad strip->stripdata = MEM_callocN(seq->len * sizeof(StripElem), "stripelem"); BLI_strncpy(strip->dir, seq_load->path, sizeof(strip->dir)); + if (seq_load->stereo3d_format) + *seq->stereo3d_format = *seq_load->stereo3d_format; + + seq->views_format = seq_load->views_format; + seq->flag |= seq_load->flag & SEQ_USE_VIEWS; + seq_load_apply(scene, seq, seq_load); return seq; @@ -4614,6 +5084,12 @@ Sequence *BKE_sequencer_add_sound_strip(bContext *C, ListBase *seqbasep, SeqLoad } #endif // WITH_AUDASPACE +static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const size_t view_id) +{ + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + IMB_suffix_anim(anim, suffix); +} + Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_load) { Scene *scene = CTX_data_scene(C); /* only for sound */ @@ -4623,29 +5099,84 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad Strip *strip; StripElem *se; char colorspace[64] = "\0"; /* MAX_COLORSPACE_NAME */ - - struct anim *an; + bool is_multiview_loaded = false; + const bool is_multiview = (seq_load->flag & SEQ_USE_VIEWS) != 0; + size_t totfiles = seq_num_files(scene, seq_load->views_format, is_multiview); + struct anim **anim_arr; + int i; BLI_strncpy(path, seq_load->path, sizeof(path)); BLI_path_abs(path, G.main->name); - an = openanim(path, IB_rect, 0, colorspace); + anim_arr = MEM_callocN(sizeof(struct anim *) * totfiles, "Video files"); - if (an == NULL) - return NULL; + if (is_multiview && (seq_load->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + char prefix[FILE_MAX]; + char *ext = NULL; + size_t j = 0; + + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); + + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + char str[FILE_MAX]; + + seq_multiview_name(scene, i, prefix, ext, str, FILE_MAX); + anim_arr[j] = openanim(str, IB_rect, 0, colorspace); + seq_anim_add_suffix(scene, anim_arr[j], i); + + if (anim_arr[j]) { + j++; + } + } + + if (j == 0) { + MEM_freeN(anim_arr); + return NULL; + } + is_multiview_loaded = true; + } + } + + if (is_multiview_loaded == false) { + anim_arr[0] = openanim(path, IB_rect, 0, colorspace); + + if (anim_arr[0] == NULL) { + MEM_freeN(anim_arr); + return NULL; + } + } seq = BKE_sequence_alloc(seqbasep, seq_load->start_frame, seq_load->channel); + + /* multiview settings */ + if (seq_load->stereo3d_format) { + *seq->stereo3d_format = *seq_load->stereo3d_format; + seq->views_format = seq_load->views_format; + } + seq->flag |= seq_load->flag & SEQ_USE_VIEWS; + seq->type = SEQ_TYPE_MOVIE; seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ - seq->anim = an; - seq->anim_preseek = IMB_anim_get_preseek(an); + for (i = 0; i < totfiles; i++) { + if (anim_arr[i]) { + StripAnim *sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim_arr[i]; + } + else { + break; + } + } + + seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2); BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq); /* basic defaults */ seq->strip = strip = MEM_callocN(sizeof(Strip), "strip"); - seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN); + seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); strip->us = 1; BLI_strncpy(seq->strip->colorspace_settings.name, colorspace, sizeof(seq->strip->colorspace_settings.name)); @@ -4673,6 +5204,7 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad /* can be NULL */ seq_load_apply(scene, seq, seq_load); + MEM_freeN(anim_arr); return seq; } @@ -4684,6 +5216,8 @@ static Sequence *seq_dupli(Scene *scene, Scene *scene_to, Sequence *seq, int dup seq->tmp = seqn; seqn->strip = MEM_dupallocN(seq->strip); + seqn->stereo3d_format = MEM_dupallocN(seq->stereo3d_format); + /* XXX: add F-Curve duplication stuff? */ if (seq->strip->crop) { @@ -4730,7 +5264,7 @@ static Sequence *seq_dupli(Scene *scene, Scene *scene_to, Sequence *seq, int dup else if (seq->type == SEQ_TYPE_MOVIE) { seqn->strip->stripdata = MEM_dupallocN(seq->strip->stripdata); - seqn->anim = NULL; + BLI_listbase_clear(&seqn->anims); } else if (seq->type == SEQ_TYPE_SOUND_RAM) { seqn->strip->stripdata = diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index b11d0ae03b0..7a800555144 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -130,5 +130,5 @@ void BKE_speaker_free(Speaker *spk) if (spk->sound) spk->sound->id.us--; - BKE_free_animdata((ID *)spk); + BKE_animdata_free((ID *)spk); } diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 940e68e29f8..0ac689f09bb 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -564,7 +564,7 @@ void BKE_texture_free(Tex *tex) if (tex->pd) BKE_texture_pointdensity_free(tex->pd); if (tex->vd) BKE_texture_voxeldata_free(tex->vd); if (tex->ot) BKE_texture_ocean_free(tex->ot); - BKE_free_animdata((struct ID *)tex); + BKE_animdata_free((struct ID *)tex); BKE_previewimg_free(&tex->preview); BKE_icon_delete((struct ID *)tex); diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 699e0d34161..f26cbf7880a 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -63,7 +63,7 @@ void BKE_world_free_ex(World *wrld, bool do_id_user) } BKE_previewimg_free(&wrld->preview); - BKE_free_animdata((ID *)wrld); + BKE_animdata_free((ID *)wrld); /* is no lib link block, but world extension */ if (wrld->nodetree) { diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index a86ffacfdbd..cec455e01b9 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -50,26 +50,34 @@ /* ********************** general blender movie support ***************************** */ -static int start_stub(Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty), - ReportList *UNUSED(reports), bool UNUSED(preview)) +static int start_stub(void *UNUSED(context_v), Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty), + ReportList *UNUSED(reports), bool UNUSED(preview), const char *UNUSED(suffix)) { return 0; } -static void end_stub(void) +static void end_stub(void *UNUSED(context_v)) {} -static int append_stub(RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels), - int UNUSED(rectx), int UNUSED(recty), ReportList *UNUSED(reports)) +static int append_stub(void *UNUSED(context_v), RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels), + int UNUSED(rectx), int UNUSED(recty), const char *UNUSED(suffix), ReportList *UNUSED(reports)) { return 0; } +static void *context_create_stub(void) +{ return NULL; } + +static void context_free_stub(void *UNUSED(context_v)) +{} + #ifdef WITH_AVI # include "AVI_avi.h" /* callbacks */ -static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview); -static void end_avi(void); -static int append_avi(RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, ReportList *reports); -static void filepath_avi(char *string, RenderData *rd, bool preview); +static int start_avi(void *context_v, Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview, const char *suffix); +static void end_avi(void *context_v); +static int append_avi(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, ReportList *reports); +static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix); +static void *context_create_avi(void); +static void context_free_avi(void *context_v); #endif /* WITH_AVI */ #ifdef WITH_QUICKTIME @@ -93,13 +101,17 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.end_movie = end_stub; mh.get_next_frame = NULL; mh.get_movie_path = NULL; - + mh.context_create = context_create_stub; + mh.context_free = context_free_stub; + /* set the default handle, as builtin */ #ifdef WITH_AVI mh.start_movie = start_avi; mh.append_movie = append_avi; mh.end_movie = end_avi; mh.get_movie_path = filepath_avi; + mh.context_create = context_create_avi; + mh.context_free = context_free_avi; #endif /* do the platform specific handles */ @@ -109,6 +121,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = append_qt; mh.end_movie = end_qt; mh.get_movie_path = filepath_qt; + mh.context_create = context_create_qt; + mh.context_free = context_free_qt; } #endif #ifdef WITH_FFMPEG @@ -117,6 +131,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = BKE_ffmpeg_append; mh.end_movie = BKE_ffmpeg_end; mh.get_movie_path = BKE_ffmpeg_filepath_get; + mh.context_create = BKE_ffmpeg_context_create; + mh.context_free = BKE_ffmpeg_context_free; } #endif #ifdef WITH_FRAMESERVER @@ -125,6 +141,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = BKE_frameserver_append; mh.end_movie = BKE_frameserver_end; mh.get_next_frame = BKE_frameserver_loop; + mh.context_create = BKE_frameserver_context_create; + mh.context_free = BKE_frameserver_context_free; } #endif @@ -137,9 +155,7 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) #ifdef WITH_AVI -static AviMovie *avi = NULL; - -static void filepath_avi(char *string, RenderData *rd, bool preview) +static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix) { int sfra, efra; @@ -170,27 +186,27 @@ static void filepath_avi(char *string, RenderData *rd, bool preview) BLI_path_frame_range(string, sfra, efra, 4); } } + + BLI_path_suffix(string, FILE_MAX, suffix, ""); } -static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview) +static int start_avi(void *context_v, Scene *UNUSED(scene), RenderData *rd, int rectx, int recty, + ReportList *reports, bool preview, const char *suffix) { int x, y; char name[256]; AviFormat format; int quality; double framerate; - - (void)scene; /* unused */ - - filepath_avi(name, rd, preview); + AviMovie *avi = context_v; + + filepath_avi(name, rd, preview, suffix); x = rectx; y = recty; quality = rd->im_format.quality; framerate = (double) rd->frs_sec / (double) rd->frs_sec_base; - - avi = MEM_mallocN(sizeof(AviMovie), "avimovie"); if (rd->im_format.imtype != R_IMF_IMTYPE_AVIJPEG) format = AVI_FORMAT_AVI_RGB; else format = AVI_FORMAT_MJPEG; @@ -216,12 +232,13 @@ static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportL return 1; } -static int append_avi(RenderData *UNUSED(rd), int start_frame, int frame, int *pixels, - int rectx, int recty, ReportList *UNUSED(reports)) +static int append_avi(void *context_v, RenderData *UNUSED(rd), int start_frame, int frame, int *pixels, + int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports)) { unsigned int *rt1, *rt2, *rectot; int x, y; char *cp, rt; + AviMovie *avi = context_v; if (avi == NULL) return 0; @@ -252,22 +269,37 @@ static int append_avi(RenderData *UNUSED(rd), int start_frame, int frame, int *p return 1; } -static void end_avi(void) +static void end_avi(void *context_v) { + AviMovie *avi = context_v; + if (avi == NULL) return; AVI_close_compress(avi); - MEM_freeN(avi); - avi = NULL; } + +static void *context_create_avi(void) +{ + AviMovie *avi = MEM_mallocN(sizeof(AviMovie), "avimovie"); + return avi; +} + +static void context_free_avi(void *context_v) +{ + AviMovie *avi = context_v; + if (avi) { + MEM_freeN(avi); + } +} + #endif /* WITH_AVI */ /* similar to BKE_image_path_from_imformat() */ -void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview) +void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix) { bMovieHandle *mh = BKE_movie_handle_get(rd->im_format.imtype); if (mh->get_movie_path) - mh->get_movie_path(string, rd, preview); + mh->get_movie_path(string, rd, preview, suffix); else string[0] = '\0'; } diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 0717369cde8..af71f19c226 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -61,36 +61,38 @@ #include "ffmpeg_compat.h" -static int ffmpeg_type = 0; -static int ffmpeg_codec = AV_CODEC_ID_MPEG4; -static int ffmpeg_audio_codec = AV_CODEC_ID_NONE; -static int ffmpeg_video_bitrate = 1150; -static int ffmpeg_audio_bitrate = 128; -static int ffmpeg_gop_size = 12; -static int ffmpeg_autosplit = 0; -static int ffmpeg_autosplit_count = 0; -static bool ffmpeg_preview = false; - -static AVFormatContext *outfile = 0; -static AVStream *video_stream = 0; -static AVStream *audio_stream = 0; -static AVFrame *current_frame = 0; -static struct SwsContext *img_convert_ctx = 0; - -static uint8_t *audio_input_buffer = 0; -static uint8_t *audio_deinterleave_buffer = 0; -static int audio_input_samples = 0; +typedef struct FFMpegContext { + int ffmpeg_type; + int ffmpeg_codec; + int ffmpeg_audio_codec; + int ffmpeg_video_bitrate; + int ffmpeg_audio_bitrate; + int ffmpeg_gop_size; + int ffmpeg_autosplit; + int ffmpeg_autosplit_count; + bool ffmpeg_preview; + + AVFormatContext *outfile; + AVStream *video_stream; + AVStream *audio_stream; + AVFrame *current_frame; + struct SwsContext *img_convert_ctx; + + uint8_t *audio_input_buffer; + uint8_t *audio_deinterleave_buffer; + int audio_input_samples; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 -static uint8_t *audio_output_buffer = 0; -static int audio_outbuf_size = 0; + uint8_t *audio_output_buffer; + int audio_outbuf_size; #endif -static double audio_time = 0.0f; -static bool audio_deinterleave = false; -static int audio_sample_size = 0; + double audio_time; + bool audio_deinterleave; + int audio_sample_size; #ifdef WITH_AUDASPACE -static AUD_Device *audio_mixdown_device = 0; + AUD_Device *audio_mixdown_device; #endif +} FFMpegContext; #define FFMPEG_AUTOSPLIT_SIZE 2000000000 @@ -99,6 +101,7 @@ static AUD_Device *audio_mixdown_device = 0; 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); /* Delete a picture buffer */ @@ -117,50 +120,50 @@ static int request_float_audio_buffer(int codec_id) } #ifdef WITH_AUDASPACE -static int write_audio_frame(void) +static int write_audio_frame(FFMpegContext *context) { AVCodecContext *c = NULL; AVPacket pkt; AVFrame *frame = NULL; int got_output = 0; - c = audio_stream->codec; + c = context->audio_stream->codec; av_init_packet(&pkt); pkt.size = 0; pkt.data = NULL; - AUD_readDevice(audio_mixdown_device, audio_input_buffer, audio_input_samples); - audio_time += (double) audio_input_samples / (double) c->sample_rate; + AUD_readDevice(context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples); + context->audio_time += (double) context->audio_input_samples / (double) c->sample_rate; #ifdef FFMPEG_HAVE_ENCODE_AUDIO2 frame = avcodec_alloc_frame(); avcodec_get_frame_defaults(frame); - frame->pts = audio_time / av_q2d(c->time_base); - frame->nb_samples = audio_input_samples; + frame->pts = context->audio_time / av_q2d(c->time_base); + frame->nb_samples = context->audio_input_samples; frame->format = c->sample_fmt; #ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT frame->channel_layout = c->channel_layout; #endif - if (audio_deinterleave) { + if (context->audio_deinterleave) { int channel, i; uint8_t *temp; for (channel = 0; channel < c->channels; channel++) { for (i = 0; i < frame->nb_samples; i++) { - memcpy(audio_deinterleave_buffer + (i + channel * frame->nb_samples) * audio_sample_size, - audio_input_buffer + (c->channels * i + channel) * audio_sample_size, audio_sample_size); + memcpy(context->audio_deinterleave_buffer + (i + channel * frame->nb_samples) * context->audio_sample_size, + context->audio_input_buffer + (c->channels * i + channel) * context->audio_sample_size, context->audio_sample_size); } } - temp = audio_deinterleave_buffer; - audio_deinterleave_buffer = audio_input_buffer; - audio_input_buffer = temp; + temp = context->audio_deinterleave_buffer; + context->audio_deinterleave_buffer = context->audio_input_buffer; + context->audio_input_buffer = temp; } - avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, audio_input_buffer, - audio_input_samples * c->channels * audio_sample_size, 1); + avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, context->audio_input_buffer, + context->audio_input_samples * c->channels * context->audio_sample_size, 1); if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) { // XXX error("Error writing audio packet"); @@ -172,30 +175,30 @@ static int write_audio_frame(void) return 0; } #else - pkt.size = avcodec_encode_audio(c, audio_output_buffer, audio_outbuf_size, (short *) audio_input_buffer); + pkt.size = avcodec_encode_audio(c, context->audio_output_buffer, context->audio_outbuf_size, (short *) context->audio_input_buffer); if (pkt.size < 0) { // XXX error("Error writing audio packet"); return -1; } - pkt.data = audio_output_buffer; + pkt.data = context->audio_output_buffer; got_output = 1; #endif if (got_output) { if (pkt.pts != AV_NOPTS_VALUE) - pkt.pts = av_rescale_q(pkt.pts, c->time_base, audio_stream->time_base); + pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base); if (pkt.dts != AV_NOPTS_VALUE) - pkt.dts = av_rescale_q(pkt.dts, c->time_base, audio_stream->time_base); + pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base); if (pkt.duration > 0) - pkt.duration = av_rescale_q(pkt.duration, c->time_base, audio_stream->time_base); + pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base); - pkt.stream_index = audio_stream->index; + pkt.stream_index = context->audio_stream->index; pkt.flags |= AV_PKT_FLAG_KEY; - if (av_interleaved_write_frame(outfile, &pkt) != 0) { + if (av_interleaved_write_frame(context->outfile, &pkt) != 0) { fprintf(stderr, "Error writing audio packet!\n"); if (frame) avcodec_free_frame(&frame); @@ -302,11 +305,11 @@ static const char **get_file_extensions(int format) } /* Write a frame to the output file */ -static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) +static int write_video_frame(FFMpegContext *context, RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) { int got_output; int ret, success = 1; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; AVPacket packet = { 0 }; av_init_packet(&packet); @@ -321,22 +324,22 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis if (ret >= 0 && got_output) { if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, c->time_base, video_stream->time_base); + packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base); PRINT("Video Frame PTS: %d\n", (int)packet.pts); } else { PRINT("Video Frame PTS: not set\n"); } if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, c->time_base, video_stream->time_base); + packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base); PRINT("Video Frame DTS: %d\n", (int)packet.dts); } else { PRINT("Video Frame DTS: not set\n"); } - packet.stream_index = video_stream->index; - ret = av_interleaved_write_frame(outfile, &packet); + packet.stream_index = context->video_stream->index; + ret = av_interleaved_write_frame(context->outfile, &packet); success = (ret == 0); } else if (ret < 0) { @@ -350,11 +353,11 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis } /* read and encode a frame of audio from the buffer */ -static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) +static AVFrame *generate_video_frame(FFMpegContext *context, uint8_t *pixels, ReportList *reports) { uint8_t *rendered_frame; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; int width = c->width; int height = c->height; AVFrame *rgb_frame; @@ -367,7 +370,7 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) } } else { - rgb_frame = current_frame; + rgb_frame = context->current_frame; } rendered_frame = pixels; @@ -411,17 +414,17 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) } if (c->pix_fmt != PIX_FMT_BGR32) { - sws_scale(img_convert_ctx, (const uint8_t *const *) rgb_frame->data, + sws_scale(context->img_convert_ctx, (const uint8_t *const *) rgb_frame->data, rgb_frame->linesize, 0, c->height, - current_frame->data, current_frame->linesize); + context->current_frame->data, context->current_frame->linesize); delete_picture(rgb_frame); } - current_frame->format = PIX_FMT_BGR32; - current_frame->width = width; - current_frame->height = height; + context->current_frame->format = PIX_FMT_BGR32; + context->current_frame->width = width; + context->current_frame->height = height; - return current_frame; + return context->current_frame; } static void set_ffmpeg_property_option(AVCodecContext *c, IDProperty *prop, AVDictionary **dictionary) @@ -516,7 +519,7 @@ static void set_ffmpeg_properties(RenderData *rd, AVCodecContext *c, const char /* prepare a video stream for the output file */ -static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContext *of, +static AVStream *alloc_video_stream(FFMpegContext *context, RenderData *rd, int codec_id, AVFormatContext *of, int rectx, int recty, char *error, int error_size) { AVStream *st; @@ -542,7 +545,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->height = recty; /* FIXME: Really bad hack (tm) for NTSC support */ - if (ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { + if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { c->time_base.den = 2997; c->time_base.num = 100; } @@ -555,8 +558,8 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->time_base.num = ((double) rd->frs_sec_base) * 100000; } - c->gop_size = ffmpeg_gop_size; - c->bit_rate = ffmpeg_video_bitrate * 1000; + c->gop_size = context->ffmpeg_gop_size; + c->bit_rate = context->ffmpeg_video_bitrate * 1000; c->rc_max_rate = rd->ffcodecdata.rc_max_rate * 1000; c->rc_min_rate = rd->ffcodecdata.rc_min_rate * 1000; c->rc_buffer_size = rd->ffcodecdata.rc_buffer_size * 1024; @@ -585,7 +588,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->pix_fmt = PIX_FMT_YUV422P; } - if (ffmpeg_type == FFMPEG_XVID) { + if (context->ffmpeg_type == FFMPEG_XVID) { /* arghhhh ... */ c->pix_fmt = PIX_FMT_YUV420P; c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X'); @@ -655,14 +658,14 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex } av_dict_free(&opts); - current_frame = alloc_picture(c->pix_fmt, c->width, c->height); + context->current_frame = alloc_picture(c->pix_fmt, c->width, c->height); - img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_BGR32, c->width, c->height, c->pix_fmt, SWS_BICUBIC, + context->img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_BGR32, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); return st; } -static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size) +static AVStream *alloc_audio_stream(FFMpegContext *context, RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size) { AVStream *st; AVCodecContext *c; @@ -680,7 +683,7 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex c->codec_type = AVMEDIA_TYPE_AUDIO; c->sample_rate = rd->ffcodecdata.audio_mixrate; - c->bit_rate = ffmpeg_audio_bitrate * 1000; + c->bit_rate = context->ffmpeg_audio_bitrate * 1000; c->sample_fmt = AV_SAMPLE_FMT_S16; c->channels = rd->ffcodecdata.audio_channels; @@ -747,35 +750,35 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex st->codec->time_base.den = st->codec->sample_rate; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - audio_outbuf_size = FF_MIN_BUFFER_SIZE; + context->audio_outbuf_size = FF_MIN_BUFFER_SIZE; #endif if (c->frame_size == 0) // used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD)) // not sure if that is needed anymore, so let's try out if there are any // complaints regarding some ffmpeg versions users might have - audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels; + context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels; else { - audio_input_samples = c->frame_size; + context->audio_input_samples = c->frame_size; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (c->frame_size * c->channels * sizeof(int16_t) * 4 > audio_outbuf_size) - audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4; + if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size) + context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4; #endif } - audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt); + context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt); - audio_sample_size = av_get_bytes_per_sample(c->sample_fmt); + context->audio_sample_size = av_get_bytes_per_sample(c->sample_fmt); - audio_input_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * audio_sample_size); + context->audio_input_buffer = (uint8_t *) av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size); #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - audio_output_buffer = (uint8_t *) av_malloc(audio_outbuf_size); + context->audio_output_buffer = (uint8_t *) av_malloc(context->audio_outbuf_size); #endif - if (audio_deinterleave) - audio_deinterleave_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * audio_sample_size); + if (context->audio_deinterleave) + context->audio_deinterleave_buffer = (uint8_t *) av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size); - audio_time = 0.0f; + context->audio_time = 0.0f; return st; } @@ -799,7 +802,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va av_dict_set(dict, key, buffer, 0); } -static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, ReportList *reports) +static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int rectx, int recty, const char *suffix, ReportList *reports) { /* Handle to the output file */ AVFormatContext *of; @@ -808,26 +811,26 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report char name[256], error[1024]; const char **exts; - ffmpeg_type = rd->ffcodecdata.type; - ffmpeg_codec = rd->ffcodecdata.codec; - ffmpeg_audio_codec = rd->ffcodecdata.audio_codec; - ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate; - ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate; - ffmpeg_gop_size = rd->ffcodecdata.gop_size; - ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT; - + context->ffmpeg_type = rd->ffcodecdata.type; + context->ffmpeg_codec = rd->ffcodecdata.codec; + context->ffmpeg_audio_codec = rd->ffcodecdata.audio_codec; + context->ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate; + context->ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate; + context->ffmpeg_gop_size = rd->ffcodecdata.gop_size; + context->ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT; + /* Determine the correct filename */ - BKE_ffmpeg_filepath_get(name, rd, ffmpeg_preview); + ffmpeg_filepath_get(context, name, rd, context->ffmpeg_preview, suffix); PRINT("Starting output to %s(ffmpeg)...\n" " Using type=%d, codec=%d, audio_codec=%d,\n" " video_bitrate=%d, audio_bitrate=%d,\n" " gop_size=%d, autosplit=%d\n" " render width=%d, render height=%d\n", - name, ffmpeg_type, ffmpeg_codec, ffmpeg_audio_codec, - ffmpeg_video_bitrate, ffmpeg_audio_bitrate, - ffmpeg_gop_size, ffmpeg_autosplit, rectx, recty); + name, context->ffmpeg_type, context->ffmpeg_codec, context->ffmpeg_audio_codec, + context->ffmpeg_video_bitrate, context->ffmpeg_audio_bitrate, + context->ffmpeg_gop_size, context->ffmpeg_autosplit, rectx, recty); - exts = get_file_extensions(ffmpeg_type); + exts = get_file_extensions(context->ffmpeg_type); if (!exts) { BKE_report(reports, RPT_ERROR, "No valid formats found"); return 0; @@ -846,7 +849,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report of->oformat = fmt; of->packet_size = rd->ffcodecdata.mux_packet_size; - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate); } else { @@ -857,15 +860,15 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report of->max_delay = (int)(0.7 * AV_TIME_BASE); - fmt->audio_codec = ffmpeg_audio_codec; + fmt->audio_codec = context->ffmpeg_audio_codec; BLI_strncpy(of->filename, name, sizeof(of->filename)); /* set the codec to the user's selection */ - switch (ffmpeg_type) { + switch (context->ffmpeg_type) { case FFMPEG_AVI: case FFMPEG_MOV: case FFMPEG_MKV: - fmt->video_codec = ffmpeg_codec; + fmt->video_codec = context->ffmpeg_codec; break; case FFMPEG_OGG: fmt->video_codec = AV_CODEC_ID_THEORA; @@ -908,9 +911,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } } - if (ffmpeg_type == FFMPEG_DV) { + if (context->ffmpeg_type == FFMPEG_DV) { fmt->audio_codec = AV_CODEC_ID_PCM_S16LE; - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) { BKE_report(reports, RPT_ERROR, "FFMPEG only supports 48khz / stereo audio for DV!"); av_dict_free(&opts); return 0; @@ -918,9 +921,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } if (fmt->video_codec != AV_CODEC_ID_NONE) { - video_stream = alloc_video_stream(rd, fmt->video_codec, of, rectx, recty, error, sizeof(error)); - PRINT("alloc video stream %p\n", video_stream); - if (!video_stream) { + context->video_stream = alloc_video_stream(context, rd, fmt->video_codec, of, rectx, recty, error, sizeof(error)); + PRINT("alloc video stream %p\n", context->video_stream); + if (!context->video_stream) { if (error[0]) BKE_report(reports, RPT_ERROR, error); else @@ -931,9 +934,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } } - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE) { - audio_stream = alloc_audio_stream(rd, fmt->audio_codec, of, error, sizeof(error)); - if (!audio_stream) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { + context->audio_stream = alloc_audio_stream(context, rd, fmt->audio_codec, of, error, sizeof(error)); + if (!context->audio_stream) { if (error[0]) BKE_report(reports, RPT_ERROR, error); else @@ -956,7 +959,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report return 0; } - outfile = of; + context->outfile = of; av_dump_format(of, 0, name, 1); av_dict_free(&opts); @@ -980,11 +983,11 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report * parameter. * </p> */ -static void flush_ffmpeg(void) +static void flush_ffmpeg(FFMpegContext *context) { int ret = 0; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; /* get the delayed frames */ while (1) { int got_output; @@ -1000,28 +1003,28 @@ static void flush_ffmpeg(void) break; } if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, c->time_base, video_stream->time_base); + packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base); PRINT("Video Frame PTS: %d\n", (int) packet.pts); } else { PRINT("Video Frame PTS: not set\n"); } if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, c->time_base, video_stream->time_base); + packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base); PRINT("Video Frame DTS: %d\n", (int) packet.dts); } else { PRINT("Video Frame DTS: not set\n"); } - packet.stream_index = video_stream->index; - ret = av_interleaved_write_frame(outfile, &packet); + packet.stream_index = context->video_stream->index; + ret = av_interleaved_write_frame(context->outfile, &packet); if (ret != 0) { fprintf(stderr, "Error writing delayed frame %d\n", ret); break; } } - avcodec_flush_buffers(video_stream->codec); + avcodec_flush_buffers(context->video_stream->codec); } /* ********************************************************************** @@ -1029,7 +1032,7 @@ static void flush_ffmpeg(void) * ********************************************************************** */ /* Get the output filename-- similar to the other output formats */ -void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) +static void ffmpeg_filepath_get(FFMpegContext *context, char *string, RenderData *rd, bool preview, const char *suffix) { char autosplit[20]; @@ -1056,7 +1059,9 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) autosplit[0] = 0; if ((rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT) != 0) { - sprintf(autosplit, "_%03d", ffmpeg_autosplit_count); + if (context) { + sprintf(autosplit, "_%03d", context->ffmpeg_autosplit_count); + } } if (rd->scemode & R_EXTENSION) { @@ -1086,19 +1091,28 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) strcat(string, autosplit); } + + BLI_path_suffix(string, FILE_MAX, suffix, ""); } -int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview) +void BKE_ffmpeg_filepath_get(char *string, 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, RenderData *rd, int rectx, int recty, + ReportList *reports, bool preview, const char *suffix) { int success; + FFMpegContext *context = context_v; - ffmpeg_autosplit_count = 0; - ffmpeg_preview = preview; + context->ffmpeg_autosplit_count = 0; + context->ffmpeg_preview = preview; - success = start_ffmpeg_impl(rd, rectx, recty, reports); + success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports); #ifdef WITH_AUDASPACE - if (audio_stream) { - AVCodecContext *c = audio_stream->codec; + if (context->audio_stream) { + AVCodecContext *c = context->audio_stream->codec; AUD_DeviceSpecs specs; specs.channels = c->channels; @@ -1123,7 +1137,7 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, } specs.rate = rd->ffcodecdata.audio_mixrate; - audio_mixdown_device = BKE_sound_mixdown(scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume); + context->audio_mixdown_device = BKE_sound_mixdown(scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume); #ifdef FFMPEG_CODEC_TIME_BASE c->time_base.den = specs.rate; c->time_base.num = 1; @@ -1133,16 +1147,16 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, return success; } -static void end_ffmpeg_impl(int is_autosplit); +static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit); #ifdef WITH_AUDASPACE -static void write_audio_frames(double to_pts) +static void write_audio_frames(FFMpegContext *context, double to_pts) { int finished = 0; - while (audio_stream && !finished) { - if ((audio_time >= to_pts) || - (write_audio_frame())) + while (context->audio_stream && !finished) { + if ((context->audio_time >= to_pts) || + (write_audio_frame(context))) { finished = 1; } @@ -1150,8 +1164,10 @@ static void write_audio_frames(double to_pts) } #endif -int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, ReportList *reports) +int BKE_ffmpeg_append(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, ReportList *reports) { + FFMpegContext *context = context_v; AVFrame *avframe; int success = 1; @@ -1160,111 +1176,112 @@ int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, i /* why is this done before writing the video frame and again at end_ffmpeg? */ // write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base)); - if (video_stream) { - avframe = generate_video_frame((unsigned char *) pixels, reports); - success = (avframe && write_video_frame(rd, frame - start_frame, avframe, reports)); + if (context->video_stream) { + avframe = generate_video_frame(context, (unsigned char *) pixels, reports); + success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports)); - if (ffmpeg_autosplit) { - if (avio_tell(outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) { - end_ffmpeg_impl(true); - ffmpeg_autosplit_count++; - success &= start_ffmpeg_impl(rd, rectx, recty, reports); + if (context->ffmpeg_autosplit) { + if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) { + end_ffmpeg_impl(context, true); + context->ffmpeg_autosplit_count++; + success &= start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports); } } } #ifdef WITH_AUDASPACE - write_audio_frames((frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); + write_audio_frames(context, (frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); #endif return success; } -static void end_ffmpeg_impl(int is_autosplit) +static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) { unsigned int i; PRINT("Closing ffmpeg...\n"); #if 0 - if (audio_stream) { /* SEE UPPER */ - write_audio_frames(); + if (context->audio_stream) { /* SEE UPPER */ + write_audio_frames(context); } #endif #ifdef WITH_AUDASPACE if (is_autosplit == false) { - if (audio_mixdown_device) { - AUD_closeReadDevice(audio_mixdown_device); - audio_mixdown_device = 0; + if (context->audio_mixdown_device) { + AUD_closeReadDevice(context->audio_mixdown_device); + context->audio_mixdown_device = 0; } } #endif - if (video_stream && video_stream->codec) { + if (context->video_stream && context->video_stream->codec) { PRINT("Flushing delayed frames...\n"); - flush_ffmpeg(); + flush_ffmpeg(context); } - if (outfile) { - av_write_trailer(outfile); + if (context->outfile) { + av_write_trailer(context->outfile); } /* Close the video codec */ - if (video_stream && video_stream->codec) { - avcodec_close(video_stream->codec); - PRINT("zero video stream %p\n", video_stream); - video_stream = 0; + if (context->video_stream && context->video_stream->codec) { + avcodec_close(context->video_stream->codec); + PRINT("zero video stream %p\n", context->video_stream); + context->video_stream = 0; } /* Close the output file */ - if (outfile) { - for (i = 0; i < outfile->nb_streams; i++) { - if (&outfile->streams[i]) { - av_freep(&outfile->streams[i]); + if (context->outfile) { + for (i = 0; i < context->outfile->nb_streams; i++) { + if (&context->outfile->streams[i]) { + av_freep(&context->outfile->streams[i]); } } } /* free the temp buffer */ - if (current_frame) { - delete_picture(current_frame); - current_frame = 0; + if (context->current_frame) { + delete_picture(context->current_frame); + context->current_frame = 0; } - if (outfile && outfile->oformat) { - if (!(outfile->oformat->flags & AVFMT_NOFILE)) { - avio_close(outfile->pb); + if (context->outfile && context->outfile->oformat) { + if (!(context->outfile->oformat->flags & AVFMT_NOFILE)) { + avio_close(context->outfile->pb); } } - if (outfile) { - av_free(outfile); - outfile = 0; + if (context->outfile) { + av_free(context->outfile); + context->outfile = 0; } - if (audio_input_buffer) { - av_free(audio_input_buffer); - audio_input_buffer = 0; + if (context->audio_input_buffer) { + av_free(context->audio_input_buffer); + context->audio_input_buffer = 0; } #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (audio_output_buffer) { - av_free(audio_output_buffer); - audio_output_buffer = 0; + if (context->audio_output_buffer) { + av_free(context->audio_output_buffer); + context->audio_output_buffer = 0; } #endif - if (audio_deinterleave_buffer) { - av_free(audio_deinterleave_buffer); - audio_deinterleave_buffer = 0; + if (context->audio_deinterleave_buffer) { + av_free(context->audio_deinterleave_buffer); + context->audio_deinterleave_buffer = 0; } - if (img_convert_ctx) { - sws_freeContext(img_convert_ctx); - img_convert_ctx = 0; + if (context->img_convert_ctx) { + sws_freeContext(context->img_convert_ctx); + context->img_convert_ctx = 0; } } -void BKE_ffmpeg_end(void) +void BKE_ffmpeg_end(void *context_v) { - end_ffmpeg_impl(false); + FFMpegContext *context = context_v; + end_ffmpeg_impl(context, false); } /* properties */ @@ -1658,4 +1675,31 @@ bool BKE_ffmpeg_alpha_channel_is_supported(RenderData *rd) return false; } -#endif +void *BKE_ffmpeg_context_create(void) +{ + FFMpegContext *context; + + /* new ffmpeg data struct */ + context = MEM_callocN(sizeof(FFMpegContext), "new ffmpeg context"); + + context->ffmpeg_codec = AV_CODEC_ID_MPEG4; + context->ffmpeg_audio_codec = AV_CODEC_ID_NONE; + context->ffmpeg_video_bitrate = 1150; + context->ffmpeg_audio_bitrate = 128; + context->ffmpeg_gop_size = 12; + context->ffmpeg_autosplit = 0; + context->ffmpeg_autosplit_count = 0; + context->ffmpeg_preview = false; + + return context; +} + +void BKE_ffmpeg_context_free(void *context_v) +{ + FFMpegContext *context = context_v; + if (context) { + MEM_freeN(context); + } +} + +#endif /* WITH_FFMPEG */ diff --git a/source/blender/blenkernel/intern/writeframeserver.c b/source/blender/blenkernel/intern/writeframeserver.c index 9cac86d62bd..ba58038bbd8 100644 --- a/source/blender/blenkernel/intern/writeframeserver.c +++ b/source/blender/blenkernel/intern/writeframeserver.c @@ -66,12 +66,15 @@ #include "BKE_report.h" #include "DNA_scene_types.h" +#include "MEM_guardedalloc.h" -static int sock; -static int connsock; -static int write_ppm; -static int render_width; -static int render_height; +typedef struct FrameserverContext { + int sock; + int connsock; + int write_ppm; + int render_width; + int render_height; +} FrameserverContext; #if defined(_WIN32) @@ -110,10 +113,11 @@ static int closesocket(int fd) } #endif -int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports, bool UNUSED(preview)) +int BKE_frameserver_start(void *context_v, struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports, bool UNUSED(preview), const char *UNUSED(suffix)) { struct sockaddr_in addr; int arg = 1; + FrameserverContext *context = context_v; (void)scene; /* unused */ @@ -122,33 +126,33 @@ int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx return 0; } - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + if ((context->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot open socket"); return 0; } - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg)); + setsockopt(context->sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg)); addr.sin_family = AF_INET; addr.sin_port = htons(U.frameserverport); addr.sin_addr.s_addr = INADDR_ANY; - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + if (bind(context->sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot bind to socket"); return 0; } - if (listen(sock, SOMAXCONN) < 0) { + if (listen(context->sock, SOMAXCONN) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot establish listen backlog"); return 0; } - connsock = -1; + context->connsock = -1; - render_width = rectx; - render_height = recty; + context->render_width = rectx; + context->render_height = recty; return 1; } @@ -177,7 +181,7 @@ static char good_bye[] = "<body><pre>\n" "Render stopped. Goodbye</pre></body></html>"; -static int safe_write(char *s, int tosend) +static int safe_write(const int connsock, char *s, int tosend) { int total = tosend; do { @@ -192,12 +196,12 @@ static int safe_write(char *s, int tosend) return total; } -static int safe_puts(char *s) +static int safe_puts(const int connsock, char *s) { - return safe_write(s, strlen(s)); + return safe_write(connsock, s, strlen(s)); } -static int handle_request(RenderData *rd, char *req) +static int handle_request(FrameserverContext *context, RenderData *rd, char *req) { char *p; char *path; @@ -214,16 +218,16 @@ static int handle_request(RenderData *rd, char *req) *p = 0; - if (STREQ(path, "/index.html") || STREQ(path, "/")) { - safe_puts(index_page); + if (STREQ(path, "/index.html") || strcmp(path, "/")) { + safe_puts(context->connsock, index_page); return -1; } - write_ppm = 0; + context->write_ppm = 0; pathlen = strlen(path); if (pathlen > 12 && memcmp(path, "/images/ppm/", 12) == 0) { - write_ppm = 1; + context->write_ppm = 1; return atoi(path + 12); } if (STREQ(path, "/info.txt")) { @@ -241,24 +245,24 @@ static int handle_request(RenderData *rd, char *req) "ratescale %d\n", rd->sfra, rd->efra, - render_width, - render_height, + context->render_width, + context->render_height, rd->frs_sec, 1 ); - safe_puts(buf); + safe_puts(context->connsock, buf); return -1; } if (STREQ(path, "/close.txt")) { - safe_puts(good_bye); + safe_puts(context->connsock, good_bye); G.is_break = true; /* Abort render */ return -1; } return -1; } -int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) +int BKE_frameserver_loop(void *context_v, RenderData *rd, ReportList *UNUSED(reports)) { fd_set readfds; struct timeval tv; @@ -271,18 +275,20 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) #endif char buf[4096]; - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + FrameserverContext *context = context_v; + + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&readfds); - FD_SET(sock, &readfds); + FD_SET(context->sock, &readfds); - rval = select(sock + 1, &readfds, NULL, NULL, &tv); + rval = select(context->sock + 1, &readfds, NULL, NULL, &tv); if (rval < 0) { return -1; } @@ -293,19 +299,19 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) socklen = sizeof(addr); - if ((connsock = accept(sock, (struct sockaddr *)&addr, &socklen)) < 0) { + if ((context->connsock = accept(context->sock, (struct sockaddr *)&addr, &socklen)) < 0) { return -1; } FD_ZERO(&readfds); - FD_SET(connsock, &readfds); + FD_SET(context->connsock, &readfds); for (;;) { /* give 10 seconds for telnet testing... */ tv.tv_sec = 10; tv.tv_usec = 0; - rval = select(connsock + 1, &readfds, NULL, NULL, &tv); + rval = select(context->connsock + 1, &readfds, NULL, NULL, &tv); if (rval > 0) { break; } @@ -319,7 +325,7 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) } } - len = recv(connsock, buf, sizeof(buf) - 1, 0); + len = recv(context->connsock, buf, sizeof(buf) - 1, 0); if (len < 0) { return -1; @@ -327,13 +333,13 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) buf[len] = 0; - return handle_request(rd, buf); + return handle_request(context, rd, buf); } -static void serve_ppm(int *pixels, int rectx, int recty) +static void serve_ppm(FrameserverContext *context, int *pixels, int rectx, int recty) { unsigned char *rendered_frame; - unsigned char *row = (unsigned char *) malloc(render_width * 3); + unsigned char *row = (unsigned char *) malloc(context->render_width * 3); int y; char header[1024]; @@ -348,7 +354,7 @@ static void serve_ppm(int *pixels, int rectx, int recty) "255\n", rectx, recty); - safe_puts(header); + safe_puts(context->connsock, header); rendered_frame = (unsigned char *)pixels; @@ -364,36 +370,54 @@ static void serve_ppm(int *pixels, int rectx, int recty) target += 3; src += 4; } - safe_write((char *)row, 3 * rectx); + safe_write(context->connsock, (char *)row, 3 * rectx); } free(row); - closesocket(connsock); - connsock = -1; + closesocket(context->connsock); + context->connsock = -1; } -int BKE_frameserver_append(RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels, - int rectx, int recty, ReportList *UNUSED(reports)) +int BKE_frameserver_append(void *context_v, RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels, + int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports)) { + FrameserverContext *context = context_v; + fprintf(stderr, "Serving frame: %d\n", frame); - if (write_ppm) { - serve_ppm(pixels, rectx, recty); + if (context->write_ppm) { + serve_ppm(context, pixels, rectx, recty); } - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } return 1; } -void BKE_frameserver_end(void) +void BKE_frameserver_end(void *context_v) { - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + FrameserverContext *context = context_v; + + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } - closesocket(sock); + closesocket(context->sock); shutdown_socket_system(); } +void *BKE_frameserver_context_create(void) +{ + FrameserverContext *context = MEM_mallocN(sizeof(FrameserverContext), "Frameserver Context"); + return context; +} + +void BKE_frameserver_context_free(void *context_v) +{ + FrameserverContext *context = context_v; + if (context) { + MEM_freeN(context); + } +} + #endif /* WITH_FRAMESERVER */ diff --git a/source/blender/blenlib/BLI_edgehash.h b/source/blender/blenlib/BLI_edgehash.h index ded4b163f71..20272dd97cd 100644 --- a/source/blender/blenlib/BLI_edgehash.h +++ b/source/blender/blenlib/BLI_edgehash.h @@ -54,6 +54,7 @@ bool BLI_edgehash_reinsert(EdgeHash *eh, unsigned int v0, unsigned in void *BLI_edgehash_lookup(EdgeHash *eh, unsigned int v0, unsigned int v1) ATTR_WARN_UNUSED_RESULT; void *BLI_edgehash_lookup_default(EdgeHash *eh, unsigned int v0, unsigned int v1, void *val_default) ATTR_WARN_UNUSED_RESULT; void **BLI_edgehash_lookup_p(EdgeHash *eh, unsigned int v0, unsigned int v1) ATTR_WARN_UNUSED_RESULT; +bool BLI_edgehash_ensure_p(EdgeHash *eh, unsigned int v0, unsigned int v1, void ***r_val) ATTR_WARN_UNUSED_RESULT; bool BLI_edgehash_remove(EdgeHash *eh, unsigned int v0, unsigned int v1, EdgeHashFreeFP valfreefp); void *BLI_edgehash_popkey(EdgeHash *eh, unsigned int v0, unsigned int v1) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index 16d18ef1315..a00ddeefb2b 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -79,6 +79,7 @@ bool BLI_ghash_reinsert(GHash *gh, void *key, void *val, GHashKeyFreeFP keyfre void *BLI_ghash_lookup(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT; void *BLI_ghash_lookup_default(GHash *gh, const void *key, void *val_default) ATTR_WARN_UNUSED_RESULT; void **BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT; +bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT; bool BLI_ghash_remove(GHash *gh, void *key, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp); void BLI_ghash_clear(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp); void BLI_ghash_clear_ex(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp, diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index 5652c8056c0..d70dfcd9e58 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -129,6 +129,9 @@ void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4]); void xyz_to_lab(float x, float y, float z, float *l, float *a, float *b); +MINLINE float rgb_to_grayscale(const float rgb[3]); +MINLINE unsigned char rgb_to_grayscale_byte(const unsigned char rgb[3]); + MINLINE int compare_rgb_uchar(const unsigned char a[3], const unsigned char b[3], const int limit); MINLINE float dither_random_value(float s, float t); diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 74291ca305e..18b8feaa99c 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -89,6 +89,7 @@ int BLI_system_num_threads_override_get(void); #define LOCK_MOVIECLIP 7 #define LOCK_COLORMANAGE 8 #define LOCK_FFTW 9 +#define LOCK_VIEW3D 10 void BLI_lock_thread(int type); void BLI_unlock_thread(int type); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e614a2a0663..73bd4869566 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -44,6 +44,7 @@ set(SRC intern/BLI_array.c intern/BLI_dial.c intern/BLI_dynstr.c + intern/BLI_filelist.c intern/BLI_ghash.c intern/BLI_heap.c intern/BLI_kdopbvh.c @@ -205,10 +206,14 @@ if(WIN32) ) endif() -blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}") +# no need to compile object files for inline headers. +set_source_files_properties( + intern/math_base_inline.c + intern/math_color_blend_inline.c + intern/math_color_inline.c + intern/math_geom_inline.c + intern/math_vector_inline.c + PROPERTIES HEADER_FILE_ONLY TRUE +) -if(MSVC) - # Quiet warning about inline math library files that do not export symbols. - # (normally you'd exclude from project, but we still want to see the files in MSVC) - set_target_properties(bf_blenlib PROPERTIES STATIC_LIBRARY_FLAGS /ignore:4221) -endif() +blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c new file mode 100644 index 00000000000..81813ce1f62 --- /dev/null +++ b/source/blender/blenlib/intern/BLI_filelist.c @@ -0,0 +1,358 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenlib/intern/BLI_filelist.c + * \ingroup bli + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef WIN32 +# include <dirent.h> +#endif + +#include <time.h> +#include <sys/stat.h> +#include <string.h> /* strcpy etc.. */ + +#ifdef WIN32 +# include <io.h> +# include <direct.h> +# include "BLI_winstuff.h" +# include "utfconv.h" +#else +# include <sys/ioctl.h> +# include <unistd.h> +# include <pwd.h> +#endif + +/* lib includes */ +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_fileops.h" +#include "BLI_fileops_types.h" +#include "BLI_path_util.h" + +#include "../imbuf/IMB_imbuf.h" + + +/* + * Ordering function for sorting lists of files/directories. Returns -1 if + * entry1 belongs before entry2, 0 if they are equal, 1 if they should be swapped. + */ +static int bli_compare(struct direntry *entry1, struct direntry *entry2) +{ + /* type is equal to stat.st_mode */ + + /* directories come before non-directories */ + if (S_ISDIR(entry1->type)) { + if (S_ISDIR(entry2->type) == 0) return (-1); + } + else { + if (S_ISDIR(entry2->type)) return (1); + } + /* non-regular files come after regular files */ + if (S_ISREG(entry1->type)) { + if (S_ISREG(entry2->type) == 0) return (-1); + } + else { + if (S_ISREG(entry2->type)) return (1); + } + /* arbitrary, but consistent, ordering of different types of non-regular files */ + if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1); + if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1); + + /* OK, now we know their S_IFMT fields are the same, go on to a name comparison */ + /* make sure "." and ".." are always first */ + if (FILENAME_IS_CURRENT(entry1->relname)) return (-1); + if (FILENAME_IS_CURRENT(entry2->relname)) return (1); + if (FILENAME_IS_PARENT(entry1->relname)) return (-1); + if (FILENAME_IS_PARENT(entry2->relname)) return (1); + + return (BLI_natstrcmp(entry1->relname, entry2->relname)); +} + + +struct BuildDirCtx { + struct direntry *files; /* array[nrfiles] */ + int nrfiles; +}; + +/** + * Scans the directory named *dirname and appends entries for its contents to files. + */ +static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname) +{ + struct ListBase dirbase = {NULL, NULL}; + int newnum = 0; + DIR *dir; + + if ((dir = opendir(dirname)) != NULL) { + const struct dirent *fname; + while ((fname = readdir(dir)) != NULL) { + struct dirlink * const dlink = (struct dirlink *)malloc(sizeof(struct dirlink)); + if (dlink != NULL) { + dlink->name = BLI_strdup(fname->d_name); + BLI_addhead(&dirbase, dlink); + newnum++; + } + } + + if (newnum) { + if (dir_ctx->files) { + void * const tmp = MEM_reallocN(dir_ctx->files, (dir_ctx->nrfiles + newnum) * sizeof(struct direntry)); + if (tmp) { + dir_ctx->files = (struct direntry *)tmp; + } + else { /* realloc fail */ + MEM_freeN(dir_ctx->files); + dir_ctx->files = NULL; + } + } + + if (dir_ctx->files == NULL) + dir_ctx->files = (struct direntry *)MEM_mallocN(newnum * sizeof(struct direntry), __func__); + + if (dir_ctx->files) { + struct dirlink * dlink = (struct dirlink *) dirbase.first; + struct direntry *file = &dir_ctx->files[dir_ctx->nrfiles]; + while (dlink) { + char fullname[PATH_MAX]; + memset(file, 0, sizeof(struct direntry)); + file->relname = dlink->name; + file->path = BLI_strdupcat(dirname, dlink->name); + BLI_join_dirfile(fullname, sizeof(fullname), dirname, dlink->name); + if (BLI_stat(fullname, &file->s) != -1) { + file->type = file->s.st_mode; + } + file->flags = 0; + dir_ctx->nrfiles++; + file++; + dlink = dlink->next; + } + } + else { + printf("Couldn't get memory for dir\n"); + exit(1); + } + + BLI_freelist(&dirbase); + if (dir_ctx->files) { + qsort(dir_ctx->files, dir_ctx->nrfiles, sizeof(struct direntry), (int (*)(const void *, const void *))bli_compare); + } + } + else { + printf("%s empty directory\n", dirname); + } + + closedir(dir); + } + else { + printf("%s non-existent directory\n", dirname); + } +} + +/** + * Fills in the "mode[123]", "size" and "string" fields in the elements of the files + * array with descriptive details about each item. "string" will have a format similar to "ls -l". + */ +static void bli_adddirstrings(struct BuildDirCtx *dir_ctx) +{ + const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"}; + /* symbolic display, indexed by mode field value */ + int num; +#ifdef WIN32 + __int64 st_size; +#else + off_t st_size; + int mode; +#endif + + struct direntry *file; + struct tm *tm; + time_t zero = 0; + + for (num = 0, file = dir_ctx->files; num < dir_ctx->nrfiles; num++, file++) { + + + /* Mode */ +#ifdef WIN32 + BLI_strncpy(file->mode1, types[0], sizeof(file->mode1)); + BLI_strncpy(file->mode2, types[0], sizeof(file->mode2)); + BLI_strncpy(file->mode3, types[0], sizeof(file->mode3)); +#else + mode = file->s.st_mode; + + BLI_strncpy(file->mode1, types[(mode & 0700) >> 6], sizeof(file->mode1)); + BLI_strncpy(file->mode2, types[(mode & 0070) >> 3], sizeof(file->mode2)); + BLI_strncpy(file->mode3, types[(mode & 0007)], sizeof(file->mode3)); + + if (((mode & S_ISGID) == S_ISGID) && (file->mode2[2] == '-')) file->mode2[2] = 'l'; + + if (mode & (S_ISUID | S_ISGID)) { + if (file->mode1[2] == 'x') file->mode1[2] = 's'; + else file->mode1[2] = 'S'; + + if (file->mode2[2] == 'x') file->mode2[2] = 's'; + } + + if (mode & S_ISVTX) { + if (file->mode3[2] == 'x') file->mode3[2] = 't'; + else file->mode3[2] = 'T'; + } +#endif + + + /* User */ +#ifdef WIN32 + strcpy(file->owner, "user"); +#else + { + struct passwd *pwuser; + pwuser = getpwuid(file->s.st_uid); + if (pwuser) { + BLI_strncpy(file->owner, pwuser->pw_name, sizeof(file->owner)); + } + else { + BLI_snprintf(file->owner, sizeof(file->owner), "%u", file->s.st_uid); + } + } +#endif + + + /* Time */ + tm = localtime(&file->s.st_mtime); + // prevent impossible dates in windows + if (tm == NULL) tm = localtime(&zero); + strftime(file->time, sizeof(file->time), "%H:%M", tm); + strftime(file->date, sizeof(file->date), "%d-%b-%y", tm); + + + /* Size */ + /* + * Seems st_size is signed 32-bit value in *nix and Windows. This + * will buy us some time until files get bigger than 4GB or until + * everyone starts using __USE_FILE_OFFSET64 or equivalent. + */ + st_size = file->s.st_size; + + if (st_size > 1024 * 1024 * 1024) { + BLI_snprintf(file->size, sizeof(file->size), "%.2f GiB", ((double)st_size) / (1024 * 1024 * 1024)); + } + else if (st_size > 1024 * 1024) { + BLI_snprintf(file->size, sizeof(file->size), "%.1f MiB", ((double)st_size) / (1024 * 1024)); + } + else if (st_size > 1024) { + BLI_snprintf(file->size, sizeof(file->size), "%d KiB", (int)(st_size / 1024)); + } + else { + BLI_snprintf(file->size, sizeof(file->size), "%d B", (int)st_size); + } + } +} + +/** + * Scans the contents of the directory named *dirname, and allocates and fills in an + * array of entries describing them in *filelist. + * + * \return The length of filelist array. + */ +unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **filelist) +{ + struct BuildDirCtx dir_ctx; + + dir_ctx.nrfiles = 0; + dir_ctx.files = NULL; + + bli_builddir(&dir_ctx, dirname); + bli_adddirstrings(&dir_ctx); + + if (dir_ctx.files) { + *filelist = dir_ctx.files; + } + else { + // keep blender happy. Blender stores this in a variable + // where 0 has special meaning..... + *filelist = MEM_mallocN(sizeof(**filelist), __func__); + } + + return dir_ctx.nrfiles; +} + +/** + * Deep-duplicate of an array of direntries, including the array itself. + * + * \param dup_poin If given, called for each non-NULL direntry->poin. Otherwise, pointer is always simply copied over. + */ +void BLI_filelist_duplicate( + struct direntry **dest_filelist, struct direntry *src_filelist, unsigned int nrentries, + void *(*dup_poin)(void *)) +{ + unsigned int i; + + *dest_filelist = MEM_mallocN(sizeof(**dest_filelist) * (size_t)(nrentries), __func__); + for (i = 0; i < nrentries; ++i) { + struct direntry * const src = &src_filelist[i]; + struct direntry *dest = &(*dest_filelist)[i]; + *dest = *src; + if (dest->image) { + dest->image = IMB_dupImBuf(src->image); + } + if (dest->relname) { + dest->relname = MEM_dupallocN(src->relname); + } + if (dest->path) { + dest->path = MEM_dupallocN(src->path); + } + if (dest->poin && dup_poin) { + dest->poin = dup_poin(src->poin); + } + } +} + +/** + * frees storage for an array of direntries, including the array itself. + */ +void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries, void (*free_poin)(void *)) +{ + unsigned int i; + for (i = 0; i < nrentries; ++i) { + struct direntry *entry = filelist + i; + if (entry->image) { + IMB_freeImBuf(entry->image); + } + if (entry->relname) + MEM_freeN(entry->relname); + if (entry->path) + MEM_freeN(entry->path); + if (entry->poin && free_poin) + free_poin(entry->poin); + } + + if (filelist != NULL) { + MEM_freeN(filelist); + } +} diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index f2705c79574..dae7af6a927 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -162,7 +162,7 @@ BLI_INLINE unsigned int ghash_bucket_index(GHash *gh, const unsigned int hash) #ifdef GHASH_USE_MODULO_BUCKETS return hash % gh->nbuckets; #else - return full_hash & gh->bucket_mask; + return hash & gh->bucket_mask; #endif } @@ -445,19 +445,35 @@ BLI_INLINE void ghash_insert_ex( } /** + * Insert function that takes a pre-allocated entry. + */ +BLI_INLINE void ghash_insert_ex_keyonly_entry( + GHash *gh, void *key, const unsigned int bucket_index, + Entry *e) +{ + BLI_assert((gh->flag & GHASH_FLAG_ALLOW_DUPES) || (BLI_ghash_haskey(gh, key) == 0)); + + e->next = gh->buckets[bucket_index]; + e->key = key; + gh->buckets[bucket_index] = e; + + ghash_buckets_expand(gh, ++gh->nentries, false); +} + +/** * Insert function that doesn't set the value (use for GSet) */ BLI_INLINE void ghash_insert_ex_keyonly( GHash *gh, void *key, const unsigned int bucket_index) { - GSetEntry *e = BLI_mempool_alloc(gh->entrypool); + Entry *e = BLI_mempool_alloc(gh->entrypool); BLI_assert((gh->flag & GHASH_FLAG_ALLOW_DUPES) || (BLI_ghash_haskey(gh, key) == 0)); BLI_assert((gh->flag & GHASH_FLAG_IS_GSET) != 0); e->next = gh->buckets[bucket_index]; e->key = key; - gh->buckets[bucket_index] = (Entry *)e; + gh->buckets[bucket_index] = e; ghash_buckets_expand(gh, ++gh->nentries, false); } @@ -498,7 +514,7 @@ BLI_INLINE bool ghash_insert_safe_keyonly(GHash *gh, void *key, const bool overr { const unsigned int hash = ghash_keyhash(gh, key); const unsigned int bucket_index = ghash_bucket_index(gh, hash); - GSetEntry *e = ghash_lookup_entry_ex(gh, key, bucket_index); + Entry *e = ghash_lookup_entry_ex(gh, key, bucket_index); BLI_assert((gh->flag & GHASH_FLAG_IS_GSET) != 0); @@ -721,6 +737,36 @@ void **BLI_ghash_lookup_p(GHash *gh, const void *key) } /** + * Ensure \a key is exists in \a gh. + * + * This handles the common situation where the caller needs ensure a key is added to \a gh, + * constructing a new value in the case the key isn't found. + * Otherwise use the existing value. + * + * Such situations typically incur multiple lookups, however this function + * avoids them by ensuring the key is added, + * returning a pointer to the value so it can be used or initialized by the caller. + * + * \returns true when the value didn't need to be added. + * (when false, the caller _must_ initialize the value). + */ +bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) +{ + const unsigned int hash = ghash_keyhash(gh, key); + const unsigned int bucket_index = ghash_bucket_index(gh, hash); + GHashEntry *e = (GHashEntry *)ghash_lookup_entry_ex(gh, key, bucket_index); + const bool haskey = (e != NULL); + + if (!haskey) { + e = BLI_mempool_alloc(gh->entrypool); + ghash_insert_ex_keyonly_entry(gh, key, bucket_index, (Entry *)e); + } + + *r_val = &e->val; + return haskey; +} + +/** * Remove \a key from \a gh, or return false if the key wasn't found. * * \param key The key to remove. diff --git a/source/blender/blenlib/intern/edgehash.c b/source/blender/blenlib/intern/edgehash.c index 82d57dad753..fd7ad4db44b 100644 --- a/source/blender/blenlib/intern/edgehash.c +++ b/source/blender/blenlib/intern/edgehash.c @@ -232,6 +232,31 @@ BLI_INLINE void edgehash_insert_ex_keyonly(EdgeHash *eh, unsigned int v0, unsign EdgeEntry *e = BLI_mempool_alloc(eh->epool); BLI_assert((eh->flag & EDGEHASH_FLAG_ALLOW_DUPES) || (BLI_edgehash_haskey(eh, v0, v1) == 0)); + IS_EDGEHASH_ASSERT(eh); + + /* this helps to track down errors with bad edge data */ + BLI_assert(v0 < v1); + BLI_assert(v0 != v1); + + e->next = eh->buckets[hash]; + e->v0 = v0; + e->v1 = v1; + eh->buckets[hash] = e; + + if (UNLIKELY(edgehash_test_expand_buckets(++eh->nentries, eh->nbuckets))) { + edgehash_resize_buckets(eh, _ehash_hashsizes[++eh->cursize]); + } +} + +/** + * Insert function that doesn't set the value (use for EdgeSet) + */ +BLI_INLINE void edgehash_insert_ex_keyonly_entry( + EdgeHash *eh, unsigned int v0, unsigned int v1, + unsigned int hash, + EdgeEntry *e) +{ + BLI_assert((eh->flag & EDGEHASH_FLAG_ALLOW_DUPES) || (BLI_edgehash_haskey(eh, v0, v1) == 0)); /* this helps to track down errors with bad edge data */ BLI_assert(v0 < v1); @@ -373,6 +398,40 @@ void **BLI_edgehash_lookup_p(EdgeHash *eh, unsigned int v0, unsigned int v1) } /** + * Ensure \a (v0, v1) is exists in \a eh. + * + * This handles the common situation where the caller needs ensure a key is added to \a eh, + * constructing a new value in the case the key isn't found. + * Otherwise use the existing value. + * + * Such situations typically incur multiple lookups, however this function + * avoids them by ensuring the key is added, + * returning a pointer to the value so it can be used or initialized by the caller. + * + * \returns true when the value didn't need to be added. + * (when false, the caller _must_ initialize the value). + */ +bool BLI_edgehash_ensure_p(EdgeHash *eh, unsigned int v0, unsigned int v1, void ***r_val) +{ + unsigned int hash; + EdgeEntry *e; + bool haskey; + + EDGE_ORD(v0, v1); /* ensure v0 is smaller */ + hash = edgehash_keyhash(eh, v0, v1); + e = edgehash_lookup_entry_ex(eh, v0, v1, hash); + haskey = (e != NULL); + + if (!haskey) { + e = BLI_mempool_alloc(eh->epool); + edgehash_insert_ex_keyonly_entry(eh, v0, v1, hash, e); + } + + *r_val = &e->val; + return haskey; +} + +/** * Return value for given edge (\a v0, \a v1), or NULL if * if key does not exist in hash. (If need exists * to differentiate between key-value being NULL and diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index b3392e28223..8719c92a2a6 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -507,8 +507,7 @@ VChar *BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character) /* Freetype2 Outline struct */ -typedef struct FT_Outline_ -{ +typedef struct FT_Outline_ { short n_contours; /* number of contours in glyph */ short n_points; /* number of points in the glyph */ diff --git a/source/blender/blenlib/intern/hash_md5.c b/source/blender/blenlib/intern/hash_md5.c index 4eec38278e9..d98b915983c 100644 --- a/source/blender/blenlib/intern/hash_md5.c +++ b/source/blender/blenlib/intern/hash_md5.c @@ -81,8 +81,7 @@ * 'BLI_hash_md5_stream' and 'BLI_hash_md5_buffer'. */ /* Structure to save state of computation between the single steps. */ -struct md5_ctx -{ +struct md5_ctx { md5_uint32 A; md5_uint32 B; md5_uint32 C; diff --git a/source/blender/blenlib/intern/math_base.c b/source/blender/blenlib/intern/math_base.c index 3ff1af3513e..cddfde371f5 100644 --- a/source/blender/blenlib/intern/math_base.c +++ b/source/blender/blenlib/intern/math_base.c @@ -31,42 +31,6 @@ #include "BLI_strict_flags.h" -/* WARNING: MSVC compiling hack for double_round() */ -#if (defined(WIN32) || defined(WIN64)) && !(defined(FREE_WINDOWS)) - -/* from python 3.1 pymath.c */ -double copysign(double x, double y) -{ - /* use atan2 to distinguish -0.0 from 0.0 */ - if (y > 0.0 || (y == 0.0 && atan2(y, -1.0) > 0.0)) { - return fabs(x); - } - else { - return -fabs(x); - } -} - -/* from python 3.1 pymath.c */ -double round(double x) -{ - double absx, y; - absx = fabs(x); - y = floor(absx); - if (absx - y >= 0.5) - y += 1.0; - return copysign(y, x); -} -#else /* OpenSuse 11.1 seems to need this. */ -# ifdef __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wredundant-decls" -# endif -double round(double x); -# ifdef __GNUC__ -# pragma GCC diagnostic pop -# endif -#endif - /* from python 3.1 floatobject.c * ndigits must be between 0 and 21 */ double double_round(double x, int ndigits) diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index 3d5d47bc2e0..7706e8e8e49 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -1043,11 +1043,12 @@ void expmap_to_quat(float r[4], const float expmap[3]) float angle; /* Obtain axis/angle representation. */ - angle = normalize_v3_v3(axis, expmap); - angle = angle_wrap_rad(angle); - - /* Convert to quaternion. */ - axis_angle_to_quat(r, axis, angle); + if (LIKELY((angle = normalize_v3_v3(axis, expmap)) != 0.0f)) { + axis_angle_normalized_to_quat(r, axis, angle_wrap_rad(angle)); + } + else { + unit_qt(r); + } } /******************************** XYZ Eulers *********************************/ diff --git a/source/blender/blenlib/intern/polyfill2d_beautify.c b/source/blender/blenlib/intern/polyfill2d_beautify.c index ba71f52b530..46f9251bea7 100644 --- a/source/blender/blenlib/intern/polyfill2d_beautify.c +++ b/source/blender/blenlib/intern/polyfill2d_beautify.c @@ -430,17 +430,19 @@ void BLI_polyfill_beautify( } if (!is_boundary_edge(e_pair[0], e_pair[1], coord_last)) { - struct PolyEdge *e = BLI_edgehash_lookup(ehash, e_pair[0], e_pair[1]); - if (e == NULL) { + struct PolyEdge *e; + void **val_p; + + if (!BLI_edgehash_ensure_p(ehash, e_pair[0], e_pair[1], &val_p)) { e = &edges[edges_tot_used++]; - BLI_edgehash_insert(ehash, e_pair[0], e_pair[1], e); + *val_p = e; memcpy(e->verts, e_pair, sizeof(e->verts)); #ifndef NDEBUG e->faces[!e_index] = (unsigned int)-1; #endif } else { - + e = *val_p; /* ensure each edge only ever has 2x users */ #ifndef NDEBUG BLI_assert(e->faces[e_index] == (unsigned int)-1); diff --git a/source/blender/blenlib/intern/scanfill_utils.c b/source/blender/blenlib/intern/scanfill_utils.c index a606ac41aa1..d0420a30448 100644 --- a/source/blender/blenlib/intern/scanfill_utils.c +++ b/source/blender/blenlib/intern/scanfill_utils.c @@ -112,11 +112,13 @@ void BLI_scanfill_view3d_dump(ScanFillContext *sf_ctx) static ListBase *edge_isect_ls_ensure(GHash *isect_hash, ScanFillEdge *eed) { ListBase *e_ls; - e_ls = BLI_ghash_lookup(isect_hash, eed); - if (e_ls == NULL) { - e_ls = MEM_callocN(sizeof(ListBase), __func__); - BLI_ghash_insert(isect_hash, eed, e_ls); + void **val_p; + + if (!BLI_ghash_ensure_p(isect_hash, eed, &val_p)) { + *val_p = MEM_callocN(sizeof(ListBase), __func__); } + e_ls = *val_p; + return e_ls; } diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c index 46c5a11949c..046dba26c69 100644 --- a/source/blender/blenlib/intern/storage.c +++ b/source/blender/blenlib/intern/storage.c @@ -35,11 +35,6 @@ #include <stdio.h> #include <stdlib.h> -#ifndef WIN32 -# include <dirent.h> -#endif - -#include <time.h> #include <sys/stat.h> #if defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || defined(__sun) @@ -79,17 +74,13 @@ /* lib includes */ #include "MEM_guardedalloc.h" -#include "DNA_listBase.h" - -#include "BLI_listbase.h" +#include "BLI_utildefines.h" #include "BLI_linklist.h" #include "BLI_string.h" #include "BLI_fileops.h" #include "BLI_fileops_types.h" #include "BLI_path_util.h" -#include "../imbuf/IMB_imbuf.h" - /** * Copies the current working directory into *dir (max size maxncpy), and * returns a pointer to same. @@ -107,42 +98,6 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy) return getcwd(dir, maxncpy); } -/* - * Ordering function for sorting lists of files/directories. Returns -1 if - * entry1 belongs before entry2, 0 if they are equal, 1 if they should be swapped. - */ -static int bli_compare(struct direntry *entry1, struct direntry *entry2) -{ - /* type is equal to stat.st_mode */ - - /* directories come before non-directories */ - if (S_ISDIR(entry1->type)) { - if (S_ISDIR(entry2->type) == 0) return (-1); - } - else { - if (S_ISDIR(entry2->type)) return (1); - } - /* non-regular files come after regular files */ - if (S_ISREG(entry1->type)) { - if (S_ISREG(entry2->type) == 0) return (-1); - } - else { - if (S_ISREG(entry2->type)) return (1); - } - /* arbitrary, but consistent, ordering of different types of non-regular files */ - if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1); - if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1); - - /* OK, now we know their S_IFMT fields are the same, go on to a name comparison */ - /* make sure "." and ".." are always first */ - if (FILENAME_IS_CURRENT(entry1->relname)) return (-1); - if (FILENAME_IS_CURRENT(entry2->relname)) return (1); - if (FILENAME_IS_PARENT(entry1->relname)) return (-1); - if (FILENAME_IS_PARENT(entry2->relname)) return (1); - - return (BLI_natstrcmp(entry1->relname, entry2->relname)); -} - /** * Returns the number of free bytes on the volume containing the specified pathname. */ /* Not actually used anywhere. @@ -204,267 +159,6 @@ double BLI_dir_free_space(const char *dir) #endif } -struct BuildDirCtx { - struct direntry *files; /* array[nrfiles] */ - int nrfiles; -}; - -/** - * Scans the directory named *dirname and appends entries for its contents to files. - */ -static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname) -{ - struct ListBase dirbase = {NULL, NULL}; - int newnum = 0; - DIR *dir; - - if ((dir = opendir(dirname)) != NULL) { - const struct dirent *fname; - while ((fname = readdir(dir)) != NULL) { - struct dirlink * const dlink = (struct dirlink *)malloc(sizeof(struct dirlink)); - if (dlink != NULL) { - dlink->name = BLI_strdup(fname->d_name); - BLI_addhead(&dirbase, dlink); - newnum++; - } - } - - if (newnum) { - if (dir_ctx->files) { - void * const tmp = MEM_reallocN(dir_ctx->files, (dir_ctx->nrfiles + newnum) * sizeof(struct direntry)); - if (tmp) { - dir_ctx->files = (struct direntry *)tmp; - } - else { /* realloc fail */ - MEM_freeN(dir_ctx->files); - dir_ctx->files = NULL; - } - } - - if (dir_ctx->files == NULL) - dir_ctx->files = (struct direntry *)MEM_mallocN(newnum * sizeof(struct direntry), __func__); - - if (dir_ctx->files) { - struct dirlink * dlink = (struct dirlink *) dirbase.first; - struct direntry *file = &dir_ctx->files[dir_ctx->nrfiles]; - while (dlink) { - char fullname[PATH_MAX]; - memset(file, 0, sizeof(struct direntry)); - file->relname = dlink->name; - file->path = BLI_strdupcat(dirname, dlink->name); - BLI_join_dirfile(fullname, sizeof(fullname), dirname, dlink->name); - if (BLI_stat(fullname, &file->s) != -1) { - file->type = file->s.st_mode; - } - file->flags = 0; - dir_ctx->nrfiles++; - file++; - dlink = dlink->next; - } - } - else { - printf("Couldn't get memory for dir\n"); - exit(1); - } - - BLI_freelist(&dirbase); - if (dir_ctx->files) { - qsort(dir_ctx->files, dir_ctx->nrfiles, sizeof(struct direntry), (int (*)(const void *, const void *))bli_compare); - } - } - else { - printf("%s empty directory\n", dirname); - } - - closedir(dir); - } - else { - printf("%s non-existent directory\n", dirname); - } -} - -/** - * Fills in the "mode[123]", "size" and "string" fields in the elements of the files - * array with descriptive details about each item. "string" will have a format similar to "ls -l". - */ -static void bli_adddirstrings(struct BuildDirCtx *dir_ctx) -{ - const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"}; - /* symbolic display, indexed by mode field value */ - int num; -#ifdef WIN32 - __int64 st_size; -#else - off_t st_size; - int mode; -#endif - - struct direntry *file; - struct tm *tm; - time_t zero = 0; - - for (num = 0, file = dir_ctx->files; num < dir_ctx->nrfiles; num++, file++) { - - - /* Mode */ -#ifdef WIN32 - BLI_strncpy(file->mode1, types[0], sizeof(file->mode1)); - BLI_strncpy(file->mode2, types[0], sizeof(file->mode2)); - BLI_strncpy(file->mode3, types[0], sizeof(file->mode3)); -#else - mode = file->s.st_mode; - - BLI_strncpy(file->mode1, types[(mode & 0700) >> 6], sizeof(file->mode1)); - BLI_strncpy(file->mode2, types[(mode & 0070) >> 3], sizeof(file->mode2)); - BLI_strncpy(file->mode3, types[(mode & 0007)], sizeof(file->mode3)); - - if (((mode & S_ISGID) == S_ISGID) && (file->mode2[2] == '-')) file->mode2[2] = 'l'; - - if (mode & (S_ISUID | S_ISGID)) { - if (file->mode1[2] == 'x') file->mode1[2] = 's'; - else file->mode1[2] = 'S'; - - if (file->mode2[2] == 'x') file->mode2[2] = 's'; - } - - if (mode & S_ISVTX) { - if (file->mode3[2] == 'x') file->mode3[2] = 't'; - else file->mode3[2] = 'T'; - } -#endif - - - /* User */ -#ifdef WIN32 - strcpy(file->owner, "user"); -#else - { - struct passwd *pwuser; - pwuser = getpwuid(file->s.st_uid); - if (pwuser) { - BLI_strncpy(file->owner, pwuser->pw_name, sizeof(file->owner)); - } - else { - BLI_snprintf(file->owner, sizeof(file->owner), "%u", file->s.st_uid); - } - } -#endif - - - /* Time */ - tm = localtime(&file->s.st_mtime); - // prevent impossible dates in windows - if (tm == NULL) tm = localtime(&zero); - strftime(file->time, sizeof(file->time), "%H:%M", tm); - strftime(file->date, sizeof(file->date), "%d-%b-%y", tm); - - - /* Size */ - /* - * Seems st_size is signed 32-bit value in *nix and Windows. This - * will buy us some time until files get bigger than 4GB or until - * everyone starts using __USE_FILE_OFFSET64 or equivalent. - */ - st_size = file->s.st_size; - - if (st_size > 1024 * 1024 * 1024) { - BLI_snprintf(file->size, sizeof(file->size), "%.2f GiB", ((double)st_size) / (1024 * 1024 * 1024)); - } - else if (st_size > 1024 * 1024) { - BLI_snprintf(file->size, sizeof(file->size), "%.1f MiB", ((double)st_size) / (1024 * 1024)); - } - else if (st_size > 1024) { - BLI_snprintf(file->size, sizeof(file->size), "%d KiB", (int)(st_size / 1024)); - } - else { - BLI_snprintf(file->size, sizeof(file->size), "%d B", (int)st_size); - } - } -} - -/** - * Scans the contents of the directory named *dirname, and allocates and fills in an - * array of entries describing them in *filelist. - * - * \return The length of filelist array. - */ -unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **filelist) -{ - struct BuildDirCtx dir_ctx; - - dir_ctx.nrfiles = 0; - dir_ctx.files = NULL; - - bli_builddir(&dir_ctx, dirname); - bli_adddirstrings(&dir_ctx); - - if (dir_ctx.files) { - *filelist = dir_ctx.files; - } - else { - // keep blender happy. Blender stores this in a variable - // where 0 has special meaning..... - *filelist = MEM_mallocN(sizeof(**filelist), __func__); - } - - return dir_ctx.nrfiles; -} - -/** - * Deep-duplicate of an array of direntries, including the array itself. - * - * \param dup_poin If given, called for each non-NULL direntry->poin. Otherwise, pointer is always simply copied over. - */ -void BLI_filelist_duplicate( - struct direntry **dest_filelist, struct direntry *src_filelist, unsigned int nrentries, - void *(*dup_poin)(void *)) -{ - unsigned int i; - - *dest_filelist = MEM_mallocN(sizeof(**dest_filelist) * (size_t)(nrentries), __func__); - for (i = 0; i < nrentries; ++i) { - struct direntry * const src = &src_filelist[i]; - struct direntry *dest = &(*dest_filelist)[i]; - *dest = *src; - if (dest->image) { - dest->image = IMB_dupImBuf(src->image); - } - if (dest->relname) { - dest->relname = MEM_dupallocN(src->relname); - } - if (dest->path) { - dest->path = MEM_dupallocN(src->path); - } - if (dest->poin && dup_poin) { - dest->poin = dup_poin(src->poin); - } - } -} - -/** - * frees storage for an array of direntries, including the array itself. - */ -void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries, void (*free_poin)(void *)) -{ - unsigned int i; - for (i = 0; i < nrentries; ++i) { - struct direntry *entry = filelist + i; - if (entry->image) { - IMB_freeImBuf(entry->image); - } - if (entry->relname) - MEM_freeN(entry->relname); - if (entry->path) - MEM_freeN(entry->path); - if (entry->poin && free_poin) - free_poin(entry->poin); - } - - if (filelist != NULL) { - MEM_freeN(filelist); - } -} - /** * Returns the file size of an opened file descriptor. @@ -674,4 +368,3 @@ bool BLI_file_older(const char *file1, const char *file2) #endif return (st1.st_mtime < st2.st_mtime); } - diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c index ded2fd7e06d..da06500565c 100644 --- a/source/blender/blenlib/intern/threads.c +++ b/source/blender/blenlib/intern/threads.c @@ -122,6 +122,7 @@ static pthread_mutex_t _nodes_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _movieclip_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _colormanage_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _fftw_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t _view3d_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_t mainid; static int thread_levels = 0; /* threads can be invoked inside threads */ static int num_threads_override = 0; @@ -402,6 +403,8 @@ void BLI_lock_thread(int type) pthread_mutex_lock(&_colormanage_lock); else if (type == LOCK_FFTW) pthread_mutex_lock(&_fftw_lock); + else if (type == LOCK_VIEW3D) + pthread_mutex_lock(&_view3d_lock); } void BLI_unlock_thread(int type) @@ -426,6 +429,8 @@ void BLI_unlock_thread(int type) pthread_mutex_unlock(&_colormanage_lock); else if (type == LOCK_FFTW) pthread_mutex_unlock(&_fftw_lock); + else if (type == LOCK_VIEW3D) + pthread_mutex_unlock(&_view3d_lock); } /* Mutex Locks */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index e57d9017710..1e83511bc33 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -142,7 +142,7 @@ #include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_sequencer.h" -#include "BKE_treehash.h" +#include "BKE_outliner_treehash.h" #include "BKE_sound.h" @@ -1536,9 +1536,15 @@ void blo_make_packed_pointer_map(FileData *fd, Main *oldmain) fd->packedmap = oldnewmap_new(); - for (ima = oldmain->image.first; ima; ima = ima->id.next) + for (ima = oldmain->image.first; ima; ima = ima->id.next) { + ImagePackedFile *imapf; if (ima->packedfile) insert_packedmap(fd, ima->packedfile); + + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) + if (imapf->packedfile) + insert_packedmap(fd, imapf->packedfile); + } for (vfont = oldmain->vfont.first; vfont; vfont = vfont->id.next) if (vfont->packedfile) @@ -1571,8 +1577,13 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) entry->newp = NULL; } - for (ima = oldmain->image.first; ima; ima = ima->id.next) + for (ima = oldmain->image.first; ima; ima = ima->id.next) { + ImagePackedFile *imapf; ima->packedfile = newpackedadr(fd, ima->packedfile); + + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) + imapf->packedfile = newpackedadr(fd, imapf->packedfile); + } for (vfont = oldmain->vfont.first; vfont; vfont = vfont->id.next) vfont->packedfile = newpackedadr(fd, vfont->packedfile); @@ -3457,6 +3468,8 @@ static void lib_link_image(FileData *fd, Main *main) static void direct_link_image(FileData *fd, Image *ima) { + ImagePackedFile *imapf; + /* for undo system, pointers could be restored */ if (fd->imamap) ima->cache = newimaadr(fd, ima->cache); @@ -3470,8 +3483,7 @@ static void direct_link_image(FileData *fd, Image *ima) ima->gputexture = NULL; ima->rr = NULL; } - - ima->anim = NULL; + ima->repbind = NULL; /* undo system, try to restore render buffers */ @@ -3485,9 +3497,18 @@ static void direct_link_image(FileData *fd, Image *ima) memset(ima->renders, 0, sizeof(ima->renders)); ima->last_render_slot = ima->render_slot; } - + + link_list(fd, &(ima->views)); + link_list(fd, &(ima->packedfiles)); + + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + imapf->packedfile = direct_link_packedfile(fd, imapf->packedfile); + } + + ima->anims.first = ima->anims.last = NULL; ima->packedfile = direct_link_packedfile(fd, ima->packedfile); ima->preview = direct_link_preview_image(fd, ima->preview); + ima->stereo3d_format = newdataadr(fd, ima->stereo3d_format); ima->ok = 1; } @@ -5450,7 +5471,7 @@ static void lib_link_scene(FileData *fd, Main *main) seq->scene_sound = BKE_sound_add_scene_sound_defaults(sce, seq); } } - seq->anim = NULL; + seq->anims.first = seq->anims.last = NULL; lib_link_sequence_modifiers(fd, sce, &seq->modifiers); } @@ -5682,6 +5703,7 @@ static void direct_link_scene(FileData *fd, Scene *sce) if (seq->seq3 == NULL) seq->seq3 = seq->seq2; seq->effectdata = newdataadr(fd, seq->effectdata); + seq->stereo3d_format = newdataadr(fd, seq->stereo3d_format); if (seq->type & SEQ_TYPE_EFFECT) seq->flag |= SEQ_EFFECT_NOT_LOADED; @@ -5794,6 +5816,7 @@ static void direct_link_scene(FileData *fd, Scene *sce) link_list(fd, &(sce->markers)); link_list(fd, &(sce->transform_spaces)); link_list(fd, &(sce->r.layers)); + link_list(fd, &(sce->r.views)); for (srl = sce->r.layers.first; srl; srl = srl->next) { link_list(fd, &(srl->freestyleConfig.modules)); @@ -5857,8 +5880,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) BLI_listbase_clear(&win->modalhandlers); BLI_listbase_clear(&win->subwindows); BLI_listbase_clear(&win->gesture); + BLI_listbase_clear(&win->drawdata); - win->drawdata = NULL; win->drawmethod = -1; win->drawfail = 0; win->active = 0; @@ -5866,6 +5889,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) win->cursor = 0; win->lastcursor = 0; win->modalcursor = 0; + win->stereo3d_format = newdataadr(fd, win->stereo3d_format); } BLI_listbase_clear(&wm->timers); @@ -6089,7 +6113,7 @@ static void lib_link_screen(FileData *fd, Main *main) } if (so->treehash) { /* rebuild hash table, because it depends on ids too */ - BKE_treehash_rebuild_from_treestore(so->treehash, so->treestore); + BKE_outliner_treehash_rebuild_from_treestore(so->treehash, so->treestore); } } } @@ -6440,7 +6464,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc } if (so->treehash) { /* rebuild hash table, because it depends on ids too */ - BKE_treehash_rebuild_from_treestore(so->treehash, so->treestore); + BKE_outliner_treehash_rebuild_from_treestore(so->treehash, so->treestore); } } } @@ -7843,7 +7867,6 @@ static void do_versions_userdef(FileData *fd, BlendFileData *bfd) user->walk_navigation.jump_height = 0.4f; /* m */ user->walk_navigation.teleport_time = 0.2f; /* s */ } - } static void do_versions(FileData *fd, Library *lib, Main *main) @@ -7967,7 +7990,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) link_list(fd, &user->user_keymaps); link_list(fd, &user->addons); link_list(fd, &user->autoexec_paths); - + for (keymap=user->user_keymaps.first; keymap; keymap=keymap->next) { keymap->modal_items= NULL; keymap->poll = NULL; diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 2fb6d292b8d..301e7284ac6 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -27,6 +27,7 @@ #include "BLI_utildefines.h" #include "BLI_compiler_attrs.h" +#include "BLI_string.h" /* for MinGW32 definition of NULL, could use BLI_blenlib.h instead too */ #include <stddef.h> @@ -49,14 +50,18 @@ #include "DNA_particle_types.h" #include "DNA_linestyle_types.h" #include "DNA_actuator_types.h" +#include "DNA_camera_types.h" +#include "DNA_view3d_types.h" #include "DNA_genfile.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_node.h" +#include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_screen.h" +#include "BKE_sequencer.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -510,10 +515,10 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) br->mtex.random_angle = 2.0 * M_PI; br->mask_mtex.random_angle = 2.0 * M_PI; } + } #undef BRUSH_RAKE #undef BRUSH_RANDOM_ROTATION - } if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "float", "clump_noise_size")) { ParticleSettings *part; @@ -890,4 +895,89 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } } + + if (!MAIN_VERSION_ATLEAST(main, 274, 4)) { + SceneRenderView *srv; + wmWindowManager *wm; + bScreen *screen; + wmWindow *win; + Scene *scene; + Camera *cam; + Image *ima; + + for (scene = main->scene.first; scene; scene = scene->id.next) { + Sequence *seq; + + BKE_scene_add_render_view(scene, STEREO_LEFT_NAME); + srv = scene->r.views.first; + BLI_strncpy(srv->suffix, STEREO_LEFT_SUFFIX, sizeof(srv->suffix)); + + BKE_scene_add_render_view(scene, STEREO_RIGHT_NAME); + srv = scene->r.views.last; + BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix)); + + SEQ_BEGIN (scene->ed, seq) + { + seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format"); + /* patch sequencer scene strips (used to be the same flag before multiview merge)*/ + if (seq->flag & SEQ_USE_VIEWS) { + seq->flag |= SEQ_SCENE_STRIPS; + seq->flag &= ~SEQ_USE_VIEWS; + } + } + SEQ_END + } + + for (screen = main->screen.first; screen; screen = screen->id.next) { + ScrArea *sa; + for (sa = screen->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + + for (sl = sa->spacedata.first; sl; sl = sl->next) { + switch (sl->spacetype) { + case SPACE_VIEW3D: + { + View3D *v3d = (View3D *)sl; + v3d->stereo3d_camera = STEREO_3D_ID; + v3d->stereo3d_flag |= V3D_S3D_DISPPLANE; + v3d->stereo3d_convergence_alpha = 0.15f; + v3d->stereo3d_volume_alpha = 0.05f; + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima = (SpaceImage *) sl; + sima->iuser.flag |= IMA_SHOW_STEREO; + sima->iuser.passtype = SCE_PASS_COMBINED; + break; + } + } + } + } + } + + for (cam = main->camera.first; cam; cam = cam->id.next) { + cam->stereo.interocular_distance = 0.065; + cam->stereo.convergence_distance = 30.f * 0.065; + } + + for (ima = main->image.first; ima; ima = ima->id.next) { + ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo 3d Format"); + + if (ima->packedfile) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packed File"); + BLI_addtail(&ima->packedfiles, imapf); + + imapf->packedfile = ima->packedfile; + BLI_strncpy(imapf->filepath, ima->name, FILE_MAX); + ima->packedfile = NULL; + } + } + + for (wm = main->wm.first; wm; wm = wm->id.next) { + for (win = wm->windows.first; win; win = win->next) { + win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format"); + } + } + } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 2fd724ad202..ef5ed5eb9c6 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2139,7 +2139,8 @@ static void write_images(WriteData *wd, ListBase *idbase) { Image *ima; PackedFile * pf; - + ImageView *iv; + ImagePackedFile *imapf; ima= idbase->first; while (ima) { @@ -2148,13 +2149,20 @@ static void write_images(WriteData *wd, ListBase *idbase) writestruct(wd, ID_IM, "Image", 1, ima); if (ima->id.properties) IDP_WriteProperty(ima->id.properties, wd); - if (ima->packedfile) { - pf = ima->packedfile; - writestruct(wd, DATA, "PackedFile", 1, pf); - writedata(wd, DATA, pf->size, pf->data); + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + writestruct(wd, DATA, "ImagePackedFile", 1, imapf); + if (imapf->packedfile) { + pf = imapf->packedfile; + writestruct(wd, DATA, "PackedFile", 1, pf); + writedata(wd, DATA, pf->size, pf->data); + } } write_previews(wd, ima->preview); + + for (iv = ima->views.first; iv; iv = iv->next) + writestruct(wd, DATA, "ImageView", 1, iv); + writestruct(wd, DATA, "Stereo3dFormat", 1, ima->stereo3d_format); } ima= ima->id.next; } @@ -2355,6 +2363,7 @@ static void write_scenes(WriteData *wd, ListBase *scebase) TimeMarker *marker; TransformOrientation *ts; SceneRenderLayer *srl; + SceneRenderView *srv; ToolSettings *tos; FreestyleModuleConfig *fmc; FreestyleLineSet *fls; @@ -2436,7 +2445,9 @@ static void write_scenes(WriteData *wd, ListBase *scebase) break; } } - + + writestruct(wd, DATA, "Stereo3dFormat", 1, seq->stereo3d_format); + strip= seq->strip; writestruct(wd, DATA, "Strip", 1, strip); if (seq->flag & SEQ_USE_CROP && strip->crop) { @@ -2501,6 +2512,10 @@ static void write_scenes(WriteData *wd, ListBase *scebase) writestruct(wd, DATA, "FreestyleLineSet", 1, fls); } } + + /* writing MultiView to the blend file */ + for (srv = sce->r.views.first; srv; srv = srv->next) + writestruct(wd, DATA, "SceneRenderView", 1, srv); if (sce->nodetree) { writestruct(wd, DATA, "bNodeTree", 1, sce->nodetree); @@ -2563,8 +2578,10 @@ static void write_windowmanagers(WriteData *wd, ListBase *lb) for (wm= lb->first; wm; wm= wm->id.next) { writestruct(wd, ID_WM, "wmWindowManager", 1, wm); - for (win= wm->windows.first; win; win= win->next) + for (win= wm->windows.first; win; win= win->next) { writestruct(wd, DATA, "wmWindow", 1, win); + writestruct(wd, DATA, "Stereo3dFormat", 1, win->stereo3d_format); + } } } diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index 158c2aa4263..c53b7f939c9 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -836,14 +836,15 @@ void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_o BMLogVert *lv; unsigned int v_id = bm_log_vert_id_get(log, v); void *key = SET_UINT_IN_POINTER(v_id); + void **val_p; /* Find or create the BMLogVert entry */ if ((lv = BLI_ghash_lookup(entry->added_verts, key))) { bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); } - else if (!BLI_ghash_haskey(entry->modified_verts, key)) { + else if (!BLI_ghash_ensure_p(entry->modified_verts, key, &val_p)) { lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(entry->modified_verts, key, lv); + *val_p = lv; } } diff --git a/source/blender/bmesh/intern/bmesh_mesh_validate.c b/source/blender/bmesh/intern/bmesh_mesh_validate.c index 3a7a4f85e99..494da3b889b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_validate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_validate.c @@ -86,20 +86,19 @@ bool BM_mesh_validate(BMesh *bm) /* check edges */ BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { - BMEdge *e_other; + void **val_p; if (e->v1 == e->v2) { ERRMSG("edge %d: duplicate index: %d", i, BM_elem_index_get(e->v1)); } - /* build edgehash at the same time */ - e_other = BLI_edgehash_lookup(edge_hash, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2)); - if (e_other) { + if (BLI_edgehash_ensure_p(edge_hash, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2), &val_p)) { + BMEdge *e_other = *val_p; ERRMSG("edge %d, %d: are duplicates", i, BM_elem_index_get(e_other)); } else { - BLI_edgehash_insert(edge_hash, BM_elem_index_get(e->v1), BM_elem_index_get(e->v2), e); + *val_p = e; } } diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c index bb7000e5534..4322a288471 100644 --- a/source/blender/bmesh/tools/bmesh_region_match.c +++ b/source/blender/bmesh/tools/bmesh_region_match.c @@ -534,12 +534,13 @@ static void bm_uuidwalk_pass_add( l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { /* fill verts_new */ + void **val_p; if (!BLI_ghash_haskey(uuidwalk->verts_uuid, l_iter->v) && - !BLI_ghash_haskey(verts_uuid_pass, l_iter->v) && + !BLI_ghash_ensure_p(verts_uuid_pass, l_iter->v, &val_p) && (bm_vert_is_uuid_connect(uuidwalk, l_iter->v) == true)) { const UUID_Int uuid = bm_uuidwalk_calc_vert_uuid(uuidwalk, l_iter->v); - BLI_ghash_insert(verts_uuid_pass, l_iter->v, (void *)uuid); + *val_p = (void *)uuid; } /* fill faces_step_next */ @@ -614,15 +615,16 @@ static unsigned int bm_uuidwalk_init_from_edge( /* turning an array into LinkNode's seems odd, * but this is just for initialization, * elsewhere using LinkNode's makes more sense */ - for (i = 0; i < f_arr_len; i++) { + for (i = 0; i < f_arr_len; ) { LinkNode *faces_pass = NULL; + const unsigned int i_init = i; const int f_len = f_arr[i]->len; do { BLI_linklist_prepend_pool(&faces_pass, f_arr[i++], uuidwalk->link_pool); } while (i < f_arr_len && (f_len == f_arr[i]->len)); - bm_uuidwalk_pass_add(uuidwalk, faces_pass, i); + bm_uuidwalk_pass_add(uuidwalk, faces_pass, i - i_init); BLI_linklist_free_pool(faces_pass, NULL, uuidwalk->link_pool); fstep_num += 1; } @@ -667,13 +669,15 @@ static bool bm_uuidwalk_facestep_begin( if (!BLI_ghash_haskey(uuidwalk->faces_uuid, f)) { const UUID_Int uuid = bm_uuidwalk_calc_face_uuid(uuidwalk, f); UUIDFaceStepItem *fstep_item; + void **val_p; ok = true; - fstep_item = BLI_ghash_lookup(uuidwalk->cache.faces_from_uuid, (void *)uuid); - if (UNLIKELY(fstep_item == NULL)) { - fstep_item = BLI_mempool_alloc(uuidwalk->step_pool_items); - BLI_ghash_insert(uuidwalk->cache.faces_from_uuid, (void *)uuid, fstep_item); + if (BLI_ghash_ensure_p(uuidwalk->cache.faces_from_uuid, (void *)uuid, &val_p)) { + fstep_item = *val_p; + } + else { + fstep_item = *val_p = BLI_mempool_alloc(uuidwalk->step_pool_items); /* add to start, so its handled on the next round of passes */ BLI_addhead(&fstep->items, fstep_item); @@ -1111,9 +1115,10 @@ static BMEdge *bm_face_region_pivot_edge_find( if (bm_edge_is_region_boundary(e)) { unsigned int j; for (j = 0; j < 2; j++) { - if (!BLI_ghash_haskey(gh, (&e->v1)[j])) { + void **val_p; + if (!BLI_ghash_ensure_p(gh, (&e->v1)[j], &val_p)) { SUID_Int v_id = bm_face_region_vert_boundary_id((&e->v1)[j]); - BLI_ghash_insert(gh, (&e->v1)[j], (void *)v_id); + *val_p = (void *)v_id; BLI_LINKSTACK_PUSH(vert_queue_prev, (&e->v1)[j]); vert_queue_used += 1; } @@ -1137,10 +1142,11 @@ static BMEdge *bm_face_region_pivot_edge_find( if (BM_elem_flag_test(e, BM_ELEM_TAG)) { BMVert *v_other = BM_edge_other_vert(e, v); if (BM_elem_flag_test(v_other, BM_ELEM_TAG)) { - if (!BLI_ghash_haskey(gh, v_other)) { + void **val_p; + if (!BLI_ghash_ensure_p(gh, v_other, &val_p)) { /* add as negative, so we know not to read from them this pass */ const SUID_Int v_id_other = -bm_face_region_vert_pass_id(gh, v_other); - BLI_ghash_insert(gh, v_other, (void *)v_id_other); + *val_p = (void *)v_id_other; BLI_LINKSTACK_PUSH(vert_queue_next, v_other); vert_queue_used += 1; } @@ -1451,7 +1457,7 @@ int BM_mesh_region_match( BMFace **faces_result; unsigned int faces_result_len_out; - if (BM_elem_flag_test(e_dst, BM_ELEM_TAG)) { + if (BM_elem_flag_test(e_dst, BM_ELEM_TAG) || BM_edge_is_wire(e_dst)) { continue; } diff --git a/source/blender/collada/ImageExporter.cpp b/source/blender/collada/ImageExporter.cpp index a5c1493208b..aac41e2e93c 100644 --- a/source/blender/collada/ImageExporter.cpp +++ b/source/blender/collada/ImageExporter.cpp @@ -74,7 +74,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies) short image_source = image->source; bool is_generated = image_source == IMA_SRC_GENERATED; - bool is_packed = image->packedfile != NULL; + bool is_packed = BKE_image_has_packedfile(image); char export_path[FILE_MAX]; char source_path[FILE_MAX]; diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 048c974423f..c23aa4ec734 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -119,6 +119,8 @@ set(SRC nodes/COM_TimeNode.h nodes/COM_SwitchNode.cpp nodes/COM_SwitchNode.h + nodes/COM_SwitchViewNode.cpp + nodes/COM_SwitchViewNode.h nodes/COM_MovieClipNode.cpp nodes/COM_MovieClipNode.h nodes/COM_OutputFileNode.cpp @@ -370,6 +372,8 @@ set(SRC operations/COM_CompositorOperation.cpp operations/COM_OutputFileOperation.h operations/COM_OutputFileOperation.cpp + operations/COM_OutputFileMultiViewOperation.h + operations/COM_OutputFileMultiViewOperation.cpp operations/COM_ViewerOperation.h operations/COM_ViewerOperation.cpp operations/COM_PreviewOperation.h diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h index 9b22444cf7f..8b0a617f889 100644 --- a/source/blender/compositor/COM_compositor.h +++ b/source/blender/compositor/COM_compositor.h @@ -316,7 +316,8 @@ extern "C" { * generation in display space */ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName); /** * @brief Deinitialize the compositor caches and allocated memory. diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index 3fbafa0a029..d7ec653f480 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -87,6 +87,11 @@ private: const ColorManagedViewSettings *m_viewSettings; const ColorManagedDisplaySettings *m_displaySettings; + /** + * @brief active rendering view name + */ + const char *m_viewName; + public: /** * @brief constructor initializes the context with default values. @@ -180,7 +185,17 @@ public: * @brief set has this system active openclDevices? */ void setHasActiveOpenCLDevices(bool hasAvtiveOpenCLDevices) { this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices; } - + + /** + * @brief get the active rendering view + */ + const char *getViewName() const { return this->m_viewName; } + + /** + * @brief set the active rendering view + */ + void setViewName(const char *viewName) { this->m_viewName = viewName; } + int getChunksize() const { return this->getbNodeTree()->chunksize; } void setFastCalculation(bool fastCalculation) {this->m_fastCalculation = fastCalculation;} diff --git a/source/blender/compositor/intern/COM_Converter.cpp b/source/blender/compositor/intern/COM_Converter.cpp index 99f66bcb5b4..9de22612bdc 100644 --- a/source/blender/compositor/intern/COM_Converter.cpp +++ b/source/blender/compositor/intern/COM_Converter.cpp @@ -101,6 +101,7 @@ extern "C" { #include "COM_Stabilize2dNode.h" #include "COM_SunBeamsNode.h" #include "COM_SwitchNode.h" +#include "COM_SwitchViewNode.h" #include "COM_TextureNode.h" #include "COM_TimeNode.h" #include "COM_TonemapNode.h" @@ -328,6 +329,9 @@ Node *Converter::convert(bNode *b_node) case CMP_NODE_SWITCH: node = new SwitchNode(b_node); break; + case CMP_NODE_SWITCH_VIEW: + node = new SwitchViewNode(b_node); + break; case CMP_NODE_GLARE: node = new GlareNode(b_node); break; diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cpp b/source/blender/compositor/intern/COM_ExecutionSystem.cpp index 2ed9e6187ba..caeaa07d9f9 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cpp +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cpp @@ -43,8 +43,10 @@ extern "C" { #endif ExecutionSystem::ExecutionSystem(RenderData *rd, Scene *scene, bNodeTree *editingtree, bool rendering, bool fastcalculation, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName) { + this->m_context.setViewName(viewName); this->m_context.setScene(scene); this->m_context.setbNodeTree(editingtree); this->m_context.setPreviewHash(editingtree->previews); diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index 555964f7b0c..41d63fb3a72 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -151,7 +151,8 @@ public: * @param rendering [true false] */ ExecutionSystem(RenderData *rd, Scene *scene, bNodeTree *editingtree, bool rendering, bool fastcalculation, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName); /** * Destructor diff --git a/source/blender/compositor/intern/COM_compositor.cpp b/source/blender/compositor/intern/COM_compositor.cpp index ff601e6f58e..7f7fc141aca 100644 --- a/source/blender/compositor/intern/COM_compositor.cpp +++ b/source/blender/compositor/intern/COM_compositor.cpp @@ -45,7 +45,8 @@ static void intern_freeCompositorCaches() void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering, const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings) + const ColorManagedDisplaySettings *displaySettings, + const char *viewName) { /* initialize mutex, TODO this mutex init is actually not thread safe and * should be done somewhere as part of blender startup, all the other @@ -82,7 +83,7 @@ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rende bool twopass = (editingtree->flag & NTREE_TWO_PASS) > 0 && !rendering; /* initialize execution system */ if (twopass) { - ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, twopass, viewSettings, displaySettings); + ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, twopass, viewSettings, displaySettings, viewName); system->execute(); delete system; @@ -95,7 +96,7 @@ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rende } ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, false, - viewSettings, displaySettings); + viewSettings, displaySettings, viewName); system->execute(); delete system; diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cpp b/source/blender/compositor/nodes/COM_CompositorNode.cpp index 933b8d0282b..9e8b40d8af4 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.cpp +++ b/source/blender/compositor/nodes/COM_CompositorNode.cpp @@ -43,6 +43,7 @@ void CompositorNode::convertToOperations(NodeConverter &converter, const Composi CompositorOperation *compositorOperation = new CompositorOperation(); compositorOperation->setSceneName(context.getScene()->id.name); compositorOperation->setRenderData(context.getRenderData()); + compositorOperation->setViewName(context.getViewName()); compositorOperation->setbNodeTree(context.getbNodeTree()); /* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */ compositorOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked()); diff --git a/source/blender/compositor/nodes/COM_ImageNode.cpp b/source/blender/compositor/nodes/COM_ImageNode.cpp index cb7ccfaedf9..93ab6dd765a 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cpp +++ b/source/blender/compositor/nodes/COM_ImageNode.cpp @@ -40,19 +40,19 @@ ImageNode::ImageNode(bNode *editorNode) : Node(editorNode) } NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter, RenderLayer *rl, Image *image, ImageUser *user, - int framenumber, int outputsocketIndex, int passindex, DataType datatype) const + int framenumber, int outputsocketIndex, int passtype, int view, DataType datatype) const { NodeOutput *outputSocket = this->getOutputSocket(outputsocketIndex); MultilayerBaseOperation *operation = NULL; switch (datatype) { case COM_DT_VALUE: - operation = new MultilayerValueOperation(passindex); + operation = new MultilayerValueOperation(passtype, view); break; case COM_DT_VECTOR: - operation = new MultilayerVectorOperation(passindex); + operation = new MultilayerVectorOperation(passtype, view); break; case COM_DT_COLOR: - operation = new MultilayerColorOperation(passindex); + operation = new MultilayerColorOperation(passtype, view); break; default: break; @@ -96,6 +96,10 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo NodeOperation *operation = NULL; socket = this->getOutputSocket(index); bNodeSocket *bnodeSocket = socket->getbNodeSocket(); + RenderPass *rpass = (RenderPass *)BLI_findstring(&rl->passes, bnodeSocket->identifier, offsetof(RenderPass, internal_name)); + + int view = (rpass ? rpass->view_id : 0); + /* Passes in the file can differ from passes stored in sockets (#36755). * Look up the correct file pass using the socket identifier instead. */ @@ -104,8 +108,22 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo int passindex = storage->pass_index;*/ RenderPass *rpass = (RenderPass *)BLI_findlink(&rl->passes, passindex); #endif - int passindex; - RenderPass *rpass; + + /* returns the image view to use for the current active view */ + if (BLI_listbase_count_ex(&image->rr->views, 2) > 1) { + const int view_image = imageuser->view; + const bool is_allview = (view_image == 0); /* if view selected == All (0) */ + + if (is_allview) { + /* heuristic to match image name with scene names + * check if the view name exists in the image */ + view = BLI_findstringindex(&image->rr->views, context.getViewName(), offsetof(RenderView, name)); + } + else { + view = view_image - 1; + } + } + if (STREQ(bnodeSocket->identifier, "Alpha")) { BLI_assert(combined_operation != NULL); NodeOutput *outputSocket = this->getOutputSocket(index); @@ -118,22 +136,21 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo operation = separate_operation; } else { - for (rpass = (RenderPass *)rl->passes.first, passindex = 0; rpass; rpass = rpass->next, ++passindex) - if (STREQ(rpass->name, bnodeSocket->identifier)) - break; if (rpass) { - imageuser->pass = passindex; switch (rpass->channels) { case 1: - operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_VALUE); + operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, + rpass->passtype, view, COM_DT_VALUE); break; /* using image operations for both 3 and 4 channels (RGB and RGBA respectively) */ /* XXX any way to detect actual vector images? */ case 3: - operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_VECTOR); + operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, + rpass->passtype, view, COM_DT_VECTOR); break; case 4: - operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_COLOR); + operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, + rpass->passtype, view, COM_DT_COLOR); break; default: /* dummy operation is added below */ @@ -168,6 +185,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo operation->setImage(image); operation->setImageUser(imageuser); operation->setFramenumber(framenumber); + operation->setRenderData(context.getRenderData()); + operation->setViewName(context.getViewName()); converter.addOperation(operation); if (outputStraightAlpha) { @@ -190,6 +209,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo alphaOperation->setImage(image); alphaOperation->setImageUser(imageuser); alphaOperation->setFramenumber(framenumber); + alphaOperation->setRenderData(context.getRenderData()); + alphaOperation->setViewName(context.getViewName()); converter.addOperation(alphaOperation); converter.mapOutputSocket(alphaImage, alphaOperation->getOutputSocket()); @@ -200,6 +221,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo depthOperation->setImage(image); depthOperation->setImageUser(imageuser); depthOperation->setFramenumber(framenumber); + depthOperation->setRenderData(context.getRenderData()); + depthOperation->setViewName(context.getViewName()); converter.addOperation(depthOperation); converter.mapOutputSocket(depthImage, depthOperation->getOutputSocket()); @@ -239,6 +262,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo } if (operation) { + /* not supporting multiview for this generic case */ converter.addOperation(operation); converter.mapOutputSocket(output, operation->getOutputSocket()); } diff --git a/source/blender/compositor/nodes/COM_ImageNode.h b/source/blender/compositor/nodes/COM_ImageNode.h index 1daa39a2a1f..267794510e1 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.h +++ b/source/blender/compositor/nodes/COM_ImageNode.h @@ -36,7 +36,7 @@ extern "C" { class ImageNode : public Node { private: NodeOperation *doMultilayerCheck(NodeConverter &converter, RenderLayer *rl, Image *image, ImageUser *user, - int framenumber, int outputsocketIndex, int passindex, DataType datatype) const; + int framenumber, int outputsocketIndex, int passtype, int view, DataType datatype) const; public: ImageNode(bNode *editorNode); void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cpp b/source/blender/compositor/nodes/COM_OutputFileNode.cpp index 92fa74b9a2e..acd2602e216 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cpp +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cpp @@ -23,8 +23,11 @@ #include "COM_OutputFileNode.h" #include "COM_OutputFileOperation.h" +#include "COM_OutputFileMultiViewOperation.h" #include "COM_ExecutionSystem.h" +#include "BKE_scene.h" + #include "BLI_path_util.h" OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode) @@ -35,6 +38,7 @@ OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode) void OutputFileNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const { NodeImageMultiFile *storage = (NodeImageMultiFile *)this->getbNode()->storage; + const bool is_multiview = (context.getRenderData()->scemode & R_MULTIVIEW) != 0; if (!context.isRendering()) { /* only output files when rendering a sequence - @@ -46,10 +50,18 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, const Composi if (storage->format.imtype == R_IMF_IMTYPE_MULTILAYER) { /* single output operation for the multilayer file */ - OutputOpenExrMultiLayerOperation *outputOperation = new OutputOpenExrMultiLayerOperation( - context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec); + OutputOpenExrMultiLayerOperation *outputOperation; + + if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) { + outputOperation = new OutputOpenExrMultiLayerMultiViewOperation( + context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, context.getViewName()); + } + else { + outputOperation = new OutputOpenExrMultiLayerOperation( + context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, context.getViewName()); + } converter.addOperation(outputOperation); - + int num_inputs = getNumberOfInputSockets(); bool previewAdded = false; for (int i = 0; i < num_inputs; ++i) { @@ -76,17 +88,31 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, const Composi NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage; ImageFormatData *format = (sockdata->use_node_format ? &storage->format : &sockdata->format); char path[FILE_MAX]; - + /* combine file path for the input */ BLI_join_dirfile(path, FILE_MAX, storage->base_path, sockdata->path); - - OutputSingleLayerOperation *outputOperation = new OutputSingleLayerOperation( - context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path, - context.getViewSettings(), context.getDisplaySettings()); + + NodeOperation *outputOperation = NULL; + + if (is_multiview && format->views_format == R_IMF_VIEWS_MULTIVIEW) { + outputOperation = new OutputOpenExrSingleLayerMultiViewOperation( + context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path, + context.getViewSettings(), context.getDisplaySettings(), context.getViewName()); + } + else if ((!is_multiview) || (format->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + outputOperation = new OutputSingleLayerOperation( + context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path, + context.getViewSettings(), context.getDisplaySettings(), context.getViewName()); + } + else { /* R_IMF_VIEWS_STEREO_3D */ + outputOperation = new OutputStereoOperation( + context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path, + sockdata->layer, context.getViewSettings(), context.getDisplaySettings(), context.getViewName()); + } + converter.addOperation(outputOperation); - converter.mapInputSocket(input, outputOperation->getInputSocket(0)); - + if (!previewAdded) { converter.addNodeInputPreview(input); previewAdded = true; diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cpp b/source/blender/compositor/nodes/COM_RenderLayersNode.cpp index cc66c688379..02bf1ec8cfb 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cpp +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cpp @@ -42,7 +42,8 @@ void RenderLayersNode::testSocketLink(NodeConverter &converter, const Compositor operation->setScene(scene); operation->setLayerId(layerId); operation->setRenderData(context.getRenderData()); - + operation->setViewName(context.getViewName()); + converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); converter.addOperation(operation); diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cpp b/source/blender/compositor/nodes/COM_SplitViewerNode.cpp index 15eca0a97e5..0f917d317f9 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.cpp +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cpp @@ -22,6 +22,8 @@ #include "COM_SplitViewerNode.h" #include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_scene.h" #include "COM_SplitOperation.h" #include "COM_ViewerOperation.h" @@ -55,6 +57,8 @@ void SplitViewerNode::convertToOperations(NodeConverter &converter, const Compos viewerOperation->setImageUser(imageUser); viewerOperation->setViewSettings(context.getViewSettings()); viewerOperation->setDisplaySettings(context.getDisplaySettings()); + viewerOperation->setRenderData(context.getRenderData()); + viewerOperation->setViewName(context.getViewName()); /* defaults - the viewer node has these options but not exposed for split view * we could use the split to define an area of interest on one axis at least */ diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cpp b/source/blender/compositor/nodes/COM_SwitchViewNode.cpp new file mode 100644 index 00000000000..5a23b8b4d9e --- /dev/null +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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. + * + * Contributor: + * Dalai Felinto + */ + +#include "COM_SwitchViewNode.h" +#include "BLI_listbase.h" + +SwitchViewNode::SwitchViewNode(bNode *editorNode) : Node(editorNode) +{ + /* pass */ +} + +void SwitchViewNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const +{ + NodeOperationOutput *result; + const char *viewName = context.getViewName(); + bNode *bnode = this->getbNode(); + + /* get the internal index of the socket with a matching name */ + int nr = BLI_findstringindex(&bnode->inputs, viewName, offsetof(bNodeSocket, name)); + nr = max(nr, 0); + + result = converter.addInputProxy(getInputSocket(nr), false); + converter.mapOutputSocket(getOutputSocket(0), result); +} diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.h b/source/blender/compositor/nodes/COM_SwitchViewNode.h new file mode 100644 index 00000000000..6ab5145bed5 --- /dev/null +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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. + * + * Contributor: + * Dalai Felinto + */ + +#ifndef _COM_SwitchViewNode_h_ +#define _COM_SwitchViewNode_h_ + +#include "COM_Node.h" +#include "COM_NodeOperation.h" +#include "DNA_node_types.h" +/** + * @brief SwitchViewNode + * @ingroup Node + */ +class SwitchViewNode : public Node { +public: + SwitchViewNode(bNode *editorNode); + void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; +}; +#endif diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cpp b/source/blender/compositor/nodes/COM_ViewerNode.cpp index ff0b8fb1706..ab819ce6e30 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cpp +++ b/source/blender/compositor/nodes/COM_ViewerNode.cpp @@ -22,6 +22,9 @@ #include "COM_ViewerNode.h" #include "BKE_global.h" +#include "BKE_image.h" +#include "BLI_listbase.h" +#include "BKE_scene.h" #include "COM_ViewerOperation.h" #include "COM_ExecutionSystem.h" @@ -51,6 +54,8 @@ void ViewerNode::convertToOperations(NodeConverter &converter, const CompositorC viewerOperation->setCenterY(editorNode->custom4); /* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */ viewerOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked()); + viewerOperation->setRenderData(context.getRenderData()); + viewerOperation->setViewName(context.getViewName()); viewerOperation->setViewSettings(context.getViewSettings()); viewerOperation->setDisplaySettings(context.getDisplaySettings()); diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cpp b/source/blender/compositor/operations/COM_CompositorOperation.cpp index ffef81d0323..eacbdf91be2 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cpp +++ b/source/blender/compositor/operations/COM_CompositorOperation.cpp @@ -52,6 +52,7 @@ CompositorOperation::CompositorOperation() : NodeOperation() this->m_active = false; this->m_sceneName[0] = '\0'; + this->m_viewName = NULL; } void CompositorOperation::initExecution() @@ -81,14 +82,16 @@ void CompositorOperation::deinitExecution() RenderResult *rr = RE_AcquireResultWrite(re); if (rr) { - if (rr->rectf != NULL) { - MEM_freeN(rr->rectf); + RenderView *rv = (RenderView *)BLI_findstring(&rr->views, this->m_viewName, offsetof(RenderView, name)); + + if (rv->rectf != NULL) { + MEM_freeN(rv->rectf); } - rr->rectf = this->m_outputBuffer; - if (rr->rectz != NULL) { - MEM_freeN(rr->rectz); + rv->rectf = this->m_outputBuffer; + if (rv->rectz != NULL) { + MEM_freeN(rv->rectz); } - rr->rectz = this->m_depthBuffer; + rv->rectz = this->m_depthBuffer; } else { if (this->m_outputBuffer) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 447d74131b3..e81ba520695 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -75,11 +75,17 @@ private: * @brief operation is active for calculating final compo result */ bool m_active; + + /** + * @brief View name, used for multiview + */ + const char *m_viewName; public: CompositorOperation(); const bool isActiveCompositorOutput() const { return this->m_active; } void executeRegion(rcti *rect, unsigned int tileNumber); void setSceneName(const char *sceneName) { BLI_strncpy(this->m_sceneName, sceneName, sizeof(this->m_sceneName)); } + void setViewName(const char *viewName) { this->m_viewName = viewName; } void setRenderData(const RenderData *rd) { this->m_rd = rd; } bool isOutputOperation(bool /*rendering*/) const { return this->isActiveCompositorOutput(); } void initExecution(); diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp index 624fdf8b626..c140b7afbd7 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cpp +++ b/source/blender/compositor/operations/COM_ImageOperation.cpp @@ -25,6 +25,7 @@ #include "BLI_listbase.h" #include "DNA_image_types.h" #include "BKE_image.h" +#include "BKE_scene.h" #include "BLI_math.h" extern "C" { @@ -48,6 +49,8 @@ BaseImageOperation::BaseImageOperation() : NodeOperation() this->m_framenumber = 0; this->m_depthBuffer = NULL; this->m_numberOfChannels = 0; + this->m_rd = NULL; + this->m_viewName = NULL; } ImageOperation::ImageOperation() : BaseImageOperation() { @@ -65,8 +68,12 @@ ImageDepthOperation::ImageDepthOperation() : BaseImageOperation() ImBuf *BaseImageOperation::getImBuf() { ImBuf *ibuf; - - ibuf = BKE_image_acquire_ibuf(this->m_image, this->m_imageUser, NULL); + ImageUser iuser = *this->m_imageUser; + + /* local changes to the original ImageUser */ + iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName); + + ibuf = BKE_image_acquire_ibuf(this->m_image, &iuser, NULL); if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) { BKE_image_release_ibuf(this->m_image, ibuf, NULL); return NULL; diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index 206f1509f60..75222559810 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -49,7 +49,9 @@ protected: int m_imagewidth; int m_framenumber; int m_numberOfChannels; - + const RenderData *m_rd; + const char *m_viewName; + BaseImageOperation(); /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -64,7 +66,8 @@ public: void deinitExecution(); void setImage(Image *image) { this->m_image = image; } void setImageUser(ImageUser *imageuser) { this->m_imageUser = imageuser; } - + void setRenderData(const RenderData *rd) { this->m_rd = rd; } + void setViewName(const char *viewName) { this->m_viewName = viewName; } void setFramenumber(int framenumber) { this->m_framenumber = framenumber; } }; class ImageOperation : public BaseImageOperation { diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp index 513140e2f62..00be3b1cdde 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp @@ -27,18 +27,27 @@ extern "C" { # include "IMB_imbuf_types.h" } -MultilayerBaseOperation::MultilayerBaseOperation(int passindex) : BaseImageOperation() +MultilayerBaseOperation::MultilayerBaseOperation(int passtype, int view) : BaseImageOperation() { - this->m_passId = passindex; + this->m_passtype = passtype; + this->m_view = view; } + ImBuf *MultilayerBaseOperation::getImBuf() { - RenderPass *rpass = (RenderPass *)BLI_findlink(&this->m_renderlayer->passes, this->m_passId); - if (rpass) { - this->m_imageUser->pass = m_passId; - BKE_image_multilayer_index(this->m_image->rr, this->m_imageUser); - return BaseImageOperation::getImBuf(); + /* temporarily changes the view to get the right ImBuf */ + int view = this->m_imageUser->view; + + this->m_imageUser->view = this->m_view; + this->m_imageUser->passtype = this->m_passtype; + + if (BKE_image_multilayer_index(this->m_image->rr, this->m_imageUser)) { + ImBuf *ibuf = BaseImageOperation::getImBuf(); + this->m_imageUser->view = view; + return ibuf; } + + this->m_imageUser->view = view; return NULL; } diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h index 37bee1b6a8c..2e140577d74 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h @@ -29,7 +29,8 @@ class MultilayerBaseOperation : public BaseImageOperation { private: - int m_passId; + int m_passtype; + int m_view; RenderLayer *m_renderlayer; protected: ImBuf *getImBuf(); @@ -37,13 +38,13 @@ public: /** * Constructor */ - MultilayerBaseOperation(int passindex); + MultilayerBaseOperation(int passtype, int view); void setRenderLayer(RenderLayer *renderlayer) { this->m_renderlayer = renderlayer; } }; class MultilayerColorOperation : public MultilayerBaseOperation { public: - MultilayerColorOperation(int passindex) : MultilayerBaseOperation(passindex) { + MultilayerColorOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) { this->addOutputSocket(COM_DT_COLOR); } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); @@ -51,7 +52,7 @@ public: class MultilayerValueOperation : public MultilayerBaseOperation { public: - MultilayerValueOperation(int passindex) : MultilayerBaseOperation(passindex) { + MultilayerValueOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) { this->addOutputSocket(COM_DT_VALUE); } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); @@ -59,7 +60,7 @@ public: class MultilayerVectorOperation : public MultilayerBaseOperation { public: - MultilayerVectorOperation(int passindex) : MultilayerBaseOperation(passindex) { + MultilayerVectorOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) { this->addOutputSocket(COM_DT_VECTOR); } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp new file mode 100644 index 00000000000..c927dfa89a1 --- /dev/null +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp @@ -0,0 +1,317 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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. + * + * Contributor: + * Jeroen Bakker + * Monique Dewanchand + * Lukas Tönne + * Dalai Felinto + */ + +#include "COM_OutputFileOperation.h" +#include "COM_OutputFileMultiViewOperation.h" + +#include <string.h> +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BKE_image.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "DNA_color_types.h" +#include "MEM_guardedalloc.h" + +extern "C" { +#include "IMB_imbuf.h" +#include "IMB_colormanagement.h" +#include "IMB_imbuf_types.h" +} + +/************************************ OpenEXR Singlelayer Multiview *****************************************/ + +OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOperation( + const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName) + : OutputSingleLayerOperation(rd, tree, datatype, format, path, viewSettings, displaySettings, viewName) +{ +} + +void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filename) +{ + size_t width = this->getWidth(); + size_t height = this->getHeight(); + SceneRenderView *srv; + + if (width != 0 && height != 0) { + void *exrhandle; + + exrhandle = IMB_exr_get_handle_name(filename); + + if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) + return exrhandle; + + IMB_exr_clear_channels(exrhandle); + + for (srv = (SceneRenderView *)this->m_rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false) + continue; + + IMB_exr_add_view(exrhandle, srv->name); + add_exr_channels(exrhandle, NULL, this->m_datatype, srv->name, width, NULL); + } + + BLI_make_existing_file(filename); + + /* prepare the file with all the channels */ + + if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_format->exr_codec) == 0) { + printf("Error Writing Singlelayer Multiview Openexr\n"); + IMB_exr_close(exrhandle); + } + else { + IMB_exr_clear_channels(exrhandle); + return exrhandle; + } + } + return NULL; +} + +void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() +{ + unsigned int width = this->getWidth(); + unsigned int height = this->getHeight(); + + if (width != 0 && height != 0) { + void *exrhandle; + Main *bmain = G.main; /* TODO, have this passed along */ + char filename[FILE_MAX]; + + BKE_image_path_from_imtype( + filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_OPENEXR, + (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL); + + exrhandle = this->get_handle(filename); + add_exr_channels(exrhandle, NULL, this->m_datatype, this->m_viewName, width, this->m_outputBuffer); + + /* memory can only be freed after we write all views to the file */ + this->m_outputBuffer = NULL; + this->m_imageInput = NULL; + + /* ready to close the file */ + if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + IMB_exr_write_channels(exrhandle); + + /* free buffer memory for all the views */ + free_exr_channels(exrhandle, this->m_rd, NULL, this->m_datatype); + + /* remove exr handle and data */ + IMB_exr_close(exrhandle); + } + } +} + +/************************************ OpenEXR Multilayer Multiview *****************************************/ + +OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOperation( + const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName) + : OutputOpenExrMultiLayerOperation(rd, tree, path, exr_codec, viewName) +{ +} + +void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename) +{ + unsigned int width = this->getWidth(); + unsigned int height = this->getHeight(); + + if (width != 0 && height != 0) { + + void *exrhandle; + SceneRenderView *srv; + + /* get a new global handle */ + exrhandle = IMB_exr_get_handle_name(filename); + + if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) + return exrhandle; + + IMB_exr_clear_channels(exrhandle); + + /* check renderdata for amount of views */ + for (srv = (SceneRenderView *) this->m_rd->views.first; srv; srv = srv->next) { + + if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false) + continue; + + IMB_exr_add_view(exrhandle, srv->name); + + for (unsigned int i = 0; i < this->m_layers.size(); ++i) + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, srv->name, width, NULL); + } + + BLI_make_existing_file(filename); + + /* prepare the file with all the channels for the header */ + if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec) == 0) { + printf("Error Writing Multilayer Multiview Openexr\n"); + IMB_exr_close(exrhandle); + } + else { + IMB_exr_clear_channels(exrhandle); + return exrhandle; + } + } + return NULL; +} + +void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution() +{ + unsigned int width = this->getWidth(); + unsigned int height = this->getHeight(); + + if (width != 0 && height != 0) { + void *exrhandle; + Main *bmain = G.main; /* TODO, have this passed along */ + char filename[FILE_MAX]; + + BKE_image_path_from_imtype( + filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER, + (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL); + + exrhandle = this->get_handle(filename); + + for (unsigned int i = 0; i < this->m_layers.size(); ++i) + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, this->m_viewName, width, this->m_layers[i].outputBuffer); + + for (unsigned int i = 0; i < this->m_layers.size(); ++i) { + /* memory can only be freed after we write all views to the file */ + this->m_layers[i].outputBuffer = NULL; + this->m_layers[i].imageInput = NULL; + } + + /* ready to close the file */ + if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + IMB_exr_write_channels(exrhandle); + + /* free buffer memory for all the views */ + for (unsigned int i = 0; i < this->m_layers.size(); ++i) { + free_exr_channels(exrhandle, this->m_rd, this->m_layers[i].name, this->m_layers[i].datatype); + } + + IMB_exr_close(exrhandle); + } + } +} + + +/******************************** Stereo3D ******************************/ + +OutputStereoOperation::OutputStereoOperation( + const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, + const char *name, const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName) + : OutputSingleLayerOperation(rd, tree, datatype, format, path, viewSettings, displaySettings, viewName) +{ + BLI_strncpy(this->m_name, name, sizeof(this->m_name)); + this->m_channels = get_datatype_size(datatype); +} + +void *OutputStereoOperation::get_handle(const char *filename) +{ + size_t width = this->getWidth(); + size_t height = this->getHeight(); + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + size_t i; + + if (width != 0 && height != 0) { + void *exrhandle; + + exrhandle = IMB_exr_get_handle_name(filename); + + if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) + return exrhandle; + + IMB_exr_clear_channels(exrhandle); + + for (i = 0; i < 2; i++) + IMB_exr_add_view(exrhandle, names[i]); + + return exrhandle; + } + return NULL; +} + +void OutputStereoOperation::deinitExecution() +{ + unsigned int width = this->getWidth(); + unsigned int height = this->getHeight(); + + if (width != 0 && height != 0) { + void *exrhandle; + + exrhandle = this->get_handle(this->m_path); + float *buf = this->m_outputBuffer; + + /* populate single EXR channel with view data */ + IMB_exr_add_channel(exrhandle, NULL, this->m_name, this->m_viewName, 1, this->m_channels * width * height, buf); + + this->m_imageInput = NULL; + this->m_outputBuffer = NULL; + + /* create stereo ibuf */ + if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + ImBuf *ibuf[3] = {NULL}; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + Main *bmain = G.main; /* TODO, have this passed along */ + char filename[FILE_MAX]; + int i; + + /* get rectf from EXR */ + for (i = 0; i < 2; i++) { + float *rectf = IMB_exr_channel_rect(exrhandle, NULL, this->m_name, names[i]); + ibuf[i] = IMB_allocImBuf(width, height, this->m_format->planes, 0); + + ibuf[i]->channels = this->m_channels; + ibuf[i]->rect_float = rectf; + ibuf[i]->mall |= IB_rectfloat; + ibuf[i]->dither = this->m_rd->dither_intensity; + + /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ + IMB_colormanagement_imbuf_for_write(ibuf[i], true, false, this->m_viewSettings, + this->m_displaySettings, this->m_format); + IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); + } + + /* create stereo buffer */ + ibuf[2] = IMB_stereo3d_ImBuf(this->m_format, ibuf[0], ibuf[1]); + + BKE_image_path_from_imformat( + filename, this->m_path, bmain->name, this->m_rd->cfra, this->m_format, + (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL); + + BKE_imbuf_write(ibuf[2], filename, this->m_format); + + /* imbuf knows which rects are not part of ibuf */ + for (i = 0; i < 3; i++) + IMB_freeImBuf(ibuf[i]); + + IMB_exr_close(exrhandle); + } + } +} diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h new file mode 100644 index 00000000000..25716fdb9e1 --- /dev/null +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -0,0 +1,74 @@ +/* + * Copyright 2015, Blender Foundation. + * + * 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. + * + * Contributor: + * Jeroen Bakker + * Monique Dewanchand + * Lukas Tönne + * Dalai Felinto + */ + +#ifndef _COM_OutputFileMultiViewOperation_h +#define _COM_OutputFileMultiViewOperation_h +#include "COM_NodeOperation.h" + +#include "BLI_rect.h" +#include "BLI_path_util.h" + +#include "DNA_color_types.h" +#include "DNA_userdef_types.h" + +#include "intern/openexr/openexr_multi.h" + +class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOperation { +private: +public: + OutputOpenExrSingleLayerMultiViewOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, + ImageFormatData *format, const char *path, + const ColorManagedViewSettings *viewSettings, + const ColorManagedDisplaySettings *displaySettings, + const char *viewName); + + void *get_handle(const char *filename); + void deinitExecution(); +}; + +/* Writes inputs into OpenEXR multilayer channels. */ +class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayerOperation { +private: +public: + OutputOpenExrMultiLayerMultiViewOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName); + + void *get_handle(const char *filename); + void deinitExecution(); +}; + +/**/ +class OutputStereoOperation : public OutputSingleLayerOperation { +private: + char m_name[FILE_MAX]; + size_t m_channels; +public: + OutputStereoOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, + struct ImageFormatData *format, const char *path, const char *name, + const ColorManagedViewSettings *viewSettings, + const ColorManagedDisplaySettings *displaySettings, const char *viewName); + void *get_handle(const char *filename); + void deinitExecution(); +}; + +#endif diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cpp b/source/blender/compositor/operations/COM_OutputFileOperation.cpp index b1c90a6355f..1a7a356f5b4 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cpp @@ -29,6 +29,7 @@ #include "BKE_image.h" #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_scene.h" #include "DNA_color_types.h" @@ -39,7 +40,60 @@ extern "C" { # include "IMB_imbuf_types.h" } -static int get_datatype_size(DataType datatype) +void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, const size_t width, float *buf) +{ + /* create channels */ + switch (datatype) { + case COM_DT_VALUE: + IMB_exr_add_channel(exrhandle, layerName, "V", viewName, 1, width, buf ? buf : NULL); + break; + case COM_DT_VECTOR: + IMB_exr_add_channel(exrhandle, layerName, "X", viewName, 3, 3 * width, buf ? buf : NULL); + IMB_exr_add_channel(exrhandle, layerName, "Y", viewName, 3, 3 * width, buf ? buf + 1 : NULL); + IMB_exr_add_channel(exrhandle, layerName, "Z", viewName, 3, 3 * width, buf ? buf + 2 : NULL); + break; + case COM_DT_COLOR: + IMB_exr_add_channel(exrhandle, layerName, "R", viewName, 4, 4 * width, buf ? buf : NULL); + IMB_exr_add_channel(exrhandle, layerName, "G", viewName, 4, 4 * width, buf ? buf + 1 : NULL); + IMB_exr_add_channel(exrhandle, layerName, "B", viewName, 4, 4 * width, buf ? buf + 2 : NULL); + IMB_exr_add_channel(exrhandle, layerName, "A", viewName, 4, 4 * width, buf ? buf + 3 : NULL); + break; + default: + break; + } +} + +void free_exr_channels(void *exrhandle, const RenderData *rd, const char *layerName, const DataType datatype) +{ + SceneRenderView *srv; + + /* check renderdata for amount of views */ + for (srv = (SceneRenderView *) rd->views.first; srv; srv = srv->next) { + float *rect = NULL; + + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + + /* the pointer is stored in the first channel of each datatype */ + switch (datatype) { + case COM_DT_VALUE: + rect = IMB_exr_channel_rect(exrhandle, layerName, "V", srv->name); + break; + case COM_DT_VECTOR: + rect = IMB_exr_channel_rect(exrhandle, layerName, "X", srv->name); + break; + case COM_DT_COLOR: + rect = IMB_exr_channel_rect(exrhandle, layerName, "R", srv->name); + break; + default: + break; + } + if (rect) + MEM_freeN(rect); + } +} + +int get_datatype_size(DataType datatype) { switch (datatype) { case COM_DT_VALUE: return 1; @@ -94,7 +148,7 @@ static void write_buffer_rect(rcti *rect, const bNodeTree *tree, OutputSingleLayerOperation::OutputSingleLayerOperation( const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, const char *viewName) { this->m_rd = rd; this->m_tree = tree; @@ -110,6 +164,7 @@ OutputSingleLayerOperation::OutputSingleLayerOperation( this->m_viewSettings = viewSettings; this->m_displaySettings = displaySettings; + this->m_viewName = viewName; } void OutputSingleLayerOperation::initExecution() @@ -131,6 +186,7 @@ void OutputSingleLayerOperation::deinitExecution() ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), this->m_format->planes, 0); Main *bmain = G.main; /* TODO, have this passed along */ char filename[FILE_MAX]; + const char *suffix; ibuf->channels = size; ibuf->rect_float = this->m_outputBuffer; @@ -140,10 +196,12 @@ void OutputSingleLayerOperation::deinitExecution() IMB_colormanagement_imbuf_for_write(ibuf, true, false, m_viewSettings, m_displaySettings, this->m_format); + suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName); + BKE_image_path_from_imformat( - filename, this->m_path, bmain->name, this->m_rd->cfra, - this->m_format, (this->m_rd->scemode & R_EXTENSION) != 0, true); - + filename, this->m_path, bmain->name, this->m_rd->cfra, this->m_format, + (this->m_rd->scemode & R_EXTENSION) != 0, true, suffix); + if (0 == BKE_imbuf_write(ibuf, filename, this->m_format)) printf("Cannot save Node File Output to %s\n", filename); else @@ -155,6 +213,7 @@ void OutputSingleLayerOperation::deinitExecution() this->m_imageInput = NULL; } +/******************************* MultiLayer *******************************/ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bool use_layer_) { @@ -168,13 +227,14 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo } OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation( - const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec) + const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName) { this->m_rd = rd; this->m_tree = tree; BLI_strncpy(this->m_path, path, sizeof(this->m_path)); this->m_exr_codec = exr_codec; + this->m_viewName = viewName; } void OutputOpenExrMultiLayerOperation::add_layer(const char *name, DataType datatype, bool use_layer) @@ -210,52 +270,21 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() if (width != 0 && height != 0) { Main *bmain = G.main; /* TODO, have this passed along */ char filename[FILE_MAX]; + const char *suffix; void *exrhandle = IMB_exr_get_handle(); - + + suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName); BKE_image_path_from_imtype( filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER, - (this->m_rd->scemode & R_EXTENSION) != 0, true); + (this->m_rd->scemode & R_EXTENSION) != 0, true, suffix); BLI_make_existing_file(filename); - + for (unsigned int i = 0; i < this->m_layers.size(); ++i) { OutputOpenExrLayer &layer = this->m_layers[i]; if (!layer.imageInput) continue; /* skip unconnected sockets */ - char channelname[EXR_TOT_MAXNAME]; - BLI_strncpy(channelname, this->m_layers[i].name, sizeof(channelname) - 2); - char *channelname_ext = channelname + strlen(channelname); - - float *buf = this->m_layers[i].outputBuffer; - - /* create channels */ - switch (this->m_layers[i].datatype) { - case COM_DT_VALUE: - strcpy(channelname_ext, ".V"); - IMB_exr_add_channel(exrhandle, 0, channelname, 1, width, buf); - break; - case COM_DT_VECTOR: - strcpy(channelname_ext, ".X"); - IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf); - strcpy(channelname_ext, ".Y"); - IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf + 1); - strcpy(channelname_ext, ".Z"); - IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf + 2); - break; - case COM_DT_COLOR: - strcpy(channelname_ext, ".R"); - IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf); - strcpy(channelname_ext, ".G"); - IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 1); - strcpy(channelname_ext, ".B"); - IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 2); - strcpy(channelname_ext, ".A"); - IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 3); - break; - default: - break; - } - + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, "", width, this->m_layers[i].outputBuffer); } /* when the filename has no permissions, this can fail */ @@ -279,3 +308,4 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } } } + diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index af16f186d9d..58ebf055583 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -34,7 +34,7 @@ /* Writes the image to a single-layer file. */ class OutputSingleLayerOperation : public NodeOperation { -private: +protected: const RenderData *m_rd; const bNodeTree *m_tree; @@ -47,9 +47,11 @@ private: const ColorManagedViewSettings *m_viewSettings; const ColorManagedDisplaySettings *m_displaySettings; + + const char *m_viewName; public: OutputSingleLayerOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, const char *viewName); void executeRegion(rcti *rect, unsigned int tileNumber); bool isOutputOperation(bool /*rendering*/) const { return true; } @@ -75,7 +77,7 @@ struct OutputOpenExrLayer { /* Writes inputs into OpenEXR multilayer channels. */ class OutputOpenExrMultiLayerOperation : public NodeOperation { -private: +protected: typedef std::vector<OutputOpenExrLayer> LayerList; const RenderData *m_rd; @@ -84,9 +86,10 @@ private: char m_path[FILE_MAX]; char m_exr_codec; LayerList m_layers; + const char *m_viewName; public: - OutputOpenExrMultiLayerOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec); + OutputOpenExrMultiLayerOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName); void add_layer(const char *name, DataType datatype, bool use_layer); @@ -99,4 +102,8 @@ public: bool isFileOutputOperation() const { return true; } }; +void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, const size_t width, float *buf); +void free_exr_channels(void *exrhandle, const RenderData *rd, const char *layerName, const DataType datatype); +int get_datatype_size(DataType datatype); + #endif diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cpp b/source/blender/compositor/operations/COM_RenderLayersProg.cpp index defbc2ab57f..af176a766ff 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cpp +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cpp @@ -23,6 +23,7 @@ #include "COM_RenderLayersProg.h" #include "BLI_listbase.h" +#include "BKE_scene.h" #include "DNA_scene_types.h" extern "C" { @@ -57,11 +58,10 @@ void RenderLayersBaseProg::initExecution() if (srl) { RenderLayer *rl = RE_GetRenderLayer(rr, srl->name); - if (rl && rl->rectf) { - this->m_inputBuffer = RE_RenderLayerGetPass(rl, this->m_renderpass); - + if (rl) { + this->m_inputBuffer = RE_RenderLayerGetPass(rl, this->m_renderpass, this->m_viewName); if (this->m_inputBuffer == NULL && this->m_renderpass == SCE_PASS_COMBINED) { - this->m_inputBuffer = rl->rectf; + this->m_inputBuffer = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, this->m_viewName); } } } @@ -195,7 +195,7 @@ void RenderLayersBaseProg::determineResolution(unsigned int resolution[2], unsig SceneRenderLayer *srl = (SceneRenderLayer *)BLI_findlink(&sce->r.layers, getLayerId()); if (srl) { RenderLayer *rl = RE_GetRenderLayer(rr, srl->name); - if (rl && rl->rectf) { + if (rl) { resolution[0] = rl->rectx; resolution[1] = rl->recty; } diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index f73d9de3e27..2ddbc968c01 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -51,7 +51,12 @@ private: * layerId of the layer where this operation needs to get its data from */ short m_layerId; - + + /** + * viewName of the view to use (unless another view is specified by the node + */ + const char *m_viewName; + /** * cached instance to the float buffer inside the layer */ @@ -97,6 +102,8 @@ public: void setRenderData(const RenderData *rd) { this->m_rd = rd; } void setLayerId(short layerId) { this->m_layerId = layerId; } short getLayerId() { return this->m_layerId; } + void setViewName(const char *viewName) { this->m_viewName = viewName; } + const char *getViewName() { return this->m_viewName; } void initExecution(); void deinitExecution(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cpp b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cpp index e4883d91bc3..1ec52571be8 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cpp @@ -62,8 +62,7 @@ void VariableSizeBokehBlurOperation::initExecution() #endif QualityStepHelper::initExecution(COM_QH_INCREASE); } -struct VariableSizeBokehBlurTileData -{ +struct VariableSizeBokehBlurTileData { MemoryBuffer *color; MemoryBuffer *bokeh; MemoryBuffer *size; diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cpp b/source/blender/compositor/operations/COM_ViewerOperation.cpp index da2706a2244..96ee5ecbb03 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cpp +++ b/source/blender/compositor/operations/COM_ViewerOperation.cpp @@ -23,6 +23,7 @@ #include "COM_ViewerOperation.h" #include "BLI_listbase.h" #include "BKE_image.h" +#include "BKE_scene.h" #include "WM_api.h" #include "WM_types.h" #include "PIL_time.h" @@ -57,6 +58,8 @@ ViewerOperation::ViewerOperation() : NodeOperation() this->m_imageInput = NULL; this->m_alphaInput = NULL; this->m_depthInput = NULL; + this->m_rd = NULL; + this->m_viewName = NULL; } void ViewerOperation::initExecution() @@ -123,8 +126,18 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) void ViewerOperation::initImage() { Image *ima = this->m_image; + ImageUser iuser = *this->m_imageUser; void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, this->m_imageUser, &lock); + ImBuf *ibuf; + + /* make sure the image has the correct number of views */ + if (ima && BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { + BKE_image_verify_viewer_views(this->m_rd, ima, this->m_imageUser); + } + + /* local changes to the original ImageUser */ + iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName); + ibuf = BKE_image_acquire_ibuf(ima, &iuser, &lock); if (!ibuf) return; BLI_lock_thread(LOCK_DRAW_IMAGE); diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index a17375c2de9..107aee3a82f 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -40,7 +40,9 @@ private: bool m_doDepthBuffer; ImBuf *m_ibuf; bool m_useAlphaInput; - + const RenderData *m_rd; + const char *m_viewName; + const ColorManagedViewSettings *m_viewSettings; const ColorManagedDisplaySettings *m_displaySettings; @@ -67,6 +69,8 @@ public: const CompositorPriority getRenderPriority() const; bool isViewerOperation() const { return true; } void setUseAlphaInput(bool value) { this->m_useAlphaInput = value; } + void setRenderData(const RenderData *rd) { this->m_rd = rd; } + void setViewName(const char *viewName) { this->m_viewName = viewName; } void setViewSettings(const ColorManagedViewSettings *viewSettings) { this->m_viewSettings = viewSettings; } void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { this->m_displaySettings = displaySettings; } diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index d71cba86f8d..ebd05d8b16b 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -3576,8 +3576,24 @@ void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, eAnimChannel // width of rename textboxes #define RENAME_TEXT_WIDTH (5 * U.widget_unit) + +/* Helper - Check if a channel needs renaming */ +static bool achannel_is_being_renamed(const bAnimContext *ac, const bAnimChannelType *acf, size_t channel_index) +{ + if (acf->name_prop && ac->ads) { + /* if rename index matches, this channel is being renamed */ + if (ac->ads->renameIndex == channel_index + 1) { + return true; + } + } + + /* not being renamed */ + return false; +} + + /* Draw the given channel */ -void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) +void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc, size_t channel_index) { const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; @@ -3664,8 +3680,8 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float } /* step 5) draw name ............................................... */ - /* TODO: when renaming, we might not want to draw this, especially if name happens to be longer than channel */ - if (acf->name) { + /* Don't draw this if renaming... */ + if (acf->name && !achannel_is_being_renamed(ac, acf, channel_index)) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; char name[ANIM_CHAN_NAME_SIZE]; /* hopefully this will be enough! */ @@ -4175,36 +4191,32 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle } /* step 4) draw text - check if renaming widget is in use... */ - if (acf->name_prop && ac->ads) { - float channel_height = ymaxc - yminc; + if (achannel_is_being_renamed(ac, acf, channel_index)) { + PointerRNA ptr = {{NULL}}; + PropertyRNA *prop = NULL; - /* if rename index matches, add widget for this */ - if (ac->ads->renameIndex == channel_index + 1) { - PointerRNA ptr = {{NULL}}; - PropertyRNA *prop = NULL; + /* draw renaming widget if we can get RNA pointer for it + * NOTE: property may only be available in some cases, even if we have + * a callback available (e.g. broken F-Curve rename) + */ + if (acf->name_prop(ale, &ptr, &prop)) { + const float channel_height = ymaxc - yminc; + uiBut *but; - /* draw renaming widget if we can get RNA pointer for it - * NOTE: property may only be available in some cases, even if we have - * a callback available (e.g. broken F-Curve rename) - */ - if (acf->name_prop(ale, &ptr, &prop)) { - uiBut *but; - - UI_block_emboss_set(block, UI_EMBOSS); - - but = uiDefButR(block, UI_BTYPE_TEXT, 1, "", offset + 3, yminc, RENAME_TEXT_WIDTH, channel_height, - &ptr, RNA_property_identifier(prop), -1, 0, 0, -1, -1, NULL); - - /* copy what outliner does here, see outliner_buttons */ - if (UI_but_active_only(C, ac->ar, block, but) == false) { - ac->ads->renameIndex = 0; - - /* send notifiers */ - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_RENAME, NULL); - } + UI_block_emboss_set(block, UI_EMBOSS); + + but = uiDefButR(block, UI_BTYPE_TEXT, 1, "", offset + 3, yminc, RENAME_TEXT_WIDTH, channel_height, + &ptr, RNA_property_identifier(prop), -1, 0, 0, -1, -1, NULL); + + /* copy what outliner does here, see outliner_buttons */ + if (UI_but_active_only(C, ac->ar, block, but) == false) { + ac->ads->renameIndex = 0; - UI_block_emboss_set(block, UI_EMBOSS_NONE); + /* send notifiers */ + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_RENAME, NULL); } + + UI_block_emboss_set(block, UI_EMBOSS_NONE); } } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 1782152560a..4828343fa0e 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -2117,7 +2117,7 @@ static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op)) /* remove AnimData? */ if (action_empty && nla_empty && drivers_empty) { - BKE_free_animdata(id); + BKE_animdata_free(id); } } diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 140b7e0b117..68660740ac7 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -266,8 +266,20 @@ void ED_markers_make_cfra_list(ListBase *markers, ListBase *lb, short only_sel) { TimeMarker *marker; - if (markers == NULL) + if (lb) { + /* Clear the list first, since callers have no way of knowing + * whether this terminated early otherwise. This may lead + * to crashes if the user didn't clear the memory first. + */ + lb->first = lb->last = NULL; + } + else { + return; + } + + if (markers == NULL) { return; + } for (marker = markers->first; marker; marker = marker->next) add_marker_to_cfra_elem(lb, marker, only_sel); diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 67ba82f1858..08e0daf8eb9 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -84,7 +84,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde /* init animdata if none available yet */ adt = BKE_animdata_from_id(id); if ((adt == NULL) && (add)) - adt = BKE_id_add_animdata(id); + adt = BKE_animdata_add_id(id); if (adt == NULL) { /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */ return NULL; diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 78509dd6079..64ced437142 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -130,7 +130,7 @@ bAction *verify_adt_action(ID *id, short add) /* init animdata if none available yet */ adt = BKE_animdata_from_id(id); if ((adt == NULL) && (add)) - adt = BKE_id_add_animdata(id); + adt = BKE_animdata_add_id(id); if (adt == NULL) { /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */ printf("ERROR: Couldn't add AnimData (ID = %s)\n", (id) ? (id->name) : "<None>"); diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 177fb6c9118..eedff896315 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -270,7 +270,7 @@ void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *n // XXX: the ID here is for armatures, but most bone drivers are actually on the object instead... { - BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname); + BKE_animdata_fix_paths_rename_all(&arm->id, "pose.bones", oldname, newname); } /* correct view locking */ diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index 552faa4ecfb..3f00068c3d2 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -271,7 +271,7 @@ void ED_keymap_armature(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "ARMATURE_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_extrude_move", EKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_extrude_forked", EKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "ARMATURE_OT_click_extrude", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + WM_keymap_add_item(keymap, "ARMATURE_OT_click_extrude", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_fill", FKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_merge", MKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_split", YKEY, KM_PRESS, 0, 0); @@ -412,5 +412,6 @@ void ED_keymap_armature(wmKeyConfig *keyconf) /* menus */ WM_keymap_add_menu(keymap, "VIEW3D_MT_pose_specials", WKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "VIEW3D_MT_pose_propagate", PKEY, KM_PRESS, KM_ALT, 0); } diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index ad9715d2ab2..cee34257b2c 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -380,7 +380,7 @@ int join_armature_exec(bContext *C, wmOperator *op) if (base->object->adt) { if (ob->adt == NULL) { /* no animdata, so just use a copy of the whole thing */ - ob->adt = BKE_copy_animdata(base->object->adt, false); + ob->adt = BKE_animdata_copy(base->object->adt, false); } else { /* merge in data - we'll fix the drivers manually */ @@ -391,7 +391,7 @@ int join_armature_exec(bContext *C, wmOperator *op) if (curarm->adt) { if (arm->adt == NULL) { /* no animdata, so just use a copy of the whole thing */ - arm->adt = BKE_copy_animdata(curarm->adt, false); + arm->adt = BKE_animdata_copy(curarm->adt, false); } else { /* merge in data - we'll fix the drivers manually */ diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index b7d14e089cf..fa774b0744a 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -127,12 +127,12 @@ struct LaplacianSystem { static void laplacian_increase_edge_count(EdgeHash *edgehash, int v1, int v2) { - void **p = BLI_edgehash_lookup_p(edgehash, v1, v2); + void **p; - if (p) + if (BLI_edgehash_ensure_p(edgehash, v1, v2, &p)) *p = (void *)((intptr_t)*p + (intptr_t)1); else - BLI_edgehash_insert(edgehash, v1, v2, (void *)(intptr_t)1); + *p = (void *)((intptr_t)1); } static int laplacian_edge_count(EdgeHash *edgehash, int v1, int v2) diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 5f9f24d23a4..09c8b4c370c 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -898,6 +898,8 @@ typedef enum ePosePropagate_Termination { /* stop when we run out of keyframes */ POSE_PROPAGATE_BEFORE_END, + /* only do on keyframes that are selected */ + POSE_PROPAGATE_SELECTED_KEYS, /* only do on the frames where markers are selected */ POSE_PROPAGATE_SELECTED_MARKERS } ePosePropagate_Termination; @@ -1137,6 +1139,11 @@ static void pose_propagate_fcurve(wmOperator *op, Object *ob, FCurve *fcu, if (ce == NULL) continue; } + else if (mode == POSE_PROPAGATE_SELECTED_KEYS) { + /* only allow if this keyframe is already selected - skip otherwise */ + if (BEZSELECTED(bezt) == 0) + continue; + } /* just flatten handles, since values will now be the same either side... */ /* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */ @@ -1219,12 +1226,20 @@ static int pose_propagate_exec(bContext *C, wmOperator *op) void POSE_OT_propagate(wmOperatorType *ot) { static EnumPropertyItem terminate_items[] = { - {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behavior)"}, - {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", "Propagate pose to first keyframe following the current frame only"}, - {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", "Propagate pose to the last keyframe only (i.e. making action cyclic)"}, - {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", "Propagate pose to all keyframes between current frame and 'Frame' property"}, - {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", "Propagate pose to all keyframes from current frame until no more are found"}, - {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers", "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"}, + {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", + "Propagate pose to all keyframes after current frame that don't change (Default behavior)"}, + {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", + "Propagate pose to first keyframe following the current frame only"}, + {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", + "Propagate pose to the last keyframe only (i.e. making action cyclic)"}, + {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", + "Propagate pose to all keyframes between current frame and 'Frame' property"}, + {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", + "Propagate pose to all keyframes from current frame until no more are found"}, + {POSE_PROPAGATE_SELECTED_KEYS, "SELECTED_KEYS", 0, "On Selected Keyframes", + "Propagate pose to all selected keyframes"}, + {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers", + "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"}, {0, NULL, 0, NULL, NULL}}; /* identifiers */ diff --git a/source/blender/editors/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c index 4bcb16d31ef..561c349edec 100644 --- a/source/blender/editors/curve/curve_ops.c +++ b/source/blender/editors/curve/curve_ops.c @@ -230,7 +230,7 @@ void ED_keymap_curve(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", VKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", AKEY, KM_PRESS, 0, 0); RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE); diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index c2072d30310..d4d601b1e8d 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -40,6 +40,9 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLF_api.h" +#include "BLF_translation.h" + #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -57,8 +60,10 @@ #include "BIF_glutil.h" #include "ED_gpencil.h" +#include "ED_screen.h" #include "ED_view3d.h" +#include "UI_interface_icons.h" #include "UI_resources.h" /* ************************************************** */ @@ -1042,6 +1047,51 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in } } +/* draw a short status message in the top-right corner */ +static void gp_draw_status_text(bGPdata *gpd, ARegion *ar) +{ + rcti rect; + + /* Cannot draw any status text when drawing OpenGL Renders */ + if (G.f & G_RENDER_OGL) + return; + + /* Get bounds of region - Necessary to avoid problems with region overlap */ + ED_region_visible_rect(ar, &rect); + + /* for now, this should only be used to indicate when we are in stroke editmode */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + const char *printable = IFACE_("GPencil Stroke Editing"); + float printable_size[2]; + int xco, yco; + + BLF_width_and_height_default(printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); + + xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; + yco = (rect.ymax - U.widget_unit); + + /* text label */ + UI_ThemeColor(TH_TEXT_HI); +#ifdef WITH_INTERNATIONAL + BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); +#else + BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); +#endif + + /* grease pencil icon... */ + // XXX: is this too intrusive? + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + xco -= U.widget_unit; + yco -= (int)printable_size[1] / 2; + + UI_icon_draw(xco, yco, ICON_GREASEPENCIL); + + glDisable(GL_BLEND); + } +} + /* draw grease-pencil datablock */ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { @@ -1193,6 +1243,11 @@ void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d) /* draw it! */ if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS); gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype); + + /* draw status text (if in screen/pixel-space) */ + if (onlyv2d == false) { + gp_draw_status_text(gpd, ar); + } } /* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly @@ -1227,10 +1282,27 @@ void ED_gpencil_draw_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d) winy = ar->winy; } - /* draw it! */ - if (only3d) dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + /* set flags */ + if (only3d) { + /* 3D strokes/3D space: + * - only 3D space points + * - don't status text either (as it's the wrong space) + */ + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + } + + if (v3d->flag2 & V3D_RENDER_OVERRIDE) { + /* don't draw status text when "only render" flag is set */ + dflag |= GP_DRAWDATA_NOSTATUS; + } + /* draw it! */ gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + + /* draw status text (if in screen/pixel-space) */ + if (only3d == false) { + gp_draw_status_text(gpd, ar); + } } void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index fc4dd3f410a..fa76029bb2e 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -350,4 +350,98 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* *********************** Hide Layers ******************************** */ + +static int gp_hide_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *layer = gpencil_layer_getactive(gpd); + bool unselected = RNA_boolean_get(op->ptr, "unselected"); + + /* sanity checks */ + if (ELEM(NULL, gpd, layer)) + return OPERATOR_CANCELLED; + + if (unselected) { + bGPDlayer *gpl; + + /* hide unselected */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl != layer) { + gpl->flag |= GP_LAYER_HIDE; + } + } + } + else { + /* hide selected/active */ + layer->flag |= GP_LAYER_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Layer(s)"; + ot->idname = "GPENCIL_OT_hide"; + ot->description = "Hide selected/unselected Grease Pencil layers"; + + /* callbacks */ + ot->exec = gp_hide_exec; + ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers"); +} + +/* ********************** Show All Layers ***************************** */ + +/* poll callback for showing layers */ +static int gp_reveal_poll(bContext *C) +{ + return ED_gpencil_data_get_active(C) != NULL; +} + +static int gp_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl; + + /* sanity checks */ + if (gpd == NULL) + return OPERATOR_CANCELLED; + + /* make all layers visible */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->flag &= ~GP_LAYER_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Show All Layers"; + ot->idname = "GPENCIL_OT_reveal"; + ot->description = "Show all Grease Pencil layers"; + + /* callbacks */ + ot->exec = gp_reveal_exec; + ot->poll = gp_reveal_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ************************************************ */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index cd0c32a35bf..72cd9022d69 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -144,6 +144,9 @@ void GPENCIL_OT_layer_remove(struct wmOperatorType *ot); void GPENCIL_OT_layer_move(struct wmOperatorType *ot); void GPENCIL_OT_layer_duplicate(struct wmOperatorType *ot); +void GPENCIL_OT_hide(struct wmOperatorType *ot); +void GPENCIL_OT_reveal(struct wmOperatorType *ot); + void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 5970a9497a0..4752f3dbb36 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -181,7 +181,18 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) #ifdef __APPLE__ WM_keymap_add_item(keymap, "GPENCIL_OT_copy", CKEY, KM_PRESS, KM_OSKEY, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_paste", VKEY, KM_PRESS, KM_OSKEY, 0); -#endif +#endif + + /* Show/Hide */ + /* NOTE: These are available only in EditMode now, since they clash with general-purpose hotkeys */ + WM_keymap_add_item(keymap, "GPENCIL_OT_reveal", HKEY, KM_PRESS, KM_ALT, 0); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "unselected", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "unselected", true); + /* Transform Tools */ kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0); @@ -260,6 +271,9 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_layer_move); WM_operatortype_append(GPENCIL_OT_layer_duplicate); + WM_operatortype_append(GPENCIL_OT_hide); + WM_operatortype_append(GPENCIL_OT_reveal); + WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_convert); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index c8774cb73a1..3a564cc6ddb 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1885,8 +1885,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) * - RIGHTMOUSE = polyline (hotkey) / eraser (all) * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) + * also making sure we have a valid event value, to not exit too early */ - if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE)) { + if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (event->val != KM_NOTHING)) { /* if painting, end stroke */ if (p->status == GP_STATUS_PAINTING) { int sketch = 0; @@ -1930,7 +1931,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* stroke could be smoothed, send notifier to refresh screen */ WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } - else { + else if (event->val == KM_RELEASE) { /* printf("\t\tGP - end of stroke + op\n"); */ p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 7fd0d35d514..9dea66adc7c 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -462,7 +462,7 @@ const bAnimChannelType *ANIM_channel_get_typeinfo(bAnimListElem *ale); void ANIM_channel_debug_print_info(bAnimListElem *ale, short indent_level); /* Draw the given channel */ -void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc); +void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc, size_t channel_index); /* Draw the widgets for the given channel */ void ANIM_channel_draw_widgets(const struct bContext *C, bAnimContext *ac, bAnimListElem *ale, struct uiBlock *block, float yminc, float ymaxc, size_t channel_index); @@ -676,6 +676,14 @@ void ED_operatormacros_graph(void); void ED_operatormacros_action(void); /* ************************************************ */ +/* Animation Editor Exports */ +/* XXX: Should we be doing these here, or at all? */ + +/* Action Editor - Action Management */ +struct AnimData *ED_actedit_animdata_from_context(struct bContext *C); +void ED_animedit_unlink_action(struct bContext *C, struct ID *id, struct AnimData *adt, struct bAction *act, struct ReportList *reports); + +/* ************************************************ */ #endif /* __ED_ANIM_API_H__ */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b4f7d806079..90b0b3510bc 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -112,6 +112,7 @@ void ED_screen_full_prevspace(struct bContext *C, ScrArea *sa); void ED_screen_full_restore(struct bContext *C, ScrArea *sa); struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *sa, const short state); void ED_screens_header_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg); +bool ED_screen_stereo3d_required(struct bScreen *screen); /* anim */ void ED_update_for_newframe(struct Main *bmain, struct Scene *scene, int mute); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 235cb2b21eb..b14173131cf 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -329,12 +329,13 @@ void ED_view3d_draw_offscreen( struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int winx, int winy, float viewmat[4][4], float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp, struct GPUOffScreen *ofs, - struct GPUFX *fx, struct GPUFXSettings *fx_settings); + struct GPUFX *fx, struct GPUFXSettings *fx_settings, + const char *viewname); struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag, - bool draw_background, int alpha_mode, char err_out[256]); + bool draw_background, int alpha_mode, const char *viewname, char err_out[256]); struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, - bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256]); + bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, const char *viewname, char err_out[256]); struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, bool do_clip); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0681cacf87e..717d5db6b66 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -893,6 +893,9 @@ void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char * PointerRNA *used_ptr, const char *used_propname, int active_state); void uiTemplateImage(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, struct PointerRNA *userptr, int compact); void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr, int color_management); +void uiTemplateImageStereo3d(uiLayout *layout, struct PointerRNA *stereo3d_format_ptr); +void uiTemplateImageViews(uiLayout *layout, struct PointerRNA *imaptr); +void uiTemplateImageFormatViews(uiLayout *layout, PointerRNA *imfptr, PointerRNA *ptr); void uiTemplateImageLayers(uiLayout *layout, struct bContext *C, struct Image *ima, struct ImageUser *iuser); void uiTemplateImageInfo(uiLayout *layout, struct bContext *C, Image *ima, ImageUser *iuser); void uiTemplateRunningJobs(uiLayout *layout, struct bContext *C); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index f5847a9d701..96f9b87611f 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -277,6 +277,10 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) eyedropper_cancel(C, op); return OPERATOR_CANCELLED; case LEFTMOUSE: + /* two release events are sent on KM_CLICK, so ignore second one */ + if (event->click_type == KM_CLICK) { + break; + } if (event->val == KM_RELEASE) { if (eye->accum_tot == 0) { eyedropper_color_sample(C, eye, event->x, event->y); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 754967d9de7..2c00447229c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1180,7 +1180,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void switch (event->type) { case LEFTMOUSE: { - if (event->val != KM_PRESS) { + if (event->val == KM_RELEASE) { done = true; } break; @@ -2721,8 +2721,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle if (data->searchbox) inbox = ui_searchbox_inside(data->searchbox, event->x, event->y); - /* for double click: we do a press again for when you first click on button (selects all text, no cursor pos) */ - if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) { + if (event->val == KM_PRESS) { float mx, my; mx = event->x; @@ -2747,7 +2746,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle } /* only select a word in button if there was no selection before */ - if (event->val == KM_DBL_CLICK && had_selection == false) { + if ((event->click_type == KM_DBL_CLICK) && (had_selection == false)) { ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_DELIM); ui_textedit_move(but, data, STRCUR_DIR_NEXT, true, STRCUR_JUMP_DELIM); retval = WM_UI_HANDLER_BREAK; @@ -2864,8 +2863,6 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle if (autocomplete == AUTOCOMPLETE_FULL_MATCH) button_activate_state(C, but, BUTTON_STATE_EXIT); - - update = true; /* do live update for tab key */ } /* the hotkey here is not well defined, was G.qual so we check all */ else if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { @@ -2944,7 +2941,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle #endif if (changed) { - /* only update when typing for TAB key */ + /* only do live update when but flag request it (UI_BUT_TEXTEDIT_UPDATE). */ if (update && data->interactive) { ui_apply_but(C, block, but, data, true); } @@ -3175,7 +3172,7 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, cons } } else if (data->state == BUTTON_STATE_WAIT_RELEASE) { - if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (!(but->flag & UI_SELECT)) data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); @@ -3743,7 +3740,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (data->dragchange) { #ifdef USE_DRAG_MULTINUM /* if we started multibutton but didnt drag, then edit */ @@ -4040,7 +4037,7 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (data->dragchange) { #ifdef USE_DRAG_MULTINUM /* if we started multibutton but didnt drag, then edit */ @@ -4185,7 +4182,7 @@ static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleBut button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } else if (event->type == MOUSEMOVE) { @@ -4233,7 +4230,7 @@ static int ui_do_but_GRIP(bContext *C, uiBlock *block, uiBut *but, uiHandleButto button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } else if (event->type == MOUSEMOVE) { @@ -4257,7 +4254,7 @@ static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data, * editing field for editing list item names */ if ((ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS && event->ctrl) || - (event->type == LEFTMOUSE && event->val == KM_DBL_CLICK)) + (event->type == LEFTMOUSE && event->click_type == KM_DBL_CLICK)) { uiBut *labelbut = ui_but_list_row_text_activate(C, but, data, event, BUTTON_ACTIVATE_TEXT_EDITING); if (labelbut) { @@ -4609,7 +4606,7 @@ static int ui_do_but_UNITVEC(bContext *C, uiBlock *block, uiBut *but, uiHandleBu ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -4930,7 +4927,7 @@ static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleBu ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -5203,7 +5200,7 @@ static int ui_do_but_HSVCIRCLE(bContext *C, uiBlock *block, uiBut *but, uiHandle } } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } return WM_UI_HANDLER_BREAK; @@ -5288,7 +5285,7 @@ static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandle ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -5527,7 +5524,7 @@ static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButt ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (data->dragsel != -1) { CurveMapping *cumap = (CurveMapping *)but->poin; CurveMap *cuma = cumap->cm + cumap->cur; @@ -5623,7 +5620,7 @@ static int ui_do_but_HISTOGRAM(bContext *C, uiBlock *block, uiBut *but, uiHandle ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } return WM_UI_HANDLER_BREAK; @@ -5696,7 +5693,7 @@ static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleB ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } return WM_UI_HANDLER_BREAK; @@ -5721,7 +5718,7 @@ static int ui_do_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data, con } else if (data->state == BUTTON_STATE_WAIT_RELEASE) { - if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (!(but->flag & UI_SELECT)) data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); @@ -5804,7 +5801,7 @@ static int ui_do_but_TRACKPREVIEW(bContext *C, uiBlock *block, uiBut *but, uiHan ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } return WM_UI_HANDLER_BREAK; @@ -8345,7 +8342,7 @@ static int ui_handle_menu_event( #ifdef USE_DRAG_POPUP if (menu->is_grab) { - if (event->type == LEFTMOUSE) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { menu->is_grab = false; retval = WM_UI_HANDLER_BREAK; } @@ -8630,7 +8627,7 @@ static int ui_handle_menu_event( uiSafetyRct *saferct = block->saferct.first; if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && - ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) + (event->val == KM_PRESS || event->click_type == KM_DBL_CLICK)) { if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) { /* for root menus, allow clicking to close */ @@ -9007,6 +9004,11 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle switch (event->type) { case MOUSEMOVE: + /* verify we have a real mousemove */ + if (event->x == event->prevx && event->y == event->prevy) { + break; + } + if (!is_click_style) { float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init); @@ -9033,7 +9035,9 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case LEFTMOUSE: if (is_click_style) { - if (block->pie_data.flags & UI_PIE_INVALID_DIR) { + /* the click_type test sends a second event on KM_CLICK that + * may destroy nested pies even before they are drawn */ + if ((block->pie_data.flags & UI_PIE_INVALID_DIR) && (event->click_type != KM_CLICK)) { menu->menuretval = UI_RETURN_CANCEL; } else { @@ -9074,9 +9078,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case YKEY: case ZKEY: { - if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) && - !IS_EVENT_MOD(event, shift, ctrl, oskey)) - { + if ((event->val == KM_PRESS) && !IS_EVENT_MOD(event, shift, ctrl, oskey)) { for (but = block->buttons.first; but; but = but->next) { if (but->menu_key == event->type) { ui_but_pie_button_activate(C, but, menu); @@ -9183,8 +9185,9 @@ static int ui_handle_menus_recursive( if (block->flag & UI_BLOCK_RADIAL) retval = ui_pie_handler(C, event, menu); - else if (event->type == LEFTMOUSE || event->val != KM_DBL_CLICK) + else if ((event->type == LEFTMOUSE) || (event->click_type != KM_DBL_CLICK)) { retval = ui_handle_menu_event(C, event, menu, level, is_parent_inside, is_parent_menu, is_floating); + } } } @@ -9220,10 +9223,16 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use retval = ui_handle_list_event(C, event, ar); if (retval == WM_UI_HANDLER_CONTINUE) { - if (but) + if (but) { retval = ui_handle_button_event(C, event, but); - else + } + else { retval = ui_handle_button_over(C, event, ar); + + /* let's make sure we are really not hovering a button by adding a mousemove! + * XXX some WM_event_add_mousemove calls may become unnecessary with this and can be removed */ + WM_event_add_mousemove(C); + } } /* re-enable tooltips */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 4baaade33b9..705b55b7ea6 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -617,6 +617,7 @@ static int editsource_exec(bContext *C, wmOperator *op) /* redraw and get active button python info */ ED_region_do_draw(C, ar); + ar->do_draw = false; for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash); BLI_ghashIterator_done(&ghi) == false; diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index d165e2719c5..7d6d5c3a6d7 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1865,7 +1865,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) uiHandlePanelData *data = panel->activedata; /* verify if we can stop */ - if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); int align = panel_aligned(sa, ar); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 7cb9dca78bf..0fbd0c40510 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2749,8 +2749,8 @@ uiPieMenu *UI_pie_menu_begin(struct bContext *C, const char *title, int icon, co pie->block_radial->puphash = ui_popup_menu_hash(title); pie->block_radial->flag |= UI_BLOCK_RADIAL; - /* if pie is spawned by a left click, it is always assumed to be click style */ - if (event->type == LEFTMOUSE) { + /* if pie is spawned by a left click or on release, it is always assumed to be click style */ + if ((event->type == LEFTMOUSE) || (event->val == KM_RELEASE)) { pie->block_radial->pie_data.flags |= UI_PIE_CLICK_STYLE; pie->block_radial->pie_data.event = EVENT_NONE; win->lock_pie_event = EVENT_NONE; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 7d019474f9e..c185ac45181 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -3279,17 +3279,18 @@ static void operator_search_cb(const bContext *C, void *UNUSED(arg), const char for (WM_operatortype_iter(&iter); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { wmOperatorType *ot = BLI_ghashIterator_getValue(&iter); + const char *ot_ui_name = CTX_IFACE_(ot->translation_context, ot->name); if ((ot->flag & OPTYPE_INTERNAL) && (G.debug & G_DEBUG_WM) == 0) continue; - if (BLI_strcasestr(ot->name, str)) { + if (BLI_strcasestr(ot_ui_name, str)) { if (WM_operator_poll((bContext *)C, ot)) { char name[256]; - int len = strlen(ot->name); + int len = strlen(ot_ui_name); /* display name for menu, can hold hotkey */ - BLI_strncpy(name, ot->name, sizeof(name)); + BLI_strncpy(name, ot_ui_name, sizeof(name)); /* check for hotkey */ if (len < sizeof(name) - 6) { diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 15e742c930a..b7545199f56 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -2602,7 +2602,12 @@ void init_userdef_do_versions(void) cp[3] = 255; } } - + + if (U.versionfile < 274 || (U.versionfile == 274 && U.subversionfile < 3)) { + if (U.click_timeout == 0) + U.click_timeout = 250; + } + if (U.pixelsize == 0.0f) U.pixelsize = 1.0f; diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 9b1b0b915c1..5cdd45061e1 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -510,7 +510,11 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w float min[3], max[3]; bool done = false; bool use_proj; - + + if (ELEM(event->click_type, KM_CLICK, KM_HOLD)) { + return OPERATOR_CANCELLED; + } + em_setup_viewcontext(C, &vc); ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 2855af063c0..79eec57712f 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -407,9 +407,9 @@ void ED_keymap_mesh(wmKeyConfig *keyconf) /* Vertex Slide */ WM_keymap_add_item(keymap, "TRANSFORM_OT_vert_slide", VKEY, KM_PRESS, KM_SHIFT, 0); /* use KM_CLICK because same key is used for tweaks */ - kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_RELEASE, KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "rotate_source", true); - kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_CLICK, KM_SHIFT | KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_PRESS, KM_SHIFT | KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "rotate_source", false); WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_mesh_delete", XKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 99b351561c7..37faa7ba664 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1051,7 +1051,7 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) */ { /* create new data for NLA hierarchy */ - AnimData *adt = BKE_id_add_animdata(&ob->id); + AnimData *adt = BKE_animdata_add_id(&ob->id); NlaTrack *nlt = add_nlatrack(adt, NULL); NlaStrip *strip = add_nla_soundstrip(scene, ob->data); strip->start = CFRA; @@ -1358,7 +1358,7 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base, basen->object = ob; /* make sure apply works */ - BKE_free_animdata(&ob->id); + BKE_animdata_free(&ob->id); ob->adt = NULL; /* Proxies are not to be copied. */ @@ -1976,7 +1976,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base /* duplicates using userflags */ if (dupflag & USER_DUP_ACT) { - BKE_copy_animdata_id_action(&obn->id); + BKE_animdata_copy_id_action(&obn->id); } if (dupflag & USER_DUP_MAT) { @@ -1989,7 +1989,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base id->us--; if (dupflag & USER_DUP_ACT) { - BKE_copy_animdata_id_action(&obn->mat[a]->id); + BKE_animdata_copy_id_action(&obn->mat[a]->id); } } } @@ -2004,7 +2004,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base psys->part = BKE_particlesettings_copy(psys->part); if (dupflag & USER_DUP_ACT) { - BKE_copy_animdata_id_action(&psys->part->id); + BKE_animdata_copy_id_action(&psys->part->id); } id->us--; @@ -2132,9 +2132,9 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_ACT) { bActuator *act; - BKE_copy_animdata_id_action((ID *)obn->data); + BKE_animdata_copy_id_action((ID *)obn->data); if (key) { - BKE_copy_animdata_id_action((ID *)key); + BKE_animdata_copy_id_action((ID *)key); } /* Update the duplicated action in the action actuators */ diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index fe9ee71aa8e..d492a4b9dda 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -929,7 +929,7 @@ cage_cleanup: BakeData *bake = &scene->r.bake; char name[FILE_MAX]; - BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false); + BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false, NULL); if (is_automatic_name) { BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_"); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index ad02f78e7b6..ac5ec1976ab 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1870,7 +1870,7 @@ void OBJECT_OT_correctivesmooth_bind(wmOperatorType *ot) { /* identifiers */ ot->name = "Corrective Smooth Bind"; - ot->description = "Bind base pose in delta mush modifier"; + ot->description = "Bind base pose in Corrective Smooth modifier"; ot->idname = "OBJECT_OT_correctivesmooth_bind"; /* api callbacks */ @@ -2045,7 +2045,7 @@ static void init_ocean_modifier_bake(struct Ocean *oc, struct OceanModifierData do_normals = (omd->flag & MOD_OCEAN_GENERATE_NORMALS); do_jacobian = (omd->flag & MOD_OCEAN_GENERATE_FOAM); - BKE_init_ocean(oc, omd->resolution * omd->resolution, omd->resolution * omd->resolution, omd->spatial_size, omd->spatial_size, + BKE_ocean_init(oc, omd->resolution * omd->resolution, omd->resolution * omd->resolution, omd->spatial_size, omd->spatial_size, omd->wind_velocity, omd->smallest_wave, 1.0, omd->wave_direction, omd->damp, omd->wave_alignment, omd->depth, omd->time, do_heightfield, do_chop, do_normals, do_jacobian, @@ -2103,7 +2103,7 @@ static void oceanbake_startjob(void *customdata, short *stop, short *do_update, G.is_break = false; /* XXX shared with render - replace with job 'stop' switch */ - BKE_bake_ocean(oj->ocean, oj->och, oceanbake_update, (void *)oj); + BKE_ocean_bake(oj->ocean, oj->och, oceanbake_update, (void *)oj); *do_update = true; *stop = 0; @@ -2114,7 +2114,7 @@ static void oceanbake_endjob(void *customdata) OceanBakeJob *oj = customdata; if (oj->ocean) { - BKE_free_ocean(oj->ocean); + BKE_ocean_free(oj->ocean); oj->ocean = NULL; } @@ -2145,7 +2145,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - och = BKE_init_ocean_cache(omd->cachepath, modifier_path_relbase(ob), + och = BKE_ocean_init_cache(omd->cachepath, modifier_path_relbase(ob), omd->bakestart, omd->bakeend, omd->wave_scale, omd->chop_amount, omd->foam_coverage, omd->foam_fade, omd->resolution); @@ -2179,11 +2179,11 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) } /* make a copy of ocean to use for baking - threadsafety */ - ocean = BKE_add_ocean(); + ocean = BKE_ocean_add(); init_ocean_modifier_bake(ocean, omd); #if 0 - BKE_bake_ocean(ocean, och); + BKE_ocean_bake(ocean, och); omd->oceancache = och; omd->cached = true; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 1045d7245fe..5c12a84ce6f 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -305,7 +305,7 @@ void ED_keymap_object(wmKeyConfig *keyconf) keymap = WM_keymap_find(keyconf, "Object Non-modal", 0, 0); /* Note: this keymap works disregarding mode */ - kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_CLICK, 0, 0); RNA_enum_set(kmi->ptr, "mode", OB_MODE_EDIT); RNA_boolean_set(kmi->ptr, "toggle", true); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index bfa501c3732..2d981594524 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1574,13 +1574,13 @@ static int make_links_data_exec(bContext *C, wmOperator *op) DAG_id_tag_update(&ob_dst->id, 0); break; case MAKE_LINKS_ANIMDATA: - BKE_copy_animdata_id((ID *)ob_dst, (ID *)ob_src, false); + BKE_animdata_copy_id((ID *)ob_dst, (ID *)ob_src, false); if (ob_dst->data && ob_src->data) { if (obdata_id->lib) { is_lib = true; break; } - BKE_copy_animdata_id((ID *)ob_dst->data, (ID *)ob_src->data, false); + BKE_animdata_copy_id((ID *)ob_dst->data, (ID *)ob_src->data, false); } DAG_id_tag_update(&ob_dst->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); break; @@ -1871,7 +1871,7 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag) case OB_MESH: ob->data = me = BKE_mesh_copy(ob->data); if (me->key) - BKE_copy_animdata_id_action((ID *)me->key); + BKE_animdata_copy_id_action((ID *)me->key); break; case OB_MBALL: ob->data = BKE_mball_copy(ob->data); @@ -1883,12 +1883,12 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag) ID_NEW(cu->bevobj); ID_NEW(cu->taperobj); if (cu->key) - BKE_copy_animdata_id_action((ID *)cu->key); + BKE_animdata_copy_id_action((ID *)cu->key); break; case OB_LATTICE: ob->data = lat = BKE_lattice_copy(ob->data); if (lat->key) - BKE_copy_animdata_id_action((ID *)lat->key); + BKE_animdata_copy_id_action((ID *)lat->key); break; case OB_ARMATURE: DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -1909,7 +1909,7 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag) * AnimData structure, which is not what we want. * (sergey) */ - BKE_copy_animdata_id_action((ID *)ob->data); + BKE_animdata_copy_id_action((ID *)ob->data); id->us--; id->newid = ob->data; @@ -1933,7 +1933,7 @@ static void single_object_action_users(Scene *scene, const int flag) ob = base->object; if (ob->id.lib == NULL && (flag == 0 || (base->flag & SELECT)) ) { DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - BKE_copy_animdata_id_action(&ob->id); + BKE_animdata_copy_id_action(&ob->id); } } } @@ -1956,7 +1956,7 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture if (ma->id.us > 1) { man = BKE_material_copy(ma); - BKE_copy_animdata_id_action(&man->id); + BKE_animdata_copy_id_action(&man->id); man->id.us = 0; assign_material(ob, man, a, BKE_MAT_ASSIGN_USERPREF); @@ -1967,7 +1967,7 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture if (tex->id.us > 1) { tex->id.us--; tex = BKE_texture_copy(tex); - BKE_copy_animdata_id_action(&tex->id); + BKE_animdata_copy_id_action(&tex->id); man->mtex[b]->tex = tex; } } @@ -1994,7 +1994,7 @@ static void do_single_tex_user(Tex **from) } else if (tex->id.us > 1) { texn = BKE_texture_copy(tex); - BKE_copy_animdata_id_action(&texn->id); + BKE_animdata_copy_id_action(&texn->id); tex->id.newid = (ID *)texn; tex->id.us--; *from = texn; diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h index ee0046b49dc..5c6744b0248 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -55,6 +55,9 @@ void MATERIAL_OT_paste(struct wmOperatorType *ot); void SCENE_OT_render_layer_add(struct wmOperatorType *ot); void SCENE_OT_render_layer_remove(struct wmOperatorType *ot); +void SCENE_OT_render_view_add(struct wmOperatorType *ot); +void SCENE_OT_render_view_remove(struct wmOperatorType *ot); + #ifdef WITH_FREESTYLE void SCENE_OT_freestyle_module_add(struct wmOperatorType *ot); void SCENE_OT_freestyle_module_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 1c9f32697d4..f76f7cad1ae 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -48,6 +48,7 @@ #include "DNA_userdef_types.h" #include "BKE_blender.h" +#include "BKE_camera.h" #include "BKE_context.h" #include "BKE_colortools.h" #include "BKE_depsgraph.h" @@ -116,7 +117,7 @@ typedef struct RenderJob { } RenderJob; /* called inside thread! */ -static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibuf, ImageUser *iuser, volatile rcti *renrect) +static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibuf, ImageUser *iuser, volatile rcti *renrect, const char *viewname) { Scene *scene = rj->scene; const float *rectf = NULL; @@ -188,11 +189,11 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu */ /* TODO(sergey): Need to check has_combined here? */ if (iuser->pass == 0) { + size_t view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); /* find current float rect for display, first case is after composite... still weak */ - if (rr->rectf) - rectf = rr->rectf; - else { - if (rr->rect32) { + rectf = RE_RenderViewGetRectf(rr, view_id); + if (rectf == NULL) { + if (RE_RenderViewGetRect32(rr, view_id)) { /* special case, currently only happens with sequencer rendering, * which updates the whole frame, so we can only mark display buffer * as invalid here (sergey) @@ -201,8 +202,8 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu return; } else { - if (rr->renlay == NULL || rr->renlay->rectf == NULL) return; - rectf = rr->renlay->rectf; + if (rr->renlay == NULL) return; + rectf = RE_RenderLayerGetPass(rr->renlay, SCE_PASS_COMBINED, viewname); } } if (rectf == NULL) return; @@ -531,6 +532,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec Image *ima = rj->image; ImBuf *ibuf; void *lock; + const char *viewname = RE_GetActiveRenderView(rj->re); /* only update if we are displaying the slot being rendered */ if (ima->render_slot != ima->last_render_slot) { @@ -563,7 +565,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec ibuf->channels == 1 || U.image_draw_method != IMAGE_DRAW_METHOD_GLSL) { - image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect); + image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect, viewname); } /* make jobs timer to send notifier */ @@ -1487,7 +1489,8 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C) if (re == NULL) return; } - RE_AcquireResultImage(re, &rres); + /* Viewport render preview doesn't support multiview, view hardcoded to 0 */ + RE_AcquireResultImage(re, &rres, 0); if (rres.rectf) { RegionView3D *rv3d = CTX_wm_region_view3d(C); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 58ea0961307..c680522219f 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -39,6 +39,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_jitter.h" +#include "BLI_threads.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -103,11 +104,17 @@ typedef struct OGLRender { bMovieHandle *mh; int cfrao, nfra; + size_t totvideos; + + /* quick lookup */ + int view_id; + /* wm vars for timer and progress cursor */ wmWindowManager *wm; wmWindow *win; wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/ + void **movie_ctx_arr; } OGLRender; /* added because v3d is not always valid */ @@ -121,16 +128,125 @@ static unsigned int screen_opengl_layers(OGLRender *oglrender) } } -static void screen_opengl_render_apply(OGLRender *oglrender) +static bool screen_opengl_is_multiview(OGLRender *oglrender) +{ + View3D *v3d = oglrender->v3d; + RegionView3D *rv3d = oglrender->rv3d; + RenderData *rd = &oglrender->scene->r; + + if ((rd == NULL) || ((!oglrender->is_sequencer) && ((rv3d == NULL) || (v3d == NULL)))) + return false; + + return (rd->scemode & R_MULTIVIEW) && ((oglrender->is_sequencer) || (rv3d->persp == RV3D_CAMOB && v3d->camera)); +} + +static void screen_opengl_views_setup(OGLRender *oglrender) +{ + RenderResult *rr; + RenderView *rv; + SceneRenderView *srv; + bool is_multiview; + View3D *v3d = oglrender->v3d; + + RenderData *rd = &oglrender->scene->r; + + rr = RE_AcquireResultWrite(oglrender->re); + + is_multiview = screen_opengl_is_multiview(oglrender); + + if (!is_multiview) { + /* we only have one view when multiview is off */ + rv = rr->views.first; + + if (rv == NULL) { + rv = MEM_callocN(sizeof(RenderView), "new opengl render view"); + BLI_addtail(&rr->views, rv); + } + + while (rv->next) { + RenderView *rv_del = rv->next; + BLI_remlink(&rr->views, rv_del); + + if (rv_del->rectf) + MEM_freeN(rv_del->rectf); + + if (rv_del->rectz) + MEM_freeN(rv_del->rectz); + + MEM_freeN(rv_del); + } + } + else { + if (!oglrender->is_sequencer) + RE_SetOverrideCamera(oglrender->re, V3D_CAMERA_SCENE(oglrender->scene, v3d)); + + /* remove all the views that are not needed */ + rv = rr->views.last; + while (rv) { + srv = BLI_findstring(&rd->views, rv->name, offsetof(SceneRenderView, name)); + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (rv->rectf == NULL) + rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect"); + rv = rv->prev; + } + else { + RenderView *rv_del = rv; + rv = rv_del->prev; + + BLI_remlink(&rr->views, rv_del); + + if (rv_del->rectf) + MEM_freeN(rv_del->rectf); + + if (rv_del->rectz) + MEM_freeN(rv_del->rectz); + + MEM_freeN(rv_del); + } + } + + /* create all the views that are needed */ + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + + rv = BLI_findstring(&rr->views, srv->name, offsetof(SceneRenderView, name)); + + if (rv == NULL) { + rv = MEM_callocN(sizeof(RenderView), "new opengl render view"); + BLI_strncpy(rv->name, srv->name, sizeof(rv->name)); + BLI_addtail(&rr->views, rv); + } + } + } + + for (rv = rr->views.first; rv; rv = rv->next) { + if (rv->rectf == NULL) { + rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect"); + } + } + + BLI_lock_thread(LOCK_DRAW_IMAGE); + if (is_multiview && BKE_scene_multiview_is_stereo3d(rd)) { + oglrender->ima->flag |= IMA_IS_STEREO; + } + else { + oglrender->ima->flag &= ~IMA_IS_STEREO; + oglrender->iuser.flag &= ~IMA_SHOW_STEREO; + } + BLI_unlock_thread(LOCK_DRAW_IMAGE); + + RE_ReleaseResult(oglrender->re); +} + +static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr) { Scene *scene = oglrender->scene; ARegion *ar = oglrender->ar; View3D *v3d = oglrender->v3d; RegionView3D *rv3d = oglrender->rv3d; - RenderResult *rr; Object *camera = NULL; ImBuf *ibuf; - void *lock; float winmat[4][4]; int sizex = oglrender->sizex; int sizey = oglrender->sizey; @@ -138,8 +254,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) bool draw_bgpic = true; bool draw_sky = (scene->r.alphamode == R_ADDSKY); unsigned char *rect = NULL; - - rr = RE_AcquireResultRead(oglrender->re); + const char *viewname = RE_GetActiveRenderView(oglrender->re); if (oglrender->is_sequencer) { SeqRenderData context; @@ -152,9 +267,11 @@ static void screen_opengl_render_apply(OGLRender *oglrender) oglrender->sizex, oglrender->sizey, 100.0f, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); ibuf = BKE_sequencer_give_ibuf(&context, CFRA, chanshown); if (ibuf) { + float *rectf; ImBuf *linear_ibuf; BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y)); @@ -175,7 +292,8 @@ static void screen_opengl_render_apply(OGLRender *oglrender) BKE_sequencer_imbuf_from_sequencer_space(scene, linear_ibuf); } - memcpy(rr->rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); + rectf = RE_RenderViewGetRectf(rr, oglrender->view_id); + memcpy(rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); IMB_freeImBuf(linear_ibuf); } @@ -221,7 +339,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) /* render 3d view */ if (rv3d->persp == RV3D_CAMOB && v3d->camera) { /*int is_ortho = scene->r.mode & R_ORTHO;*/ - camera = v3d->camera; + camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname); RE_GetCameraWindow(oglrender->re, camera, scene->r.cfra, winmat); if (camera->type == OB_CAMERA) { Camera *cam = camera->data; @@ -249,7 +367,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); } else { @@ -265,7 +383,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); for (i = 0; i < sizex * sizey * 4; i++) @@ -281,7 +399,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat_jitter, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); for (i = 0; i < sizex * sizey * 4; i++) @@ -301,7 +419,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) char err_out[256] = "unknown"; ImBuf *ibuf_view = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, oglrender->sizex, oglrender->sizey, IB_rect, OB_SOLID, false, true, true, - (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL, err_out); + (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL, viewname, err_out); camera = scene->camera; if (ibuf_view) { @@ -326,6 +444,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) if (rect) { int profile_to; + float *rectf = RE_RenderViewGetRectf(rr, oglrender->view_id); if (BKE_scene_check_color_management_enabled(scene)) profile_to = IB_PROFILE_LINEAR_RGB; @@ -334,47 +453,60 @@ static void screen_opengl_render_apply(OGLRender *oglrender) /* sequencer has got trickier conversion happened above * also assume opengl's space matches byte buffer color space */ - IMB_buffer_float_from_byte(rr->rectf, rect, + IMB_buffer_float_from_byte(rectf, rect, profile_to, IB_PROFILE_SRGB, true, oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex); - } - /* rr->rectf is now filled with image data */ + /* rr->rectf is now filled with image data */ - if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) { - BKE_image_stamp_buf(scene, camera, rect, rr->rectf, rr->rectx, rr->recty, 4); + if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) + BKE_image_stamp_buf(scene, camera, rect, rectf, rr->rectx, rr->recty, 4); + + MEM_freeN(rect); } +} - RE_ReleaseResult(oglrender->re); +static void screen_opengl_render_write(OGLRender *oglrender) +{ + Scene *scene = oglrender->scene; + RenderResult *rr; + bool ok; + char name[FILE_MAX]; + Object *camera = RE_GetCamera(oglrender->re); + + rr = RE_AcquireResultRead(oglrender->re); - /* update byte from float buffer */ - ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock); + BKE_image_path_from_imformat( + name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL); - if (ibuf) { - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + /* write images as individual images or stereo */ + ok = RE_WriteRenderViewsImage(oglrender->reports, rr, scene, camera, false, name); - /* write file for animation */ - if (oglrender->write_still) { - char name[FILE_MAX]; - int ok; + RE_ReleaseResultImage(oglrender->re); - if (scene->r.im_format.planes == R_IMF_CHAN_DEPTH_8) { - IMB_color_to_bw(ibuf); - } + if (ok) printf("OpenGL Render written to '%s'\n", name); + else printf("OpenGL Render failed to write '%s'\n", name); +} - BKE_image_path_from_imformat( - name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); - ok = BKE_imbuf_write_as(ibuf, name, &scene->r.im_format, true); /* no need to stamp here */ - if (ok) printf("OpenGL Render written to '%s'\n", name); - else printf("OpenGL Render failed to write '%s'\n", name); - } +static void screen_opengl_render_apply(OGLRender *oglrender) +{ + RenderResult *rr; + RenderView *rv; + int view_id; + + rr = RE_AcquireResultRead(oglrender->re); + for (rv = rr->views.first, view_id = 0; rv; rv = rv->next, view_id++) { + RE_SetActiveRenderView(oglrender->re, rv->name); + oglrender->view_id = view_id; + screen_opengl_render_doit(oglrender, rr); } - - BKE_image_release_ibuf(oglrender->ima, ibuf, lock); - if (rect) - MEM_freeN(rect); + RE_ReleaseResult(oglrender->re); + + if (oglrender->write_still) { + screen_opengl_render_write(oglrender); + } } static bool screen_opengl_render_init(bContext *C, wmOperator *op) @@ -386,7 +518,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ScrArea *prevsa = CTX_wm_area(C); ARegion *prevar = CTX_wm_region(C); - RenderResult *rr; GPUOffScreen *ofs; OGLRender *oglrender; int sizex, sizey; @@ -459,7 +590,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->sseq = CTX_wm_space_seq(C); } - oglrender->prevsa = prevsa; oglrender->prevar = prevar; @@ -493,15 +623,17 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) /* create render result */ RE_InitState(oglrender->re, NULL, &scene->r, NULL, sizex, sizey, NULL); - rr = RE_AcquireResultWrite(oglrender->re); - if (rr->rectf == NULL) - rr->rectf = MEM_callocN(sizeof(float) * 4 * sizex * sizey, "screen_opengl_render_init rect"); - RE_ReleaseResult(oglrender->re); + /* create render views */ + screen_opengl_views_setup(oglrender); /* wm vars */ oglrender->wm = wm; oglrender->win = win; + oglrender->totvideos = 0; + oglrender->mh = NULL; + oglrender->movie_ctx_arr = NULL; + return true; } @@ -509,10 +641,19 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) { Main *bmain = CTX_data_main(C); Scene *scene = oglrender->scene; + size_t i; if (oglrender->mh) { - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) - oglrender->mh->end_movie(); + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + for (i = 0; i < oglrender->totvideos; i++) { + oglrender->mh->end_movie(oglrender->movie_ctx_arr[i]); + oglrender->mh->context_free(oglrender->movie_ctx_arr[i]); + } + } + + if (oglrender->movie_ctx_arr) { + MEM_freeN(oglrender->movie_ctx_arr); + } } if (oglrender->timer) { /* exec will not have a timer */ @@ -553,13 +694,27 @@ static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) oglrender = op->customdata; scene = oglrender->scene; + oglrender->totvideos = BKE_scene_multiview_num_videos_get(&scene->r); oglrender->reports = op->reports; - oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype); + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - if (!oglrender->mh->start_movie(scene, &scene->r, oglrender->sizex, oglrender->sizey, oglrender->reports, PRVRANGEON != 0)) { - screen_opengl_render_end(C, oglrender); - return 0; + size_t i, width, height; + + BKE_scene_multiview_videos_dimensions_get(&scene->r, oglrender->sizex, oglrender->sizey, &width, &height); + oglrender->movie_ctx_arr = MEM_mallocN(sizeof(void *) * oglrender->totvideos, "Movies"); + oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype); + + for (i = 0; i < oglrender->totvideos; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i); + + oglrender->movie_ctx_arr[i] = oglrender->mh->context_create(); + if (!oglrender->mh->start_movie(oglrender->movie_ctx_arr[i], scene, &scene->r, oglrender->sizex, + oglrender->sizey, oglrender->reports, PRVRANGEON != 0, suffix)) + { + screen_opengl_render_end(C, oglrender); + return 0; + } } } @@ -569,18 +724,17 @@ static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) return 1; } + static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); OGLRender *oglrender = op->customdata; Scene *scene = oglrender->scene; - ImBuf *ibuf, *ibuf_save = NULL; - void *lock; char name[FILE_MAX]; bool ok = false; const bool view_context = (oglrender->v3d != NULL); - Object *camera = NULL; bool is_movie; + RenderResult *rr; /* go to next frame */ if (CFRA < oglrender->nfra) @@ -600,7 +754,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) if (!is_movie) { BKE_image_path_from_imformat( name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) { BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name); @@ -620,89 +774,40 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) if (BKE_scene_camera_switch_update(scene)) { oglrender->v3d->camera = scene->camera; } - - camera = oglrender->v3d->camera; } } else { BKE_scene_camera_switch_update(scene); - - camera = scene->camera; } /* render into offscreen buffer */ screen_opengl_render_apply(oglrender); /* save to disk */ - ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock); - - if (ibuf) { - bool needs_free = false; - - ibuf_save = ibuf; - - if (is_movie || !BKE_imtype_requires_linear_float(scene->r.im_format.imtype)) { - ibuf_save = IMB_colormanagement_imbuf_for_write(ibuf, true, true, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); - - needs_free = true; - } - - /* color -> grayscale */ - /* editing directly would alter the render view */ - if (scene->r.im_format.planes == R_IMF_PLANES_BW) { - ImBuf *ibuf_bw = IMB_dupImBuf(ibuf_save); - IMB_color_to_bw(ibuf_bw); - - if (needs_free) - IMB_freeImBuf(ibuf_save); - - ibuf_save = ibuf_bw; - } - else { - /* this is lightweight & doesnt re-alloc the buffers, only do this - * to save the correct bit depth since the image is always RGBA */ - ImBuf *ibuf_cpy = IMB_allocImBuf(ibuf_save->x, ibuf_save->y, scene->r.im_format.planes, 0); - - ibuf_cpy->rect = ibuf_save->rect; - ibuf_cpy->rect_float = ibuf_save->rect_float; - ibuf_cpy->zbuf_float = ibuf_save->zbuf_float; - - if (needs_free) { - ibuf_cpy->mall = ibuf_save->mall; - ibuf_save->mall = 0; - IMB_freeImBuf(ibuf_save); - } + rr = RE_AcquireResultRead(oglrender->re); - ibuf_save = ibuf_cpy; + if (is_movie) { + ok = RE_WriteRenderViewsMovie(oglrender->reports, rr, scene, &scene->r, oglrender->mh, oglrender->sizex, + oglrender->sizey, oglrender->movie_ctx_arr, oglrender->totvideos); + if (ok) { + printf("Append frame %d", scene->r.cfra); + BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra); } - - if (is_movie) { - ok = oglrender->mh->append_movie(&scene->r, PSFRA, CFRA, (int *)ibuf_save->rect, - oglrender->sizex, oglrender->sizey, oglrender->reports); - if (ok) { - printf("Append frame %d", scene->r.cfra); - BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra); - } + } + else { + ok = RE_WriteRenderViewsImage(op->reports, rr, scene, scene->camera, true, name); + if (ok) { + printf("Saved: %s", name); + BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name); } else { - ok = BKE_imbuf_write_stamp(scene, camera, ibuf_save, name, &scene->r.im_format); - - if (ok == 0) { - printf("Write error: cannot save %s\n", name); - BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name); - } - else { - printf("Saved: %s", name); - BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name); - } + printf("Write error: cannot save %s\n", name); + BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name); } - - if (needs_free) - IMB_freeImBuf(ibuf_save); } - BKE_image_release_ibuf(oglrender->ima, ibuf, lock); + RE_ReleaseResult(oglrender->re); + /* movie stats prints have no line break */ printf("\n"); diff --git a/source/blender/editors/render/render_ops.c b/source/blender/editors/render/render_ops.c index 0d334082a2b..6aaad98f946 100644 --- a/source/blender/editors/render/render_ops.c +++ b/source/blender/editors/render/render_ops.c @@ -58,6 +58,9 @@ void ED_operatortypes_render(void) WM_operatortype_append(SCENE_OT_render_layer_add); WM_operatortype_append(SCENE_OT_render_layer_remove); + WM_operatortype_append(SCENE_OT_render_view_add); + WM_operatortype_append(SCENE_OT_render_view_remove); + #ifdef WITH_FREESTYLE WM_operatortype_append(SCENE_OT_freestyle_module_add); WM_operatortype_append(SCENE_OT_freestyle_module_remove); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 9dbd2e88563..e6fbfda3067 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -548,7 +548,9 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect, /* test if something rendered ok */ re = RE_GetRender(name); - RE_AcquireResultImage(re, &rres); + + /* material preview only needs monoscopy (view 0) */ + RE_AcquireResultImage(re, &rres, 0); if (rres.rectf) { @@ -561,9 +563,11 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect, unsigned char *rect_byte = MEM_mallocN(rres.rectx * rres.recty * sizeof(int), "ed_preview_draw_rect"); float fx = rect->xmin + offx; float fy = rect->ymin; + + /* material preview only needs monoscopy (view 0) */ if (re) - RE_AcquiredResultGet32(re, &rres, (unsigned int *)rect_byte); - + RE_AcquiredResultGet32(re, &rres, (unsigned int *)rect_byte, 0); + glaDrawPixelsSafe(fx, fy, rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect_byte); MEM_freeN(rect_byte); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index cf712a653e6..3de21536148 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -605,6 +605,70 @@ void SCENE_OT_render_layer_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/********************** render view operators *********************/ + +static int render_view_remove_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + + /* don't allow user to remove "left" and "right" views */ + return scene->r.actview > 1; +} + +static int render_view_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + + BKE_scene_add_render_view(scene, NULL); + scene->r.actview = BLI_listbase_count(&scene->r.views) - 1; + + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + + return OPERATOR_FINISHED; +} + +void SCENE_OT_render_view_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Render View"; + ot->idname = "SCENE_OT_render_view_add"; + ot->description = "Add a render view"; + + /* api callbacks */ + ot->exec = render_view_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int render_view_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + SceneRenderView *rv = BLI_findlink(&scene->r.views, scene->r.actview); + + if (!BKE_scene_remove_render_view(scene, rv)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + + return OPERATOR_FINISHED; +} + +void SCENE_OT_render_view_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Render View"; + ot->idname = "SCENE_OT_render_view_remove"; + ot->description = "Remove the selected render view"; + + /* api callbacks */ + ot->exec = render_view_remove_exec; + ot->poll = render_view_remove_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + #ifdef WITH_FREESTYLE static bool freestyle_linestyle_check_report(FreestyleLineSet *lineset, ReportList *reports) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 90c09070bc3..455d280e9ef 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -493,7 +493,6 @@ void ED_region_do_draw(bContext *C, ARegion *ar) glDisable(GL_BLEND); #endif - ar->do_draw = 0; memset(&ar->drawrct, 0, sizeof(ar->drawrct)); UI_blocklist_free_inactive(C, &ar->uiblocks); @@ -773,7 +772,7 @@ static void region_azone_tab_plus(ScrArea *sa, AZone *az, ARegion *ar) switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: - if (ar->winrct.ymax == sa->totrct.ymin) add = 1; else add = 0; + add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0; az->x1 = ar->winrct.xmax - 2.5f * AZONEPAD_TAB_PLUSW; az->y1 = ar->winrct.ymax - add; az->x2 = ar->winrct.xmax - 1.5f * AZONEPAD_TAB_PLUSW; @@ -818,7 +817,7 @@ static void region_azone_tab(ScrArea *sa, AZone *az, ARegion *ar) switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: - if (ar->winrct.ymax == sa->totrct.ymin) add = 1; else add = 0; + add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0; az->x1 = ar->winrct.xmax - 2 * AZONEPAD_TABW; az->y1 = ar->winrct.ymax - add; az->x2 = ar->winrct.xmax - AZONEPAD_TABW; @@ -863,7 +862,7 @@ static void region_azone_tria(ScrArea *sa, AZone *az, ARegion *ar) switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: - if (ar->winrct.ymax == sa->totrct.ymin) add = 1; else add = 0; + add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0; az->x1 = ar->winrct.xmax - 2 * AZONEPAD_TRIAW; az->y1 = ar->winrct.ymax - add; az->x2 = ar->winrct.xmax - AZONEPAD_TRIAW; diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 115f7829420..3a40c300338 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -59,6 +59,7 @@ #include "ED_screen.h" #include "ED_screen_types.h" #include "ED_clip.h" +#include "ED_node.h" #include "ED_render.h" #include "UI_interface.h" @@ -2124,4 +2125,85 @@ void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute)) } +/* + * return true if any active area requires to see in 3D + */ +bool ED_screen_stereo3d_required(bScreen *screen) +{ + ScrArea *sa; + Scene *sce = screen->scene; + const bool is_multiview = (sce->r.scemode & R_MULTIVIEW) != 0; + + for (sa = screen->areabase.first; sa; sa = sa->next) { + switch (sa->spacetype) { + case SPACE_VIEW3D: + { + View3D *v3d; + + if (!is_multiview) + continue; + + v3d = sa->spacedata.first; + if (v3d->camera && v3d->stereo3d_camera == STEREO_3D_ID) { + ARegion *ar; + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiondata && ar->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = ar->regiondata; + if (rv3d->persp == RV3D_CAMOB) { + return true; + } + } + } + } + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima; + + /* images should always show in stereo, even if + * the file doesn't have views enabled */ + sima = sa->spacedata.first; + if (sima->image && (sima->image->flag & IMA_IS_STEREO) && + (sima->iuser.flag & IMA_SHOW_STEREO)) + { + return true; + } + break; + } + case SPACE_NODE: + { + SpaceNode *snode; + + if (!is_multiview) + continue; + snode = sa->spacedata.first; + if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { + return true; + } + break; + } + case SPACE_SEQ: + { + SpaceSeq *sseq; + + if (!is_multiview) + continue; + + sseq = sa->spacedata.first; + if (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW)) { + return true; + } + + if (sseq->draw_flag & SEQ_DRAW_OVERDROP) { + return true; + } + + break; + } + } + } + + return false; +} diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index b5d7eb98595..c723a30bb73 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -717,7 +717,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) else event.type = EVT_ACTIONZONE_REGION; - event.val = 0; + event.val = KM_NOTHING; event.customdata = op->customdata; event.customdatafree = true; op->customdata = NULL; @@ -990,6 +990,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) rect.ymax = rect.ymin + BLI_rcti_size_y(&rect) / U.pixelsize; newwin = WM_window_open(C, &rect); + *newwin->stereo3d_format = *win->stereo3d_format; /* allocs new screen and adds to newly created window, using window size */ newsc = ED_screen_add(newwin, CTX_data_scene(C), sc->id.name + 2); diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 330bc7e3395..4644f0ae0b8 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -303,6 +303,7 @@ typedef struct ScreenshotJob { const short *stop; const short *do_update; ReportList reports; + void *movie_ctx; } ScreenshotJob; @@ -312,7 +313,10 @@ static void screenshot_freejob(void *sjv) if (sj->dumprect) MEM_freeN(sj->dumprect); - + + if (sj->movie_ctx) + MEM_freeN(sj->movie_ctx); + MEM_freeN(sj); } @@ -337,20 +341,21 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float { ScreenshotJob *sj = sjv; RenderData rd = sj->scene->r; - bMovieHandle *mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype); - + bMovieHandle *mh = NULL; + /* we need this as local variables for renderdata */ rd.frs_sec = U.scrcastfps; rd.frs_sec_base = 1.0f; if (BKE_imtype_is_movie(rd.im_format.imtype)) { - if (!mh->start_movie(sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports, false)) { + mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype); + sj->movie_ctx = mh->context_create(); + + if (!mh->start_movie(sj->movie_ctx, sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports, false, "")) { printf("screencast job stopped\n"); return; } } - else - mh = NULL; sj->stop = stop; sj->do_update = do_update; @@ -362,8 +367,8 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float if (sj->dumprect) { if (mh) { - if (mh->append_movie(&rd, rd.sfra, rd.cfra, (int *)sj->dumprect, - sj->dumpsx, sj->dumpsy, &sj->reports)) + if (mh->append_movie(sj->movie_ctx, &rd, rd.sfra, rd.cfra, (int *)sj->dumprect, + sj->dumpsx, sj->dumpsy, "", &sj->reports)) { BKE_reportf(&sj->reports, RPT_INFO, "Appended frame: %d", rd.cfra); printf("Appended frame %d\n", rd.cfra); @@ -379,7 +384,7 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float BKE_image_path_from_imformat( name, rd.pic, sj->bmain->name, rd.cfra, - &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true); + &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true, NULL); ibuf->rect = sj->dumprect; ok = BKE_imbuf_write(ibuf, name, &rd.im_format); @@ -410,8 +415,10 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float PIL_sleep_ms(U.scrcastwait); } - if (mh) - mh->end_movie(); + if (mh) { + mh->end_movie(sj->movie_ctx); + mh->context_free(sj->movie_ctx); + } BKE_report(&sj->reports, RPT_INFO, "Screencast job stopped"); } diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 5cfbd164153..7992767072a 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -499,7 +499,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te int w = imapaintpartial.x2 - imapaintpartial.x1; int h = imapaintpartial.y2 - imapaintpartial.y1; /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); + GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); } } @@ -1393,7 +1393,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) if (ma && ma->texpaintslot) ima = ma->texpaintslot[ma->paint_active_slot].ima; } - else if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { ima = imapaint->canvas; } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 4f71bea7cd2..9d39eea7b96 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1109,7 +1109,7 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) if (ima == NULL) { return 0; } - else if (ima->packedfile && ima->rr) { + else if (BKE_image_has_packedfile(ima) && ima->rr) { s->warnpackedfile = ima->id.name + 2; return 0; } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index f853fd91491..b01d315650f 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1964,7 +1964,7 @@ static float angle_2d_clockwise(const float p1[2], const float p2[2], const floa v1[0] = p1[0] - p2[0]; v1[1] = p1[1] - p2[1]; v2[0] = p3[0] - p2[0]; v2[1] = p3[1] - p2[1]; - return -atan2(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); + return -atan2f(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); } #endif @@ -5233,7 +5233,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) if (w > maxsize) w = maxsize; if (h > maxsize) h = maxsize; - ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, err_out); + ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, NULL, err_out); if (!ibuf) { /* Mostly happens when OpenGL offscreen buffer was failed to create, */ /* but could be other reasons. Should be handled in the future. nazgul */ @@ -5444,7 +5444,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain) RNA_string_get(op->ptr, "name", imagename); } ima = BKE_image_add_generated(bmain, width, height, imagename, alpha ? 32 : 24, use_float, - gen_type, color); + gen_type, color, false); return ima; } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index bfd429d0924..0db2259b307 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -1022,7 +1022,7 @@ static void paint_stroke_line_constrain (PaintStroke *stroke, float mouse[2]) float angle, len, res; sub_v2_v2v2(line, mouse, stroke->last_mouse_position); - angle = atan2(line[1], line[0]); + angle = atan2f(line[1], line[0]); len = len_v2(line); /* divide angle by PI/4 */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 5c55ad1c63d..6ca368ea4f7 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -4912,7 +4912,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) /* Leave sculptmode */ ob->mode &= ~mode_flag; - BKE_free_sculptsession(ob); + BKE_sculptsession_free(ob); paint_cursor_delete_textures(); } @@ -4950,7 +4950,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) /* Create sculpt mode session data */ if (ob->sculpt) - BKE_free_sculptsession(ob); + BKE_sculptsession_free(ob); sculpt_init_session(scene, ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index a4adbc6bca8..7b4e6109f1a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -484,7 +484,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) BKE_mesh_calc_normals_tessface(mesh->mvert, mesh->totvert, mesh->mface, mesh->totface, NULL); - BKE_free_sculptsession_deformMats(ss); + BKE_sculptsession_free_deformMats(ss); tag_update |= true; } diff --git a/source/blender/editors/space_action/CMakeLists.txt b/source/blender/editors/space_action/CMakeLists.txt index 50cf84079d2..68e56490217 100644 --- a/source/blender/editors/space_action/CMakeLists.txt +++ b/source/blender/editors/space_action/CMakeLists.txt @@ -36,6 +36,7 @@ set(INC_SYS ) set(SRC + action_data.c action_draw.c action_edit.c action_ops.c diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c new file mode 100644 index 00000000000..a7358172ef5 --- /dev/null +++ b/source/blender/editors/space_action/action_data.c @@ -0,0 +1,938 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2015 Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_action/action_data.c + * \ingroup spaction + */ + + +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <float.h> + + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BLF_translation.h" + +#include "DNA_anim_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_key_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_mask_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_fcurve.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_key.h" +#include "BKE_main.h" +#include "BKE_nla.h" +#include "BKE_scene.h" +#include "BKE_context.h" +#include "BKE_report.h" + +#include "UI_view2d.h" + +#include "ED_anim_api.h" +#include "ED_gpencil.h" +#include "ED_keyframing.h" +#include "ED_keyframes_edit.h" +#include "ED_screen.h" +#include "ED_markers.h" +#include "ED_mask.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" + +#include "action_intern.h" + +/* ************************************************************************** */ +/* ACTION CREATION */ + +/* Helper function to find the active AnimData block from the Action Editor context */ +AnimData *ED_actedit_animdata_from_context(bContext *C) +{ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + Object *ob = CTX_data_active_object(C); + AnimData *adt = NULL; + + /* Get AnimData block to use */ + if (saction->mode == SACTCONT_ACTION) { + /* Currently, "Action Editor" means object-level only... */ + if (ob) { + adt = ob->adt; + } + } + else if (saction->mode == SACTCONT_SHAPEKEY) { + Key *key = BKE_key_from_object(ob); + if (key) { + adt = key->adt; + } + } + + return adt; +} + +/* -------------------------------------------------------------------- */ + +/* Create new action */ +static bAction *action_create_new(bContext *C, bAction *oldact) +{ + ScrArea *sa = CTX_wm_area(C); + bAction *action; + + /* create action - the way to do this depends on whether we've got an + * existing one there already, in which case we make a copy of it + * (which is useful for "versioning" actions within the same file) + */ + if (oldact && GS(oldact->id.name) == ID_AC) { + /* make a copy of the existing action */ + action = BKE_action_copy(oldact); + } + else { + /* just make a new (empty) action */ + action = add_empty_action(CTX_data_main(C), "Action"); + } + + /* when creating new ID blocks, there is already 1 user (as for all new datablocks), + * but the RNA pointer code will assign all the proper users instead, so we compensate + * for that here + */ + BLI_assert(action->id.us == 1); + action->id.us--; + + /* set ID-Root type */ + if (sa->spacetype == SPACE_ACTION) { + SpaceAction *saction = (SpaceAction *)sa->spacedata.first; + + if (saction->mode == SACTCONT_SHAPEKEY) + action->idroot = ID_KE; + else + action->idroot = ID_OB; + } + + return action; +} + +/* Change the active action used by the action editor */ +static void actedit_change_action(bContext *C, bAction *act) +{ + bScreen *screen = CTX_wm_screen(C); + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + + PointerRNA ptr, idptr; + PropertyRNA *prop; + + /* create RNA pointers and get the property */ + RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, saction, &ptr); + prop = RNA_struct_find_property(&ptr, "action"); + + /* NOTE: act may be NULL here, so better to just use a cast here */ + RNA_id_pointer_create((ID *)act, &idptr); + + /* set the new pointer, and force a refresh */ + RNA_property_pointer_set(&ptr, prop, idptr); + RNA_property_update(C, &ptr, prop); +} + +/* ******************** New Action Operator *********************** */ + +/* Criteria: + * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions... + * OR + * The NLA Editor is active (i.e. Animation Data panel -> new action) + * 2) The associated AnimData block must not be in tweakmode + */ +static int action_new_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + + /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */ + /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */ + if (ED_operator_action_active(C)) { + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + Object *ob = CTX_data_active_object(C); + + /* For now, actions are only for the active object, and on object and shapekey levels... */ + if (saction->mode == SACTCONT_ACTION) { + /* XXX: This assumes that actions are assigned to the active object in this mode */ + if (ob) { + if ((ob->adt == NULL) || (ob->adt->flag & ADT_NLA_EDIT_ON) == 0) + return true; + } + } + else if (saction->mode == SACTCONT_SHAPEKEY) { + Key *key = BKE_key_from_object(ob); + if (key) { + if ((key->adt == NULL) || (key->adt->flag & ADT_NLA_EDIT_ON) == 0) + return true; + } + } + } + else if (ED_operator_nla_active(C)) { + if (!(scene->flag & SCE_NLA_EDIT_ON)) { + return true; + } + } + + /* something failed... */ + return false; +} + +static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PointerRNA ptr, idptr; + PropertyRNA *prop; + + /* hook into UI */ + UI_context_active_but_prop_get_templateID(C, &ptr, &prop); + + if (prop) { + bAction *action = NULL, *oldact = NULL; + AnimData *adt = NULL; + PointerRNA oldptr; + + oldptr = RNA_property_pointer_get(&ptr, prop); + oldact = (bAction *)oldptr.id.data; + + /* stash the old action to prevent it from being lost */ + if (ptr.type == &RNA_AnimData) { + adt = ptr.data; + } + else if (ptr.type == &RNA_SpaceDopeSheetEditor) { + adt = ED_actedit_animdata_from_context(C); + } + + /* Perform stashing operation - But only if there is an action */ + if (adt && oldact) { + /* stash the action */ + if (BKE_nla_action_stash(adt)) { + /* The stash operation will remove the user already + * (and unlink the action from the AnimData action slot). + * Hence, we must unset the ref to the action in the + * action editor too (if this is where we're being called from) + * first before setting the new action once it is created, + * or else the user gets decremented twice! + */ + if (ptr.type == &RNA_SpaceDopeSheetEditor) { + SpaceAction *saction = (SpaceAction *)ptr.data; + saction->action = NULL; + } + } + else { + //printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n", oldact->id.name); + } + } + + /* create action */ + action = action_create_new(C, oldact); + + /* set this new action + * NOTE: we can't use actedit_change_action, as this function is also called from the NLA + */ + RNA_id_pointer_create(&action->id, &idptr); + RNA_property_pointer_set(&ptr, prop, idptr); + RNA_property_update(C, &ptr, prop); + } + + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); + + return OPERATOR_FINISHED; +} + +void ACTION_OT_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Action"; + ot->idname = "ACTION_OT_new"; + ot->description = "Create new action"; + + /* api callbacks */ + ot->exec = action_new_exec; + ot->poll = action_new_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************* Action Push-Down Operator ******************** */ + +/* Criteria: + * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions + * 2) There must be an action active + * 3) The associated AnimData block must not be in tweakmode + */ +static int action_pushdown_poll(bContext *C) +{ + if (ED_operator_action_active(C)) { + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Check for AnimData, Actions, and that tweakmode is off */ + if (adt && saction->action) { + /* NOTE: We check this for the AnimData block in question and not the global flag, + * as the global flag may be left dirty by some of the browsing ops here. + */ + if (!(adt->flag & ADT_NLA_EDIT_ON)) + return true; + } + } + + /* something failed... */ + return false; +} + +static int action_pushdown_exec(bContext *C, wmOperator *op) +{ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Do the deed... */ + if (adt) { + /* Perform the pushdown operation + * - This will deal with all the AnimData-side usercounts + */ + if (action_has_motion(adt->action) == 0) { + /* action may not be suitable... */ + BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); + return OPERATOR_CANCELLED; + } + else { + /* action can be safely added */ + BKE_nla_action_pushdown(adt); + } + + /* Stop displaying this action in this editor + * NOTE: The editor itself doesn't set a user... + */ + saction->action = NULL; + } + + /* Send notifiers that stuff has changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + return OPERATOR_FINISHED; +} + +void ACTION_OT_push_down(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Push Down Action"; + ot->idname = "ACTION_OT_push_down"; + ot->description = "Push action down on to the NLA stack as a new strip"; + + /* callbacks */ + ot->exec = action_pushdown_exec; + ot->poll = action_pushdown_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************* Action Stash Operator ******************** */ + +static int action_stash_exec(bContext *C, wmOperator *op) +{ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Perform stashing operation */ + if (adt) { + /* don't do anything if this action is empty... */ + if (action_has_motion(adt->action) == 0) { + /* action may not be suitable... */ + BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); + return OPERATOR_CANCELLED; + } + else { + /* stash the action */ + if (BKE_nla_action_stash(adt)) { + /* The stash operation will remove the user already, + * so the flushing step later shouldn't double up + * the usercount fixes. Hence, we must unset this ref + * first before setting the new action. + */ + saction->action = NULL; + } + else { + /* action has already been added - simply warn about this, and clear */ + BKE_report(op->reports, RPT_ERROR, "Action has already been stashed"); + } + + /* clear action refs from editor, and then also the backing data (not necessary) */ + actedit_change_action(C, NULL); + } + } + + /* Send notifiers that stuff has changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + return OPERATOR_FINISHED; +} + +void ACTION_OT_stash(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Stash Action"; + ot->idname = "ACTION_OT_stash"; + ot->description = "Store this action in the NLA stack as a non-contributing strip for later use"; + + /* callbacks */ + ot->exec = action_stash_exec; + ot->poll = action_pushdown_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean(ot->srna, "create_new", true, "Create New Action", + "Create a new action once the existing one has been safely stored"); +} + +/* ----------------- */ + +/* Criteria: + * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions + * 2) The associated AnimData block must not be in tweakmode + */ +static int action_stash_create_poll(bContext *C) +{ + if (ED_operator_action_active(C)) { + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */ + /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */ + if (adt) { + if (!(adt->flag & ADT_NLA_EDIT_ON)) + return true; + } + else { + /* There may not be any action/animdata yet, so, just fallback to the global setting + * (which may not be totally valid yet if the action editor was used and things are + * now in an inconsistent state) + */ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + Scene *scene = CTX_data_scene(C); + + if (!(scene->flag & SCE_NLA_EDIT_ON)) { + /* For now, actions are only for the active object, and on object and shapekey levels... */ + return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY); + } + } + } + + /* something failed... */ + return false; +} + +static int action_stash_create_exec(bContext *C, wmOperator *op) +{ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Check for no action... */ + if (saction->action == NULL) { + /* just create a new action */ + bAction *action = action_create_new(C, NULL); + actedit_change_action(C, action); + } + else if (adt) { + /* Perform stashing operation */ + if (action_has_motion(adt->action) == 0) { + /* don't do anything if this action is empty... */ + BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); + return OPERATOR_CANCELLED; + } + else { + /* stash the action */ + if (BKE_nla_action_stash(adt)) { + bAction *new_action = NULL; + + /* create new action not based on the old one (since the "new" operator already does that) */ + new_action = action_create_new(C, NULL); + + /* The stash operation will remove the user already, + * so the flushing step later shouldn't double up + * the usercount fixes. Hence, we must unset this ref + * first before setting the new action. + */ + saction->action = NULL; + actedit_change_action(C, new_action); + } + else { + /* action has already been added - simply warn about this, and clear */ + BKE_report(op->reports, RPT_ERROR, "Action has already been stashed"); + actedit_change_action(C, NULL); + } + } + } + + /* Send notifiers that stuff has changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + return OPERATOR_FINISHED; +} + +void ACTION_OT_stash_and_create(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Stash Action"; + ot->idname = "ACTION_OT_stash_and_create"; + ot->description = "Store this action in the NLA stack as a non-contributing strip for later use, and create a new action"; + + /* callbacks */ + ot->exec = action_stash_create_exec; + ot->poll = action_stash_create_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ************************************************************************** */ +/* ACTION UNLINK */ + +/* ******************* Action Unlink Operator ******************** */ +/* We use a custom unlink operator here, as there are some technicalities which need special care: + * 1) When in Tweak Mode, it shouldn't be possible to unlink the active action, + * or else, everything turns to custard. + * 2) If the Action doesn't have any other users, the user should at least get + * a warning that it is going to get lost. + * 3) We need a convenient way to exit Tweak Mode from the Action Editor + */ + +void ED_animedit_unlink_action(bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports) +{ + ScrArea *sa = CTX_wm_area(C); + + /* If the old action only has a single user (that it's about to lose), + * warn user about it + * + * TODO: Maybe we should just save it for them? But then, there's the problem of + * trying to get rid of stuff that's actually unwanted! + */ + if (act->id.us == 1) { + BKE_reportf(reports, RPT_ERROR, + "Action '%s' will not be saved, create Fake User or Stash in NLA Stack to retain", + act->id.name + 2); + } + + /* If in Tweak Mode, don't unlink. Instead, this + * becomes a shortcut to exit Tweak Mode instead + */ + if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) { + /* Exit Tweak Mode */ + BKE_nla_tweakmode_exit(adt); + + /* Flush this to the Action Editor (if that's where this change was initiated) */ + if (sa->spacetype == SPACE_ACTION) { + actedit_change_action(C, NULL); + } + } + else { + /* Unlink normally - Setting it to NULL should be enough to get the old one unlinked */ + if (sa->spacetype == SPACE_ACTION) { + /* clear action editor -> action */ + actedit_change_action(C, NULL); + } + else { + /* clear AnimData -> action */ + PointerRNA ptr; + PropertyRNA *prop; + + /* create AnimData RNA pointers */ + RNA_pointer_create(id, &RNA_AnimData, adt, &ptr); + prop = RNA_struct_find_property(&ptr, "action"); + + /* clear... */ + RNA_property_pointer_set(&ptr, prop, PointerRNA_NULL); + RNA_property_update(C, &ptr, prop); + } + } +} + +/* -------------------------- */ + +static int action_unlink_poll(bContext *C) +{ + if (ED_operator_action_active(C)) { + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Only when there's an active action, in the right modes... */ + if (saction->action && adt) + return true; + } + + /* something failed... */ + return false; +} + +static int action_unlink_exec(bContext *C, wmOperator *op) +{ + AnimData *adt = ED_actedit_animdata_from_context(C); + + if (adt && adt->action) { + ED_animedit_unlink_action(C, NULL, adt, adt->action, op->reports); + } + + return OPERATOR_FINISHED; +} + +void ACTION_OT_unlink(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlink Action"; + ot->idname = "ACTION_OT_unlink"; + ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)"; + + /* callbacks */ + ot->exec = action_unlink_exec; + ot->poll = action_unlink_poll; +} + +/* ************************************************************************** */ +/* ACTION BROWSING */ + +/* Get the NLA Track that the active action comes from, since this is not stored in AnimData */ +/* TODO: Move this to blenkernel/nla.c */ +static NlaTrack *nla_tweak_track_get(AnimData *adt) +{ + NlaTrack *nlt; + + /* sanity check */ + if (adt == NULL) + return NULL; + + /* Since the track itself gets disabled, we want the first disabled... */ + for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { + if (nlt->flag & (NLATRACK_ACTIVE | NLATRACK_DISABLED)) { + /* For good measure, make sure that strip actually exists there */ + if (BLI_findindex(&nlt->strips, adt->actstrip) != -1) { + return nlt; + } + else if (G.debug & G_DEBUG) { + printf("%s: Active strip (%p, %s) not in NLA track found (%p, %s)\n", + __func__, + adt->actstrip, (adt->actstrip) ? adt->actstrip->name : "<None>", + nlt, (nlt) ? nlt->name : "<None>"); + } + } + } + + /* Not found! */ + return NULL; +} + +/* Try to find NLA Strip to use for action layer up/down tool */ +static NlaStrip *action_layer_get_nlastrip(ListBase *strips, float ctime) +{ + NlaStrip *strip; + + for (strip = strips->first; strip; strip = strip->next) { + /* Can we use this? */ + if (IN_RANGE_INCL(ctime, strip->start, strip->end)) { + /* in range - use this one */ + return strip; + } + else if ((ctime < strip->start) && (strip->prev == NULL)) { + /* before first - use this one */ + return strip; + } + else if ((ctime > strip->end) && (strip->next == NULL)) { + /* after last - use this one */ + return strip; + } + } + + /* nothing suitable found... */ + return NULL; +} + +/* Switch NLA Strips/Actions */ +static void action_layer_switch_strip(AnimData *adt, + NlaTrack *old_track, NlaStrip *old_strip, + NlaTrack *nlt, NlaStrip *strip) +{ + /* Exit tweakmode on old strip + * NOTE: We need to manually clear this stuff ourselves, as tweakmode exit doesn't do it + */ + BKE_nla_tweakmode_exit(adt); + + if (old_strip) { + old_strip->flag &= ~(NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT); + } + if (old_track) { + old_track->flag &= ~(NLATRACK_ACTIVE | NLATRACK_SELECTED); + } + + /* Make this one the active one instead */ + strip->flag |= (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT); + nlt->flag |= NLATRACK_ACTIVE; + + /* Copy over "solo" flag - This is useful for stashed actions... */ + if (old_track) { + if (old_track->flag & NLATRACK_SOLO) { + old_track->flag &= ~NLATRACK_SOLO; + nlt->flag |= NLATRACK_SOLO; + } + } + else { + /* NLA muting <==> Solo Tracks */ + if (adt->flag & ADT_NLA_EVAL_OFF) { + /* disable NLA muting */ + adt->flag &= ~ADT_NLA_EVAL_OFF; + + /* mark this track as being solo */ + adt->flag |= ADT_NLA_SOLO_TRACK; + nlt->flag |= NLATRACK_SOLO; + + // TODO: Needs restpose flushing (when we get reference track) + } + } + + /* Enter tweakmode again - hopefully we're now "it" */ + BKE_nla_tweakmode_enter(adt); + BLI_assert(adt->actstrip == strip); +} + +/* ********************** One Layer Up Operator ************************** */ + +static int action_layer_next_poll(bContext *C) +{ + /* Action Editor's action editing modes only */ + if (ED_operator_action_active(C)) { + AnimData *adt = ED_actedit_animdata_from_context(C); + if (adt) { + /* only allow if we're in tweakmode, and there's something above us... */ + if (adt->flag & ADT_NLA_EDIT_ON) { + /* We need to check if there are any tracks above the active one + * since the track the action comes from is not stored in AnimData + */ + if (adt->nla_tracks.last) { + NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.last; + + if (nlt->flag & NLATRACK_DISABLED) { + /* A disabled track will either be the track itself, + * or one of the ones above it. + * + * If this is the top-most one, there is the possibility + * that there is no active action. For now, we let this + * case return true too, so that there is a natural way + * to "move to an empty layer", even though this means + * that we won't actually have an action. + */ + // return (adt->tmpact != NULL); + return true; + } + } + } + } + } + + /* something failed... */ + return false; +} + +static int action_layer_next_exec(bContext *C, wmOperator *op) +{ + AnimData *adt = ED_actedit_animdata_from_context(C); + NlaTrack *act_track; + + Scene *scene = CTX_data_scene(C); + float ctime = BKE_scene_frame_get(scene); + + /* Get active track */ + act_track = nla_tweak_track_get(adt); + + if (act_track == NULL) { + BKE_report(op->reports, RPT_ERROR, "Could not find current NLA Track"); + return OPERATOR_CANCELLED; + } + + /* Find next action, and hook it up */ + if (act_track->next) { + NlaTrack *nlt; + + /* Find next action to use */ + for (nlt = act_track->next; nlt; nlt = nlt->next) { + NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime); + + if (strip) { + action_layer_switch_strip(adt, act_track, adt->actstrip, nlt, strip); + break; + } + } + } + else { + /* No more actions (strips) - Go back to editing the original active action + * NOTE: This will mean exiting tweakmode... + */ + BKE_nla_tweakmode_exit(adt); + + /* Deal with solo flags... + * Assume: Solo Track == NLA Muting + */ + if (adt->flag & ADT_NLA_SOLO_TRACK) { + /* turn off solo flags on tracks */ + act_track->flag &= ~NLATRACK_SOLO; + adt->flag &= ~ADT_NLA_SOLO_TRACK; + + /* turn on NLA muting (to keep same effect) */ + adt->flag |= ADT_NLA_EVAL_OFF; + + // TODO: Needs restpose flushing (when we get reference track) + } + } + + /* Update the action that this editor now uses + * NOTE: The calls above have already handled the usercount/animdata side of things + */ + actedit_change_action(C, adt->action); + return OPERATOR_FINISHED; +} + +void ACTION_OT_layer_next(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Next Layer"; + ot->idname = "ACTION_OT_layer_next"; + ot->description = "Switch to editing action in animation layer above the current action in the NLA Stack"; + + /* callbacks */ + ot->exec = action_layer_next_exec; + ot->poll = action_layer_next_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************* One Layer Down Operator ************************* */ + +static int action_layer_prev_poll(bContext *C) +{ + /* Action Editor's action editing modes only */ + if (ED_operator_action_active(C)) { + AnimData *adt = ED_actedit_animdata_from_context(C); + if (adt) { + if (adt->flag & ADT_NLA_EDIT_ON) { + /* Tweak Mode: We need to check if there are any tracks below the active one that we can move to */ + if (adt->nla_tracks.first) { + NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.first; + + /* Since the first disabled track is the track being tweaked/edited, + * we can simplify things by only checking the first track: + * - If it is disabled, this is the track being tweaked, + * so there can't be anything below it + * - Otherwise, there is at least 1 track below the tweaking + * track that we can descend to + */ + if ((nlt->flag & NLATRACK_DISABLED) == 0) { + /* not disabled = there are actions below the one being tweaked */ + return true; + } + } + } + else { + /* Normal Mode: If there are any tracks, we can try moving to those */ + return (adt->nla_tracks.first != NULL); + } + } + } + + /* something failed... */ + return false; +} + +static int action_layer_prev_exec(bContext *C, wmOperator *op) +{ + AnimData *adt = ED_actedit_animdata_from_context(C); + NlaTrack *act_track; + NlaTrack *nlt; + + Scene *scene = CTX_data_scene(C); + float ctime = BKE_scene_frame_get(scene); + + /* Sanity Check */ + if (adt == NULL) { + BKE_report(op->reports, RPT_ERROR, "Internal Error: Could not find Animation Data/NLA Stack to use"); + return OPERATOR_CANCELLED; + } + + /* Get active track */ + act_track = nla_tweak_track_get(adt); + + /* If there is no active track, that means we are using the active action... */ + if (act_track) { + /* Active Track - Start from the one below it */ + nlt = act_track->prev; + } + else { + /* Active Action - Use the top-most track */ + nlt = adt->nla_tracks.last; + } + + /* Find previous action and hook it up */ + for (; nlt; nlt = nlt->prev) { + NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime); + + if (strip) { + action_layer_switch_strip(adt, act_track, adt->actstrip, nlt, strip); + break; + } + } + + /* Update the action that this editor now uses + * NOTE: The calls above have already handled the usercount/animdata side of things + */ + actedit_change_action(C, adt->action); + return OPERATOR_FINISHED; +} + +void ACTION_OT_layer_prev(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Previous Layer"; + ot->idname = "ACTION_OT_layer_prev"; + ot->description = "Switch to editing action in animation layer below the current action in the NLA Stack"; + + /* callbacks */ + ot->exec = action_layer_prev_exec; + ot->poll = action_layer_prev_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ************************************************************************** */ diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index cc30639332a..10748c2fe15 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -93,6 +93,8 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) /* loop through channels, and set up drawing depending on their type */ { /* first pass: just the standard GL-drawing for backdrop + text */ + size_t channel_index = 0; + y = (float)ACHANNEL_FIRST; for (ale = anim_data.first; ale; ale = ale->next) { @@ -104,11 +106,12 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* draw all channels using standard channel-drawing API */ - ANIM_channel_draw(ac, ale, yminc, ymaxc); + ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index); } /* adjust y-position for next one */ y -= ACHANNEL_STEP; + channel_index++; } } { /* second pass: widgets */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index ce8723c572d..4b8ddacb418 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -80,432 +80,6 @@ #include "action_intern.h" -/* ************************************************************************** */ -/* ACTION MANAGEMENT */ - -/* Helper function to find the active AnimData block from the Action Editor context */ -static AnimData *actedit_animdata_from_context(bContext *C) -{ - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - Object *ob = CTX_data_active_object(C); - AnimData *adt = NULL; - - /* Get AnimData block to use */ - if (saction->mode == SACTCONT_ACTION) { - /* Currently, "Action Editor" means object-level only... */ - if (ob) { - adt = ob->adt; - } - } - else if (saction->mode == SACTCONT_SHAPEKEY) { - Key *key = BKE_key_from_object(ob); - if (key) { - adt = key->adt; - } - } - - return adt; -} - -/* Create new action */ -static bAction *action_create_new(bContext *C, bAction *oldact) -{ - ScrArea *sa = CTX_wm_area(C); - bAction *action; - - /* create action - the way to do this depends on whether we've got an - * existing one there already, in which case we make a copy of it - * (which is useful for "versioning" actions within the same file) - */ - if (oldact && GS(oldact->id.name) == ID_AC) { - /* make a copy of the existing action */ - action = BKE_action_copy(oldact); - } - else { - /* just make a new (empty) action */ - action = add_empty_action(CTX_data_main(C), "Action"); - } - - /* when creating new ID blocks, there is already 1 user (as for all new datablocks), - * but the RNA pointer code will assign all the proper users instead, so we compensate - * for that here - */ - BLI_assert(action->id.us == 1); - action->id.us--; - - /* set ID-Root type */ - if (sa->spacetype == SPACE_ACTION) { - SpaceAction *saction = (SpaceAction *)sa->spacedata.first; - - if (saction->mode == SACTCONT_SHAPEKEY) - action->idroot = ID_KE; - else - action->idroot = ID_OB; - } - - return action; -} - -/* Change the active action used by the action editor */ -static void actedit_change_action(bContext *C, bAction *act) -{ - bScreen *screen = CTX_wm_screen(C); - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - - PointerRNA ptr, idptr; - PropertyRNA *prop; - - /* create RNA pointers and get the property */ - RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, saction, &ptr); - prop = RNA_struct_find_property(&ptr, "action"); - - /* NOTE: act may be NULL here, so better to just use a cast here */ - RNA_id_pointer_create((ID *)act, &idptr); - - /* set the new pointer, and force a refresh */ - RNA_property_pointer_set(&ptr, prop, idptr); - RNA_property_update(C, &ptr, prop); -} - -/* ******************** New Action Operator *********************** */ - -/* Criteria: - * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions... - * OR - * The NLA Editor is active (i.e. Animation Data panel -> new action) - * 2) The associated AnimData block must not be in tweakmode - */ -static int action_new_poll(bContext *C) -{ - Scene *scene = CTX_data_scene(C); - - /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */ - /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */ - if (!(scene->flag & SCE_NLA_EDIT_ON)) { - if (ED_operator_action_active(C)) { - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - Object *ob = CTX_data_active_object(C); - - /* For now, actions are only for the active object, and on object and shapekey levels... */ - if (saction->mode == SACTCONT_ACTION) { - /* XXX: This assumes that actions are assigned to the active object */ - if (ob) - return true; - } - else if (saction->mode == SACTCONT_SHAPEKEY) { - Key *key = BKE_key_from_object(ob); - if (key) - return true; - } - } - else if (ED_operator_nla_active(C)) { - return true; - } - } - - /* something failed... */ - return false; -} - -static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) -{ - PointerRNA ptr, idptr; - PropertyRNA *prop; - - /* hook into UI */ - UI_context_active_but_prop_get_templateID(C, &ptr, &prop); - - if (prop) { - bAction *action = NULL, *oldact = NULL; - AnimData *adt = NULL; - PointerRNA oldptr; - - oldptr = RNA_property_pointer_get(&ptr, prop); - oldact = (bAction *)oldptr.id.data; - - /* stash the old action to prevent it from being lost */ - if (ptr.type == &RNA_AnimData) { - adt = ptr.data; - } - else if (ptr.type == &RNA_SpaceDopeSheetEditor) { - adt = actedit_animdata_from_context(C); - } - - /* Perform stashing operation - But only if there is an action */ - if (adt && oldact) { - /* stash the action */ - if (BKE_nla_action_stash(adt)) { - /* The stash operation will remove the user already - * (and unlink the action from the AnimData action slot). - * Hence, we must unset the ref to the action in the - * action editor too (if this is where we're being called from) - * first before setting the new action once it is created, - * or else the user gets decremented twice! - */ - if (ptr.type == &RNA_SpaceDopeSheetEditor) { - SpaceAction *saction = (SpaceAction *)ptr.data; - saction->action = NULL; - } - } - else { - //printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n", oldact->id.name); - } - } - - /* create action */ - action = action_create_new(C, oldact); - - /* set this new action - * NOTE: we can't use actedit_change_action, as this function is also called from the NLA - */ - RNA_id_pointer_create(&action->id, &idptr); - RNA_property_pointer_set(&ptr, prop, idptr); - RNA_property_update(C, &ptr, prop); - } - - /* set notifier that keyframes have changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); - - return OPERATOR_FINISHED; -} - -void ACTION_OT_new(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "New Action"; - ot->idname = "ACTION_OT_new"; - ot->description = "Create new action"; - - /* api callbacks */ - ot->exec = action_new_exec; - ot->poll = action_new_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ******************* Action Push-Down Operator ******************** */ - -/* Criteria: - * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions - * 2) There must be an action active - * 3) The associated AnimData block must not be in tweakmode - */ -static int action_pushdown_poll(bContext *C) -{ - if (ED_operator_action_active(C)) { - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - - /* Check for actions and that tweakmode is off */ - if ((saction->action) && !(scene->flag & SCE_NLA_EDIT_ON)) { - /* For now, actions are only for the active object, and on object and shapekey levels... */ - if (saction->mode == SACTCONT_ACTION) { - return (ob->adt != NULL); - } - else if (saction->mode == SACTCONT_SHAPEKEY) { - Key *key = BKE_key_from_object(ob); - - return (key && key->adt); - } - } - } - - /* something failed... */ - return false; -} - -static int action_pushdown_exec(bContext *C, wmOperator *op) -{ - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = actedit_animdata_from_context(C); - - /* Do the deed... */ - if (adt) { - /* Perform the pushdown operation - * - This will deal with all the AnimData-side usercounts - */ - if (action_has_motion(adt->action) == 0) { - /* action may not be suitable... */ - BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); - return OPERATOR_CANCELLED; - } - else { - /* action can be safely added */ - BKE_nla_action_pushdown(adt); - } - - /* Stop displaying this action in this editor - * NOTE: The editor itself doesn't set a user... - */ - saction->action = NULL; - } - - /* Send notifiers that stuff has changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); - return OPERATOR_FINISHED; -} - -void ACTION_OT_push_down(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Push Down Action"; - ot->idname = "ACTION_OT_push_down"; - ot->description = "Push action down on to the NLA stack as a new strip"; - - /* callbacks */ - ot->exec = action_pushdown_exec; - ot->poll = action_pushdown_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ******************* Action Stash Operator ******************** */ - -static int action_stash_exec(bContext *C, wmOperator *op) -{ - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = actedit_animdata_from_context(C); - - /* Perform stashing operation */ - if (adt) { - /* don't do anything if this action is empty... */ - if (action_has_motion(adt->action) == 0) { - /* action may not be suitable... */ - BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); - return OPERATOR_CANCELLED; - } - else { - /* stash the action */ - if (BKE_nla_action_stash(adt)) { - /* The stash operation will remove the user already, - * so the flushing step later shouldn't double up - * the usercount fixes. Hence, we must unset this ref - * first before setting the new action. - */ - saction->action = NULL; - } - else { - /* action has already been added - simply warn about this, and clear */ - BKE_report(op->reports, RPT_ERROR, "Action has already been stashed"); - } - - /* clear action refs from editor, and then also the backing data (not necessary) */ - actedit_change_action(C, NULL); - } - } - - /* Send notifiers that stuff has changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); - return OPERATOR_FINISHED; -} - -void ACTION_OT_stash(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Stash Action"; - ot->idname = "ACTION_OT_stash"; - ot->description = "Store this action in the NLA stack as a non-contributing strip for later use"; - - /* callbacks */ - ot->exec = action_stash_exec; - ot->poll = action_pushdown_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_boolean(ot->srna, "create_new", true, "Create New Action", - "Create a new action once the existing one has been safely stored"); -} - -/* ----------------- */ - -/* Criteria: - * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions - * 2) The associated AnimData block must not be in tweakmode - */ -static int action_stash_create_poll(bContext *C) -{ - if (ED_operator_action_active(C)) { - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - Scene *scene = CTX_data_scene(C); - - /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */ - /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */ - if (!(scene->flag & SCE_NLA_EDIT_ON)) { - /* For now, actions are only for the active object, and on object and shapekey levels... */ - return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY); - } - } - - /* something failed... */ - return false; -} - -static int action_stash_create_exec(bContext *C, wmOperator *op) -{ - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = actedit_animdata_from_context(C); - - /* Check for no action... */ - if (saction->action == NULL) { - /* just create a new action */ - bAction *action = action_create_new(C, NULL); - actedit_change_action(C, action); - } - else if (adt) { - /* Perform stashing operation */ - if (action_has_motion(adt->action) == 0) { - /* don't do anything if this action is empty... */ - BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); - return OPERATOR_CANCELLED; - } - else { - /* stash the action */ - if (BKE_nla_action_stash(adt)) { - bAction *new_action = NULL; - - /* create new action not based on the old one (since the "new" operator already does that) */ - new_action = action_create_new(C, NULL); - - /* The stash operation will remove the user already, - * so the flushing step later shouldn't double up - * the usercount fixes. Hence, we must unset this ref - * first before setting the new action. - */ - saction->action = NULL; - actedit_change_action(C, new_action); - } - else { - /* action has already been added - simply warn about this, and clear */ - BKE_report(op->reports, RPT_ERROR, "Action has already been stashed"); - actedit_change_action(C, NULL); - } - } - } - - /* Send notifiers that stuff has changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); - return OPERATOR_FINISHED; -} - -void ACTION_OT_stash_and_create(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Stash Action"; - ot->idname = "ACTION_OT_stash_and_create"; - ot->description = "Store this action in the NLA stack as a non-contributing strip for later use, and create a new action"; - - /* callbacks */ - ot->exec = action_stash_create_exec; - ot->poll = action_stash_create_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} /* ************************************************************************** */ /* POSE MARKERS STUFF */ diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index 9a19e4a926b..9f9b15c3c26 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -98,10 +98,15 @@ void ACTION_OT_snap(struct wmOperatorType *ot); void ACTION_OT_mirror(struct wmOperatorType *ot); void ACTION_OT_new(struct wmOperatorType *ot); +void ACTION_OT_unlink(struct wmOperatorType *ot); + void ACTION_OT_push_down(struct wmOperatorType *ot); void ACTION_OT_stash(struct wmOperatorType *ot); void ACTION_OT_stash_and_create(struct wmOperatorType *ot); +void ACTION_OT_layer_next(struct wmOperatorType *ot); +void ACTION_OT_layer_prev(struct wmOperatorType *ot); + void ACTION_OT_markers_make_local(struct wmOperatorType *ot); /* defines for snap keyframes diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index 8c706d8da57..9ad0931a957 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -79,10 +79,15 @@ void action_operatortypes(void) WM_operatortype_append(ACTION_OT_paste); WM_operatortype_append(ACTION_OT_new); + WM_operatortype_append(ACTION_OT_unlink); + WM_operatortype_append(ACTION_OT_push_down); WM_operatortype_append(ACTION_OT_stash); WM_operatortype_append(ACTION_OT_stash_and_create); + WM_operatortype_append(ACTION_OT_layer_next); + WM_operatortype_append(ACTION_OT_layer_prev); + WM_operatortype_append(ACTION_OT_previewrange_set); WM_operatortype_append(ACTION_OT_view_all); WM_operatortype_append(ACTION_OT_view_selected); diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index 92731c2f135..b3149c1bbf4 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -1140,8 +1140,11 @@ static int console_modal_select(bContext *C, wmOperator *op, const wmEvent *even case LEFTMOUSE: case MIDDLEMOUSE: case RIGHTMOUSE: - console_cursor_set_exit(C, op); - return OPERATOR_FINISHED; + if (event->val == KM_RELEASE) { + console_cursor_set_exit(C, op); + return OPERATOR_FINISHED; + } + break; case MOUSEMOVE: console_modal_select_apply(C, op, event); break; diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 14b72eb8f6d..6858f536e92 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -642,8 +642,8 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche */ for (i = 0; i < n; i++) { file = filelist_file(sfile->files, i); - /* use same rule as 'FileCheckType.CHECK_FILES' */ - if (S_ISREG(file->type) && (fnmatch(pattern, file->relname, 0) == 0)) { + /* Do not check wether file is a file or dir here! Causes T44243 (we do accept dirs at this stage). */ + if (fnmatch(pattern, file->relname, 0) == 0) { file->selflag |= FILE_SEL_SELECTED; if (!match) { BLI_strncpy(matched_file, file->relname, FILE_MAX); @@ -694,13 +694,8 @@ int autocomplete_directory(struct bContext *C, char *str, void *UNUSED(arg_v)) closedir(dir); match = UI_autocomplete_end(autocpl, str); - if (match) { - if (match == AUTOCOMPLETE_FULL_MATCH) { - BLI_add_slash(str); - } - else { - BLI_strncpy(sfile->params->dir, str, sizeof(sfile->params->dir)); - } + if (match == AUTOCOMPLETE_FULL_MATCH) { + BLI_add_slash(str); } } } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 782b318b8a2..557a495cb8f 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -444,27 +444,27 @@ static void file_keymap(struct wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "need_active", true); /* left mouse selects and opens */ - WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, 0, 0); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "fill", true); /* right mouse selects without opening */ - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, 0, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "open", false); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "open", false); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, KM_ALT, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, KM_ALT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "fill", true); RNA_boolean_set(kmi->ptr, "open", false); /* front and back mouse folder navigation */ - WM_keymap_add_item(keymap, "FILE_OT_previous", BUTTON4MOUSE, KM_CLICK, 0, 0); - WM_keymap_add_item(keymap, "FILE_OT_next", BUTTON5MOUSE, KM_CLICK, 0, 0); + WM_keymap_add_item(keymap, "FILE_OT_previous", BUTTON4MOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "FILE_OT_next", BUTTON5MOUSE, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "FILE_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "FILE_OT_refresh", PADPERIOD, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index a5c04ce2cb4..0313ce4b1d4 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -1137,6 +1137,8 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) /* loop through channels, and set up drawing depending on their type */ { /* first pass: just the standard GL-drawing for backdrop + text */ + size_t channel_index = 0; + y = (float)ACHANNEL_FIRST; for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) { @@ -1148,11 +1150,12 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* draw all channels using standard channel-drawing API */ - ANIM_channel_draw(ac, ale, yminc, ymaxc); + ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index); } /* adjust y-position for next one */ y -= ACHANNEL_STEP; + channel_index++; } } { /* second pass: widgets */ diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index 33e8b522335..e266a5089c8 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -574,7 +574,7 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) /* insertkey */ WM_keymap_add_item(keymap, "GRAPH_OT_keyframe_insert", IKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); /* copy/paste */ WM_keymap_add_item(keymap, "GRAPH_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 78dbae7618b..28107337084 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -1089,6 +1089,8 @@ typedef struct tNearestVertInfo { short hpoint; /* the handle index that we hit (eHandleIndex) */ short sel; /* whether the handle is selected or not */ int dist; /* distance from mouse to vert */ + + eAnim_ChannelType ctype; /* type of animation channel this FCurve comes from */ } tNearestVertInfo; /* Tags for the type of graph vert that we have */ @@ -1116,7 +1118,7 @@ static bool fcurve_handle_sel_check(SpaceIpo *sipo, BezTriple *bezt) /* check if the given vertex is within bounds or not */ // TODO: should we return if we hit something? static void nearest_fcurve_vert_store( - ListBase *matches, View2D *v2d, FCurve *fcu, + ListBase *matches, View2D *v2d, FCurve *fcu, eAnim_ChannelType ctype, BezTriple *bezt, FPoint *fpt, short hpoint, const int mval[2], float unit_scale) { /* Keyframes or Samples? */ @@ -1149,6 +1151,8 @@ static void nearest_fcurve_vert_store( /* store values */ nvi->fcu = fcu; + nvi->ctype = ctype; + nvi->bezt = bezt; nvi->hpoint = hpoint; nvi->dist = dist; @@ -1190,29 +1194,29 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L FCurve *fcu = (FCurve *)ale->key_data; AnimData *adt = ANIM_nla_mapping_get(ac, ale); float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag); - + /* apply NLA mapping to all the keyframes */ if (adt) ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - + if (fcu->bezt) { BezTriple *bezt1 = fcu->bezt, *prevbezt = NULL; int i; for (i = 0; i < fcu->totvert; i++, prevbezt = bezt1, bezt1++) { /* keyframe */ - nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_KEY, mval, unit_scale); + nearest_fcurve_vert_store(matches, v2d, fcu, ale->type, bezt1, NULL, NEAREST_HANDLE_KEY, mval, unit_scale); /* handles - only do them if they're visible */ if (fcurve_handle_sel_check(sipo, bezt1) && (fcu->totvert > 1)) { /* first handle only visible if previous segment had handles */ if ((!prevbezt && (bezt1->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) { - nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_LEFT, mval, unit_scale); + nearest_fcurve_vert_store(matches, v2d, fcu, ale->type, bezt1, NULL, NEAREST_HANDLE_LEFT, mval, unit_scale); } /* second handle only visible if this segment is bezier */ if (bezt1->ipo == BEZT_IPO_BEZ) { - nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_RIGHT, mval, unit_scale); + nearest_fcurve_vert_store(matches, v2d, fcu, ale->type, bezt1, NULL, NEAREST_HANDLE_RIGHT, mval, unit_scale); } } } @@ -1405,7 +1409,7 @@ static void mouse_graph_keys(bAnimContext *ac, const int mval[2], short select_m /* needs to be called with (sipo->flag & SIPO_SELCUVERTSONLY) otherwise the active flag won't be set [#26452] */ if (nvi->fcu->flag & FCURVE_SELECTED) { int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); - ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, ANIMTYPE_FCURVE); + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, nvi->ctype); } /* free temp sample data for filtering */ diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index fb3c140fddf..a02f64d68d8 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -44,6 +44,7 @@ #include "BKE_image.h" #include "BKE_node.h" #include "BKE_screen.h" +#include "BKE_scene.h" #include "RE_pipeline.h" @@ -82,9 +83,9 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf, else { if (ima->source == IMA_SRC_MOVIE) { ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Movie"), len - ofs); - if (ima->anim) + if (BKE_image_has_anim(ima)) ofs += BLI_snprintf(str + ofs, len - ofs, IFACE_(" %d frs"), - IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN)); + IMB_anim_get_duration(((ImageAnim *)ima->anims.first)->anim, IMB_TC_RECORD_RUN)); } else { ofs += BLI_strncpy_rlen(str, IFACE_("Image"), len - ofs); @@ -311,10 +312,10 @@ static void ui_imageuser_slot_menu(bContext *UNUSED(C), uiLayout *layout, void * static const char *ui_imageuser_layer_fake_name(RenderResult *rr) { - if (rr->rectf) { + if (RE_RenderViewGetRectf(rr, 0)) { return IFACE_("Composite"); } - else if (rr->rect32) { + else if (RE_RenderViewGetRect32(rr, 0)) { return IFACE_("Sequence"); } else { @@ -375,7 +376,7 @@ final: static const char *ui_imageuser_pass_fake_name(RenderLayer *rl) { - if (rl == NULL || rl->rectf) { + if (rl == NULL) { return IFACE_("Combined"); } else { @@ -383,9 +384,9 @@ static const char *ui_imageuser_pass_fake_name(RenderLayer *rl) } } -static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *ptrpair_p) +static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) { - void **rnd_data = ptrpair_p; + void **rnd_data = rnd_pt; uiBlock *block = uiLayoutGetBlock(layout); Image *image = rnd_data[0]; ImageUser *iuser = rnd_data[1]; @@ -398,6 +399,7 @@ static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void * RenderPass *rpass; const char *fake_name; int nr; + int passflag = 0; /* may have been freed since drawing */ rr = BKE_image_acquire_renderresult(scene, image); @@ -419,15 +421,22 @@ static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void * fake_name = ui_imageuser_pass_fake_name(rl); if (fake_name) { - BLI_strncpy(rpass_fake.name, fake_name, sizeof(rpass_fake.name)); + BLI_strncpy(rpass_fake.internal_name, fake_name, sizeof(rpass_fake.internal_name)); nr += 1; } /* rendered results don't have a Combined pass */ for (rpass = rl ? rl->passes.last : NULL; rpass; rpass = rpass->prev, nr--) { + + /* just show one pass of each kind */ + if (passflag & rpass->passtype) + continue; + + passflag |= rpass->passtype; + final: - uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rpass->name), 0, 0, - UI_UNIT_X * 5, UI_UNIT_X, &iuser->pass, (float) nr, 0.0, 0, -1, ""); + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rpass->internal_name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->passtype, (float) rpass->passtype, 0.0, 0, -1, ""); } if (fake_name) { @@ -441,21 +450,81 @@ final: BKE_image_release_renderresult(scene, image); } +/**************************** view menus *****************************/ +static void ui_imageuser_view_menu_rr(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) +{ + void **rnd_data = rnd_pt; + uiBlock *block = uiLayoutGetBlock(layout); + Image *image = rnd_data[0]; + ImageUser *iuser = rnd_data[1]; + RenderResult *rr; + RenderView *rview; + int nr; + Scene *scene = iuser->scene; + + /* may have been freed since drawing */ + rr = BKE_image_acquire_renderresult(scene, image); + if (UNLIKELY(rr == NULL)) { + return; + } + + UI_block_layout_set_current(block, layout); + uiLayoutColumn(layout, false); + + uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("View"), + 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + + uiItemS(layout); + + nr = (rr ? BLI_listbase_count(&rr->views) : 0) - 1; + for (rview = rr ? rr->views.last : NULL; rview; rview = rview->prev, nr--) { + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rview->name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->view, (float) nr, 0.0, 0, -1, ""); + } + + BKE_image_release_renderresult(scene, image); +} + +static void ui_imageuser_view_menu_multiview(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) +{ + void **rnd_data = rnd_pt; + uiBlock *block = uiLayoutGetBlock(layout); + Image *image = rnd_data[0]; + ImageUser *iuser = rnd_data[1]; + int nr; + ImageView *iv; + + UI_block_layout_set_current(block, layout); + uiLayoutColumn(layout, false); + + uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("View"), + 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + + uiItemS(layout); + + nr = BLI_listbase_count(&image->views) - 1; + for (iv = image->views.last; iv; iv = iv->prev, nr--) { + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(iv->name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->view, (float) nr, 0.0, 0, -1, ""); + } +} + /* 5 layer button callbacks... */ static void image_multi_cb(bContext *C, void *rr_v, void *iuser_v) { ImageUser *iuser = iuser_v; - BKE_image_multilayer_index(rr_v, iuser); + BKE_image_multilayer_index(rr_v, iuser); WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); } + static void image_multi_inclay_cb(bContext *C, void *rr_v, void *iuser_v) { RenderResult *rr = rr_v; ImageUser *iuser = iuser_v; int tot = BLI_listbase_count(&rr->layers); - if (rr->rectf || rr->rect32) + if (RE_HasFakeLayer(rr)) tot++; /* fake compo/sequencer layer */ if (iuser->layer < tot - 1) { @@ -483,7 +552,7 @@ static void image_multi_incpass_cb(bContext *C, void *rr_v, void *iuser_v) if (rl) { int tot = BLI_listbase_count(&rl->passes); - if (rr->rectf || rr->rect32) + if (RE_HasFakeLayer(rr)) tot++; /* fake compo/sequencer layer */ if (iuser->pass < tot - 1) { @@ -504,6 +573,16 @@ static void image_multi_decpass_cb(bContext *C, void *rr_v, void *iuser_v) } } +/* 5 view button callbacks... */ +static void image_multiview_cb(bContext *C, void *ima_v, void *iuser_v) +{ + Image *ima = ima_v; + ImageUser *iuser = iuser_v; + + BKE_image_multiview_index(ima, iuser); + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); +} + #if 0 static void image_freecache_cb(bContext *C, void *ima_v, void *unused) { @@ -523,13 +602,14 @@ static void image_user_change(bContext *C, void *iuser_v, void *unused) static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w, short *render_slot) { - static void *rnd_pt[3]; /* XXX, workaround */ + static void *rnd_pt[4]; /* XXX, workaround */ uiBlock *block = uiLayoutGetBlock(layout); uiBut *but; RenderLayer *rl = NULL; - int wmenu1, wmenu2, wmenu3; + int wmenu1, wmenu2, wmenu3, wmenu4; const char *fake_name; - const char *display_name; + const char *display_name = ""; + const bool show_stereo = (iuser->flag & IMA_SHOW_STEREO); uiLayoutRow(layout, true); @@ -537,6 +617,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes wmenu1 = (2 * w) / 5; wmenu2 = (3 * w) / 5; wmenu3 = (3 * w) / 6; + wmenu4 = (3 * w) / 6; rnd_pt[0] = image; rnd_pt[1] = iuser; @@ -558,6 +639,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes if (rr) { RenderPass *rpass; + RenderView *rview; int rpass_index; /* layer */ @@ -566,20 +648,50 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes rl = BLI_findlink(&rr->layers, rpass_index); rnd_pt[2] = SET_INT_IN_POINTER(rpass_index); - display_name = rl ? rl->name : (fake_name ? fake_name : ""); - but = uiDefMenuBut(block, ui_imageuser_layer_menu, rnd_pt, display_name, 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer")); - UI_but_func_set(but, image_multi_cb, rr, iuser); - UI_but_type_set_menu_from_pulldown(but); - + if (RE_layers_have_name(rr)) { + display_name = rl ? rl->name : (fake_name ? fake_name : ""); + but = uiDefMenuBut(block, ui_imageuser_layer_menu, rnd_pt, display_name, 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer")); + UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_type_set_menu_from_pulldown(but); + } /* pass */ fake_name = ui_imageuser_pass_fake_name(rl); rpass = (rl ? BLI_findlink(&rl->passes, iuser->pass - (fake_name ? 1 : 0)) : NULL); - display_name = rpass ? rpass->name : (fake_name ? fake_name : ""); + display_name = rpass ? rpass->internal_name : (fake_name ? fake_name : ""); but = uiDefMenuBut(block, ui_imageuser_pass_menu, rnd_pt, display_name, 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass")); UI_but_func_set(but, image_multi_cb, rr, iuser); UI_but_type_set_menu_from_pulldown(but); + + /* view */ + if (BLI_listbase_count_ex(&rr->views, 2) > 1 && !show_stereo) { + rview = BLI_findlink(&rr->views, iuser->view); + display_name = rview ? rview->name : ""; + + but = uiDefMenuBut(block, ui_imageuser_view_menu_rr, rnd_pt, display_name, 0, 0, wmenu4, UI_UNIT_Y, TIP_("Select View")); + UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_type_set_menu_from_pulldown(but); + } + } + + /* stereo image */ + else if (((image->flag & IMA_IS_STEREO) && (!show_stereo)) || + ((image->flag & IMA_IS_MULTIVIEW) && ((image->flag & IMA_IS_STEREO) == 0))) + { + ImageView *iv; + int nr = 0; + + for (iv = image->views.first; iv; iv = iv->next) { + if (nr++ == iuser->view) { + display_name = iv->name; + break; + } + } + + but = uiDefMenuBut(block, ui_imageuser_view_menu_multiview, rnd_pt, display_name, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select View")); + UI_but_func_set(but, image_multiview_cb, image, iuser); + UI_but_type_set_menu_from_pulldown(but); } } @@ -734,13 +846,13 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char if (ima->source != IMA_SRC_GENERATED) { row = uiLayoutRow(layout, true); - if (ima->packedfile) + if (BKE_image_has_packedfile(ima)) uiItemO(row, "", ICON_PACKAGE, "image.unpack"); else uiItemO(row, "", ICON_UGLYPACKAGE, "image.pack"); row = uiLayoutRow(row, true); - uiLayoutSetEnabled(row, ima->packedfile == NULL); + uiLayoutSetEnabled(row, BKE_image_has_packedfile(ima) == false); uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE); uiItemO(row, "", ICON_FILE_REFRESH, "image.reload"); } @@ -792,6 +904,17 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char uiItemR(row, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE); } + if ((scene->r.scemode & R_MULTIVIEW) != 0) { + uiItemS(layout); + + col = uiLayoutColumn(layout, false); + uiItemR(col, &imaptr, "use_multiview", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, RNA_boolean_get(&imaptr, "use_multiview")); + uiTemplateImageViews(col, &imaptr); + } + if (ima->source == IMA_SRC_MOVIE) { col = uiLayoutColumn(layout, false); uiItemR(col, &imaptr, "use_deinterlace", 0, IFACE_("Deinterlace"), ICON_NONE); @@ -962,6 +1085,101 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, int color_man } } +void uiTemplateImageStereo3d(uiLayout *layout, PointerRNA *stereo3d_format_ptr) +{ + Stereo3dFormat *stereo3d_format = stereo3d_format_ptr->data; + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE); + + switch (stereo3d_format->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + { + uiItemR(col, stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE); + break; + } + case S3D_DISPLAY_INTERLACE: + { + uiItemR(col, stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE); + uiItemR(col, stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE); + break; + } + case S3D_DISPLAY_SIDEBYSIDE: + { + uiItemR(col, stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE); + /* fall-through */ + } + case S3D_DISPLAY_TOPBOTTOM: + { + uiItemR(col, stereo3d_format_ptr, "use_squeezed_frame", 0, NULL, ICON_NONE); + break; + } + } +} + +static void uiTemplateViewsFormat(uiLayout *layout, PointerRNA *ptr, PointerRNA *stereo3d_format_ptr) +{ + uiLayout *col, *box; + + col = uiLayoutColumn(layout, false); + + uiItemL(col, IFACE_("Views Format:"), ICON_NONE); + uiItemR(uiLayoutRow(col, false), ptr, "views_format", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + + if (stereo3d_format_ptr) { + box = uiLayoutBox(col); + uiLayoutSetActive(box, RNA_enum_get(ptr, "views_format") == R_IMF_VIEWS_STEREO_3D); + uiTemplateImageStereo3d(box, stereo3d_format_ptr); + } +} + +void uiTemplateImageViews(uiLayout *layout, PointerRNA *imaptr) +{ + Image *ima = imaptr->data; + + if (ima->type != IMA_TYPE_MULTILAYER) { + PropertyRNA *prop; + PointerRNA stereo3d_format_ptr; + + prop = RNA_struct_find_property(imaptr, "stereo_3d_format"); + stereo3d_format_ptr = RNA_property_pointer_get(imaptr, prop); + + uiTemplateViewsFormat(layout, imaptr, &stereo3d_format_ptr); + } + else { + uiTemplateViewsFormat(layout, imaptr, NULL); + } +} + +void uiTemplateImageFormatViews(uiLayout *layout, PointerRNA *imfptr, PointerRNA *ptr) +{ + ImageFormatData *imf = imfptr->data; + uiLayout *col; + + if (ptr) { + uiItemR(layout, ptr, "use_multiview", 0, NULL, ICON_NONE); + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_multiview")); + } + else { + col = uiLayoutColumn(layout, false); + } + + if (imf->imtype != R_IMF_IMTYPE_MULTILAYER) { + PropertyRNA *prop; + PointerRNA stereo3d_format_ptr; + + prop = RNA_struct_find_property(imfptr, "stereo_3d_format"); + stereo3d_format_ptr = RNA_property_pointer_get(imfptr, prop); + + uiTemplateViewsFormat(col, imfptr, &stereo3d_format_ptr); + } + else { + uiTemplateViewsFormat(col, imfptr, NULL); + } +} + void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser *iuser) { Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 517cf64214c..5a3c3e90618 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -783,7 +783,7 @@ void draw_image_main(const bContext *C, ARegion *ar) Image *ima; ImBuf *ibuf; float zoomx, zoomy; - bool show_viewer, show_render, show_paint; + bool show_viewer, show_render, show_paint, show_stereo3d, show_multilayer; void *lock; /* XXX can we do this in refresh? */ @@ -813,6 +813,8 @@ void draw_image_main(const bContext *C, ARegion *ar) show_viewer = (ima && ima->source == IMA_SRC_VIEWER) != 0; show_render = (show_viewer && ima->type == IMA_TYPE_R_RESULT) != 0; show_paint = (ima && (sima->mode == SI_MODE_PAINT) && (show_viewer == false) && (show_render == false)); + show_stereo3d = (ima && (ima->flag & IMA_IS_STEREO) && (sima->iuser.flag & IMA_SHOW_STEREO)); + show_multilayer = ima && BKE_image_is_multilayer(ima); if (show_viewer) { /* use locked draw for drawing viewer image buffer since the compositor @@ -823,6 +825,14 @@ void draw_image_main(const bContext *C, ARegion *ar) BLI_lock_thread(LOCK_DRAW_IMAGE); } + if (show_stereo3d) { + if (show_multilayer) + /* update multiindex and pass for the current eye */ + BKE_image_multilayer_index(ima->rr, &sima->iuser); + else + BKE_image_multiview_index(ima, &sima->iuser); + } + ibuf = ED_space_image_acquire_buffer(sima, &lock); /* draw the image or grid */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 976c7675371..aa625210464 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -30,14 +30,21 @@ #include <stddef.h> #include <string.h> +#include <fcntl.h> #include <stdlib.h> #include <errno.h> +#ifndef WIN32 +# include <unistd.h> +#else +# include <io.h> +#endif #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_string_utf8.h" #include "BLF_translation.h" @@ -60,6 +67,7 @@ #include "BKE_report.h" #include "BKE_screen.h" #include "BKE_sound.h" +#include "BKE_scene.h" #include "GPU_draw.h" #include "GPU_buffers.h" @@ -68,6 +76,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_moviecache.h" +#include "intern/openexr/openexr_multi.h" #include "RE_pipeline.h" @@ -936,6 +945,7 @@ static void image_filesel(bContext *C, wmOperator *op, const char *path) typedef struct ImageOpenData { PropertyPointerRNA pprop; ImageUser *iuser; + ImageFormatData im_format; } ImageOpenData; typedef struct ImageFrame { @@ -946,7 +956,6 @@ typedef struct ImageFrame { static void image_open_init(bContext *C, wmOperator *op) { ImageOpenData *iod; - op->customdata = iod = MEM_callocN(sizeof(ImageOpenData), __func__); iod->iuser = CTX_data_pointer_get_type(C, "image_user", &RNA_ImageUser).data; UI_context_active_but_prop_get_templateID(C, &iod->pprop.ptr, &iod->pprop.prop); @@ -1048,7 +1057,7 @@ static int image_open_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); ImageUser *iuser = NULL; - ImageOpenData *iod; + ImageOpenData *iod = op->customdata; PointerRNA idptr; Image *ima = NULL; char path[FILE_MAX]; @@ -1085,6 +1094,21 @@ static int image_open_exec(bContext *C, wmOperator *op) if (!op->customdata) image_open_init(C, op); + /* handle multiview images */ + if (RNA_boolean_get(op->ptr, "use_multiview")) { + ImageFormatData *imf = &iod->im_format; + + ima->flag |= IMA_USE_VIEWS; + ima->views_format = imf->views_format; + *ima->stereo3d_format = imf->stereo3d_format; + } + else { + ima->flag &= ~IMA_USE_VIEWS; + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + BKE_image_free_views(ima); + } + /* only image path after save, never ibuf */ if (is_relative_path) { if (!exists) { @@ -1128,6 +1152,8 @@ static int image_open_exec(bContext *C, wmOperator *op) iuser->framenr = 1; iuser->offset = frame_ofs - 1; iuser->fie_ima = 2; + iuser->scene = scene; + BKE_image_init_imageuser(ima, iuser); } /* XXX unpackImage frees image buffers */ @@ -1146,7 +1172,8 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( SpaceImage *sima = CTX_wm_space_image(C); /* XXX other space types can call */ const char *path = U.textudir; Image *ima = NULL; - + Scene *scene = CTX_data_scene(C); + PropertyRNA *prop; if (sima) { ima = sima->image; } @@ -1185,11 +1212,44 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( image_open_init(C, op); + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + image_filesel(C, op, path); return OPERATOR_RUNNING_MODAL; } +static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + return !(STREQ(prop_id, "filepath") || + STREQ(prop_id, "directory") || + STREQ(prop_id, "filename") + ); +} + +static void image_open_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + ImageOpenData *iod = op->customdata; + ImageFormatData *imf = &iod->im_format; + PointerRNA imf_ptr, ptr; + + /* main draw call */ + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + uiDefAutoButsRNA(layout, &ptr, image_open_draw_check_prop, '\0'); + + /* image template */ + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + + /* multiview template */ + if (RNA_boolean_get(op->ptr, "show_multiview")) + uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr); +} + /* called by other space types too */ void IMAGE_OT_open(wmOperatorType *ot) { @@ -1202,6 +1262,7 @@ void IMAGE_OT_open(wmOperatorType *ot) ot->exec = image_open_exec; ot->invoke = image_open_invoke; ot->cancel = image_open_cancel; + ot->ui = image_open_draw; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1233,10 +1294,10 @@ static int image_match_len_exec(bContext *C, wmOperator *UNUSED(op)) } - if (!ima || !iuser || !ima->anim) + if (!ima || !iuser || !BKE_image_has_anim(ima)) return OPERATOR_CANCELLED; - iuser->frames = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN); + iuser->frames = IMB_anim_get_duration(((ImageAnim *) ima->anims.first)->anim, IMB_TC_RECORD_RUN); BKE_image_user_frame_calc(iuser, scene->r.cfra, 0); return OPERATOR_FINISHED; @@ -1433,6 +1494,10 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, } } + /* use the multiview image settings as the default */ + simopts->im_format.stereo3d_format = *ima->stereo3d_format; + simopts->im_format.views_format = ima->views_format; + /* color management */ BKE_color_managed_display_settings_copy(&simopts->im_format.display_settings, &scene->display_settings); BKE_color_managed_view_settings_copy(&simopts->im_format.view_settings, &scene->view_settings); @@ -1468,30 +1533,131 @@ static void save_image_options_to_op(SaveImageOptions *simopts, wmOperator *op) RNA_string_set(op->ptr, "filepath", simopts->filepath); } +/* returns the pass index for the view_id */ +static int get_multiview_pass_id(RenderResult *rr, ImageUser *iuser, const int view_id) +{ + RenderLayer *rl; + RenderPass *rpass; + int passtype; + short rl_index = 0, rp_index; + + if (rr == NULL || iuser == NULL) + return 0; + + if (BLI_listbase_count_ex(&rr->views, 2) < 2) + return iuser->pass; + + if (RE_HasFakeLayer(rr)) + rl_index ++; /* fake compo/sequencer layer */ + + rl = BLI_findlink(&rr->layers, rl_index); + if (!rl) return iuser->pass; + + rpass = BLI_findlink(&rl->passes, iuser->pass); + passtype = rpass->passtype; + + rp_index = 0; + for (rpass = rl->passes.first; rpass; rpass = rpass->next, rp_index++) { + if (rpass->passtype == passtype && + rpass->view_id == view_id) + { + return rp_index; + } + } + + return iuser->pass; +} + +static void save_image_post(wmOperator *op, ImBuf *ibuf, Image *ima, int ok, int save_copy, const char *relbase, int relative, int do_newpath, const char *filepath) +{ + if (ok) { + if (!save_copy) { + if (do_newpath) { + BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name)); + BLI_strncpy(ima->name, filepath, sizeof(ima->name)); + } + + ibuf->userflags &= ~IB_BITMAPDIRTY; + + /* change type? */ + if (ima->type == IMA_TYPE_R_RESULT) { + ima->type = IMA_TYPE_IMAGE; + + /* workaround to ensure the render result buffer is no longer used + * by this image, otherwise can crash when a new render result is + * created. */ + if (ibuf->rect && !(ibuf->mall & IB_rect)) + imb_freerectImBuf(ibuf); + if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat)) + imb_freerectfloatImBuf(ibuf); + if (ibuf->zbuf && !(ibuf->mall & IB_zbuf)) + IMB_freezbufImBuf(ibuf); + if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat)) + IMB_freezbuffloatImBuf(ibuf); + } + if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { + ima->source = IMA_SRC_FILE; + ima->type = IMA_TYPE_IMAGE; + } + + /* only image path, never ibuf */ + if (relative) { + BLI_path_rel(ima->name, relbase); /* only after saving */ + } + + IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); + + } + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", filepath); + } +} + +static void save_imbuf_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) +{ + if (colormanaged_ibuf != ibuf) { + /* This guys might be modified by image buffer write functions, + * need to copy them back from color managed image buffer to an + * original one, so file type of image is being properly updated. + */ + ibuf->ftype = colormanaged_ibuf->ftype; + ibuf->planes = colormanaged_ibuf->planes; + + IMB_freeImBuf(colormanaged_ibuf); + } +} + /** * \return success. * \note ``ima->name`` and ``ibuf->name`` should end up the same. + * \note for multiview the first ``ibuf`` is important to get the settings. */ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath) { Image *ima = ED_space_image(sima); void *lock; ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); + Scene *scene; + RenderResult *rr = NULL; bool ok = false; + WM_cursor_wait(1); + if (ibuf) { - ImBuf *colormanaged_ibuf; + ImBuf *colormanaged_ibuf = NULL; const char *relbase = ID_BLEND_PATH(CTX_data_main(C), &ima->id); const bool relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path")); const bool save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")); const bool save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render")); ImageFormatData *imf = &simopts->im_format; + const bool is_multilayer = imf->imtype == R_IMF_IMTYPE_MULTILAYER; + bool is_mono; + /* old global to ensure a 2nd save goes to same dir */ BLI_strncpy(G.ima, simopts->filepath, sizeof(G.ima)); - WM_cursor_wait(1); - if (ima->type == IMA_TYPE_R_RESULT) { /* enforce user setting for RGB or RGBA, but skip BW */ if (simopts->im_format.planes == R_IMF_PLANES_RGBA) { @@ -1512,83 +1678,197 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI } } - colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + /* we need renderresult for exr and rendered multiview */ + scene = CTX_data_scene(C); + rr = BKE_image_acquire_renderresult(scene, ima); + is_mono = rr ? BLI_listbase_count_ex(&rr->views, 2) < 2 : (ima->flag & IMA_IS_MULTIVIEW) == 0; - if (simopts->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { - Scene *scene = CTX_data_scene(C); - RenderResult *rr = BKE_image_acquire_renderresult(scene, ima); - if (rr) { - ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, simopts->im_format.exr_codec); - } - else { + /* error handling */ + if (!rr) { + if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { BKE_report(op->reports, RPT_ERROR, "Did not write, no Multilayer Image"); + goto cleanup; } - BKE_image_release_renderresult(scene, ima); } else { - ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, &simopts->im_format, save_copy); - } - - if (ok) { - if (!save_copy) { - if (do_newpath) { - BLI_strncpy(ibuf->name, simopts->filepath, sizeof(ibuf->name)); - BLI_strncpy(ima->name, simopts->filepath, sizeof(ima->name)); + if (imf->views_format == R_IMF_VIEWS_STEREO_3D) { + if ((ima->flag & IMA_IS_STEREO) == 0) { + BKE_reportf(op->reports, RPT_ERROR, "Did not write, the image doesn't have a \"%s\" and \"%s\" views", + STEREO_LEFT_NAME, STEREO_RIGHT_NAME); + goto cleanup; } - ibuf->userflags &= ~IB_BITMAPDIRTY; - - /* change type? */ - if (ima->type == IMA_TYPE_R_RESULT) { - ima->type = IMA_TYPE_IMAGE; - - /* workaround to ensure the render result buffer is no longer used - * by this image, otherwise can crash when a new render result is - * created. */ - if (ibuf->rect && !(ibuf->mall & IB_rect)) - imb_freerectImBuf(ibuf); - if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat)) - imb_freerectfloatImBuf(ibuf); - if (ibuf->zbuf && !(ibuf->mall & IB_zbuf)) - IMB_freezbufImBuf(ibuf); - if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat)) - IMB_freezbuffloatImBuf(ibuf); + /* it shouldn't ever happen*/ + if ((BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)) == NULL) || + (BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name)) == NULL)) + { + BKE_reportf(op->reports, RPT_ERROR, "Did not write, the image doesn't have a \"%s\" and \"%s\" views", + STEREO_LEFT_NAME, STEREO_RIGHT_NAME); + goto cleanup; } - if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { - ima->source = IMA_SRC_FILE; - ima->type = IMA_TYPE_IMAGE; + } + } + + /* fancy multiview OpenEXR */ + if ((imf->imtype == R_IMF_IMTYPE_MULTILAYER) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, true, NULL); + save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + else if ((imf->imtype == R_IMF_IMTYPE_OPENEXR) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) { + /* treat special Openexr case separetely (this is the singlelayer multiview OpenEXR */ + BKE_imbuf_write_prepare(ibuf, imf); + ok = BKE_image_save_openexr_multiview(ima, ibuf, simopts->filepath, (IB_rect | IB_zbuf | IB_zbuffloat | IB_multiview)); + ED_space_image_release_buffer(sima, ibuf, lock); + } + /* regular mono pipeline */ + else if (is_mono) { + if (is_multilayer) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL); + } + else { + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, imf, save_copy); + save_imbuf_post(ibuf, colormanaged_ibuf); + } + save_image_post(op, ibuf, ima, ok, (is_multilayer ? true : save_copy), relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + /* individual multiview images */ + else if (imf->views_format == R_IMF_VIEWS_INDIVIDUAL) { + size_t i; + unsigned char planes = ibuf->planes; + const size_t totviews = (rr ? BLI_listbase_count(&rr->views) : BLI_listbase_count(&ima->views)); + + if (!is_multilayer) { + ED_space_image_release_buffer(sima, ibuf, lock); + } + + for (i = 0; i < totviews; i++) { + char filepath[FILE_MAX]; + bool ok_view = false; + const char *view = rr ? ((RenderView *) BLI_findlink(&rr->views, i))->name : + ((ImageView *) BLI_findlink(&ima->views, i))->name; + + if (is_multilayer) { + BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath); + ok_view = RE_WriteRenderResult(op->reports, rr, filepath, imf, false, view); + save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath); } + else { + /* copy iuser to get the correct ibuf for this view */ + ImageUser iuser = sima->iuser; + iuser.view = i; + iuser.flag &= ~IMA_SHOW_STEREO; + + if (rr) { + iuser.pass = get_multiview_pass_id(rr, &sima->iuser, i); + BKE_image_multilayer_index(rr, &iuser); + } + else { + BKE_image_multiview_index(ima, &iuser); + } + + ibuf = BKE_image_acquire_ibuf(sima->image, &iuser, &lock); + ibuf->planes = planes; + + BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath); - /* only image path, never ibuf */ - if (relative) { - BLI_path_rel(ima->name, relbase); /* only after saving */ + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + ok_view = BKE_imbuf_write_as(colormanaged_ibuf, filepath, &simopts->im_format, save_copy); + save_imbuf_post(ibuf, colormanaged_ibuf); + save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath); + BKE_image_release_ibuf(sima->image, ibuf, lock); } + ok &= ok_view; + } - IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); + if (is_multilayer) { + ED_space_image_release_buffer(sima, ibuf, lock); } } - else { - BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", simopts->filepath); - } + /* stereo (multiview) images */ + else if (simopts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) { + if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL); + save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + else { + ImBuf *ibuf_stereo[2] = {NULL}; + unsigned char planes = ibuf->planes; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + int i; - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image); + /* we need to get the specific per-view buffers */ + ED_space_image_release_buffer(sima, ibuf, lock); + + for (i = 0; i < 2; i ++) { + ImageUser iuser = sima->iuser; + iuser.flag &= ~IMA_SHOW_STEREO; + + if (rr) { + int id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + iuser.pass = get_multiview_pass_id(rr, &sima->iuser, id); + iuser.view = id; + + BKE_image_multilayer_index(rr, &iuser); + } + else { + iuser.view = i; + BKE_image_multiview_index(ima, &iuser); + } + + ibuf = BKE_image_acquire_ibuf(sima->image, &iuser, &lock); + + if (ibuf == NULL) { + BKE_report(op->reports, RPT_ERROR, "Did not write, unexpected error when saving stereo image"); + goto cleanup; + } + + ibuf->planes = planes; - WM_cursor_wait(0); + /* color manage the ImBuf leaving it ready for saving */ + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, + &imf->view_settings, &imf->display_settings, imf); - if (colormanaged_ibuf != ibuf) { - /* This guys might be modified by image buffer write functions, - * need to copy them back from color managed image buffer to an - * original one, so file type of image is being properly updated. - */ - ibuf->ftype = colormanaged_ibuf->ftype; - ibuf->planes = colormanaged_ibuf->planes; + BKE_imbuf_write_prepare(colormanaged_ibuf, imf); + IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf); - IMB_freeImBuf(colormanaged_ibuf); + /* duplicate buffer to prevent locker issue when using render result */ + ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf); + + save_imbuf_post(ibuf, colormanaged_ibuf); + BKE_image_release_ibuf(sima->image, ibuf, lock); + } + + ibuf = IMB_stereo3d_ImBuf(imf, ibuf_stereo[0], ibuf_stereo[1]); + + /* save via traditional path */ + ok = BKE_imbuf_write_as(ibuf, simopts->filepath, imf, save_copy); + + IMB_freeImBuf(ibuf); + + for (i = 0; i < 2; i ++) { + IMB_freeImBuf(ibuf_stereo[i]); + } + } } + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image); + + } + else { +cleanup: + ED_space_image_release_buffer(sima, ibuf, lock); } - ED_space_image_release_buffer(sima, ibuf, lock); + if (rr) { + BKE_image_release_renderresult(scene, ima); + } + + WM_cursor_wait(0); return ok; } @@ -1636,6 +1916,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS Image *ima = ED_space_image(sima); Scene *scene = CTX_data_scene(C); SaveImageOptions simopts; + PropertyRNA *prop; const bool save_as_render = ((ima->source == IMA_SRC_VIEWER) || (ima->flag & IMA_VIEW_AS_RENDER)); if (RNA_struct_property_is_set(op->ptr, "filepath")) @@ -1657,6 +1938,12 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS op->customdata = MEM_mallocN(sizeof(simopts.im_format), __func__); memcpy(op->customdata, &simopts.im_format, sizeof(simopts.im_format)); + /* show multiview save options only if image has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (ima->flag & IMA_IS_MULTIVIEW) != 0); + prop = RNA_struct_find_property(op->ptr, "use_multiview"); + RNA_property_boolean_set(op->ptr, prop, (ima->flag & IMA_IS_MULTIVIEW) != 0); + image_filesel(C, op, simopts.filepath); return OPERATOR_RUNNING_MODAL; @@ -1683,15 +1970,20 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; ImageFormatData *imf = op->customdata; - PointerRNA ptr; + PointerRNA imf_ptr, ptr; + const bool is_multiview = RNA_boolean_get(op->ptr, "use_multiview"); /* image template */ - RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &ptr); - uiTemplateImageSettings(layout, &ptr, false); + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + uiTemplateImageSettings(layout, &imf_ptr, false); /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA(layout, &ptr, image_save_as_draw_check_prop, '\0'); + + /* multiview template */ + if (is_multiview) + uiTemplateImageFormatViews(layout, &imf_ptr, NULL); } static int image_save_as_poll(bContext *C) @@ -1714,8 +2006,6 @@ static int image_save_as_poll(bContext *C) void IMAGE_OT_save_as(wmOperatorType *ot) { -// PropertyRNA *prop; - /* identifiers */ ot->name = "Save As Image"; ot->idname = "IMAGE_OT_save_as"; @@ -1932,6 +2222,7 @@ static int image_new_exec(bContext *C, wmOperator *op) float color[4]; int width, height, floatbuf, gen_type, alpha; int gen_context; + int stereo3d; /* retrieve state */ sima = CTX_wm_space_image(C); @@ -1952,11 +2243,12 @@ static int image_new_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "color", color); alpha = RNA_boolean_get(op->ptr, "alpha"); gen_context = RNA_enum_get(op->ptr, "gen_context"); + stereo3d = RNA_boolean_get(op->ptr, "use_stereo_3d"); if (!alpha) color[3] = 1.0f; - ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color); + ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d); if (!ima) return OPERATOR_CANCELLED; @@ -2036,6 +2328,53 @@ static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y); } +static void image_new_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *split, *col[2]; + uiLayout *layout = op->layout; + PointerRNA ptr; +#if 0 + Scene *scene = CTX_data_scene(C); + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; +#endif + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + /* copy of WM_operator_props_dialog_popup() layout */ + + split = uiLayoutSplit(layout, 0.5f, false); + col[0] = uiLayoutColumn(split, false); + col[1] = uiLayoutColumn(split, false); + + uiItemL(col[0], IFACE_("Name"), ICON_NONE); + uiItemR(col[1], &ptr, "name", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Width"), ICON_NONE); + uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Height"), ICON_NONE); + uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Color"), ICON_NONE); + uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE); + + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "alpha", 0, NULL, ICON_NONE); + + uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE); + uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE); + + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE); + +#if 0 + if (is_multiview) { + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE); + } +#endif +} + void IMAGE_OT_new(wmOperatorType *ot) { PropertyRNA *prop; @@ -2056,6 +2395,7 @@ void IMAGE_OT_new(wmOperatorType *ot) /* api callbacks */ ot->exec = image_new_exec; ot->invoke = image_new_invoke; + ot->ui = image_new_draw; /* flags */ ot->flag = OPTYPE_UNDO; @@ -2075,7 +2415,8 @@ void IMAGE_OT_new(wmOperatorType *ot) RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); prop = RNA_def_enum(ot->srna, "gen_context", gen_context_items, 0, "Gen Context", "Generation context"); RNA_def_property_flag(prop, PROP_HIDDEN); - + prop = RNA_def_boolean(ot->srna, "use_stereo_3d", 0, "Stereo 3D", "Create an image with left and right views"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } #undef IMA_DEF_NAME @@ -2200,7 +2541,7 @@ static bool image_pack_test(bContext *C, wmOperator *op) if (!ima) return 0; - if (!as_png && ima->packedfile) + if (!as_png && BKE_image_has_packedfile(ima)) return 0; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2229,7 +2570,7 @@ static int image_pack_exec(bContext *C, wmOperator *op) if (as_png) BKE_image_memorypack(ima); else - ima->packedfile = newPackedFile(op->reports, ima->name, ID_BLEND_PATH(bmain, &ima->id)); + BKE_image_packfiles(op->reports, ima, ID_BLEND_PATH(bmain, &ima->id)); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -2301,7 +2642,7 @@ static int image_unpack_exec(bContext *C, wmOperator *op) if (!ima) ima = CTX_data_edit_image(C); } - if (!ima || !ima->packedfile) + if (!ima || !BKE_image_has_packedfile(ima)) return OPERATOR_CANCELLED; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2329,7 +2670,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE if (RNA_struct_property_is_set(op->ptr, "id")) return image_unpack_exec(C, op); - if (!ima || !ima->packedfile) + if (!ima || !BKE_image_has_packedfile(ima)) return OPERATOR_CANCELLED; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2340,7 +2681,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE if (G.fileflags & G_AUTOPACK) BKE_report(op->reports, RPT_WARNING, "AutoPack is enabled, so image will be packed again on file save"); - unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", ima->packedfile); + unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", BKE_image_has_packedfile(ima) ? ((ImagePackedFile *)ima->packedfiles.first)->packedfile : NULL); return OPERATOR_FINISHED; } @@ -2609,8 +2950,11 @@ static int image_sample_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case LEFTMOUSE: case RIGHTMOUSE: // XXX hardcoded - image_sample_exit(C, op); - return OPERATOR_CANCELLED; + if (event->val == KM_RELEASE) { + image_sample_exit(C, op); + return OPERATOR_CANCELLED; + } + break; case MOUSEMOVE: image_sample_apply(C, op, event); break; diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index bd3d76bbaba..1023f9641b3 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -156,7 +156,8 @@ static SpaceLink *image_new(const bContext *UNUSED(C)) simage->iuser.ok = true; simage->iuser.fie_ima = 2; simage->iuser.frames = 100; - + simage->iuser.flag = IMA_SHOW_STEREO; + scopes_new(&simage->scopes); simage->sample_line_hist.height = 100; diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 66023ce1243..1fb7228dd98 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -159,7 +159,9 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p } /* AnimData pointer */ - RNA_pointer_create(id, &RNA_AnimData, ale->adt, adt_ptr); + if (adt_ptr) { + RNA_pointer_create(id, &RNA_AnimData, ale->adt, adt_ptr); + } /* set found status to -1, since setting to 1 would break the loop * and potentially skip an active NLA-Track in some cases... @@ -282,7 +284,7 @@ static void nla_panel_animdata(const bContext *C, Panel *pa) /* Active Action Properties ------------------------------------- */ /* action */ row = uiLayoutRow(layout, true); - uiTemplateID(row, (bContext *)C, &adt_ptr, "action", "ACTION_OT_new", NULL, NULL /*"ACTION_OT_unlink"*/); // XXX: need to make these operators + uiTemplateID(row, (bContext *)C, &adt_ptr, "action", "ACTION_OT_new", NULL, "NLA_OT_action_unlink"); /* extrapolation */ row = uiLayoutRow(layout, true); diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 5476d1c2683..4ed4a3f9514 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -504,6 +504,52 @@ void NLA_OT_action_pushdown(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } +/* ******************** Action Unlink ******************************** */ + +static int nla_action_unlink_poll(bContext *C) +{ + if (ED_operator_nla_active(C)) { + return nla_panel_context(C, NULL, NULL, NULL); + } + + /* something failed... */ + return false; +} + +static int nla_action_unlink_exec(bContext *C, wmOperator *op) +{ + PointerRNA adt_ptr; + AnimData *adt; + + /* check context and also validity of pointer */ + if (!nla_panel_context(C, &adt_ptr, NULL, NULL)) + return OPERATOR_CANCELLED; + + /* get animdata */ + adt = adt_ptr.data; + if (adt == NULL) + return OPERATOR_CANCELLED; + + /* do unlinking */ + if (adt && adt->action) { + ED_animedit_unlink_action(C, adt_ptr.id.data, adt, adt->action, op->reports); + } + + return OPERATOR_FINISHED; +} + +void NLA_OT_action_unlink(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlink Action"; + ot->idname = "NLA_OT_action_unlink"; + ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)"; + + /* callbacks */ + ot->exec = nla_action_unlink_exec; + ot->poll = nla_action_unlink_poll; +} + /* ******************** Add Tracks Operator ***************************** */ /* Add NLA Tracks to the same AnimData block as a selected track, or above the selected tracks */ @@ -725,7 +771,7 @@ static int nlaedit_objects_add_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { /* ensure that object has AnimData... that's all */ - BKE_id_add_animdata(&ob->id); + BKE_animdata_add_id(&ob->id); } CTX_DATA_END; diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index aae0e38696c..5d9a77c8576 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -650,6 +650,8 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *ar) /* draw channels */ { /* first pass: just the standard GL-drawing for backdrop + text */ + size_t channel_index = 0; + y = (float)(-NLACHANNEL_HEIGHT(snla)); for (ale = anim_data.first; ale; ale = ale->next) { @@ -661,11 +663,12 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *ar) IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* draw all channels using standard channel-drawing API */ - ANIM_channel_draw(ac, ale, yminc, ymaxc); + ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index); } /* adjust y-position for next one */ y -= NLACHANNEL_STEP(snla); + channel_index++; } } { /* second pass: UI widgets */ diff --git a/source/blender/editors/space_nla/nla_intern.h b/source/blender/editors/space_nla/nla_intern.h index bd96b5a4de5..70fdb2d478d 100644 --- a/source/blender/editors/space_nla/nla_intern.h +++ b/source/blender/editors/space_nla/nla_intern.h @@ -137,6 +137,7 @@ bool nlaedit_add_tracks_empty(bAnimContext *ac); void NLA_OT_channels_click(wmOperatorType *ot); void NLA_OT_action_pushdown(wmOperatorType *ot); +void NLA_OT_action_unlink(wmOperatorType *ot); void NLA_OT_tracks_add(wmOperatorType *ot); void NLA_OT_tracks_delete(wmOperatorType *ot); diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index b3a875047db..43bc4b9df0f 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -114,6 +114,7 @@ void nla_operatortypes(void) WM_operatortype_append(NLA_OT_channels_click); WM_operatortype_append(NLA_OT_action_pushdown); + WM_operatortype_append(NLA_OT_action_unlink); WM_operatortype_append(NLA_OT_tracks_add); WM_operatortype_append(NLA_OT_tracks_delete); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index eedf03ef0b9..3a5067b87f0 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -703,7 +703,9 @@ static void node_buts_image_user(uiLayout *layout, bContext *C, PointerRNA *ptr, uiItemR(col, ptr, "use_auto_refresh", 0, NULL, ICON_NONE); } - if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER) { + if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER && + RNA_boolean_get(ptr, "has_layers")) + { col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "layer", 0, NULL, ICON_NONE); } @@ -1238,6 +1240,24 @@ static void node_shader_set_butfunc(bNodeType *ntype) /* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */ +static void node_buts_image_views(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr, + PointerRNA *imaptr) +{ + uiLayout *col; + + if (!imaptr->data) + return; + + col = uiLayoutColumn(layout, false); + + if (RNA_boolean_get(ptr, "has_views")) { + if (RNA_enum_get(ptr, "view") == 0) + uiItemR(col, ptr, "view", 0, NULL, ICON_CAMERA_STEREO); + else + uiItemR(col, ptr, "view", 0, NULL, ICON_SCENE); + } +} + static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) { bNode *node = ptr->data; @@ -1251,6 +1271,8 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * imaptr = RNA_pointer_get(ptr, "image"); node_buts_image_user(layout, C, ptr, &imaptr, &iuserptr); + + node_buts_image_views(layout, C, ptr, &imaptr); } static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1741,8 +1763,8 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { PointerRNA imfptr = RNA_pointer_get(ptr, "format"); - int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER); - + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + if (multilayer) uiItemL(layout, IFACE_("Path:"), ICON_NONE); else @@ -1751,15 +1773,22 @@ static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C) } static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { + Scene *scene = CTX_data_scene(C); PointerRNA imfptr = RNA_pointer_get(ptr, "format"); PointerRNA active_input_ptr, op_ptr; uiLayout *row, *col; int active_index; - int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER); + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; node_composit_buts_file_output(layout, C, ptr); uiTemplateImageSettings(layout, &imfptr, false); + /* disable stereo output for multilayer, too much work for something that no one will use */ + /* if someone asks for that we can implement it */ + if (is_multiview) + uiTemplateImageFormatViews(layout, &imfptr, NULL); + uiItemS(layout); uiItemO(layout, IFACE_("Add Input"), ICON_ZOOMIN, "NODE_OT_output_file_add_socket"); @@ -1821,6 +1850,9 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, RNA_boolean_get(&active_input_ptr, "use_node_format") == false); uiTemplateImageSettings(col, &imfptr, false); + + if (is_multiview) + uiTemplateImageFormatViews(layout, &imfptr, NULL); } } } @@ -2114,6 +2146,18 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "check", 0, NULL, ICON_NONE); } +static void node_composit_buts_switch_view_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) +{ + PointerRNA op_ptr; + wmOperatorType *ot = WM_operatortype_find("NODE_OT_switch_view_update", 1); + + BLI_assert(ot != 0); + + WM_operator_properties_create_ptr(&op_ptr, ot); + + uiItemFullO_ptr(layout, ot, "Update Views", ICON_FILE_REFRESH, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); +} + static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *row; @@ -2611,6 +2655,9 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_SWITCH: ntype->draw_buttons = node_composit_buts_switch; break; + case CMP_NODE_SWITCH_VIEW: + ntype->draw_buttons_ex = node_composit_buts_switch_view_ex; + break; case CMP_NODE_MASK_BOX: ntype->draw_buttons = node_composit_buts_boxmask; ntype->draw_backdrop = node_composit_backdrop_boxmask; @@ -2983,6 +3030,7 @@ static void node_file_output_socket_draw(bContext *C, uiLayout *layout, PointerR imfptr = RNA_pointer_get(node_ptr, "format"); imtype = RNA_enum_get(&imfptr, "file_format"); + if (imtype == R_IMF_IMTYPE_MULTILAYER) { NodeImageMultiFileSocket *input = sock->storage; RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr); diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 3595b4520ea..e2bcf358a33 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -212,13 +212,13 @@ static void compo_progressjob(void *cjv, float progress) *(cj->progress) = progress; } - /* only this runs inside thread */ static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress) { CompoJob *cj = cjv; bNodeTree *ntree = cj->localtree; Scene *scene = cj->scene; + SceneRenderView *srv; if (scene->use_nodes == false) return; @@ -238,7 +238,11 @@ static void compo_startjob(void *cjv, short *stop, short *do_update, float *prog // XXX BIF_store_spare(); /* 1 is do_previews */ - ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings); + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) continue; + ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings, srv->name); + } ntree->test_break = NULL; ntree->stats_draw = NULL; @@ -1660,6 +1664,54 @@ void NODE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ****************** Switch View ******************* */ + +static int node_switch_view_poll(bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + if (snode && snode->edittree) + return true; + + return false; +} + +static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node, *next; + + for (node = snode->edittree->nodes.first; node; node = next) { + next = node->next; + if (node->flag & SELECT) { + /* call the update function from the Switch View node */ + node->update = NODE_UPDATE_OPERATOR; + } + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_switch_view_update(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Update Views"; + ot->description = "Update views of selected node"; + ot->idname = "NODE_OT_switch_view_update"; + + /* api callbacks */ + ot->exec = node_switch_view_exec; + ot->poll = node_switch_view_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ****************** Delete with reconnect ******************* */ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index d49d37c651e..b15e9025a82 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -200,6 +200,8 @@ void NODE_OT_output_file_add_socket(struct wmOperatorType *ot); void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot); void NODE_OT_output_file_move_active_socket(struct wmOperatorType *ot); +void NODE_OT_switch_view_update (struct wmOperatorType *ot); + /* Note: clipboard_cut is a simple macro of copy + delete */ void NODE_OT_clipboard_copy(struct wmOperatorType *ot); void NODE_OT_clipboard_paste(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index e2d83c243a2..474ad4db4af 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -123,6 +123,8 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_viewer_border); WM_operatortype_append(NODE_OT_clear_viewer_border); + WM_operatortype_append(NODE_OT_switch_view_update); + WM_operatortype_append(NODE_OT_tree_socket_add); WM_operatortype_append(NODE_OT_tree_socket_remove); WM_operatortype_append(NODE_OT_tree_socket_move); diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index c8951a1172e..f72def8377f 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -633,11 +633,14 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) case RIGHTMOUSE: case MIDDLEMOUSE: { - node_link_exit(C, op, true); - - ED_area_headerprint(CTX_wm_area(C), NULL); - ED_region_tag_redraw(ar); - return OPERATOR_FINISHED; + if (event->val != KM_NOTHING) { + node_link_exit(C, op, true); + + ED_area_headerprint(CTX_wm_area(C), NULL); + ED_region_tag_redraw(ar); + return OPERATOR_FINISHED; + } + break; } } diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c index 8ff16df9bf0..12e1603761b 100644 --- a/source/blender/editors/space_node/node_view.c +++ b/source/blender/editors/space_node/node_view.c @@ -613,8 +613,11 @@ static int sample_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case LEFTMOUSE: case RIGHTMOUSE: // XXX hardcoded - sample_exit(C, op); - return OPERATOR_CANCELLED; + if (event->val == KM_RELEASE) { + sample_exit(C, op); + return OPERATOR_CANCELLED; + } + break; case MOUSEMOVE: sample_apply(C, op, event); break; diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index f5869575cc6..93116dbe22c 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -91,19 +91,19 @@ void outliner_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "OUTLINER_OT_item_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0); - kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, 0, 0); + kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "recursive", false); RNA_boolean_set(kmi->ptr, "extend", false); - kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "recursive", false); RNA_boolean_set(kmi->ptr, "extend", true); - kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "recursive", true); RNA_boolean_set(kmi->ptr, "extend", false); - kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "recursive", true); RNA_boolean_set(kmi->ptr, "extend", true); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index af21478150b..e5833103658 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -467,7 +467,7 @@ void outliner_do_object_operation(bContext *C, Scene *scene_act, SpaceOops *soop static void clear_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te), TreeStoreElem *tselem, void *UNUSED(arg)) { - BKE_free_animdata(tselem->id); + BKE_animdata_free(tselem->id); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 9239d9cb27e..17a6e952ee1 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -66,7 +66,7 @@ #include "BKE_modifier.h" #include "BKE_sequencer.h" #include "BKE_idcode.h" -#include "BKE_treehash.h" +#include "BKE_outliner_treehash.h" #include "ED_armature.h" #include "ED_screen.h" @@ -114,7 +114,7 @@ static void outliner_storage_cleanup(SpaceOops *soops) BLI_mempool_destroy(ts); soops->treestore = NULL; if (soops->treehash) { - BKE_treehash_free(soops->treehash); + BKE_outliner_treehash_free(soops->treehash); soops->treehash = NULL; } } @@ -133,7 +133,7 @@ static void outliner_storage_cleanup(SpaceOops *soops) soops->treestore = new_ts; if (soops->treehash) { /* update hash table to fix broken pointers */ - BKE_treehash_rebuild_from_treestore(soops->treehash, soops->treestore); + BKE_outliner_treehash_rebuild_from_treestore(soops->treehash, soops->treestore); } } } @@ -151,12 +151,12 @@ static void check_persistent(SpaceOops *soops, TreeElement *te, ID *id, short ty } if (soops->treehash == NULL) { - soops->treehash = BKE_treehash_create_from_treestore(soops->treestore); + soops->treehash = BKE_outliner_treehash_create_from_treestore(soops->treestore); } /* find any unused tree element in treestore and mark it as used * (note that there may be multiple unused elements in case of linked objects) */ - tselem = BKE_treehash_lookup_unused(soops->treehash, type, nr, id); + tselem = BKE_outliner_treehash_lookup_unused(soops->treehash, type, nr, id); if (tselem) { te->store_elem = tselem; tselem->used = 1; @@ -171,7 +171,7 @@ static void check_persistent(SpaceOops *soops, TreeElement *te, ID *id, short ty tselem->used = 0; tselem->flag = TSE_CLOSED; te->store_elem = tselem; - BKE_treehash_add_element(soops->treehash, tselem); + BKE_outliner_treehash_add_element(soops->treehash, tselem); } /* ********************************************************* */ @@ -216,7 +216,7 @@ TreeElement *outliner_find_tse(SpaceOops *soops, TreeStoreElem *tse) if (tse->id == NULL) return NULL; /* check if 'tse' is in treestore */ - tselem = BKE_treehash_lookup_any(soops->treehash, tse->type, tse->nr, tse->id); + tselem = BKE_outliner_treehash_lookup_any(soops->treehash, tse->type, tse->nr, tse->id); if (tselem) return outliner_find_tree_element(&soops->tree, tselem); diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index e1aea8892b5..7b1ec174a6b 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -41,7 +41,7 @@ #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_scene.h" -#include "BKE_treehash.h" +#include "BKE_outliner_treehash.h" #include "ED_space_api.h" #include "ED_screen.h" @@ -464,7 +464,7 @@ static void outliner_free(SpaceLink *sl) BLI_mempool_destroy(soutliner->treestore); } if (soutliner->treehash) { - BKE_treehash_free(soutliner->treehash); + BKE_outliner_treehash_free(soutliner->treehash); } } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 329e6db4cd0..373095d68f9 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -60,6 +60,7 @@ #include "ED_screen.h" #include "ED_sequencer.h" +#include "UI_interface.h" #include "BKE_sound.h" @@ -70,6 +71,10 @@ /* own include */ #include "sequencer_intern.h" +typedef struct SequencerAddData { + ImageFormatData im_format; +} SequencerAddData; + /* Generic functions, reused by add strip operators */ /* avoid passing multiple args and be more verbose */ @@ -222,6 +227,19 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, wmOperator *op) } RNA_PROP_END; } + + if ((prop = RNA_struct_find_property(op->ptr, "use_multiview")) && RNA_property_boolean_get(op->ptr, prop)) { + if (op->customdata) { + SequencerAddData *sad = op->customdata; + ImageFormatData *imf = &sad->im_format; + + seq_load->views_format = imf->views_format; + seq_load->flag |= SEQ_USE_VIEWS; + + /* operator custom data is always released after the SeqLoadInfo, no need to handle the memory here */ + seq_load->stereo3d_format = &imf->stereo3d_format; + } + } } /** @@ -573,6 +591,9 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad return OPERATOR_CANCELLED; } + if (op->customdata) + MEM_freeN(op->customdata); + BKE_sequencer_sort(scene); BKE_sequencer_update_muting(ed); @@ -581,15 +602,40 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad return OPERATOR_FINISHED; } +/* add sequencer operators */ +static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op) +{ + op->customdata = MEM_callocN(sizeof(SequencerAddData), __func__); +} + +static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op) +{ + if (op->customdata) + MEM_freeN(op->customdata); + op->customdata = NULL; +} + +static bool sequencer_add_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + return !(STREQ(prop_id, "filepath") || + STREQ(prop_id, "directory") || + STREQ(prop_id, "filename") + ); +} + /* add movie operator */ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) { return sequencer_add_generic_strip_exec(C, op, BKE_sequencer_add_movie_strip); } - static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + /* This is for drag and drop */ if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) || RNA_struct_property_is_set(op->ptr, "filepath")) @@ -599,13 +645,37 @@ static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const w } sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MOVIE); - + + sequencer_add_init(C, op); + + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; //return sequencer_add_movie_strip_exec(C, op); } +static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + SequencerAddData *sad = op->customdata; + ImageFormatData *imf = &sad->im_format; + PointerRNA imf_ptr, ptr; + + /* main draw call */ + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + uiDefAutoButsRNA(layout, &ptr, sequencer_add_draw_check_prop, '\0'); + + /* image template */ + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + + /* multiview template */ + if (RNA_boolean_get(op->ptr, "show_multiview")) + uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr); +} void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) { @@ -618,9 +688,11 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = sequencer_add_movie_strip_invoke; ot->exec = sequencer_add_movie_strip_exec; + ot->cancel = sequencer_add_cancel; + ot->ui = sequencer_add_draw; ot->poll = ED_operator_sequencer_active_editable; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -735,6 +807,9 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) sequencer_add_apply_overlap(C, op, seq); + if (op->customdata) + MEM_freeN(op->customdata); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -742,6 +817,9 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + /* drag drop has set the names */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME | SEQPROP_NOPATHS, SEQ_TYPE_IMAGE); @@ -750,6 +828,12 @@ static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const w sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME, SEQ_TYPE_IMAGE); + sequencer_add_init(C, op); + + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; } @@ -766,6 +850,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = sequencer_add_image_strip_invoke; ot->exec = sequencer_add_image_strip_exec; + ot->cancel = sequencer_add_cancel; + ot->ui = sequencer_add_draw; ot->poll = ED_operator_sequencer_active_editable; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 4e4b108cd95..98937716cc0 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -50,6 +50,7 @@ #include "BKE_main.h" #include "BKE_sequencer.h" #include "BKE_sound.h" +#include "BKE_scene.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" @@ -879,7 +880,7 @@ void ED_sequencer_special_preview_clear(void) sequencer_special_update_set(NULL); } -ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int cfra, int frame_ofs) +ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int cfra, int frame_ofs, const char *viewname) { SeqRenderData context; ImBuf *ibuf; @@ -907,6 +908,7 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int bmain->eval_ctx, bmain, scene, rectx, recty, proxy_size, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); /* sequencer could start rendering, in this case we need to be sure it wouldn't be canceled * by Esc pressed somewhere in the past @@ -1013,6 +1015,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq const bool is_imbuf = ED_space_sequencer_check_show_imbuf(sseq); int format, type; bool glsl_used = false; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; if (G.is_rendering == false && (scene->r.seq_flag & R_SEQ_GL_PREV) == 0) { /* stop all running jobs, except screen one. currently previews frustrate Render @@ -1042,8 +1045,9 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq return; } - ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs); - + /* for now we only support Left/Right */ + ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]); + if (ibuf == NULL) return; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index e2602ca81ef..f5c55cd91f0 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -180,8 +180,6 @@ static void seq_proxy_build_job(const bContext *C) Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); ScrArea *sa = CTX_wm_area(C); - struct SeqIndexBuildContext *context; - LinkData *link; Sequence *seq; GSet *file_list; @@ -209,9 +207,7 @@ static void seq_proxy_build_job(const bContext *C) SEQP_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { - context = BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list); - link = BLI_genericNodeN(context); - BLI_addtail(&pj->queue, link); + BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list, &pj->queue); } } SEQ_END @@ -3452,12 +3448,18 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) SEQP_BEGIN(ed, seq) { if ((seq->flag & SELECT)) { - struct SeqIndexBuildContext *context; + ListBase queue = {NULL, NULL}; + LinkData *link; short stop = 0, do_update; float progress; - context = BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list); - BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress); - BKE_sequencer_proxy_rebuild_finish(context, 0); + + BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list, &queue); + + for (link = queue.first; link; link = link->next) { + struct SeqIndexBuildContext *context = link->data; + BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress); + BKE_sequencer_proxy_rebuild_finish(context, 0); + } BKE_sequencer_free_imbuf(scene, &ed->seqbase, false); } } diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 63ae52c3207..7c7bdde9165 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -62,7 +62,7 @@ void sequencer_special_update_set(Sequence *seq); /* UNUSED */ // void seq_reset_imageofs(struct SpaceSeq *sseq); -struct ImBuf *sequencer_ibuf_get(struct Main *bmain, struct Scene *scene, struct SpaceSeq *sseq, int cfra, int frame_ofs); +struct ImBuf *sequencer_ibuf_get(struct Main *bmain, struct Scene *scene, struct SpaceSeq *sseq, int cfra, int frame_ofs, const char *viewname); /* sequencer_edit.c */ struct View2D; diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 40191905c38..e34388db42d 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -368,5 +368,5 @@ void ED_operatormacros_sequencer(void) "Duplicate selected strips and move them", OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "SEQUENCER_OT_duplicate"); - WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + WM_operatortype_macro_define(ot, "TRANSFORM_OT_seq_slide"); } diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 71eb64a74f5..5a564dd6f25 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -98,7 +98,7 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) Scene *scene = CTX_data_scene(C); SpaceSeq *sseq = (SpaceSeq *) CTX_wm_space_data(C); ARegion *ar = CTX_wm_region(C); - ImBuf *ibuf = sequencer_ibuf_get(bmain, scene, sseq, CFRA, 0); + ImBuf *ibuf = sequencer_ibuf_get(bmain, scene, sseq, CFRA, 0, NULL); ImageSampleInfo *info = op->customdata; float fx, fy; @@ -205,8 +205,11 @@ static int sample_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case LEFTMOUSE: case RIGHTMOUSE: /* XXX hardcoded */ - sample_exit(C, op); - return OPERATOR_CANCELLED; + if (event->val == KM_RELEASE) { + sample_exit(C, op); + return OPERATOR_CANCELLED; + } + break; case MOUSEMOVE: sample_apply(C, op, event); break; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index ad8050a50e8..9d517b20f01 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -2163,6 +2163,11 @@ static int text_scroll_modal(bContext *C, wmOperator *op, const wmEvent *event) case LEFTMOUSE: case RIGHTMOUSE: case MIDDLEMOUSE: + /* don't exit on dummy events */ + if (event->val == KM_NOTHING) { + return OPERATOR_RUNNING_MODAL; + } + if (ELEM(tsc->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) { txt_screen_skip(st, ar, st->viewlines * (tsc->zone == SCROLLHANDLE_MIN_OUTSIDE ? 1 : -1)); @@ -2667,8 +2672,11 @@ static int text_set_selection_modal(bContext *C, wmOperator *op, const wmEvent * case LEFTMOUSE: case MIDDLEMOUSE: case RIGHTMOUSE: - text_cursor_set_exit(C, op); - return OPERATOR_FINISHED; + if (event->val != KM_NOTHING) { + text_cursor_set_exit(C, op); + return OPERATOR_FINISHED; + } + break; case TIMER: case MOUSEMOVE: text_cursor_set_apply(C, op, event); diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index f0b73efe63c..aeb7d108edc 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -1741,6 +1741,238 @@ static void draw_viewport_reconstruction(Scene *scene, Base *base, View3D *v3d, GPU_select_load_id(base->selcol); } +static void drawcamera_volume(float near_plane[4][3], float far_plane[4][3], const GLenum mode) +{ + glBegin(mode); + glVertex3fv(near_plane[0]); + glVertex3fv(far_plane[0]); + glVertex3fv(far_plane[1]); + glVertex3fv(near_plane[1]); + glEnd(); + + glBegin(mode); + glVertex3fv(near_plane[1]); + glVertex3fv(far_plane[1]); + glVertex3fv(far_plane[2]); + glVertex3fv(near_plane[2]); + glEnd(); + + glBegin(mode); + glVertex3fv(near_plane[2]); + glVertex3fv(near_plane[1]); + glVertex3fv(far_plane[1]); + glVertex3fv(far_plane[2]); + glEnd(); + + glBegin(mode); + glVertex3fv(far_plane[0]); + glVertex3fv(near_plane[0]); + glVertex3fv(near_plane[3]); + glVertex3fv(far_plane[3]); + glEnd(); +} + +/* camera frame */ +static void drawcamera_frame(float vec[4][3], const GLenum mode) +{ + glBegin(mode); + glVertex3fv(vec[0]); + glVertex3fv(vec[1]); + glVertex3fv(vec[2]); + glVertex3fv(vec[3]); + glEnd(); +} + +/* center point to camera frame */ +static void drawcamera_framelines(float vec[4][3], float origin[3]) +{ + glBegin(GL_LINE_STRIP); + glVertex3fv(vec[1]); + glVertex3fv(origin); + glVertex3fv(vec[0]); + glVertex3fv(vec[3]); + glVertex3fv(origin); + glVertex3fv(vec[2]); + glEnd(); +} + +static bool drawcamera_is_stereo3d(Scene *scene, View3D *v3d, Object *ob) +{ + return (ob == v3d->camera) && + (scene->r.scemode & R_MULTIVIEW) != 0 && + (v3d->stereo3d_flag); +} + +static void drawcamera_stereo3d( + Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, const Camera *cam, + float vec[4][3], float drawsize, const float scale[3]) +{ + int i, j; + float obmat[4][4]; + float vec_lr[2][4][3]; + const float fac = (cam->stereo.pivot == CAM_S3D_PIVOT_CENTER) ? 2.0f : 1.0f; + float origin[2][3] = {{0}}; + float tvec[3]; + const Camera *cam_lr[2]; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + const bool is_stereo3d_cameras = (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS) && (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); + const bool is_stereo3d_plane = (v3d->stereo3d_flag & V3D_S3D_DISPPLANE) && (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); + const bool is_stereo3d_volume = (v3d->stereo3d_flag & V3D_S3D_DISPVOLUME); + + zero_v3(tvec); + + glPushMatrix(); + + for (i = 0; i < 2; i++) { + ob = BKE_camera_multiview_render(scene, ob, names[i]); + cam_lr[i] = ob->data; + + glLoadMatrixf(rv3d->viewmat); + BKE_camera_multiview_model_matrix(&scene->r, ob, names[i], obmat); + glMultMatrixf(obmat); + + copy_m3_m3(vec_lr[i], vec); + copy_v3_v3(vec_lr[i][3], vec[3]); + + if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) { + const float shift_x = + ((BKE_camera_multiview_shift_x(&scene->r, ob, names[i]) - cam->shiftx) * + (drawsize * scale[0] * fac)); + + for (j = 0; j < 4; j++) { + vec_lr[i][j][0] += shift_x; + } + } + + if (is_stereo3d_cameras) { + /* camera frame */ + drawcamera_frame(vec_lr[i], GL_LINE_LOOP); + + /* center point to camera frame */ + drawcamera_framelines(vec_lr[i], tvec); + } + + /* connecting line */ + mul_m4_v3(obmat, origin[i]); + + /* convergence plane */ + if (is_stereo3d_plane || is_stereo3d_volume) { + for (j = 0; j < 4; j++) { + mul_m4_v3(obmat, vec_lr[i][j]); + } + } + } + + + /* the remaining drawing takes place in the view space */ + glLoadMatrixf(rv3d->viewmat); + + if (is_stereo3d_cameras) { + /* draw connecting lines */ + glPushAttrib(GL_ENABLE_BIT); + + glLineStipple(2, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + + glBegin(GL_LINES); + glVertex3fv(origin[0]); + glVertex3fv(origin[1]); + glEnd(); + glPopAttrib(); + } + + /* draw convergence plane*/ + if (is_stereo3d_plane) { + float axis_center[3], screen_center[3]; + float world_plane[4][3]; + float local_plane[4][3]; + float offset; + + mid_v3_v3v3(axis_center, origin[0], origin[1]); + + for (i = 0; i < 4; i++) { + mid_v3_v3v3(world_plane[i], vec_lr[0][i], vec_lr[1][i]); + sub_v3_v3v3(local_plane[i], world_plane[i], axis_center); + } + + mid_v3_v3v3(screen_center, world_plane[0], world_plane[2]); + offset = cam->stereo.convergence_distance / len_v3v3(screen_center, axis_center); + + for (i = 0; i < 4; i++) { + mul_v3_fl(local_plane[i], offset); + add_v3_v3(local_plane[i], axis_center); + } + + glColor3f(0.0f, 0.0f, 0.0f); + + /* camera frame */ + drawcamera_frame(local_plane, GL_LINE_LOOP); + + if (v3d->stereo3d_convergence_alpha > 0.0f) { + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + + glColor4f(0.0f, 0.0f, 0.0f, v3d->stereo3d_convergence_alpha); + + drawcamera_frame(local_plane, GL_QUADS); + + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } + } + + /* draw convergence plane*/ + if (is_stereo3d_volume) { + float screen_center[3]; + float near_plane[4][3], far_plane[4][3]; + float offset; + int j; + + for (i = 0; i < 2; i++) { + mid_v3_v3v3(screen_center, vec_lr[i][0], vec_lr[i][2]); + + offset = len_v3v3(screen_center, origin[i]); + + for (j = 0; j < 4; j++) { + sub_v3_v3v3(near_plane[j], vec_lr[i][j], origin[i]); + mul_v3_fl(near_plane[j], cam_lr[i]->clipsta / offset); + add_v3_v3(near_plane[j], origin[i]); + + sub_v3_v3v3(far_plane[j], vec_lr[i][j], origin[i]); + mul_v3_fl(far_plane[j], cam_lr[i]->clipend / offset); + add_v3_v3(far_plane[j], origin[i]); + } + + /* camera frame */ + glColor3f(0.0f, 0.0f, 0.0f); + + drawcamera_frame(near_plane, GL_LINE_LOOP); + drawcamera_frame(far_plane, GL_LINE_LOOP); + drawcamera_volume(near_plane, far_plane, GL_LINE_LOOP); + + if (v3d->stereo3d_volume_alpha > 0.0f) { + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + + if (i == 0) + glColor4f(0.0f, 1.0f, 1.0f, v3d->stereo3d_volume_alpha); + else + glColor4f(1.0f, 0.0f, 0.0f, v3d->stereo3d_volume_alpha); + + drawcamera_frame(near_plane, GL_QUADS); + drawcamera_frame(far_plane, GL_QUADS); + drawcamera_volume(near_plane, far_plane, GL_QUADS); + + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } + } + } + + glPopMatrix(); +} + /* flag similar to draw_object() */ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, const short dflag, const unsigned char ob_wire_col[4]) @@ -1755,6 +1987,12 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base const bool is_view = (rv3d->persp == RV3D_CAMOB && ob == v3d->camera); MovieClip *clip = BKE_object_movieclip_get(scene, base->object, false); + const bool is_stereo3d = drawcamera_is_stereo3d(scene, v3d, ob); + const bool is_stereo3d_cameras = (ob == scene->camera) && + (scene->r.scemode & R_MULTIVIEW) && + (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) && + (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS); + /* draw data for movie clip set as active for scene */ if (clip) { draw_viewport_reconstruction(scene, base, v3d, clip, dflag, ob_wire_col, false); @@ -1791,12 +2029,8 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base glDisable(GL_CULL_FACE); /* camera frame */ - glBegin(GL_LINE_LOOP); - glVertex3fv(vec[0]); - glVertex3fv(vec[1]); - glVertex3fv(vec[2]); - glVertex3fv(vec[3]); - glEnd(); + if (!is_stereo3d_cameras) + drawcamera_frame(vec, GL_LINE_LOOP); if (is_view) return; @@ -1804,20 +2038,12 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base zero_v3(tvec); /* center point to camera frame */ - glBegin(GL_LINE_STRIP); - glVertex3fv(vec[1]); - glVertex3fv(tvec); - glVertex3fv(vec[0]); - glVertex3fv(vec[3]); - glVertex3fv(tvec); - glVertex3fv(vec[2]); - glEnd(); - + if (!is_stereo3d_cameras) + drawcamera_framelines(vec, tvec); /* arrow on top */ tvec[2] = vec[1][2]; /* copy the depth */ - /* draw an outline arrow for inactive cameras and filled * for active cameras. We actually draw both outline+filled * for active cameras so the wire can be seen side-on */ @@ -1867,6 +2093,11 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base glPopMatrix(); } } + + /* stereo cameras drawing */ + if (is_stereo3d) { + drawcamera_stereo3d(scene, v3d, rv3d, ob, cam, vec, drawsize, scale); + } } /* flag similar to draw_object() */ diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 25b333f2b95..8558a90d5b7 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -354,7 +354,13 @@ static SpaceLink *view3d_new(const bContext *C) v3d->bundle_size = 0.2f; v3d->bundle_drawtype = OB_PLAINAXES; - + + /* stereo */ + v3d->stereo3d_camera = STEREO_3D_ID; + v3d->stereo3d_flag |= V3D_S3D_DISPPLANE; + v3d->stereo3d_convergence_alpha = 0.15f; + v3d->stereo3d_volume_alpha = 0.05f; + /* header */ ar = MEM_callocN(sizeof(ARegion), "header for view3d"); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 285c0645c37..b1fb7b907b8 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -50,6 +50,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_endian_switch.h" +#include "BLI_threads.h" #include "BKE_anim.h" #include "BKE_camera.h" @@ -100,6 +101,11 @@ #include "view3d_intern.h" /* own include */ +/* prototypes */ +static void view3d_stereo3d_setup(Scene *scene, View3D *v3d, ARegion *ar); +static void view3d_stereo3d_setup_offscreen(Scene *scene, View3D *v3d, ARegion *ar, + float winmat[4][4], const char *viewname); + /* handy utility for drawing shapes in the viewport for arbitrary code. * could add lines and points too */ // #define DEBUG_DRAW @@ -1482,14 +1488,15 @@ ImBuf *view3d_read_backbuf(ViewContext *vc, short xmin, short ymin, short xmax, short xminc, yminc, xmaxc, ymaxc, xs, ys; /* clip */ - if (xmin < 0) xminc = 0; else xminc = xmin; - if (xmax >= vc->ar->winx) xmaxc = vc->ar->winx - 1; else xmaxc = xmax; - if (xminc > xmaxc) return NULL; + xminc = max_ii(xmin, 0); + yminc = max_ii(ymin, 0); + xmaxc = min_ii(xmax, vc->ar->winx - 1); + ymaxc = min_ii(ymax, vc->ar->winy - 1); + + if (UNLIKELY((xminc > xmaxc) || (yminc > ymaxc))) { + return NULL; + } - if (ymin < 0) yminc = 0; else yminc = ymin; - if (ymax >= vc->ar->winy) ymaxc = vc->ar->winy - 1; else ymaxc = ymax; - if (yminc > ymaxc) return NULL; - ibuf = IMB_allocImBuf((xmaxc - xminc + 1), (ymaxc - yminc + 1), 32, IB_rect); view3d_validate_backbuf(vc); @@ -1602,6 +1609,24 @@ exit: /* ************************************************************* */ +static void view3d_stereo_bgpic_setup(Scene *scene, View3D *v3d, Image *ima, ImageUser *iuser) +{ + if ((ima->flag & IMA_IS_STEREO)) { + iuser->flag |= IMA_SHOW_STEREO; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + iuser->multiview_eye = STEREO_LEFT_ID; + + /* show only left or right camera */ + else if (v3d->stereo3d_camera != STEREO_3D_ID) + iuser->multiview_eye = v3d->stereo3d_camera; + + BKE_image_multiview_index(ima, iuser); + } + else + iuser->flag &= ~IMA_SHOW_STEREO; +} + static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, const bool do_foreground, const bool do_camera_frame) { @@ -1645,6 +1670,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, ibuf = NULL; /* frame is out of range, dont show */ } else { + view3d_stereo_bgpic_setup(scene, v3d, ima, &bgpic->iuser); ibuf = BKE_image_acquire_ibuf(ima, &bgpic->iuser, &lock); releaseibuf = ibuf; } @@ -2127,7 +2153,7 @@ static void draw_dupli_objects_color( lb = object_duplilist(G.main->eval_ctx, scene, base->object); // BLI_listbase_sort(lb, dupli_ob_sort); /* might be nice to have if we have a dupli list with mixed objects. */ - apply_data = duplilist_apply(base->object, lb); + apply_data = duplilist_apply(base->object, scene, lb); dob = dupli_step(lb->first); if (dob) dob_next = dupli_step(dob->next); @@ -2672,7 +2698,7 @@ static void gpu_update_lamps_shadows_world(Scene *scene, View3D *v3d) ED_view3d_draw_offscreen( scene, v3d, &ar, winsize, winsize, viewmat, winmat, false, false, true, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); GPU_lamp_shadow_buffer_unbind(shadow->lamp); v3d->drawtype = drawtype; @@ -3250,7 +3276,8 @@ void ED_view3d_draw_offscreen( float viewmat[4][4], float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp, GPUOffScreen *ofs, - GPUFX *fx, GPUFXSettings *fx_settings) + GPUFX *fx, GPUFXSettings *fx_settings, + const char *viewname) { struct bThemeState theme_state; int bwinx, bwiny; @@ -3285,7 +3312,10 @@ void ED_view3d_draw_offscreen( } /* setup view matrices before fx or unbinding the offscreen buffers will cause issues */ - view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + if ((viewname != NULL && viewname[0] != '\0') && (viewmat == NULL)) + view3d_stereo3d_setup_offscreen(scene, v3d, ar, winmat, viewname); + else + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); /* framebuffer fx needed, we need to draw offscreen first */ if (v3d->fx_settings.fx_flag && fx) { @@ -3349,7 +3379,7 @@ void ED_view3d_draw_offscreen( /* utility func for ED_view3d_draw_offscreen */ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, int sizex, int sizey, unsigned int flag, - bool draw_background, int alpha_mode, char err_out[256]) + bool draw_background, int alpha_mode, const char *viewname, char err_out[256]) { RegionView3D *rv3d = ar->regiondata; ImBuf *ibuf; @@ -3374,13 +3404,14 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in if (rv3d->persp == RV3D_CAMOB && v3d->camera) { CameraParams params; GPUFXSettings fx_settings = {NULL}; - Object *camera = v3d->camera; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); BKE_camera_params_init(¶ms); /* fallback for non camera objects */ params.clipsta = v3d->near; params.clipend = v3d->far; - BKE_camera_params_from_object(¶ms, v3d->camera); + BKE_camera_params_from_object(¶ms, camera); + BKE_camera_multiview_params(&scene->r, ¶ms, camera, viewname); BKE_camera_params_compute_viewplane(¶ms, sizex, sizey, scene->r.xasp, scene->r.yasp); BKE_camera_params_compute_matrix(¶ms); @@ -3389,13 +3420,13 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, params.winmat, draw_background, draw_sky, !params.is_ortho, - ofs, NULL, &fx_settings); + ofs, NULL, &fx_settings, viewname); } else { ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, NULL, draw_background, draw_sky, true, - ofs, NULL, NULL); + ofs, NULL, NULL, viewname); } /* read in pixels & stamp */ @@ -3420,7 +3451,8 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in /* creates own 3d views, used by the sequencer */ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int width, int height, unsigned int flag, int drawtype, - bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256]) + bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, + const char *viewname, char err_out[256]) { View3D v3d = {NULL}; ARegion ar = {NULL}; @@ -3450,9 +3482,11 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w { CameraParams params; + Object *camera = BKE_camera_multiview_render(scene, v3d.camera, viewname); BKE_camera_params_init(¶ms); - BKE_camera_params_from_object(¶ms, v3d.camera); + BKE_camera_params_from_object(¶ms, camera); + BKE_camera_multiview_params(&scene->r, ¶ms, camera, viewname); BKE_camera_params_compute_viewplane(¶ms, width, height, scene->r.xasp, scene->r.yasp); BKE_camera_params_compute_matrix(¶ms); @@ -3466,7 +3500,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w invert_m4_m4(rv3d.persinv, rv3d.viewinv); return ED_view3d_draw_offscreen_imbuf(scene, &v3d, &ar, width, height, flag, - draw_background, alpha_mode, err_out); + draw_background, alpha_mode, viewname, err_out); // seq_view3d_cb(scene, cfra, render_size, seqrectx, seqrecty); } @@ -3671,6 +3705,103 @@ static void view3d_main_area_draw_engine_info(View3D *v3d, RegionView3D *rv3d, A ED_region_info_draw(ar, rv3d->render_engine->text, 1, fill_color); } +static bool view3d_stereo3d_active(const bContext *C, Scene *scene, View3D *v3d, RegionView3D *rv3d) +{ + wmWindow *win = CTX_wm_window(C); + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return false; + + if (WM_stereo3d_enabled(win, true) == false) + return false; + + if ((v3d->camera == NULL) || rv3d->persp != RV3D_CAMOB) + return false; + + if (scene->r.views_format & SCE_VIEWS_FORMAT_MULTIVIEW) { + if (v3d->stereo3d_camera == STEREO_MONO_ID) + return false; + + return BKE_scene_multiview_is_stereo3d(&scene->r); + } + + return true; +} + +/* setup the view and win matrices for the multiview cameras + * + * unlike view3d_stereo3d_setup_offscreen, when view3d_stereo3d_setup is called + * we have no winmatrix (i.e., projection matrix) defined at that time. + * Since the camera and the camera shift are needed for the winmat calculation + * we do a small hack to replace it temporarily so we don't need to change the + * view3d)main_area_setup_view() code to account for that. + */ +static void view3d_stereo3d_setup(Scene *scene, View3D *v3d, ARegion *ar) +{ + bool is_left; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + const char *viewname; + + /* show only left or right camera */ + if (v3d->stereo3d_camera != STEREO_3D_ID) + v3d->multiview_eye = v3d->stereo3d_camera; + + is_left = v3d->multiview_eye == STEREO_LEFT_ID; + viewname = names[is_left ? STEREO_LEFT_ID : STEREO_RIGHT_ID]; + + /* update the viewport matrices with the new camera */ + if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + Camera *data; + float viewmat[4][4]; + float shiftx; + + data = (Camera *)v3d->camera->data; + shiftx = data->shiftx; + + BLI_lock_thread(LOCK_VIEW3D); + data->shiftx = BKE_camera_multiview_shift_x(&scene->r, v3d->camera, viewname); + + BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, NULL); + + data->shiftx = shiftx; + BLI_unlock_thread(LOCK_VIEW3D); + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + float viewmat[4][4]; + Object *view_ob = v3d->camera; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); + + BLI_lock_thread(LOCK_VIEW3D); + v3d->camera = camera; + + BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, NULL); + + v3d->camera = view_ob; + BLI_unlock_thread(LOCK_VIEW3D); + } +} + +static void view3d_stereo3d_setup_offscreen(Scene *scene, View3D *v3d, ARegion *ar, + float winmat[4][4], const char *viewname) +{ + /* update the viewport matrices with the new camera */ + if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + float viewmat[4][4]; + const bool is_left = STREQ(viewname, STEREO_LEFT_NAME); + + BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + float viewmat[4][4]; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); + + BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + } +} #ifdef WITH_GAMEENGINE static void update_lods(Scene *scene, float camera_pos[3]) @@ -3705,8 +3836,11 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 GPU_default_lights(); } - /* setup view matrices */ - view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL); + /* setup the view matrix */ + if (view3d_stereo3d_active(C, scene, v3d, rv3d)) + view3d_stereo3d_setup(scene, v3d, ar); + else + view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL); rv3d->rflag &= ~RV3D_IS_GAME_ENGINE; #ifdef WITH_GAMEENGINE diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 785d0e60c4c..3fc90c97678 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4701,16 +4701,24 @@ bool ED_view3d_autodist(Scene *scene, ARegion *ar, View3D *v3d, bglMats mats; /* ZBuffer depth vars */ float depth_close; double cent[2], p[3]; + int margin_arr[] = {0, 2, 4}; + int i; + bool depth_ok = false; /* Get Z Depths, needed for perspective, nice for ortho */ bgl_get_mats(&mats); ED_view3d_draw_depth(scene, ar, v3d, alphaoverride); - depth_close = view_autodist_depth_margin(ar, mval, 4); + /* Attempt with low margin's first */ + i = 0; + do { + depth_close = view_autodist_depth_margin(ar, mval, margin_arr[i++] * U.pixelsize); + depth_ok = (depth_close != FLT_MAX); + } while ((depth_ok == false) && (i < ARRAY_SIZE(margin_arr))); - if (depth_close != FLT_MAX) { - cent[0] = (double)mval[0]; - cent[1] = (double)mval[1]; + if (depth_ok) { + cent[0] = (double)mval[0] + 0.5; + cent[1] = (double)mval[1] + 0.5; if (gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2])) @@ -4761,8 +4769,8 @@ bool ED_view3d_autodist_simple(ARegion *ar, const int mval[2], float mouse_world if (depth == FLT_MAX) return false; - cent[0] = (double)mval[0]; - cent[1] = (double)mval[1]; + cent[0] = (double)mval[0] + 0.5; + cent[1] = (double)mval[1] + 0.5; bgl_get_mats(&mats); diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index da77c4f75f7..a3dfd62ba6b 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -806,7 +806,7 @@ static int flyApply(bContext *C, FlyInfo *fly) copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f); mul_m3_v3(mat, upvec); /* Rotate about the relative up vec */ - axis_angle_to_quat(tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC); + axis_angle_to_quat(tmp_quat, upvec, moffset[1] * time_redraw * -FLY_ROTATE_FAC); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); if (fly->xlock != FLY_AXISLOCK_STATE_OFF) @@ -836,7 +836,7 @@ static int flyApply(bContext *C, FlyInfo *fly) } /* Rotate about the relative up vec */ - axis_angle_to_quat(tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); + axis_angle_to_quat(tmp_quat, upvec, moffset[0] * time_redraw * FLY_ROTATE_FAC); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); if (fly->xlock != FLY_AXISLOCK_STATE_OFF) diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index ac397506438..d1b50b1579f 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -236,8 +236,9 @@ void view3d_keymap(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "VIEW3D_OT_navigate", FKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_verify_item(keymap, "VIEW3D_OT_smoothview", TIMER1, KM_ANY, KM_ANY, 0); - + /* value is set to KM_NOTHING to avoid conflicts with click type (see T44251) */ + WM_keymap_verify_item(keymap, "VIEW3D_OT_smoothview", TIMER1, KM_NOTHING, KM_ANY, 0); + WM_keymap_add_item(keymap, "VIEW3D_OT_rotate", MOUSEPAN, 0, 0, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_rotate", MOUSEROTATE, 0, 0, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_move", MOUSEPAN, 0, KM_SHIFT, 0); @@ -386,7 +387,7 @@ void view3d_keymap(wmKeyConfig *keyconf) /* drawtype */ - kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_CLICK, 0, 0); RNA_string_set(kmi->ptr, "data_path", "space_data.viewport_shade"); RNA_string_set(kmi->ptr, "value_1", "SOLID"); RNA_string_set(kmi->ptr, "value_2", "WIREFRAME"); diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 191eeb05c71..11756e4feca 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -220,8 +220,7 @@ void walk_modal_keymap(wmKeyConfig *keyconf) } -typedef struct WalkTeleport -{ +typedef struct WalkTeleport { eWalkTeleportState state; float duration; /* from user preferences */ float origin[3]; diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 72be301480e..801e360a288 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -866,10 +866,10 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items); /* items for modal map */ - WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL); - WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, ESCKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, TFM_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, RETKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, PADENTER, KM_RELEASE, KM_ANY, 0, TFM_MODAL_CONFIRM); WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE); WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE); @@ -5300,9 +5300,10 @@ static void slide_origdata_create_data_vert( loop_weights = BLI_array_alloca(loop_weights, l_num); for (j = 0; j < l_num; j++) { BMLoop *l = BM_iter_step(&liter); - if (!BLI_ghash_haskey(sod->origfaces, l->f)) { + void **val_p; + if (!BLI_ghash_ensure_p(sod->origfaces, l->f, &val_p)) { BMFace *f_copy = BM_face_copy(sod->bm_origfaces, bm, l->f, true, true); - BLI_ghash_insert(sod->origfaces, l->f, f_copy); + *val_p = f_copy; } loop_weights[j] = BM_loop_calc_face_angle(l); } diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index d1285cf2c18..6b102410aa6 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -494,13 +494,14 @@ void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_r return; rl = render_get_active_layer( freestyle_render, freestyle_render->result ); - if (!rl || rl->rectf == NULL) { + if (!rl) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "No Freestyle result image to composite" << endl; } return; } - src = rl->rectf; + + src = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); #if 0 if (G.debug & G_DEBUG_FREESTYLE) { cout << "src: " << rl->rectx << " x " << rl->recty << endl; @@ -508,13 +509,13 @@ void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_r #endif rl = RE_GetRenderLayer(re->result, srl->name); - if (!rl || rl->rectf == NULL) { + if (!rl || src == NULL) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "No layer to composite to" << endl; } return; } - dest = rl->rectf; + dest = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); #if 0 if (G.debug & G_DEBUG_FREESTYLE) { cout << "dest: " << rl->rectx << " x " << rl->recty << endl; diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h index cc1a63d6256..20aa4919416 100644 --- a/source/blender/gpu/GPU_draw.h +++ b/source/blender/gpu/GPU_draw.h @@ -121,7 +121,7 @@ void GPU_set_gpu_mipmapping(int gpu_mipmap); /* Image updates and free * - these deal with images bound as opengl textures */ -void GPU_paint_update_image(struct Image *ima, int x, int y, int w, int h); +void GPU_paint_update_image(struct Image *ima, ImageUser *iuser, int x, int y, int w, int h); void GPU_update_images_framechange(void); int GPU_update_image_time(struct Image *ima, double time); int GPU_verify_image(struct Image *ima, struct ImageUser *iuser, int tftile, bool compare, bool mipmap, bool is_data); diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 7f3a9981a5c..daf6b90f604 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -1040,11 +1040,11 @@ static bool GPU_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, return false; } -void GPU_paint_update_image(Image *ima, int x, int y, int w, int h) +void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h) { ImBuf *ibuf; - ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if (ima->repbind || (GPU_get_mipmap() && !GTS.gpu_mipmap) || !ima->bindcode || !ibuf || (w == 0) || (h == 0)) diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index afff97a8409..d4814a4e3a2 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -89,8 +89,7 @@ struct IK_Target; typedef void (*ErrorCallback)(const iTaSC::ConstraintValues *values, unsigned int nvalues, IK_Target *iktarget); // one structure for each target in the scene -struct IK_Target -{ +struct IK_Target { struct Scene *blscene; iTaSC::MovingFrame* target; iTaSC::ConstraintSet* constraint; diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index e8977913948..eadedbd5e00 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC intern/rectop.c intern/rotate.c intern/scaling.c + intern/stereoimbuf.c intern/targa.c intern/thumbs.c intern/thumbs_blend.c diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 18682302b3a..a39832dd1cb 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -89,6 +89,13 @@ struct ColorManagedDisplay; struct GSet; /** * + * \attention defined in DNA_scene_types.h + */ +struct ImageFormatData; +struct Stereo3dFormat; + +/** + * * \attention Defined in allocimbuf.c */ void IMB_init(void); @@ -279,10 +286,10 @@ bool IMB_anim_get_fps(struct anim *anim, * \attention Defined in anim_movie.c */ struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE]); +void IMB_suffix_anim(struct anim *anim, const char *suffix); void IMB_close_anim(struct anim *anim); void IMB_close_anim_proxies(struct anim *anim); - /** * * \attention Defined in anim_movie.c @@ -377,6 +384,7 @@ void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int * \attention Defined in writeimage.c */ short IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags); +struct ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, struct ImBuf *ibuf); /** * @@ -399,6 +407,12 @@ int imb_get_anim_type(const char *name); /** * + * \attention Defined in util.c + */ +bool IMB_isfloat(struct ImBuf *ibuf); + +/** + * * \attention Defined in divers.c */ void IMB_de_interlace(struct ImBuf *ibuf); @@ -557,5 +571,27 @@ void IMB_processor_apply_threaded(int buffer_lines, int handle_size, void *init_ void IMB_ffmpeg_init(void); const char *IMB_ffmpeg_last_error(void); -#endif +/** + * + * \attention defined in stereoimbuf.c + */ +void IMB_stereo3d_write_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height); +void IMB_stereo3d_read_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height); +int *IMB_stereo3d_from_rect( + struct ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right); +float *IMB_stereo3d_from_rectf( + struct ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + float *rectf_left, float *rectf_right); +struct ImBuf *IMB_stereo3d_ImBuf( + struct ImageFormatData *im_format, + struct ImBuf *ibuf_left, struct ImBuf *ibuf_right); +void IMB_ImBufFromStereo3d( + struct Stereo3dFormat *s3d, struct ImBuf *ibuf_stereo, + struct ImBuf **r_ibuf_left, struct ImBuf **r_ibuf_right); +#endif diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 9e7201f53b4..862c587f75b 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -110,7 +110,7 @@ typedef struct ImBuf { int index; /* reference index for ImBuf lists */ int userflags; /* used to set imbuf to dirty and other stuff */ struct IDProperty *metadata; /* image metadata */ - void *userdata; /* temporary storage, only used by baking at the moment */ + void *userdata; /* temporary storage */ /* file information */ int ftype; /* file type we are going to save as */ @@ -172,6 +172,8 @@ typedef struct ImBuf { #define IB_alphamode_premul (1 << 12) /* indicates whether image on disk have premul alpha */ #define IB_alphamode_detect (1 << 13) /* if this flag is set, alpha mode would be guessed from file */ #define IB_ignore_alpha (1 << 14) /* ignore alpha on load and substitude it with 1.0f */ +#define IB_thumbnail (1 << 15) +#define IB_multiview (1 << 16) /* * The bit flag is stored in the ImBuf.ftype variable. diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index ed349e8f7eb..1fc43e22f68 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -194,6 +194,7 @@ struct anim { struct anim_index *curr_idx[IMB_TC_MAX_SLOT]; char colorspace[64]; + char suffix[64]; /* MAX_NAME - multiview */ }; #endif diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index ffdecb793aa..0bb9f0fce51 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -288,6 +288,11 @@ struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char return(anim); } +void IMB_suffix_anim(struct anim *anim, const char *suffix) +{ + BLI_strncpy(anim->suffix, suffix, sizeof(anim->suffix)); +} + #ifdef WITH_AVI static int startavi(struct anim *anim) { diff --git a/source/blender/imbuf/intern/cineon/logImageCore.h b/source/blender/imbuf/intern/cineon/logImageCore.h index 6b1435817d2..389e88a24de 100644 --- a/source/blender/imbuf/intern/cineon/logImageCore.h +++ b/source/blender/imbuf/intern/cineon/logImageCore.h @@ -52,8 +52,7 @@ enum format { format_Cineon }; -typedef struct LogImageElement -{ +typedef struct LogImageElement { int depth; int bitsPerSample; int dataOffset; @@ -67,8 +66,7 @@ typedef struct LogImageElement float maxValue; /* = 2^bitsPerSample - 1 (used internally, doesn't come from the file header) */ } LogImageElement; -typedef struct LogImageFile -{ +typedef struct LogImageFile { /* specified in header */ int width; int height; diff --git a/source/blender/imbuf/intern/dds/BlockDXT.h b/source/blender/imbuf/intern/dds/BlockDXT.h index 7e5a1e504b8..6aae9c9817c 100644 --- a/source/blender/imbuf/intern/dds/BlockDXT.h +++ b/source/blender/imbuf/intern/dds/BlockDXT.h @@ -64,8 +64,7 @@ #include <Stream.h> /// DXT1 block. -struct BlockDXT1 -{ +struct BlockDXT1 { Color16 col0; Color16 col1; union { @@ -98,8 +97,7 @@ inline bool BlockDXT1::isFourColorMode() const /// DXT3 alpha block with explicit alpha. -struct AlphaBlockDXT3 -{ +struct AlphaBlockDXT3 { union { struct { uint alpha0 : 4; @@ -130,8 +128,7 @@ struct AlphaBlockDXT3 /// DXT3 block. -struct BlockDXT3 -{ +struct BlockDXT3 { AlphaBlockDXT3 alpha; BlockDXT1 color; @@ -144,8 +141,7 @@ struct BlockDXT3 /// DXT5 alpha block. -struct AlphaBlockDXT5 -{ +struct AlphaBlockDXT5 { // uint64 unions do not compile on all platforms #if 0 union { @@ -208,8 +204,7 @@ struct AlphaBlockDXT5 /// DXT5 block. -struct BlockDXT5 -{ +struct BlockDXT5 { AlphaBlockDXT5 alpha; BlockDXT1 color; @@ -221,8 +216,7 @@ struct BlockDXT5 }; /// ATI1 block. -struct BlockATI1 -{ +struct BlockATI1 { AlphaBlockDXT5 alpha; void decodeBlock(ColorBlock * block) const; @@ -232,8 +226,7 @@ struct BlockATI1 }; /// ATI2 block. -struct BlockATI2 -{ +struct BlockATI2 { AlphaBlockDXT5 x; AlphaBlockDXT5 y; @@ -244,8 +237,7 @@ struct BlockATI2 }; /// CTX1 block. -struct BlockCTX1 -{ +struct BlockCTX1 { uint8 col0[2]; uint8 col1[2]; union { diff --git a/source/blender/imbuf/intern/dds/ColorBlock.h b/source/blender/imbuf/intern/dds/ColorBlock.h index 730a19d84fd..8d5031aa603 100644 --- a/source/blender/imbuf/intern/dds/ColorBlock.h +++ b/source/blender/imbuf/intern/dds/ColorBlock.h @@ -41,8 +41,7 @@ #include <Image.h> /// Uncompressed 4x4 color block. -struct ColorBlock -{ +struct ColorBlock { ColorBlock(); ColorBlock(const uint * linearImage); ColorBlock(const ColorBlock & block); diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp index 028026527dc..15c8d86d61c 100644 --- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp +++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp @@ -496,8 +496,7 @@ void mem_read(Stream & mem, DDSHeader & header) namespace { -struct FormatDescriptor -{ +struct FormatDescriptor { uint format; uint bitcount; uint rmask; diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.h b/source/blender/imbuf/intern/dds/DirectDrawSurface.h index 3d308ba1ff1..44c27a98c1d 100644 --- a/source/blender/imbuf/intern/dds/DirectDrawSurface.h +++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.h @@ -63,8 +63,7 @@ #include <ColorBlock.h> #include <Image.h> -struct DDSPixelFormat -{ +struct DDSPixelFormat { uint size; uint flags; uint fourcc; @@ -75,8 +74,7 @@ struct DDSPixelFormat uint amask; }; -struct DDSCaps -{ +struct DDSCaps { uint caps1; uint caps2; uint caps3; @@ -84,8 +82,7 @@ struct DDSCaps }; /// DDS file header for DX10. -struct DDSHeader10 -{ +struct DDSHeader10 { uint dxgiFormat; uint resourceDimension; uint miscFlag; @@ -94,8 +91,7 @@ struct DDSHeader10 }; /// DDS file header. -struct DDSHeader -{ +struct DDSHeader { uint fourcc; uint size; uint flags; diff --git a/source/blender/imbuf/intern/dds/Stream.h b/source/blender/imbuf/intern/dds/Stream.h index a1ac49b58da..6557fb4f063 100644 --- a/source/blender/imbuf/intern/dds/Stream.h +++ b/source/blender/imbuf/intern/dds/Stream.h @@ -30,8 +30,7 @@ #ifndef __STREAM_H__ #define __STREAM_H__ -struct Stream -{ +struct Stream { unsigned char *mem; // location in memory unsigned int size; // size unsigned int pos; // current position diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 183159f7d6c..150ea0995ac 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -398,7 +398,7 @@ static void get_proxy_filename(struct anim *anim, IMB_Proxy_Size preview_size, } BLI_snprintf(proxy_name, sizeof(proxy_name), name, - (int) (proxy_fac[i] * 100), stream_suffix); + (int) (proxy_fac[i] * 100), stream_suffix, anim->suffix); get_index_dir(anim, index_dir, sizeof(index_dir)); @@ -411,10 +411,10 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char index_dir[FILE_MAXDIR]; int i = IMB_timecode_to_array_index(tc); const char *index_names[] = { - "record_run%s.blen_tc", - "free_run%s.blen_tc", - "interp_free_run%s.blen_tc", - "record_run_no_gaps%s.blen_tc" + "record_run%s%s.blen_tc", + "free_run%s%s.blen_tc", + "interp_free_run%s%s.blen_tc", + "record_run_no_gaps%s%s.blen_tc" }; char stream_suffix[20]; @@ -426,7 +426,7 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex); } - BLI_snprintf(index_name, 256, index_names[i], stream_suffix); + BLI_snprintf(index_name, 256, index_names[i], stream_suffix, anim->suffix); get_index_dir(anim, index_dir, sizeof(index_dir)); diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 45eae89ad9d..1262f0af95d 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -92,9 +92,32 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #include <ImfStringAttribute.h> #include <ImfStandardAttributes.h> +/* multiview/multipart */ +#include <ImfMultiView.h> +#include <ImfMultiPartInputFile.h> +#include <ImfInputPart.h> +#include <ImfOutputPart.h> +#include <ImfMultiPartOutputFile.h> +#include <ImfTiledOutputPart.h> +#include <ImfPartType.h> +#include <ImfPartHelper.h> + using namespace Imf; using namespace Imath; +extern "C" +{ +/* prototype */ +static struct ExrPass *imb_exr_get_pass(ListBase *lb, char *passname); +static bool exr_has_multiview(MultiPartInputFile& file); +static bool exr_has_multipart_file(MultiPartInputFile& file); +static int exr_has_alpha(MultiPartInputFile& file); +static int exr_has_zbuffer(MultiPartInputFile& file); +static void exr_printf(const char *__restrict format, ...); +static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views, + bool *r_singlelayer, bool *r_multilayer, bool *r_multiview); +} + /* Memory Input Stream */ class Mem_IStream : public Imf::IStream @@ -340,13 +363,21 @@ static void openexr_header_metadata(Header *header, struct ImBuf *ibuf) addXDensity(*header, ibuf->ppm[0] / 39.3700787); /* 1 meter = 39.3700787 inches */ } -static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags) +static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) { const int channels = ibuf->channels; const int is_alpha = (channels >= 4) && (ibuf->planes == 32); const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */ const int width = ibuf->x; const int height = ibuf->y; + const bool is_multiview = (flags & IB_multiview) && ibuf->userdata; + + BLI_assert((!is_multiview) || (getview && getbuffer)); + + std::vector <string> views; + size_t view_id; try { @@ -355,13 +386,22 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS); openexr_header_metadata(&header, ibuf); - header.channels().insert("R", Channel(HALF)); - header.channels().insert("G", Channel(HALF)); - header.channels().insert("B", Channel(HALF)); - if (is_alpha) - header.channels().insert("A", Channel(HALF)); - if (is_zbuf) // z we do as float always - header.channels().insert("Z", Channel(Imf::FLOAT)); + /* create views when possible */ + for (view_id = 0; view_id < totviews; view_id ++) + views.push_back(is_multiview ? getview(ibuf->userdata, view_id) : ""); + + if (is_multiview) + addMultiView(header, views); + + for (view_id = 0; view_id < totviews; view_id ++) { + header.channels().insert(insertViewName("R", views, view_id), Channel(HALF)); + header.channels().insert(insertViewName("G", views, view_id), Channel(HALF)); + header.channels().insert(insertViewName("B", views, view_id), Channel(HALF)); + if (is_alpha) + header.channels().insert(insertViewName("A", views, view_id), Channel(HALF)); + if (is_zbuf) // z we do as float always + header.channels().insert(insertViewName("Z", views, view_id), Channel(Imf::FLOAT)); + } FrameBuffer frameBuffer; @@ -370,75 +410,91 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags OutputFile file(file_stream, header); /* we store first everything in half array */ - RGBAZ *pixels = new RGBAZ[height * width]; - RGBAZ *to = pixels; + RGBAZ *pixels = new RGBAZ[height * width * totviews]; int xstride = sizeof(RGBAZ); int ystride = xstride * width; - /* indicate used buffers */ - frameBuffer.insert("R", Slice(HALF, (char *) &pixels[0].r, xstride, ystride)); - frameBuffer.insert("G", Slice(HALF, (char *) &pixels[0].g, xstride, ystride)); - frameBuffer.insert("B", Slice(HALF, (char *) &pixels[0].b, xstride, ystride)); - if (is_alpha) - frameBuffer.insert("A", Slice(HALF, (char *) &pixels[0].a, xstride, ystride)); - if (is_zbuf) - frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)(ibuf->zbuf_float + (height - 1) * width), - sizeof(float), sizeof(float) * -width)); - if (ibuf->rect_float) { - float *from; - - for (int i = ibuf->y - 1; i >= 0; i--) { - from = ibuf->rect_float + channels * i * width; - - for (int j = ibuf->x; j > 0; j--) { - to->r = from[0]; - to->g = (channels >= 2) ? from[1] : from[0]; - to->b = (channels >= 3) ? from[2] : from[0]; - to->a = (channels >= 4) ? from[3] : 1.0f; - to++; from += channels; + for (view_id = 0; view_id < totviews; view_id ++) { + ImBuf *view_ibuf = is_multiview ? getbuffer(ibuf->userdata, view_id) : ibuf; + const size_t offset = view_id * width * height; + RGBAZ *to = pixels + offset; + + /* indicate used buffers */ + frameBuffer.insert(insertViewName("R", views, view_id), Slice(HALF, (char *) &pixels[offset].r, xstride, ystride)); + frameBuffer.insert(insertViewName("G", views, view_id), Slice(HALF, (char *) &pixels[offset].g, xstride, ystride)); + frameBuffer.insert(insertViewName("B", views, view_id), Slice(HALF, (char *) &pixels[offset].b, xstride, ystride)); + if (is_alpha) + frameBuffer.insert(insertViewName("A", views, view_id), Slice(HALF, (char *) &pixels[offset].a, xstride, ystride)); + if (is_zbuf) + frameBuffer.insert(insertViewName("Z", views, view_id), Slice(Imf::FLOAT, (char *)(view_ibuf->zbuf_float + (height - 1) * width), + sizeof(float), sizeof(float) * -width)); + if (view_ibuf->rect_float) { + float *from; + + for (int i = view_ibuf->y - 1; i >= 0; i--) { + from = view_ibuf->rect_float + channels * i * width; + + for (int j = view_ibuf->x; j > 0; j--) { + to->r = from[0]; + to->g = (channels >= 2) ? from[1] : from[0]; + to->b = (channels >= 3) ? from[2] : from[0]; + to->a = (channels >= 4) ? from[3] : 1.0f; + to++; from += channels; + } } } - } - else { - unsigned char *from; - - for (int i = ibuf->y - 1; i >= 0; i--) { - from = (unsigned char *)ibuf->rect + 4 * i * width; - - for (int j = ibuf->x; j > 0; j--) { - to->r = srgb_to_linearrgb((float)from[0] / 255.0f); - to->g = srgb_to_linearrgb((float)from[1] / 255.0f); - to->b = srgb_to_linearrgb((float)from[2] / 255.0f); - to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f; - to++; from += 4; + else { + unsigned char *from; + + for (int i = view_ibuf->y - 1; i >= 0; i--) { + from = (unsigned char *)view_ibuf->rect + 4 * i * width; + + for (int j = view_ibuf->x; j > 0; j--) { + to->r = srgb_to_linearrgb((float)from[0] / 255.0f); + to->g = srgb_to_linearrgb((float)from[1] / 255.0f); + to->b = srgb_to_linearrgb((float)from[2] / 255.0f); + to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f; + to++; from += 4; + } } } + + if (is_multiview) + IMB_freeImBuf(view_ibuf); } -// printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height); + exr_printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height); file.setFrameBuffer(frameBuffer); file.writePixels(height); delete[] pixels; } - catch (const std::exception &exc) + catch (const std::exception& exc) { printf("OpenEXR-save: ERROR: %s\n", exc.what()); - return (0); + return false; } - return (1); + return true; } -static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flags) +static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, const size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) { const int channels = ibuf->channels; const int is_alpha = (channels >= 4) && (ibuf->planes == 32); const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */ const int width = ibuf->x; const int height = ibuf->y; + const bool is_multiview = (flags & IB_multiview) && ibuf->userdata; + + BLI_assert((!is_multiview) || (getview && getbuffer)); + + std::vector <string> views; + size_t view_id; try { @@ -447,13 +503,22 @@ static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flag openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS); openexr_header_metadata(&header, ibuf); - header.channels().insert("R", Channel(Imf::FLOAT)); - header.channels().insert("G", Channel(Imf::FLOAT)); - header.channels().insert("B", Channel(Imf::FLOAT)); - if (is_alpha) - header.channels().insert("A", Channel(Imf::FLOAT)); - if (is_zbuf) - header.channels().insert("Z", Channel(Imf::FLOAT)); + /* create views when possible */ + for (view_id = 0; view_id < totviews; view_id ++) + views.push_back(is_multiview ? getview(ibuf->userdata, view_id) : ""); + + if (is_multiview) + addMultiView(header, views); + + for (view_id = 0; view_id < totviews; view_id ++) { + header.channels().insert(insertViewName("R", views, view_id), Channel(Imf::FLOAT)); + header.channels().insert(insertViewName("G", views, view_id), Channel(Imf::FLOAT)); + header.channels().insert(insertViewName("B", views, view_id), Channel(Imf::FLOAT)); + if (is_alpha) + header.channels().insert(insertViewName("A", views, view_id), Channel(Imf::FLOAT)); + if (is_zbuf) + header.channels().insert(insertViewName("Z", views, view_id), Channel(Imf::FLOAT)); + } FrameBuffer frameBuffer; @@ -463,37 +528,41 @@ static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flag int xstride = sizeof(float) * channels; int ystride = -xstride * width; - float *rect[4] = {NULL, NULL, NULL, NULL}; - /* last scanline, stride negative */ - rect[0] = ibuf->rect_float + channels * (height - 1) * width; - rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0]; - rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0]; - rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */ - - frameBuffer.insert("R", Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride)); - frameBuffer.insert("G", Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride)); - frameBuffer.insert("B", Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride)); - if (is_alpha) - frameBuffer.insert("A", Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride)); - if (is_zbuf) - frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *) (ibuf->zbuf_float + (height - 1) * width), - sizeof(float), sizeof(float) * -width)); + for (view_id = 0; view_id < totviews; view_id ++) { + float *rect[4] = {NULL, NULL, NULL, NULL}; + ImBuf *view_ibuf = is_multiview ? getbuffer(ibuf->userdata, view_id) : ibuf; + + /* last scanline, stride negative */ + rect[0] = view_ibuf->rect_float + channels * (height - 1) * width; + rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0]; + rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0]; + rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */ + + frameBuffer.insert(insertViewName("R", views, view_id), Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride)); + frameBuffer.insert(insertViewName("G", views, view_id), Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride)); + frameBuffer.insert(insertViewName("B", views, view_id), Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride)); + if (is_alpha) + frameBuffer.insert(insertViewName("A", views, view_id), Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride)); + if (is_zbuf) + frameBuffer.insert(insertViewName("Z", views, view_id), Slice(Imf::FLOAT, (char *) (view_ibuf->zbuf_float + (height - 1) * width), + sizeof(float), sizeof(float) * -width)); + + if (is_multiview) + IMB_freeImBuf(view_ibuf); + } file.setFrameBuffer(frameBuffer); file.writePixels(height); } - catch (const std::exception &exc) + catch (const std::exception& exc) { printf("OpenEXR-save: ERROR: %s\n", exc.what()); - - return (0); + return false; } - return (1); - // printf("OpenEXR-save: Done.\n"); + return true; } - int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags) { if (flags & IB_mem) { @@ -504,16 +573,48 @@ int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags) } if (ibuf->ftype & OPENEXR_HALF) - return imb_save_openexr_half(ibuf, name, flags); + return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL); + else { + /* when no float rect, we save as half (16 bits is sufficient) */ + if (ibuf->rect_float == NULL) + return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL); + else + return (int) imb_save_openexr_float(ibuf, name, flags, 1, NULL, NULL); + } +} + +static bool imb_save_openexr_multiview(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, const size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + if (flags & IB_mem) { + printf("OpenEXR-save: Create multiview EXR in memory CURRENTLY NOT SUPPORTED !\n"); + imb_addencodedbufferImBuf(ibuf); + ibuf->encodedsize = 0; + return false; + } + + if (ibuf->ftype & OPENEXR_HALF) + return imb_save_openexr_half(ibuf, name, flags, totviews, getview, getbuffer); else { /* when no float rect, we save as half (16 bits is sufficient) */ if (ibuf->rect_float == NULL) - return imb_save_openexr_half(ibuf, name, flags); + return imb_save_openexr_half(ibuf, name, flags, totviews, getview, getbuffer); else - return imb_save_openexr_float(ibuf, name, flags); + return imb_save_openexr_float(ibuf, name, flags, totviews, getview, getbuffer); } } +/* Save single-layer multiview OpenEXR + * If we have more multiview formats in the future, the function below could be incorporated + * in our ImBuf write functions, meanwhile this is an OpenEXR special case only */ +bool IMB_exr_multiview_save(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + return imb_save_openexr_multiview(ibuf, name, flags, totviews, getview, getbuffer); +} + /* ********************* Nicer API, MultiLayer and with Tile file support ************************************ */ /* naming rules: @@ -527,18 +628,22 @@ static ListBase exrhandles = {NULL, NULL}; typedef struct ExrHandle { struct ExrHandle *next, *prev; + char name[FILE_MAX]; IFileStream *ifile_stream; - InputFile *ifile; + MultiPartInputFile *ifile; OFileStream *ofile_stream; - TiledOutputFile *tofile; + MultiPartOutputFile *mpofile; OutputFile *ofile; int tilex, tiley; int width, height; int mipmap; + StringVector *multiView; /* it needs to be a pointer due to Windows release builds of EXR2.0 segfault when opening EXR bug */ + int parts; + ListBase channels; /* flattened out, ExrChannel */ ListBase layers; /* hierarchical, pointing in end to ExrChannel */ } ExrHandle; @@ -547,10 +652,12 @@ typedef struct ExrHandle { typedef struct ExrChannel { struct ExrChannel *next, *prev; - char name[EXR_TOT_MAXNAME + 1]; /* full name of layer+pass */ + char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */ + struct MultiViewChannelName *m; /* struct to store all multipart channel info */ int xstride, ystride; /* step to next pixel, to next scanline */ float *rect; /* first pointer to write in */ char chan_id; /* quick lookup of channel char */ + int view_id; /* quick lookup of channel view */ } ExrChannel; @@ -562,6 +669,10 @@ typedef struct ExrPass { float *rect; struct ExrChannel *chan[EXR_PASS_MAXCHAN]; char chan_id[EXR_PASS_MAXCHAN]; + + char internal_name[EXR_PASS_MAXNAME]; /* name with no view */ + char view[EXR_VIEW_MAXNAME]; + int view_id; } ExrPass; typedef struct ExrLayer { @@ -575,39 +686,141 @@ typedef struct ExrLayer { void *IMB_exr_get_handle(void) { ExrHandle *data = (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle"); + data->multiView = new StringVector(); + BLI_addtail(&exrhandles, data); return data; } +void *IMB_exr_get_handle_name(const char *name) +{ + ExrHandle *data = (ExrHandle *) BLI_rfindstring(&exrhandles, name, offsetof(ExrHandle, name)); + + if (data == NULL) { + data = (ExrHandle *)IMB_exr_get_handle(); + BLI_strncpy(data->name, name, strlen(name) + 1); + } + return data; +} + +/* multiview functions */ +} // extern "C" + +extern "C" +{ + +void IMB_exr_add_view(void *handle, const char *name) +{ + ExrHandle *data = (ExrHandle *)handle; + data->multiView->push_back(name); +} + +static int imb_exr_get_multiView_id(StringVector& views, const std::string& name) +{ + int count = 0; + for (StringVector::const_iterator i = views.begin(); count < views.size(); ++i) { + if (name == *i) + return count; + else + count ++; + } + + /* no views or wrong name */ + return -1; +} + +static void imb_exr_get_views(MultiPartInputFile& file, StringVector& views) +{ + if (exr_has_multipart_file(file) == false) { + if (exr_has_multiview(file)) { + StringVector sv = multiView(file.header(0)); + for (StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i) + views.push_back(*i); + } + } + + else { + for (int p = 0; p < file.parts(); p++) { + std::string view = ""; + if (file.header(p).hasView()) + view = file.header(p).view(); + + if (imb_exr_get_multiView_id(views, view) == -1) + views.push_back(view); + } + } +} + +/* Multilayer Blender files have the view name in all the passes (even the default view one) */ +static const char *imb_exr_insert_view_name(const char *passname, const char *viewname) +{ + if (viewname == NULL || viewname[0] == '\0') + return passname; + + static char retstr[EXR_PASS_MAXNAME]; + const char *end = passname + strlen(passname); + const char *token; + + int len = IMB_exr_split_token(passname, end, &token); + + if (len == 0) + BLI_snprintf(retstr, sizeof(retstr), "%s.%s", passname, viewname); + else + BLI_snprintf(retstr, sizeof(retstr), "%.*s%s.%s", + (int)(end - passname) - len, passname, viewname, token); + + return retstr; +} + /* adds flattened ExrChannels */ /* xstride, ystride and rect can be done in set_channel too, for tile writing */ -void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) +/* passname does not include view */ +void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *viewname, int xstride, int ystride, float *rect) { ExrHandle *data = (ExrHandle *)handle; ExrChannel *echan; - echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr tile channel"); + echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr channel"); + echan->m = new MultiViewChannelName (); - if (layname) { - char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1]; - BLI_strncpy(lay, layname, EXR_LAY_MAXNAME); - BLI_strncpy(pass, passname, EXR_PASS_MAXNAME); + if (layname && layname[0] != '\0') { + echan->m->name = layname; + echan->m->name.append("."); + echan->m->name.append(passname); + } + else { + echan->m->name.assign(passname); + } - BLI_snprintf(echan->name, sizeof(echan->name), "%s.%s", lay, pass); + echan->m->internal_name = echan->m->name; + + echan->m->view.assign(viewname ? viewname : ""); + + /* quick look up */ + echan->view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, echan->m->view)); + + /* name has to be unique, thus it's a combination of layer, pass, view, and channel */ + if (layname && layname[0] != '\0') { + std::string raw_name = imb_exr_insert_view_name(echan->m->name.c_str(), echan->m->view.c_str()); + BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name)); + } + else if (data->multiView->size() > 1) { + std::string raw_name = insertViewName(echan->m->name, *data->multiView, echan->view_id); + BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name)); } else { - BLI_strncpy(echan->name, passname, EXR_TOT_MAXNAME - 1); + BLI_strncpy(echan->name, echan->m->name.c_str(), sizeof(echan->name)); } echan->xstride = xstride; echan->ystride = ystride; echan->rect = rect; - // printf("added channel %s\n", echan->name); + exr_printf("added channel %s\n", echan->name); BLI_addtail(&data->channels, echan); } -/* only used for writing temp. render results (not image files) */ +/* used for output files (from RenderResult) (single and multilayer, single and multiview) */ int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress) { ExrHandle *data = (ExrHandle *)handle; @@ -617,6 +830,8 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh data->width = width; data->height = height; + bool is_singlelayer, is_multilayer, is_multiview; + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) header.channels().insert(echan->name, Channel(Imf::FLOAT)); @@ -624,7 +839,13 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh // openexr_header_metadata(&header, ibuf); // no imbuf. cant write /* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */ - header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer")); + imb_exr_type_by_channels(header.channels(), *data->multiView, &is_singlelayer, &is_multilayer, &is_multiview); + + if (is_multilayer) + header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer")); + + if (is_multiview) + addMultiView(header, *data->multiView); /* avoid crash/abort when we don't have permission to write here */ /* manually create ofstream, so we can handle utf-8 filepaths on windows */ @@ -632,7 +853,7 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh data->ofile_stream = new OFileStream(filename); data->ofile = new OutputFile(*(data->ofile_stream), header); } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl; delete data->ofile; @@ -645,10 +866,13 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh return (data->ofile != NULL); } +/* only used for writing temp. render results (not image files) + * (FSA and Save Buffers) */ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) { ExrHandle *data = (ExrHandle *)handle; Header header(width, height); + std::vector<Header> headers; ExrChannel *echan; data->tilex = tilex; @@ -657,26 +881,47 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int data->height = height; data->mipmap = mipmap; - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) - header.channels().insert(echan->name, Channel(Imf::FLOAT)); - header.setTileDescription(TileDescription(tilex, tiley, (mipmap) ? MIPMAP_LEVELS : ONE_LEVEL)); - header.lineOrder() = RANDOM_Y; header.compression() = RLE_COMPRESSION; + header.setType(TILEDIMAGE); header.insert("BlenderMultiChannel", StringAttribute("Blender V2.43")); + int numparts = data->multiView->size(); + + /* copy header from all parts of input to our header array + * those temporary files have one part per view */ + for (int i = 0; i < numparts; i++) { + headers.push_back (header); + headers[headers.size() - 1].setView((*(data->multiView))[i]); + headers[headers.size() - 1].setName((*(data->multiView))[i]); + } + + exr_printf("\nIMB_exrtile_begin_write\n"); + exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name"); + exr_printf("---------------------------------------------------------------\n"); + + /* assign channels */ + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + + echan->m->internal_name = echan->m->name; + echan->m->part_number = echan->view_id; + + headers[echan->view_id].channels().insert(echan->m->internal_name, Channel(Imf::FLOAT)); + exr_printf("%d %-6s %-22s \"%s\"\n", echan->m->part_number, echan->m->view.c_str(), echan->m->name.c_str(), echan->m->internal_name.c_str()); + } + /* avoid crash/abort when we don't have permission to write here */ /* manually create ofstream, so we can handle utf-8 filepaths on windows */ try { data->ofile_stream = new OFileStream(filename); - data->tofile = new TiledOutputFile(*(data->ofile_stream), header); + data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), &headers[0], headers.size()); } catch (const std::exception &) { - delete data->tofile; + delete data->mpofile; delete data->ofile_stream; - data->tofile = NULL; + data->mpofile = NULL; data->ofile_stream = NULL; } } @@ -685,12 +930,13 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height) { ExrHandle *data = (ExrHandle *)handle; + ExrChannel *echan; if (BLI_exists(filename) && BLI_file_size(filename) > 32) { /* 32 is arbitrary, but zero length files crashes exr */ /* avoid crash/abort when we don't have permission to write here */ try { data->ifile_stream = new IFileStream(filename); - data->ifile = new InputFile(*(data->ifile_stream)); + data->ifile = new MultiPartInputFile(*(data->ifile_stream)); } catch (const std::exception &) { delete data->ifile; @@ -701,14 +947,24 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig } if (data->ifile) { - Box2i dw = data->ifile->header().dataWindow(); + Box2i dw = data->ifile->header(0).dataWindow(); data->width = *width = dw.max.x - dw.min.x + 1; data->height = *height = dw.max.y - dw.min.y + 1; - const ChannelList &channels = data->ifile->header().channels(); + imb_exr_get_views(*data->ifile, *data->multiView); + + std::vector<MultiViewChannelName> channels; + GetChannelsInMultiPartFile(*data->ifile, channels); - for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) - IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); + for (size_t i = 0; i < channels.size(); i++) { + IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL); + + echan = (ExrChannel *)data->channels.last; + echan->m->name = channels[i].name; + echan->m->view = channels[i].view; + echan->m->part_number = channels[i].part_number; + echan->m->internal_name = channels[i].internal_name; + } return 1; } @@ -717,6 +973,7 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig } /* still clumsy name handling, layers/channels can be ordered as list in list later */ +/* passname here is the raw channel name without the layer */ void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) { ExrHandle *data = (ExrHandle *)handle; @@ -741,37 +998,53 @@ void IMB_exr_set_channel(void *handle, const char *layname, const char *passname echan->rect = rect; } else - printf("IMB_exrtile_set_channel error %s\n", name); -} - -void IMB_exrtile_clear_channels(void *handle) -{ - ExrHandle *data = (ExrHandle *)handle; - BLI_freelistN(&data->channels); + printf("IMB_exr_set_channel error %s\n", name); } -void IMB_exrtile_write_channels(void *handle, int partx, int party, int level) +float *IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *viewname) { ExrHandle *data = (ExrHandle *)handle; - FrameBuffer frameBuffer; ExrChannel *echan; + char name[EXR_TOT_MAXNAME + 1]; - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - float *rect = echan->rect - echan->xstride * partx - echan->ystride * party; + if (layname) { + char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1]; + BLI_strncpy(lay, layname, EXR_LAY_MAXNAME); + BLI_strncpy(pass, passname, EXR_PASS_MAXNAME); - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)rect, - echan->xstride * sizeof(float), echan->ystride * sizeof(float))); + BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass); } + else + BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1); - data->tofile->setFrameBuffer(frameBuffer); - - try { - // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley); - data->tofile->writeTile(partx / data->tilex, party / data->tiley, level); + /* name has to be unique, thus it's a combination of layer, pass, view, and channel */ + if (layname && layname[0] != '\0') { + std::string raw_name = imb_exr_insert_view_name(name, viewname); + BLI_strncpy(name, raw_name.c_str(), sizeof(name)); } - catch (const std::exception &exc) { - std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl; + else if (data->multiView->size() > 1) { + size_t view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, viewname)); + std::string raw_name = insertViewName(name, *data->multiView, view_id); + BLI_strncpy(name, raw_name.c_str(), sizeof(name)); } + + echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name)); + + if (echan) + return echan->rect; + + return NULL; +} + +void IMB_exr_clear_channels(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + ExrChannel *chan; + + for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) + delete chan->m; + + BLI_freelistN(&data->channels); } void IMB_exr_write_channels(void *handle) @@ -793,7 +1066,7 @@ void IMB_exr_write_channels(void *handle) try { data->ofile->writePixels(data->height); } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl; } } @@ -802,49 +1075,171 @@ void IMB_exr_write_channels(void *handle) } } -void IMB_exr_read_channels(void *handle) +/* temporary function, used for FSA and Save Buffers */ +/* called once per tile * view */ +void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname) { ExrHandle *data = (ExrHandle *)handle; FrameBuffer frameBuffer; ExrChannel *echan; + std::string view(viewname); + const size_t view_id = imb_exr_get_multiView_id(*data->multiView, view); + + exr_printf("\nIMB_exrtile_write_channels(view: %s)\n", viewname); + exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name"); + exr_printf("---------------------------------------------------------------------\n"); + + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + + /* eventually we can make the parts' channels to include + only the current view TODO */ + if (strcmp(viewname, echan->m->view.c_str()) != 0) + continue; + + exr_printf("%d %-6s %-22s \"%s\"\n", + echan->m->part_number, + echan->m->view.c_str(), + echan->m->name.c_str(), + echan->m->internal_name.c_str() + ); + + float *rect = echan->rect - echan->xstride * partx - echan->ystride * party; + frameBuffer.insert(echan->m->internal_name, + Slice(Imf::FLOAT, + (char *)rect, + echan->xstride * sizeof(float), + echan->ystride * sizeof(float) + ) + ); + } + + TiledOutputPart out (*data->mpofile, view_id); + out.setFrameBuffer(frameBuffer); + + try { + // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley); + out.writeTile(partx / data->tilex, party / data->tiley, level); + } + catch (const std::exception& exc) { + std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl; + } +} + +/* called only when handle has all views */ +void IMB_exrmultiview_write_channels(void *handle, const char *viewname) +{ + ExrHandle *data = (ExrHandle *)handle; + const size_t view_id = viewname ? imb_exr_get_multiView_id(*data->multiView, viewname) : -1; + int numparts = (view_id == -1 ? data->parts : view_id + 1); + std::vector <FrameBuffer> frameBuffers(numparts); + std::vector <OutputPart> outputParts; + ExrChannel *echan; + int i, part; + + if (data->channels.first == NULL) + return; + + exr_printf("\nIMB_exrmultiview_write_channels()\n"); + + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + if (view_id != -1 && echan->view_id != view_id) + continue; + + part = (view_id == -1 ? echan->m->part_number : echan->view_id); + + /* last scanline, stride negative */ + float *rect = echan->rect + echan->xstride * (data->height - 1) * data->width; + frameBuffers[part].insert(echan->m->internal_name, + Slice(Imf::FLOAT, + (char *)rect, + echan->xstride * sizeof(float), + -echan->ystride * sizeof(float)) + ); + } + + for (i = 0; i < numparts; i++) { + OutputPart out(*data->mpofile, i); + out.setFrameBuffer(frameBuffers[i]); + outputParts.push_back(out); + } + + try { + for (i = 0; i < numparts; i++) { + if (view_id != -1 && i != view_id) + continue; + + outputParts[i].writePixels(data->height); + } + } + catch (const std::exception& exc) { + std::cerr << "OpenEXR-write Multi Part: ERROR: " << exc.what() << std::endl; + } +} + +void IMB_exr_read_channels(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + ExrChannel *echan; + int numparts = data->ifile->parts(); + std::vector<FrameBuffer> frameBuffers(numparts); + std::vector<InputPart> inputParts; /* check if exr was saved with previous versions of blender which flipped images */ - const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel"); + const StringAttribute *ta = data->ifile->header(0).findTypedAttribute <StringAttribute> ("BlenderMultiChannel"); short flip = (ta && STREQLEN(ta->value().c_str(), "Blender V2.43", 13)); /* 'previous multilayer attribute, flipped */ + exr_printf("\nIMB_exr_read_channels\n%s %-6s %-22s \"%s\"\n---------------------------------------------------------------------\n", "p", "view", "name", "internal_name"); + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + exr_printf("%d %-6s %-22s \"%s\"\n", echan->m->part_number, echan->m->view.c_str(), echan->m->name.c_str(), echan->m->internal_name.c_str()); if (echan->rect) { if (flip) - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)echan->rect, + frameBuffers[echan->m->part_number].insert(echan->m->internal_name, Slice(Imf::FLOAT, (char *)echan->rect, echan->xstride * sizeof(float), echan->ystride * sizeof(float))); else - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width), + frameBuffers[echan->m->part_number].insert(echan->m->internal_name, Slice(Imf::FLOAT, (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width), echan->xstride * sizeof(float), -echan->ystride * sizeof(float))); } else - printf("warning, channel with no rect set %s\n", echan->name); + printf("warning, channel with no rect set %s\n", echan->m->internal_name.c_str()); } - data->ifile->setFrameBuffer(frameBuffer); + for (int i = 0; i < numparts; i++) { + InputPart in (*data->ifile, i); + in.setFrameBuffer(frameBuffers[i]); + inputParts.push_back(in); + } try { - data->ifile->readPixels(0, data->height - 1); + for (int i = 0; i < numparts; i++) { + Header header = inputParts[i].header(); + exr_printf("readPixels:readPixels[%d]: min.y: %d, max.y: %d\n", i, header.dataWindow().min.y, header.dataWindow().max.y); + inputParts[i].readPixels(header.dataWindow().min.y, header.dataWindow().max.y); + inputParts[i].readPixels(0, data->height - 1); + } } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl; } } void IMB_exr_multilayer_convert(void *handle, void *base, + void * (*addview)(void *base, const char *str), void * (*addlayer)(void *base, const char *str), void (*addpass)(void *base, void *lay, const char *str, - float *rect, int totchan, const char *chan_id)) + float *rect, int totchan, const char *chan_id, + const char *view)) { ExrHandle *data = (ExrHandle *)handle; ExrLayer *lay; ExrPass *pass; + /* add views to RenderResult */ + for (StringVector::const_iterator i = data->multiView->begin(); i != data->multiView->end(); ++i) { + addview(base, (*i).c_str()); + } + if (BLI_listbase_is_empty(&data->layers)) { printf("cannot convert multilayer, no layers in handle\n"); return; @@ -854,32 +1249,98 @@ void IMB_exr_multilayer_convert(void *handle, void *base, void *laybase = addlayer(base, lay->name); if (laybase) { for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { - addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id); + addpass(base, laybase, pass->internal_name, pass->rect, pass->totchan, pass->chan_id, pass->view); pass->rect = NULL; } } } } +void IMB_exr_multiview_convert(void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, ImBuf *ibuf, const int frame), + const int frame) +{ + ExrHandle *data = (ExrHandle *)handle; + MultiPartInputFile *file = data->ifile; + ExrLayer *lay; + ExrPass *pass; + ImBuf *ibuf = NULL; + const int is_alpha = exr_has_alpha(*file); + Box2i dw = file->header(0).dataWindow(); + const size_t width = dw.max.x - dw.min.x + 1; + const size_t height = dw.max.y - dw.min.y + 1; + const bool is_depth = exr_has_zbuffer(*file); + + /* add views to RenderResult */ + for (StringVector::const_iterator i = data->multiView->begin(); i != data->multiView->end(); ++i) { + addview(base, (*i).c_str()); + } + + if (BLI_listbase_is_empty(&data->layers)) { + printf("cannot convert multiviews, no views in handle\n"); + return; + } + + /* there is one float/pass per layer (layer here is a view) */ + BLI_assert(BLI_listbase_count_ex(&data->layers, 2) == 1); + lay = (ExrLayer *)data->layers.first; + for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { + if (STREQ(pass->chan_id, "RGB") || STREQ(pass->chan_id, "RGBA")) { + ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, IB_rectfloat); + + if (!ibuf) { + printf("error creating multiview buffer\n"); + return; + } + + IMB_buffer_float_from_float( + ibuf->rect_float, pass->rect, pass->totchan, + IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, + ibuf->x, ibuf->y, ibuf->x, ibuf->x); + + if (hasXDensity(file->header(0))) { + ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f; + ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio(); + } + + if (is_depth) { + ExrPass *zpass; + for (zpass = (ExrPass *)lay->passes.first; zpass; zpass = zpass->next) { + if (STREQ(zpass->chan_id, "Z") && STREQ(zpass->view, pass->view)) { + addzbuffloatImBuf(ibuf); + memcpy(ibuf->zbuf_float, zpass->rect, sizeof(float) * ibuf->x * ibuf->y); + } + } + } + + addbuffer(base, pass->view, ibuf, frame); + } + } +} void IMB_exr_close(void *handle) { ExrHandle *data = (ExrHandle *)handle; ExrLayer *lay; ExrPass *pass; + ExrChannel *chan; delete data->ifile; delete data->ifile_stream; delete data->ofile; - delete data->tofile; + delete data->mpofile; delete data->ofile_stream; data->ifile = NULL; data->ifile_stream = NULL; data->ofile = NULL; - data->tofile = NULL; + data->mpofile = NULL; data->ofile_stream = NULL; + for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) { + delete chan->m; + } BLI_freelistN(&data->channels); for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) { @@ -897,7 +1358,7 @@ void IMB_exr_close(void *handle) /* ********* */ /* get a substring from the end of the name, separated by '.' */ -static int imb_exr_split_token(const char *str, const char *end, const char **token) +int IMB_exr_split_token(const char *str, const char *end, const char **token) { ptrdiff_t maxlen = end - str; int len = 0; @@ -911,7 +1372,7 @@ static int imb_exr_split_token(const char *str, const char *end, const char **to static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname) { - const char *name = echan->name; + const char *name = echan->m->name.c_str(); const char *end = name + strlen(name); const char *token; char tokenbuf[EXR_TOT_MAXNAME]; @@ -933,7 +1394,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa } /* last token is single character channel identifier */ - len = imb_exr_split_token(name, end, &token); + len = IMB_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; @@ -971,7 +1432,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa end -= len + 1; /* +1 to skip '.' separator */ /* second token is pass name */ - len = imb_exr_split_token(name, end, &token); + len = IMB_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; @@ -1020,7 +1481,7 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname) } /* creates channels, makes a hierarchy and assigns memory to channels */ -static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) +static ExrHandle *imb_exr_begin_read_mem(MultiPartInputFile& file, int width, int height) { ExrLayer *lay; ExrPass *pass; @@ -1029,30 +1490,57 @@ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) int a; char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME]; - data->ifile = file; + data->ifile = &file; data->width = width; data->height = height; - const ChannelList &channels = data->ifile->header().channels(); + std::vector<MultiViewChannelName> channels; + GetChannelsInMultiPartFile(*data->ifile, channels); - for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) - IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); + data->multiView = new StringVector(); + imb_exr_get_views(*data->ifile, *data->multiView); + + for (size_t i = 0; i < channels.size(); i++) { + IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL); + + echan = (ExrChannel *)data->channels.last; + echan->m->name = channels[i].name; + echan->m->view = channels[i].view; + echan->m->part_number = channels[i].part_number; + echan->m->internal_name = channels[i].internal_name; + } /* now try to sort out how to assign memory to the channels */ /* first build hierarchical layer list */ for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - if (imb_exr_split_channel_name(echan, layname, passname) ) { + if (imb_exr_split_channel_name(echan, layname, passname)) { + + const char *view = echan->m->view.c_str(); + char internal_name[EXR_PASS_MAXNAME]; + + BLI_strncpy(internal_name, passname, EXR_PASS_MAXNAME); + + if (view[0] != '\0') { + char tmp_pass[EXR_PASS_MAXNAME]; + BLI_snprintf(tmp_pass, sizeof(tmp_pass), "%s.%s", passname, view); + BLI_strncpy(passname, tmp_pass, sizeof(passname)); + } + ExrLayer *lay = imb_exr_get_layer(&data->layers, layname); ExrPass *pass = imb_exr_get_pass(&lay->passes, passname); pass->chan[pass->totchan] = echan; pass->totchan++; + pass->view_id = echan->view_id; + BLI_strncpy(pass->view, view, sizeof(pass->view)); + BLI_strncpy(pass->internal_name, internal_name, EXR_PASS_MAXNAME); + if (pass->totchan >= EXR_PASS_MAXCHAN) break; } } if (echan) { - printf("error, too many channels in one pass: %s\n", echan->name); + printf("error, too many channels in one pass: %s\n", echan->m->name.c_str()); IMB_exr_close(data); return NULL; } @@ -1122,20 +1610,52 @@ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) /* ********************************************************* */ /* debug only */ -static void exr_print_filecontents(InputFile *file) +static void exr_printf(const char *fmt, ...) { - const ChannelList &channels = file->header().channels(); +#if 0 + char output[1024]; + va_list args; + va_start(args, fmt); + std::vsprintf(output, fmt, args); + va_end(args); + printf("%s", output); +#else + (void)fmt; +#endif +} - for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { - const Channel &channel = i.channel(); - printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type); +static void exr_print_filecontents(MultiPartInputFile& file) +{ + int numparts = file.parts(); + if (numparts == 1 && hasMultiView(file.header(0))) { + const StringVector views = multiView(file.header(0)); + printf("OpenEXR-load: MultiView file\n"); + printf("OpenEXR-load: Default view: %s\n", defaultViewName(views).c_str()); + for (StringVector::const_iterator i = views.begin(); i != views.end(); ++i) { + printf("OpenEXR-load: Found view %s\n", (*i).c_str()); + } + } + else if (numparts > 1) { + printf("OpenEXR-load: MultiPart file\n"); + for (int i = 0; i < numparts; i++) { + if (file.header(i).hasView()) + printf("OpenEXR-load: Part %d: view = \"%s\"\n", i, file.header(i).view().c_str()); + } + } + + for (int j = 0; j < numparts; j++) { + const ChannelList& channels = file.header(j).channels(); + for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { + const Channel& channel = i.channel(); + printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type); + } } } /* for non-multilayer, map R G B A channel names to something that's in this file */ -static const char *exr_rgba_channelname(InputFile *file, const char *chan) +static const char *exr_rgba_channelname(MultiPartInputFile& file, const char *chan) { - const ChannelList &channels = file->header().channels(); + const ChannelList& channels = file.header(0).channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { /* const Channel &channel = i.channel(); */ /* Not used yet */ @@ -1150,48 +1670,48 @@ static const char *exr_rgba_channelname(InputFile *file, const char *chan) return chan; } -static bool exr_has_rgb(InputFile *file) +static bool exr_has_rgb(MultiPartInputFile& file) { - return file->header().channels().findChannel("R") != NULL && - file->header().channels().findChannel("G") != NULL && - file->header().channels().findChannel("B") != NULL; + return file.header(0).channels().findChannel("R") != NULL && + file.header(0).channels().findChannel("G") != NULL && + file.header(0).channels().findChannel("B") != NULL; } -static bool exr_has_luma(InputFile *file) +static bool exr_has_luma(MultiPartInputFile& file) { /* Y channel is the luma and should always present fir luma space images, * optionally it could be also channels for chromas called BY and RY. */ - return file->header().channels().findChannel("Y") != NULL; + return file.header(0).channels().findChannel("Y") != NULL; } -static bool exr_has_chroma(InputFile *file) +static bool exr_has_chroma(MultiPartInputFile& file) { - return file->header().channels().findChannel("BY") != NULL && - file->header().channels().findChannel("RY") != NULL; + return file.header(0).channels().findChannel("BY") != NULL && + file.header(0).channels().findChannel("RY") != NULL; } -static int exr_has_zbuffer(InputFile *file) +static int exr_has_zbuffer(MultiPartInputFile& file) { - return !(file->header().channels().findChannel("Z") == NULL); + return !(file.header(0).channels().findChannel("Z") == NULL); } -static int exr_has_alpha(InputFile *file) +static int exr_has_alpha(MultiPartInputFile& file) { - return !(file->header().channels().findChannel("A") == NULL); + return !(file.header(0).channels().findChannel("A") == NULL); } -static bool exr_is_multilayer(InputFile *file) +static bool imb_exr_is_multilayer_file(MultiPartInputFile& file) { - const StringAttribute *comments = file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel"); - const ChannelList &channels = file->header().channels(); + const StringAttribute *comments = file.header(0).findTypedAttribute<StringAttribute>("BlenderMultiChannel"); + const ChannelList& channels = file.header(0).channels(); std::set <std::string> layerNames; /* will not include empty layer names */ channels.layers(layerNames); if (comments || layerNames.size() > 1) - return 1; + return true; if (layerNames.size()) { /* if layerNames is not empty, it means at least one layer is non-empty, @@ -1206,17 +1726,122 @@ static bool exr_is_multilayer(InputFile *file) size_t pos = layerName.rfind ('.'); if (pos == std::string::npos) - return 1; + return true; } } - return 0; + return false; +} + +static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views, + bool *r_singlelayer, bool *r_multilayer, bool *r_multiview) +{ + std::set <std::string> layerNames; + + *r_singlelayer = true; + *r_multilayer = *r_multiview = false; + + /* will not include empty layer names */ + channels.layers(layerNames); + + if (views.size() && views[0] != "") + *r_multiview = true; + + if (layerNames.size()) { + /* if layerNames is not empty, it means at least one layer is non-empty, + * but it also could be layers without names in the file and such case + * shall be considered a multilayer exr + * + * that's what we do here: test whether there're empty layer names together + * with non-empty ones in the file + */ + for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) + for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++) + /* see if any layername differs from a viewname */ + if (imb_exr_get_multiView_id(views, *i) == -1) { + std::string layerName = *i; + size_t pos = layerName.rfind ('.'); + + if (pos != std::string::npos) { + *r_multilayer = true; + *r_singlelayer = false; + return; + } + } + } + else { + *r_singlelayer = true; + *r_multilayer = false; + *r_multiview = false; + } + + BLI_assert(r_singlelayer != r_multilayer); +} + +bool IMB_exr_has_singlelayer_multiview(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + MultiPartInputFile *file = data->ifile; + std::set <std::string> layerNames; + const ChannelList& channels = file->header(0).channels(); + const StringAttribute *comments; + + if (exr_has_multiview(*file) == false) + return false; + + comments = file->header(0).findTypedAttribute<StringAttribute>("BlenderMultiChannel"); + + if (comments) + return false; + + /* will not include empty layer names */ + channels.layers(layerNames); + + /* returns false if any layer differs from views list */ + if (layerNames.size()) + for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++) + if (imb_exr_get_multiView_id(*data->multiView, *i) == -1) + return false; + + return true; +} + +bool IMB_exr_has_multilayer(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + return imb_exr_is_multilayer_file(*data->ifile); +} + +static bool exr_has_multiview(MultiPartInputFile& file) +{ + return hasMultiView(file.header(0)); +} + +static bool exr_has_multipart_file(MultiPartInputFile& file) +{ + return file.parts() > 1; +} + +/* it returns true if the file is multilayer or multiview */ +static bool imb_exr_is_multi(MultiPartInputFile& file) +{ + /* multipart files are treated as multilayer in blender - even if they are single layer openexr with multiview */ + if (exr_has_multipart_file(file)) + return true; + + if (exr_has_multiview(file)) + return true; + + if (imb_exr_is_multilayer_file(file)) + return true; + + return false; } struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; - InputFile *file = NULL; + MultiPartInputFile *file = NULL; if (imb_is_a_openexr(mem) == 0) return(NULL); @@ -1226,9 +1851,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char { Mem_IStream *membuf = new Mem_IStream(mem, size); bool is_multi; - file = new InputFile(*membuf); + file = new MultiPartInputFile(*membuf); - Box2i dw = file->header().dataWindow(); + Box2i dw = file->header(0).dataWindow(); const int width = dw.max.x - dw.min.x + 1; const int height = dw.max.y - dw.min.y + 1; @@ -1236,38 +1861,38 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char // dw.min.x, dw.min.y, dw.max.x, dw.max.y); if (0) // debug - exr_print_filecontents(file); + exr_print_filecontents(*file); - is_multi = exr_is_multilayer(file); + is_multi = imb_exr_is_multi(*file); /* do not make an ibuf when */ if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) { printf("Error: can't process EXR multilayer file\n"); } else { - const int is_alpha = exr_has_alpha(file); + const int is_alpha = exr_has_alpha(*file); ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0); - if (hasXDensity(file->header())) { - ibuf->ppm[0] = xDensity(file->header()) * 39.3700787f; - ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header().pixelAspectRatio(); + if (hasXDensity(file->header(0))) { + ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f; + ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio(); } ibuf->ftype = OPENEXR; if (!(flags & IB_test)) { - if (is_multi) { /* only enters with IB_multilayer flag set */ + if (is_multi && ((flags & IB_thumbnail) == 0)) { /* only enters with IB_multilayer flag set */ /* constructs channels for reading, allocates memory in channels */ - ExrHandle *handle = imb_exr_begin_read_mem(file, width, height); + ExrHandle *handle = imb_exr_begin_read_mem(*file, width, height); if (handle) { IMB_exr_read_channels(handle); ibuf->userdata = handle; /* potential danger, the caller has to check for this! */ } } else { - const bool has_rgb = exr_has_rgb(file); - const bool has_luma = exr_has_luma(file); + const bool has_rgb = exr_has_rgb(*file); + const bool has_luma = exr_has_luma(*file); FrameBuffer frameBuffer; float *first; int xstride = sizeof(float) * 4; @@ -1281,27 +1906,27 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char first += 4 * (height - 1) * width; if (has_rgb) { - frameBuffer.insert(exr_rgba_channelname(file, "R"), + frameBuffer.insert(exr_rgba_channelname(*file, "R"), Slice(Imf::FLOAT, (char *) first, xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "G"), + frameBuffer.insert(exr_rgba_channelname(*file, "G"), Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "B"), + frameBuffer.insert(exr_rgba_channelname(*file, "B"), Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride)); } else if (has_luma) { - frameBuffer.insert(exr_rgba_channelname(file, "Y"), + frameBuffer.insert(exr_rgba_channelname(*file, "Y"), Slice(Imf::FLOAT, (char *) first, xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "BY"), + frameBuffer.insert(exr_rgba_channelname(*file, "BY"), Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride, 1, 1, 0.5f)); - frameBuffer.insert(exr_rgba_channelname(file, "RY"), + frameBuffer.insert(exr_rgba_channelname(*file, "RY"), Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride, 1, 1, 0.5f)); } /* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */ - frameBuffer.insert(exr_rgba_channelname(file, "A"), + frameBuffer.insert(exr_rgba_channelname(*file, "A"), Slice(Imf::FLOAT, (char *) (first + 3), xstride, ystride, 1, 1, 1.0f)); - if (exr_has_zbuffer(file)) { + if (exr_has_zbuffer(*file)) { float *firstz; addzbuffloatImBuf(ibuf); @@ -1310,8 +1935,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)firstz, sizeof(float), -width * sizeof(float))); } - file->setFrameBuffer(frameBuffer); - file->readPixels(dw.min.y, dw.max.y); + InputPart in (*file, 0); + in.setFrameBuffer(frameBuffer); + in.readPixels(dw.min.y, dw.max.y); // XXX, ImBuf has no nice way to deal with this. // ideally IM_rect would be used when the caller wants a rect BUT @@ -1325,7 +1951,7 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char if (!has_rgb && has_luma) { size_t a; - if (exr_has_chroma(file)) { + if (exr_has_chroma(*file)) { for (a = 0; a < (size_t) ibuf->x * ibuf->y; ++a) { float *color = ibuf->rect_float + a * 4; ycc_to_rgb(color[0] * 255.0f, color[1] * 255.0f, color[2] * 255.0f, @@ -1351,7 +1977,7 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char } return(ibuf); } - catch (const std::exception &exc) + catch (const std::exception& exc) { std::cerr << exc.what() << std::endl; if (ibuf) IMB_freeImBuf(ibuf); diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h index 376d2401b1c..45ab0ed37c7 100644 --- a/source/blender/imbuf/intern/openexr/openexr_multi.h +++ b/source/blender/imbuf/intern/openexr/openexr_multi.h @@ -39,8 +39,9 @@ /* This api also supports max 8 channels per pass now. easy to fix! */ #define EXR_LAY_MAXNAME 64 #define EXR_PASS_MAXNAME 64 +#define EXR_VIEW_MAXNAME 64 #define EXR_TOT_MAXNAME 64 -#define EXR_PASS_MAXCHAN 8 +#define EXR_PASS_MAXCHAN 24 #ifdef __cplusplus @@ -48,25 +49,49 @@ extern "C" { #endif void *IMB_exr_get_handle(void); -void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect); +void *IMB_exr_get_handle_name(const char *name); +void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect); int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height); int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress); void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley); void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect); +float *IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *view); void IMB_exr_read_channels(void *handle); void IMB_exr_write_channels(void *handle); -void IMB_exrtile_write_channels(void *handle, int partx, int party, int level); -void IMB_exrtile_clear_channels(void *handle); +void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname); +void IMB_exrmultiview_write_channels(void *handle, const char *viewname); +void IMB_exr_clear_channels(void *handle); -void IMB_exr_multilayer_convert(void *handle, void *base, - void * (*addlayer)(void *base, const char *str), - void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id)); +void IMB_exr_multilayer_convert( + void *handle, void *base, + void * (*addview)(void *base, const char *str), + void * (*addlayer)(void *base, const char *str), + void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, + const char *chan_id, const char *view)); + +void IMB_exr_multiview_convert( + void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, struct ImBuf *ibuf, const int frame), + const int frame); + +bool IMB_exr_multiview_save( + struct ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + struct ImBuf * (*getbuffer)(void *base, const size_t view_id)); void IMB_exr_close(void *handle); +void IMB_exr_add_view(void *handle, const char *name); + +int IMB_exr_split_token(const char *str, const char *end, const char **token); + +bool IMB_exr_has_multilayer(void *handle); +bool IMB_exr_has_singlelayer_multiview(void *handle); + #ifdef __cplusplus } // extern "C" #endif diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 21fa878c08a..c326efd2427 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -31,27 +31,55 @@ #include "openexr_api.h" #include "openexr_multi.h" - +#include "BLI_utildefines.h" /* UNUSED_VARS */ void *IMB_exr_get_handle (void) {return NULL;} -void IMB_exr_add_channel (void *handle, const char *layname, const char *channame, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)channame; (void)xstride; (void)ystride; (void)rect; } +void *IMB_exr_get_handle_name (const char *name) {(void)name; return NULL;} +void IMB_exr_add_channel (void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)passname; (void)xstride; (void)ystride; (void)rect; } int IMB_exr_begin_read (void *handle, const char *filename, int *width, int *height) { (void)handle; (void)filename; (void)width; (void)height; return 0;} int IMB_exr_begin_write (void *handle, const char *filename, int width, int height, int compress) { (void)handle; (void)filename; (void)width; (void)height; (void)compress; return 0;} void IMB_exrtile_begin_write (void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) { (void)handle; (void)filename; (void)mipmap; (void)width; (void)height; (void)tilex; (void)tiley; } -void IMB_exr_set_channel (void *handle, const char *layname, const char *channame, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)channame; (void)xstride; (void)ystride; (void)rect; } +void IMB_exr_set_channel (void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)passname; (void)xstride; (void)ystride; (void)rect; } +float *IMB_exr_channel_rect (void *handle, const char *layname, const char *passname, const char *view) { (void)handle; (void)layname; (void)passname; (void)view; return NULL; } void IMB_exr_read_channels (void *handle) { (void)handle; } void IMB_exr_write_channels (void *handle) { (void)handle; } -void IMB_exrtile_write_channels (void *handle, int partx, int party, int level) { (void)handle; (void)partx; (void)party; (void)level; } -void IMB_exrtile_clear_channels (void *handle) { (void)handle; } +void IMB_exrtile_write_channels (void *handle, int partx, int party, int level, const char *viewname) { UNUSED_VARS(handle, partx, party, level, viewname); } +void IMB_exrmultiview_write_channels(void *handle, const char *viewname) { UNUSED_VARS(handle, viewname); } +void IMB_exr_clear_channels (void *handle) { (void)handle; } + +void IMB_exr_multilayer_convert( + void *handle, void *base, + void * (*addview)(void *base, const char *str), + void * (*addlayer)(void *base, const char *str), + void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, + const char *chan_id, const char *view)) +{ + UNUSED_VARS(handle, base, addview, addlayer, addpass); +} -void IMB_exr_multilayer_convert (void *handle, void *base, - void * (*addlayer)(void *base, const char *str), - void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id)) +void IMB_exr_multiview_convert( + void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, struct ImBuf *ibuf, const int frame), const int frame) { - (void)handle; (void)base; (void)addlayer; (void)addpass; + UNUSED_VARS(handle, base, addview, addbuffer, frame); +} + +bool IMB_exr_multiview_save( + struct ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + struct ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + UNUSED_VARS(ibuf, name, flags, totviews, getview, getbuffer); + return false; } void IMB_exr_close (void *handle) { (void)handle; } + +void IMB_exr_add_view(void *handle, const char *name) { UNUSED_VARS(handle, name); } +int IMB_exr_split_token(const char *str, const char *end, const char **token) { UNUSED_VARS(str, end, token); return 1; } +bool IMB_exr_has_multilayer(void *handle) { UNUSED_VARS(handle); return false; } +bool IMB_exr_has_singlelayer_multiview(void *handle) { UNUSED_VARS(handle); return false; } diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c new file mode 100644 index 00000000000..cf9c6b388ff --- /dev/null +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -0,0 +1,1292 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2015 by Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/imbuf/intern/stereoimbuf.c + * \ingroup imbuf + */ + +#include <stddef.h> + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "IMB_allocimbuf.h" +#include "IMB_filetype.h" +#include "IMB_metadata.h" +#include "IMB_colormanagement_intern.h" + +#include "imbuf.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "BLI_math.h" + +#include "DNA_userdef_types.h" +#include "DNA_scene_types.h" + +/* prototypes */ +struct Stereo3DData; +static void imb_stereo3d_write_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d); +static void imb_stereo3d_read_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d); + +typedef struct Stereo3DData { + struct { float *left, *right, *stereo; } rectf; + struct { uchar *left, *right, *stereo; } rect; + size_t x, y, channels; + bool is_float; +} Stereo3DData; + +static void imb_stereo3d_write_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + int anaglyph_encoding[3][3] = { + {0, 1, 1}, + {1, 0, 1}, + {0, 0, 1}, + }; + + int r, g, b; + + r = anaglyph_encoding[mode][0]; + g = anaglyph_encoding[mode][1]; + b = anaglyph_encoding[mode][2]; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 4; + float *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + to[3] = MAX2(from[0][2], from[0][2]); + } + } + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + to[3] = MAX2(from[0][2], from[0][2]); + } + } + } + } +} + +static void imb_stereo3d_write_interlace(Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + memcpy(to, from[i], sizeof(float) * channels * stride_from); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y; + const float *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[i][0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + const float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3(to, from[i]); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4(to, from[i]); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y; + const float *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[j][0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + const float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3(to, from[j]); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 4; + const float *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4(to, from[j]); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + memcpy(to, from[i], sizeof(uchar) * channels * stride_from); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y; + const uchar *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[i][0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + const uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3_char((char *)to, (char *)from[i]); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + const uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4_char((char *)to, (char *)from[i]); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y; + const uchar *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[j][0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + const uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3_char((char *)to, (char *)from[j]); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + const uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4_char((char *)to, (char *)from[j]); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } +} + +/* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_write_sidebyside(Stereo3DData *s3d, const bool crosseyed) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width * 2; + + const int l = (int) crosseyed; + const int r = !l; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[l], sizeof(float) * channels * stride_from); + memcpy(to + channels * stride_from, from[r], sizeof(float) * channels * stride_from); + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[l], sizeof(uchar) * channels * stride_from); + memcpy(to + channels * stride_from, from[r], sizeof(uchar) * channels * stride_from); + } + } +} + +/* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_write_topbottom(Stereo3DData *s3d) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[1], sizeof(float) * channels * stride_from); + memcpy(to + channels * height * stride_from, from[0], sizeof(float) * channels * stride_from); + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[1], sizeof(uchar) * channels * stride_from); + memcpy(to + channels * height * stride_from, from[0], sizeof(uchar) * channels * stride_from); + } + } +} + +/**************************** dimension utils ****************************************/ + +void IMB_stereo3d_write_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + switch (mode) { + case S3D_DISPLAY_SIDEBYSIDE: + { + *r_width = is_squeezed ? width : width * 2; + *r_height = height; + break; + } + case S3D_DISPLAY_TOPBOTTOM: + { + *r_width = width; + *r_height = is_squeezed ? height : height * 2; + break; + } + case S3D_DISPLAY_ANAGLYPH: + case S3D_DISPLAY_INTERLACE: + default: + { + *r_width = width; + *r_height = height; + break; + } + } +} + +void IMB_stereo3d_read_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + switch (mode) { + case S3D_DISPLAY_SIDEBYSIDE: + { + *r_width = is_squeezed ? width / 2 : width; + *r_height = height; + break; + } + case S3D_DISPLAY_TOPBOTTOM: + { + *r_width = width; + *r_height = is_squeezed ? height / 2 : height; + break; + } + case S3D_DISPLAY_ANAGLYPH: + case S3D_DISPLAY_INTERLACE: + default: + { + *r_width = width; + *r_height = height; + break; + } + } +} + +/**************************** un/squeeze frame ****************************************/ + +static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, const size_t x, const size_t y) +{ + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + IMB_scaleImBuf_threaded(ibuf, x, y); +} + +static void imb_stereo3d_unsqueeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, const size_t x, const size_t y) +{ + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + IMB_scaleImBuf_threaded(ibuf, x, y); +} + +static void imb_stereo3d_squeeze_rectf(float *rectf, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) +{ + ImBuf *ibuf; + size_t width, height; + + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + /* creates temporary imbuf to store the rectf */ + IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); + ibuf = IMB_allocImBuf(width, height, channels, IB_rectfloat); + + IMB_buffer_float_from_float( + ibuf->rect_float, rectf, channels, + IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, + width, height, width, width); + + IMB_scaleImBuf_threaded(ibuf, x, y); + rectf = MEM_dupallocN(ibuf->rect_float); + IMB_freeImBuf(ibuf); +} + +static void imb_stereo3d_squeeze_rect(int *rect, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) +{ + ImBuf *ibuf; + size_t width, height; + + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + /* creates temporary imbuf to store the rectf */ + IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); + ibuf = IMB_allocImBuf(width, height, channels, IB_rect); + + IMB_buffer_byte_from_byte( + (unsigned char *)ibuf->rect, (unsigned char *)rect, + IB_PROFILE_SRGB, IB_PROFILE_SRGB, false, + width, height, width, width); + + IMB_scaleImBuf_threaded(ibuf, x, y); + rect = MEM_dupallocN(ibuf->rect); + IMB_freeImBuf(ibuf); +} + + +/*************************** preparing to call the write functions **************************/ + +static void imb_stereo3d_data_initialize( + Stereo3DData *s3d_data, const bool is_float, + const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right, int *rect_stereo, + float *rectf_left, float *rectf_right, float *rectf_stereo) +{ + s3d_data->is_float = is_float; + s3d_data->x = x; + s3d_data->y = y; + s3d_data->channels = channels; + s3d_data->rect.left = (uchar *)rect_left; + s3d_data->rect.right = (uchar *)rect_right; + s3d_data->rect.stereo = (uchar *)rect_stereo; + s3d_data->rectf.left = rectf_left; + s3d_data->rectf.right = rectf_right; + s3d_data->rectf.stereo = rectf_stereo; +} + +int *IMB_stereo3d_from_rect( + ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right) +{ + int *r_rect; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, x, y, &width, &height); + r_rect = MEM_mallocN(channels * sizeof(int) * width * height, __func__); + + imb_stereo3d_data_initialize(&s3d_data, is_float, x, y, channels, rect_left, rect_right, r_rect, NULL, NULL, NULL); + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_rect(r_rect, &im_format->stereo3d_format, x, y, channels); + + return r_rect; +} + +float *IMB_stereo3d_from_rectf( + ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + float *rectf_left, float *rectf_right) +{ + float *r_rectf; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, x, y, &width, &height); + r_rectf = MEM_mallocN(channels * sizeof(float) * width * height, __func__); + + imb_stereo3d_data_initialize(&s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, r_rectf); + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_rectf(r_rectf, &im_format->stereo3d_format, x, y, channels); + + return r_rectf; +} + +/* left/right are always float */ +ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) +{ + ImBuf *ibuf_stereo = NULL; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, ibuf_left->x, ibuf_left->y, &width, &height); + ibuf_stereo = IMB_allocImBuf(width, height, ibuf_left->planes, (is_float ? IB_rectfloat : IB_rect)); + + /* copy flags for IB_fields and other settings */ + ibuf_stereo->flags = ibuf_left->flags; + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 4, + (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo->rect, + ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo->rect_float); + + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_ImBuf(ibuf_stereo, &im_format->stereo3d_format, ibuf_left->x, ibuf_left->y); + + return ibuf_stereo; +} + +static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) +{ + switch (s3d->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + imb_stereo3d_write_anaglyph(s3d_data, s3d->anaglyph_type); + break; + case S3D_DISPLAY_INTERLACE: + imb_stereo3d_write_interlace(s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0); + break; + case S3D_DISPLAY_SIDEBYSIDE: + imb_stereo3d_write_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0); + break; + case S3D_DISPLAY_TOPBOTTOM: + imb_stereo3d_write_topbottom(s3d_data); + break; + default: + break; + } +} + +/******************************** reading stereo imbufs **********************/ + +static void imb_stereo3d_read_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + int anaglyph_encoding[3][3] = { + {0, 1, 1}, + {1, 0, 1}, + {0, 0, 1}, + }; + + int r, g, b; + + r = anaglyph_encoding[mode][0]; + g = anaglyph_encoding[mode][1]; + b = anaglyph_encoding[mode][2]; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + float *rect_from = s3d->rectf.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *from = rect_from + stride_from * y * 4; + float *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + to[0][3] = to[1][3] = from[3]; + } + } + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + uchar *rect_from = s3d->rect.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + to[0][3] = to[1][3] = from[3]; + } + } + } + } +} + +static void imb_stereo3d_read_interlace(Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + memcpy(to[i], from, sizeof(float) * channels * stride_to); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y; + float *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[i][0] = from[0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3(to[i], from); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4(to[i], from); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y; + float *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char j = i; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[j][0] = from[0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3(to[j], from); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 4; + float *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4(to[j], from); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } + else { + uchar *rect_left = s3d->rect.right; + uchar *rect_right = s3d->rect.left; + const uchar *rect_from = s3d->rect.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + memcpy(to[i], from, sizeof(uchar) * channels * stride_to); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y; + uchar *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[i][0] = from[0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3_char((char *)to[i], (char *)from); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4_char((char *)to[i], (char *)from); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y; + uchar *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char j = i; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[j][0] = from[0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3_char((char *)to[j], (char *)from); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4_char((char *)to[j], (char *)from); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } +} + +/* stereo input (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_read_sidebyside(Stereo3DData *s3d, const bool crosseyed) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width * 2; + const int stride_to = width; + + const int l = (int) crosseyed; + const int r = !l; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[l], from, sizeof(float) * channels * stride_to); + memcpy(to[r], from + channels * stride_to, sizeof(float) * channels * stride_to); + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + const uchar *rect_from = s3d->rect.stereo; + + /* always RGBA input/output */ + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[l], from, sizeof(uchar) * channels * stride_to); + memcpy(to[r], from + channels * stride_to, sizeof(uchar) * channels * stride_to); + } + } +} + +/* stereo input (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_read_topbottom(Stereo3DData *s3d) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[1], from, sizeof(float) * channels * stride_to); + memcpy(to[0], from + channels * height * stride_to, sizeof(float) * channels * stride_to); + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + const uchar *rect_from = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[1], from, sizeof(uchar) * channels * stride_to); + memcpy(to[0], from + channels * height * stride_to, sizeof(uchar) * channels * stride_to); + } + } +} + + +/*************************** preparing to call the read functions **************************/ + +/* reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right) */ +void IMB_ImBufFromStereo3d( + Stereo3dFormat *s3d, ImBuf *ibuf_stereo3d, + ImBuf **r_ibuf_left, ImBuf **r_ibuf_right) +{ + Stereo3DData s3d_data = {{NULL}}; + ImBuf *ibuf_left, *ibuf_right; + size_t width, height; + const bool is_float = (ibuf_stereo3d->rect_float != NULL); + + IMB_stereo3d_read_dimensions( + s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y, + &width, &height); + + ibuf_left = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect)); + ibuf_right = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect)); + + /* copy flags for IB_fields and other settings */ + ibuf_left->flags = ibuf_stereo3d->flags; + ibuf_right->flags = ibuf_stereo3d->flags; + + /* we always work with unsqueezed formats */ + IMB_stereo3d_write_dimensions( + s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y, + &width, &height); + imb_stereo3d_unsqueeze_ImBuf(ibuf_stereo3d, s3d, width, height); + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 4, + (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo3d->rect, + ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo3d->rect_float); + + imb_stereo3d_read_doit(&s3d_data, s3d); + + if (ibuf_stereo3d->flags & (IB_zbuf | IB_zbuffloat)) { + if (is_float) { + addzbuffloatImBuf(ibuf_left); + addzbuffloatImBuf(ibuf_right); + } + else { + addzbufImBuf(ibuf_left); + addzbufImBuf(ibuf_right); + } + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 1, + (int *)ibuf_left->zbuf, (int *)ibuf_right->zbuf, (int *)ibuf_stereo3d->zbuf, + ibuf_left->zbuf_float, ibuf_right->zbuf_float, ibuf_stereo3d->zbuf_float); + + imb_stereo3d_read_doit(&s3d_data, s3d); + } + + IMB_freeImBuf(ibuf_stereo3d); + + *r_ibuf_left = ibuf_left; + *r_ibuf_right = ibuf_right; +} + +static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) +{ + switch (s3d->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + imb_stereo3d_read_anaglyph(s3d_data, s3d->anaglyph_type); + break; + case S3D_DISPLAY_INTERLACE: + imb_stereo3d_read_interlace(s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0); + break; + case S3D_DISPLAY_SIDEBYSIDE: + imb_stereo3d_read_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0); + break; + case S3D_DISPLAY_TOPBOTTOM: + imb_stereo3d_read_topbottom(s3d_data); + break; + default: + break; + } +} diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 118f0405303..21f440a8232 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -338,7 +338,7 @@ ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, Im img = IMB_loadblend_thumb(path); } else { - img = IMB_loadiffname(path, IB_rect | IB_metadata, NULL); + img = IMB_loadiffname(path, IB_rect | IB_metadata | IB_thumbnail, NULL); } } diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 32100aa2288..3fee6688014 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -439,3 +439,15 @@ bool IMB_isanim(const char *filename) return (type && type != ANIM_SEQUENCE); } + +bool IMB_isfloat(ImBuf *ibuf) +{ + ImFileType *type; + + for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) { + if (type->ftype(type, ibuf)) { + return (type->flag & IM_FTYPE_FLOAT) != 0; + } + } + return false; +} diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c index 087330d10d2..5e9ccf4437e 100644 --- a/source/blender/imbuf/intern/writeimage.c +++ b/source/blender/imbuf/intern/writeimage.c @@ -43,19 +43,7 @@ static ImBuf *prepare_write_imbuf(ImFileType *type, ImBuf *ibuf) { - ImBuf *write_ibuf = ibuf; - - if (type->flag & IM_FTYPE_FLOAT) { - /* pass */ - } - else { - if (ibuf->rect == NULL && ibuf->rect_float) { - ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); - IMB_rect_from_float(ibuf); - } - } - - return write_ibuf; + return IMB_prepare_write_ImBuf((type->flag & IM_FTYPE_FLOAT), ibuf); } short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags) @@ -86,3 +74,19 @@ short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags) return false; } +ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf) +{ + ImBuf *write_ibuf = ibuf; + + if (isfloat) { + /* pass */ + } + else { + if (ibuf->rect == NULL && ibuf->rect_float) { + ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); + IMB_rect_from_float(ibuf); + } + } + + return write_ibuf; +} diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index dd66acf2781..6d6d33a6f1b 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -137,16 +137,14 @@ typedef struct Brush { float mask_stencil_dimension[2]; } Brush; -typedef struct PaletteColor -{ +typedef struct PaletteColor { struct PaletteColor *next, *prev; /* two values, one to store rgb, other to store values for sculpt/weight */ float rgb[3]; float value; } PaletteColor; -typedef struct Palette -{ +typedef struct Palette { ID id; /* pointer to individual colours */ @@ -156,14 +154,12 @@ typedef struct Palette int pad; } Palette; -typedef struct PaintCurvePoint -{ +typedef struct PaintCurvePoint { BezTriple bez; /* bezier handle */ float pressure; /* pressure on that point */ } PaintCurvePoint; -typedef struct PaintCurve -{ +typedef struct PaintCurve { ID id; PaintCurvePoint *points; /* points of curve */ int tot_points; diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index c67a356a708..6a9942aea66 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -44,6 +44,16 @@ struct Object; struct AnimData; struct Ipo; +/* ------------------------------------------- */ +/* Stereo Settings */ +typedef struct CameraStereoSettings { + float interocular_distance; + float convergence_distance; + short convergence_mode; + short pivot; + short pad, pad2; +} CameraStereoSettings; + typedef struct Camera { ID id; struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */ @@ -69,6 +79,9 @@ typedef struct Camera { char sensor_fit; char pad[7]; + + /* Stereo settings */ + struct CameraStereoSettings stereo; } Camera; /* **************** CAMERA ********************* */ @@ -123,6 +136,20 @@ enum { #define DEFAULT_SENSOR_WIDTH 32.0f #define DEFAULT_SENSOR_HEIGHT 18.0f +/* stereo->convergence_mode */ +enum { + CAM_S3D_OFFAXIS = 0, + CAM_S3D_PARALLEL = 1, + CAM_S3D_TOE = 2, +}; + +/* stereo->pivot */ +enum { + CAM_S3D_PIVOT_LEFT = 0, + CAM_S3D_PIVOT_RIGHT = 1, + CAM_S3D_PIVOT_CENTER = 2, +}; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 7444abb6d11..79980c6d2d5 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -43,7 +43,6 @@ struct MovieCache; struct RenderResult; struct GPUTexture; - /* ImageUser is in Texture, in Nodes, Background Image, Image Window, .... */ /* should be used in conjunction with an ID * to Image. */ typedef struct ImageUser { @@ -53,16 +52,34 @@ typedef struct ImageUser { int frames; /* total amount of frames to use */ int offset, sfra; /* offset within movie, start frame in global time */ char fie_ima, cycl; /* fields/image in movie, cyclic flag */ - char ok, pad; + char ok; + + char multiview_eye; /* multiview current eye - for internal use of drawing routines */ - short multi_index, layer, pass; /* listbase indices, for menu browsing or retrieve buffer */ + short multi_index, view, layer, pass; /* listbase indices, for menu browsing or retrieve buffer */ short flag; - - int pad2; + short passtype; } ImageUser; +typedef struct ImageAnim { + struct ImageAnim *next, *prev; + struct anim *anim; +} ImageAnim; + +typedef struct ImageView { + struct ImageView *next, *prev; + char name[64]; /* MAX_NAME */ + char filepath[1024]; /* 1024 = FILE_MAX */ +} ImageView; + +typedef struct ImagePackedFile { + struct ImagePackedFile *next, *prev; + struct PackedFile *packedfile; + char filepath[1024]; /* 1024 = FILE_MAX */ +} ImagePackedFile; + typedef struct RenderSlot { char name[64]; /* 64 = MAX_NAME */ } RenderSlot; @@ -72,6 +89,7 @@ typedef struct RenderSlot { #define IMA_ANIM_REFRESHED 2 /* #define IMA_DO_PREMUL 4 */ #define IMA_NEED_FRAME_RECALC 8 +#define IMA_SHOW_STEREO 16 typedef struct Image { ID id; @@ -82,13 +100,13 @@ typedef struct Image { struct GPUTexture *gputexture; /* not written in file */ /* sources from: */ - struct anim *anim; + ListBase anims; struct RenderResult *rr; struct RenderResult *renders[8]; /* IMA_MAX_RENDER_SLOT */ short render_slot, last_render_slot; - - short ok, flag; + + int flag; short source, type; int lastframe; @@ -99,14 +117,16 @@ typedef struct Image { unsigned int bindcode; /* only for current image... */ unsigned int *repbind; /* for repeat of parts of images */ - struct PackedFile *packedfile; + struct PackedFile *packedfile; /* deprecated */ + struct ListBase packedfiles; struct PreviewImage *preview; /* game engine tile animation */ float lastupdate; int lastused; short animspeed; - short pad2; + + short ok; /* for generated images */ int gen_x, gen_y; @@ -121,7 +141,14 @@ typedef struct Image { ColorManagedColorspaceSettings colorspace_settings; char alpha_mode; - char pad[7]; + char pad[5]; + + /* Multiview */ + char eye; /* for viewer node stereoscopy */ + char views_format; + ListBase views; + struct Stereo3dFormat *stereo3d_format; + RenderSlot render_slots[8]; /* 8 = IMA_MAX_RENDER_SLOT */ } Image; @@ -143,6 +170,9 @@ enum { IMA_VIEW_AS_RENDER = (1 << 11), IMA_IGNORE_ALPHA = (1 << 12), IMA_DEINTERLACE = (1 << 13), + IMA_USE_VIEWS = (1 << 14), + IMA_IS_STEREO = (1 << 15), + IMA_IS_MULTIVIEW = (1 << 16), /* similar to stereo, but a more general case */ }; #if (DNA_DEPRECATED_GCC_POISON == 1) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 52ce7a29977..37bc65999a9 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -263,12 +263,12 @@ typedef struct bNode { */ #define NODE_UPDATE 0xFFFF /* generic update flag (includes all others) */ #define NODE_UPDATE_ID 1 /* associated id data block has changed */ +#define NODE_UPDATE_OPERATOR 2 /* node update triggered from update operator */ /* Unique hash key for identifying node instances * Defined as a struct because DNA does not support other typedefs. */ -typedef struct bNodeInstanceKey -{ +typedef struct bNodeInstanceKey { unsigned int value; } bNodeInstanceKey; diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 4cfd4f33dcb..e320c7369cd 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -443,7 +443,7 @@ enum { OB_DUPLIVERTS = 1 << 4, OB_DUPLIROT = 1 << 5, OB_DUPLINOSPEED = 1 << 6, -/* OB_POWERTRACK = 1 << 7,*/ /*UNUSED*/ + OB_DUPLICALCDERIVED = 1 << 7, /* runtime, calculate derivedmesh for dupli before it's used */ OB_DUPLIGROUP = 1 << 8, OB_DUPLIFACES = 1 << 9, OB_DUPLIFACES_SCALE = 1 << 10, diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index e9dcf18500e..0b8bc8c5d00 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -47,6 +47,7 @@ extern "C" { #include "DNA_ID.h" #include "DNA_freestyle_types.h" #include "DNA_gpu_types.h" +#include "DNA_userdef_types.h" struct CurveMapping; struct Object; @@ -249,6 +250,73 @@ typedef enum ScenePassType { /* note, srl->passflag is treestore element 'nr' in outliner, short still... */ +/* View - MultiView */ +typedef struct SceneRenderView { + struct SceneRenderView *next, *prev; + + char name[64]; /* MAX_NAME */ + char suffix[64]; /* MAX_NAME */ + + int viewflag; + int pad[2]; + char pad2[4]; + +} SceneRenderView; + +/* srv->viewflag */ +#define SCE_VIEW_DISABLE (1<<0) + +/* scene.render.views_format */ +enum { + SCE_VIEWS_FORMAT_STEREO_3D = 0, + SCE_VIEWS_FORMAT_MULTIVIEW = 1, +}; + +/* ImageFormatData.views_output */ +enum { + R_IMF_VIEWS_INDIVIDUAL = 0, + R_IMF_VIEWS_STEREO_3D = 1, + R_IMF_VIEWS_MULTIVIEW = 2, +}; + +typedef struct Stereo3dFormat { + short flag; + char display_mode; /* encoding mode */ + char anaglyph_type; /* anaglyph scheme for the user display */ + char interlace_type; /* interlace type for the user display */ + char pad[3]; +} Stereo3dFormat; + +/* Stereo3dFormat.display_mode */ +typedef enum eStereoDisplayMode { + S3D_DISPLAY_ANAGLYPH = 0, + S3D_DISPLAY_INTERLACE = 1, + S3D_DISPLAY_PAGEFLIP = 2, + S3D_DISPLAY_SIDEBYSIDE = 3, + S3D_DISPLAY_TOPBOTTOM = 4, +} eStereoDisplayMode; + +/* Stereo3dFormat.flag */ +typedef enum eStereo3dFlag { + S3D_INTERLACE_SWAP = (1 << 0), + S3D_SIDEBYSIDE_CROSSEYED = (1 << 1), + S3D_SQUEEZED_FRAME = (1 << 2), +} eStereo3dFlag; + +/* Stereo3dFormat.anaglyph_type */ +typedef enum eStereo3dAnaglyphType { + S3D_ANAGLYPH_REDCYAN = 0, + S3D_ANAGLYPH_GREENMAGENTA = 1, + S3D_ANAGLYPH_YELLOWBLUE = 2, +} eStereo3dAnaglyphType; + +/* Stereo3dFormat.interlace_type */ +typedef enum eStereo3dInterlaceType { + S3D_INTERLACE_ROW = 0, + S3D_INTERLACE_COLUMN = 1, + S3D_INTERLACE_CHECKERBOARD = 2, +} eStereo3dInterlaceType; + /* *************************************************************** */ /* Generic image format settings, @@ -286,7 +354,11 @@ typedef struct ImageFormatData { char jp2_flag; char jp2_codec; - char pad[6]; + char pad[5]; + + /* Multiview */ + char views_format; + Stereo3dFormat stereo3d_format; /* color management */ ColorManagedViewSettings view_settings; @@ -616,6 +688,12 @@ typedef struct RenderData { int preview_start_resolution; int pad; + + /* MultiView */ + ListBase views; + short actview; + short views_format; + short pad8[2]; } RenderData; /* *************************************************************** */ @@ -810,6 +888,19 @@ enum { #define UV_SCULPT_TOOL_RELAX_LAPLACIAN 1 #define UV_SCULPT_TOOL_RELAX_HC 2 +/* Stereo Flags */ +#define STEREO_RIGHT_NAME "right" +#define STEREO_LEFT_NAME "left" +#define STEREO_RIGHT_SUFFIX "_R" +#define STEREO_LEFT_SUFFIX "_L" + +typedef enum StereoViews { + STEREO_LEFT_ID = 0, + STEREO_RIGHT_ID = 1, + STEREO_3D_ID = 2, + STEREO_MONO_ID = 3, +} StereoViews; + /* Markers */ typedef struct TimeMarker { @@ -1440,6 +1531,7 @@ typedef struct Scene { #define R_TEXNODE_PREVIEW 0x40000 #define R_VIEWPORT_PREVIEW 0x80000 #define R_EXR_CACHE_FILE 0x100000 +#define R_MULTIVIEW 0x200000 /* r->stamp */ #define R_STAMP_TIME 0x0001 diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index f6ce9107621..d45467373d6 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -53,6 +53,11 @@ struct MovieClip; /* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */ +typedef struct StripAnim { + struct StripAnim *next, *prev; + struct anim *anim; +} StripAnim; + typedef struct StripElem { char name[256]; int orig_width, orig_height; @@ -156,8 +161,7 @@ typedef struct Sequence { struct Object *scene_camera; /* override scene camera */ struct MovieClip *clip; /* for MOVIECLIP strips */ struct Mask *mask; /* for MASK strips */ - - struct anim *anim; /* for MOVIE strips */ + ListBase anims; /* for MOVIE strips */ float effect_fader; float speed_fader; @@ -187,7 +191,11 @@ typedef struct Sequence { int sfra; /* starting frame according to the timeline of the scene. */ char alpha_mode; - char pad[3]; + char pad[2]; + + /* Multiview */ + char views_format; + struct Stereo3dFormat *stereo3d_format; struct IDProperty *prop; @@ -382,8 +390,9 @@ enum { /* don't include Grease Pencil in OpenGL previews of Scene strips */ SEQ_SCENE_NO_GPENCIL = (1 << 28), + SEQ_USE_VIEWS = (1 << 29), /* access scene strips directly (like a metastrip) */ - SEQ_SCENE_STRIPS = (1 << 29), + SEQ_SCENE_STRIPS = (1 << 30), SEQ_INVALID_EFFECT = (1 << 31), }; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 302718dc40e..be9a7fc39d8 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -502,6 +502,9 @@ typedef struct SpaceSeq { struct bGPdata *gpd; /* grease-pencil data */ struct SequencerScopes scopes; /* different scoped displayed in space */ + + char multiview_eye; /* multiview current eye - for internal use */ + char pad2[7]; } SpaceSeq; @@ -553,8 +556,7 @@ typedef enum eSpaceSeq_Proxy_RenderSize { SEQ_PROXY_RENDER_SIZE_FULL = 100 } eSpaceSeq_Proxy_RenderSize; -typedef struct MaskSpaceInfo -{ +typedef struct MaskSpaceInfo { /* **** mask editing **** */ struct Mask *mask; /* draw options */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index afa1f03a8ca..348e895e0a9 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -447,7 +447,9 @@ typedef struct UserDef { short versions; short dbl_click_time; - + short click_timeout; + short pad3; + short gameflags; short wheellinescroll; int uiflag, uiflag2; @@ -465,7 +467,8 @@ typedef struct UserDef { char pad2[2]; short transopts; short menuthreshold1, menuthreshold2; - + int pad4; + struct ListBase themes; struct ListBase uifonts; struct ListBase uistyles; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 4f555b7303e..82016ba4f70 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -218,8 +218,10 @@ typedef struct View3D { /* drawflags, denoting state */ char zbuf, transp, xray; + char multiview_eye; /* multiview current eye - for internal use */ + /* built-in shader effects (eGPUFXFlags) */ - char pad3[5]; + char pad3[4]; /* note, 'fx_settings.dof' is currently _not_ allocated, * instead set (temporarily) from camera */ @@ -230,9 +232,22 @@ typedef struct View3D { /* XXX deprecated? */ struct bGPdata *gpd DNA_DEPRECATED; /* Grease-Pencil Data (annotation layers) */ + + /* multiview - stereo 3d */ + short stereo3d_flag; + char stereo3d_camera; + char pad4; + float stereo3d_convergence_factor; + float stereo3d_volume_alpha; + float stereo3d_convergence_alpha; } View3D; +/* View3D->stereo_flag (short) */ +#define V3D_S3D_DISPCAMERAS (1 << 0) +#define V3D_S3D_DISPPLANE (1 << 1) +#define V3D_S3D_DISPVOLUME (1 << 2) + /* View3D->flag (short) */ /*#define V3D_DISPIMAGE 1*/ /*UNUSED*/ #define V3D_DISPBGPICS 2 diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 74f6f5f75e8..14a7217e781 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -33,6 +33,7 @@ #include "DNA_listBase.h" #include "DNA_vec_types.h" +#include "DNA_userdef_types.h" #include "DNA_ID.h" @@ -56,6 +57,7 @@ struct PointerRNA; struct ReportList; struct Report; struct uiLayout; +struct Stereo3dFormat; #define OP_MAX_TYPENAME 64 #define KMAP_MAX_NAME 64 @@ -207,7 +209,7 @@ typedef struct wmWindow { struct wmIMEData *ime_data; int drawmethod, drawfail; /* internal for wm_draw.c only */ - void *drawdata; /* internal for wm_draw.c only */ + ListBase drawdata; /* internal for wm_draw.c only */ ListBase queue; /* all events (ghost level events were handled) */ ListBase handlers; /* window+screen handlers, handled last */ @@ -215,6 +217,8 @@ typedef struct wmWindow { ListBase subwindows; /* opengl stuff for sub windows, see notes in wm_subwindow.c */ ListBase gesture; /* gesture stuff */ + + struct Stereo3dFormat *stereo3d_format; /* properties for stereoscopic displays */ } wmWindow; #ifdef ime_data @@ -250,7 +254,8 @@ typedef struct wmKeyMapItem { /* event */ short type; /* event code itself */ - short val; /* KM_ANY, KM_PRESS, KM_NOTHING etc */ + short val; /* NOTE: other than event->val this can be the value itself + * (KM_ANY, KM_PRESS, etc) AND the clicktype (KM_DBL_CLICK, KM_HOLD, etc) */ short shift, ctrl, alt, oskey; /* oskey is apple or windowskey, value denotes order of pressed */ short keymodifier; /* rawkey modifier */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index b5da3db21f5..a62a7489814 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -175,6 +175,7 @@ extern StructRNA RNA_CompositorNodeSepYCCA; extern StructRNA RNA_CompositorNodeSepYUVA; extern StructRNA RNA_CompositorNodeSetAlpha; extern StructRNA RNA_CompositorNodeSplitViewer; +extern StructRNA RNA_CompositorNodeSwitchView; extern StructRNA RNA_CompositorNodeTexture; extern StructRNA RNA_CompositorNodeTime; extern StructRNA RNA_CompositorNodeTonemap; @@ -570,6 +571,7 @@ extern StructRNA RNA_SpeedControlSequence; extern StructRNA RNA_Spline; extern StructRNA RNA_SplineIKConstraint; extern StructRNA RNA_SpotLamp; +extern StructRNA RNA_Stereo3dDisplay; extern StructRNA RNA_StretchToConstraint; extern StructRNA RNA_StringProperty; extern StructRNA RNA_Struct; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 70758191671..66eec0b0147 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -73,6 +73,13 @@ extern EnumPropertyItem normal_space_items[]; extern EnumPropertyItem normal_swizzle_items[]; extern EnumPropertyItem bake_save_mode_items[]; +extern EnumPropertyItem views_format_items[]; +extern EnumPropertyItem views_format_multilayer_items[]; +extern EnumPropertyItem views_format_multiview_items[]; +extern EnumPropertyItem stereo3d_display_items[]; +extern EnumPropertyItem stereo3d_anaglyph_type_items[]; +extern EnumPropertyItem stereo3d_interlace_type_items[]; + extern EnumPropertyItem exr_codec_items[]; extern EnumPropertyItem color_sets_items[]; diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b8eda09ca8c..a1e2ac373b6 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -601,12 +601,12 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_function_ui_description(func, "Clear the user count of a datablock so its not saved, " "on reload the data will be removed"); - func = RNA_def_function(srna, "animation_data_create", "BKE_id_add_animdata"); + func = RNA_def_function(srna, "animation_data_create", "BKE_animdata_add_id"); RNA_def_function_ui_description(func, "Create animation data to this ID, note that not all ID types support this"); parm = RNA_def_pointer(func, "anim_data", "AnimData", "", "New animation data or NULL"); RNA_def_function_return(func, parm); - func = RNA_def_function(srna, "animation_data_clear", "BKE_free_animdata"); + func = RNA_def_function(srna, "animation_data_clear", "BKE_animdata_free"); RNA_def_function_ui_description(func, "Clear animation on this this ID"); func = RNA_def_function(srna, "update_tag", "rna_ID_update_tag"); diff --git a/source/blender/makesrna/intern/rna_cache_library.c b/source/blender/makesrna/intern/rna_cache_library.c index 4de85f5895d..f7791ddc7a2 100644 --- a/source/blender/makesrna/intern/rna_cache_library.c +++ b/source/blender/makesrna/intern/rna_cache_library.c @@ -132,7 +132,7 @@ static void rna_CacheLibraryModifier_name_set(PointerRNA *ptr, const char *value } /* fix all the animation data which may link to this */ - BKE_all_animdata_fix_paths_rename(NULL, "modifiers", oldname, md->name); + BKE_animdata_fix_paths_rename_all(NULL, "modifiers", oldname, md->name); } static char *rna_CacheLibraryModifier_path(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 31e991dd2b6..2baaa5f7cec 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -89,6 +89,65 @@ static void rna_Camera_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointer #else +static void rna_def_camera_stereo_data(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem convergence_mode_items[] = { + {CAM_S3D_OFFAXIS, "OFFAXIS", 0, "Off-Axis", "Off-axis frustums converging in a plane"}, + {CAM_S3D_PARALLEL, "PARALLEL", 0, "Parallel", "Parallel cameras with no convergence"}, + {CAM_S3D_TOE, "TOE", 0, "Toe-in", "Rotated cameras, looking at the convergence distance"}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem pivot_items[] = { + {CAM_S3D_PIVOT_LEFT, "LEFT", 0, "Left", ""}, + {CAM_S3D_PIVOT_RIGHT, "RIGHT", 0, "Right", ""}, + {CAM_S3D_PIVOT_CENTER, "CENTER", 0, "Center", ""}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "CameraStereoData", NULL); + RNA_def_struct_sdna(srna, "CameraStereoSettings"); + RNA_def_struct_nested(brna, srna, "Camera"); + RNA_def_struct_ui_text(srna, "Stereo", "Stereoscopy settings for a Camera datablock"); + + prop = RNA_def_property(srna, "convergence_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, convergence_mode_items); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "pivot", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, pivot_items); + RNA_def_property_ui_text(prop, "Pivot", ""); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "interocular_distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.f, 1, 2); + RNA_def_property_ui_text(prop, "Interocular Distance", + "Set the distance between the eyes - the stereo plane distance / 30 should be fine"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "convergence_distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 0.00001f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 15.f, 1, 2); + RNA_def_property_ui_text(prop, "Convergence Plane Distance", + "The converge point for the stereo cameras " + "(often the distance between a projector and the projection screen)"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "viewport_convergence", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "convergence_distance"); + RNA_def_property_range(prop, 0.00001f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 15.f, 1, 2); + RNA_def_property_ui_text(prop, "Viewport Convergence", + "Preview convergence distance for the stereo effect in the viewport " + "(it does not affect the render!)"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); +} + void RNA_def_camera(BlenderRNA *brna) { StructRNA *srna; @@ -241,6 +300,13 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_ui_text(prop, "DOF Distance", "Distance to the focus point for depth of field"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + /* Stereo Settings */ + prop = RNA_def_property(srna, "stereo", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_pointer_sdna(prop, NULL, "stereo"); + RNA_def_property_struct_type(prop, "CameraStereoData"); + RNA_def_property_ui_text(prop, "Stereo", ""); + /* flag */ prop = RNA_def_property(srna, "show_limits", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOWLIMITS); @@ -265,7 +331,8 @@ void RNA_def_camera(BlenderRNA *brna) prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOW_SAFE_CENTER); - RNA_def_property_ui_text(prop, "Show Center-cut safe areas", "Show safe areas to fit content in a different aspect ratio"); + RNA_def_property_ui_text(prop, "Show Center-cut safe areas", + "Show safe areas to fit content in a different aspect ratio"); RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); prop = RNA_def_property(srna, "show_name", PROP_BOOLEAN, PROP_NONE); @@ -298,6 +365,12 @@ void RNA_def_camera(BlenderRNA *brna) rna_def_animdata_common(srna); + /* Nested Data */ + RNA_define_animate_sdna(true); + + /* *** Animated *** */ + rna_def_camera_stereo_data(brna); + /* Camera API */ RNA_api_camera(srna); } diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index e6193b31260..0c871317d04 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -620,10 +620,8 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *UNUSED(bmain) } if (seq_found) { - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } + BKE_sequence_free_anim(seq); + if (seq->strip->proxy && seq->strip->proxy->anim) { IMB_free_anim(seq->strip->proxy->anim); seq->strip->proxy->anim = NULL; @@ -635,10 +633,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *UNUSED(bmain) else { SEQ_BEGIN(scene->ed, seq); { - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } + BKE_sequence_free_anim(seq); } SEQ_END; diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index e4aa21867e7..2cff8cfb376 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -241,7 +241,7 @@ static void rna_Constraint_name_set(PointerRNA *ptr, const char *value) } /* fix all the animation data which may link to this */ - BKE_all_animdata_fix_paths_rename(NULL, "constraints", oldname, con->name); + BKE_animdata_fix_paths_rename_all(NULL, "constraints", oldname, con->name); } static char *rna_Constraint_path(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index cf2973c49a7..bac5c4aadba 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -35,6 +35,7 @@ #include "BKE_depsgraph.h" #include "BKE_image.h" +#include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -137,6 +138,23 @@ static void rna_Image_colormanage_update(Main *UNUSED(bmain), Scene *UNUSED(scen WM_main_add_notifier(NC_IMAGE | NA_EDITED, &ima->id); } +static void rna_Image_views_format_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +{ + Image *ima = ptr->id.data; + ImBuf *ibuf; + void *lock; + + ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + + if (ibuf) { + ImageUser iuser = {NULL}; + iuser.scene = scene; + BKE_image_signal(ima, &iuser, IMA_SIGNAL_FREE); + } + + BKE_image_release_ibuf(ima, ibuf, lock); +} + static void rna_ImageUser_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) { ImageUser *iuser = ptr->data; @@ -295,17 +313,16 @@ static int rna_Image_frame_duration_get(PointerRNA *ptr) Image *ima = ptr->id.data; int duration = 1; - if (!ima->anim) { + if (BKE_image_has_anim(ima)) { + duration = IMB_anim_get_duration(((ImageAnim *)ima->anims.first)->anim, IMB_TC_RECORD_RUN); + } + else { /* acquire ensures ima->anim is set, if possible! */ void *lock; ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); BKE_image_release_ibuf(ima, ibuf, lock); } - if (ima->anim) { - duration = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN); - } - return duration; } @@ -409,6 +426,19 @@ static int rna_Image_is_float_get(PointerRNA *ptr) return is_float; } +static PointerRNA rna_Image_packed_file_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->id.data; + + if (BKE_image_has_packedfile(ima)) { + ImagePackedFile *imapf = ima->packedfiles.first; + return rna_pointer_inherit_refine(ptr, &RNA_PackedFile, imapf->packedfile); + } + else { + return PointerRNA_NULL; + } +} + static void rna_Image_render_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { Image *image = (Image *)ptr->id.data; @@ -512,6 +542,32 @@ static void rna_def_imageuser(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "pass"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ RNA_def_property_ui_text(prop, "Pass", "Pass in multilayer image"); + + prop = RNA_def_property(srna, "multilayer_view", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "view"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ + RNA_def_property_ui_text(prop, "View", "View in multilayer image"); +} + +/* image.packed_files */ +static void rna_def_image_packed_files(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ImagePackedFile", NULL); + RNA_def_struct_sdna(srna, "ImagePackedFile"); + + prop = RNA_def_property(srna, "packed_file", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "packedfile"); + RNA_def_property_ui_text(prop, "Packed File", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "filepath"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); } static void rna_def_render_slot(BlenderRNA *brna) @@ -613,15 +669,23 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL); prop = RNA_def_property(srna, "packed_file", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "PackedFile"); RNA_def_property_pointer_sdna(prop, NULL, "packedfile"); - RNA_def_property_ui_text(prop, "Packed File", ""); - + RNA_def_property_pointer_funcs(prop, "rna_Image_packed_file_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Packed File", "First packed file of the image"); + + prop = RNA_def_property(srna, "packed_files", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "packedfiles", NULL); + RNA_def_property_struct_type(prop, "ImagePackedFile"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Packed Files", "Collection of packed images"); + prop = RNA_def_property(srna, "field_order", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, prop_field_order_items); RNA_def_property_ui_text(prop, "Field Order", "Order of video fields (select which lines are displayed first)"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL); - + /* booleans */ prop = RNA_def_property(srna, "use_fields", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_FIELDS); @@ -645,6 +709,21 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Deinterlace", "Deinterlace movie file on load"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_reload_update"); + prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_USE_VIEWS); + RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_views_format_update"); + + prop = RNA_def_property(srna, "is_stereo_3d", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_IS_STEREO); + RNA_def_property_ui_text(prop, "Stereo 3D", "Image has left and right views"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "is_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_IS_MULTIVIEW); + RNA_def_property_ui_text(prop, "Multiple Views", "Image has more than one view"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_Image_dirty_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -825,6 +904,19 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Alpha Mode", "Representation of alpha information in the RGBA pixels"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_colormanage_update"); + /* multiview */ + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "views_format"); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_ui_text(prop, "Views Format", "Mode to load image views"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_views_format_update"); + + prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dFormat"); + RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d"); + RNA_api_image(srna); } @@ -834,6 +926,7 @@ void RNA_def_image(BlenderRNA *brna) rna_def_render_slots(brna); rna_def_image(brna); rna_def_imageuser(brna); + rna_def_image_packed_files(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index b9a1237c1f2..47be7bec432 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -167,7 +167,7 @@ static void rna_Image_pack( image->packedfile = newPackedFileMemory(data_dup, data_len); } else { - image->packedfile = newPackedFile(reports, image->name, ID_BLEND_PATH(bmain, &image->id)); + BKE_image_packfiles(reports, image, ID_BLEND_PATH(bmain, &image->id)); } } @@ -288,6 +288,11 @@ static void rna_Image_filepath_from_user(Image *image, ImageUser *image_user, ch BKE_image_user_file_path(image_user, image, filepath); } +static void rna_Image_buffers_free(Image *image) +{ + BKE_image_free_buffers(image); +} + #else void RNA_api_image(StructRNA *srna) @@ -374,6 +379,9 @@ void RNA_api_image(StructRNA *srna) RNA_def_property_flag(parm, PROP_THICK_WRAP); /* needed for string return value */ RNA_def_function_output(func, parm); + func = RNA_def_function(srna, "buffers_free", "rna_Image_buffers_free"); + RNA_def_function_ui_description(func, "Free the image buffers from memory"); + /* TODO, pack/unpack, maybe should be generic functions? */ } diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index fe62df57eed..c2ae113cdd9 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -90,7 +90,7 @@ static void rna_ShapeKey_name_set(PointerRNA *ptr, const char *value) } /* fix all the animation data which may link to this */ - BKE_all_animdata_fix_paths_rename(NULL, "key_blocks", oldname, kb->name); + BKE_animdata_fix_paths_rename_all(NULL, "key_blocks", oldname, kb->name); } static float rna_ShapeKey_frame_get(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index e6347547848..7db21d0f989 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -401,10 +401,10 @@ static void rna_Main_lamps_remove(Main *bmain, ReportList *reports, PointerRNA * } } -static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, int alpha, int float_buffer) +static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, int alpha, int float_buffer, int stereo3d) { float color[4] = {0.0, 0.0, 0.0, 1.0}; - Image *image = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, float_buffer, 0, color); + Image *image = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d); id_us_min(&image->id); return image; } @@ -1269,6 +1269,7 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_flag(parm, PROP_REQUIRED); RNA_def_boolean(func, "alpha", 0, "Alpha", "Use alpha channel"); RNA_def_boolean(func, "float_buffer", 0, "Float Buffer", "Create an image with floating point color"); + RNA_def_boolean(func, "stereo3d", 0, "Stereo 3D", "Create left and right views"); /* return type */ parm = RNA_def_pointer(func, "image", "Image", "", "New image datablock"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 76167a9f4c6..b28dc2d6eb6 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -410,7 +410,7 @@ static void rna_Modifier_name_set(PointerRNA *ptr, const char *value) } /* fix all the animation data which may link to this */ - BKE_all_animdata_fix_paths_rename(NULL, "modifiers", oldname, md->name); + BKE_animdata_fix_paths_rename_all(NULL, "modifiers", oldname, md->name); } static char *rna_Modifier_path(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 8b16173af50..e4c72f6cd0e 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -1552,7 +1552,7 @@ static void rna_Node_name_set(PointerRNA *ptr, const char *value) nodeUniqueName(ntree, node); /* fix all the animation data which may link to this */ - BKE_all_animdata_fix_paths_rename(NULL, "nodes", oldname, node->name); + BKE_animdata_fix_paths_rename_all(NULL, "nodes", oldname, node->name); } static bNodeSocket *rna_Node_inputs_new(ID *id, bNode *node, ReportList *reports, const char *type, const char *name, const char *identifier) @@ -2619,6 +2619,70 @@ static EnumPropertyItem *rna_Node_image_layer_itemf(bContext *UNUSED(C), Pointer return item; } +static int rna_Node_image_has_layers_get(PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + Image *ima = (Image *)node->id; + + if (!ima || !(ima->rr)) return 0; + + return RE_layers_have_name(ima->rr); +} + +static int rna_Node_image_has_views_get(PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + Image *ima = (Image *)node->id; + + if (!ima || !(ima->rr)) return 0; + + return BLI_listbase_count_ex(&ima->rr->views, 2) > 1; +} + +static EnumPropertyItem *renderresult_views_add_enum(RenderView *rv) +{ + EnumPropertyItem *item = NULL; + EnumPropertyItem tmp = {0, "ALL", 0, "All", ""}; + int i = 1, totitem = 0; + + /* option to use all views */ + RNA_enum_item_add(&item, &totitem, &tmp); + + while (rv) { + tmp.identifier = rv->name; + /* little trick: using space char instead empty string makes the item selectable in the dropdown */ + if (rv->name[0] == '\0') + tmp.name = " "; + else + tmp.name = rv->name; + tmp.value = i++; + RNA_enum_item_add(&item, &totitem, &tmp); + rv = rv->next; + } + + RNA_enum_item_end(&item, &totitem); + + return item; +} + +static EnumPropertyItem *rna_Node_image_view_itemf(bContext *UNUSED(C), PointerRNA *ptr, + PropertyRNA *UNUSED(prop), bool *free) +{ + bNode *node = (bNode *)ptr->data; + Image *ima = (Image *)node->id; + EnumPropertyItem *item = NULL; + RenderView *rv; + + if (!ima || !(ima->rr)) return NULL; + + rv = ima->rr->views.first; + item = renderresult_views_add_enum(rv); + + *free = true; + + return item; +} + static EnumPropertyItem *rna_Node_scene_layer_itemf(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { @@ -2993,6 +3057,11 @@ static EnumPropertyItem prop_image_layer_items[] = { {0, NULL, 0, NULL, NULL} }; +static EnumPropertyItem prop_image_view_items[] = { + { 0, "ALL", 0, "All", ""}, + {0, NULL, 0, NULL, NULL} +}; + static EnumPropertyItem prop_scene_layer_items[] = { { 0, "PLACEHOLDER", 0, "Placeholder", ""}, {0, NULL, 0, NULL, NULL} @@ -4448,6 +4517,24 @@ static void def_node_image_user(StructRNA *srna) RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); RNA_def_property_ui_text(prop, "Layer", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_image_layer_update"); + + prop = RNA_def_property(srna, "has_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_Node_image_has_layers_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Has Layers", "True if this image has any named layer"); + + prop = RNA_def_property(srna, "view", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "view"); + RNA_def_property_enum_items(prop, prop_image_view_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Node_image_view_itemf"); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + RNA_def_property_ui_text(prop, "View", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "has_views", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_Node_image_has_views_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Has View", "True if this image has multiple views"); } static void def_cmp_image(StructRNA *srna) @@ -5996,6 +6083,16 @@ static void def_cmp_switch(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_cmp_switch_view(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "check", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "custom1", 0); + RNA_def_property_ui_text(prop, "Switch", "Off: first socket, On: second socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_cmp_colorcorrection(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 124a83a5f30..94fabb857e3 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -27,6 +27,7 @@ #include <stdlib.h> #include "DNA_scene_types.h" +#include "DNA_object_types.h" #include "BLI_utildefines.h" #include "BLI_path_util.h" @@ -322,38 +323,35 @@ static PointerRNA rna_RenderEngine_render_get(PointerRNA *ptr) } } -static void rna_RenderResult_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +static PointerRNA rna_RenderEngine_camera_override_get(PointerRNA *ptr) { - RenderResult *rr = (RenderResult *)ptr->data; - rna_iterator_listbase_begin(iter, &rr->layers, NULL); -} + RenderEngine *engine = (RenderEngine *)ptr->data; -static void rna_RenderLayer_passes_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ - RenderLayer *rl = (RenderLayer *)ptr->data; - rna_iterator_listbase_begin(iter, &rl->passes, NULL); + if (engine->re) { + Object *cam = RE_GetCamera(engine->re); + return rna_pointer_inherit_refine(ptr, &RNA_Object, cam); + } + else { + return rna_pointer_inherit_refine(ptr, &RNA_Object, engine->camera_override); + } } -static int rna_RenderLayer_rect_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static void rna_RenderResult_views_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { - RenderLayer *rl = (RenderLayer *)ptr->data; - - length[0] = rl->rectx * rl->recty; - length[1] = 4; - - return length[0] * length[1]; + RenderResult *rr = (RenderResult *)ptr->data; + rna_iterator_listbase_begin(iter, &rr->views, NULL); } -static void rna_RenderLayer_rect_get(PointerRNA *ptr, float *values) +static void rna_RenderResult_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { - RenderLayer *rl = (RenderLayer *)ptr->data; - memcpy(values, rl->rectf, sizeof(float) * rl->rectx * rl->recty * 4); + RenderResult *rr = (RenderResult *)ptr->data; + rna_iterator_listbase_begin(iter, &rr->layers, NULL); } -void rna_RenderLayer_rect_set(PointerRNA *ptr, const float *values) +static void rna_RenderLayer_passes_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { RenderLayer *rl = (RenderLayer *)ptr->data; - memcpy(rl->rectf, values, sizeof(float) * rl->rectx * rl->recty * 4); + rna_iterator_listbase_begin(iter, &rl->passes, NULL); } static int rna_RenderPass_rect_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) @@ -384,6 +382,18 @@ static PointerRNA rna_BakePixel_next_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_BakePixel, bp + 1); } +static RenderPass *rna_RenderPass_find_by_type(RenderLayer *rl, int passtype, const char *view) +{ + RenderPass *rp; + for (rp = rl->passes.first; rp; rp = rp->next) { + if (rp->passtype == passtype) { + if (STREQ(rp->view, view)) + return rp; + } + } + return NULL; +} + #else /* RNA_RUNTIME */ static void rna_def_render_engine(BlenderRNA *brna) @@ -467,6 +477,7 @@ static void rna_def_render_engine(BlenderRNA *brna) prop = RNA_def_int(func, "h", 0, 0, INT_MAX, "Height", "", 0, INT_MAX); RNA_def_property_flag(prop, PROP_REQUIRED); RNA_def_string(func, "layer", NULL, 0, "Layer", "Single layer to get render result for"); /* NULL ok here */ + RNA_def_string(func, "view", NULL, 0, "View", "Single view to get render result for"); /* NULL ok here */ prop = RNA_def_pointer(func, "result", "RenderResult", "Result", ""); RNA_def_function_return(func, prop); @@ -487,6 +498,22 @@ static void rna_def_render_engine(BlenderRNA *brna) prop = RNA_def_boolean(func, "do_break", 0, "Break", ""); RNA_def_function_return(func, prop); + func = RNA_def_function(srna, "active_view_set", "RE_engine_active_view_set"); + RNA_def_string(func, "view", NULL, 0, "View", "Single view to set as active"); /* NULL ok here */ + RNA_def_property_flag(prop, PROP_REQUIRED); + + func = RNA_def_function(srna, "camera_shift_x", "RE_engine_get_camera_shift_x"); + prop = RNA_def_pointer(func, "camera", "Object", "", ""); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop = RNA_def_float(func, "shift_x", 0.0f, 0.0f, FLT_MAX, "Shift X", "", 0.0f, FLT_MAX); + RNA_def_function_return(func, prop); + + func = RNA_def_function(srna, "camera_model_matrix", "RE_engine_get_camera_model_matrix"); + prop = RNA_def_pointer(func, "camera", "Object", "", ""); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop = RNA_def_float_matrix(func, "r_model_matrix", 4, 4, NULL, 0.0f, 0.0f, "Model Matrix", "Normalized camera model matrix", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_REQUIRED); + func = RNA_def_function(srna, "update_stats", "RE_engine_update_stats"); RNA_def_function_ui_description(func, "Update and signal to redraw render status text"); prop = RNA_def_string(func, "stats", NULL, 0, "Stats", ""); @@ -548,7 +575,7 @@ static void rna_def_render_engine(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", RE_ENGINE_PREVIEW); prop = RNA_def_property(srna, "camera_override", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "camera_override"); + RNA_def_property_pointer_funcs(prop, "rna_RenderEngine_camera_override_get", NULL, NULL, NULL); RNA_def_property_struct_type(prop, "Object"); prop = RNA_def_property(srna, "layer_override", PROP_BOOLEAN, PROP_LAYER_MEMBER); @@ -647,9 +674,56 @@ static void rna_def_render_result(BlenderRNA *brna) "rna_iterator_listbase_end", "rna_iterator_listbase_get", NULL, NULL, NULL, NULL); + parm = RNA_def_property(srna, "views", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(parm, "RenderView"); + RNA_def_property_collection_funcs(parm, "rna_RenderResult_views_begin", "rna_iterator_listbase_next", + "rna_iterator_listbase_end", "rna_iterator_listbase_get", + NULL, NULL, NULL, NULL); + + RNA_define_verify_sdna(1); +} + +static void rna_def_render_view(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "RenderView", NULL); + RNA_def_struct_ui_text(srna, "Render View", ""); + + RNA_define_verify_sdna(0); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "name"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_struct_name_property(srna, prop); + RNA_define_verify_sdna(1); } +static void rna_def_render_passes(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "RenderPasses"); + srna = RNA_def_struct(brna, "RenderPasses", NULL); + RNA_def_struct_sdna(srna, "RenderLayer"); + RNA_def_struct_ui_text(srna, "Render Passes", "Collection of render passes"); + + func = RNA_def_function(srna, "find_by_type", "rna_RenderPass_find_by_type"); + RNA_def_function_ui_description(func, "Get the render pass for a given type and view"); + parm = RNA_def_enum(func, "pass_type", render_pass_type_items, SCE_PASS_COMBINED, "Pass", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_string(func, "view", NULL, 0, "View", "Render view to get pass from"); /* NULL ok here */ + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_pointer(func, "render_pass", "RenderPass", "", "The matching render pass"); + RNA_def_function_return(func, parm); + +} + static void rna_def_render_layer(BlenderRNA *brna) { StructRNA *srna; @@ -679,12 +753,7 @@ static void rna_def_render_layer(BlenderRNA *brna) RNA_def_property_collection_funcs(prop, "rna_RenderLayer_passes_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", NULL, NULL, NULL, NULL); - - prop = RNA_def_property(srna, "rect", PROP_FLOAT, PROP_NONE); - RNA_def_property_flag(prop, PROP_DYNAMIC); - RNA_def_property_multi_array(prop, 2, NULL); - RNA_def_property_dynamic_array_funcs(prop, "rna_RenderLayer_rect_get_length"); - RNA_def_property_float_funcs(prop, "rna_RenderLayer_rect_get", "rna_RenderLayer_rect_set", NULL); + rna_def_render_passes(brna, prop); RNA_define_verify_sdna(1); } @@ -728,6 +797,10 @@ static void rna_def_render_pass(BlenderRNA *brna) RNA_def_property_dynamic_array_funcs(prop, "rna_RenderPass_rect_get_length"); RNA_def_property_float_funcs(prop, "rna_RenderPass_rect_get", "rna_RenderPass_rect_set", NULL); + prop = RNA_def_property(srna, "view_id", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "view_id"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "debug_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "debug_type"); RNA_def_property_enum_items(prop, render_pass_debug_type_items); @@ -783,6 +856,7 @@ void RNA_def_render(BlenderRNA *brna) { rna_def_render_engine(brna); rna_def_render_result(brna); + rna_def_render_view(brna); rna_def_render_layer(brna); rna_def_render_pass(brna); rna_def_render_bake_pixel(brna); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 700d1aa91ea..3bcac9c6696 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -36,6 +36,8 @@ #include "DNA_userdef_types.h" #include "DNA_world_types.h" +#include "IMB_imbuf_types.h" + #include "BLI_math.h" #include "BLF_translation.h" @@ -217,13 +219,13 @@ EnumPropertyItem snap_uv_element_items[] = { #endif #ifdef WITH_OPENEXR -# define R_IMF_ENUM_EXR_MULTI {R_IMF_IMTYPE_MULTILAYER, "OPEN_EXR_MULTILAYER", ICON_FILE_IMAGE, \ +# define R_IMF_ENUM_EXR_MULTILAYER {R_IMF_IMTYPE_MULTILAYER, "OPEN_EXR_MULTILAYER", ICON_FILE_IMAGE, \ "OpenEXR MultiLayer", \ "Output image in multilayer OpenEXR format"}, # define R_IMF_ENUM_EXR {R_IMF_IMTYPE_OPENEXR, "OPEN_EXR", ICON_FILE_IMAGE, "OpenEXR", \ "Output image in OpenEXR format"}, #else -# define R_IMF_ENUM_EXR_MULTI +# define R_IMF_ENUM_EXR_MULTILAYER # define R_IMF_ENUM_EXR #endif @@ -252,7 +254,7 @@ EnumPropertyItem snap_uv_element_items[] = { {0, "", 0, " ", NULL}, \ R_IMF_ENUM_CINEON \ R_IMF_ENUM_DPX \ - R_IMF_ENUM_EXR_MULTI \ + R_IMF_ENUM_EXR_MULTILAYER \ R_IMF_ENUM_EXR \ R_IMF_ENUM_HDR \ R_IMF_ENUM_TIFF \ @@ -336,6 +338,64 @@ EnumPropertyItem bake_save_mode_items[] = { {0, NULL, 0, NULL, NULL} }; +#define R_IMF_VIEWS_ENUM_IND {R_IMF_VIEWS_INDIVIDUAL, "INDIVIDUAL", 0, "Individual", \ + "Individual files for each view with the prefix as defined by the scene views"}, +#define R_IMF_VIEWS_ENUM_S3D {R_IMF_VIEWS_STEREO_3D, "STEREO_3D", 0, "Stereo 3D", \ + "Single file with an encoded stereo pair"}, +#define R_IMF_VIEWS_ENUM_MV {R_IMF_VIEWS_MULTIVIEW, "MULTIVIEW", 0, "Multi-View", "Single file with all the views"}, + +EnumPropertyItem views_format_items[] = { + R_IMF_VIEWS_ENUM_IND + R_IMF_VIEWS_ENUM_S3D + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem views_format_multilayer_items[] = { + R_IMF_VIEWS_ENUM_IND + R_IMF_VIEWS_ENUM_MV + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem views_format_multiview_items[] = { + R_IMF_VIEWS_ENUM_IND + R_IMF_VIEWS_ENUM_S3D + R_IMF_VIEWS_ENUM_MV + {0, NULL, 0, NULL, NULL} +}; + +#undef R_IMF_VIEWS_ENUM_IND +#undef R_IMF_VIEWS_ENUM_S3D +#undef R_IMF_VIEWS_ENUM_MV + +EnumPropertyItem stereo3d_display_items[] = { + {S3D_DISPLAY_ANAGLYPH, "ANAGLYPH", 0, "Anaglyph", + "Render views for left and right eyes as two differently filtered colors in a single image " + "(anaglyph glasses are required)"}, + {S3D_DISPLAY_INTERLACE, "INTERLACE", 0, "Interlace", + "Render views for left and right eyes interlaced in a single image (3D-ready monitor is required)"}, +#ifdef DEBUG /* MULTIVIEW_TODO: quadbuffer mode not fully working */ + {S3D_DISPLAY_PAGEFLIP, "TIMESEQUENTIAL", 0, "Time Sequential", + "Render alternate eyes (also known as page flip, quad buffer support in the graphic card is required)"}, +#endif + {S3D_DISPLAY_SIDEBYSIDE, "SIDEBYSIDE", 0, "Side-by-Side", "Render views for left and right eyes side-by-side"}, + {S3D_DISPLAY_TOPBOTTOM, "TOPBOTTOM", 0, "Top-Bottom", "Render views for left and right eyes one above another"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem stereo3d_anaglyph_type_items[] = { + {S3D_ANAGLYPH_REDCYAN, "RED_CYAN", 0, "Red-Cyan", ""}, + {S3D_ANAGLYPH_GREENMAGENTA, "GREEN_MAGENTA", 0, "Green-Magenta", ""}, + {S3D_ANAGLYPH_YELLOWBLUE, "YELLOW_BLUE", 0, "Yellow-Blue", ""}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem stereo3d_interlace_type_items[] = { + {S3D_INTERLACE_ROW, "ROW_INTERLEAVED", 0, "Row Interleaved", ""}, + {S3D_INTERLACE_COLUMN, "COLUMN_INTERLEAVED", 0, "Column Interleaved", ""}, + {S3D_INTERLACE_CHECKERBOARD, "CHECKERBOARD_INTERLEAVED", 0, "Checkerboard Interleaved", ""}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME #include "DNA_anim_types.h" @@ -723,6 +783,26 @@ static void rna_Scene_all_keyingsets_next(CollectionPropertyIterator *iter) iter->valid = (internal->link != NULL); } +static int rna_RenderSettings_stereoViews_skip(CollectionPropertyIterator *iter, void *UNUSED(data)) +{ + ListBaseIterator *internal = &iter->internal.listbase; + SceneRenderView *srv = (SceneRenderView *)internal->link; + + if ((STREQ(srv->name, STEREO_LEFT_NAME)) || + (STREQ(srv->name, STEREO_RIGHT_NAME))) + { + return 0; + } + + return 1; +}; + +static void rna_RenderSettings_stereoViews_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + RenderData *rd = (RenderData *)ptr->data; + rna_iterator_listbase_begin(iter, &rd->views, rna_RenderSettings_stereoViews_skip); +} + static char *rna_RenderSettings_path(PointerRNA *UNUSED(ptr)) { return BLI_sprintfN("render"); @@ -825,8 +905,8 @@ static void rna_ImageFormatSettings_file_format_set(PointerRNA *ptr, int value) } } -static EnumPropertyItem *rna_ImageFormatSettings_file_format_itemf(bContext *UNUSED(C), PointerRNA *ptr, - PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) +static EnumPropertyItem *rna_ImageFormatSettings_file_format_itemf( + bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) { ID *id = ptr->id.data; if (id && GS(id->name) == ID_SCE) { @@ -837,8 +917,8 @@ static EnumPropertyItem *rna_ImageFormatSettings_file_format_itemf(bContext *UNU } } -static EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf(bContext *UNUSED(C), PointerRNA *ptr, - PropertyRNA *UNUSED(prop), bool *r_free) +static EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf( + bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { ImageFormatData *imf = (ImageFormatData *)ptr->data; ID *id = ptr->id.data; @@ -882,8 +962,8 @@ static EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf(bContext *UNUS } } -static EnumPropertyItem *rna_ImageFormatSettings_color_depth_itemf(bContext *UNUSED(C), PointerRNA *ptr, - PropertyRNA *UNUSED(prop), bool *r_free) +static EnumPropertyItem *rna_ImageFormatSettings_color_depth_itemf( + bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { ImageFormatData *imf = (ImageFormatData *)ptr->data; @@ -945,11 +1025,30 @@ static EnumPropertyItem *rna_ImageFormatSettings_color_depth_itemf(bContext *UNU } } +static EnumPropertyItem *rna_ImageFormatSettings_views_format_itemf( + bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) +{ + ImageFormatData *imf = (ImageFormatData *)ptr->data; + + if (imf == NULL) { + return views_format_items; + } + else if (imf->imtype == R_IMF_IMTYPE_OPENEXR) { + return views_format_multiview_items; + } + else if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { + return views_format_multilayer_items; + } + else { + return views_format_items; + } +} + #ifdef WITH_OPENEXR /* OpenEXR */ -static EnumPropertyItem *rna_ImageFormatSettings_exr_codec_itemf(bContext *UNUSED(C), PointerRNA *ptr, -PropertyRNA *UNUSED(prop), bool *r_free) +static EnumPropertyItem *rna_ImageFormatSettings_exr_codec_itemf( + bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { ImageFormatData *imf = (ImageFormatData *)ptr->data; @@ -1005,8 +1104,8 @@ static void rna_RenderSettings_qtcodecsettings_codecType_set(PointerRNA *ptr, in settings->codecType = quicktime_videocodecType_from_rnatmpvalue(value); } -static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_codecType_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), bool *r_free) +static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_codecType_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { EnumPropertyItem *item = NULL; EnumPropertyItem tmp = {0, "", 0, "", ""}; @@ -1043,8 +1142,8 @@ static void rna_RenderSettings_qtcodecsettings_audiocodecType_set(PointerRNA *pt settings->audiocodecType = quicktime_audiocodecType_from_rnatmpvalue(value); } -static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_audiocodecType_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), bool *r_free) +static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_audiocodecType_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { EnumPropertyItem *item = NULL; EnumPropertyItem tmp = {0, "", 0, "", ""}; @@ -1104,8 +1203,8 @@ static void rna_RenderSettings_active_layer_index_set(PointerRNA *ptr, int value rd->actlay = min_ff(value, num_layers - 1); } -static void rna_RenderSettings_active_layer_index_range(PointerRNA *ptr, int *min, int *max, - int *UNUSED(softmin), int *UNUSED(softmax)) +static void rna_RenderSettings_active_layer_index_range( + PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax)) { RenderData *rd = (RenderData *)ptr->data; @@ -1140,8 +1239,8 @@ static SceneRenderLayer *rna_RenderLayer_new(ID *id, RenderData *UNUSED(rd), con return srl; } -static void rna_RenderLayer_remove(ID *id, RenderData *UNUSED(rd), Main *bmain, ReportList *reports, - PointerRNA *srl_ptr) +static void rna_RenderLayer_remove( + ID *id, RenderData *UNUSED(rd), Main *bmain, ReportList *reports, PointerRNA *srl_ptr) { SceneRenderLayer *srl = srl_ptr->data; Scene *scene = (Scene *)id; @@ -1158,6 +1257,70 @@ static void rna_RenderLayer_remove(ID *id, RenderData *UNUSED(rd), Main *bmain, WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL); } +static int rna_RenderSettings_active_view_index_get(PointerRNA *ptr) +{ + RenderData *rd = (RenderData *)ptr->data; + return rd->actview; +} + +static void rna_RenderSettings_active_view_index_set(PointerRNA *ptr, int value) +{ + RenderData *rd = (RenderData *)ptr->data; + rd->actview = value; +} + +static void rna_RenderSettings_active_view_index_range( + PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax)) +{ + RenderData *rd = (RenderData *)ptr->data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&rd->views) - 1); +} + +static PointerRNA rna_RenderSettings_active_view_get(PointerRNA *ptr) +{ + RenderData *rd = (RenderData *)ptr->data; + SceneRenderView *srv = BLI_findlink(&rd->views, rd->actview); + + return rna_pointer_inherit_refine(ptr, &RNA_SceneRenderView, srv); +} + +static void rna_RenderSettings_active_view_set(PointerRNA *ptr, PointerRNA value) +{ + RenderData *rd = (RenderData *)ptr->data; + SceneRenderView *srv = (SceneRenderView *)value.data; + const int index = BLI_findindex(&rd->views, srv); + if (index != -1) rd->actview = index; +} + +static SceneRenderView *rna_RenderView_new(ID *id, RenderData *UNUSED(rd), const char *name) +{ + Scene *scene = (Scene *)id; + SceneRenderView *srv = BKE_scene_add_render_view(scene, name); + + WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL); + + return srv; +} + +static void rna_RenderView_remove( + ID *id, RenderData *UNUSED(rd), Main *UNUSED(bmain), ReportList *reports, PointerRNA *srv_ptr) +{ + SceneRenderView *srv = srv_ptr->data; + Scene *scene = (Scene *)id; + + if (!BKE_scene_remove_render_view(scene, srv)) { + BKE_reportf(reports, RPT_ERROR, "Render view '%s' could not be removed from scene '%s'", + srv->name, scene->id.name + 2); + return; + } + + RNA_POINTER_INVALIDATE(srv_ptr); + + WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL); +} + static void rna_RenderSettings_engine_set(PointerRNA *ptr, int value) { RenderData *rd = (RenderData *)ptr->data; @@ -1167,8 +1330,8 @@ static void rna_RenderSettings_engine_set(PointerRNA *ptr, int value) BLI_strncpy_utf8(rd->engine, type->idname, sizeof(rd->engine)); } -static EnumPropertyItem *rna_RenderSettings_engine_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), bool *r_free) +static EnumPropertyItem *rna_RenderSettings_engine_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { RenderEngineType *type; EnumPropertyItem *item = NULL; @@ -1251,7 +1414,7 @@ static void rna_SceneRenderLayer_name_set(PointerRNA *ptr, const char *value) } /* fix all the animation data which may link to this */ - BKE_all_animdata_fix_paths_rename(NULL, "render.layers", oldname, rl->name); + BKE_animdata_fix_paths_rename_all(NULL, "render.layers", oldname, rl->name); } static char *rna_SceneRenderLayer_path(PointerRNA *ptr) @@ -1263,6 +1426,34 @@ static char *rna_SceneRenderLayer_path(PointerRNA *ptr) return BLI_sprintfN("render.layers[\"%s\"]", name_esc); } +static void rna_SceneRenderView_name_set(PointerRNA *ptr, const char *value) +{ + Scene *scene = (Scene *)ptr->id.data; + SceneRenderView *rv = (SceneRenderView *)ptr->data; + BLI_strncpy_utf8(rv->name, value, sizeof(rv->name)); + BLI_uniquename(&scene->r.views, rv, DATA_("RenderView"), '.', offsetof(SceneRenderView, name), sizeof(rv->name)); +} + +static char *rna_SceneRenderView_path(PointerRNA *ptr) +{ + SceneRenderView *srv = (SceneRenderView *)ptr->data; + return BLI_sprintfN("render.views[\"%s\"]", srv->name); +} + +static void rna_RenderSettings_views_format_set(PointerRNA *ptr, int value) +{ + RenderData *rd = (RenderData *)ptr->data; + + if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW && + value == SCE_VIEWS_FORMAT_STEREO_3D) + { + /* make sure the actview is visible */ + if (rd->actview > 1) rd->actview = 1; + } + + rd->views_format = value; +} + static int rna_RenderSettings_multiple_engines_get(PointerRNA *UNUSED(ptr)) { return (BLI_listbase_count(&R_engines) > 1); @@ -1638,7 +1829,8 @@ static void rna_FreestyleLineSet_linestyle_set(PointerRNA *ptr, PointerRNA value lineset->linestyle->id.us++; } -static FreestyleLineSet *rna_FreestyleSettings_lineset_add(ID *id, FreestyleSettings *config, Main *bmain, const char *name) +static FreestyleLineSet *rna_FreestyleSettings_lineset_add( + ID *id, FreestyleSettings *config, Main *bmain, const char *name) { Scene *scene = (Scene *)id; FreestyleLineSet *lineset = BKE_freestyle_lineset_add(bmain, (FreestyleConfig *)config, name); @@ -1649,8 +1841,8 @@ static FreestyleLineSet *rna_FreestyleSettings_lineset_add(ID *id, FreestyleSett return lineset; } -static void rna_FreestyleSettings_lineset_remove(ID *id, FreestyleSettings *config, ReportList *reports, - PointerRNA *lineset_ptr) +static void rna_FreestyleSettings_lineset_remove( + ID *id, FreestyleSettings *config, ReportList *reports, PointerRNA *lineset_ptr) { FreestyleLineSet *lineset = lineset_ptr->data; Scene *scene = (Scene *)id; @@ -1673,8 +1865,8 @@ static PointerRNA rna_FreestyleSettings_active_lineset_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_FreestyleLineSet, lineset); } -static void rna_FreestyleSettings_active_lineset_index_range(PointerRNA *ptr, int *min, int *max, - int *UNUSED(softmin), int *UNUSED(softmax)) +static void rna_FreestyleSettings_active_lineset_index_range( + PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax)) { FreestyleConfig *config = (FreestyleConfig *)ptr->data; @@ -1705,8 +1897,8 @@ static FreestyleModuleConfig *rna_FreestyleSettings_module_add(ID *id, Freestyle return module; } -static void rna_FreestyleSettings_module_remove(ID *id, FreestyleSettings *config, ReportList *reports, - PointerRNA *module_ptr) +static void rna_FreestyleSettings_module_remove( + ID *id, FreestyleSettings *config, ReportList *reports, PointerRNA *module_ptr) { Scene *scene = (Scene *)id; FreestyleModuleConfig *module = module_ptr->data; @@ -1759,6 +1951,26 @@ static void rna_GPUDOFSettings_blades_set(PointerRNA *ptr, const int value) dofsettings->num_blades = value; } +static void rna_Stereo3dFormat_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->id.data; + + if (id && GS(id->name) == ID_IM) { + Image *ima = (Image *)id; + ImBuf *ibuf; + void *lock; + + if ((ima->flag & IMA_IS_STEREO) == 0) + return; + + ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + + if (ibuf) { + BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE); + } + BKE_image_release_ibuf(ima, ibuf, lock); + } +} static int rna_gpu_is_hq_supported_get(PointerRNA *UNUSED(ptr)) { @@ -1850,9 +2062,11 @@ static void rna_def_tool_settings(BlenderRNA *brna) static EnumPropertyItem gpencil_source_3d_items[] = { {GP_TOOL_SOURCE_SCENE, "SCENE", 0, "Scene", - "Grease Pencil data attached to the current scene is used, unless the active object already has Grease Pencil data (i.e. for old files)"}, + "Grease Pencil data attached to the current scene is used, " + "unless the active object already has Grease Pencil data (i.e. for old files)"}, {GP_TOOL_SOURCE_OBJECT, "OBJECT", 0, "Object", - "Grease Pencil datablocks attached to the active object are used (required using pre 2.73 add-ons, e.g. BSurfaces)"}, + "Grease Pencil datablocks attached to the active object are used " + "(required using pre 2.73 add-ons, e.g. BSurfaces)"}, {0, NULL, 0, NULL, NULL} }; @@ -2134,7 +2348,8 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "use_etch_autoname", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "skgen_retarget_options", SK_RETARGET_AUTONAME); - RNA_def_property_ui_text(prop, "Autoname Bones", "Automatically generate values to replace &N and &S suffix placeholders in template names"); + RNA_def_property_ui_text(prop, "Autoname Bones", + "Automatically generate values to replace &N and &S suffix placeholders in template names"); prop = RNA_def_property(srna, "etch_number", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "skgen_num_string"); @@ -3022,12 +3237,14 @@ static void rna_def_freestyle_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "face_mark_condition", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flags"); RNA_def_property_enum_items(prop, face_mark_condition_items); - RNA_def_property_ui_text(prop, "Face Mark Condition", "Specify a feature edge selection condition based on face marks"); + RNA_def_property_ui_text(prop, "Face Mark Condition", + "Specify a feature edge selection condition based on face marks"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); prop = RNA_def_property(srna, "select_silhouette", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "edge_types", FREESTYLE_FE_SILHOUETTE); - RNA_def_property_ui_text(prop, "Silhouette", "Select silhouettes (edges at the boundary of visible and hidden faces)"); + RNA_def_property_ui_text(prop, "Silhouette", + "Select silhouettes (edges at the boundary of visible and hidden faces)"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); prop = RNA_def_property(srna, "select_border", PROP_BOOLEAN, PROP_NONE); @@ -3037,12 +3254,14 @@ static void rna_def_freestyle_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "select_crease", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "edge_types", FREESTYLE_FE_CREASE); - RNA_def_property_ui_text(prop, "Crease", "Select crease edges (those between two faces making an angle smaller than the Crease Angle)"); + RNA_def_property_ui_text(prop, "Crease", + "Select crease edges (those between two faces making an angle smaller than the Crease Angle)"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); prop = RNA_def_property(srna, "select_ridge_valley", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "edge_types", FREESTYLE_FE_RIDGE_VALLEY); - RNA_def_property_ui_text(prop, "Ridge & Valley", "Select ridges and valleys (boundary lines between convex and concave areas of surface)"); + RNA_def_property_ui_text(prop, "Ridge & Valley", + "Select ridges and valleys (boundary lines between convex and concave areas of surface)"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); prop = RNA_def_property(srna, "select_suggestive_contour", PROP_BOOLEAN, PROP_NONE); @@ -3062,7 +3281,8 @@ static void rna_def_freestyle_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "select_external_contour", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "edge_types", FREESTYLE_FE_EXTERNAL_CONTOUR); - RNA_def_property_ui_text(prop, "External Contour", "Select external contours (outer silhouettes of occluding and occluded objects)"); + RNA_def_property_ui_text(prop, "External Contour", + "Select external contours (outer silhouettes of occluding and occluded objects)"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); prop = RNA_def_property(srna, "select_edge_mark", PROP_BOOLEAN, PROP_NONE); @@ -3211,7 +3431,8 @@ static void rna_def_freestyle_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "use_view_map_cache", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FREESTYLE_VIEW_MAP_CACHE); - RNA_def_property_ui_text(prop, "View Map Cache", "Keep the computed view map and avoid re-calculating it if mesh geometry is unchanged"); + RNA_def_property_ui_text(prop, "View Map Cache", + "Keep the computed view map and avoid re-calculating it if mesh geometry is unchanged"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_use_view_map_cache_update"); prop = RNA_def_property(srna, "sphere_radius", PROP_FLOAT, PROP_NONE); @@ -4019,7 +4240,8 @@ static void rna_def_gpu_fx(BlenderRNA *brna) prop = RNA_def_property(srna, "use_dof", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "fx_flag", GPU_FX_FLAG_DOF); - RNA_def_property_ui_text(prop, "Depth Of Field", "Use depth of field on viewport using the values from active camera"); + RNA_def_property_ui_text(prop, "Depth Of Field", + "Use depth of field on viewport using the values from active camera"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPUFXSettings_fx_update"); @@ -4103,6 +4325,138 @@ static void rna_def_render_layers(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); } +/* Render Views - MultiView */ +static void rna_def_scene_render_view(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SceneRenderView", NULL); + RNA_def_struct_ui_text(srna, "Scene Render View", "Render viewpoint for 3D stereo and multiview rendering"); + RNA_def_struct_ui_icon(srna, ICON_RESTRICT_RENDER_OFF); + RNA_def_struct_path_func(srna, "rna_SceneRenderView_path"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SceneRenderView_name_set"); + RNA_def_property_ui_text(prop, "Name", "Render view name"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "file_suffix", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "suffix"); + RNA_def_property_ui_text(prop, "File Suffix", "Suffix added to the render images for this view"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "camera_suffix", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "suffix"); + RNA_def_property_ui_text(prop, "Camera Suffix", + "Suffix to identify the cameras to use, and added to the render images for this view"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "use", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "viewflag", SCE_VIEW_DISABLE); + RNA_def_property_ui_text(prop, "Enabled", "Disable or enable the render view"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); +} + +static void rna_def_render_views(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "RenderViews"); + srna = RNA_def_struct(brna, "RenderViews", NULL); + RNA_def_struct_sdna(srna, "RenderData"); + RNA_def_struct_ui_text(srna, "Render Views", "Collection of render views"); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "actview"); + RNA_def_property_int_funcs(prop, "rna_RenderSettings_active_view_index_get", + "rna_RenderSettings_active_view_index_set", + "rna_RenderSettings_active_view_index_range"); + RNA_def_property_ui_text(prop, "Active View Index", "Active index in render view array"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "SceneRenderView"); + RNA_def_property_pointer_funcs(prop, "rna_RenderSettings_active_view_get", + "rna_RenderSettings_active_view_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL); + RNA_def_property_ui_text(prop, "Active Render View", "Active Render View"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + func = RNA_def_function(srna, "new", "rna_RenderView_new"); + RNA_def_function_ui_description(func, "Add a render view to scene"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); + parm = RNA_def_string(func, "name", "RenderView", 0, "", "New name for the marker (not unique)"); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_pointer(func, "result", "SceneRenderView", "", "Newly created render view"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_RenderView_remove"); + RNA_def_function_ui_description(func, "Remove a render view"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS | FUNC_USE_SELF_ID); + parm = RNA_def_pointer(func, "view", "SceneRenderView", "", "Render view to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); +} + +static void rna_def_image_format_stereo3d_format(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem stereo3d_display_items[] = { + {S3D_DISPLAY_ANAGLYPH, "ANAGLYPH", 0, "Anaglyph", + "Render views for left and right eyes as two differently filtered colors in a single image " + "(anaglyph glasses are required)"}, + {S3D_DISPLAY_INTERLACE, "INTERLACE", 0, "Interlace", + "Render views for left and right eyes interlaced in a single image (3D-ready monitor is required)"}, + {S3D_DISPLAY_SIDEBYSIDE, "SIDEBYSIDE", 0, "Side-by-Side", "Render views for left and right eyes side-by-side"}, + {S3D_DISPLAY_TOPBOTTOM, "TOPBOTTOM", 0, "Top-Bottom", "Render views for left and right eyes one above another"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "Stereo3dFormat", NULL); + RNA_def_struct_sdna(srna, "Stereo3dFormat"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Stereo Output", "Settings for stereo output"); + + prop = RNA_def_property(srna, "display_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "display_mode"); + RNA_def_property_enum_items(prop, stereo3d_display_items); + RNA_def_property_ui_text(prop, "Stereo Mode", ""); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "anaglyph_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_anaglyph_type_items); + RNA_def_property_ui_text(prop, "Anaglyph Type", ""); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "interlace_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_interlace_type_items); + RNA_def_property_ui_text(prop, "Interlace Type", ""); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "use_interlace_swap", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_INTERLACE_SWAP); + RNA_def_property_ui_text(prop, "Swap Left/Right", "Swap left and right stereo channels"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "use_sidebyside_crosseyed", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SIDEBYSIDE_CROSSEYED); + RNA_def_property_ui_text(prop, "Cross-Eyed", "Right eye should see left image and vice-versa"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "use_squeezed_frame", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SQUEEZED_FRAME); + RNA_def_property_ui_text(prop, "Squeezed Frame", "Combine both views in a squeezed image"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); +} + /* use for render output and image save operator, * note: there are some cases where the members act differently when this is * used from a scene, video formats can only be selected for render output @@ -4122,6 +4476,8 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + rna_def_image_format_stereo3d_format(brna); + srna = RNA_def_struct(brna, "ImageFormatSettings", NULL); RNA_def_struct_sdna(srna, "ImageFormatData"); RNA_def_struct_nested(brna, srna, "Scene"); @@ -4190,7 +4546,6 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_ImageFormatSettings_exr_codec_itemf"); RNA_def_property_ui_text(prop, "Codec", "Codec settings for OpenEXR"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - #endif #ifdef WITH_OPENJPEG @@ -4242,6 +4597,20 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "G", "Log conversion gamma"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + /* multiview */ + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "views_format"); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_ImageFormatSettings_views_format_itemf"); + RNA_def_property_ui_text(prop, "Views Format", "Format of multiview media"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dFormat"); + RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d"); + /* color management */ prop = RNA_def_property(srna, "view_settings", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "view_settings"); @@ -4644,6 +5013,16 @@ static void rna_def_scene_render_data(BlenderRNA *brna) "resolution to 480 pixels"}, {0, NULL, 0, NULL, NULL}}; + static EnumPropertyItem views_format_items[] = { + {SCE_VIEWS_FORMAT_STEREO_3D, "STEREO_3D", 0, "Stereo 3D", + "Single stereo camera system, adjust the stereo settings in the camera panel"}, + {SCE_VIEWS_FORMAT_MULTIVIEW, "MULTIVIEW", 0, "Multi-View", + "Multi camera system, adjust the cameras individually"}, + {0, NULL, 0, NULL, NULL} + }; + + + rna_def_scene_ffmpeg_settings(brna); #ifdef WITH_QUICKTIME rna_def_scene_quicktime_settings(brna); @@ -5326,6 +5705,32 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + /* views (stereoscopy et al) */ + prop = RNA_def_property(srna, "views", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "SceneRenderView"); + RNA_def_property_ui_text(prop, "Render Views", ""); + rna_def_render_views(brna, prop); + + prop = RNA_def_property(srna, "stereo_views", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "views", NULL); + RNA_def_property_collection_funcs(prop, "rna_RenderSettings_stereoViews_begin", "rna_iterator_listbase_next", + "rna_iterator_listbase_end", "rna_iterator_listbase_get", + NULL, NULL, NULL, NULL); + RNA_def_property_struct_type(prop, "SceneRenderView"); + RNA_def_property_ui_text(prop, "Render Views", ""); + + prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_MULTIVIEW); + RNA_def_property_ui_text(prop, "Multiple Views", "Use multiple views in the scene"); + RNA_def_property_update(prop, NC_WINDOW, NULL); + + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Setup Stereo Mode", ""); + RNA_def_property_enum_funcs(prop, NULL, "rna_RenderSettings_views_format_set", NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + /* engine */ prop = RNA_def_property(srna, "engine", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, engine_items); @@ -6076,8 +6481,8 @@ void RNA_def_scene(BlenderRNA *brna) /* *** Animated *** */ rna_def_scene_render_data(brna); rna_def_scene_render_layer(brna); - rna_def_gpu_fx(brna); + rna_def_scene_render_view(brna); /* Scene API */ RNA_api_scene(srna); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 0d8d58eae43..0d70b6f3ba8 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -119,15 +119,15 @@ static void rna_Scene_update_tagged(Scene *scene) #endif } -static void rna_SceneRender_get_frame_path(RenderData *rd, int frame, int preview, char *name) +static void rna_SceneRender_get_frame_path(RenderData *rd, int frame, int preview, const char *view, char *name) { if (BKE_imtype_is_movie(rd->im_format.imtype)) { - BKE_movie_filepath_get(name, rd, preview != 0); + BKE_movie_filepath_get(name, rd, preview != 0, view); } else { BKE_image_path_from_imformat( name, rd->pic, G.main->name, (frame == INT_MIN) ? rd->cfra : frame, - &rd->im_format, (rd->scemode & R_EXTENSION) != 0, true); + &rd->im_format, (rd->scemode & R_EXTENSION) != 0, true, view); } } @@ -288,6 +288,8 @@ void RNA_api_scene_render(StructRNA *srna) RNA_def_int(func, "frame", INT_MIN, INT_MIN, INT_MAX, "", "Frame number to use, if unset the current frame will be used", MINAFRAME, MAXFRAME); parm = RNA_def_boolean(func, "preview", 0, "Preview", "Use preview range"); + parm = RNA_def_string_file_path(func, "view", NULL, FILE_MAX, "View", + "The name of the view to use to replace the \"%\" chars"); parm = RNA_def_string_file_path(func, "filepath", NULL, FILE_MAX, "File Path", "The resulting filepath from the scenes render settings"); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 1eabbc7d042..ccfa656d9f1 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -186,6 +186,11 @@ static void rna_SequenceEditor_elements_begin(CollectionPropertyIterator *iter, rna_SequenceEditor_elements_length(ptr), 0, NULL); } +static void rna_Sequence_views_format_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_Sequence_update(bmain, scene, ptr); +} + static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) { Editing *ed = BKE_sequencer_editing_get(scene, false); @@ -485,7 +490,7 @@ static void rna_Sequence_name_set(PointerRNA *ptr, const char *value) /* fix all the animation data which may link to this */ /* don't rename everywhere because these are per scene */ - /* BKE_all_animdata_fix_paths_rename(NULL, "sequence_editor.sequences_all", oldname, seq->name + 2); */ + /* BKE_animdata_fix_paths_rename_all(NULL, "sequence_editor.sequences_all", oldname, seq->name + 2); */ adt = BKE_animdata_from_id(&scene->id); if (adt) BKE_animdata_fix_paths_rename(&scene->id, adt, NULL, "sequence_editor.sequences_all", oldname, seq->name + 2, 0, 0, 1); @@ -1834,6 +1839,24 @@ static void rna_def_image(BlenderRNA *brna) "rna_SequenceEditor_elements_length", NULL, NULL, NULL); RNA_api_sequence_elements(brna, prop); + /* multiview */ + prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_VIEWS); + RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_views_format_update"); + + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "views_format"); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_ui_text(prop, "Views Format", "Mode to load image views"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Sequence_views_format_update"); + + prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dFormat"); + RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d"); + rna_def_filter_video(srna); rna_def_proxy(srna); rna_def_input(srna); @@ -1930,6 +1953,24 @@ static void rna_def_movie(BlenderRNA *brna) "rna_Sequence_filepath_set"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_filepath_update"); + /* multiview */ + prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_VIEWS); + RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_views_format_update"); + + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "views_format"); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_ui_text(prop, "Views Format", "Mode to load movie views"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Sequence_views_format_update"); + + prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dFormat"); + RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d"); + rna_def_filter_video(srna); rna_def_proxy(srna); rna_def_input(srna); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 2d3e3c56672..881688281f3 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -198,6 +198,7 @@ static Sequence *rna_Sequences_new_movie(ID *id, Editing *ed, ReportList *report { Scene *scene = (Scene *)id; Sequence *seq; + StripAnim *sanim; struct anim *an = openanim(file, IB_rect, 0, NULL); @@ -207,7 +208,11 @@ static Sequence *rna_Sequences_new_movie(ID *id, Editing *ed, ReportList *report } seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIE, file); - seq->anim = an; + + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = an; + seq->anim_preseek = IMB_anim_get_preseek(an); seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 09cb1fd736e..2bf355f2512 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -31,6 +31,7 @@ #include "BLF_translation.h" +#include "BKE_image.h" #include "BKE_key.h" #include "BKE_movieclip.h" #include "BKE_node.h" @@ -90,6 +91,34 @@ EnumPropertyItem space_type_items[] = { {0, NULL, 0, NULL, NULL} }; +#define V3D_S3D_CAMERA_LEFT {STEREO_LEFT_ID, "LEFT", ICON_RESTRICT_RENDER_OFF, "Left", ""}, +#define V3D_S3D_CAMERA_RIGHT {STEREO_RIGHT_ID, "RIGHT", ICON_RESTRICT_RENDER_OFF, "Right", ""}, +#define V3D_S3D_CAMERA_S3D {STEREO_3D_ID, "S3D", ICON_CAMERA_STEREO, "3D", ""}, +#ifdef RNA_RUNTIME +#define V3D_S3D_CAMERA_VIEWS {STEREO_MONO_ID, "MONO", ICON_RESTRICT_RENDER_OFF, "Views", ""}, +#endif + +static EnumPropertyItem stereo3d_camera_items[] = { + V3D_S3D_CAMERA_LEFT + V3D_S3D_CAMERA_RIGHT + V3D_S3D_CAMERA_S3D + {0, NULL, 0, NULL, NULL} +}; + +#ifdef RNA_RUNTIME +static EnumPropertyItem multiview_camera_items[] = { + V3D_S3D_CAMERA_VIEWS + V3D_S3D_CAMERA_S3D + {0, NULL, 0, NULL, NULL} +}; +#endif + +#undef V3D_S3D_CAMERA_LEFT +#undef V3D_S3D_CAMERA_RIGHT +#undef V3D_S3D_CAMERA_S3D +#undef V3D_S3D_CAMERA_VIEWS + + static EnumPropertyItem pivot_items_full[] = { {V3D_CENTER, "BOUNDING_BOX_CENTER", ICON_ROTATE, "Bounding Box Center", "Pivot around bounding box center of selected object(s)"}, @@ -659,6 +688,17 @@ static EnumPropertyItem *rna_SpaceView3D_viewport_shade_itemf(bContext *UNUSED(C return item; } +static EnumPropertyItem *rna_SpaceView3D_stereo3d_camera_itemf(bContext *UNUSED(C), PointerRNA *ptr, + PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) +{ + Scene *scene = ((bScreen *)ptr->id.data)->scene; + + if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) + return multiview_camera_items; + else + return stereo3d_camera_items; +} + /* Space Image Editor */ static PointerRNA rna_SpaceImageEditor_uvedit_get(PointerRNA *ptr) @@ -671,6 +711,38 @@ static void rna_SpaceImageEditor_mode_update(Main *bmain, Scene *scene, PointerR ED_space_image_paint_update(bmain->wm.first, scene->toolsettings); } + +static void rna_SpaceImageEditor_show_stereo_set(PointerRNA *ptr, int value) +{ + SpaceImage *sima = (SpaceImage *)(ptr->data); + + if (value) + sima->iuser.flag |= IMA_SHOW_STEREO; + else + sima->iuser.flag &= ~IMA_SHOW_STEREO; +} + +static int rna_SpaceImageEditor_show_stereo_get(PointerRNA *ptr) +{ + SpaceImage *sima = (SpaceImage *)(ptr->data); + return (sima->iuser.flag & IMA_SHOW_STEREO) != 0; +} + +static void rna_SpaceImageEditor_show_stereo_update(Main *UNUSED(bmain), Scene *UNUSED(unused), PointerRNA *ptr) +{ + SpaceImage *sima = (SpaceImage *)(ptr->data); + Image *ima = sima->image; + + if (ima) { + if (ima->rr) { + BKE_image_multilayer_index(ima->rr, &sima->iuser); + } + else { + BKE_image_multiview_index(ima, &sima->iuser); + } + } +} + static int rna_SpaceImageEditor_show_render_get(PointerRNA *ptr) { SpaceImage *sima = (SpaceImage *)(ptr->data); @@ -798,6 +870,24 @@ static void rna_SpaceImageEditor_cursor_location_set(PointerRNA *ptr, const floa } } +static void rna_SpaceImageEditor_image_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + SpaceImage *sima = (SpaceImage *)ptr->data; + Image *ima = sima->image; + + /* make sure all the iuser settings are valid for the sima image */ + if (ima) { + if (ima->rr) { + if (BKE_image_multilayer_index(sima->image->rr, &sima->iuser) == NULL) { + BKE_image_init_imageuser(sima->image, &sima->iuser); + } + } + else { + BKE_image_multiview_index(ima, &sima->iuser); + } + } +} + static void rna_SpaceImageEditor_scopes_update(struct bContext *C, struct PointerRNA *ptr) { SpaceImage *sima = (SpaceImage *)ptr->data; @@ -1117,12 +1207,12 @@ static void rna_SpaceDopeSheetEditor_action_update(Main *UNUSED(bmain), Scene *s if (saction->mode == SACTCONT_ACTION) { /* TODO: context selector could help decide this with more control? */ - adt = BKE_id_add_animdata(&obact->id); /* this only adds if non-existent */ + adt = BKE_animdata_add_id(&obact->id); /* this only adds if non-existent */ } else if (saction->mode == SACTCONT_SHAPEKEY) { Key *key = BKE_key_from_object(obact); if (key) - adt = BKE_id_add_animdata(&key->id); /* this only adds if non-existent */ + adt = BKE_animdata_add_id(&key->id); /* this only adds if non-existent */ } /* set action */ @@ -2482,6 +2572,41 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "FX Options", "Options used for real time compositing"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + /* Stereo Settings */ + prop = RNA_def_property(srna, "stereo_3d_camera", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "stereo3d_camera"); + RNA_def_property_enum_items(prop, stereo3d_camera_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_SpaceView3D_stereo3d_camera_itemf"); + RNA_def_property_ui_text(prop, "Camera", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "show_stereo_3d_cameras", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPCAMERAS); + RNA_def_property_ui_text(prop, "Cameras", "Show the left and right cameras"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "show_stereo_3d_convergence_plane", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPPLANE); + RNA_def_property_ui_text(prop, "Plane", "Show the stereo 3d convergence plane"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "stereo_3d_convergence_plane_alpha", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "stereo3d_convergence_alpha"); + RNA_def_property_ui_text(prop, "Plane Alpha", "Opacity (alpha) of the convergence plane"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "show_stereo_3d_volume", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPVOLUME); + RNA_def_property_ui_text(prop, "Volume", "Show the stereo 3d frustum volume"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "stereo_3d_volume_alpha", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "stereo3d_volume_alpha"); + RNA_def_property_ui_text(prop, "Volume Alpha", "Opacity (alpha) of the cameras' frustum volume"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + /* *** Animated *** */ + RNA_define_animate_sdna(true); /* region */ srna = RNA_def_struct(brna, "RegionView3D", NULL); @@ -2655,7 +2780,7 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceImageEditor_image_set", NULL, NULL); RNA_def_property_ui_text(prop, "Image", "Image displayed and edited in this space"); RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_update(prop, NC_GEOM | ND_DATA, NULL); /* is handled in image editor too */ + RNA_def_property_update(prop, NC_GEOM | ND_DATA, "rna_SpaceImageEditor_image_update"); /* is handled in image editor too */ prop = RNA_def_property(srna, "image_user", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); @@ -2707,6 +2832,12 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Draw Channels", "Channels of the image to draw"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "show_stereo_3d", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_SpaceImageEditor_show_stereo_get", "rna_SpaceImageEditor_show_stereo_set"); + RNA_def_property_ui_text(prop, "Show Stereo", "Display the image in Stereo 3D"); + RNA_def_property_ui_icon(prop, ICON_CAMERA_STEREO, 0); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_SpaceImageEditor_show_stereo_update"); + /* uv */ prop = RNA_def_property(srna, "uv_editor", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index a42f7433082..fa5fb05783b 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -779,6 +779,16 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL); RNA_def_boolean(func, "color_management", false, "", "Show color management settings"); + func = RNA_def_function(srna, "template_image_stereo_3d", "uiTemplateImageStereo3d"); + RNA_def_function_ui_description(func, "User interface for setting image stereo 3d options"); + parm = RNA_def_pointer(func, "stereo_3d_format", "Stereo3dFormat", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL); + + func = RNA_def_function(srna, "template_image_views", "uiTemplateImageViews"); + RNA_def_function_ui_description(func, "User interface for setting image views output options"); + parm = RNA_def_pointer(func, "image_settings", "ImageFormatSettings", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL); + func = RNA_def_function(srna, "template_movieclip", "uiTemplateMovieClip"); RNA_def_function_ui_description(func, "Item(s). User interface for selecting movie clips and their source paths"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index cb911af4cdc..9e4f1656db2 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -4134,8 +4134,8 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "uiflag2", USER_REGION_OVERLAP); RNA_def_property_ui_text(prop, "Region Overlap", "Draw tool/property regions over the main region, when using Triple Buffer"); - RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); - + RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); + #ifdef WITH_CYCLES prop = RNA_def_property(srna, "compute_device_type", PROP_ENUM, PROP_NONE); RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT); @@ -4334,12 +4334,16 @@ static void rna_def_userdef_input(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_FLY_HELICOPTER); RNA_def_property_ui_text(prop, "Helicopter Mode", "Device up/down directly controls your Z position"); - - prop = RNA_def_property(srna, "mouse_double_click_time", PROP_INT, PROP_NONE); + prop = RNA_def_property(srna, "double_click_time", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "dbl_click_time"); RNA_def_property_range(prop, 1, 1000); RNA_def_property_ui_text(prop, "Double Click Timeout", "Time/delay (in ms) for a double click"); + prop = RNA_def_property(srna, "click_timeout", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "click_timeout"); + RNA_def_property_range(prop, 0, 10000); + RNA_def_property_ui_text(prop, "Click Timeout", "Time (in ms) to determine if a key is clicked or held"); + prop = RNA_def_property(srna, "use_mouse_emulate_3_button", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_TWOBUTTONMOUSE); RNA_def_property_ui_text(prop, "Emulate 3 Button Mouse", diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 0b63539aab9..d46b01dd156 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -50,6 +50,7 @@ static EnumPropertyItem event_keymouse_value_items[] = { {KM_PRESS, "PRESS", 0, "Press", ""}, {KM_RELEASE, "RELEASE", 0, "Release", ""}, {KM_CLICK, "CLICK", 0, "Click", ""}, + {KM_HOLD, "HOLD", 0, "Hold", ""}, {KM_DBL_CLICK, "DOUBLE_CLICK", 0, "Double Click", ""}, {0, NULL, 0, NULL, NULL} }; @@ -381,6 +382,7 @@ EnumPropertyItem event_value_items[] = { {KM_PRESS, "PRESS", 0, "Press", ""}, {KM_RELEASE, "RELEASE", 0, "Release", ""}, {KM_CLICK, "CLICK", 0, "Click", ""}, + {KM_HOLD, "HOLD", 0, "Hold", ""}, {KM_DBL_CLICK, "DOUBLE_CLICK", 0, "Double Click", ""}, {EVT_GESTURE_N, "NORTH", 0, "North", ""}, {EVT_GESTURE_NE, "NORTH_EAST", 0, "North-East", ""}, @@ -1998,6 +2000,37 @@ static void rna_def_piemenu(BlenderRNA *brna) RNA_define_verify_sdna(1); /* not in sdna */ } +static void rna_def_window_stereo3d(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Stereo3dDisplay", NULL); + RNA_def_struct_sdna(srna, "Stereo3dFormat"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Stereo 3D Display", "Settings for stereo 3D display"); + + prop = RNA_def_property(srna, "display_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_display_items); + RNA_def_property_ui_text(prop, "Display Mode", ""); + + prop = RNA_def_property(srna, "anaglyph_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_anaglyph_type_items); + RNA_def_property_ui_text(prop, "Anaglyph Type", ""); + + prop = RNA_def_property(srna, "interlace_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_interlace_type_items); + RNA_def_property_ui_text(prop, "Interlace Type", ""); + + prop = RNA_def_property(srna, "use_interlace_swap", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_INTERLACE_SWAP); + RNA_def_property_ui_text(prop, "Swap Left/Right", "Swap left and right stereo channels"); + + prop = RNA_def_property(srna, "use_sidebyside_crosseyed", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SIDEBYSIDE_CROSSEYED); + RNA_def_property_ui_text(prop, "Cross-Eyed", "Right eye should see left image and vice-versa"); +} + static void rna_def_window(BlenderRNA *brna) { StructRNA *srna; @@ -2007,6 +2040,8 @@ static void rna_def_window(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Window", "Open window"); RNA_def_struct_sdna(srna, "wmWindow"); + rna_def_window_stereo3d(brna); + prop = RNA_def_property(srna, "screen", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_struct_type(prop, "Screen"); @@ -2036,6 +2071,12 @@ static void rna_def_window(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Height", "Window height"); + prop = RNA_def_property(srna, "stereo_3d_display", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dDisplay"); + RNA_def_property_ui_text(prop, "Stereo 3D Display", "Settings for stereo 3d display"); + RNA_api_window(srna); } diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 1dc1a1f8d64..96337c33373 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -131,6 +131,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *UNUSED(ob), MPoly *mpoly, *mp; MLoop *ml, *mloop; MEdge *medge; + uintptr_t hash_num, hash_num_alt; if (bmd->flag & MOD_BUILD_FLAG_RANDOMIZE) { BLI_array_randomize(faceMap, sizeof(*faceMap), @@ -142,40 +143,44 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *UNUSED(ob), */ mpoly = mpoly_src; mloop = mloop_src; + hash_num = 0; for (i = 0; i < numFaces_dst; i++) { mp = mpoly + faceMap[i]; ml = mloop + mp->loopstart; for (j = 0; j < mp->totloop; j++, ml++) { - if (!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(ml->v))) - BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(ml->v), - SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); + void **val_p; + if (!BLI_ghash_ensure_p(vertHash, SET_INT_IN_POINTER(ml->v), &val_p)) { + *val_p = (void *)hash_num; + hash_num++; + } } - + numLoops_dst += mp->totloop; } + BLI_assert(hash_num == BLI_ghash_size(vertHash)); /* get the set of edges that will be in the new mesh (i.e. all edges * that have both verts in the new mesh) */ medge = medge_src; - for (i = 0; i < numEdge_src; i++) { + hash_num = 0; + hash_num_alt = 0; + for (i = 0; i < numEdge_src; i++, hash_num_alt++) { MEdge *me = medge + i; if (BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v1)) && BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v2))) { - j = BLI_ghash_size(edgeHash); - - BLI_ghash_insert(edgeHash, SET_INT_IN_POINTER(j), - SET_INT_IN_POINTER(i)); - BLI_ghash_insert(edgeHash2, SET_INT_IN_POINTER(i), - SET_INT_IN_POINTER(j)); + BLI_ghash_insert(edgeHash, (void *)hash_num, (void *)hash_num_alt); + BLI_ghash_insert(edgeHash2, (void *)hash_num_alt, (void *)hash_num); + hash_num++; } } } else if (numEdges_dst) { MEdge *medge, *me; + uintptr_t hash_num; if (bmd->flag & MOD_BUILD_FLAG_RANDOMIZE) BLI_array_randomize(edgeMap, sizeof(*edgeMap), @@ -185,17 +190,22 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *UNUSED(ob), * mapped to the new indices */ medge = medge_src; + hash_num = 0; + BLI_assert(hash_num == BLI_ghash_size(vertHash)); for (i = 0; i < numEdges_dst; i++) { + void **val_p; me = medge + edgeMap[i]; - if (!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v1))) { - BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(me->v1), - SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); + if (!BLI_ghash_ensure_p(vertHash, SET_INT_IN_POINTER(me->v1), &val_p)) { + *val_p = (void *)hash_num; + hash_num++; } - if (!BLI_ghash_haskey(vertHash, SET_INT_IN_POINTER(me->v2))) { - BLI_ghash_insert(vertHash, SET_INT_IN_POINTER(me->v2), SET_INT_IN_POINTER(BLI_ghash_size(vertHash))); + if (!BLI_ghash_ensure_p(vertHash, SET_INT_IN_POINTER(me->v2), &val_p)) { + *val_p = (void *)hash_num; + hash_num++; } } + BLI_assert(hash_num == BLI_ghash_size(vertHash)); /* get the set of edges that will be in the new mesh */ for (i = 0; i < numEdges_dst; i++) { diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 1c8dcdff46d..4cbe0967b01 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -48,14 +48,14 @@ static void init_cache_data(Object *ob, struct OceanModifierData *omd) { const char *relbase = modifier_path_relbase(ob); - omd->oceancache = BKE_init_ocean_cache(omd->cachepath, relbase, + omd->oceancache = BKE_ocean_init_cache(omd->cachepath, relbase, omd->bakestart, omd->bakeend, omd->wave_scale, omd->chop_amount, omd->foam_coverage, omd->foam_fade, omd->resolution); } static void clear_cache_data(struct OceanModifierData *omd) { - BKE_free_ocean_cache(omd->oceancache); + BKE_ocean_free_cache(omd->oceancache); omd->oceancache = NULL; omd->cached = false; } @@ -72,8 +72,8 @@ static void init_ocean_modifier(struct OceanModifierData *omd) do_normals = (omd->flag & MOD_OCEAN_GENERATE_NORMALS); do_jacobian = (omd->flag & MOD_OCEAN_GENERATE_FOAM); - BKE_free_ocean_data(omd->ocean); - BKE_init_ocean(omd->ocean, omd->resolution * omd->resolution, omd->resolution * omd->resolution, + BKE_ocean_free_data(omd->ocean); + BKE_ocean_init(omd->ocean, omd->resolution * omd->resolution, omd->resolution * omd->resolution, omd->spatial_size, omd->spatial_size, omd->wind_velocity, omd->smallest_wave, 1.0, omd->wave_direction, omd->damp, omd->wave_alignment, omd->depth, omd->time, @@ -85,7 +85,7 @@ static void simulate_ocean_modifier(struct OceanModifierData *omd) { if (!omd || !omd->ocean) return; - BKE_simulate_ocean(omd->ocean, omd->time, omd->wave_scale, omd->chop_amount); + BKE_ocean_simulate(omd->ocean, omd->time, omd->wave_scale, omd->chop_amount); } #endif /* WITH_OCEANSIM */ @@ -133,7 +133,7 @@ static void initData(ModifierData *md) omd->foam_fade = 0.98; omd->foamlayername[0] = '\0'; /* layer name empty by default */ - omd->ocean = BKE_add_ocean(); + omd->ocean = BKE_ocean_add(); init_ocean_modifier(omd); simulate_ocean_modifier(omd); #else /* WITH_OCEANSIM */ @@ -147,9 +147,9 @@ static void freeData(ModifierData *md) #ifdef WITH_OCEANSIM OceanModifierData *omd = (OceanModifierData *) md; - BKE_free_ocean(omd->ocean); + BKE_ocean_free(omd->ocean); if (omd->oceancache) - BKE_free_ocean_cache(omd->oceancache); + BKE_ocean_free_cache(omd->oceancache); #else /* WITH_OCEANSIM */ /* unused */ (void)md; @@ -195,7 +195,7 @@ static void copyData(ModifierData *md, ModifierData *target) tomd->bakeend = omd->bakeend; tomd->oceancache = NULL; - tomd->ocean = BKE_add_ocean(); + tomd->ocean = BKE_ocean_add(); init_ocean_modifier(tomd); simulate_ocean_modifier(tomd); #else /* WITH_OCEANSIM */ @@ -426,7 +426,7 @@ static DerivedMesh *doOcean(ModifierData *md, Object *ob, /* update modifier */ if (omd->refresh & MOD_OCEAN_REFRESH_ADD) - omd->ocean = BKE_add_ocean(); + omd->ocean = BKE_ocean_add(); if (omd->refresh & MOD_OCEAN_REFRESH_RESET) init_ocean_modifier(omd); if (omd->refresh & MOD_OCEAN_REFRESH_CLEAR_CACHE) @@ -437,7 +437,7 @@ static DerivedMesh *doOcean(ModifierData *md, Object *ob, /* do ocean simulation */ if (omd->cached == true) { if (!omd->oceancache) init_cache_data(ob, omd); - BKE_simulate_ocean_cache(omd->oceancache, md->scene->r.cfra); + BKE_ocean_simulate_cache(omd->oceancache, md->scene->r.cfra); } else { simulate_ocean_modifier(omd); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 79f3fbc2e5f..3fd1241f3fa 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -122,6 +122,7 @@ set(SRC composite/nodes/node_composite_boxmask.c composite/nodes/node_composite_ellipsemask.c composite/nodes/node_composite_switch.c + composite/nodes/node_composite_switchview.c composite/nodes/node_composite_colorcorrection.c composite/nodes/node_composite_pixelate.c diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 961fdbfc0fb..0215db1dd55 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -133,6 +133,7 @@ void register_node_type_cmp_ellipsemask(void); void register_node_type_cmp_bokehimage(void); void register_node_type_cmp_bokehblur(void); void register_node_type_cmp_switch(void); +void register_node_type_cmp_switch_view(void); void register_node_type_cmp_pixelate(void); void register_node_type_cmp_trackpos(void); void register_node_type_cmp_planetrackdeform(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index df6c2c16401..9e1a0c926fa 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -203,6 +203,7 @@ DefNode( CompositorNode, CMP_NODE_MASK_ELLIPSE, def_cmp_ellipsemask, "ELLIP DefNode( CompositorNode, CMP_NODE_BOKEHIMAGE, def_cmp_bokehimage, "BOKEHIMAGE", BokehImage, "Bokeh Image", "" ) DefNode( CompositorNode, CMP_NODE_BOKEHBLUR, def_cmp_bokehblur, "BOKEHBLUR", BokehBlur, "Bokeh Blur", "" ) DefNode( CompositorNode, CMP_NODE_SWITCH, def_cmp_switch, "SWITCH", Switch, "Switch", "" ) +DefNode( CompositorNode, CMP_NODE_SWITCH_VIEW, def_cmp_switch_view, "VIEWSWITCH", SwitchView, "View Switch", "" ) DefNode( CompositorNode, CMP_NODE_COLORCORRECTION,def_cmp_colorcorrection,"COLORCORRECTION",ColorCorrection, "Color Correction", "" ) DefNode( CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" ) DefNode( CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "Keying Screen", "" ) diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c index e616680647e..8bc61862458 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.c @@ -226,12 +226,13 @@ void *COM_linker_hack = NULL; void ntreeCompositExecTree(Scene *scene, bNodeTree *ntree, RenderData *rd, int rendering, int do_preview, const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings) + const ColorManagedDisplaySettings *display_settings, + const char *view_name) { #ifdef WITH_COMPOSITOR - COM_execute(rd, scene, ntree, rendering, view_settings, display_settings); + COM_execute(rd, scene, ntree, rendering, view_settings, display_settings, view_name); #else - UNUSED_VARS(scene, ntree, rd, rendering, view_settings, display_settings); + UNUSED_VARS(scene, ntree, rd, rendering, view_settings, display_settings, view_name); #endif UNUSED_VARS(do_preview); diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c index 34f3350c2ff..23d47065f59 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.c @@ -168,14 +168,21 @@ static void cmp_node_image_add_multilayer_outputs(bNodeTree *ntree, bNode *node, NodeImageLayer *sockdata; RenderPass *rpass; int index; + int passflag = 0; for (rpass = rl->passes.first, index = 0; rpass; rpass = rpass->next, ++index) { int type; if (rpass->channels == 1) type = SOCK_FLOAT; else type = SOCK_RGBA; - - sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, rpass->name, rpass->name); + + /* we only need one socket per type */ + if (passflag & rpass->passtype) + continue; + + passflag |= rpass->passtype; + + sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, rpass->internal_name, rpass->internal_name); /* extra socket info */ sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer"); sock->storage = sockdata; diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.c index 8e602c6179c..7d1087435c2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c +++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.c @@ -127,7 +127,7 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, con BKE_imformat_defaults(&sockdata->format); /* use node data format by default */ sockdata->use_node_format = true; - + nimf->active_input = BLI_findindex(&node->inputs, sock); return sock; @@ -189,7 +189,7 @@ static void init_output_file(const bContext *C, PointerRNA *ptr) } else BKE_imformat_defaults(&nimf->format); - + /* add one socket by default */ ntreeCompositOutputFileAddSocket(ntree, node, "Image", format); } diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.c new file mode 100644 index 00000000000..d805cf4d87f --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.c @@ -0,0 +1,153 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2006 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): + * Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/nodes/composite/nodes/node_composite_switchview.c + * \ingroup cmpnodes + */ + +#include "BKE_context.h" +#include "../node_composite_util.h" + +/* **************** SWITCH VIEW ******************** */ +static bNodeSocketTemplate cmp_node_switch_view_out[] = { + { SOCK_RGBA, 0, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, + { -1, 0, "" } +}; + +static bNodeSocket *ntreeCompositSwitchViewAddSocket(bNodeTree *ntree, bNode *node, const char *name) +{ + bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name); + return sock; +} + +static void cmp_node_switch_view_sanitycheck(bNodeTree *ntree, bNode *node) +{ + bNodeSocket *sock; + + if (!BLI_listbase_is_empty(&node->inputs)) + return; + + sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View"); + sock->flag |= SOCK_HIDDEN; +} + +static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) +{ + bNodeSocket *sock; + SceneRenderView *srv; + Scene *scene = (Scene *)node->id; + + /* only update when called from the operator button */ + if (node->update != NODE_UPDATE_OPERATOR) + return; + + if (scene == NULL) { + nodeRemoveAllSockets(ntree, node); + /* make sure there is always one socket */ + cmp_node_switch_view_sanitycheck(ntree, node); + return; + } + + /* remove the views that were removed */ + sock = node->inputs.last; + while (sock) { + srv = BLI_findstring(&scene->r.views, sock->name, offsetof(SceneRenderView, name)); + + if (srv == NULL) { + bNodeSocket *sock_del = sock; + sock = sock->prev; + nodeRemoveSocket(ntree, node, sock_del); + } + else { + if (srv->viewflag & SCE_VIEW_DISABLE) + sock->flag |= SOCK_HIDDEN; + else + sock->flag &= ~SOCK_HIDDEN; + + sock = sock->prev; + } + } + + /* add the new views */ + for (srv = scene->r.views.first; srv; srv = srv->next) { + sock = BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name)); + + if (sock == NULL) + sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); + + if (srv->viewflag & SCE_VIEW_DISABLE) + sock->flag |= SOCK_HIDDEN; + else + sock->flag &= ~SOCK_HIDDEN; + } + + /* make sure there is always one socket */ + cmp_node_switch_view_sanitycheck(ntree, node); +} + +static void init_switch_view(const bContext *C, PointerRNA *ptr) +{ + Scene *scene = CTX_data_scene(C); + bNodeTree *ntree = ptr->id.data; + bNode *node = ptr->data; + SceneRenderView *srv; + bNodeSocket *sock; + int nr; + + /* store scene for updates */ + node->id = (ID *)scene; + + if (scene) { + RenderData *rd = &scene->r; + + for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) { + sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); + + if ((srv->viewflag & SCE_VIEW_DISABLE)) + sock->flag |= SOCK_HIDDEN; + } + } + + /* make sure there is always one socket */ + cmp_node_switch_view_sanitycheck(ntree, node); +} + +/* custom1 = mix type */ +void register_node_type_cmp_switch_view(void) +{ + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTOR, 0); + node_type_socket_templates(&ntype, NULL, cmp_node_switch_view_out); + + ntype.initfunc_api = init_switch_view; + + node_type_update(&ntype, cmp_node_switch_view_update, NULL); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index ae5ea88abdf..72133badbbb 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -47,8 +47,7 @@ #include "../generic/python_utildefines.h" -typedef struct -{ +typedef struct { PyObject_HEAD /* The string used to separate context from actual message in PY_TRANSLATE RNA props. */ const char *context_separator; diff --git a/source/blender/quicktime/apple/qtkit_export.m b/source/blender/quicktime/apple/qtkit_export.m index 4214fa9ac32..db2d4e72dc5 100644 --- a/source/blender/quicktime/apple/qtkit_export.m +++ b/source/blender/quicktime/apple/qtkit_export.m @@ -98,8 +98,6 @@ typedef struct QuicktimeExport { } QuicktimeExport; -static struct QuicktimeExport *qtexport; - #define AUDIOOUTPUTBUFFERSIZE 65536 #pragma mark rna helper functions @@ -245,7 +243,7 @@ void makeqtstring(RenderData *rd, char *string, bool preview) } } -void filepath_qt(char *string, RenderData *rd, bool preview) +void filepath_qt(char *string, RenderData *rd, bool preview, const char *suffix) { int sfra, efra; @@ -276,8 +274,23 @@ void filepath_qt(char *string, RenderData *rd, bool preview) BLI_path_frame_range(string, sfra, efra, 4); } } + + BLI_path_suffix(string, FILE_MAX, suffix, ""); } +void *context_create_qt(void) +{ + QuicktimeExport *qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport"); + return qtexport; +} + +void context_free_qt(void *context_v) +{ + QuicktimeExport *qtexport = context_v; + if (qtexport) { + MEM_freeN(qtexport); + } +} #pragma mark audio export functions @@ -302,12 +315,13 @@ static OSStatus write_cookie(AudioConverterRef converter, AudioFileID outfile) } /* AudioConverter input stream callback */ -static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter, +static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData) -{ +{ + QuicktimeExport *qtexport = inUserData; if (qtexport->audioTotalExportedFrames >= qtexport->audioLastFrame) { /* EOF */ *ioNumberDataPackets = 0; return noErr; @@ -334,7 +348,7 @@ static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter, #pragma mark export functions -int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports, bool preview) +int start_qt(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports, bool preview, const char *UNUSED(suffix)) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSError *error; @@ -342,9 +356,8 @@ int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, R int success = 1; OSStatus err = noErr; int sfra, efra; + QuicktimeExport *qtexport = context_v; - if (qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport"); - if (preview) { sfra = rd->psfra; efra = rd->pefra; @@ -639,7 +652,7 @@ int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, R return success; } -int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, ReportList *reports) +int append_qt(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, const char *UNUSED(suffix), ReportList *reports) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSBitmapImageRep *blBitmapFormatImage; @@ -647,6 +660,7 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in OSStatus err = noErr; unsigned char *from_Ptr,*to_Ptr; int y,from_i,to_i; + QuicktimeExport *qtexport = context_v; /* Create bitmap image rep in blender format (32bit RGBA) */ blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL @@ -704,7 +718,7 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in audioPacketsConverted = 1; err = AudioConverterFillComplexBuffer(qtexport->audioConverter, AudioConverterInputCallback, - NULL, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc); + qtexport, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc); if (audioPacketsConverted) { AudioFileWritePackets(qtexport->audioFile, false, qtexport->audioBufferList.mBuffers[0].mDataByteSize, qtexport->audioOutputPktDesc, qtexport->audioOutputPktPos, &audioPacketsConverted, qtexport->audioOutputBuffer); @@ -737,9 +751,11 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in } -void end_qt(void) +void end_qt(void *context_v) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + QuicktimeExport *qtexport = context_v; + if (qtexport->movie) { if (qtexport->audioFile) @@ -830,11 +846,6 @@ void end_qt(void) } [QTMovie exitQTKitOnThread]; - - if (qtexport) { - MEM_freeN(qtexport); - qtexport = NULL; - } [pool drain]; } diff --git a/source/blender/quicktime/quicktime_export.h b/source/blender/quicktime/quicktime_export.h index 360c475fdb5..8a10a4a05d6 100644 --- a/source/blender/quicktime/quicktime_export.h +++ b/source/blender/quicktime/quicktime_export.h @@ -55,10 +55,12 @@ struct RenderData; struct ReportList; struct Scene; -int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); //for movie handle (BKE writeavi.c now) -int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, struct ReportList *reports); -void end_qt(void); -void filepath_qt(char *string, struct RenderData *rd, bool preview); +int start_qt(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview, const char *suffix); //for movie handle (BKE writeavi.c now) +int append_qt(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, const char *suffix, struct ReportList *reports); +void end_qt(void *context_v); +void filepath_qt(char *string, struct RenderData *rd, bool preview, const char *suffix); +void *context_create_qt(void); +void context_free_qt(void *context_v); /*RNA helper functions */ void quicktime_verify_image_type(struct RenderData *rd, struct ImageFormatData *imf); //used by RNA for defaults values init, if needed diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h index a52244ff87f..4857c409f08 100644 --- a/source/blender/render/extern/include/RE_engine.h +++ b/source/blender/render/extern/include/RE_engine.h @@ -135,10 +135,14 @@ void RE_engine_free(RenderEngine *engine); void RE_layer_load_from_file(struct RenderLayer *layer, struct ReportList *reports, const char *filename, int x, int y); void RE_result_load_from_file(struct RenderResult *result, struct ReportList *reports, const char *filename); -struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername); +struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname); void RE_engine_update_result(RenderEngine *engine, struct RenderResult *result); void RE_engine_end_result(RenderEngine *engine, struct RenderResult *result, int cancel, int merge_results); +void RE_engine_active_view_set(RenderEngine *engine, const char *viewname); +float RE_engine_get_camera_shift_x(RenderEngine *engine, struct Object *camera); +void RE_engine_get_camera_model_matrix(RenderEngine *engine, struct Object *camera, float *r_modelmat); + int RE_engine_test_break(RenderEngine *engine); void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info); void RE_engine_update_progress(RenderEngine *engine, float progress); diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index d1b6673c792..2a07246eaba 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -35,8 +35,10 @@ #include "DNA_listBase.h" #include "DNA_vec_types.h" +struct bMovieHandle; struct bNodeTree; struct Image; +struct ImageFormatData; struct Main; struct NodeBlurData; struct Object; @@ -46,6 +48,7 @@ struct ReportList; struct Scene; struct SceneRenderLayer; struct EnvMap; +struct RenderResult; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* this include is what is exposed of render to outside world */ @@ -65,6 +68,19 @@ typedef struct Render Render; * and how it's converted */ +typedef struct RenderView { + struct RenderView *next, *prev; + char name[64]; /* EXR_VIEW_MAXNAME */ + + /* if this exists, result of composited layers */ + float *rectf; + /* if this exists, result of composited layers */ + float *rectz; + /* optional, 32 bits version of picture, used for sequencer, ogl render and image curves */ + int *rect32; + +} RenderView; + typedef struct RenderPass { struct RenderPass *next, *prev; int passtype, channels; @@ -72,6 +88,11 @@ typedef struct RenderPass { char chan_id[8]; /* amount defined in openexr_multi.h */ float *rect; int rectx, recty; + + char internal_name[64]; /* EXR_PASS_MAXNAME */ + char view[64]; /* EXR_VIEW_MAXNAME */ + int view_id; /* quick lookup */ + int debug_type; } RenderPass; @@ -81,7 +102,7 @@ enum { /* a renderlayer is a full image, but with all passes and samples */ /* size of the rects is defined in RenderResult */ -/* after render, the Combined pass is in rectf, for renderlayers read from files it is a real pass */ +/* after render, the Combined pass is in combined, for renderlayers read from files it is a real pass */ typedef struct RenderLayer { struct RenderLayer *next, *prev; @@ -92,8 +113,10 @@ typedef struct RenderLayer { struct Material *mat_override; struct Group *light_override; - - float *rectf; /* 4 float, standard rgba buffer (read not above!) */ + + /* MULTIVIEW_TODO: acolrect and scolrect are not supported by multiview at the moment. + * If they are really required they should be in RenderView instead */ + float *acolrect; /* 4 float, optional transparent buffer, needs storage for display updates */ float *scolrect; /* 4 float, optional strand buffer, needs storage for display updates */ int *display_buffer; /* 4 char, optional color managed display buffer which is used when @@ -129,6 +152,9 @@ typedef struct RenderResult { /* the main buffers */ ListBase layers; + /* multiView maps to a StringVector in OpenEXR */ + ListBase views; + /* allowing live updates: */ volatile rcti renrect; volatile RenderLayer *renlay; @@ -183,16 +209,18 @@ void RE_FreeRenderResult(struct RenderResult *rr); struct RenderResult *RE_AcquireResultRead(struct Render *re); struct RenderResult *RE_AcquireResultWrite(struct Render *re); void RE_ReleaseResult(struct Render *re); -void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr); +void RE_AcquireResultImageViews(struct Render *re, struct RenderResult *rr); +void RE_ReleaseResultImageViews(struct Render *re, struct RenderResult *rr); +void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr, const int view_id); void RE_ReleaseResultImage(struct Render *re); void RE_SwapResult(struct Render *re, struct RenderResult **rr); struct RenderStats *RE_GetStats(struct Render *re); void RE_ResultGet32(struct Render *re, unsigned int *rect); -void RE_AcquiredResultGet32(struct Render *re, struct RenderResult *result, unsigned int *rect); +void RE_AcquiredResultGet32(struct Render *re, struct RenderResult *result, unsigned int *rect, const int view_id); struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name); -float *RE_RenderLayerGetPass(struct RenderLayer *rl, int passtype); +float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname); /* obligatory initialize call, disprect is optional */ void RE_InitState(struct Render *re, struct Render *source, struct RenderData *rd, @@ -203,6 +231,7 @@ void RE_ChangeModeFlag(struct Render *re, int flag, bool clear); /* set up the viewplane/perspective matrix, three choices */ struct Object *RE_GetCamera(struct Render *re); /* return camera override if set */ +void RE_SetOverrideCamera(struct Render *re, struct Object *camera); void RE_SetCamera(struct Render *re, struct Object *camera); void RE_SetEnvmapCamera(struct Render *re, struct Object *cam_ob, float viewscale, float clipsta, float clipend); void RE_SetWindow(struct Render *re, rctf *viewplane, float clipsta, float clipend); @@ -232,6 +261,11 @@ void RE_init_threadcount(Render *re); /* the main processor, assumes all was set OK! */ void RE_TileProcessor(struct Render *re); +bool RE_WriteRenderViewsImage(struct ReportList *reports, struct RenderResult *rr, struct Scene *scene, struct Object *camera, const bool stamp, char *name); +bool RE_WriteRenderViewsMovie(struct ReportList *reports, struct RenderResult *rr, struct Scene *scene, struct RenderData *rd, + struct bMovieHandle *mh, const size_t width, const size_t height, void **movie_ctx_arr, + const size_t totvideos); + /* only RE_NewRender() needed, main Blender render calls */ void RE_BlenderFrame(struct Render *re, struct Main *bmain, struct Scene *scene, struct SceneRenderLayer *srl, struct Object *camera_override, @@ -243,6 +277,9 @@ void RE_RenderFreestyleStrokes(struct Render *re, struct Main *bmain, struct Sce void RE_RenderFreestyleExternal(struct Render *re); #endif +void RE_SetActiveRenderView(struct Render *re, const char *viewname); +const char *RE_GetActiveRenderView(struct Render *re); + /* error reporting */ void RE_SetReports(struct Render *re, struct ReportList *reports); @@ -250,7 +287,7 @@ void RE_SetReports(struct Render *re, struct ReportList *reports); void RE_PreviewRender(struct Render *re, struct Main *bmain, struct Scene *scene); bool RE_ReadRenderResult(struct Scene *scene, struct Scene *scenode); -bool RE_WriteRenderResult(struct ReportList *reports, RenderResult *rr, const char *filename, int compress); +bool RE_WriteRenderResult(struct ReportList *reports, RenderResult *rr, const char *filename, struct ImageFormatData *imf, const bool multiview, const char *view); struct RenderResult *RE_MultilayerConvert(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty); extern const float default_envmap_layout[]; @@ -276,6 +313,8 @@ void RE_zbuf_accumulate_vecblur(struct NodeBlurData *nbd, int xsize, int ysize, int RE_seq_render_active(struct Scene *scene, struct RenderData *rd); +bool RE_layers_have_name(struct RenderResult *result); + /* shaded view or baking options */ #define RE_BAKE_LIGHT 0 /* not listed in rna_scene.c -> can't be enabled! */ #define RE_BAKE_ALL 1 @@ -297,6 +336,7 @@ void RE_Database_Baking(struct Render *re, struct Main *bmain, struct Scene *sce void RE_DataBase_GetView(struct Render *re, float mat[4][4]); void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, float mat[4][4]); +void RE_GetCameraModelMatrix(struct Render *re, struct Object *camera, float r_mat[4][4]); struct Scene *RE_GetScene(struct Render *re); bool RE_force_single_renderlayer(struct Scene *scene); @@ -304,5 +344,16 @@ bool RE_is_rendering_allowed(struct Scene *scene, struct Object *camera_override bool RE_allow_render_generic_object(struct Object *ob); +/******* defined in render_result.c *********/ + +bool RE_HasFakeLayer(RenderResult *res); +bool RE_RenderResult_is_stereo(RenderResult *res); + +float *RE_RenderViewGetRectf(struct RenderResult *rr, const int view_id); +float *RE_RenderViewGetRectz(struct RenderResult *rr, const int view_id); +int *RE_RenderViewGetRect32(struct RenderResult *rr, const int view_id); +void RE_RenderViewSetRectf(struct RenderResult *res, const int view_id, float *rect); +void RE_RenderViewSetRectz(struct RenderResult *res, const int view_id, float *rect); + #endif /* __RE_PIPELINE_H__ */ diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h index 90ff69dbfbe..e7a2cbf99cd 100644 --- a/source/blender/render/intern/include/render_result.h +++ b/source/blender/render/intern/include/render_result.h @@ -38,6 +38,7 @@ #define RR_USE_EXR 1 #define RR_ALL_LAYERS NULL +#define RR_ALL_VIEWS NULL struct ImBuf; struct ListBase; @@ -53,12 +54,14 @@ struct ColorManagedViewSettings; /* New */ struct RenderResult *render_result_new(struct Render *re, - struct rcti *partrct, int crop, int savebuffers, const char *layername); + struct rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname); struct RenderResult *render_result_new_full_sample(struct Render *re, - struct ListBase *lb, struct rcti *partrct, int crop, int savebuffers); + struct ListBase *lb, struct rcti *partrct, int crop, int savebuffers, const char *viewname); struct RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty); +void render_result_views_new(struct RenderResult *rr, struct RenderData *rd); + /* Merge */ void render_result_merge(struct RenderResult *rr, struct RenderResult *rrpart); @@ -78,7 +81,7 @@ void render_result_single_layer_end(struct Render *re); void render_result_exr_file_begin(struct Render *re); void render_result_exr_file_end(struct Render *re); -void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart); +void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart, const char *viewname); void render_result_exr_file_path(struct Scene *scene, const char *layname, int sample, char *filepath); int render_result_exr_file_read_sample(struct Render *re, int sample); @@ -91,15 +94,19 @@ bool render_result_exr_file_cache_read(struct Render *re); /* Combined Pixel Rect */ -struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd); +struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd, const int view_id); void render_result_rect_from_ibuf(struct RenderResult *rr, struct RenderData *rd, - struct ImBuf *ibuf); + struct ImBuf *ibuf, const int view_id); -void render_result_rect_fill_zero(struct RenderResult *rr); +void render_result_rect_fill_zero(struct RenderResult *rr, const int view_id); void render_result_rect_get_pixels(struct RenderResult *rr, unsigned int *rect, int rectx, int recty, const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings); + const struct ColorManagedDisplaySettings *display_settings, + const int view_id); + +void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResult *src); +void render_result_views_shallowdelete(struct RenderResult *rr); #endif /* __RENDER_RESULT_H__ */ diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index 0d23c81afb0..e12a1575992 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -122,8 +122,7 @@ enum { }; /* controls state of render, everything that's read-only during render stage */ -struct Render -{ +struct Render { struct Render *next, *prev; char name[RE_MAXNAME]; int slot; @@ -275,6 +274,9 @@ struct Render struct ImagePool *pool; struct EvaluationContext *eval_ctx; + + void **movie_ctx_arr; + char viewname[MAX_NAME]; }; /* ------------------------------------------------------------------------- */ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index e900d29aa19..d36ab46d145 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -3813,7 +3813,9 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) } /* set flag for spothalo en initvars */ - if (la->type==LA_SPOT && (la->mode & LA_HALO) && (la->buftype != LA_SHADBUF_DEEP)) { + if ((la->type == LA_SPOT) && (la->mode & LA_HALO) && + (!(la->mode & LA_SHAD_BUF) || la->buftype != LA_SHADBUF_DEEP)) + { if (la->haint>0.0f) { re->flag |= R_LAMPHALO; @@ -3832,7 +3834,7 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) lar->sh_invcampos[2]*= lar->sh_zfac; /* halfway shadow buffer doesn't work for volumetric effects */ - if (lar->buftype == LA_SHADBUF_HALFWAY) + if (ELEM(lar->buftype, LA_SHADBUF_HALFWAY, LA_SHADBUF_DEEP)) lar->buftype = LA_SHADBUF_REGULAR; } @@ -5002,7 +5004,7 @@ static void database_init_objects(Render *re, unsigned int renderlay, int nolamp * system need to have render settings set for dupli particles */ dupli_render_particle_set(re, ob, timeoffset, 0, 1); duplilist = object_duplilist(re->eval_ctx, re->scene, ob); - duplilist_apply_data = duplilist_apply(ob, duplilist); + duplilist_apply_data = duplilist_apply(ob, NULL, duplilist); dupli_render_particle_set(re, ob, timeoffset, 0, 0); for (dob= duplilist->first, i = 0; dob; dob= dob->next, ++i) { @@ -5169,8 +5171,7 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l * above call to BKE_scene_update_for_newframe, fixes bug. [#22702]. * following calls don't depend on 'RE_SetCamera' */ RE_SetCamera(re, camera); - - normalize_m4_m4(mat, camera->obmat); + RE_GetCameraModelMatrix(re, camera, mat); invert_m4(mat); RE_SetView(re, mat); @@ -5340,7 +5341,8 @@ static void database_fromscene_vectors(Render *re, Scene *scene, unsigned int la /* if no camera, viewmat should have been set! */ if (camera) { - normalize_m4_m4(mat, camera->obmat); + RE_GetCameraModelMatrix(re, camera, mat); + normalize_m4(mat); invert_m4(mat); RE_SetView(re, mat); } diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c index 0698f849073..ed3033d8393 100644 --- a/source/blender/render/intern/source/envmap.c +++ b/source/blender/render/intern/source/envmap.c @@ -495,9 +495,11 @@ static void render_envmap(Render *re, EnvMap *env) RenderLayer *rl = envre->result->layers.first; int y; float *alpha; - + float *rect; + + rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); ibuf = IMB_allocImBuf(envre->rectx, envre->recty, 24, IB_rect | IB_rectfloat); - memcpy(ibuf->rect_float, rl->rectf, ibuf->channels * ibuf->x * ibuf->y * sizeof(float)); + memcpy(ibuf->rect_float, rect, ibuf->channels * ibuf->x * ibuf->y * sizeof(float)); /* envmap renders without alpha */ alpha = ibuf->rect_float + 3; diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index e56466bdf48..343c4a9387a 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -42,6 +42,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BKE_camera.h" #include "BKE_global.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -181,7 +182,7 @@ static RenderPart *get_part_from_result(Render *re, RenderResult *result) return NULL; } -RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername) +RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) { Render *re = engine->re; RenderResult *result; @@ -204,7 +205,7 @@ RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, disprect.ymin = y; disprect.ymax = y + h; - result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername); + result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); /* todo: make this thread safe */ @@ -269,7 +270,7 @@ void RE_engine_end_result(RenderEngine *engine, RenderResult *result, int cancel if (!cancel || merge_results) { if (re->result->do_exr_tile) { if (!cancel) { - render_result_exr_file_merge(re->result, result); + render_result_exr_file_merge(re->result, result, re->viewname); } } else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) @@ -368,6 +369,24 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg) } } +void RE_engine_active_view_set(RenderEngine *engine, const char *viewname) +{ + Render *re = engine->re; + RE_SetActiveRenderView(re, viewname); +} + +float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera) +{ + Render *re = engine->re; + return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname); +} + +void RE_engine_get_camera_model_matrix(RenderEngine *engine, Object *camera, float *r_modelmat) +{ + Render *re = engine->re; + BKE_camera_multiview_model_matrix(re ? &re->r : NULL, camera, re->viewname, (float (*)[4])r_modelmat); +} + rcti* RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free) { static rcti tiles_static[BLENDER_MAX_THREADS]; @@ -612,7 +631,7 @@ int RE_engine_render(Render *re, int do_all) if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) savebuffers = RR_USE_EXR; - re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c index a0fcc7cdcd2..970a3937657 100644 --- a/source/blender/render/intern/source/initrender.c +++ b/source/blender/render/intern/source/initrender.c @@ -426,10 +426,10 @@ void make_sample_tables(Render *re) /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - struct Object *RE_GetCamera(Render *re) { - return re->camera_override ? re->camera_override : re->scene->camera; + Object *camera = re->camera_override ? re->camera_override : re->scene->camera; + return BKE_camera_multiview_render(re->scene, camera, re->viewname); } static void re_camera_params_get(Render *re, CameraParams *params, Object *cam_ob) @@ -470,6 +470,16 @@ void RE_SetEnvmapCamera(Render *re, Object *cam_ob, float viewscale, float clips re_camera_params_get(re, ¶ms, cam_ob); } +void RE_SetOverrideCamera(Render *re, Object *camera) +{ + re->camera_override = camera; +} + +static void re_camera_params_stereo3d(Render *re, CameraParams *params, Object *cam_ob) +{ + BKE_camera_multiview_params(&re->r, params, cam_ob, re->viewname); +} + /* call this after InitState() */ /* per render, there's one persistent viewplane. Parts will set their own viewplanes */ void RE_SetCamera(Render *re, Object *cam_ob) @@ -479,6 +489,7 @@ void RE_SetCamera(Render *re, Object *cam_ob) /* setup parameters */ BKE_camera_params_init(¶ms); BKE_camera_params_from_object(¶ms, cam_ob); + re_camera_params_stereo3d(re, ¶ms, cam_ob); params.use_fields = (re->r.mode & R_FIELDS); params.field_second = (re->flag & R_SEC_FIELD); @@ -505,6 +516,11 @@ void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, flo copy_m4_m4(mat, re->winmat); } +void RE_GetCameraModelMatrix(Render *re, struct Object *camera, float r_mat[4][4]) +{ + BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_mat); +} + /* ~~~~~~~~~~~~~~~~ part (tile) calculus ~~~~~~~~~~~~~~~~~~~~~~ */ diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 449c4be042b..4af69e46e5c 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -70,6 +70,7 @@ #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */ +#include "BKE_object.h" #include "PIL_time.h" #include "IMB_colormanagement.h" @@ -131,7 +132,7 @@ Render R; /* ********* alloc and free ******** */ -static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override); +static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const size_t totvideos, const char *name_override); static volatile int g_break = 0; static int thread_break(void *UNUSED(arg)) @@ -190,14 +191,23 @@ void RE_FreeRenderResult(RenderResult *res) render_result_free(res); } -float *RE_RenderLayerGetPass(RenderLayer *rl, int passtype) +float *RE_RenderLayerGetPass(volatile RenderLayer *rl, int passtype, const char *viewname) { RenderPass *rpass; - - for (rpass = rl->passes.first; rpass; rpass = rpass->next) - if (rpass->passtype == passtype) - return rpass->rect; - return NULL; + float *rect = NULL; + + for (rpass = rl->passes.last; rpass; rpass = rpass->prev) { + if (rpass->passtype == passtype) { + rect = rpass->rect; + + if (viewname == NULL) + break; + else if (strcmp(rpass->view, viewname) == 0) + break; + } + } + + return rect; } RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name) @@ -307,8 +317,69 @@ Scene *RE_GetScene(Render *re) return NULL; } +/** + * Same as #RE_AcquireResultImage but creating the necessary views to store the result + * fill provided result struct with a copy of thew views of what is done so far the + * #RenderResult.views #ListBase needs to be freed after with #RE_ReleaseResultImageViews +*/ +void RE_AcquireResultImageViews(Render *re, RenderResult *rr) +{ + memset(rr, 0, sizeof(RenderResult)); + + if (re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); + + if (re->result) { + RenderLayer *rl; + RenderView *rv, *rview; + + rr->rectx = re->result->rectx; + rr->recty = re->result->recty; + + /* creates a temporary duplication of views */ + render_result_views_shallowcopy(rr, re->result); + + rv = rr->views.first; + + rr->have_combined = (rv->rectf != NULL); + + /* active layer */ + rl = render_get_active_layer(re, re->result); + + if (rl) { + if (rv->rectf == NULL) { + for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { + rview->rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, rview->name); + } + } + + if (rv->rectz == NULL) { + for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { + rview->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z, rview->name); + } + } + } + + rr->layers = re->result->layers; + rr->xof = re->disprect.xmin; + rr->yof = re->disprect.ymin; + } + } +} + +/* clear temporary renderresult struct */ +void RE_ReleaseResultImageViews(Render *re, RenderResult *rr) +{ + if (re) { + if (rr) { + render_result_views_shallowdelete(rr); + } + BLI_rw_mutex_unlock(&re->resultmutex); + } +} + /* fill provided result struct with what's currently active or done */ -void RE_AcquireResultImage(Render *re, RenderResult *rr) +void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id) { memset(rr, 0, sizeof(RenderResult)); @@ -317,27 +388,35 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr) if (re->result) { RenderLayer *rl; + RenderView *rv; rr->rectx = re->result->rectx; rr->recty = re->result->recty; - rr->rectf = re->result->rectf; - rr->rectz = re->result->rectz; - rr->rect32 = re->result->rect32; - + /* actview view */ + rv = BLI_findlink(&re->result->views, view_id); + if (rv == NULL) + rv = (RenderView *)re->result->views.first; + + rr->rectf = rv ? rv->rectf : NULL; + rr->rectz = rv ? rv->rectz : NULL; + rr->rect32 = rv ? rv->rect32 : NULL; + /* active layer */ rl = render_get_active_layer(re, re->result); - if (rl) { - if (rr->rectf == NULL) - rr->rectf = rl->rectf; - if (rr->rectz == NULL) - rr->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z); + if (rl && rv) { + if (rv->rectf == NULL) + rr->rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, rv->name); + + if (rv->rectz == NULL) + rr->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z, rv->name); } - rr->have_combined = (re->result->rectf != NULL); + rr->have_combined = rv ? (rv->rectf != NULL) : false; rr->layers = re->result->layers; - + rr->views = re->result->views; + rr->xof = re->disprect.xmin; rr->yof = re->disprect.ymin; } @@ -354,17 +433,18 @@ void RE_ReleaseResultImage(Render *re) void RE_ResultGet32(Render *re, unsigned int *rect) { RenderResult rres; - - RE_AcquireResultImage(re, &rres); - render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings); + const size_t view_id = BKE_scene_multiview_view_id_get(&re->r, re->viewname); + + RE_AcquireResultImage(re, &rres, view_id); + render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, 0); RE_ReleaseResultImage(re); } /* caller is responsible for allocating rect in correct size! */ /* Only for acquired results, for lock */ -void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect) +void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect, const int view_id) { - render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings); + render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, view_id); } RenderStats *RE_GetStats(Render *re) @@ -683,6 +763,8 @@ void RE_InitState(Render *re, Render *source, RenderData *rd, RE_init_threadcount(re); } +/* This function is only called by view3d rendering, which doesn't support + * multiview at the moment. so handle only one view here */ static void render_result_rescale(Render *re) { RenderResult *result = re->result; @@ -694,7 +776,7 @@ static void render_result_rescale(Render *re) if (src_rectf == NULL) { RenderLayer *rl = render_get_active_layer(re, re->result); if (rl != NULL) { - src_rectf = rl->rectf; + src_rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, ""); } } @@ -704,7 +786,8 @@ static void render_result_rescale(Render *re) &re->disprect, 0, RR_USE_MEM, - RR_ALL_LAYERS); + RR_ALL_LAYERS, + ""); if (re->result != NULL) { dst_rectf = re->result->rectf; @@ -712,7 +795,7 @@ static void render_result_rescale(Render *re) RenderLayer *rl; rl = render_get_active_layer(re, re->result); if (rl != NULL) { - dst_rectf = rl->rectf; + dst_rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, ""); } } @@ -910,9 +993,9 @@ static void *do_part_thread(void *pa_v) if (R.test_break(R.tbh) == 0) { if (!R.sss_points && (R.r.scemode & R_FULL_SAMPLE)) - pa->result = render_result_new_full_sample(&R, &pa->fullresult, &pa->disprect, pa->crop, RR_USE_MEM); + pa->result = render_result_new_full_sample(&R, &pa->fullresult, &pa->disprect, pa->crop, RR_USE_MEM, R.viewname); else - pa->result = render_result_new(&R, &pa->disprect, pa->crop, RR_USE_MEM, RR_ALL_LAYERS); + pa->result = render_result_new(&R, &pa->disprect, pa->crop, RR_USE_MEM, RR_ALL_LAYERS, R.viewname); /* Copy EXR tile settings, so pipeline knows whether this is a result * for Save Buffers enabled rendering. @@ -935,7 +1018,7 @@ static void *do_part_thread(void *pa_v) /* merge too on break! */ if (R.result->do_exr_tile) { - render_result_exr_file_merge(R.result, pa->result); + render_result_exr_file_merge(R.result, pa->result, R.viewname); } else if (render_display_update_enabled(&R)) { /* on break, don't merge in result for preview renders, looks nicer */ @@ -1115,16 +1198,23 @@ static void *do_render_thread(void *thread_v) return NULL; } -static void threaded_tile_processor(Render *re) +static void main_render_result_end(Render *re) +{ + if (re->result->do_exr_tile) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_end(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } + + if (re->r.scemode & R_EXR_CACHE_FILE) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_cache_write(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } +} + +static void main_render_result_new(Render *re) { - RenderThread thread[BLENDER_MAX_THREADS]; - ThreadQueue *workqueue, *donequeue; - ListBase threads; - RenderPart *pa; - rctf viewplane = re->viewplane; - double lastdraw, elapsed, redrawtime = 1.0f; - int totpart = 0, minx = 0, slice = 0, a, wait; - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); /* first step; free the entire render result, make new, and/or prepare exr buffer saving */ @@ -1132,25 +1222,35 @@ static void threaded_tile_processor(Render *re) render_result_free(re->result); if (re->sss_points && render_display_update_enabled(re)) - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); else if (re->r.scemode & R_FULL_SAMPLE) - re->result = render_result_new_full_sample(re, &re->fullresult, &re->disprect, 0, RR_USE_EXR); + re->result = render_result_new_full_sample(re, &re->fullresult, &re->disprect, 0, RR_USE_EXR, RR_ALL_VIEWS); else re->result = render_result_new(re, &re->disprect, 0, - (re->r.scemode & R_EXR_TILE_FILE) ? RR_USE_EXR : RR_USE_MEM, RR_ALL_LAYERS); + (re->r.scemode & R_EXR_TILE_FILE) ? RR_USE_EXR : RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); + + if (re->result->do_exr_tile) + render_result_exr_file_begin(re); +} + +static void threaded_tile_processor(Render *re) +{ + RenderThread thread[BLENDER_MAX_THREADS]; + ThreadQueue *workqueue, *donequeue; + ListBase threads; + RenderPart *pa; + rctf viewplane = re->viewplane; + double lastdraw, elapsed, redrawtime = 1.0f; + int totpart = 0, minx = 0, slice = 0, a, wait; if (re->result == NULL) return; /* warning; no return here without closing exr file */ - RE_parts_init(re, true); - - if (re->result->do_exr_tile) - render_result_exr_file_begin(re); /* assuming no new data gets added to dbase... */ R = *re; @@ -1256,18 +1356,6 @@ static void threaded_tile_processor(Render *re) BLI_thread_queue_free(donequeue); BLI_thread_queue_free(workqueue); - if (re->result->do_exr_tile) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_exr_file_end(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - - if (re->r.scemode & R_EXR_CACHE_FILE) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_exr_file_cache_write(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - /* unset threadsafety */ g_break = 0; BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); @@ -1284,6 +1372,7 @@ static void free_all_freestyle_renders(void); /* currently only called by preview renders and envmap */ void RE_TileProcessor(Render *re) { + main_render_result_new(re); threaded_tile_processor(re); re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; @@ -1309,6 +1398,7 @@ void RE_TileProcessor(Render *re) static void do_render_3d(Render *re) { + RenderView *rv; int cfra_backup; re->current_scene_update(re->suh, re->scene); @@ -1325,40 +1415,49 @@ static void do_render_3d(Render *re) BKE_scene_frame_set(re->scene, (double)re->scene->r.cfra + (double)re->mblur_offs + (double)re->field_offs); - /* lock drawing in UI during data phase */ - if (re->draw_lock) - re->draw_lock(re->dlh, 1); - - /* make render verts/faces/halos/lamps */ - if (render_scene_needs_vector(re)) { - RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay); - } - else { - RE_Database_FromScene(re, re->main, re->scene, re->lay, 1); - RE_Database_Preprocess(re); - } + /* init main render result */ + main_render_result_new(re); + + /* we need a new database for each view */ + for (rv = re->result->views.first; rv; rv = rv->next) { + RE_SetActiveRenderView(re, rv->name); + + /* lock drawing in UI during data phase */ + if (re->draw_lock) + re->draw_lock(re->dlh, 1); + + /* make render verts/faces/halos/lamps */ + if (render_scene_needs_vector(re)) + RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay); + else { + RE_Database_FromScene(re, re->main, re->scene, re->lay, 1); + RE_Database_Preprocess(re); + } - /* clear UI drawing locks */ - if (re->draw_lock) - re->draw_lock(re->dlh, 0); + /* clear UI drawing locks */ + if (re->draw_lock) + re->draw_lock(re->dlh, 0); - threaded_tile_processor(re); + threaded_tile_processor(re); #ifdef WITH_FREESTYLE - /* Freestyle */ - if (re->r.mode & R_EDGE_FRS) - if (!re->test_break(re->tbh)) - add_freestyle(re, 1); + /* Freestyle */ + if (re->r.mode & R_EDGE_FRS) + if (!re->test_break(re->tbh)) + add_freestyle(re, 1); #endif - /* do left-over 3d post effects (flares) */ - if (re->flag & R_HALO) - if (!re->test_break(re->tbh)) - add_halo_flare(re); - - /* free all render verts etc */ - RE_Database_Free(re); - + /* do left-over 3d post effects (flares) */ + if (re->flag & R_HALO) + if (!re->test_break(re->tbh)) + add_halo_flare(re); + + /* free all render verts etc */ + RE_Database_Free(re); + } + + main_render_result_end(re); + re->scene->r.cfra = cfra_backup; re->scene->r.subframe = 0.f; } @@ -1431,19 +1530,13 @@ static void merge_renderresult_blur(RenderResult *rr, RenderResult *brr, float b rl1 = brr->layers.first; for (rl = rr->layers.first; rl && rl1; rl = rl->next, rl1 = rl1->next) { - - /* combined */ - if (rl->rectf && rl1->rectf) { - if (key_alpha) - addblur_rect_key(rr, rl->rectf, rl1->rectf, blurfac); - else - addblur_rect(rr, rl->rectf, rl1->rectf, blurfac, 4); - } - /* passes are allocated in sync */ rpass1 = rl1->passes.first; for (rpass = rl->passes.first; rpass && rpass1; rpass = rpass->next, rpass1 = rpass1->next) { - addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels); + if ((rpass->passtype & SCE_PASS_COMBINED) && key_alpha) + addblur_rect_key(rr, rpass->rect, rpass1->rect, blurfac); + else + addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels); } } } @@ -1456,7 +1549,7 @@ static void do_render_blur_3d(Render *re) int blur = re->r.mblur_samples; /* create accumulation render result */ - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); /* do the blur steps */ while (blur--) { @@ -1518,10 +1611,6 @@ static void merge_renderresult_fields(RenderResult *rr, RenderResult *rr1, Rende rl2 = rr2->layers.first; for (rl = rr->layers.first; rl && rl1 && rl2; rl = rl->next, rl1 = rl1->next, rl2 = rl2->next) { - /* combined */ - if (rl->rectf && rl1->rectf && rl2->rectf) - interleave_rect(rr, rl->rectf, rl1->rectf, rl2->rectf, 4); - /* passes are allocated in sync */ rpass1 = rl1->passes.first; rpass2 = rl2->passes.first; @@ -1589,7 +1678,7 @@ static void do_render_fields_3d(Render *re) re->disprect.ymax *= 2; BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); if (rr2) { if (re->r.mode & R_ODDFIELD) @@ -1661,7 +1750,7 @@ static void do_render_fields_blur_3d(Render *re) /* weak is: it chances disprect from border */ render_result_disprect_to_full_resolution(re); - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); render_result_merge(rres, re->result); render_result_free(re->result); @@ -2068,11 +2157,14 @@ static void free_all_freestyle_renders(void) } #endif -/* reads all buffers, calls optional composite, merges in first result->rectf */ +/* reads all buffers, calls optional composite, merges in first result->views rectf */ static void do_merge_fullsample(Render *re, bNodeTree *ntree) { + ListBase *rectfs; + RenderView *rv; float *rectf, filt[3][3]; int x, y, sample; + int nr, numviews; /* interaction callbacks */ if (ntree) { @@ -2087,9 +2179,18 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) /* filtmask needs it */ R = *re; - /* we accumulate in here */ - rectf = MEM_mapallocN(re->rectx * re->recty * sizeof(float) * 4, "fullsample rgba"); - + /* temporary storage of the acccumulation buffers */ + rectfs = MEM_callocN(sizeof(ListBase), "fullsample accumulation buffers"); + + numviews = BLI_listbase_count(&re->result->views); + for (nr = 0; nr < numviews; nr++) { + rv = MEM_callocN(sizeof(RenderView), "fullsample renderview"); + + /* we accumulate in here */ + rv->rectf = MEM_mapallocN(re->rectx * re->recty * sizeof(float) * 4, "fullsample rgba"); + BLI_addtail(rectfs, rv); + } + for (sample = 0; sample < re->r.osa; sample++) { Scene *sce; Render *re1; @@ -2126,54 +2227,70 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) if (ntree) { ntreeCompositTagRender(re->scene); ntreeCompositTagAnimated(ntree); - - ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings); + + for (rv = re->result->views.first; rv; rv = rv->next) { + ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name); + } } - - /* ensure we get either composited result or the active layer */ - RE_AcquireResultImage(re, &rres); - - /* accumulate with filter, and clip */ - mask = (1 << sample); - mask_array(mask, filt); - for (y = 0; y < re->recty; y++) { - float *rf = rectf + 4 * y * re->rectx; - float *col = rres.rectf + 4 * y * re->rectx; - - for (x = 0; x < re->rectx; x++, rf += 4, col += 4) { - /* clamping to 1.0 is needed for correct AA */ - if (col[0] < 0.0f) col[0] = 0.0f; else if (col[0] > 1.0f) col[0] = 1.0f; - if (col[1] < 0.0f) col[1] = 0.0f; else if (col[1] > 1.0f) col[1] = 1.0f; - if (col[2] < 0.0f) col[2] = 0.0f; else if (col[2] > 1.0f) col[2] = 1.0f; + for (nr = 0, rv = re->result->views.first; rv; rv = rv->next, nr++) { + rectf = rv->rectf; + + /* ensure we get either composited result or the active layer */ + RE_AcquireResultImage(re, &rres, nr); + + /* accumulate with filter, and clip */ + mask = (1 << sample); + mask_array(mask, filt); + + for (y = 0; y < re->recty; y++) { + float *rf = rectf + 4 * y * re->rectx; + float *col = rres.rectf + 4 * y * re->rectx; - add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y); + for (x = 0; x < re->rectx; x++, rf += 4, col += 4) { + /* clamping to 1.0 is needed for correct AA */ + if (col[0] < 0.0f) col[0] = 0.0f; else if (col[0] > 1.0f) col[0] = 1.0f; + if (col[1] < 0.0f) col[1] = 0.0f; else if (col[1] > 1.0f) col[1] = 1.0f; + if (col[2] < 0.0f) col[2] = 0.0f; else if (col[2] > 1.0f) col[2] = 1.0f; + + add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y); + } } - } - RE_ReleaseResultImage(re); + RE_ReleaseResultImage(re); - /* show stuff */ - if (sample != re->osa - 1) { - /* weak... the display callback wants an active renderlayer pointer... */ - re->result->renlay = render_get_active_layer(re, re->result); - re->display_update(re->duh, re->result, NULL); + /* show stuff */ + if (sample != re->osa - 1) { + /* weak... the display callback wants an active renderlayer pointer... */ + re->result->renlay = render_get_active_layer(re, re->result); + RE_SetActiveRenderView(re, rv->name); + re->display_update(re->duh, re->result, NULL); + } } - - if (re->test_break(re->tbh)) - break; } - /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ - for (y = 0; y < re->recty; y++) { - float *rf = rectf + 4 * y * re->rectx; + for (nr = 0; nr < numviews; nr++) { + rectf = ((RenderView *)BLI_findlink(rectfs, nr))->rectf; + + /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ + for (y = 0; y < re->recty; y++) { + float *rf = rectf + 4 * y * re->rectx; - for (x = 0; x < re->rectx; x++, rf += 4) { - rf[0] = MAX2(rf[0], 0.0f); - rf[1] = MAX2(rf[1], 0.0f); - rf[2] = MAX2(rf[2], 0.0f); - CLAMP(rf[3], 0.0f, 1.0f); + for (x = 0; x < re->rectx; x++, rf += 4) { + rf[0] = MAX2(rf[0], 0.0f); + rf[1] = MAX2(rf[1], 0.0f); + rf[2] = MAX2(rf[2], 0.0f); + CLAMP(rf[3], 0.0f, 1.0f); + } } + + /* store the final result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + rv = BLI_findlink(&re->result->views, nr); + if (rv->rectf) + MEM_freeN(rv->rectf); + rv->rectf = rectf; + BLI_rw_mutex_unlock(&re->resultmutex); } /* clear interaction callbacks */ @@ -2186,12 +2303,14 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) /* disable full sample print */ R.i.curfsa = 0; - - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - if (re->result->rectf) - MEM_freeN(re->result->rectf); - re->result->rectf = rectf; - BLI_rw_mutex_unlock(&re->resultmutex); + + /* garbage collection */ + while (rectfs->first) { + RenderView *rv = rectfs->first; + BLI_remlink(rectfs, rv); + MEM_freeN(rv); + } + MEM_freeN(rectfs); } /* called externally, via compositor */ @@ -2267,7 +2386,11 @@ static void do_render_composite_fields_blur_3d(Render *re) if (composite_needs_render(re->scene, 1)) { /* save memory... free all cached images */ ntreeFreeCache(ntree); - + + /* render the frames + * it could be optimized to render only the needed view + * but what if a scene has a different number of views + * than the main scene? */ do_render_fields_blur_3d(re); } else { @@ -2280,7 +2403,7 @@ static void do_render_composite_fields_blur_3d(Render *re) if ((re->r.mode & R_CROP) == 0) { render_result_disprect_to_full_resolution(re); } - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); @@ -2325,7 +2448,10 @@ static void do_render_composite_fields_blur_3d(Render *re) if (re->r.scemode & R_FULL_SAMPLE) do_merge_fullsample(re, ntree); else { - ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings); + RenderView *rv; + for (rv = re->result->views.first; rv; rv = rv->next) { + ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name); + } } ntree->stats_draw = NULL; @@ -2350,13 +2476,17 @@ static void do_render_composite_fields_blur_3d(Render *re) static void renderresult_stampinfo(Render *re) { RenderResult rres; + RenderView *rv; + int nr; /* this is the basic trick to get the displayed float or char rect from render result */ - RE_AcquireResultImage(re, &rres); - BKE_image_stamp_buf( - re->scene, RE_GetCamera(re), - (unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4); - RE_ReleaseResultImage(re); + nr = 0; + for (rv = re->result->views.first;rv;rv = rv->next, nr++) { + RE_SetActiveRenderView(re, rv->name); + RE_AcquireResultImage(re, &rres, nr); + BKE_image_stamp_buf(re->scene, RE_GetCamera(re), (unsigned char *)rv->rect32, rv->rectf, rres.rectx, rres.recty, 4); + RE_ReleaseResultImage(re); + } } int RE_seq_render_active(Scene *scene, RenderData *rd) @@ -2380,10 +2510,12 @@ int RE_seq_render_active(Scene *scene, RenderData *rd) static void do_render_seq(Render *re) { static int recurs_depth = 0; - struct ImBuf *ibuf, *out; + struct ImBuf *out; RenderResult *rr; /* don't assign re->result here as it might change during give_ibuf_seq */ int cfra = re->r.cfra; SeqRenderData context; + size_t view_id, tot_views; + struct ImBuf **ibuf_arr; int re_x, re_y; re->i.cfra = cfra; @@ -2406,45 +2538,67 @@ static void do_render_seq(Render *re) re_y = re->result->recty; } + tot_views = BKE_scene_multiview_num_views_get(&re->r); + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * tot_views, "Sequencer Views ImBufs"); + BKE_sequencer_new_render_data( re->eval_ctx, re->main, re->scene, re_x, re_y, 100, &context); - out = BKE_sequencer_give_ibuf(&context, cfra, 0); + /* the renderresult gets destroyed during the rendering, so we first collect all ibufs + * and then we populate the final renderesult */ - if (out) { - ibuf = IMB_dupImBuf(out); - IMB_freeImBuf(out); - BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf); - } - else { - ibuf = NULL; - } + for (view_id = 0; view_id < tot_views; view_id++) { + context.view_id = view_id; + out = BKE_sequencer_give_ibuf(&context, cfra, 0); - recurs_depth--; + if (out) { + ibuf_arr[view_id] = IMB_dupImBuf(out); + IMB_freeImBuf(out); + BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf_arr[view_id]); + } + else { + ibuf_arr[view_id] = NULL; + } + } rr = re->result; - + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_views_new(rr, &re->r); + BLI_rw_mutex_unlock(&re->resultmutex); - if (ibuf) { - /* copy ibuf into combined pixel rect */ - render_result_rect_from_ibuf(rr, &re->r, ibuf); - - if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ - Editing *ed = re->scene->ed; - if (ed) - BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true); + for (view_id = 0; view_id < tot_views; view_id++) { + RenderView *rv = BLI_findlink(&rr->views, view_id); + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + + if (ibuf_arr[view_id]) { + /* copy ibuf into combined pixel rect */ + render_result_rect_from_ibuf(rr, &re->r, ibuf_arr[view_id], view_id); + + if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ + Editing *ed = re->scene->ed; + if (ed) + BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true); + } + IMB_freeImBuf(ibuf_arr[view_id]); } - IMB_freeImBuf(ibuf); - } - else { - /* render result is delivered empty in most cases, nevertheless we handle all cases */ - render_result_rect_fill_zero(rr); + else { + /* render result is delivered empty in most cases, nevertheless we handle all cases */ + render_result_rect_fill_zero(rr, view_id); + } + + BLI_rw_mutex_unlock(&re->resultmutex); + + /* would mark display buffers as invalid */ + RE_SetActiveRenderView(re, rv->name); + re->display_update(re->duh, re->result, NULL); } - BLI_rw_mutex_unlock(&re->resultmutex); + MEM_freeN(ibuf_arr); + + recurs_depth--; /* just in case this flag went missing at some point */ re->r.scemode |= R_DOSEQ; @@ -2454,9 +2608,6 @@ static void do_render_seq(Render *re) re->progress(re->prh, (float)(cfra - re->r.sfra) / (re->r.efra - re->r.sfra)); else re->progress(re->prh, 1.0f); - - /* would mark display buffers as invalid */ - re->display_update(re->duh, re->result, NULL); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -2543,6 +2694,42 @@ static bool check_valid_compositing_camera(Scene *scene, Object *camera_override } } +static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportList *reports) +{ + SceneRenderView *srv; + bool active_view = false; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return true; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + active_view = true; + + if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + Object *view_camera; + view_camera = BKE_camera_multiview_render(scene, camera, srv->name); + + if (view_camera == camera) { + /* if the suffix is not in the camera, means we are using the fallback camera */ + if (!BLI_str_endswith(view_camera->id.name + 2, srv->suffix)) { + BKE_reportf(reports, RPT_ERROR, "Camera \"%s\" is not a multi-view camera", + camera->id.name + 2); + return false; + } + } + } + } + } + + if (!active_view) { + BKE_reportf(reports, RPT_ERROR, "No active view found in scene \"%s\"", scene->id.name + 2); + return false; + } + + return true; +} + static int check_valid_camera(Scene *scene, Object *camera_override, ReportList *reports) { const char *err_msg = "No camera found in scene \"%s\""; @@ -2550,6 +2737,9 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList if (camera_override == NULL && scene->camera == NULL) scene->camera = BKE_scene_camera_find(scene); + if (!check_valid_camera_multiview(scene, scene->camera, reports)) + return false; + if (RE_seq_render_active(scene, &scene->r)) { if (scene->ed) { Sequence *seq = scene->ed->seqbase.first; @@ -2570,6 +2760,8 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList } } } + else if (!check_valid_camera_multiview(seq->scene, seq->scene_camera, reports)) + return false; } seq = seq->next; @@ -2734,6 +2926,17 @@ static void update_physics_cache(Render *re, Scene *scene, int UNUSED(anim_init) BKE_ptcache_bake(&baker); } + +void RE_SetActiveRenderView(Render *re, const char *viewname) +{ + BLI_strncpy(re->viewname, viewname, sizeof(re->viewname)); +} + +const char *RE_GetActiveRenderView(Render *re) +{ + return re->viewname; +} + /* evaluating scene options for general Blender render */ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, Scene *scene, SceneRenderLayer *srl, Object *camera_override, unsigned int lay_override, int anim, int anim_init) @@ -2769,7 +2972,8 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, re->lay = lay_override ? lay_override : scene->lay; re->layer_override = lay_override; re->i.localview = (re->lay & 0xFF000000) != 0; - + re->viewname[0] = '\0'; + /* not too nice, but it survives anim-border render */ if (anim) { render_update_anim_renderdata(re, &scene->r); @@ -2843,10 +3047,10 @@ void RE_BlenderFrame(Render *re, Main *bmain, Scene *scene, SceneRenderLayer *sr char name[FILE_MAX]; BKE_image_path_from_imformat( name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL); /* reports only used for Movie */ - do_write_image_or_movie(re, bmain, scene, NULL, name); + do_write_image_or_movie(re, bmain, scene, NULL, 0, name); } } @@ -2884,96 +3088,269 @@ void RE_RenderFreestyleExternal(Render *re) } #endif -static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override) +bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scene, struct Object *camera, const bool stamp, char *name) { - char name[FILE_MAX]; - RenderResult rres; - Object *camera = RE_GetCamera(re); - double render_time; - int ok = 1; - - RE_AcquireResultImage(re, &rres); + bool is_mono; + bool ok = true; + RenderData *rd = &scene->r; - /* write movie or image */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - bool do_free = false; - ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r); + if (!rr) + return false; - /* note; the way it gets 32 bits rects is weak... */ - if (ibuf->rect == NULL) { - ibuf->rect = MEM_mapallocN(sizeof(int) * rres.rectx * rres.recty, "temp 32 bits rect"); - ibuf->mall |= IB_rect; - RE_AcquiredResultGet32(re, &rres, ibuf->rect); - do_free = true; - } + is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2; + if (ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && + rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW) + { + RE_WriteRenderResult(reports, rr, name, &rd->im_format, true, NULL); + printf("Saved: %s\n", name); + } - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); + /* mono, legacy code */ + else if (is_mono || (rd->im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) + { + RenderView *rv; + size_t view_id; + char filepath[FILE_MAX]; - ok = mh->append_movie(&re->r, scene->r.sfra, scene->r.cfra, (int *) ibuf->rect, - ibuf->x, ibuf->y, re->reports); - if (do_free) { - MEM_freeN(ibuf->rect); - ibuf->rect = NULL; - ibuf->mall &= ~IB_rect; - } + BLI_strncpy(filepath, name, sizeof(filepath)); - /* imbuf knows which rects are not part of ibuf */ - IMB_freeImBuf(ibuf); + for (view_id = 0, rv = rr->views.first; rv; rv = rv->next, view_id++) { + BKE_scene_multiview_view_filepath_get(&scene->r, filepath, rv->name, name); - printf("Append frame %d", scene->r.cfra); - } - else { - if (name_override) - BLI_strncpy(name, name_override, sizeof(name)); - else - BKE_image_path_from_imformat( - name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); - - if (re->r.im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { - if (re->result) { - RE_WriteRenderResult(re->reports, re->result, name, scene->r.im_format.exr_codec); - printf("Saved: %s", name); + if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { + + RE_WriteRenderResult(reports, rr, name, &rd->im_format, false, rv->name); + printf("Saved: %s\n", name); } + else { + ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &rd->im_format); + + if (stamp) { + /* writes the name of the individual cameras */ + Object *view_camera = BKE_camera_multiview_render(scene, camera, rv->name); + ok = BKE_imbuf_write_stamp(scene, view_camera, ibuf, name, &rd->im_format); + } + else { + ok = BKE_imbuf_write(ibuf, name, &rd->im_format); + } + + if (ok == false) { + printf("Render error: cannot save %s\n", name); + } + else printf("Saved: %s\n", name); + + /* optional preview images for exr */ + if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { + ImageFormatData imf = rd->im_format; + imf.imtype = R_IMF_IMTYPE_JPEG90; + + if (BLI_testextensie(name, ".exr")) + name[strlen(name) - 4] = 0; + BKE_image_path_ensure_ext_from_imformat(name, &imf); + ibuf->planes = 24; + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &rd->im_format); + + if (stamp) { + /* writes the name of the individual cameras */ + Object *view_camera = BKE_camera_multiview_render(scene, camera, rv->name); + ok = BKE_imbuf_write_stamp(scene, view_camera, ibuf, name, &rd->im_format); + } + else { + ok = BKE_imbuf_write(ibuf, name, &rd->im_format); + } + printf("Saved: %s\n", name); + } + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf); + } + } + } + else { /* R_IMF_VIEWS_STEREO_3D */ + BLI_assert(scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D); + + if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { + printf("Stereo 3D not support for MultiLayer image: %s\n", name); } else { - ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r); + ImBuf *ibuf_arr[3] = {NULL}; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + int i; + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = render_result_rect_to_ibuf(rr, rd, view_id); + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); + } - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); + ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); - ok = BKE_imbuf_write_stamp(scene, camera, ibuf, name, &scene->r.im_format); - - if (ok == 0) { + if (stamp) + ok = BKE_imbuf_write_stamp(scene, camera, ibuf_arr[2], name, &rd->im_format); + else + ok = BKE_imbuf_write(ibuf_arr[2], name, &rd->im_format); + + if (ok == false) printf("Render error: cannot save %s\n", name); - } - else printf("Saved: %s", name); - + else + printf("Saved: %s\n", name); + /* optional preview images for exr */ - if (ok && scene->r.im_format.imtype == R_IMF_IMTYPE_OPENEXR && (scene->r.im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { - ImageFormatData imf = scene->r.im_format; + if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR && + (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) + { + ImageFormatData imf = rd->im_format; imf.imtype = R_IMF_IMTYPE_JPEG90; if (BLI_testextensie(name, ".exr")) name[strlen(name) - 4] = 0; + BKE_image_path_ensure_ext_from_imformat(name, &imf); - ibuf->planes = 24; + ibuf_arr[2]->planes = 24; - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + IMB_colormanagement_imbuf_for_write(ibuf_arr[2], true, false, &scene->view_settings, &scene->display_settings, &imf); - BKE_imbuf_write_stamp(scene, camera, ibuf, name, &imf); - printf("\nSaved: %s", name); + if (stamp) + ok = BKE_imbuf_write_stamp(scene, camera, ibuf_arr[2], name, &rd->im_format); + else + ok = BKE_imbuf_write(ibuf_arr[2], name, &imf); + + printf("Saved: %s\n", name); } - + + /* imbuf knows which rects are not part of ibuf */ + for (i = 0; i < 3; i++) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + } + + return ok; +} + +bool RE_WriteRenderViewsMovie(ReportList *reports, RenderResult *rr, Scene *scene, RenderData *rd, bMovieHandle *mh, + const size_t width, const size_t height, void **movie_ctx_arr, const size_t totvideos) +{ + bool is_mono; + bool ok = true; + + if (!rr) + return false; + + is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2; + + if (is_mono || (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { + size_t view_id; + for (view_id = 0; view_id < totvideos; view_id++) { + bool do_free = false; + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + ImBuf *ibuf = render_result_rect_to_ibuf(rr, &scene->r, view_id); + + /* note; the way it gets 32 bits rects is weak... */ + if (ibuf->rect == NULL) { + ibuf->rect = MEM_mapallocN(sizeof(int) * rr->rectx * rr->recty, "temp 32 bits rect"); + ibuf->mall |= IB_rect; + render_result_rect_get_pixels(rr, ibuf->rect, width, height, &scene->view_settings, &scene->display_settings, view_id); + do_free = true; + } + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + + ok &= mh->append_movie(movie_ctx_arr[view_id], rd, scene->r.sfra, scene->r.cfra, + (int *) ibuf->rect, ibuf->x, ibuf->y, suffix, reports); + + if (do_free) { + MEM_freeN(ibuf->rect); + ibuf->rect = NULL; + ibuf->mall &= ~IB_rect; + } + /* imbuf knows which rects are not part of ibuf */ IMB_freeImBuf(ibuf); } + printf("Append frame %d\n", scene->r.cfra); + } + else { /* R_IMF_VIEWS_STEREO_3D */ + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + ImBuf *ibuf_arr[3] = {NULL}; + bool do_free[2] = {false, false}; + size_t i; + + BLI_assert((totvideos == 1) && (scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D)); + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = render_result_rect_to_ibuf(rr, &scene->r, view_id); + + /* note; the way it gets 32 bits rects is weak... */ + if (ibuf_arr[i]->rect == NULL) { + ibuf_arr[i]->rect = MEM_mapallocN(sizeof(int) * width * height, "temp 32 bits rect"); + ibuf_arr[i]->mall |= IB_rect; + render_result_rect_get_pixels(rr, ibuf_arr[i]->rect, width, height, &scene->view_settings, &scene->display_settings, view_id); + do_free[i] = true; + } + + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + } + + ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); + + ok = mh->append_movie(movie_ctx_arr[0], rd, scene->r.sfra, scene->r.cfra, (int *) ibuf_arr[2]->rect, + ibuf_arr[2]->x, ibuf_arr[2]->y, "", reports); + + for (i = 0; i < 2; i++) { + if (do_free[i]) { + MEM_freeN(ibuf_arr[i]->rect); + ibuf_arr[i]->rect = NULL; + ibuf_arr[i]->mall &= ~IB_rect; + } + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf_arr[i]); + } + } + + return ok; +} + +static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const size_t totvideos, const char *name_override) +{ + char name[FILE_MAX]; + RenderResult rres; + double render_time; + bool ok = true; + Object *camera = RE_GetCamera(re); + + RE_AcquireResultImageViews(re, &rres); + + /* write movie or image */ + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + RE_WriteRenderViewsMovie(re->reports, &rres, scene, &re->r, mh, re->rectx, re->recty, re->movie_ctx_arr, totvideos); + } + else { + if (name_override) + BLI_strncpy(name, name_override, sizeof(name)); + else + BKE_image_path_from_imformat( + name, scene->r.pic, bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); + + /* write images as individual images or stereo */ + ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, camera, true, name); } - RE_ReleaseResultImage(re); + RE_ReleaseResultImageViews(re, &rres); render_time = re->i.lastframetime; re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; @@ -2992,20 +3369,51 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie return ok; } +static void get_videos_dimensions(Render *re, RenderData *rd, size_t *r_width, size_t *r_height) +{ + size_t width, height; + if (re->r.mode & R_BORDER) { + if ((re->r.mode & R_CROP) == 0) { + width = re->winx; + height = re->winy; + } + else { + width = re->rectx; + height = re->recty; + } + } + else { + width = re->rectx; + height = re->recty; + } + + BKE_scene_multiview_videos_dimensions_get(rd, width, height, r_width, r_height); +} + /* saves images to disk */ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_override, unsigned int lay_override, int sfra, int efra, int tfra) { RenderData rd = scene->r; - bMovieHandle *mh = BKE_movie_handle_get(scene->r.im_format.imtype); + bMovieHandle *mh = NULL; int cfrao = scene->r.cfra; int nfra, totrendered = 0, totskipped = 0; - + const size_t totvideos = BKE_scene_multiview_num_videos_get(&rd); + const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype); + const bool is_multiview_name = ((scene->r.scemode & R_MULTIVIEW) != 0 && + (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)); + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_INIT); /* do not fully call for each frame, it initializes & pops output window */ if (!render_initialize_from_main(re, &rd, bmain, scene, NULL, camera_override, lay_override, 0, 1)) return; + + /* we don't support Frame Server and streaming of individual views */ + if ((rd.im_format.imtype == R_IMF_IMTYPE_FRAMESERVER) && (totvideos > 1)) { + BKE_report(re->reports, RPT_ERROR, "Frame Server only support stereo output for multiview rendering"); + return; + } /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */ /* is also set by caller renderwin.c */ @@ -3013,31 +3421,33 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri re->flag |= R_ANIMATION; - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - int width, height; - if (re->r.mode & R_BORDER) { - if ((re->r.mode & R_CROP) == 0) { - width = re->winx; - height = re->winy; - } - else { - width = re->rectx; - height = re->recty; - } - } - else { - width = re->rectx; - height = re->recty; - } + if (is_movie) { + size_t i, width, height; + + get_videos_dimensions(re, &rd, &width, &height); + + mh = BKE_movie_handle_get(scene->r.im_format.imtype); + re->movie_ctx_arr = MEM_mallocN(sizeof(void *) * totvideos, "Movies' Context"); + + for (i = 0; i < totvideos; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&re->r, i); - /* last argument here depends on users really, but no users using preview have been found so far */ - if (!mh->start_movie(scene, &re->r, width, height, re->reports, false)) - G.is_break = true; + re->movie_ctx_arr[i] = mh->context_create(); + + if (!mh->start_movie(re->movie_ctx_arr[i], scene, &re->r, width, height, re->reports, false, suffix)) + G.is_break = true; + } } - if (mh->get_next_frame) { + if (mh && mh->get_next_frame) { + /* MULTIVIEW_TODO: + * in case a new video format is added that implements get_next_frame multiview has to be addressed + * or the error throwing for R_IMF_IMTYPE_FRAMESERVER has to be extended for those cases as well + */ + BLI_assert(totvideos < 2); + while (!(G.is_break == 1)) { - int nf = mh->get_next_frame(&re->r, re->reports); + int nf = mh->get_next_frame(re->movie_ctx_arr[0], &re->r, re->reports); if (nf >= 0 && nf >= scene->r.sfra && nf <= scene->r.efra) { scene->r.cfra = re->r.cfra = nf; @@ -3047,7 +3457,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri totrendered++; if (re->test_break(re->tbh) == 0) { - if (!do_write_image_or_movie(re, bmain, scene, mh, NULL)) + if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) G.is_break = true; } @@ -3090,20 +3500,67 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri nfra += tfra; /* Touch/NoOverwrite options are only valid for image's */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) { + if (is_movie == false) { if (scene->r.mode & (R_NO_OVERWRITE | R_TOUCH)) BKE_image_path_from_imformat( name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); + + if (scene->r.mode & R_NO_OVERWRITE) { + if (!is_multiview_name) { + if (BLI_exists(name)) { + printf("skipping existing frame \"%s\"\n", name); + totskipped++; + continue; + } + } + else { + SceneRenderView *srv; + bool is_skip = false; + char filepath[FILE_MAX]; - if (scene->r.mode & R_NO_OVERWRITE && BLI_exists(name)) { - printf("skipping existing frame \"%s\"\n", name); - totskipped++; - continue; + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if (BLI_exists(filepath)) { + is_skip = true; + printf("skipping existing frame \"%s\" for view \"%s\"\n", filepath, srv->name); + } + } + + if (is_skip) { + totskipped++; + continue; + } + } } - if (scene->r.mode & R_TOUCH && !BLI_exists(name)) { - BLI_make_existing_file(name); /* makes the dir if its not there */ - BLI_file_touch(name); + + if (scene->r.mode & R_TOUCH) { + if (!is_multiview_name) { + if (!BLI_exists(name)) { + BLI_make_existing_file(name); /* makes the dir if its not there */ + BLI_file_touch(name); + } + } + else { + SceneRenderView *srv; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if (!BLI_exists(filepath)) { + BLI_make_existing_file(filepath); /* makes the dir if its not there */ + BLI_file_touch(filepath); + } + } + } } } @@ -3118,7 +3575,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri if (re->test_break(re->tbh) == 0) { if (!G.is_break) - if (!do_write_image_or_movie(re, bmain, scene, mh, NULL)) + if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) G.is_break = true; } else @@ -3126,10 +3583,30 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri if (G.is_break == true) { /* remove touched file */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) { - if ((scene->r.mode & R_TOUCH) && (BLI_file_size(name) == 0)) { - /* BLI_exists(name) is implicit */ - BLI_delete(name, false, false); + if (is_movie == false) { + if ((scene->r.mode & R_TOUCH)) { + if (!is_multiview_name) { + if ((BLI_file_size(name) == 0)) { + /* BLI_exists(name) is implicit */ + BLI_delete(name, false, false); + } + } + else { + SceneRenderView *srv; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if ((BLI_file_size(filepath) == 0)) { + /* BLI_exists(filepath) is implicit */ + BLI_delete(filepath, false, false); + } + } + } } } @@ -3144,8 +3621,17 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri } /* end movie */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) - mh->end_movie(); + if (is_movie) { + size_t i; + for (i = 0; i < totvideos; i++) { + mh->end_movie(re->movie_ctx_arr[i]); + mh->context_free(re->movie_ctx_arr[i]); + } + + if (re->movie_ctx_arr) { + MEM_freeN(re->movie_ctx_arr); + } + } if (totskipped && totrendered == 0) BKE_report(re->reports, RPT_INFO, "No frames rendered, skipped to not overwrite"); @@ -3243,13 +3729,22 @@ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char { /* OCIO_TODO: assume layer was saved in defaule color space */ ImBuf *ibuf = IMB_loadiffname(filename, IB_rect, NULL); + RenderPass *rpass = NULL; + + /* multiview: since the API takes no 'view', we use the first combined pass found */ + for (rpass = layer->passes.first; rpass; rpass = rpass->next) + if (rpass->passtype == SCE_PASS_COMBINED) + break; + + if (rpass == NULL) + BKE_reportf(reports, RPT_ERROR, "%s: no Combined pass found in the render layer '%s'", __func__, filename); if (ibuf && (ibuf->rect || ibuf->rect_float)) { if (ibuf->x == layer->rectx && ibuf->y == layer->recty) { if (ibuf->rect_float == NULL) IMB_float_from_rect(ibuf); - memcpy(layer->rectf, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); + memcpy(rpass->rect, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); } else { if ((ibuf->x - x >= layer->rectx) && (ibuf->y - y >= layer->recty)) { @@ -3262,29 +3757,29 @@ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char if (ibuf_clip) { IMB_rectcpy(ibuf_clip, ibuf, 0, 0, x, y, layer->rectx, layer->recty); - memcpy(layer->rectf, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); + memcpy(rpass->rect, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); IMB_freeImBuf(ibuf_clip); } else { - BKE_reportf(reports, RPT_ERROR, "RE_result_rect_from_file: failed to allocate clip buffer '%s'", filename); + BKE_reportf(reports, RPT_ERROR, "%s: failed to allocate clip buffer '%s'", __func__, filename); } } else { - BKE_reportf(reports, RPT_ERROR, "RE_result_rect_from_file: incorrect dimensions for partial copy '%s'", filename); + BKE_reportf(reports, RPT_ERROR, "%s: incorrect dimensions for partial copy '%s'", __func__, filename); } } IMB_freeImBuf(ibuf); } else { - BKE_reportf(reports, RPT_ERROR, "RE_result_rect_from_file: failed to load '%s'", filename); + BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filename); } } void RE_result_load_from_file(RenderResult *result, ReportList *reports, const char *filename) { if (!render_result_exr_file_read_path(result, NULL, filename)) { - BKE_reportf(reports, RPT_ERROR, "RE_result_rect_from_file: failed to load '%s'", filename); + BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filename); return; } } @@ -3350,3 +3845,21 @@ bool RE_WriteEnvmapResult(struct ReportList *reports, Scene *scene, EnvMap *env, } } +/* used in the interface to decide whether to show layers */ +bool RE_layers_have_name(struct RenderResult *rr) +{ + switch (BLI_listbase_count_ex(&rr->layers, 2)) { + case 0: + return false; + break; + case 1: + return (((RenderLayer *)rr->layers.first)->name[0] != '\0'); + break; + default: + return true; + break; + } + return false; +} + + diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index 6486844bacf..90f6eab0cc1 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -48,9 +48,8 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_report.h" -#ifdef WITH_CYCLES_DEBUG -# include "BKE_scene.h" -#endif +#include "BKE_camera.h" +#include "BKE_scene.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -63,14 +62,32 @@ /********************************** Free *************************************/ +static void render_result_views_free(RenderResult *res) +{ + while (res->views.first) { + RenderView *rv = res->views.first; + BLI_remlink(&res->views, rv); + + if (rv->rect32) + MEM_freeN(rv->rect32); + + if (rv->rectz) + MEM_freeN(rv->rectz); + + if (rv->rectf) + MEM_freeN(rv->rectf); + + MEM_freeN(rv); + } +} + void render_result_free(RenderResult *res) { if (res == NULL) return; while (res->layers.first) { RenderLayer *rl = res->layers.first; - - if (rl->rectf) MEM_freeN(rl->rectf); + /* acolrect and scolrect are optionally allocated in shade_tile, only free here since it can be used for drawing */ if (rl->acolrect) MEM_freeN(rl->acolrect); if (rl->scolrect) MEM_freeN(rl->scolrect); @@ -85,7 +102,9 @@ void render_result_free(RenderResult *res) BLI_remlink(&res->layers, rl); MEM_freeN(rl); } - + + render_result_views_free(res); + if (res->rect32) MEM_freeN(res->rect32); if (res->rectz) @@ -115,13 +134,44 @@ void render_result_free_list(ListBase *lb, RenderResult *rr) } } -/********************************* Names *************************************/ +/********************************* multiview *************************************/ -/* NOTE: OpenEXR only supports 32 chars for layer+pass names - * In blender we now use max 10 chars for pass, max 20 for layer */ -static const char *get_pass_name(int passtype, int channel) +/* create a new views Listbase in rr without duplicating the memory pointers */ +void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src) +{ + RenderView *rview; + + if (dst == NULL || src == NULL) + return; + + for (rview = src->views.first; rview; rview = rview->next) { + RenderView *rv; + + rv = MEM_mallocN(sizeof(RenderView), "new render view"); + BLI_addtail(&dst->views, rv); + + BLI_strncpy(rv->name, rview->name, sizeof(rv->name)); + rv->rectf = rview->rectf; + rv->rectz = rview->rectz; + rv->rect32 = rview->rect32; + } +} + +/* free the views created temporarily */ +void render_result_views_shallowdelete(RenderResult *rr) +{ + if (rr == NULL) + return; + + while (rr->views.first) { + RenderView *rv = rr->views.first; + BLI_remlink(&rr->views, rv); + MEM_freeN(rv); + } +} + +static const char *name_from_passtype(int passtype, int channel) { - if (passtype == SCE_PASS_COMBINED) { if (channel == -1) return "Combined"; if (channel == 0) return "Combined.R"; @@ -308,108 +358,131 @@ static const char *get_pass_name(int passtype, int channel) static int passtype_from_name(const char *str) { - - if (STREQ(str, "Combined")) + if (STRPREFIX(str, "Combined")) return SCE_PASS_COMBINED; - if (STREQ(str, "Depth")) + if (STRPREFIX(str, "Depth")) return SCE_PASS_Z; - if (STREQ(str, "Vector")) + if (STRPREFIX(str, "Vector")) return SCE_PASS_VECTOR; - if (STREQ(str, "Normal")) + if (STRPREFIX(str, "Normal")) return SCE_PASS_NORMAL; - if (STREQ(str, "UV")) + if (STRPREFIX(str, "UV")) return SCE_PASS_UV; - if (STREQ(str, "Color")) + if (STRPREFIX(str, "Color")) return SCE_PASS_RGBA; - if (STREQ(str, "Emit")) + if (STRPREFIX(str, "Emit")) return SCE_PASS_EMIT; - if (STREQ(str, "Diffuse")) + if (STRPREFIX(str, "Diffuse")) return SCE_PASS_DIFFUSE; - if (STREQ(str, "Spec")) + if (STRPREFIX(str, "Spec")) return SCE_PASS_SPEC; - if (STREQ(str, "Shadow")) + if (STRPREFIX(str, "Shadow")) return SCE_PASS_SHADOW; - if (STREQ(str, "AO")) + if (STRPREFIX(str, "AO")) return SCE_PASS_AO; - if (STREQ(str, "Env")) + if (STRPREFIX(str, "Env")) return SCE_PASS_ENVIRONMENT; - if (STREQ(str, "Indirect")) + if (STRPREFIX(str, "Indirect")) return SCE_PASS_INDIRECT; - if (STREQ(str, "Reflect")) + if (STRPREFIX(str, "Reflect")) return SCE_PASS_REFLECT; - if (STREQ(str, "Refract")) + if (STRPREFIX(str, "Refract")) return SCE_PASS_REFRACT; - if (STREQ(str, "IndexOB")) + if (STRPREFIX(str, "IndexOB")) return SCE_PASS_INDEXOB; - if (STREQ(str, "IndexMA")) + if (STRPREFIX(str, "IndexMA")) return SCE_PASS_INDEXMA; - if (STREQ(str, "Mist")) + if (STRPREFIX(str, "Mist")) return SCE_PASS_MIST; - if (STREQ(str, "RayHits")) + if (STRPREFIX(str, "RayHits")) return SCE_PASS_RAYHITS; - if (STREQ(str, "DiffDir")) + if (STRPREFIX(str, "DiffDir")) return SCE_PASS_DIFFUSE_DIRECT; - if (STREQ(str, "DiffInd")) + if (STRPREFIX(str, "DiffInd")) return SCE_PASS_DIFFUSE_INDIRECT; - if (STREQ(str, "DiffCol")) + if (STRPREFIX(str, "DiffCol")) return SCE_PASS_DIFFUSE_COLOR; - if (STREQ(str, "GlossDir")) + if (STRPREFIX(str, "GlossDir")) return SCE_PASS_GLOSSY_DIRECT; - if (STREQ(str, "GlossInd")) + if (STRPREFIX(str, "GlossInd")) return SCE_PASS_GLOSSY_INDIRECT; - if (STREQ(str, "GlossCol")) + if (STRPREFIX(str, "GlossCol")) return SCE_PASS_GLOSSY_COLOR; - if (STREQ(str, "TransDir")) + if (STRPREFIX(str, "TransDir")) return SCE_PASS_TRANSM_DIRECT; - if (STREQ(str, "TransInd")) + if (STRPREFIX(str, "TransInd")) return SCE_PASS_TRANSM_INDIRECT; - if (STREQ(str, "TransCol")) + if (STRPREFIX(str, "TransCol")) return SCE_PASS_TRANSM_COLOR; - if (STREQ(str, "SubsurfaceDir")) + if (STRPREFIX(str, "SubsurfaceDir")) return SCE_PASS_SUBSURFACE_DIRECT; - if (STREQ(str, "SubsurfaceInd")) + if (STRPREFIX(str, "SubsurfaceInd")) return SCE_PASS_SUBSURFACE_INDIRECT; - if (STREQ(str, "SubsurfaceCol")) + if (STRPREFIX(str, "SubsurfaceCol")) return SCE_PASS_SUBSURFACE_COLOR; return 0; } + +static void set_pass_name(char *passname, int passtype, int channel, const char *view) +{ + const char *end; + const char *token; + int len; + + const char *passtype_name = name_from_passtype(passtype, channel); + + if (view == NULL || view[0] == '\0') { + BLI_strncpy(passname, passtype_name, EXR_PASS_MAXNAME); + return; + } + + end = passtype_name + strlen(passtype_name); + len = IMB_exr_split_token(passtype_name, end, &token); + + if (len == strlen(passtype_name)) + sprintf(passname, "%s.%s", passtype_name, view); + else + sprintf(passname, "%.*s%s.%s", (int)(end-passtype_name) - len, passtype_name, view, token); +} + /********************************** New **************************************/ -static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype) +static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype, const char *viewname) { - const char *typestr = get_pass_name(passtype, 0); + const size_t view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name)); + const char *typestr = name_from_passtype(passtype, -1); RenderPass *rpass = MEM_callocN(sizeof(RenderPass), typestr); int rectsize = rr->rectx * rr->recty * channels; @@ -418,12 +491,16 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int rpass->channels = channels; rpass->rectx = rl->rectx; rpass->recty = rl->recty; - BLI_strncpy(rpass->name, get_pass_name(rpass->passtype, -1), sizeof(rpass->name)); + rpass->view_id = view_id; + + set_pass_name(rpass->name, rpass->passtype, -1, viewname); + BLI_strncpy(rpass->internal_name, typestr, sizeof(rpass->internal_name)); + BLI_strncpy(rpass->view, viewname, sizeof(rpass->view)); if (rl->exrhandle) { int a; for (a = 0; a < channels; a++) - IMB_exr_add_channel(rl->exrhandle, rl->name, get_pass_name(passtype, a), 0, 0, NULL); + IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(passtype, a), viewname, 0, 0, NULL); } else { float *rect; @@ -460,9 +537,10 @@ static RenderPass *render_layer_add_debug_pass(RenderResult *rr, RenderLayer *rl, int channels, int pass_type, - int debug_type) + int debug_type, + const char *view) { - RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type); + RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type, view); rpass->debug_type = debug_type; BLI_strncpy(rpass->name, debug_pass_type_name_get(debug_type), @@ -475,12 +553,14 @@ static RenderPass *render_layer_add_debug_pass(RenderResult *rr, /* will read info from Render *re to define layers */ /* called in threads */ /* re->winx,winy is coordinate space of entire image, partrct the part within */ -RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername) +RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname) { RenderResult *rr; RenderLayer *rl; + RenderView *rv; SceneRenderLayer *srl; - int rectx, recty, nr; + int rectx, recty; + int nr, i; rectx = BLI_rcti_size_x(partrct); recty = BLI_rcti_size_y(partrct); @@ -505,6 +585,8 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf rr->do_exr_tile = true; } + render_result_views_new(rr, &re->r); + /* check renderdata for amount of layers */ for (nr = 0, srl = re->r.layers.first; srl; srl = srl->next, nr++) { @@ -538,84 +620,90 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf if (rr->do_exr_tile) { rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba"); - rl->exrhandle = IMB_exr_get_handle(); - - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.R", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.G", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.B", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.A", 0, 0, NULL); } - else - rl->rectf = MEM_mapallocN(rectx * recty * sizeof(float) * 4, "Combined rgba"); - - if (srl->passflag & SCE_PASS_Z) - render_layer_add_pass(rr, rl, 1, SCE_PASS_Z); - if (srl->passflag & SCE_PASS_VECTOR) - render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR); - if (srl->passflag & SCE_PASS_NORMAL) - render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL); - if (srl->passflag & SCE_PASS_UV) - render_layer_add_pass(rr, rl, 3, SCE_PASS_UV); - if (srl->passflag & SCE_PASS_RGBA) - render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA); - if (srl->passflag & SCE_PASS_EMIT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_EMIT); - if (srl->passflag & SCE_PASS_DIFFUSE) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE); - if (srl->passflag & SCE_PASS_SPEC) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC); - if (srl->passflag & SCE_PASS_AO) - render_layer_add_pass(rr, rl, 3, SCE_PASS_AO); - if (srl->passflag & SCE_PASS_ENVIRONMENT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_ENVIRONMENT); - if (srl->passflag & SCE_PASS_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_INDIRECT); - if (srl->passflag & SCE_PASS_SHADOW) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW); - if (srl->passflag & SCE_PASS_REFLECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT); - if (srl->passflag & SCE_PASS_REFRACT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT); - if (srl->passflag & SCE_PASS_INDEXOB) - render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB); - if (srl->passflag & SCE_PASS_INDEXMA) - render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXMA); - if (srl->passflag & SCE_PASS_MIST) - render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST); - if (rl->passflag & SCE_PASS_RAYHITS) - render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS); - if (srl->passflag & SCE_PASS_DIFFUSE_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_DIRECT); - if (srl->passflag & SCE_PASS_DIFFUSE_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_INDIRECT); - if (srl->passflag & SCE_PASS_DIFFUSE_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_COLOR); - if (srl->passflag & SCE_PASS_GLOSSY_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_DIRECT); - if (srl->passflag & SCE_PASS_GLOSSY_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_INDIRECT); - if (srl->passflag & SCE_PASS_GLOSSY_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_COLOR); - if (srl->passflag & SCE_PASS_TRANSM_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_DIRECT); - if (srl->passflag & SCE_PASS_TRANSM_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_INDIRECT); - if (srl->passflag & SCE_PASS_TRANSM_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_COLOR); - if (srl->passflag & SCE_PASS_SUBSURFACE_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_DIRECT); - if (srl->passflag & SCE_PASS_SUBSURFACE_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT); - if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR); + + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; + + if (viewname && viewname[0]) + if (!STREQ(view, viewname)) + continue; + + if (rr->do_exr_tile) + IMB_exr_add_view(rl->exrhandle, view); + + /* a renderlayer should always have a Combined pass*/ + render_layer_add_pass(rr, rl, 4, SCE_PASS_COMBINED, view); + + if (srl->passflag & SCE_PASS_Z) + render_layer_add_pass(rr, rl, 1, SCE_PASS_Z, view); + if (srl->passflag & SCE_PASS_VECTOR) + render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR, view); + if (srl->passflag & SCE_PASS_NORMAL) + render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL, view); + if (srl->passflag & SCE_PASS_UV) + render_layer_add_pass(rr, rl, 3, SCE_PASS_UV, view); + if (srl->passflag & SCE_PASS_RGBA) + render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA, view); + if (srl->passflag & SCE_PASS_EMIT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_EMIT, view); + if (srl->passflag & SCE_PASS_DIFFUSE) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE, view); + if (srl->passflag & SCE_PASS_SPEC) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC, view); + if (srl->passflag & SCE_PASS_AO) + render_layer_add_pass(rr, rl, 3, SCE_PASS_AO, view); + if (srl->passflag & SCE_PASS_ENVIRONMENT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_ENVIRONMENT, view); + if (srl->passflag & SCE_PASS_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_INDIRECT, view); + if (srl->passflag & SCE_PASS_SHADOW) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW, view); + if (srl->passflag & SCE_PASS_REFLECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT, view); + if (srl->passflag & SCE_PASS_REFRACT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT, view); + if (srl->passflag & SCE_PASS_INDEXOB) + render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB, view); + if (srl->passflag & SCE_PASS_INDEXMA) + render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXMA, view); + if (srl->passflag & SCE_PASS_MIST) + render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST, view); + if (rl->passflag & SCE_PASS_RAYHITS) + render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS, view); + if (srl->passflag & SCE_PASS_DIFFUSE_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_DIRECT, view); + if (srl->passflag & SCE_PASS_DIFFUSE_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_INDIRECT, view); + if (srl->passflag & SCE_PASS_DIFFUSE_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_COLOR, view); + if (srl->passflag & SCE_PASS_GLOSSY_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_DIRECT, view); + if (srl->passflag & SCE_PASS_GLOSSY_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_INDIRECT, view); + if (srl->passflag & SCE_PASS_GLOSSY_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_COLOR, view); + if (srl->passflag & SCE_PASS_TRANSM_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_DIRECT, view); + if (srl->passflag & SCE_PASS_TRANSM_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_INDIRECT, view); + if (srl->passflag & SCE_PASS_TRANSM_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_COLOR, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_DIRECT, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR, view); #ifdef WITH_CYCLES_DEBUG - if (BKE_scene_use_new_shading_nodes(re->scene)) { - render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG, - RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS); - } + if (BKE_scene_use_new_shading_nodes(re->scene)) { + render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG, + RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS, view); + } #endif + } } /* sss, previewrender and envmap don't do layers, so we make a default one */ if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) { @@ -628,18 +716,27 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf /* duplicate code... */ if (rr->do_exr_tile) { rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba"); - rl->exrhandle = IMB_exr_get_handle(); - - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.R", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.G", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.B", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.A", 0, 0, NULL); } - else { - rl->rectf = MEM_mapallocN(rectx * recty * sizeof(float) * 4, "Combined rgba"); + + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; + + if (viewname && viewname[0]) + if (strcmp(view, viewname) != 0) + continue; + + if (rr->do_exr_tile) { + IMB_exr_add_view(rl->exrhandle, view); + + for (i=0; i < 4; i++) + IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(SCE_PASS_COMBINED, i), view, 0, 0, NULL); + } + else { + render_layer_add_pass(rr, rl, 4, SCE_PASS_COMBINED, view); + } } - + /* note, this has to be in sync with scene.c */ rl->lay = (1 << 20) - 1; rl->layflag = 0x7FFF; /* solid ztra halo strand */ @@ -657,15 +754,15 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf } /* allocate osa new results for samples */ -RenderResult *render_result_new_full_sample(Render *re, ListBase *lb, rcti *partrct, int crop, int savebuffers) +RenderResult *render_result_new_full_sample(Render *re, ListBase *lb, rcti *partrct, int crop, int savebuffers, const char *viewname) { int a; if (re->osa == 0) - return render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS); + return render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS, viewname); for (a = 0; a < re->osa; a++) { - RenderResult *rr = render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS); + RenderResult *rr = render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS, viewname); BLI_addtail(lb, rr); rr->sample_nr = a; } @@ -686,28 +783,98 @@ static void *ml_addlayer_cb(void *base, const char *str) return rl; } -static void ml_addpass_cb(void *UNUSED(base), void *lay, const char *str, float *rect, int totchan, const char *chan_id) +static void ml_addpass_cb(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id, const char *view) { + RenderResult *rr = base; RenderLayer *rl = lay; RenderPass *rpass = MEM_callocN(sizeof(RenderPass), "loaded pass"); int a; BLI_addtail(&rl->passes, rpass); rpass->channels = totchan; - rpass->passtype = passtype_from_name(str); if (rpass->passtype == 0) printf("unknown pass %s\n", str); rl->passflag |= rpass->passtype; - BLI_strncpy(rpass->name, str, EXR_PASS_MAXNAME); /* channel id chars */ for (a = 0; a < totchan; a++) rpass->chan_id[a] = chan_id[a]; - + rpass->rect = rect; + if (view[0] != '\0') { + BLI_snprintf(rpass->name, sizeof(rpass->name), "%s.%s", str, view); + rpass->view_id = BLI_findstringindex(&rr->views, view, offsetof(RenderView, name)); + } + else { + BLI_strncpy(rpass->name, str, sizeof(rpass->name)); + rpass->view_id = 0; + } + + BLI_strncpy(rpass->view, view, sizeof(rpass->view)); + BLI_strncpy(rpass->internal_name, str, sizeof(rpass->internal_name)); } -/* from imbuf, if a handle was returned we convert this to render result */ +static void *ml_addview_cb(void *base, const char *str) +{ + RenderResult *rr = base; + RenderView *rv; + + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_strncpy(rv->name, str, EXR_VIEW_MAXNAME); + + /* For stereo drawing we need to ensure: + * STEREO_LEFT_NAME == STEREO_LEFT_ID and + * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */ + + if (STREQ(str, STEREO_LEFT_NAME)) { + BLI_addhead(&rr->views, rv); + } + else if (STREQ(str, STEREO_RIGHT_NAME)) { + RenderView *left_rv = BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)); + + if (left_rv == NULL) { + BLI_addhead(&rr->views, rv); + } + else { + BLI_insertlinkafter(&rr->views, left_rv, rv); + } + } + else { + BLI_addtail(&rr->views, rv); + } + + return rv; +} + +static int order_render_passes(const void *a, const void *b) +{ + // 1 if a is after b + RenderPass *rpa = (RenderPass *) a; + RenderPass *rpb = (RenderPass *) b; + + if (rpa->passtype > rpb->passtype) + return 1; + else if (rpa->passtype < rpb->passtype) + return 0; + + /* they have the same type */ + /* left first */ + if (STREQ(rpa->view, STEREO_LEFT_NAME)) + return 0; + else if (STREQ(rpb->view, STEREO_LEFT_NAME)) + return 1; + + /* right second */ + if (STREQ(rpa->view, STEREO_RIGHT_NAME)) + return 0; + else if (STREQ(rpb->view, STEREO_RIGHT_NAME)) + return 1; + + /* remaining in ascending id order */ + return (rpa->view_id < rpb->view_id); +} + +/* from imbuf, if a handle was returned and it's not a singlelayer multiview we convert this to render result */ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty) { RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__); @@ -718,13 +885,17 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace rr->rectx = rectx; rr->recty = recty; - IMB_exr_multilayer_convert(exrhandle, rr, ml_addlayer_cb, ml_addpass_cb); + IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb); for (rl = rr->layers.first; rl; rl = rl->next) { + int c=0; rl->rectx = rectx; rl->recty = recty; + BLI_listbase_sort(&rl->passes, order_render_passes); + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + printf("%d: %s\n", c++, rpass->name); rpass->rectx = rectx; rpass->recty = recty; @@ -738,6 +909,33 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace return rr; } +void render_result_views_new(RenderResult *rr, RenderData *rd) +{ + SceneRenderView *srv; + RenderView *rv; + + /* clear previously existing views - for sequencer */ + render_result_views_free(rr); + + /* check renderdata for amount of views */ + if ((rd->scemode & R_MULTIVIEW)) { + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) continue; + + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_addtail(&rr->views, rv); + + BLI_strncpy(rv->name, srv->name, sizeof(rv->name)); + } + } + + /* we always need at least one view */ + if (BLI_listbase_count_ex(&rr->views, 1) == 0) { + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_addtail(&rr->views, rv); + } +} + /*********************************** Merge ***********************************/ static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize) @@ -783,16 +981,19 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart) for (rl = rr->layers.first; rl; rl = rl->next) { rlp = RE_GetRenderLayer(rrpart, rl->name); if (rlp) { - /* combined */ - if (rl->rectf && rlp->rectf) - do_merge_tile(rr, rrpart, rl->rectf, rlp->rectf, 4); - /* passes are allocated in sync */ for (rpass = rl->passes.first, rpassp = rlp->passes.first; rpass && rpassp; - rpass = rpass->next, rpassp = rpassp->next) + rpass = rpass->next) { + /* renderresult have all passes, renderpart only the active view's passes */ + if (strcmp(rpassp->name, rpass->name) != 0) + continue; + do_merge_tile(rr, rrpart, rpass->rect, rpassp->rect, rpass->channels); + + /* manually get next render pass */ + rpassp = rpassp->next; } } } @@ -801,7 +1002,7 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart) /* for passes read from files, these have names stored */ static char *make_pass_name(RenderPass *rpass, int chan) { - static char name[16]; + static char name[EXR_PASS_MAXNAME]; int len; BLI_strncpy(name, rpass->name, EXR_PASS_MAXNAME); @@ -813,65 +1014,111 @@ static char *make_pass_name(RenderPass *rpass, int chan) return name; } -/* filename already made absolute */ -/* called from within UI, saves both rendered result as a file-read result */ -bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, int compress) +/* called from within UI and render pipeline, saves both rendered result as a file-read result + * if multiview is true saves all views in a multiview exr + * else if view is not NULL saves single view + * else saves stereo3d + */ +bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, ImageFormatData *imf, const bool multiview, const char *view) { RenderLayer *rl; RenderPass *rpass; + RenderView *rview; void *exrhandle = IMB_exr_get_handle(); - bool success; - - BLI_make_existing_file(filename); - - /* composite result */ - if (rr->rectf) { - IMB_exr_add_channel(exrhandle, "Composite", "Combined.R", 4, 4 * rr->rectx, rr->rectf); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.G", 4, 4 * rr->rectx, rr->rectf + 1); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.B", 4, 4 * rr->rectx, rr->rectf + 2); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.A", 4, 4 * rr->rectx, rr->rectf + 3); + bool success = false; + int a, nr; + const char *chan_view = NULL; + int compress = (imf ? imf->exr_codec : 0); + size_t width, height; + + const bool is_mono = view && !multiview; + + width = rr->rectx; + height = rr->recty; + + if (imf && imf->imtype == R_IMF_IMTYPE_OPENEXR && multiview) { + /* single layer OpenEXR */ + const char *RGBAZ[] = {"R", "G", "B", "A", "Z"}; + for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) { + IMB_exr_add_view(exrhandle, rview->name); + + if (rview->rectf) { + for (a = 0; a < 4; a++) + IMB_exr_add_channel(exrhandle, "", RGBAZ[a], + rview->name, 4, 4 * width, rview->rectf + a); + if (rview->rectz) + IMB_exr_add_channel(exrhandle, "", RGBAZ[4], + rview->name, 1, width, rview->rectz); + } + } } - - /* add layers/passes and assign channels */ - for (rl = rr->layers.first; rl; rl = rl->next) { - - /* combined */ - if (rl->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) { - IMB_exr_add_channel(exrhandle, rl->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rr->rectx, rl->rectf + a); + else { + for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) { + if (is_mono) { + if (!STREQ(view, rview->name)) { + continue; + } + chan_view = ""; + } + else { + /* if rendered only one view, we treat as a a non-view render */ + chan_view = rview->name; + } + + IMB_exr_add_view(exrhandle, rview->name); + + if (rview->rectf) { + for (a = 0; a < 4; a++) + IMB_exr_add_channel(exrhandle, "Composite", name_from_passtype(SCE_PASS_COMBINED, a), + chan_view, 4, 4 * width, rview->rectf + a); } } - - /* passes are allocated in sync */ - for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - int a, xstride = rpass->channels; - for (a = 0; a < xstride; a++) { - if (rpass->passtype) { - IMB_exr_add_channel(exrhandle, rl->name, get_pass_name(rpass->passtype, a), - xstride, xstride * rr->rectx, rpass->rect + a); + + /* add layers/passes and assign channels */ + for (rl = rr->layers.first; rl; rl = rl->next) { + + /* passes are allocated in sync */ + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + const int xstride = rpass->channels; + + if (is_mono) { + if (!STREQ(view, rpass->view)) { + continue; + } + chan_view = ""; } else { - IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a), - xstride, xstride * rr->rectx, rpass->rect + a); + /* if rendered only one view, we treat as a a non-view render */ + chan_view = (nr > 1 ? rpass->view :""); + } + + for (a = 0; a < xstride; a++) { + + if (rpass->passtype) { + IMB_exr_add_channel(exrhandle, rl->name, name_from_passtype(rpass->passtype, a), chan_view, + xstride, xstride * width, rpass->rect + a); + } + else { + IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a), chan_view, + xstride, xstride * width, rpass->rect + a); + } } } } } - /* when the filename has no permissions, this can fail */ - if (IMB_exr_begin_write(exrhandle, filename, rr->rectx, rr->recty, compress)) { + BLI_make_existing_file(filename); + + if (IMB_exr_begin_write(exrhandle, filename, width, height, compress)) { IMB_exr_write_channels(exrhandle); - success = true; } else { /* TODO, get the error from openexr's exception */ BKE_report(reports, RPT_ERROR, "Error writing render result (see console)"); success = false; } - IMB_exr_close(exrhandle); + IMB_exr_close(exrhandle); return success; } @@ -932,7 +1179,7 @@ void render_result_single_layer_end(Render *re) /************************* EXR Tile File Rendering ***************************/ -static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) +static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, const char *viewname) { RenderLayer *rlp, *rl; RenderPass *rpassp; @@ -955,21 +1202,17 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) else { offs = 0; } - - /* combined */ - if (rlp->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) { - IMB_exr_set_channel(rl->exrhandle, rlp->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rrpart->rectx, rlp->rectf + a + xstride * offs); - } - } - + /* passes are allocated in sync */ for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { - int a, xstride = rpassp->channels; + const int xstride = rpassp->channels; + int a; + char passname[EXR_PASS_MAXNAME]; + for (a = 0; a < xstride; a++) { - IMB_exr_set_channel(rl->exrhandle, rlp->name, get_pass_name(rpassp->passtype, a), + set_pass_name(passname, rpassp->passtype, a, rpassp->view); + + IMB_exr_set_channel(rl->exrhandle, rlp->name, passname, xstride, xstride * rrpart->rectx, rpassp->rect + a + xstride * offs); } } @@ -988,7 +1231,7 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) continue; } - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0); + IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, viewname); } BLI_unlock_thread(LOCK_IMAGE); @@ -1002,13 +1245,13 @@ static void save_empty_result_tiles(Render *re) for (rr = re->result; rr; rr = rr->next) { for (rl = rr->layers.first; rl; rl = rl->next) { - IMB_exrtile_clear_channels(rl->exrhandle); + IMB_exr_clear_channels(rl->exrhandle); for (pa = re->parts.first; pa; pa = pa->next) { if (pa->status != PART_STATUS_READY) { int party = pa->disprect.ymin - re->disprect.ymin + pa->crop; int partx = pa->disprect.xmin - re->disprect.xmin + pa->crop; - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0); + IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, re->viewname); } } } @@ -1055,10 +1298,10 @@ void render_result_exr_file_end(Render *re) } /* save part into exr file */ -void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart) +void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart, const char *viewname) { for (; rr && rrpart; rr = rr->next, rrpart = rrpart->next) - save_render_result_tile(rr, rrpart); + save_render_result_tile(rr, rrpart, viewname); } /* path to temporary exr file */ @@ -1088,7 +1331,7 @@ int render_result_exr_file_read_sample(Render *re, int sample) bool success = true; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); for (rl = re->result->layers.first; rl; rl = rl->next) { render_result_exr_file_path(re->scene, rl->name, sample, str); @@ -1129,23 +1372,20 @@ int render_result_exr_file_read_path(RenderResult *rr, RenderLayer *rl_single, c for (rl = rr->layers.first; rl; rl = rl->next) { if (rl_single && rl_single != rl) continue; - - /* combined */ - if (rl->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) - IMB_exr_set_channel(exrhandle, rl->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rectx, rl->rectf + a); - } /* passes are allocated in sync */ for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - int a, xstride = rpass->channels; - for (a = 0; a < xstride; a++) - IMB_exr_set_channel(exrhandle, rl->name, get_pass_name(rpass->passtype, a), + const int xstride = rpass->channels; + int a; + char passname[EXR_PASS_MAXNAME]; + + for (a = 0; a < xstride; a++) { + set_pass_name(passname, rpass->passtype, a, rpass->view); + IMB_exr_set_channel(exrhandle, rl->name, passname, xstride, xstride * rectx, rpass->rect + a); + } - BLI_strncpy(rpass->name, get_pass_name(rpass->passtype, -1), sizeof(rpass->name)); + set_pass_name(rpass->name, rpass->passtype, -1, rpass->view); } } @@ -1191,7 +1431,8 @@ void render_result_exr_file_cache_write(Render *re) render_result_exr_file_cache_path(re->scene, root, str); printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str); - RE_WriteRenderResult(NULL, rr, str, 0); + + RE_WriteRenderResult(NULL, rr, str, NULL, true, NULL); } /* For cache, makes exact copy of render result */ @@ -1201,7 +1442,7 @@ bool render_result_exr_file_cache_read(Render *re) char *root = U.render_cachedir; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); /* First try cache. */ render_result_exr_file_cache_path(re->scene, root, str); @@ -1216,14 +1457,14 @@ bool render_result_exr_file_cache_read(Render *re) /*************************** Combined Pixel Rect *****************************/ -ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd) +ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd, const int view_id) { ImBuf *ibuf = IMB_allocImBuf(rr->rectx, rr->recty, rd->im_format.planes, 0); /* if not exists, BKE_imbuf_write makes one */ - ibuf->rect = (unsigned int *)rr->rect32; - ibuf->rect_float = rr->rectf; - ibuf->zbuf_float = rr->rectz; + ibuf->rect = (unsigned int *) RE_RenderViewGetRect32(rr, view_id); + ibuf->rect_float = RE_RenderViewGetRectf(rr, view_id); + ibuf->zbuf_float = RE_RenderViewGetRectz(rr, view_id); /* float factor for random dither, imbuf takes care of it */ ibuf->dither = rd->dither_intensity; @@ -1259,53 +1500,73 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd) return ibuf; } -void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf) +void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf, const int view_id) { + RenderView *rv = BLI_findlink(&rr->views, view_id); + if (ibuf->rect_float) { - if (!rr->rectf) - rr->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf"); + if (!rv->rectf) + rv->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf"); - memcpy(rr->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty); + memcpy(rv->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty); /* TSK! Since sequence render doesn't free the *rr render result, the old rect32 * can hang around when sequence render has rendered a 32 bits one before */ - if (rr->rect32) { - MEM_freeN(rr->rect32); - rr->rect32 = NULL; + if (rv->rect32) { + MEM_freeN(rv->rect32); + rv->rect32 = NULL; } } else if (ibuf->rect) { - if (!rr->rect32) - rr->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); + if (!rv->rect32) + rv->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); - memcpy(rr->rect32, ibuf->rect, 4 * rr->rectx * rr->recty); + memcpy(rv->rect32, ibuf->rect, 4 * rr->rectx * rr->recty); /* Same things as above, old rectf can hang around from previous render. */ + if (rv->rectf) { + MEM_freeN(rv->rectf); + rv->rectf = NULL; + } + } + + /* clean up non-view buffers */ + if (rr) { if (rr->rectf) { MEM_freeN(rr->rectf); rr->rectf = NULL; } + + if (rr->rect32) { + MEM_freeN(rr->rect32); + rr->rect32 = NULL; + } } } -void render_result_rect_fill_zero(RenderResult *rr) +void render_result_rect_fill_zero(RenderResult *rr, const int view_id) { - if (rr->rectf) - memset(rr->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty); - else if (rr->rect32) - memset(rr->rect32, 0, 4 * rr->rectx * rr->recty); + RenderView *rv = BLI_findlink(&rr->views, view_id); + + if (rv->rectf) + memset(rv->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty); + else if (rv->rect32) + memset(rv->rect32, 0, 4 * rr->rectx * rr->recty); else - rr->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); + rv->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); } void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rectx, int recty, - const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) + const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, + const int view_id) { if (rr->rect32) { - memcpy(rect, rr->rect32, sizeof(int) * rr->rectx * rr->recty); + int *rect32 = RE_RenderViewGetRect32(rr, view_id); + memcpy(rect, (rect32 ? rect32 : rr->rect32), sizeof(int) * rr->rectx * rr->recty); } else if (rr->rectf) { - IMB_display_buffer_transform_apply((unsigned char *) rect, rr->rectf, rr->rectx, rr->recty, 4, + float *rectf = RE_RenderViewGetRectf(rr, view_id); + IMB_display_buffer_transform_apply((unsigned char *) rect, (rectf ? rectf : rr->rectf), rr->rectx, rr->recty, 4, view_settings, display_settings, true); } else @@ -1313,3 +1574,75 @@ void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rec memset(rect, 0, sizeof(int) * rectx * recty); } + +/*************************** multiview functions *****************************/ + +bool RE_HasFakeLayer(RenderResult *res) +{ + RenderView *rv; + + if (res == NULL) + return false; + + rv = res->views.first; + if (rv == NULL) + return false; + + return (rv->rect32 || rv->rectf); +} + +bool RE_RenderResult_is_stereo(RenderResult *res) +{ + if (! BLI_findstring(&res->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) + return false; + + if (! BLI_findstring(&res->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) + return false; + + return true; +} + +void RE_RenderViewSetRectf(RenderResult *res, const int view_id, float *rect) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + rv->rectf = rect; + } +} + +void RE_RenderViewSetRectz(RenderResult *res, const int view_id, float *rect) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + rv->rectz = rect; + } +} + +float *RE_RenderViewGetRectz(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rectz; + } + return res->rectz; +} + +float *RE_RenderViewGetRectf(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rectf; + } + return res->rectf; +} + +int *RE_RenderViewGetRect32(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rect32; + } + return res->rect32; +} + + diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index dc8c714bea5..c6a80c822a3 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -1348,7 +1348,7 @@ int multitex_ext_safe(Tex *tex, float texvec[3], TexResult *texres, struct Image /* fact = texture strength, facg = button strength value */ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype) { - float facm, col; + float facm; switch (blendtype) { case MTEX_BLEND: @@ -1435,13 +1435,10 @@ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], floa case MTEX_LIGHT: fact*= facg; - - col= fact*tex[0]; - if (col > out[0]) in[0]= col; else in[0]= out[0]; - col= fact*tex[1]; - if (col > out[1]) in[1]= col; else in[1]= out[1]; - col= fact*tex[2]; - if (col > out[2]) in[2]= col; else in[2]= out[2]; + + in[0] = max_ff(fact * tex[0], out[0]); + in[1] = max_ff(fact * tex[1], out[1]); + in[2] = max_ff(fact * tex[2], out[2]); break; case MTEX_BLEND_HUE: diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 5b054005bac..99611a9c5a8 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -181,8 +181,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in if (fullsample) { for (sample=0; sample<totsample; sample++) - if (ps->mask & (1 << sample)) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + if (ps->mask & (1 << sample)) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } else { fac= ((float)amountm)/(float)R.osa; @@ -214,8 +216,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in if (fullsample) { for (sample=0; sample<totsample; sample++) - if (!(mask & (1 << sample))) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + if (!(mask & (1 << sample))) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } else { col[0]= accol[0]; @@ -223,8 +227,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in col[2]= accol[2]; col[3]= accol[3]; - for (sample=0; sample<totsample; sample++) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + for (sample=0; sample<totsample; sample++) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } } @@ -305,8 +311,10 @@ static void halo_tile(RenderPart *pa, RenderLayer *rl) zz= calchalo_z(har, *rz); if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) { - for (sample=0; sample<totsample; sample++) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + for (sample=0; sample<totsample; sample++) { + float * rect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(rect + od*4, col, har->add); + } } } } @@ -359,7 +367,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) if (fullsample) { for (sample=0; sample<totsample; sample++) { if (ps->mask & (1 << sample)) { - pass= rlpp[sample]->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -370,7 +379,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) } else { fac= ((float)count)/(float)R.osa; - pass= rl->rectf + od*4; + pass = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= fac*col[0]; pass[1]+= fac*col[1]; pass[2]+= fac*col[2]; @@ -390,7 +400,9 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) if (fullsample) { for (sample=0; sample<totsample; sample++) { if (!(mask & (1 << sample))) { - pass= rlpp[sample]->rectf + od*4; + + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -401,7 +413,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) } else { fac= ((float)R.osa-totsamp)/(float)R.osa; - pass= rl->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= fac*col[0]; pass[1]+= fac*col[1]; pass[2]+= fac*col[2]; @@ -420,7 +433,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) renderspothalo(&shi, col, 1.0f); for (sample=0; sample<totsample; sample++) { - pass= rlpp[sample]->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -444,14 +458,14 @@ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, { RenderPass *rpass; - /* combined rgb */ - add_filt_fmask(curmask, shr->combined, rl->rectf + 4*offset, rectx); - for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *fp, *col= NULL; int pixsize= 3; switch (rpass->passtype) { + case SCE_PASS_COMBINED: + add_filt_fmask(curmask, shr->combined, rpass->rect + 4*offset, rectx); + break; case SCE_PASS_Z: fp= rpass->rect + offset; *fp= shr->z; @@ -555,15 +569,16 @@ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult { RenderPass *rpass; float *fp; - - fp= rl->rectf + 4*offset; - copy_v4_v4(fp, shr->combined); - + for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *col= NULL, uvcol[3]; int a, pixsize= 3; switch (rpass->passtype) { + case SCE_PASS_COMBINED: + /* copy combined to use for preview */ + copy_v4_v4(rpass->rect + 4*offset, shr->combined); + break; case SCE_PASS_Z: fp= rpass->rect + offset; *fp= shr->z; @@ -681,7 +696,8 @@ static void sky_tile(RenderPart *pa, RenderLayer *rl) bool done = false; for (sample= 0; sample<totsample; sample++) { - float *pass= rlpp[sample]->rectf + od; + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od; if (pass[3]<1.0f) { @@ -742,8 +758,8 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl) int sample; for (sample=0; sample<totsample; sample++) { - const float *zrect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z) + od; - float *rgbrect = rlpp[sample]->rectf + 4*od; + const float *zrect = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z, R.viewname) + od; + float *rgbrect = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname) + 4*od; float rgb[3] = {0}; bool done = false; @@ -978,8 +994,8 @@ static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl) return; for (sample= 0; sample<totsample; sample++) { - float *rectf= rlpp[sample]->rectf; - + float *rectf = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) { rectf[0] = MAX2(rectf[0], 0.0f); rectf[1] = MAX2(rectf[1], 0.0f); @@ -1060,7 +1076,7 @@ static void reset_sky_speed(RenderPart *pa, RenderLayer *rl) totsample= get_sample_layers(pa, rl, rlpp); for (sample= 0; sample<totsample; sample++) { - fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR); + fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR, R.viewname); if (fp==NULL) break; for (a= 4*pa->rectx*pa->recty - 1; a>=0; a--) @@ -1171,6 +1187,8 @@ void zbufshadeDA_tile(RenderPart *pa) pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); for (rl= rr->layers.first; rl; rl= rl->next) { + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); @@ -1211,7 +1229,7 @@ void zbufshadeDA_tile(RenderPart *pa) if (R.flag & R_ZTRA || R.totstrand) { if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) { if (pa->fullresult.first) { - zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist); + zbuffer_transp_shade(pa, rl, rect, &psmlist); } else { unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */ @@ -1220,9 +1238,9 @@ void zbufshadeDA_tile(RenderPart *pa) rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); /* swap for live updates, and it is used in zbuf.c!!! */ - SWAP(float *, rl->acolrect, rl->rectf); - ztramask= zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist); - SWAP(float *, rl->acolrect, rl->rectf); + SWAP(float *, rl->acolrect, rect); + ztramask = zbuffer_transp_shade(pa, rl, rect, &psmlist); + SWAP(float *, rl->acolrect, rect); /* zbuffer transp only returns ztramask if there's solid rendered */ if (ztramask) @@ -1231,7 +1249,8 @@ void zbufshadeDA_tile(RenderPart *pa) if (ztramask && solidmask) { unsigned short *sps= solidmask, *spz= ztramask; unsigned short fullmask= (1<<R.osa)-1; - float *fcol= rl->rectf; float *acol= rl->acolrect; + float *fcol= rect; + float *acol= rl->acolrect; int x; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) { @@ -1242,7 +1261,8 @@ void zbufshadeDA_tile(RenderPart *pa) } } else { - float *fcol= rl->rectf; float *acol= rl->acolrect; + float *fcol= rect; + float *acol= rl->acolrect; int x; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { addAlphaOverFloat(fcol, acol); @@ -1265,7 +1285,7 @@ void zbufshadeDA_tile(RenderPart *pa) /* extra layers */ if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) - edge_enhance_add(pa, rl->rectf, edgerect); + edge_enhance_add(pa, rect, edgerect); if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); @@ -1319,6 +1339,7 @@ void zbufshade_tile(RenderPart *pa) pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); for (rl= rr->layers.first; rl; rl= rl->next) { + float *rect= RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); @@ -1342,7 +1363,7 @@ void zbufshade_tile(RenderPart *pa) rr->renlay= rl; if (rl->layflag & SCE_LAY_SOLID) { - const float *fcol= rl->rectf; + const float *fcol = rect; const int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz; int x, y, offs=0, seed; @@ -1405,11 +1426,11 @@ void zbufshade_tile(RenderPart *pa) rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); /* swap for live updates */ - SWAP(float *, rl->acolrect, rl->rectf); - zbuffer_transp_shade(pa, rl, rl->rectf, NULL); - SWAP(float *, rl->acolrect, rl->rectf); + SWAP(float *, rl->acolrect, rect); + zbuffer_transp_shade(pa, rl, rect, NULL); + SWAP(float *, rl->acolrect, rect); - fcol= rl->rectf; acol= rl->acolrect; + fcol= rect; acol= rl->acolrect; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { addAlphaOverFloat(fcol, acol); } @@ -1427,7 +1448,7 @@ void zbufshade_tile(RenderPart *pa) if (!R.test_break(R.tbh)) { if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) - edge_enhance_add(pa, rl->rectf, edgerect); + edge_enhance_add(pa, rect, edgerect); } if (rl->passflag & SCE_PASS_VECTOR) @@ -1654,7 +1675,7 @@ void zbufshade_sss_tile(RenderPart *pa) return; } - fcol= rl->rectf; + fcol= RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo"); color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor"); @@ -1938,6 +1959,7 @@ void add_halo_flare(Render *re) RenderLayer *rl; HaloRen *har; int a, mode; + float *rect; /* for now, we get the first renderlayer in list with halos set */ for (rl= rr->layers.first; rl; rl= rl->next) { @@ -1945,8 +1967,11 @@ void add_halo_flare(Render *re) if ((rl->layflag & SCE_LAY_HALO) == 0) continue; - if (rl->rectf==NULL) - continue; + + rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); + + if (rl==NULL || rect) + return; mode= R.r.mode; R.r.mode &= ~R_PANORAMA; @@ -1958,7 +1983,7 @@ void add_halo_flare(Render *re) if (har->flarec && (har->lay & rl->lay)) { do_draw = true; - renderflare(rr, rl->rectf, har); + renderflare(rr, rect, har); } } diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index 01055d87a38..9ea7cb62c58 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -633,7 +633,7 @@ void shade_input_calc_viewco(ShadeInput *shi, float x, float y, float z, float v dyco[2] = 0.0f; if (dxyview) { - if (co[2] != 0.0f) fac = 1.0f / co[2]; else fac = 0.0f; + fac = (co[2] != 0.0f) ? (1.0f / co[2]) : 0.0f; dxyview[0] = -R.viewdx * fac; dxyview[1] = -R.viewdy * fac; } diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c index 7e8f0e3e9fc..b6628796e0d 100644 --- a/source/blender/render/intern/source/zbuf.c +++ b/source/blender/render/intern/source/zbuf.c @@ -330,8 +330,8 @@ static void zbuffillAc4(ZSpan *zspan, int obi, int zvlnr, /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; - if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; - if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); if (my2<my0) return; @@ -1073,8 +1073,8 @@ static void zbuffillGLinv4(ZSpan *zspan, int obi, int zvlnr, /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; - if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; - if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); // printf("my %d %d\n", my0, my2); if (my2<my0) return; @@ -1196,8 +1196,8 @@ static void zbuffillGL4(ZSpan *zspan, int obi, int zvlnr, /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; - if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; - if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); // printf("my %d %d\n", my0, my2); if (my2<my0) return; @@ -1324,8 +1324,8 @@ static void zbuffillGL_onlyZ(ZSpan *zspan, int UNUSED(obi), int UNUSED(zvlnr), /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; - if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; - if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); // printf("my %d %d\n", my0, my2); if (my2<my0) return; @@ -1426,8 +1426,8 @@ void zspan_scanconvert_strand(ZSpan *zspan, void *handle, float *v1, float *v2, /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; - if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; - if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); // printf("my %d %d\n", my0, my2); if (my2<my0) return; @@ -1513,7 +1513,7 @@ void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float * float x0, y0, x1, y1, x2, y2, z0, z1, z2; float u, v, uxd, uyd, vxd, vyd, uy0, vy0, xx1; const float *span1, *span2; - int x, y, sn1, sn2, rectx= zspan->rectx, my0, my2; + int i, j, x, y, sn1, sn2, rectx = zspan->rectx, my0, my2; /* init */ zbuf_init_span(zspan); @@ -1526,8 +1526,8 @@ void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float * /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; - if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; - if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); // printf("my %d %d\n", my0, my2); if (my2<my0) return; @@ -1574,7 +1574,7 @@ void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float * span2= zspan->span1+my2; } - for (y=my2; y>=my0; y--, span1--, span2--) { + for (i = 0, y = my2; y >= my0; i++, y--, span1--, span2--) { sn1= floor(*span1); sn2= floor(*span2); @@ -1583,14 +1583,12 @@ void zspan_scanconvert(ZSpan *zspan, void *handle, float *v1, float *v2, float * if (sn2>=rectx) sn2= rectx-1; if (sn1<0) sn1= 0; - u= (double)sn1*uxd + uy0; - v= (double)sn1*vxd + vy0; - - for (x= sn1; x<=sn2; x++, u+=uxd, v+=vxd) - func(handle, x, y, u, v); + u = (((double)sn1 * uxd) + uy0) - (i * uyd); + v = (((double)sn1 * vxd) + vy0) - (i * vyd); - uy0 -= uyd; - vy0 -= vyd; + for (j = 0, x = sn1; x <= sn2; j++, x++) { + func(handle, x, y, u + (j * uxd), v + (j * vxd)); + } } } @@ -2482,8 +2480,8 @@ static void zbuffill_sss(ZSpan *zspan, int obi, int zvlnr, /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; - if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; - if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); if (my2<my0) return; @@ -2683,8 +2681,8 @@ static void zbuf_fill_in_rgba(ZSpan *zspan, DrawBufPixel *col, float *v1, float /* clipped */ if (zspan->minp2==NULL || zspan->maxp2==NULL) return; - if (zspan->miny1 < zspan->miny2) my0= zspan->miny2; else my0= zspan->miny1; - if (zspan->maxy1 > zspan->maxy2) my2= zspan->maxy2; else my2= zspan->maxy1; + my0 = max_ii(zspan->miny1, zspan->miny2); + my2 = min_ii(zspan->maxy1, zspan->maxy2); // printf("my %d %d\n", my0, my2); if (my2<my0) return; @@ -3961,7 +3959,7 @@ static void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf float *fp, *col; int a; - fp= RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR); + fp = RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR, R.viewname); if (fp==NULL) return; col= rectf+3; @@ -4054,9 +4052,10 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas /* zero alpha pixels get speed vector max again */ if (addpassflag & SCE_PASS_VECTOR) - if (rl->layflag & SCE_LAY_SOLID) - reset_sky_speedvectors(pa, rl, rl->acolrect?rl->acolrect:rl->rectf); /* if acolrect is set we use it */ - + if (rl->layflag & SCE_LAY_SOLID) { + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + reset_sky_speedvectors(pa, rl, rl->acolrect ? rl->acolrect : rect); /* if acolrect is set we use it */ + } /* filtered render, for now we assume only 1 filter size */ if (pa->crop) { crop= 1; @@ -4241,8 +4240,9 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas alpha= samp_shr[a].combined[3]; if (alpha!=0.0f) { RenderLayer *rl= ssamp.rlpp[a]; - - addAlphaOverFloat(rl->rectf + 4*od, samp_shr[a].combined); + + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + addAlphaOverFloat(rect + 4 * od, samp_shr[a].combined); add_transp_passes(rl, od, &samp_shr[a], alpha); if (addpassflag & SCE_PASS_VECTOR) diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index df5826338c0..75df966887c 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC intern/wm_operators.c intern/wm_subwindow.c intern/wm_window.c + intern/wm_stereo.c intern/wm_widgets.c intern/wm_generic_widgets.c 3d_widgets/arrow_widget.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 0ccc3888b5e..83aacefb247 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -111,6 +111,7 @@ void WM_window_open_temp (struct bContext *C, struct rcti *position, int type); /* returns true if draw method is triple buffer */ bool WM_is_draw_triple(struct wmWindow *win); +bool WM_stereo3d_enabled(struct wmWindow *win, bool only_fullscreen_test); /* files */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 455375a0fec..91767a990d4 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -180,9 +180,11 @@ enum { #define KM_NOTHING 0 #define KM_PRESS 1 #define KM_RELEASE 2 -#define KM_CLICK 3 -#define KM_DBL_CLICK 4 +/* clicktype */ +#define KM_CLICK 3 /* clicked key (click_time <= U.click_timeout) */ +#define KM_DBL_CLICK 4 /* double click - keep at 4 to avoid breakage with older key configs */ +#define KM_HOLD 5 /* held key (click_time > U.click_timeout) */ /* ************** UI Handler ***************** */ @@ -427,7 +429,9 @@ typedef struct wmEvent { short type; /* event code itself (short, is also in keymap) */ short val; /* press, release, scrollvalue */ + short click_type; /* click, hold or double click */ int x, y; /* mouse pointer position, screen coord */ + double click_time; /* the time since keypress started */ int mval[2]; /* region mouse position, name convention pre 2.5 :) */ char utf8_buf[6]; /* from, ghost if utf8 is enabled for the platform, * BLI_str_utf8_size() must _always_ be valid, check @@ -435,20 +439,19 @@ typedef struct wmEvent { char ascii; /* from ghost, fallback if utf8 isn't set */ char pad; - /* previous state, used for double click and the 'click' */ + bool is_key_pressed; /* is a (non-modifier) key is pressed? (keyboard, mouse, NDOF, ...) */ + + /* previous state, used for clicktype */ short prevtype; short prevval; int prevx, prevy; - double prevclicktime; + double prevclick_time; int prevclickx, prevclicky; /* modifier states */ short shift, ctrl, alt, oskey; /* oskey is apple or windowskey, value denotes order of pressed */ short keymodifier; /* rawkey modifier */ - /* set in case a KM_PRESS went by unhandled */ - short check_click; - /* keymap item, set by handler (weak?) */ const char *keymap_idname; diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 7440570f4a0..16fe9ca5142 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -48,9 +48,11 @@ #include "BIF_gl.h" #include "BKE_context.h" +#include "BKE_image.h" #include "GHOST_C-api.h" +#include "ED_node.h" #include "ED_view3d.h" #include "ED_screen.h" @@ -165,6 +167,7 @@ static void wm_method_draw_full(bContext *C, wmWindow *win) if (ar->swinid) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; wm_paintcursor_draw(C, ar); CTX_wm_region_set(C, NULL); } @@ -175,12 +178,14 @@ static void wm_method_draw_full(bContext *C, wmWindow *win) } ED_screen_draw(win); + win->screen->do_draw = false; /* draw overlapping regions */ for (ar = screen->regionbase.first; ar; ar = ar->next) { if (ar->swinid) { CTX_wm_menu_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_menu_set(C, NULL); } } @@ -278,6 +283,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) if (ar->do_draw) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; wm_paintcursor_draw(C, ar); CTX_wm_region_set(C, NULL); @@ -288,6 +294,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) if (ar->swap == WIN_FRONT_OK) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; wm_paintcursor_draw(C, ar); CTX_wm_region_set(C, NULL); @@ -300,7 +307,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) } } } - + wm_area_mark_invalid_backbuf(sa); CTX_wm_area_set(C, NULL); } @@ -308,6 +315,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) /* after area regions so we can do area 'overlay' drawing */ if (screen->do_draw) { ED_screen_draw(win); + win->screen->do_draw = false; if (exchange) screen->swap = WIN_FRONT_OK; @@ -315,6 +323,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) else if (exchange) { if (screen->swap == WIN_FRONT_OK) { ED_screen_draw(win); + win->screen->do_draw = false; screen->swap = WIN_BOTH_OK; } else if (screen->swap == WIN_BACK_OK) @@ -328,6 +337,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) if (ar->swinid && ar->do_draw) { CTX_wm_menu_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_menu_set(C, NULL); } } @@ -360,15 +370,6 @@ static void wm_method_draw_damage(bContext *C, wmWindow *win) /* used. if not, multiple smaller ones are used, with */ /* worst case wasted space being 23.4% for 3x3 textures */ -#define MAX_N_TEX 3 - -typedef struct wmDrawTriple { - GLuint bind[MAX_N_TEX * MAX_N_TEX]; - int x[MAX_N_TEX], y[MAX_N_TEX]; - int nx, ny; - GLenum target; -} wmDrawTriple; - static void split_width(int x, int n, int *splitx, int *nx) { int a, newnx, waste; @@ -405,16 +406,13 @@ static void split_width(int x, int n, int *splitx, int *nx) } } -static void wm_draw_triple_free(wmWindow *win) +static void wm_draw_triple_free(wmDrawTriple *triple) { - if (win->drawdata) { - wmDrawTriple *triple = win->drawdata; + if (triple) { glDeleteTextures(triple->nx * triple->ny, triple->bind); MEM_freeN(triple); - - win->drawdata = NULL; } } @@ -499,7 +497,7 @@ static int wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple) return 1; } -static void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) +void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) { const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); @@ -571,7 +569,7 @@ static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) glBindTexture(triple->target, 0); } -static void wm_draw_region_blend(wmWindow *win, ARegion *ar) +static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *triple) { float fac = ED_region_blend_factor(ar); @@ -580,7 +578,7 @@ static void wm_draw_region_blend(wmWindow *win, ARegion *ar) wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct, true); glEnable(GL_BLEND); - wm_triple_draw_textures(win, win->drawdata, 1.0f - fac); + wm_triple_draw_textures(win, triple, 1.0f - fac); glDisable(GL_BLEND); } } @@ -589,29 +587,46 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) { wmWindowManager *wm = CTX_wm_manager(C); wmDrawTriple *triple; + wmDrawData *dd, *dd_next, *drawdata = win->drawdata.first; bScreen *screen = win->screen; ScrArea *sa; ARegion *ar; - int copytex = 0; + int copytex = false; - if (win->drawdata) { + if (drawdata && drawdata->triple) { glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); wmSubWindowSet(win, screen->mainwin); - wm_triple_draw_textures(win, win->drawdata, 1.0f); + wm_triple_draw_textures(win, drawdata->triple, 1.0f); } else { - win->drawdata = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); + /* we run it when we start OR when we turn stereo on */ + if (drawdata == NULL) { + drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData"); + BLI_addhead(&win->drawdata, drawdata); + } + + drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); - if (!wm_triple_gen_textures(win, win->drawdata)) { + if (!wm_triple_gen_textures(win, drawdata->triple)) { wm_draw_triple_fail(C, win); return; } } - triple = win->drawdata; + /* it means stereo was just turned off */ + /* note: we are removing all drawdatas that are not the first */ + for (dd = drawdata->next; dd; dd = dd_next) { + dd_next = dd->next; + + BLI_remlink(&win->drawdata, dd); + wm_draw_triple_free(dd->triple); + MEM_freeN(dd); + } + + triple = drawdata->triple; /* draw marked area regions */ for (sa = screen->areabase.first; sa; sa = sa->next) { @@ -619,16 +634,17 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->swinid && ar->do_draw) { - - if (ar->overlap == 0) { + + if (ar->overlap == false) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_region_set(C, NULL); - copytex = 1; + copytex = true; } } } - + wm_area_mark_invalid_backbuf(sa); CTX_wm_area_set(C, NULL); } @@ -662,14 +678,15 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) /* draw overlapping area regions (always like popups) */ for (sa = screen->areabase.first; sa; sa = sa->next) { CTX_wm_area_set(C, sa); - + for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->swinid && ar->overlap) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_region_set(C, NULL); - - wm_draw_region_blend(win, ar); + + wm_draw_region_blend(win, ar, triple); } } @@ -678,12 +695,14 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) /* after area regions so we can do area 'overlay' drawing */ ED_screen_draw(win); + win->screen->do_draw = false; /* draw floating regions (menus) */ for (ar = screen->regionbase.first; ar; ar = ar->next) { if (ar->swinid) { CTX_wm_menu_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_menu_set(C, NULL); } } @@ -691,13 +710,188 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) /* always draw, not only when screen tagged */ if (win->gesture.first) wm_gesture_draw(win); - + /* needs pixel coords in screen */ if (wm->drags.first) { wm_drags_draw(C, win, NULL); } } +static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoViews sview) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmDrawData *drawdata; + wmDrawTriple *triple_data, *triple_all; + bScreen *screen = win->screen; + ScrArea *sa; + ARegion *ar; + int copytex = false; + int id; + + /* we store the triple_data in sequence to triple_all */ + for (id = 0; id < 2; id++) { + drawdata = BLI_findlink(&win->drawdata, (sview * 2) + id); + + if (drawdata && drawdata->triple) { + if (id == 0) { + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + wmSubWindowSet(win, screen->mainwin); + + wm_triple_draw_textures(win, drawdata->triple, 1.0f); + } + } + else { + /* we run it when we start OR when we turn stereo on */ + if (drawdata == NULL) { + drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData"); + BLI_addtail(&win->drawdata, drawdata); + } + + drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); + + if (!wm_triple_gen_textures(win, drawdata->triple)) { + wm_draw_triple_fail(C, win); + return; + } + } + } + + triple_data = ((wmDrawData *) BLI_findlink(&win->drawdata, sview * 2))->triple; + triple_all = ((wmDrawData *) BLI_findlink(&win->drawdata, (sview * 2) + 1))->triple; + + /* draw marked area regions */ + for (sa = screen->areabase.first; sa; sa = sa->next) { + CTX_wm_area_set(C, sa); + + switch (sa->spacetype) { + case SPACE_IMAGE: + { + SpaceImage *sima = sa->spacedata.first; + sima->iuser.multiview_eye = sview; + break; + } + case SPACE_VIEW3D: + { + View3D *v3d = sa->spacedata.first; + BGpic *bgpic = v3d->bgpicbase.first; + v3d->multiview_eye = sview; + if (bgpic) bgpic->iuser.multiview_eye = sview; + break; + } + case SPACE_NODE: + { + SpaceNode *snode = sa->spacedata.first; + if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { + Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node"); + ima->eye = sview; + } + break; + } + case SPACE_SEQ: + { + SpaceSeq *sseq = sa->spacedata.first; + sseq->multiview_eye = sview; + break; + } + } + + /* draw marked area regions */ + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->swinid && ar->do_draw) { + + if (ar->overlap == false) { + CTX_wm_region_set(C, ar); + ED_region_do_draw(C, ar); + + if (sview == STEREO_RIGHT_ID) + ar->do_draw = false; + + CTX_wm_region_set(C, NULL); + copytex = true; + } + } + } + + wm_area_mark_invalid_backbuf(sa); + CTX_wm_area_set(C, NULL); + } + + if (copytex) { + wmSubWindowSet(win, screen->mainwin); + + wm_triple_copy_textures(win, triple_data); + } + + if (wm->paintcursors.first) { + for (sa = screen->areabase.first; sa; sa = sa->next) { + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->swinid && ar->swinid == screen->subwinactive) { + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, ar); + + /* make region ready for draw, scissor, pixelspace */ + ED_region_set(C, ar); + wm_paintcursor_draw(C, ar); + + CTX_wm_region_set(C, NULL); + CTX_wm_area_set(C, NULL); + } + } + } + + wmSubWindowSet(win, screen->mainwin); + } + + /* draw overlapping area regions (always like popups) */ + for (sa = screen->areabase.first; sa; sa = sa->next) { + CTX_wm_area_set(C, sa); + + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->swinid && ar->overlap) { + CTX_wm_region_set(C, ar); + ED_region_do_draw(C, ar); + if (sview == STEREO_RIGHT_ID) + ar->do_draw = false; + CTX_wm_region_set(C, NULL); + + wm_draw_region_blend(win, ar, triple_data); + } + } + + CTX_wm_area_set(C, NULL); + } + + /* after area regions so we can do area 'overlay' drawing */ + ED_screen_draw(win); + if (sview == STEREO_RIGHT_ID) + win->screen->do_draw = false; + + /* draw floating regions (menus) */ + for (ar = screen->regionbase.first; ar; ar = ar->next) { + if (ar->swinid) { + CTX_wm_menu_set(C, ar); + ED_region_do_draw(C, ar); + if (sview == STEREO_RIGHT_ID) + ar->do_draw = false; + CTX_wm_menu_set(C, NULL); + } + } + + /* always draw, not only when screen tagged */ + if (win->gesture.first) + wm_gesture_draw(win); + + /* needs pixel coords in screen */ + if (wm->drags.first) { + wm_drags_draw(C, win, NULL); + } + + /* copy the ui + overlays */ + wmSubWindowSet(win, screen->mainwin); + wm_triple_copy_textures(win, triple_all); +} /****************** main update call **********************/ @@ -848,8 +1042,16 @@ void wm_draw_update(bContext *C) wm_method_draw_overlap_all(C, win, 0); else if (drawmethod == USER_DRAW_OVERLAP_FLIP) wm_method_draw_overlap_all(C, win, 1); - else // if (drawmethod == USER_DRAW_TRIPLE) - wm_method_draw_triple(C, win); + else { /* USER_DRAW_TRIPLE */ + if ((WM_stereo3d_enabled(win, false)) == false) { + wm_method_draw_triple(C, win); + } + else { + wm_method_draw_triple_multiview(C, win, STEREO_LEFT_ID); + wm_method_draw_triple_multiview(C, win, STEREO_RIGHT_ID); + wm_method_draw_stereo3d(C, win); + } + } win->screen->do_draw_gesture = false; win->screen->do_draw_paintcursor = false; @@ -862,15 +1064,23 @@ void wm_draw_update(bContext *C) } } +void wm_draw_data_free(wmWindow *win) +{ + wmDrawData *dd; + + for (dd = win->drawdata.first; dd; dd = dd->next) { + wm_draw_triple_free(dd->triple); + } + BLI_freelistN(&win->drawdata); +} + void wm_draw_window_clear(wmWindow *win) { bScreen *screen = win->screen; ScrArea *sa; ARegion *ar; - int drawmethod = wm_automatic_draw_method(win); - if (drawmethod == USER_DRAW_TRIPLE) - wm_draw_triple_free(win); + wm_draw_data_free(win); /* clear screen swap flags */ if (screen) { diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 59c35ff541f..c5252e28690 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -404,7 +404,7 @@ static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *eve * to make the DBL_CLICK conversion work, we just don't send this to UI, except mouse clicks */ if (((handler->flag & WM_HANDLER_ACCEPT_DBL_CLICK) == 0) && (event->type != LEFTMOUSE) && - (event->val == KM_DBL_CLICK)) + (event->click_type == KM_DBL_CLICK)) { return WM_HANDLER_CONTINUE; } @@ -1476,6 +1476,7 @@ int WM_userdef_event_map(int kmitype) } +/* XXX rename to something more descriptive like wm_event_is_keymapitem_matching and use bool */ static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi) { int kmitype = WM_userdef_event_map(kmi->type); @@ -1492,10 +1493,13 @@ static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi) if (kmitype != KM_ANY) if (winevent->type != kmitype) return 0; - + + /* KM_ANY excludes KM_HOLD since it's time based and not a real input - filter it out */ + if (kmi->val == KM_ANY && winevent->click_type == KM_HOLD) return 0; + if (kmi->val != KM_ANY) - if (winevent->val != kmi->val) return 0; - + if (!ELEM(kmi->val, winevent->val, winevent->click_type)) return 0; + /* modifiers also check bits, so it allows modifier order */ if (kmi->shift != KM_ANY) if (winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0; @@ -1542,8 +1546,9 @@ static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *eve /* modal keymap checking returns handled events fine, but all hardcoded modal * handling typically swallows all events (OPERATOR_RUNNING_MODAL). * This bypass just disables support for double clicks in hardcoded modal handlers */ - if (event->val == KM_DBL_CLICK) { + if (event->click_type == KM_DBL_CLICK) { event->val = KM_PRESS; + event->click_type = 0; *dbl_click_disabled = true; } } @@ -1575,9 +1580,9 @@ static void wm_event_modalmap_end(wmEvent *event, bool dbl_click_disabled) event->val = event->prevval; event->prevval = 0; } - else if (dbl_click_disabled) - event->val = KM_DBL_CLICK; - + else if (dbl_click_disabled) { + event->click_type = KM_DBL_CLICK; + } } /* Warning: this function removes a modal handler, when finished */ @@ -2120,47 +2125,21 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) /* test for CLICK events */ if (wm_action_not_handled(action)) { wmWindow *win = CTX_wm_window(C); - - /* eventstate stores if previous event was a KM_PRESS, in case that - * wasn't handled, the KM_RELEASE will become a KM_CLICK */ - - if (win && event->val == KM_PRESS) { - win->eventstate->check_click = true; - } - - if (win && win->eventstate->prevtype == event->type) { - - if ((event->val == KM_RELEASE) && - (win->eventstate->prevval == KM_PRESS) && - (win->eventstate->check_click == true)) - { - event->val = KM_CLICK; - - if (G.debug & (G_DEBUG_HANDLERS)) { - printf("%s: handling CLICK\n", __func__); - } - - action |= wm_handlers_do_intern(C, event, handlers); - event->val = KM_RELEASE; - } - else if (event->val == KM_DBL_CLICK) { + /* XXX check if those double click hacks can be removed/improved since click_type was introduced */ + if (win && win->eventstate->prevtype == event->type) { + if (event->click_type == KM_DBL_CLICK) { event->val = KM_PRESS; + event->click_type = 0; action |= wm_handlers_do_intern(C, event, handlers); /* revert value if not handled */ if (wm_action_not_handled(action)) { - event->val = KM_DBL_CLICK; + event->click_type = KM_DBL_CLICK; } } } } - else { - wmWindow *win = CTX_wm_window(C); - - if (win) - win->eventstate->check_click = 0; - } } return action; @@ -3104,22 +3083,50 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi return NULL; } -static bool wm_event_is_double_click(wmEvent *event, wmEvent *event_state) +/** + * Clicktype test + * + * We have 3 different click_types: #KM_CLICK, #KM_HOLD# and #KM_DBL_CLICK. + * + * Time is used to determine, what to send. It works as follows: + * - #KM_RELEASE && time since first #KM_PRESS < U.click_timeout --> send #KM_CLICK + * - #KM_PRESS && time since first #KM_PRESS > U.click_timeout --> send #KM_HOLD + * - #KM_PRESS after a #KM_RELEASE && time since previous #KM_PRESS < U.dbl_click_time --> send #KM_DBL_CLICK + * + * \note: only #KM_DBL_CLICK is handled here, rest in #wm_window_event_clicktype_init (wm_window.c) + */ +static void wm_event_clicktype_init(wmWindow *win, wmEvent *event, wmEvent *event_state) { - if ((event->type == event_state->prevtype) && - (event_state->prevval == KM_RELEASE) && - (event->val == KM_PRESS)) + short click_type = 0; + + if ((event->val == KM_PRESS) && + (event_state->prevval != KM_PRESS || event->prevtype != win->eventstate->prevtype)) + { + event_state->prevclick_time = event->click_time; + event_state->prevclickx = event->x; + event_state->prevclicky = event->y; + } + + /* double click */ + if (event->type == event_state->prevtype && + event_state->prevval == KM_RELEASE && + event->val == KM_PRESS) { if ((ISMOUSE(event->type) == false) || ((ABS(event->x - event_state->prevclickx)) <= 2 && (ABS(event->y - event_state->prevclicky)) <= 2)) { - if ((PIL_check_seconds_timer() - event_state->prevclicktime) * 1000 < U.dbl_click_time) { - return true; + if ((PIL_check_seconds_timer() - event_state->prevclick_time) * 1000 < U.dbl_click_time) { + click_type = KM_DBL_CLICK; + if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS)) { + printf("%s Send double click event\n", __func__); + } } } } - return false; + if (click_type != event->click_type) { + event_state->click_type = event->click_type = click_type; + } } static void wm_event_add_mousemove(wmWindow *win, const wmEvent *event) @@ -3208,7 +3215,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U event.x = evt->x = pd->x; event.y = evt->y = pd->y; - event.val = 0; + event.val = KM_NOTHING; /* Use prevx/prevy so we can calculate the delta later */ event.prevx = event.x - pd->deltaX; @@ -3251,6 +3258,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U evt->val = event.val; evt->type = event.type; + /* click_type */ + wm_event_clicktype_init(win, &event, evt); + if (win->active == 0) { int cx, cy; @@ -3261,18 +3271,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U event.y = evt->y = cy; } - /* double click test */ - if (wm_event_is_double_click(&event, evt)) { - if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS)) - printf("%s Send double click\n", __func__); - event.val = KM_DBL_CLICK; - } - if (event.val == KM_PRESS) { - evt->prevclicktime = PIL_check_seconds_timer(); - evt->prevclickx = event.x; - evt->prevclicky = event.y; - } - /* add to other window if event is there (not to both!) */ owin = wm_event_cursor_other_windows(wm, win, &event); if (owin) { @@ -3311,7 +3309,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U /* copy to event state */ evt->val = event.val; evt->type = event.type; - + + /* clicktype */ + wm_event_clicktype_init(win, &event, evt); + /* exclude arrow keys, esc, etc from text input */ if (type == GHOST_kEventKeyUp) { event.ascii = '\0'; @@ -3377,14 +3378,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U event.keymodifier = evt->keymodifier = 0; break; } - - /* double click test */ - /* if previous event was same type, and previous was release, and now it presses... */ - if (wm_event_is_double_click(&event, evt)) { - if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS)) - printf("%s Send double click\n", __func__); - evt->val = event.val = KM_DBL_CLICK; - } /* this case happens on holding a key pressed, it should not generate * press events events with the same key as modifier */ @@ -3405,13 +3398,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U G.is_break = true; } - /* double click test - only for press */ - if (event.val == KM_PRESS) { - evt->prevclicktime = PIL_check_seconds_timer(); - evt->prevclickx = event.x; - evt->prevclicky = event.y; - } - wm_event_add(win, &event); break; @@ -3436,7 +3422,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U event.type = TIMER; event.custom = EVT_DATA_TIMER; event.customdata = customdata; - event.val = 0; + event.val = KM_NOTHING; event.keymodifier = 0; wm_event_add(win, &event); @@ -3446,7 +3432,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U case GHOST_kEventNDOFMotion: { event.type = NDOF_MOTION; - event.val = 0; + event.val = KM_NOTHING; attach_ndof_data(&event, customdata); wm_event_add(win, &event); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 3fd1fe427c9..f3e9c416e7b 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -846,11 +846,11 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt) if (scene->camera) { ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, err_out); + IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, NULL, err_out); } else { ibuf = ED_view3d_draw_offscreen_imbuf(scene, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, false, R_ALPHAPREMUL, err_out); + IB_rect, false, R_ALPHAPREMUL, NULL, err_out); } if (ibuf) { diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index b1bb45686fc..8edccbce25d 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -60,7 +60,7 @@ #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" -#include "BKE_mball.h" +#include "BKE_mball_tessellate.h" #include "BKE_node.h" #include "BKE_report.h" diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 82e46c1b333..0a89ca113c4 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -1192,11 +1192,15 @@ int WM_keymap_item_compare(wmKeyMapItem *k1, wmKeyMapItem *k2) return 0; if (k1->val != KM_ANY && k2->val != KM_ANY) { + +#if 0 /* thanks to clicktype those shouldn't be needed anymore */ /* take click, press, release conflict into account */ if (k1->val == KM_CLICK && ELEM(k2->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0) return 0; if (k2->val == KM_CLICK && ELEM(k1->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0) return 0; +#endif + if (k1->val != k2->val) return 0; } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index a0749e01e38..c325afce2b6 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -172,6 +172,7 @@ void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties); /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); + ot->translation_context = BLF_I18NCONTEXT_OPERATOR_DEFAULT; opfunc(ot); if (ot->name == NULL) { @@ -194,6 +195,7 @@ void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void * ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties); /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); + ot->translation_context = BLF_I18NCONTEXT_OPERATOR_DEFAULT; opfunc(ot, userdata); RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); RNA_def_struct_identifier(ot->srna, ot->idname); @@ -374,6 +376,7 @@ static void wm_macro_cancel(bContext *C, wmOperator *op) wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *name, const char *description, int flag) { wmOperatorType *ot; + const char *i18n_context; if (WM_operatortype_find(idname, true)) { printf("%s: macro error: operator %s exists\n", __func__, idname); @@ -400,8 +403,9 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); RNA_def_struct_identifier(ot->srna, ot->idname); /* Use i18n context from ext.srna if possible (py operators). */ - RNA_def_struct_translation_context(ot->srna, ot->ext.srna ? RNA_struct_translation_context(ot->ext.srna) : - BLF_I18NCONTEXT_OPERATOR_DEFAULT); + i18n_context = ot->ext.srna ? RNA_struct_translation_context(ot->ext.srna) : BLF_I18NCONTEXT_OPERATOR_DEFAULT; + RNA_def_struct_translation_context(ot->srna, i18n_context); + ot->translation_context = i18n_context; BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); @@ -427,6 +431,7 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *), /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); + ot->translation_context = BLF_I18NCONTEXT_OPERATOR_DEFAULT; opfunc(ot, userdata); RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); @@ -1263,6 +1268,13 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, if (flag & WM_FILESEL_RELPATH) RNA_def_boolean(ot->srna, "relative_path", true, "Relative Path", "Select the file relative to the blend file"); + if ((filter & FILE_TYPE_IMAGE) || (filter & FILE_TYPE_MOVIE)) { + prop = RNA_def_boolean(ot->srna, "show_multiview", 0, "Enable Multi-View", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "use_multiview", 0, "Use Multi-View", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + } + prop = RNA_def_enum(ot->srna, "display_type", file_display_items, display, "Display Type", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -4617,8 +4629,10 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) for (a = 0; a < iter; a++) { if (type == 0) { - if (ar) + if (ar) { ED_region_do_draw(C, ar); + ar->do_draw = false; + } } else if (type == 1) { wmWindow *win = CTX_wm_window(C); @@ -4646,6 +4660,7 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) if (ar_iter->swinid) { CTX_wm_region_set(C, ar_iter); ED_region_do_draw(C, ar_iter); + ar->do_draw = false; } } } @@ -4857,6 +4872,36 @@ static void operatortype_ghash_free_cb(wmOperatorType *ot) } /* ******************************************************* */ +/* toggle 3D for current window, turning it fullscreen if needed */ +static void WM_OT_stereo3d_set(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Set Stereo 3D"; + ot->idname = "WM_OT_set_stereo_3d"; + ot->description = "Toggle 3D stereo support for current window (or change the display mode)"; + + ot->exec = wm_stereo3d_set_exec; + ot->invoke = wm_stereo3d_set_invoke; + ot->poll = WM_operator_winactive; + ot->ui = wm_stereo3d_set_draw; + ot->cancel = wm_stereo3d_set_cancel; + + prop = RNA_def_enum(ot->srna, "display_mode", stereo3d_display_items, S3D_DISPLAY_ANAGLYPH, "Display Mode", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "anaglyph_type", stereo3d_anaglyph_type_items, S3D_ANAGLYPH_REDCYAN, "Anaglyph Type", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "interlace_type", stereo3d_interlace_type_items, S3D_INTERLACE_ROW, "Interlace Type", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "use_interlace_swap", false, "Swap Left/Right", + "Swap left and right stereo channels"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "use_sidebyside_crosseyed", false, "Cross-Eyed", + "Right eye should see left image and vice-versa"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/* ******************************************************* */ /* called on initialize WM_exit() */ void wm_operatortype_free(void) { @@ -4899,6 +4944,7 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_call_menu_pie); WM_operatortype_append(WM_OT_radial_control); WM_operatortype_append(WM_OT_widget_tweak); + WM_operatortype_append(WM_OT_stereo3d_set); #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); #endif @@ -5134,7 +5180,7 @@ void wm_window_keymap(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "WM_OT_debug_menu", DKEY, KM_PRESS, KM_ALT | KM_CTRL, 0); /* menus that can be accessed anywhere in blender */ - WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_PRESS, 0, 0); + WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_CLICK, 0, 0); WM_keymap_add_menu(keymap, "USERPREF_MT_ndof_settings", NDOF_BUTTON_MENU, KM_PRESS, 0, 0); /* Space switching */ diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c new file mode 100644 index 00000000000..5d7b11272c6 --- /dev/null +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -0,0 +1,534 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2015 by Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_stereo.c + * \ingroup wm + */ + + +#include <stdlib.h> +#include <string.h> + +#include "DNA_listBase.h" + +#include "RNA_access.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_rect.h" +#include "BLI_utildefines.h" + +#include "BIF_gl.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_report.h" + +#include "GHOST_C-api.h" + +#include "ED_screen.h" + +#include "GPU_glew.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm.h" +#include "wm_draw.h" /* wmDrawTriple */ +#include "wm_window.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +static void wm_method_draw_stereo3d_pageflip(wmWindow *win) +{ + wmDrawData *drawdata; + int view; + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + + if (view == STEREO_LEFT_ID) + glDrawBuffer(GL_BACK_LEFT); + else //STEREO_RIGHT_ID + glDrawBuffer(GL_BACK_RIGHT); + + wm_triple_draw_textures(win, drawdata->triple, 1.0f); + } +} + +static GLuint left_interlace_mask[32]; +static GLuint right_interlace_mask[32]; +static enum eStereo3dInterlaceType interlace_prev_type = -1; +static char interlace_prev_swap = -1; + +static void wm_interlace_masks_create(wmWindow *win) +{ + GLuint pattern; + char i; + bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0; + enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type; + + if (interlace_prev_type == interlace_type && interlace_prev_swap == swap) + return; + + switch (interlace_type) { + case S3D_INTERLACE_ROW: + pattern = 0x00000000; + pattern = swap ? ~pattern : pattern; + for (i = 0; i < 32; i += 2) { + left_interlace_mask[i] = pattern; + right_interlace_mask[i] = ~pattern; + } + for (i = 1; i < 32; i += 2) { + left_interlace_mask[i] = ~pattern; + right_interlace_mask[i] = pattern; + } + break; + case S3D_INTERLACE_COLUMN: + pattern = 0x55555555; + pattern = swap ? ~pattern : pattern; + for (i = 0; i < 32; i++) { + left_interlace_mask[i] = pattern; + right_interlace_mask[i] = ~pattern; + } + break; + case S3D_INTERLACE_CHECKERBOARD: + default: + pattern = 0x55555555; + pattern = swap ? ~pattern : pattern; + for (i = 0; i < 32; i += 2) { + left_interlace_mask[i] = pattern; + right_interlace_mask[i] = ~pattern; + } + for (i = 1; i < 32; i += 2) { + left_interlace_mask[i] = ~pattern; + right_interlace_mask[i] = pattern; + } + break; + } + interlace_prev_type = interlace_type; + interlace_prev_swap = swap; +} + +static void wm_method_draw_stereo3d_interlace(wmWindow *win) +{ + wmDrawData *drawdata; + int view; + + wm_interlace_masks_create(win); + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + + glEnable(GL_POLYGON_STIPPLE); + glPolygonStipple(view ? (GLubyte *) right_interlace_mask : (GLubyte *) left_interlace_mask); + + wm_triple_draw_textures(win, drawdata->triple, 1.0f); + glDisable(GL_POLYGON_STIPPLE); + } +} + +static void wm_method_draw_stereo3d_anaglyph(wmWindow *win) +{ + wmDrawData *drawdata; + int view, bit; + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + + bit = view + 1; + switch (win->stereo3d_format->anaglyph_type) { + case S3D_ANAGLYPH_REDCYAN: + glColorMask((1&bit) ? GL_TRUE : GL_FALSE, + (2&bit) ? GL_TRUE : GL_FALSE, + (2&bit) ? GL_TRUE : GL_FALSE, + GL_FALSE); + break; + case S3D_ANAGLYPH_GREENMAGENTA: + glColorMask((2&bit) ? GL_TRUE : GL_FALSE, + (1&bit) ? GL_TRUE : GL_FALSE, + (2&bit) ? GL_TRUE : GL_FALSE, + GL_FALSE); + break; + case S3D_ANAGLYPH_YELLOWBLUE: + glColorMask((1&bit) ? GL_TRUE : GL_FALSE, + (1&bit) ? GL_TRUE : GL_FALSE, + (2&bit) ? GL_TRUE : GL_FALSE, + GL_FALSE); + break; + } + + wm_triple_draw_textures(win, drawdata->triple, 1.0f); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } +} + +static void wm_method_draw_stereo3d_sidebyside(wmWindow *win) +{ + wmDrawData *drawdata; + wmDrawTriple *triple; + float halfx, halfy, ratiox, ratioy; + int x, y, offx, offy; + float alpha = 1.0f; + int view; + int soffx; + bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0; + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + triple = drawdata->triple; + + soffx = WM_window_pixels_x(win) * 0.5f; + if (view == STEREO_LEFT_ID) { + if (!cross_eyed) + soffx = 0; + } + else { //RIGHT_LEFT_ID + if (cross_eyed) + soffx = 0; + } + + glEnable(triple->target); + + for (y = 0, offy = 0; y < triple->ny; offy += triple->y[y], y++) { + for (x = 0, offx = 0; x < triple->nx; offx += triple->x[x], x++) { + const int sizex = triple->x[x]; + const int sizey = triple->y[y]; + + /* wmOrtho for the screen has this same offset */ + ratiox = sizex; + ratioy = sizey; + halfx = GLA_PIXEL_OFS; + halfy = GLA_PIXEL_OFS; + + /* texture rectangle has unnormalized coordinates */ + if (triple->target == GL_TEXTURE_2D) { + ratiox /= triple->x[x]; + ratioy /= triple->y[y]; + halfx /= triple->x[x]; + halfy /= triple->y[y]; + } + + glBindTexture(triple->target, triple->bind[x + y * triple->nx]); + + glColor4f(1.0f, 1.0f, 1.0f, alpha); + glBegin(GL_QUADS); + glTexCoord2f(halfx, halfy); + glVertex2f(soffx + (offx * 0.5f), offy); + + glTexCoord2f(ratiox + halfx, halfy); + glVertex2f(soffx + ((offx + sizex) * 0.5f), offy); + + glTexCoord2f(ratiox + halfx, ratioy + halfy); + glVertex2f(soffx + ((offx + sizex) * 0.5f), offy + sizey); + + glTexCoord2f(halfx, ratioy + halfy); + glVertex2f(soffx + (offx * 0.5f), offy + sizey); + glEnd(); + } + } + + glBindTexture(triple->target, 0); + glDisable(triple->target); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } +} + +static void wm_method_draw_stereo3d_topbottom(wmWindow *win) +{ + wmDrawData *drawdata; + wmDrawTriple *triple; + float halfx, halfy, ratiox, ratioy; + int x, y, offx, offy; + float alpha = 1.0f; + int view; + int soffy; + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + triple = drawdata->triple; + + if (view == STEREO_LEFT_ID) { + soffy = WM_window_pixels_y(win) * 0.5f; + } + else { /* STEREO_RIGHT_ID */ + soffy = 0; + } + + glEnable(triple->target); + + for (y = 0, offy = 0; y < triple->ny; offy += triple->y[y], y++) { + for (x = 0, offx = 0; x < triple->nx; offx += triple->x[x], x++) { + const int sizex = triple->x[x]; + const int sizey = triple->y[y]; + + /* wmOrtho for the screen has this same offset */ + ratiox = sizex; + ratioy = sizey; + halfx = GLA_PIXEL_OFS; + halfy = GLA_PIXEL_OFS; + + /* texture rectangle has unnormalized coordinates */ + if (triple->target == GL_TEXTURE_2D) { + ratiox /= triple->x[x]; + ratioy /= triple->y[y]; + halfx /= triple->x[x]; + halfy /= triple->y[y]; + } + + glBindTexture(triple->target, triple->bind[x + y * triple->nx]); + + glColor4f(1.0f, 1.0f, 1.0f, alpha); + glBegin(GL_QUADS); + glTexCoord2f(halfx, halfy); + glVertex2f(offx, soffy + (offy * 0.5f)); + + glTexCoord2f(ratiox + halfx, halfy); + glVertex2f(offx + sizex, soffy + (offy * 0.5f)); + + glTexCoord2f(ratiox + halfx, ratioy + halfy); + glVertex2f(offx + sizex, soffy + ((offy + sizey) * 0.5f)); + + glTexCoord2f(halfx, ratioy + halfy); + glVertex2f(offx, soffy + ((offy + sizey) * 0.5f)); + glEnd(); + } + } + + glBindTexture(triple->target, 0); + glDisable(triple->target); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } +} + +void wm_method_draw_stereo3d(const bContext *UNUSED(C), wmWindow *win) +{ + switch (win->stereo3d_format->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + wm_method_draw_stereo3d_anaglyph(win); + break; + case S3D_DISPLAY_INTERLACE: + wm_method_draw_stereo3d_interlace(win); + break; + case S3D_DISPLAY_PAGEFLIP: + wm_method_draw_stereo3d_pageflip(win); + break; + case S3D_DISPLAY_SIDEBYSIDE: + wm_method_draw_stereo3d_sidebyside(win); + break; + case S3D_DISPLAY_TOPBOTTOM: + wm_method_draw_stereo3d_topbottom(win); + break; + default: + break; + } +} + +static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display) +{ + return ELEM(stereo_display, + S3D_DISPLAY_SIDEBYSIDE, + S3D_DISPLAY_TOPBOTTOM, + S3D_DISPLAY_PAGEFLIP); +} + +bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) +{ + bScreen *screen = win->screen; + + if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false)) + return false; + + if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) + return WM_window_is_fullscreen(win); + + return true; +} + +/************************** Stereo 3D operator **********************************/ +typedef struct Stereo3dData { + Stereo3dFormat stereo3d_format; +} Stereo3dData; + +static bool wm_stereo3d_set_properties(bContext *C, wmOperator *op) +{ + wmWindow *win = CTX_wm_window(C); + Stereo3dFormat *s3d = win->stereo3d_format; + PropertyRNA *prop; + bool is_set = false; + + prop = RNA_struct_find_property(op->ptr, "display_mode"); + if (RNA_property_is_set(op->ptr, prop)) { + s3d->display_mode = RNA_property_enum_get(op->ptr, prop); + is_set = true; + } + + prop = RNA_struct_find_property(op->ptr, "anaglyph_type"); + if (RNA_property_is_set(op->ptr, prop)) { + s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop); + is_set = true; + } + + prop = RNA_struct_find_property(op->ptr, "interlace_type"); + if (RNA_property_is_set(op->ptr, prop)) { + s3d->interlace_type = RNA_property_enum_get(op->ptr, prop); + is_set = true; + } + + prop = RNA_struct_find_property(op->ptr, "use_interlace_swap"); + if (RNA_property_is_set(op->ptr, prop)) { + if (RNA_property_boolean_get(op->ptr, prop)) + s3d->flag |= S3D_INTERLACE_SWAP; + else + s3d->flag &= ~S3D_INTERLACE_SWAP; + is_set = true; + } + + prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed"); + if (RNA_property_is_set(op->ptr, prop)) { + if (RNA_property_boolean_get(op->ptr, prop)) + s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED; + else + s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED; + is_set = true; + } + + return is_set; +} + +static void wm_stereo3d_set_init(bContext *C, wmOperator *op) +{ + Stereo3dData *s3dd; + wmWindow *win = CTX_wm_window(C); + + op->customdata = s3dd = MEM_callocN(sizeof(Stereo3dData), __func__); + + /* store the original win stereo 3d settings in case of cancel */ + s3dd->stereo3d_format = *win->stereo3d_format; +} + +int wm_stereo3d_set_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + const bool is_fullscreen = WM_window_is_fullscreen(win); + + if (G.background) + return OPERATOR_CANCELLED; + + /* pagelfip requires a new window to be created with the proper OS flags */ + if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) { + if (wm_window_duplicate_exec(C, op) == OPERATOR_FINISHED) { + wm_window_close(C, wm, win); + win = wm->windows.last; + } + else { + BKE_reportf(op->reports, RPT_ERROR, + "Fail to create a window compatible with time sequential (page-flip) display method"); + return OPERATOR_CANCELLED; + } + } + + if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) { + if (!is_fullscreen) { + wm_window_fullscreen_toggle_exec(C, op); + } + } + + if (op->customdata) { + MEM_freeN(op->customdata); + } + + WM_event_add_notifier(C, NC_WINDOW, NULL); + return OPERATOR_FINISHED; +} + +int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wm_stereo3d_set_init(C, op); + + if (wm_stereo3d_set_properties(C, op)) + return wm_stereo3d_set_exec(C, op); + else + return WM_operator_props_dialog_popup(C, op, 250, 100); +} + +void wm_stereo3d_set_draw(bContext *C, wmOperator *op) +{ + wmWindow *win = CTX_wm_window(C); + Stereo3dFormat *stereo3d_format; + PointerRNA stereo3d_format_ptr; + uiLayout *layout = op->layout; + uiLayout *col; + + stereo3d_format = win->stereo3d_format; + RNA_pointer_create(NULL, &RNA_Stereo3dDisplay, stereo3d_format, &stereo3d_format_ptr); + + col = uiLayoutColumn(layout, false); + uiItemR(col, &stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE); + + switch (stereo3d_format->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + { + uiItemR(col, &stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE); + break; + } + case S3D_DISPLAY_INTERLACE: + { + uiItemR(col, &stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE); + uiItemR(col, &stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE); + break; + } + case S3D_DISPLAY_SIDEBYSIDE: + { + uiItemR(col, &stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE); + /* fall-through */ + } + case S3D_DISPLAY_PAGEFLIP: + case S3D_DISPLAY_TOPBOTTOM: + default: + { + break; + } + } +} + +void wm_stereo3d_set_cancel(bContext *C, wmOperator *op) +{ + Stereo3dData *s3dd = op->customdata; + wmWindow *win = CTX_wm_window(C); + + /* roll back to the original */ + if (win) { + *win->stereo3d_format = s3dd->stereo3d_format; + } + + MEM_freeN(op->customdata); + op->customdata = NULL; +} diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 4f7e5ab75b3..61bddb144ae 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -206,12 +206,13 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win) wm_event_free_all(win); wm_subwindows_free(win); - - if (win->drawdata) - MEM_freeN(win->drawdata); - + + wm_draw_data_free(win); + wm_ghostwindow_destroy(win); - + + MEM_freeN(win->stereo3d_format); + MEM_freeN(win); } @@ -236,6 +237,8 @@ wmWindow *wm_window_new(bContext *C) BLI_addtail(&wm->windows, win); win->winid = find_free_winid(wm); + win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)"); + return win; } @@ -259,8 +262,11 @@ wmWindow *wm_window_copy(bContext *C, wmWindow *winorig) win->screen->do_draw = true; win->drawmethod = U.wmdrawmethod; - win->drawdata = NULL; - + + BLI_listbase_clear(&win->drawdata); + + *win->stereo3d_format = *winorig->stereo3d_format; + return win; } @@ -367,6 +373,10 @@ static void wm_window_add_ghostwindow(wmWindowManager *wm, const char *title, wm glSettings.numOfAASamples = multisamples; + /* a new window is created when pageflip mode is required for a window */ + if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) + glSettings.flags |= GHOST_glStereoVisual; + if (!(U.uiflag2 & USER_OPENGL_NO_WARN_SUPPORT)) glSettings.flags |= GHOST_glWarnSupport; @@ -519,8 +529,7 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect) win->sizey = BLI_rcti_size_y(rect); win->drawmethod = U.wmdrawmethod; - win->drawdata = NULL; - + WM_check(C); return win; @@ -1097,6 +1106,76 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr return 1; } +/** + * #KM_DBL_CLICK is set in wm_event_clicktype_init (wm_event_system.c) + * Normally, this should be there too, but for #KM_CLICK/#KM_HOLD, we need a + * time precision of a few milliseconds, which we can't get from there + */ +static void wm_window_event_clicktype_init(const bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + + if (wm->winactive) { + wmWindow *win = wm->winactive; + wmEvent *event = win->eventstate; + short click_type = event->click_type; + + BLI_assert(event != NULL); + + if ((event->type == EVENT_NONE) || + ((event->val == KM_NOTHING) && (event->is_key_pressed == false))) + { + /* nothing needs to be done here */ + return; + } + + /* we always want click_type of last clicked button (to enable + * use with modifier keys) - unnecessary for mouse though */ + if (!ISMOUSE(event->type) && + event->val == KM_PRESS && + event->type != event->keymodifier) + { + event->is_key_pressed = false; + } + else if (event->val == KM_PRESS && !event->is_key_pressed) { + event->is_key_pressed = true; + event->click_time = PIL_check_seconds_timer(); + } + else if (event->val == KM_RELEASE && event->is_key_pressed) { + event->is_key_pressed = false; + } + else if (event->is_key_pressed == false) { + return; + } + + /* the actual test */ + if ((PIL_check_seconds_timer() - event->click_time) * 1000 <= U.click_timeout) { + /* for any reason some X11 systems send two release events triggering two KM_CLICK + * events - making the rules more strict by checking for prevval resolves this */ + if (event->val == KM_RELEASE && event->prevval != KM_RELEASE) { + click_type = KM_CLICK; + if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS)) { + printf("%s Send click event\n", __func__); + } + } + } + else if (event->is_key_pressed) { + click_type = KM_HOLD; + if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS)) { + printf("%s Send hold event\n", __func__); + } + + /* the event we send in this case is a "dummy" event - don't send value */ + event->val = KM_NOTHING; + } + + /* send event with new click_type */ + if (event->click_type != click_type) { + event->click_type = click_type; + wm_event_add(win, event); + } + } +} /* This timer system only gives maximum 1 timer event per redraw cycle, * to prevent queues to get overloaded. @@ -1132,7 +1211,7 @@ static int wm_window_timer(const bContext *C) wm_event_init_from_window(win, &event); event.type = wt->event_type; - event.val = 0; + event.val = KM_NOTHING; event.keymodifier = 0; event.custom = EVT_DATA_TIMER; event.customdata = wt; @@ -1156,7 +1235,11 @@ void wm_window_process_events(const bContext *C) if (hasevent) GHOST_DispatchEvents(g_system); - + + /* not nice to have this here, but it's the only place + * that can call it with the needed time precision */ + wm_window_event_clicktype_init(C); + hasevent |= wm_window_timer(C); /* no event, we sleep 5 milliseconds */ diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index 811d5ca6921..42581bd8517 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -153,6 +153,13 @@ void wm_autosave_delete(void); void wm_autosave_read(bContext *C, struct ReportList *reports); void wm_autosave_location(char *filepath); +/* wm_stereo.c */ +void wm_method_draw_stereo3d(const bContext *C, wmWindow *win); +int wm_stereo3d_set_exec(bContext *C, wmOperator *op); +int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *event); +void wm_stereo3d_set_draw(bContext *C, wmOperator *op); +void wm_stereo3d_set_cancel(bContext *C, wmOperator *op); + /* init operator properties */ void wm_open_init_load_ui(wmOperator *op, bool use_prefs); void wm_open_init_use_scripts(wmOperator *op, bool use_prefs); diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h index 3d72fe17c79..5dc52b2e4fb 100644 --- a/source/blender/windowmanager/wm_draw.h +++ b/source/blender/windowmanager/wm_draw.h @@ -32,6 +32,23 @@ #ifndef __WM_DRAW_H__ #define __WM_DRAW_H__ +#include "GPU_glew.h" + + +#define MAX_N_TEX 3 + +typedef struct wmDrawTriple { + GLuint bind[MAX_N_TEX * MAX_N_TEX]; + int x[MAX_N_TEX], y[MAX_N_TEX]; + int nx, ny; + GLenum target; +} wmDrawTriple; + +typedef struct wmDrawData { + struct wmDrawData *next, *prev; + wmDrawTriple *triple; +} wmDrawData; + struct bContext; struct wmWindow; struct ARegion; @@ -43,5 +60,9 @@ void wm_draw_region_clear (struct wmWindow *win, struct ARegion *ar); void wm_tag_redraw_overlay (struct wmWindow *win, struct ARegion *ar); +void wm_triple_draw_textures (struct wmWindow *win, struct wmDrawTriple *triple, float alpha); + +void wm_draw_data_free (struct wmWindow *win); + #endif /* __WM_DRAW_H__ */ diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index fc530d39e9f..d4893407332 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -225,12 +225,20 @@ void EDBM_mesh_normals_update(struct BMEditMesh *em) RET_NONE void *g_system; bool EDBM_mtexpoly_check(struct BMEditMesh *em) RET_ZERO -float *RE_RenderLayerGetPass(struct RenderLayer *rl, int passtype) RET_NULL +float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname) RET_NULL float RE_filter_value(int type, float x) RET_ZERO struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name) RET_NULL void RE_init_texture_rng() RET_NONE void RE_exit_texture_rng() RET_NONE +float *RE_RenderViewGetRectf(struct RenderResult *rr, int view_id) {STUB_ASSERT(0); return (float *) NULL;} +float *RE_RenderViewGetRectz(struct RenderResult *rr, int view_id) {STUB_ASSERT(0); return (float *) NULL;} +bool RE_layers_have_name(struct RenderResult *result) {STUB_ASSERT(0); return 0;} +void RE_engine_active_view_set(struct RenderEngine *engine, const char *viewname) {STUB_ASSERT(0);} +void RE_engine_get_camera_model_matrix(struct RenderEngine *engine, struct Object *camera, float *r_modelmat) {STUB_ASSERT(0);} +float RE_engine_get_camera_shift_x(struct RenderEngine *engine, struct Object *camera) RET_ZERO +void RE_SetActiveRenderView(struct Render *re, const char *viewname) {STUB_ASSERT(0);} + /* zbuf.c stub */ void antialias_tagbuf(int xsize, int ysize, char *rectmove) RET_NONE void RE_zbuf_accumulate_vecblur(struct NodeBlurData *nbd, int xsize, int ysize, float *newrect, float *imgrect, float *vecbufrect, float *zbufrect) RET_NONE @@ -254,6 +262,7 @@ void RE_sample_material_color(struct Material *mat, float color[3], float *alpha /* nodes */ struct Render *RE_GetRender(const char *name) RET_NULL +struct Object *RE_GetCamera(struct Render *re) RET_NULL float RE_lamp_get_data(struct ShadeInput *shi, struct Object *lamp_obj, float col[4], float lv[3], float *dist, float shadow[4]) RET_ZERO /* blenkernel */ @@ -601,9 +610,10 @@ void uiTemplateNodeSocket(struct uiLayout *layout, struct bContext *C, float *co void uiTemplatePalette(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color) RET_NONE struct uiLayout *uiTemplateCacheLibraryItem(struct uiLayout *layout, struct bContext *C, struct CacheLibrary *cachelib, struct Object *ob, int type, int index, int enabled) RET_NULL +void uiTemplateImageStereo3d(struct uiLayout *layout, struct PointerRNA *stereo3d_format_ptr) RET_NONE /* rna render */ -struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername) RET_NULL +struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) RET_NULL struct RenderResult *RE_AcquireResultRead(struct Render *re) RET_NULL struct RenderResult *RE_AcquireResultWrite(struct Render *re) RET_NULL struct RenderStats *RE_GetStats(struct Render *re) RET_NULL @@ -615,7 +625,7 @@ void RE_engine_end_result(RenderEngine *engine, struct RenderResult *result, int void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info) RET_NONE void RE_layer_load_from_file(struct RenderLayer *layer, struct ReportList *reports, const char *filename, int x, int y) RET_NONE void RE_result_load_from_file(struct RenderResult *result, struct ReportList *reports, const char *filename) RET_NONE -void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr) RET_NONE +void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr, const int view_id) RET_NONE void RE_ReleaseResult(struct Render *re) RET_NONE void RE_ReleaseResultImage(struct Render *re) RET_NONE int RE_engine_test_break(struct RenderEngine *engine) RET_ZERO @@ -731,6 +741,11 @@ struct CCLDeviceInfo *CCL_compute_device_list(int opencl) RET_NULL /* compositor */ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) RET_NONE + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName) RET_NONE + +/*multiview*/ +bool RE_RenderResult_is_stereo(RenderResult *res) RET_ZERO +void uiTemplateImageViews(uiLayout *layout, struct PointerRNA *imfptr) RET_NONE #endif // WITH_GAMEENGINE diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index ed405961b9c..efcd388b520 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -844,7 +844,7 @@ elseif(APPLE) ) # install release and app files - set(BLENDER_TEXT_FILES_DESTINATION blender.app/Contents) + set(BLENDER_TEXT_FILES_DESTINATION ".") install( FILES ${OSX_APP_SOURCEDIR}/Contents/PkgInfo @@ -861,6 +861,11 @@ elseif(APPLE) FILES ${LIBDIR}/openmp/lib/libiomp5.dylib DESTINATION blender.app/Contents/Resources/lib/ ) + install( + FILES ${LIBDIR}/openmp/LICENSE.txt + DESTINATION "." + RENAME LICENSE-libiomp5.txt + ) endif() if(WITH_LLVM AND NOT LLVM_STATIC) diff --git a/source/gameengine/Converter/KX_BlenderSceneConverter.cpp b/source/gameengine/Converter/KX_BlenderSceneConverter.cpp index 21c8f39f782..4f251a66ccf 100644 --- a/source/gameengine/Converter/KX_BlenderSceneConverter.cpp +++ b/source/gameengine/Converter/KX_BlenderSceneConverter.cpp @@ -675,7 +675,7 @@ void KX_BlenderSceneConverter::WritePhysicsObjectToAnimationIpo(int frameNumber) if (blenderObject && blenderObject->parent==NULL && gameObj->IsRecordAnimation()) { if (blenderObject->adt==NULL) - BKE_id_add_animdata(&blenderObject->id); + BKE_animdata_add_id(&blenderObject->id); if (blenderObject->adt) { diff --git a/source/gameengine/Ketsji/KX_ObjectActuator.cpp b/source/gameengine/Ketsji/KX_ObjectActuator.cpp index 0eec86987be..f65b0ac0f4f 100644 --- a/source/gameengine/Ketsji/KX_ObjectActuator.cpp +++ b/source/gameengine/Ketsji/KX_ObjectActuator.cpp @@ -223,6 +223,11 @@ bool KX_ObjectActuator::Update() else if (m_bitLocalFlag.CharacterMotion) { MT_Vector3 dir = m_dloc; + if (m_bitLocalFlag.DLoc) { + MT_Matrix3x3 basis = parent->GetPhysicsController()->GetOrientation(); + dir = basis * dir; + } + if (m_bitLocalFlag.AddOrSetCharLoc) { MT_Vector3 old_dir = character->GetWalkDirection(); @@ -236,11 +241,6 @@ bool KX_ObjectActuator::Update() } // We always want to set the walk direction since a walk direction of (0, 0, 0) should stop the character - if (m_bitLocalFlag.DLoc) - { - MT_Matrix3x3 basis = parent->GetPhysicsController()->GetOrientation(); - dir = basis*dir; - } character->SetWalkDirection(dir/parent->GetScene()->GetPhysicsEnvironment()->GetNumTimeSubSteps()); if (!m_bitLocalFlag.ZeroDRot) diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index 193a72ebd2a..db4ed58d65f 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -869,7 +869,7 @@ void KX_Scene::DupliGroupRecurse(CValue* obj, int level) SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject, - class CValue* parentobject, + class CValue* referenceobject, int lifespan) { @@ -878,7 +878,7 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject, m_groupGameObjects.clear(); KX_GameObject* originalobj = (KX_GameObject*) originalobject; - KX_GameObject* parentobj = (KX_GameObject*) parentobject; + KX_GameObject* referenceobj = (KX_GameObject*) referenceobject; m_ueberExecutionPriority++; @@ -916,14 +916,14 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject, // At this stage all the objects in the hierarchy have been duplicated, // we can update the scenegraph, we need it for the duplication of logic - MT_Point3 newpos = ((KX_GameObject*) parentobject)->NodeGetWorldPosition(); + MT_Point3 newpos = referenceobj->NodeGetWorldPosition(); replica->NodeSetLocalPosition(newpos); - MT_Matrix3x3 newori = ((KX_GameObject*) parentobject)->NodeGetWorldOrientation(); + MT_Matrix3x3 newori = referenceobj->NodeGetWorldOrientation(); replica->NodeSetLocalOrientation(newori); // get the rootnode's scale - MT_Vector3 newscale = parentobj->GetSGNode()->GetRootSGParent()->GetLocalScale(); + MT_Vector3 newscale = referenceobj->GetSGNode()->GetRootSGParent()->GetLocalScale(); // set the replica's relative scale with the rootnode's scale replica->NodeSetRelativeScale(newscale); @@ -945,13 +945,13 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject, { // this will also relink the actuators in the hierarchy (*git)->Relink(&m_map_gameobject_to_replica); - // add the object in the layer of the parent - (*git)->SetLayer(parentobj->GetLayer()); + // add the object in the layer of the reference object + (*git)->SetLayer(referenceobj->GetLayer()); // If the object was a light, we need to update it's RAS_LightObject as well if ((*git)->GetGameObjectType()==SCA_IObject::OBJ_LIGHT) { KX_LightObject* lightobj = static_cast<KX_LightObject*>(*git); - lightobj->SetLayer(parentobj->GetLayer()); + lightobj->SetLayer(referenceobj->GetLayer()); } } @@ -2493,23 +2493,23 @@ KX_PYMETHODDEF_DOC(KX_Scene, addObject, "addObject(object, other, time=0)\n" "Returns the added object.\n") { - PyObject *pyob, *pyother; - KX_GameObject *ob, *other; + PyObject *pyob, *pyreference; + KX_GameObject *ob, *reference; int time = 0; - if (!PyArg_ParseTuple(args, "OO|i:addObject", &pyob, &pyother, &time)) + if (!PyArg_ParseTuple(args, "OO|i:addObject", &pyob, &pyreference, &time)) return NULL; - if ( !ConvertPythonToGameObject(pyob, &ob, false, "scene.addObject(object, other, time): KX_Scene (first argument)") || - !ConvertPythonToGameObject(pyother, &other, false, "scene.addObject(object, other, time): KX_Scene (second argument)") ) + if ( !ConvertPythonToGameObject(pyob, &ob, false, "scene.addObject(object, reference, time): KX_Scene (first argument)") || + !ConvertPythonToGameObject(pyreference, &reference, false, "scene.addObject(object, reference, time): KX_Scene (second argument)") ) return NULL; if (!m_inactivelist->SearchValue(ob)) { - PyErr_Format(PyExc_ValueError, "scene.addObject(object, other, time): KX_Scene (first argument): object must be in an inactive layer"); + PyErr_Format(PyExc_ValueError, "scene.addObject(object, reference, time): KX_Scene (first argument): object must be in an inactive layer"); return NULL; } - SCA_IObject* replica = AddReplicaObject((SCA_IObject*)ob, other, time); + SCA_IObject* replica = AddReplicaObject((SCA_IObject*)ob, reference, time); // release here because AddReplicaObject AddRef's // the object is added to the scene so we don't want python to own a reference |