diff options
author | Benoit Bolsee <benoit.bolsee@online.be> | 2016-05-18 23:56:31 +0300 |
---|---|---|
committer | Benoit Bolsee <benoit.bolsee@online.be> | 2016-05-18 23:56:31 +0300 |
commit | ad2bf24a19084360babf3db09197499bfaaa251a (patch) | |
tree | 386d190767db8c0c66fa09917b926d72ab675d1a /source/blender | |
parent | b8435f48f02e3fdd69bfba68c2a890bd3340b085 (diff) | |
parent | 6988061bf1258fe583ea2d95bc26572477709868 (diff) |
Merge remote-tracking branch 'origin/master' into decklink
Conflicts:
intern/atomic/atomic_ops.h
Diffstat (limited to 'source/blender')
403 files changed, 15484 insertions, 7311 deletions
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index e565ffe3cc1..1f38d64924c 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -153,7 +153,7 @@ void BLF_disable(int fontid, int option); * the other argument are the rgba color. * Take care that shadow need to be enable using BLF_enable!!! */ -void BLF_shadow(int fontid, int level, float r, float g, float b, float a); +void BLF_shadow(int fontid, int level, const float rgba[4]) ATTR_NONNULL(3); /* Set the offset for shadow text, this is the current cursor * position plus this offset, don't need call BLF_position before @@ -174,7 +174,7 @@ void BLF_shadow_offset(int fontid, int x, int y); void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, int nch, struct ColorManagedDisplay *display); /* Set the color to be used for text. */ -void BLF_buffer_col(int fontid, float r, float g, float b, float a); +void BLF_buffer_col(int fontid, const float rgba[4]) ATTR_NONNULL(2); /* Draw the string into the buffer, this function draw in both buffer, float and unsigned char _BUT_ * it's not necessary set both buffer, NULL is valid here. diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 977fa771014..132a0ec3808 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -849,16 +849,13 @@ void BLF_wordwrap(int fontid, int wrap_width) } } -void BLF_shadow(int fontid, int level, float r, float g, float b, float a) +void BLF_shadow(int fontid, int level, const float rgba[4]) { FontBLF *font = blf_get(fontid); if (font) { font->shadow = level; - font->shadow_col[0] = r; - font->shadow_col[1] = g; - font->shadow_col[2] = b; - font->shadow_col[3] = a; + copy_v4_v4(font->shadow_col, rgba); } } @@ -886,12 +883,12 @@ void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, int } } -void BLF_buffer_col(int fontid, float r, float g, float b, float a) +void BLF_buffer_col(int fontid, const float rgba[4]) { FontBLF *font = blf_get(fontid); if (font) { - ARRAY_SET_ITEMS(font->buf_info.col_init, r, g, b, a); + copy_v4_v4(font->buf_info.col_init, rgba); } } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 7c6bef57aa4..dfebaecb96e 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -411,7 +411,7 @@ static void blf_font_draw_buffer_ex( cbuf[0] = (unsigned char)((b_col_char[0] * a) + (cbuf[0] * (1.0f - a))); cbuf[1] = (unsigned char)((b_col_char[1] * a) + (cbuf[1] * (1.0f - a))); cbuf[2] = (unsigned char)((b_col_char[2] * a) + (cbuf[2] * (1.0f - a))); - cbuf[3] = (unsigned char)((alphatest = ((int)cbuf[3] + (int)(a * 255)) < 255) ? + cbuf[3] = (unsigned char)(((alphatest = ((int)cbuf[3] + (int)(a * 255))) < 255) ? alphatest : 255); } } diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index d7d6daa7e2a..2b13a847e14 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -71,6 +71,7 @@ * as it is and stick with using BMesh and CDDM. */ +#include "DNA_defs.h" #include "DNA_customdata_types.h" #include "DNA_meshdata_types.h" @@ -180,7 +181,7 @@ struct DerivedMesh { int numVertData, numEdgeData, numTessFaceData, numLoopData, numPolyData; int needsFree; /* checked on ->release, is set to 0 for cached results */ int deformedOnly; /* set by modifier stack if only deformed from original */ - BVHCache bvhCache; + BVHCache *bvhCache; struct GPUDrawObject *drawObject; DerivedMeshType type; float auto_bump_scale; @@ -200,6 +201,8 @@ struct DerivedMesh { /* use for converting to BMesh which doesn't store bevel weight and edge crease by default */ char cd_flag; + char tangent_mask; /* which tangent layers are calculated */ + /** Calculate vert and face normals */ void (*calcNormals)(DerivedMesh *dm); @@ -210,7 +213,9 @@ struct DerivedMesh { void (*calcLoopNormalsSpaceArray)(DerivedMesh *dm, const bool use_split_normals, const float split_angle, struct MLoopNorSpaceArray *r_lnors_spacearr); - void (*calcLoopTangents)(DerivedMesh *dm); + void (*calcLoopTangents)( + DerivedMesh *dm, bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], int tangent_names_count); /** Recalculates mesh tessellation */ void (*recalcTessellation)(DerivedMesh *dm); @@ -763,7 +768,7 @@ typedef struct DMVertexAttribs { struct { float (*array)[4]; int em_offset, gl_index; - } tang; + } tang[MAX_MTFACE]; struct { float (*array)[3]; @@ -779,7 +784,20 @@ void DM_vertex_attributes_from_gpu( void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, int loop); -void DM_calc_loop_tangents(DerivedMesh *dm); +void DM_calc_tangents_names_from_gpu( + const struct GPUVertexAttribs *gattribs, + char (*tangent_names)[MAX_NAME], int *tangent_names_count); +void DM_add_named_tangent_layer_for_uv( + CustomData *uv_data, CustomData *tan_data, int numLoopData, + const char *layer_name); +void DM_calc_loop_tangents_step_0( + const CustomData *loopData, bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], int tangent_names_count, + bool *rcalc_act, bool *rcalc_ren, int *ract_uv_n, int *rren_uv_n, + char *ract_uv_name, char *rren_uv_name, char *rtangent_mask); +void DM_calc_loop_tangents( + DerivedMesh *dm, bool calc_active_tangent, const char (*tangent_names)[MAX_NAME], + int tangent_names_count); void DM_calc_auto_bump_scale(DerivedMesh *dm); /** Set object's bounding box based on DerivedMesh min/max data */ diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index 3fceef5d95d..cb282b46bec 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -79,12 +79,15 @@ typedef enum eAction_TransformFlags { ACT_TRANS_ROT = (1 << 1), /* scaling */ ACT_TRANS_SCALE = (1 << 2), - + + /* bbone shape - for all the parameters, provided one is set */ + ACT_TRANS_BBONE = (1 << 3), + /* strictly not a transform, but custom properties are also * quite often used in modern rigs */ - ACT_TRANS_PROP = (1 << 3), - + ACT_TRANS_PROP = (1 << 4), + /* all flags */ ACT_TRANS_ONLY = (ACT_TRANS_LOC | ACT_TRANS_ROT | ACT_TRANS_SCALE), ACT_TRANS_ALL = (ACT_TRANS_ONLY | ACT_TRANS_PROP) @@ -144,6 +147,8 @@ void BKE_pose_channels_remove( struct Object *ob, bool (*filter_fn)(const char *bone_name, void *user_data), void *user_data); +void BKE_pose_free_data_ex(struct bPose *pose, bool do_id_user); +void BKE_pose_free_data(struct bPose *pose); void BKE_pose_free(struct bPose *pose); void BKE_pose_free_ex(struct bPose *pose, bool do_id_user); void BKE_pose_copy_data(struct bPose **dst, struct bPose *src, const bool copy_constraints); diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 6d00110e318..cc082c084ce 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -135,6 +135,7 @@ typedef struct Mat4 { float mat[4][4]; } Mat4; +void equalize_bbone_bezier(float *data, int desired); void b_bone_spline_setup(struct bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BBONE_SUBDIV]); /* like EBONE_VISIBLE */ diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index fdb34743f36..8ce85c8e615 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -38,88 +38,20 @@ extern "C" { #endif -/* these lines are grep'd, watch out for our not-so-awesome regex - * and keep comment above the defines. - * Use STRINGIFY() rather than defining with quotes */ -#define BLENDER_VERSION 277 -#define BLENDER_SUBVERSION 0 -/* Several breakages with 270, e.g. constraint deg vs rad */ -#define BLENDER_MINVERSION 270 -#define BLENDER_MINSUBVERSION 6 +void BKE_blender_free(void); -/* used by packaging tools */ -/* can be left blank, otherwise a,b,c... etc with no quotes */ -#define BLENDER_VERSION_CHAR -/* alpha/beta/rc/release, docs use this */ -#define BLENDER_VERSION_CYCLE alpha +void BKE_blender_globals_init(void); +void BKE_blender_globals_clear(void); -extern char versionstr[]; /* from blender.c */ - -struct MemFile; -struct bContext; -struct ReportList; -struct Scene; -struct Main; -struct ID; - -int BKE_read_file(struct bContext *C, const char *filepath, struct ReportList *reports); - -#define BKE_READ_FILE_FAIL 0 /* no load */ -#define BKE_READ_FILE_OK 1 /* OK */ -#define BKE_READ_FILE_OK_USERPREFS 2 /* OK, and with new user settings */ - -bool BKE_read_file_from_memory( - struct bContext *C, const void *filebuf, - int filelength, struct ReportList *reports, bool update_defaults); -bool BKE_read_file_from_memfile( - struct bContext *C, struct MemFile *memfile, - struct ReportList *reports); - -int BKE_read_file_userdef(const char *filepath, struct ReportList *reports); -int BKE_write_file_userdef(const char *filepath, struct ReportList *reports); - -void free_blender(void); -void initglobals(void); - -/* load new userdef from file, exit blender */ -void BKE_userdef_free(void); -/* handle changes in userdef */ -void BKE_userdef_state(void); +void BKE_blender_userdef_free(void); +void BKE_blender_userdef_refresh(void); /* set this callback when a UI is running */ void BKE_blender_callback_test_break_set(void (*func)(void)); -int blender_test_break(void); - -#define BKE_UNDO_STR_MAX 64 - -/* global undo */ -extern void BKE_undo_write(struct bContext *C, const char *name); -extern void BKE_undo_step(struct bContext *C, int step); -extern void BKE_undo_name(struct bContext *C, const char *name); -extern bool BKE_undo_is_valid(const char *name); -extern void BKE_undo_reset(void); -extern void BKE_undo_number(struct bContext *C, int nr); -extern const char *BKE_undo_get_name(int nr, bool *r_active); -extern bool BKE_undo_save_file(const char *filename); -extern struct Main *BKE_undo_get_main(struct Scene **r_scene); - -/* partial blend file writing */ -void BKE_blendfile_write_partial_tag_ID(struct ID *id, bool set); -void BKE_blendfile_write_partial_begin(struct Main *bmain_src); -bool BKE_blendfile_write_partial( - struct Main *bmain_src, const char *filepath, const int write_flags, struct ReportList *reports); -void BKE_blendfile_write_partial_end(struct Main *bmain_src); - - -/* copybuffer (wrapper for BKE_blendfile_write_partial) */ -void BKE_copybuffer_begin(struct Main *bmain_src); -void BKE_copybuffer_tag_ID(struct ID *id); -bool BKE_copybuffer_save(struct Main *bmain_src, const char *filename, struct ReportList *reports); -bool BKE_copybuffer_paste(struct bContext *C, const char *libname, const short flag, struct ReportList *reports); +int BKE_blender_test_break(void); #ifdef __cplusplus } #endif -#endif - +#endif /* __BKE_BLENDER_H__ */ diff --git a/source/blender/blenkernel/BKE_blender_copybuffer.h b/source/blender/blenkernel/BKE_blender_copybuffer.h new file mode 100644 index 00000000000..8aaf295c33a --- /dev/null +++ b/source/blender/blenkernel/BKE_blender_copybuffer.h @@ -0,0 +1,46 @@ +/* + * ***** 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 ***** + */ +#ifndef __BKE_BLENDER_COPYBUFFER_H__ +#define __BKE_BLENDER_COPYBUFFER_H__ + +/** \file BKE_blender_copybuffer.h + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bContext; +struct ReportList; +struct Main; +struct ID; + +/* copybuffer (wrapper for BKE_blendfile_write_partial) */ +void BKE_copybuffer_begin(struct Main *bmain_src); +void BKE_copybuffer_tag_ID(struct ID *id); +bool BKE_copybuffer_save(struct Main *bmain_src, const char *filename, struct ReportList *reports); +bool BKE_copybuffer_paste(struct bContext *C, const char *libname, const short flag, struct ReportList *reports); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_BLENDER_COPYBUFFER_H__ */ diff --git a/source/blender/blenkernel/BKE_blender_undo.h b/source/blender/blenkernel/BKE_blender_undo.h new file mode 100644 index 00000000000..cd18bd8db40 --- /dev/null +++ b/source/blender/blenkernel/BKE_blender_undo.h @@ -0,0 +1,52 @@ +/* + * ***** 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 ***** + */ +#ifndef __BKE_BLENDER_UNDO_H__ +#define __BKE_BLENDER_UNDO_H__ + +/** \file BKE_blender_undo.h + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bContext; +struct Scene; +struct Main; + +#define BKE_UNDO_STR_MAX 64 + +/* global undo */ +extern void BKE_undo_write(struct bContext *C, const char *name); +extern void BKE_undo_step(struct bContext *C, int step); +extern void BKE_undo_name(struct bContext *C, const char *name); +extern bool BKE_undo_is_valid(const char *name); +extern void BKE_undo_reset(void); +extern void BKE_undo_number(struct bContext *C, int nr); +extern const char *BKE_undo_get_name(int nr, bool *r_active); +extern bool BKE_undo_save_file(const char *filename); +extern struct Main *BKE_undo_get_main(struct Scene **r_scene); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_BLENDER_UNDO_H__ */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h new file mode 100644 index 00000000000..618b36c5851 --- /dev/null +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -0,0 +1,44 @@ +/* + * ***** 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 ***** + */ +#ifndef __BKE_BLENDER_VERSION_H__ +#define __BKE_BLENDER_VERSION_H__ + +/** \file BKE_blender_version.h + * \ingroup bke + */ + +/* these lines are grep'd, watch out for our not-so-awesome regex + * and keep comment above the defines. + * Use STRINGIFY() rather than defining with quotes */ +#define BLENDER_VERSION 277 +#define BLENDER_SUBVERSION 1 +/* Several breakages with 270, e.g. constraint deg vs rad */ +#define BLENDER_MINVERSION 270 +#define BLENDER_MINSUBVERSION 6 + +/* used by packaging tools */ +/* can be left blank, otherwise a,b,c... etc with no quotes */ +#define BLENDER_VERSION_CHAR +/* alpha/beta/rc/release, docs use this */ +#define BLENDER_VERSION_CYCLE alpha + +extern char versionstr[]; /* from blender.c */ + +#endif /* __BKE_BLENDER_VERSION_H__ */ diff --git a/source/blender/blenkernel/BKE_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h new file mode 100644 index 00000000000..6767fce3abd --- /dev/null +++ b/source/blender/blenkernel/BKE_blendfile.h @@ -0,0 +1,67 @@ +/* + * ***** 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 ***** + */ +#ifndef __BKE_BLENDFILE_H__ +#define __BKE_BLENDFILE_H__ + +/** \file BKE_blendfile.h + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bContext; +struct ID; +struct Main; +struct MemFile; +struct ReportList; + +int BKE_blendfile_read(struct bContext *C, const char *filepath, struct ReportList *reports); + +enum { + BKE_BLENDFILE_READ_FAIL = 0, /* no load */ + BKE_BLENDFILE_READ_OK = 1, /* OK */ + BKE_BLENDFILE_READ_OK_USERPREFS = 2, /* OK, and with new user settings */ +}; + +bool BKE_blendfile_read_from_memory( + struct bContext *C, const void *filebuf, + int filelength, struct ReportList *reports, bool update_defaults); +bool BKE_blendfile_read_from_memfile( + struct bContext *C, struct MemFile *memfile, + struct ReportList *reports); + +int BKE_blendfile_read_userdef(const char *filepath, struct ReportList *reports); +int BKE_blendfile_write_userdef(const char *filepath, struct ReportList *reports); + + +/* partial blend file writing */ +void BKE_blendfile_write_partial_tag_ID(struct ID *id, bool set); +void BKE_blendfile_write_partial_begin(struct Main *bmain_src); +bool BKE_blendfile_write_partial( + struct Main *bmain_src, const char *filepath, const int write_flags, struct ReportList *reports); +void BKE_blendfile_write_partial_end(struct Main *bmain_src); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_BLENDFILE_H__ */ diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 08c0fcc0f3c..7bd3ca88076 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -39,10 +39,30 @@ */ struct DerivedMesh; +struct BMEditMesh; struct MVert; struct MFace; /** +* struct that kepts basic information about a BVHTree build from a editmesh +*/ +typedef struct BVHTreeFromEditMesh { + struct BVHTree *tree; + + /* default callbacks to bvh nearest and raycast */ + BVHTree_NearestPointCallback nearest_callback; + BVHTree_RayCastCallback raycast_callback; + BVHTree_NearestToRayCallback nearest_to_ray_callback; + + /* radius for raycast */ + float sphere_radius; + + /* Private data */ + struct BMEditMesh *em; + +} BVHTreeFromEditMesh; + +/** * struct that kepts basic information about a BVHTree build from a mesh */ typedef struct BVHTreeFromMesh { @@ -69,8 +89,6 @@ typedef struct BVHTreeFromMesh { float sphere_radius; /* Private data */ - void *em_evil; - bool em_evil_all; /* ignore selection/hidden state, adding all loops to the tree */ bool cached; } BVHTreeFromMesh; @@ -85,11 +103,19 @@ typedef struct BVHTreeFromMesh { * * free_bvhtree_from_mesh should be called when the tree is no longer needed. */ +BVHTree *bvhtree_from_editmesh_verts( + BVHTreeFromEditMesh *data, struct BMEditMesh *em, + float epsilon, int tree_type, int axis); +BVHTree *bvhtree_from_editmesh_verts_ex( + BVHTreeFromEditMesh *data, struct BMEditMesh *em, + const BLI_bitmap *mask, int verts_num_active, + float epsilon, int tree_type, int axis); + BVHTree *bvhtree_from_mesh_verts( struct BVHTreeFromMesh *data, struct DerivedMesh *mesh, float epsilon, int tree_type, int axis); BVHTree *bvhtree_from_mesh_verts_ex( struct BVHTreeFromMesh *data, struct MVert *vert, const int numVerts, - const bool vert_allocated, BLI_bitmap *mask, int numVerts_active, + const bool vert_allocated, const BLI_bitmap *mask, int verts_num_active, float epsilon, int tree_type, int axis); BVHTree *bvhtree_from_mesh_edges( @@ -103,7 +129,15 @@ BVHTree *bvhtree_from_mesh_faces_ex( struct BVHTreeFromMesh *data, struct MVert *vert, const bool vert_allocated, struct MFace *face, const int numFaces, const bool face_allocated, - BLI_bitmap *mask, int numFaces_active, + const BLI_bitmap *mask, int numFaces_active, + float epsilon, int tree_type, int axis); + +BVHTree *bvhtree_from_editmesh_looptri( + BVHTreeFromEditMesh *data, struct BMEditMesh *em, float epsilon, + int tree_type, int axis); +BVHTree *bvhtree_from_editmesh_looptri_ex( + BVHTreeFromEditMesh *data, struct BMEditMesh *em, + const BLI_bitmap *mask, int looptri_num_active, float epsilon, int tree_type, int axis); BVHTree *bvhtree_from_mesh_looptri( @@ -113,12 +147,13 @@ BVHTree *bvhtree_from_mesh_looptri_ex( const struct MVert *vert, const bool vert_allocated, const struct MLoop *mloop, const bool loop_allocated, const struct MLoopTri *looptri, const int looptri_num, const bool looptri_allocated, - BLI_bitmap *mask, int looptri_num_active, + const BLI_bitmap *mask, int looptri_num_active, float epsilon, int tree_type, int axis); /** * Frees data allocated by a call to bvhtree_from_mesh_*. */ +void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data); void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data); /** @@ -144,36 +179,14 @@ enum { BVHTREE_FROM_EDGES = 1, BVHTREE_FROM_FACES = 2, BVHTREE_FROM_LOOPTRI = 3, - /* all faces */ - BVHTREE_FROM_FACES_EDITMESH_ALL = 4, - /* visible unselected, only used for transform snapping */ - BVHTREE_FROM_FACES_EDITMESH_SNAP = 5, - // BVHTREE_FROM_EDGES_EDITMESH_SNAP = 6, - BVHTREE_FROM_VERTS_EDITMESH_SNAP = 7, }; -typedef struct LinkNode *BVHCache; +typedef struct LinkNode BVHCache; - -/** - * Queries a bvhcache for the cache bvhtree of the request type - */ BVHTree *bvhcache_find(BVHCache *cache, int type); - -/** - * Inserts a BVHTree of the given type under the cache - * After that the caller no longer needs to worry when to free the BVHTree - * as that will be done when the cache is freed. - * - * A call to this assumes that there was no previous cached tree of the given type - */ -void bvhcache_insert(BVHCache *cache, BVHTree *tree, int type); - -/** - * inits and frees a bvhcache - */ -void bvhcache_init(BVHCache *cache); -void bvhcache_free(BVHCache *cache); +bool bvhcache_has_tree(const BVHCache *cache, const BVHTree *tree); +void bvhcache_insert(BVHCache **cache_p, BVHTree *tree, int type); +void bvhcache_init(BVHCache **cache_p); +void bvhcache_free(BVHCache **cache_p); #endif - diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index 28de270bbd1..50fcbd8e139 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -115,7 +115,7 @@ typedef struct ClothVertex { float mass; /* mass / weight of the vertex */ float goal; /* goal, from SB */ float impulse[3]; /* used in collision.c */ - float *xrest; /* temporary valid for building springs */ + float xrest[3]; /* rest position of the vertex */ unsigned int impulse_count; /* same as above */ float avg_spring_len; /* average length of connected springs */ float struct_stiff; diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 17ad51a7a16..2cdda34b9b5 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -244,7 +244,7 @@ void CustomData_free_elem(struct CustomData *data, int index, int count); */ void CustomData_interp( const struct CustomData *source, struct CustomData *dest, - int *src_indices, float *weights, float *sub_weights, + const int *src_indices, const float *weights, const float *sub_weights, int count, int dest_index); void CustomData_bmesh_interp_n( struct CustomData *data, const void **src_blocks, const float *weights, @@ -358,7 +358,7 @@ void CustomData_file_write_prepare( struct CustomDataLayer **r_write_layers, struct CustomDataLayer *write_layers_buff, size_t write_layers_size); /* query info over types */ -void CustomData_file_write_info(int type, const char **structname, int *structnum); +void CustomData_file_write_info(int type, const char **r_struct_name, int *r_struct_num); int CustomData_sizeof(int type); /* get the name of a layer type */ diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 664338214bf..2022d11d508 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -93,7 +93,11 @@ void bezt_add_to_cfra_elem(ListBase *lb, struct BezTriple *bezt); void fcurve_free_driver(struct FCurve *fcu); struct ChannelDriver *fcurve_copy_driver(struct ChannelDriver *driver); -void driver_free_variable(struct ChannelDriver *driver, struct DriverVar *dvar); +void driver_variables_copy(struct ListBase *dst_list, const struct ListBase *src_list); + +void driver_free_variable(struct ListBase *variables, struct DriverVar *dvar); +void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dvar); + void driver_change_variable_type(struct DriverVar *dvar, int type); void driver_variable_name_validate(struct DriverVar *dvar); struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver); diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 894ccae0dc8..eb98268c9f0 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -61,6 +61,8 @@ void BKE_image_free_buffers(struct Image *image); /* call from library */ void BKE_image_free(struct Image *image); +void BKE_image_init(struct Image *image); + typedef void (StampCallback)(void *data, const char *propname, char *propvalue, int len); void BKE_render_result_stamp_info(struct Scene *scene, struct Object *camera, struct RenderResult *rr, bool allocate_only); diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 21585a160dd..2215fbfbd7d 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -119,7 +119,8 @@ void BKE_main_lib_objects_recalc_all(struct Main *bmain); /* (MAX_ID_NAME - 2) + 3 */ void BKE_id_ui_prefix(char name[66 + 1], const struct ID *id); -void BKE_library_make_local(struct Main *bmain, struct Library *lib, bool untagged_only, bool set_fake); +void BKE_library_make_local( + struct Main *bmain, const struct Library *lib, const bool untagged_only, const bool set_fake); typedef void (*BKE_library_free_window_manager_cb)(struct bContext *, struct wmWindowManager *); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); diff --git a/source/blender/blenkernel/BKE_mask.h b/source/blender/blenkernel/BKE_mask.h index 2f85db4d5d2..25893d54dca 100644 --- a/source/blender/blenkernel/BKE_mask.h +++ b/source/blender/blenkernel/BKE_mask.h @@ -61,8 +61,10 @@ typedef enum { MASK_HANDLE_MODE_INDIVIDUAL_HANDLES = 2, } eMaskhandleMode; -struct MaskSplinePoint *BKE_mask_spline_point_array(struct MaskSpline *spline); -struct MaskSplinePoint *BKE_mask_spline_point_array_from_point(struct MaskSpline *spline, struct MaskSplinePoint *point_ref); +struct MaskSplinePoint *BKE_mask_spline_point_array( + struct MaskSpline *spline); +struct MaskSplinePoint *BKE_mask_spline_point_array_from_point( + struct MaskSpline *spline, const struct MaskSplinePoint *point_ref); /* mask layers */ struct MaskLayer *BKE_mask_layer_new(struct Mask *mask, const char *name); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index a8f20a4ebc5..c7d5857b873 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -69,7 +69,7 @@ extern "C" { /* *** mesh.c *** */ -struct BMesh *BKE_mesh_to_bmesh(struct Mesh *me, struct Object *ob); +struct BMesh *BKE_mesh_to_bmesh(struct Mesh *me, struct Object *ob, const bool add_key_index); int poly_find_loop_from_vert( const struct MPoly *poly, @@ -293,8 +293,9 @@ void BKE_mesh_loops_to_mface_corners( void BKE_mesh_loops_to_tessdata( struct CustomData *fdata, struct CustomData *ldata, struct CustomData *pdata, struct MFace *mface, int *polyindices, unsigned int (*loopindices)[4], const int num_faces); -void BKE_mesh_tangent_loops_to_tessdata(struct CustomData *fdata, struct CustomData *ldata, struct MFace *mface, - int *polyindices, unsigned int (*loopindices)[4], const int num_faces); +void BKE_mesh_tangent_loops_to_tessdata( + struct CustomData *fdata, struct CustomData *ldata, struct MFace *mface, + int *polyindices, unsigned int (*loopindices)[4], const int num_faces, const char *layer_name); int BKE_mesh_recalc_tessellation( struct CustomData *fdata, struct CustomData *ldata, struct CustomData *pdata, struct MVert *mvert, diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 455912ab819..f6c08909d23 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -101,7 +101,8 @@ typedef enum { eModifierTypeFlag_NoUserAdd = (1 << 8), /* For modifiers that use CD_PREVIEW_MCOL for preview. */ - eModifierTypeFlag_UsesPreview = (1 << 9) + eModifierTypeFlag_UsesPreview = (1 << 9), + eModifierTypeFlag_AcceptsLattice = (1 << 10), } ModifierTypeFlag; /* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2ece0f7a028..76e49566d19 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -642,12 +642,12 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree); * Examples: * * \code{.c} - * FOREACH_NODETREE(bmain, nodetree) { + * FOREACH_NODETREE(bmain, nodetree, id) { * if (id == nodetree) * printf("This is a linkable node tree"); * } FOREACH_NODETREE_END * - * FOREACH_NODETREE(bmain, nodetree) { + * FOREACH_NODETREE(bmain, nodetree, id) { * if (nodetree->idname == "ShaderNodeTree") * printf("This is a shader node tree); * if (GS(id) == ID_MA) diff --git a/source/blender/blenkernel/BKE_sketch.h b/source/blender/blenkernel/BKE_sketch.h index c4cfcba4b35..9b9c125fbe6 100644 --- a/source/blender/blenkernel/BKE_sketch.h +++ b/source/blender/blenkernel/BKE_sketch.h @@ -82,7 +82,6 @@ typedef struct SK_Intersection { typedef struct SK_Sketch { ListBase strokes; - ListBase depth_peels; SK_Stroke *active_stroke; SK_Stroke *gesture; SK_Point next_point; diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h index b351bc6fe3e..278b303a315 100644 --- a/source/blender/blenkernel/BKE_unit.h +++ b/source/blender/blenkernel/BKE_unit.h @@ -54,11 +54,11 @@ bool bUnit_IsValid(int system, int type); /* loop over scales, coudl add names later */ //double bUnit_Iter(void **unit, char **name, int system, int type); -void bUnit_GetSystem(void **usys_pt, int *len, int system, int type); -int bUnit_GetBaseUnit(void *usys_pt); -const char *bUnit_GetName(void *usys_pt, int index); -const char *bUnit_GetNameDisplay(void *usys_pt, int index); -double bUnit_GetScaler(void *usys_pt, int index); +void bUnit_GetSystem(int system, int type, void const **r_usys_pt, int *r_len); +int bUnit_GetBaseUnit(const void *usys_pt); +const char *bUnit_GetName(const void *usys_pt, int index); +const char *bUnit_GetNameDisplay(const void *usys_pt, int index); +double bUnit_GetScaler(const void *usys_pt, int index); /* aligned with PropertyUnit */ enum { diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 7311f330962..afab0ccc5a5 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -75,6 +75,9 @@ set(SRC intern/armature_update.c intern/autoexec.c intern/blender.c + intern/blender_copybuffer.c + intern/blender_undo.c + intern/blendfile.c intern/bmfont.c intern/boids.c intern/bpath.c @@ -194,6 +197,10 @@ set(SRC BKE_armature.h BKE_autoexec.h BKE_blender.h + BKE_blender_copybuffer.h + BKE_blender_undo.h + BKE_blender_version.h + BKE_blendfile.h BKE_bmfont.h BKE_bmfont_types.h BKE_boids.h @@ -414,7 +421,7 @@ if(WITH_PYTHON) endif() - if (PYTHON_EXECUTABLE) + if(PYTHON_EXECUTABLE) get_filename_component(_python_exe_name ${PYTHON_EXECUTABLE} NAME) add_definitions(-DPYTHON_EXECUTABLE_NAME=${_python_exe_name}) unset(_python_exe_name) diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index d120678c005..1bfc3d90730 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -49,6 +49,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_linklist.h" +#include "BLI_task.h" #include "BKE_cdderivedmesh.h" #include "BKE_editmesh.h" @@ -595,50 +596,49 @@ void DM_generate_tangent_tessface_data(DerivedMesh *dm, bool generate) int mf_idx; int *polyindex = CustomData_get_layer(fdata, CD_ORIGINDEX); - unsigned int (*loopindex)[4]; + unsigned int (*loopindex)[4] = NULL; /* Should never occure, but better abort than segfault! */ if (!polyindex) return; if (generate) { - for (int i = 0; i < ldata->totlayer; i++) { - if (ldata->layers[i].type == CD_TANGENT) { - CustomData_add_layer_named(fdata, CD_TANGENT, CD_CALLOC, NULL, totface, ldata->layers[i].name); - } - } - CustomData_bmesh_update_active_layers(fdata, pdata, ldata); - } - - BLI_assert(CustomData_from_bmeshpoly_test(fdata, pdata, ldata, true)); - - loopindex = MEM_mallocN(sizeof(*loopindex) * totface, __func__); - - for (mf_idx = 0, mf = mface; mf_idx < totface; mf_idx++, mf++) { - const int mf_len = mf->v4 ? 4 : 3; - unsigned int *ml_idx = loopindex[mf_idx]; - int i, not_done; + for (int j = 0; j < ldata->totlayer; j++) { + if (ldata->layers[j].type == CD_TANGENT) { + CustomData_add_layer_named(fdata, CD_TANGENT, CD_CALLOC, NULL, totface, ldata->layers[j].name); + CustomData_bmesh_update_active_layers(fdata, pdata, ldata); + + if (!loopindex) { + loopindex = MEM_mallocN(sizeof(*loopindex) * totface, __func__); + for (mf_idx = 0, mf = mface; mf_idx < totface; mf_idx++, mf++) { + const int mf_len = mf->v4 ? 4 : 3; + unsigned int *ml_idx = loopindex[mf_idx]; + + /* Find out loop indices. */ + /* NOTE: This assumes tessface are valid and in sync with loop/poly... Else, most likely, segfault! */ + for (int i = mp[polyindex[mf_idx]].loopstart, not_done = mf_len; not_done; i++) { + const int tf_v = BKE_MESH_TESSFACE_VINDEX_ORDER(mf, ml[i].v); + if (tf_v != -1) { + ml_idx[tf_v] = i; + not_done--; + } + } + } + } - /* Find out loop indices. */ - /* NOTE: This assumes tessface are valid and in sync with loop/poly... Else, most likely, segfault! */ - for (i = mp[polyindex[mf_idx]].loopstart, not_done = mf_len; not_done; i++) { - const int tf_v = BKE_MESH_TESSFACE_VINDEX_ORDER(mf, ml[i].v); - if (tf_v != -1) { - ml_idx[tf_v] = i; - not_done--; + /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: + * Here, our tfaces' fourth vertex index is never 0 for a quad. However, we know our fourth loop index may be + * 0 for quads (because our quads may have been rotated compared to their org poly, see tessellation code). + * So we pass the MFace's, and BKE_mesh_loops_to_tessdata will use MFace->v4 index as quad test. + */ + BKE_mesh_tangent_loops_to_tessdata(fdata, ldata, mface, polyindex, loopindex, totface, ldata->layers[j].name); } } + if (loopindex) + MEM_freeN(loopindex); + BLI_assert(CustomData_from_bmeshpoly_test(fdata, pdata, ldata, true)); } - /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: - * Here, our tfaces' fourth vertex index is never 0 for a quad. However, we know our fourth loop index may be - * 0 for quads (because our quads may have been rotated compared to their org poly, see tessellation code). - * So we pass the MFace's, and BKE_mesh_loops_to_tessdata will use MFace->v4 index as quad test. - */ - BKE_mesh_tangent_loops_to_tessdata(fdata, ldata, mface, polyindex, loopindex, totface); - - MEM_freeN(loopindex); - if (G.debug & G_DEBUG) printf("%s: Updated tessellated tangents of dm %p\n", __func__, dm); } @@ -647,13 +647,15 @@ void DM_generate_tangent_tessface_data(DerivedMesh *dm, bool generate) void DM_update_materials(DerivedMesh *dm, Object *ob) { int i, totmat = ob->totcol + 1; /* materials start from 1, default material is 0 */ - dm->totmat = totmat; - /* invalidate old materials */ - if (dm->mat) - MEM_freeN(dm->mat); + if (dm->totmat != totmat) { + dm->totmat = totmat; + /* invalidate old materials */ + if (dm->mat) + MEM_freeN(dm->mat); - dm->mat = MEM_callocN(totmat * sizeof(*dm->mat), "DerivedMesh.mat"); + dm->mat = MEM_mallocN(totmat * sizeof(*dm->mat), "DerivedMesh.mat"); + } /* we leave last material as empty - rationale here is being able to index * the materials by using the mf->mat_nr directly and leaving the last @@ -661,6 +663,7 @@ void DM_update_materials(DerivedMesh *dm, Object *ob) for (i = 0; i < totmat - 1; i++) { dm->mat[i] = give_current_material(ob, i + 1); } + dm->mat[i] = NULL; } MLoopUV *DM_paint_uvlayer_active_get(DerivedMesh *dm, int mat_nr) @@ -3207,96 +3210,28 @@ finally: pRes[3] = fSign; } -void DM_calc_loop_tangents(DerivedMesh *dm) +void DM_calc_tangents_names_from_gpu( + const GPUVertexAttribs *gattribs, + char (*tangent_names)[MAX_NAME], int *r_tangent_names_count) { - /* mesh vars */ - const MLoopTri *looptri; - MVert *mvert; - MLoopUV *mloopuv; - MPoly *mpoly; - MLoop *mloop; - float (*orco)[3] = NULL, (*tangent)[4]; - int /* totvert, */ totface; - float (*fnors)[3]; - float (*tlnors)[3]; - - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) != -1) - return; - - fnors = dm->getPolyDataArray(dm, CD_NORMAL); - /* Note, we assume we do have tessellated loop normals at this point (in case it is object-enabled), - * have to check this is valid... - */ - tlnors = dm->getLoopDataArray(dm, CD_NORMAL); - - /* check we have all the needed layers */ - /* totvert = dm->getNumVerts(dm); */ /* UNUSED */ - looptri = dm->getLoopTriArray(dm); - totface = dm->getNumLoopTri(dm); - - mvert = dm->getVertArray(dm); - mpoly = dm->getPolyArray(dm); - mloop = dm->getLoopArray(dm); - mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); - - if (!mloopuv) { - orco = dm->getVertDataArray(dm, CD_ORCO); - if (!orco) - return; - } - - /* create tangent layer */ - DM_add_loop_layer(dm, CD_TANGENT, CD_CALLOC, NULL); - tangent = DM_get_loop_data_layer(dm, CD_TANGENT); - -#ifdef USE_LOOPTRI_DETECT_QUADS - int num_face_as_quad_map; - int *face_as_quad_map = NULL; - - /* map faces to quads */ - if (totface != dm->getNumPolys(dm)) { - /* over alloc, since we dont know how many ngon or quads we have */ - - /* map fake face index to looptri */ - face_as_quad_map = MEM_mallocN(sizeof(int) * totface, __func__); - int i, j; - for (i = 0, j = 0; j < totface; i++, j++) { - face_as_quad_map[i] = j; - /* step over all quads */ - if (mpoly[looptri[j].poly].totloop == 4) { - j++; /* skips the nest looptri */ - } + int count = 0; + for (int b = 0; b < gattribs->totlayer; b++) { + if (gattribs->layer[b].type == CD_TANGENT) { + strcpy(tangent_names[count++], gattribs->layer[b].name); } - num_face_as_quad_map = i; - } - else { - num_face_as_quad_map = totface; } -#endif + *r_tangent_names_count = count; +} +static void DM_calc_loop_tangents_thread(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + struct SGLSLMeshToTangent *mesh2tangent = taskdata; /* new computation method */ { - SGLSLMeshToTangent mesh2tangent = {NULL}; SMikkTSpaceContext sContext = {NULL}; SMikkTSpaceInterface sInterface = {NULL}; - mesh2tangent.precomputedFaceNormals = fnors; - mesh2tangent.precomputedLoopNormals = tlnors; - mesh2tangent.looptri = looptri; - mesh2tangent.mloopuv = mloopuv; - mesh2tangent.mpoly = mpoly; - mesh2tangent.mloop = mloop; - mesh2tangent.mvert = mvert; - mesh2tangent.orco = orco; - mesh2tangent.tangent = tangent; - mesh2tangent.numTessFaces = totface; - -#ifdef USE_LOOPTRI_DETECT_QUADS - mesh2tangent.face_as_quad_map = face_as_quad_map; - mesh2tangent.num_face_as_quad_map = num_face_as_quad_map; -#endif - - sContext.m_pUserData = &mesh2tangent; + sContext.m_pUserData = mesh2tangent; sContext.m_pInterface = &sInterface; sInterface.m_getNumFaces = dm_ts_GetNumFaces; sInterface.m_getNumVerticesOfFace = dm_ts_GetNumVertsOfFace; @@ -3307,13 +3242,211 @@ void DM_calc_loop_tangents(DerivedMesh *dm) /* 0 if failed */ genTangSpaceDefault(&sContext); + } +} + +void DM_add_named_tangent_layer_for_uv( + CustomData *uv_data, CustomData *tan_data, int numLoopData, + const char *layer_name) +{ + if (CustomData_get_named_layer_index(tan_data, CD_TANGENT, layer_name) == -1 && + CustomData_get_named_layer_index(uv_data, CD_MLOOPUV, layer_name) != -1) + { + CustomData_add_layer_named( + tan_data, CD_TANGENT, CD_CALLOC, NULL, + numLoopData, layer_name); + } +} + +/** + * Here we get some useful information such as active uv layer name and search if it is already in tangent_names. + * Also, we calculate tangent_mask that works as a descriptor of tangents state. + * If tangent_mask has changed, then recalculate tangents. + */ +void DM_calc_loop_tangents_step_0( + const CustomData *loopData, bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], int tangent_names_count, + bool *rcalc_act, bool *rcalc_ren, int *ract_uv_n, int *rren_uv_n, + char *ract_uv_name, char *rren_uv_name, char *rtangent_mask) { + /* Active uv in viewport */ + *ract_uv_n = CustomData_get_active_layer(loopData, CD_MLOOPUV); + ract_uv_name[0] = 0; + if (*ract_uv_n != -1) { + strcpy(ract_uv_name, loopData->layers[*ract_uv_n].name); + } + + /* Active tangent in render */ + *rren_uv_n = CustomData_get_render_layer(loopData, CD_MLOOPUV); + rren_uv_name[0] = 0; + if (*rren_uv_n != -1) { + strcpy(rren_uv_name, loopData->layers[*rren_uv_n].name); + } + + /* If active tangent not in tangent_names we take it into account */ + *rcalc_act = false; + *rcalc_ren = false; + for (int i = 0; i < tangent_names_count; i++) { + if (tangent_names[i][0] == 0) { + calc_active_tangent = true; + } + } + if (calc_active_tangent) { + *rcalc_act = true; + *rcalc_ren = true; + for (int i = 0; i < tangent_names_count; i++) { + if (STREQ(ract_uv_name, tangent_names[i])) + *rcalc_act = false; + if (STREQ(rren_uv_name, tangent_names[i])) + *rcalc_ren = false; + } + } + *rtangent_mask = 0; + + const int uv_layer_num = CustomData_number_of_layers(loopData, CD_MLOOPUV); + for (int n = 0; n < uv_layer_num; n++) { + const char *name = CustomData_get_layer_name(loopData, CD_MLOOPUV, n); + bool add = false; + for (int i = 0; i < tangent_names_count; i++) { + if (tangent_names[i][0] && STREQ(tangent_names[i], name)) { + add = true; + break; + } + } + if ((*rcalc_act && ract_uv_name[0] && STREQ(ract_uv_name, name)) || + (*rcalc_ren && rren_uv_name[0] && STREQ(rren_uv_name, name))) + { + add = true; + } + if (add) + *rtangent_mask |= 1 << n; + } +} + +void DM_calc_loop_tangents( + DerivedMesh *dm, bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], int tangent_names_count) +{ + if (CustomData_number_of_layers(&dm->loopData, CD_MLOOPUV) == 0) + return; + int act_uv_n = -1; + int ren_uv_n = -1; + bool calc_act = false; + bool calc_ren = false; + char act_uv_name[MAX_NAME]; + char ren_uv_name[MAX_NAME]; + char tangent_mask = 0; + DM_calc_loop_tangents_step_0( + &dm->loopData, calc_active_tangent, tangent_names, tangent_names_count, + &calc_act, &calc_ren, &act_uv_n, &ren_uv_n, act_uv_name, ren_uv_name, &tangent_mask); + if ((dm->tangent_mask | tangent_mask) != dm->tangent_mask) { + /* Check we have all the needed layers */ + MPoly *mpoly = dm->getPolyArray(dm); + const MLoopTri *looptri = dm->getLoopTriArray(dm); + int totface = dm->getNumLoopTri(dm); + /* Allocate needed tangent layers */ + for (int i = 0; i < tangent_names_count; i++) + if (tangent_names[i][0]) + DM_add_named_tangent_layer_for_uv(&dm->loopData, &dm->loopData, dm->numLoopData, tangent_names[i]); + if (calc_act && act_uv_name[0]) + DM_add_named_tangent_layer_for_uv(&dm->loopData, &dm->loopData, dm->numLoopData, act_uv_name); + if (calc_ren && ren_uv_name[0]) + DM_add_named_tangent_layer_for_uv(&dm->loopData, &dm->loopData, dm->numLoopData, ren_uv_name); + +#ifdef USE_LOOPTRI_DETECT_QUADS + int num_face_as_quad_map; + int *face_as_quad_map = NULL; + + /* map faces to quads */ + if (totface != dm->getNumPolys(dm)) { + /* over alloc, since we dont know how many ngon or quads we have */ + + /* map fake face index to looptri */ + face_as_quad_map = MEM_mallocN(sizeof(int) * totface, __func__); + int k, j; + for (k = 0, j = 0; j < totface; k++, j++) { + face_as_quad_map[k] = j; + /* step over all quads */ + if (mpoly[looptri[j].poly].totloop == 4) { + j++; /* skips the nest looptri */ + } + } + num_face_as_quad_map = k; + } + else { + num_face_as_quad_map = totface; + } +#endif + + /* Calculation */ + { + TaskScheduler *scheduler = BLI_task_scheduler_get(); + TaskPool *task_pool; + task_pool = BLI_task_pool_create(scheduler, NULL); + + dm->tangent_mask = 0; + /* Calculate tangent layers */ + SGLSLMeshToTangent data_array[MAX_MTFACE]; + const int tangent_layer_num = CustomData_number_of_layers(&dm->loopData, CD_TANGENT); + for (int n = 0; n < tangent_layer_num; n++) { + int index = CustomData_get_layer_index_n(&dm->loopData, CD_TANGENT, n); + BLI_assert(n < MAX_MTFACE); + SGLSLMeshToTangent *mesh2tangent = &data_array[n]; + mesh2tangent->numTessFaces = totface; +#ifdef USE_LOOPTRI_DETECT_QUADS + mesh2tangent->face_as_quad_map = face_as_quad_map; + mesh2tangent->num_face_as_quad_map = num_face_as_quad_map; +#endif + mesh2tangent->mvert = dm->getVertArray(dm); + mesh2tangent->mpoly = dm->getPolyArray(dm); + mesh2tangent->mloop = dm->getLoopArray(dm); + mesh2tangent->looptri = dm->getLoopTriArray(dm); + /* Note, we assume we do have tessellated loop normals at this point (in case it is object-enabled), + * have to check this is valid... + */ + mesh2tangent->precomputedLoopNormals = dm->getLoopDataArray(dm, CD_NORMAL); + mesh2tangent->precomputedFaceNormals = CustomData_get_layer(&dm->faceData, CD_NORMAL); + + mesh2tangent->orco = NULL; + mesh2tangent->mloopuv = CustomData_get_layer_named(&dm->loopData, CD_MLOOPUV, dm->loopData.layers[index].name); + if (!mesh2tangent->mloopuv) { + mesh2tangent->orco = dm->getVertDataArray(dm, CD_ORCO); + if (!mesh2tangent->orco) + continue; + } + mesh2tangent->tangent = dm->loopData.layers[index].data; + + /* Fill the resulting tangent_mask */ + int uv_ind = CustomData_get_named_layer_index(&dm->loopData, CD_MLOOPUV, dm->loopData.layers[index].name); + int uv_start = CustomData_get_layer_index(&dm->loopData, CD_MLOOPUV); + BLI_assert(uv_ind != -1 && uv_start != -1); + BLI_assert(uv_ind - uv_start < MAX_MTFACE); + dm->tangent_mask |= 1 << (uv_ind - uv_start); + BLI_task_pool_push(task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, TASK_PRIORITY_LOW); + } + BLI_assert(dm->tangent_mask == tangent_mask); + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + } #ifdef USE_LOOPTRI_DETECT_QUADS if (face_as_quad_map) { MEM_freeN(face_as_quad_map); } #undef USE_LOOPTRI_DETECT_QUADS + #endif + + int uv_index, tan_index; + + /* Update active layer index */ + uv_index = CustomData_get_layer_index_n(&dm->loopData, CD_MLOOPUV, act_uv_n); + tan_index = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, dm->loopData.layers[uv_index].name); + CustomData_set_layer_active_index(&dm->loopData, CD_TANGENT, tan_index); + + /* Update render layer index */ + uv_index = CustomData_get_layer_index_n(&dm->loopData, CD_MLOOPUV, ren_uv_n); + tan_index = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, dm->loopData.layers[uv_index].name); + CustomData_set_layer_render_index(&dm->loopData, CD_TANGENT, tan_index); } } @@ -3487,15 +3620,13 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, if (dm->auto_bump_scale <= 0.0f) DM_calc_auto_bump_scale(dm); - /* add a tangent layer if necessary */ - for (b = 0; b < gattribs->totlayer; b++) { - if (gattribs->layer[b].type == CD_TANGENT) { - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { - dm->calcLoopTangents(dm); - } - break; - } - } + char tangent_names[MAX_MTFACE][MAX_NAME]; + int tangent_names_count; + /* Add a tangent layer/layers. */ + DM_calc_tangents_names_from_gpu(gattribs, tangent_names, &tangent_names_count); + + if (tangent_names_count) + dm->calcLoopTangents(dm, false, (const char (*)[MAX_NAME])tangent_names, tangent_names_count); for (b = 0; b < gattribs->totlayer; b++) { if (gattribs->layer[b].type == CD_MTFACE) { @@ -3541,20 +3672,24 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, } else if (gattribs->layer[b].type == CD_TANGENT) { /* note, even with 'is_editmesh' this uses the derived-meshes loop data */ - layer = CustomData_get_layer_index(&dm->loopData, CD_TANGENT); - attribs->tottang = 1; + if (gattribs->layer[b].name[0]) + layer = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, gattribs->layer[b].name); + else + layer = CustomData_get_active_layer_index(&dm->loopData, CD_TANGENT); + + a = attribs->tottang++; if (layer != -1) { - attribs->tang.array = dm->loopData.layers[layer].data; - attribs->tang.em_offset = dm->loopData.layers[layer].offset; + attribs->tang[a].array = dm->loopData.layers[layer].data; + attribs->tang[a].em_offset = dm->loopData.layers[layer].offset; } else { - attribs->tang.array = NULL; - attribs->tang.em_offset = -1; + attribs->tang[a].array = NULL; + attribs->tang[a].em_offset = -1; } - attribs->tang.gl_index = gattribs->layer[b].glindex; + attribs->tang[a].gl_index = gattribs->layer[b].glindex; } else if (gattribs->layer[b].type == CD_ORCO) { /* original coordinates */ @@ -3636,10 +3771,12 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, } /* tangent for normal mapping */ - if (attribs->tottang) { - /*const*/ float (*array)[4] = attribs->tang.array; - const float *tang = (array) ? array[loop] : zero; - glVertexAttrib4fv(attribs->tang.gl_index, tang); + for (b = 0; b < attribs->tottang; b++) { + if (attribs->tang[b].array) { + /*const*/ float (*array)[4] = attribs->tang[b].array; + const float *tang = (array) ? array[a * 4 + vert] : zero; + glVertexAttrib4fv(attribs->tang[b].gl_index, tang); + } } } @@ -3863,7 +4000,7 @@ void DM_init_origspace(DerivedMesh *dm) float p_nor[3], co[3]; float mat[3][3]; - float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {FLT_MIN, FLT_MIN}; + float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {-FLT_MAX, -FLT_MAX}; float translate[2], scale[2]; BKE_mesh_calc_poly_normal(mp, l, mv, p_nor); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 3e1cf6a74a4..3a31e4c36cf 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -493,6 +493,8 @@ bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name) unit_axis_angle(chan->rotAxis, &chan->rotAngle); chan->size[0] = chan->size[1] = chan->size[2] = 1.0f; + chan->scaleIn = chan->scaleOut = 1.0f; + chan->limitmin[0] = chan->limitmin[1] = chan->limitmin[2] = -180.0f; chan->limitmax[0] = chan->limitmax[1] = chan->limitmax[2] = 180.0f; chan->stiffness[0] = chan->stiffness[1] = chan->stiffness[2] = 0.0f; @@ -824,26 +826,35 @@ void BKE_pose_channels_free(bPose *pose) BKE_pose_channels_free_ex(pose, true); } +void BKE_pose_free_data_ex(bPose *pose, bool do_id_user) +{ + /* free pose-channels */ + BKE_pose_channels_free_ex(pose, do_id_user); + + /* free pose-groups */ + if (pose->agroups.first) + BLI_freelistN(&pose->agroups); + + /* free IK solver state */ + BIK_clear_data(pose); + + /* free IK solver param */ + if (pose->ikparam) + MEM_freeN(pose->ikparam); +} + +void BKE_pose_free_data(bPose *pose) +{ + BKE_pose_free_data_ex(pose, true); +} + /** * Removes and deallocates all data from a pose, and also frees the pose. */ void BKE_pose_free_ex(bPose *pose, bool do_id_user) { if (pose) { - /* free pose-channels */ - BKE_pose_channels_free_ex(pose, do_id_user); - - /* free pose-groups */ - if (pose->agroups.first) - BLI_freelistN(&pose->agroups); - - /* free IK solver state */ - BIK_clear_data(pose); - - /* free IK solver param */ - if (pose->ikparam) - MEM_freeN(pose->ikparam); - + BKE_pose_free_data_ex(pose, do_id_user); /* free pose */ MEM_freeN(pose); } @@ -1271,6 +1282,18 @@ short action_get_item_transforms(bAction *act, Object *ob, bPoseChannel *pchan, } } + if ((curves) || (flags & ACT_TRANS_BBONE) == 0) { + /* bbone shape properties */ + pPtr = strstr(bPtr, "bbone_"); + if (pPtr) { + flags |= ACT_TRANS_BBONE; + + if (curves) + BLI_addtail(curves, BLI_genericNodeN(fcu)); + continue; + } + } + if ((curves) || (flags & ACT_TRANS_PROP) == 0) { /* custom properties only */ pPtr = strstr(bPtr, "[\""); /* extra '"' comment here to keep my texteditor functionlist working :) */ @@ -1414,6 +1437,18 @@ void what_does_obaction(Object *ob, Object *workob, bPose *pose, bAction *act, c workob->constraints.last = ob->constraints.last; workob->pose = pose; /* need to set pose too, since this is used for both types of Action Constraint */ + if (pose) { + /* This function is most likely to be used with a temporary pose with a single bone in there. + * For such cases it makes no sense to create hash since it'll only waste CPU ticks on memory + * allocation and also will make lookup slower. + */ + if (pose->chanbase.first != pose->chanbase.last) { + BKE_pose_channels_hash_make(pose); + } + if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { + BKE_pose_update_constraint_flags(pose); + } + } BLI_strncpy(workob->parsubstr, ob->parsubstr, sizeof(workob->parsubstr)); BLI_strncpy(workob->id.name, "OB<ConstrWorkOb>", sizeof(workob->id.name)); /* we don't use real object name, otherwise RNA screws with the real thing */ diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index de21d9105e2..b1dcc40279f 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -32,7 +32,7 @@ #include "BLI_fileops.h" #include "BLI_path_util.h" -#include "BKE_blender.h" /* BLENDER_VERSION */ +#include "BKE_blender_version.h" #include "BKE_appdir.h" /* own include */ #include "GHOST_Path-api.h" diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 54fe98940aa..5c95cde4d8d 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -41,6 +41,7 @@ #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_ghash.h" +#include "BLI_task.h" #include "BLI_utildefines.h" #include "DNA_anim_types.h" @@ -48,6 +49,7 @@ #include "DNA_constraint_types.h" #include "DNA_mesh_types.h" #include "DNA_lattice_types.h" +#include "DNA_listBase.h" #include "DNA_meshdata_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -427,7 +429,7 @@ int bone_autoside_name(char name[MAXBONENAME], int UNUSED(strip_number), short a /* ************* B-Bone support ******************* */ /* data has MAX_BBONE_SUBDIV+1 interpolated points, will become desired amount with equal distances */ -static void equalize_bezier(float *data, int desired) +void equalize_bbone_bezier(float *data, int desired) { float *fp, totdist, ddist, dist, fac1, fac2; float pdist[MAX_BBONE_SUBDIV + 1]; @@ -497,13 +499,21 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB hlength1 = bone->ease1 * length * 0.390464f; /* 0.5f * sqrt(2) * kappa, the handle length for near-perfect circles */ hlength2 = bone->ease2 * length * 0.390464f; - /* evaluate next and prev bones */ - if (bone->flag & BONE_CONNECTED) - prev = pchan->parent; - else - prev = NULL; + /* get "next" and "prev" bones - these are used for handle calculations */ + if (pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) { + /* use the provided bones as the next/prev - leave blank to eliminate this effect altogether */ + prev = pchan->bbone_prev; + next = pchan->bbone_next; + } + else { + /* evaluate next and prev bones */ + if (bone->flag & BONE_CONNECTED) + prev = pchan->parent; + else + prev = NULL; - next = pchan->child; + next = pchan->child; + } /* find the handle points, since this is inside bone space, the * first point = (0, 0, 0) @@ -523,10 +533,27 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB float difmat[4][4], result[3][3], imat3[3][3]; /* transform previous point inside this bone space */ - if (rest) - copy_v3_v3(h1, prev->bone->arm_head); - else - copy_v3_v3(h1, prev->pose_head); + if ((pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) && + (pchan->bboneflag & PCHAN_BBONE_CUSTOM_START_REL)) + { + /* Use delta movement (from restpose), and apply this relative to the current bone's head */ + if (rest) { + /* in restpose, arm_head == pose_head */ + h1[0] = h1[1] = h1[2] = 0.0f; + } + else { + float delta[3]; + sub_v3_v3v3(delta, prev->pose_head, prev->bone->arm_head); + sub_v3_v3v3(h1, pchan->pose_head, delta); + } + } + else { + /* Use bone head as absolute position */ + if (rest) + copy_v3_v3(h1, prev->bone->arm_head); + else + copy_v3_v3(h1, prev->pose_head); + } mul_m4_v3(imat, h1); if (prev->bone->segments > 1) { @@ -562,10 +589,27 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB float difmat[4][4], result[3][3], imat3[3][3]; /* transform next point inside this bone space */ - if (rest) - copy_v3_v3(h2, next->bone->arm_tail); - else - copy_v3_v3(h2, next->pose_tail); + if ((pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) && + (pchan->bboneflag & PCHAN_BBONE_CUSTOM_END_REL)) + { + /* Use delta movement (from restpose), and apply this relative to the current bone's tail */ + if (rest) { + /* in restpose, arm_tail == pose_tail */ + h2[0] = h2[1] = h2[2] = 0.0f; + } + else { + float delta[3]; + sub_v3_v3v3(delta, next->pose_tail, next->bone->arm_tail); + add_v3_v3v3(h2, pchan->pose_tail, delta); + } + } + else { + /* Use bone tail as absolute position */ + if (rest) + copy_v3_v3(h2, next->bone->arm_tail); + else + copy_v3_v3(h2, next->pose_tail); + } mul_m4_v3(imat, h2); /* if next bone is B-bone too, use average handle direction */ @@ -599,6 +643,50 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB roll2 = 0.0; } + /* Add effects from bbone properties over the top + * - These properties allow users to hand-animate the + * bone curve/shape, without having to resort to using + * extra bones + * - The "bone" level offsets are for defining the restpose + * shape of the bone (e.g. for curved eyebrows for example). + * -> In the viewport, it's needed to define what the rest pose + * looks like + * -> For "rest == 0", we also still need to have it present + * so that we can "cancel out" this restpose when it comes + * time to deform some geometry, it won't cause double transforms. + * - The "pchan" level offsets are the ones that animators actually + * end up animating + */ + { + /* add extra rolls */ + roll1 += bone->roll1 + (!rest ? pchan->roll1 : 0.0f); + roll2 += bone->roll2 + (!rest ? pchan->roll2 : 0.0f); + + if (bone->flag & BONE_ADD_PARENT_END_ROLL) { + if (prev) { + if (prev->bone) + roll1 += prev->bone->roll2; + + if (!rest) + roll1 += prev->roll2; + } + } + + /* extra curve x / y */ + /* NOTE: Scale correction factors here are to compensate for some random floating-point glitches + * when scaling up the bone or it's parent by a factor of approximately 8.15/6, which results + * in the bone length getting scaled up too (from 1 to 8), causing the curve to flatten out. + */ + const float xscale_correction = (do_scale) ? scale[0] : 1.0f; + const float yscale_correction = (do_scale) ? scale[2] : 1.0f; + + h1[0] += (bone->curveInX + (!rest ? pchan->curveInX : 0.0f)) * xscale_correction; + h1[2] += (bone->curveInY + (!rest ? pchan->curveInY : 0.0f)) * yscale_correction; + + h2[0] += (bone->curveOutX + (!rest ? pchan->curveOutX : 0.0f)) * xscale_correction; + h2[2] += (bone->curveOutY + (!rest ? pchan->curveOutY : 0.0f)) * yscale_correction; + } + /* make curve */ if (bone->segments > MAX_BBONE_SUBDIV) bone->segments = MAX_BBONE_SUBDIV; @@ -608,20 +696,45 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB BKE_curve_forward_diff_bezier(0.0f, h1[2], h2[2], 0.0f, data[0] + 2, MAX_BBONE_SUBDIV, 4 * sizeof(float)); BKE_curve_forward_diff_bezier(roll1, roll1 + 0.390464f * (roll2 - roll1), roll2 - 0.390464f * (roll2 - roll1), roll2, data[0] + 3, MAX_BBONE_SUBDIV, 4 * sizeof(float)); - equalize_bezier(data[0], bone->segments); /* note: does stride 4! */ + equalize_bbone_bezier(data[0], bone->segments); /* note: does stride 4! */ /* make transformation matrices for the segments for drawing */ for (a = 0, fp = data[0]; a < bone->segments; a++, fp += 4) { sub_v3_v3v3(h1, fp + 4, fp); vec_roll_to_mat3(h1, fp[3], mat3); /* fp[3] is roll */ - + copy_m4_m3(result_array[a].mat, mat3); copy_v3_v3(result_array[a].mat[3], fp); - + if (do_scale) { /* correct for scaling when this matrix is used in scaled space */ mul_m4_series(result_array[a].mat, iscalemat, result_array[a].mat, scalemat); } + + /* BBone scale... */ + { + const int num_segments = bone->segments; + + const float scaleIn = bone->scaleIn * (!rest ? pchan->scaleIn : 1.0f); + const float scaleFactorIn = 1.0f + (scaleIn - 1.0f) * ((float)(num_segments - a) / (float)num_segments); + + const float scaleOut = bone->scaleOut * (!rest ? pchan->scaleOut : 1.0f); + const float scaleFactorOut = 1.0f + (scaleOut - 1.0f) * ((float)(a + 1) / (float)num_segments); + + const float scalefac = scaleFactorIn * scaleFactorOut; + float bscalemat[4][4], bscale[3]; + + bscale[0] = scalefac; + bscale[1] = 1.0f; + bscale[2] = scalefac; + + size_to_mat4(bscalemat, bscale); + + /* Note: don't multiply by inverse scale mat here, as it causes problems with scaling shearing and breaking segment chains */ + /*mul_m4_series(result_array[a].mat, ibscalemat, result_array[a].mat, bscalemat);*/ + mul_m4_series(result_array[a].mat, result_array[a].mat, bscalemat); + } + } } @@ -633,7 +746,7 @@ typedef struct bPoseChanDeform { DualQuat *b_bone_dual_quats; } bPoseChanDeform; -static void pchan_b_bone_defmats(bPoseChannel *pchan, bPoseChanDeform *pdef_info, int use_quaternion) +static void pchan_b_bone_defmats(bPoseChannel *pchan, bPoseChanDeform *pdef_info, const bool use_quaternion) { Bone *bone = pchan->bone; Mat4 b_bone[MAX_BBONE_SUBDIV], b_bone_rest[MAX_BBONE_SUBDIV]; @@ -667,7 +780,6 @@ static void pchan_b_bone_defmats(bPoseChannel *pchan, bPoseChanDeform *pdef_info float tmat[4][4]; invert_m4_m4(tmat, b_bone_rest[a].mat); - mul_m4_series(b_bone_mats[a + 1].mat, pchan->chan_mat, bone->arm_mat, b_bone[a].mat, tmat, b_bone_mats[0].mat); if (use_quaternion) @@ -681,10 +793,10 @@ static void b_bone_deform(bPoseChanDeform *pdef_info, Bone *bone, float co[3], D float (*mat)[4] = b_bone[0].mat; float segment, y; int a; - + /* need to transform co back to bonespace, only need y */ y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1]; - + /* now calculate which of the b_bones are deforming this */ segment = bone->length / ((float)bone->segments); a = (int)(y / segment); @@ -854,6 +966,32 @@ static void pchan_bone_deform(bPoseChannel *pchan, bPoseChanDeform *pdef_info, f (*contrib) += weight; } +typedef struct ArmatureBBoneDefmatsData { + bPoseChanDeform *pdef_info_array; + DualQuat *dualquats; + bool use_quaternion; +} ArmatureBBoneDefmatsData; + +static void armature_bbone_defmats_cb(void *userdata, Link *iter, int index) +{ + ArmatureBBoneDefmatsData *data = userdata; + bPoseChannel *pchan = (bPoseChannel *)iter; + + if (!(pchan->bone->flag & BONE_NO_DEFORM)) { + bPoseChanDeform *pdef_info = &data->pdef_info_array[index]; + const bool use_quaternion = data->use_quaternion; + + if (pchan->bone->segments > 1) { + pchan_b_bone_defmats(pchan, pdef_info, use_quaternion); + } + + if (use_quaternion) { + pdef_info->dual_quat = &data->dualquats[index]; + mat4_to_dquat(pdef_info->dual_quat, pchan->bone->arm_mat, pchan->chan_mat); + } + } +} + void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, float (*vertexCos)[3], float (*defMats)[3][3], int numVerts, int deformflag, float (*prevCos)[3], const char *defgrp_name) @@ -867,16 +1005,19 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, float bDeformGroup *dg; DualQuat *dualquats = NULL; float obinv[4][4], premat[4][4], postmat[4][4]; - const short use_envelope = deformflag & ARM_DEF_ENVELOPE; - const short use_quaternion = deformflag & ARM_DEF_QUATERNION; - const short invert_vgroup = deformflag & ARM_DEF_INVERT_VGROUP; + const bool use_envelope = (deformflag & ARM_DEF_ENVELOPE) != 0; + const bool use_quaternion = (deformflag & ARM_DEF_QUATERNION) != 0; + const bool invert_vgroup = (deformflag & ARM_DEF_INVERT_VGROUP) != 0; int defbase_tot = 0; /* safety for vertexgroup index overflow */ int i, target_totvert = 0; /* safety for vertexgroup overflow */ bool use_dverts = false; int armature_def_nr; int totchan; - if (arm->edbo) return; + /* in editmode, or not an armature */ + if (arm->edbo || (armOb->pose == NULL)) { + return; + } invert_m4_m4(obinv, target->obmat); copy_m4_m4(premat, target->obmat); @@ -894,19 +1035,10 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, float pdef_info_array = MEM_callocN(sizeof(bPoseChanDeform) * totchan, "bPoseChanDeform"); - totchan = 0; - pdef_info = pdef_info_array; - for (pchan = armOb->pose->chanbase.first; pchan; pchan = pchan->next, pdef_info++) { - if (!(pchan->bone->flag & BONE_NO_DEFORM)) { - if (pchan->bone->segments > 1) - pchan_b_bone_defmats(pchan, pdef_info, use_quaternion); - - if (use_quaternion) { - pdef_info->dual_quat = &dualquats[totchan++]; - mat4_to_dquat(pdef_info->dual_quat, pchan->bone->arm_mat, pchan->chan_mat); - } - } - } + ArmatureBBoneDefmatsData data = { + .pdef_info_array = pdef_info_array, .dualquats = dualquats, .use_quaternion = use_quaternion + }; + BLI_task_parallel_listbase(&armOb->pose->chanbase, &data, armature_bbone_defmats_cb, totchan > 512); /* get the def_nr for the overall armature vertex group if present */ armature_def_nr = defgroup_name_index(target, defgrp_name); diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index ceda9f056bb..826bb12a912 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -619,7 +619,9 @@ void BKE_pose_eval_bone(EvaluationContext *UNUSED(eval_ctx), else { /* TODO(sergey): Use time source node for time. */ float ctime = BKE_scene_frame_get(scene); /* not accurate... */ - BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + if ((pchan->flag & POSE_DONE) == 0) { + BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + } } } } @@ -631,12 +633,18 @@ void BKE_pose_constraints_evaluate(EvaluationContext *UNUSED(eval_ctx), { Scene *scene = G.main->scene.first; DEBUG_PRINT("%s on %s pchan %s\n", __func__, ob->id.name, pchan->name); - if (pchan->flag & POSE_IKTREE || pchan->flag & POSE_IKSPLINE) { + bArmature *arm = (bArmature *)ob->data; + if (arm->flag & ARM_RESTPOS) { + return; + } + else if (pchan->flag & POSE_IKTREE || pchan->flag & POSE_IKSPLINE) { /* IK are being solved separately/ */ } else { float ctime = BKE_scene_frame_get(scene); /* not accurate... */ - BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + if ((pchan->flag & POSE_DONE) == 0) { + BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + } } } diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index baf93ffd824..15492fbd20d 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -27,89 +27,55 @@ /** \file blender/blenkernel/intern/blender.c * \ingroup bke + * + * Application level startup/shutdown functionality. */ -#ifndef _GNU_SOURCE -/* Needed for O_NOFOLLOW on some platforms. */ -# define _GNU_SOURCE 1 -#endif - -#ifndef _WIN32 -# include <unistd.h> // for read close -#else -# include <io.h> // for open close read -#endif - #include <stdlib.h> #include <stdio.h> -#include <stddef.h> #include <string.h> -#include <fcntl.h> /* for open */ -#include <errno.h> #include "MEM_guardedalloc.h" -#include "DNA_userdef_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_view3d_types.h" -#include "DNA_windowmanager_types.h" - -#include "BLI_blenlib.h" +#include "BLI_string.h" +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "BLI_callbacks.h" #include "IMB_imbuf.h" #include "IMB_moviecache.h" -#include "BKE_appdir.h" -#include "BKE_blender.h" -#include "BKE_bpath.h" +#include "BKE_blender.h" /* own include */ +#include "BKE_blender_version.h" /* own include */ +#include "BKE_blendfile.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_image.h" -#include "BKE_ipo.h" #include "BKE_library.h" -#include "BKE_main.h" #include "BKE_node.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_sequencer.h" -#include "BKE_sound.h" #include "RE_pipeline.h" #include "RE_render_ext.h" #include "BLF_api.h" -#include "BLO_undofile.h" -#include "BLO_readfile.h" -#include "BLO_writefile.h" - -#include "RNA_access.h" - -#include "WM_api.h" // XXXXX BAD, very BAD dependency (bad level call) - remove asap, elubie - -#include "IMB_colormanagement.h" - -#ifdef WITH_PYTHON -# include "BPY_extern.h" -#endif Global G; UserDef U; -/* ListBase = {NULL, NULL}; */ char versionstr[48] = ""; /* ********** free ********** */ /* only to be called on exit blender */ -void free_blender(void) +void BKE_blender_free(void) { /* samples are in a global list..., also sets G.main->sound->sample NULL */ BKE_main_free(G.main); @@ -132,7 +98,7 @@ void free_blender(void) free_nodesystem(); } -void initglobals(void) +void BKE_blender_globals_init(void) { memset(&G, 0, sizeof(Global)); @@ -154,300 +120,14 @@ void initglobals(void) #endif } -/***/ - -static void clear_global(void) +void BKE_blender_globals_clear(void) { -// extern short winqueue_break; /* screen.c */ - BKE_main_free(G.main); /* free all lib data */ - -// free_vertexpaint(); G.main = NULL; } -static bool clean_paths_visit_cb(void *UNUSED(userdata), char *path_dst, const char *path_src) -{ - strcpy(path_dst, path_src); - BLI_path_native_slash(path_dst); - return !STREQ(path_dst, path_src); -} - -/* make sure path names are correct for OS */ -static void clean_paths(Main *main) -{ - Scene *scene; - - BKE_bpath_traverse_main(main, clean_paths_visit_cb, BKE_BPATH_TRAVERSE_SKIP_MULTIFILE, NULL); - - for (scene = main->scene.first; scene; scene = scene->id.next) { - BLI_path_native_slash(scene->r.pic); - } -} - -static bool wm_scene_is_visible(wmWindowManager *wm, Scene *scene) -{ - wmWindow *win; - for (win = wm->windows.first; win; win = win->next) { - if (win->screen->scene == scene) { - return true; - } - } - return false; -} - -/** - * Context matching, handle no-ui case - * - * \note this is called on Undo so any slow conversion functions here - * should be avoided or check (mode != LOAD_UNDO). - * - * \param bfd: Blend file data, freed by this function on exit. - * \param filepath: File path or identifier. - */ -static void setup_app_data( - bContext *C, BlendFileData *bfd, - const char *filepath, ReportList *reports) -{ - Scene *curscene = NULL; - const bool recover = (G.fileflags & G_FILE_RECOVER) != 0; - enum { - LOAD_UI = 1, - LOAD_UI_OFF, - LOAD_UNDO, - } mode; - - if (BLI_listbase_is_empty(&bfd->main->screen)) { - mode = LOAD_UNDO; - } - else if (G.fileflags & G_FILE_NO_UI) { - mode = LOAD_UI_OFF; - } - else { - mode = LOAD_UI; - } - - if (mode != LOAD_UNDO) { - /* may happen with library files */ - if (ELEM(NULL, bfd->curscreen, bfd->curscene)) { - BKE_report(reports, RPT_WARNING, "Library file, loading empty scene"); - mode = LOAD_UI_OFF; - } - } - - /* Free all render results, without this stale data gets displayed after loading files */ - if (mode != LOAD_UNDO) { - RE_FreeAllRenderResults(); - } - - /* Only make filepaths compatible when loading for real (not undo) */ - if (mode != LOAD_UNDO) { - clean_paths(bfd->main); - } - - /* XXX here the complex windowmanager matching */ - - /* no load screens? */ - if (mode != LOAD_UI) { - /* Logic for 'track_undo_scene' is to keep using the scene which the active screen has, - * as long as the scene associated with the undo operation is visible in one of the open windows. - * - * - 'curscreen->scene' - scene the user is currently looking at. - * - 'bfd->curscene' - scene undo-step was created in. - * - * This means users can have 2+ windows open and undo in both without screens switching. - * But if they close one of the screens, - * undo will ensure that the scene being operated on will be activated - * (otherwise we'd be undoing on an off-screen scene which isn't acceptable). - * see: T43424 - */ - bScreen *curscreen = NULL; - bool track_undo_scene; - - /* comes from readfile.c */ - SWAP(ListBase, G.main->wm, bfd->main->wm); - SWAP(ListBase, G.main->screen, bfd->main->screen); - - /* we re-use current screen */ - curscreen = CTX_wm_screen(C); - /* but use new Scene pointer */ - curscene = bfd->curscene; - - track_undo_scene = (mode == LOAD_UNDO && curscreen && curscene && bfd->main->wm.first); - - if (curscene == NULL) { - curscene = bfd->main->scene.first; - } - /* empty file, we add a scene to make Blender work */ - if (curscene == NULL) { - curscene = BKE_scene_add(bfd->main, "Empty"); - } - - if (track_undo_scene) { - /* keep the old (free'd) scene, let 'blo_lib_link_screen_restore' - * replace it with 'curscene' if its needed */ - } - else { - /* and we enforce curscene to be in current screen */ - if (curscreen) { - /* can run in bgmode */ - curscreen->scene = curscene; - } - } - - /* clear_global will free G.main, here we can still restore pointers */ - blo_lib_link_screen_restore(bfd->main, curscreen, curscene); - /* curscreen might not be set when loading without ui (see T44217) so only re-assign if available */ - if (curscreen) { - curscene = curscreen->scene; - } - - if (track_undo_scene) { - wmWindowManager *wm = bfd->main->wm.first; - if (wm_scene_is_visible(wm, bfd->curscene) == false) { - curscene = bfd->curscene; - curscreen->scene = curscene; - BKE_screen_view3d_scene_sync(curscreen); - } - } - } - - /* free G.main Main database */ -// CTX_wm_manager_set(C, NULL); - clear_global(); - - /* clear old property update cache, in case some old references are left dangling */ - RNA_property_update_cache_free(); - - G.main = bfd->main; - - CTX_data_main_set(C, G.main); - - if (bfd->user) { - - /* only here free userdef themes... */ - BKE_userdef_free(); - - U = *bfd->user; - - /* Security issue: any blend file could include a USER block. - * - * Currently we load prefs from BLENDER_STARTUP_FILE and later on load BLENDER_USERPREF_FILE, - * to load the preferences defined in the users home dir. - * - * This means we will never accidentally (or maliciously) - * enable scripts auto-execution by loading a '.blend' file. - */ - U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE; - - MEM_freeN(bfd->user); - } - - /* case G_FILE_NO_UI or no screens in file */ - if (mode != LOAD_UI) { - /* leave entire context further unaltered? */ - CTX_data_scene_set(C, curscene); - } - else { - G.fileflags = bfd->fileflags; - CTX_wm_manager_set(C, G.main->wm.first); - CTX_wm_screen_set(C, bfd->curscreen); - CTX_data_scene_set(C, bfd->curscene); - CTX_wm_area_set(C, NULL); - CTX_wm_region_set(C, NULL); - CTX_wm_menu_set(C, NULL); - curscene = bfd->curscene; - } - - /* this can happen when active scene was lib-linked, and doesn't exist anymore */ - if (CTX_data_scene(C) == NULL) { - /* in case we don't even have a local scene, add one */ - if (!G.main->scene.first) - BKE_scene_add(G.main, "Empty"); - - CTX_data_scene_set(C, G.main->scene.first); - CTX_wm_screen(C)->scene = CTX_data_scene(C); - curscene = CTX_data_scene(C); - } - - BLI_assert(curscene == CTX_data_scene(C)); - - - /* special cases, override loaded flags: */ - if (G.f != bfd->globalf) { - const int flags_keep = (G_SWAP_EXCHANGE | G_SCRIPT_AUTOEXEC | G_SCRIPT_OVERRIDE_PREF); - bfd->globalf = (bfd->globalf & ~flags_keep) | (G.f & flags_keep); - } - - - G.f = bfd->globalf; - -#ifdef WITH_PYTHON - /* let python know about new main */ - BPY_context_update(C); -#endif - - /* FIXME: this version patching should really be part of the file-reading code, - * but we still get too many unrelated data-corruption crashes otherwise... */ - if (G.main->versionfile < 250) - do_versions_ipos_to_animato(G.main); - - G.main->recovered = 0; - - /* startup.blend or recovered startup */ - if (bfd->filename[0] == 0) { - G.main->name[0] = 0; - } - else if (recover && G.relbase_valid) { - /* in case of autosave or quit.blend, use original filename instead - * use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */ - filepath = bfd->filename; - G.main->recovered = 1; - - /* these are the same at times, should never copy to the same location */ - if (G.main->name != filepath) - BLI_strncpy(G.main->name, filepath, FILE_MAX); - } - - /* baseflags, groups, make depsgraph, etc */ - /* first handle case if other windows have different scenes visible */ - if (mode == LOAD_UI) { - wmWindowManager *wm = G.main->wm.first; - - if (wm) { - wmWindow *win; - - for (win = wm->windows.first; win; win = win->next) { - if (win->screen && win->screen->scene) /* zealous check... */ - if (win->screen->scene != curscene) - BKE_scene_set_background(G.main, win->screen->scene); - } - } - } - BKE_scene_set_background(G.main, curscene); - - if (mode != LOAD_UNDO) { - RE_FreeAllPersistentData(); - IMB_colormanagement_check_file_config(G.main); - } - - MEM_freeN(bfd); - -} - -static int handle_subversion_warning(Main *main, ReportList *reports) -{ - if (main->minversionfile > BLENDER_VERSION || - (main->minversionfile == BLENDER_VERSION && - main->minsubversionfile > BLENDER_SUBVERSION)) - { - BKE_reportf(reports, RPT_ERROR, "File written by newer Blender binary (%d.%d), expect loss of data!", - main->minversionfile, main->minsubversionfile); - } - - return 1; -} +/***/ static void keymap_item_free(wmKeyMapItem *kmi) { @@ -459,7 +139,11 @@ static void keymap_item_free(wmKeyMapItem *kmi) MEM_freeN(kmi->ptr); } -void BKE_userdef_free(void) +/** + * When loading a new userdef from file, + * or when exiting Blender. + */ +void BKE_blender_userdef_free(void) { wmKeyMap *km; wmKeyMapItem *kmi; @@ -509,8 +193,10 @@ void BKE_userdef_free(void) BLI_freelistN(&U.user_keymaps); } -/* handle changes in settings that need recalc */ -void BKE_userdef_state(void) +/** + * Handle changes in settings that need refreshing. + */ +void BKE_blender_userdef_refresh(void) { /* prevent accidents */ if (U.pixelsize == 0) U.pixelsize = 1; @@ -520,116 +206,6 @@ void BKE_userdef_state(void) } -int BKE_read_file(bContext *C, const char *filepath, ReportList *reports) -{ - BlendFileData *bfd; - int retval = BKE_READ_FILE_OK; - - if (strstr(filepath, BLENDER_STARTUP_FILE) == NULL) /* don't print user-pref loading */ - printf("read blend: %s\n", filepath); - - bfd = BLO_read_from_file(filepath, reports); - if (bfd) { - if (bfd->user) retval = BKE_READ_FILE_OK_USERPREFS; - - if (0 == handle_subversion_warning(bfd->main, reports)) { - BKE_main_free(bfd->main); - MEM_freeN(bfd); - bfd = NULL; - retval = BKE_READ_FILE_FAIL; - } - else { - setup_app_data(C, bfd, filepath, reports); - } - } - else - BKE_reports_prependf(reports, "Loading '%s' failed: ", filepath); - - return (bfd ? retval : BKE_READ_FILE_FAIL); -} - -bool BKE_read_file_from_memory( - bContext *C, const void *filebuf, int filelength, - ReportList *reports, bool update_defaults) -{ - BlendFileData *bfd; - - bfd = BLO_read_from_memory(filebuf, filelength, reports); - if (bfd) { - if (update_defaults) - BLO_update_defaults_startup_blend(bfd->main); - setup_app_data(C, bfd, "<memory2>", reports); - } - else { - BKE_reports_prepend(reports, "Loading failed: "); - } - - return (bfd != NULL); -} - -/* memfile is the undo buffer */ -bool BKE_read_file_from_memfile( - bContext *C, MemFile *memfile, - ReportList *reports) -{ - BlendFileData *bfd; - - bfd = BLO_read_from_memfile(CTX_data_main(C), G.main->name, memfile, reports); - if (bfd) { - /* remove the unused screens and wm */ - while (bfd->main->wm.first) - BKE_libblock_free_ex(bfd->main, bfd->main->wm.first, true); - while (bfd->main->screen.first) - BKE_libblock_free_ex(bfd->main, bfd->main->screen.first, true); - - setup_app_data(C, bfd, "<memory1>", reports); - } - else { - BKE_reports_prepend(reports, "Loading failed: "); - } - - return (bfd != NULL); -} - -/* only read the userdef from a .blend */ -int BKE_read_file_userdef(const char *filepath, ReportList *reports) -{ - BlendFileData *bfd; - int retval = BKE_READ_FILE_FAIL; - - bfd = BLO_read_from_file(filepath, reports); - if (bfd) { - if (bfd->user) { - retval = BKE_READ_FILE_OK_USERPREFS; - - /* only here free userdef themes... */ - BKE_userdef_free(); - - U = *bfd->user; - MEM_freeN(bfd->user); - } - BKE_main_free(bfd->main); - MEM_freeN(bfd); - } - - return retval; -} - -/* only write the userdef in a .blend */ -int BKE_write_file_userdef(const char *filepath, ReportList *reports) -{ - Main *mainb = MEM_callocN(sizeof(Main), "empty main"); - int retval = 0; - - if (BLO_write_file(mainb, filepath, G_FILE_USERPREFS, reports, NULL)) { - retval = 1; - } - - MEM_freeN(mainb); - - return retval; -} - /* ***************** testing for break ************* */ static void (*blender_test_break_cb)(void) = NULL; @@ -640,7 +216,7 @@ void BKE_blender_callback_test_break_set(void (*func)(void)) } -int blender_test_break(void) +int BKE_blender_test_break(void) { if (!G.background) { if (blender_test_break_cb) @@ -650,514 +226,3 @@ int blender_test_break(void) return (G.is_break == true); } - -/* -------------------------------------------------------------------- */ - -/** \name Global Undo - * \{ */ - -#define UNDO_DISK 0 - -typedef struct UndoElem { - struct UndoElem *next, *prev; - char str[FILE_MAX]; - char name[BKE_UNDO_STR_MAX]; - MemFile memfile; - uintptr_t undosize; -} UndoElem; - -static ListBase undobase = {NULL, NULL}; -static UndoElem *curundo = NULL; - - -static int read_undosave(bContext *C, UndoElem *uel) -{ - char mainstr[sizeof(G.main->name)]; - int success = 0, fileflags; - - /* This is needed so undoing/redoing doesn't crash with threaded previews going */ - WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(C)); - - BLI_strncpy(mainstr, G.main->name, sizeof(mainstr)); /* temporal store */ - - fileflags = G.fileflags; - G.fileflags |= G_FILE_NO_UI; - - if (UNDO_DISK) - success = (BKE_read_file(C, uel->str, NULL) != BKE_READ_FILE_FAIL); - else - success = BKE_read_file_from_memfile(C, &uel->memfile, NULL); - - /* restore */ - BLI_strncpy(G.main->name, mainstr, sizeof(G.main->name)); /* restore */ - G.fileflags = fileflags; - - if (success) { - /* important not to update time here, else non keyed tranforms are lost */ - DAG_on_visible_update(G.main, false); - } - - return success; -} - -/* name can be a dynamic string */ -void BKE_undo_write(bContext *C, const char *name) -{ - uintptr_t maxmem, totmem, memused; - int nr /*, success */ /* UNUSED */; - UndoElem *uel; - - if ((U.uiflag & USER_GLOBALUNDO) == 0) { - return; - } - - if (U.undosteps == 0) { - return; - } - - /* remove all undos after (also when curundo == NULL) */ - while (undobase.last != curundo) { - uel = undobase.last; - BLI_remlink(&undobase, uel); - BLO_memfile_free(&uel->memfile); - MEM_freeN(uel); - } - - /* make new */ - curundo = uel = MEM_callocN(sizeof(UndoElem), "undo file"); - BLI_strncpy(uel->name, name, sizeof(uel->name)); - BLI_addtail(&undobase, uel); - - /* and limit amount to the maximum */ - nr = 0; - uel = undobase.last; - while (uel) { - nr++; - if (nr == U.undosteps) break; - uel = uel->prev; - } - if (uel) { - while (undobase.first != uel) { - UndoElem *first = undobase.first; - BLI_remlink(&undobase, first); - /* the merge is because of compression */ - BLO_memfile_merge(&first->memfile, &first->next->memfile); - MEM_freeN(first); - } - } - - - /* disk save version */ - if (UNDO_DISK) { - static int counter = 0; - char filepath[FILE_MAX]; - char numstr[32]; - int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on undo */ - - /* calculate current filepath */ - counter++; - counter = counter % U.undosteps; - - BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter); - BLI_make_file_string("/", filepath, BKE_tempdir_session(), numstr); - - /* success = */ /* UNUSED */ BLO_write_file(CTX_data_main(C), filepath, fileflags, NULL, NULL); - - BLI_strncpy(curundo->str, filepath, sizeof(curundo->str)); - } - else { - MemFile *prevfile = NULL; - - if (curundo->prev) prevfile = &(curundo->prev->memfile); - - memused = MEM_get_memory_in_use(); - /* success = */ /* UNUSED */ BLO_write_file_mem(CTX_data_main(C), prevfile, &curundo->memfile, G.fileflags); - curundo->undosize = MEM_get_memory_in_use() - memused; - } - - if (U.undomemory != 0) { - /* limit to maximum memory (afterwards, we can't know in advance) */ - totmem = 0; - maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024; - - /* keep at least two (original + other) */ - uel = undobase.last; - while (uel && uel->prev) { - totmem += uel->undosize; - if (totmem > maxmem) break; - uel = uel->prev; - } - - if (uel) { - if (uel->prev && uel->prev->prev) - uel = uel->prev; - - while (undobase.first != uel) { - UndoElem *first = undobase.first; - BLI_remlink(&undobase, first); - /* the merge is because of compression */ - BLO_memfile_merge(&first->memfile, &first->next->memfile); - MEM_freeN(first); - } - } - } -} - -/* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */ -void BKE_undo_step(bContext *C, int step) -{ - - if (step == 0) { - read_undosave(C, curundo); - } - else if (step == 1) { - /* curundo should never be NULL, after restart or load file it should call undo_save */ - if (curundo == NULL || curundo->prev == NULL) { - // XXX error("No undo available"); - } - else { - if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name); - curundo = curundo->prev; - read_undosave(C, curundo); - } - } - else { - /* curundo has to remain current situation! */ - - if (curundo == NULL || curundo->next == NULL) { - // XXX error("No redo available"); - } - else { - read_undosave(C, curundo->next); - curundo = curundo->next; - if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name); - } - } -} - -void BKE_undo_reset(void) -{ - UndoElem *uel; - - uel = undobase.first; - while (uel) { - BLO_memfile_free(&uel->memfile); - uel = uel->next; - } - - BLI_freelistN(&undobase); - curundo = NULL; -} - -/* based on index nr it does a restore */ -void BKE_undo_number(bContext *C, int nr) -{ - curundo = BLI_findlink(&undobase, nr); - BKE_undo_step(C, 0); -} - -/* go back to the last occurance of name in stack */ -void BKE_undo_name(bContext *C, const char *name) -{ - UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name)); - - if (uel && uel->prev) { - curundo = uel->prev; - BKE_undo_step(C, 0); - } -} - -/* name optional */ -bool BKE_undo_is_valid(const char *name) -{ - if (name) { - UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name)); - return uel && uel->prev; - } - - return undobase.last != undobase.first; -} - -/* get name of undo item, return null if no item with this index */ -/* if active pointer, set it to 1 if true */ -const char *BKE_undo_get_name(int nr, bool *r_active) -{ - UndoElem *uel = BLI_findlink(&undobase, nr); - - if (r_active) *r_active = false; - - if (uel) { - if (r_active && (uel == curundo)) { - *r_active = true; - } - return uel->name; - } - return NULL; -} - -/** - * Saves .blend using undo buffer. - * - * \return success. - */ -bool BKE_undo_save_file(const char *filename) -{ - UndoElem *uel; - MemFileChunk *chunk; - int file, oflags; - - if ((U.uiflag & USER_GLOBALUNDO) == 0) { - return false; - } - - uel = curundo; - if (uel == NULL) { - fprintf(stderr, "No undo buffer to save recovery file\n"); - return false; - } - - /* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK, - * however if this is ever executed explicitly by the user, we may want to allow writing to symlinks. - */ - - oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC; -#ifdef O_NOFOLLOW - /* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */ - oflags |= O_NOFOLLOW; -#else - /* TODO(sergey): How to deal with symlinks on windows? */ -# ifndef _MSC_VER -# warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103" -# endif -#endif - file = BLI_open(filename, oflags, 0666); - - if (file == -1) { - fprintf(stderr, "Unable to save '%s': %s\n", - filename, errno ? strerror(errno) : "Unknown error opening file"); - return false; - } - - for (chunk = uel->memfile.chunks.first; chunk; chunk = chunk->next) { - if (write(file, chunk->buf, chunk->size) != chunk->size) { - break; - } - } - - close(file); - - if (chunk) { - fprintf(stderr, "Unable to save '%s': %s\n", - filename, errno ? strerror(errno) : "Unknown error writing file"); - return false; - } - return true; -} - -/* sets curscene */ -Main *BKE_undo_get_main(Scene **r_scene) -{ - Main *mainp = NULL; - BlendFileData *bfd = BLO_read_from_memfile(G.main, G.main->name, &curundo->memfile, NULL); - - if (bfd) { - mainp = bfd->main; - if (r_scene) { - *r_scene = bfd->curscene; - } - - MEM_freeN(bfd); - } - - return mainp; -} - -/** \} */ - - -/* -------------------------------------------------------------------- */ - -/** \name Partial `.blend` file save. - * \{ */ - -void BKE_blendfile_write_partial_begin(Main *bmain_src) -{ - BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false); -} - -void BKE_blendfile_write_partial_tag_ID(ID *id, bool set) -{ - if (set) { - id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT; - } - else { - id->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT); - } -} - -static void blendfile_write_partial_cb(void *UNUSED(handle), Main *UNUSED(bmain), void *vid) -{ - if (vid) { - ID *id = vid; - /* only tag for need-expand if not done, prevents eternal loops */ - if ((id->tag & LIB_TAG_DOIT) == 0) - id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT; - } -} - -/** - * \return Success. - */ -bool BKE_blendfile_write_partial( - Main *bmain_src, const char *filepath, const int write_flags, ReportList *reports) -{ - Main *bmain_dst = MEM_callocN(sizeof(Main), "copybuffer"); - ListBase *lbarray_dst[MAX_LIBARRAY], *lbarray_src[MAX_LIBARRAY]; - int a, retval; - - void *path_list_backup = NULL; - const int path_list_flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE); - - if (write_flags & G_FILE_RELATIVE_REMAP) { - path_list_backup = BKE_bpath_list_backup(bmain_src, path_list_flag); - } - - BLO_main_expander(blendfile_write_partial_cb); - BLO_expand_main(NULL, bmain_src); - - /* move over all tagged blocks */ - set_listbasepointers(bmain_src, lbarray_src); - a = set_listbasepointers(bmain_dst, lbarray_dst); - while (a--) { - ID *id, *nextid; - ListBase *lb_dst = lbarray_dst[a], *lb_src = lbarray_src[a]; - - for (id = lb_src->first; id; id = nextid) { - nextid = id->next; - if (id->tag & LIB_TAG_DOIT) { - BLI_remlink(lb_src, id); - BLI_addtail(lb_dst, id); - } - } - } - - - /* save the buffer */ - retval = BLO_write_file(bmain_dst, filepath, write_flags, reports, NULL); - - /* move back the main, now sorted again */ - set_listbasepointers(bmain_src, lbarray_dst); - a = set_listbasepointers(bmain_dst, lbarray_src); - while (a--) { - ID *id; - ListBase *lb_dst = lbarray_dst[a], *lb_src = lbarray_src[a]; - - while ((id = BLI_pophead(lb_src))) { - BLI_addtail(lb_dst, id); - id_sort_by_name(lb_dst, id); - } - } - - MEM_freeN(bmain_dst); - - if (path_list_backup) { - BKE_bpath_list_restore(bmain_src, path_list_flag, path_list_backup); - BKE_bpath_list_free(path_list_backup); - } - - return retval; -} - -void BKE_blendfile_write_partial_end(Main *bmain_src) -{ - BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false); -} - -/** \} */ - - -/* -------------------------------------------------------------------- */ - -/** \name Copy/Paste `.blend`, partial saves. - * \{ */ - -void BKE_copybuffer_begin(Main *bmain_src) -{ - BKE_blendfile_write_partial_begin(bmain_src); -} - -void BKE_copybuffer_tag_ID(ID *id) -{ - BKE_blendfile_write_partial_tag_ID(id, true); -} - -/** - * \return Success. - */ -bool BKE_copybuffer_save(Main *bmain_src, const char *filename, ReportList *reports) -{ - const int write_flags = G_FILE_RELATIVE_REMAP; - - bool retval = BKE_blendfile_write_partial(bmain_src, filename, write_flags, reports); - - BKE_blendfile_write_partial_end(bmain_src); - - return retval; -} - -/** - * \return Success. - */ -bool BKE_copybuffer_paste(bContext *C, const char *libname, const short flag, ReportList *reports) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - Main *mainl = NULL; - Library *lib; - BlendHandle *bh; - - bh = BLO_blendhandle_from_file(libname, reports); - - if (bh == NULL) { - /* error reports will have been made by BLO_blendhandle_from_file() */ - return false; - } - - BKE_scene_base_deselect_all(scene); - - /* tag everything, all untagged data can be made local - * its also generally useful to know what is new - * - * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); - - /* here appending/linking starts */ - mainl = BLO_library_link_begin(bmain, &bh, libname); - - BLO_library_link_copypaste(mainl, bh); - - BLO_library_link_end(mainl, &bh, flag, scene, v3d); - - /* mark all library linked objects to be updated */ - BKE_main_lib_objects_recalc_all(bmain); - IMB_colormanagement_check_file_config(bmain); - - /* append, rather than linking */ - lib = BLI_findstring(&bmain->library, libname, offsetof(Library, filepath)); - BKE_library_make_local(bmain, lib, true, false); - - /* important we unset, otherwise these object wont - * link into other scenes from this blend file */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); - - /* recreate dependency graph to include new objects */ - DAG_relations_tag_update(bmain); - - BLO_blendhandle_close(bh); - /* remove library... */ - - return true; -} - -/** \} */ diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c new file mode 100644 index 00000000000..ad01fac2144 --- /dev/null +++ b/source/blender/blenkernel/intern/blender_copybuffer.c @@ -0,0 +1,143 @@ +/* + * ***** 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/blenkernel/intern/blender_copybuffer.c + * \ingroup bke + * + * Used for copy/paste operator, (using a temporary file). + */ + +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_userdef_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" +#include "DNA_windowmanager_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_callbacks.h" + +#include "IMB_imbuf.h" +#include "IMB_moviecache.h" + +#include "BKE_blender_copybuffer.h" /* own include */ +#include "BKE_blendfile.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "BLO_readfile.h" +#include "BLO_writefile.h" + +#include "IMB_colormanagement.h" + + +/* -------------------------------------------------------------------- */ + +/** \name Copy/Paste `.blend`, partial saves. + * \{ */ + +void BKE_copybuffer_begin(Main *bmain_src) +{ + BKE_blendfile_write_partial_begin(bmain_src); +} + +void BKE_copybuffer_tag_ID(ID *id) +{ + BKE_blendfile_write_partial_tag_ID(id, true); +} + +/** + * \return Success. + */ +bool BKE_copybuffer_save(Main *bmain_src, const char *filename, ReportList *reports) +{ + const int write_flags = G_FILE_RELATIVE_REMAP; + + bool retval = BKE_blendfile_write_partial(bmain_src, filename, write_flags, reports); + + BKE_blendfile_write_partial_end(bmain_src); + + return retval; +} + +/** + * \return Success. + */ +bool BKE_copybuffer_paste(bContext *C, const char *libname, const short flag, ReportList *reports) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + Main *mainl = NULL; + Library *lib; + BlendHandle *bh; + + bh = BLO_blendhandle_from_file(libname, reports); + + if (bh == NULL) { + /* error reports will have been made by BLO_blendhandle_from_file() */ + return false; + } + + BKE_scene_base_deselect_all(scene); + + /* tag everything, all untagged data can be made local + * its also generally useful to know what is new + * + * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); + + /* here appending/linking starts */ + mainl = BLO_library_link_begin(bmain, &bh, libname); + + BLO_library_link_copypaste(mainl, bh); + + BLO_library_link_end(mainl, &bh, flag, scene, v3d); + + /* mark all library linked objects to be updated */ + BKE_main_lib_objects_recalc_all(bmain); + IMB_colormanagement_check_file_config(bmain); + + /* append, rather than linking */ + lib = BLI_findstring(&bmain->library, libname, offsetof(Library, filepath)); + BKE_library_make_local(bmain, lib, true, false); + + /* important we unset, otherwise these object wont + * link into other scenes from this blend file */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + + /* recreate dependency graph to include new objects */ + DAG_relations_tag_update(bmain); + + BLO_blendhandle_close(bh); + /* remove library... */ + + return true; +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c new file mode 100644 index 00000000000..ca0a1b91cea --- /dev/null +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -0,0 +1,393 @@ +/* + * ***** 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/blenkernel/intern/blender_undo.c + * \ingroup bke + * + * Blend file undo (known as 'Global Undo'). + * DNA level diffing for undo. + */ + +#ifndef _WIN32 +# include <unistd.h> // for read close +#else +# include <io.h> // for open close read +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <string.h> +#include <fcntl.h> /* for open */ +#include <errno.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" + +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "IMB_imbuf.h" +#include "IMB_moviecache.h" + +#include "BKE_blender_undo.h" /* own include */ +#include "BKE_blendfile.h" +#include "BKE_appdir.h" +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_main.h" +#include "RE_pipeline.h" + +#include "BLO_undofile.h" +#include "BLO_readfile.h" +#include "BLO_writefile.h" + +#include "WM_api.h" // XXXXX BAD, very BAD dependency (bad level call) - remove asap, elubie + + +/* -------------------------------------------------------------------- */ + +/** \name Global Undo + * \{ */ + +#define UNDO_DISK 0 + +typedef struct UndoElem { + struct UndoElem *next, *prev; + char str[FILE_MAX]; + char name[BKE_UNDO_STR_MAX]; + MemFile memfile; + uintptr_t undosize; +} UndoElem; + +static ListBase undobase = {NULL, NULL}; +static UndoElem *curundo = NULL; + + +static int read_undosave(bContext *C, UndoElem *uel) +{ + char mainstr[sizeof(G.main->name)]; + int success = 0, fileflags; + + /* This is needed so undoing/redoing doesn't crash with threaded previews going */ + WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(C)); + + BLI_strncpy(mainstr, G.main->name, sizeof(mainstr)); /* temporal store */ + + fileflags = G.fileflags; + G.fileflags |= G_FILE_NO_UI; + + if (UNDO_DISK) + success = (BKE_blendfile_read(C, uel->str, NULL) != BKE_BLENDFILE_READ_FAIL); + else + success = BKE_blendfile_read_from_memfile(C, &uel->memfile, NULL); + + /* restore */ + BLI_strncpy(G.main->name, mainstr, sizeof(G.main->name)); /* restore */ + G.fileflags = fileflags; + + if (success) { + /* important not to update time here, else non keyed tranforms are lost */ + DAG_on_visible_update(G.main, false); + } + + return success; +} + +/* name can be a dynamic string */ +void BKE_undo_write(bContext *C, const char *name) +{ + uintptr_t maxmem, totmem, memused; + int nr /*, success */ /* UNUSED */; + UndoElem *uel; + + if ((U.uiflag & USER_GLOBALUNDO) == 0) { + return; + } + + if (U.undosteps == 0) { + return; + } + + /* remove all undos after (also when curundo == NULL) */ + while (undobase.last != curundo) { + uel = undobase.last; + BLI_remlink(&undobase, uel); + BLO_memfile_free(&uel->memfile); + MEM_freeN(uel); + } + + /* make new */ + curundo = uel = MEM_callocN(sizeof(UndoElem), "undo file"); + BLI_strncpy(uel->name, name, sizeof(uel->name)); + BLI_addtail(&undobase, uel); + + /* and limit amount to the maximum */ + nr = 0; + uel = undobase.last; + while (uel) { + nr++; + if (nr == U.undosteps) break; + uel = uel->prev; + } + if (uel) { + while (undobase.first != uel) { + UndoElem *first = undobase.first; + BLI_remlink(&undobase, first); + /* the merge is because of compression */ + BLO_memfile_merge(&first->memfile, &first->next->memfile); + MEM_freeN(first); + } + } + + + /* disk save version */ + if (UNDO_DISK) { + static int counter = 0; + char filepath[FILE_MAX]; + char numstr[32]; + int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on undo */ + + /* calculate current filepath */ + counter++; + counter = counter % U.undosteps; + + BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter); + BLI_make_file_string("/", filepath, BKE_tempdir_session(), numstr); + + /* success = */ /* UNUSED */ BLO_write_file(CTX_data_main(C), filepath, fileflags, NULL, NULL); + + BLI_strncpy(curundo->str, filepath, sizeof(curundo->str)); + } + else { + MemFile *prevfile = NULL; + + if (curundo->prev) prevfile = &(curundo->prev->memfile); + + memused = MEM_get_memory_in_use(); + /* success = */ /* UNUSED */ BLO_write_file_mem(CTX_data_main(C), prevfile, &curundo->memfile, G.fileflags); + curundo->undosize = MEM_get_memory_in_use() - memused; + } + + if (U.undomemory != 0) { + /* limit to maximum memory (afterwards, we can't know in advance) */ + totmem = 0; + maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024; + + /* keep at least two (original + other) */ + uel = undobase.last; + while (uel && uel->prev) { + totmem += uel->undosize; + if (totmem > maxmem) break; + uel = uel->prev; + } + + if (uel) { + if (uel->prev && uel->prev->prev) + uel = uel->prev; + + while (undobase.first != uel) { + UndoElem *first = undobase.first; + BLI_remlink(&undobase, first); + /* the merge is because of compression */ + BLO_memfile_merge(&first->memfile, &first->next->memfile); + MEM_freeN(first); + } + } + } +} + +/* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */ +void BKE_undo_step(bContext *C, int step) +{ + + if (step == 0) { + read_undosave(C, curundo); + } + else if (step == 1) { + /* curundo should never be NULL, after restart or load file it should call undo_save */ + if (curundo == NULL || curundo->prev == NULL) { + // XXX error("No undo available"); + } + else { + if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name); + curundo = curundo->prev; + read_undosave(C, curundo); + } + } + else { + /* curundo has to remain current situation! */ + + if (curundo == NULL || curundo->next == NULL) { + // XXX error("No redo available"); + } + else { + read_undosave(C, curundo->next); + curundo = curundo->next; + if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name); + } + } +} + +void BKE_undo_reset(void) +{ + UndoElem *uel; + + uel = undobase.first; + while (uel) { + BLO_memfile_free(&uel->memfile); + uel = uel->next; + } + + BLI_freelistN(&undobase); + curundo = NULL; +} + +/* based on index nr it does a restore */ +void BKE_undo_number(bContext *C, int nr) +{ + curundo = BLI_findlink(&undobase, nr); + BKE_undo_step(C, 0); +} + +/* go back to the last occurance of name in stack */ +void BKE_undo_name(bContext *C, const char *name) +{ + UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name)); + + if (uel && uel->prev) { + curundo = uel->prev; + BKE_undo_step(C, 0); + } +} + +/* name optional */ +bool BKE_undo_is_valid(const char *name) +{ + if (name) { + UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name)); + return uel && uel->prev; + } + + return undobase.last != undobase.first; +} + +/* get name of undo item, return null if no item with this index */ +/* if active pointer, set it to 1 if true */ +const char *BKE_undo_get_name(int nr, bool *r_active) +{ + UndoElem *uel = BLI_findlink(&undobase, nr); + + if (r_active) *r_active = false; + + if (uel) { + if (r_active && (uel == curundo)) { + *r_active = true; + } + return uel->name; + } + return NULL; +} + +/** + * Saves .blend using undo buffer. + * + * \return success. + */ +bool BKE_undo_save_file(const char *filename) +{ + UndoElem *uel; + MemFileChunk *chunk; + int file, oflags; + + if ((U.uiflag & USER_GLOBALUNDO) == 0) { + return false; + } + + uel = curundo; + if (uel == NULL) { + fprintf(stderr, "No undo buffer to save recovery file\n"); + return false; + } + + /* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK, + * however if this is ever executed explicitly by the user, we may want to allow writing to symlinks. + */ + + oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC; +#ifdef O_NOFOLLOW + /* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */ + oflags |= O_NOFOLLOW; +#else + /* TODO(sergey): How to deal with symlinks on windows? */ +# ifndef _MSC_VER +# warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103" +# endif +#endif + file = BLI_open(filename, oflags, 0666); + + if (file == -1) { + fprintf(stderr, "Unable to save '%s': %s\n", + filename, errno ? strerror(errno) : "Unknown error opening file"); + return false; + } + + for (chunk = uel->memfile.chunks.first; chunk; chunk = chunk->next) { + if (write(file, chunk->buf, chunk->size) != chunk->size) { + break; + } + } + + close(file); + + if (chunk) { + fprintf(stderr, "Unable to save '%s': %s\n", + filename, errno ? strerror(errno) : "Unknown error writing file"); + return false; + } + return true; +} + +/* sets curscene */ +Main *BKE_undo_get_main(Scene **r_scene) +{ + Main *mainp = NULL; + BlendFileData *bfd = BLO_read_from_memfile(G.main, G.main->name, &curundo->memfile, NULL); + + if (bfd) { + mainp = bfd->main; + if (r_scene) { + *r_scene = bfd->curscene; + } + + MEM_freeN(bfd); + } + + return mainp; +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c new file mode 100644 index 00000000000..bedf262541f --- /dev/null +++ b/source/blender/blenkernel/intern/blendfile.c @@ -0,0 +1,569 @@ +/* + * ***** 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/blenkernel/intern/blendfile.c + * \ingroup bke + * + * High level `.blend` file read/write, + * and functions for writing *partial* files (only selected data-blocks). + */ + +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_path_util.h" +#include "BLI_utildefines.h" + +#include "IMB_colormanagement.h" + +#include "BKE_appdir.h" +#include "BKE_blender.h" +#include "BKE_blender_version.h" +#include "BKE_blendfile.h" +#include "BKE_bpath.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_ipo.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "BLO_readfile.h" +#include "BLO_writefile.h" + +#include "RNA_access.h" + +#include "RE_pipeline.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +/* -------------------------------------------------------------------- */ + +/** \name High Level `.blend` file read/write. + * \{ */ + +static bool clean_paths_visit_cb(void *UNUSED(userdata), char *path_dst, const char *path_src) +{ + strcpy(path_dst, path_src); + BLI_path_native_slash(path_dst); + return !STREQ(path_dst, path_src); +} + +/* make sure path names are correct for OS */ +static void clean_paths(Main *main) +{ + Scene *scene; + + BKE_bpath_traverse_main(main, clean_paths_visit_cb, BKE_BPATH_TRAVERSE_SKIP_MULTIFILE, NULL); + + for (scene = main->scene.first; scene; scene = scene->id.next) { + BLI_path_native_slash(scene->r.pic); + } +} + +static bool wm_scene_is_visible(wmWindowManager *wm, Scene *scene) +{ + wmWindow *win; + for (win = wm->windows.first; win; win = win->next) { + if (win->screen->scene == scene) { + return true; + } + } + return false; +} + +/** + * Context matching, handle no-ui case + * + * \note this is called on Undo so any slow conversion functions here + * should be avoided or check (mode != LOAD_UNDO). + * + * \param bfd: Blend file data, freed by this function on exit. + * \param filepath: File path or identifier. + */ +static void setup_app_data( + bContext *C, BlendFileData *bfd, + const char *filepath, ReportList *reports) +{ + Scene *curscene = NULL; + const bool recover = (G.fileflags & G_FILE_RECOVER) != 0; + enum { + LOAD_UI = 1, + LOAD_UI_OFF, + LOAD_UNDO, + } mode; + + if (BLI_listbase_is_empty(&bfd->main->screen)) { + mode = LOAD_UNDO; + } + else if (G.fileflags & G_FILE_NO_UI) { + mode = LOAD_UI_OFF; + } + else { + mode = LOAD_UI; + } + + if (mode != LOAD_UNDO) { + /* may happen with library files */ + if (ELEM(NULL, bfd->curscreen, bfd->curscene)) { + BKE_report(reports, RPT_WARNING, "Library file, loading empty scene"); + mode = LOAD_UI_OFF; + } + } + + /* Free all render results, without this stale data gets displayed after loading files */ + if (mode != LOAD_UNDO) { + RE_FreeAllRenderResults(); + } + + /* Only make filepaths compatible when loading for real (not undo) */ + if (mode != LOAD_UNDO) { + clean_paths(bfd->main); + } + + /* XXX here the complex windowmanager matching */ + + /* no load screens? */ + if (mode != LOAD_UI) { + /* Logic for 'track_undo_scene' is to keep using the scene which the active screen has, + * as long as the scene associated with the undo operation is visible in one of the open windows. + * + * - 'curscreen->scene' - scene the user is currently looking at. + * - 'bfd->curscene' - scene undo-step was created in. + * + * This means users can have 2+ windows open and undo in both without screens switching. + * But if they close one of the screens, + * undo will ensure that the scene being operated on will be activated + * (otherwise we'd be undoing on an off-screen scene which isn't acceptable). + * see: T43424 + */ + bScreen *curscreen = NULL; + bool track_undo_scene; + + /* comes from readfile.c */ + SWAP(ListBase, G.main->wm, bfd->main->wm); + SWAP(ListBase, G.main->screen, bfd->main->screen); + + /* we re-use current screen */ + curscreen = CTX_wm_screen(C); + /* but use new Scene pointer */ + curscene = bfd->curscene; + + track_undo_scene = (mode == LOAD_UNDO && curscreen && curscene && bfd->main->wm.first); + + if (curscene == NULL) { + curscene = bfd->main->scene.first; + } + /* empty file, we add a scene to make Blender work */ + if (curscene == NULL) { + curscene = BKE_scene_add(bfd->main, "Empty"); + } + + if (track_undo_scene) { + /* keep the old (free'd) scene, let 'blo_lib_link_screen_restore' + * replace it with 'curscene' if its needed */ + } + else { + /* and we enforce curscene to be in current screen */ + if (curscreen) { + /* can run in bgmode */ + curscreen->scene = curscene; + } + } + + /* BKE_blender_globals_clear will free G.main, here we can still restore pointers */ + blo_lib_link_screen_restore(bfd->main, curscreen, curscene); + /* curscreen might not be set when loading without ui (see T44217) so only re-assign if available */ + if (curscreen) { + curscene = curscreen->scene; + } + + if (track_undo_scene) { + wmWindowManager *wm = bfd->main->wm.first; + if (wm_scene_is_visible(wm, bfd->curscene) == false) { + curscene = bfd->curscene; + curscreen->scene = curscene; + BKE_screen_view3d_scene_sync(curscreen); + } + } + } + + /* free G.main Main database */ +// CTX_wm_manager_set(C, NULL); + BKE_blender_globals_clear(); + + /* clear old property update cache, in case some old references are left dangling */ + RNA_property_update_cache_free(); + + G.main = bfd->main; + + CTX_data_main_set(C, G.main); + + if (bfd->user) { + + /* only here free userdef themes... */ + BKE_blender_userdef_free(); + + U = *bfd->user; + + /* Security issue: any blend file could include a USER block. + * + * Currently we load prefs from BLENDER_STARTUP_FILE and later on load BLENDER_USERPREF_FILE, + * to load the preferences defined in the users home dir. + * + * This means we will never accidentally (or maliciously) + * enable scripts auto-execution by loading a '.blend' file. + */ + U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE; + + MEM_freeN(bfd->user); + } + + /* case G_FILE_NO_UI or no screens in file */ + if (mode != LOAD_UI) { + /* leave entire context further unaltered? */ + CTX_data_scene_set(C, curscene); + } + else { + G.fileflags = bfd->fileflags; + CTX_wm_manager_set(C, G.main->wm.first); + CTX_wm_screen_set(C, bfd->curscreen); + CTX_data_scene_set(C, bfd->curscene); + CTX_wm_area_set(C, NULL); + CTX_wm_region_set(C, NULL); + CTX_wm_menu_set(C, NULL); + curscene = bfd->curscene; + } + + /* this can happen when active scene was lib-linked, and doesn't exist anymore */ + if (CTX_data_scene(C) == NULL) { + /* in case we don't even have a local scene, add one */ + if (!G.main->scene.first) + BKE_scene_add(G.main, "Empty"); + + CTX_data_scene_set(C, G.main->scene.first); + CTX_wm_screen(C)->scene = CTX_data_scene(C); + curscene = CTX_data_scene(C); + } + + BLI_assert(curscene == CTX_data_scene(C)); + + + /* special cases, override loaded flags: */ + if (G.f != bfd->globalf) { + const int flags_keep = (G_SWAP_EXCHANGE | G_SCRIPT_AUTOEXEC | G_SCRIPT_OVERRIDE_PREF); + bfd->globalf = (bfd->globalf & ~flags_keep) | (G.f & flags_keep); + } + + + G.f = bfd->globalf; + +#ifdef WITH_PYTHON + /* let python know about new main */ + BPY_context_update(C); +#endif + + /* FIXME: this version patching should really be part of the file-reading code, + * but we still get too many unrelated data-corruption crashes otherwise... */ + if (G.main->versionfile < 250) + do_versions_ipos_to_animato(G.main); + + G.main->recovered = 0; + + /* startup.blend or recovered startup */ + if (bfd->filename[0] == 0) { + G.main->name[0] = 0; + } + else if (recover && G.relbase_valid) { + /* in case of autosave or quit.blend, use original filename instead + * use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */ + filepath = bfd->filename; + G.main->recovered = 1; + + /* these are the same at times, should never copy to the same location */ + if (G.main->name != filepath) + BLI_strncpy(G.main->name, filepath, FILE_MAX); + } + + /* baseflags, groups, make depsgraph, etc */ + /* first handle case if other windows have different scenes visible */ + if (mode == LOAD_UI) { + wmWindowManager *wm = G.main->wm.first; + + if (wm) { + wmWindow *win; + + for (win = wm->windows.first; win; win = win->next) { + if (win->screen && win->screen->scene) /* zealous check... */ + if (win->screen->scene != curscene) + BKE_scene_set_background(G.main, win->screen->scene); + } + } + } + BKE_scene_set_background(G.main, curscene); + + if (mode != LOAD_UNDO) { + RE_FreeAllPersistentData(); + IMB_colormanagement_check_file_config(G.main); + } + + MEM_freeN(bfd); + +} + +static int handle_subversion_warning(Main *main, ReportList *reports) +{ + if (main->minversionfile > BLENDER_VERSION || + (main->minversionfile == BLENDER_VERSION && + main->minsubversionfile > BLENDER_SUBVERSION)) + { + BKE_reportf(reports, RPT_ERROR, "File written by newer Blender binary (%d.%d), expect loss of data!", + main->minversionfile, main->minsubversionfile); + } + + return 1; +} + +int BKE_blendfile_read(bContext *C, const char *filepath, ReportList *reports) +{ + BlendFileData *bfd; + int retval = BKE_BLENDFILE_READ_OK; + + if (strstr(filepath, BLENDER_STARTUP_FILE) == NULL) /* don't print user-pref loading */ + printf("read blend: %s\n", filepath); + + bfd = BLO_read_from_file(filepath, reports); + if (bfd) { + if (bfd->user) retval = BKE_BLENDFILE_READ_OK_USERPREFS; + + if (0 == handle_subversion_warning(bfd->main, reports)) { + BKE_main_free(bfd->main); + MEM_freeN(bfd); + bfd = NULL; + retval = BKE_BLENDFILE_READ_FAIL; + } + else { + setup_app_data(C, bfd, filepath, reports); + } + } + else + BKE_reports_prependf(reports, "Loading '%s' failed: ", filepath); + + return (bfd ? retval : BKE_BLENDFILE_READ_FAIL); +} + +bool BKE_blendfile_read_from_memory( + bContext *C, const void *filebuf, int filelength, + ReportList *reports, bool update_defaults) +{ + BlendFileData *bfd; + + bfd = BLO_read_from_memory(filebuf, filelength, reports); + if (bfd) { + if (update_defaults) + BLO_update_defaults_startup_blend(bfd->main); + setup_app_data(C, bfd, "<memory2>", reports); + } + else { + BKE_reports_prepend(reports, "Loading failed: "); + } + + return (bfd != NULL); +} + +/* memfile is the undo buffer */ +bool BKE_blendfile_read_from_memfile( + bContext *C, struct MemFile *memfile, + ReportList *reports) +{ + BlendFileData *bfd; + + bfd = BLO_read_from_memfile(CTX_data_main(C), G.main->name, memfile, reports); + if (bfd) { + /* remove the unused screens and wm */ + while (bfd->main->wm.first) + BKE_libblock_free_ex(bfd->main, bfd->main->wm.first, true); + while (bfd->main->screen.first) + BKE_libblock_free_ex(bfd->main, bfd->main->screen.first, true); + + setup_app_data(C, bfd, "<memory1>", reports); + } + else { + BKE_reports_prepend(reports, "Loading failed: "); + } + + return (bfd != NULL); +} + +/* only read the userdef from a .blend */ +int BKE_blendfile_read_userdef(const char *filepath, ReportList *reports) +{ + BlendFileData *bfd; + int retval = BKE_BLENDFILE_READ_FAIL; + + bfd = BLO_read_from_file(filepath, reports); + if (bfd) { + if (bfd->user) { + retval = BKE_BLENDFILE_READ_OK_USERPREFS; + + /* only here free userdef themes... */ + BKE_blender_userdef_free(); + + U = *bfd->user; + MEM_freeN(bfd->user); + } + BKE_main_free(bfd->main); + MEM_freeN(bfd); + } + + return retval; +} + +/* only write the userdef in a .blend */ +int BKE_blendfile_write_userdef(const char *filepath, ReportList *reports) +{ + Main *mainb = MEM_callocN(sizeof(Main), "empty main"); + int retval = 0; + + if (BLO_write_file(mainb, filepath, G_FILE_USERPREFS, reports, NULL)) { + retval = 1; + } + + MEM_freeN(mainb); + + return retval; +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Partial `.blend` file save. + * \{ */ + +void BKE_blendfile_write_partial_begin(Main *bmain_src) +{ + BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false); +} + +void BKE_blendfile_write_partial_tag_ID(ID *id, bool set) +{ + if (set) { + id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT; + } + else { + id->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT); + } +} + +static void blendfile_write_partial_cb(void *UNUSED(handle), Main *UNUSED(bmain), void *vid) +{ + if (vid) { + ID *id = vid; + /* only tag for need-expand if not done, prevents eternal loops */ + if ((id->tag & LIB_TAG_DOIT) == 0) + id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT; + + if (id->lib && (id->lib->id.tag & LIB_TAG_DOIT) == 0) + id->lib->id.tag |= LIB_TAG_DOIT; + } +} + +/** + * \return Success. + */ +bool BKE_blendfile_write_partial( + Main *bmain_src, const char *filepath, const int write_flags, ReportList *reports) +{ + Main *bmain_dst = MEM_callocN(sizeof(Main), "copybuffer"); + ListBase *lbarray_dst[MAX_LIBARRAY], *lbarray_src[MAX_LIBARRAY]; + int a, retval; + + void *path_list_backup = NULL; + const int path_list_flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE); + + if (write_flags & G_FILE_RELATIVE_REMAP) { + path_list_backup = BKE_bpath_list_backup(bmain_src, path_list_flag); + } + + BLO_main_expander(blendfile_write_partial_cb); + BLO_expand_main(NULL, bmain_src); + + /* move over all tagged blocks */ + set_listbasepointers(bmain_src, lbarray_src); + a = set_listbasepointers(bmain_dst, lbarray_dst); + while (a--) { + ID *id, *nextid; + ListBase *lb_dst = lbarray_dst[a], *lb_src = lbarray_src[a]; + + for (id = lb_src->first; id; id = nextid) { + nextid = id->next; + if (id->tag & LIB_TAG_DOIT) { + BLI_remlink(lb_src, id); + BLI_addtail(lb_dst, id); + } + } + } + + + /* save the buffer */ + retval = BLO_write_file(bmain_dst, filepath, write_flags, reports, NULL); + + /* move back the main, now sorted again */ + set_listbasepointers(bmain_src, lbarray_dst); + a = set_listbasepointers(bmain_dst, lbarray_src); + while (a--) { + ID *id; + ListBase *lb_dst = lbarray_dst[a], *lb_src = lbarray_src[a]; + + while ((id = BLI_pophead(lb_src))) { + BLI_addtail(lb_dst, id); + id_sort_by_name(lb_dst, id); + } + } + + MEM_freeN(bmain_dst); + + if (path_list_backup) { + BKE_bpath_list_restore(bmain_src, path_list_flag, path_list_backup); + BKE_bpath_list_free(path_list_backup); + } + + return retval; +} + +void BKE_blendfile_write_partial_end(Main *bmain_src) +{ + BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c index abba61310a4..7821946eb6e 100644 --- a/source/blender/blenkernel/intern/bvhutils.c +++ b/source/blender/blenkernel/intern/bvhutils.c @@ -151,10 +151,10 @@ static void mesh_looptri_nearest_point(void *userdata, int index, const float co } } /* copy of function above (warning, should de-duplicate with editmesh_bvh.c) */ -static void editmesh_faces_nearest_point(void *userdata, int index, const float co[3], BVHTreeNearest *nearest) +static void editmesh_looptri_nearest_point(void *userdata, int index, const float co[3], BVHTreeNearest *nearest) { - const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata; - BMEditMesh *em = data->em_evil; + const BVHTreeFromEditMesh *data = userdata; + BMEditMesh *em = data->em; const BMLoop **ltri = (const BMLoop **)em->looptris[index]; const float *t0, *t1, *t2; @@ -240,10 +240,10 @@ static void mesh_looptri_spherecast(void *userdata, int index, const BVHTreeRay } } /* copy of function above (warning, should de-duplicate with editmesh_bvh.c) */ -static void editmesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +static void editmesh_looptri_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { - const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata; - BMEditMesh *em = data->em_evil; + const BVHTreeFromEditMesh *data = (BVHTreeFromEditMesh *)userdata; + BMEditMesh *em = data->em; const BMLoop **ltri = (const BMLoop **)em->looptris[index]; const float *t0, *t1, *t2; @@ -296,7 +296,7 @@ static void mesh_edges_nearest_point(void *userdata, int index, const float co[3 /* Helper, does all the point-spherecast work actually. */ static void mesh_verts_spherecast_do( - const BVHTreeFromMesh *UNUSED(data), int index, const float v[3], const BVHTreeRay *ray, BVHTreeRayHit *hit) + int index, const float v[3], const BVHTreeRay *ray, BVHTreeRayHit *hit) { float dist; const float *r1; @@ -314,6 +314,14 @@ static void mesh_verts_spherecast_do( } } +static void editmesh_verts_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + const BVHTreeFromEditMesh *data = userdata; + BMVert *eve = BM_vert_at_index(data->em->bm, index); + + mesh_verts_spherecast_do(index, eve->co, ray, hit); +} + /* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_verts. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ static void mesh_verts_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) @@ -321,7 +329,7 @@ static void mesh_verts_spherecast(void *userdata, int index, const BVHTreeRay *r const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata; const float *v = data->vert[index].co; - mesh_verts_spherecast_do(data, index, v, ray, hit); + mesh_verts_spherecast_do(index, v, ray, hit); } /* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_edges. @@ -341,7 +349,7 @@ static void mesh_edges_spherecast(void *userdata, int index, const BVHTreeRay *r /* In case we get a zero-length edge, handle it as a point! */ if (equals_v3v3(v1, v2)) { - mesh_verts_spherecast_do(data, index, v1, ray, hit); + mesh_verts_spherecast_do(index, v1, ray, hit); return; } @@ -380,78 +388,64 @@ static void mesh_edges_spherecast(void *userdata, int index, const BVHTreeRay *r /** \name Vertex Builder * \{ */ -static BVHTree *bvhtree_from_mesh_verts_create_tree( +static BVHTree *bvhtree_from_editmesh_verts_create_tree( float epsilon, int tree_type, int axis, - BMEditMesh *em, const int *index_array, - MVert *vert, const int numVerts, - BLI_bitmap *mask, int numVerts_active) + BMEditMesh *em, const int verts_num, + const BLI_bitmap *verts_mask, int verts_num_active) { BVHTree *tree = NULL; - BMVert *eve = NULL; int i; - int index = 0; - if (em != NULL) { - BM_mesh_elem_table_ensure(em->bm, BM_VERT); + BM_mesh_elem_table_ensure(em->bm, BM_VERT); + if (verts_mask) { + BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num)); } - if (vert) { - if (mask && numVerts_active < 0) { - numVerts_active = 0; - for (i = 0; i < numVerts; i++) { - if (BLI_BITMAP_TEST_BOOL(mask, i)) { - if (em != NULL) { - if (index_array) { - index = index_array[i]; - if (index == ORIGINDEX_NONE) { - continue; - } - } - else { - index = i; - } - - eve = BM_vert_at_index(em->bm, index); - if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN) || - BM_elem_flag_test(eve, BM_ELEM_SELECT)) - { - continue; - } - } - numVerts_active++; - } + else { + verts_num_active = verts_num; + } + + tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis); + + if (tree) { + BMIter iter; + BMVert *eve; + BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { + if (verts_mask && !BLI_BITMAP_TEST_BOOL(verts_mask, i)) { + continue; } + BLI_bvhtree_insert(tree, i, eve->co, 1); } - else if (!mask) { - numVerts_active = numVerts; + BLI_assert(BLI_bvhtree_get_size(tree) == verts_num_active); + BLI_bvhtree_balance(tree); + } + + return tree; +} + +static BVHTree *bvhtree_from_mesh_verts_create_tree( + float epsilon, int tree_type, int axis, + MVert *vert, const int verts_num, + const BLI_bitmap *verts_mask, int verts_num_active) +{ + BVHTree *tree = NULL; + int i; + if (vert) { + if (verts_mask) { + BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num)); + } + else { + verts_num_active = verts_num; } - tree = BLI_bvhtree_new(numVerts_active, epsilon, tree_type, axis); + tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis); if (tree) { - for (i = 0; i < numVerts; i++) { - if (mask && !BLI_BITMAP_TEST_BOOL(mask, i)) { + for (i = 0; i < verts_num; i++) { + if (verts_mask && !BLI_BITMAP_TEST_BOOL(verts_mask, i)) { continue; } - if (em != NULL) { - if (index_array) { - index = index_array[i]; - if (index == ORIGINDEX_NONE) { - continue; - } - } - else { - index = i; - } - - eve = BM_vert_at_index(em->bm, index); - if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN) || - BM_elem_flag_test(eve, BM_ELEM_SELECT)) - { - continue; - } - } BLI_bvhtree_insert(tree, i, vert[i].co, 1); } - + BLI_assert(BLI_bvhtree_get_size(tree) == verts_num_active); BLI_bvhtree_balance(tree); } } @@ -488,17 +482,51 @@ static void bvhtree_from_mesh_verts_setup_data( } } +/* Builds a bvh tree where nodes are the vertices of the given em */ +BVHTree *bvhtree_from_editmesh_verts_ex( + BVHTreeFromEditMesh *data, BMEditMesh *em, + const BLI_bitmap *verts_mask, int verts_num_active, + float epsilon, int tree_type, int axis) +{ + int vert_num = em->bm->totvert; + + BVHTree *tree = bvhtree_from_editmesh_verts_create_tree( + epsilon, tree_type, axis, + em, vert_num, verts_mask, verts_num_active); + + if (tree) { + memset(data, 0, sizeof(*data)); + data->tree = tree; + data->em = em; + data->nearest_callback = NULL; + data->raycast_callback = editmesh_verts_spherecast; + data->nearest_to_ray_callback = NULL; + } + + return tree; +} +BVHTree *bvhtree_from_editmesh_verts( + BVHTreeFromEditMesh *data, BMEditMesh *em, + float epsilon, int tree_type, int axis) +{ + return bvhtree_from_editmesh_verts_ex( + data, em, + NULL, -1, + epsilon, tree_type, axis); +} + + /* Builds a bvh tree where nodes are the vertices of the given dm */ -BVHTree *bvhtree_from_mesh_verts(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) +BVHTree *bvhtree_from_mesh_verts( + BVHTreeFromMesh *data, DerivedMesh *dm, + float epsilon, int tree_type, int axis) { - BMEditMesh *em = data->em_evil; - const int bvhcache_type = em ? BVHTREE_FROM_VERTS_EDITMESH_SNAP : BVHTREE_FROM_VERTS; BVHTree *tree; MVert *vert; bool vert_allocated; BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_VERTS); BLI_rw_mutex_unlock(&cache_rwlock); vert = DM_get_vert_array(dm, &vert_allocated); @@ -506,26 +534,20 @@ BVHTree *bvhtree_from_mesh_verts(BVHTreeFromMesh *data, DerivedMesh *dm, float e /* Not in cache */ if (tree == NULL) { BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_VERTS); if (tree == NULL) { - int vert_num, *index_array = NULL; - if (em != NULL) { - vert_num = em->bm->totvert; - index_array = dm->getVertDataArray(dm, CD_ORIGINDEX); - } - else { - vert_num = dm->getNumVerts(dm); - BLI_assert(vert_num != 0); - } + + int vert_num = dm->getNumVerts(dm); + BLI_assert(vert_num != 0); + tree = bvhtree_from_mesh_verts_create_tree( epsilon, tree_type, axis, - em, index_array, vert, vert_num, NULL, -1); if (tree) { /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ - bvhcache_insert(&dm->bvhCache, tree, bvhcache_type); + bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_VERTS); } } BLI_rw_mutex_unlock(&cache_rwlock); @@ -544,14 +566,15 @@ BVHTree *bvhtree_from_mesh_verts(BVHTreeFromMesh *data, DerivedMesh *dm, float e * Builds a bvh tree where nodes are the given vertices (note: does not copy given mverts!). * \param vert_allocated if true, vert freeing will be done when freeing data. * \param mask if not null, true elements give which vert to add to BVH tree. - * \param numVerts_active if >= 0, number of active verts to add to BVH tree (else will be computed from mask). + * \param verts_num_active if >= 0, number of active verts to add to BVH tree (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_verts_ex( - BVHTreeFromMesh *data, MVert *vert, const int numVerts, const bool vert_allocated, - BLI_bitmap *mask, int numVerts_active, + BVHTreeFromMesh *data, MVert *vert, const int verts_num, const bool vert_allocated, + const BLI_bitmap *verts_mask, int verts_num_active, float epsilon, int tree_type, int axis) { - BVHTree *tree = bvhtree_from_mesh_verts_create_tree(epsilon, tree_type, axis, NULL, NULL, vert, numVerts, mask, numVerts_active); + BVHTree *tree = bvhtree_from_mesh_verts_create_tree( + epsilon, tree_type, axis, vert, verts_num, verts_mask, verts_num_active); /* Setup BVHTreeFromMesh */ bvhtree_from_mesh_verts_setup_data(data, tree, false, epsilon, vert, vert_allocated); @@ -568,7 +591,9 @@ BVHTree *bvhtree_from_mesh_verts_ex( * \{ */ /* Builds a bvh tree where nodes are the edges of the given dm */ -BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) +BVHTree *bvhtree_from_mesh_edges( + BVHTreeFromMesh *data, DerivedMesh *dm, + float epsilon, int tree_type, int axis) { BVHTree *tree; MVert *vert; @@ -576,7 +601,7 @@ BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float e bool vert_allocated, edge_allocated; BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); - tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_EDGES); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_EDGES); BLI_rw_mutex_unlock(&cache_rwlock); vert = DM_get_vert_array(dm, &vert_allocated); @@ -585,7 +610,7 @@ BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float e /* Not in cache */ if (tree == NULL) { BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); - tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_EDGES); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_EDGES); if (tree == NULL) { int i; int numEdges = dm->getNumEdges(dm); @@ -655,109 +680,41 @@ BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float e static BVHTree *bvhtree_from_mesh_faces_create_tree( float epsilon, int tree_type, int axis, - BMEditMesh *em, const bool em_all, - MVert *vert, MFace *face, const int numFaces, - BLI_bitmap *mask, int numFaces_active) + MVert *vert, MFace *face, const int faces_num, + const BLI_bitmap *faces_mask, int faces_num_active) { BVHTree *tree = NULL; int i; - if (numFaces) { - if (mask && numFaces_active < 0) { - numFaces_active = 0; - for (i = 0; i < numFaces; i++) { - if (BLI_BITMAP_TEST_BOOL(mask, i)) { - numFaces_active++; - } - } + if (faces_num) { + if (faces_mask) { + BLI_assert(IN_RANGE_INCL(faces_num_active, 0, faces_num)); } - else if (!mask) { - numFaces_active = numFaces; + else { + faces_num_active = faces_num; } /* Create a bvh-tree of the given target */ /* printf("%s: building BVH, total=%d\n", __func__, numFaces); */ - tree = BLI_bvhtree_new(numFaces_active, epsilon, tree_type, axis); + tree = BLI_bvhtree_new(faces_num_active, epsilon, tree_type, axis); if (tree) { - if (em) { - const struct BMLoop *(*looptris)[3] = (void *)em->looptris; - - /* avoid double-up on face searches for quads-ngons */ - bool insert_prev = false; - BMFace *f_prev = NULL; - - /* data->em_evil is only set for snapping, and only for the mesh of the object - * which is currently open in edit mode. When set, the bvhtree should not contain - * faces that will interfere with snapping (e.g. faces that are hidden/selected - * or faces that have selected verts). */ - - /* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden - * and/or selected. Even if the faces themselves are not selected for the snapped - * transform, having a vertex selected means the face (and thus it's tessellated - * triangles) will be moving and will not be a good snap targets. */ - for (i = 0; i < numFaces; i++) { - const BMLoop **ltri = looptris[i]; - BMFace *f = ltri[0]->f; - bool insert = mask ? BLI_BITMAP_TEST_BOOL(mask, i) : true; - - /* Start with the assumption the triangle should be included for snapping. */ - if (f == f_prev) { - insert = insert_prev; - } - else if (insert) { - if (em_all) { - /* pass */ - } - else if (BM_elem_flag_test(f, BM_ELEM_SELECT) || BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - /* Don't insert triangles tessellated from faces that are hidden or selected */ - insert = false; - } - else { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { - /* Don't insert triangles tessellated from faces that have any selected verts */ - insert = false; - break; - } - } while ((l_iter = l_iter->next) != l_first); - } - - /* skip if face doesn't change */ - f_prev = f; - insert_prev = insert; + if (vert && face) { + for (i = 0; i < faces_num; i++) { + float co[4][3]; + if (faces_mask && !BLI_BITMAP_TEST_BOOL(faces_mask, i)) { + continue; } - if (insert) { - /* No reason found to block hit-testing the triangle for snap, so insert it now.*/ - float co[3][3]; - copy_v3_v3(co[0], ltri[0]->v->co); - copy_v3_v3(co[1], ltri[1]->v->co); - copy_v3_v3(co[2], ltri[2]->v->co); + copy_v3_v3(co[0], vert[face[i].v1].co); + copy_v3_v3(co[1], vert[face[i].v2].co); + copy_v3_v3(co[2], vert[face[i].v3].co); + if (face[i].v4) + copy_v3_v3(co[3], vert[face[i].v4].co); - BLI_bvhtree_insert(tree, i, co[0], 3); - } - } - } - else { - if (vert && face) { - for (i = 0; i < numFaces; i++) { - float co[4][3]; - if (mask && !BLI_BITMAP_TEST_BOOL(mask, i)) { - continue; - } - - copy_v3_v3(co[0], vert[face[i].v1].co); - copy_v3_v3(co[1], vert[face[i].v2].co); - copy_v3_v3(co[2], vert[face[i].v3].co); - if (face[i].v4) - copy_v3_v3(co[3], vert[face[i].v4].co); - - BLI_bvhtree_insert(tree, i, co[0], face[i].v4 ? 4 : 3); - } + BLI_bvhtree_insert(tree, i, co[0], face[i].v4 ? 4 : 3); } } + BLI_assert(BLI_bvhtree_get_size(tree) == faces_num_active); BLI_bvhtree_balance(tree); } } @@ -766,33 +723,24 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree( } static void bvhtree_from_mesh_faces_setup_data( - BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, - float epsilon, BMEditMesh *em, + BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon, MVert *vert, const bool vert_allocated, MFace *face, const bool face_allocated) { memset(data, 0, sizeof(*data)); - data->em_evil = em; if (tree) { data->tree = tree; data->cached = is_cached; - if (em) { - data->nearest_callback = editmesh_faces_nearest_point; - data->raycast_callback = editmesh_faces_spherecast; - data->nearest_to_ray_callback = NULL; - } - else { - data->nearest_callback = mesh_faces_nearest_point; - data->raycast_callback = mesh_faces_spherecast; - data->nearest_to_ray_callback = NULL; - - data->vert = vert; - data->vert_allocated = vert_allocated; - data->face = face; - data->face_allocated = face_allocated; - } + data->nearest_callback = mesh_faces_nearest_point; + data->raycast_callback = mesh_faces_spherecast; + data->nearest_to_ray_callback = NULL; + + data->vert = vert; + data->vert_allocated = vert_allocated; + data->face = face; + data->face_allocated = face_allocated; data->sphere_radius = epsilon; } @@ -807,54 +755,37 @@ static void bvhtree_from_mesh_faces_setup_data( } /* Builds a bvh tree where nodes are the tesselated faces of the given dm */ -BVHTree *bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) +BVHTree *bvhtree_from_mesh_faces( + BVHTreeFromMesh *data, DerivedMesh *dm, + float epsilon, int tree_type, int axis) { - BMEditMesh *em = data->em_evil; - const int bvhcache_type = em ? - (data->em_evil_all ? BVHTREE_FROM_FACES_EDITMESH_ALL : BVHTREE_FROM_FACES_EDITMESH_SNAP) : - BVHTREE_FROM_FACES; BVHTree *tree; MVert *vert = NULL; MFace *face = NULL; bool vert_allocated = false, face_allocated = false; BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_FACES); BLI_rw_mutex_unlock(&cache_rwlock); - if (em == NULL) { - vert = DM_get_vert_array(dm, &vert_allocated); - face = DM_get_tessface_array(dm, &face_allocated); - } + vert = DM_get_vert_array(dm, &vert_allocated); + face = DM_get_tessface_array(dm, &face_allocated); /* Not in cache */ if (tree == NULL) { BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_FACES); if (tree == NULL) { - int numFaces; - - /* BMESH specific check that we have tessfaces, - * we _could_ tessellate here but rather not - campbell - * - * this assert checks we have tessfaces, - * if not caller should use DM_ensure_tessface() */ - if (em) { - numFaces = em->tottri; - } - else { - numFaces = dm->getNumTessFaces(dm); - BLI_assert(!(numFaces == 0 && dm->getNumPolys(dm) != 0)); - } + int numFaces = dm->getNumTessFaces(dm); + BLI_assert(!(numFaces == 0 && dm->getNumPolys(dm) != 0)); tree = bvhtree_from_mesh_faces_create_tree( epsilon, tree_type, axis, - em, (bvhcache_type == BVHTREE_FROM_FACES_EDITMESH_ALL), vert, face, numFaces, NULL, -1); if (tree) { /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ - bvhcache_insert(&dm->bvhCache, tree, bvhcache_type); + bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_FACES); } } BLI_rw_mutex_unlock(&cache_rwlock); @@ -864,7 +795,7 @@ BVHTree *bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *dm, float e } /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_faces_setup_data(data, tree, true, epsilon, em, vert, vert_allocated, face, face_allocated); + bvhtree_from_mesh_faces_setup_data(data, tree, true, epsilon, vert, vert_allocated, face, face_allocated); return data->tree; } @@ -879,16 +810,16 @@ BVHTree *bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *dm, float e BVHTree *bvhtree_from_mesh_faces_ex( BVHTreeFromMesh *data, MVert *vert, const bool vert_allocated, MFace *face, const int numFaces, const bool face_allocated, - BLI_bitmap *mask, int numFaces_active, float epsilon, int tree_type, int axis) + const BLI_bitmap *faces_mask, int faces_num_active, + float epsilon, int tree_type, int axis) { BVHTree *tree = bvhtree_from_mesh_faces_create_tree( epsilon, tree_type, axis, - NULL, false, vert, face, numFaces, - mask, numFaces_active); + faces_mask, faces_num_active); /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_faces_setup_data(data, tree, false, epsilon, NULL, vert, vert_allocated, face, face_allocated); + bvhtree_from_mesh_faces_setup_data(data, tree, false, epsilon, vert, vert_allocated, face, face_allocated); return data->tree; } @@ -901,25 +832,19 @@ BVHTree *bvhtree_from_mesh_faces_ex( /** \name LoopTri Face Builder * \{ */ -static BVHTree *bvhtree_from_mesh_looptri_create_tree( +static BVHTree *bvhtree_from_editmesh_looptri_create_tree( float epsilon, int tree_type, int axis, - BMEditMesh *em, const bool em_all, - const MVert *vert, const MLoop *mloop, const MLoopTri *looptri, const int looptri_num, - BLI_bitmap *mask, int looptri_num_active) + BMEditMesh *em, const int looptri_num, + const BLI_bitmap *looptri_mask, int looptri_num_active) { BVHTree *tree = NULL; int i; if (looptri_num) { - if (mask && looptri_num_active < 0) { - looptri_num_active = 0; - for (i = 0; i < looptri_num; i++) { - if (BLI_BITMAP_TEST_BOOL(mask, i)) { - looptri_num_active++; - } - } + if (looptri_mask) { + BLI_assert(IN_RANGE_INCL(looptri_num_active, 0, looptri_num)); } - else if (!mask) { + else { looptri_num_active = looptri_num; } @@ -930,52 +855,13 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree( if (em) { const struct BMLoop *(*looptris)[3] = (void *)em->looptris; - /* avoid double-up on face searches for quads-ngons */ - bool insert_prev = false; - BMFace *f_prev = NULL; - - /* data->em_evil is only set for snapping, and only for the mesh of the object - * which is currently open in edit mode. When set, the bvhtree should not contain - * faces that will interfere with snapping (e.g. faces that are hidden/selected - * or faces that have selected verts). */ - /* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden * and/or selected. Even if the faces themselves are not selected for the snapped * transform, having a vertex selected means the face (and thus it's tessellated * triangles) will be moving and will not be a good snap targets. */ for (i = 0; i < looptri_num; i++) { const BMLoop **ltri = looptris[i]; - BMFace *f = ltri[0]->f; - bool insert = mask ? BLI_BITMAP_TEST_BOOL(mask, i) : true; - - /* Start with the assumption the triangle should be included for snapping. */ - if (f == f_prev) { - insert = insert_prev; - } - else if (insert) { - if (em_all) { - /* pass */ - } - else if (BM_elem_flag_test(f, BM_ELEM_SELECT) || BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - /* Don't insert triangles tessellated from faces that are hidden or selected */ - insert = false; - } - else { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { - /* Don't insert triangles tessellated from faces that have any selected verts */ - insert = false; - break; - } - } while ((l_iter = l_iter->next) != l_first); - } - - /* skip if face doesn't change */ - f_prev = f; - insert_prev = insert; - } + bool insert = looptri_mask ? BLI_BITMAP_TEST_BOOL(looptri_mask, i) : true; if (insert) { /* No reason found to block hit-testing the triangle for snap, so insert it now.*/ @@ -988,22 +874,49 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree( } } } - else { - if (vert && looptri) { - for (i = 0; i < looptri_num; i++) { - float co[3][3]; - if (mask && !BLI_BITMAP_TEST_BOOL(mask, i)) { - continue; - } + BLI_assert(BLI_bvhtree_get_size(tree) == looptri_num_active); + BLI_bvhtree_balance(tree); + } + } - copy_v3_v3(co[0], vert[mloop[looptri[i].tri[0]].v].co); - copy_v3_v3(co[1], vert[mloop[looptri[i].tri[1]].v].co); - copy_v3_v3(co[2], vert[mloop[looptri[i].tri[2]].v].co); + return tree; +} - BLI_bvhtree_insert(tree, i, co[0], 3); +static BVHTree *bvhtree_from_mesh_looptri_create_tree( + float epsilon, int tree_type, int axis, + const MVert *vert, const MLoop *mloop, const MLoopTri *looptri, const int looptri_num, + const BLI_bitmap *looptri_mask, int looptri_num_active) +{ + BVHTree *tree = NULL; + int i; + + if (looptri_num) { + if (looptri_mask) { + BLI_assert(IN_RANGE_INCL(looptri_num_active, 0, looptri_num)); + } + else { + looptri_num_active = looptri_num; + } + + /* Create a bvh-tree of the given target */ + /* printf("%s: building BVH, total=%d\n", __func__, numFaces); */ + tree = BLI_bvhtree_new(looptri_num_active, epsilon, tree_type, axis); + if (tree) { + if (vert && looptri) { + for (i = 0; i < looptri_num; i++) { + float co[3][3]; + if (looptri_mask && !BLI_BITMAP_TEST_BOOL(looptri_mask, i)) { + continue; } + + copy_v3_v3(co[0], vert[mloop[looptri[i].tri[0]].v].co); + copy_v3_v3(co[1], vert[mloop[looptri[i].tri[1]].v].co); + copy_v3_v3(co[2], vert[mloop[looptri[i].tri[2]].v].co); + + BLI_bvhtree_insert(tree, i, co[0], 3); } } + BLI_assert(BLI_bvhtree_get_size(tree) == looptri_num_active); BLI_bvhtree_balance(tree); } } @@ -1012,36 +925,27 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree( } static void bvhtree_from_mesh_looptri_setup_data( - BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, - float epsilon, BMEditMesh *em, + BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon, const MVert *vert, const bool vert_allocated, const MLoop *mloop, const bool loop_allocated, const MLoopTri *looptri, const bool looptri_allocated) { memset(data, 0, sizeof(*data)); - data->em_evil = em; if (tree) { data->tree = tree; data->cached = is_cached; - if (em) { - data->nearest_callback = editmesh_faces_nearest_point; - data->raycast_callback = editmesh_faces_spherecast; - data->nearest_to_ray_callback = NULL; - } - else { - data->nearest_callback = mesh_looptri_nearest_point; - data->raycast_callback = mesh_looptri_spherecast; - data->nearest_to_ray_callback = NULL; - - data->vert = vert; - data->vert_allocated = vert_allocated; - data->loop = mloop; - data->loop_allocated = loop_allocated; - data->looptri = looptri; - data->looptri_allocated = looptri_allocated; - } + data->nearest_callback = mesh_looptri_nearest_point; + data->raycast_callback = mesh_looptri_spherecast; + data->nearest_to_ray_callback = NULL; + + data->vert = vert; + data->vert_allocated = vert_allocated; + data->loop = mloop; + data->loop_allocated = loop_allocated; + data->looptri = looptri; + data->looptri_allocated = looptri_allocated; data->sphere_radius = epsilon; } @@ -1059,16 +963,52 @@ static void bvhtree_from_mesh_looptri_setup_data( } /** + * Builds a bvh tree where nodes are the looptri faces of the given bm + */ +BVHTree *bvhtree_from_editmesh_looptri_ex( + BVHTreeFromEditMesh *data, BMEditMesh *em, + const BLI_bitmap *looptri_mask, int looptri_num_active, + float epsilon, int tree_type, int axis) +{ + /* BMESH specific check that we have tessfaces, + * we _could_ tessellate here but rather not - campbell + * + * this assert checks we have tessfaces, + * if not caller should use DM_ensure_tessface() */ + + BVHTree *tree = bvhtree_from_editmesh_looptri_create_tree( + epsilon, tree_type, axis, + em, em->tottri, looptri_mask, looptri_num_active); + + if (tree) { + data->tree = tree; + data->nearest_callback = editmesh_looptri_nearest_point; + data->raycast_callback = editmesh_looptri_spherecast; + data->nearest_to_ray_callback = NULL; + data->sphere_radius = 0.0f; + data->em = em; + } + return tree; +} + +BVHTree *bvhtree_from_editmesh_looptri( + BVHTreeFromEditMesh *data, BMEditMesh *em, + float epsilon, int tree_type, int axis) +{ + return bvhtree_from_editmesh_looptri_ex( + data, em, NULL, -1, + epsilon, tree_type, axis); +} + +/** * Builds a bvh tree where nodes are the looptri faces of the given dm * * \note for editmesh this is currently a duplicate of bvhtree_from_mesh_faces */ -BVHTree *bvhtree_from_mesh_looptri(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) +BVHTree *bvhtree_from_mesh_looptri( + BVHTreeFromMesh *data, DerivedMesh *dm, + float epsilon, int tree_type, int axis) { - BMEditMesh *em = data->em_evil; - const int bvhcache_type = em ? - (data->em_evil_all ? BVHTREE_FROM_FACES_EDITMESH_ALL : BVHTREE_FROM_FACES_EDITMESH_SNAP) : - BVHTREE_FROM_LOOPTRI; BVHTree *tree; MVert *mvert = NULL; MLoop *mloop = NULL; @@ -1078,58 +1018,42 @@ BVHTree *bvhtree_from_mesh_looptri(BVHTreeFromMesh *data, DerivedMesh *dm, float bool looptri_allocated = false; BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_LOOPTRI); BLI_rw_mutex_unlock(&cache_rwlock); - if (em == NULL) { - MPoly *mpoly; - bool poly_allocated = false; - - mvert = DM_get_vert_array(dm, &vert_allocated); - mpoly = DM_get_poly_array(dm, &poly_allocated); + MPoly *mpoly; + bool poly_allocated = false; - mloop = DM_get_loop_array(dm, &loop_allocated); - looptri = DM_get_looptri_array( - dm, - mvert, - mpoly, dm->getNumPolys(dm), - mloop, dm->getNumLoops(dm), - &looptri_allocated); + mvert = DM_get_vert_array(dm, &vert_allocated); + mpoly = DM_get_poly_array(dm, &poly_allocated); - if (poly_allocated) { - MEM_freeN(mpoly); - } + mloop = DM_get_loop_array(dm, &loop_allocated); + looptri = DM_get_looptri_array( + dm, + mvert, + mpoly, dm->getNumPolys(dm), + mloop, dm->getNumLoops(dm), + &looptri_allocated); + if (poly_allocated) { + MEM_freeN(mpoly); } /* Not in cache */ if (tree == NULL) { BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_LOOPTRI); if (tree == NULL) { - int looptri_num; - - /* BMESH specific check that we have tessfaces, - * we _could_ tessellate here but rather not - campbell - * - * this assert checks we have tessfaces, - * if not caller should use DM_ensure_tessface() */ - if (em) { - looptri_num = em->tottri; - } - else { - looptri_num = dm->getNumLoopTri(dm); - BLI_assert(!(looptri_num == 0 && dm->getNumPolys(dm) != 0)); - } + int looptri_num = dm->getNumLoopTri(dm); + BLI_assert(!(looptri_num == 0 && dm->getNumPolys(dm) != 0)); tree = bvhtree_from_mesh_looptri_create_tree( epsilon, tree_type, axis, - em, (bvhcache_type == BVHTREE_FROM_FACES_EDITMESH_ALL), mvert, mloop, looptri, looptri_num, NULL, -1); if (tree) { /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ - bvhcache_insert(&dm->bvhCache, tree, bvhcache_type); + bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_LOOPTRI); } } BLI_rw_mutex_unlock(&cache_rwlock); @@ -1140,7 +1064,7 @@ BVHTree *bvhtree_from_mesh_looptri(BVHTreeFromMesh *data, DerivedMesh *dm, float /* Setup BVHTreeFromMesh */ bvhtree_from_mesh_looptri_setup_data( - data, tree, true, epsilon, em, + data, tree, true, epsilon, mvert, vert_allocated, mloop, loop_allocated, looptri, looptri_allocated); @@ -1153,18 +1077,17 @@ BVHTree *bvhtree_from_mesh_looptri_ex( const struct MVert *vert, const bool vert_allocated, const struct MLoop *mloop, const bool loop_allocated, const struct MLoopTri *looptri, const int looptri_num, const bool looptri_allocated, - BLI_bitmap *mask, int looptri_num_active, + const BLI_bitmap *looptri_mask, int looptri_num_active, float epsilon, int tree_type, int axis) { BVHTree *tree = bvhtree_from_mesh_looptri_create_tree( epsilon, tree_type, axis, - NULL, false, vert, mloop, looptri, looptri_num, - mask, looptri_num_active); + looptri_mask, looptri_num_active); /* Setup BVHTreeFromMesh */ bvhtree_from_mesh_looptri_setup_data( - data, tree, false, epsilon, NULL, + data, tree, false, epsilon, vert, vert_allocated, mloop, loop_allocated, looptri, looptri_allocated); @@ -1175,6 +1098,15 @@ BVHTree *bvhtree_from_mesh_looptri_ex( /** \} */ +/* Frees data allocated by a call to bvhtree_from_editmesh_*. */ +void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data) +{ + if (data->tree) { + BLI_bvhtree_free(data->tree); + memset(data, 0, sizeof(*data)); + } +} + /* Frees data allocated by a call to bvhtree_from_mesh_*. */ void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data) { @@ -1215,32 +1147,46 @@ typedef struct BVHCacheItem { } BVHCacheItem; -static void bvhcacheitem_set_if_match(void *_cached, void *_search) +/** + * Queries a bvhcache for the cache bvhtree of the request type + */ +BVHTree *bvhcache_find(BVHCache *cache, int type) { - BVHCacheItem *cached = (BVHCacheItem *)_cached; - BVHCacheItem *search = (BVHCacheItem *)_search; - - if (search->type == cached->type) { - search->tree = cached->tree; + while (cache) { + const BVHCacheItem *item = cache->link; + if (item->type == type) { + return item->tree; + } + cache = cache->next; } -} + return NULL; +} -BVHTree *bvhcache_find(BVHCache *cache, int type) +bool bvhcache_has_tree(const BVHCache *cache, const BVHTree *tree) { - BVHCacheItem item; - item.type = type; - item.tree = NULL; - - BLI_linklist_apply(*cache, bvhcacheitem_set_if_match, &item); - return item.tree; + while (cache) { + const BVHCacheItem *item = cache->link; + if (item->tree == tree) { + return true; + } + cache = cache->next; + } + return false; } -void bvhcache_insert(BVHCache *cache, BVHTree *tree, int type) +/** + * Inserts a BVHTree of the given type under the cache + * After that the caller no longer needs to worry when to free the BVHTree + * as that will be done when the cache is freed. + * + * A call to this assumes that there was no previous cached tree of the given type + */ +void bvhcache_insert(BVHCache **cache_p, BVHTree *tree, int type) { BVHCacheItem *item = NULL; assert(tree != NULL); - assert(bvhcache_find(cache, type) == NULL); + assert(bvhcache_find(*cache_p, type) == NULL); item = MEM_mallocN(sizeof(BVHCacheItem), "BVHCacheItem"); assert(item != NULL); @@ -1248,13 +1194,15 @@ void bvhcache_insert(BVHCache *cache, BVHTree *tree, int type) item->type = type; item->tree = tree; - BLI_linklist_prepend(cache, item); + BLI_linklist_prepend(cache_p, item); } - -void bvhcache_init(BVHCache *cache) +/** + * inits and frees a bvhcache + */ +void bvhcache_init(BVHCache **cache_p) { - *cache = NULL; + *cache_p = NULL; } static void bvhcacheitem_free(void *_item) @@ -1266,10 +1214,10 @@ static void bvhcacheitem_free(void *_item) } -void bvhcache_free(BVHCache *cache) +void bvhcache_free(BVHCache **cache_p) { - BLI_linklist_free(*cache, (LinkNodeFreeFP)bvhcacheitem_free); - *cache = NULL; + BLI_linklist_free(*cache_p, (LinkNodeFreeFP)bvhcacheitem_free); + *cache_p = NULL; } /** \} */ diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 6fd756e2788..96bac2c2f41 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -76,6 +76,8 @@ void BKE_camera_init(Camera *cam) /* stereoscopy 3d */ cam->stereo.interocular_distance = 0.065f; cam->stereo.convergence_distance = 30.f * 0.065f; + cam->stereo.pole_merge_angle_from = DEG2RAD(60.0f); + cam->stereo.pole_merge_angle_to = DEG2RAD(75.0f); } void *BKE_camera_add(Main *bmain, const char *name) @@ -904,7 +906,7 @@ static Object *camera_multiview_advanced(Scene *scene, Object *camera, const cha /* returns the camera to be used for render */ Object *BKE_camera_multiview_render(Scene *scene, Object *camera, const char *viewname) { - const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + const bool is_multiview = (camera != NULL) && (scene->r.scemode & R_MULTIVIEW) != 0; if (!is_multiview) { return camera; diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 519b7b44637..e6741657f47 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -1049,11 +1049,13 @@ static void cdDM_drawMappedFacesGLSL( numdata++; } } - if (matconv[a].attribs.tottang && matconv[a].attribs.tang.array) { - matconv[a].datatypes[numdata].index = matconv[a].attribs.tang.gl_index; - matconv[a].datatypes[numdata].size = 4; - matconv[a].datatypes[numdata].type = GL_FLOAT; - numdata++; + for (b = 0; b < matconv[a].attribs.tottang; b++) { + if (matconv[a].attribs.tang[b].array) { + matconv[a].datatypes[numdata].index = matconv[a].attribs.tang[b].gl_index; + matconv[a].datatypes[numdata].size = 4; + matconv[a].datatypes[numdata].type = GL_FLOAT; + numdata++; + } } if (numdata != 0) { matconv[a].numdata = numdata; @@ -1105,11 +1107,13 @@ static void cdDM_drawMappedFacesGLSL( offset += sizeof(unsigned char) * 4; } } - if (matconv[i].attribs.tottang && matconv[i].attribs.tang.array) { - const float (*looptang)[4] = (const float (*)[4])matconv[i].attribs.tang.array; - for (j = 0; j < mpoly->totloop; j++) - copy_v4_v4((float *)&varray[offset + j * max_element_size], looptang[mpoly->loopstart + j]); - offset += sizeof(float) * 4; + for (b = 0; b < matconv[i].attribs.tottang; b++) { + if (matconv[i].attribs.tottang && matconv[i].attribs.tang[b].array) { + const float (*looptang)[4] = (const float (*)[4])matconv[i].attribs.tang[b].array; + for (j = 0; j < mpoly->totloop; j++) + copy_v4_v4((float *)&varray[offset + j * max_element_size], looptang[mpoly->loopstart + j]); + offset += sizeof(float) * 4; + } } } diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 7a2a4e09b3d..3e6df3ed77c 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -58,6 +58,7 @@ static void cloth_to_object (Object *ob, ClothModifierData *clmd, float (*verte static void cloth_from_mesh ( ClothModifierData *clmd, DerivedMesh *dm ); static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float framenr, int first); static void cloth_update_springs( ClothModifierData *clmd ); +static void cloth_update_spring_lengths( ClothModifierData *clmd, DerivedMesh *dm ); static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ); static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ); @@ -369,6 +370,10 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul /* Support for dynamic vertex groups, changing from frame to frame */ cloth_apply_vgroup ( clmd, result ); + + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW ) + cloth_update_spring_lengths ( clmd, result ); + cloth_update_springs( clmd ); // TIMEIT_START(cloth_step) @@ -644,6 +649,7 @@ int cloth_uses_vgroup(ClothModifierData *clmd) { return (((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SCALING ) || (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL ) || + (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW) || (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF)) && ((clmd->sim_parms->vgroup_mass>0) || (clmd->sim_parms->vgroup_struct>0)|| @@ -684,6 +690,9 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) else verts->goal= 0.0f; + /* Compute base cloth shrink weight */ + verts->shrink_factor = 0.0f; + /* Reset vertex flags */ verts->flags &= ~CLOTH_VERT_FLAG_PINNED; verts->flags &= ~CLOTH_VERT_FLAG_NOSELFCOLL; @@ -721,16 +730,14 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) verts->flags |= CLOTH_VERT_FLAG_NOSELFCOLL; } } - + } + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW ) { if (clmd->sim_parms->vgroup_shrink > 0) { if (dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_shrink - 1)) { - /* linear interpolation between min and max shrink factor based on weight */ - verts->shrink_factor = clmd->sim_parms->shrink_min * (1.0f - dvert->dw[j].weight) + clmd->sim_parms->shrink_max * dvert->dw [j].weight; + /* used for linear interpolation between min and max shrink factor based on weight */ + verts->shrink_factor = dvert->dw[j].weight; } } - else { - verts->shrink_factor = clmd->sim_parms->shrink_min; - } } } } @@ -738,6 +745,23 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) } } +static float cloth_shrink_factor(ClothModifierData *clmd, ClothVertex *verts, int i1, int i2) +{ + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW ) { + /* linear interpolation between min and max shrink factor based on weight */ + float base = 1.0f - clmd->sim_parms->shrink_min; + float delta = clmd->sim_parms->shrink_min - clmd->sim_parms->shrink_max; + + float k1 = base + delta * verts[i1].shrink_factor; + float k2 = base + delta * verts[i2].shrink_factor; + + /* Use geometrical mean to average two factors since it behaves better + for diagonals when a rectangle transforms into a trapezoid. */ + return sqrtf(k1 * k2); + } + else + return 1.0f; +} static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float UNUSED(framenr), int first) { @@ -795,11 +819,11 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d mul_m4_v3(ob->obmat, verts->x); if ( shapekey_rest ) { - verts->xrest= shapekey_rest[i]; + copy_v3_v3(verts->xrest, shapekey_rest[i]); mul_m4_v3(ob->obmat, verts->xrest); } else - verts->xrest = verts->x; + copy_v3_v3(verts->xrest, verts->x); } /* no GUI interface yet */ @@ -811,6 +835,8 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d else verts->goal= 0.0f; + verts->shrink_factor = 0.0f; + verts->flags = 0; copy_v3_v3 ( verts->xold, verts->x ); copy_v3_v3 ( verts->xconst, verts->x ); @@ -1154,6 +1180,53 @@ static void cloth_update_springs( ClothModifierData *clmd ) cloth_hair_update_bending_targets(clmd); } +/* Update spring rest lenght, for dynamically deformable cloth */ +static void cloth_update_spring_lengths( ClothModifierData *clmd, DerivedMesh *dm ) +{ + Cloth *cloth = clmd->clothObject; + LinkNode *search = cloth->springs; + unsigned int struct_springs = 0; + unsigned int i = 0; + unsigned int mvert_num = (unsigned int)dm->getNumVerts(dm); + float shrink_factor; + + clmd->sim_parms->avg_spring_len = 0.0f; + + for (i = 0; i < mvert_num; i++) { + cloth->verts[i].avg_spring_len = 0.0f; + } + + while (search) { + ClothSpring *spring = search->link; + + if ( spring->type != CLOTH_SPRING_TYPE_SEWING ) { + if ( spring->type & (CLOTH_SPRING_TYPE_STRUCTURAL | CLOTH_SPRING_TYPE_SHEAR | CLOTH_SPRING_TYPE_BENDING) ) + shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); + else + shrink_factor = 1.0f; + + spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor; + } + + if ( spring->type == CLOTH_SPRING_TYPE_STRUCTURAL ) { + clmd->sim_parms->avg_spring_len += spring->restlen; + cloth->verts[spring->ij].avg_spring_len += spring->restlen; + cloth->verts[spring->kl].avg_spring_len += spring->restlen; + struct_springs++; + } + + search = search->next; + } + + if (struct_springs > 0) + clmd->sim_parms->avg_spring_len /= struct_springs; + + for (i = 0; i < mvert_num; i++) { + if (cloth->verts[i].spring_count > 0) + cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count); + } +} + BLI_INLINE void cross_identity_v3(float r[3][3], const float v[3]) { zero_m3(r); @@ -1193,7 +1266,7 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) { Cloth *cloth = clmd->clothObject; ClothSpring *spring = NULL, *tspring = NULL, *tspring2 = NULL; - unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0; + unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0, struct_springs_real = 0; unsigned int i = 0; unsigned int mvert_num = (unsigned int)dm->getNumVerts(dm); unsigned int numedges = (unsigned int)dm->getNumEdges (dm); @@ -1237,19 +1310,19 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) spring->type = CLOTH_SPRING_TYPE_SEWING; } else { - if (clmd->sim_parms->vgroup_shrink > 0) - shrink_factor = 1.0f - ((cloth->verts[spring->ij].shrink_factor + cloth->verts[spring->kl].shrink_factor) / 2.0f); - else - shrink_factor = 1.0f - clmd->sim_parms->shrink_min; + shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor; spring->stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f; spring->type = CLOTH_SPRING_TYPE_STRUCTURAL; + + clmd->sim_parms->avg_spring_len += spring->restlen; + cloth->verts[spring->ij].avg_spring_len += spring->restlen; + cloth->verts[spring->kl].avg_spring_len += spring->restlen; + cloth->verts[spring->ij].spring_count++; + cloth->verts[spring->kl].spring_count++; + struct_springs_real++; } - clmd->sim_parms->avg_spring_len += spring->restlen; - cloth->verts[spring->ij].avg_spring_len += spring->restlen; - cloth->verts[spring->kl].avg_spring_len += spring->restlen; - cloth->verts[spring->ij].spring_count++; - cloth->verts[spring->kl].spring_count++; + spring->flags = 0; struct_springs++; @@ -1261,11 +1334,12 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) } } - if (struct_springs > 0) - clmd->sim_parms->avg_spring_len /= struct_springs; + if (struct_springs_real > 0) + clmd->sim_parms->avg_spring_len /= struct_springs_real; for (i = 0; i < mvert_num; i++) { - cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count); + if (cloth->verts[i].spring_count > 0) + cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count); } // shear springs @@ -1287,10 +1361,7 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) mloop[mpoly[i].loopstart + (j + 0)].v, mloop[mpoly[i].loopstart + (j + 2)].v); - if (clmd->sim_parms->vgroup_shrink > 0) - shrink_factor = 1.0f - ((cloth->verts[spring->ij].shrink_factor + cloth->verts[spring->kl].shrink_factor) / 2.0f); - else - shrink_factor = 1.0f - clmd->sim_parms->shrink_min; + shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor; spring->type = CLOTH_SPRING_TYPE_SHEAR; spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f; @@ -1334,7 +1405,8 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) } spring_verts_ordered_set(spring, tspring2->ij, index2); - spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest); + shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); + spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor; spring->type = CLOTH_SPRING_TYPE_BENDING; spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f; BLI_edgeset_insert(edgeset, spring->ij, spring->kl); diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index d35762a6f13..8cac856b560 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -224,7 +224,7 @@ static int cloth_collision_response_static ( ClothModifierData *clmd, CollisionM float w1, w2, w3, u1, u2, u3; float v1[3], v2[3], relativeVelocity[3]; float magrelVel; - float epsilon2 = BLI_bvhtree_getepsilon ( collmd->bvhtree ); + float epsilon2 = BLI_bvhtree_get_epsilon ( collmd->bvhtree ); cloth1 = clmd->clothObject; @@ -396,7 +396,7 @@ static CollPair* cloth_collision(ModifierData *md1, ModifierData *md2, #endif double distance = 0; float epsilon1 = clmd->coll_parms->epsilon; - float epsilon2 = BLI_bvhtree_getepsilon ( collmd->bvhtree ); + float epsilon2 = BLI_bvhtree_get_epsilon ( collmd->bvhtree ); tri_a = &clmd->clothObject->tri[overlap->indexA]; tri_b = &collmd->tri[overlap->indexB]; @@ -909,7 +909,7 @@ static bool cloth_points_collision_response_static(ClothModifierData *clmd, Coll // float w1, w2; float u1, u2, u3; float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3]; - float epsilon2 = BLI_bvhtree_getepsilon ( collmd->bvhtree ); + float epsilon2 = BLI_bvhtree_get_epsilon ( collmd->bvhtree ); for ( ; collpair != collision_end; collpair++ ) { float margin_distance = (float)(collpair->distance - (double)epsilon2); @@ -1249,7 +1249,7 @@ int cloth_points_objcollision(Object *ob, ClothModifierData *clmd, float step, f /* search for overlapping collision pairs */ overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result, NULL, NULL); - epsilon = BLI_bvhtree_getepsilon(collmd->bvhtree); + epsilon = BLI_bvhtree_get_epsilon(collmd->bvhtree); // go to next object if no overlap is there if (result && overlap) { @@ -1375,7 +1375,7 @@ void cloth_find_point_contacts(Object *ob, ClothModifierData *clmd, float step, /* search for overlapping collision pairs */ overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result, NULL, NULL); - epsilon = BLI_bvhtree_getepsilon(collmd->bvhtree); + epsilon = BLI_bvhtree_get_epsilon(collmd->bvhtree); // go to next object if no overlap is there if (result && overlap) { diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index dabe606fa55..a591d536b43 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -535,10 +535,10 @@ static void contarget_get_lattice_mat(Object *ob, const char *substring, float m /* generic function to get the appropriate matrix for most target cases */ /* The cases where the target can be object data have not been implemented */ -static void constraint_target_to_mat4(Object *ob, const char *substring, float mat[4][4], short from, short to, float headtail) +static void constraint_target_to_mat4(Object *ob, const char *substring, float mat[4][4], short from, short to, short flag, float headtail) { /* Case OBJECT */ - if (!strlen(substring)) { + if (substring[0] == '\0') { copy_m4_m4(mat, ob->obmat); BKE_constraint_mat_convertspace(ob, NULL, mat, from, to, false); } @@ -573,6 +573,58 @@ static void constraint_target_to_mat4(Object *ob, const char *substring, float m /* skip length interpolation if set to head */ mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); } + else if ((pchan->bone) && (pchan->bone->segments > 1) && (flag & CONSTRAINT_BBONE_SHAPE)) { + /* use point along bbone */ + Mat4 bbone[MAX_BBONE_SUBDIV]; + float tempmat[4][4]; + float loc[3], fac; + + /* get bbone segments */ + b_bone_spline_setup(pchan, 0, bbone); + + /* figure out which segment(s) the headtail value falls in */ + fac = (float)pchan->bone->segments * headtail; + + if (fac >= pchan->bone->segments - 1) { + /* special case: end segment doesn't get created properly... */ + float pt[3], sfac; + int index; + + /* bbone points are in bonespace, so need to move to posespace first */ + index = pchan->bone->segments - 1; + mul_v3_m4v3(pt, pchan->pose_mat, bbone[index].mat[3]); + + /* interpolate between last segment point and the endpoint */ + sfac = fac - (float)(pchan->bone->segments - 1); /* fac is just the "leftover" between penultimate and last points */ + interp_v3_v3v3(loc, pt, pchan->pose_tail, sfac); + } + else { + /* get indices for finding interpolating between points along the bbone */ + float pt_a[3], pt_b[3], pt[3]; + int index_a, index_b; + + index_a = floorf(fac); + CLAMP(index_a, 0, MAX_BBONE_SUBDIV - 1); + + index_b = ceilf(fac); + CLAMP(index_b, 0, MAX_BBONE_SUBDIV - 1); + + /* interpolate between these points */ + copy_v3_v3(pt_a, bbone[index_a].mat[3]); + copy_v3_v3(pt_b, bbone[index_b].mat[3]); + + interp_v3_v3v3(pt, pt_a, pt_b, fac - floorf(fac)); + + /* move the point from bone local space to pose space... */ + mul_v3_m4v3(loc, pchan->pose_mat, pt); + } + + /* use interpolated distance for subtarget */ + copy_m4_m4(tempmat, pchan->pose_mat); + copy_v3_v3(tempmat[3], loc); + + mul_m4_m4m4(mat, ob->obmat, tempmat); + } else { float tempmat[4][4], loc[3]; @@ -634,7 +686,7 @@ static bConstraintTypeInfo CTI_CONSTRNAME = { static void default_get_tarmat(bConstraint *con, bConstraintOb *UNUSED(cob), bConstraintTarget *ct, float UNUSED(ctime)) { if (VALID_CONS_TARGET(ct)) - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); else if (ct) unit_m4(ct->matrix); } @@ -1102,7 +1154,7 @@ static void kinematic_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstrai bKinematicConstraint *data = con->data; if (VALID_CONS_TARGET(ct)) - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); else if (ct) { if (data->flag & CONSTRAINT_IK_AUTO) { Object *ob = cob->ob; @@ -1985,7 +2037,7 @@ static void pycon_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstraintTa /* firstly calculate the matrix the normal way, then let the py-function override * this matrix if it needs to do so */ - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); /* only execute target calculation if allowed */ #ifdef WITH_PYTHON @@ -2097,7 +2149,7 @@ static void actcon_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstraintT unit_m4(ct->matrix); /* get the transform matrix of the target */ - constraint_target_to_mat4(ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); /* determine where in transform range target is */ /* data->type is mapped as follows for backwards compatibility: @@ -2143,29 +2195,26 @@ static void actcon_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstraintT } else if (cob->type == CONSTRAINT_OBTYPE_BONE) { Object workob; - bPose *pose; + bPose pose = {0}; bPoseChannel *pchan, *tchan; - - /* make a temporary pose and evaluate using that */ - pose = MEM_callocN(sizeof(bPose), "pose"); - + /* make a copy of the bone of interest in the temp pose before evaluating action, so that it can get set * - we need to manually copy over a few settings, including rotation order, otherwise this fails */ pchan = cob->pchan; - tchan = BKE_pose_channel_verify(pose, pchan->name); + tchan = BKE_pose_channel_verify(&pose, pchan->name); tchan->rotmode = pchan->rotmode; /* evaluate action using workob (it will only set the PoseChannel in question) */ - what_does_obaction(cob->ob, &workob, pose, data->act, pchan->name, t); + what_does_obaction(cob->ob, &workob, &pose, data->act, pchan->name, t); /* convert animation to matrices for use here */ BKE_pchan_calc_mat(tchan); copy_m4_m4(ct->matrix, tchan->chan_mat); /* Clean up */ - BKE_pose_free(pose); + BKE_pose_free_data(&pose); } else { /* behavior undefined... */ @@ -3523,8 +3572,6 @@ static void shrinkwrap_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstra free_bvhtree_from_mesh(&treeData); - target->release(target); - if (fail == true) { /* Don't move the point */ zero_v3(co); diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 4c099987404..8afb451f768 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1402,14 +1402,14 @@ void BKE_nurb_makeCurve(Nurb *nu, float *coord_array, float *tilt_array, float * } } - coord_fp = (float *)(((char *)coord_fp) + stride); + coord_fp = POINTER_OFFSET(coord_fp, stride); if (tilt_fp) - tilt_fp = (float *)(((char *)tilt_fp) + stride); + tilt_fp = POINTER_OFFSET(tilt_fp, stride); if (radius_fp) - radius_fp = (float *)(((char *)radius_fp) + stride); + radius_fp = POINTER_OFFSET(radius_fp, stride); if (weight_fp) - weight_fp = (float *)(((char *)weight_fp) + stride); + weight_fp = POINTER_OFFSET(weight_fp, stride); u += ustep; } @@ -1440,7 +1440,7 @@ void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float for (a = 0; a <= it; a++) { *p = q0; - p = (float *)(((char *)p) + stride); + p = POINTER_OFFSET(p, stride); q0 += q1; q1 += q2; q2 += q3; @@ -1465,7 +1465,7 @@ void BKE_curve_forward_diff_tangent_bezier(float q0, float q1, float q2, float q for (a = 0; a <= it; a++) { *p = q0; - p = (float *)(((char *)p) + stride); + p = POINTER_OFFSET(p, stride); q0 += q1; q1 += q2; } @@ -1490,7 +1490,7 @@ static void forward_diff_bezier_cotangent(const float p0[3], const float p1[3], ( 6.0f * t) * p3[i]; } normalize_v3(p); - p = (float *)(((char *)p) + stride); + p = POINTER_OFFSET(p, stride); } } @@ -2102,7 +2102,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * *tilt_array = t[0] * pprev->alfa + t[1] * prevbezt->alfa + t[2] * bezt->alfa + t[3] * next->alfa; } - tilt_array = (float *)(((char *)tilt_array) + stride); + tilt_array = POINTER_OFFSET(tilt_array, stride); } if (radius_array) { @@ -2123,7 +2123,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * t[2] * bezt->radius + t[3] * next->radius; } - radius_array = (float *)(((char *)radius_array) + stride); + radius_array = POINTER_OFFSET(radius_array, stride); } if (weight_array) { @@ -2131,7 +2131,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * *weight_array = prevbezt->weight + (bezt->weight - prevbezt->weight) * (3.0f * fac * fac - 2.0f * fac * fac * fac); - weight_array = (float *)(((char *)weight_array) + stride); + weight_array = POINTER_OFFSET(weight_array, stride); } } } diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index a12d5434b30..de79a30bd60 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1259,7 +1259,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { layerDefault_mloopcol, layerEqual_mloopcol, layerMultiply_mloopcol, layerInitMinMax_mloopcol, layerAdd_mloopcol, layerDoMinMax_mloopcol, layerCopyValue_mloopcol, NULL, NULL, NULL, layerMaxNum_mloopcol}, /* 18: CD_TANGENT */ - {sizeof(float) * 4 * 4, "", 0, NULL, NULL, NULL, NULL, NULL, NULL}, + {sizeof(float) * 4 * 4, "", 0, N_("Tangent"), NULL, NULL, NULL, NULL, NULL}, /* 19: CD_MDISPS */ {sizeof(MDisps), "MDisps", 1, NULL, layerCopy_mdisps, layerFree_mdisps, NULL, layerSwap_mdisps, NULL, @@ -1530,13 +1530,11 @@ void CustomData_realloc(CustomData *data, int totelem) for (i = 0; i < data->totlayer; ++i) { CustomDataLayer *layer = &data->layers[i]; const LayerTypeInfo *typeInfo; - int size; if (layer->flag & CD_FLAG_NOFREE) { continue; } typeInfo = layerType_getInfo(layer->type); - size = totelem * typeInfo->size; - layer->data = MEM_reallocN(layer->data, size); + layer->data = MEM_reallocN(layer->data, (size_t)totelem * typeInfo->size); } } @@ -1844,7 +1842,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, int typ int totelem, const char *name) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); - const int size = totelem * typeInfo->size; + const size_t size = (size_t)totelem * typeInfo->size; int flag = 0, index = data->totlayer; void *newlayerdata = NULL; @@ -2064,7 +2062,7 @@ static void *customData_duplicate_referenced_layer_index(CustomData *data, const const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if (typeInfo->copy) { - void *dst_data = MEM_mallocN(totelem * typeInfo->size, "CD duplicate ref layer"); + void *dst_data = MEM_mallocN((size_t)totelem * typeInfo->size, "CD duplicate ref layer"); typeInfo->copy(layer->data, dst_data, totelem); layer->data = dst_data; } @@ -2172,7 +2170,7 @@ void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs, if (typeInfo->copy) typeInfo->copy(src_data_ofs, dst_data_ofs, count); else - memcpy(dst_data_ofs, src_data_ofs, count * typeInfo->size); + memcpy(dst_data_ofs, src_data_ofs, (size_t)count * typeInfo->size); } static void CustomData_copy_data_layer( @@ -2181,16 +2179,14 @@ static void CustomData_copy_data_layer( int src_index, int dst_index, int count) { const LayerTypeInfo *typeInfo; - int src_offset; - int dst_offset; const void *src_data = source->layers[src_i].data; void *dst_data = dest->layers[dst_i].data; typeInfo = layerType_getInfo(source->layers[src_i].type); - src_offset = src_index * typeInfo->size; - dst_offset = dst_index * typeInfo->size; + const size_t src_offset = (size_t)src_index * typeInfo->size; + const size_t dst_offset = (size_t)dst_index * typeInfo->size; if (!count || !src_data || !dst_data) { if (count && !(src_data == NULL && dst_data == NULL)) { @@ -2201,14 +2197,16 @@ static void CustomData_copy_data_layer( return; } - if (typeInfo->copy) + if (typeInfo->copy) { typeInfo->copy(POINTER_OFFSET(src_data, src_offset), POINTER_OFFSET(dst_data, dst_offset), count); - else + } + else { memcpy(POINTER_OFFSET(dst_data, dst_offset), POINTER_OFFSET(src_data, src_offset), - count * typeInfo->size); + (size_t)count * typeInfo->size); + } } void CustomData_copy_data_named(const CustomData *source, CustomData *dest, @@ -2270,7 +2268,7 @@ void CustomData_free_elem(CustomData *data, int index, int count) typeInfo = layerType_getInfo(data->layers[i].type); if (typeInfo->free) { - int offset = index * typeInfo->size; + size_t offset = (size_t)index * typeInfo->size; typeInfo->free(POINTER_OFFSET(data->layers[i].data, offset), count, typeInfo->size); } @@ -2281,7 +2279,7 @@ void CustomData_free_elem(CustomData *data, int index, int count) #define SOURCE_BUF_SIZE 100 void CustomData_interp(const CustomData *source, CustomData *dest, - int *src_indices, float *weights, float *sub_weights, + const int *src_indices, const float *weights, const float *sub_weights, int count, int dest_index) { int src_i, dest_i; @@ -2316,11 +2314,11 @@ void CustomData_interp(const CustomData *source, CustomData *dest, void *src_data = source->layers[src_i].data; for (j = 0; j < count; ++j) { - sources[j] = POINTER_OFFSET(src_data, src_indices[j] * typeInfo->size); + sources[j] = POINTER_OFFSET(src_data, (size_t)src_indices[j] * typeInfo->size); } typeInfo->interp(sources, weights, sub_weights, count, - POINTER_OFFSET(dest->layers[dest_i].data, dest_index * typeInfo->size)); + POINTER_OFFSET(dest->layers[dest_i].data, (size_t)dest_index * typeInfo->size)); /* if there are multiple source & dest layers of the same type, * we don't want to copy all source layers to the same dest, so @@ -2349,7 +2347,7 @@ void CustomData_swap_corners(struct CustomData *data, int index, const int *corn typeInfo = layerType_getInfo(data->layers[i].type); if (typeInfo->swap) { - const int offset = index * typeInfo->size; + const size_t offset = (size_t)index * typeInfo->size; typeInfo->swap(POINTER_OFFSET(data->layers[i].data, offset), corner_indices); } @@ -2387,7 +2385,6 @@ void CustomData_swap(struct CustomData *data, const int index_a, const int index void *CustomData_get(const CustomData *data, int index, int type) { - int offset; int layer_index; BLI_assert(index >= 0); @@ -2397,7 +2394,7 @@ void *CustomData_get(const CustomData *data, int index, int type) if (layer_index == -1) return NULL; /* get the offset of the desired element */ - offset = layerType_getInfo(type)->size * index; + const size_t offset = (size_t)index * layerType_getInfo(type)->size; return POINTER_OFFSET(data->layers[layer_index].data, offset); } @@ -2405,7 +2402,6 @@ void *CustomData_get(const CustomData *data, int index, int type) void *CustomData_get_n(const CustomData *data, int type, int index, int n) { int layer_index; - int offset; BLI_assert(index >= 0 && n >= 0); @@ -2413,7 +2409,7 @@ void *CustomData_get_n(const CustomData *data, int type, int index, int n) layer_index = data->typemap[type]; if (layer_index == -1) return NULL; - offset = layerType_getInfo(type)->size * index; + const size_t offset = (size_t)index * layerType_getInfo(type)->size; return POINTER_OFFSET(data->layers[layer_index + n].data, offset); } @@ -2838,7 +2834,7 @@ void CustomData_bmesh_free_block_data(CustomData *data, void *block) typeInfo = layerType_getInfo(data->layers[i].type); if (typeInfo->free) { - int offset = data->layers[i].offset; + const size_t offset = data->layers[i].offset; typeInfo->free(POINTER_OFFSET(block, offset), 1, typeInfo->size); } } @@ -3218,7 +3214,7 @@ void CustomData_to_bmesh_block(const CustomData *source, CustomData *dest, int src_index, void **dest_block, bool use_default_init) { const LayerTypeInfo *typeInfo; - int dest_i, src_i, src_offset; + int dest_i, src_i; if (*dest_block == NULL) CustomData_bmesh_alloc_block(dest, dest_block); @@ -3247,7 +3243,7 @@ void CustomData_to_bmesh_block(const CustomData *source, CustomData *dest, void *dest_data = POINTER_OFFSET(*dest_block, offset); typeInfo = layerType_getInfo(dest->layers[dest_i].type); - src_offset = src_index * typeInfo->size; + const size_t src_offset = (size_t)src_index * typeInfo->size; if (typeInfo->copy) typeInfo->copy(POINTER_OFFSET(src_data, src_offset), dest_data, 1); @@ -3294,7 +3290,7 @@ void CustomData_from_bmesh_block(const CustomData *source, CustomData *dest, const LayerTypeInfo *typeInfo = layerType_getInfo(dest->layers[dest_i].type); int offset = source->layers[src_i].offset; const void *src_data = POINTER_OFFSET(src_block, offset); - void *dst_data = POINTER_OFFSET(dest->layers[dest_i].data, dst_index * typeInfo->size); + void *dst_data = POINTER_OFFSET(dest->layers[dest_i].data, (size_t)dst_index * typeInfo->size); if (typeInfo->copy) typeInfo->copy(src_data, dst_data, 1); @@ -3311,12 +3307,12 @@ void CustomData_from_bmesh_block(const CustomData *source, CustomData *dest, } -void CustomData_file_write_info(int type, const char **structname, int *structnum) +void CustomData_file_write_info(int type, const char **r_struct_name, int *r_struct_num) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); - *structname = typeInfo->structname; - *structnum = typeInfo->structnum; + *r_struct_name = typeInfo->structname; + *r_struct_num = typeInfo->structnum; } /** diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 1dcacc3ae0a..7292ed2b5c5 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -459,9 +459,18 @@ static void data_transfer_layersmapping_add_item_cd( ListBase *r_map, const int cddata_type, const int mix_mode, const float mix_factor, const float *mix_weights, void *data_src, void *data_dst, cd_datatransfer_interp interp, void *interp_data) { + uint64_t data_flag = 0; + + if (cddata_type == CD_FREESTYLE_EDGE) { + data_flag = FREESTYLE_EDGE_MARK; + } + else if (cddata_type == CD_FREESTYLE_FACE) { + data_flag = FREESTYLE_FACE_MARK; + } + data_transfer_layersmapping_add_item( r_map, cddata_type, mix_mode, mix_factor, mix_weights, data_src, data_dst, - 0, 0, 0, 0, 0, 0, interp, interp_data); + 0, 0, 0, 0, 0, data_flag, interp, interp_data); } /* Note: All those layer mapping handlers return false *only* if they were given invalid parameters. diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index a029c8c7748..14c230e4a45 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -937,6 +937,8 @@ DagForest *build_dag(Main *bmain, Scene *sce, short mask) } dag->need_update = false; + BKE_main_id_tag_idcode(bmain, ID_OB, LIB_TAG_DOIT, false); + /* clear "LIB_TAG_DOIT" flag from all materials, to prevent infinite recursion problems later [#32017] */ BKE_main_id_tag_idcode(bmain, ID_MA, LIB_TAG_DOIT, false); BKE_main_id_tag_idcode(bmain, ID_LA, LIB_TAG_DOIT, false); @@ -948,14 +950,43 @@ DagForest *build_dag(Main *bmain, Scene *sce, short mask) /* add current scene objects */ for (base = sce->base.first; base; base = base->next) { ob = base->object; - + ob->id.tag |= LIB_TAG_DOIT; build_dag_object(dag, scenenode, bmain, sce, ob, mask); if (ob->proxy) build_dag_object(dag, scenenode, bmain, sce, ob->proxy, mask); if (ob->dup_group) build_dag_group(dag, scenenode, bmain, sce, ob->dup_group, mask); } - + + /* There might be situations when object from current scene depends on + * objects form other scene AND objects from other scene has own + * dependencies on objects from other scene. + * + * This is really important to include such indirect dependencies in order + * to keep threaded update safe but since we don't really know if object is + * coming from current scene or another scene we do rather stupid tag-based + * check here: all the objects for which build_dag_object() was called are + * getting tagged with LIB_TAG_DOIT. This way if some node has untagged + * object we know it's an object from other scene. + * + * It should be enough to to it once, because if there's longer chain of + * indirect dependencies, all the new nodes will be added to the end of the + * list, meaning we'll keep covering them in this for loop. + */ + for (node = sce->theDag->DagNode.first; node != NULL; node = node->next) { + if (node->type == ID_OB) { + ob = node->ob; + if ((ob->id.tag & LIB_TAG_DOIT) == 0) { + ob->id.tag |= LIB_TAG_DOIT; + build_dag_object(dag, scenenode, bmain, sce, ob, mask); + if (ob->proxy) + build_dag_object(dag, scenenode, bmain, sce, ob->proxy, mask); + if (ob->dup_group) + build_dag_group(dag, scenenode, bmain, sce, ob->dup_group, mask); + } + } + } + BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false); /* Now all relations were built, but we need to solve 1 exceptional case; diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index dbf095d3832..18672b93714 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -85,7 +85,7 @@ /* could enable at some point but for now there are far too many conversions */ #ifdef __GNUC__ -# pragma GCC diagnostic ignored "-Wdouble-promotion" +//# pragma GCC diagnostic ignored "-Wdouble-promotion" #endif /* precalculated gaussian factors for 5x super sampling */ @@ -123,7 +123,7 @@ static int neighY[8] = {0, 1, 1, 1, 0, -1, -1, -1}; /* dissolve inline function */ -BLI_INLINE void value_dissolve(float *r_value, const float time, const float scale, const int is_log) +BLI_INLINE void value_dissolve(float *r_value, const float time, const float scale, const bool is_log) { *r_value = (is_log) ? (*r_value) * (powf(MIN_WETNESS, 1.0f / (1.2f * time / scale))) : @@ -144,13 +144,12 @@ typedef struct Bounds3D { typedef struct VolumeGrid { int dim[3]; - Bounds3D grid_bounds; /* whole grid bounds */ + Bounds3D grid_bounds; /* whole grid bounds */ - Bounds3D *bounds; /* (x*y*z) precalculated grid cell bounds */ - int *s_pos; /* (x*y*z) t_index begin id */ - int *s_num; /* (x*y*z) number of t_index points */ - int *t_index; /* actual surface point index, - * access: (s_pos+s_num) */ + Bounds3D *bounds; /* (x*y*z) precalculated grid cell bounds */ + int *s_pos; /* (x*y*z) t_index begin id */ + int *s_num; /* (x*y*z) number of t_index points */ + int *t_index; /* actual surface point index, access: (s_pos + s_num) */ } VolumeGrid; typedef struct Vec3f { @@ -165,21 +164,20 @@ typedef struct BakeAdjPoint { /* Surface data used while processing a frame */ typedef struct PaintBakeNormal { float invNorm[3]; /* current pixel world-space inverted normal */ - float normal_scale; /* normal directional scale for displace mapping */ + float normal_scale; /* normal directional scale for displace mapping */ } PaintBakeNormal; /* Temp surface data used to process a frame */ typedef struct PaintBakeData { /* point space data */ PaintBakeNormal *bNormal; - int *s_pos; /* index to start reading point sample realCoord */ - int *s_num; /* num of realCoord samples */ - Vec3f *realCoord; /* current pixel center world-space coordinates for each sample - * ordered as (s_pos+s_num)*/ + int *s_pos; /* index to start reading point sample realCoord */ + int *s_num; /* num of realCoord samples */ + Vec3f *realCoord; /* current pixel center world-space coordinates for each sample ordered as (s_pos + s_num) */ Bounds3D mesh_bounds; /* adjacency info */ - BakeAdjPoint *bNeighs; /* current global neighbor distances and directions, if required */ + BakeAdjPoint *bNeighs; /* current global neighbor distances and directions, if required */ double average_dist; /* space partitioning */ VolumeGrid *grid; /* space partitioning grid to optimize brush checks */ @@ -188,7 +186,7 @@ typedef struct PaintBakeData { Vec3f *velocity; /* speed vector in global space movement per frame, if required */ Vec3f *prev_velocity; float *brush_velocity; /* special temp data for post-p velocity based brushes like smudge - * 3 float dir vec + 1 float str */ + * 3 float dir vec + 1 float str */ MVert *prev_verts; /* copy of previous frame vertices. used to observe surface movement */ float prev_obmat[4][4]; /* previous frame object matrix */ int clear; /* flag to check if surface was cleared/reset -> have to redo velocity etc. */ @@ -201,8 +199,7 @@ typedef struct PaintUVPoint { unsigned int tri_index, pixel_index; /* tri index on domain derived mesh */ unsigned int v1, v2, v3; /* vertex indexes */ - unsigned int neighbour_pixel; /* If this pixel isn't uv mapped to any face, - * but it's neighboring pixel is */ + unsigned int neighbour_pixel; /* If this pixel isn't uv mapped to any face, but it's neighboring pixel is */ } PaintUVPoint; typedef struct ImgSeqFormatData { @@ -214,8 +211,7 @@ typedef struct ImgSeqFormatData { #define ADJ_ON_MESH_EDGE (1 << 0) typedef struct PaintAdjData { - int *n_target; /* array of neighboring point indexes, - * for single sample use (n_index + neigh_num) */ + int *n_target; /* array of neighboring point indexes, for single sample use (n_index + neigh_num) */ int *n_index; /* index to start reading n_target for each point */ int *n_num; /* num of neighs for each point */ int *flags; /* vertex adjacency flags */ @@ -239,11 +235,10 @@ static int dynamicPaint_surfaceNumOfPoints(DynamicPaintSurface *surface) return 0; /* not supported atm */ } else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - if (!surface->canvas->dm) return 0; /* invalid derived mesh */ - return surface->canvas->dm->getNumVerts(surface->canvas->dm); + return (surface->canvas->dm) ? surface->canvas->dm->getNumVerts(surface->canvas->dm) : 0; } - else - return 0; + + return 0; } /* checks whether surface's format/type has realtime preview */ @@ -253,32 +248,16 @@ bool dynamicPaint_surfaceHasColorPreview(DynamicPaintSurface *surface) return false; } else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) - { - return false; - } - else { - return true; - } - } - else { - return true; + return !ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE); } + + return true; } /* get currently active surface (in user interface) */ DynamicPaintSurface *get_activeSurface(DynamicPaintCanvasSettings *canvas) { - DynamicPaintSurface *surface = canvas->surfaces.first; - int i; - - for (i = 0; surface; surface = surface->next) { - if (i == canvas->active_sur) - return surface; - i++; - } - return NULL; + return BLI_findlink(&canvas->surfaces, canvas->active_sur); } /* set preview to first previewable surface */ @@ -292,8 +271,9 @@ void dynamicPaint_resetPreview(DynamicPaintCanvasSettings *canvas) surface->flags |= MOD_DPAINT_PREVIEW; done = true; } - else + else { surface->flags &= ~MOD_DPAINT_PREVIEW; + } } } @@ -325,8 +305,9 @@ bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface, Object Mesh *me = ob->data; return (CustomData_get_named_layer_index(&me->ldata, CD_MLOOPCOL, name) != -1); } - else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) - return (defgroup_name_index(ob, surface->output_name) != -1); + else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { + return (defgroup_name_index(ob, name) != -1); + } } return false; @@ -334,15 +315,16 @@ bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface, Object static bool surface_duplicateOutputExists(void *arg, const char *name) { - DynamicPaintSurface *t_surface = (DynamicPaintSurface *)arg; + DynamicPaintSurface *t_surface = arg; DynamicPaintSurface *surface = t_surface->canvas->surfaces.first; for (; surface; surface = surface->next) { - if (surface != t_surface && surface->type == t_surface->type && - surface->format == t_surface->format) - { - if (surface->output_name[0] != '\0' && !BLI_path_cmp(name, surface->output_name)) return true; - if (surface->output_name2[0] != '\0' && !BLI_path_cmp(name, surface->output_name2)) return true; + if (surface != t_surface && surface->type == t_surface->type && surface->format == t_surface->format) { + if ((surface->output_name[0] != '\0' && !BLI_path_cmp(name, surface->output_name)) || + (surface->output_name2[0] != '\0' && !BLI_path_cmp(name, surface->output_name2))) + { + return true; + } } } return false; @@ -352,20 +334,25 @@ static void surface_setUniqueOutputName(DynamicPaintSurface *surface, char *base { char name[64]; BLI_strncpy(name, basename, sizeof(name)); /* in case basename is surface->name use a copy */ - if (!output) - BLI_uniquename_cb(surface_duplicateOutputExists, surface, name, '.', surface->output_name, sizeof(surface->output_name)); - if (output) - BLI_uniquename_cb(surface_duplicateOutputExists, surface, name, '.', surface->output_name2, sizeof(surface->output_name2)); + if (output == 0) { + BLI_uniquename_cb(surface_duplicateOutputExists, surface, name, '.', + surface->output_name, sizeof(surface->output_name)); + } + else if (output == 1) { + BLI_uniquename_cb(surface_duplicateOutputExists, surface, name, '.', + surface->output_name2, sizeof(surface->output_name2)); + } } static bool surface_duplicateNameExists(void *arg, const char *name) { - DynamicPaintSurface *t_surface = (DynamicPaintSurface *)arg; + DynamicPaintSurface *t_surface = arg; DynamicPaintSurface *surface = t_surface->canvas->surfaces.first; for (; surface; surface = surface->next) { - if (surface != t_surface && STREQ(name, surface->name)) return true; + if (surface != t_surface && STREQ(name, surface->name)) + return true; } return false; } @@ -420,9 +407,7 @@ void dynamicPaintSurface_updateType(struct DynamicPaintSurface *surface) static int surface_totalSamples(DynamicPaintSurface *surface) { - if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ && - surface->flags & MOD_DPAINT_ANTIALIAS) - { + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ && surface->flags & MOD_DPAINT_ANTIALIAS) { return (surface->data->total_points * 5); } if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && @@ -436,6 +421,7 @@ static int surface_totalSamples(DynamicPaintSurface *surface) static void blendColors(const float t_color[3], float t_alpha, const float s_color[3], float s_alpha, float result[4]) { + /* Same thing as BLI's blend_color_mix_float(), but for non-premultiplied alpha. */ int i; float i_alpha = 1.0f - s_alpha; float f_alpha = t_alpha * i_alpha + s_alpha; @@ -508,15 +494,11 @@ static int surface_getBrushFlags(DynamicPaintSurface *surface, const Scene *scen /* select object */ if (surface->brush_group) { - if (go->ob) brushObj = go->ob; + if (go->ob) + brushObj = go->ob; } - else + else { brushObj = base->object; - - if (!brushObj) { - if (surface->brush_group) go = go->next; - else base = base->next; - continue; } if (surface->brush_group) @@ -524,6 +506,10 @@ static int surface_getBrushFlags(DynamicPaintSurface *surface, const Scene *scen else base = base->next; + if (!brushObj) { + continue; + } + md = modifiers_findByType(brushObj, eModifierType_DynamicPaint); if (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) { DynamicPaintModifierData *pmd2 = (DynamicPaintModifierData *)md; @@ -546,50 +532,52 @@ static int brush_usesMaterial(DynamicPaintBrushSettings *brush, Scene *scene) } /* check whether two bounds intersect */ -static int boundsIntersect(Bounds3D *b1, Bounds3D *b2) +static bool boundsIntersect(Bounds3D *b1, Bounds3D *b2) { - int i = 2; - if (!b1->valid || !b2->valid) return 0; - for (; i >= 0; i -= 1) - if (!(b1->min[i] <= b2->max[i] && b1->max[i] >= b2->min[i])) return 0; - return 1; + if (!b1->valid || !b2->valid) + return false; + for (int i = 2; i--;) { + if (!(b1->min[i] <= b2->max[i] && b1->max[i] >= b2->min[i])) + return false; + } + return true; } /* check whether two bounds intersect inside defined proximity */ -static int boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, float dist) +static bool boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, const float dist) { - int i = 2; - if (!b1->valid || !b2->valid) return 0; - for (; i >= 0; i -= 1) - if (!(b1->min[i] <= (b2->max[i] + dist) && b1->max[i] >= (b2->min[i] - dist))) return 0; - return 1; + if (!b1->valid || !b2->valid) + return false; + for (int i = 2; i--;) { + if (!(b1->min[i] <= (b2->max[i] + dist) && b1->max[i] >= (b2->min[i] - dist))) + return false; + } + return true; } /* check whether bounds intersects a point with given radius */ -static int boundIntersectPoint(Bounds3D *b, float point[3], float radius) +static bool boundIntersectPoint(Bounds3D *b, float point[3], const float radius) { - int i = 2; - if (!b->valid) return 0; - for (; i >= 0; i -= 1) - if (!(b->min[i] <= (point[i] + radius) && b->max[i] >= (point[i] - radius))) return 0; - return 1; + if (!b->valid) + return false; + for (int i = 2; i--;) { + if (!(b->min[i] <= (point[i] + radius) && b->max[i] >= (point[i] - radius))) + return false; + } + return true; } /* expand bounds by a new point */ static void boundInsert(Bounds3D *b, float point[3]) { - int i = 2; if (!b->valid) { copy_v3_v3(b->min, point); copy_v3_v3(b->max, point); b->valid = 1; + return; } - else { - for (; i >= 0; i -= 1) { - if (point[i] < b->min[i]) b->min[i] = point[i]; - if (point[i] > b->max[i]) b->max[i] = point[i]; - } - } + + minmax_v3v3_v3(b->min, b->max, point); } static float getSurfaceDimension(PaintSurfaceData *sData) @@ -636,7 +624,7 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) bData->grid = MEM_callocN(sizeof(VolumeGrid), "Surface Grid"); grid = bData->grid; - if (grid && grid_bounds) { + { int i, error = 0; float dim_factor, volume, dim[3]; float td[3]; @@ -659,6 +647,8 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) boundInsert(&grid->grid_bounds, grid_bounds[i].max); } + MEM_freeN(grid_bounds); + /* get dimensions */ sub_v3_v3v3(dim, grid->grid_bounds.max, grid->grid_bounds.min); copy_v3_v3(td, dim); @@ -666,11 +656,13 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) /* deactivate zero axises */ for (i = 0; i < 3; i++) { - if (td[i] < min_dim) { td[i] = 1.0f; axis -= 1; } + if (td[i] < min_dim) { + td[i] = 1.0f; + axis--; + } } if (axis == 0 || max_fff(td[0], td[1], td[2]) < 0.0001f) { - MEM_freeN(grid_bounds); MEM_freeN(bData->grid); bData->grid = NULL; return; @@ -764,8 +756,10 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) } } - if (temp_s_num) MEM_freeN(temp_s_num); - if (temp_t_index) MEM_freeN(temp_t_index); + if (temp_s_num) + MEM_freeN(temp_s_num); + if (temp_t_index) + MEM_freeN(temp_t_index); /* free per thread s_num values */ grid->s_num = MEM_reallocN(grid->s_num, sizeof(int) * grid_cells); @@ -775,8 +769,6 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) freeGrid(sData); } } - - if (grid_bounds) MEM_freeN(grid_bounds); } /***************************** Freeing data ******************************/ @@ -791,10 +783,8 @@ void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd) if (pmd->brush->paint_ramp) MEM_freeN(pmd->brush->paint_ramp); - pmd->brush->paint_ramp = NULL; if (pmd->brush->vel_ramp) MEM_freeN(pmd->brush->vel_ramp); - pmd->brush->vel_ramp = NULL; MEM_freeN(pmd->brush); pmd->brush = NULL; @@ -804,10 +794,14 @@ void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd) static void dynamicPaint_freeAdjData(PaintSurfaceData *data) { if (data->adj_data) { - if (data->adj_data->n_index) MEM_freeN(data->adj_data->n_index); - if (data->adj_data->n_num) MEM_freeN(data->adj_data->n_num); - if (data->adj_data->n_target) MEM_freeN(data->adj_data->n_target); - if (data->adj_data->flags) MEM_freeN(data->adj_data->flags); + if (data->adj_data->n_index) + MEM_freeN(data->adj_data->n_index); + if (data->adj_data->n_num) + MEM_freeN(data->adj_data->n_num); + if (data->adj_data->n_target) + MEM_freeN(data->adj_data->n_target); + if (data->adj_data->flags) + MEM_freeN(data->adj_data->flags); MEM_freeN(data->adj_data); data->adj_data = NULL; } @@ -817,15 +811,24 @@ static void free_bakeData(PaintSurfaceData *data) { PaintBakeData *bData = data->bData; if (bData) { - if (bData->bNormal) MEM_freeN(bData->bNormal); - if (bData->s_pos) MEM_freeN(bData->s_pos); - if (bData->s_num) MEM_freeN(bData->s_num); - if (bData->realCoord) MEM_freeN(bData->realCoord); - if (bData->bNeighs) MEM_freeN(bData->bNeighs); - if (bData->grid) freeGrid(data); - if (bData->prev_verts) MEM_freeN(bData->prev_verts); - if (bData->velocity) MEM_freeN(bData->velocity); - if (bData->prev_velocity) MEM_freeN(bData->prev_velocity); + if (bData->bNormal) + MEM_freeN(bData->bNormal); + if (bData->s_pos) + MEM_freeN(bData->s_pos); + if (bData->s_num) + MEM_freeN(bData->s_num); + if (bData->realCoord) + MEM_freeN(bData->realCoord); + if (bData->bNeighs) + MEM_freeN(bData->bNeighs); + if (bData->grid) + freeGrid(data); + if (bData->prev_verts) + MEM_freeN(bData->prev_verts); + if (bData->velocity) + MEM_freeN(bData->velocity); + if (bData->prev_velocity) + MEM_freeN(bData->prev_velocity); MEM_freeN(data->bData); data->bData = NULL; @@ -835,12 +838,11 @@ static void free_bakeData(PaintSurfaceData *data) /* free surface data if it's not used anymore */ static void surface_freeUnusedData(DynamicPaintSurface *surface) { - if (!surface->data) return; + if (!surface->data) + return; /* free bakedata if not active or surface is baked */ - if (!(surface->flags & MOD_DPAINT_ACTIVE) || - (surface->pointcache && surface->pointcache->flag & PTCACHE_BAKED)) - { + if (!(surface->flags & MOD_DPAINT_ACTIVE) || (surface->pointcache && surface->pointcache->flag & PTCACHE_BAKED)) { free_bakeData(surface->data); } } @@ -848,7 +850,9 @@ static void surface_freeUnusedData(DynamicPaintSurface *surface) void dynamicPaint_freeSurfaceData(DynamicPaintSurface *surface) { PaintSurfaceData *data = surface->data; - if (!data) return; + if (!data) + return; + if (data->format_data) { /* format specific free */ if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { @@ -861,7 +865,8 @@ void dynamicPaint_freeSurfaceData(DynamicPaintSurface *surface) MEM_freeN(data->format_data); } /* type data */ - if (data->type_data) MEM_freeN(data->type_data); + if (data->type_data) + MEM_freeN(data->type_data); dynamicPaint_freeAdjData(data); /* bake data */ free_bakeData(data); @@ -1224,31 +1229,24 @@ static void dynamicPaint_allocateSurfaceType(DynamicPaintSurface *surface) break; } - if (sData->type_data == NULL) setError(surface->canvas, N_("Not enough free memory")); + if (sData->type_data == NULL) + setError(surface->canvas, N_("Not enough free memory")); } -static int surface_usesAdjDistance(DynamicPaintSurface *surface) +static bool surface_usesAdjDistance(DynamicPaintSurface *surface) { - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && surface->effect) return 1; - if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) return 1; - return 0; + return ((surface->type == MOD_DPAINT_SURFACE_T_PAINT && surface->effect) || + (surface->type == MOD_DPAINT_SURFACE_T_WAVE)); } -static int surface_usesAdjData(DynamicPaintSurface *surface) +static bool surface_usesAdjData(DynamicPaintSurface *surface) { - if (surface_usesAdjDistance(surface)) return 1; - if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && - surface->flags & MOD_DPAINT_ANTIALIAS) - { - return 1; - } - else { - return 0; - } + return (surface_usesAdjDistance(surface) || + (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && surface->flags & MOD_DPAINT_ANTIALIAS)); } /* initialize surface adjacency data */ -static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int force_init) +static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const bool force_init) { PaintSurfaceData *sData = surface->data; DerivedMesh *dm = surface->canvas->dm; @@ -1256,20 +1254,24 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for int *temp_data; int neigh_points = 0; - if (!surface_usesAdjData(surface) && !force_init) return; + if (!surface_usesAdjData(surface) && !force_init) + return; if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { /* For vertex format, neighbors are connected by edges */ neigh_points = 2 * dm->getNumEdges(dm); } - else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { neigh_points = sData->total_points * 8; + } - if (!neigh_points) return; + if (!neigh_points) + return; /* allocate memory */ ad = sData->adj_data = MEM_callocN(sizeof(PaintAdjData), "Surface Adj Data"); - if (!ad) return; + if (!ad) + return; ad->n_index = MEM_callocN(sizeof(int) * sData->total_points, "Surface Adj Index"); ad->n_num = MEM_callocN(sizeof(int) * sData->total_points, "Surface Adj Counts"); temp_data = MEM_callocN(sizeof(int) * sData->total_points, "Temp Adj Data"); @@ -1280,7 +1282,8 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for /* in case of allocation error, free memory */ if (!ad->n_index || !ad->n_num || !ad->n_target || !temp_data) { dynamicPaint_freeAdjData(sData); - if (temp_data) MEM_freeN(temp_data); + if (temp_data) + MEM_freeN(temp_data); setError(surface->canvas, N_("Not enough free memory")); return; } @@ -1308,8 +1311,7 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for /* also add number of vertices to temp_data * to locate points on "mesh edge" */ for (i = 0; i < numOfPolys; i++) { - int j = 0; - for (; j < mpoly[i].totloop; j++) { + for (int j = 0; j < mpoly[i].totloop; j++) { temp_data[mloop[mpoly[i].loopstart + j].v]++; } } @@ -1317,9 +1319,7 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for /* now check if total number of edges+faces for * each vertex is even, if not -> vertex is on mesh edge */ for (i = 0; i < sData->total_points; i++) { - if ((temp_data[i] % 2) || - (temp_data[i] < 4)) - { + if ((temp_data[i] % 2) || (temp_data[i] < 4)) { ad->flags[i] |= ADJ_ON_MESH_EDGE; } @@ -1370,8 +1370,9 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface if (surface->init_color_type == MOD_DPAINT_INITIAL_NONE) return; + /* Single color */ - else if (surface->init_color_type == MOD_DPAINT_INITIAL_COLOR) { + if (surface->init_color_type == MOD_DPAINT_INITIAL_COLOR) { /* apply color to every surface point */ #pragma omp parallel for schedule(static) for (i = 0; i < sData->total_points; i++) { @@ -1389,12 +1390,14 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - if (!tex) return; + if (!tex) + return; /* get uv map */ CustomData_validate_layer_name(&dm->loopData, CD_MLOOPUV, surface->init_layername, uvname); mloopuv = CustomData_get_layer_named(&dm->loopData, CD_MLOOPUV, uvname); - if (!mloopuv) return; + if (!mloopuv) + return; /* for vertex surface loop through tfaces and find uv color * that provides highest alpha */ @@ -1462,7 +1465,8 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface const MLoop *mloop = dm->getLoopArray(dm); const int totloop = dm->getNumLoops(dm); const MLoopCol *col = CustomData_get_layer_named(&dm->loopData, CD_MLOOPCOL, surface->init_layername); - if (!col) return; + if (!col) + return; #pragma omp parallel for schedule(static) for (i = 0; i < totloop; i++) { @@ -1474,7 +1478,8 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; int samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; MLoopCol *col = CustomData_get_layer_named(&dm->loopData, CD_MLOOPCOL, surface->init_layername); - if (!col) return; + if (!col) + return; #pragma omp parallel for schedule(static) for (i = 0; i < sData->total_points; i++) { @@ -1527,20 +1532,24 @@ bool dynamicPaint_resetSurface(const Scene *scene, DynamicPaintSurface *surface) { int numOfPoints = dynamicPaint_surfaceNumOfPoints(surface); /* free existing data */ - if (surface->data) dynamicPaint_freeSurfaceData(surface); + if (surface->data) + dynamicPaint_freeSurfaceData(surface); /* don't reallocate for image sequence types. they get handled only on bake */ - if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) return true; - if (numOfPoints < 1) return false; + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + return true; + if (numOfPoints < 1) + return false; /* allocate memory */ surface->data = MEM_callocN(sizeof(PaintSurfaceData), "PaintSurfaceData"); - if (!surface->data) return false; + if (!surface->data) + return false; /* allocate data depending on surface type and format */ surface->data->total_points = numOfPoints; dynamicPaint_allocateSurfaceType(surface); - dynamicPaint_initAdjacencyData(surface, 0); + dynamicPaint_initAdjacencyData(surface, false); /* set initial color */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) @@ -1567,7 +1576,8 @@ static void dynamicPaint_applySurfaceDisplace(DynamicPaintSurface *surface, Deri { PaintSurfaceData *sData = surface->data; - if (!sData || surface->format != MOD_DPAINT_SURFACE_F_VERTEX) return; + if (!sData || surface->format != MOD_DPAINT_SURFACE_F_VERTEX) + return; /* displace paint */ if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { @@ -1591,9 +1601,8 @@ static void dynamicPaint_applySurfaceDisplace(DynamicPaintSurface *surface, Deri /* * Apply canvas data to the object derived mesh */ -static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, - Object *ob, - DerivedMesh *dm) +static DerivedMesh *dynamicPaint_Modifier_apply( + DynamicPaintModifierData *pmd, Object *ob, DerivedMesh *dm) { DerivedMesh *result = CDDM_copy(dm); @@ -1607,7 +1616,8 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, PaintSurfaceData *sData = surface->data; if (surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ && sData) { - if (!(surface->flags & (MOD_DPAINT_ACTIVE))) continue; + if (!(surface->flags & MOD_DPAINT_ACTIVE)) + continue; /* process vertex surface previews */ if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { @@ -1627,7 +1637,8 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, #pragma omp parallel for schedule(static) for (i = 0; i < sData->total_points; i++) { /* blend dry and wet layer */ - blendColors(pPoint[i].color, pPoint[i].color[3], pPoint[i].e_color, pPoint[i].e_color[3], &fcolor[i * 4]); + blendColors(pPoint[i].color, pPoint[i].color[3], + pPoint[i].e_color, pPoint[i].e_color[3], &fcolor[i * 4]); } /* viewport preview */ @@ -1647,17 +1658,17 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, /* Save preview results to weight layer to be * able to share same drawing methods */ col = CustomData_get_layer(&result->loopData, CD_PREVIEW_MLOOPCOL); - if (!col) - col = CustomData_add_layer(&result->loopData, CD_PREVIEW_MLOOPCOL, CD_CALLOC, - NULL, totloop); + if (!col) { + col = CustomData_add_layer( + &result->loopData, CD_PREVIEW_MLOOPCOL, CD_CALLOC, NULL, totloop); + } if (col) { #pragma omp parallel for schedule(static) for (i = 0; i < totpoly; i++) { - int j = 0; Material *material = give_current_material(ob, mp[i].mat_nr + 1); - for (; j < mp[i].totloop; j++) { + for (int j = 0; j < mp[i].totloop; j++) { int l_index = mp[i].loopstart + j; int v_index = mloop[l_index].v; @@ -1683,9 +1694,10 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, rgb_float_to_uchar((unsigned char *)&col[l_index].r, c); } else { - col[l_index].r = - col[l_index].g = - col[l_index].b = FTOCHAR(pPoint[v_index].wetness); + const char c = FTOCHAR(pPoint[v_index].wetness); + col[l_index].r = c; + col[l_index].g = c; + col[l_index].b = c; col[l_index].a = 255; } } @@ -1699,15 +1711,16 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, /* paint layer */ col = CustomData_get_layer_named(&result->loopData, CD_MLOOPCOL, surface->output_name); /* if output layer is lost from a constructive modifier, re-add it */ - if (!col && dynamicPaint_outputLayerExists(surface, ob, 0)) - col = CustomData_add_layer_named(&result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name); + if (!col && dynamicPaint_outputLayerExists(surface, ob, 0)) { + col = CustomData_add_layer_named( + &result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name); + } /* apply color */ if (col) { #pragma omp parallel for schedule(static) for (i = 0; i < totloop; i++) { int index = mloop[i].v * 4; - rgb_float_to_uchar((unsigned char *)&col[i].r, &fcolor[index]); - col[i].a = FTOCHAR(fcolor[index + 3]); /* IS THIS NEEDED? */ + rgba_float_to_uchar((unsigned char *)&col[i].r, &fcolor[index]); } } @@ -1716,16 +1729,18 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, /* wet layer */ col = CustomData_get_layer_named(&result->loopData, CD_MLOOPCOL, surface->output_name2); /* if output layer is lost from a constructive modifier, re-add it */ - if (!col && dynamicPaint_outputLayerExists(surface, ob, 1)) - col = CustomData_add_layer_named(&result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name2); + if (!col && dynamicPaint_outputLayerExists(surface, ob, 1)) { + col = CustomData_add_layer_named( + &result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name2); + } /* apply color */ if (col) { #pragma omp parallel for schedule(static) for (i = 0; i < totloop; i++) { - int index = mloop[i].v; - col[i].r = - col[i].g = - col[i].b = FTOCHAR(pPoint[index].wetness); + const char c = FTOCHAR(pPoint[mloop[i].v].wetness); + col[i].r = c; + col[i].g = c; + col[i].b = c; col[i].a = 255; } } @@ -1748,9 +1763,10 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, } /* apply weights into a vertex group, if doesnt exists add a new layer */ - if (defgrp_index != -1 && !dvert && (surface->output_name[0] != '\0')) + if (defgrp_index != -1 && !dvert && (surface->output_name[0] != '\0')) { dvert = CustomData_add_layer_named(&result->vertData, CD_MDEFORMVERT, CD_CALLOC, NULL, sData->total_points, surface->output_name); + } if (defgrp_index != -1 && dvert) { int i; for (i = 0; i < sData->total_points; i++) { @@ -1759,7 +1775,6 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, /* skip if weight value is 0 and no existing weight is found */ if ((def_weight != NULL) || (weight[i] != 0.0f)) { - /* if not found, add a weight for it */ if (def_weight == NULL) { def_weight = defvert_verify_index(dv, defgrp_index); @@ -1801,7 +1816,8 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, } /* make a copy of dm to use as brush data */ if (pmd->brush) { - if (pmd->brush->dm) pmd->brush->dm->release(pmd->brush->dm); + if (pmd->brush->dm) + pmd->brush->dm->release(pmd->brush->dm); pmd->brush->dm = CDDM_copy(result); } @@ -1839,7 +1855,8 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene canvas_copyDerivedMesh(canvas, dm); /* in case image sequence baking, stop here */ - if (canvas->flags & MOD_DPAINT_BAKING) return; + if (canvas->flags & MOD_DPAINT_BAKING) + return; /* loop through surfaces */ for (; surface; surface = surface->next) { @@ -1850,17 +1867,20 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene surface_freeUnusedData(surface); /* image sequences are handled by bake operator */ - if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) continue; - if (!(surface->flags & MOD_DPAINT_ACTIVE)) continue; + if ((surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) || !(surface->flags & MOD_DPAINT_ACTIVE)) + continue; /* make sure surface is valid */ no_surface_data = surface->data == NULL; - if (!dynamicPaint_checkSurfaceData(scene, surface)) continue; + if (!dynamicPaint_checkSurfaceData(scene, surface)) + continue; /* limit frame range */ CLAMP(current_frame, surface->start_frame, surface->end_frame); - if (no_surface_data || current_frame != surface->current_frame || (int)scene->r.cfra == surface->start_frame) { + if (no_surface_data || current_frame != surface->current_frame || + (int)scene->r.cfra == surface->start_frame) + { PointCache *cache = surface->pointcache; PTCacheID pid; surface->current_frame = current_frame; @@ -1943,8 +1963,8 @@ static int dynamicPaint_findNeighbourPixel(PaintUVPoint *tempPoints, DerivedMesh x = px + neighX[n_index]; y = py + neighY[n_index]; - if (x < 0 || x >= w) return OUT_OF_TEXTURE; - if (y < 0 || y >= h) return OUT_OF_TEXTURE; + if (x < 0 || x >= w || y < 0 || y >= h) + return OUT_OF_TEXTURE; tPoint = &tempPoints[x + w * y]; /* UV neighbor */ cPoint = &tempPoints[px + w * py]; /* Origin point */ @@ -1993,8 +2013,8 @@ static int dynamicPaint_findNeighbourPixel(PaintUVPoint *tempPoints, DerivedMesh /* distances only used for comparison */ float dist_squared, t_dist_squared; - int i, edge1_index, edge2_index, - e1_index, e2_index, target_tri; + int i, edge1_index, edge2_index; + int e1_index, e2_index, target_tri; float closest_point[2], lambda, dir_vec[2]; int target_uv1 = 0, target_uv2 = 0, final_pixel[2], final_index; @@ -2050,33 +2070,28 @@ static int dynamicPaint_findNeighbourPixel(PaintUVPoint *tempPoints, DerivedMesh target_tri = -1; for (i = 0; i < tottri; i++) { + const int v0 = mloop[mlooptri[i].tri[0]].v; + const int v1 = mloop[mlooptri[i].tri[1]].v; + const int v2 = mloop[mlooptri[i].tri[2]].v; /* * Check if both edge vertices share this face */ - if ((e1_index == mloop[mlooptri[i].tri[0]].v || e1_index == mloop[mlooptri[i].tri[1]].v || e1_index == mloop[mlooptri[i].tri[2]].v) && - (e2_index == mloop[mlooptri[i].tri[0]].v || e2_index == mloop[mlooptri[i].tri[1]].v || e2_index == mloop[mlooptri[i].tri[2]].v)) - { - if (i == cPoint->tri_index) continue; + if (ELEM(e1_index, v0, v1, v2) && ELEM(e2_index, v0, v1, v2)) { + if (i == cPoint->tri_index) + continue; target_tri = i; - /* - * Get edge UV index - */ - if (e1_index == mloop[mlooptri[i].tri[0]].v) target_uv1 = 0; - else if (e1_index == mloop[mlooptri[i].tri[1]].v) target_uv1 = 1; - else if (e1_index == mloop[mlooptri[i].tri[2]].v) target_uv1 = 2; - - if (e2_index == mloop[mlooptri[i].tri[0]].v) target_uv2 = 0; - else if (e2_index == mloop[mlooptri[i].tri[1]].v) target_uv2 = 1; - else if (e2_index == mloop[mlooptri[i].tri[2]].v) target_uv2 = 2; - + /* Get edge UV index */ + target_uv1 = (e1_index == v0) ? 0 : ((e1_index == v1) ? 1 : 2); + target_uv2 = (e2_index == v0) ? 0 : ((e2_index == v1) ? 1 : 2); break; } } /* If none found pixel is on mesh edge */ - if (target_tri == -1) return ON_MESH_EDGE; + if (target_tri == -1) + return ON_MESH_EDGE; /* * If target face is connected in UV space as well, just use original index @@ -2104,8 +2119,7 @@ static int dynamicPaint_findNeighbourPixel(PaintUVPoint *tempPoints, DerivedMesh closest_point, pixel, mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv, mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv); - if (lambda < 0.0f) lambda = 0.0f; - if (lambda > 1.0f) lambda = 1.0f; + CLAMP(lambda, 0.0f, 1.0f); sub_v2_v2v2( dir_vec, @@ -2119,26 +2133,25 @@ static int dynamicPaint_findNeighbourPixel(PaintUVPoint *tempPoints, DerivedMesh pixel[0] = (pixel[0] * (float)w) - 0.5f; pixel[1] = (pixel[1] * (float)h) - 0.5f; - final_pixel[0] = (int)floor(pixel[0]); - final_pixel[1] = (int)floor(pixel[1]); + final_pixel[0] = (int)floorf(pixel[0]); + final_pixel[1] = (int)floorf(pixel[1]); /* If current pixel uv is outside of texture */ - if (final_pixel[0] < 0 || final_pixel[0] >= w) return OUT_OF_TEXTURE; - if (final_pixel[1] < 0 || final_pixel[1] >= h) return OUT_OF_TEXTURE; + if (final_pixel[0] < 0 || final_pixel[0] >= w || final_pixel[1] < 0 || final_pixel[1] >= h) + return OUT_OF_TEXTURE; final_index = final_pixel[0] + w * final_pixel[1]; /* If we ended up to our origin point ( mesh has smaller than pixel sized faces) */ - if (final_index == (px + w * py)) return NOT_FOUND; + if (final_index == (px + w * py)) + return NOT_FOUND; /* If found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ - if (tempPoints[final_index].tri_index != target_tri) { + if (tempPoints[final_index].tri_index != target_tri) return NOT_FOUND; - } - /* - * If final point is an "edge pixel", use it's "real" neighbor instead - */ - if (tempPoints[final_index].neighbour_pixel != -1) final_index = cPoint->neighbour_pixel; + /* If final point is an "edge pixel", use it's "real" neighbor instead */ + if (tempPoints[final_index].neighbour_pixel != -1) + final_index = cPoint->neighbour_pixel; return final_index; } @@ -2163,7 +2176,7 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) int tottri; char uvname[MAX_CUSTOMDATA_LAYER_NAME]; int active_points = 0; - int error = 0; + bool error = false; PaintSurfaceData *sData; DynamicPaintCanvasSettings *canvas = surface->canvas; @@ -2208,20 +2221,24 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) printf("DynamicPaint: Preparing UV surface of %ix%i pixels and %i tris.\n", w, h, tottri); /* Init data struct */ - if (surface->data) dynamicPaint_freeSurfaceData(surface); + if (surface->data) + dynamicPaint_freeSurfaceData(surface); sData = surface->data = MEM_callocN(sizeof(PaintSurfaceData), "PaintSurfaceData"); if (!surface->data) return setError(canvas, N_("Not enough free memory")); aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; tempPoints = (struct PaintUVPoint *) MEM_callocN(w * h * sizeof(struct PaintUVPoint), "Temp PaintUVPoint"); - if (!tempPoints) error = 1; + if (!tempPoints) + error = true; final_index = (int *) MEM_callocN(w * h * sizeof(int), "Temp UV Final Indexes"); - if (!final_index) error = 1; + if (!final_index) + error = true; tempWeights = (struct Vec3f *) MEM_mallocN(w * h * aa_samples * sizeof(struct Vec3f), "Temp bWeights"); - if (!tempWeights) error = 1; + if (!tempWeights) + error = true; /* * Generate a temporary bounding box array for UV faces to optimize @@ -2229,10 +2246,11 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) */ if (!error) { faceBB = (struct Bounds2D *) MEM_mallocN(tottri * sizeof(struct Bounds2D), "MPCanvasFaceBB"); - if (!faceBB) error = 1; + if (!faceBB) + error = true; } - if (!error) + if (!error) { for (ty = 0; ty < tottri; ty++) { int i; @@ -2240,18 +2258,11 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) copy_v2_v2(faceBB[ty].max, mloopuv[mlooptri[ty].tri[0]].uv); for (i = 1; i < 3; i++) { - CLAMP_MAX(faceBB[ty].min[0], mloopuv[mlooptri[ty].tri[i]].uv[0]); - CLAMP_MAX(faceBB[ty].min[1], mloopuv[mlooptri[ty].tri[i]].uv[1]); - CLAMP_MIN(faceBB[ty].max[0], mloopuv[mlooptri[ty].tri[i]].uv[0]); - CLAMP_MIN(faceBB[ty].max[1], mloopuv[mlooptri[ty].tri[i]].uv[1]); + minmax_v2v2_v2(faceBB[ty].min, faceBB[ty].max, mloopuv[mlooptri[ty].tri[i]].uv); } } - /* - * Loop through every pixel and check - * if pixel is uv-mapped on a canvas face. - */ - if (!error) { + /* Loop through every pixel and check if pixel is uv-mapped on a canvas face. */ #pragma omp parallel for schedule(static) for (ty = 0; ty < h; ty++) { int tx; @@ -2259,11 +2270,7 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) int i, sample; int index = tx + w * ty; PaintUVPoint *tPoint = (&tempPoints[index]); - - bool isInside = false; /* if point is inside a uv face */ - - float d1[2], d2[2], d3[2], point[5][2]; - float dot00, dot01, dot02, dot11, dot12, invDenom, u, v; + float point[5][2]; /* Init per pixel settings */ tPoint->tri_index = -1; @@ -2293,54 +2300,31 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) /* Loop through samples, starting from middle point */ for (sample = 0; sample < 5; sample++) { - /* Loop through every face in the mesh */ for (i = 0; i < tottri; i++) { - /* Check uv bb */ - if (faceBB[i].min[0] > (point[sample][0])) continue; - if (faceBB[i].min[1] > (point[sample][1])) continue; - if (faceBB[i].max[0] < (point[sample][0])) continue; - if (faceBB[i].max[1] < (point[sample][1])) continue; - - /* Calculate point inside a triangle check - * for uv0, 1, 2 */ - sub_v2_v2v2(d1, mloopuv[mlooptri[i].tri[2]].uv, mloopuv[mlooptri[i].tri[0]].uv); /* uv2 - uv0 */ - sub_v2_v2v2(d2, mloopuv[mlooptri[i].tri[1]].uv, mloopuv[mlooptri[i].tri[0]].uv); /* uv1 - uv0 */ - sub_v2_v2v2(d3, point[sample], mloopuv[mlooptri[i].tri[0]].uv); /* point - uv0 */ - - dot00 = d1[0] * d1[0] + d1[1] * d1[1]; - dot01 = d1[0] * d2[0] + d1[1] * d2[1]; - dot02 = d1[0] * d3[0] + d1[1] * d3[1]; - dot11 = d2[0] * d2[0] + d2[1] * d2[1]; - dot12 = d2[0] * d3[0] + d2[1] * d3[1]; - - invDenom = (dot00 * dot11 - dot01 * dot01); - invDenom = invDenom ? 1.0f / invDenom : 1.0f; - u = (dot11 * dot02 - dot01 * dot12) * invDenom; - v = (dot00 * dot12 - dot01 * dot02) * invDenom; - - if ((u > 0) && (v > 0) && (u + v < 1)) { isInside = true; } /* is inside a triangle */ - - /* - * If point was inside the face - */ - if (isInside) { + if ((faceBB[i].min[0] > (point[sample][0])) || + (faceBB[i].min[1] > (point[sample][1])) || + (faceBB[i].max[0] < (point[sample][0])) || + (faceBB[i].max[1] < (point[sample][1]))) + { + continue; + } - float uv1co[2], uv2co[2], uv3co[2], uv[2]; - int j; + const float *uv1 = mloopuv[mlooptri[i].tri[0]].uv; + const float *uv2 = mloopuv[mlooptri[i].tri[1]].uv; + const float *uv3 = mloopuv[mlooptri[i].tri[2]].uv; - /* Get triagnle uvs */ - copy_v2_v2(uv1co, mloopuv[mlooptri[i].tri[0]].uv); - copy_v2_v2(uv2co, mloopuv[mlooptri[i].tri[1]].uv); - copy_v2_v2(uv3co, mloopuv[mlooptri[i].tri[2]].uv); + /* If point is inside the face */ + if (isect_point_tri_v2(point[sample], uv1, uv2, uv3) != 0) { + float uv[2]; /* Add b-weights per anti-aliasing sample */ - for (j = 0; j < aa_samples; j++) { + for (int j = 0; j < aa_samples; j++) { uv[0] = point[0][0] + jitter5sample[j * 2] / w; uv[1] = point[0][1] + jitter5sample[j * 2 + 1] / h; - barycentric_weights_v2(uv1co, uv2co, uv3co, uv, tempWeights[index * aa_samples + j].v); + barycentric_weights_v2(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v); } /* Set surface point face values */ @@ -2396,21 +2380,18 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) /* if neighbor has index */ if (tempPoints[ind].tri_index != -1) { - - float uv1co[2], uv2co[2], uv3co[2], uv[2]; - int i = tempPoints[ind].tri_index, j; + float uv[2]; + const int i = tempPoints[ind].tri_index; + const float *uv1 = mloopuv[mlooptri[i].tri[0]].uv; + const float *uv2 = mloopuv[mlooptri[i].tri[1]].uv; + const float *uv3 = mloopuv[mlooptri[i].tri[2]].uv; /* Now calculate pixel data for this pixel as it was on polygon surface */ - copy_v2_v2(uv1co, mloopuv[mlooptri[i].tri[0]].uv); - copy_v2_v2(uv2co, mloopuv[mlooptri[i].tri[1]].uv); - copy_v2_v2(uv3co, mloopuv[mlooptri[i].tri[2]].uv); - /* Add b-weights per anti-aliasing sample */ - for (j = 0; j < aa_samples; j++) { - + for (int j = 0; j < aa_samples; j++) { uv[0] = point[0] + jitter5sample[j * 2] / w; uv[1] = point[1] + jitter5sample[j * 2 + 1] / h; - barycentric_weights_v2(uv1co, uv2co, uv3co, uv, tempWeights[index * aa_samples + j].v); + barycentric_weights_v2(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v); } /* Set values */ @@ -2461,7 +2442,7 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) } /* allocate memory */ sData->total_points = w * h; - dynamicPaint_initAdjacencyData(surface, 1); + dynamicPaint_initAdjacencyData(surface, true); if (sData->adj_data) { PaintAdjData *ed = sData->adj_data; @@ -2503,7 +2484,8 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) f_data->uv_p = MEM_callocN(active_points * sizeof(struct PaintUVPoint), "PaintUVPoint"); f_data->barycentricWeights = MEM_callocN(active_points * aa_samples * sizeof(struct Vec3f), "PaintUVPoint"); - if (!f_data->uv_p || !f_data->barycentricWeights) error = 1; + if (!f_data->uv_p || !f_data->barycentricWeights) + error = 1; } else { error = 1; @@ -2514,8 +2496,10 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) /* in case of allocation error, free everything */ if (error) { if (f_data) { - if (f_data->uv_p) MEM_freeN(f_data->uv_p); - if (f_data->barycentricWeights) MEM_freeN(f_data->barycentricWeights); + if (f_data->uv_p) + MEM_freeN(f_data->uv_p); + if (f_data->barycentricWeights) + MEM_freeN(f_data->barycentricWeights); MEM_freeN(f_data); } } @@ -2527,7 +2511,8 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) for (index = 0; index < (w * h); index++) { if (tempPoints[index].tri_index != -1) { memcpy(&f_data->uv_p[cursor], &tempPoints[index], sizeof(PaintUVPoint)); - memcpy(&f_data->barycentricWeights[cursor * aa_samples], &tempWeights[index * aa_samples], sizeof(Vec3f) * aa_samples); + memcpy(&f_data->barycentricWeights[cursor * aa_samples], &tempWeights[index * aa_samples], + sizeof(Vec3f) * aa_samples); cursor++; } } @@ -2537,10 +2522,14 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) if (error == 1) setError(canvas, N_("Not enough free memory")); - if (faceBB) MEM_freeN(faceBB); - if (tempPoints) MEM_freeN(tempPoints); - if (tempWeights) MEM_freeN(tempWeights); - if (final_index) MEM_freeN(final_index); + if (faceBB) + MEM_freeN(faceBB); + if (tempPoints) + MEM_freeN(tempPoints); + if (tempWeights) + MEM_freeN(tempWeights); + if (final_index) + MEM_freeN(final_index); /* Init surface type data */ if (!error) { @@ -2551,19 +2540,21 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) * For debug, output pixel statuses to the color map * -----------------------------------------------------------------*/ #pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) - { + for (index = 0; index < sData->total_points; index++) { ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; PaintUVPoint *uvPoint = &((PaintUVPoint *)f_data->uv_p)[index]; PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; pPoint->alpha = 1.0f; /* Every pixel that is assigned as "edge pixel" gets blue color */ - if (uvPoint->neighbour_pixel != -1) pPoint->color[2] = 1.0f; + if (uvPoint->neighbour_pixel != -1) + pPoint->color[2] = 1.0f; /* and every pixel that finally got an polygon gets red color */ - if (uvPoint->tri_index != -1) pPoint->color[0] = 1.0f; + if (uvPoint->tri_index != -1) + pPoint->color[0] = 1.0f; /* green color shows pixel face index hash */ - if (uvPoint->tri_index != -1) pPoint->color[1] = (float)(uvPoint->tri_index % 255) / 256.0f; + if (uvPoint->tri_index != -1) + pPoint->color[1] = (float)(uvPoint->tri_index % 255) / 256.0f; } #endif @@ -2592,7 +2583,8 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam } /* if selected format is openexr, but current build doesnt support one */ #ifndef WITH_OPENEXR - if (format == R_IMF_IMTYPE_OPENEXR) format = R_IMF_IMTYPE_PNG; + if (format == R_IMF_IMTYPE_OPENEXR) + format = R_IMF_IMTYPE_PNG; #endif BLI_strncpy(output_file, filename, sizeof(output_file)); BKE_image_path_ensure_ext_from_imtype(output_file, format); @@ -2619,9 +2611,7 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam PaintPoint *point = &((PaintPoint *)sData->type_data)[index]; float value = (point->wetness > 1.0f) ? 1.0f : point->wetness; - ibuf->rect_float[pos] = value; - ibuf->rect_float[pos + 1] = value; - ibuf->rect_float[pos + 2] = value; + copy_v3_fl(&ibuf->rect_float[pos], value); ibuf->rect_float[pos + 3] = 1.0f; } } @@ -2635,9 +2625,7 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam /* Multiply color by alpha if enabled */ if (surface->flags & MOD_DPAINT_MULALPHA) { - ibuf->rect_float[pos] *= ibuf->rect_float[pos + 3]; - ibuf->rect_float[pos + 1] *= ibuf->rect_float[pos + 3]; - ibuf->rect_float[pos + 2] *= ibuf->rect_float[pos + 3]; + mul_v3_fl(&ibuf->rect_float[pos], ibuf->rect_float[pos + 3]); } } /* displace */ @@ -2652,9 +2640,7 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam CLAMP(depth, 0.0f, 1.0f); - ibuf->rect_float[pos] = depth; - ibuf->rect_float[pos + 1] = depth; - ibuf->rect_float[pos + 2] = depth; + copy_v3_fl(&ibuf->rect_float[pos], depth); ibuf->rect_float[pos + 3] = 1.0f; } /* waves */ @@ -2666,9 +2652,7 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam depth = (0.5f + depth / 2.0f); CLAMP(depth, 0.0f, 1.0f); - ibuf->rect_float[pos] = depth; - ibuf->rect_float[pos + 1] = depth; - ibuf->rect_float[pos + 2] = depth; + copy_v3_fl(&ibuf->rect_float[pos], depth); ibuf->rect_float[pos + 3] = 1.0f; } } @@ -2762,9 +2746,11 @@ static void dynamicPaint_doMaterialTex( if (mat == NULL) { if (bMats->ob_mats) { int mat_nr = mpoly[mlooptri[triIndex].poly].mat_nr; - if (mat_nr >= (*give_totcolp(brushOb))) return; + if (mat_nr >= (*give_totcolp(brushOb))) + return; mat = bMats->ob_mats[mat_nr]; - if (mat == NULL) return; /* No material assigned */ + if (mat == NULL) + return; /* No material assigned */ } else { return; @@ -2777,7 +2763,7 @@ static void dynamicPaint_doMaterialTex( /***************************** Ray / Nearest Point Utils ******************************/ -/* A modified callback to bvh tree raycast. The tree must bust have been built using bvhtree_from_mesh_looptri. +/* A modified callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_looptri. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. * * To optimize brush detection speed this doesn't calculate hit coordinates or normal. @@ -2803,10 +2789,9 @@ static void mesh_tris_spherecast_dp(void *userdata, int index, const BVHTreeRay hit->dist = dist; hit->no[0] = 0.0f; } - } -/* A modified callback to bvh tree nearest point. The tree must bust have been built using bvhtree_from_mesh_looptri. +/* A modified callback to bvh tree nearest point. The tree must have been built using bvhtree_from_mesh_looptri. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. * * To optimize brush detection speed this doesn't calculate hit normal. @@ -2915,7 +2900,8 @@ static void dynamicPaint_mixPaintColors( static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, DynamicPaintBrushSettings *brush, float isect_height) { float isect_change = isect_height - wPoint->brush_isect; - int hit = 0; + bool hit = false; + /* intersection marked regardless of brush type or hit */ wPoint->brush_isect = isect_height; wPoint->state = DPAINT_WAVE_ISECT_CHANGED; @@ -2924,9 +2910,9 @@ static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, DynamicPaintBrush /* determine hit depending on wave_factor */ if (brush->wave_factor > 0.0f && wPoint->height > isect_height) - hit = 1; + hit = true; else if (brush->wave_factor < 0.0f && wPoint->height < isect_height) - hit = 1; + hit = true; if (hit) { if (brush->wave_type == MOD_DPAINT_WAVEB_DEPTH) { @@ -2948,8 +2934,9 @@ static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, DynamicPaintBrush /* * add brush results to the surface data depending on surface type */ -static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned int index, DynamicPaintBrushSettings *brush, - float paint[3], float influence, float depth, float vel_factor, float timescale) +static void dynamicPaint_updatePointData( + DynamicPaintSurface *surface, unsigned int index, DynamicPaintBrushSettings *brush, + float paint[3], float influence, float depth, float vel_factor, float timescale) { PaintSurfaceData *sData = surface->data; float strength; @@ -2969,9 +2956,7 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned if (do_colorband(brush->vel_ramp, vel_factor, coba_res)) { if (brush->flags & MOD_DPAINT_VELOCITY_COLOR) { - paint[0] = coba_res[0]; - paint[1] = coba_res[1]; - paint[2] = coba_res[2]; + copy_v3_v3(paint, coba_res); } if (brush->flags & MOD_DPAINT_VELOCITY_ALPHA) strength *= coba_res[3]; @@ -2982,12 +2967,10 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned /* mix paint surface */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - float paintWetness = brush->wetness * strength; float paintAlpha = strength; dynamicPaint_mixPaintColors(surface, index, brush->flags, paint, &paintAlpha, &paintWetness, ×cale); - } /* displace surface */ else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { @@ -3002,10 +2985,10 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned if (brush->flags & MOD_DPAINT_ERASE) { value[index] *= (1.0f - strength); - if (value[index] < 0.0f) value[index] = 0.0f; + CLAMP_MIN(value[index], 0.0f); } else { - if (value[index] < depth) value[index] = depth; + CLAMP_MIN(value[index], depth); } } /* vertex weight group surface */ @@ -3014,10 +2997,10 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned if (brush->flags & MOD_DPAINT_ERASE) { value[index] *= (1.0f - strength); - if (value[index] < 0.0f) value[index] = 0.0f; + CLAMP_MIN(value[index], 0.0f); } else { - if (value[index] < strength) value[index] = strength; + CLAMP_MIN(value[index], strength); } } /* wave surface */ @@ -3026,8 +3009,7 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned CLAMP(depth, 0.0f - brush->wave_clamp, brush->wave_clamp); } - dynamicPaint_mixWaveHeight(&((PaintWavePoint *)sData->type_data)[index], - brush, 0.0f - depth); + dynamicPaint_mixWaveHeight(&((PaintWavePoint *)sData->type_data)[index], brush, 0.0f - depth); } /* doing velocity based painting */ @@ -3037,17 +3019,18 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned } /* checks whether surface and brush bounds intersect depending on brush type */ -static int meshBrush_boundsIntersect(Bounds3D *b1, Bounds3D *b2, DynamicPaintBrushSettings *brush, float brush_radius) +static bool meshBrush_boundsIntersect(Bounds3D *b1, Bounds3D *b2, DynamicPaintBrushSettings *brush, float brush_radius) { if (brush->collision == MOD_DPAINT_COL_VOLUME) return boundsIntersect(b1, b2); else if (brush->collision == MOD_DPAINT_COL_DIST || brush->collision == MOD_DPAINT_COL_VOLDIST) return boundsIntersectDist(b1, b2, brush_radius); - else return 1; + return true; } /* calculate velocity for mesh vertices */ -static void dynamicPaint_brushMeshCalculateVelocity(Scene *scene, Object *ob, DynamicPaintBrushSettings *brush, Vec3f **brushVel, float timescale) +static void dynamicPaint_brushMeshCalculateVelocity( + Scene *scene, Object *ob, DynamicPaintBrushSettings *brush, Vec3f **brushVel, float timescale) { int i; float prev_obmat[4][4]; @@ -3069,7 +3052,8 @@ static void dynamicPaint_brushMeshCalculateVelocity(Scene *scene, Object *ob, Dy scene->r.cfra = prev_fra; scene->r.subframe = prev_sfra; - BKE_object_modifier_update_subframe(scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe( + scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); dm_p = CDDM_copy(brush->dm); numOfVerts_p = dm_p->getNumVerts(dm_p); mvert_p = dm_p->getVertArray(dm_p); @@ -3079,13 +3063,15 @@ static void dynamicPaint_brushMeshCalculateVelocity(Scene *scene, Object *ob, Dy scene->r.cfra = cur_fra; scene->r.subframe = cur_sfra; - BKE_object_modifier_update_subframe(scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe( + scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); dm_c = brush->dm; numOfVerts_c = dm_c->getNumVerts(dm_c); mvert_c = dm_p->getVertArray(dm_c); (*brushVel) = (struct Vec3f *) MEM_mallocN(numOfVerts_c * sizeof(Vec3f), "Dynamic Paint brush velocity"); - if (!(*brushVel)) return; + if (!(*brushVel)) + return; /* if mesh is constructive -> num of verts has changed, * only use current frame derived mesh */ @@ -3129,13 +3115,15 @@ static void dynamicPaint_brushObjectCalculateVelocity(Scene *scene, Object *ob, /* previous frame dm */ scene->r.cfra = prev_fra; scene->r.subframe = prev_sfra; - BKE_object_modifier_update_subframe(scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe( + scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); copy_m4_m4(prev_obmat, ob->obmat); /* current frame dm */ scene->r.cfra = cur_fra; scene->r.subframe = cur_sfra; - BKE_object_modifier_update_subframe(scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe( + scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); /* calculate speed */ mul_m4_v3(prev_obmat, prev_loc); @@ -3166,7 +3154,9 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, if (brush->flags & MOD_DPAINT_USES_VELOCITY) dynamicPaint_brushMeshCalculateVelocity(scene, brushOb, brush, &brushVelocity, timescale); - if (!brush->dm) return 0; + if (!brush->dm) + return 0; + { BVHTreeFromMesh treeData = {NULL}; float avg_brushNor[3] = {0.0f}; @@ -3220,8 +3210,11 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, int id; /* check grid cell bounding box */ - if (!grid->s_num[c_index] || !meshBrush_boundsIntersect(&grid->bounds[c_index], &mesh_bb, brush, brush_radius)) + if (!grid->s_num[c_index] || + !meshBrush_boundsIntersect(&grid->bounds[c_index], &mesh_bb, brush, brush_radius)) + { continue; + } /* loop through cell points and process brush */ #pragma omp parallel for schedule(static) @@ -3282,7 +3275,9 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, /* Check volume collision */ if (brush->collision == MOD_DPAINT_COL_VOLUME || brush->collision == MOD_DPAINT_COL_VOLDIST) - if (BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, &treeData) != -1) { + BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, + &hit, mesh_tris_spherecast_dp, &treeData); + if (hit.index != -1) { /* We hit a triangle, now check if collision point normal is facing the point */ /* For optimization sake, hit point normal isn't calculated in ray cast loop */ @@ -3308,7 +3303,8 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, hit.index = -1; hit.dist = BVH_RAYCAST_DIST_MAX; - BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, &treeData); + BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, + &hit, mesh_tris_spherecast_dp, &treeData); if (hit.index != -1) { /* Add factor on supersample filter */ @@ -3336,7 +3332,9 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, /* If pure distance proximity, find the nearest point on the mesh */ if (!(brush->flags & MOD_DPAINT_PROX_PROJECT)) { - if (BLI_bvhtree_find_nearest(treeData.tree, ray_start, &nearest, mesh_tris_nearest_point_dp, &treeData) != -1) { + BLI_bvhtree_find_nearest(treeData.tree, ray_start, + &nearest, mesh_tris_nearest_point_dp, &treeData); + if (nearest.index != -1) { proxDist = sqrtf(nearest.dist_sq); copy_v3_v3(hitCo, nearest.co); tri = nearest.index; @@ -3359,7 +3357,9 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, hit.dist = brush_radius; /* Do a face normal directional raycast, and use that distance */ - if (BLI_bvhtree_ray_cast(treeData.tree, ray_start, proj_ray, 0.0f, &hit, mesh_tris_spherecast_dp, &treeData) != -1) { + BLI_bvhtree_ray_cast(treeData.tree, ray_start, proj_ray, 0.0f, + &hit, mesh_tris_spherecast_dp, &treeData); + if (hit.index != -1) { proxDist = hit.dist; madd_v3_v3v3fl(hitCo, ray_start, proj_ray, hit.dist); /* Calculate final hit coordinates */ tri = hit.index; @@ -3386,7 +3386,9 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, /* mix final sample strength depending on brush settings */ if (hit_found) { /* if "negate volume" enabled, negate all factors within volume*/ - if (brush->collision == MOD_DPAINT_COL_VOLDIST && brush->flags & MOD_DPAINT_NEGATE_VOLUME) { + if (brush->collision == MOD_DPAINT_COL_VOLDIST && + brush->flags & MOD_DPAINT_NEGATE_VOLUME) + { volume_factor = 1.0f - volume_factor; if (inner_proximity) proximity_factor = 1.0f - proximity_factor; @@ -3398,10 +3400,14 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, } else if (hit_found == HIT_PROXIMITY) { /* apply falloff curve to the proximity_factor */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && do_colorband(brush->paint_ramp, (1.0f - proximity_factor), prox_colorband)) + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && + do_colorband(brush->paint_ramp, (1.0f - proximity_factor), prox_colorband)) + { proximity_factor = prox_colorband[3]; - else if (brush->proximity_falloff == MOD_DPAINT_PRFALL_CONSTANT) + } + else if (brush->proximity_falloff == MOD_DPAINT_PRFALL_CONSTANT) { proximity_factor = (!inner_proximity || brush->flags & MOD_DPAINT_NEGATE_VOLUME) ? 1.0f : 0.0f; + } /* apply sample */ sampleStrength = proximity_factor; } @@ -3466,11 +3472,16 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, sampleColor[2] = brush->b; /* Get material+textures color on hit point if required */ - if (brush_usesMaterial(brush, scene)) - dynamicPaint_doMaterialTex(bMats, sampleColor, &alpha_factor, brushOb, bData->realCoord[bData->s_pos[index] + ss].v, hitCoord, hitTri, brush->dm); + if (brush_usesMaterial(brush, scene)) { + dynamicPaint_doMaterialTex(bMats, sampleColor, &alpha_factor, brushOb, + bData->realCoord[bData->s_pos[index] + ss].v, + hitCoord, hitTri, dm); + } /* Sample proximity colorband if required */ - if ((hit_found == HIT_PROXIMITY) && (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP)) { + if ((hit_found == HIT_PROXIMITY) && + (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP)) + { if (!(brush->flags & MOD_DPAINT_RAMP_ALPHA)) { sampleColor[0] = prox_colorband[0]; sampleColor[1] = prox_colorband[1]; @@ -3493,7 +3504,6 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, /* if any sample was inside paint range */ if (brushStrength > 0.0f || depth > 0.0f) { - /* apply supersampling results */ if (samples > 1) { brushStrength /= total_sample; @@ -3513,7 +3523,8 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, depth /= bData->bNormal[index].normal_scale * total_sample; } - dynamicPaint_updatePointData(surface, index, brush, paintColor, brushStrength, depth, velocity_val, timescale); + dynamicPaint_updatePointData(surface, index, brush, paintColor, + brushStrength, depth, velocity_val, timescale); } } } @@ -3550,7 +3561,8 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, int invalidParticles = 0; int p = 0; - float solidradius = surface->radius_scale * ((brush->flags & MOD_DPAINT_PART_RAD) ? psys->part->size : brush->particle_radius); + float solidradius = surface->radius_scale * + ((brush->flags & MOD_DPAINT_PART_RAD) ? psys->part->size : brush->particle_radius); float smooth = brush->particle_smooth * surface->radius_scale; float range = solidradius + smooth; @@ -3558,7 +3570,8 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, Bounds3D part_bb = {0}; - if (psys->totpart < 1) return 1; + if (psys->totpart < 1) + return 1; /* * Build a kd-tree to optimize distance search @@ -3568,18 +3581,24 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, /* loop through particles and insert valid ones to the tree */ p = 0; for (ParticleData *pa = psys->particles; p < psys->totpart; p++, pa++) { - /* Proceed only if particle is active */ - if (pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN) == 0) continue; - else if (pa->alive == PARS_DEAD && (part->flag & PART_DIED) == 0) continue; - else if (pa->flag & PARS_UNEXIST) continue; + if ((pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN) == 0) || + (pa->alive == PARS_DEAD && (part->flag & PART_DIED) == 0) || + (pa->flag & PARS_UNEXIST)) + { + continue; + } /* for debug purposes check if any NAN particle proceeds * For some reason they get past activity check, this should rule most of them out */ - if (isnan(pa->state.co[0]) || isnan(pa->state.co[1]) || isnan(pa->state.co[2])) { invalidParticles++; continue; } + if (isnan(pa->state.co[0]) || isnan(pa->state.co[1]) || isnan(pa->state.co[2])) { + invalidParticles++; + continue; + } /* make sure particle is close enough to canvas */ - if (!boundIntersectPoint(&grid->grid_bounds, pa->state.co, range)) continue; + if (!boundIntersectPoint(&grid->grid_bounds, pa->state.co, range)) + continue; BLI_kdtree_insert(tree, p, pa->state.co); @@ -3640,7 +3659,8 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, /* Find nearest particle and get distance to it */ BLI_kdtree_find_nearest(tree, bData->realCoord[bData->s_pos[index]].v, &nearest); /* if outside maximum range, no other particle can influence either */ - if (nearest.dist > range) continue; + if (nearest.dist > range) + continue; if (brush->flags & MOD_DPAINT_PART_RAD) { /* use particles individual size */ @@ -3721,11 +3741,14 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, } /* do smoothness if enabled */ - if (smooth_range < 0.0f) smooth_range = 0.0f; - if (smooth) smooth_range /= smooth; + if (smooth_range < 0.0f) + smooth_range = 0.0f; + if (smooth) + smooth_range /= smooth; str = 1.0f - smooth_range; /* if influence is greater, use this one */ - if (str > strength) strength = str; + if (str > strength) + strength = str; } } @@ -3746,8 +3769,8 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, velocity_val = len_v3(velocity); /* store brush velocity for smudge */ - if ( (surface->type == MOD_DPAINT_SURFACE_T_PAINT) && - (brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity)) + if ((surface->type == MOD_DPAINT_SURFACE_T_PAINT) && + (brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity)) { copy_v3_v3(&bData->brush_velocity[index * 4], velocity); mul_v3_fl(&bData->brush_velocity[index * 4], 1.0f / velocity_val); @@ -3758,8 +3781,8 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { copy_v3_v3(paintColor, &brush->r); } - else if ( (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) || - (surface->type == MOD_DPAINT_SURFACE_T_WAVE)) + else if ((surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) || + (surface->type == MOD_DPAINT_SURFACE_T_WAVE)) { /* get displace depth */ disp_intersect = (1.0f - sqrtf(disp_intersect / radius)) * radius; @@ -3767,7 +3790,8 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, if (depth < 0.0f) depth = 0.0f; } - dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); + dynamicPaint_updatePointData(surface, index, brush, paintColor, + strength, depth, velocity_val, timescale); } } } @@ -3779,8 +3803,9 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, } /* paint a single point of defined proximity radius to the surface */ -static int dynamicPaint_paintSinglePoint(DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush, - Object *brushOb, BrushMaterials *bMats, Scene *scene, float timescale) +static int dynamicPaint_paintSinglePoint( + DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush, + Object *brushOb, BrushMaterials *bMats, Scene *scene, float timescale) { int index; float brush_radius = brush->paint_distance * surface->radius_scale; @@ -3800,7 +3825,8 @@ static int dynamicPaint_paintSinglePoint(DynamicPaintSurface *surface, float *po float colorband[4] = {0.0f}; float strength; - if (distance > brush_radius) continue; + if (distance > brush_radius) + continue; /* Smooth range or color ramp */ if (brush->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH || @@ -3827,12 +3853,16 @@ static int dynamicPaint_paintSinglePoint(DynamicPaintSurface *surface, float *po copy_v3_v3(hit_coord, mvert[0].co); mul_m4_v3(brushOb->obmat, hit_coord); - dynamicPaint_doMaterialTex(bMats, paintColor, &alpha_factor, brushOb, bData->realCoord[bData->s_pos[index]].v, hit_coord, 0, brush->dm); + dynamicPaint_doMaterialTex(bMats, paintColor, &alpha_factor, brushOb, + bData->realCoord[bData->s_pos[index]].v, hit_coord, 0, brush->dm); } /* color ramp */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && do_colorband(brush->paint_ramp, (1.0f - strength), colorband)) + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && + do_colorband(brush->paint_ramp, (1.0f - strength), colorband)) + { strength = colorband[3]; + } if (brush->flags & MOD_DPAINT_USES_VELOCITY) { float velocity[3]; @@ -3878,7 +3908,7 @@ static int dynamicPaint_paintSinglePoint(DynamicPaintSurface *surface, float *po /* get displace depth */ float disp_intersect = (1.0f - sqrtf((brush_radius - distance) / brush_radius)) * brush_radius; depth = (brush_radius - disp_intersect) / bData->bNormal[index].normal_scale; - if (depth < 0.0f) depth = 0.0f; + CLAMP_MIN(depth, 0.0f); } dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); } @@ -3893,7 +3923,7 @@ static int dynamicPaint_paintSinglePoint(DynamicPaintSurface *surface, float *po /* * Calculate current frame distances and directions for adjacency data */ -static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, int force_init) +static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, const bool force_init) { PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; @@ -3902,11 +3932,14 @@ static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, int Vec3f *realCoord = bData->realCoord; int index; - if ((!surface_usesAdjDistance(surface) && !force_init) || !sData->adj_data) return; + if ((!surface_usesAdjDistance(surface) && !force_init) || !sData->adj_data) + return; - if (bData->bNeighs) MEM_freeN(bData->bNeighs); + if (bData->bNeighs) + MEM_freeN(bData->bNeighs); bNeighs = bData->bNeighs = MEM_mallocN(sData->adj_data->total_targets * sizeof(struct BakeAdjPoint), "PaintEffectBake"); - if (!bNeighs) return; + if (!bNeighs) + return; #pragma omp parallel for schedule(static) for (index = 0; index < sData->total_points; index++) { @@ -3922,7 +3955,8 @@ static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, int /* dist */ bNeighs[n_index].dist = len_v3(bNeighs[n_index].dir); /* normalize dir */ - if (bNeighs[n_index].dist) mul_v3_fl(bNeighs[n_index].dir, 1.0f / bNeighs[n_index].dist); + if (bNeighs[n_index].dist) + mul_v3_fl(bNeighs[n_index].dir, 1.0f / bNeighs[n_index].dist); } } @@ -3940,7 +3974,8 @@ static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, int } /* find two adjacency points (closest_id) and influence (closest_d) to move paint towards when affected by a force */ -static void surface_determineForceTargetPoints(PaintSurfaceData *sData, int index, float force[3], float closest_d[2], int closest_id[2]) +static void surface_determineForceTargetPoints( + PaintSurfaceData *sData, int index, float force[3], float closest_d[2], int closest_id[2]) { BakeAdjPoint *bNeighs = sData->bData->bNeighs; int numOfNeighs = sData->adj_data->n_num[index]; @@ -3954,10 +3989,14 @@ static void surface_determineForceTargetPoints(PaintSurfaceData *sData, int inde int n_index = sData->adj_data->n_index[index] + i; float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); - if (dir_dot > closest_d[0] && dir_dot > 0.0f) { closest_d[0] = dir_dot; closest_id[0] = n_index; } + if (dir_dot > closest_d[0] && dir_dot > 0.0f) { + closest_d[0] = dir_dot; + closest_id[0] = n_index; + } } - if (closest_d[0] < 0.0f) return; + if (closest_d[0] < 0.0f) + return; /* find second closest neigh */ for (i = 0; i < numOfNeighs; i++) { @@ -3965,7 +4004,8 @@ static void surface_determineForceTargetPoints(PaintSurfaceData *sData, int inde float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); float closest_dot = dot_v3v3(bNeighs[n_index].dir, bNeighs[closest_id[0]].dir); - if (n_index == closest_id[0]) continue; + if (n_index == closest_id[0]) + continue; /* only accept neighbor at "other side" of the first one in relation to force dir * so make sure angle between this and closest neigh is greater than first angle */ @@ -4016,12 +4056,13 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus int index, steps, step; float eff_scale, max_velocity = 0.0f; - if (!sData->adj_data) return; + if (!sData->adj_data) + return; /* find max velocity */ for (index = 0; index < sData->total_points; index++) { float vel = bData->brush_velocity[index * 4 + 3]; - if (vel > max_velocity) max_velocity = vel; + CLAMP_MIN(max_velocity, vel); } steps = (int)ceil(max_velocity / bData->average_dist * timescale); @@ -4029,7 +4070,6 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus eff_scale = brush->smudge_strength / (float)steps * timescale; for (step = 0; step < steps; step++) { - for (index = 0; index < sData->total_points; index++) { int i; PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; @@ -4039,7 +4079,8 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus int closest_id[2]; float closest_d[2]; - if (!smudge_str) continue; + if (!smudge_str) + continue; /* get force affect points */ surface_determineForceTargetPoints(sData, index, &bData->brush_velocity[index * 4], closest_d, closest_id); @@ -4053,7 +4094,8 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus PaintPoint *ePoint = &((PaintPoint *)sData->type_data)[sData->adj_data->n_target[n_index]]; /* just skip if angle is too extreme */ - if (dir_dot <= 0.0f) continue; + if (dir_dot <= 0.0f) + continue; dir_factor = dir_dot * speed_scale; CLAMP_MAX(dir_factor, brush->smudge_strength); @@ -4076,7 +4118,8 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus * Prepare data required by effects for current frame. * Returns number of steps required */ -static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *scene, Object *ob, float **force, float timescale) +static int dynamicPaint_prepareEffectStep( + DynamicPaintSurface *surface, Scene *scene, Object *ob, float **force, float timescale) { double average_force = 0.0f; float shrink_speed = 0.0f, spread_speed = 0.0f; @@ -4132,7 +4175,8 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s /* force strength */ (*force)[index * 4 + 3] = len_v3(forc); /* normalize and copy */ - if ((*force)[index * 4 + 3]) mul_v3_fl(forc, 1.0f / (*force)[index * 4 + 3]); + if ((*force)[index * 4 + 3]) + mul_v3_fl(forc, 1.0f / (*force)[index * 4 + 3]); copy_v3_v3(&((*force)[index * 4]), forc); } @@ -4166,7 +4210,8 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s /** * Processes active effect step. */ -static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force, PaintPoint *prevPoint, float timescale, float steps) +static void dynamicPaint_doEffectStep( + DynamicPaintSurface *surface, float *force, PaintPoint *prevPoint, float timescale, float steps) { PaintSurfaceData *sData = surface->data; BakeAdjPoint *bNeighs = sData->bData->bNeighs; @@ -4174,7 +4219,8 @@ static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force int index; timescale /= steps; - if (!sData->adj_data) return; + if (!sData->adj_data) + return; /* * Spread Effect @@ -4203,17 +4249,20 @@ static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force float color_mix = (MIN3(ePoint->wetness, pPoint->wetness, 1.0f)) * 0.25f * surface->color_spread_speed; /* do color mixing */ - if (color_mix) mixColors(pPoint->e_color, pPoint->e_color[3], ePoint->e_color, ePoint->e_color[3], color_mix); + if (color_mix) + mixColors(pPoint->e_color, pPoint->e_color[3], ePoint->e_color, ePoint->e_color[3], color_mix); /* Only continue if surrounding point has higher wetness */ - if (ePoint->wetness < pPoint->wetness || ePoint->wetness < MIN_WETNESS) continue; + if (ePoint->wetness < pPoint->wetness || ePoint->wetness < MIN_WETNESS) + continue; w_factor = 1.0f / numOfNeighs * MIN2(ePoint->wetness, 1.0f) * speed_scale; CLAMP(w_factor, 0.0f, 1.0f); /* mix new wetness and color */ pPoint->wetness = (1.0f - w_factor) * pPoint->wetness + w_factor * ePoint->wetness; - pPoint->e_color[3] = mixColors(pPoint->e_color, pPoint->e_color[3], ePoint->e_color, ePoint->e_color[3], w_factor); + pPoint->e_color[3] = mixColors(pPoint->e_color, pPoint->e_color[3], ePoint->e_color, ePoint->e_color[3], + w_factor); } } } @@ -4244,7 +4293,8 @@ static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force /* Check if neighboring point has lower alpha, * if so, decrease this point's alpha as well*/ - if (pPoint->color[3] <= 0.0f && pPoint->e_color[3] <= 0.0f && pPoint->wetness <= 0.0f) continue; + if (pPoint->color[3] <= 0.0f && pPoint->e_color[3] <= 0.0f && pPoint->wetness <= 0.0f) + continue; /* decrease factor for dry paint alpha */ a_factor = (1.0f - ePoint->color[3]) / numOfNeighs * (pPoint->color[3] - ePoint->color[3]) * speed_scale; @@ -4284,7 +4334,8 @@ static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force /* adjust drip speed depending on wetness */ float w_factor = pPoint_prev->wetness - 0.025f; - if (w_factor <= 0) continue; + if (w_factor <= 0) + continue; CLAMP(w_factor, 0.0f, 1.0f); /* get force affect points */ @@ -4300,7 +4351,8 @@ static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force float e_wet = ePoint->wetness; /* just skip if angle is too extreme */ - if (dir_dot <= 0.0f) continue; + if (dir_dot <= 0.0f) + continue; dir_factor = dir_dot * MIN2(speed_scale, 1.0f) * w_factor; CLAMP_MAX(dir_factor, 0.5f); @@ -4312,7 +4364,8 @@ static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force /* mix new color */ a_factor = dir_factor / pPoint_prev->wetness; CLAMP(a_factor, 0.0f, 1.0f); - mixColors(ePoint->e_color, ePoint->e_color[3], pPoint_prev->e_color, pPoint_prev->e_color[3], a_factor); + mixColors(ePoint->e_color, ePoint->e_color[3], pPoint_prev->e_color, pPoint_prev->e_color[3], + a_factor); /* dripping is supposed to preserve alpha level */ if (pPoint_prev->e_color[3] > ePoint->e_color[3]) { ePoint->e_color[3] += a_factor * pPoint_prev->e_color[3]; @@ -4343,7 +4396,8 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal /* allocate memory */ PaintWavePoint *prevPoint = MEM_mallocN(sData->total_points * sizeof(PaintWavePoint), "Temp previous points for wave simulation"); - if (!prevPoint) return; + if (!prevPoint) + return; /* calculate average neigh distance (single thread) */ for (index = 0; index < sData->total_points; index++) { @@ -4367,7 +4421,6 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal damp_factor = pow((1.0f - surface->wave_damping), timescale * surface->wave_timescale); for (ss = 0; ss < steps; ss++) { - /* copy previous frame data */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(PaintWavePoint)); @@ -4379,7 +4432,8 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal int numOfN = 0, numOfRN = 0; int i; - if (wPoint->state > 0) continue; + if (wPoint->state > 0) + continue; /* calculate force from surrounding points */ for (i = 0; i < numOfNeighs; i++) { @@ -4387,8 +4441,9 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal float dist = bNeighs[n_index].dist * wave_scale; PaintWavePoint *tPoint = &prevPoint[sData->adj_data->n_target[n_index]]; - if (!dist || tPoint->state > 0) continue; - if (dist < min_dist) dist = min_dist; + if (!dist || tPoint->state > 0) + continue; + CLAMP_MIN(dist, min_dist); avg_dist += dist; numOfN++; @@ -4408,7 +4463,8 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal { /* if open borders, apply a fake height to keep waves going on */ avg_n_height = (numOfRN) ? avg_n_height / numOfRN : 0.0f; - wPoint->height = (dt * wave_speed * avg_n_height + wPoint->height * avg_dist) / (avg_dist + dt * wave_speed); + wPoint->height = (dt * wave_speed * avg_n_height + wPoint->height * avg_dist) / + (avg_dist + dt * wave_speed); } /* else do wave eq */ else { @@ -4427,8 +4483,10 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal if (wave_max_slope && avg_dist) { float max_offset = wave_max_slope * avg_dist; float offset = (numOfN) ? (avg_height / numOfN - wPoint->height) : 0.0f; - if (offset > max_offset) wPoint->height += offset - max_offset; - if (offset < -max_offset) wPoint->height += offset + max_offset; + if (offset > max_offset) + wPoint->height += offset - max_offset; + if (offset < -max_offset) + wPoint->height += offset + max_offset; } } } @@ -4465,8 +4523,10 @@ static void dynamicPaint_surfacePreStep(DynamicPaintSurface *surface, float time int i; float dry_ratio, f_color[4]; float p_wetness = pPoint->wetness; - value_dissolve(&pPoint->wetness, surface->dry_speed, timescale, (surface->flags & MOD_DPAINT_DRY_LOG)); - if (pPoint->wetness < 0.0f) pPoint->wetness = 0.0f; + + value_dissolve(&pPoint->wetness, surface->dry_speed, timescale, + (surface->flags & MOD_DPAINT_DRY_LOG) != 0); + CLAMP_MIN(pPoint->wetness, 0.0f); if (pPoint->wetness < surface->color_dry_threshold) { dry_ratio = pPoint->wetness / p_wetness; @@ -4489,7 +4549,8 @@ static void dynamicPaint_surfacePreStep(DynamicPaintSurface *surface, float time * with these new alpha values. (wet layer color doesnt change)*/ if (pPoint->color[3]) { for (i = 0; i < 3; i++) { - pPoint->color[i] = (f_color[i] * f_color[3] - pPoint->e_color[i] * pPoint->e_color[3]) / (pPoint->color[3] * (1.0f - pPoint->e_color[3])); + pPoint->color[i] = (f_color[i] * f_color[3] - pPoint->e_color[i] * pPoint->e_color[3]) / + (pPoint->color[3] * (1.0f - pPoint->e_color[3])); } } } @@ -4509,28 +4570,29 @@ static void dynamicPaint_surfacePreStep(DynamicPaintSurface *surface, float time } if (surface->flags & MOD_DPAINT_DISSOLVE) { - value_dissolve(&pPoint->color[3], surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); + value_dissolve(&pPoint->color[3], surface->diss_speed, timescale, + (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0); CLAMP_MIN(pPoint->color[3], 0.0f); - value_dissolve(&pPoint->e_color[3], surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); + value_dissolve(&pPoint->e_color[3], surface->diss_speed, timescale, + (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0); CLAMP_MIN(pPoint->e_color[3], 0.0f); } } /* dissolve for float types */ else if (surface->flags & MOD_DPAINT_DISSOLVE && - (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)) + (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)) { float *point = &((float *)sData->type_data)[index]; /* log or linear */ - value_dissolve(point, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); + value_dissolve(point, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0); CLAMP_MIN(*point, 0.0f); } } } -static int dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob) +static bool dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob) { PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; @@ -4539,50 +4601,26 @@ static int dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob int numOfVerts = dm->getNumVerts(dm); int i; - int ret = 0; + bool ret = false; - if (!bData->prev_verts) return 1; + if (!bData->prev_verts) + return true; /* matrix comparison */ - for (i = 0; i < 4; i++) { - int j; - for (j = 0; j < 4; j++) - if (bData->prev_obmat[i][j] != ob->obmat[i][j]) return 1; - } + if (!equals_m4m4(bData->prev_obmat, ob->obmat)) + return true; /* vertices */ #pragma omp parallel for schedule(static) for (i = 0; i < numOfVerts; i++) { - int j; - for (j = 0; j < 3; j++) - if (bData->prev_verts[i].co[j] != mvert[i].co[j]) { - ret = 1; - break; - } + if (!equals_v3v3(bData->prev_verts[i].co, mvert[i].co)) { + ret = true; + } } return ret; } -static int surface_needsVelocityData(DynamicPaintSurface *surface, const Scene *scene) -{ - if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) - return 1; - - if (surface_getBrushFlags(surface, scene) & BRUSH_USES_VELOCITY) - return 1; - - return 0; -} - -static int surface_needsAccelerationData(DynamicPaintSurface *surface) -{ - if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) - return 1; - - return 0; -} - /* Prepare for surface step by creating PaintBakeNormal data */ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Scene *scene, Object *ob) { @@ -4591,15 +4629,16 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce PaintBakeData *bData = sData->bData; DerivedMesh *dm = surface->canvas->dm; int index, new_bdata = 0; - int do_velocity_data = surface_needsVelocityData(surface, scene); - int do_accel_data = surface_needsAccelerationData(surface); + const bool do_velocity_data = ((surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) || + (surface_getBrushFlags(surface, scene) & BRUSH_USES_VELOCITY)); + const bool do_accel_data = (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) != 0; int canvasNumOfVerts = dm->getNumVerts(dm); MVert *mvert = dm->getVertArray(dm); Vec3f *canvas_verts; if (bData) { - int surface_moved = dynamicPaint_surfaceHasMoved(surface, ob); + const bool surface_moved = dynamicPaint_surfaceHasMoved(surface, ob); /* get previous speed for accelertaion */ if (do_accel_data && bData->prev_velocity && bData->velocity) @@ -4615,13 +4654,15 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce } canvas_verts = (struct Vec3f *) MEM_mallocN(canvasNumOfVerts * sizeof(struct Vec3f), "Dynamic Paint transformed canvas verts"); - if (!canvas_verts) return 0; + if (!canvas_verts) + return 0; /* allocate memory if required */ if (!bData) { sData->bData = bData = (struct PaintBakeData *) MEM_callocN(sizeof(struct PaintBakeData), "Dynamic Paint bake data"); if (!bData) { - if (canvas_verts) MEM_freeN(canvas_verts); + if (canvas_verts) + MEM_freeN(canvas_verts); return 0; } @@ -4634,11 +4675,16 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce /* if any allocation failed, free everything */ if (!bData->bNormal || !bData->s_pos || !bData->s_num || !bData->realCoord || !canvas_verts) { - if (bData->bNormal) MEM_freeN(bData->bNormal); - if (bData->s_pos) MEM_freeN(bData->s_pos); - if (bData->s_num) MEM_freeN(bData->s_num); - if (bData->realCoord) MEM_freeN(bData->realCoord); - if (canvas_verts) MEM_freeN(canvas_verts); + if (bData->bNormal) + MEM_freeN(bData->bNormal); + if (bData->s_pos) + MEM_freeN(bData->s_pos); + if (bData->s_num) + MEM_freeN(bData->s_num); + if (bData->realCoord) + MEM_freeN(bData->realCoord); + if (canvas_verts) + MEM_freeN(canvas_verts); return setError(surface->canvas, N_("Not enough free memory")); } @@ -4726,7 +4772,8 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce int t_index = adj_data->n_index[index] + (ss - 1); /* get vertex position at 1/3 of each neigh edge */ mul_v3_fl(bData->realCoord[bData->s_pos[index] + ss].v, 2.0f / 3.0f); - madd_v3_v3fl(bData->realCoord[bData->s_pos[index] + ss].v, canvas_verts[adj_data->n_target[t_index]].v, 1.0f / 3.0f); + madd_v3_v3fl(bData->realCoord[bData->s_pos[index] + ss].v, + canvas_verts[adj_data->n_target[t_index]].v, 1.0f / 3.0f); } } @@ -4755,8 +4802,7 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce normal_short_to_float_v3(n1, mvert[tPoint->v1].no); normal_short_to_float_v3(n2, mvert[tPoint->v2].no); normal_short_to_float_v3(n3, mvert[tPoint->v3].no); - interp_v3_v3v3v3(temp_nor, - n1, n2, n3, f_data->barycentricWeights[index * bData->s_num[index]].v); + interp_v3_v3v3v3(temp_nor, n1, n2, n3, f_data->barycentricWeights[index * bData->s_num[index]].v); } mul_v3_v3(temp_nor, ob->size); @@ -4774,7 +4820,7 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce /* generate surface space partitioning grid */ surfaceGenerateGrid(surface); /* calculate current frame adjacency point distances and global dirs */ - dynamicPaint_prepareAdjacencyData(surface, 0); + dynamicPaint_prepareAdjacencyData(surface, false); /* Copy current frame vertices to check against in next frame */ copy_m4_m4(bData->prev_obmat, ob->obmat); @@ -4794,7 +4840,8 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su PaintBakeData *bData = sData->bData; DynamicPaintCanvasSettings *canvas = surface->canvas; int ret = 1; - if (sData->total_points < 1) return 0; + if (sData->total_points < 1) + return 0; dynamicPaint_surfacePreStep(surface, timescale); /* @@ -4820,24 +4867,23 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su brushObj = NULL; /* select object */ if (surface->brush_group) { - if (go->ob) brushObj = go->ob; + if (go->ob) + brushObj = go->ob; } else brushObj = base->object; - if (!brushObj) { - /* skip item */ - if (surface->brush_group) go = go->next; - else base = base->next; - continue; - } - /* next item */ if (surface->brush_group) go = go->next; else base = base->next; + if (!brushObj) { + /* skip item */ + continue; + } + /* check if target has an active dp modifier */ md = modifiers_findByType(brushObj, eModifierType_DynamicPaint); if (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) { @@ -4852,15 +4898,16 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su bData->brush_velocity = MEM_callocN(sData->total_points * sizeof(float) * 4, "Dynamic Paint brush velocity"); /* init adjacency data if not already */ if (!sData->adj_data) - dynamicPaint_initAdjacencyData(surface, 1); + dynamicPaint_initAdjacencyData(surface, true); if (!bData->bNeighs) - dynamicPaint_prepareAdjacencyData(surface, 1); + dynamicPaint_prepareAdjacencyData(surface, true); } /* update object data on this subframe */ if (subframe) { scene_setSubframe(scene, subframe); - BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION, + BKE_scene_frame_get(scene), eModifierType_DynamicPaint); } /* Prepare materials if required */ if (brush_usesMaterial(brush, scene)) @@ -4869,12 +4916,13 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su /* Apply brush on the surface depending on it's collision type */ /* Particle brush: */ if (brush->collision == MOD_DPAINT_COL_PSYS) { - if (brush->psys && brush->psys->part && ELEM(brush->psys->part->type, PART_EMITTER, PART_FLUID) && + if (brush->psys && brush->psys->part && + ELEM(brush->psys->part->type, PART_EMITTER, PART_FLUID) && psys_check_enabled(brushObj, brush->psys)) { - /* Paint a particle system */ - BKE_animsys_evaluate_animdata(scene, &brush->psys->part->id, brush->psys->part->adt, BKE_scene_frame_get(scene), ADT_RECALC_ANIM); + BKE_animsys_evaluate_animdata(scene, &brush->psys->part->id, brush->psys->part->adt, + BKE_scene_frame_get(scene), ADT_RECALC_ANIM); dynamicPaint_paintParticles(surface, brush->psys, brush, timescale); } } @@ -4894,7 +4942,8 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su if (subframe) { scene->r.cfra = scene_frame; scene->r.subframe = scene_subframe; - BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION, + BKE_scene_frame_get(scene), eModifierType_DynamicPaint); } /* process special brush effects, like smudge */ @@ -4934,8 +4983,10 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su } /* Free temporary effect data */ - if (prevPoint) MEM_freeN(prevPoint); - if (force) MEM_freeN(force); + if (prevPoint) + MEM_freeN(prevPoint); + if (force) + MEM_freeN(force); } } @@ -4963,7 +5014,8 @@ int dynamicPaint_calculateFrame(DynamicPaintSurface *surface, Scene *scene, Obje for (st = 1; st <= surface->substeps; st++) { float subframe = ((float) st) / (surface->substeps + 1); - if (!dynamicPaint_doStep(scene, cObject, surface, timescale, subframe)) return 0; + if (!dynamicPaint_doStep(scene, cObject, surface, timescale, subframe)) + return 0; } } diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index 96bdfe88722..6c117447664 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -44,6 +44,7 @@ #include "BLI_math.h" #include "BLI_jitter.h" #include "BLI_bitmap.h" +#include "BLI_task.h" #include "BKE_cdderivedmesh.h" #include "BKE_mesh.h" @@ -451,118 +452,148 @@ finally: pRes[3] = fSign; } +static void emDM_calc_loop_tangents_thread(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + struct SGLSLEditMeshToTangent *mesh2tangent = taskdata; + /* new computation method */ + { + SMikkTSpaceContext sContext = {NULL}; + SMikkTSpaceInterface sInterface = {NULL}; + sContext.m_pUserData = mesh2tangent; + sContext.m_pInterface = &sInterface; + sInterface.m_getNumFaces = emdm_ts_GetNumFaces; + sInterface.m_getNumVerticesOfFace = emdm_ts_GetNumVertsOfFace; + sInterface.m_getPosition = emdm_ts_GetPosition; + sInterface.m_getTexCoord = emdm_ts_GetTextureCoordinate; + sInterface.m_getNormal = emdm_ts_GetNormal; + sInterface.m_setTSpaceBasic = emdm_ts_SetTSpace; + /* 0 if failed */ + genTangSpaceDefault(&sContext); + } +} + /** * \see #DM_calc_loop_tangents, same logic but used arrays instead of #BMesh data. * * \note This function is not so normal, its using `bm->ldata` as input, but output's to `dm->loopData`. * This is done because #CD_TANGENT is cache data used only for drawing. */ -static void emDM_calcLoopTangents(DerivedMesh *dm) + +static void emDM_calc_loop_tangents( + DerivedMesh *dm, bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], int tangent_names_count) { EditDerivedBMesh *bmdm = (EditDerivedBMesh *)dm; BMEditMesh *em = bmdm->em; BMesh *bm = bmdm->em->bm; - - /* mesh vars */ - int cd_loop_uv_offset; - float (*orco)[3] = NULL, (*tangent)[4]; - int /* totvert, */ totface; - const float (*fnors)[3]; - const float (*tlnors)[3]; - char htype_index = BM_LOOP; - - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) != -1) + if (CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV) == 0) return; - fnors = bmdm->polyNos; /* dm->getPolyDataArray(dm, CD_NORMAL) */ - - /* Note, we assume we do have tessellated loop normals at this point (in case it is object-enabled), - * have to check this is valid... - */ - tlnors = dm->getLoopDataArray(dm, CD_NORMAL); - - /* check we have all the needed layers */ - totface = em->tottri; - - cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - - /* needed for indexing loop-tangents */ - htype_index = BM_LOOP; - if (cd_loop_uv_offset == -1) { - orco = dm->getVertDataArray(dm, CD_ORCO); - if (!orco) - return; - /* needed for orco lookups */ - htype_index |= BM_VERT; - } - - if (fnors) { - /* needed for face normal lookups */ - htype_index |= BM_FACE; - } - - BM_mesh_elem_index_ensure(bm, htype_index); + int act_uv_n = -1; + int ren_uv_n = -1; + bool calc_act = false; + bool calc_ren = false; + char act_uv_name[MAX_NAME]; + char ren_uv_name[MAX_NAME]; + char tangent_mask = 0; + + DM_calc_loop_tangents_step_0( + &bm->ldata, calc_active_tangent, tangent_names, tangent_names_count, + &calc_act, &calc_ren, &act_uv_n, &ren_uv_n, act_uv_name, ren_uv_name, &tangent_mask); + + if ((dm->tangent_mask | tangent_mask) != dm->tangent_mask) { + for (int i = 0; i < tangent_names_count; i++) + if (tangent_names[i][0]) + DM_add_named_tangent_layer_for_uv(&bm->ldata, &dm->loopData, dm->numLoopData, tangent_names[i]); + if (calc_act && act_uv_name[0]) + DM_add_named_tangent_layer_for_uv(&bm->ldata, &dm->loopData, dm->numLoopData, act_uv_name); + if (calc_ren && ren_uv_name[0]) + DM_add_named_tangent_layer_for_uv(&bm->ldata, &dm->loopData, dm->numLoopData, ren_uv_name); + int totface = em->tottri; +#ifdef USE_LOOPTRI_DETECT_QUADS + int num_face_as_quad_map; + int *face_as_quad_map = NULL; - /* create tangent layer */ - DM_add_loop_layer(dm, CD_TANGENT, CD_CALLOC, NULL); - tangent = DM_get_loop_data_layer(dm, CD_TANGENT); + /* map faces to quads */ + if (bmdm->em->tottri != bm->totface) { + /* over alloc, since we dont know how many ngon or quads we have */ -#ifdef USE_LOOPTRI_DETECT_QUADS - int num_face_as_quad_map; - int *face_as_quad_map = NULL; - - /* map faces to quads */ - if (bmdm->em->tottri != bm->totface) { - /* over alloc, since we dont know how many ngon or quads we have */ - - /* map fake face index to looptri */ - face_as_quad_map = MEM_mallocN(sizeof(int) * totface, __func__); - int i, j; - for (i = 0, j = 0; j < totface; i++, j++) { - face_as_quad_map[i] = j; - /* step over all quads */ - if (em->looptris[j][0]->f->len == 4) { - j++; /* skips the nest looptri */ + /* map fake face index to looptri */ + face_as_quad_map = MEM_mallocN(sizeof(int) * totface, __func__); + int i, j; + for (i = 0, j = 0; j < totface; i++, j++) { + face_as_quad_map[i] = j; + /* step over all quads */ + if (em->looptris[j][0]->f->len == 4) { + j++; /* skips the nest looptri */ + } } + num_face_as_quad_map = i; + } + else { + num_face_as_quad_map = totface; } - num_face_as_quad_map = i; - } - else { - num_face_as_quad_map = totface; - } #endif - - /* new computation method */ - { - SGLSLEditMeshToTangent mesh2tangent = {NULL}; - SMikkTSpaceContext sContext = {NULL}; - SMikkTSpaceInterface sInterface = {NULL}; - - mesh2tangent.precomputedFaceNormals = fnors; - mesh2tangent.precomputedLoopNormals = tlnors; - mesh2tangent.looptris = (const BMLoop *(*)[3])em->looptris; - mesh2tangent.cd_loop_uv_offset = cd_loop_uv_offset; - mesh2tangent.orco = (const float (*)[3])orco; - mesh2tangent.tangent = tangent; - mesh2tangent.numTessFaces = totface; - + /* Calculation */ + { + TaskScheduler *scheduler = BLI_task_scheduler_get(); + TaskPool *task_pool; + task_pool = BLI_task_pool_create(scheduler, NULL); + + dm->tangent_mask = 0; + /* Calculate tangent layers */ + SGLSLEditMeshToTangent data_array[MAX_MTFACE]; + int index = 0; + int n = 0; + CustomData_update_typemap(&dm->loopData); + const int tangent_layer_num = CustomData_number_of_layers(&dm->loopData, CD_TANGENT); + for (n = 0; n < tangent_layer_num; n++) { + index = CustomData_get_layer_index_n(&dm->loopData, CD_TANGENT, n); + BLI_assert(n < MAX_MTFACE); + SGLSLEditMeshToTangent *mesh2tangent = &data_array[n]; + mesh2tangent->numTessFaces = em->tottri; #ifdef USE_LOOPTRI_DETECT_QUADS - mesh2tangent.face_as_quad_map = face_as_quad_map; - mesh2tangent.num_face_as_quad_map = num_face_as_quad_map; + mesh2tangent->face_as_quad_map = face_as_quad_map; + mesh2tangent->num_face_as_quad_map = num_face_as_quad_map; #endif + mesh2tangent->precomputedFaceNormals = bmdm->polyNos; /* dm->getPolyDataArray(dm, CD_NORMAL) */ + /* Note, we assume we do have tessellated loop normals at this point (in case it is object-enabled), + * have to check this is valid... + */ + mesh2tangent->precomputedLoopNormals = CustomData_get_layer(&dm->loopData, CD_NORMAL); + mesh2tangent->cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, n); + + /* needed for indexing loop-tangents */ + int htype_index = BM_LOOP; + if (mesh2tangent->cd_loop_uv_offset == -1) { + mesh2tangent->orco = dm->getVertDataArray(dm, CD_ORCO); + if (!mesh2tangent->orco) + continue; + /* needed for orco lookups */ + htype_index |= BM_VERT; + } + if (mesh2tangent->precomputedFaceNormals) { + /* needed for face normal lookups */ + htype_index |= BM_FACE; + } + BM_mesh_elem_index_ensure(bm, htype_index); + + mesh2tangent->looptris = (const BMLoop *(*)[3])em->looptris; + mesh2tangent->tangent = dm->loopData.layers[index].data; + + /* Fill the resulting tangent_mask */ + int uv_ind = CustomData_get_named_layer_index(&bm->ldata, CD_MLOOPUV, dm->loopData.layers[index].name); + int uv_start = CustomData_get_layer_index(&bm->ldata, CD_MLOOPUV); + BLI_assert(uv_ind != -1 && uv_start != -1); + BLI_assert(uv_ind - uv_start < MAX_MTFACE); + dm->tangent_mask |= 1 << (uv_ind - uv_start); + BLI_task_pool_push(task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, TASK_PRIORITY_LOW); + } - sContext.m_pUserData = &mesh2tangent; - sContext.m_pInterface = &sInterface; - sInterface.m_getNumFaces = emdm_ts_GetNumFaces; - sInterface.m_getNumVerticesOfFace = emdm_ts_GetNumVertsOfFace; - sInterface.m_getPosition = emdm_ts_GetPosition; - sInterface.m_getTexCoord = emdm_ts_GetTextureCoordinate; - sInterface.m_getNormal = emdm_ts_GetNormal; - sInterface.m_setTSpaceBasic = emdm_ts_SetTSpace; - - /* 0 if failed */ - genTangSpaceDefault(&sContext); - + BLI_assert(dm->tangent_mask == tangent_mask); + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + } #ifdef USE_LOOPTRI_DETECT_QUADS if (face_as_quad_map) { MEM_freeN(face_as_quad_map); @@ -570,6 +601,15 @@ static void emDM_calcLoopTangents(DerivedMesh *dm) #undef USE_LOOPTRI_DETECT_QUADS #endif } + /* Update active layer index */ + int uv_index = CustomData_get_layer_index_n(&bm->ldata, CD_MLOOPUV, act_uv_n); + int tan_index = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, bm->ldata.layers[uv_index].name); + CustomData_set_layer_active_index(&dm->loopData, CD_TANGENT, tan_index); + + /* Update render layer index */ + uv_index = CustomData_get_layer_index_n(&bm->ldata, CD_MLOOPUV, ren_uv_n); + tan_index = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, bm->ldata.layers[uv_index].name); + CustomData_set_layer_render_index(&dm->loopData, CD_TANGENT, tan_index); } /** \} */ @@ -1419,15 +1459,16 @@ static void emdm_pass_attrib_vertex_glsl(const DMVertexAttribs *attribs, const B } glVertexAttrib4ubv(attribs->mcol[i].gl_index, col); } - if (attribs->tottang) { + + for (i = 0; i < attribs->tottang; i++) { const float *tang; - if (attribs->tang.em_offset != -1) { - tang = attribs->tang.array[BM_elem_index_get(loop)]; + if (attribs->tang[i].em_offset != -1) { + tang = attribs->tang[i].array[BM_elem_index_get(loop)]; } else { tang = zero; } - glVertexAttrib4fv(attribs->tang.gl_index, tang); + glVertexAttrib4fv(attribs->tang[i].gl_index, tang); } } @@ -2218,7 +2259,7 @@ DerivedMesh *getEditDerivedBMesh( bmdm->dm.calcNormals = emDM_calcNormals; bmdm->dm.calcLoopNormals = emDM_calcLoopNormals; bmdm->dm.calcLoopNormalsSpaceArray = emDM_calcLoopNormalsSpaceArray; - bmdm->dm.calcLoopTangents = emDM_calcLoopTangents; + bmdm->dm.calcLoopTangents = emDM_calc_loop_tangents; bmdm->dm.recalcTessellation = emDM_recalcTessellation; bmdm->dm.recalcLoopTri = emDM_recalcLoopTri; diff --git a/source/blender/blenkernel/intern/editmesh_bvh.c b/source/blender/blenkernel/intern/editmesh_bvh.c index ccea2d4ece4..2927354241c 100644 --- a/source/blender/blenkernel/intern/editmesh_bvh.c +++ b/source/blender/blenkernel/intern/editmesh_bvh.c @@ -544,7 +544,7 @@ BVHTreeOverlap *BKE_bmbvh_overlap(const BMBVHTree *bmtree_a, const BMBVHTree *bm data.tree_pair[0] = bmtree_a; data.tree_pair[1] = bmtree_b; - data.epsilon = max_ff(BLI_bvhtree_getepsilon(bmtree_a->tree), BLI_bvhtree_getepsilon(bmtree_b->tree)); + data.epsilon = max_ff(BLI_bvhtree_get_epsilon(bmtree_a->tree), BLI_bvhtree_get_epsilon(bmtree_b->tree)); return BLI_bvhtree_overlap(bmtree_a->tree, bmtree_b->tree, r_overlap_tot, bmbvh_overlap_cb, &data); }
\ No newline at end of file diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 12bce70594b..6284b3a46fb 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -746,13 +746,15 @@ static void do_texture_effector(EffectorCache *eff, EffectorData *efd, EffectedP copy_v3_v3(tex_co, point->loc); - if (eff->pd->flag & PFIELD_TEX_2D) { - float fac=-dot_v3v3(tex_co, efd->nor); - madd_v3_v3fl(tex_co, efd->nor, fac); - } - if (eff->pd->flag & PFIELD_TEX_OBJECT) { mul_m4_v3(eff->ob->imat, tex_co); + + if (eff->pd->flag & PFIELD_TEX_2D) + tex_co[2] = 0.0f; + } + else if (eff->pd->flag & PFIELD_TEX_2D) { + float fac=-dot_v3v3(tex_co, efd->nor); + madd_v3_v3fl(tex_co, efd->nor, fac); } scene_color_manage = BKE_scene_check_color_management_enabled(eff->scene); diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 11b1bbd64ac..a2b5a05feac 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1600,8 +1600,8 @@ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) /* Driver API --------------------------------- */ -/* This frees the driver variable itself */ -void driver_free_variable(ChannelDriver *driver, DriverVar *dvar) +/* Perform actual freeing driver variable and remove it from the given list */ +void driver_free_variable(ListBase *variables, DriverVar *dvar) { /* sanity checks */ if (dvar == NULL) @@ -1621,8 +1621,15 @@ void driver_free_variable(ChannelDriver *driver, DriverVar *dvar) DRIVER_TARGETS_LOOPER_END /* remove the variable from the driver */ - BLI_freelinkN(&driver->variables, dvar); + BLI_freelinkN(variables, dvar); +} +/* Free the driver variable and do extra updates */ +void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar) +{ + /* remove and free the driver variable */ + driver_free_variable(&driver->variables, dvar); + #ifdef WITH_PYTHON /* since driver variables are cached, the expression needs re-compiling too */ if (driver->type == DRIVER_TYPE_PYTHON) @@ -1630,6 +1637,24 @@ void driver_free_variable(ChannelDriver *driver, DriverVar *dvar) #endif } +/* Copy driver variables from src_vars list to dst_vars list */ +void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars) +{ + BLI_assert(BLI_listbase_is_empty(dst_vars)); + BLI_duplicatelist(dst_vars, src_vars); + + for (DriverVar *dvar = dst_vars->first; dvar; dvar = dvar->next) { + /* need to go over all targets so that we don't leave any dangling paths */ + DRIVER_TARGETS_LOOPER(dvar) + { + /* make a copy of target's rna path if available */ + if (dtar->rna_path) + dtar->rna_path = MEM_dupallocN(dtar->rna_path); + } + DRIVER_TARGETS_LOOPER_END + } +} + /* Change the type of driver variable */ void driver_change_variable_type(DriverVar *dvar, int type) { @@ -1770,7 +1795,7 @@ void fcurve_free_driver(FCurve *fcu) /* free driver targets */ for (dvar = driver->variables.first; dvar; dvar = dvarn) { dvarn = dvar->next; - driver_free_variable(driver, dvar); + driver_free_variable_ex(driver, dvar); } #ifdef WITH_PYTHON @@ -1788,7 +1813,6 @@ void fcurve_free_driver(FCurve *fcu) ChannelDriver *fcurve_copy_driver(ChannelDriver *driver) { ChannelDriver *ndriver; - DriverVar *dvar; /* sanity checks */ if (driver == NULL) @@ -1799,19 +1823,8 @@ ChannelDriver *fcurve_copy_driver(ChannelDriver *driver) ndriver->expr_comp = NULL; /* copy variables */ - BLI_listbase_clear(&ndriver->variables); - BLI_duplicatelist(&ndriver->variables, &driver->variables); - - for (dvar = ndriver->variables.first; dvar; dvar = dvar->next) { - /* need to go over all targets so that we don't leave any dangling paths */ - DRIVER_TARGETS_LOOPER(dvar) - { - /* make a copy of target's rna path if available */ - if (dtar->rna_path) - dtar->rna_path = MEM_dupallocN(dtar->rna_path); - } - DRIVER_TARGETS_LOOPER_END - } + BLI_listbase_clear(&ndriver->variables); /* to get rid of refs to non-copied data (that's still used on original) */ + driver_variables_copy(&ndriver->variables, &driver->variables); /* return the new driver */ return ndriver; diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 0a887dcf676..aed33d2c64d 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -1040,7 +1040,7 @@ makebreak: timeofs += distfac * cu->xof; /* not cyclic */ ct = chartransdata; - for (i = 0; i <= slen; i++, ct++) { + for (i = 0; i < slen; i++, ct++) { float ctime, dtime, vec[4], tvec[4], rotvec[3]; float si, co; @@ -1082,8 +1082,9 @@ makebreak: sb = &selboxes[i - selstart]; sb->rot = -ct->rot; } - } + /* null character is always zero width, no need to iterate over it */ + chartransdata[slen] = chartransdata[slen - 1]; } } diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 485c4f5b29f..f3eb5430bce 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -68,6 +68,7 @@ bool free_gpencil_strokes(bGPDframe *gpf) /* free stroke memory arrays, then stroke itself */ if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } @@ -261,6 +262,11 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ ARRAY_SET_ITEMS(gpl->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ + /* high quality fill by default */ + gpl->flag |= GP_LAYER_HQ_FILL; + + /* default smooth iterations */ + gpl->draw_smoothlvl = 1; /* auto-name */ BLI_strncpy(gpl->info, name, sizeof(gpl->info)); @@ -315,7 +321,8 @@ bGPDframe *gpencil_frame_duplicate(bGPDframe *src) /* make copy of source stroke, then adjust pointer to points too */ gpsd = MEM_dupallocN(gps); gpsd->points = MEM_dupallocN(gps->points); - + gpsd->triangles = MEM_dupallocN(gps->triangles); + gpsd->flag |= GP_STROKE_RECALC_CACHES; BLI_addtail(&dst->strokes, gpsd); } @@ -424,6 +431,7 @@ void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) /* free the stroke and its data */ MEM_freeN(gps->points); + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); /* if frame has no strokes after this, delete it */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 63cc57d0a21..d4551fd0b2c 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -350,27 +350,41 @@ void BKE_image_free(Image *ima) } /* only image block itself */ -static Image *image_alloc(Main *bmain, const char *name, short source, short type) +static void image_init(Image *ima, short source, short type) { - Image *ima; + BLI_assert(MEMCMP_STRUCT_OFS_IS_ZERO(ima, id)); - ima = BKE_libblock_alloc(bmain, ID_IM, name); - if (ima) { - ima->ok = IMA_OK; + ima->ok = IMA_OK; + + ima->xrep = ima->yrep = 1; + ima->aspx = ima->aspy = 1.0; + ima->gen_x = 1024; ima->gen_y = 1024; + ima->gen_type = IMA_GENTYPE_GRID; - ima->xrep = ima->yrep = 1; - ima->aspx = ima->aspy = 1.0; - ima->gen_x = 1024; ima->gen_y = 1024; - ima->gen_type = 1; /* no defines yet? */ + ima->source = source; + ima->type = type; - ima->source = source; - ima->type = type; + if (source == IMA_SRC_VIEWER) + ima->flag |= IMA_VIEW_AS_RENDER; - if (source == IMA_SRC_VIEWER) - 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"); +} - BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); - ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); +void BKE_image_init(struct Image *image) +{ + if (image) { + image_init(image, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); + } +} + +static Image *image_alloc(Main *bmain, const char *name, short source, short type) +{ + Image *ima; + + ima = BKE_libblock_alloc(bmain, ID_IM, name); + if (ima) { + image_init(ima, source, type); } return ima; @@ -828,13 +842,6 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char break; } - if (rect_float) { - /* both byte and float buffers are filling in sRGB space, need to linearize float buffer after BKE_image_buf_fill* functions */ - - IMB_buffer_float_from_float(rect_float, rect_float, ibuf->channels, IB_PROFILE_LINEAR_RGB, IB_PROFILE_SRGB, - true, ibuf->x, ibuf->y, ibuf->x, ibuf->x); - } - return ibuf; } @@ -1992,7 +1999,7 @@ void BKE_image_stamp_buf( BLF_wordwrap(mono, width - (BUFF_MARGIN_X * 2)); BLF_buffer(mono, rectf, rect, width, height, channels, display); - BLF_buffer_col(mono, UNPACK4(scene->r.fg_stamp)); + BLF_buffer_col(mono, scene->r.fg_stamp); pad = BLF_width_max(mono); /* use 'h_fixed' rather than 'h', aligns better */ @@ -4494,13 +4501,17 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *width, int *height) ImBuf *ibuf = NULL; void *lock; - ibuf = BKE_image_acquire_ibuf(image, iuser, &lock); + if (image != NULL) { + ibuf = BKE_image_acquire_ibuf(image, iuser, &lock); + } if (ibuf && ibuf->x > 0 && ibuf->y > 0) { *width = ibuf->x; *height = ibuf->y; } - else if (image->type == IMA_TYPE_R_RESULT && iuser != NULL && iuser->scene != NULL) { + else if (image != NULL && image->type == IMA_TYPE_R_RESULT && + iuser != NULL && iuser->scene != NULL) + { Scene *scene = iuser->scene; *width = (scene->r.xsch * scene->r.size) / 100; *height = (scene->r.ysch * scene->r.size) / 100; @@ -4514,7 +4525,9 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *width, int *height) *height = IMG_SIZE_FALLBACK; } - BKE_image_release_ibuf(image, ibuf, lock); + if (image != NULL) { + BKE_image_release_ibuf(image, ibuf, lock); + } } void BKE_image_get_size_fl(Image *image, ImageUser *iuser, float size[2]) diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c index 303d0c6adfc..2c8399adece 100644 --- a/source/blender/blenkernel/intern/image_gen.c +++ b/source/blender/blenkernel/intern/image_gen.c @@ -33,30 +33,42 @@ #include "BKE_image.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "BLF_api.h" -void BKE_image_buf_fill_color(unsigned char *rect, float *rect_float, int width, int height, const float color[4]) +typedef struct FillColorThreadData { + unsigned char *rect; + float *rect_float; + int width; + float color[4]; +} FillColorThreadData; + +static void image_buf_fill_color_slice(unsigned char *rect, + float *rect_float, + int width, int height, + const float color[4]) { int x, y; /* blank image */ if (rect_float) { + float linear_color[4]; + srgb_to_linearrgb_v4(linear_color, color); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - copy_v4_v4(rect_float, color); + copy_v4_v4(rect_float, linear_color); rect_float += 4; } } } - + if (rect) { unsigned char ccol[4]; - rgba_float_to_uchar(ccol, color); - for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - rect[0] = ccol[0]; rect[1] = ccol[1]; rect[2] = ccol[2]; @@ -67,37 +79,79 @@ void BKE_image_buf_fill_color(unsigned char *rect, float *rect_float, int width, } } +static void image_buf_fill_color_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + FillColorThreadData *data = (FillColorThreadData *)data_v; + size_t offset = ((size_t)start_scanline) * data->width * 4; + unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; + float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; + image_buf_fill_color_slice(rect, + rect_float, + data->width, + num_scanlines, + data->color); +} -void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int width, int height) +void BKE_image_buf_fill_color(unsigned char *rect, + float *rect_float, + int width, int height, + const float color[4]) { - /* these two passes could be combined into one, but it's more readable and + if (((size_t)width) * height < 64 * 64) { + image_buf_fill_color_slice(rect, rect_float, width, height, color); + } + else { + FillColorThreadData data; + data.rect = rect; + data.rect_float = rect_float; + data.width = width; + copy_v4_v4(data.color, color); + IMB_processor_apply_threaded_scanlines( + height, image_buf_fill_color_thread_do, &data); + } +} + +static void image_buf_fill_checker_slice(unsigned char *rect, + float *rect_float, + int width, int height, + int offset) +{ + /* these two passes could be combined into one, but it's more readable and * easy to tweak like this, speed isn't really that much of an issue in this situation... */ - + int checkerwidth = 32, dark = 1; int x, y; unsigned char *rect_orig = rect; float *rect_float_orig = rect_float; - + float h = 0.0, hoffs = 0.0; float hsv[3] = {0.0f, 0.9f, 0.9f}; float rgb[3]; + float dark_linear_color, bright_linear_color; + if (rect_float != NULL) { + dark_linear_color = srgb_to_linearrgb(0.25f); + bright_linear_color = srgb_to_linearrgb(0.58f); + } + /* checkers */ - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { dark = powf(-1.0f, floorf(y / checkerwidth)); - + for (x = 0; x < width; x++) { if (x % checkerwidth == 0) dark = -dark; - + if (rect_float) { if (dark > 0) { - rect_float[0] = rect_float[1] = rect_float[2] = 0.25f; + rect_float[0] = rect_float[1] = rect_float[2] = dark_linear_color; rect_float[3] = 1.0f; } else { - rect_float[0] = rect_float[1] = rect_float[2] = 0.58f; + rect_float[0] = rect_float[1] = rect_float[2] = bright_linear_color; rect_float[3] = 1.0f; } rect_float += 4; @@ -120,12 +174,12 @@ void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int widt rect_float = rect_float_orig; /* 2nd pass, colored + */ - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { hoffs = 0.125f * floorf(y / checkerwidth); - + for (x = 0; x < width; x++) { h = 0.125f * floorf(x / checkerwidth); - + if ((abs((x % checkerwidth) - (checkerwidth / 2)) < 4) && (abs((y % checkerwidth) - (checkerwidth / 2)) < 4)) { @@ -134,18 +188,16 @@ void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int widt { hsv[0] = fmodf(fabsf(h - hoffs), 1.0f); hsv_to_rgb_v(hsv, rgb); - + if (rect) { rect[0] = (char)(rgb[0] * 255.0f); rect[1] = (char)(rgb[1] * 255.0f); rect[2] = (char)(rgb[2] * 255.0f); rect[3] = 255; } - + if (rect_float) { - rect_float[0] = rgb[0]; - rect_float[1] = rgb[1]; - rect_float[2] = rgb[2]; + srgb_to_linearrgb_v3_v3(rect_float, rgb); rect_float[3] = 1.0f; } } @@ -157,13 +209,55 @@ void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int widt } } +typedef struct FillCheckerThreadData { + unsigned char *rect; + float *rect_float; + int width; +} FillCheckerThreadData; + +static void image_buf_fill_checker_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + FillCheckerThreadData *data = (FillCheckerThreadData *)data_v; + size_t offset = ((size_t)start_scanline) * data->width * 4; + unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; + float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; + image_buf_fill_checker_slice(rect, + rect_float, + data->width, + num_scanlines, + start_scanline); +} + +void BKE_image_buf_fill_checker(unsigned char *rect, + float *rect_float, + int width, int height) +{ + if (((size_t)width) * height < 64 * 64) { + image_buf_fill_checker_slice(rect, rect_float, width, height, 0); + } + else { + FillCheckerThreadData data; + data.rect = rect; + data.rect_float = rect_float; + data.width = width; + IMB_processor_apply_threaded_scanlines( + height, image_buf_fill_checker_thread_do, &data); + } +} /* Utility functions for BKE_image_buf_fill_checker_color */ #define BLEND_FLOAT(real, add) (real + add <= 1.0f) ? (real + add) : 1.0f #define BLEND_CHAR(real, add) ((real + (char)(add * 255.0f)) <= 255) ? (real + (char)(add * 255.0f)) : 255 -static void checker_board_color_fill(unsigned char *rect, float *rect_float, int width, int height) +static void checker_board_color_fill(unsigned char *rect, + float *rect_float, + int width, + int height, + int offset, + int total_height) { int hue_step, y, x; float hsv[3], rgb[3]; @@ -173,9 +267,9 @@ static void checker_board_color_fill(unsigned char *rect, float *rect_float, int hue_step = power_of_2_max_i(width / 8); if (hue_step < 8) hue_step = 8; - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { - hsv[2] = 0.1 + (y * (0.4 / height)); /* use a number lower then 1.0 else its too bright */ + hsv[2] = 0.1 + (y * (0.4 / total_height)); /* use a number lower then 1.0 else its too bright */ for (x = 0; x < width; x++) { hsv[0] = (float)((double)(x / hue_step) * 1.0 / width * hue_step); hsv_to_rgb_v(hsv, rgb); @@ -185,7 +279,7 @@ static void checker_board_color_fill(unsigned char *rect, float *rect_float, int rect[1] = (char)(rgb[1] * 255.0f); rect[2] = (char)(rgb[2] * 255.0f); rect[3] = 255; - + rect += 4; } @@ -194,27 +288,35 @@ static void checker_board_color_fill(unsigned char *rect, float *rect_float, int rect_float[1] = rgb[1]; rect_float[2] = rgb[2]; rect_float[3] = 1.0f; - + rect_float += 4; } } } } -static void checker_board_color_tint(unsigned char *rect, float *rect_float, int width, int height, int size, float blend) +static void checker_board_color_tint(unsigned char *rect, + float *rect_float, + int width, + int height, + int size, + float blend, + int offset) { int x, y; float blend_half = blend * 0.5f; - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { for (x = 0; x < width; x++) { - if (((y / size) % 2 == 1 && (x / size) % 2 == 1) || ( (y / size) % 2 == 0 && (x / size) % 2 == 0)) { + if (((y / size) % 2 == 1 && (x / size) % 2 == 1) || + ((y / size) % 2 == 0 && (x / size) % 2 == 0)) + { if (rect) { rect[0] = (char)BLEND_CHAR(rect[0], blend); rect[1] = (char)BLEND_CHAR(rect[1], blend); rect[2] = (char)BLEND_CHAR(rect[2], blend); rect[3] = 255; - + rect += 4; } if (rect_float) { @@ -222,7 +324,7 @@ static void checker_board_color_tint(unsigned char *rect, float *rect_float, int rect_float[1] = BLEND_FLOAT(rect_float[1], blend); rect_float[2] = BLEND_FLOAT(rect_float[2], blend); rect_float[3] = 1.0f; - + rect_float += 4; } } @@ -232,7 +334,7 @@ static void checker_board_color_tint(unsigned char *rect, float *rect_float, int rect[1] = (char)BLEND_CHAR(rect[1], blend_half); rect[2] = (char)BLEND_CHAR(rect[2], blend_half); rect[3] = 255; - + rect += 4; } if (rect_float) { @@ -240,19 +342,24 @@ static void checker_board_color_tint(unsigned char *rect, float *rect_float, int rect_float[1] = BLEND_FLOAT(rect_float[1], blend_half); rect_float[2] = BLEND_FLOAT(rect_float[2], blend_half); rect_float[3] = 1.0f; - + rect_float += 4; } } - + } } } -static void checker_board_grid_fill(unsigned char *rect, float *rect_float, int width, int height, float blend) +static void checker_board_grid_fill(unsigned char *rect, + float *rect_float, + int width, + int height, + float blend, + int offset) { int x, y; - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { for (x = 0; x < width; x++) { if (((y % 32) == 0) || ((x % 32) == 0) || x == 0) { if (rect) { @@ -268,7 +375,7 @@ static void checker_board_grid_fill(unsigned char *rect, float *rect_float, int rect_float[1] = BLEND_FLOAT(rect_float[1], blend); rect_float[2] = BLEND_FLOAT(rect_float[2], blend); rect_float[3] = 1.0f; - + rect_float += 4; } } @@ -282,7 +389,12 @@ static void checker_board_grid_fill(unsigned char *rect, float *rect_float, int /* defined in image.c */ -static void checker_board_text(unsigned char *rect, float *rect_float, int width, int height, int step, int outline) +static void checker_board_text(unsigned char *rect, + float *rect_float, + int width, + int height, + int step, + int outline) { int x, y; int pen_x, pen_y; @@ -297,6 +409,9 @@ static void checker_board_text(unsigned char *rect, float *rect_float, int width */ BLF_buffer(mono, rect_float, rect, width, height, 4, NULL); + const float text_color[4] = {0.0, 0.0, 0.0, 1.0}; + const float text_outline[4] = {1.0, 1.0, 1.0, 1.0}; + for (y = 0; y < height; y += step) { text[1] = '1'; @@ -306,7 +421,7 @@ static void checker_board_text(unsigned char *rect, float *rect_float, int width pen_y = y + 44; /* terribly crappy outline font! */ - BLF_buffer_col(mono, 1.0, 1.0, 1.0, 1.0); + BLF_buffer_col(mono, text_outline); BLF_position(mono, pen_x - outline, pen_y, 0.0); BLF_draw_buffer(mono, text, 2); @@ -326,7 +441,7 @@ static void checker_board_text(unsigned char *rect, float *rect_float, int width BLF_position(mono, pen_x + outline, pen_y - outline, 0.0); BLF_draw_buffer(mono, text, 2); - BLF_buffer_col(mono, 0.0, 0.0, 0.0, 1.0); + BLF_buffer_col(mono, text_color); BLF_position(mono, pen_x, pen_y, 0.0); BLF_draw_buffer(mono, text, 2); @@ -339,14 +454,71 @@ static void checker_board_text(unsigned char *rect, float *rect_float, int width BLF_buffer(mono, NULL, NULL, 0, 0, 0, NULL); } +static void checker_board_color_prepare_slice(unsigned char *rect, + float *rect_float, + int width, + int height, + int offset, + int total_height) +{ + checker_board_color_fill(rect, rect_float, width, height, offset, total_height); + checker_board_color_tint(rect, rect_float, width, height, 1, 0.03f, offset); + checker_board_color_tint(rect, rect_float, width, height, 4, 0.05f, offset); + checker_board_color_tint(rect, rect_float, width, height, 32, 0.07f, offset); + checker_board_color_tint(rect, rect_float, width, height, 128, 0.15f, offset); + checker_board_grid_fill(rect, rect_float, width, height, 1.0f / 4.0f, offset); +} + +typedef struct FillCheckerColorThreadData { + unsigned char *rect; + float *rect_float; + int width, height; +} FillCheckerColorThreadData; + +static void checker_board_color_prepare_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + FillCheckerColorThreadData *data = (FillCheckerColorThreadData *)data_v; + size_t offset = ((size_t)data->width) * start_scanline * 4; + unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; + float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; + checker_board_color_prepare_slice(rect, + rect_float, + data->width, + num_scanlines, + start_scanline, + data->height); +} + void BKE_image_buf_fill_checker_color(unsigned char *rect, float *rect_float, int width, int height) { - checker_board_color_fill(rect, rect_float, width, height); - checker_board_color_tint(rect, rect_float, width, height, 1, 0.03f); - checker_board_color_tint(rect, rect_float, width, height, 4, 0.05f); - checker_board_color_tint(rect, rect_float, width, height, 32, 0.07f); - checker_board_color_tint(rect, rect_float, width, height, 128, 0.15f); - checker_board_grid_fill(rect, rect_float, width, height, 1.0f / 4.0f); + if (((size_t)width) * height < 64 * 64) { + checker_board_color_prepare_slice(rect, rect_float, width, height, 0, height); + } + else { + FillCheckerColorThreadData data; + data.rect = rect; + data.rect_float = rect_float; + data.width = width; + data.height = height; + IMB_processor_apply_threaded_scanlines( + height, checker_board_color_prepare_thread_do, &data); + } checker_board_text(rect, rect_float, width, height, 128, 2); + + if (rect_float != NULL) { + /* TODO(sergey): Currently it's easier to fill in form buffer and + * linearize it afterwards. This could be optimized with some smart + * trickery around blending factors and such. + */ + IMB_buffer_float_from_float_threaded(rect_float, rect_float, + 4, + IB_PROFILE_LINEAR_RGB, + IB_PROFILE_SRGB, + true, + width, height, + width, width); + } } diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 57c02ec6329..b350e932281 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -1082,6 +1082,7 @@ void BKE_lattice_modifiers_calc(Scene *scene, Object *ob) md->scene = scene; + if (!(mti->flags & eModifierTypeFlag_AcceptsLattice)) continue; if (!(md->mode & eModifierMode_Realtime)) continue; if (editmode && !(md->mode & eModifierMode_Editmode)) continue; if (mti->isDisabled && mti->isDisabled(md, 0)) continue; diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 895d215ca91..961b45df4e6 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -931,7 +931,7 @@ void BKE_libblock_init_empty(ID *id) BKE_texture_default((Tex *)id); break; case ID_IM: - /* Image is a bit complicated, for now assume NULLified im is OK. */ + BKE_image_init((Image *)id); break; case ID_LT: BKE_lattice_init((Lattice *)id); @@ -1783,7 +1783,7 @@ void BKE_main_id_clear_newpoins(Main *bmain) } } -static void lib_indirect_test_id(ID *id, Library *lib) +static void lib_indirect_test_id(ID *id, const Library *lib) { #define LIBTAG(a) \ if (a && a->id.lib) { a->id.tag &= ~LIB_TAG_INDIRECT; a->id.tag |= LIB_TAG_EXTERN; } (void)0 @@ -1830,9 +1830,14 @@ static void lib_indirect_test_id(ID *id, Library *lib) #undef LIBTAG } -/* if lib!=NULL, only all from lib local - * bmain is almost certainly G.main */ -void BKE_library_make_local(Main *bmain, Library *lib, bool untagged_only, bool set_fake) +/** Make linked datablocks local. + * + * \param bmain Almost certainly G.main. + * \param lib If not NULL, only make local datablocks from this library. + * \param untagged_only If true, only make local datablocks not tagged with LIB_TAG_PRE_EXISTING. + * \param set_fake If true, set fake user on all localized datablocks (except group and objects ones). + */ +void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged_only, const bool set_fake) { ListBase *lbarray[MAX_LIBARRAY]; ID *id, *idn; diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 12db3a87ba0..930a3c487ec 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -116,7 +116,7 @@ MaskSplinePoint *BKE_mask_spline_point_array(MaskSpline *spline) return spline->points_deform ? spline->points_deform : spline->points; } -MaskSplinePoint *BKE_mask_spline_point_array_from_point(MaskSpline *spline, MaskSplinePoint *point_ref) +MaskSplinePoint *BKE_mask_spline_point_array_from_point(MaskSpline *spline, const MaskSplinePoint *point_ref) { if ((point_ref >= spline->points) && (point_ref < &spline->points[spline->tot_point])) { return spline->points; diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 809b45d4b36..1fec725dbb7 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1148,7 +1148,7 @@ static void init_render_nodetree(bNodeTree *ntree, Material *basemat, int r_mode /* parses the geom+tex nodes */ ntreeShaderGetTexcoMode(ntree, r_mode, &basemat->texco, &basemat->mode_l); - + basemat->nmap_tangent_names_count = 0; for (node = ntree->nodes.first; node; node = node->next) { if (node->id) { if (GS(node->id->name) == ID_MA) { @@ -1170,6 +1170,21 @@ static void init_render_nodetree(bNodeTree *ntree, Material *basemat, int r_mode else if (node->type == NODE_GROUP) init_render_nodetree((bNodeTree *)node->id, basemat, r_mode, amb); } + else if (node->typeinfo->type == SH_NODE_NORMAL_MAP) { + basemat->mode2_l |= MA_TANGENT_CONCRETE; + NodeShaderNormalMap *nm = node->storage; + bool taken_into_account = false; + for (int i = 0; i < basemat->nmap_tangent_names_count; i++) { + if (STREQ(basemat->nmap_tangent_names[i], nm->uv_map)) { + taken_into_account = true; + break; + } + } + if (!taken_into_account) { + BLI_assert(basemat->nmap_tangent_names_count < MAX_MTFACE + 1); + strcpy(basemat->nmap_tangent_names[basemat->nmap_tangent_names_count++], nm->uv_map); + } + } } } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index c10592882c0..2af78cca79f 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -595,14 +595,17 @@ Mesh *BKE_mesh_copy(Mesh *me) return BKE_mesh_copy_ex(G.main, me); } -BMesh *BKE_mesh_to_bmesh(Mesh *me, Object *ob) +BMesh *BKE_mesh_to_bmesh(Mesh *me, Object *ob, const bool add_key_index) { BMesh *bm; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); bm = BM_mesh_create(&allocsize); - BM_mesh_bm_from_me(bm, me, false, true, ob->shapenr); + BM_mesh_bm_from_me( + bm, me, (&(struct BMeshFromMeshParams){ + .add_key_index = add_key_index, .use_shapekey = true, .active_shapekey = ob->shapenr, + })); return bm; } diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 83e020cf2ea..577a21285f8 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -2417,30 +2417,42 @@ void BKE_mesh_loops_to_tessdata(CustomData *fdata, CustomData *ldata, CustomData } } -void BKE_mesh_tangent_loops_to_tessdata(CustomData *fdata, CustomData *ldata, MFace *mface, - int *polyindices, unsigned int (*loopindices)[4], const int num_faces) +void BKE_mesh_tangent_loops_to_tessdata( + CustomData *fdata, CustomData *ldata, MFace *mface, + int *polyindices, unsigned int (*loopindices)[4], const int num_faces, const char *layer_name) { /* Note: performances are sub-optimal when we get a NULL mface, we could be ~25% quicker with dedicated code... * Issue is, unless having two different functions with nearly the same code, there's not much ways to solve * this. Better imho to live with it for now. :/ --mont29 */ - const bool hasLoopTangent = CustomData_has_layer(ldata, CD_TANGENT); + + float (*ftangents)[4] = NULL; + float (*ltangents)[4] = NULL; + int findex, j; const int *pidx; unsigned int (*lidx)[4]; - if (hasLoopTangent) { - /* need to do for all uv maps at some point */ - float (*ftangents)[4] = CustomData_get_layer(fdata, CD_TANGENT); - float (*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); + if (layer_name) + ltangents = CustomData_get_layer_named(ldata, CD_TANGENT, layer_name); + else + ltangents = CustomData_get_layer(ldata, CD_TANGENT); - for (findex = 0, pidx = polyindices, lidx = loopindices; - findex < num_faces; - pidx++, lidx++, findex++) - { - int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; - for (j = nverts; j--;) { - copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]); + if (ltangents) { + /* need to do for all uv maps at some point */ + if (layer_name) + ftangents = CustomData_get_layer_named(fdata, CD_TANGENT, layer_name); + else + ftangents = CustomData_get_layer(fdata, CD_TANGENT); + if (ftangents) { + for (findex = 0, pidx = polyindices, lidx = loopindices; + findex < num_faces; + pidx++, lidx++, findex++) + { + int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; + for (j = nverts; j--;) { + copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]); + } } } } diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index 49dd0b104ab..ba890b005d8 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -284,7 +284,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, bool fix_normal = true; for (j = 0; j < 3; j++) { - if (!finite(mv->co[j])) { + if (!isfinite(mv->co[j])) { PRINT_ERR("\tVertex %u: has invalid coordinate\n", i); if (do_fixes) { @@ -765,7 +765,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, for (j = 0, dw = dv->dw; j < dv->totweight; j++, dw++) { /* note, greater than max defgroups is accounted for in our code, but not < 0 */ - if (!finite(dw->weight)) { + if (!isfinite(dw->weight)) { PRINT_ERR("\tVertex deform %u, group %d has weight: %f\n", i, dw->def_nr, dw->weight); if (do_fixes) { dw->weight = 0.0f; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 1ba4852623c..65ee153e00b 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -244,6 +244,11 @@ bool BKE_object_support_modifier_type_check(Object *ob, int modifier_type) mti = modifierType_getInfo(modifier_type); + + if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsLattice) == 0) { + return false; + } + if (!((mti->flags & eModifierTypeFlag_AcceptsCVs) || (ob->type == OB_MESH && (mti->flags & eModifierTypeFlag_AcceptsMesh)))) { diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index 7f742dee73c..63c37fddc60 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -1253,15 +1253,16 @@ DupliApplyData *duplilist_apply(Object *ob, Scene *scene, ListBase *duplilist) "DupliObject apply extra data"); 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; } + } + 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); copy_m4_m4(dob->ob->obmat, dob->mat); /* copy layers from the main duplicator object */ diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 1b6fc92ef5e..3a2663c5d48 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -635,7 +635,7 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder) } if (reorder) BM_log_mesh_elems_reorder(ss->bm, ss->bm_log); - BM_mesh_bm_to_me(ss->bm, ob->data, false); + BM_mesh_bm_to_me(ss->bm, ob->data, (&(struct BMeshToMeshParams){0})); } } } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 098700495a0..7dce237c737 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -4225,8 +4225,8 @@ void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3] /* can happen with bad pointcache or physics calculation * since this becomes geometry, nan's and inf's crash raytrace code. * better not allow this. */ - if ((!finite(bb->vec[0])) || (!finite(bb->vec[1])) || (!finite(bb->vec[2])) || - (!finite(bb->vel[0])) || (!finite(bb->vel[1])) || (!finite(bb->vel[2])) ) + if ((!isfinite(bb->vec[0])) || (!isfinite(bb->vec[1])) || (!isfinite(bb->vec[2])) || + (!isfinite(bb->vel[0])) || (!isfinite(bb->vel[1])) || (!isfinite(bb->vel[2])) ) { zero_v3(bb->vec); zero_v3(bb->vel); diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 9185c0964b9..f6bddfa6f99 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -390,23 +390,26 @@ static void psys_uv_to_w(float u, float v, int quad, float *w) /* Find the index in "sum" array before "value" is crossed. */ static int distribute_binary_search(float *sum, int n, float value) { - int mid, low=0, high=n; + int mid, low = 0, high = n - 1; - if (value == 0.f) - return 0; + if (sum[low] >= value) + return low; + + if (sum[high] < value) + return high; - while (low <= high) { - mid= (low + high)/2; + while (low < high) { + mid = (low + high) / 2; - if (sum[mid] < value && value <= sum[mid+1]) + if ((sum[mid] < value) && (sum[mid + 1] >= value)) return mid; - if (sum[mid] >= value) - high= mid - 1; - else if (sum[mid] < value) - low= mid + 1; - else - return mid; + if (sum[mid] >= value) { + high = mid - 1; + } + else if (sum[mid] < value) { + low = mid + 1; + } } return low; @@ -778,7 +781,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, Parti int cfrom=0; int totelem=0, totpart, *particle_element=0, children=0, totseam=0; int jitlevel= 1, distr; - float *element_weight=NULL,*element_sum=NULL,*jitter_offset=NULL, *vweight=NULL; + float *element_weight=NULL,*jitter_offset=NULL, *vweight=NULL; float cur, maxweight=0.0, tweight, totweight, inv_totweight, co[3], nor[3], orco[3]; if (ELEM(NULL, ob, psys, psys->part)) @@ -915,7 +918,6 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, Parti element_weight = MEM_callocN(sizeof(float)*totelem, "particle_distribution_weights"); particle_element= MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes"); - element_sum = MEM_mallocN(sizeof(*element_sum) * totelem, "particle_distribution_sum"); jitter_offset = MEM_callocN(sizeof(float)*totelem, "particle_distribution_jitoff"); /* Calculate weights from face areas */ @@ -1003,27 +1005,55 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, Parti } /* Calculate total weight of all elements */ - totweight= 0.0f; - for (i=0;i<totelem; i++) + int totmapped = 0; + totweight = 0.0f; + for (i = 0; i < totelem; i++) { + if (element_weight[i] != 0.0f) { + totmapped++; + } totweight += element_weight[i]; + } + + if (totweight == 0.0f) { + /* We are not allowed to distribute particles anywhere... */ + return 0; + } inv_totweight = (totweight > 0.f ? 1.f/totweight : 0.f); - /* Calculate cumulative weights */ - element_sum[0] = element_weight[0] * inv_totweight; - for (i = 1; i < totelem; i++) { - element_sum[i] = element_sum[i - 1] + element_weight[i] * inv_totweight; + /* Calculate cumulative weights. + * We remove all null-weighted elements from element_sum, and create a new mapping + * 'activ'_elem_index -> orig_elem_index. + * This simplifies greatly the filtering of zero-weighted items - and can be much mor efficient + * especially in random case (reducing a lot the size of binary-searched array)... + */ + float *element_sum = MEM_mallocN(sizeof(*element_sum) * totmapped, __func__); + int *element_map = MEM_mallocN(sizeof(*element_map) * totmapped, __func__); + int i_mapped = 0; + + for (i = 0; i < totelem && element_weight[i] == 0.0f; i++); + element_sum[i_mapped] = element_weight[i] * inv_totweight; + element_map[i_mapped] = i; + i_mapped++; + for (i++; i < totelem; i++) { + if (element_weight[i] != 0.0f) { + element_sum[i_mapped] = element_sum[i_mapped - 1] + element_weight[i] * inv_totweight; + element_map[i_mapped] = i; + i_mapped++; + } } + + BLI_assert(i_mapped == totmapped); /* Finally assign elements to particles */ - if ((part->flag&PART_TRAND) || (part->simplify_flag&PART_SIMPLIFY_ENABLE)) { + if ((part->flag & PART_TRAND) || (part->simplify_flag & PART_SIMPLIFY_ENABLE)) { float pos; - for (p=0; p<totpart; p++) { - /* In theory element_sum[totelem] should be 1.0, but due to float errors this is not necessarily always true, so scale pos accordingly. */ - pos= BLI_frand() * element_sum[totelem - 1]; - particle_element[p] = distribute_binary_search(element_sum, totelem, pos); - particle_element[p] = MIN2(totelem-1, particle_element[p]); + for (p = 0; p < totpart; p++) { + /* In theory element_sum[totelem - 1] should be 1.0, + * but due to float errors this is not necessarily always true, so scale pos accordingly. */ + pos = BLI_frand() * element_sum[totmapped - 1]; + particle_element[p] = element_map[distribute_binary_search(element_sum, totmapped, pos)]; jitter_offset[particle_element[p]] = pos; } } @@ -1036,31 +1066,23 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, Parti * 'midpoint between v and v+1' (or 'p and p+1', depending whether we have more vertices than particles or not), * and avoid stumbling over float imprecisions in element_sum. */ if (from == PART_FROM_VERT) { - pos = (totpart < totelem) ? 0.5 / (double)totelem : step * 0.5; /* We choose the smaller step. */ + pos = (totpart < totmapped) ? 0.5 / (double)totmapped : step * 0.5; /* We choose the smaller step. */ } else { pos = 0.0; } - /* Avoid initial zero-weight items. */ - for (i = 0; (element_sum[i] == 0.0) && (i < totelem - 1); i++); - - for (p = 0; p < totpart; p++, pos += step) { - for ( ; (pos > (double)element_sum[i]) && (i < totelem - 1); i++); + for (i = 0, p = 0; p < totpart; p++, pos += step) { + for ( ; (i < totmapped - 1) && (pos > (double)element_sum[i]); i++); - particle_element[p] = i; + particle_element[p] = element_map[i]; jitter_offset[particle_element[p]] = pos; } - - /* Avoid final zero weight items. */ - BLI_assert(p == totpart); - if (element_weight[particle_element[--p]] == 0.0f) { - particle_element[p] = particle_element[p - 1]; - } } MEM_freeN(element_sum); + MEM_freeN(element_map); /* For hair, sort by origindex (allows optimization's in rendering), */ /* however with virtual parents the children need to be in random order. */ diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 330b5922c9a..d73f087a3fe 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -979,16 +979,7 @@ static void pbvh_update_normals_accum_task_cb(void *userdata, const int n) * Not exact equivalent though, since atomicity is only ensured for one component * of the vector at a time, but here it shall not make any sensible difference. */ for (int k = 3; k--; ) { - /* Atomic float addition. - * Note that since collision are unlikely, loop will nearly always run once. */ - float oldval, newval; - uint32_t prevval; - do { - oldval = vnors[v][k]; - newval = oldval + fn[k]; - prevval = atomic_cas_uint32( - (uint32_t *)&vnors[v][k], *(uint32_t *)(&oldval), *(uint32_t *)(&newval)); - } while (UNLIKELY(prevval != *(uint32_t *)(&oldval))); + atomic_add_fl(&vnors[v][k], fn[k]); } } } diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 448aaaa7830..f0ba63e35b4 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -59,7 +59,6 @@ #include "BKE_appdir.h" #include "BKE_anim.h" -#include "BKE_blender.h" #include "BKE_cloth.h" #include "BKE_dynamicpaint.h" #include "BKE_global.h" diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 6b297a143d6..27abbb62762 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -101,11 +101,6 @@ #include "bmesh.h" -#ifdef WIN32 -#else -# include <sys/time.h> -#endif - const char *RE_engine_id_BLENDER_RENDER = "BLENDER_RENDER"; const char *RE_engine_id_BLENDER_GAME = "BLENDER_GAME"; const char *RE_engine_id_CYCLES = "CYCLES"; @@ -616,6 +611,12 @@ void BKE_scene_init(Scene *sce) sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_LENGTH; sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE; + sce->toolsettings->curve_paint_settings.curve_type = CU_BEZIER; + sce->toolsettings->curve_paint_settings.flag |= CURVE_PAINT_FLAG_CORNERS_DETECT; + sce->toolsettings->curve_paint_settings.error_threshold = 8; + sce->toolsettings->curve_paint_settings.radius_max = 1.0f; + sce->toolsettings->curve_paint_settings.corner_angle = DEG2RADF(70.0f); + sce->toolsettings->statvis.overhang_axis = OB_NEGZ; sce->toolsettings->statvis.overhang_min = 0; sce->toolsettings->statvis.overhang_max = DEG2RADF(45.0f); @@ -1768,7 +1769,7 @@ static void prepare_mesh_for_viewport_render(Main *bmain, Scene *scene) { if (check_rendered_viewport_visible(bmain)) { BMesh *bm = mesh->edit_btmesh->bm; - BM_mesh_bm_to_me(bm, mesh, false); + BM_mesh_bm_to_me(bm, mesh, (&(struct BMeshToMeshParams){0})); DAG_id_tag_update(&mesh->id, 0); } } @@ -2423,7 +2424,6 @@ SceneRenderView *BKE_scene_multiview_render_view_findindex(const RenderData *rd, 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) @@ -2454,7 +2454,6 @@ int BKE_scene_multiview_view_id_get(const RenderData *rd, const char *viewname) 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)) { diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index 1b807adca4b..3de4a426973 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -3060,7 +3060,7 @@ static ImBuf *do_gaussian_blur_effect(const SeqRenderData *context, ibuf1 = out; init_data.ibuf = ibuf1; - out = prepare_effect_imbufs(context, ibuf1, NULL, NULL);; + out = prepare_effect_imbufs(context, ibuf1, NULL, NULL); init_data.out = out; IMB_processor_apply_threaded(out->y, @@ -3084,6 +3084,10 @@ static void init_text_effect(Sequence *seq) data = seq->effectdata = MEM_callocN(sizeof(TextVars), "textvars"); data->text_size = 30; + + copy_v4_fl(data->color, 1.0f); + data->shadow_color[3] = 1.0f; + BLI_strncpy(data->text, "Text", sizeof(data->text)); data->loc[0] = 0.5f; @@ -3099,7 +3103,9 @@ static int num_inputs_text(void) static int early_out_text(Sequence *seq, float UNUSED(facf0), float UNUSED(facf1)) { TextVars *data = seq->effectdata; - if (data->text[0] == 0 || data->text_size < 1) { + if (data->text[0] == 0 || data->text_size < 1 || + ((data->color[3] == 0.0f) && (data->shadow_color[3] == 0.0f || (data->flag & SEQ_TEXT_SHADOW) == 0))) + { return EARLY_USE_INPUT_1; } return EARLY_NO_INPUT; @@ -3188,11 +3194,11 @@ static ImBuf *do_text_effect(const SeqRenderData *context, Sequence *seq, float fontx = BLF_width_max(mono); fonty = line_height; BLF_position(mono, x + max_ii(fontx / 25, 1), y + max_ii(fonty / 25, 1), 0.0f); - BLF_buffer_col(mono, 0.0f, 0.0f, 0.0f, 1.0f); + BLF_buffer_col(mono, data->shadow_color); BLF_draw_buffer(mono, data->text, BLF_DRAW_STR_DUMMY_MAX); } BLF_position(mono, x, y, 0.0f); - BLF_buffer_col(mono, 1.0f, 1.0f, 1.0f, 1.0f); + BLF_buffer_col(mono, data->color); BLF_draw_buffer(mono, data->text, BLF_DRAW_STR_DUMMY_MAX); BLF_buffer(mono, NULL, NULL, 0, 0, 0, NULL); diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index badf78edfb1..e855f6faa22 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -245,11 +245,11 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for * for finding the best hit, to get the real dist, * measure the len_v3v3() from the input coord to hit.co */ BVHTreeRayHit hit; - BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; + void *treeData = NULL; /* auxiliary target */ DerivedMesh *auxMesh = NULL; - BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; + void *auxData = NULL; SpaceTransform local2aux; /* If the user doesn't allows to project in any direction of projection axis @@ -285,19 +285,44 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for } /* use editmesh to avoid array allocation */ + BMEditMesh *emtarget = NULL, *emaux = NULL; + BVHTreeFromEditMesh emtreedata_stack, emauxdata_stack; + BVHTreeFromMesh dmtreedata_stack, dmauxdata_stack; + BVHTree *targ_tree; + void *targ_callback; if (calc->smd->target && calc->target->type == DM_TYPE_EDITBMESH) { - treeData.em_evil = BKE_editmesh_from_object(calc->smd->target); - treeData.em_evil_all = true; + emtarget = BKE_editmesh_from_object(calc->smd->target); + if ((targ_tree = bvhtree_from_editmesh_looptri(&emtreedata_stack, emtarget, 0.0, 4, 6))) { + targ_callback = emtreedata_stack.raycast_callback; + treeData = &emtreedata_stack; + } } - if (calc->smd->auxTarget && auxMesh->type == DM_TYPE_EDITBMESH) { - auxData.em_evil = BKE_editmesh_from_object(calc->smd->auxTarget); - auxData.em_evil_all = true; + else { + if ((targ_tree = bvhtree_from_mesh_looptri(&dmtreedata_stack, calc->target, 0.0, 4, 6))) { + targ_callback = dmtreedata_stack.raycast_callback; + treeData = &dmtreedata_stack; + } } - - /* After sucessufuly build the trees, start projection vertexs */ - if (bvhtree_from_mesh_looptri(&treeData, calc->target, 0.0, 4, 6) && - (auxMesh == NULL || bvhtree_from_mesh_looptri(&auxData, auxMesh, 0.0, 4, 6))) - { + if (targ_tree) { + BVHTree *aux_tree = NULL; + void *aux_callback; + if (auxMesh != NULL) { + /* use editmesh to avoid array allocation */ + if (calc->smd->auxTarget && auxMesh->type == DM_TYPE_EDITBMESH) { + emaux = BKE_editmesh_from_object(calc->smd->auxTarget); + if ((aux_tree = bvhtree_from_editmesh_looptri(&emauxdata_stack, emaux, 0.0, 4, 6)) != NULL) { + aux_callback = emauxdata_stack.raycast_callback; + auxData = &emauxdata_stack; + } + } + else { + if ((aux_tree = bvhtree_from_mesh_looptri(&dmauxdata_stack, calc->target, 0.0, 4, 6)) != NULL) { + aux_callback = dmauxdata_stack.raycast_callback; + auxData = &dmauxdata_stack; + } + } + } + /* After sucessufuly build the trees, start projection vertexs */ #ifndef __APPLE__ #pragma omp parallel for private(i, hit) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) @@ -340,15 +365,17 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for /* Project over positive direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { - if (auxData.tree) { - BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, - &local2aux, auxData.tree, &hit, - auxData.raycast_callback, &auxData); + if (aux_tree) { + BKE_shrinkwrap_project_normal( + 0, tmp_co, tmp_no, + &local2aux, aux_tree, &hit, + aux_callback, auxData); } - BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, tmp_no, - &calc->local2target, treeData.tree, &hit, - treeData.raycast_callback, &treeData); + BKE_shrinkwrap_project_normal( + calc->smd->shrinkOpts, tmp_co, tmp_no, + &calc->local2target, targ_tree, &hit, + targ_callback, treeData); } /* Project over negative direction of axis */ @@ -356,15 +383,17 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for float inv_no[3]; negate_v3_v3(inv_no, tmp_no); - if (auxData.tree) { - BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, - &local2aux, auxData.tree, &hit, - auxData.raycast_callback, &auxData); + if (aux_tree) { + BKE_shrinkwrap_project_normal( + 0, tmp_co, inv_no, + &local2aux, aux_tree, &hit, + aux_callback, auxData); } - BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, inv_no, - &calc->local2target, treeData.tree, &hit, - treeData.raycast_callback, &treeData); + BKE_shrinkwrap_project_normal( + calc->smd->shrinkOpts, tmp_co, inv_no, + &calc->local2target, targ_tree, &hit, + targ_callback, treeData); } /* don't set the initial dist (which is more efficient), @@ -383,8 +412,14 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for } /* free data structures */ - free_bvhtree_from_mesh(&treeData); - free_bvhtree_from_mesh(&auxData); + if (treeData) { + if (emtarget) free_bvhtree_from_editmesh(treeData); + else free_bvhtree_from_mesh(treeData); + } + if (auxData) { + if (emaux) free_bvhtree_from_editmesh(auxData); + else free_bvhtree_from_mesh(auxData); + } } /* diff --git a/source/blender/blenkernel/intern/sketch.c b/source/blender/blenkernel/intern/sketch.c index 0d355abb49b..6f5c264f658 100644 --- a/source/blender/blenkernel/intern/sketch.c +++ b/source/blender/blenkernel/intern/sketch.c @@ -50,8 +50,6 @@ void freeSketch(SK_Sketch *sketch) sk_freeStroke(stk); } - BLI_freelistN(&sketch->depth_peels); - MEM_freeN(sketch); } diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 97994332411..a84b8352417 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -3016,11 +3016,13 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, numdata++; } } - if (matconv[a].attribs.tottang && matconv[a].attribs.tang.array) { - matconv[a].datatypes[numdata].index = matconv[a].attribs.tang.gl_index; - matconv[a].datatypes[numdata].size = 4; - matconv[a].datatypes[numdata].type = GL_FLOAT; - numdata++; + for (b = 0; b < matconv[a].attribs.tottang; b++) { + if (matconv[a].attribs.tottang && matconv[a].attribs.tang[b].array) { + matconv[a].datatypes[numdata].index = matconv[a].attribs.tang[b].gl_index; + matconv[a].datatypes[numdata].size = 4; + matconv[a].datatypes[numdata].type = GL_FLOAT; + numdata++; + } } if (numdata != 0) { matconv[a].numdata = numdata; @@ -3105,15 +3107,17 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, offset += sizeof(unsigned char) * 4; } } - if (matconv[i].attribs.tottang && matconv[i].attribs.tang.array) { - const float (*looptang)[4] = (const float (*)[4])matconv[i].attribs.tang.array + tot_loops; + for (b = 0; b < matconv[i].attribs.tottang; b++) { + if (matconv[i].attribs.tottang && matconv[i].attribs.tang[b].array) { + const float (*looptang)[4] = (const float (*)[4])matconv[i].attribs.tang[b].array + tot_loops; - copy_v4_v4((float *)&varray[offset], looptang[0]); - copy_v4_v4((float *)&varray[offset + max_element_size], looptang[3]); - copy_v4_v4((float *)&varray[offset + 2 * max_element_size], looptang[2]); - copy_v4_v4((float *)&varray[offset + 3 * max_element_size], looptang[1]); + copy_v4_v4((float *)&varray[offset], looptang[0]); + copy_v4_v4((float *)&varray[offset + max_element_size], looptang[3]); + copy_v4_v4((float *)&varray[offset + 2 * max_element_size], looptang[2]); + copy_v4_v4((float *)&varray[offset + 3 * max_element_size], looptang[1]); - offset += sizeof(float) * 4; + offset += sizeof(float) * 4; + } } tot_loops += 4; @@ -4361,6 +4365,7 @@ static void ccgDM_recalcLoopTri(DerivedMesh *UNUSED(dm)) static const MLoopTri *ccgDM_getLoopTriArray(DerivedMesh *dm) { + BLI_rw_mutex_lock(&loops_cache_rwlock, THREAD_LOCK_WRITE); if (dm->looptris.array) { BLI_assert(poly_to_tri_count(dm->numPolyData, dm->numLoopData) == dm->looptris.num); } @@ -4391,6 +4396,7 @@ static const MLoopTri *ccgDM_getLoopTriArray(DerivedMesh *dm) lt->poly = poly_index; } } + BLI_rw_mutex_unlock(&loops_cache_rwlock); return dm->looptris.array; } diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index cf11fe2323d..c0a373395dc 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -102,7 +102,7 @@ typedef struct bUnitDef { /* define a single unit */ typedef struct bUnitCollection { - struct bUnitDef *units; + const struct bUnitDef *units; int base_unit; /* basic unit index (when user doesn't specify unit explicitly) */ int flag; /* options for this system */ int length; /* to quickly find the last item */ @@ -113,7 +113,7 @@ static struct bUnitDef buDummyDef[] = { {"", NULL, "", NULL, NULL, 1.0, 0.0}, {N static struct bUnitCollection buDummyCollection = {buDummyDef, 0, 0, sizeof(buDummyDef)}; /* Lengths */ -static struct bUnitDef buMetricLenDef[] = { +static const struct bUnitDef buMetricLenDef[] = { {"kilometer", "kilometers", "km", NULL, "Kilometers", UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, {"hectometer", "hectometers", "hm", NULL, "100 Meters", UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, {"dekameter", "dekameters", "dam", NULL, "10 Meters", UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, @@ -131,7 +131,7 @@ static struct bUnitDef buMetricLenDef[] = { #endif {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0} }; -static struct bUnitCollection buMetricLenCollection = {buMetricLenDef, 3, 0, sizeof(buMetricLenDef) / sizeof(bUnitDef)}; +static const struct bUnitCollection buMetricLenCollection = {buMetricLenDef, 3, 0, sizeof(buMetricLenDef) / sizeof(bUnitDef)}; static struct bUnitDef buImperialLenDef[] = { {"mile", "miles", "mi", "m", "Miles", UN_SC_MI, 0.0, B_UNIT_DEF_NONE}, @@ -289,7 +289,7 @@ static struct bUnitCollection buCameraLenCollection = {buCameraLenDef, 3, 0, siz #define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / B_UNIT_TYPE_TOT) / sizeof(void *)) - 1) -static struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { +static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { {NULL, NULL, NULL, NULL, NULL, &buNaturalRotCollection, &buNaturalTimeCollection, NULL, NULL, NULL}, {NULL, &buMetricLenCollection, &buMetricAreaCollection, &buMetricVolCollection, &buMetricMassCollection, &buNaturalRotCollection, &buNaturalTimeCollection, &buMetricVelCollection, &buMetricAclCollection, &buCameraLenCollection}, /* metric */ {NULL, &buImperialLenCollection, &buImperialAreaCollection, &buImperialVolCollection, &buImperialMassCollection, &buNaturalRotCollection, &buNaturalTimeCollection, &buImperialVelCollection, &buImperialAclCollection, &buCameraLenCollection}, /* imperial */ @@ -299,20 +299,21 @@ static struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { /* internal, has some option not exposed */ -static bUnitCollection *unit_get_system(int system, int type) +static const bUnitCollection *unit_get_system(int system, int type) { assert((system > -1) && (system < UNIT_SYSTEM_TOT) && (type > -1) && (type < B_UNIT_TYPE_TOT)); return bUnitSystems[system][type]; /* select system to use, metric/imperial/other? */ } -static bUnitDef *unit_default(bUnitCollection *usys) +static const bUnitDef *unit_default(const bUnitCollection *usys) { return &usys->units[usys->base_unit]; } -static bUnitDef *unit_best_fit(double value, bUnitCollection *usys, bUnitDef *unit_start, int suppress) +static const bUnitDef *unit_best_fit( + double value, const bUnitCollection *usys, const bUnitDef *unit_start, int suppress) { - bUnitDef *unit; + const bUnitDef *unit; double value_abs = value > 0.0 ? value : -value; for (unit = unit_start ? unit_start : usys->units; unit->name; unit++) { @@ -337,19 +338,21 @@ static bUnitDef *unit_best_fit(double value, bUnitCollection *usys, bUnitDef *un } /* convert into 2 units and 2 values for "2ft, 3inch" syntax */ -static void unit_dual_convert(double value, bUnitCollection *usys, bUnitDef **unit_a, bUnitDef **unit_b, - double *value_a, double *value_b) +static void unit_dual_convert( + double value, const bUnitCollection *usys, + bUnitDef const **r_unit_a, bUnitDef const **r_unit_b, + double *r_value_a, double *r_value_b) { - bUnitDef *unit = unit_best_fit(value, usys, NULL, 1); + const bUnitDef *unit = unit_best_fit(value, usys, NULL, 1); - *value_a = (value < 0.0 ? ceil : floor)(value / unit->scalar) * unit->scalar; - *value_b = value - (*value_a); + *r_value_a = (value < 0.0 ? ceil : floor)(value / unit->scalar) * unit->scalar; + *r_value_b = value - (*r_value_a); - *unit_a = unit; - *unit_b = unit_best_fit(*value_b, usys, *unit_a, 1); + *r_unit_a = unit; + *r_unit_b = unit_best_fit(*r_value_b, usys, *r_unit_a, 1); } -static size_t unit_as_string(char *str, int len_max, double value, int prec, bUnitCollection *usys, +static size_t unit_as_string(char *str, int len_max, double value, int prec, const bUnitCollection *usys, /* non exposed options */ const bUnitDef *unit, char pad) { @@ -422,14 +425,14 @@ static size_t unit_as_string(char *str, int len_max, double value, int prec, bUn */ size_t bUnit_AsString(char *str, int len_max, double value, int prec, int system, int type, bool split, bool pad) { - bUnitCollection *usys = unit_get_system(system, type); + const bUnitCollection *usys = unit_get_system(system, type); if (usys == NULL || usys->units[0].name == NULL) usys = &buDummyCollection; /* split output makes sense only for length, mass and time */ if (split && (type == B_UNIT_LENGTH || type == B_UNIT_MASS || type == B_UNIT_TIME || type == B_UNIT_CAMERA)) { - bUnitDef *unit_a, *unit_b; + const bUnitDef *unit_a, *unit_b; double value_a, value_b; unit_dual_convert(value, usys, &unit_a, &unit_b, &value_a, &value_b); @@ -522,7 +525,7 @@ static bool ch_is_op(char op) } } -static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pref, bUnitDef *unit, +static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit, const char *replace_str) { char *str_found; @@ -571,7 +574,7 @@ static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pr return 0; } -static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref, bUnitDef *unit) +static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit) { int ofs = 0; ofs += unit_scale_str(str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_short); @@ -581,7 +584,7 @@ static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref return ofs; } -static bool unit_find(const char *str, bUnitDef *unit) +static bool unit_find(const char *str, const bUnitDef *unit) { if (unit_find_str(str, unit->name_short)) return true; if (unit_find_str(str, unit->name_plural)) return true; @@ -591,12 +594,12 @@ static bool unit_find(const char *str, bUnitDef *unit) return false; } -static bUnitDef *unit_detect_from_str(bUnitCollection *usys, const char *str, const char *str_prev) +static const bUnitDef *unit_detect_from_str(const bUnitCollection *usys, const char *str, const char *str_prev) { /* Try to find a default unit from current or previous string. * This allows us to handle cases like 2 + 2mm, people would expect to get 4mm, not 2.002m! * Note this does not handle corner cases like 2 + 2cm + 1 + 2.5mm... We can't support everything. */ - bUnitDef *unit = NULL; + const bUnitDef *unit = NULL; /* see which units the new value has */ for (unit = usys->units; unit->name; unit++) { @@ -636,9 +639,9 @@ static bUnitDef *unit_detect_from_str(bUnitCollection *usys, const char *str, co */ bool bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) { - bUnitCollection *usys = unit_get_system(system, type); + const bUnitCollection *usys = unit_get_system(system, type); - bUnitDef *unit = NULL, *default_unit; + const bUnitDef *unit = NULL, *default_unit; double scale_pref_base = scale_pref; char str_tmp[TEMP_STR_SIZE]; bool changed = false; @@ -679,7 +682,7 @@ bool bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double sc * In other words, when in metrics, typing '2+2in' will give 2 meters 2 inches, not 4 inches. * I do think this is the desired behavior! */ - bUnitCollection *usys_iter; + const bUnitCollection *usys_iter; int system_iter; for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) { @@ -730,9 +733,9 @@ bool bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double sc /* 45µm --> 45um */ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int system, int type) { - bUnitCollection *usys = unit_get_system(system, type); + const bUnitCollection *usys = unit_get_system(system, type); - bUnitDef *unit; + const bUnitDef *unit; /* find and substitute all units */ for (unit = usys->units; unit->name; unit++) { @@ -769,8 +772,8 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste double bUnit_ClosestScalar(double value, int system, int type) { - bUnitCollection *usys = unit_get_system(system, type); - bUnitDef *unit; + const bUnitCollection *usys = unit_get_system(system, type); + const bUnitDef *unit; if (usys == NULL) return -1; @@ -784,7 +787,7 @@ double bUnit_ClosestScalar(double value, int system, int type) double bUnit_BaseScalar(int system, int type) { - bUnitCollection *usys = unit_get_system(system, type); + const bUnitCollection *usys = unit_get_system(system, type); return unit_default(usys)->scalar; } @@ -794,34 +797,34 @@ bool bUnit_IsValid(int system, int type) return !(system < 0 || system > UNIT_SYSTEM_TOT || type < 0 || type > B_UNIT_TYPE_TOT); } -void bUnit_GetSystem(void **usys_pt, int *len, int system, int type) +void bUnit_GetSystem(int system, int type, void const **r_usys_pt, int *r_len) { - bUnitCollection *usys = unit_get_system(system, type); - *usys_pt = usys; + const bUnitCollection *usys = unit_get_system(system, type); + *r_usys_pt = usys; if (usys == NULL) { - *len = 0; + *r_len = 0; return; } - *len = usys->length; + *r_len = usys->length; } -int bUnit_GetBaseUnit(void *usys_pt) +int bUnit_GetBaseUnit(const void *usys_pt) { return ((bUnitCollection *)usys_pt)->base_unit; } -const char *bUnit_GetName(void *usys_pt, int index) +const char *bUnit_GetName(const void *usys_pt, int index) { return ((bUnitCollection *)usys_pt)->units[index].name; } -const char *bUnit_GetNameDisplay(void *usys_pt, int index) +const char *bUnit_GetNameDisplay(const void *usys_pt, int index) { return ((bUnitCollection *)usys_pt)->units[index].name_display; } -double bUnit_GetScaler(void *usys_pt, int index) +double bUnit_GetScaler(const void *usys_pt, int index) { return ((bUnitCollection *)usys_pt)->units[index].scalar; } diff --git a/source/blender/blenlib/BLI_array_utils.h b/source/blender/blenlib/BLI_array_utils.h index c5359d56f9c..5ef8421c003 100644 --- a/source/blender/blenlib/BLI_array_utils.h +++ b/source/blender/blenlib/BLI_array_utils.h @@ -64,4 +64,16 @@ void _bli_array_binary_or( CHECK_TYPE_PAIR_INLINE(*(arr), *(arr_b)), \ _bli_array_binary_or(arr, arr_a, arr_b, arr_len, sizeof(*(arr)))) +bool _bli_array_iter_span( + const void *arr, + unsigned int arr_len, size_t arr_stride, + bool use_wrap, bool use_delimit_bounds, + bool (*test_fn)(const void *arr_item, void *user_data), void *user_data, + unsigned int span_step[2], unsigned int *r_span_len); +#define BLI_array_iter_span(arr, arr_len, use_wrap, use_delimit_bounds, test_fn, user_data, \ + span_step, r_span_len) \ + _bli_array_iter_span( \ + arr, arr_len, sizeof(*(arr)), use_wrap, use_delimit_bounds, test_fn, user_data, \ + span_step, r_span_len) + #endif /* __BLI_ARRAY_UTILS_H__ */ diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h index be792669ef1..fb8c2520e67 100644 --- a/source/blender/blenlib/BLI_kdopbvh.h +++ b/source/blender/blenlib/BLI_kdopbvh.h @@ -132,7 +132,9 @@ BVHTreeOverlap *BLI_bvhtree_overlap( const BVHTree *tree1, const BVHTree *tree2, unsigned int *r_overlap_tot, BVHTree_OverlapCallback callback, void *userdata); -float BLI_bvhtree_getepsilon(const BVHTree *tree); +int BLI_bvhtree_get_size(const BVHTree *tree); + +float BLI_bvhtree_get_epsilon(const BVHTree *tree); /* find nearest node to the given coordinates * (if nearest is given it will only search nodes where square distance is smaller than nearest->dist) */ @@ -152,12 +154,12 @@ int BLI_bvhtree_ray_cast( BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata); -int BLI_bvhtree_ray_cast_all_ex( - BVHTree *tree, const float co[3], const float dir[3], float radius, +void BLI_bvhtree_ray_cast_all_ex( + BVHTree *tree, const float co[3], const float dir[3], float radius, float hit_dist, BVHTree_RayCastCallback callback, void *userdata, int flag); -int BLI_bvhtree_ray_cast_all( - BVHTree *tree, const float co[3], const float dir[3], float radius, +void BLI_bvhtree_ray_cast_all( + BVHTree *tree, const float co[3], const float dir[3], float radius, float hit_dist, BVHTree_RayCastCallback callback, void *userdata); float BLI_bvhtree_bb_raycast(const float bv[6], const float light_start[3], const float light_end[3], float pos[3]); diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 20b76354f58..e97a250cd24 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -37,10 +37,6 @@ #include <math.h> #include "BLI_math_inline.h" -#ifdef __sun__ -#include <ieeefp.h> /* for finite() */ -#endif - #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ #endif @@ -146,12 +142,6 @@ static const int NAN_INT = 0x7FC00000; #endif /* C99, POSIX.1-2001 or MSVC12 (partial C99) */ -#ifdef WIN32 -# if defined(_MSC_VER) -# define finite(n) _finite(n) -# endif -#endif - #if BLI_MATH_DO_INLINE #include "intern/math_base_inline.c" #endif diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 7836f12a03e..45edaca544d 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -127,6 +127,12 @@ void closest_to_plane3_v3(float r_close[3], const float plane[3], const float pt /* Set 'r' to the point in triangle (t1, t2, t3) closest to point 'p' */ void closest_on_tri_to_point_v3(float r[3], const float p[3], const float t1[3], const float t2[3], const float t3[3]); +float ray_point_factor_v3_ex( + const float p[3], const float ray_origin[3], const float ray_direction[3], + const float epsilon, const float fallback); +float ray_point_factor_v3( + const float p[3], const float ray_origin[3], const float ray_direction[3]); + float line_point_factor_v3_ex( const float p[3], const float l1[3], const float l2[3], const float epsilon, const float fallback); diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 6fb983a622e..6d6fbe4e7af 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -246,6 +246,9 @@ bool is_negative_m4(float mat[4][4]); bool is_zero_m3(float mat[3][3]); bool is_zero_m4(float mat[4][4]); +bool equals_m3m3(float mat1[3][3], float mat2[3][3]); +bool equals_m4m4(float mat1[4][4], float mat2[4][4]); + /* SpaceTransform helper */ typedef struct SpaceTransform { float local2target[4][4]; diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index c51446d6cc8..5f76b79b298 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -237,9 +237,9 @@ MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; -MINLINE bool is_finite_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; -MINLINE bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; -MINLINE bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; +bool is_finite_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; +bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; +bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_one_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; @@ -272,6 +272,7 @@ float angle_normalized_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT; float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT; float cos_v3v3v3(const float p1[3], const float p2[3], const float p3[3]) ATTR_WARN_UNUSED_RESULT; +float cos_v2v2v2(const float p1[2], const float p2[2], const float p3[2]) ATTR_WARN_UNUSED_RESULT; float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT; float angle_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT; float angle_signed_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 63a07957336..967e0be6d0a 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -21,6 +21,9 @@ #ifndef __BLI_TASK_H__ #define __BLI_TASK_H__ +struct Link; +struct ListBase; + /** \file BLI_task.h * \ingroup bli */ @@ -84,7 +87,9 @@ void BLI_task_pool_push_ex( TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata, TaskPriority priority); void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, - void *taskdata, bool free_taskdata, TaskPriority priority); + void *taskdata, bool free_taskdata, TaskPriority priority); +void BLI_task_pool_push_from_thread(TaskPool *pool, TaskRunFunction run, + void *taskdata, bool free_taskdata, TaskPriority priority, int thread_id); /* work and wait until all tasks are done */ void BLI_task_pool_work_and_wait(TaskPool *pool); @@ -114,11 +119,13 @@ size_t BLI_task_pool_tasks_done(TaskPool *pool); /* Parallel for routines */ typedef void (*TaskParallelRangeFunc)(void *userdata, const int iter); typedef void (*TaskParallelRangeFuncEx)(void *userdata, void *userdata_chunk, const int iter, const int thread_id); +typedef void (*TaskParallelRangeFuncFinalize)(void *userdata, void *userdata_chunk); void BLI_task_parallel_range_ex( int start, int stop, void *userdata, void *userdata_chunk, - const size_t userdata_chunk_size, TaskParallelRangeFuncEx func_ex, + const size_t userdata_chunk_size, + TaskParallelRangeFuncEx func_ex, const bool use_threading, const bool use_dynamic_scheduling); void BLI_task_parallel_range( @@ -127,6 +134,25 @@ void BLI_task_parallel_range( TaskParallelRangeFunc func, const bool use_threading); +void BLI_task_parallel_range_finalize( + int start, int stop, + void *userdata, + void *userdata_chunk, + const size_t userdata_chunk_size, + TaskParallelRangeFuncEx func_ex, + TaskParallelRangeFuncFinalize func_finalize, + const bool use_threading, + const bool use_dynamic_scheduling); + +typedef void (*TaskParallelListbaseFunc)(void *userdata, + struct Link *iter, + int index); +void BLI_task_parallel_listbase( + struct ListBase *listbase, + void *userdata, + TaskParallelListbaseFunc func, + const bool use_threading); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index bba3fdb37bc..6cef1924e33 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -1114,8 +1114,16 @@ void BLI_bvhtree_update_tree(BVHTree *tree) for (; index >= root; index--) node_join(tree, *index); } +/** + * Number of times #BLI_bvhtree_insert has been called. + * mainly useful for asserts functions to check we added the correct number. + */ +int BLI_bvhtree_get_size(const BVHTree *tree) +{ + return tree->totleaf; +} -float BLI_bvhtree_getepsilon(const BVHTree *tree) +float BLI_bvhtree_get_epsilon(const BVHTree *tree) { return tree->epsilon; } @@ -1565,7 +1573,7 @@ int BLI_bvhtree_find_nearest( /* Determines the distance that the ray must travel to hit the bounding volume of the given node */ -static float ray_nearest_hit(BVHRayCastData *data, const float bv[6]) +static float ray_nearest_hit(const BVHRayCastData *data, const float bv[6]) { int i; @@ -1635,7 +1643,9 @@ static void dfs_raycast(BVHRayCastData *data, BVHNode *node) * before calling the ray-primitive functions */ /* XXX: temporary solution for particles until fast_ray_nearest_hit supports ray.radius */ float dist = (data->ray.radius == 0.0f) ? fast_ray_nearest_hit(data, node) : ray_nearest_hit(data, node->bv); - if (dist >= data->hit.dist) return; + if (dist >= data->hit.dist) { + return; + } if (node->totnode == 0) { if (data->callback) { @@ -1662,6 +1672,9 @@ static void dfs_raycast(BVHRayCastData *data, BVHNode *node) } } +/** + * A version of #dfs_raycast with minor changes to reset the index & dist each ray cast. + */ static void dfs_raycast_all(BVHRayCastData *data, BVHNode *node) { int i; @@ -1670,18 +1683,16 @@ static void dfs_raycast_all(BVHRayCastData *data, BVHNode *node) * before calling the ray-primitive functions */ /* XXX: temporary solution for particles until fast_ray_nearest_hit supports ray.radius */ float dist = (data->ray.radius == 0.0f) ? fast_ray_nearest_hit(data, node) : ray_nearest_hit(data, node->bv); + if (dist >= data->hit.dist) { + return; + } if (node->totnode == 0) { - if (data->callback) { - data->hit.index = -1; - data->hit.dist = BVH_RAYCAST_DIST_MAX; - data->callback(data->userdata, node->index, &data->ray, &data->hit); - } - else { - data->hit.index = node->index; - data->hit.dist = dist; - madd_v3_v3v3fl(data->hit.co, data->ray.origin, data->ray.direction, dist); - } + /* no need to check for 'data->callback' (using 'all' only makes sense with a callback). */ + dist = data->hit.dist; + data->callback(data->userdata, node->index, &data->ray, &data->hit); + data->hit.index = -1; + data->hit.dist = dist; } else { /* pick loop direction to dive into the tree (based on ray direction and split axis) */ @@ -1832,9 +1843,14 @@ float BLI_bvhtree_bb_raycast(const float bv[6], const float light_start[3], cons /** * Calls the callback for every ray intersection + * + * \note Using a \a callback which resets or never sets the #BVHTreeRayHit index & dist works too, + * however using this function means existing generic callbacks can be used from custom callbacks without + * having to handle resetting the hit beforehand. + * It also avoid redundant argument and return value which aren't meaningful when collecting multiple hits. */ -int BLI_bvhtree_ray_cast_all_ex( - BVHTree *tree, const float co[3], const float dir[3], float radius, +void BLI_bvhtree_ray_cast_all_ex( + BVHTree *tree, const float co[3], const float dir[3], float radius, float hit_dist, BVHTree_RayCastCallback callback, void *userdata, int flag) { @@ -1842,6 +1858,7 @@ int BLI_bvhtree_ray_cast_all_ex( BVHNode *root = tree->nodes[tree->totleaf]; BLI_ASSERT_UNIT_V3(dir); + BLI_assert(callback != NULL); data.tree = tree; @@ -1855,20 +1872,18 @@ int BLI_bvhtree_ray_cast_all_ex( bvhtree_ray_cast_data_precalc(&data, flag); data.hit.index = -1; - data.hit.dist = BVH_RAYCAST_DIST_MAX; + data.hit.dist = hit_dist; if (root) { dfs_raycast_all(&data, root); } - - return data.hit.index; } -int BLI_bvhtree_ray_cast_all( - BVHTree *tree, const float co[3], const float dir[3], float radius, +void BLI_bvhtree_ray_cast_all( + BVHTree *tree, const float co[3], const float dir[3], float radius, float hit_dist, BVHTree_RayCastCallback callback, void *userdata) { - return BLI_bvhtree_ray_cast_all_ex(tree, co, dir, radius, callback, userdata, BVH_RAYCAST_DEFAULT); + BLI_bvhtree_ray_cast_all_ex(tree, co, dir, radius, hit_dist, callback, userdata, BVH_RAYCAST_DEFAULT); } diff --git a/source/blender/blenlib/intern/array_utils.c b/source/blender/blenlib/intern/array_utils.c index 52ca835876e..5d485e2033d 100644 --- a/source/blender/blenlib/intern/array_utils.c +++ b/source/blender/blenlib/intern/array_utils.c @@ -169,4 +169,128 @@ void _bli_array_binary_or( while (i--) { *(dst++) = *(src_a++) | *(src_b++); } -}
\ No newline at end of file +} + +/** + * Utility function to iterate over contiguous items in an array. + * + * \param use_wrap: Detect contiguous ranges across the first/last points. + * In this case the second index of \a span_step may be lower than the first, + * which indicates the values are wrapped. + * \param use_delimit_bounds: When false, ranges that defined by the start/end indices are excluded. + * This option has no effect when \a use_wrap is enabled. + * \param test_fn: Function to test if the item should be included in the range. + * \param user_data: User data for \a test_fn. + * \param span_step: Indices to iterate over, + * initialize both values to the array length to initialize iteration. + * \param: r_span_len: The length of the span, useful when \a use_wrap is enabled, + * where calculating the length isnt a simple subtraction. + */ +bool _bli_array_iter_span( + const void *arr, + unsigned int arr_len, size_t arr_stride, + bool use_wrap, bool use_delimit_bounds, + bool (*test_fn)(const void *arr_item, void *user_data), void *user_data, + unsigned int span_step[2], unsigned int *r_span_len) +{ + if (arr_len == 0) { + return false; + } + else if (use_wrap && (span_step[0] != arr_len) && (span_step[0] > span_step[1])) { + return false; + } + + const unsigned int arr_stride_uint = (unsigned int)arr_stride; + const void *item_prev; + bool test_prev; + + unsigned int i_curr; + + if ((span_step[0] == arr_len) && (span_step[1] == arr_len)) { + if (use_wrap) { + item_prev = POINTER_OFFSET(arr, (arr_len - 1) * arr_stride_uint); + i_curr = 0; + test_prev = test_fn(item_prev, user_data); + } + else if (use_delimit_bounds == false) { + item_prev = arr; + i_curr = 1; + test_prev = test_fn(item_prev, user_data); + } + else { + item_prev = NULL; + i_curr = 0; + test_prev = false; + } + } + else if ((i_curr = span_step[1] + 2) < arr_len) { + item_prev = POINTER_OFFSET(arr, (span_step[1] + 1) * arr_stride_uint); + test_prev = test_fn(item_prev, user_data); + } + else { + return false; + } + BLI_assert(i_curr < arr_len); + + const void *item_curr = POINTER_OFFSET(arr, i_curr * arr_stride_uint); + + while (i_curr < arr_len) { + bool test_curr = test_fn(item_curr, user_data); + if ((test_prev == false) && + (test_curr == true)) + { + unsigned int span_len; + unsigned int i_step_prev = i_curr; + + if (use_wrap) { + unsigned int i_step = i_curr + 1; + if (UNLIKELY(i_step == arr_len)) { + i_step = 0; + } + while (test_fn(POINTER_OFFSET(arr, i_step * arr_stride_uint), user_data)) { + i_step_prev = i_step; + i_step++; + if (UNLIKELY(i_step == arr_len)) { + i_step = 0; + } + } + + if (i_step_prev < i_curr) { + span_len = (i_step_prev + (arr_len - i_curr)) + 1; + } + else { + span_len = (i_step_prev - i_curr) + 1; + } + } + else { + unsigned int i_step = i_curr + 1; + while ((i_step != arr_len) && + test_fn(POINTER_OFFSET(arr, i_step * arr_stride_uint), user_data)) + { + i_step_prev = i_step; + i_step++; + } + + span_len = (i_step_prev - i_curr) + 1; + + if ((use_delimit_bounds == false) && (i_step_prev == arr_len - 1)) { + return false; + } + } + + span_step[0] = i_curr; + span_step[1] = i_step_prev; + *r_span_len = span_len; + + return true; + } + + test_prev = test_curr; + + item_prev = item_curr; + item_curr = POINTER_OFFSET(item_curr, arr_stride_uint); + i_curr++; + } + + return false; +} diff --git a/source/blender/blenlib/intern/graph.c b/source/blender/blenlib/intern/graph.c index 81cc9fde01f..911e8aae340 100644 --- a/source/blender/blenlib/intern/graph.c +++ b/source/blender/blenlib/intern/graph.c @@ -578,7 +578,7 @@ static void handleRadialSymmetry(BGraph *graph, BNode *root_node, int depth, flo unit = ring; /* fill in the ring */ - for (unit = ring, i = 0; i < root_node->degree; i++) { + for (i = 0; i < root_node->degree; i++) { BArc *connectedArc = root_node->arcs[i]; /* depth is store as a negative in flag. symmetry level is positive */ diff --git a/source/blender/blenlib/intern/math_base.c b/source/blender/blenlib/intern/math_base.c index 0a1e9e8a8a1..14eba3e61a4 100644 --- a/source/blender/blenlib/intern/math_base.c +++ b/source/blender/blenlib/intern/math_base.c @@ -56,7 +56,7 @@ double double_round(double x, int ndigits) pow2 = 1.0; y = (x * pow1) * pow2; /* if y overflows, then rounded value is exactly x */ - if (!finite(y)) + if (!isfinite(y)) return x; } else { diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index f70c12bd26e..8d2d80c2a35 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -33,7 +33,10 @@ #include <float.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> + +#ifdef __SSE2__ +# include <emmintrin.h> +#endif #include "BLI_math_base.h" @@ -311,4 +314,98 @@ MINLINE int signum_i(float a) else return 0; } +/* Internal helpers for SSE2 implementation. + * + * NOTE: Are to be called ONLY from inside `#ifdef __SSE2__` !!! + */ + +#ifdef __SSE2__ + +/* Calculate initial guess for arg^exp based on float representation + * This method gives a constant bias, which can be easily compensated by + * multiplicating with bias_coeff. + * Gives better results for exponents near 1 (e. g. 4/5). + * exp = exponent, encoded as uint32_t + * e2coeff = 2^(127/exponent - 127) * bias_coeff^(1/exponent), encoded as + * uint32_t + * + * We hope that exp and e2coeff gets properly inlined + */ +MALWAYS_INLINE __m128 _bli_math_fastpow(const int exp, + const int e2coeff, + const __m128 arg) +{ + __m128 ret; + ret = _mm_mul_ps(arg, _mm_castsi128_ps(_mm_set1_epi32(e2coeff))); + ret = _mm_cvtepi32_ps(_mm_castps_si128(ret)); + ret = _mm_mul_ps(ret, _mm_castsi128_ps(_mm_set1_epi32(exp))); + ret = _mm_castsi128_ps(_mm_cvtps_epi32(ret)); + return ret; +} + +/* Improve x ^ 1.0f/5.0f solution with Newton-Raphson method */ +MALWAYS_INLINE __m128 _bli_math_improve_5throot_solution( + const __m128 old_result, + const __m128 x) +{ + __m128 approx2 = _mm_mul_ps(old_result, old_result); + __m128 approx4 = _mm_mul_ps(approx2, approx2); + __m128 t = _mm_div_ps(x, approx4); + __m128 summ = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(4.0f), old_result), t); /* fma */ + return _mm_mul_ps(summ, _mm_set1_ps(1.0f / 5.0f)); +} + +/* Calculate powf(x, 2.4). Working domain: 1e-10 < x < 1e+10 */ +MALWAYS_INLINE __m128 _bli_math_fastpow24(const __m128 arg) +{ + /* max, avg and |avg| errors were calculated in gcc without FMA instructions + * The final precision should be better than powf in glibc */ + + /* Calculate x^4/5, coefficient 0.994 was constructed manually to minimize + * avg error. + */ + /* 0x3F4CCCCD = 4/5 */ + /* 0x4F55A7FB = 2^(127/(4/5) - 127) * 0.994^(1/(4/5)) */ + /* error max = 0.17 avg = 0.0018 |avg| = 0.05 */ + __m128 x = _bli_math_fastpow(0x3F4CCCCD, 0x4F55A7FB, arg); + __m128 arg2 = _mm_mul_ps(arg, arg); + __m128 arg4 = _mm_mul_ps(arg2, arg2); + /* error max = 0.018 avg = 0.0031 |avg| = 0.0031 */ + x = _bli_math_improve_5throot_solution(x, arg4); + /* error max = 0.00021 avg = 1.6e-05 |avg| = 1.6e-05 */ + x = _bli_math_improve_5throot_solution(x, arg4); + /* error max = 6.1e-07 avg = 5.2e-08 |avg| = 1.1e-07 */ + x = _bli_math_improve_5throot_solution(x, arg4); + return _mm_mul_ps(x, _mm_mul_ps(x, x)); +} + +/* Calculate powf(x, 1.0f / 2.4) */ +MALWAYS_INLINE __m128 _bli_math_fastpow512(const __m128 arg) +{ + /* 5/12 is too small, so compute the 4th root of 20/12 instead. + * 20/12 = 5/3 = 1 + 2/3 = 2 - 1/3. 2/3 is a suitable argument for fastpow. + * weighting coefficient: a^-1/2 = 2 a; a = 2^-2/3 + */ + __m128 xf = _bli_math_fastpow(0x3f2aaaab, 0x5eb504f3, arg); + __m128 xover = _mm_mul_ps(arg, xf); + __m128 xfm1 = _mm_rsqrt_ps(xf); + __m128 x2 = _mm_mul_ps(arg, arg); + __m128 xunder = _mm_mul_ps(x2, xfm1); + /* sqrt2 * over + 2 * sqrt2 * under */ + __m128 xavg = _mm_mul_ps(_mm_set1_ps(1.0f / (3.0f * 0.629960524947437f) * 0.999852f), + _mm_add_ps(xover, xunder)); + xavg = _mm_mul_ps(xavg, _mm_rsqrt_ps(xavg)); + xavg = _mm_mul_ps(xavg, _mm_rsqrt_ps(xavg)); + return xavg; +} + +MALWAYS_INLINE __m128 _bli_math_blend_sse(const __m128 mask, + const __m128 a, + const __m128 b) +{ + return _mm_or_ps(_mm_and_ps(mask, a), _mm_andnot_ps(mask, b)); +} + +#endif /* __SSE2__ */ + #endif /* __MATH_BASE_INLINE_C__ */ diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index 45466226e72..180d62105c4 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -28,6 +28,7 @@ */ +#include "BLI_math_base.h" #include "BLI_math_color.h" #include "BLI_utildefines.h" @@ -38,6 +39,52 @@ /******************************** Color Space ********************************/ +#ifdef __SSE2__ + +MALWAYS_INLINE __m128 srgb_to_linearrgb_v4_simd(const __m128 c) +{ + __m128 cmp = _mm_cmplt_ps(c, _mm_set1_ps(0.04045f)); + __m128 lt = _mm_max_ps(_mm_mul_ps(c, _mm_set1_ps(1.0f / 12.92f)), + _mm_set1_ps(0.0f)); + __m128 gtebase = _mm_mul_ps(_mm_add_ps(c, _mm_set1_ps(0.055f)), + _mm_set1_ps(1.0f / 1.055f)); /* fma */ + __m128 gte = _bli_math_fastpow24(gtebase); + return _bli_math_blend_sse(cmp, lt, gte); +} + +MALWAYS_INLINE __m128 linearrgb_to_srgb_v4_simd(const __m128 c) +{ + __m128 cmp = _mm_cmplt_ps(c, _mm_set1_ps(0.0031308f)); + __m128 lt = _mm_max_ps(_mm_mul_ps(c, _mm_set1_ps(12.92f)), + _mm_set1_ps(0.0f)); + __m128 gte = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(1.055f), + _bli_math_fastpow512(c)), + _mm_set1_ps(-0.055f)); + return _bli_math_blend_sse(cmp, lt, gte); +} + +MINLINE void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3]) +{ + float r[4] = {srgb[0], srgb[1], srgb[2], 0.0f}; + __m128 *rv = (__m128 *)&r; + *rv = srgb_to_linearrgb_v4_simd(*rv); + linear[0] = r[0]; + linear[1] = r[1]; + linear[2] = r[2]; +} + +MINLINE void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3]) +{ + float r[4] = {linear[0], linear[1], linear[2], 0.0f}; + __m128 *rv = (__m128 *)&r; + *rv = linearrgb_to_srgb_v4_simd(*rv); + srgb[0] = r[0]; + srgb[1] = r[1]; + srgb[2] = r[2]; +} + +#else /* __SSE2__ */ + MINLINE void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3]) { linear[0] = srgb_to_linearrgb(srgb[0]); @@ -51,6 +98,7 @@ MINLINE void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3]) srgb[1] = linearrgb_to_srgb(linear[1]); srgb[2] = linearrgb_to_srgb(linear[2]); } +#endif /* __SSE2__ */ MINLINE void srgb_to_linearrgb_v4(float linear[4], const float srgb[4]) { @@ -98,10 +146,14 @@ MINLINE void srgb_to_linearrgb_predivide_v4(float linear[4], const float srgb[4] inv_alpha = 1.0f / alpha; } - linear[0] = srgb_to_linearrgb(srgb[0] * inv_alpha) * alpha; - linear[1] = srgb_to_linearrgb(srgb[1] * inv_alpha) * alpha; - linear[2] = srgb_to_linearrgb(srgb[2] * inv_alpha) * alpha; + linear[0] = srgb[0] * inv_alpha; + linear[1] = srgb[1] * inv_alpha; + linear[2] = srgb[2] * inv_alpha; linear[3] = srgb[3]; + srgb_to_linearrgb_v3_v3(linear, linear); + linear[0] *= alpha; + linear[1] *= alpha; + linear[2] *= alpha; } MINLINE void linearrgb_to_srgb_predivide_v4(float srgb[4], const float linear[4]) @@ -117,10 +169,14 @@ MINLINE void linearrgb_to_srgb_predivide_v4(float srgb[4], const float linear[4] inv_alpha = 1.0f / alpha; } - srgb[0] = linearrgb_to_srgb(linear[0] * inv_alpha) * alpha; - srgb[1] = linearrgb_to_srgb(linear[1] * inv_alpha) * alpha; - srgb[2] = linearrgb_to_srgb(linear[2] * inv_alpha) * alpha; + srgb[0] = linear[0] * inv_alpha; + srgb[1] = linear[1] * inv_alpha; + srgb[2] = linear[2] * inv_alpha; srgb[3] = linear[3]; + linearrgb_to_srgb_v3_v3(srgb, srgb); + srgb[0] *= alpha; + srgb[1] *= alpha; + srgb[2] *= alpha; } /* LUT accelerated conversions */ diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 482709e572f..f80099bee1b 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2506,6 +2506,22 @@ float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], return lambda; } +float ray_point_factor_v3_ex( + const float p[3], const float ray_origin[3], const float ray_direction[3], + const float epsilon, const float fallback) +{ + float p_relative[3]; + sub_v3_v3v3(p_relative, p, ray_origin); + const float dot = len_squared_v3(ray_direction); + return (dot > epsilon) ? (dot_v3v3(ray_direction, p_relative) / dot) : fallback; +} + +float ray_point_factor_v3( + const float p[3], const float ray_origin[3], const float ray_direction[3]) +{ + return ray_point_factor_v3_ex(p, ray_origin, ray_direction, 0.0f, 0.0f); +} + /** * A simplified version of #closest_to_line_v3 * we only need to return the ``lambda`` @@ -2525,8 +2541,8 @@ float line_point_factor_v3_ex( return (dot_v3v3(u, h) / dot_v3v3(u, u)); #else /* better check for zero */ - dot = dot_v3v3(u, u); - return (fabsf(dot) > epsilon) ? (dot_v3v3(u, h) / dot) : fallback; + dot = len_squared_v3(u); + return (dot > epsilon) ? (dot_v3v3(u, h) / dot) : fallback; #endif } float line_point_factor_v3( @@ -2547,8 +2563,8 @@ float line_point_factor_v2_ex( return (dot_v2v2(u, h) / dot_v2v2(u, u)); #else /* better check for zero */ - dot = dot_v2v2(u, u); - return (fabsf(dot) > epsilon) ? (dot_v2v2(u, h) / dot) : fallback; + dot = len_squared_v2(u); + return (dot > epsilon) ? (dot_v2v2(u, h) / dot) : fallback; #endif } diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index 52ff6fd3e8a..0e3f905ef16 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -1828,6 +1828,21 @@ bool is_zero_m4(float mat[4][4]) is_zero_v4(mat[3])); } +bool equals_m3m3(float mat1[3][3], float mat2[3][3]) +{ + return (equals_v3v3(mat1[0], mat2[0]) && + equals_v3v3(mat1[1], mat2[1]) && + equals_v3v3(mat1[2], mat2[2])); +} + +bool equals_m4m4(float mat1[4][4], float mat2[4][4]) +{ + return (equals_v4v4(mat1[0], mat2[0]) && + equals_v4v4(mat1[1], mat2[1]) && + equals_v4v4(mat1[2], mat2[2]) && + equals_v4v4(mat1[3], mat2[3])); +} + /* make a 4x4 matrix out of 3 transform components */ /* matrices are made in the order: scale * rot * loc */ /* TODO: need to have a version that allows for rotation order... */ diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 8d33e04241a..7f2db3743df 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -336,6 +336,25 @@ void flip_v2_v2v2(float v[2], const float v1[2], const float v2[2]) v[1] = v1[1] + (v1[1] - v2[1]); } + +/********************************* Comparison ********************************/ + +bool is_finite_v2(const float v[2]) +{ + return (isfinite(v[0]) && isfinite(v[1])); +} + +bool is_finite_v3(const float v[3]) +{ + return (isfinite(v[0]) && isfinite(v[1]) && isfinite(v[2])); +} + +bool is_finite_v4(const float v[4]) +{ + return (isfinite(v[0]) && isfinite(v[1]) && isfinite(v[2]) && isfinite(v[3])); +} + + /********************************** Angles ***********************************/ /* Return the angle in radians between vecs 1-2 and 2-3 in radians @@ -397,6 +416,19 @@ float angle_v2v2v2(const float v1[2], const float v2[2], const float v3[2]) return angle_normalized_v2v2(vec1, vec2); } +/* Quicker than full angle computation */ +float cos_v2v2v2(const float p1[2], const float p2[2], const float p3[2]) +{ + float vec1[2], vec2[2]; + + sub_v2_v2v2(vec1, p2, p1); + sub_v2_v2v2(vec2, p2, p3); + normalize_v2(vec1); + normalize_v2(vec2); + + return dot_v2v2(vec1, vec2); +} + /* Return the shortest angle in radians between the 2 vectors */ float angle_v2v2(const float v1[2], const float v2[2]) { @@ -578,19 +610,28 @@ void project_v3_v3v3(float c[3], const float v1[3], const float v2[3]) * Projecting will make \a c a copy of \a v orthogonal to \a v_plane. * * \note If \a v is exactly perpendicular to \a v_plane, \a c will just be a copy of \a v. + * + * \note This function is a convenience to call: + * \code{.c} + * project_v3_v3v3(c, v, v_plane); + * sub_v3_v3v3(c, v, c); + * \endcode */ void project_plane_v3_v3v3(float c[3], const float v[3], const float v_plane[3]) { - float delta[3]; - project_v3_v3v3(delta, v, v_plane); - sub_v3_v3v3(c, v, delta); + const float mul = dot_v3v3(v, v_plane) / dot_v3v3(v_plane, v_plane); + + c[0] = v[0] - (mul * v_plane[0]); + c[1] = v[1] - (mul * v_plane[1]); + c[2] = v[2] - (mul * v_plane[2]); } void project_plane_v2_v2v2(float c[2], const float v[2], const float v_plane[2]) { - float delta[2]; - project_v2_v2v2(delta, v, v_plane); - sub_v2_v2v2(c, v, delta); + const float mul = dot_v2v2(v, v_plane) / dot_v2v2(v_plane, v_plane); + + c[0] = v[0] - (mul * v_plane[0]); + c[1] = v[1] - (mul * v_plane[1]); } /* project a vector on a plane defined by normal and a plane point p */ @@ -647,7 +688,7 @@ void ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3]) if (f > eps) { const float d = 1.0f / sqrtf(f); - BLI_assert(finite(d)); + BLI_assert(isfinite(d)); r_n1[0] = n[1] * d; r_n1[1] = -n[0] * d; diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 17e2da3f1cb..b43fb6e986c 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -952,21 +952,6 @@ MINLINE bool is_zero_v4(const float v[4]) return (v[0] == 0.0f && v[1] == 0.0f && v[2] == 0.0f && v[3] == 0.0f); } -MINLINE bool is_finite_v2(const float v[2]) -{ - return (finite(v[0]) && finite(v[1])); -} - -MINLINE bool is_finite_v3(const float v[3]) -{ - return (finite(v[0]) && finite(v[1]) && finite(v[2])); -} - -MINLINE bool is_finite_v4(const float v[4]) -{ - return (finite(v[0]) && finite(v[1]) && finite(v[2]) && finite(v[3])); -} - MINLINE bool is_one_v3(const float v[3]) { return (v[0] == 1.0f && v[1] == 1.0f && v[2] == 1.0f); diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index fadfb64f1e1..2b793841c38 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1927,8 +1927,7 @@ const char *BLI_first_slash(const char *string) if (!ffslash) return fbslash; else if (!fbslash) return ffslash; - if ((intptr_t)ffslash < (intptr_t)fbslash) return ffslash; - else return fbslash; + return (ffslash < fbslash) ? ffslash : fbslash; } /** @@ -1942,8 +1941,7 @@ const char *BLI_last_slash(const char *string) if (!lfslash) return lbslash; else if (!lbslash) return lfslash; - if ((intptr_t)lfslash < (intptr_t)lbslash) return lbslash; - else return lfslash; + return (lfslash > lbslash) ? lfslash : lbslash; } /** diff --git a/source/blender/blenlib/intern/polyfill2d.c b/source/blender/blenlib/intern/polyfill2d.c index 800f4cb23c3..8d9881e4539 100644 --- a/source/blender/blenlib/intern/polyfill2d.c +++ b/source/blender/blenlib/intern/polyfill2d.c @@ -768,11 +768,7 @@ static void pf_ear_tip_cut(PolyFill *pf, PolyIndex *pi_ear_tip) } /** - * Triangulates the given (convex or concave) simple polygon to a list of triangle vertices. - * - * \param coords pairs describing vertices of the polygon, in either clockwise or counterclockwise order. - * \return triples of triangle indices in clockwise order. - * Note the returned array is reused for later calls to the same method. + * Initializes the #PolyFill structure before tessellating with #polyfill_calc. */ static void polyfill_prepare( PolyFill *pf, @@ -862,6 +858,9 @@ static void polyfill_calc( pf_triangulate(pf); } +/** + * A version of #BLI_polyfill_calc that uses a memory arena to avoid re-allocations. + */ void BLI_polyfill_calc_arena( const float (*coords)[2], const unsigned int coords_tot, @@ -905,6 +904,19 @@ void BLI_polyfill_calc_arena( #endif } +/** + * Triangulates the given (convex or concave) simple polygon to a list of triangle vertices. + * + * \param coords: 2D coordinates describing vertices of the polygon, + * in either clockwise or counterclockwise order. + * \param coords_tot: Total points in the array. + * \param coords_sign: Pass this when we know the sign in advance to avoid extra calculations. + * + * \param r_tris: This array is filled in with triangle indices in clockwise order. + * The length of the array must be ``coords_tot - 2``. + * Indices are guaranteed to be assigned to unique triangles, with valid indices, + * even in the case of degenerate input (self intersecting polygons, zero area ears... etc). + */ void BLI_polyfill_calc( const float (*coords)[2], const unsigned int coords_tot, diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index 8a96daeeb91..4406a45d4fc 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -28,6 +28,15 @@ /** \file blender/blenlib/intern/scanfill.c * \ingroup bli + * + * Triangulate multiple 2D/3D polygon with support for holes, + * use for tessellating curves, fonts and geometry. + * See main function #BLI_scanfill_calc + * + * Uses sweep-line method. + * + * \note There is a similar API in polyfill2d.c + * which uses ear clipping, but has no hole support. */ #include <stdio.h> @@ -602,7 +611,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl else { /* test rest of vertices */ ScanFillVertLink *best_sc = NULL; - float best_angle = 3.14f; + float angle_best_cos = -1.0f; float miny; bool firsttime = false; @@ -633,21 +642,18 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl best_sc = sc1; } else { - float angle; - /* prevent angle calc for the simple cases only 1 vertex is found */ if (firsttime == false) { - best_angle = angle_v2v2v2(v2->xy, v1->xy, best_sc->vert->xy); + angle_best_cos = cos_v2v2v2(v2->xy, v1->xy, best_sc->vert->xy); firsttime = true; } - angle = angle_v2v2v2(v2->xy, v1->xy, sc1->vert->xy); - if (angle < best_angle) { + const float angle_test_cos = cos_v2v2v2(v2->xy, v1->xy, sc1->vert->xy); + if (angle_test_cos > angle_best_cos) { best_sc = sc1; - best_angle = angle; + angle_best_cos = angle_test_cos; } } - } } } diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c index e7938b750da..4ef42dcce6f 100644 --- a/source/blender/blenlib/intern/task.c +++ b/source/blender/blenlib/intern/task.c @@ -28,6 +28,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_listBase.h" + #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_task.h" @@ -35,8 +37,17 @@ #include "atomic_ops.h" +/* Define this to enable some detailed statistic print. */ +#undef DEBUG_STATS + /* Types */ +/* Number of per-thread pre-allocated tasks. + * + * For more details see description of TaskMemPool. + */ +#define MEMPOOL_SIZE 256 + typedef struct Task { struct Task *next, *prev; @@ -47,6 +58,50 @@ typedef struct Task { TaskPool *pool; } Task; +/* This is a per-thread storage of pre-allocated tasks. + * + * The idea behind this is simple: reduce amount of malloc() calls when pushing + * new task to the pool. This is done by keeping memory from the tasks which + * were finished already, so instead of freeing that memory we put it to the + * pool for the later re-use. + * + * The tricky part here is to avoid any inter-thread synchronization, hence no + * lock must exist around this pool. The pool will become an owner of the pointer + * from freed task, and only corresponding thread will be able to use this pool + * (no memory stealing and such). + * + * This leads to the following use of the pool: + * + * - task_push() should provide proper thread ID from which the task is being + * pushed from. + * + * - Task allocation function which check corresponding memory pool and if there + * is any memory in there it'll mark memory as re-used, remove it from the pool + * and use that memory for the new task. + * + * At this moment task queue owns the memory. + * + * - When task is done and task_free() is called the memory will be put to the + * pool which corresponds to a thread which handled the task. + */ +typedef struct TaskMemPool { + /* Number of pre-allocated tasks in the pool. */ + int num_tasks; + /* Pre-allocated task memory pointers. */ + Task *tasks[MEMPOOL_SIZE]; +} TaskMemPool; + +#ifdef DEBUG_STATS +typedef struct TaskMemPoolStats { + /* Number of allocations. */ + int num_alloc; + /* Number of avoided allocations (pointer was re-used from the pool). */ + int num_reuse; + /* Number of discarded memory due to pool saturation, */ + int num_discard; +} TaskMemPoolStats; +#endif + struct TaskPool { TaskScheduler *scheduler; @@ -62,14 +117,32 @@ struct TaskPool { volatile bool do_cancel; - /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler has to use its special - * background fallback thread in case we are in single-threaded situation. */ + /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler + * has to use its special background fallback thread in case we are in + * single-threaded situation. + */ bool run_in_background; + + /* This pool is used for caching task pointers for thread id 0. + * This could either point to a global scheduler's task_mempool[0] if the + * pool is handled form the main thread or point to task_mempool_local + * otherwise. + * + * This way we solve possible threading conflicts accessing same global + * memory pool from multiple threads from which wait_work() is called. + */ + TaskMemPool *task_mempool; + TaskMemPool task_mempool_local; + +#ifdef DEBUG_STATS + TaskMemPoolStats *mempool_stats; +#endif }; struct TaskScheduler { pthread_t *threads; struct TaskThread *task_threads; + TaskMemPool *task_mempool; int num_threads; bool background_thread_only; @@ -98,6 +171,63 @@ static void task_data_free(Task *task, const int thread_id) } } +BLI_INLINE TaskMemPool *get_task_mempool(TaskPool *pool, const int thread_id) +{ + if (thread_id == 0) { + return pool->task_mempool; + } + return &pool->scheduler->task_mempool[thread_id]; +} + +static Task *task_alloc(TaskPool *pool, const int thread_id) +{ + assert(thread_id <= pool->scheduler->num_threads); + if (thread_id != -1) { + assert(thread_id >= 0); + TaskMemPool *mem_pool = get_task_mempool(pool, thread_id); + /* Try to re-use task memory from a thread local storage. */ + if (mem_pool->num_tasks > 0) { + --mem_pool->num_tasks; + /* Success! We've just avoided task allocation. */ +#ifdef DEBUG_STATS + pool->mempool_stats[thread_id].num_reuse++; +#endif + return mem_pool->tasks[mem_pool->num_tasks]; + } + /* We are doomed to allocate new task data. */ +#ifdef DEBUG_STATS + pool->mempool_stats[thread_id].num_alloc++; +#endif + } + return MEM_mallocN(sizeof(Task), "New task"); +} + +static void task_free(TaskPool *pool, Task *task, const int thread_id) +{ + task_data_free(task, thread_id); + assert(thread_id >= 0); + assert(thread_id <= pool->scheduler->num_threads); + TaskMemPool *mem_pool = get_task_mempool(pool, thread_id); + if (mem_pool->num_tasks < MEMPOOL_SIZE - 1) { + /* Successfully allowed the task to be re-used later. */ + mem_pool->tasks[mem_pool->num_tasks] = task; + ++mem_pool->num_tasks; + } + else { + /* Local storage saturated, no other way than just discard + * the memory. + * + * TODO(sergey): We can perhaps store such pointer in a global + * scheduler pool, maybe it'll be faster than discarding and + * allocating again. + */ + MEM_freeN(task); +#ifdef DEBUG_STATS + pool->mempool_stats[thread_id].num_discard++; +#endif + } +} + /* Task Scheduler */ static void task_pool_num_decrease(TaskPool *pool, size_t done) @@ -196,8 +326,7 @@ static void *task_scheduler_thread_run(void *thread_p) task->run(pool, task->taskdata, thread_id); /* delete task */ - task_data_free(task, thread_id); - MEM_freeN(task); + task_free(pool, task, thread_id); /* notify pool task was done */ task_pool_num_decrease(pool, 1); @@ -249,6 +378,9 @@ TaskScheduler *BLI_task_scheduler_create(int num_threads) fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads); } } + + scheduler->task_mempool = MEM_callocN(sizeof(*scheduler->task_mempool) * (num_threads + 1), + "TaskScheduler task_mempool"); } return scheduler; @@ -281,6 +413,16 @@ void BLI_task_scheduler_free(TaskScheduler *scheduler) MEM_freeN(scheduler->task_threads); } + /* Delete task memory pool */ + if (scheduler->task_mempool) { + for (int i = 0; i <= scheduler->num_threads; ++i) { + for (int j = 0; j < scheduler->task_mempool[i].num_tasks; ++j) { + MEM_freeN(scheduler->task_mempool[i].tasks[j]); + } + } + MEM_freeN(scheduler->task_mempool); + } + /* delete leftover tasks */ for (task = scheduler->queue.first; task; task = task->next) { task_data_free(task, 0); @@ -344,7 +486,7 @@ static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool) static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, void *userdata, const bool is_background) { - TaskPool *pool = MEM_callocN(sizeof(TaskPool), "TaskPool"); + TaskPool *pool = MEM_mallocN(sizeof(TaskPool), "TaskPool"); #ifndef NDEBUG /* Assert we do not try to create a background pool from some parent task - those only work OK from main thread. */ @@ -360,6 +502,7 @@ static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, void *userdata, c pool->scheduler = scheduler; pool->num = 0; + pool->done = 0; pool->num_threads = 0; pool->currently_running_tasks = 0; pool->do_cancel = false; @@ -371,6 +514,20 @@ static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, void *userdata, c pool->userdata = userdata; BLI_mutex_init(&pool->user_mutex); + if (BLI_thread_is_main()) { + pool->task_mempool = scheduler->task_mempool; + } + else { + pool->task_mempool = &pool->task_mempool_local; + pool->task_mempool_local.num_tasks = 0; + } + +#ifdef DEBUG_STATS + pool->mempool_stats = + MEM_callocN(sizeof(*pool->mempool_stats) * (scheduler->num_threads + 1), + "per-taskpool mempool stats"); +#endif + /* Ensure malloc will go fine from threads, * * This is needed because we could be in main thread here @@ -416,16 +573,36 @@ void BLI_task_pool_free(TaskPool *pool) BLI_mutex_end(&pool->user_mutex); + /* Free local memory pool, those pointers are lost forever. */ + if (pool->task_mempool == &pool->task_mempool_local) { + for (int i = 0; i < pool->task_mempool_local.num_tasks; i++) { + MEM_freeN(pool->task_mempool_local.tasks[i]); + } + } + +#ifdef DEBUG_STATS + printf("Thread ID Allocated Reused Discarded\n"); + for (int i = 0; i < pool->scheduler->num_threads + 1; ++i) { + printf("%02d %05d %05d %05d\n", + i, + pool->mempool_stats[i].num_alloc, + pool->mempool_stats[i].num_reuse, + pool->mempool_stats[i].num_discard); + } + MEM_freeN(pool->mempool_stats); +#endif + MEM_freeN(pool); BLI_end_threaded_malloc(); } -void BLI_task_pool_push_ex( +static void task_pool_push( TaskPool *pool, TaskRunFunction run, void *taskdata, - bool free_taskdata, TaskFreeFunction freedata, TaskPriority priority) + bool free_taskdata, TaskFreeFunction freedata, TaskPriority priority, + int thread_id) { - Task *task = MEM_callocN(sizeof(Task), "Task"); + Task *task = task_alloc(pool, thread_id); task->run = run; task->taskdata = taskdata; @@ -436,12 +613,25 @@ void BLI_task_pool_push_ex( task_scheduler_push(pool->scheduler, task, priority); } +void BLI_task_pool_push_ex( + TaskPool *pool, TaskRunFunction run, void *taskdata, + bool free_taskdata, TaskFreeFunction freedata, TaskPriority priority) +{ + task_pool_push(pool, run, taskdata, free_taskdata, freedata, priority, -1); +} + void BLI_task_pool_push( TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskPriority priority) { BLI_task_pool_push_ex(pool, run, taskdata, free_taskdata, NULL, priority); } +void BLI_task_pool_push_from_thread(TaskPool *pool, TaskRunFunction run, + void *taskdata, bool free_taskdata, TaskPriority priority, int thread_id) +{ + task_pool_push(pool, run, taskdata, free_taskdata, NULL, priority, thread_id); +} + void BLI_task_pool_work_and_wait(TaskPool *pool) { TaskScheduler *scheduler = pool->scheduler; @@ -481,8 +671,7 @@ void BLI_task_pool_work_and_wait(TaskPool *pool) work_task->run(pool, work_task->taskdata, 0); /* delete task */ - task_data_free(task, 0); - MEM_freeN(work_task); + task_free(pool, task, 0); /* notify pool task was done */ task_pool_num_decrease(pool, 1); @@ -563,16 +752,13 @@ size_t BLI_task_pool_tasks_done(TaskPool *pool) * * Main functions: * - #BLI_task_parallel_range + * - #BLI_task_parallel_listbase (#ListBase - double linked list) * * TODO: - * - #BLI_task_parallel_foreach_listbase (#ListBase - double linked list) * - #BLI_task_parallel_foreach_link (#Link - single linked list) * - #BLI_task_parallel_foreach_ghash/gset (#GHash/#GSet - hash & set) * - #BLI_task_parallel_foreach_mempool (#BLI_mempool - iterate over mempools) * - * Possible improvements: - * - * - Chunk iterations to reduce number of spin locks. */ /* Allows to avoid using malloc for userdata_chunk in tasks, when small enough. */ @@ -582,53 +768,38 @@ size_t BLI_task_pool_tasks_done(TaskPool *pool) typedef struct ParallelRangeState { int start, stop; void *userdata; - void *userdata_chunk; - size_t userdata_chunk_size; TaskParallelRangeFunc func; TaskParallelRangeFuncEx func_ex; int iter; int chunk_size; - SpinLock lock; } ParallelRangeState; BLI_INLINE bool parallel_range_next_iter_get( ParallelRangeState * __restrict state, int * __restrict iter, int * __restrict count) { - bool result = false; - BLI_spin_lock(&state->lock); - if (state->iter < state->stop) { - *count = min_ii(state->chunk_size, state->stop - state->iter); - *iter = state->iter; - state->iter += *count; - result = true; - } - BLI_spin_unlock(&state->lock); - return result; + uint32_t previter = atomic_fetch_and_add_uint32((uint32_t *)(&state->iter), state->chunk_size); + + *iter = (int)previter; + *count = max_ii(0, min_ii(state->chunk_size, state->stop - previter)); + + return (previter < state->stop); } static void parallel_range_func( TaskPool * __restrict pool, - void *UNUSED(taskdata), + void *userdata_chunk, int threadid) { ParallelRangeState * __restrict state = BLI_task_pool_userdata(pool); int iter, count; - const bool use_userdata_chunk = (state->func_ex != NULL) && - (state->userdata_chunk_size != 0) && (state->userdata_chunk != NULL); - void *userdata_chunk = use_userdata_chunk ? MALLOCA(state->userdata_chunk_size) : NULL; - while (parallel_range_next_iter_get(state, &iter, &count)) { int i; if (state->func_ex) { - if (use_userdata_chunk) { - memcpy(userdata_chunk, state->userdata_chunk, state->userdata_chunk_size); - } - for (i = 0; i < count; ++i) { state->func_ex(state->userdata, userdata_chunk, iter + i, threadid); } @@ -639,8 +810,6 @@ static void parallel_range_func( } } } - - MALLOCA_FREE(userdata_chunk, state->userdata_chunk_size); } /** @@ -655,6 +824,7 @@ static void task_parallel_range_ex( const size_t userdata_chunk_size, TaskParallelRangeFunc func, TaskParallelRangeFuncEx func_ex, + TaskParallelRangeFuncFinalize func_finalize, const bool use_threading, const bool use_dynamic_scheduling) { @@ -663,6 +833,10 @@ static void task_parallel_range_ex( ParallelRangeState state; int i, num_threads, num_tasks; + void *userdata_chunk_local = NULL; + void *userdata_chunk_array = NULL; + const bool use_userdata_chunk = (func_ex != NULL) && (userdata_chunk_size != 0) && (userdata_chunk != NULL); + if (start == stop) { return; } @@ -678,9 +852,6 @@ static void task_parallel_range_ex( */ if (!use_threading) { if (func_ex) { - const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); - void *userdata_chunk_local = NULL; - if (use_userdata_chunk) { userdata_chunk_local = MALLOCA(userdata_chunk_size); memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); @@ -690,6 +861,10 @@ static void task_parallel_range_ex( func_ex(userdata, userdata_chunk, i, 0); } + if (func_finalize) { + func_finalize(userdata, userdata_chunk); + } + MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size); } else { @@ -711,12 +886,9 @@ static void task_parallel_range_ex( */ num_tasks = num_threads * 2; - BLI_spin_init(&state.lock); state.start = start; state.stop = stop; state.userdata = userdata; - state.userdata_chunk = userdata_chunk; - state.userdata_chunk_size = userdata_chunk_size; state.func = func; state.func_ex = func_ex; state.iter = start; @@ -727,23 +899,41 @@ static void task_parallel_range_ex( state.chunk_size = max_ii(1, (stop - start) / (num_tasks)); } - num_tasks = max_ii(1, (stop - start) / state.chunk_size); + num_tasks = min_ii(num_tasks, (stop - start) / state.chunk_size); + atomic_fetch_and_add_uint32((uint32_t *)(&state.iter), 0); + + if (use_userdata_chunk) { + userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks); + } for (i = 0; i < num_tasks; i++) { - BLI_task_pool_push(task_pool, - parallel_range_func, - NULL, false, - TASK_PRIORITY_HIGH); + if (use_userdata_chunk) { + userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); + memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + } + /* Use this pool's pre-allocated tasks. */ + BLI_task_pool_push_from_thread(task_pool, + parallel_range_func, + userdata_chunk_local, false, + TASK_PRIORITY_HIGH, 0); } BLI_task_pool_work_and_wait(task_pool); BLI_task_pool_free(task_pool); - BLI_spin_end(&state.lock); + if (use_userdata_chunk) { + if (func_finalize) { + for (i = 0; i < num_tasks; i++) { + userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); + func_finalize(userdata, userdata_chunk_local); + } + } + MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks); + } } /** - * This function allows to parallelized for loops in a similar way to OpenMP's 'parallel for' statement. + * This function allows to parallelize for loops in a similar way to OpenMP's 'parallel for' statement. * * \param start First index to process. * \param stop Index to stop looping (excluded). @@ -767,7 +957,7 @@ void BLI_task_parallel_range_ex( const bool use_dynamic_scheduling) { task_parallel_range_ex( - start, stop, userdata, userdata_chunk, userdata_chunk_size, NULL, func_ex, + start, stop, userdata, userdata_chunk, userdata_chunk_size, NULL, func_ex, NULL, use_threading, use_dynamic_scheduling); } @@ -788,9 +978,153 @@ void BLI_task_parallel_range( TaskParallelRangeFunc func, const bool use_threading) { - task_parallel_range_ex(start, stop, userdata, NULL, 0, func, NULL, use_threading, false); + task_parallel_range_ex(start, stop, userdata, NULL, 0, func, NULL, NULL, use_threading, false); +} + +/** + * This function allows to parallelize for loops in a similar way to OpenMP's 'parallel for' statement, + * with an additional 'finalize' func called from calling thread once whole range have been processed. + * + * \param start First index to process. + * \param stop Index to stop looping (excluded). + * \param userdata Common userdata passed to all instances of \a func. + * \param userdata_chunk Optional, each instance of looping chunks will get a copy of this data + * (similar to OpenMP's firstprivate). + * \param userdata_chunk_size Memory size of \a userdata_chunk. + * \param func_ex Callback function (advanced version). + * \param func_finalize Callback function, called after all workers have finisehd, useful to finalize accumulative tasks. + * \param use_threading If \a true, actually split-execute loop in threads, else just do a sequential forloop + * (allows caller to use any kind of test to switch on parallelization or not). + * \param use_dynamic_scheduling If \a true, the whole range is divided in a lot of small chunks (of size 32 currently), + * otherwise whole range is split in a few big chunks (num_threads * 2 chunks currently). + */ +void BLI_task_parallel_range_finalize( + int start, int stop, + void *userdata, + void *userdata_chunk, + const size_t userdata_chunk_size, + TaskParallelRangeFuncEx func_ex, + TaskParallelRangeFuncFinalize func_finalize, + const bool use_threading, + const bool use_dynamic_scheduling) +{ + task_parallel_range_ex( + start, stop, userdata, userdata_chunk, userdata_chunk_size, NULL, func_ex, func_finalize, + use_threading, use_dynamic_scheduling); } #undef MALLOCA #undef MALLOCA_FREE +typedef struct ParallelListbaseState { + void *userdata; + TaskParallelListbaseFunc func; + + int chunk_size; + int index; + Link *link; + SpinLock lock; +} ParallelListState; + +BLI_INLINE Link *parallel_listbase_next_iter_get( + ParallelListState * __restrict state, + int * __restrict index, + int * __restrict count) +{ + int task_count = 0; + BLI_spin_lock(&state->lock); + Link *result = state->link; + if (LIKELY(result != NULL)) { + *index = state->index; + while (state->link != NULL && task_count < state->chunk_size) { + ++task_count; + state->link = state->link->next; + } + state->index += task_count; + } + BLI_spin_unlock(&state->lock); + *count = task_count; + return result; +} + +static void parallel_listbase_func( + TaskPool * __restrict pool, + void *UNUSED(taskdata), + int UNUSED(threadid)) +{ + ParallelListState * __restrict state = BLI_task_pool_userdata(pool); + Link *link; + int index, count; + + while ((link = parallel_listbase_next_iter_get(state, &index, &count)) != NULL) { + for (int i = 0; i < count; ++i) { + state->func(state->userdata, link, index + i); + link = link->next; + } + } +} + +/** + * This function allows to parallelize for loops over ListBase items. + * + * \param listbase The double linked list to loop over. + * \param userdata Common userdata passed to all instances of \a func. + * \param func Callback function. + * \param use_threading If \a true, actually split-execute loop in threads, else just do a sequential forloop + * (allows caller to use any kind of test to switch on parallelization or not). + * + * \note There is no static scheduling here, since it would need another full loop over items to count them... + */ +void BLI_task_parallel_listbase( + struct ListBase *listbase, + void *userdata, + TaskParallelListbaseFunc func, + const bool use_threading) +{ + TaskScheduler *task_scheduler; + TaskPool *task_pool; + ParallelListState state; + int i, num_threads, num_tasks; + + if (BLI_listbase_is_empty(listbase)) { + return; + } + + if (!use_threading) { + i = 0; + for (Link *link = listbase->first; link != NULL; link = link->next, ++i) { + func(userdata, link, i); + } + return; + } + + task_scheduler = BLI_task_scheduler_get(); + task_pool = BLI_task_pool_create(task_scheduler, &state); + num_threads = BLI_task_scheduler_num_threads(task_scheduler); + + /* The idea here is to prevent creating task for each of the loop iterations + * and instead have tasks which are evenly distributed across CPU cores and + * pull next iter to be crunched using the queue. + */ + num_tasks = num_threads * 2; + + state.index = 0; + state.link = listbase->first; + state.userdata = userdata; + state.func = func; + state.chunk_size = 32; + BLI_spin_init(&state.lock); + + for (i = 0; i < num_tasks; i++) { + /* Use this pool's pre-allocated tasks. */ + BLI_task_pool_push_from_thread(task_pool, + parallel_listbase_func, + NULL, false, + TASK_PRIORITY_HIGH, 0); + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + BLI_spin_end(&state.lock); +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index a5267175dfa..d6e2f237be9 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4926,6 +4926,9 @@ static void direct_link_pose(FileData *fd, bPose *pose) pchan->child = newdataadr(fd, pchan->child); pchan->custom_tx = newdataadr(fd, pchan->custom_tx); + pchan->bbone_prev = newdataadr(fd, pchan->bbone_prev); + pchan->bbone_next = newdataadr(fd, pchan->bbone_next); + direct_link_constraints(fd, &pchan->constraints); pchan->prop = newdataadr(fd, pchan->prop); @@ -6159,6 +6162,11 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) for (gps = gpf->strokes.first; gps; gps = gps->next) { gps->points = newdataadr(fd, gps->points); + + /* the triangulation is not saved, so need to be recalculated */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->triangles = NULL; + gps->tot_triangles = 0; } } } @@ -7447,9 +7455,13 @@ static void direct_link_mask(FileData *fd, Mask *mask) MaskSpline *spline; MaskLayerShape *masklay_shape; + /* can't use newdataadr since it's a pointer within an array */ + MaskSplinePoint *act_point_search = NULL; + link_list(fd, &masklay->splines); for (spline = masklay->splines.first; spline; spline = spline->next) { + MaskSplinePoint *points_old = spline->points; int i; spline->points = newdataadr(fd, spline->points); @@ -7460,6 +7472,14 @@ static void direct_link_mask(FileData *fd, Mask *mask) if (point->tot_uw) point->uw = newdataadr(fd, point->uw); } + + /* detect active point */ + if ((act_point_search == NULL) && + (masklay->act_point >= points_old) && + (masklay->act_point < points_old + spline->tot_point)) + { + act_point_search = &spline->points[masklay->act_point - points_old]; + } } link_list(fd, &masklay->splines_shapes); @@ -7477,7 +7497,7 @@ static void direct_link_mask(FileData *fd, Mask *mask) } masklay->act_spline = newdataadr(fd, masklay->act_spline); - masklay->act_point = newdataadr(fd, masklay->act_point); + masklay->act_point = act_point_search; } } @@ -8206,7 +8226,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ /* WATCH IT 2!: Userdef struct init see do_versions_userdef() above! */ - /* don't forget to set version number in BKE_blender.h! */ + /* don't forget to set version number in BKE_blender_version.h! */ } #if 0 // XXX: disabled for now... we still don't have this in the right place in the loading code for it to work diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index b9191d545ed..0ea4078a5cb 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -34,6 +34,7 @@ /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW +#include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" @@ -137,6 +138,40 @@ static void do_version_constraints_stretch_to_limits(ListBase *lb) } } +static void do_version_action_editor_properties_region(ListBase *regionbase) +{ + ARegion *ar; + + for (ar = regionbase->first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_UI) { + /* already exists */ + return; + } + else if (ar->regiontype == RGN_TYPE_WINDOW) { + /* add new region here */ + ARegion *arnew = MEM_callocN(sizeof(ARegion), "buttons for action"); + + BLI_insertlinkbefore(regionbase, ar, arnew); + + arnew->regiontype = RGN_TYPE_UI; + arnew->alignment = RGN_ALIGN_RIGHT; + arnew->flag = RGN_FLAG_HIDDEN; + + return; + } + } +} + +static void do_version_bones_super_bbone(ListBase *lb) +{ + for (Bone *bone = lb->first; bone; bone = bone->next) { + bone->scaleIn = 1.0f; + bone->scaleOut = 1.0f; + + do_version_bones_super_bbone(&bone->childbase); + } +} + void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) { if (!MAIN_VERSION_ATLEAST(main, 270, 0)) { @@ -1040,6 +1075,15 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } + /* init grease pencil smooth level iterations */ + for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->draw_smoothlvl == 0) { + gpl->draw_smoothlvl = 1; + } + } + } + for (bScreen *screen = main->screen.first; screen; screen = screen->id.next) { for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { @@ -1072,5 +1116,89 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } } + + for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { + CurvePaintSettings *cps = &scene->toolsettings->curve_paint_settings; + if (cps->error_threshold == 0) { + cps->curve_type = CU_BEZIER; + cps->flag |= CURVE_PAINT_FLAG_CORNERS_DETECT; + cps->error_threshold = 8; + cps->radius_max = 1.0f; + cps->corner_angle = DEG2RADF(70.0f); + } + } + + for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { + Sequence *seq; + + SEQ_BEGIN (scene->ed, seq) + { + if (seq->type == SEQ_TYPE_TEXT) { + TextVars *data = seq->effectdata; + if (data->color[3] == 0.0f) { + copy_v4_fl(data->color, 1.0f); + data->shadow_color[3] = 1.0f; + } + } + } + SEQ_END + } + + /* Adding "Properties" region to DopeSheet */ + for (bScreen *screen = main->screen.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + /* handle pushed-back space data first */ + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_ACTION) { + SpaceAction *saction = (SpaceAction *)sl; + do_version_action_editor_properties_region(&saction->regionbase); + } + } + + /* active spacedata info must be handled too... */ + if (sa->spacetype == SPACE_ACTION) { + do_version_action_editor_properties_region(&sa->regionbase); + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(main, 277, 2)) { + if (!DNA_struct_elem_find(fd->filesdna, "Bone", "float", "scaleIn")) { + for (bArmature *arm = main->armature.first; arm; arm = arm->id.next) { + do_version_bones_super_bbone(&arm->bonebase); + } + } + if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "scaleIn")) { + for (Object *ob = main->object.first; ob; ob = ob->id.next) { + if (ob->pose) { + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + /* see do_version_bones_super_bbone()... */ + pchan->scaleIn = 1.0f; + pchan->scaleOut = 1.0f; + + /* also make sure some legacy (unused for over a decade) flags are unset, + * so that we can reuse them for stuff that matters now... + * (i.e. POSE_IK_MAT, (unknown/unused x 4), POSE_HAS_IK) + * + * These seem to have been runtime flags used by the IK solver, but that stuff + * should be able to be recalculated automatically anyway, so it should be fine. + */ + pchan->flag &= ~((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8)); + } + } + } + } + } + + { + for (Camera *camera = main->camera.first; camera != NULL; camera = camera->id.next) { + if (camera->stereo.pole_merge_angle_from == 0.0f && + camera->stereo.pole_merge_angle_to == 0.0f) + { + camera->stereo.pole_merge_angle_from = DEG2RAD(60.0f); + camera->stereo.pole_merge_angle_to = DEG2RAD(75.0f); + } + } } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 4871dc2f161..124b1efddac 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -145,7 +145,7 @@ #include "BLI_mempool.h" #include "BKE_action.h" -#include "BKE_blender.h" +#include "BKE_blender_version.h" #include "BKE_bpath.h" #include "BKE_curve.h" #include "BKE_constraint.h" diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index efd59c3fa94..3838e8c827c 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -54,6 +54,7 @@ const char *BLT_translate_do_iface(const char *msgctxt, const char *msgid); const char *BLT_translate_do_tooltip(const char *msgctxt, const char *msgid); const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid); +bool BLT_lang_is_ime_supported(void); /* The "translation-marker" macro. */ #define N_(msgid) msgid diff --git a/source/blender/blentranslation/CMakeLists.txt b/source/blender/blentranslation/CMakeLists.txt index 59bd7f0adfa..a3e85344027 100644 --- a/source/blender/blentranslation/CMakeLists.txt +++ b/source/blender/blentranslation/CMakeLists.txt @@ -53,4 +53,10 @@ if(WITH_PYTHON) ) endif() +if(WIN32) + if(WITH_INPUT_IME) + add_definitions(-DWITH_INPUT_IME) + endif() +endif() + blender_add_lib(bf_blentranslation "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c index 1ad62fa5869..74c2b4b7472 100644 --- a/source/blender/blentranslation/intern/blt_lang.c +++ b/source/blender/blentranslation/intern/blt_lang.c @@ -54,6 +54,10 @@ #include "MEM_guardedalloc.h" +/* Cached IME support flags */ +static bool ime_is_lang_supported = false; +static void blt_lang_check_ime_supported(void); + #ifdef WITH_INTERNATIONAL #include "boost_locale_wrapper.h" @@ -280,6 +284,7 @@ void BLT_lang_set(const char *str) #else (void)str; #endif + blt_lang_check_ime_supported(); } /* Get the current locale (short code, e.g. es_ES). */ @@ -355,3 +360,32 @@ void BLT_lang_locale_explode( MEM_freeN(_t); } } + +/* Test if the translation context allows IME input - used to + * avoid weird character drawing if IME inputs non-ascii chars. + */ +static void blt_lang_check_ime_supported(void) +{ +#ifdef WITH_INPUT_IME + const char *uilng = BLT_lang_get(); + if (U.transopts & USER_DOTRANSLATE) { + ime_is_lang_supported = STREQ(uilng, "zh_CN") || + STREQ(uilng, "zh_TW") || + STREQ(uilng, "ja_JP"); + } + else { + ime_is_lang_supported = false; + } +#else + ime_is_lang_supported = false; +#endif +} + +bool BLT_lang_is_ime_supported(void) +{ +#ifdef WITH_INPUT_IME + return ime_is_lang_supported; +#else + return false; +#endif +} diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index f7e709ce9cc..1d68cdcf28a 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -1246,7 +1246,6 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del) BLI_array_staticdeclare(deledges, BM_DEFAULT_NGON_STACK_SIZE); BLI_array_staticdeclare(delverts, BM_DEFAULT_NGON_STACK_SIZE); BMVert *v1 = NULL, *v2 = NULL; - const char *err = NULL; int i, tote = 0; const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); @@ -1267,7 +1266,7 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del) int rlen = bm_loop_systag_count_radial(l_iter, _FLAG_JF); if (rlen > 2) { - err = N_("Input faces do not form a contiguous manifold region"); + /* Input faces do not form a contiguous manifold region */ goto error; } else if (rlen == 1) { @@ -1288,7 +1287,7 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del) if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) { /* don't remove an edge it makes up the side of another face * else this will remove the face as well - campbell */ - if (!BM_edge_face_count_is_over(l_iter->e, 3)) { + if (!BM_edge_face_count_is_over(l_iter->e, 2)) { if (do_del) { BLI_array_append(deledges, l_iter->e); } @@ -1328,9 +1327,8 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del) /* create region face */ f_new = tote ? BM_face_create_ngon(bm, v1, v2, edges, tote, faces[0], BM_CREATE_NOP) : NULL; - if (UNLIKELY(!f_new || BMO_error_occurred(bm))) { - if (!BMO_error_occurred(bm)) - err = N_("Invalid boundary region to join faces"); + if (UNLIKELY(f_new == NULL)) { + /* Invalid boundary region to join faces */ goto error; } @@ -1426,9 +1424,6 @@ error: BLI_array_free(deledges); BLI_array_free(delverts); - if (err) { - BMO_error_raise(bm, bm->currentop, BMERR_DISSOLVEFACES_FAILED, err); - } return NULL; } diff --git a/source/blender/bmesh/intern/bmesh_delete.c b/source/blender/bmesh/intern/bmesh_delete.c index c5c9403884b..882d78ce6b3 100644 --- a/source/blender/bmesh/intern/bmesh_delete.c +++ b/source/blender/bmesh/intern/bmesh_delete.c @@ -161,6 +161,7 @@ void BMO_mesh_delete_oflag_context(BMesh *bm, const short oflag, const int type) break; } case DEL_FACES: + case DEL_FACES_KEEP_BOUNDARY: { /* go through and mark all edges and all verts of all faces for delete */ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { @@ -190,11 +191,20 @@ void BMO_mesh_delete_oflag_context(BMesh *bm, const short oflag, const int type) } /* also mark all the vertices of remaining edges for keeping */ BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { + + /* Only exception to normal 'DEL_FACES' logic. */ + if (type == DEL_FACES_KEEP_BOUNDARY) { + if (BM_edge_is_boundary(e)) { + BMO_elem_flag_disable(bm, e, oflag); + } + } + if (!BMO_elem_flag_test(bm, e, oflag)) { BMO_elem_flag_disable(bm, e->v1, oflag); BMO_elem_flag_disable(bm, e->v2, oflag); } } + /* now delete marked face */ bmo_remove_tagged_faces(bm, oflag); /* delete marked edge */ diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c index 4014d29966a..cc79e28a361 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.c +++ b/source/blender/bmesh/intern/bmesh_iterators.c @@ -31,6 +31,7 @@ #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" +#include "BLI_bitmap.h" #include "bmesh.h" #include "intern/bmesh_private.h" @@ -250,6 +251,64 @@ void *BMO_iter_as_arrayN( } } +int BM_iter_mesh_bitmap_from_filter( + const char itype, BMesh *bm, + BLI_bitmap *bitmap, + bool (*test_fn)(BMElem *, void *user_data), + void *user_data) +{ + BMIter iter; + BMElem *ele; + int i; + int bitmap_enabled = 0; + + BM_ITER_MESH_INDEX (ele, &iter, bm, itype, i) { + if (test_fn(ele, user_data)) { + BLI_BITMAP_ENABLE(bitmap, i); + bitmap_enabled++; + } + else { + BLI_BITMAP_DISABLE(bitmap, i); + } + } + + return bitmap_enabled; +} + +/** + * Needed when we want to check faces, but return a loop aligned array. + */ +int BM_iter_mesh_bitmap_from_filter_tessface( + BMesh *bm, + BLI_bitmap *bitmap, + bool (*test_fn)(BMFace *, void *user_data), + void *user_data) +{ + BMIter iter; + BMFace *f; + int i; + int j = 0; + int bitmap_enabled = 0; + + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + if (test_fn(f, user_data)) { + for (int tri = 2; tri < f->len; tri++) { + BLI_BITMAP_ENABLE(bitmap, j); + bitmap_enabled++; + j++; + } + } + else { + for (int tri = 2; tri < f->len; tri++) { + BLI_BITMAP_DISABLE(bitmap, j); + j++; + } + } + } + + return bitmap_enabled; +} + /** * \brief Elem Iter Flag Count * diff --git a/source/blender/bmesh/intern/bmesh_iterators.h b/source/blender/bmesh/intern/bmesh_iterators.h index 336e9d88dea..0551d824131 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.h +++ b/source/blender/bmesh/intern/bmesh_iterators.h @@ -208,6 +208,18 @@ void *BMO_iter_as_arrayN( int *r_len, /* optional args to avoid an alloc (normally stack array) */ void **stack_array, int stack_array_size); + +int BM_iter_mesh_bitmap_from_filter( + const char itype, BMesh *bm, + unsigned int *bitmap, + bool (*test_fn)(BMElem *, void *user_data), + void *user_data); +int BM_iter_mesh_bitmap_from_filter_tessface( + BMesh *bm, + unsigned int *bitmap, + bool (*test_fn)(BMFace *, void *user_data), + void *user_data); + int BM_iter_elem_count_flag(const char itype, void *data, const char hflag, const bool value); int BMO_iter_elem_count_flag(BMesh *bm, const char itype, void *data, const short oflag, const bool value); int BM_iter_mesh_count(const char itype, BMesh *bm); diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c index 931413d834f..7b102c9283b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -224,7 +224,7 @@ static BMFace *bm_face_create_from_mpoly( */ void BM_mesh_bm_from_me( BMesh *bm, Mesh *me, - const bool calc_face_normal, const bool set_key, int act_key_nr) + const struct BMeshFromMeshParams *params) { MVert *mvert; MEdge *medge; @@ -237,11 +237,6 @@ void BM_mesh_bm_from_me( float (*keyco)[3] = NULL; int totuv, totloops, i, j; - int cd_vert_bweight_offset; - int cd_edge_bweight_offset; - int cd_edge_crease_offset; - int cd_shape_keyindex_offset; - /* free custom data */ /* this isnt needed in most cases but do just incase */ CustomData_free(&bm->vdata, bm->totvert); @@ -278,16 +273,21 @@ void BM_mesh_bm_from_me( CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name); } - if ((act_key_nr != 0) && (me->key != NULL)) { - actkey = BLI_findlink(&me->key->block, act_key_nr - 1); + if ((params->active_shapekey != 0) && (me->key != NULL)) { + actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1); } else { actkey = NULL; } - if (me->key) { + const int tot_shape_keys = me->key ? BLI_listbase_count(&me->key->block) : 0; + const float (**shape_key_table)[3] = tot_shape_keys ? BLI_array_alloca(shape_key_table, tot_shape_keys) : NULL; + + if (tot_shape_keys || params->add_key_index) { CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + } + if (tot_shape_keys) { /* check if we need to generate unique ids for the shapekeys. * this also exists in the file reading code, but is here for * a sanity check */ @@ -305,7 +305,7 @@ void BM_mesh_bm_from_me( if (actkey && actkey->totelem == me->totvert) { keyco = actkey->data; - bm->shapenr = act_key_nr; + bm->shapenr = params->active_shapekey; } for (i = 0, block = me->key->block.first; block; block = block->next, i++) { @@ -314,6 +314,8 @@ void BM_mesh_bm_from_me( j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); bm->vdata.layers[j].uid = block->uid; + + shape_key_table[i] = (const float (*)[3])block->data; } } @@ -324,13 +326,17 @@ void BM_mesh_bm_from_me( BM_mesh_cd_flag_apply(bm, me->cd_flag); - cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); - cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); - cd_shape_keyindex_offset = me->key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; + const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); + const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); + const int cd_shape_key_offset = me->key ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : -1; + const int cd_shape_keyindex_offset = (tot_shape_keys || params->add_key_index) ? + CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { - v = vtable[i] = BM_vert_create(bm, keyco && set_key ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD); + v = vtable[i] = BM_vert_create( + bm, keyco && params->use_shapekey ? keyco[i] : mvert->co, NULL, + BM_CREATE_SKIP_CD); BM_elem_index_set(v, i); /* set_ok */ /* transfer flag */ @@ -348,17 +354,14 @@ void BM_mesh_bm_from_me( if (cd_vert_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f); - /* set shapekey data */ - if (me->key) { - /* set shape key original index */ - if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i); - - for (block = me->key->block.first, j = 0; block; block = block->next, j++) { - float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j); + /* set shape key original index */ + if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i); - if (co) { - copy_v3_v3(co, ((float *)block->data) + 3 * i); - } + /* set shapekey data */ + if (tot_shape_keys) { + float (*co_dst)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); + for (j = 0; j < tot_shape_keys; j++, co_dst++) { + copy_v3_v3(*co_dst, shape_key_table[j][i]); } } } @@ -438,7 +441,7 @@ void BM_mesh_bm_from_me( /* Copy Custom Data */ CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data, true); - if (calc_face_normal) { + if (params->calc_face_normal) { BM_face_normal_update(f); } } @@ -511,7 +514,12 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) if (cd_shape_keyindex_offset != -1) { BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); - if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) { + if ((keyi != ORIGINDEX_NONE) && + (keyi < ototvert) && + /* not fool-proof, but chances are if we have many verts with the same index, + * we will want to use the first one, since the second is more likely to be a duplicate. */ + (vertMap[keyi] == NULL)) + { vertMap[keyi] = eve; } } @@ -568,7 +576,9 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) } } -void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) +void BM_mesh_bm_to_me( + BMesh *bm, Mesh *me, + const struct BMeshToMeshParams *params) { MLoop *mloop; MPoly *mpoly; @@ -629,10 +639,13 @@ void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) me->totface = 0; me->act_face = -1; - CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert); - CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge); - CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop); - CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly); + { + const CustomDataMask mask = CD_MASK_MESH | params->cd_mask_extra; + CustomData_copy(&bm->vdata, &me->vdata, mask, CD_CALLOC, me->totvert); + CustomData_copy(&bm->edata, &me->edata, mask, CD_CALLOC, me->totedge); + CustomData_copy(&bm->ldata, &me->ldata, mask, CD_CALLOC, me->totloop); + CustomData_copy(&bm->pdata, &me->pdata, mask, CD_CALLOC, me->totpoly); + } CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); @@ -781,11 +794,11 @@ void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface) if (vertMap) MEM_freeN(vertMap); } - if (do_tessface) { + if (params->calc_tessface) { BKE_mesh_tessface_calc(me); } - BKE_mesh_update_customdata_pointers(me, do_tessface); + BKE_mesh_update_customdata_pointers(me, params->calc_tessface); { BMEditSelection *selected; diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.h b/source/blender/bmesh/intern/bmesh_mesh_conv.h index ce286f6c662..7cbfe2d9210 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.h +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.h @@ -39,9 +39,28 @@ void BM_mesh_cd_flag_ensure(BMesh *bm, struct Mesh *mesh, const char cd_flag); void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag); char BM_mesh_cd_flag_from_bmesh(BMesh *bm); + +struct BMeshFromMeshParams { + unsigned int calc_face_normal : 1; + /* add a vertex CD_SHAPE_KEYINDEX layer */ + unsigned int add_key_index : 1; + /* set vertex coordinates from the shapekey */ + unsigned int use_shapekey : 1; + /* define the active shape key (index + 1) */ + int active_shapekey; +}; void BM_mesh_bm_from_me( BMesh *bm, struct Mesh *me, - const bool calc_face_normal, const bool set_key, int act_key_nr); -void BM_mesh_bm_to_me(BMesh *bm, struct Mesh *me, bool do_tessface); + const struct BMeshFromMeshParams *params) +ATTR_NONNULL(1, 3); + +struct BMeshToMeshParams { + unsigned int calc_tessface : 1; + int64_t cd_mask_extra; +}; +void BM_mesh_bm_to_me( + BMesh *bm, struct Mesh *me, + const struct BMeshToMeshParams *params) +ATTR_NONNULL(1, 2, 3); #endif /* __BMESH_MESH_CONV_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h index 14e9bf81be7..cf93fe0935e 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api.h +++ b/source/blender/bmesh/intern/bmesh_operator_api.h @@ -285,6 +285,9 @@ enum { DEL_ONLYFACES, DEL_EDGESFACES, DEL_FACES, + /* A version of 'DEL_FACES' that keeps edges on face boundaries, + * allowing the surrounding edge-loop to be kept from removed face regions. */ + DEL_FACES_KEEP_BOUNDARY, DEL_ONLYTAGGED }; diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index ffad3e59e47..9b074dc4db4 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -2186,14 +2186,13 @@ bool BM_face_exists_overlap_subset(BMVert **varr, const int len) { BMIter viter; BMFace *f; - int i; bool is_init = false; bool is_overlap = false; LinkNode *f_lnk = NULL; #ifdef DEBUG /* check flag isn't already set */ - for (i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { BLI_assert(BM_ELEM_API_FLAG_TEST(varr[i], _FLAG_OVERLAP) == 0); BM_ITER_ELEM (f, &viter, varr[i], BM_FACES_OF_VERT) { BLI_assert(BM_ELEM_API_FLAG_TEST(f, _FLAG_OVERLAP) == 0); @@ -2201,7 +2200,7 @@ bool BM_face_exists_overlap_subset(BMVert **varr, const int len) } #endif - for (i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { BM_ITER_ELEM (f, &viter, varr[i], BM_FACES_OF_VERT) { if ((f->len <= len) && (BM_ELEM_API_FLAG_TEST(f, _FLAG_OVERLAP) == 0)) { /* check if all vers in this face are flagged*/ @@ -2209,8 +2208,8 @@ bool BM_face_exists_overlap_subset(BMVert **varr, const int len) if (is_init == false) { is_init = true; - for (i = 0; i < len; i++) { - BM_ELEM_API_FLAG_ENABLE(varr[i], _FLAG_OVERLAP); + for (int j = 0; j < len; j++) { + BM_ELEM_API_FLAG_ENABLE(varr[j], _FLAG_OVERLAP); } } @@ -2234,7 +2233,7 @@ bool BM_face_exists_overlap_subset(BMVert **varr, const int len) } if (is_init == true) { - for (i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { BM_ELEM_API_FLAG_DISABLE(varr[i], _FLAG_OVERLAP); } } diff --git a/source/blender/bmesh/intern/bmesh_queries_inline.h b/source/blender/bmesh/intern/bmesh_queries_inline.h index 430ba10fb42..09cf39b526d 100644 --- a/source/blender/bmesh/intern/bmesh_queries_inline.h +++ b/source/blender/bmesh/intern/bmesh_queries_inline.h @@ -146,6 +146,13 @@ BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) return (ELEM(l_b, l_a->next, l_a->prev)); } +ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) +BLI_INLINE bool BM_loop_is_manifold(const BMLoop *l) +{ + return ((l != l->radial_next) && + (l == l->radial_next->radial_next)); +} + /** * Check if we have a single wire edge user. */ diff --git a/source/blender/bmesh/intern/bmesh_walkers.h b/source/blender/bmesh/intern/bmesh_walkers.h index f5a801a31c3..b2373a069db 100644 --- a/source/blender/bmesh/intern/bmesh_walkers.h +++ b/source/blender/bmesh/intern/bmesh_walkers.h @@ -125,6 +125,7 @@ enum { BMW_LOOPDATA_ISLAND, BMW_ISLANDBOUND, BMW_ISLAND, + BMW_ISLAND_MANIFOLD, BMW_CONNECTED_VERTEX, /* end of array index enum vals */ diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c index 627f8358410..018700c0efa 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_impl.c +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -711,13 +711,6 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker) e = l->e; v = BM_edge_other_vert(e, iwalk->lastv); - - if (!BM_vert_is_manifold(v)) { - BMW_reset(walker); - BMO_error_raise(walker->bm, NULL, BMERR_WALKER_FAILED, - "Non-manifold vert while searching region boundary"); - return NULL; - } /* pop off current state */ BMW_state_remove(walker); @@ -726,7 +719,7 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker) while (1) { l = BM_loop_other_edge_loop(l, v); - if (l != l->radial_next) { + if (BM_loop_is_manifold(l)) { l = l->radial_next; f = l->f; e = l->e; @@ -737,6 +730,7 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker) } } else { + /* treat non-manifold edges as boundaries */ f = l->f; e = l->e; break; @@ -791,25 +785,44 @@ static void *bmw_IslandWalker_yield(BMWalker *walker) return iwalk->cur; } -static void *bmw_IslandWalker_step(BMWalker *walker) +static void *bmw_IslandWalker_step_ex(BMWalker *walker, bool only_manifold) { BMwIslandWalker *iwalk, owalk; - BMIter iter, liter; - BMFace *f; - BMLoop *l; + BMLoop *l_iter, *l_first; BMW_state_remove_r(walker, &owalk); iwalk = &owalk; - l = BM_iter_new(&liter, walker->bm, BM_LOOPS_OF_FACE, iwalk->cur); - for ( ; l; l = BM_iter_step(&liter)) { + l_iter = l_first = BM_FACE_FIRST_LOOP(iwalk->cur); + do { /* could skip loop here too, but don't add unless we need it */ - if (!bmw_mask_check_edge(walker, l->e)) { + if (!bmw_mask_check_edge(walker, l_iter->e)) { continue; } - f = BM_iter_new(&iter, walker->bm, BM_FACES_OF_EDGE, l->e); - for ( ; f; f = BM_iter_step(&iter)) { + BMLoop *l_radial_iter; + + if (only_manifold && (l_iter->radial_next != l_iter)) { + int face_count = 1; + /* check other faces (not this one), ensure only one other can be walked onto. */ + l_radial_iter = l_iter->radial_next; + do { + if (bmw_mask_check_face(walker, l_radial_iter->f)) { + face_count++; + if (face_count == 3) { + break; + } + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter); + + if (face_count != 2) { + continue; + } + } + + l_radial_iter = l_iter; + while ((l_radial_iter = l_radial_iter->radial_next) != l_iter) { + BMFace *f = l_radial_iter->f; if (!bmw_mask_check_face(walker, f)) { continue; @@ -823,17 +836,30 @@ static void *bmw_IslandWalker_step(BMWalker *walker) if (BLI_gset_haskey(walker->visit_set, f)) { continue; } - + iwalk = BMW_state_add(walker); iwalk->cur = f; BLI_gset_insert(walker->visit_set, f); break; } - } - + } while ((l_iter = l_iter->next) != l_first); + return owalk.cur; } +static void *bmw_IslandWalker_step(BMWalker *walker) +{ + return bmw_IslandWalker_step_ex(walker, false); +} + +/** + * Ignore edges that don't have 2x usable faces. + */ +static void *bmw_IslandManifoldWalker_step(BMWalker *walker) +{ + return bmw_IslandWalker_step_ex(walker, true); +} + /** \} */ @@ -1608,6 +1634,16 @@ static BMWalker bmw_IslandWalker_Type = { BM_EDGE | BM_FACE, /* valid restrict masks */ }; +static BMWalker bmw_IslandManifoldWalker_Type = { + BM_FACE, + bmw_IslandWalker_begin, + bmw_IslandManifoldWalker_step, /* only difference with BMW_ISLAND */ + bmw_IslandWalker_yield, + sizeof(BMwIslandWalker), + BMW_BREADTH_FIRST, + BM_EDGE | BM_FACE, /* valid restrict masks */ +}; + static BMWalker bmw_EdgeLoopWalker_Type = { BM_EDGE, bmw_EdgeLoopWalker_begin, @@ -1680,6 +1716,7 @@ BMWalker *bm_walker_types[] = { &bmw_UVEdgeWalker_Type, /* BMW_LOOPDATA_ISLAND */ &bmw_IslandboundWalker_Type, /* BMW_ISLANDBOUND */ &bmw_IslandWalker_Type, /* BMW_ISLAND */ + &bmw_IslandManifoldWalker_Type, /* BMW_ISLAND_MANIFOLD */ &bmw_ConnectedVertexWalker_Type, /* BMW_CONNECTED_VERTEX */ }; diff --git a/source/blender/bmesh/operators/bmo_bridge.c b/source/blender/bmesh/operators/bmo_bridge.c index c7bf481d7d5..a0149a41921 100644 --- a/source/blender/bmesh/operators/bmo_bridge.c +++ b/source/blender/bmesh/operators/bmo_bridge.c @@ -262,7 +262,7 @@ static void bridge_loop_pair( if (bm->totface) { struct BMEdgeLoopStore *estore_pair[2] = {el_store_a, el_store_b}; int i; - int winding_votes = 0; + int winding_votes[2] = {0, 0}; int winding_dir = 1; for (i = 0; i < 2; i++, winding_dir = -winding_dir) { LinkData *el; @@ -271,15 +271,49 @@ static void bridge_loop_pair( if (el_next) { BMEdge *e = BM_edge_exists(el->data, el_next->data); if (e && BM_edge_is_boundary(e)) { - winding_votes += ((e->l->v == el->data) ? winding_dir : -winding_dir); + winding_votes[i] += ((e->l->v == el->data) ? winding_dir : -winding_dir); } } } } - if (winding_votes < 0) { - BM_edgeloop_flip(bm, el_store_a); - BM_edgeloop_flip(bm, el_store_b); + if (winding_votes[0] || winding_votes[1]) { + bool flip[2] = {false, false}; + + /* for direction aligned loops we can't rely on the directly we have, + * use the winding defined by the connected faces (see T48356). */ + if (fabsf(dot_a) < eps) { + if (winding_votes[0] < 0) { + flip[0] = !flip[0]; + winding_votes[0] *= -1; + + } + } + if (fabsf(dot_b) < eps) { + if (winding_votes[1] < 0) { + flip[1] = !flip[1]; + winding_votes[1] *= -1; + } + } + + /* when both loops contradict the winding, flip them so surrounding geometry matches */ + if ((winding_votes[0] + winding_votes[1]) < 0) { + flip[0] = !flip[0]; + flip[1] = !flip[1]; + + /* valid but unused */ +#if 0 + winding_votes[0] *= -1; + winding_votes[1] *= -1; +#endif + } + + if (flip[0]) { + BM_edgeloop_flip(bm, el_store_a); + } + if (flip[1]) { + BM_edgeloop_flip(bm, el_store_b); + } } } } diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c index c8dff4a8598..86062ff0b66 100644 --- a/source/blender/bmesh/operators/bmo_dissolve.c +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -43,6 +43,7 @@ #define FACE_MARK 1 #define FACE_ORIG 2 #define FACE_NEW 4 +#define FACE_TAG 8 #define EDGE_MARK 1 #define EDGE_TAG 2 @@ -156,19 +157,19 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op) } } - BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_MARK); + BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_MARK | FACE_TAG); /* collect region */ BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) { BMFace *f_iter; - if (!BMO_elem_flag_test(bm, f, FACE_MARK)) { + if (!BMO_elem_flag_test(bm, f, FACE_TAG)) { continue; } BLI_array_empty(faces); faces = NULL; /* forces different allocatio */ - BMW_init(®walker, bm, BMW_ISLAND, + BMW_init(®walker, bm, BMW_ISLAND_MANIFOLD, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK, BMW_FLAG_NOP, /* no need to check BMW_FLAG_TEST_HIDDEN, faces are already marked by the bmo */ BMW_NIL_LAY); @@ -180,7 +181,7 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op) for (i = 0; i < BLI_array_count(faces); i++) { f_iter = faces[i]; - BMO_elem_flag_disable(bm, f_iter, FACE_MARK); + BMO_elem_flag_disable(bm, f_iter, FACE_TAG); BMO_elem_flag_enable(bm, f_iter, FACE_ORIG); } @@ -193,7 +194,10 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op) BLI_array_append(faces, NULL); BLI_array_append(regions, faces); } - + + /* track how many faces we should end up with */ + int totface_target = bm->totface; + for (i = 0; i < BLI_array_count(regions); i++) { BMFace *f_new; int tot = 0; @@ -215,6 +219,7 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op) if (act_face && bm->act_face == NULL) { bm->act_face = f_new; } + totface_target -= tot - 1; } else { BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, @@ -226,11 +231,12 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op) * unmark the original faces for deletion */ BMO_elem_flag_disable(bm, f_new, FACE_ORIG); BMO_elem_flag_enable(bm, f_new, FACE_NEW); - } - BMO_op_callf(bm, op->flag, "delete geom=%ff context=%i", FACE_ORIG, DEL_FACES); - + /* Typically no faces need to be deleted */ + if (totface_target != bm->totface) { + BMO_op_callf(bm, op->flag, "delete geom=%ff context=%i", FACE_ORIG, DEL_FACES); + } if (use_verts) { BMIter viter; diff --git a/source/blender/bmesh/operators/bmo_mesh_conv.c b/source/blender/bmesh/operators/bmo_mesh_conv.c index d124aaaf80e..0eb9bf90ca8 100644 --- a/source/blender/bmesh/operators/bmo_mesh_conv.c +++ b/source/blender/bmesh/operators/bmo_mesh_conv.c @@ -45,7 +45,10 @@ void bmo_mesh_to_bmesh_exec(BMesh *bm, BMOperator *op) Mesh *me = BMO_slot_ptr_get(op->slots_in, "mesh"); bool set_key = BMO_slot_bool_get(op->slots_in, "use_shapekey"); - BM_mesh_bm_from_me(bm, me, false, set_key, ob->shapenr); + BM_mesh_bm_from_me( + bm, me, (&(struct BMeshFromMeshParams){ + .use_shapekey = set_key, .active_shapekey = ob->shapenr, + })); if (me->key && ob->shapenr > me->key->totkey) { ob->shapenr = me->key->totkey - 1; @@ -69,5 +72,5 @@ void bmo_bmesh_to_mesh_exec(BMesh *bm, BMOperator *op) /* Object *ob = BMO_slot_ptr_get(op, "object"); */ const bool dotess = !BMO_slot_bool_get(op->slots_in, "skip_tessface"); - BM_mesh_bm_to_me(bm, me, dotess); + BM_mesh_bm_to_me(bm, me, (&(struct BMeshToMeshParams){ .calc_tessface = dotess, })); } diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index fcf02c4bc3f..b120b48447f 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -376,8 +376,8 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) phid /= 2; for (a = 0; a <= tot; a++) { /* Going in this direction, then edge extruding, makes normals face outward */ - vec[0] = -dia * sinf(phi); - vec[1] = 0.0; + vec[0] = 0.0; + vec[1] = dia * sinf(phi); vec[2] = dia * cosf(phi); eve = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); BMO_elem_flag_enable(bm, eve, VERT_MARK); @@ -1042,13 +1042,14 @@ void bmo_create_cube_exec(BMesh *bm, BMOperator *op) float off = BMO_slot_float_get(op->slots_in, "size") / 2.0f; const bool calc_uvs = BMO_slot_bool_get(op->slots_in, "calc_uvs"); int i, x, y, z; + /* rotation order set to match 'BM_mesh_calc_uvs_cube' */ const char faces[6][4] = { - {1, 3, 2, 0}, - {3, 7, 6, 2}, - {7, 5, 4, 6}, - {5, 1, 0, 4}, - {0, 2, 6, 4}, - {5, 7, 3, 1}, + {0, 1, 3, 2}, + {2, 3, 7, 6}, + {6, 7, 5, 4}, + {4, 5, 1, 0}, + {2, 6, 4, 0}, + {7, 3, 1, 5}, }; BMO_slot_mat4_get(op->slots_in, "matrix", mat); @@ -1093,7 +1094,8 @@ void bmo_create_cube_exec(BMesh *bm, BMOperator *op) /** * Fills first available UVmap with cube-like UVs for all faces OpFlag-ged by given flag. * - * \note Expects tagged faces to be six quads... + * \note Expects tagged faces to be six quads. + * \note Caller must order faces for correct alignment. * * \param bm The BMesh to operate on. * \param oflag The flag to check faces with. diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c index a1460cec7d1..ad35d57f35b 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c +++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c @@ -37,6 +37,9 @@ #include "bmesh.h" #include "bmesh_decimate.h" /* own include */ +/* check that collapsing a vertex between 2 edges doesn't cause a degenerate face. */ +#define USE_DEGENERATE_CHECK + #define COST_INVALID FLT_MAX @@ -88,9 +91,6 @@ static float bm_edge_calc_dissolve_error( const BMEdge *e, const BMO_Delimit delimit, const struct DelimitData *delimit_data) { - const bool is_contig = BM_edge_is_contiguous(e); - float angle; - if (!BM_edge_is_manifold(e)) { goto fail; } @@ -113,6 +113,8 @@ static float bm_edge_calc_dissolve_error( goto fail; } + const bool is_contig = BM_edge_is_contiguous(e); + if ((delimit & BMO_DELIM_NORMAL) && (is_contig == false)) { @@ -125,18 +127,136 @@ static float bm_edge_calc_dissolve_error( goto fail; } - angle = BM_edge_calc_face_angle(e); - if (is_contig == false) { - angle = (float)M_PI - angle; + float angle_cos_neg = dot_v3v3(e->l->f->no, e->l->radial_next->f->no); + if (is_contig) { + angle_cos_neg *= -1; } - return angle; + return angle_cos_neg; fail: return COST_INVALID; } +#ifdef USE_DEGENERATE_CHECK + +static void mul_v2_m3v3_center(float r[2], float m[3][3], const float a[3], const float center[3]) +{ + BLI_assert(r != a); + BLI_assert(r != center); + + float co[3]; + sub_v3_v3v3(co, a, center); + + r[0] = m[0][0] * co[0] + m[1][0] * co[1] + m[2][0] * co[2]; + r[1] = m[0][1] * co[0] + m[1][1] * co[1] + m[2][1] * co[2]; +} + +static bool bm_loop_collapse_is_degenerate(BMLoop *l_ear) +{ + /* calculate relative to the centeral vertex for higher precision */ + const float *center = l_ear->v->co; + + float tri_2d[3][2]; + float axis_mat[3][3]; + + axis_dominant_v3_to_m3(axis_mat, l_ear->f->no); + + { + mul_v2_m3v3_center(tri_2d[0], axis_mat, l_ear->prev->v->co, center); +#if 0 + mul_v2_m3v3_center(tri_2d[1], axis_mat, l_ear->v->co, center); +#else + zero_v2(tri_2d[1]); +#endif + mul_v2_m3v3_center(tri_2d[2], axis_mat, l_ear->next->v->co, center); + } + + /* check we're not flipping face corners before or after the ear */ + { + float adjacent_2d[2]; + + if (!BM_vert_is_edge_pair(l_ear->prev->v)) { + mul_v2_m3v3_center(adjacent_2d, axis_mat, l_ear->prev->prev->v->co, center); + if (signum_i(cross_tri_v2(adjacent_2d, tri_2d[0], tri_2d[1])) != + signum_i(cross_tri_v2(adjacent_2d, tri_2d[0], tri_2d[2]))) + { + return true; + } + } + + if (!BM_vert_is_edge_pair(l_ear->next->v)) { + mul_v2_m3v3_center(adjacent_2d, axis_mat, l_ear->next->next->v->co, center); + if (signum_i(cross_tri_v2(adjacent_2d, tri_2d[2], tri_2d[1])) != + signum_i(cross_tri_v2(adjacent_2d, tri_2d[2], tri_2d[0]))) + { + return true; + } + } + } + + /* check no existing verts are inside the triangle */ + { + /* triangle may be concave, if so - flip so we can use clockwise check */ + if (cross_tri_v2(UNPACK3(tri_2d)) < 0.0f) { + swap_v2_v2(tri_2d[1], tri_2d[2]); + } + + /* skip l_ear and adjacent verts */ + BMLoop *l_iter, *l_first; + + l_iter = l_ear->next->next; + l_first = l_ear->prev; + do { + float co_2d[2]; + mul_v2_m3v3_center(co_2d, axis_mat, l_iter->v->co, center); + if (isect_point_tri_v2_cw(co_2d, tri_2d[0], tri_2d[1], tri_2d[2])) { + return true; + } + } while ((l_iter = l_iter->next) != l_first); + } + + return false; +} + +static bool bm_vert_collapse_is_degenerate(BMVert *v) +{ + BMEdge *e_pair[2]; + BMVert *v_pair[2]; + + if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) { + + /* allow wire edges */ + if (BM_edge_is_wire(e_pair[0]) || BM_edge_is_wire(e_pair[1])) { + return false; + } + + v_pair[0] = BM_edge_other_vert(e_pair[0], v); + v_pair[1] = BM_edge_other_vert(e_pair[1], v); + + if (fabsf(cos_v3v3v3(v_pair[0]->co, v->co, v_pair[1]->co)) < (1.0f - FLT_EPSILON)) { + BMLoop *l_iter, *l_first; + l_iter = l_first = e_pair[1]->l; + do { + if (l_iter->f->len > 3) { + BMLoop *l_pivot = (l_iter->v == v ? l_iter : l_iter->next); + BLI_assert(v == l_pivot->v); + if (bm_loop_collapse_is_degenerate(l_pivot)) { + return true; + } + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + return false; + } + else { + return true; + } +} +#endif /* USE_DEGENERATE_CHECK */ + + void BM_mesh_decimate_dissolve_ex( BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, BMO_Delimit delimit, @@ -144,6 +264,7 @@ void BM_mesh_decimate_dissolve_ex( BMEdge **einput_arr, const int einput_len, const short oflag_out) { + const float angle_limit_cos_neg = -cosf(angle_limit); struct DelimitData delimit_data = {0}; const int eheap_table_len = do_dissolve_boundaries ? einput_len : max_ii(einput_len, vinput_len); void *_heap_table = MEM_mallocN(sizeof(HeapNode *) * eheap_table_len, __func__); @@ -175,7 +296,6 @@ void BM_mesh_decimate_dissolve_ex( /* --- setup heap --- */ eheap = BLI_heap_new_ex(einput_len); - eheap_table = _heap_table; /* wire -> tag */ BM_ITER_MESH (e_iter, &iter, bm, BM_EDGES_OF_MESH) { @@ -193,7 +313,7 @@ void BM_mesh_decimate_dissolve_ex( } while ((BLI_heap_is_empty(eheap) == false) && - (BLI_heap_node_value((enode_top = BLI_heap_top(eheap))) < angle_limit)) + (BLI_heap_node_value((enode_top = BLI_heap_top(eheap))) < angle_limit_cos_neg)) { BMFace *f_new = NULL; BMEdge *e; @@ -328,7 +448,14 @@ void BM_mesh_decimate_dissolve_ex( v = BLI_heap_node_ptr(vnode_top); i = BM_elem_index_get(v); - if (BM_vert_is_edge_pair(v)) { + if ( +#ifdef USE_DEGENERATE_CHECK + !bm_vert_collapse_is_degenerate(v) +#else + BM_vert_is_edge_pair(v) +#endif + ) + { e_new = BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */ if (e_new) { @@ -343,7 +470,6 @@ void BM_mesh_decimate_dissolve_ex( do { BM_face_normal_update(l_iter->f); } while ((l_iter = l_iter->radial_next) != l_first); - } /* re-calculate costs */ @@ -355,6 +481,33 @@ void BM_mesh_decimate_dissolve_ex( vheap_table[j] = BLI_heap_insert(vheap, cost, v_iter); } } + +#ifdef USE_DEGENERATE_CHECK + /* dissolving a vertex may mean vertices we previously weren't able to dissolve + * can now be re-evaluated. */ + if (e_new->l) { + BMLoop *l_first, *l_iter; + l_iter = l_first = e_new->l; + do { + /* skip vertices part of this edge, evaluated above */ + BMLoop *l_cycle_first, *l_cycle_iter; + l_cycle_iter = l_iter->next->next; + l_cycle_first = l_iter->prev; + do { + const int j = BM_elem_index_get(l_cycle_iter->v); + if (j != -1 && vheap_table[j] && + (BLI_heap_node_value(vheap_table[j]) == COST_INVALID)) + { + const float cost = bm_vert_edge_face_angle(l_cycle_iter->v); + BLI_heap_remove(vheap, vheap_table[j]); + vheap_table[j] = BLI_heap_insert(vheap, cost, l_cycle_iter->v); + } + } while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_first); + + } while ((l_iter = l_iter->radial_next) != l_first); + } +#endif /* USE_DEGENERATE_CHECK */ + } } diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c index e6437aa6c59..9d1f7fa45d2 100644 --- a/source/blender/bmesh/tools/bmesh_intersect.c +++ b/source/blender/bmesh/tools/bmesh_intersect.c @@ -85,6 +85,8 @@ /* insert bl_debug_draw_quad_clear... here */ #endif +// #define USE_DUMP + static void tri_v3_scale( float v1[3], float v2[3], float v3[3], const float t) @@ -652,7 +654,7 @@ static void bm_isect_tri_tri( #endif if (e) { #ifdef USE_DUMP - printf(" adding to edge %d\n", BM_elem_index_get(e)); + printf("# adding to edge %d\n", BM_elem_index_get(e)); #endif edge_verts_add(s, e, fv_b[i_b], true); } @@ -769,16 +771,13 @@ static void bm_isect_tri_tri( continue; iv = bm_isect_edge_tri(s, fv_b[i_e0], fv_b[i_e1], fv_a, a_index, f_a_cos, f_a_nor, &side); if (iv) { - /* check this wasn't handled above */ - if (!(side >= IX_EDGE_TRI_EDGE0 && side <= IX_EDGE_TRI_EDGE2)) { - BLI_assert(BLI_array_findindex((void **)iv_ls_a, STACK_SIZE(iv_ls_a), iv) == -1); - BLI_assert(BLI_array_findindex((void **)iv_ls_b, STACK_SIZE(iv_ls_b), iv) == -1); - STACK_PUSH(iv_ls_a, iv); - STACK_PUSH(iv_ls_b, iv); + BLI_assert(BLI_array_findindex((void **)iv_ls_a, STACK_SIZE(iv_ls_a), iv) == -1); + BLI_assert(BLI_array_findindex((void **)iv_ls_b, STACK_SIZE(iv_ls_b), iv) == -1); + STACK_PUSH(iv_ls_a, iv); + STACK_PUSH(iv_ls_b, iv); #ifdef USE_DUMP - printf(" ('EDGE-RAY-B', %d),\n", side); + printf(" ('EDGE-TRI-B', %d),\n", side); #endif - } } } } @@ -873,7 +872,7 @@ static void raycast_callback(void *userdata, #endif #ifdef USE_DUMP - printf("%s: Adding depth %f\n", __func__, depth); + printf("%s: Adding depth %f\n", __func__, dist); #endif BLI_buffer_append(raycast_data->z_buffer, float, dist); } @@ -909,7 +908,7 @@ static int isect_bvhtree_point_v3( &raycast_data); #ifdef USE_DUMP - printf("%s: Total intersections: %d\n", __func__, raycast_data.num_isect); + printf("%s: Total intersections: %d\n", __func__, z_buffer.count); #endif int num_isect; diff --git a/source/blender/collada/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp index 15e95a05425..bd32e989ae3 100644 --- a/source/blender/collada/DocumentExporter.cpp +++ b/source/blender/collada/DocumentExporter.cpp @@ -90,7 +90,7 @@ extern "C" #include "BKE_action.h" // pose functions #include "BKE_animsys.h" #include "BKE_armature.h" -#include "BKE_blender.h" // version info +#include "BKE_blender_version.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_image.h" diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp index 64df42fc823..a884268fd5e 100644 --- a/source/blender/collada/MeshImporter.cpp +++ b/source/blender/collada/MeshImporter.cpp @@ -39,7 +39,6 @@ #include "COLLADAFWPolygons.h" extern "C" { - #include "BKE_blender.h" #include "BKE_customdata.h" #include "BKE_displist.h" #include "BKE_global.h" diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index b1cbc01a9a6..b64b10e0833 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -118,12 +118,11 @@ int collada_export(Scene *sce, export_settings.export_set = BKE_object_relational_superset(sce, objectSet, (eObRelationTypes)includeFilter); int export_count = BLI_linklist_count(export_settings.export_set); - if (export_count==0) - { + if (export_count == 0) { if (export_settings.selected) { fprintf(stderr, "Collada: Found no objects to export.\nPlease ensure that all objects which shall be exported are also visible in the 3D Viewport.\n"); } - else{ + else { fprintf(stderr, "Collada: Your scene seems to be empty. No Objects will be exported.\n"); } } diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp index efdfaadd3e2..f8feed8145c 100644 --- a/source/blender/collada/collada_utils.cpp +++ b/source/blender/collada/collada_utils.cpp @@ -357,9 +357,12 @@ void bc_triangulate_mesh(Mesh *me) int quad_method = MOD_TRIANGULATE_QUAD_SHORTEDGE; /* XXX: The triangulation method selection could be offered in the UI */ BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default); - BM_mesh_bm_from_me(bm, me, true, false, 0); + BMeshFromMeshParams bm_from_me_params = {0}; + bm_from_me_params.calc_face_normal = true; + BM_mesh_bm_from_me(bm, me, &bm_from_me_params); BM_mesh_triangulate(bm, quad_method, use_beauty, tag_only, NULL, NULL, NULL); - BM_mesh_bm_to_me(bm, me, false); + BMeshToMeshParams bm_to_me_params = {0}; + BM_mesh_bm_to_me(bm, me, &bm_to_me_params); BM_mesh_free(bm); } diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 35e5ec98e57..3180e7e4154 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -542,11 +542,6 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}/operations ) -if(MSVC AND NOT CMAKE_CL_64) - set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} /arch:SSE2") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:SSE2") -endif() - data_to_c(${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h SRC) diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.cpp b/source/blender/compositor/nodes/COM_ColorBalanceNode.cpp index a531493d486..46a70716349 100644 --- a/source/blender/compositor/nodes/COM_ColorBalanceNode.cpp +++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.cpp @@ -58,7 +58,12 @@ void ColorBalanceNode::convertToOperations(NodeConverter &converter, const Compo } else { ColorBalanceASCCDLOperation *operationCDL = new ColorBalanceASCCDLOperation(); - operationCDL->setOffset(n->offset); + + float offset[3]; + copy_v3_fl(offset, n->offset_basis); + add_v3_v3(offset, n->offset); + + operationCDL->setOffset(offset); operationCDL->setPower(n->power); operationCDL->setSlope(n->slope); operation = operationCDL; diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp index c55366ab370..624378f2ae9 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cpp +++ b/source/blender/compositor/operations/COM_ImageOperation.cpp @@ -159,7 +159,11 @@ static void sampleImageAtLocation(ImBuf *ibuf, float x, float y, PixelSampler sa void ImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - if (this->m_imageFloatBuffer == NULL && this->m_imageByteBuffer == NULL) { + int ix = x, iy = y; + if (ix < 0 || iy < 0 || ix >= this->m_buffer->x || iy >= this->m_buffer->y) { + zero_v4(output); + } + else if (this->m_imageFloatBuffer == NULL && this->m_imageByteBuffer == NULL) { zero_v4(output); } else { diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cpp b/source/blender/compositor/operations/COM_RenderLayersProg.cpp index 543ca0f7c11..099208ce600 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cpp +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cpp @@ -77,21 +77,19 @@ void RenderLayersBaseProg::doInterpolation(float output[4], float x, float y, Pi unsigned int offset; int width = this->getWidth(), height = this->getHeight(); + int ix = x, iy = y; + if (ix < 0 || iy < 0 || ix >= width || iy >= height) { + if (this->m_elementsize == 1) + output[0] = 0.0f; + else if (this->m_elementsize == 3) + zero_v3(output); + else + zero_v4(output); + return; + } + switch (sampler) { case COM_PS_NEAREST: { - int ix = x; - int iy = y; - if (ix < 0 || iy < 0 || ix >= width || iy >= height) { - if (this->m_elementsize == 1) - output[0] = 0.0f; - else if (this->m_elementsize == 3) - zero_v3(output); - else - zero_v4(output); - break; - - } - offset = (iy * width + ix) * this->m_elementsize; if (this->m_elementsize == 1) diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index e1dc8b020fb..2b4df85f29c 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -83,7 +83,9 @@ set(SRC util/depsgraph_util_transitive.h ) -if(HAVE_STD_UNORDERED_MAP_HEADER) +if(WITH_CXX11) + add_definitions(-DDEG_STD_UNORDERED_MAP) +elseif(HAVE_STD_UNORDERED_MAP_HEADER) if(HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) add_definitions(-DDEG_STD_UNORDERED_MAP) else() diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 18c7c5bd8e1..d293d03f63d 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -413,18 +413,28 @@ DepsRelation::DepsRelation(DepsNode *from, */ #endif - /* Hook it up to the nodes which use it. */ - from->outlinks.insert(this); - to->inlinks.insert(this); + /* Hook it up to the nodes which use it. + * + * NOTE: We register relation in the nodes which this link connects to here + * in constructor but we don't unregister it in the destructor. + * + * Reasoning: + * + * - Destructor is currently used on global graph destruction, so there's no + * real need in avoiding dangling pointers, all the memory is to be freed + * anyway. + * + * - Unregistering relation is not a cheap operation, so better to have it + * as an explicit call if we need this. + */ + from->outlinks.push_back(this); + to->inlinks.push_back(this); } DepsRelation::~DepsRelation() { /* Sanity check. */ BLI_assert(this->from && this->to); - /* Remove it from the nodes that use it. */ - this->from->outlinks.erase(this); - this->to->inlinks.erase(this); } /* Low level tagging -------------------------------------- */ diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index a62b23bde68..877ce6e52e9 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -244,17 +244,21 @@ static void deg_graph_build_finalize(Depsgraph *graph) id_node->layers |= id_to->layers; } } - - /* Re-tag ID for update if it was tagged before the relations - * update tag. - */ - ID *id = id_node->id; - if (id->tag & LIB_TAG_ID_RECALC_ALL && - id->tag & LIB_TAG_DOIT) - { - id_node->tag_update(graph); - id->tag &= ~LIB_TAG_DOIT; - } + } + } + + /* Re-tag IDs for update if it was tagged before the relations update tag. */ + for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); + it != graph->id_hash.end(); + ++it) + { + IDDepsNode *id_node = it->second; + ID *id = id_node->id; + if (id->tag & LIB_TAG_ID_RECALC_ALL && + id->tag & LIB_TAG_DOIT) + { + id_node->tag_update(graph); + id->tag &= ~LIB_TAG_DOIT; } } } diff --git a/source/blender/depsgraph/intern/depsgraph_build_nodes.cc b/source/blender/depsgraph/intern/depsgraph_build_nodes.cc index 0a5235a6d11..be706ffb338 100644 --- a/source/blender/depsgraph/intern/depsgraph_build_nodes.cc +++ b/source/blender/depsgraph/intern/depsgraph_build_nodes.cc @@ -248,6 +248,13 @@ void DepsgraphNodeBuilder::build_scene(Main *bmain, Scene *scene) * needed. */ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + /* XXX nested node trees are not included in tag-clearing above, + * so we need to do this manually. + */ + FOREACH_NODETREE(bmain, nodetree, id) { + if (id != (ID *)nodetree) + nodetree->id.tag &= ~LIB_TAG_DOIT; + } FOREACH_NODETREE_END /* scene ID block */ add_id_node(&scene->id); @@ -778,6 +785,14 @@ void DepsgraphNodeBuilder::build_rig(Scene *scene, Object *ob) } } + /* speed optimization for animation lookups */ + if (ob->pose) { + BKE_pose_channels_hash_make(ob->pose); + if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { + BKE_pose_update_constraint_flags(ob->pose); + } + } + /* Make sure pose is up-to-date with armature updates. */ add_operation_node(&arm->id, DEPSNODE_TYPE_PARAMETERS, @@ -872,6 +887,14 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *ob) ID *obdata = (ID *)ob->data; build_animdata(obdata); + BLI_assert(ob->pose != NULL); + + /* speed optimization for animation lookups */ + BKE_pose_channels_hash_make(ob->pose); + if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { + BKE_pose_update_constraint_flags(ob->pose); + } + add_operation_node(&ob->id, DEPSNODE_TYPE_EVAL_POSE, DEPSOP_TYPE_INIT, diff --git a/source/blender/depsgraph/intern/depsgraph_build_relations.cc b/source/blender/depsgraph/intern/depsgraph_build_relations.cc index 226991e7b11..52af483d87c 100644 --- a/source/blender/depsgraph/intern/depsgraph_build_relations.cc +++ b/source/blender/depsgraph/intern/depsgraph_build_relations.cc @@ -244,6 +244,13 @@ void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) * created or not. */ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + /* XXX nested node trees are not included in tag-clearing above, + * so we need to do this manually. + */ + FOREACH_NODETREE(bmain, nodetree, id) { + if (id != (ID *)nodetree) + nodetree->id.tag &= ~LIB_TAG_DOIT; + } FOREACH_NODETREE_END if (scene->set) { // TODO: link set to scene, especially our timesource... @@ -1683,7 +1690,9 @@ void DepsgraphRelationBuilder::build_obdata_geom(Main *bmain, Scene *scene, Obje if (mom != ob) { /* non-motherball -> cannot be directly evaluated! */ ComponentKey mom_key(&mom->id, DEPSNODE_TYPE_GEOMETRY); + ComponentKey transform_key(&ob->id, DEPSNODE_TYPE_TRANSFORM); add_relation(geom_key, mom_key, DEPSREL_TYPE_GEOMETRY_EVAL, "Metaball Motherball"); + add_relation(transform_key, mom_key, DEPSREL_TYPE_GEOMETRY_EVAL, "Metaball Motherball"); } break; } diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index e8065766332..66535f5214b 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -56,6 +56,9 @@ extern "C" { static bool use_legacy_depsgraph = true; #endif +/* Unfinished and unused, and takes quite some pre-processing time. */ +#undef USE_EVAL_PRIORITY + bool DEG_depsgraph_use_legacy(void) { #ifdef DISABLE_NEW_DEPSGRAPH @@ -122,7 +125,8 @@ void DEG_evaluation_context_free(EvaluationContext *eval_ctx) static void schedule_children(TaskPool *pool, Depsgraph *graph, OperationDepsNode *node, - const int layers); + const int layers, + const int thread_id); struct DepsgraphEvalState { EvaluationContext *eval_ctx; @@ -132,75 +136,159 @@ struct DepsgraphEvalState { static void deg_task_run_func(TaskPool *pool, void *taskdata, - int UNUSED(threadid)) + int thread_id) { DepsgraphEvalState *state = (DepsgraphEvalState *)BLI_task_pool_userdata(pool); OperationDepsNode *node = (OperationDepsNode *)taskdata; - if (!node->is_noop()) { + BLI_assert(!node->is_noop() && "NOOP nodes should not actually be scheduled"); + + /* Should only be the case for NOOPs, which never get to this point. */ + BLI_assert(node->evaluate); + + while (true) { /* Get context. */ // TODO: who initialises this? "Init" operations aren't able to initialise it!!! - /* TODO(sergey): Wedon't use component contexts at this moment. */ + /* TODO(sergey): We don't use component contexts at this moment. */ /* ComponentDepsNode *comp = node->owner; */ BLI_assert(node->owner != NULL); - /* Take note of current time. */ - double start_time = PIL_check_seconds_timer(); - DepsgraphDebug::task_started(state->graph, node); - - /* Should only be the case for NOOPs, which never get to this point. */ - BLI_assert(node->evaluate); - - /* Perform operation. */ - node->evaluate(state->eval_ctx); + /* Since we're not leaving the thread for until the graph branches it is + * possible to have NO-OP on the way. for which evaluate() will be NULL. + * but that's all fine, we'll just scheduler it's children. + */ + if (node->evaluate) { + /* Take note of current time. */ + double start_time = PIL_check_seconds_timer(); + DepsgraphDebug::task_started(state->graph, node); + + /* Perform operation. */ + node->evaluate(state->eval_ctx); + + /* Note how long this took. */ + double end_time = PIL_check_seconds_timer(); + DepsgraphDebug::task_completed(state->graph, + node, + end_time - start_time); + } - /* Note how long this took. */ - double end_time = PIL_check_seconds_timer(); - DepsgraphDebug::task_completed(state->graph, - node, - end_time - start_time); + /* If there's only one outgoing link we try to immediately switch to + * that node evaluation, without leaving the thread. + * + * It's only doable if the child don't have extra relations or all they + * are satisfied. + * + * TODO(sergey): Checks here can be de-duplicated with the ones from + * schedule_node(), however, how to do it nicely? + */ + if (node->outlinks.size() == 1) { + DepsRelation *rel = node->outlinks[0]; + OperationDepsNode *child = (OperationDepsNode *)rel->to; + BLI_assert(child->type == DEPSNODE_TYPE_OPERATION); + if (!child->scheduled) { + int id_layers = child->owner->owner->layers; + if (!((child->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 && + (id_layers & state->layers) != 0)) + { + /* Node does not need an update, so can;t continue with the + * chain and need to switch to another one by leaving the + * thread. + */ + break; + } + if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { + BLI_assert(child->num_links_pending > 0); + atomic_sub_uint32(&child->num_links_pending, 1); + } + if (child->num_links_pending == 0) { + bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&child->scheduled, (uint8_t)true); + if (!is_scheduled) { + /* Node was not scheduled, switch to it! */ + node = child; + } + else { + /* Someone else scheduled the node, leaving us + * unemployed in this thread, we're leaving. + */ + break; + } + } + else { + /* There are other dependencies on the child, can't do + * anything in the current thread. + */ + break; + } + } + else { + /* Happens when having cyclic dependencies. + * + * Nothing to do here, single child was already scheduled, we + * can leave the thread now. + */ + break; + } + } + else { + /* TODO(sergey): It's possible to use one of the outgoing relations + * as a chain which we'll try to keep alive, but it's a bit more + * involved change. + */ + schedule_children(pool, state->graph, node, state->layers, thread_id); + break; + } } - - schedule_children(pool, state->graph, node, state->layers); } -static void calculate_pending_parents(Depsgraph *graph, int layers) +typedef struct CalculatePengindData { + Depsgraph *graph; + int layers; +} CalculatePengindData; + +static void calculate_pending_func(void *data_v, int i) { - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) + CalculatePengindData *data = (CalculatePengindData *)data_v; + Depsgraph *graph = data->graph; + int layers = data->layers; + OperationDepsNode *node = graph->operations[i]; + IDDepsNode *id_node = node->owner->owner; + + node->num_links_pending = 0; + node->scheduled = false; + + /* count number of inputs that need updates */ + if ((id_node->layers & layers) != 0 && + (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) { - OperationDepsNode *node = *it_op; - IDDepsNode *id_node = node->owner->owner; - - node->num_links_pending = 0; - node->scheduled = false; - - /* count number of inputs that need updates */ - if ((id_node->layers & layers) != 0 && - (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) + DEPSNODE_RELATIONS_ITER_BEGIN(node->inlinks, rel) { - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) + if (rel->from->type == DEPSNODE_TYPE_OPERATION && + (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - DepsRelation *rel = *it_rel; - if (rel->from->type == DEPSNODE_TYPE_OPERATION && - (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) + OperationDepsNode *from = (OperationDepsNode *)rel->from; + IDDepsNode *id_from_node = from->owner->owner; + if ((id_from_node->layers & layers) != 0 && + (from->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) { - OperationDepsNode *from = (OperationDepsNode *)rel->from; - IDDepsNode *id_from_node = from->owner->owner; - if ((id_from_node->layers & layers) != 0 && - (from->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) - { - ++node->num_links_pending; - } + ++node->num_links_pending; } } } + DEPSNODE_RELATIONS_ITER_END; } } +static void calculate_pending_parents(Depsgraph *graph, int layers) +{ + const int num_operations = graph->operations.size(); + const bool do_threads = num_operations > 256; + CalculatePengindData data; + data.graph = graph; + data.layers = layers; + BLI_task_parallel_range(0, num_operations, &data, calculate_pending_func, do_threads); +} + +#ifdef USE_EVAL_PRIORITY static void calculate_eval_priority(OperationDepsNode *node) { if (node->done) { @@ -229,68 +317,77 @@ static void calculate_eval_priority(OperationDepsNode *node) node->eval_priority = 0.0f; } } +#endif + +/* Schedule a node if it needs evaluation. + * dec_parents: Decrement pending parents count, true when child nodes are scheduled + * after a task has been completed. + */ +static void schedule_node(TaskPool *pool, Depsgraph *graph, int layers, + OperationDepsNode *node, bool dec_parents, + const int thread_id) +{ + int id_layers = node->owner->owner->layers; + + if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 && + (id_layers & layers) != 0) + { + if (dec_parents) { + BLI_assert(node->num_links_pending > 0); + atomic_sub_uint32(&node->num_links_pending, 1); + } + + if (node->num_links_pending == 0) { + bool is_scheduled = atomic_fetch_and_or_uint8((uint8_t *)&node->scheduled, (uint8_t)true); + if (!is_scheduled) { + if (node->is_noop()) { + /* skip NOOP node, schedule children right away */ + schedule_children(pool, graph, node, layers, thread_id); + } + else { + /* children are scheduled once this task is completed */ + BLI_task_pool_push_from_thread(pool, + deg_task_run_func, + node, + false, + TASK_PRIORITY_LOW, + thread_id); + } + } + } + } +} static void schedule_graph(TaskPool *pool, Depsgraph *graph, const int layers) { - BLI_spin_lock(&graph->lock); for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); it != graph->operations.end(); ++it) { OperationDepsNode *node = *it; - IDDepsNode *id_node = node->owner->owner; - if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) && - node->num_links_pending == 0 && - (id_node->layers & layers) != 0) - { - BLI_task_pool_push(pool, deg_task_run_func, node, false, TASK_PRIORITY_LOW); - node->scheduled = true; - } + schedule_node(pool, graph, layers, node, false, 0); } - BLI_spin_unlock(&graph->lock); } static void schedule_children(TaskPool *pool, Depsgraph *graph, OperationDepsNode *node, - const int layers) + const int layers, + const int thread_id) { - for (OperationDepsNode::Relations::const_iterator it = node->outlinks.begin(); - it != node->outlinks.end(); - ++it) + DEPSNODE_RELATIONS_ITER_BEGIN(node->outlinks, rel) { - DepsRelation *rel = *it; OperationDepsNode *child = (OperationDepsNode *)rel->to; BLI_assert(child->type == DEPSNODE_TYPE_OPERATION); - if (child->scheduled) { /* Happens when having cyclic dependencies. */ continue; } - - IDDepsNode *id_child = child->owner->owner; - if ((id_child->layers & layers) != 0 && - (child->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) - { - if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - BLI_assert(child->num_links_pending > 0); - atomic_sub_uint32(&child->num_links_pending, 1); - } - - if (child->num_links_pending == 0) { - BLI_spin_lock(&graph->lock); - bool need_schedule = !child->scheduled; - child->scheduled = true; - BLI_spin_unlock(&graph->lock); - - if (need_schedule) { - BLI_task_pool_push(pool, deg_task_run_func, child, false, TASK_PRIORITY_LOW); - } - } - } + schedule_node(pool, graph, layers, child, (rel->flag & DEPSREL_FLAG_CYCLIC) == 0, thread_id); } + DEPSNODE_RELATIONS_ITER_END; } /** @@ -341,6 +438,7 @@ void DEG_evaluate_on_refresh_ex(EvaluationContext *eval_ctx, } /* Calculate priority for operation nodes. */ +#ifdef USE_EVAL_PRIORITY for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); it != graph->operations.end(); ++it) @@ -348,6 +446,7 @@ void DEG_evaluate_on_refresh_ex(EvaluationContext *eval_ctx, OperationDepsNode *node = *it; calculate_eval_priority(node); } +#endif DepsgraphDebug::eval_begin(eval_ctx); diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 486526ed46c..3ca23965749 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -42,6 +42,8 @@ extern "C" { #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" +#include "BLI_task.h" + #include "BKE_library.h" #include "BKE_main.h" #include "BKE_node.h" @@ -263,6 +265,19 @@ void DEG_id_type_tag(Main *bmain, short idtype) /* XXX This may get a dedicated implementation later if needed - lukas */ typedef std::queue<OperationDepsNode *> FlushQueue; +static void flush_init_func(void *data_v, int i) +{ + /* ID node's done flag is used to avoid multiple editors update + * for the same ID. + */ + Depsgraph *graph = (Depsgraph *)data_v; + OperationDepsNode *node = graph->operations[i]; + IDDepsNode *id_node = node->owner->owner; + id_node->done = 0; + node->scheduled = false; + node->owner->flags &= ~DEPSCOMP_FULLY_SCHEDULED; +} + /* Flush updates from tagged nodes outwards until all affected nodes are tagged. */ void DEG_graph_flush_updates(Main *bmain, Depsgraph *graph) { @@ -278,14 +293,9 @@ void DEG_graph_flush_updates(Main *bmain, Depsgraph *graph) /* TODO(sergey): With a bit of flag magic we can get rid of this * extra loop. */ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; - node->scheduled = false; - node->owner->flags &= ~DEPSCOMP_FULLY_SCHEDULED; - } + const int num_operations = graph->operations.size(); + const bool do_threads = num_operations > 256; + BLI_task_parallel_range(0, num_operations, graph, flush_init_func, do_threads); FlushQueue queue; /* Starting from the tagged "entry" nodes, flush outwards... */ @@ -301,7 +311,10 @@ void DEG_graph_flush_updates(Main *bmain, Depsgraph *graph) OperationDepsNode *node = *it; IDDepsNode *id_node = node->owner->owner; queue.push(node); - deg_editors_id_update(bmain, id_node->id); + if (id_node->done == 0) { + deg_editors_id_update(bmain, id_node->id); + id_node->done = 1; + } node->scheduled = true; } @@ -346,7 +359,10 @@ void DEG_graph_flush_updates(Main *bmain, Depsgraph *graph) to_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; queue.push(to_node); to_node->scheduled = true; - deg_editors_id_update(bmain, id_node->id); + if (id_node->done == 0) { + deg_editors_id_update(bmain, id_node->id); + id_node->done = 1; + } } } @@ -384,23 +400,21 @@ void DEG_ids_flush_tagged(Main *bmain) } } +static void graph_clear_func(void *data_v, int i) +{ + Depsgraph *graph = (Depsgraph *)data_v; + OperationDepsNode *node = graph->operations[i]; + /* Clear node's "pending update" settings. */ + node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE); +} + /* Clear tags from all operation nodes. */ void DEG_graph_clear_tags(Depsgraph *graph) { /* Go over all operation nodes, clearing tags. */ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; - - /* Clear node's "pending update" settings. */ - node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE); - /* Reset so that it can be bumped up again. */ - node->num_links_pending = 0; - node->scheduled = false; - } - + const int num_operations = graph->operations.size(); + const bool do_threads = num_operations > 256; + BLI_task_parallel_range(0, num_operations, graph, graph_clear_func, do_threads); /* Clear any entry tags which haven't been flushed. */ graph->entry_tags.clear(); } @@ -436,12 +450,11 @@ void DEG_graph_on_visible_update(Main *bmain, Scene *scene) * This is mainly needed on file load only, after that updates of invisible objects * will be stored in the pending list. */ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); + for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); + it != graph->id_hash.end(); ++it) { - OperationDepsNode *node = *it; - IDDepsNode *id_node = node->owner->owner; + IDDepsNode *id_node = it->second; ID *id = id_node->id; if ((id->tag & LIB_TAG_ID_RECALC_ALL) != 0 || (id_node->layers & scene->lay_updated) == 0) diff --git a/source/blender/depsgraph/intern/depsnode.cc b/source/blender/depsgraph/intern/depsnode.cc index 7d4d6890c83..8aa1059d2dc 100644 --- a/source/blender/depsgraph/intern/depsnode.cc +++ b/source/blender/depsgraph/intern/depsnode.cc @@ -71,21 +71,16 @@ DepsNode::DepsNode() DepsNode::~DepsNode() { - /* free links - * note: deleting relations will remove them from the node relations set, - * but only touch the same position as we are using here, which is safe. + /* Free links. */ + /* NOTE: We only free incoming links. This is to avoid double-free of links + * when we're trying to free same link from both it's sides. We don't have + * dangling links so this is not a problem from memory leaks point of view. */ DEPSNODE_RELATIONS_ITER_BEGIN(this->inlinks, rel) { OBJECT_GUARDED_DELETE(rel, DepsRelation); } DEPSNODE_RELATIONS_ITER_END; - - DEPSNODE_RELATIONS_ITER_BEGIN(this->outlinks, rel) - { - OBJECT_GUARDED_DELETE(rel, DepsRelation); - } - DEPSNODE_RELATIONS_ITER_END; } diff --git a/source/blender/depsgraph/intern/depsnode.h b/source/blender/depsgraph/intern/depsnode.h index 53826ae8e71..4a464955384 100644 --- a/source/blender/depsgraph/intern/depsnode.h +++ b/source/blender/depsgraph/intern/depsnode.h @@ -73,7 +73,7 @@ struct DepsNode { * from basic serialization benefits - from the typeinfo) is that we can have * relationships between these nodes! */ - typedef unordered_set<DepsRelation *> Relations; + typedef vector<DepsRelation *> Relations; /* Nodes which this one depends on. */ Relations inlinks; diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt index 473b0e6bc5a..1bf1bb2a474 100644 --- a/source/blender/editors/animation/CMakeLists.txt +++ b/source/blender/editors/animation/CMakeLists.txt @@ -59,6 +59,10 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +if(WITH_PYTHON) + add_definitions(-DWITH_PYTHON) +endif() + add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_editor_animation "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 9d54fd8f730..ea2f7fc5588 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -3818,6 +3818,9 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void /* send notifiers before doing anything else... */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + if (ale_setting->type == ANIMTYPE_GPLAYER) + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); + /* verify animation context */ if (ANIM_animdata_get_context(C, &ac) == 0) return; diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 40376c38c3b..823cde75334 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -39,6 +39,8 @@ #include "BLI_math_base.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_fcurve.h" #include "BKE_main.h" @@ -685,7 +687,7 @@ static void ed_marker_move_update_header(bContext *C, wmOperator *op) MarkerMove *mm = op->customdata; TimeMarker *marker, *selmarker = NULL; const int offs = RNA_int_get(op->ptr, "frames"); - char str[256]; + char str[UI_MAX_DRAW_STR]; char str_offs[NUM_STR_REP_LEN]; int totmark; const bool use_time = ed_marker_move_use_time(mm); @@ -710,14 +712,14 @@ static void ed_marker_move_update_header(bContext *C, wmOperator *op) if (totmark == 1 && selmarker) { /* we print current marker value */ if (use_time) { - BLI_snprintf(str, sizeof(str), "Marker %.2f offset %s", FRA2TIME(selmarker->frame), str_offs); + BLI_snprintf(str, sizeof(str), IFACE_("Marker %.2f offset %s"), FRA2TIME(selmarker->frame), str_offs); } else { - BLI_snprintf(str, sizeof(str), "Marker %d offset %s", selmarker->frame, str_offs); + BLI_snprintf(str, sizeof(str), IFACE_("Marker %d offset %s"), selmarker->frame, str_offs); } } else { - BLI_snprintf(str, sizeof(str), "Marker offset %s", str_offs); + BLI_snprintf(str, sizeof(str), IFACE_("Marker offset %s"), str_offs); } ED_area_headerprint(CTX_wm_area(C), str); diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index f75f2ce322c..afc4e5c9e61 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -61,9 +61,6 @@ #include "anim_intern.h" -/* called by WM */ -void free_anim_drivers_copybuf(void); - /* ************************************************** */ /* Animation Data Validation */ @@ -514,8 +511,7 @@ bool ANIM_remove_driver(ReportList *UNUSED(reports), ID *id, const char rna_path static FCurve *channeldriver_copypaste_buf = NULL; /* This function frees any MEM_calloc'ed copy/paste buffer data */ -// XXX find some header to put this in! -void free_anim_drivers_copybuf(void) +void ANIM_drivers_copybuf_free(void) { /* free the buffer F-Curve if it exists, as if it were just another F-Curve */ if (channeldriver_copypaste_buf) @@ -553,7 +549,7 @@ bool ANIM_copy_driver(ReportList *reports, ID *id, const char rna_path[], int ar fcu = verify_driver_fcurve(id, rna_path, array_index, 0); /* clear copy/paste buffer first (for consistency with other copy/paste buffers) */ - free_anim_drivers_copybuf(); + ANIM_drivers_copybuf_free(); /* copy this to the copy/paste buf if it exists */ if (fcu && fcu->driver) { @@ -604,7 +600,7 @@ bool ANIM_paste_driver(ReportList *reports, ID *id, const char rna_path[], int a /* create Driver F-Curve, but without data which will be copied across... */ fcu = verify_driver_fcurve(id, rna_path, array_index, -1); - + if (fcu) { /* copy across the curve data from the buffer curve * NOTE: this step needs care to not miss new settings @@ -629,6 +625,117 @@ bool ANIM_paste_driver(ReportList *reports, ID *id, const char rna_path[], int a } /* ************************************************** */ +/* Driver Management API - Copy/Paste Driver Variables */ + +/* Copy/Paste Buffer for Driver Variables... */ +static ListBase driver_vars_copybuf = {NULL, NULL}; + +/* This function frees any MEM_calloc'ed copy/paste buffer data */ +void ANIM_driver_vars_copybuf_free(void) +{ + /* Free the driver variables kept in the buffer */ + if (driver_vars_copybuf.first) { + DriverVar *dvar, *dvarn; + + /* Free variables (and any data they use) */ + for (dvar = driver_vars_copybuf.first; dvar; dvar = dvarn) { + dvarn = dvar->next; + driver_free_variable(&driver_vars_copybuf, dvar); + } + } + + BLI_listbase_clear(&driver_vars_copybuf); +} + +/* Checks if there are driver variables in the copy/paste buffer */ +bool ANIM_driver_vars_can_paste(void) +{ + return (BLI_listbase_is_empty(&driver_vars_copybuf) == false); +} + +/* -------------------------------------------------- */ + +/* Copy the given driver's variables to the buffer */ +bool ANIM_driver_vars_copy(ReportList *reports, FCurve *fcu) +{ + /* sanity checks */ + if (ELEM(NULL, fcu, fcu->driver)) { + BKE_report(reports, RPT_ERROR, "No driver to copy variables from"); + return false; + } + + if (BLI_listbase_is_empty(&fcu->driver->variables)) { + BKE_report(reports, RPT_ERROR, "Driver has no variables to copy"); + return false; + } + + /* clear buffer */ + ANIM_driver_vars_copybuf_free(); + + /* copy over the variables */ + driver_variables_copy(&driver_vars_copybuf, &fcu->driver->variables); + + return (BLI_listbase_is_empty(&driver_vars_copybuf) == false); +} + +/* Paste the variables in the buffer to the given FCurve */ +bool ANIM_driver_vars_paste(ReportList *reports, FCurve *fcu, bool replace) +{ + ChannelDriver *driver = (fcu) ? fcu->driver : NULL; + ListBase tmp_list = {NULL, NULL}; + + /* sanity checks */ + if (BLI_listbase_is_empty(&driver_vars_copybuf)) { + BKE_report(reports, RPT_ERROR, "No driver variables in clipboard to paste"); + return false; + } + + if (ELEM(NULL, fcu, fcu->driver)) { + BKE_report(reports, RPT_ERROR, "Cannot paste driver variables without a driver"); + return false; + } + + /* 1) Make a new copy of the variables in the buffer - these will get pasted later... */ + driver_variables_copy(&tmp_list, &driver_vars_copybuf); + + /* 2) Prepare destination array */ + if (replace) { + DriverVar *dvar, *dvarn; + + /* Free all existing vars first - We aren't retaining anything */ + for (dvar = driver->variables.first; dvar; dvar = dvarn) { + dvarn = dvar->next; + driver_free_variable_ex(driver, dvar); + } + + BLI_listbase_clear(&driver->variables); + } + + /* 3) Add new vars */ + if (driver->variables.last) { + DriverVar *last = driver->variables.last; + DriverVar *first = tmp_list.first; + + last->next = first; + first->prev = last; + + driver->variables.last = tmp_list.last; + } + else { + driver->variables.first = tmp_list.first; + driver->variables.last = tmp_list.last; + } + +#ifdef WITH_PYTHON + /* since driver variables are cached, the expression needs re-compiling too */ + if (driver->type == DRIVER_TYPE_PYTHON) + driver->flag |= DRIVER_FLAG_RENAMEVAR; +#endif + + return true; +} + +/* ************************************************** */ /* UI-Button Interface */ /* Add Driver - Enum Defines ------------------------- */ @@ -641,7 +748,8 @@ EnumPropertyItem prop_driver_create_mapping_types[] = { "Drive all components of this property using the target picked"}, {CREATEDRIVER_MAPPING_1_1, "DIRECT", 0, "Single from Target", "Drive this component of this property using the target picked"}, - {CREATEDRIVER_MAPPING_N_N, "MATCH", 0, "Match Indices", + + {CREATEDRIVER_MAPPING_N_N, "MATCH", ICON_COLOR, "Match Indices", "Create drivers for each pair of corresponding elements"}, {CREATEDRIVER_MAPPING_NONE_ALL, "NONE_ALL", ICON_HAND, "Manually Create Later", @@ -778,7 +886,7 @@ void ANIM_OT_driver_button_add(wmOperatorType *ot) /* identifiers */ ot->name = "Add Driver"; ot->idname = "ANIM_OT_driver_button_add"; - ot->description = "Add driver(s) for the property(s) connected represented by the highlighted button"; + ot->description = "Add driver(s) for the property(s) represented by the highlighted button"; /* callbacks */ /* NOTE: No exec, as we need all these to use the current context info diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 91705d63286..6bb73416fec 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -697,14 +697,14 @@ static ListBase fmodifier_copypaste_buf = {NULL, NULL}; /* ---------- */ /* free the copy/paste buffer */ -void free_fmodifiers_copybuf(void) +void ANIM_fmodifiers_copybuf_free(void) { /* just free the whole buffer */ free_fmodifiers(&fmodifier_copypaste_buf); } /* copy the given F-Modifiers to the buffer, returning whether anything was copied or not - * assuming that the buffer has been cleared already with free_fmodifiers_copybuf() + * assuming that the buffer has been cleared already with ANIM_fmodifiers_copybuf_free() * - active: only copy the active modifier */ bool ANIM_fmodifiers_copy_to_buf(ListBase *modifiers, bool active) diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 9733d920b78..1703210f0b6 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -527,8 +527,7 @@ typedef struct tAnimCopybufItem { /* This function frees any MEM_calloc'ed copy/paste buffer data */ -// XXX find some header to put this in! -void free_anim_copybuf(void) +void ANIM_fcurves_copybuf_free(void) { tAnimCopybufItem *aci, *acn; @@ -563,7 +562,7 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) Scene *scene = ac->scene; /* clear buffer first */ - free_anim_copybuf(); + ANIM_fcurves_copybuf_free(); /* assume that each of these is an F-Curve */ for (ale = anim_data->first; ale; ale = ale->next) { diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 218f215a350..847b45d612c 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -83,6 +83,15 @@ EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name) bone->segments = 1; bone->layer = arm->layer; + bone->roll1 = 0.0f; + bone->roll2 = 0.0f; + bone->curveInX = 0.0f; + bone->curveInY = 0.0f; + bone->curveOutX = 0.0f; + bone->curveOutY = 0.0f; + bone->scaleIn = 1.0f; + bone->scaleOut = 1.0f; + return bone; } @@ -291,12 +300,8 @@ void preEditBoneDuplicate(ListBase *editbones) /** * Helper function for #postEditBoneDuplicate, * return the destination pchan from the original. - * - * \param use_orig_fallback: return the input value if no new channel is found. */ -static bPoseChannel *pchan_duplicate_map( - const bPose *pose, GHash *name_map, - bPoseChannel *pchan_src, bool use_orig_fallback) +static bPoseChannel *pchan_duplicate_map(const bPose *pose, GHash *name_map, bPoseChannel *pchan_src) { bPoseChannel *pchan_dst = NULL; const char *name_src = pchan_src->name; @@ -305,7 +310,7 @@ static bPoseChannel *pchan_duplicate_map( pchan_dst = BKE_pose_channel_find_name(pose, name_dst); } - if ((pchan_dst == NULL) && use_orig_fallback) { + if (pchan_dst == NULL) { pchan_dst = pchan_src; } @@ -325,6 +330,9 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) for (EditBone *ebone_src = editbones->first; ebone_src; ebone_src = ebone_src->next) { EditBone *ebone_dst = ebone_src->temp.ebone; + if (!ebone_dst) { + ebone_dst = ED_armature_bone_get_mirrored(editbones, ebone_src); + } if (ebone_dst) { BLI_ghash_insert(name_map, ebone_src->name, ebone_dst->name); } @@ -338,7 +346,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) bPoseChannel *pchan_dst = BKE_pose_channel_find_name(ob->pose, ebone_dst->name); if (pchan_dst) { if (pchan_src->custom_tx) { - pchan_dst->custom_tx = pchan_duplicate_map(ob->pose, name_map, pchan_src->custom_tx, true); + pchan_dst->custom_tx = pchan_duplicate_map(ob->pose, name_map, pchan_src->custom_tx); } } } @@ -682,7 +690,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) } } - /* Run though the list and fix the pointers */ + /* Run through the list and fix the pointers */ for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { if (ebone_iter->temp.ebone) { /* copy all flags except for ... */ @@ -891,6 +899,16 @@ static int armature_extrude_exec(bContext *C, wmOperator *op) newbone->segments = 1; newbone->layer = ebone->layer; + newbone->roll1 = ebone->roll1; + newbone->roll2 = ebone->roll2; + newbone->curveInX = ebone->curveInX; + newbone->curveInY = ebone->curveInY; + newbone->curveOutX = ebone->curveOutX; + newbone->curveOutY = ebone->curveOutY; + newbone->scaleIn = ebone->scaleIn; + newbone->scaleOut = ebone->scaleOut; + + BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name)); if (flipbone && forked) { // only set if mirror edit diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index ac150b9af74..02aefce3464 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -170,6 +170,11 @@ typedef struct tPChanFCurveLink { float oldangle; float oldaxis[3]; + float roll1, roll2; /* old bbone values (to be restored along with the transform properties) */ + float curveInX, curveInY; /* (NOTE: we haven't renamed these this time, as their names are already long enough) */ + float curveOutX, curveOutY; + float scaleIn, scaleOut; + struct IDProperty *oldprops; /* copy of custom properties at start of operator (to be restored before each modal step) */ } tPChanFCurveLink; diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 61ed7fdc41b..d73536e5ba7 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -179,7 +179,6 @@ EditBone *ED_armature_bone_find_shared_parent(EditBone *ebone_child[], const uns /* accumulate */ for (i = 0; i < ebone_child_tot; i++) { - ebone_iter = ebone_child[i]; for (ebone_iter = ebone_child[i]->parent; ebone_iter; ebone_iter = ebone_iter->parent) { EBONE_TEMP_UINT(ebone_iter) += 1; } @@ -457,7 +456,16 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone eBone->rad_tail = curBone->rad_tail; eBone->segments = curBone->segments; eBone->layer = curBone->layer; - + + eBone->roll1 = curBone->roll1; + eBone->roll2 = curBone->roll2; + eBone->curveInX = curBone->curveInX; + eBone->curveInY = curBone->curveInY; + eBone->curveOutX = curBone->curveOutX; + eBone->curveOutY = curBone->curveOutY; + eBone->scaleIn = curBone->scaleIn; + eBone->scaleOut = curBone->scaleOut; + if (curBone->prop) eBone->prop = IDP_CopyProperty(curBone->prop); @@ -612,7 +620,17 @@ void ED_armature_from_edit(bArmature *arm) newBone->rad_tail = eBone->rad_tail; newBone->segments = eBone->segments; newBone->layer = eBone->layer; - + + newBone->roll1 = eBone->roll1; + newBone->roll2 = eBone->roll2; + newBone->curveInX = eBone->curveInX; + newBone->curveInY = eBone->curveInY; + newBone->curveOutX = eBone->curveOutX; + newBone->curveOutY = eBone->curveOutY; + newBone->scaleIn = eBone->scaleIn; + newBone->scaleOut = eBone->scaleOut; + + if (eBone->prop) newBone->prop = IDP_CopyProperty(eBone->prop); } diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c index 7c09ad49f35..fa7bf6e7ad4 100644 --- a/source/blender/editors/armature/editarmature_retarget.c +++ b/source/blender/editors/armature/editarmature_retarget.c @@ -1451,6 +1451,15 @@ static EditBone *add_editbonetolist(char *name, ListBase *list) bone->segments = 1; bone->layer = 1; //arm->layer; + bone->roll1 = 0.0f; + bone->roll2 = 0.0f; + bone->curveInX = 0.0f; + bone->curveInY = 0.0f; + bone->curveOutX = 0.0f; + bone->curveOutY = 0.0f; + bone->scaleIn = 1.0f; + bone->scaleOut = 1.0f; + return bone; } #endif diff --git a/source/blender/editors/armature/editarmature_sketch.c b/source/blender/editors/armature/editarmature_sketch.c index 87d75aa8fad..5530e293edd 100644 --- a/source/blender/editors/armature/editarmature_sketch.c +++ b/source/blender/editors/armature/editarmature_sketch.c @@ -47,6 +47,7 @@ #include "BIF_generate.h" #include "ED_transform.h" +#include "ED_transform_snap_object_context.h" #include "WM_api.h" #include "WM_types.h" @@ -969,100 +970,30 @@ static int sk_getStrokeSnapPoint(bContext *C, SK_Point *pt, SK_Sketch *sketch, S ToolSettings *ts = CTX_data_tool_settings(C); int point_added = 0; - if (ts->snap_mode == SCE_SNAP_MODE_VOLUME) { - DepthPeel *p1, *p2; - float *last_p = NULL; - float dist = FLT_MAX; - float p[3] = {0}; - float size = 0; - float mvalf[2]; - - BLI_freelistN(&sketch->depth_peels); - BLI_listbase_clear(&sketch->depth_peels); - - mvalf[0] = dd->mval[0]; - mvalf[1] = dd->mval[1]; - peelObjectsContext(C, mvalf, SNAP_ALL, &sketch->depth_peels); - - if (stk->nb_points > 0 && stk->points[stk->nb_points - 1].type == PT_CONTINUOUS) { - last_p = stk->points[stk->nb_points - 1].p; - } - else if (LAST_SNAP_POINT_VALID) { - last_p = LAST_SNAP_POINT; - } - - - for (p1 = sketch->depth_peels.first; p1; p1 = p1->next) { - if (p1->flag == 0) { - float vec[3]; - float new_dist; - float new_size = 0; + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), CTX_data_scene(C), 0, + CTX_wm_region(C), CTX_wm_view3d(C)); - p2 = NULL; - p1->flag = 1; + float mvalf[2] = {UNPACK2(dd->mval)}; + float loc[3], dummy_no[3]; - /* if peeling objects, take the first and last from each object */ - if (ts->snap_flag & SCE_SNAP_PEEL_OBJECT) { - DepthPeel *peel; - for (peel = p1->next; peel; peel = peel->next) { - if (peel->ob == p1->ob) { - peel->flag = 1; - p2 = peel; - } - } - } - /* otherwise, pair first with second and so on */ - else { - for (p2 = p1->next; p2 && p2->ob != p1->ob; p2 = p2->next) { - /* nothing to do here */ - } - } - - if (p2) { - p2->flag = 1; - - add_v3_v3v3(vec, p1->p, p2->p); - mul_v3_fl(vec, 0.5f); - new_size = len_v3v3(p1->p, p2->p); - } - else { - copy_v3_v3(vec, p1->p); - } - - if (last_p == NULL) { - copy_v3_v3(p, vec); - size = new_size; - dist = 0; - break; - } - - new_dist = len_v3v3(last_p, vec); - - if (new_dist < dist) { - copy_v3_v3(p, vec); - dist = new_dist; - size = new_size; - } - } - } - - if (dist != FLT_MAX) { + if (ts->snap_mode == SCE_SNAP_MODE_VOLUME) { + float size; + if (peelObjectsSnapContext( + snap_context, mvalf, SNAP_ALL, + (ts->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, + loc, dummy_no, &size)) + { pt->type = dd->type; pt->mode = PT_SNAP; pt->size = size / 2; - copy_v3_v3(pt->p, p); + copy_v3_v3(pt->p, loc); point_added = 1; } - - //BLI_freelistN(&depth_peels); } else { SK_Stroke *snap_stk; - float vec[3]; - float no[3]; - float mval[2]; - int found = 0; float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here /* snap to strokes */ @@ -1081,23 +1012,28 @@ static int sk_getStrokeSnapPoint(bContext *C, SK_Point *pt, SK_Sketch *sketch, S point_added = 1; } } - - mval[0] = dd->mval[0]; - mval[1] = dd->mval[1]; /* try to snap to closer object */ - found = snapObjectsContext( - C, mval, SNAP_NOT_SELECTED, - vec, no, &dist_px); - if (found == 1) { - pt->type = dd->type; - pt->mode = PT_SNAP; - copy_v3_v3(pt->p, vec); + { + if (ED_transform_snap_object_project_view3d( + snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_NOT_SELECTED, + .snap_to = ts->snap_mode, + }, + mvalf, &dist_px, NULL, + loc, dummy_no)) + { + pt->type = dd->type; + pt->mode = PT_SNAP; + copy_v3_v3(pt->p, loc); - point_added = 1; + point_added = 1; + } } } + ED_transform_snap_object_context_destroy(snap_context); return point_added; } diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index a984e5d1ccd..dca9aa3e446 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -762,7 +762,7 @@ typedef struct tPoseLib_PreviewData { char searchstr[64]; /* (Part of) Name to search for to filter poses that get shown */ char searchold[64]; /* Previously set searchstr (from last loop run), so that we can detected when to rebuild searchp */ - char headerstr[200]; /* Info-text to print in header */ + char headerstr[UI_MAX_DRAW_STR]; /* Info-text to print in header */ } tPoseLib_PreviewData; /* defines for tPoseLib_PreviewData->state values */ @@ -1016,7 +1016,7 @@ static void poselib_preview_apply(bContext *C, wmOperator *op) if (pld->state == PL_PREVIEW_RUNNING) { if (pld->flag & PL_PREVIEW_SHOWORIGINAL) { BLI_strncpy(pld->headerstr, - "PoseLib Previewing Pose: [Showing Original Pose] | Use Tab to start previewing poses again", + IFACE_("PoseLib Previewing Pose: [Showing Original Pose] | Use Tab to start previewing poses again"), sizeof(pld->headerstr)); ED_area_headerprint(pld->sa, pld->headerstr); } @@ -1041,16 +1041,16 @@ static void poselib_preview_apply(bContext *C, wmOperator *op) BLI_strncpy(markern, pld->marker ? pld->marker->name : "No Matches", sizeof(markern)); BLI_snprintf(pld->headerstr, sizeof(pld->headerstr), - "PoseLib Previewing Pose: Filter - [%s] | " - "Current Pose - \"%s\" | " - "Use ScrollWheel or PageUp/Down to change", + IFACE_("PoseLib Previewing Pose: Filter - [%s] | " + "Current Pose - \"%s\" | " + "Use ScrollWheel or PageUp/Down to change"), tempstr, markern); ED_area_headerprint(pld->sa, pld->headerstr); } else { BLI_snprintf(pld->headerstr, sizeof(pld->headerstr), - "PoseLib Previewing Pose: \"%s\" | " - "Use ScrollWheel or PageUp/Down to change", + IFACE_("PoseLib Previewing Pose: \"%s\" | " + "Use ScrollWheel or PageUp/Down to change"), pld->marker->name); ED_area_headerprint(pld->sa, pld->headerstr); } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 16ba1483e38..cd0ea23e2d3 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -51,6 +51,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "UI_interface.h" + #include "ED_armature.h" #include "ED_keyframes_draw.h" #include "ED_markers.h" @@ -301,8 +303,8 @@ static void pose_slide_apply_vec3(tPoseSlideOp *pso, tPChanFCurveLink *pfl, floa MEM_freeN(path); } -/* helper for apply() - perform sliding for custom properties */ -static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) +/* helper for apply() - perform sliding for custom properties or bbone properties */ +static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl, const char prop_prefix[]) { PointerRNA ptr = {{NULL}}; LinkData *ld; @@ -311,8 +313,10 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) /* setup pointer RNA for resolving paths */ RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr); - /* custom properties are just denoted using ["..."][etc.] after the end of the base path, - * so just check for opening pair after the end of the path + /* - custom properties are just denoted using ["..."][etc.] after the end of the base path, + * so just check for opening pair after the end of the path + * - bbone properties are similar, but they always start with a prefix "bbone_*", + * so a similar method should work here for those too */ for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; @@ -326,7 +330,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) * - pPtr is the chunk of the path which is left over */ bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len; - pPtr = strstr(bPtr, "[\""); /* dummy " for texteditor bugs */ + pPtr = strstr(bPtr, prop_prefix); if (pPtr) { /* use RNA to try and get a handle on this property, then, assuming that it is just @@ -515,9 +519,16 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) } } + if (pchan->flag & POSE_BBONE_SHAPE) { + /* bbone properties - they all start a "bbone_" prefix */ + pose_slide_apply_props(pso, pfl, "bbone_"); + } + if (pfl->oldprops) { - /* not strictly a transform, but contributes to the pose produced in many rigs */ - pose_slide_apply_props(pso, pfl); + /* not strictly a transform, but custom properties contribute to the pose produced in many rigs + * (e.g. the facial rigs used in Sintel) + */ + pose_slide_apply_props(pso, pfl, "[\""); /* dummy " for texteditor bugs */ } } @@ -544,7 +555,7 @@ static void pose_slide_reset(tPoseSlideOp *pso) /* draw percentage indicator in header */ static void pose_slide_draw_status(tPoseSlideOp *pso) { - char status_str[256]; + char status_str[UI_MAX_DRAW_STR]; char mode_str[32]; switch (pso->mode) { diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 01e16df9f08..df906a3638a 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -367,10 +367,26 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bo axis_angle_to_quat(pchan->quat, chan->rotAxis, pchan->rotAngle); } + /* B-Bone posing options should also be included... */ + pchan->curveInX = chan->curveInX; + pchan->curveInY = chan->curveInY; + pchan->curveOutX = chan->curveOutX; + pchan->curveOutY = chan->curveOutY; + + pchan->roll1 = chan->roll1; + pchan->roll2 = chan->roll2; + pchan->scaleIn = chan->scaleIn; + pchan->scaleOut = chan->scaleOut; + /* paste flipped pose? */ if (flip) { pchan->loc[0] *= -1; + pchan->curveInX *= -1; + pchan->curveOutX *= -1; + pchan->roll1 *= -1; // XXX? + pchan->roll2 *= -1; // XXX? + /* has to be done as eulers... */ if (pchan->rotmode > 0) { pchan->eul[1] *= -1; @@ -540,6 +556,9 @@ static void pchan_clear_scale(bPoseChannel *pchan) pchan->size[1] = 1.0f; if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0) pchan->size[2] = 1.0f; + + pchan->scaleIn = 1.0f; + pchan->scaleOut = 1.0f; } /* clear location of pose-channel */ @@ -650,6 +669,15 @@ static void pchan_clear_rot(bPoseChannel *pchan) zero_v3(pchan->eul); } } + + /* Clear also Bendy Bone stuff - Roll is obvious, but Curve X/Y stuff is also kindof rotational in nature... */ + pchan->roll1 = 0.0f; + pchan->roll2 = 0.0f; + + pchan->curveInX = 0.0f; + pchan->curveInY = 0.0f; + pchan->curveOutX = 0.0f; + pchan->curveOutY = 0.0f; } /* clear loc/rot/scale of pose-channel */ diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index 2ba1eedd33b..b960bec3603 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -71,7 +71,7 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a ListBase curves = {NULL, NULL}; int transFlags = action_get_item_transforms(act, ob, pchan, &curves); - pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE); + pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); /* check if any transforms found... */ if (transFlags) { @@ -96,6 +96,8 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a pchan->flag |= POSE_ROT; if (transFlags & ACT_TRANS_SCALE) pchan->flag |= POSE_SIZE; + if (transFlags & ACT_TRANS_BBONE) + pchan->flag |= POSE_BBONE_SHAPE; /* store current transforms */ copy_v3_v3(pfl->oldloc, pchan->loc); @@ -105,6 +107,16 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a copy_v3_v3(pfl->oldaxis, pchan->rotAxis); pfl->oldangle = pchan->rotAngle; + /* store current bbone values */ + pfl->roll1 = pchan->roll1; + pfl->roll2 = pchan->roll2; + pfl->curveInX = pchan->curveInX; + pfl->curveInY = pchan->curveInY; + pfl->curveOutX = pchan->curveOutX; + pfl->curveOutY = pchan->curveOutY; + pfl->scaleIn = pchan->scaleIn; + pfl->scaleOut = pchan->scaleOut; + /* make copy of custom properties */ if (pchan->prop && (transFlags & ACT_TRANS_PROP)) pfl->oldprops = IDP_CopyProperty(pchan->prop); @@ -133,6 +145,7 @@ void poseAnim_mapping_get(bContext *C, ListBase *pfLinks, Object *ob, bAction *a fcurves_to_pchan_links_get(pfLinks, ob, act, pchan); } CTX_DATA_END; + } } @@ -199,6 +212,16 @@ void poseAnim_mapping_reset(ListBase *pfLinks) copy_v3_v3(pchan->rotAxis, pfl->oldaxis); pchan->rotAngle = pfl->oldangle; + /* store current bbone values */ + pchan->roll1 = pfl->roll1; + pchan->roll2 = pfl->roll2; + pchan->curveInX = pfl->curveInX; + pchan->curveInY = pfl->curveInY; + pchan->curveOutX = pfl->curveOutX; + pchan->curveOutY = pfl->curveOutY; + pchan->scaleIn = pfl->scaleIn; + pchan->scaleOut = pfl->scaleOut; + /* just overwrite values of properties from the stored copies (there should be some) */ if (pfl->oldprops) IDP_SyncGroupValues(pfl->pchan->prop, pfl->oldprops); diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt index 83346e9550c..ebdf6bb43ff 100644 --- a/source/blender/editors/curve/CMakeLists.txt +++ b/source/blender/editors/curve/CMakeLists.txt @@ -23,20 +23,24 @@ set(INC ../../blenkernel ../../blenlib ../../blentranslation + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx + ../../../../extern/curve_fit_nd ) set(INC_SYS - + ${GLEW_INCLUDE_PATH} ) set(SRC curve_ops.c editcurve.c editcurve_add.c + editcurve_paint.c editcurve_select.c editfont.c @@ -47,4 +51,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h index 1af0b49bd86..856573ffab0 100644 --- a/source/blender/editors/curve/curve_intern.h +++ b/source/blender/editors/curve/curve_intern.h @@ -102,6 +102,7 @@ void CURVE_OT_separate(struct wmOperatorType *ot); void CURVE_OT_split(struct wmOperatorType *ot); void CURVE_OT_duplicate(struct wmOperatorType *ot); void CURVE_OT_delete(struct wmOperatorType *ot); +void CURVE_OT_dissolve_verts(struct wmOperatorType *ot); void CURVE_OT_spline_type_set(struct wmOperatorType *ot); void CURVE_OT_radius_set(struct wmOperatorType *ot); @@ -166,4 +167,7 @@ void SURFACE_OT_primitive_nurbs_surface_cylinder_add(struct wmOperatorType *ot); void SURFACE_OT_primitive_nurbs_surface_sphere_add(struct wmOperatorType *ot); void SURFACE_OT_primitive_nurbs_surface_torus_add(struct wmOperatorType *ot); +/* editcurve_paint.c */ +void CURVE_OT_draw(struct wmOperatorType *ot); + #endif /* __CURVE_INTERN_H__ */ diff --git a/source/blender/editors/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c index 967187e66c9..fce6425b9be 100644 --- a/source/blender/editors/curve/curve_ops.c +++ b/source/blender/editors/curve/curve_ops.c @@ -87,6 +87,7 @@ void ED_operatortypes_curve(void) WM_operatortype_append(CURVE_OT_split); WM_operatortype_append(CURVE_OT_duplicate); WM_operatortype_append(CURVE_OT_delete); + WM_operatortype_append(CURVE_OT_dissolve_verts); WM_operatortype_append(CURVE_OT_spline_type_set); WM_operatortype_append(CURVE_OT_radius_set); @@ -135,6 +136,7 @@ void ED_operatortypes_curve(void) WM_operatortype_append(CURVE_OT_make_segment); WM_operatortype_append(CURVE_OT_spin); WM_operatortype_append(CURVE_OT_vertex_add); + WM_operatortype_append(CURVE_OT_draw); WM_operatortype_append(CURVE_OT_extrude); WM_operatortype_append(CURVE_OT_cyclic_toggle); @@ -234,6 +236,9 @@ void ED_keymap_curve(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "CURVE_OT_draw", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", AKEY, KM_PRESS, 0, 0); RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE); kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0); @@ -258,8 +263,12 @@ void ED_keymap_curve(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "CURVE_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "CURVE_OT_make_segment", FKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "CURVE_OT_cyclic_toggle", CKEY, KM_PRESS, KM_ALT, 0); - WM_keymap_add_item(keymap, "CURVE_OT_delete", XKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "CURVE_OT_delete", DELKEY, KM_PRESS, 0, 0); + + WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_curve_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_curve_delete", DELKEY, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "CURVE_OT_dissolve_verts", XKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "CURVE_OT_dissolve_verts", DELKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "CURVE_OT_tilt_clear", TKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "TRANSFORM_OT_tilt", TKEY, KM_PRESS, KM_CTRL, 0); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 77fc7a83256..18fdcb546b0 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -36,6 +36,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_array_utils.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_ghash.h" @@ -63,6 +64,7 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_transform.h" +#include "ED_transform_snap_object_context.h" #include "ED_types.h" #include "ED_util.h" #include "ED_view3d.h" @@ -70,6 +72,8 @@ #include "curve_intern.h" +#include "curve_fit_nd.h" + #include "UI_interface.h" #include "UI_resources.h" @@ -1390,7 +1394,9 @@ void CURVE_OT_split(wmOperatorType *ot) /* ******************* FLAGS ********************* */ -static short isNurbselUV(Nurb *nu, int *u, int *v, int flag) +static bool isNurbselUV( + const Nurb *nu, int flag, + int *r_u, int *r_v) { /* return (u != -1): 1 row in u-direction selected. U has value between 0-pntsv * return (v != -1): 1 column in v-direction selected. V has value between 0-pntsu @@ -1398,7 +1404,7 @@ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag) BPoint *bp; int a, b, sel; - *u = *v = -1; + *r_u = *r_v = -1; bp = nu->bp; for (b = 0; b < nu->pntsv; b++) { @@ -1407,7 +1413,7 @@ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag) if (bp->f1 & flag) sel++; } if (sel == nu->pntsu) { - if (*u == -1) *u = b; + if (*r_u == -1) *r_u = b; else return 0; } else if (sel > 1) { @@ -1422,7 +1428,7 @@ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag) if (bp->f1 & flag) sel++; } if (sel == nu->pntsv) { - if (*v == -1) *v = a; + if (*r_v == -1) *r_v = a; else return 0; } else if (sel > 1) { @@ -1430,8 +1436,8 @@ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag) } } - if (*u == -1 && *v > -1) return 1; - if (*v == -1 && *u > -1) return 1; + if (*r_u == -1 && *r_v > -1) return 1; + if (*r_v == -1 && *r_u > -1) return 1; return 0; } @@ -1851,7 +1857,7 @@ bool ed_editnurb_extrude_flag(EditNurb *editnurb, const short flag) else { /* which row or column is selected */ - if (isNurbselUV(nu, &u, &v, flag)) { + if (isNurbselUV(nu, flag, &u, &v)) { /* deselect all */ bp = nu->bp; @@ -4991,11 +4997,22 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (use_proj) { const float mval[2] = {UNPACK2(event->mval)}; - float no_dummy[3]; - float dist_px_dummy; - snapObjectsContext( - C, mval, SNAP_NOT_OBEDIT, - location, no_dummy, &dist_px_dummy); + + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), vc.scene, 0, + vc.ar, vc.v3d); + + ED_transform_snap_object_project_view3d_mixed( + snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_NOT_OBEDIT, + .snap_to_flag = SCE_SELECT_FACE, + }, + mval, NULL, true, + location, NULL); + + + ED_transform_snap_object_context_destroy(snap_context); } if ((cu->flag & CU_3D) == 0) { @@ -5763,6 +5780,115 @@ void CURVE_OT_delete(wmOperatorType *ot) ot->prop = prop; } +static bool test_bezt_is_sel_any(const void *bezt_v, void *user_data) +{ + Curve *cu = user_data; + const BezTriple *bezt = bezt_v; + return BEZT_ISSEL_ANY_HIDDENHANDLES(cu, bezt); +} + +static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *obedit = CTX_data_edit_object(C); + Curve *cu = (Curve *)obedit->data; + + { + ListBase *editnurb = object_editcurve_get(obedit); + Nurb *nu; + + for (nu = editnurb->first; nu; nu = nu->next) { + if ((nu->type == CU_BEZIER) && (nu->pntsu > 2)) { + unsigned int span_step[2] = {nu->pntsu, nu->pntsu}; + unsigned int span_len; + + while (BLI_array_iter_span( + nu->bezt, nu->pntsu, + (nu->flagu & CU_NURB_CYCLIC) != 0, false, + test_bezt_is_sel_any, cu, + span_step, &span_len)) + { + BezTriple *bezt_prev = &nu->bezt[mod_i(span_step[0] - 1, nu->pntsu)]; + BezTriple *bezt_next = &nu->bezt[mod_i(span_step[1] + 1, nu->pntsu)]; + + int i_span_edge_len = span_len + 1; + const unsigned int dims = 3; + + const unsigned int points_len = ((cu->resolu - 1) * i_span_edge_len) + 1; + float *points = MEM_mallocN(points_len * dims * sizeof(float), __func__); + float *points_stride = points; + const int points_stride_len = (cu->resolu - 1); + + for (int segment = 0; segment < i_span_edge_len; segment++) { + BezTriple *bezt_a = &nu->bezt[mod_i((span_step[0] + segment) - 1, nu->pntsu)]; + BezTriple *bezt_b = &nu->bezt[mod_i((span_step[0] + segment), nu->pntsu)]; + + for (int axis = 0; axis < dims; axis++) { + BKE_curve_forward_diff_bezier( + bezt_a->vec[1][axis], bezt_a->vec[2][axis], + bezt_b->vec[0][axis], bezt_b->vec[1][axis], + points_stride + axis, points_stride_len, dims * sizeof(float)); + } + + points_stride += dims * points_stride_len; + } + + BLI_assert(points_stride + dims == points + (points_len * dims)); + + float tan_l[3], tan_r[3], error_sq_dummy; + + sub_v3_v3v3(tan_l, bezt_prev->vec[1], bezt_prev->vec[2]); + normalize_v3(tan_l); + sub_v3_v3v3(tan_r, bezt_next->vec[0], bezt_next->vec[1]); + normalize_v3(tan_r); + + curve_fit_cubic_to_points_single_fl( + points, points_len, dims, FLT_EPSILON, + tan_l, tan_r, + bezt_prev->vec[2], bezt_next->vec[0], + &error_sq_dummy); + + if (!ELEM(bezt_prev->h2, HD_FREE, HD_ALIGN)) { + bezt_prev->h2 = (bezt_prev->h2 == HD_VECT) ? HD_FREE : HD_ALIGN; + } + if (!ELEM(bezt_next->h1, HD_FREE, HD_ALIGN)) { + bezt_next->h1 = (bezt_next->h1 == HD_VECT) ? HD_FREE : HD_ALIGN; + } + + MEM_freeN(points); + } + } + } + } + + ed_curve_delete_selected(obedit); + + { + cu->actnu = cu->actvert = CU_ACT_NONE; + + if (ED_curve_updateAnimPaths(obedit->data)) WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit); + + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + DAG_id_tag_update(obedit->data, 0); + } + + return OPERATOR_FINISHED; +} + +void CURVE_OT_dissolve_verts(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Dissolve Vertices"; + ot->description = "Delete selected control points, correcting surrounding handles"; + ot->idname = "CURVE_OT_dissolve_verts"; + + /* api callbacks */ + ot->exec = curve_dissolve_exec; + ot->poll = ED_operator_editcurve; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /********************** shade smooth/flat operator *********************/ static int shade_smooth_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c new file mode 100644 index 00000000000..bb7cc61f580 --- /dev/null +++ b/source/blender/editors/curve/editcurve_paint.c @@ -0,0 +1,1234 @@ +/* + * ***** 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/editors/curve/editcurve_paint.c + * \ingroup edcurve + */ + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_mempool.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_space_api.h" +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_curve.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "curve_intern.h" + +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#define USE_SPLINE_FIT + +#ifdef USE_SPLINE_FIT +#include "curve_fit_nd.h" +#endif + +/* Distance between input samples */ +#define STROKE_SAMPLE_DIST_MIN_PX 3 +#define STROKE_SAMPLE_DIST_MAX_PX 6 + + +/* -------------------------------------------------------------------- */ + +/** \name Depth Utilities + * \{ */ + + +static float depth_read_zbuf(const ViewContext *vc, int x, int y) +{ + ViewDepths *vd = vc->rv3d->depths; + + if (vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h) + return vd->depths[y * vd->w + x]; + else + return -1.0f; +} + +static bool depth_unproject( + const ARegion *ar, const bglMats *mats, + const int mval[2], const double depth, + float r_location_world[3]) +{ + double p[3]; + if (gluUnProject( + (double)ar->winrct.xmin + mval[0] + 0.5, + (double)ar->winrct.ymin + mval[1] + 0.5, + depth, mats->modelview, mats->projection, (const GLint *)mats->viewport, + &p[0], &p[1], &p[2])) + { + copy_v3fl_v3db(r_location_world, p); + return true; + } + return false; +} + +static bool depth_read_normal( + const ViewContext *vc, const bglMats *mats, const int mval[2], + float r_normal[3]) +{ + /* pixels surrounding */ + bool depths_valid[9] = {false}; + float coords[9][3] = {{0}}; + + ARegion *ar = vc->ar; + const ViewDepths *depths = vc->rv3d->depths; + + for (int x = 0, i = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + const int mval_ofs[2] = {mval[0] + (x - 1), mval[1] + (y - 1)}; + + const double depth = (double)depth_read_zbuf(vc, mval_ofs[0], mval_ofs[1]); + if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { + if (depth_unproject(ar, mats, mval_ofs, depth, coords[i])) { + depths_valid[i] = true; + } + } + i++; + } + } + + const int edges[2][6][2] = { + /* x edges */ + {{0, 1}, {1, 2}, + {3, 4}, {4, 5}, + {6, 7}, {7, 8}}, + /* y edges */ + {{0, 3}, {3, 6}, + {1, 4}, {4, 7}, + {2, 5}, {5, 8}}, + }; + + float cross[2][3] = {{0.0f}}; + + for (int i = 0; i < 6; i++) { + for (int axis = 0; axis < 2; axis++) { + if (depths_valid[edges[axis][i][0]] && depths_valid[edges[axis][i][1]]) { + float delta[3]; + sub_v3_v3v3(delta, coords[edges[axis][i][0]], coords[edges[axis][i][1]]); + add_v3_v3(cross[axis], delta); + } + } + } + + cross_v3_v3v3(r_normal, cross[0], cross[1]); + + if (normalize_v3(r_normal) != 0.0f) { + return true; + } + else { + return false; + } +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name StrokeElem / #RNA_OperatorStrokeElement Conversion Functions + * \{ */ + +struct StrokeElem { + float mval[2]; + float location_world[3]; + float location_local[3]; + + /* surface normal, may be zero'd */ + float normal_world[3]; + float normal_local[3]; + + float pressure; +}; + +struct CurveDrawData { + short init_event_type; + short curve_type; + + /* projecting 2D into 3D space */ + struct { + /* use a plane or project to the surface */ + bool use_plane; + float plane[4]; + + /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but thats OK. */ + bool use_depth; + + /* offset projection by this value */ + bool use_offset; + float offset[3]; /* worldspace */ + float surface_offset; + bool use_surface_offset_absolute; + } project; + + /* cursor sampling */ + struct { + /* use substeps, needed for nicely interpolating depth */ + bool use_substeps; + } sample; + + struct { + float min, max, range; + } radius; + + struct { + float mouse[2]; + /* used incase we can't calculate the depth */ + float location_world[3]; + + float location_world_valid[3]; + + const struct StrokeElem *selem; + } prev; + + ViewContext vc; + bglMats mats; + enum { + CURVE_DRAW_IDLE = 0, + CURVE_DRAW_PAINTING = 1, + } state; + + /* StrokeElem */ + BLI_mempool *stroke_elem_pool; + + void *draw_handle_view; +}; + +static float stroke_elem_radius_from_pressure(const struct CurveDrawData *cdd, const float pressure) +{ + const Curve *cu = cdd->vc.obedit->data; + return ((pressure * cdd->radius.range) + cdd->radius.min) * cu->ext2; +} + +static float stroke_elem_radius(const struct CurveDrawData *cdd, const struct StrokeElem *selem) +{ + return stroke_elem_radius_from_pressure(cdd, selem->pressure); +} + +static void stroke_elem_pressure_set(const struct CurveDrawData *cdd, struct StrokeElem *selem, float pressure) +{ + if ((cdd->project.surface_offset != 0.0f) && + !cdd->project.use_surface_offset_absolute && + !is_zero_v3(selem->normal_local)) + { + const float adjust = stroke_elem_radius_from_pressure(cdd, pressure) - + stroke_elem_radius_from_pressure(cdd, selem->pressure); + madd_v3_v3fl(selem->location_local, selem->normal_local, adjust); + mul_v3_m4v3(selem->location_world, cdd->vc.obedit->obmat, selem->location_local); + } + selem->pressure = pressure; +} + +static void stroke_elem_interp( + struct StrokeElem *selem_out, + const struct StrokeElem *selem_a, const struct StrokeElem *selem_b, float t) +{ + interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t); + interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t); + interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t); + selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t); +} + + +/** + * Sets the depth from #StrokeElem.mval + */ +static bool stroke_elem_project( + const struct CurveDrawData *cdd, + const int mval_i[2], const float mval_fl[2], + float surface_offset, const float radius, + float r_location_world[3], float r_normal_world[3]) +{ + View3D *v3d = cdd->vc.v3d; + ARegion *ar = cdd->vc.ar; + RegionView3D *rv3d = cdd->vc.rv3d; + + bool is_location_world_set = false; + + /* project to 'location_world' */ + if (cdd->project.use_plane) { + /* get the view vector to 'location' */ + float ray_origin[3], ray_direction[3]; + ED_view3d_win_to_ray(cdd->vc.ar, v3d, mval_fl, ray_origin, ray_direction, false); + + float lambda; + if (isect_ray_plane_v3(ray_origin, ray_direction, cdd->project.plane, &lambda, true)) { + madd_v3_v3v3fl(r_location_world, ray_origin, ray_direction, lambda); + if (r_normal_world) { + zero_v3(r_normal_world); + } + is_location_world_set = true; + } + } + else { + const ViewDepths *depths = rv3d->depths; + if (depths && + ((unsigned int)mval_i[0] < depths->w) && + ((unsigned int)mval_i[1] < depths->h)) + { + const double depth = (double)depth_read_zbuf(&cdd->vc, mval_i[0], mval_i[1]); + if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) { + if (depth_unproject(ar, &cdd->mats, mval_i, depth, r_location_world)) { + is_location_world_set = true; + if (r_normal_world) { + zero_v3(r_normal_world); + } + + if (surface_offset != 0.0f) { + const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius; + float normal[3]; + if (depth_read_normal(&cdd->vc, &cdd->mats, mval_i, normal)) { + madd_v3_v3fl(r_location_world, normal, offset * surface_offset); + if (r_normal_world) { + copy_v3_v3(r_normal_world, normal); + } + } + } + } + } + } + } + + if (is_location_world_set) { + if (cdd->project.use_offset) { + add_v3_v3(r_location_world, cdd->project.offset); + } + } + + return is_location_world_set; +} + +static bool stroke_elem_project_fallback( + const struct CurveDrawData *cdd, + const int mval_i[2], const float mval_fl[2], + const float surface_offset, const float radius, + const float location_fallback_depth[3], + float r_location_world[3], float r_location_local[3], + float r_normal_world[3], float r_normal_local[3]) +{ + bool is_depth_found = stroke_elem_project( + cdd, mval_i, mval_fl, + surface_offset, radius, + r_location_world, r_normal_world); + if (is_depth_found == false) { + ED_view3d_win_to_3d(cdd->vc.ar, location_fallback_depth, mval_fl, r_location_world); + zero_v3(r_normal_local); + } + mul_v3_m4v3(r_location_local, cdd->vc.obedit->imat, r_location_world); + + if (!is_zero_v3(r_normal_world)) { + copy_v3_v3(r_normal_local, r_normal_world); + mul_transposed_mat3_m4_v3(cdd->vc.obedit->obmat, r_normal_local); + normalize_v3(r_normal_local); + } + else { + zero_v3(r_normal_local); + } + + return is_depth_found; +} + +/** + * \note #StrokeElem.mval & #StrokeElem.pressure must be set first. + */ +static bool stroke_elem_project_fallback_elem( + const struct CurveDrawData *cdd, + const float location_fallback_depth[3], + struct StrokeElem *selem) +{ + const int mval_i[2] = {UNPACK2(selem->mval)}; + const float radius = stroke_elem_radius(cdd, selem); + return stroke_elem_project_fallback( + cdd, mval_i, selem->mval, + cdd->project.surface_offset, radius, + location_fallback_depth, + selem->location_world, selem->location_local, + selem->normal_world, selem->normal_local); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Operator/Stroke Conversion + * \{ */ + +static void curve_draw_stroke_to_operator_elem( + wmOperator *op, const struct StrokeElem *selem) +{ + PointerRNA itemptr; + RNA_collection_add(op->ptr, "stroke", &itemptr); + + RNA_float_set_array(&itemptr, "mouse", selem->mval); + RNA_float_set_array(&itemptr, "location", selem->location_world); + RNA_float_set(&itemptr, "pressure", selem->pressure); +} + +static void curve_draw_stroke_from_operator_elem( + wmOperator *op, PointerRNA *itemptr) +{ + struct CurveDrawData *cdd = op->customdata; + + struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool); + + RNA_float_get_array(itemptr, "mouse", selem->mval); + RNA_float_get_array(itemptr, "location", selem->location_world); + mul_v3_m4v3(selem->location_local, cdd->vc.obedit->imat, selem->location_world); + selem->pressure = RNA_float_get(itemptr, "pressure"); +} + +static void curve_draw_stroke_to_operator(wmOperator *op) +{ + struct CurveDrawData *cdd = op->customdata; + + BLI_mempool_iter iter; + const struct StrokeElem *selem; + + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) { + curve_draw_stroke_to_operator_elem(op, selem); + } +} + +static void curve_draw_stroke_from_operator(wmOperator *op) +{ + RNA_BEGIN (op->ptr, itemptr, "stroke") + { + curve_draw_stroke_from_operator_elem(op, &itemptr); + } + RNA_END; +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Operator Callbacks & Helpers + * \{ */ + +static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +{ + wmOperator *op = arg; + struct CurveDrawData *cdd = op->customdata; + + const int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool); + + if (stroke_len == 0) { + return; + } + + View3D *v3d = cdd->vc.v3d; + Object *obedit = cdd->vc.obedit; + Curve *cu = obedit->data; + + UI_ThemeColor(TH_WIRE); + + if (cu->ext2 > 0.0f) { + GLUquadricObj *qobj = gluNewQuadric(); + + gluQuadricDrawStyle(qobj, GLU_FILL); + + BLI_mempool_iter iter; + const struct StrokeElem *selem; + + const float location_zero[3] = {0}; + const float *location_prev = location_zero; + + /* scale to edit-mode space */ + glPushMatrix(); + glMultMatrixf(obedit->obmat); + + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) { + glTranslatef( + selem->location_local[0] - location_prev[0], + selem->location_local[1] - location_prev[1], + selem->location_local[2] - location_prev[2]); + location_prev = selem->location_local; + const float radius = stroke_elem_radius(cdd, selem); + gluSphere(qobj, radius, 12, 8); + + location_prev = selem->location_local; + } + + glPopMatrix(); + + gluDeleteQuadric(qobj); + } + + if (stroke_len > 1) { + float (*coord_array)[3] = MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__); + + { + BLI_mempool_iter iter; + const struct StrokeElem *selem; + int i; + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + for (selem = BLI_mempool_iterstep(&iter), i = 0; selem; selem = BLI_mempool_iterstep(&iter), i++) { + copy_v3_v3(coord_array[i], selem->location_world); + } + } + + { + glEnable(GL_BLEND); + glEnable(GL_LINE_SMOOTH); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, coord_array); + + cpack(0x0); + glLineWidth(3.0f); + glDrawArrays(GL_LINE_STRIP, 0, stroke_len); + + if (v3d->zbuf) + glDisable(GL_DEPTH_TEST); + + cpack(0xffffffff); + glLineWidth(1.0f); + glDrawArrays(GL_LINE_STRIP, 0, stroke_len); + + if (v3d->zbuf) + glEnable(GL_DEPTH_TEST); + + glDisableClientState(GL_VERTEX_ARRAY); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } + + MEM_freeN(coord_array); + } +} + +static void curve_draw_event_add(wmOperator *op, const wmEvent *event) +{ + struct CurveDrawData *cdd = op->customdata; + Object *obedit = cdd->vc.obedit; + + invert_m4_m4(obedit->imat, obedit->obmat); + + struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool); + + ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]); + + /* handle pressure sensitivity (which is supplied by tablets) */ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + selem->pressure = wmtab->Pressure; + } + else { + selem->pressure = 1.0f; + } + + bool is_depth_found = stroke_elem_project_fallback_elem( + cdd, cdd->prev.location_world_valid, selem); + + if (is_depth_found) { + /* use the depth if a fallback wasn't used */ + copy_v3_v3(cdd->prev.location_world_valid, selem->location_world); + } + copy_v3_v3(cdd->prev.location_world, selem->location_world); + + float len_sq = len_squared_v2v2(cdd->prev.mouse, selem->mval); + copy_v2_v2(cdd->prev.mouse, selem->mval); + + if (cdd->sample.use_substeps && cdd->prev.selem) { + const struct StrokeElem selem_target = *selem; + struct StrokeElem *selem_new_last = selem; + if (len_sq >= SQUARE(STROKE_SAMPLE_DIST_MAX_PX)) { + int n = (int)ceil(sqrt((double)len_sq)) / STROKE_SAMPLE_DIST_MAX_PX ; + + for (int i = 1; i < n; i++) { + struct StrokeElem *selem_new = selem_new_last; + stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, (float)i / n); + + const bool is_depth_found_substep = stroke_elem_project_fallback_elem( + cdd, cdd->prev.location_world_valid, selem_new); + if (is_depth_found == false) { + if (is_depth_found_substep) { + copy_v3_v3(cdd->prev.location_world_valid, selem_new->location_world); + } + } + + selem_new_last = BLI_mempool_calloc(cdd->stroke_elem_pool); + } + } + selem = selem_new_last; + *selem_new_last = selem_target; + } + + cdd->prev.selem = selem; + + ED_region_tag_redraw(cdd->vc.ar); +} + +static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event) +{ + struct CurveDrawData *cdd = op->customdata; + const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings; + + /* add first point */ + curve_draw_event_add(op, event); + + if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && cdd->project.use_depth && + (cps->flag & CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS)) + { + RegionView3D *rv3d = cdd->vc.rv3d; + + cdd->project.use_depth = false; + cdd->project.use_plane = true; + + float normal[3] = {0.0f}; + if (ELEM(cps->surface_plane, + CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW, + CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE)) + { + if (depth_read_normal(&cdd->vc, &cdd->mats, event->mval, normal)) { + if (cps->surface_plane == CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW) { + float cross_a[3], cross_b[3]; + cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal); + cross_v3_v3v3(cross_b, normal, cross_a); + copy_v3_v3(normal, cross_b); + } + } + } + + /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */ + if (is_zero_v3(normal)) { + copy_v3_v3(normal, rv3d->viewinv[2]); + } + + normalize_v3_v3(cdd->project.plane, normal); + cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, cdd->prev.location_world_valid); + + /* Special case for when we only have offset applied on the first-hit, + * the remaining stroke must be offset too. */ + if (cdd->project.surface_offset != 0.0f) { + const float mval_fl[2] = {UNPACK2(event->mval)}; + + float location_no_offset[3]; + + if (stroke_elem_project( + cdd, event->mval, mval_fl, 0.0f, 0.0f, + location_no_offset, NULL)) + { + sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset); + if (!is_zero_v3(cdd->project.offset)) { + cdd->project.use_offset = true; + } + } + } + /* end special case */ + + } + + cdd->init_event_type = event->type; + cdd->state = CURVE_DRAW_PAINTING; +} + +static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke) +{ + BLI_assert(op->customdata == NULL); + + struct CurveDrawData *cdd = MEM_callocN(sizeof(*cdd), __func__); + + if (is_invoke) { + view3d_set_viewcontext(C, &cdd->vc); + if (ELEM(NULL, cdd->vc.ar, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) { + MEM_freeN(cdd); + BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport"); + return false; + } + } + else { + cdd->vc.scene = CTX_data_scene(C); + cdd->vc.obedit = CTX_data_edit_object(C); + } + + op->customdata = cdd; + + const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings; + + cdd->curve_type = cps->curve_type; + + cdd->radius.min = cps->radius_min; + cdd->radius.max = cps->radius_max; + cdd->radius.range = cps->radius_max - cps->radius_min; + cdd->project.surface_offset = cps->surface_offset; + cdd->project.use_surface_offset_absolute = (cps->flag & CURVE_PAINT_FLAG_DEPTH_STROKE_OFFSET_ABS) != 0; + + cdd->stroke_elem_pool = BLI_mempool_create( + sizeof(struct StrokeElem), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + + return true; +} + + +static void curve_draw_exit(wmOperator *op) +{ + struct CurveDrawData *cdd = op->customdata; + if (cdd) { + if (cdd->draw_handle_view) { + ED_region_draw_cb_exit(cdd->vc.ar->type, cdd->draw_handle_view); + WM_cursor_modal_restore(cdd->vc.win); + } + + if (cdd->stroke_elem_pool) { + BLI_mempool_destroy(cdd->stroke_elem_pool); + } + + MEM_freeN(cdd); + op->customdata = NULL; + } +} + +/** + * Initialize values before calling 'exec' (when running interactively). + */ +static void curve_draw_exec_precalc(wmOperator *op) +{ + struct CurveDrawData *cdd = op->customdata; + const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings; + PropertyRNA *prop; + + prop = RNA_struct_find_property(op->ptr, "corner_angle"); + if (!RNA_property_is_set(op->ptr, prop)) { + const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle : (float)M_PI; + RNA_property_float_set(op->ptr, prop, corner_angle); + } + + prop = RNA_struct_find_property(op->ptr, "error_threshold"); + if (!RNA_property_is_set(op->ptr, prop)) { + + /* error isnt set so we'll have to calculate it from the pixel values */ + BLI_mempool_iter iter; + const struct StrokeElem *selem, *selem_prev; + + float len_3d = 0.0f, len_2d = 0.0f; + float scale_px; /* pixel to local space scale */ + + int i = 0; + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + selem_prev = BLI_mempool_iterstep(&iter); + for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) { + len_3d += len_v3v3(selem->location_local, selem_prev->location_local); + len_2d += len_v2v2(selem->mval, selem_prev->mval); + selem_prev = selem; + } + scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f; + float error_threshold = (cps->error_threshold * U.pixelsize) * scale_px; + RNA_property_float_set(op->ptr, prop, error_threshold); + } + + if ((cps->radius_taper_start != 0.0f) || + (cps->radius_taper_end != 0.0f)) + { + /* note, we could try to de-duplicate the length calculations above */ + const int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool); + + BLI_mempool_iter iter; + struct StrokeElem *selem, *selem_prev; + + float *lengths = MEM_mallocN(sizeof(float) * stroke_len, __func__); + struct StrokeElem **selem_array = MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__); + lengths[0] = 0.0f; + + float len_3d = 0.0f; + + int i = 1; + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + selem_prev = BLI_mempool_iterstep(&iter); + selem_array[0] = selem_prev; + for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) { + const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local); + len_3d += len_3d_segment; + lengths[i] = len_3d; + selem_array[i] = selem; + selem_prev = selem; + } + + if (cps->radius_taper_start != 0.0f) { + const float len_taper_max = cps->radius_taper_start * len_3d; + for (i = 0; i < stroke_len && lengths[i] < len_taper_max; i++) { + const float pressure_new = selem_array[i]->pressure * (lengths[i] / len_taper_max); + stroke_elem_pressure_set(cdd, selem_array[i], pressure_new); + } + } + + if (cps->radius_taper_end != 0.0f) { + const float len_taper_max = cps->radius_taper_end * len_3d; + const float len_taper_min = len_3d - len_taper_max; + for (i = stroke_len - 1; i > 0 && lengths[i] > len_taper_min; i--) { + const float pressure_new = selem_array[i]->pressure * ((len_3d - lengths[i]) / len_taper_max); + stroke_elem_pressure_set(cdd, selem_array[i], pressure_new); + } + } + + MEM_freeN(lengths); + MEM_freeN(selem_array); + } +} + +static int curve_draw_exec(bContext *C, wmOperator *op) +{ + if (op->customdata == NULL) { + if (!curve_draw_init(C, op, false)) { + return OPERATOR_CANCELLED; + } + } + + struct CurveDrawData *cdd = op->customdata; + + const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings; + Object *obedit = cdd->vc.scene->obedit; + Curve *cu = obedit->data; + ListBase *nurblist = object_editcurve_get(obedit); + + int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool); + + const bool is_3d = (cu->flag & CU_3D) != 0; + invert_m4_m4(obedit->imat, obedit->obmat); + + if (BLI_mempool_count(cdd->stroke_elem_pool) == 0) { + curve_draw_stroke_from_operator(op); + stroke_len = BLI_mempool_count(cdd->stroke_elem_pool); + } + + ED_curve_deselect_all(cu->editnurb); + + const float radius_min = cps->radius_min; + const float radius_max = cps->radius_max; + const float radius_range = cps->radius_max - cps->radius_min; + + Nurb *nu = MEM_callocN(sizeof(Nurb), __func__); + nu->pntsv = 1; + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + nu->flag |= CU_SMOOTH; + + const bool use_pressure_radius = + (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) || + ((cps->radius_taper_start != 0.0f) || + (cps->radius_taper_end != 0.0f)); + + if (cdd->curve_type == CU_BEZIER) { + nu->type = CU_BEZIER; + +#ifdef USE_SPLINE_FIT + + /* Allow to interpolate multiple channels */ + int dims = 3; + struct { + int radius; + } coords_indices; + coords_indices.radius = use_pressure_radius ? dims++ : -1; + + float *coords = MEM_mallocN(sizeof(*coords) * stroke_len * dims, __func__); + + float *cubic_spline = NULL; + unsigned int cubic_spline_len = 0; + + /* error in object local space */ + const float error_threshold = RNA_float_get(op->ptr, "error_threshold"); + const float corner_angle = RNA_float_get(op->ptr, "corner_angle"); + + { + BLI_mempool_iter iter; + const struct StrokeElem *selem; + float *co = coords; + + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), co += dims) { + copy_v3_v3(co, selem->location_local); + if (coords_indices.radius != -1) { + co[coords_indices.radius] = selem->pressure; + } + + /* remove doubles */ + if ((co != coords) && UNLIKELY(memcmp(co, co - dims, sizeof(float) * dims) == 0)) { + co -= dims; + stroke_len--; + } + } + } + + unsigned int *corners = NULL; + unsigned int corners_len = 0; + + if (corner_angle < (float)M_PI) { + /* this could be configurable... */ + const float corner_radius_min = error_threshold / 8; + const float corner_radius_max = error_threshold * 2; + const unsigned int samples_max = 16; + + curve_fit_corners_detect_fl( + (const float *)coords, stroke_len, dims, + corner_radius_min, corner_radius_max, + samples_max, corner_angle, + &corners, &corners_len); + } + + unsigned int *corners_index = NULL; + unsigned int corners_index_len = 0; + + const int result = curve_fit_cubic_to_points_fl( + coords, stroke_len, dims, error_threshold, + corners, corners_len, + &cubic_spline, &cubic_spline_len, + NULL, + &corners_index, &corners_index_len); + + MEM_freeN(coords); + if (corners) { + free(corners); + } + + if (result == 0) { + nu->pntsu = cubic_spline_len; + nu->bezt = MEM_callocN(sizeof(BezTriple) * nu->pntsu, __func__); + + float *co = cubic_spline; + BezTriple *bezt = nu->bezt; + for (int j = 0; j < cubic_spline_len; j++, bezt++, co += (dims * 3)) { + const float *handle_l = co + (dims * 0); + const float *pt = co + (dims * 1); + const float *handle_r = co + (dims * 2); + + copy_v3_v3(bezt->vec[0], handle_l); + copy_v3_v3(bezt->vec[1], pt); + copy_v3_v3(bezt->vec[2], handle_r); + + if (coords_indices.radius != -1) { + bezt->radius = (pt[coords_indices.radius] * cdd->radius.range) + cdd->radius.min; + } + else { + bezt->radius = radius_max; + } + + bezt->h1 = bezt->h2 = HD_ALIGN; /* will set to free in second pass */ + bezt->f1 = bezt->f2 = bezt->f3 = SELECT; + } + + if (corners_index) { + /* ignore the first and last */ + for (unsigned int i = 1; i < corners_index_len - 1; i++) { + bezt = &nu->bezt[corners_index[i]]; + bezt->h1 = bezt->h2 = HD_FREE; + } + } + } + + if (corners_index) { + free(corners_index); + } + + if (cubic_spline) { + free(cubic_spline); + } + +#else + nu->pntsu = stroke_len; + nu->bezt = MEM_callocN(nu->pntsu * sizeof(BezTriple), __func__); + + BezTriple *bezt = nu->bezt; + + { + BLI_mempool_iter iter; + const struct StrokeElem *selem; + + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) { + copy_v3_v3(bezt->vec[1], selem->location_local); + if (!is_3d) { + bezt->vec[1][2] = 0.0f; + } + + if (use_pressure_radius) { + bezt->radius = selem->pressure; + } + else { + bezt->radius = radius_max; + } + + bezt->h1 = bezt->h2 = HD_AUTO; + + bezt->f1 |= SELECT; + bezt->f2 |= SELECT; + bezt->f3 |= SELECT; + + bezt++; + } + } +#endif + + BKE_nurb_handles_calc(nu); + } + else { /* CU_POLY */ + BLI_mempool_iter iter; + const struct StrokeElem *selem; + + nu->pntsu = stroke_len; + nu->type = CU_POLY; + nu->bp = MEM_callocN(nu->pntsu * sizeof(BPoint), __func__); + + BPoint *bp = nu->bp; + + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) { + copy_v3_v3(bp->vec, selem->location_local); + if (!is_3d) { + bp->vec[2] = 0.0f; + } + + if (use_pressure_radius) { + bp->radius = (selem->pressure * radius_range) + radius_min; + } + else { + bp->radius = cps->radius_max; + } + bp->f1 = SELECT; + bp->vec[3] = 1.0f; + + bp++; + } + + BKE_nurb_knot_calc_u(nu); + } + + BLI_addtail(nurblist, nu); + + BKE_curve_nurb_active_set(cu, nu); + cu->actvert = nu->pntsu - 1; + + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + DAG_id_tag_update(obedit->data, 0); + + curve_draw_exit(op); + + return OPERATOR_FINISHED; +} + +static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (RNA_struct_property_is_set(op->ptr, "stroke")) { + return curve_draw_exec(C, op); + } + + if (!curve_draw_init(C, op, true)) { + return OPERATOR_CANCELLED; + } + + struct CurveDrawData *cdd = op->customdata; + + const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings; + + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + + /* fallback (incase we can't find the depth on first test) */ + { + const float mval_fl[2] = {UNPACK2(event->mval)}; + float center[3]; + negate_v3_v3(center, cdd->vc.rv3d->ofs); + ED_view3d_win_to_3d(cdd->vc.ar, center, mval_fl, cdd->prev.location_world); + copy_v3_v3(cdd->prev.location_world_valid, cdd->prev.location_world); + } + + cdd->draw_handle_view = ED_region_draw_cb_activate( + cdd->vc.ar->type, curve_draw_stroke_3d, op, REGION_DRAW_POST_VIEW); + WM_cursor_modal_set(cdd->vc.win, BC_PAINTBRUSHCURSOR); + + { + View3D *v3d = cdd->vc.v3d; + RegionView3D *rv3d = cdd->vc.rv3d; + Object *obedit = cdd->vc.obedit; + Curve *cu = obedit->data; + + const float *plane_no = NULL; + const float *plane_co = NULL; + + if ((cu->flag & CU_3D) == 0) { + /* 2D overrides other options */ + plane_co = obedit->obmat[3]; + plane_no = obedit->obmat[2]; + cdd->project.use_plane = true; + } + else { + if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && + (v3d->drawtype > OB_WIRE)) + { + view3d_get_transformation(cdd->vc.ar, cdd->vc.rv3d, NULL, &cdd->mats); + + /* needed or else the draw matrix can be incorrect */ + view3d_operator_needs_opengl(C); + + ED_view3d_autodist_init(cdd->vc.scene, cdd->vc.ar, cdd->vc.v3d, 0); + + if (cdd->vc.rv3d->depths) { + cdd->vc.rv3d->depths->damaged = true; + } + + ED_view3d_depth_update(cdd->vc.ar); + + if (cdd->vc.rv3d->depths != NULL) { + cdd->project.use_depth = true; + } + else { + BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane"); + cdd->project.use_depth = false; + } + } + + /* use view plane (when set or as fallback when surface can't be found) */ + if (cdd->project.use_depth == false) { + plane_co = ED_view3d_cursor3d_get(cdd->vc.scene, v3d); + plane_no = rv3d->viewinv[2]; + cdd->project.use_plane = true; + } + + if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) { + cdd->sample.use_substeps = true; + } + } + + if (cdd->project.use_plane) { + normalize_v3_v3(cdd->project.plane, plane_no); + cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co); + } + } + + if (is_modal == false) { + curve_draw_event_add_first(op, event); + } + + /* add temp handler */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static void curve_draw_cancel(bContext *UNUSED(C), wmOperator *op) +{ + curve_draw_exit(op); +} + + +/* Modal event handling of frame changing */ +static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + int ret = OPERATOR_RUNNING_MODAL; + struct CurveDrawData *cdd = op->customdata; + + UNUSED_VARS(C, op); + + if (event->type == cdd->init_event_type) { + if (event->val == KM_RELEASE) { + ED_region_tag_redraw(cdd->vc.ar); + + curve_draw_exec_precalc(op); + + curve_draw_stroke_to_operator(op); + + curve_draw_exec(C, op); + + return OPERATOR_FINISHED; + } + } + else if (ELEM(event->type, ESCKEY, RIGHTMOUSE)) { + ED_region_tag_redraw(cdd->vc.ar); + curve_draw_cancel(C, op); + return OPERATOR_CANCELLED; + } + else if (ELEM(event->type, LEFTMOUSE)) { + if (event->val == KM_PRESS) { + curve_draw_event_add_first(op, event); + } + } + else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + if (cdd->state == CURVE_DRAW_PAINTING) { + const float mval_fl[2] = {UNPACK2(event->mval)}; + if (len_squared_v2v2(mval_fl, cdd->prev.mouse) > SQUARE(STROKE_SAMPLE_DIST_MIN_PX)) { + curve_draw_event_add(op, event); + } + } + } + + return ret; +} + +void CURVE_OT_draw(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Draw Curve"; + ot->idname = "CURVE_OT_draw"; + ot->description = "Draw a freehand spline"; + + /* api callbacks */ + ot->exec = curve_draw_exec; + ot->invoke = curve_draw_invoke; + ot->cancel = curve_draw_cancel; + ot->modal = curve_draw_modal; + ot->poll = ED_operator_editcurve; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + + prop = RNA_def_float_distance( + ot->srna, "error_threshold", 0.0f, 0.0f, 10.0f, "Error", + "Error distance threshold (in object units)", + 0.0001f, 10.0f); + RNA_def_property_ui_range(prop, 0.0, 10, 1, 4); + + prop = RNA_def_float_distance( + ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI); + RNA_def_property_subtype(prop, PROP_ANGLE); + + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + +/** \} */ diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index b5d9283f6c6..0329598d711 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -35,10 +35,13 @@ #include <math.h> #include <float.h> +#include "MEM_guardedalloc.h" + #include "BLI_sys_types.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_polyfill2d.h" #include "BLF_api.h" #include "BLT_translation.h" @@ -82,6 +85,7 @@ typedef enum eDrawStrokeFlags { GP_DRAWDATA_NO_ONIONS = (1 << 6), /* no onionskins should be drawn (for animation playback) */ GP_DRAWDATA_VOLUMETRIC = (1 << 7), /* draw strokes as "volumetric" circular billboards */ GP_DRAWDATA_FILL = (1 << 8), /* fill insides/bounded-regions of strokes */ + GP_DRAWDATA_HQ_FILL = (1 << 9) /* Use high quality fill */ } eDrawStrokeFlags; @@ -325,36 +329,186 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor /* --------------- Stroke Fills ----------------- */ -/* draw fills for shapes */ -static void gp_draw_stroke_fill(bGPDspoint *points, int totpoints, short UNUSED(thickness), - short UNUSED(dflag), short sflag, - int offsx, int offsy, int winx, int winy) +/* Get points of stroke always flat to view not affected by camera view or view position */ +static void gp_stroke_2d_flat(bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction) { - bGPDspoint *pt; - int i; + bGPDspoint *pt0 = &points[0]; + bGPDspoint *pt1 = &points[1]; + bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; - BLI_assert(totpoints >= 3); + float locx[3]; + float locy[3]; + float loc3[3]; + float normal[3]; - /* As an initial implementation, we use the OpenGL filled polygon drawing - * here since it's the easiest option to implement for this case. It does - * come with limitations (notably for concave shapes), though it shouldn't - * be much of an issue in most cases. - */ - glBegin(GL_POLYGON); + /* local X axis (p0 -> p1) */ + sub_v3_v3v3(locx, &pt1->x, &pt0->x); - for (i = 0, pt = points; i < totpoints; i++, pt++) { - if (sflag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + /* point vector at 3/4 */ + sub_v3_v3v3(loc3, &pt3->x, &pt0->x); + + /* vector orthogonal to polygon plane */ + cross_v3_v3v3(normal, locx, loc3); + + /* local Y axis (cross to normal/x axis) */ + cross_v3_v3v3(locy, normal, locx); + + /* Normalize vectors */ + normalize_v3(locx); + normalize_v3(locy); + + /* Get all points in local space */ + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt = &points[i]; + float loc[3]; + + /* Get local space using first point as origin */ + sub_v3_v3v3(loc, &pt->x, &pt0->x); + + points2d[i][0] = dot_v3v3(loc, locx); + points2d[i][1] = dot_v3v3(loc, locy); + } + + /* Concave (-1), Convex (1), or Autodetect (0)? */ + *r_direction = (int)locy[2]; +} + + +/* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was modified) */ +static void gp_triangulate_stroke_fill(bGPDstroke *gps) +{ + BLI_assert(gps->totpoints >= 3); + gps->tot_triangles = gps->totpoints - 2; + + /* allocate memory for temporary areas */ + unsigned int (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation"); + float (*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points"); + + int direction = 0; + + /* convert to 2d and triangulate */ + gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); + BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)gps->totpoints, direction, (unsigned int(*)[3])tmp_triangles); + + /* save triangulation data in stroke cache */ + if (gps->tot_triangles > 0) { + if (gps->triangles == NULL) { + gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, "GP Stroke triangulation"); } else { - float co[2]; - - gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); - glVertex2fv(co); + gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); + } + + int i; + + for (i = 0; i < gps->tot_triangles; i++) { + bGPDtriangle *stroke_triangle = &gps->triangles[i]; + stroke_triangle->v1 = tmp_triangles[i][0]; + stroke_triangle->v2 = tmp_triangles[i][1]; + stroke_triangle->v3 = tmp_triangles[i][2]; } } + else { + /* No triangles needed - Free anything allocated previously */ + if (gps->triangles) + MEM_freeN(gps->triangles); + + gps->triangles = NULL; + } - glEnd(); + /* disable recalculation flag */ + if (gps->flag & GP_STROKE_RECALC_CACHES) { + gps->flag &= ~GP_STROKE_RECALC_CACHES; + } + + /* clear memory */ + if (tmp_triangles) MEM_freeN(tmp_triangles); + if (points2d) MEM_freeN(points2d); +} + + +/* draw fills for shapes */ +static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short dflag, int offsx, int offsy, int winx, int winy) +{ + BLI_assert(gps->totpoints >= 3); + + /* Triangulation fill if high quality flag is enabled */ + if (dflag & GP_DRAWDATA_HQ_FILL) { + bGPDtriangle *stroke_triangle; + bGPDspoint *pt; + int i; + + /* Calculate triangles cache for filling area (must be done only after changes) */ + if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) { + gp_triangulate_stroke_fill(gps); + } + + /* Draw all triangles for filling the polygon (cache must be calculated before) */ + BLI_assert(gps->tot_triangles >= 1); + glBegin(GL_TRIANGLES); + for (i = 0, stroke_triangle = gps->triangles; i < gps->tot_triangles; i++, stroke_triangle++) { + if (gps->flag & GP_STROKE_3DSPACE) { + /* vertex 1 */ + pt = &gps->points[stroke_triangle->v1]; + glVertex3fv(&pt->x); + + /* vertex 2 */ + pt = &gps->points[stroke_triangle->v2]; + glVertex3fv(&pt->x); + + /* vertex 3 */ + pt = &gps->points[stroke_triangle->v3]; + glVertex3fv(&pt->x); + } + else { + float co[2]; + + /* vertex 1 */ + pt = &gps->points[stroke_triangle->v1]; + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + glVertex2fv(co); + + /* vertex 2 */ + pt = &gps->points[stroke_triangle->v2]; + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + glVertex2fv(co); + + /* vertex 3 */ + pt = &gps->points[stroke_triangle->v3]; + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + glVertex2fv(co); + } + } + glEnd(); + } + else { + /* As an initial implementation, we use the OpenGL filled polygon drawing + * here since it's the easiest option to implement for this case. It does + * come with limitations (notably for concave shapes), though it works well + * enough for many simple situations. + * + * We keep this legacy implementation around despite now having the high quality + * fills, as this is necessary for keeping everything working nicely for files + * created using old versions of Blender which may have depended on the artifacts + * the old fills created. + */ + bGPDspoint *pt; + int i; + + glBegin(GL_POLYGON); + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (gps->flag & GP_STROKE_3DSPACE) { + glVertex3fv(&pt->x); + } + else { + float co[2]; + + gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + glVertex2fv(co); + } + } + glEnd(); + } } /* ----- Existing Strokes Drawing (3D and Point) ------ */ @@ -695,7 +849,7 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int /* 3D Fill */ if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { glColor4fv(fill_color); - gp_draw_stroke_fill(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); } /* 3D Stroke */ @@ -730,7 +884,7 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int /* 2D - Fill */ if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { glColor4fv(fill_color); - gp_draw_stroke_fill(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); } /* 2D Strokes... */ @@ -890,7 +1044,7 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of copy_v3_v3(color, gpl->color); } - if (gpl->gstep) { + if (gpl->gstep > 0) { bGPDframe *gf; float fac; @@ -907,13 +1061,16 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of break; } } - else { + else if (gpl->gstep == 0) { /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->prev) { color[3] = (alpha / 7); gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); } } + else { + /* don't draw - disabled */ + } /* 2) Now draw next frames */ @@ -924,7 +1081,7 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of copy_v3_v3(color, gpl->color); } - if (gpl->gstep_next) { + if (gpl->gstep_next > 0) { bGPDframe *gf; float fac; @@ -941,13 +1098,16 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of break; } } - else { + else if (gpl->gstep_next == 0) { /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->next) { color[3] = (alpha / 4); gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); } } + else { + /* don't draw - disabled */ + } /* 3) restore alpha */ glColor4fv(gpl->color); @@ -990,7 +1150,10 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in /* volumetric strokes... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_VOLUMETRIC), GP_DRAWDATA_VOLUMETRIC); - + + /* HQ fills... */ + GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL); + /* fill strokes... */ // XXX: this is not a very good limit GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL); diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 09a72c10457..a49b3362155 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -460,7 +460,8 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* make a copy of stroke, then of its points array */ gpsn = MEM_dupallocN(gps); gpsn->points = MEM_dupallocN(gps->points); - + /* duplicate triangle information */ + gpsn->triangles = MEM_dupallocN(gps->triangles); /* append stroke to frame */ BLI_addtail(&gpf->strokes, gpsn); } diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 43751dbadb9..df05397258c 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -766,8 +766,9 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) new_stroke = MEM_dupallocN(gps); new_stroke->points = MEM_dupallocN(gps->points); - new_stroke->next = new_stroke->prev = NULL; + new_stroke->triangles = MEM_dupallocN(gps->triangles); + new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); /* Adjust all the stroke's points, so that the strokes @@ -905,7 +906,7 @@ static void gpencil_toggle_brush_cursor(bContext *C, bool enable) static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) { const char *brush_name = NULL; - char str[256] = ""; + char str[UI_MAX_DRAW_STR] = ""; RNA_enum_name(rna_enum_gpencil_sculpt_brush_items, gso->brush_type, &brush_name); @@ -1284,6 +1285,12 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); break; } + + /* Triangulation must be calculated if changed */ + if (changed) { + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + } } CTX_DATA_END; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 03d5ed3e24f..bd1697b9a54 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -170,6 +170,11 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes) /* make a stupid copy first of the entire stroke (to get the flags too) */ gpsd = MEM_dupallocN(gps); + /* initialize triangle memory - will be calculated on next redraw */ + gpsd->triangles = NULL; + gpsd->flag |= GP_STROKE_RECALC_CACHES; + gpsd->tot_triangles = 0; + /* now, make a new points array, and copy of the relevant parts */ gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy"); memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); @@ -223,6 +228,10 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) gpsd = MEM_dupallocN(gps); gpsd->points = MEM_dupallocN(gps->points); + /* triangle information - will be calculated on next redraw */ + gpsd->flag |= GP_STROKE_RECALC_CACHES; + gpsd->triangles = NULL; + /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; BLI_addtail(&new_strokes, gpsd); @@ -287,11 +296,13 @@ void ED_gpencil_strokes_copybuf_free(void) for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) { gpsn = gps->next; - MEM_freeN(gps->points); + if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) MEM_freeN(gps->triangles); + BLI_freelinkN(&gp_strokes_copypastebuf, gps); } - BLI_listbase_clear(&gp_strokes_copypastebuf); + gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL; } /* --------------------- */ @@ -336,6 +347,11 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) gpsd = MEM_dupallocN(gps); gpsd->points = MEM_dupallocN(gps->points); + /* triangles cache - will be recalculated on next redraw */ + gpsd->flag |= GP_STROKE_RECALC_CACHES; + gpsd->tot_triangles = 0; + gpsd->triangles = NULL; + /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; BLI_addtail(&gp_strokes_copypastebuf, gpsd); @@ -371,6 +387,14 @@ void GPENCIL_OT_copy(wmOperatorType *ot) /* --------------------- */ /* Paste selected strokes */ +static int gp_strokes_paste_poll(bContext *C) +{ + /* 1) Must have GP layer to paste to... + * 2) Copy buffer must at least have something (though it may be the wrong sort...) + */ + return (CTX_data_active_gpencil_layer(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); +} + static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -450,8 +474,11 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) bGPDstroke *new_stroke = MEM_dupallocN(gps); new_stroke->points = MEM_dupallocN(gps->points); - new_stroke->next = new_stroke->prev = NULL; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke->triangles = NULL; + + new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); } } @@ -472,7 +499,7 @@ void GPENCIL_OT_paste(wmOperatorType *ot) /* callbacks */ ot->exec = gp_strokes_paste_exec; - ot->poll = gp_stroke_edit_poll; + ot->poll = gp_strokes_paste_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -672,6 +699,7 @@ static int gp_delete_selected_strokes(bContext *C) if (gps->flag & GP_STROKE_SELECT) { /* free stroke memory arrays, then stroke itself */ if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); changed = true; @@ -732,6 +760,9 @@ static int gp_dissolve_selected_points(bContext *C) if (tot <= 0) { /* remove the entire stroke */ MEM_freeN(gps->points); + if (gps->triangles) { + MEM_freeN(gps->triangles); + } BLI_freelinkN(&gpf->strokes, gps); } else { @@ -753,6 +784,10 @@ static int gp_dissolve_selected_points(bContext *C) gps->points = new_points; gps->totpoints = tot; + /* triangles cache needs to be recalculated */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + /* deselect the stroke, since none of its selected points will still be selected */ gps->flag &= ~GP_STROKE_SELECT; } @@ -842,6 +877,11 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke tGPDeleteIsland *island = &islands[idx]; bGPDstroke *new_stroke = MEM_dupallocN(gps); + /* initialize triangle memory - to be calculated on next redraw */ + new_stroke->triangles = NULL; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke->tot_triangles = 0; + /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ new_stroke->totpoints = island->end_idx - island->start_idx + 1; new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); @@ -886,6 +926,9 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke /* Delete the old stroke */ MEM_freeN(gps->points); + if (gps->triangles) { + MEM_freeN(gps->triangles); + } BLI_freelinkN(&gpf->strokes, gps); } diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 368da618a1d..dd28f6ac531 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -177,6 +177,8 @@ void GPENCIL_OT_select_linked(struct wmOperatorType *ot); void GPENCIL_OT_select_grouped(struct wmOperatorType *ot); void GPENCIL_OT_select_more(struct wmOperatorType *ot); void GPENCIL_OT_select_less(struct wmOperatorType *ot); +void GPENCIL_OT_select_first(struct wmOperatorType *ot); +void GPENCIL_OT_select_last(struct wmOperatorType *ot); void GPENCIL_OT_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_delete(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 9f5633ae668..cf9828f8105 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -140,6 +140,36 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius"); + + /* Sculpting ------------------------------------- */ + + /* Brush-Based Editing: + * EKEY + LMB = Single stroke, draw immediately + * + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc. + * + * For the modal version, use D+E -> Sculpt + */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, EKEY); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, EKEY); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/ + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, EKEY); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/ + + + /* Shift-FKEY = Sculpt Strength */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength"); + + /* Ctrl-FKEY = Sculpt Brush Size */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); + RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size"); + + /* Selection ------------------------------------- */ /* select all */ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_all", AKEY, KM_PRESS, 0, 0); @@ -160,6 +190,15 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_SHIFT | KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "deselect", true); + /* In the Node Editor, lasso select needs ALT modifier too (as somehow CTRL+LMB drag gets taken for "cut" quite early) + * There probably isn't too much harm adding this for other editors too as part of standard GP editing keymap. This hotkey + * combo doesn't seem to see much use under standard scenarios? + */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "deselect", false); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_SHIFT | KM_CTRL | KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "deselect", true); + /* normal select */ WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); @@ -229,36 +268,6 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "GPENCIL_OT_move_to_layer", MKEY, KM_PRESS, 0, 0); - - /* Brush-Based Editing: - * EKEY + LMB = Single stroke, draw immediately - * + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc. - * - * For the modal version, use D+E -> Sculpt - */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/ - - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/ - - - /* Shift-FKEY = Sculpt Strength */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); - RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength"); - - /* Ctrl-FKEY = Sculpt Brush Size */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); - RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size"); - - - - /* Transform Tools */ kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0); @@ -314,6 +323,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_select_grouped); WM_operatortype_append(GPENCIL_OT_select_more); WM_operatortype_append(GPENCIL_OT_select_less); + WM_operatortype_append(GPENCIL_OT_select_first); + WM_operatortype_append(GPENCIL_OT_select_last); WM_operatortype_append(GPENCIL_OT_duplicate); WM_operatortype_append(GPENCIL_OT_delete); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 2a81b481ed1..fba2f30e715 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -572,6 +572,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gps->flag = gpd->sbuffer_sflag; gps->inittime = p->inittime; + /* enable recalculation flag by default (only used if hq fill) */ + gps->flag |= GP_STROKE_RECALC_CACHES; + /* allocate enough memory for a continuous array for storage points */ int sublevel = gpl->sublevel; int new_totpoints = gps->totpoints; @@ -580,7 +583,10 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) new_totpoints += new_totpoints - 1; } gps->points = MEM_callocN(sizeof(bGPDspoint) * new_totpoints, "gp_stroke_points"); - + /* initialize triangle memory to dummy data */ + gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; /* set pointer to first non-initialized point */ pt = gps->points + (gps->totpoints - totelem); @@ -710,11 +716,18 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } } - /* smooth stroke - only if there's something to do */ - /* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */ + /* smooth stroke after subdiv - only if there's something to do + * for each iteration, the factor is reduced to get a better smoothing without changing too much + * the original stroke + */ if (gpl->draw_smoothfac > 0.0f) { - for (i = 0; i < gps->totpoints; i++) { - gp_smooth_stroke(gps, i, gpl->draw_smoothfac, false); + float reduce = 0.0f; + for (int r = 0; r < gpl->draw_smoothlvl; ++r) { + for (i = 0; i < gps->totpoints; i++) { + /* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */ + gp_smooth_stroke(gps, i, gpl->draw_smoothfac - reduce, false); + } + reduce += 0.25f; // reduce the factor } } @@ -795,6 +808,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* just free stroke */ if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } else if (gps->totpoints == 1) { @@ -807,6 +822,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* free stroke */ // XXX: pressure sensitive eraser should apply here too? MEM_freeN(gps->points); + if (gps->triangles) + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } } diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 0a36df471f1..b6482786b4f 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -349,6 +349,126 @@ void GPENCIL_OT_select_grouped(wmOperatorType *ot) } /* ********************************************** */ +/* Select First */ + +static int gpencil_select_first_exec(bContext *C, wmOperator *op) +{ + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + /* skip stroke if we're only manipulating selected strokes */ + if (only_selected && !(gps->flag & GP_STROKE_SELECT)) { + continue; + } + + /* select first point */ + BLI_assert(gps->totpoints >= 1); + + gps->points->flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + + /* deselect rest? */ + if ((extend == false) && (gps->totpoints > 1)) { + /* start from index 1, to skip the first point that we'd just selected... */ + bGPDspoint *pt = &gps->points[1]; + int i = 1; + + for (; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_select_first(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select First"; + ot->idname = "GPENCIL_OT_select_first"; + ot->description = "Select first point in Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_select_first_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "only_selected_strokes", false, "Selected Strokes Only", + "Only select the first point of strokes that already have points selected"); + + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting all other selected points"); +} + +/* ********************************************** */ +/* Select First */ + +static int gpencil_select_last_exec(bContext *C, wmOperator *op) +{ + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + /* skip stroke if we're only manipulating selected strokes */ + if (only_selected && !(gps->flag & GP_STROKE_SELECT)) { + continue; + } + + /* select last point */ + BLI_assert(gps->totpoints >= 1); + + gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + + /* deselect rest? */ + if ((extend == false) && (gps->totpoints > 1)) { + /* don't include the last point... */ + bGPDspoint *pt = gps->points; + int i = 1; + + for (; i < gps->totpoints - 1; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_select_last(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Last"; + ot->idname = "GPENCIL_OT_select_last"; + ot->description = "Select last point in Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_select_last_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "only_selected_strokes", false, "Selected Strokes Only", + "Only select the last point of strokes that already have points selected"); + + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting all other selected points"); +} + +/* ********************************************** */ /* Select More */ static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index 34e640a4b7b..c39a3aa5cfc 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -41,7 +41,7 @@ #include "BLI_listbase.h" -#include "BKE_blender.h" +#include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_gpencil.h" diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 64cb0b5bb6a..b2c6107ab61 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -569,6 +569,10 @@ bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure) /* add the point itself */ madd_v3_v3fl(sco, &pt->x, average_fac); + if (affect_pressure) { + pressure += pt->pressure * average_fac; + } + /* n-steps before/after current point */ // XXX: review how the endpoints are treated by this algorithm // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight @@ -637,3 +641,21 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) } /* ******************************************************** */ + + +bool ED_gpencil_stroke_minmax( + const bGPDstroke *gps, const bool use_select, + float r_min[3], float r_max[3]) +{ + const bGPDspoint *pt; + int i; + bool changed = false; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {; + minmax_v3v3_v3(r_min, r_max, &pt->x); + changed = true; + } + } + return changed; +} diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index e45e5f5e7ab..0ac5c17a552 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -143,6 +143,8 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo */ void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect); +void glaDrawPixelsTex_clipping(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, + float clip_min_x, float clip_min_y, float clip_max_x, float clip_max_y); /** * glaDrawPixelsAuto - Switches between texture or pixel drawing using UserDef. @@ -150,9 +152,13 @@ void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, int ty * needs glaDefine2DArea to be set. */ void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect); +void glaDrawPixelsAuto_clipping(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, + float clip_min_x, float clip_min_y, float clip_max_x, float clip_max_y); void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, float scaleX, float scaleY); +void glaDrawPixelsTexScaled_clipping(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, float scaleX, float scaleY, + float clip_min_x, float clip_min_y, float clip_max_x, float clip_max_y); /* 2D Drawing Assistance */ @@ -205,9 +211,21 @@ void bgl_get_mats(bglMats *mats); void glaDrawImBuf_glsl(struct ImBuf *ibuf, float x, float y, int zoomfilter, struct ColorManagedViewSettings *view_settings, struct ColorManagedDisplaySettings *display_settings); +void glaDrawImBuf_glsl_clipping(struct ImBuf *ibuf, float x, float y, int zoomfilter, + struct ColorManagedViewSettings *view_settings, + struct ColorManagedDisplaySettings *display_settings, + float clip_min_x, float clip_min_y, + float clip_max_x, float clip_max_y); + /* Draw imbuf on a screen, preferably using GLSL display transform */ void glaDrawImBuf_glsl_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, int zoomfilter); +void glaDrawImBuf_glsl_ctx_clipping(const struct bContext *C, + struct ImBuf *ibuf, + float x, float y, + int zoomfilter, + float clip_min_x, float clip_min_y, + float clip_max_x, float clip_max_y); void glaDrawBorderCorners(const struct rcti *border, float zoomx, float zoomy); diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 3c8442218be..fb4897c6532 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -544,10 +544,10 @@ void ANIM_uiTemplate_fmodifier_draw(struct uiLayout *layout, struct ID *id, List /* free the copy/paste buffer */ -void free_fmodifiers_copybuf(void); +void ANIM_fmodifiers_copybuf_free(void); /* copy the given F-Modifiers to the buffer, returning whether anything was copied or not - * assuming that the buffer has been cleared already with free_fmodifiers_copybuf() + * assuming that the buffer has been cleared already with ANIM_fmodifiers_copybuf_free() * - active: only copy the active modifier */ bool ANIM_fmodifiers_copy_to_buf(ListBase *modifiers, bool active); diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 15c68378b9a..904132b8876 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -74,7 +74,10 @@ typedef struct EditBone { float xwidth, length, zwidth; /* put them in order! transform uses this as scale */ float ease1, ease2; float rad_head, rad_tail; - + float roll1, roll2; + float curveOutX, curveOutY; + float curveInX, curveInY; + float scaleIn, scaleOut; float oldlength; /* for envelope scaling */ short segments; diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 0f638c449ad..255827db373 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -86,6 +86,10 @@ bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfr bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *sa, const struct bGPDstroke *gps); bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps); +bool ED_gpencil_stroke_minmax( + const struct bGPDstroke *gps, const bool use_select, + float r_min[3], float r_max[3]); + /* ----------- Grease Pencil Operators ----------------- */ void ED_keymap_gpencil(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index bbfa8ec7b1d..fae3e3677a0 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -272,7 +272,7 @@ void sample_fcurve(struct FCurve *fcu); /* ----------- */ -void free_anim_copybuf(void); +void ANIM_fcurves_copybuf_free(void); short copy_animedit_keys(struct bAnimContext *ac, ListBase *anim_data); short paste_animedit_keys(struct bAnimContext *ac, ListBase *anim_data, const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode, bool flip); diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 0ad1dc4d8c1..81e2558e765 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -288,6 +288,14 @@ bool ANIM_remove_driver(struct ReportList *reports, struct ID *id, const char rn /* -------- */ +/* Clear copy-paste buffer for drivers */ +void ANIM_drivers_copybuf_free(void); + +/* Clear copy-paste buffer for driver variable sets */ +void ANIM_driver_vars_copybuf_free(void); + +/* -------- */ + /* Returns whether there is a driver in the copy/paste buffer to paste */ bool ANIM_driver_can_paste(void); @@ -302,6 +310,17 @@ bool ANIM_copy_driver(struct ReportList *reports, struct ID *id, const char rna_ */ bool ANIM_paste_driver(struct ReportList *reports, struct ID *id, const char rna_path[], int array_index, short flag); +/* -------- */ + +/* Checks if there are driver variables in the copy/paste buffer */ +bool ANIM_driver_vars_can_paste(void); + +/* Copy the given driver's variables to the buffer */ +bool ANIM_driver_vars_copy(struct ReportList *reports, struct FCurve *fcu); + +/* Paste the variables in the buffer to the given FCurve */ +bool ANIM_driver_vars_paste(struct ReportList *reports, struct FCurve *fcu, bool replace); + /* ************ Auto-Keyframing ********************** */ /* Notes: * - All the defines for this (User-Pref settings and Per-Scene settings) diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 5436ef4b06b..de798b1fce2 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -80,7 +80,7 @@ void EDBM_mesh_normals_update(struct BMEditMesh *em); void EDBM_mesh_clear(struct BMEditMesh *em); void EDBM_selectmode_to_scene(struct bContext *C); -void EDBM_mesh_make(struct ToolSettings *ts, struct Object *ob); +void EDBM_mesh_make(struct ToolSettings *ts, struct Object *ob, const bool add_key_index); void EDBM_mesh_free(struct BMEditMesh *em); void EDBM_mesh_load(struct Object *ob); struct DerivedMesh *EDBM_mesh_deform_dm_get(struct BMEditMesh *em); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 85370de0013..6a558d1c185 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -67,6 +67,7 @@ void ED_region_panels( const bool vertical); void ED_region_header_init(struct ARegion *ar); void ED_region_header(const struct bContext *C, struct ARegion *ar); +void ED_region_cursor_set(struct wmWindow *win, struct ScrArea *sa, struct ARegion *ar); void ED_region_toggle_hidden(struct bContext *C, struct ARegion *ar); void ED_region_info_draw(struct ARegion *ar, const char *text, float fill_color[4], const bool full_redraw); void ED_region_image_metadata_draw(int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy); diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index db8085a6696..933f480a554 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -43,6 +43,8 @@ struct wmEvent; struct wmKeyConfig; struct wmKeyMap; struct wmOperatorType; +struct Main; +struct SnapObjectContext; void transform_keymap_for_space(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap, int spaceid); void transform_operatortypes(void); @@ -156,19 +158,6 @@ void BIF_draw_manipulator(const struct bContext *C); /* Snapping */ - -typedef struct DepthPeel { - struct DepthPeel *next, *prev; - - float depth; - float p[3]; - float no[3]; - struct Object *ob; - int flag; -} DepthPeel; - -struct ListBase; - typedef enum SnapSelect { SNAP_ALL = 0, SNAP_NOT_SELECTED = 1, @@ -177,37 +166,23 @@ typedef enum SnapSelect { #define SNAP_MIN_DISTANCE 30 -bool peelObjectsTransForm( - struct TransInfo *t, const float mval[2], SnapSelect snap_select, +bool peelObjectsTransform( + struct TransInfo *t, const float mval[2], + SnapSelect snap_select, bool use_peel_object, /* return args */ - struct ListBase *r_depth_peels); -bool peelObjectsContext( - struct bContext *C, const float mval[2], SnapSelect snap_select, + float r_loc[3], float r_no[3], float *r_thickness); +bool peelObjectsSnapContext( + struct SnapObjectContext *sctx, + const float mval[2], + SnapSelect snap_select, bool use_peel_object, /* return args */ - struct ListBase *r_depth_peels); + float r_loc[3], float r_no[3], float *r_thickness); + bool snapObjectsTransform( struct TransInfo *t, const float mval[2], SnapSelect snap_select, + float *dist_px, /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px); -bool snapObjectsContext( - struct bContext *C, const float mval[2], SnapSelect snap_select, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px); -/* taks args for all settings */ -bool snapObjectsEx( - struct Scene *scene, struct View3D *v3d, struct ARegion *ar, struct Base *base_act, struct Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_mode, - float *ray_depth, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px); -bool snapObjectsRayEx( - struct Scene *scene, struct View3D *v3d, struct ARegion *ar, struct Base *base_act, struct Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_mode, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index, - struct Object **r_ob, float r_obmat[4][4]); - + float r_loc[3], float r_no[3]); bool snapNodesTransform( struct TransInfo *t, const int mval[2], SnapSelect snap_select, /* return args */ diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h new file mode 100644 index 00000000000..900b7593f2e --- /dev/null +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -0,0 +1,140 @@ +/* + * ***** 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 ED_transform_snap_object_context.h + * \ingroup editors + */ + +#ifndef __ED_TRANSFORM_SNAP_OBJECT_CONTEXT_H__ +#define __ED_TRANSFORM_SNAP_OBJECT_CONTEXT_H__ + +struct BMVert; +struct BMEdge; +struct BMFace; + +struct ListBase; +struct Scene; +struct Main; +struct Object; +struct ARegion; +struct View3D; + +/* transform_snap_object.c */ + +/* ED_transform_snap_object_*** API */ + +/** used for storing multiple hits */ +struct SnapObjectHitDepth { + struct SnapObjectHitDepth *next, *prev; + + float depth; + float co[3]; + float no[3]; + int index; + + struct Object *ob; + float obmat[4][4]; + + /* needed to tell which ray-cast this was part of, + * the same object may be part of many ray-casts when dupli's are used. */ + unsigned int ob_uuid; +}; + +struct SnapObjectParams { + int snap_select; /* SnapSelect */ + union { + unsigned int snap_to : 4; + /* snap_target_flag: Snap to vert/edge/face. */ + unsigned int snap_to_flag : 4; + }; + /* use editmode cage */ + unsigned int use_object_edit : 1; + /* special context sensitive handling for the active object */ + unsigned int use_object_active : 1; +}; + +enum { + SNAP_OBJECT_USE_CACHE = (1 << 0), +}; + +typedef struct SnapObjectContext SnapObjectContext; +SnapObjectContext *ED_transform_snap_object_context_create( + struct Main *bmain, struct Scene *scene, int flag); +SnapObjectContext *ED_transform_snap_object_context_create_view3d( + struct Main *bmain, struct Scene *scene, int flag, + /* extra args for view3d */ + struct ARegion *ar, struct View3D *v3d); +void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx); + +/* callbacks to filter how snap works */ +void ED_transform_snap_object_context_set_editmesh_callbacks( + SnapObjectContext *sctx, + bool (*test_vert_fn)(struct BMVert *, void *user_data), + bool (*test_edge_fn)(struct BMEdge *, void *user_data), + bool (*test_face_fn)(struct BMFace *, void *user_data), + void *user_data); + +bool ED_transform_snap_object_project_ray_ex( + struct SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + struct Object **r_ob, float r_obmat[4][4]); +bool ED_transform_snap_object_project_ray( + SnapObjectContext *sctx, + const float ray_origin[3], const float ray_direction[3], float *ray_depth, + float r_co[3], float r_no[3]); + +bool ED_transform_snap_object_project_ray_all( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float ray_start[3], const float ray_normal[3], + float ray_depth, bool sort, + struct ListBase *r_hit_list); + +bool ED_transform_snap_object_project_view3d_ex( + struct SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float *ray_depth, + float r_loc[3], float r_no[3], int *r_index); +bool ED_transform_snap_object_project_view3d( + struct SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3]); +bool ED_transform_snap_object_project_view3d_mixed( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval_fl[2], float *dist_px, + bool use_depth, + float r_co[3], float r_no[3]); + +bool ED_transform_snap_object_project_all_view3d_ex( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], + float ray_depth, bool sort, + ListBase *r_hit_list); + +#endif /* __ED_TRANSFORM_SNAP_OBJECT_CONTEXT_H__ */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index d8a3f5e75f5..c23c2eed890 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -396,18 +396,6 @@ void ED_view3d_operator_properties_viewmat_set(struct bContext *C, struct wmOper void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, int *winx, int *winy, float persmat[4][4]); #endif -bool ED_view3d_snap_from_region( - struct Scene *scene, struct View3D *v3d, struct ARegion *ar, - const float mval[2], float dist_px, - bool use_depth, bool use_obedit, - bool use_vert, bool use_edge, bool use_face, - float r_co[3], float r_no[3]); - -bool ED_view3d_snap_from_ray( - struct Scene *scene, - const float ray_start[3], const float ray_normal[3], - float r_co[3]); - /* render */ void ED_view3d_stop_render_preview(struct wmWindowManager *wm, struct ARegion *ar); void ED_view3d_shade_update(struct Main *bmain, struct Scene *scene, struct View3D *v3d, struct ScrArea *sa); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 76ff9e0fbd8..2c80701bf69 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -1020,3 +1020,25 @@ DEF_VICO(KEYTYPE_KEYFRAME_VEC) DEF_VICO(KEYTYPE_BREAKDOWN_VEC) DEF_VICO(KEYTYPE_EXTREME_VEC) DEF_VICO(KEYTYPE_JITTER_VEC) + +DEF_VICO(COLORSET_01_VEC) +DEF_VICO(COLORSET_02_VEC) +DEF_VICO(COLORSET_03_VEC) +DEF_VICO(COLORSET_04_VEC) +DEF_VICO(COLORSET_05_VEC) +DEF_VICO(COLORSET_06_VEC) +DEF_VICO(COLORSET_07_VEC) +DEF_VICO(COLORSET_08_VEC) +DEF_VICO(COLORSET_09_VEC) +DEF_VICO(COLORSET_10_VEC) +DEF_VICO(COLORSET_11_VEC) +DEF_VICO(COLORSET_12_VEC) +DEF_VICO(COLORSET_13_VEC) +DEF_VICO(COLORSET_14_VEC) +DEF_VICO(COLORSET_15_VEC) +DEF_VICO(COLORSET_16_VEC) +DEF_VICO(COLORSET_17_VEC) +DEF_VICO(COLORSET_18_VEC) +DEF_VICO(COLORSET_19_VEC) +DEF_VICO(COLORSET_20_VEC) + diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 06831576507..88924495ae5 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -232,13 +232,8 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) if (IS_AUTOKEY_ON(scene)) { ReportList *reports = CTX_wm_reports(C); ToolSettings *ts = scene->toolsettings; - PointerRNA ptr = {{NULL}}; - PropertyRNA *prop = NULL; - int index; - UI_context_active_but_prop_get(C, &ptr, &prop, &index); - - insert_keyframe_direct(reports, ptr, prop, fcu, cfra, ts->keyframe_type, 0); + insert_keyframe_direct(reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, 0); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } } @@ -249,13 +244,8 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) if (IS_AUTOKEY_ON(scene)) { ReportList *reports = CTX_wm_reports(C); ToolSettings *ts = scene->toolsettings; - PointerRNA ptr = {{NULL}}; - PropertyRNA *prop = NULL; - int index; - - UI_context_active_but_prop_get(C, &ptr, &prop, &index); - insert_keyframe_direct(reports, ptr, prop, fcu, cfra, ts->keyframe_type, INSERTKEY_DRIVER); + insert_keyframe_direct(reports, but->rnapoin, but->rnaprop, fcu, cfra, ts->keyframe_type, INSERTKEY_DRIVER); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 023439fddee..10ab85a6142 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -60,7 +60,7 @@ #include "PIL_time.h" -#include "BKE_blender.h" +#include "BKE_blender_undo.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -2945,18 +2945,6 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in } #ifdef WITH_INPUT_IME -/* test if the translation context allows IME input - used to - * avoid weird character drawing if IME inputs non-ascii chars */ -static bool ui_ime_is_lang_supported(void) -{ - const char *uilng = BLT_lang_get(); - const bool is_lang_supported = STREQ(uilng, "zh_CN") || - STREQ(uilng, "zh_TW") || - STREQ(uilng, "ja_JP"); - - return ((U.transopts & USER_DOTRANSLATE) && is_lang_supported); -} - /* enable ime, and set up uibut ime data */ static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but)) { @@ -3078,7 +3066,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) WM_cursor_modal_set(win, BC_TEXTEDITCURSOR); #ifdef WITH_INPUT_IME - if (is_num_but == false && ui_ime_is_lang_supported()) { + if (is_num_but == false && BLT_lang_is_ime_supported()) { ui_textedit_ime_begin(win, but); } #endif @@ -3405,7 +3393,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle #ifdef WITH_INPUT_IME && !is_ime_composing && - !WM_event_is_ime_switch(event) + (!WM_event_is_ime_switch(event) || !BLT_lang_is_ime_supported()) #endif ) { @@ -6537,7 +6525,6 @@ static void menu_add_shortcut_cancel(struct bContext *C, void *arg1) static void popup_change_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; - UI_but_tooltip_timer_remove(C, but); UI_popup_block_invoke(C, menu_change_shortcut, but); } @@ -6559,7 +6546,6 @@ static void remove_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; - UI_but_tooltip_timer_remove(C, but); UI_popup_block_ex(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but); } @@ -6608,8 +6594,6 @@ static bool ui_but_menu(bContext *C, uiBut *but) return false; } - UI_but_tooltip_timer_remove(C, but); - /* highly unlikely getting the label ever fails */ UI_but_string_info_get(C, but, &label, NULL); @@ -6778,10 +6762,19 @@ static bool ui_but_menu(bContext *C, uiBut *but) ICON_NONE, "UI_OT_unset_property_button"); } + if (is_array_component) { + uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy All To Selected"), + ICON_NONE, "UI_OT_copy_to_selected_button", "all", true); + uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Single To Selected"), + ICON_NONE, "UI_OT_copy_to_selected_button", "all", false); + } + else { + uiItemBooleanO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy To Selected"), + ICON_NONE, "UI_OT_copy_to_selected_button", "all", true); + } + uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Data Path"), ICON_NONE, "UI_OT_copy_data_path_button"); - uiItemO(layout, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy To Selected"), - ICON_NONE, "UI_OT_copy_to_selected_button"); uiItemS(layout); } @@ -7378,7 +7371,6 @@ static void ui_blocks_set_tooltips(ARegion *ar, const bool enable) static bool ui_region_contains_point_px(ARegion *ar, int x, int y) { - uiBlock *block = ar->uiblocks.first; rcti winrct; /* scale down area rect to exclude shadow */ @@ -7386,7 +7378,7 @@ static bool ui_region_contains_point_px(ARegion *ar, int x, int y) /* check if the mouse is in the region */ if (!BLI_rcti_isect_pt(&winrct, x, y)) { - for (block = ar->uiblocks.first; block; block = block->next) + for (uiBlock *block = ar->uiblocks.first; block; block = block->next) block->auto_open = false; return false; @@ -7880,6 +7872,19 @@ static void button_activate_exit( ui_apply_but_undo(but); ui_apply_but_autokey(C, but); +#ifdef USE_ALLSELECT + { + /* only RNA from this button is used */ + uiBut but_temp = *but; + uiSelectContextStore *selctx_data = &data->select_others; + for (int i = 0; i < selctx_data->elems_len; i++) { + uiSelectContextElem *other = &selctx_data->elems[i]; + but_temp.rnapoin = other->ptr; + ui_apply_but_autokey(C, &but_temp); + } + } +#endif + /* popup menu memory */ if (block->flag & UI_BLOCK_POPUP_MEMORY) ui_popup_menu_memory_set(block, but); @@ -8446,8 +8451,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *ar, } if (val == KM_PRESS) { - if (ELEM(type, UPARROWKEY, DOWNARROWKEY) || - ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl))) + if ((ELEM(type, UPARROWKEY, DOWNARROWKEY) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) || + ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)))) { const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop); int value, min, max, inc; @@ -9818,9 +9823,17 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use retval = ui_handler_panel_region(C, event, ar, listbox ? listbox : but); - if (retval == WM_UI_HANDLER_CONTINUE && listbox) + if (retval == WM_UI_HANDLER_CONTINUE && listbox) { retval = ui_handle_list_event(C, event, ar, listbox); + /* interactions with the listbox should disable tips */ + if (retval == WM_UI_HANDLER_BREAK) { + if (but) { + UI_but_tooltip_timer_remove(C, but); + } + } + } + if (retval == WM_UI_HANDLER_CONTINUE) { if (but) retval = ui_handle_button_event(C, event, but); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 7be153e942e..0da21fd442f 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -511,6 +511,63 @@ static void vicon_keytype_jitter_draw(int x, int y, int w, int h, float alpha) vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_JITTER); } +static void vicon_colorset_draw(int index, int x, int y, int w, int h, float UNUSED(alpha)) +{ + bTheme *btheme = UI_GetTheme(); + ThemeWireColor *cs = &btheme->tarm[index]; + + /* Draw three bands of color: One per color + * x-----a-----b-----c + * | N | S | A | + * x-----a-----b-----c + */ + const int a = x + w / 3; + const int b = x + w / 3 * 2; + const int c = x + w; + + /* XXX: Include alpha into this... */ + /* normal */ + glColor3ubv(cs->solid); + glRecti(x, y, a, y + h); + + /* selected */ + glColor3ubv(cs->select); + glRecti(a, y, b, y + h); + + /* active */ + glColor3ubv(cs->active); + glRecti(b, y, c, y + h); +} + +#define DEF_VICON_COLORSET_DRAW_NTH(prefix, index) \ + static void vicon_colorset_draw_##prefix(int x, int y, int w, int h, float alpha) \ + { \ + vicon_colorset_draw(index, x, y, w, h, alpha); \ + } + +DEF_VICON_COLORSET_DRAW_NTH(01, 0) +DEF_VICON_COLORSET_DRAW_NTH(02, 1) +DEF_VICON_COLORSET_DRAW_NTH(03, 2) +DEF_VICON_COLORSET_DRAW_NTH(04, 3) +DEF_VICON_COLORSET_DRAW_NTH(05, 4) +DEF_VICON_COLORSET_DRAW_NTH(06, 5) +DEF_VICON_COLORSET_DRAW_NTH(07, 6) +DEF_VICON_COLORSET_DRAW_NTH(08, 7) +DEF_VICON_COLORSET_DRAW_NTH(09, 8) +DEF_VICON_COLORSET_DRAW_NTH(10, 9) +DEF_VICON_COLORSET_DRAW_NTH(11, 10) +DEF_VICON_COLORSET_DRAW_NTH(12, 11) +DEF_VICON_COLORSET_DRAW_NTH(13, 12) +DEF_VICON_COLORSET_DRAW_NTH(14, 13) +DEF_VICON_COLORSET_DRAW_NTH(15, 14) +DEF_VICON_COLORSET_DRAW_NTH(16, 15) +DEF_VICON_COLORSET_DRAW_NTH(17, 16) +DEF_VICON_COLORSET_DRAW_NTH(18, 17) +DEF_VICON_COLORSET_DRAW_NTH(19, 18) +DEF_VICON_COLORSET_DRAW_NTH(20, 19) + +#undef DEF_VICON_COLORSET_DRAW_NTH + #ifndef WITH_HEADLESS static void init_brush_icons(void) @@ -741,6 +798,27 @@ static void init_internal_icons(void) def_internal_vicon(VICO_KEYTYPE_BREAKDOWN_VEC, vicon_keytype_breakdown_draw); def_internal_vicon(VICO_KEYTYPE_EXTREME_VEC, vicon_keytype_extreme_draw); def_internal_vicon(VICO_KEYTYPE_JITTER_VEC, vicon_keytype_jitter_draw); + + def_internal_vicon(VICO_COLORSET_01_VEC, vicon_colorset_draw_01); + def_internal_vicon(VICO_COLORSET_02_VEC, vicon_colorset_draw_02); + def_internal_vicon(VICO_COLORSET_03_VEC, vicon_colorset_draw_03); + def_internal_vicon(VICO_COLORSET_04_VEC, vicon_colorset_draw_04); + def_internal_vicon(VICO_COLORSET_05_VEC, vicon_colorset_draw_05); + def_internal_vicon(VICO_COLORSET_06_VEC, vicon_colorset_draw_06); + def_internal_vicon(VICO_COLORSET_07_VEC, vicon_colorset_draw_07); + def_internal_vicon(VICO_COLORSET_08_VEC, vicon_colorset_draw_08); + def_internal_vicon(VICO_COLORSET_09_VEC, vicon_colorset_draw_09); + def_internal_vicon(VICO_COLORSET_10_VEC, vicon_colorset_draw_10); + def_internal_vicon(VICO_COLORSET_11_VEC, vicon_colorset_draw_11); + def_internal_vicon(VICO_COLORSET_12_VEC, vicon_colorset_draw_12); + def_internal_vicon(VICO_COLORSET_13_VEC, vicon_colorset_draw_13); + def_internal_vicon(VICO_COLORSET_14_VEC, vicon_colorset_draw_14); + def_internal_vicon(VICO_COLORSET_15_VEC, vicon_colorset_draw_15); + def_internal_vicon(VICO_COLORSET_16_VEC, vicon_colorset_draw_16); + def_internal_vicon(VICO_COLORSET_17_VEC, vicon_colorset_draw_17); + def_internal_vicon(VICO_COLORSET_18_VEC, vicon_colorset_draw_18); + def_internal_vicon(VICO_COLORSET_19_VEC, vicon_colorset_draw_19); + def_internal_vicon(VICO_COLORSET_20_VEC, vicon_colorset_draw_20); IMB_freeImBuf(b16buf); IMB_freeImBuf(b32buf); @@ -1342,7 +1420,7 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) Object *ob = CTX_data_active_object(C); SpaceImage *sima; EnumPropertyItem *items = NULL; - int tool, mode = 0; + int tool = PAINT_TOOL_DRAW, mode = 0; /* XXX: this is not nice, should probably make brushes * be strictly in one paint mode only to avoid diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index bacae0a28c6..7a9c3e827cf 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -557,7 +557,7 @@ static void UI_OT_copy_to_selected_button(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array"); + RNA_def_boolean(ot->srna, "all", true, "All", "Copy to selected all elements of the array"); } /* Reports to Textblock Operator ------------------------ */ diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 2fdd84b626b..62b373c58c8 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1679,7 +1679,7 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) BLF_size(fontid, fstyle_points, U.dpi); BLF_enable(fontid, BLF_SHADOW); - BLF_shadow(fontid, 3, 1.0f, 1.0f, 1.0f, 0.25f); + BLF_shadow(fontid, 3, (const float[4]){1.0f, 1.0f, 1.0f, 0.25f}); BLF_shadow_offset(fontid, -1, -1); BLI_assert(UI_panel_category_is_visible(ar)); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index abd395afbe0..9e49d7e7e90 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -1788,10 +1788,20 @@ void ui_popup_block_scrolltest(uiBlock *block) static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle) { - ui_region_temp_remove(C, CTX_wm_screen(C), handle->region); + wmWindow *win = CTX_wm_window(C); + bScreen *sc = CTX_wm_screen(C); + + ui_region_temp_remove(C, sc, handle->region); + + /* reset to region cursor (only if there's not another menu open) */ + if (BLI_listbase_is_empty(&sc->regionbase)) { + ED_region_cursor_set(win, CTX_wm_area(C), CTX_wm_region(C)); + /* in case cursor needs to be changed again */ + WM_event_add_mousemove(C); + } if (handle->scrolltimer) - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer); + WM_event_remove_timer(CTX_wm_manager(C), win, handle->scrolltimer); } /** @@ -1965,11 +1975,19 @@ uiPopupBlockHandle *ui_popup_block_create( void *arg) { wmWindow *window = CTX_wm_window(C); + uiBut *activebut = UI_context_active_but_get(C); static ARegionType type; ARegion *ar; uiBlock *block; uiPopupBlockHandle *handle; + /* disable tooltips from buttons below */ + if (activebut) { + UI_but_tooltip_timer_remove(C, activebut); + } + /* standard cursor by default */ + WM_cursor_set(window, CURSOR_STD); + /* create handle */ handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle"); @@ -2988,7 +3006,8 @@ int UI_pie_menu_invoke(struct bContext *C, const char *idname, const wmEvent *ev } if (mt->poll && mt->poll(C, mt) == 0) - return OPERATOR_CANCELLED; + /* cancel but allow event to pass through, just like operators do */ + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); pie = UI_pie_menu_begin(C, IFACE_(mt->label), ICON_NONE, event); layout = UI_pie_menu_layout(pie); @@ -3219,7 +3238,8 @@ int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) } if (mt->poll && mt->poll(C, mt) == 0) - return OPERATOR_CANCELLED; + /* cancel but allow event to pass through, just like operators do */ + return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); pup = UI_popup_menu_begin(C, IFACE_(mt->label), ICON_NONE); layout = UI_popup_menu_layout(pup); diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index bfde02dbefd..423c48e5f55 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -160,7 +160,8 @@ void UI_fontstyle_draw_ex( /* set the flag */ if (fs->shadow) { font_flag |= BLF_SHADOW; - BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha); + const float shadow_color[4] = {fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha}; + BLF_shadow(fs->uifont_id, fs->shadow, shadow_color); BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady); } if (fs->kerning == 1) { @@ -251,7 +252,8 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs, const rcti *rect, const ch if (fs->shadow) { BLF_enable(fs->uifont_id, BLF_SHADOW); - BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha); + const float shadow_color[4] = {fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha}; + BLF_shadow(fs->uifont_id, fs->shadow, shadow_color); BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady); } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index a47d60812b4..a3b04e1c9bc 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2947,7 +2947,11 @@ void uiTemplateList( /* We tag the list id with the list type... */ BLI_snprintf(ui_list_id, sizeof(ui_list_id), "%s_%s", ui_list_type->idname, list_id ? list_id : ""); - ar = CTX_wm_region(C); + /* Allows to work in popups. */ + ar = CTX_wm_menu(C); + if (ar == NULL) { + ar = CTX_wm_region(C); + } ui_list = BLI_findstring(&ar->ui_lists, ui_list_id, offsetof(uiList, list_id)); if (!ui_list) { diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 841b76c676b..19e0b55374e 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -859,10 +859,17 @@ static void widget_draw_icon( else if (but->flag & UI_ACTIVE) {} else alpha = 0.5f; } - - /* extra feature allows more alpha blending */ - if ((but->type == UI_BTYPE_LABEL) && but->a1 == 1.0f) - alpha *= but->a2; + else if ((but->type == UI_BTYPE_LABEL)) { + /* extra feature allows more alpha blending */ + if (but->a1 == 1.0f) { + alpha *= but->a2; + } + } + else if (ELEM(but->type, UI_BTYPE_BUT)) { + if (but->flag & UI_BUT_DISABLED) { + alpha *= 0.5f; + } + } glEnable(GL_BLEND); @@ -3042,6 +3049,15 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat wcol->shaded = 0; wcol->alpha_check = (wcol->inner[3] < 255); + + if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + /* Now we reduce alpha of the inner color (i.e. the color shown) + * so that this setting can look greyed out, while retaining + * the checkboard (for transparent values). This is needed + * here as the effects of ui_widget_color_disabled() are overwritten. + */ + wcol->inner[3] /= 2; + } widgetbase_draw(&wtb, wcol); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index b1d2e329da1..e2e2413c717 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -96,7 +96,7 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo static char setting = 0; const char *cp = error; - /* ensure we're not getting a color after running BKE_userdef_free */ + /* ensure we're not getting a color after running BKE_blender_userdef_free */ BLI_assert(BLI_findindex(&U.themes, theme_active) != -1); BLI_assert(colorid != TH_UNDEFINED); @@ -786,9 +786,14 @@ static void ui_theme_init_new_do(ThemeSpace *ts) rgba_char_args_set(ts->panel_text_hi, 255, 255, 255, 255); #endif + ts->panelcolors.show_back = false; + ts->panelcolors.show_header = false; + rgba_char_args_set(ts->panelcolors.back, 114, 114, 114, 128); + rgba_char_args_set(ts->panelcolors.header, 0, 0, 0, 25); + rgba_char_args_set(ts->button, 145, 145, 145, 245); rgba_char_args_set(ts->button_title, 0, 0, 0, 255); - rgba_char_args_set(ts->button_text, 0, 0, 0, 255); + rgba_char_args_set(ts->button_text, 0, 0, 0, 255); rgba_char_args_set(ts->button_text_hi, 255, 255, 255, 255); rgba_char_args_set(ts->list, 165, 165, 165, 255); @@ -848,14 +853,9 @@ void ui_theme_init_default(void) /* UI buttons */ ui_widget_color_init(&btheme->tui); - + btheme->tui.iconfile[0] = 0; - btheme->tui.panel.show_back = false; - btheme->tui.panel.show_header = false; - rgba_char_args_set(btheme->tui.panel.header, 0, 0, 0, 25); - rgba_char_args_set(btheme->tui.wcol_tooltip.text, 255, 255, 255, 255); - rgba_char_args_set_fl(btheme->tui.widget_emboss, 1.0f, 1.0f, 1.0f, 0.02f); rgba_char_args_set(btheme->tui.xaxis, 220, 0, 0, 255); @@ -872,10 +872,6 @@ void ui_theme_init_default(void) ui_theme_init_new(btheme); /* space view3d */ - btheme->tv3d.panelcolors.show_back = false; - btheme->tv3d.panelcolors.show_header = false; - rgba_char_args_set_fl(btheme->tv3d.panelcolors.back, 0.45, 0.45, 0.45, 0.5); - rgba_char_args_set_fl(btheme->tv3d.panelcolors.header, 0, 0, 0, 0.01); rgba_char_args_set_fl(btheme->tv3d.back, 0.225, 0.225, 0.225, 1.0); rgba_char_args_set(btheme->tv3d.text, 0, 0, 0, 255); rgba_char_args_set(btheme->tv3d.text_hi, 255, 255, 255, 255); diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 822bb429f9e..e3e8f35e7d8 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -194,12 +194,12 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, /******************** add vertex *********************/ -static void setup_vertex_point(Mask *mask, MaskSpline *spline, MaskSplinePoint *new_point, - const float point_co[2], const float u, - MaskSplinePoint *reference_point, const bool reference_adjacent) +static void setup_vertex_point( + Mask *mask, MaskSpline *spline, MaskSplinePoint *new_point, + const float point_co[2], const float u, const float ctime, + const MaskSplinePoint *reference_point, const bool reference_adjacent) { - MaskSplinePoint *prev_point = NULL; - MaskSplinePoint *next_point = NULL; + const MaskSplinePoint *reference_parent_point = NULL; BezTriple *bezt; float co[3]; @@ -247,14 +247,44 @@ static void setup_vertex_point(Mask *mask, MaskSpline *spline, MaskSplinePoint * else { bezt->h1 = bezt->h2 = MAX2(reference_point->bezt.h2, reference_point->bezt.h1); } + + reference_parent_point = reference_point; } else if (reference_adjacent) { if (spline->tot_point != 1) { - int index = (int)(new_point - spline->points); - prev_point = &spline->points[(index - 1) % spline->tot_point]; - next_point = &spline->points[(index + 1) % spline->tot_point]; + MaskSplinePoint *prev_point, *next_point, *close_point; - bezt->h1 = bezt->h2 = MAX2(prev_point->bezt.h2, next_point->bezt.h1); + const int index = (int)(new_point - spline->points); + if (spline->flag & MASK_SPLINE_CYCLIC) { + prev_point = &spline->points[mod_i(index - 1, spline->tot_point)]; + next_point = &spline->points[mod_i(index + 1, spline->tot_point)]; + } + else { + prev_point = (index != 0) ? &spline->points[index - 1] : NULL; + next_point = (index != spline->tot_point - 1) ? &spline->points[index + 1] : NULL; + } + + if (prev_point && next_point) { + close_point = (len_squared_v2v2(new_point->bezt.vec[1], prev_point->bezt.vec[1]) < + len_squared_v2v2(new_point->bezt.vec[1], next_point->bezt.vec[1])) ? + prev_point : next_point; + } + else { + close_point = prev_point ? prev_point : next_point; + } + + /* handle type */ + char handle_type = 0; + if (prev_point) { + handle_type = prev_point->bezt.h2; + } + if (next_point) { + handle_type = MAX2(next_point->bezt.h2, handle_type); + } + bezt->h1 = bezt->h2 = handle_type; + + /* parent */ + reference_parent_point = close_point; /* note, we may want to copy other attributes later, radius? pressure? color? */ } @@ -264,7 +294,20 @@ static void setup_vertex_point(Mask *mask, MaskSpline *spline, MaskSplinePoint * copy_v3_v3(bezt->vec[1], co); copy_v3_v3(bezt->vec[2], co); - BKE_mask_parent_init(&new_point->parent); + if (reference_parent_point) { + new_point->parent = reference_parent_point->parent; + + if (new_point->parent.id) { + float parent_matrix[3][3]; + BKE_mask_point_parent_matrix_get(new_point, ctime, parent_matrix); + invert_m3(parent_matrix); + mul_m3_v2(parent_matrix, new_point->bezt.vec[1]); + } + } + else { + BKE_mask_parent_init(&new_point->parent); + } + if (spline->tot_point != 1) { BKE_mask_calc_handle_adjacent_interp(spline, new_point, u); } @@ -348,6 +391,9 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 if (ED_mask_find_nearest_diff_point(C, mask, co, threshold, false, tangent, true, true, &masklay, &spline, &point, &u, NULL)) { + Scene *scene = CTX_data_scene(C); + const float ctime = CFRA; + MaskSplinePoint *new_point; int point_index = point - spline->points; @@ -357,7 +403,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 new_point = &spline->points[point_index + 1]; - setup_vertex_point(mask, spline, new_point, co, u, NULL, true); + setup_vertex_point(mask, spline, new_point, co, u, ctime, NULL, true); /* TODO - we could pass the spline! */ BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index + 1, true, true); @@ -375,6 +421,9 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 static bool add_vertex_extrude(const bContext *C, Mask *mask, MaskLayer *masklay, const float co[2]) { + Scene *scene = CTX_data_scene(C); + const float ctime = CFRA; + MaskSpline *spline; MaskSplinePoint *point; MaskSplinePoint *new_point = NULL, *ref_point = NULL; @@ -454,7 +503,7 @@ static bool add_vertex_extrude(const bContext *C, Mask *mask, MaskLayer *masklay masklay->act_point = new_point; - setup_vertex_point(mask, spline, new_point, co, 0.5f, ref_point, false); + setup_vertex_point(mask, spline, new_point, co, 0.5f, ctime, ref_point, false); if (masklay->splines_shapes.first) { point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point); @@ -468,34 +517,28 @@ static bool add_vertex_extrude(const bContext *C, Mask *mask, MaskLayer *masklay static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *masklay, const float co[2]) { + Scene *scene = CTX_data_scene(C); + const float ctime = CFRA; + MaskSpline *spline; - MaskSplinePoint *point; MaskSplinePoint *new_point = NULL, *ref_point = NULL; if (!masklay) { /* if there's no masklay currently operationg on, create new one */ masklay = BKE_mask_layer_new(mask, ""); mask->masklay_act = mask->masklay_tot - 1; - spline = NULL; - point = NULL; - } - else { - finSelectedSplinePoint(masklay, &spline, &point, true); } ED_mask_select_toggle_all(mask, SEL_DESELECT); - if (!spline) { - /* no selected splines in active masklay, create new spline */ - spline = BKE_mask_spline_add(masklay); - } + spline = BKE_mask_spline_add(masklay); masklay->act_spline = spline; new_point = spline->points; masklay->act_point = new_point; - setup_vertex_point(mask, spline, new_point, co, 0.5f, ref_point, false); + setup_vertex_point(mask, spline, new_point, co, 0.5f, ctime, ref_point, false); { int point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point); diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 847a7b5336e..242cbf79a83 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -44,6 +44,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "UI_interface.h" + #include "ED_mesh.h" #include "ED_numinput.h" #include "ED_screen.h" @@ -69,16 +71,15 @@ typedef struct { BMBackup mesh_backup; void *draw_handle_pixel; short twtype; + float segments; /* Segments as float so smooth mouse pan works in small increments */ } BevelData; -#define HEADER_LENGTH 180 - static void edbm_bevel_update_header(bContext *C, wmOperator *op) { const char *str = IFACE_("Confirm: (Enter/LMB), Cancel: (Esc/RMB), Mode: %s (M), Clamp Overlap: %s (C), " "Vertex Only: %s (V), Offset: %s, Segments: %d"); - char msg[HEADER_LENGTH]; + char msg[UI_MAX_DRAW_STR]; ScrArea *sa = CTX_wm_area(C); Scene *sce = CTX_data_scene(C); @@ -97,7 +98,7 @@ static void edbm_bevel_update_header(bContext *C, wmOperator *op) RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &type_str); - BLI_snprintf(msg, HEADER_LENGTH, str, type_str, + BLI_snprintf(msg, sizeof(msg), str, type_str, WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")), WM_bool_as_string(RNA_boolean_get(op->ptr, "vertex_only")), offset_str, RNA_int_get(op->ptr, "segments")); @@ -137,8 +138,11 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) opdata->mesh_backup = EDBM_redo_state_store(em); opdata->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); G.moving = G_TRANSFORM_EDIT; - opdata->twtype = v3d->twtype; - v3d->twtype = 0; + + if (v3d) { + opdata->twtype = v3d->twtype; + v3d->twtype = 0; + } } return true; @@ -206,7 +210,9 @@ static void edbm_bevel_exit(bContext *C, wmOperator *op) ARegion *ar = CTX_wm_region(C); EDBM_redo_state_free(&opdata->mesh_backup, NULL, false); ED_region_draw_cb_exit(ar->type, opdata->draw_handle_pixel); - v3d->twtype = opdata->twtype; + if (v3d) { + v3d->twtype = opdata->twtype; + } G.moving = 0; } MEM_freeN(opdata); @@ -327,7 +333,6 @@ static float edbm_bevel_mval_factor(wmOperator *op, const wmEvent *event) static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) { BevelData *opdata = op->customdata; - int segments = RNA_int_get(op->ptr, "segments"); const bool has_numinput = hasNumInput(&opdata->num_input); /* Modal numinput active, try to handle numeric inputs first... */ @@ -368,6 +373,19 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) } break; + case MOUSEPAN: { + float delta = 0.02f * (event->y - event->prevy); + if (opdata->segments >= 1 && opdata->segments + delta < 1) + opdata->segments = 1; + else + opdata->segments += delta; + RNA_int_set(op->ptr, "segments", (int)opdata->segments); + edbm_bevel_calc(op); + edbm_bevel_update_header(C, op); + handled = true; + break; + } + /* Note this will prevent padplus and padminus to ever activate modal numinput. * This is not really an issue though, as we only expect positive values here... * Else we could force them to only modify segments number when shift is pressed, or so. @@ -378,8 +396,8 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->val == KM_RELEASE) break; - segments++; - RNA_int_set(op->ptr, "segments", segments); + opdata->segments = opdata->segments + 1; + RNA_int_set(op->ptr, "segments", (int)opdata->segments); edbm_bevel_calc(op); edbm_bevel_update_header(C, op); handled = true; @@ -390,8 +408,8 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->val == KM_RELEASE) break; - segments = max_ii(segments - 1, 1); - RNA_int_set(op->ptr, "segments", segments); + opdata->segments = max_ff(opdata->segments - 1, 1); + RNA_int_set(op->ptr, "segments", (int)opdata->segments); edbm_bevel_calc(op); edbm_bevel_update_header(C, op); handled = true; diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 161159d0be0..1dfb1cddd2c 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -673,7 +673,7 @@ void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot) /* api callbacks */ ot->invoke = edbm_dupli_extrude_cursor_invoke; - ot->poll = ED_operator_editmesh; + ot->poll = ED_operator_editmesh_region_view3d; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -732,8 +732,17 @@ static int edbm_spin_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = ED_view3d_context_rv3d(C); - RNA_float_set_array(op->ptr, "center", ED_view3d_cursor3d_get(scene, v3d)); - RNA_float_set_array(op->ptr, "axis", rv3d->viewinv[2]); + PropertyRNA *prop; + prop = RNA_struct_find_property(op->ptr, "center"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_set_array(op->ptr, prop, ED_view3d_cursor3d_get(scene, v3d)); + } + if (rv3d) { + prop = RNA_struct_find_property(op->ptr, "axis"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_set_array(op->ptr, prop, rv3d->viewinv[2]); + } + } return edbm_spin_exec(C, op); } @@ -859,8 +868,17 @@ static int edbm_screw_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = ED_view3d_context_rv3d(C); - RNA_float_set_array(op->ptr, "center", ED_view3d_cursor3d_get(scene, v3d)); - RNA_float_set_array(op->ptr, "axis", rv3d->viewinv[1]); + PropertyRNA *prop; + prop = RNA_struct_find_property(op->ptr, "center"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_set_array(op->ptr, prop, ED_view3d_cursor3d_get(scene, v3d)); + } + if (rv3d) { + prop = RNA_struct_find_property(op->ptr, "axis"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_set_array(op->ptr, prop, rv3d->viewinv[1]); + } + } return edbm_screw_exec(C, op); } diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index 937547c99ef..3e0747f055f 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -44,6 +44,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "UI_interface.h" + #include "ED_mesh.h" #include "ED_numinput.h" #include "ED_screen.h" @@ -54,8 +56,6 @@ #include "mesh_intern.h" /* own include */ -#define HEADER_LENGTH 180 - typedef struct { float old_thickness; float old_depth; @@ -83,7 +83,7 @@ static void edbm_inset_update_header(wmOperator *op, bContext *C) const char *str = IFACE_("Confirm: Enter/LClick, Cancel: (Esc/RClick), Thickness: %s, " "Depth (Ctrl to tweak): %s (%s), Outset (O): (%s), Boundary (B): (%s), Individual (I): (%s)"); - char msg[HEADER_LENGTH]; + char msg[UI_MAX_DRAW_STR]; ScrArea *sa = CTX_wm_area(C); Scene *sce = CTX_data_scene(C); @@ -95,7 +95,7 @@ static void edbm_inset_update_header(wmOperator *op, bContext *C) BLI_snprintf(flts_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "thickness")); BLI_snprintf(flts_str + NUM_STR_REP_LEN, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "depth")); } - BLI_snprintf(msg, HEADER_LENGTH, str, + BLI_snprintf(msg, sizeof(msg), str, flts_str, flts_str + NUM_STR_REP_LEN, WM_bool_as_string(opdata->modify_depth), @@ -143,8 +143,10 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal) opdata->mesh_backup = EDBM_redo_state_store(em); opdata->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); G.moving = G_TRANSFORM_EDIT; - opdata->twtype = v3d->twtype; - v3d->twtype = 0; + if (v3d) { + opdata->twtype = v3d->twtype; + v3d->twtype = 0; + } } return true; @@ -162,7 +164,9 @@ static void edbm_inset_exit(bContext *C, wmOperator *op) ARegion *ar = CTX_wm_region(C); EDBM_redo_state_free(&opdata->mesh_backup, NULL, false); ED_region_draw_cb_exit(ar->type, opdata->draw_handle_pixel); - v3d->twtype = opdata->twtype; + if (v3d) { + v3d->twtype = opdata->twtype; + } G.moving = 0; } diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index c35a36069a7..f1c1e4105d0 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -47,6 +47,8 @@ #include "BIF_gl.h" +#include "UI_interface.h" + #include "ED_screen.h" #include "ED_space_api.h" #include "ED_view3d.h" @@ -87,6 +89,9 @@ typedef struct RingSelOpData { bool extend; bool do_cut; + + float cuts; /* cuts as float so smooth mouse pan works in small increments */ + float smoothness; } RingSelOpData; /* modal loop selection drawing callback */ @@ -501,6 +506,8 @@ static int ringsel_init(bContext *C, wmOperator *op, bool do_cut) lcd->em = BKE_editmesh_from_object(lcd->ob); lcd->extend = do_cut ? false : RNA_boolean_get(op->ptr, "extend"); lcd->do_cut = do_cut; + lcd->cuts = RNA_int_get(op->ptr, "number_cuts"); + lcd->smoothness = RNA_float_get(op->ptr, "smoothness"); initNumInput(&lcd->num); lcd->num.idx_max = 1; @@ -648,9 +655,9 @@ static int loopcut_finish(RingSelOpData *lcd, bContext *C, wmOperator *op) static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) { - float smoothness = RNA_float_get(op->ptr, "smoothness"); - int cuts = RNA_int_get(op->ptr, "number_cuts"); RingSelOpData *lcd = op->customdata; + float cuts = lcd->cuts; + float smoothness = lcd->smoothness; bool show_cuts = false; const bool has_numinput = hasNumInput(&lcd->num); @@ -662,20 +669,10 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) /* using the keyboard to input the number of cuts */ /* Modal numinput active, try to handle numeric inputs first... */ if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &lcd->num, event)) { - float values[2] = {(float)cuts, smoothness}; + float values[2] = {cuts, smoothness}; applyNumInput(&lcd->num, values); - - /* allow zero so you can backspace and type in a value - * otherwise 1 as minimum would make more sense */ - cuts = CLAMPIS(values[0], 0, SUBD_CUTS_MAX); - smoothness = CLAMPIS(values[1], -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); - - RNA_int_set(op->ptr, "number_cuts", cuts); - ringsel_find_edge(lcd, cuts); - show_cuts = true; - RNA_float_set(op->ptr, "smoothness", smoothness); - - ED_region_tag_redraw(lcd->ar); + cuts = values[0]; + smoothness = values[1]; } else { bool handled = false; @@ -708,25 +705,28 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_region_tag_redraw(lcd->ar); handled = true; break; + case MOUSEPAN: + if (event->alt == 0) { + cuts += 0.02f * (event->y - event->prevy); + if (cuts < 1 && lcd->cuts >= 1) + cuts = 1; + } + else { + smoothness += 0.002f * (event->y - event->prevy); + } + handled = true; + break; case PADPLUSKEY: case PAGEUPKEY: case WHEELUPMOUSE: /* change number of cuts */ if (event->val == KM_RELEASE) break; if (event->alt == 0) { - cuts++; - cuts = CLAMPIS(cuts, 0, SUBD_CUTS_MAX); - RNA_int_set(op->ptr, "number_cuts", cuts); - ringsel_find_edge(lcd, cuts); - show_cuts = true; + cuts += 1; } else { - smoothness = min_ff(smoothness + 0.05f, SUBD_SMOOTH_MAX); - RNA_float_set(op->ptr, "smoothness", smoothness); - show_cuts = true; + smoothness += 0.05f; } - - ED_region_tag_redraw(lcd->ar); handled = true; break; case PADMINUS: @@ -734,27 +734,19 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) case WHEELDOWNMOUSE: /* change number of cuts */ if (event->val == KM_RELEASE) break; - if (event->alt == 0) { - cuts = max_ii(cuts - 1, 1); - RNA_int_set(op->ptr, "number_cuts", cuts); - ringsel_find_edge(lcd, cuts); - show_cuts = true; + cuts = max_ff(cuts - 1, 1); } else { - smoothness = max_ff(smoothness - 0.05f, -SUBD_SMOOTH_MAX); - RNA_float_set(op->ptr, "smoothness", smoothness); - show_cuts = true; + smoothness -= 0.05f; } - - ED_region_tag_redraw(lcd->ar); handled = true; break; case MOUSEMOVE: /* mouse moved somewhere to select another loop */ if (!has_numinput) { lcd->vc.mval[0] = event->mval[0]; lcd->vc.mval[1] = event->mval[1]; - loopcut_mouse_move(lcd, cuts); + loopcut_mouse_move(lcd, (int)lcd->cuts); ED_region_tag_redraw(lcd->ar); handled = true; @@ -764,32 +756,39 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Modal numinput inactive, try to handle numeric inputs last... */ if (!handled && event->val == KM_PRESS && handleNumInput(C, &lcd->num, event)) { - float values[2] = {(float)cuts, smoothness}; + float values[2] = {cuts, smoothness}; applyNumInput(&lcd->num, values); + cuts = values[0]; + smoothness = values[1]; + } + } - /* allow zero so you can backspace and type in a value - * otherwise 1 as minimum would make more sense */ - cuts = CLAMPIS(values[0], 0, SUBD_CUTS_MAX); - smoothness = CLAMPIS(values[1], -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); - - RNA_int_set(op->ptr, "number_cuts", cuts); - ringsel_find_edge(lcd, cuts); - show_cuts = true; - RNA_float_set(op->ptr, "smoothness", smoothness); + if (cuts != lcd->cuts) { + /* allow zero so you can backspace and type in a value + * otherwise 1 as minimum would make more sense */ + lcd->cuts = CLAMPIS(cuts, 0, SUBD_CUTS_MAX); + RNA_int_set(op->ptr, "number_cuts", (int)lcd->cuts); + ringsel_find_edge(lcd, (int)lcd->cuts); + show_cuts = true; + ED_region_tag_redraw(lcd->ar); + } - ED_region_tag_redraw(lcd->ar); - } + if (smoothness != lcd->smoothness) { + lcd->smoothness = CLAMPIS(smoothness, -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); + RNA_float_set(op->ptr, "smoothness", lcd->smoothness); + show_cuts = true; + ED_region_tag_redraw(lcd->ar); } if (show_cuts) { Scene *sce = CTX_data_scene(C); - char buf[64 + NUM_STR_REP_LEN * 2]; + char buf[UI_MAX_DRAW_STR]; char str_rep[NUM_STR_REP_LEN * 2]; if (hasNumInput(&lcd->num)) { outputNumInput(&lcd->num, str_rep, &sce->unit); } else { - BLI_snprintf(str_rep, NUM_STR_REP_LEN, "%d", cuts); + BLI_snprintf(str_rep, NUM_STR_REP_LEN, "%d", (int)lcd->cuts); BLI_snprintf(str_rep + NUM_STR_REP_LEN, NUM_STR_REP_LEN, "%.2f", smoothness); } BLI_snprintf(buf, sizeof(buf), IFACE_("Number of Cuts: %s, Smooth: %s (Alt)"), diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index 55ed0e521ea..4431712e720 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -588,6 +588,17 @@ static BMElem *edbm_elem_find_nearest(ViewContext *vc, const char htype) return NULL; } +static BMElem *edbm_elem_active_elem_or_face_get(BMesh *bm) +{ + BMElem *ele = BM_mesh_active_elem_get(bm); + + if ((ele == NULL) && bm->act_face && BM_elem_flag_test(bm->act_face, BM_ELEM_SELECT)) { + ele = (BMElem *)bm->act_face; + } + + return ele; +} + static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) { if (RNA_struct_property_is_set(op->ptr, "index")) { @@ -605,7 +616,7 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE view3d_operator_needs_opengl(C); BMElem *ele_src, *ele_dst; - if (!(ele_src = BM_mesh_active_elem_get(em->bm)) || + if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) || !(ele_dst = edbm_elem_find_nearest(&vc, ele_src->head.htype))) { /* special case, toggle edge tags even when we don't have a path */ @@ -655,7 +666,7 @@ static int edbm_shortest_path_pick_exec(bContext *C, wmOperator *op) } BMElem *ele_src, *ele_dst; - if (!(ele_src = BM_mesh_active_elem_get(em->bm)) || + if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) || !(ele_dst = EDBM_elem_from_index_any(em, index))) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index c7a7828f3b2..9e71e646b1a 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -77,14 +77,14 @@ static float edbm_rip_edgedist_squared( const float dist_2d = len_v2v2(vec1, vec2); if (dist_2d > FLT_EPSILON) { const float dist = inset / dist_2d; - BLI_assert(finite(dist)); + BLI_assert(isfinite(dist)); interp_v2_v2v2(vec1, vec1, vec2, dist); interp_v2_v2v2(vec2, vec2, vec1, dist); } } dist_sq = dist_squared_to_line_segment_v2(mvalf, vec1, vec2); - BLI_assert(finite(dist_sq)); + BLI_assert(isfinite(dist_sq)); return dist_sq; } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 17250fbfb09..84ae35fae6e 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -2430,13 +2430,10 @@ static void select_linked_delimit_validate(BMesh *bm, int *delimit) } } -static void select_linked_delimit_begin(BMesh *bm, short selectmode, int delimit) +static void select_linked_delimit_begin(BMesh *bm, int delimit) { struct DelimitData delimit_data = {0}; - BMIter iter; - BMEdge *e; - if (delimit & BMO_DELIM_UV) { delimit_data.cd_loop_type = CD_MLOOPUV; delimit_data.cd_loop_offset = CustomData_get_offset(&bm->ldata, delimit_data.cd_loop_type); @@ -2447,19 +2444,13 @@ static void select_linked_delimit_begin(BMesh *bm, short selectmode, int delimit /* grr, shouldn't need to alloc BMO flags here */ BM_mesh_elem_toolflags_ensure(bm); - if (selectmode == SCE_SELECT_FACE) { - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - const bool is_walk_ok = ( - (select_linked_delimit_test(e, delimit, &delimit_data) == false)); - BMO_elem_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok); - } - } - else { - /* don't delimit selected edges in vert/edge mode */ + { + BMIter iter; + BMEdge *e; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { const bool is_walk_ok = ( - BM_elem_flag_test(e, BM_ELEM_SELECT) || (select_linked_delimit_test(e, delimit, &delimit_data) == false)); BMO_elem_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok); @@ -2491,7 +2482,7 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) select_linked_delimit_validate(bm, &delimit); if (delimit) { - select_linked_delimit_begin(em->bm, em->selectmode, delimit); + select_linked_delimit_begin(em->bm, delimit); } if (em->selectmode & SCE_SELECT_VERTEX) { @@ -2501,6 +2492,17 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT)); } + /* exclude all delimited verts */ + if (delimit) { + BMEdge *e; + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + if (!BMO_elem_flag_test(bm, e, BMO_ELE_TAG)) { + BM_elem_flag_disable(e->v1, BM_ELEM_TAG); + BM_elem_flag_disable(e->v2, BM_ELEM_TAG); + } + } + } + BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, @@ -2546,8 +2548,17 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) else if (em->selectmode & SCE_SELECT_EDGE) { BMEdge *e; - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT)); + if (delimit) { + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + BM_elem_flag_set( + e, BM_ELEM_TAG, + (BM_elem_flag_test(e, BM_ELEM_SELECT) && BMO_elem_flag_test(bm, e, BMO_ELE_TAG))); + } + } + else { + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT)); + } } BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, @@ -2661,7 +2672,7 @@ static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, in select_linked_delimit_validate(bm, &delimit); if (delimit) { - select_linked_delimit_begin(bm, em->selectmode, delimit); + select_linked_delimit_begin(bm, delimit); } /* Note: logic closely matches 'edbm_select_linked_exec', keep in sync */ diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index ee33f5f1655..ba17684dd39 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -68,6 +68,7 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_transform.h" +#include "ED_transform_snap_object_context.h" #include "ED_uvedit.h" #include "ED_view3d.h" @@ -301,20 +302,30 @@ void EMBM_project_snap_verts(bContext *C, ARegion *ar, BMEditMesh *em) ED_view3d_init_mats_rv3d(obedit, ar->regiondata); + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), CTX_data_scene(C), SNAP_OBJECT_USE_CACHE, + ar, CTX_wm_view3d(C)); + BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - float mval[2], co_proj[3], no_dummy[3]; - float dist_px_dummy; + float mval[2], co_proj[3]; if (ED_view3d_project_float_object(ar, eve->co, mval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - if (snapObjectsContext( - C, mval, SNAP_NOT_OBEDIT, - co_proj, no_dummy, &dist_px_dummy)) + if (ED_transform_snap_object_project_view3d_mixed( + snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_NOT_OBEDIT, + .snap_to_flag = SCE_SELECT_FACE, + }, + mval, NULL, true, + co_proj, NULL)) { mul_v3_m4v3(eve->co, obedit->imat, co_proj); } } } } + + ED_transform_snap_object_context_destroy(snap_context); } @@ -532,7 +543,7 @@ void MESH_OT_edge_collapse(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static int edbm_add_edge_face__smooth_get(BMesh *bm) +static bool edbm_add_edge_face__smooth_get(BMesh *bm) { BMEdge *e; BMIter iter; @@ -700,7 +711,7 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) BMOperator bmop; Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); - const short use_smooth = edbm_add_edge_face__smooth_get(em->bm); + const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm); const int totedge_orig = em->bm->totedge; const int totface_orig = em->bm->totface; /* when this is used to dissolve we could avoid this, but checking isnt too slow */ @@ -3001,7 +3012,7 @@ static Base *mesh_separate_tagged(Main *bmain, Scene *scene, Base *base_old, BMe BM_mesh_normals_update(bm_new); - BM_mesh_bm_to_me(bm_new, base_new->object->data, false); + BM_mesh_bm_to_me(bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm_new); ((Mesh *)base_new->object->data)->edit_btmesh = NULL; @@ -3284,7 +3295,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) bm_old = BM_mesh_create(&bm_mesh_allocsize_default); - BM_mesh_bm_from_me(bm_old, me, false, false, 0); + BM_mesh_bm_from_me(bm_old, me, (&(struct BMeshFromMeshParams){0})); switch (type) { case MESH_SEPARATE_MATERIAL: @@ -3299,7 +3310,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) } if (retval_iter) { - BM_mesh_bm_to_me(bm_old, me, false); + BM_mesh_bm_to_me(bm_old, me, (&(struct BMeshToMeshParams){0})); DAG_id_tag_update(&me->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); @@ -3566,7 +3577,7 @@ static int edbm_fill_grid_exec(bContext *C, wmOperator *op) BMOperator bmop; Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); - const short use_smooth = edbm_add_edge_face__smooth_get(em->bm); + const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm); const int totedge_orig = em->bm->totedge; const int totface_orig = em->bm->totface; const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple"); @@ -5114,6 +5125,17 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) "bridge_loops edges=%he use_pairs=%b use_cyclic=%b use_merge=%b merge_factor=%f twist_offset=%i", edge_hflag, use_pairs, use_cyclic, use_merge, merge_factor, twist_offset); + if (use_faces && totface_del) { + int i; + BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); + for (i = 0; i < totface_del; i++) { + BM_elem_flag_enable(totface_del_arr[i], BM_ELEM_TAG); + } + BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS, + "delete geom=%hf context=%i", + BM_ELEM_TAG, DEL_FACES_KEEP_BOUNDARY); + } + BMO_op_exec(em->bm, &bmop); if (!BMO_error_occurred(em->bm)) { @@ -5123,17 +5145,6 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); } - if (use_faces && totface_del) { - int i; - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); - for (i = 0; i < totface_del; i++) { - BM_elem_flag_enable(totface_del_arr[i], BM_ELEM_TAG); - } - BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS, - "delete geom=%hf context=%i", - BM_ELEM_TAG, DEL_FACES); - } - if (use_merge == false) { struct EdgeRingOpSubdProps op_props; mesh_operator_edgering_props_get(op, &op_props); diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 33f8455e614..82ec93c162f 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -347,7 +347,7 @@ void EDBM_selectmode_to_scene(bContext *C) WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, scene); } -void EDBM_mesh_make(ToolSettings *ts, Object *ob) +void EDBM_mesh_make(ToolSettings *ts, Object *ob, const bool add_key_index) { Mesh *me = ob->data; BMesh *bm; @@ -356,7 +356,7 @@ void EDBM_mesh_make(ToolSettings *ts, Object *ob) BKE_mesh_convert_mfaces_to_mpolys(me); } - bm = BKE_mesh_to_bmesh(me, ob); + bm = BKE_mesh_to_bmesh(me, ob, add_key_index); if (me->edit_btmesh) { /* this happens when switching shape keys */ @@ -395,7 +395,7 @@ void EDBM_mesh_load(Object *ob) bm->shapenr = 1; } - BM_mesh_bm_to_me(bm, me, false); + BM_mesh_bm_to_me(bm, me, (&(struct BMeshToMeshParams){0})); #ifdef USE_TESSFACE_DEFAULT BKE_mesh_tessface_calc(me); @@ -533,7 +533,10 @@ static void *editbtMesh_to_undoMesh(void *emv, void *obdata) /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ - BM_mesh_bm_to_me(em->bm, &um->me, false); + BM_mesh_bm_to_me( + em->bm, &um->me, (&(struct BMeshToMeshParams){ + .cd_mask_extra = CD_MASK_SHAPE_KEYINDEX, + })); um->selectmode = em->selectmode; um->shapenr = em->bm->shapenr; @@ -557,7 +560,10 @@ static void undoMesh_to_editbtMesh(void *umv, void *em_v, void *obdata) bm = BM_mesh_create(&allocsize); - BM_mesh_bm_from_me(bm, &um->me, true, false, um->shapenr); + BM_mesh_bm_from_me( + bm, &um->me, (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, .active_shapekey = um->shapenr, + })); em_tmp = BKE_editmesh_create(bm, true); *em = *em_tmp; diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index a2054a5f43c..e0ddc017e93 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -582,7 +582,7 @@ static int drop_named_image_invoke(bContext *C, wmOperator *op, const wmEvent *e obedit = base->object; me = obedit->data; if (me->edit_btmesh == NULL) { - EDBM_mesh_make(scene->toolsettings, obedit); + EDBM_mesh_make(scene->toolsettings, obedit, false); exitmode = 1; } if (me->edit_btmesh == NULL) diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index bad835b13e7..1b2d4b7c130 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -572,7 +572,7 @@ int join_mesh_exec(bContext *C, wmOperator *op) ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR | EM_DO_UNDO); #else /* toggle editmode using lower level functions so this can be called from python */ - EDBM_mesh_make(scene->toolsettings, ob); + EDBM_mesh_make(scene->toolsettings, ob, false); EDBM_mesh_load(ob); EDBM_mesh_free(me->edit_btmesh); MEM_freeN(me->edit_btmesh); @@ -811,9 +811,9 @@ static BMVert *editbmesh_get_x_mirror_vert_spatial(Object *ob, BMEditMesh *em, c int i; /* ignore nan verts */ - if ((finite(co[0]) == false) || - (finite(co[1]) == false) || - (finite(co[2]) == false)) + if ((isfinite(co[0]) == false) || + (isfinite(co[1]) == false) || + (isfinite(co[2]) == false)) { return NULL; } @@ -902,8 +902,8 @@ static float *editmesh_get_mirror_uv(BMEditMesh *em, int axis, float *uv, float float cent[2]; /* ignore nan verts */ - if (isnan(uv[0]) || !finite(uv[0]) || - isnan(uv[1]) || !finite(uv[1]) + if (isnan(uv[0]) || !isfinite(uv[0]) || + isnan(uv[1]) || !isfinite(uv[1]) ) { return NULL; diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index 888bf832042..11d96da5786 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -700,7 +700,7 @@ static void bake_startjob(void *bkv, short *stop, short *do_update, float *progr bkr->progress = progress; RE_test_break_cb(bkr->re, NULL, thread_break); - G.is_break = false; /* blender_test_break uses this global */ + G.is_break = false; /* BKE_blender_test_break uses this global */ RE_Database_Baking(bkr->re, bmain, scene, scene->lay, scene->r.bake_mode, bkr->actob); @@ -827,7 +827,7 @@ static int bake_image_exec(bContext *C, wmOperator *op) bkr.reports = op->reports; RE_test_break_cb(bkr.re, NULL, thread_break); - G.is_break = false; /* blender_test_break uses this global */ + G.is_break = false; /* BKE_blender_test_break uses this global */ RE_Database_Baking(bkr.re, bmain, scene, scene->lay, scene->r.bake_mode, (scene->r.bake_flag & R_BAKE_TO_ACTIVE) ? OBACT : NULL); @@ -843,7 +843,7 @@ static int bake_image_exec(bContext *C, wmOperator *op) /* used to redraw in 2.4x but this is just for exec in 2.5 */ if (!G.background) - blender_test_break(); + BKE_blender_test_break(); } BLI_end_threads(&threads); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 38f22beff8d..06200778ee5 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -316,6 +316,27 @@ void OBJECT_OT_hide_render_set(wmOperatorType *ot) /* ******************* toggle editmode operator ***************** */ +static bool mesh_needs_keyindex(const Mesh *me) +{ + if (me->key) { + return false; /* will be added */ + } + + for (const Object *ob = G.main->object.first; ob; ob = ob->id.next) { + if ((ob->parent) && (ob->parent->data == me) && ELEM(ob->partype, PARVERT1, PARVERT3)) { + return true; + } + if (ob->data == me) { + for (const ModifierData *md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Hook) { + return true; + } + } + } + } + return false; +} + /** * Load EditMode data back into the object, * optionally freeing the editmode data. @@ -494,15 +515,15 @@ void ED_object_editmode_enter(bContext *C, int flag) ok = 1; scene->obedit = ob; /* context sees this */ - EDBM_mesh_make(scene->toolsettings, ob); + const bool use_key_index = mesh_needs_keyindex(ob->data); + + EDBM_mesh_make(scene->toolsettings, ob, use_key_index); em = BKE_editmesh_from_object(ob); if (LIKELY(em)) { /* order doesn't matter */ EDBM_mesh_normals_update(em); BKE_editmesh_tessface_calc(em); - - BM_mesh_select_mode_flush(em->bm); } WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_MESH, scene); diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index 492b6724f93..438c10c51fa 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -316,7 +316,7 @@ static bool object_hook_index_array(Scene *scene, Object *obedit, BMEditMesh *em; EDBM_mesh_load(obedit); - EDBM_mesh_make(scene->toolsettings, obedit); + EDBM_mesh_make(scene->toolsettings, obedit, true); DAG_id_tag_update(obedit->data, 0); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 84cf97ecb7c..2f10f83e276 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -136,7 +136,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) BMEditMesh *em; EDBM_mesh_load(obedit); - EDBM_mesh_make(scene->toolsettings, obedit); + EDBM_mesh_make(scene->toolsettings, obedit, true); DAG_id_tag_update(obedit->data, 0); diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 4619f998a11..414cc476be5 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -2096,14 +2096,19 @@ static void dvert_mirror_op(MDeformVert *dvert, MDeformVert *dvert_mirr, MDeformWeight *dw = defvert_find_index(dvert, act_vgroup); MDeformWeight *dw_mirr = defvert_find_index(dvert_mirr, act_vgroup); - if (dw || dw_mirr) { - if (dw_mirr == NULL) - dw_mirr = defvert_verify_index(dvert_mirr, act_vgroup); - if (dw == NULL) - dw = defvert_verify_index(dvert, act_vgroup); - + if (dw && dw_mirr) { SWAP(float, dw->weight, dw_mirr->weight); } + else if (dw) { + dw_mirr = defvert_verify_index(dvert_mirr, act_vgroup); + dw_mirr->weight = dw->weight; + defvert_remove_group(dvert, dw); + } + else if (dw_mirr) { + dw = defvert_verify_index(dvert, act_vgroup); + dw->weight = dw_mirr->weight; + defvert_remove_group(dvert_mirr, dw_mirr); + } } } @@ -2197,28 +2202,34 @@ void ED_vgroup_mirror(Object *ob, EDBM_verts_mirror_cache_begin(em, 0, true, false, use_topology); + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + /* Go through the list of editverts and assign them */ BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if ((eve_mirr = EDBM_verts_mirror_get(em, eve))) { - if (eve_mirr != eve) { - sel = BM_elem_flag_test(eve, BM_ELEM_SELECT); - sel_mirr = BM_elem_flag_test(eve_mirr, BM_ELEM_SELECT); + if (!BM_elem_flag_test(eve, BM_ELEM_TAG)) { + if ((eve_mirr = EDBM_verts_mirror_get(em, eve))) { + if (eve_mirr != eve) { + if (!BM_elem_flag_test(eve_mirr, BM_ELEM_TAG)) { + sel = BM_elem_flag_test(eve, BM_ELEM_SELECT); + sel_mirr = BM_elem_flag_test(eve_mirr, BM_ELEM_SELECT); - if ((sel || sel_mirr) && (eve != eve_mirr)) { - dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); - dvert_mirr = BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset); + if ((sel || sel_mirr) && (eve != eve_mirr)) { + dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + dvert_mirr = BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset); - VGROUP_MIRR_OP; - totmirr++; + VGROUP_MIRR_OP; + totmirr++; + } + + /* don't use these again */ + BM_elem_flag_enable(eve, BM_ELEM_TAG); + BM_elem_flag_enable(eve_mirr, BM_ELEM_TAG); + } } } - - /* don't use these again */ - EDBM_verts_mirror_cache_clear(em, eve); - EDBM_verts_mirror_cache_clear(em, eve_mirr); - } - else { - totfail++; + else { + totfail++; + } } } EDBM_verts_mirror_cache_end(em); diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c index 7cc17e4bfea..a5d2d2c8be7 100644 --- a/source/blender/editors/physics/dynamicpaint_ops.c +++ b/source/blender/editors/physics/dynamicpaint_ops.c @@ -39,7 +39,6 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BKE_blender.h" #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_object_deform.h" @@ -426,7 +425,7 @@ static void dpaint_bake_startjob(void *customdata, short *stop, short *do_update job->start = PIL_check_seconds_timer(); job->success = 1; - G.is_break = false; /* reset blender_test_break*/ + G.is_break = false; /* reset BKE_blender_test_break*/ /* XXX annoying hack: needed to prevent data corruption when changing * scene frame in separate threads diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 29824a348f5..3da3a451d66 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -102,7 +102,7 @@ void update_world_cos(Object *ob, PTCacheEdit *edit); #define LOOP_SELECTED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if ((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE)) #define LOOP_TAGGED_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (key->flag & PEK_TAG) -#define KEY_WCO (key->flag & PEK_USE_WCO ? key->world_co : key->co) +#define KEY_WCO ((key->flag & PEK_USE_WCO) ? key->world_co : key->co) /**************************** utilities *******************************/ @@ -4111,7 +4111,9 @@ static bool shape_cut_test_point(PEData *data, ParticleCacheKey *key) userdata.bvhdata = data->shape_bvh; userdata.num_hits = 0; - BLI_bvhtree_ray_cast_all(shape_bvh->tree, key->co, dir, 0.0f, point_inside_bvh_cb, &userdata); + BLI_bvhtree_ray_cast_all( + shape_bvh->tree, key->co, dir, 0.0f, BVH_RAYCAST_DIST_MAX, + point_inside_bvh_cb, &userdata); /* for any point inside a watertight mesh the number of hits is uneven */ return (userdata.num_hits % 2) == 1; @@ -4809,3 +4811,113 @@ void PARTICLE_OT_edited_clear(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } +/************************ Unify length operator ************************/ + +static float calculate_point_length(PTCacheEditPoint *point) +{ + float length = 0.0f; + KEY_K; + LOOP_KEYS { + if (k > 0) { + length += len_v3v3((key - 1)->co, key->co); + } + } + return length; +} + +static float calculate_average_length(PTCacheEdit *edit) +{ + int num_selected = 0; + float total_length = 0; + POINT_P; + LOOP_SELECTED_POINTS { + total_length += calculate_point_length(point); + ++num_selected; + } + if (num_selected == 0) { + return 0.0f; + } + return total_length / num_selected; +} + +static void scale_point_factor(PTCacheEditPoint *point, float factor) +{ + float orig_prev_co[3], prev_co[3]; + KEY_K; + LOOP_KEYS { + if (k == 0) { + copy_v3_v3(orig_prev_co, key->co); + copy_v3_v3(prev_co, key->co); + } + else { + float new_co[3]; + float delta[3]; + + sub_v3_v3v3(delta, key->co, orig_prev_co); + mul_v3_fl(delta, factor); + add_v3_v3v3(new_co, prev_co, delta); + + copy_v3_v3(orig_prev_co, key->co); + copy_v3_v3(key->co, new_co); + copy_v3_v3(prev_co, key->co); + } + } + point->flag |= PEP_EDIT_RECALC; +} + +static void scale_point_to_length(PTCacheEditPoint *point, float length) +{ + const float point_length = calculate_point_length(point); + if (point_length != 0.0f) { + const float factor = length / point_length; + scale_point_factor(point, factor); + } +} + +static void scale_points_to_length(PTCacheEdit *edit, float length) +{ + POINT_P; + LOOP_SELECTED_POINTS { + scale_point_to_length(point, length); + } + recalc_lengths(edit); +} + +static int unify_length_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + PTCacheEdit *edit = PE_get_current(scene, ob); + float average_length = calculate_average_length(edit); + if (average_length == 0.0f) { + return OPERATOR_CANCELLED; + } + scale_points_to_length(edit, average_length); + + PE_update_object(scene, ob, 1); + if (edit->psys) { + WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob); + } + else { + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob); + } + + return OPERATOR_FINISHED; +} + +void PARTICLE_OT_unify_length(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unify Length"; + ot->idname = "PARTICLE_OT_unify_length"; + ot->description = "Make selected hair the same length"; + + /* api callbacks */ + ot->exec = unify_length_exec; + ot->poll = PE_poll_view3d; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} + diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h index 666ed2397d2..361c058647a 100644 --- a/source/blender/editors/physics/physics_intern.h +++ b/source/blender/editors/physics/physics_intern.h @@ -61,6 +61,8 @@ void PARTICLE_OT_shape_cut(struct wmOperatorType *ot); void PARTICLE_OT_particle_edit_toggle(struct wmOperatorType *ot); void PARTICLE_OT_edited_clear(struct wmOperatorType *ot); +void PARTICLE_OT_unify_length(struct wmOperatorType *ot); + /* particle_object.c */ void OBJECT_OT_particle_system_add(struct wmOperatorType *ot); void OBJECT_OT_particle_system_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c index c765bff796e..5074a41ad20 100644 --- a/source/blender/editors/physics/physics_ops.c +++ b/source/blender/editors/physics/physics_ops.c @@ -69,6 +69,8 @@ static void operatortypes_particle(void) WM_operatortype_append(PARTICLE_OT_particle_edit_toggle); WM_operatortype_append(PARTICLE_OT_edited_clear); + WM_operatortype_append(PARTICLE_OT_unify_length); + WM_operatortype_append(OBJECT_OT_particle_system_add); WM_operatortype_append(OBJECT_OT_particle_system_remove); diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index eb8ea01f750..1203889cf0e 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -49,7 +49,8 @@ #include "DNA_view3d_types.h" #include "DNA_userdef_types.h" -#include "BKE_blender.h" +#include "BKE_blender_undo.h" +#include "BKE_blender_version.h" #include "BKE_camera.h" #include "BKE_context.h" #include "BKE_colortools.h" diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 4ede89e1620..ab8b7d4e138 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -635,8 +635,8 @@ void ED_area_headerprint(ScrArea *sa, const char *str) if (ar->regiontype == RGN_TYPE_HEADER) { if (str) { if (ar->headerstr == NULL) - ar->headerstr = MEM_mallocN(256, "headerprint"); - BLI_strncpy(ar->headerstr, str, 256); + ar->headerstr = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint"); + BLI_strncpy(ar->headerstr, str, UI_MAX_DRAW_STR); } else if (ar->headerstr) { MEM_freeN(ar->headerstr); @@ -1507,6 +1507,16 @@ void ED_region_init(bContext *C, ARegion *ar) region_update_rect(ar); } +void ED_region_cursor_set(wmWindow *win, ScrArea *sa, ARegion *ar) +{ + if (ar && sa && ar->type && ar->type->cursor) { + ar->type->cursor(win, sa, ar); + } + else { + WM_cursor_set(win, CURSOR_STD); + } +} + /* for quick toggle, can skip fades */ void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade) { diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index b7ad911b6f5..cbf87062955 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -368,7 +368,11 @@ static int get_cached_work_texture(int *r_w, int *r_h) return texid; } -void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, float scaleX, float scaleY) +void glaDrawPixelsTexScaled_clipping(float x, float y, int img_w, int img_h, + int format, int type, int zoomfilter, void *rect, + float scaleX, float scaleY, + float clip_min_x, float clip_min_y, + float clip_max_x, float clip_max_y) { unsigned char *uc_rect = (unsigned char *) rect; const float *f_rect = (float *)rect; @@ -377,6 +381,7 @@ void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, int seamless, offset_x, offset_y, nsubparts_x, nsubparts_y; int texid = get_cached_work_texture(&tex_w, &tex_h); int components; + const bool use_clipping = ((clip_min_x < clip_max_x) && (clip_min_y < clip_max_y)); /* Specify the color outside this function, and tex will modulate it. * This is useful for changing alpha without using glPixelTransferf() @@ -443,11 +448,23 @@ void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, int offset_top = (seamless && remainder_y > tex_h) ? 1 : 0; float rast_x = x + subpart_x * offset_x * xzoom; float rast_y = y + subpart_y * offset_y * yzoom; - /* check if we already got these because we always get 2 more when doing seamless */ if (subpart_w <= seamless || subpart_h <= seamless) continue; - + + if (use_clipping) { + if (rast_x + (float)(subpart_w - offset_right) * xzoom * scaleX < clip_min_x || + rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY < clip_min_y) + { + continue; + } + if (rast_x + (float)offset_left * xzoom > clip_max_x || + rast_y + (float)offset_bot * yzoom > clip_max_y) + { + continue; + } + } + if (type == GL_FLOAT) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_FLOAT, &f_rect[((size_t)subpart_y) * offset_y * img_w * components + subpart_x * offset_x * components]); @@ -497,9 +514,26 @@ void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, #endif } +void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, + int format, int type, int zoomfilter, void *rect, + float scaleX, float scaleY) +{ + glaDrawPixelsTexScaled_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, + scaleX, scaleY, 0.0f, 0.0f, 0.0f, 0.0f); +} + void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect) { - glaDrawPixelsTexScaled(x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f); + glaDrawPixelsTexScaled_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 0.0f); +} + +void glaDrawPixelsTex_clipping(float x, float y, int img_w, int img_h, + int format, int type, int zoomfilter, void *rect, + float clip_min_x, float clip_min_y, float clip_max_x, float clip_max_y) +{ + glaDrawPixelsTexScaled_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, 1.0f, 1.0f, + clip_min_x, clip_min_y, clip_max_x, clip_max_y); } void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int format, int type, void *rect) @@ -580,17 +614,27 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo } /* uses either DrawPixelsSafe or DrawPixelsTex, based on user defined maximum */ -void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect) +void glaDrawPixelsAuto_clipping(float x, float y, int img_w, int img_h, + int format, int type, int zoomfilter, void *rect, + float clip_min_x, float clip_min_y, + float clip_max_x, float clip_max_y) { if (U.image_draw_method != IMAGE_DRAW_METHOD_DRAWPIXELS) { glColor4f(1.0, 1.0, 1.0, 1.0); - glaDrawPixelsTex(x, y, img_w, img_h, format, type, zoomfilter, rect); + glaDrawPixelsTex_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, + clip_min_x, clip_min_y, clip_max_x, clip_max_y); } else { glaDrawPixelsSafe(x, y, img_w, img_h, img_w, format, type, rect); } } +void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect) +{ + glaDrawPixelsAuto_clipping(x, y, img_w, img_h, format, type, zoomfilter, rect, + 0.0f, 0.0f, 0.0f, 0.0f); +} + /* 2D Drawing Assistance */ void glaDefine2DArea(rcti *screen_rect) @@ -795,8 +839,13 @@ void bglPolygonOffset(float viewdist, float dist) #endif } else { - /* should be clipping value or so... */ - offs = 0.0005f * dist; + /* This adjustment effectively results in reducing the Z value by 0.25%. + * + * winmat[14] actually evaluates to `-2 * far * near / (far - near)`, + * is very close to -0.2 with default clip range, and is used as the coefficient multiplied by `w / z`, + * thus controlling the z dependent part of the depth value. + */ + offs = winmat[14] * -0.0025f * dist; } winmat[14] -= offs; @@ -817,9 +866,11 @@ void bglPolygonOffset(float viewdist, float dist) /* **** Color management helper functions for GLSL display/transform ***** */ /* Draw given image buffer on a screen using GLSL for display transform */ -void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, - ColorManagedViewSettings *view_settings, - ColorManagedDisplaySettings *display_settings) +void glaDrawImBuf_glsl_clipping(ImBuf *ibuf, float x, float y, int zoomfilter, + ColorManagedViewSettings *view_settings, + ColorManagedDisplaySettings *display_settings, + float clip_min_x, float clip_min_y, + float clip_max_x, float clip_max_y) { bool force_fallback = false; bool need_fallback = true; @@ -869,14 +920,16 @@ void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, BLI_assert(!"Incompatible number of channels for GLSL display"); if (format != 0) { - glaDrawPixelsTex(x, y, ibuf->x, ibuf->y, format, GL_FLOAT, - zoomfilter, ibuf->rect_float); + glaDrawPixelsTex_clipping(x, y, ibuf->x, ibuf->y, format, GL_FLOAT, + zoomfilter, ibuf->rect_float, + clip_min_x, clip_min_y, clip_max_x, clip_max_y); } } else if (ibuf->rect) { /* ibuf->rect is always RGBA */ - glaDrawPixelsTex(x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, - zoomfilter, ibuf->rect); + glaDrawPixelsTex_clipping(x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, + zoomfilter, ibuf->rect, + clip_min_x, clip_min_y, clip_max_x, clip_max_y); } IMB_colormanagement_finish_glsl_draw(); @@ -892,21 +945,43 @@ void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, display_buffer = IMB_display_buffer_acquire(ibuf, view_settings, display_settings, &cache_handle); - if (display_buffer) - glaDrawPixelsAuto(x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, zoomfilter, display_buffer); + if (display_buffer) { + glaDrawPixelsAuto_clipping(x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, + zoomfilter, display_buffer, + clip_min_x, clip_min_y, clip_max_x, clip_max_y); + } IMB_display_buffer_release(cache_handle); } } -void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter) +void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, + ColorManagedViewSettings *view_settings, + ColorManagedDisplaySettings *display_settings) +{ + glaDrawImBuf_glsl_clipping(ibuf, x, y, zoomfilter, view_settings, display_settings, + 0.0f, 0.0f, 0.0f, 0.0f); +} + +void glaDrawImBuf_glsl_ctx_clipping(const bContext *C, + ImBuf *ibuf, + float x, float y, + int zoomfilter, + float clip_min_x, float clip_min_y, + float clip_max_x, float clip_max_y) { ColorManagedViewSettings *view_settings; ColorManagedDisplaySettings *display_settings; IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings); - glaDrawImBuf_glsl(ibuf, x, y, zoomfilter, view_settings, display_settings); + glaDrawImBuf_glsl_clipping(ibuf, x, y, zoomfilter, view_settings, display_settings, + clip_min_x, clip_min_y, clip_max_x, clip_max_y); +} + +void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter) +{ + glaDrawImBuf_glsl_ctx_clipping(C, ibuf, x, y, zoomfilter, 0.0f, 0.0f, 0.0f, 0.0f); } void cpack(unsigned int x) diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index cb07ce756a6..23c6aa37a83 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1064,17 +1064,11 @@ bScreen *ED_screen_duplicate(wmWindow *win, bScreen *sc) /* screen sets cursor based on swinid */ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed) { - ScrArea *sa = win->screen->areabase.first; - - for (; sa; sa = sa->next) { - ARegion *ar = sa->regionbase.first; - for (; ar; ar = ar->next) { + for (ScrArea *sa = win->screen->areabase.first; sa; sa = sa->next) { + for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->swinid == swinid) { if (swin_changed || (ar->type && ar->type->event_cursor)) { - if (ar->type && ar->type->cursor) - ar->type->cursor(win, sa, ar); - else - WM_cursor_set(win, CURSOR_STD); + ED_region_cursor_set(win, sa, ar); } return; } @@ -1146,7 +1140,7 @@ void ED_screen_draw(wmWindow *win) /* blended join arrow */ if (sa1 && sa2) { int dir = area_getorientation(sa1, sa2); - int dira; + int dira = -1; if (dir != -1) { switch (dir) { case 0: /* W */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index be421d779eb..4111f67553a 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3467,7 +3467,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv if ((scene->audio.flag & AUDIO_SYNC) && (sad->flag & ANIMPLAY_FLAG_REVERSE) == false && - finite(time = BKE_sound_sync_scene(scene))) + isfinite(time = BKE_sound_sync_scene(scene))) { double newfra = (double)time * FPS; diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index a9cd601c9b0..88b3bc5d8fd 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -36,15 +36,12 @@ #include "MEM_guardedalloc.h" -#ifdef WIN32 -# include "BLI_winstuff.h" -#endif - #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_threads.h" +#include "BLT_translation.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -64,6 +61,7 @@ #include "BKE_paint.h" #include "BKE_texture.h" +#include "UI_interface.h" #include "UI_view2d.h" #include "ED_image.h" @@ -498,8 +496,10 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te if (texpaint || (sima && sima->lock)) { 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, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); + if (w && h) { + /* Testing with partial update in uv editor too */ + GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); + } } } @@ -1169,20 +1169,17 @@ typedef struct { static void sample_color_update_header(SampleColorData *data, bContext *C) { -#define HEADER_LENGTH 150 - char msg[HEADER_LENGTH]; + char msg[UI_MAX_DRAW_STR]; ScrArea *sa = CTX_wm_area(C); if (sa) { - BLI_snprintf(msg, HEADER_LENGTH, - "Sample color for %s", + BLI_snprintf(msg, sizeof(msg), + IFACE_("Sample color for %s"), !data->sample_palette ? - "Brush. Use Left Click to sample for palette instead" : - "Palette. Use Left Click to sample more colors"); + IFACE_("Brush. Use Left Click to sample for palette instead") : + IFACE_("Palette. Use Left Click to sample more colors")); ED_area_headerprint(sa, msg); } - -#undef HEADER_LENGTH } static int sample_color_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index c5a066e9b14..080bd5b73c7 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -42,6 +42,7 @@ #include "BLI_math_color_blend.h" #include "BLI_stack.h" #include "BLI_bitmap.h" +#include "BLI_task.h" #include "BKE_context.h" #include "BKE_depsgraph.h" @@ -302,8 +303,8 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const srcx = srcy = 0; w = cache->tex_mask_old_w; h = cache->tex_mask_old_h; - destx = (int)painter->lastpaintpos[0] - (int)pos[0] + (diameter / 2 - w / 2); - desty = (int)painter->lastpaintpos[1] - (int)pos[1] + (diameter / 2 - h / 2); + destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); + desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); /* hack, use temporary rects so that clipping works */ IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); @@ -570,8 +571,8 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, const floa srcx = srcy = 0; w = oldtexibuf->x; h = oldtexibuf->y; - destx = (int)painter->lastpaintpos[0] - (int)pos[0] + (diameter / 2 - w / 2); - desty = (int)painter->lastpaintpos[1] - (int)pos[1] + (diameter / 2 - h / 2); + destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); + desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); } @@ -641,8 +642,8 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const mapping->ymax = 1.0f; } else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { - mapping->xmin = (int)(-diameter * 0.5) + (int)pos[0] - (int)startpos[0]; - mapping->ymin = (int)(-diameter * 0.5) + (int)pos[1] - (int)startpos[1]; + mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); + mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); mapping->xmax = 1.0f; mapping->ymax = 1.0f; } @@ -758,8 +759,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai } else if (do_partial_update) { /* do only partial update of texture */ - int dx = (int)painter->lastpaintpos[0] - (int)pos[0]; - int dy = (int)painter->lastpaintpos[1] - (int)pos[1]; + int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]); + int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]); if ((dx != 0) || (dy != 0)) { brush_painter_imbuf_partial_update(painter, pos, diameter); @@ -1019,6 +1020,64 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[ ipos[1] = (int)floorf((pos[1] - ibufb->y / 2)); } +static void paint_2d_do_making_brush(ImagePaintState *s, + ImagePaintRegion *region, + unsigned short *curveb, + unsigned short *texmaskb, + ImBuf *frombuf, + float mask_max, + short blend, + int tilex, int tiley, + int tilew, int tileh) +{ + ImBuf tmpbuf; + IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + + for (int ty = tiley; ty <= tileh; ty++) { + for (int tx = tilex; tx <= tilew; tx++) { + /* retrieve original pixels + mask from undo buffer */ + unsigned short *mask; + int origx = region->destx - tx * IMAPAINT_TILE_SIZE; + int origy = region->desty - ty * IMAPAINT_TILE_SIZE; + + if (s->canvas->rect_float) + tmpbuf.rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); + else + tmpbuf.rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); + + IMB_rectblend(s->canvas, &tmpbuf, frombuf, mask, + curveb, texmaskb, mask_max, + region->destx, region->desty, + origx, origy, + region->srcx, region->srcy, + region->width, region->height, + blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0)); + } + } +} + +typedef struct Paint2DForeachData { + ImagePaintState *s; + ImagePaintRegion *region; + unsigned short *curveb; + unsigned short *texmaskb; + ImBuf *frombuf; + float mask_max; + short blend; + int tilex; + int tilew; +} Paint2DForeachData; + +static void paint_2d_op_foreach_do(void *data_v, const int iter) +{ + Paint2DForeachData *data = (Paint2DForeachData *)data_v; + paint_2d_do_making_brush(data->s, data->region, data->curveb, + data->texmaskb, data->frombuf, data->mask_max, + data->blend, + data->tilex, iter, + data->tilew, iter); +} + static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2]) { ImagePaintState *s = ((ImagePaintState *)state); @@ -1072,45 +1131,40 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign if (s->do_masking) { /* masking, find original pixels tiles from undo buffer to composite over */ - int tilex, tiley, tilew, tileh, tx, ty; - ImBuf *tmpbuf; + int tilex, tiley, tilew, tileh; imapaint_region_tiles(s->canvas, region[a].destx, region[a].desty, region[a].width, region[a].height, &tilex, &tiley, &tilew, &tileh); - tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); - - for (ty = tiley; ty <= tileh; ty++) { - for (tx = tilex; tx <= tilew; tx++) { - /* retrieve original pixels + mask from undo buffer */ - unsigned short *mask; - int origx = region[a].destx - tx * IMAPAINT_TILE_SIZE; - int origy = region[a].desty - ty * IMAPAINT_TILE_SIZE; - - if (s->canvas->rect_float) - tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); - else - tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); - - IMB_rectblend(s->canvas, tmpbuf, frombuf, mask, - curveb, texmaskb, mask_max, - region[a].destx, region[a].desty, - origx, origy, - region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0)); - } + if (tiley == tileh) { + paint_2d_do_making_brush(s, ®ion[a], curveb, texmaskb, frombuf, + mask_max, blend, tilex, tiley, tilew, tileh); } + else { + Paint2DForeachData data; + data.s = s; + data.region = ®ion[a]; + data.curveb = curveb; + data.texmaskb = texmaskb; + data.frombuf = frombuf; + data.mask_max = mask_max; + data.blend = blend; + data.tilex = tilex; + data.tilew = tilew; + BLI_task_parallel_range(tiley, tileh + 1, &data, + paint_2d_op_foreach_do, + true); - IMB_freeImBuf(tmpbuf); + } } else { /* no masking, composite brush directly onto canvas */ - IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max, - region[a].destx, region[a].desty, - region[a].destx, region[a].desty, - region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend, false); + IMB_rectblend_threaded(s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max, + region[a].destx, region[a].desty, + region[a].destx, region[a].desty, + region[a].srcx, region[a].srcy, + region[a].width, region[a].height, blend, false); } } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index b8693639673..d273f8320a1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -107,6 +107,8 @@ #include "paint_intern.h" +static void partial_redraw_array_init(ImagePaintPartialRedraw *pr); + /* Defines and Structs */ /* FTOCHAR as inline function */ BLI_INLINE unsigned char f_to_char(const float val) @@ -1100,6 +1102,10 @@ static void uv_image_outset( float (*orig_uv)[2], float (*outset_uv)[2], const float scaler, const int ibuf_x, const int ibuf_y, const bool cw) { + /* disallow shell-thickness to outset extreme values, + * otherwise near zero area UV's may extend thousands of pixels. */ + const float scale_clamp = 5.0f; + float a1, a2, a3; float puv[3][2]; /* pixelspace uv's */ float no1[2], no2[2], no3[2]; /* normals */ @@ -1150,6 +1156,10 @@ static void uv_image_outset( a2 = shell_v2v2_normal_dir_to_dist(no2, dir1); a3 = shell_v2v2_normal_dir_to_dist(no3, dir2); + CLAMP_MAX(a1, scale_clamp); + CLAMP_MAX(a2, scale_clamp); + CLAMP_MAX(a3, scale_clamp); + mul_v2_fl(no1, a1 * scaler); mul_v2_fl(no2, a2 * scaler); mul_v2_fl(no3, a3 * scaler); @@ -3639,7 +3649,7 @@ static void project_paint_build_proj_ima( projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(projIma->ibuf->y); projIma->partRedrawRect = BLI_memarena_alloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); - memset(projIma->partRedrawRect, 0, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + partial_redraw_array_init(projIma->partRedrawRect); projIma->undoRect = (volatile void **) BLI_memarena_alloc(arena, size); memset((void *)projIma->undoRect, 0, size); projIma->maskRect = BLI_memarena_alloc(arena, size); @@ -3990,8 +4000,8 @@ static void project_paint_end(ProjPaintState *ps) /* 1 = an undo, -1 is a redo. */ static void partial_redraw_single_init(ImagePaintPartialRedraw *pr) { - pr->x1 = 10000000; - pr->y1 = 10000000; + pr->x1 = INT_MAX; + pr->y1 = INT_MAX; pr->x2 = -1; pr->y2 = -1; @@ -5352,9 +5362,6 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) float lastpos[2] = {0.0, 0.0}; int a; - for (a = 0; a < ps.image_tot; a++) - partial_redraw_array_init(ps.projImages[a].partRedrawRect); - project_paint_op(&ps, lastpos, pos); project_image_refresh_tagged(&ps); @@ -5858,15 +5865,17 @@ static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op)) ED_mesh_uv_texture_ensure(me, NULL); - BM_mesh_bm_from_me(bm, me, true, false, 0); - + BM_mesh_bm_from_me( + bm, me, (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); /* select all uv loops first - pack parameters needs this to make sure charts are registered */ ED_uvedit_select_all(bm); ED_uvedit_unwrap_cube_project(ob, bm, 1.0, false); /* set the margin really quickly before the packing operation*/ scene->toolsettings->uvcalc_margin = 0.001f; ED_uvedit_pack_islands(scene, ob, bm, false, false, true); - BM_mesh_bm_to_me(bm, me, false); + BM_mesh_bm_to_me(bm, me, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm); if (synch_selection) diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c index 42f0aaab173..27d3f6648a2 100644 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -34,7 +34,7 @@ #include "DNA_userdef_types.h" -#include "BKE_blender.h" +#include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_global.h" diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index bf751bfe74a..15ab4ca04a7 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2836,7 +2836,8 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) struct VPaintData *vpd = paint_stroke_mode_data(stroke); ViewContext *vc = &vpd->vc; Object *ob = vc->obact; - + Mesh *me = ob->data; + ED_vpaint_proj_handle_free(vpd->vp_handle); MEM_freeN(vpd->indexar); @@ -2847,6 +2848,7 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) MEM_freeN(vpd->mlooptag); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + DAG_id_tag_update(&me->id, 0); MEM_freeN(vpd); } @@ -2979,7 +2981,7 @@ typedef struct DMGradient_userData { const float *sco_end; /* [2] */ float sco_line_div; /* store (1.0f / len_v2v2(sco_start, sco_end)) */ int def_nr; - short is_init; + bool is_init; DMGradient_vertStore *vert_cache; /* only for init */ BLI_bitmap *vert_visit; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 40ff662a2c2..c173156de3a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2997,7 +2997,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t const bool flip = (ss->cache->bstrength < 0); const float radius = flip ? -ss->cache->radius : ss->cache->radius; const float offset = get_offset(sd, ss); - const float displace = radius * (0.25f + offset);; + const float displace = radius * (0.25f + offset); float area_no_sp[3]; /* the sculpt-plane normal (whatever its set to) */ float area_no[3]; /* geometry normal */ @@ -5003,7 +5003,10 @@ void sculpt_dynamic_topology_enable(bContext *C) /* Create triangles-only BMesh */ ss->bm = BM_mesh_create(&allocsize); - BM_mesh_bm_from_me(ss->bm, me, true, true, ob->shapenr); + BM_mesh_bm_from_me( + ss->bm, me, (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, .use_shapekey = true, .active_shapekey = ob->shapenr, + })); sculpt_dynamic_topology_triangulate(ss->bm); BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); sculpt_dyntopo_node_layers_add(ss); diff --git a/source/blender/editors/space_action/CMakeLists.txt b/source/blender/editors/space_action/CMakeLists.txt index 839071d1330..24c3ee9cd3d 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_buttons.c action_data.c action_draw.c action_edit.c diff --git a/source/blender/editors/space_action/action_buttons.c b/source/blender/editors/space_action/action_buttons.c new file mode 100644 index 00000000000..063a477d2b6 --- /dev/null +++ b/source/blender/editors/space_action/action_buttons.c @@ -0,0 +1,132 @@ +/* + * ***** 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) 2016 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_action/action_buttons.c + * \ingroup spaction + */ + + +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <float.h> + +#include "DNA_anim_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_main.h" +#include "BKE_global.h" +#include "BKE_screen.h" +#include "BKE_unit.h" + + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "action_intern.h" // own include + +/* ******************* action editor space & buttons ************** */ + +/* ******************* general ******************************** */ + +void action_buttons_register(ARegionType *UNUSED(art)) +{ +#if 0 + PanelType *pt; + + // TODO: AnimData / Actions List + + pt = MEM_callocN(sizeof(PanelType), "spacetype action panel properties"); + strcpy(pt->idname, "ACTION_PT_properties"); + strcpy(pt->label, N_("Active F-Curve")); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->draw = action_anim_panel_properties; + pt->poll = action_anim_panel_poll; + BLI_addtail(&art->paneltypes, pt); + + pt = MEM_callocN(sizeof(PanelType), "spacetype action panel properties"); + strcpy(pt->idname, "ACTION_PT_key_properties"); + strcpy(pt->label, N_("Active Keyframe")); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->draw = action_anim_panel_key_properties; + pt->poll = action_anim_panel_poll; + BLI_addtail(&art->paneltypes, pt); + + pt = MEM_callocN(sizeof(PanelType), "spacetype action panel modifiers"); + strcpy(pt->idname, "ACTION_PT_modifiers"); + strcpy(pt->label, N_("Modifiers")); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->draw = action_anim_panel_modifiers; + pt->poll = action_anim_panel_poll; + BLI_addtail(&art->paneltypes, pt); +#endif +} + +static int action_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = action_has_buttons_region(sa); + + if (ar) + ED_region_toggle_hidden(C, ar); + + return OPERATOR_FINISHED; +} + +void ACTION_OT_properties(wmOperatorType *ot) +{ + ot->name = "Properties"; + ot->idname = "ACTION_OT_properties"; + ot->description = "Toggle display properties panel"; + + ot->exec = action_properties_toggle_exec; + ot->poll = ED_operator_action_active; + + /* flags */ + ot->flag = 0; +} diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index c66028b17fc..c0947dacbf0 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -239,6 +239,11 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const } } + if (fabsf(*max - *min) < 0.001f) { + *min -= 0.0005f; + *max += 0.0005f; + } + /* free memory */ ANIM_animdata_freelist(&anim_data); } @@ -431,13 +436,7 @@ static int actkeys_viewsel_exec(bContext *C, wmOperator *UNUSED(op)) return actkeys_viewall(C, true); } -static int actkeys_view_frame_exec(bContext *C, wmOperator *op) -{ - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - ANIM_center_frame(C, smooth_viewtx); - - return OPERATOR_FINISHED; -} +/* ......... */ void ACTION_OT_view_all(wmOperatorType *ot) { @@ -469,17 +468,27 @@ void ACTION_OT_view_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ****************** View-All Operator ****************** */ + +static int actkeys_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + + return OPERATOR_FINISHED; +} + void ACTION_OT_view_frame(wmOperatorType *ot) { /* identifiers */ ot->name = "View Frame"; ot->idname = "ACTION_OT_view_frame"; ot->description = "Reset viewable area to show range around current frame"; - + /* api callbacks */ ot->exec = actkeys_view_frame_exec; - ot->poll = ED_operator_action_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */ - + ot->poll = ED_operator_action_active; + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -496,7 +505,7 @@ static short copy_action_keys(bAnimContext *ac) int filter, ok = 0; /* clear buffer first */ - free_anim_copybuf(); + ANIM_fcurves_copybuf_free(); /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index 17f1f404225..50e10e7e154 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -34,11 +34,21 @@ struct bContext; struct bAnimContext; struct SpaceAction; +struct ScrArea; struct ARegion; +struct ARegionType; struct wmOperatorType; /* internal exports only */ +/* **************************************** */ +/* space_action.c / action_buttons.c */ + +struct ARegion *action_has_buttons_region(struct ScrArea *sa); + +void action_buttons_register(struct ARegionType *art); +void ACTION_OT_properties(struct wmOperatorType *ot); + /* ***************************************** */ /* action_draw.c */ void draw_channel_names(struct bContext *C, struct bAnimContext *ac, struct ARegion *ar); diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index 59b147c6f6c..f69f9944f8a 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -51,6 +51,9 @@ void action_operatortypes(void) { + /* view */ + WM_operatortype_append(ACTION_OT_properties); + /* keyframes */ /* selection */ WM_operatortype_append(ACTION_OT_clickselect); @@ -257,6 +260,13 @@ void action_keymap(wmKeyConfig *keyconf) { wmKeyMap *keymap; + /* keymap for all regions */ + keymap = WM_keymap_find(keyconf, "Dopesheet Generic", SPACE_ACTION, 0); + + /* region management... */ + WM_keymap_add_item(keymap, "ACTION_OT_properties", NKEY, KM_PRESS, 0, 0); + + /* channels */ /* Channels are not directly handled by the Action Editor module, but are inherited from the Animation module. * All the relevant operations, keymaps, drawing, etc. can therefore all be found in that module instead, as these diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 53c5a008af8..60240109432 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -59,6 +59,32 @@ #include "action_intern.h" /* own include */ +/* ******************** manage regions ********************* */ + +ARegion *action_has_buttons_region(ScrArea *sa) +{ + ARegion *ar, *arnew; + + ar = BKE_area_find_region_type(sa, RGN_TYPE_UI); + if (ar) return ar; + + /* add subdiv level; after main */ + ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + + /* is error! */ + if (ar == NULL) return NULL; + + arnew = MEM_callocN(sizeof(ARegion), "buttons for action"); + + BLI_insertlinkafter(&sa->regionbase, ar, arnew); + arnew->regiontype = RGN_TYPE_UI; + arnew->alignment = RGN_ALIGN_RIGHT; + + arnew->flag = RGN_FLAG_HIDDEN; + + return arnew; +} + /* ******************** default callbacks for action space ***************** */ static SpaceLink *action_new(const bContext *C) @@ -93,6 +119,14 @@ static SpaceLink *action_new(const bContext *C) ar->v2d.scroll = V2D_SCROLL_BOTTOM; ar->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL; + /* ui buttons */ + ar = MEM_callocN(sizeof(ARegion), "buttons region for action"); + + BLI_addtail(&saction->regionbase, ar); + ar->regiontype = RGN_TYPE_UI; + ar->alignment = RGN_ALIGN_RIGHT; + ar->flag = RGN_FLAG_HIDDEN; + /* main region */ ar = MEM_callocN(sizeof(ARegion), "main region for action"); @@ -159,6 +193,8 @@ static void action_main_region_init(wmWindowManager *wm, ARegion *ar) /* own keymap */ keymap = WM_keymap_find(wm->defaultconf, "Dopesheet", SPACE_ACTION, 0); WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + keymap = WM_keymap_find(wm->defaultconf, "Dopesheet Generic", SPACE_ACTION, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); } static void action_main_region_draw(const bContext *C, ARegion *ar) @@ -231,6 +267,9 @@ static void action_channel_region_init(wmWindowManager *wm, ARegion *ar) /* own keymap */ keymap = WM_keymap_find(wm->defaultconf, "Animation Channels", 0, 0); WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + + keymap = WM_keymap_find(wm->defaultconf, "Dopesheet Generic", SPACE_ACTION, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); } static void action_channel_region_draw(const bContext *C, ARegion *ar) @@ -498,6 +537,54 @@ static void action_header_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(s } +/* add handlers, stuff you only do once or on area/region changes */ +static void action_buttons_area_init(wmWindowManager *wm, ARegion *ar) +{ + wmKeyMap *keymap; + + ED_region_panels_init(wm, ar); + + keymap = WM_keymap_find(wm->defaultconf, "Dopesheet Generic", SPACE_ACTION, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); +} + +static void action_buttons_area_draw(const bContext *C, ARegion *ar) +{ + ED_region_panels(C, ar, NULL, -1, true); +} + +static void action_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn) +{ + /* context changes */ + switch (wmn->category) { + case NC_ANIMATION: + ED_region_tag_redraw(ar); + break; + case NC_SCENE: + switch (wmn->data) { + case ND_OB_ACTIVE: + case ND_FRAME: + case ND_MARKERS: + ED_region_tag_redraw(ar); + break; + } + break; + case NC_OBJECT: + switch (wmn->data) { + case ND_BONE_ACTIVE: + case ND_BONE_SELECT: + case ND_KEYS: + ED_region_tag_redraw(ar); + break; + } + break; + default: + if (wmn->data == ND_KEYS) + ED_region_tag_redraw(ar); + break; + } +} + static void action_refresh(const bContext *C, ScrArea *sa) { SpaceAction *saction = (SpaceAction *)sa->spacedata.first; @@ -579,6 +666,18 @@ void ED_spacetype_action(void) BLI_addhead(&st->regiontypes, art); + /* regions: UI buttons */ + art = MEM_callocN(sizeof(ARegionType), "spacetype action region"); + art->regionid = RGN_TYPE_UI; + art->prefsizex = 200; + art->keymapflag = ED_KEYMAP_UI; + art->listener = action_region_listener; + art->init = action_buttons_area_init; + art->draw = action_buttons_area_draw; + + BLI_addhead(&st->regiontypes, art); + + action_buttons_register(art); BKE_spacetype_register(st); } diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 182f2bd2e80..97a85bce006 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -27,10 +27,6 @@ #include <string.h> #include <stdio.h> -#ifdef WIN32 -# include "BLI_winstuff.h" -#endif - #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 207879c2809..8e1f781827a 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1426,7 +1426,7 @@ int filelist_files_ensure(FileList *filelist) filelist_filter(filelist); } - return filelist->filelist.nbr_entries_filtered;; + return filelist->filelist.nbr_entries_filtered; } static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int index) diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 981b101519c..bf90a4ea170 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -242,7 +242,7 @@ short ED_fileselect_set_params(SpaceFile *sfile) if (params->display == FILE_DEFAULTDISPLAY) { if (U.uiflag & USER_SHOW_THUMBNAILS) { - if (params->filter & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE)) + if (params->filter & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT)) params->display = FILE_IMGDISPLAY; else params->display = FILE_SHORTDISPLAY; @@ -477,7 +477,7 @@ static void column_widths(FileSelectParams *params, struct FileLayout *layout) layout->column_widths[i] = 0; } - layout->column_widths[COLUMN_NAME] = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X;; + layout->column_widths[COLUMN_NAME] = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X; /* Biggest possible reasonable values... */ layout->column_widths[COLUMN_DATE] = file_string_width(small_size ? "23/08/89" : "23-Dec-89"); layout->column_widths[COLUMN_TIME] = file_string_width("23:59"); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index e77f545fbc0..7fb294529c9 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -225,7 +225,7 @@ static void file_refresh(const bContext *C, ScrArea *sa) filelist_setsorting(sfile->files, params->sort); filelist_setfilter_options(sfile->files, (params->flag & FILE_HIDE_DOT) != 0, false, /* TODO hide_parent, should be controllable? */ - params->flag & FILE_FILTER ? params->filter : 0, + (params->flag & FILE_FILTER) ? params->filter : 0, params->filter_id, params->filter_glob, params->filter_search); diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 39b17410e62..a9ab1502e16 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -516,7 +516,7 @@ static void driver_delete_var_cb(bContext *UNUSED(C), void *driver_v, void *dvar DriverVar *dvar = (DriverVar *)dvar_v; /* remove the active variable */ - driver_free_variable(driver, dvar); + driver_free_variable_ex(driver, dvar); } /* callback to report why a driver variable is invalid */ @@ -607,6 +607,7 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa } /* settings for 'rotation difference' driver variable type */ +/* FIXME: 1) Must be same armature for both dtars, 2) Alignment issues... */ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *dvar) { DriverTarget *dtar = &dvar->targets[0]; @@ -623,7 +624,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar * /* Bone 1 */ col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Bone 1:")); + uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Bone 1"), ICON_NONE); if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) { PointerRNA tar_ptr; @@ -634,7 +635,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar * col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Bone 2:")); + uiItemR(col, &dtar2_ptr, "id", 0, IFACE_("Bone 2"), ICON_NONE); if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) { PointerRNA tar_ptr; @@ -661,13 +662,13 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar * /* Bone 1 */ col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone 1:")); - + uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Object 1"), ICON_NONE); + if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr); - uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); + uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA); } uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */ @@ -675,13 +676,13 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar * col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Ob/Bone 2:")); - + uiItemR(col, &dtar2_ptr, "id", 0, IFACE_("Object 2"), ICON_NONE); + if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr); - uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); + uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA); } uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */ @@ -702,13 +703,13 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar /* properties */ col = uiLayoutColumn(layout, true); uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ - uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone:")); - + uiItemR(col, &dtar_ptr, "id", 0, IFACE_("Object"), ICON_NONE); + if (dtar->id && GS(dtar->id->name) == ID_OB && ob->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar->id, &RNA_Pose, ob->pose, &tar_ptr); - uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); + uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA); } sub = uiLayoutColumn(layout, true); @@ -825,34 +826,59 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) uiItemL(row, valBuf, ICON_NONE); } - /* add driver variables */ - col = uiLayoutColumn(pa->layout, false); - block = uiLayoutGetBlock(col); - but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ZOOMIN, IFACE_("Add Variable"), - 0, 0, 10 * UI_UNIT_X, UI_UNIT_Y, - NULL, 0.0, 0.0, 0, 0, - TIP_("Driver variables ensure that all dependencies will be accounted for and that drivers will update correctly")); - UI_but_func_set(but, driver_add_var_cb, driver, NULL); + /* add/copy/paste driver variables */ + { + uiLayout *row; + + /* add driver variable */ + row = uiLayoutRow(pa->layout, false); + block = uiLayoutGetBlock(row); + but = uiDefIconTextBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_ZOOMIN, IFACE_("Add Variable"), + 0, 0, 10 * UI_UNIT_X, UI_UNIT_Y, + NULL, 0.0, 0.0, 0, 0, + TIP_("Driver variables ensure that all dependencies will be accounted for and that drivers will update correctly")); + UI_but_func_set(but, driver_add_var_cb, driver, NULL); + + /* copy/paste (as sub-row) */ + row = uiLayoutRow(row, true); + block = uiLayoutGetBlock(row); + + uiItemO(row, "", ICON_COPYDOWN, "GRAPH_OT_driver_variables_copy"); + uiItemO(row, "", ICON_PASTEDOWN, "GRAPH_OT_driver_variables_paste"); + } /* loop over targets, drawing them */ for (dvar = driver->variables.first; dvar; dvar = dvar->next) { PointerRNA dvar_ptr; uiLayout *box, *row; + uiLayout *subrow, *sub; /* sub-layout column for this variable's settings */ col = uiLayoutColumn(pa->layout, true); - /* header panel */ + /* 1) header panel */ box = uiLayoutBox(col); - /* first row context info for driver */ RNA_pointer_create(ale->id, &RNA_DriverVariable, dvar, &dvar_ptr); row = uiLayoutRow(box, false); block = uiLayoutGetBlock(row); - /* variable name */ - uiItemR(row, &dvar_ptr, "name", 0, "", ICON_NONE); - /* invalid name? */ + /* 1.1) variable type and name */ + subrow = uiLayoutRow(row, true); + + /* 1.1.1) variable type */ + sub = uiLayoutRow(subrow, true); /* HACK: special group just for the enum, otherwise we */ + uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_LEFT); /* we get ugly layout with text included too... */ + + uiItemR(sub, &dvar_ptr, "type", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); + + /* 1.1.2) variable name */ + sub = uiLayoutRow(subrow, true); /* HACK: special group to counteract the effects of the previous */ + uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_EXPAND); /* enum, which now pushes everything too far right */ + + uiItemR(sub, &dvar_ptr, "name", 0, "", ICON_NONE); + + /* 1.2) invalid name? */ UI_block_emboss_set(block, UI_EMBOSS_NONE); if (dvar->flag & DVAR_FLAG_INVALID_NAME) { @@ -861,17 +887,14 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) UI_but_func_set(but, driver_dvar_invalid_name_query_cb, dvar, NULL); // XXX: reports? } - /* remove button */ + /* 1.3) remove button */ but = uiDefIconBut(block, UI_BTYPE_BUT, B_IPO_DEPCHANGE, ICON_X, 290, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0.0, 0.0, IFACE_("Delete target variable")); UI_but_func_set(but, driver_delete_var_cb, driver, dvar); UI_block_emboss_set(block, UI_EMBOSS); - /* variable type */ - row = uiLayoutRow(box, false); - uiItemR(row, &dvar_ptr, "type", 0, "", ICON_NONE); - - /* variable type settings */ + + /* 2) variable type settings */ box = uiLayoutBox(col); /* controls to draw depends on the type of variable */ switch (dvar->type) { @@ -889,7 +912,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) break; } - /* value of variable */ + /* 3) value of variable */ if (driver->flag & DRIVER_FLAG_SHOWDEBUG) { char valBuf[32]; @@ -948,15 +971,13 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa) /* 'add modifier' button at top of panel */ { row = uiLayoutRow(pa->layout, false); - block = uiLayoutGetBlock(row); /* this is an operator button which calls a 'add modifier' operator... * a menu might be nicer but would be tricky as we need some custom filtering */ - uiDefButO(block, UI_BTYPE_BUT, "GRAPH_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, IFACE_("Add Modifier"), - 0.5 * UI_UNIT_X, 0, 7.5 * UI_UNIT_X, UI_UNIT_Y, TIP_("Adds a new F-Curve Modifier for the active F-Curve")); + uiItemMenuEnumO(row, (bContext *)C, "GRAPH_OT_fmodifier_add", "type", IFACE_("Add Modifier"), ICON_NONE); - /* copy/paste (as sub-row)*/ + /* copy/paste (as sub-row) */ row = uiLayoutRow(row, true); uiItemO(row, "", ICON_COPYDOWN, "GRAPH_OT_fmodifier_copy"); uiItemO(row, "", ICON_PASTEDOWN, "GRAPH_OT_fmodifier_paste"); @@ -970,7 +991,7 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa) ANIM_uiTemplate_fmodifier_draw(col, ale->id, &fcu->modifiers, fcm); } - + MEM_freeN(ale); } @@ -980,14 +1001,6 @@ void graph_buttons_register(ARegionType *art) { PanelType *pt; - pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel view"); - strcpy(pt->idname, "GRAPH_PT_view"); - strcpy(pt->label, N_("View Properties")); - strcpy(pt->category, "View"); - strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); - pt->draw = graph_panel_view; - BLI_addtail(&art->paneltypes, pt); - pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel properties"); strcpy(pt->idname, "GRAPH_PT_properties"); strcpy(pt->label, N_("Active F-Curve")); @@ -1024,6 +1037,14 @@ void graph_buttons_register(ARegionType *art) pt->draw = graph_panel_modifiers; pt->poll = graph_panel_poll; BLI_addtail(&art->paneltypes, pt); + + pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel view"); + strcpy(pt->idname, "GRAPH_PT_view"); + strcpy(pt->label, N_("View Properties")); + strcpy(pt->category, "View"); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->draw = graph_panel_view; + BLI_addtail(&art->paneltypes, pt); } static int graph_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 10baed8508d..f1063996ca3 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -54,6 +54,7 @@ #include "BLT_translation.h" +#include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_nla.h" @@ -264,13 +265,7 @@ static int graphkeys_view_selected_exec(bContext *C, wmOperator *op) return graphkeys_viewall(C, true, include_handles, smooth_viewtx); } -static int graphkeys_view_frame_exec(bContext *C, wmOperator *op) -{ - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); - ANIM_center_frame(C, smooth_viewtx); - return OPERATOR_FINISHED; -} - +/* ......... */ void GRAPH_OT_view_all(wmOperatorType *ot) { @@ -310,17 +305,26 @@ void GRAPH_OT_view_selected(wmOperatorType *ot) "Include handles of keyframes when calculating extents"); } +/* ********************** View Frame Operator ****************************** */ + +static int graphkeys_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + return OPERATOR_FINISHED; +} + void GRAPH_OT_view_frame(wmOperatorType *ot) { /* identifiers */ ot->name = "View Frame"; ot->idname = "GRAPH_OT_view_frame"; ot->description = "Reset viewable area to show range around current frame"; - + /* api callbacks */ ot->exec = graphkeys_view_frame_exec; - ot->poll = ED_operator_graphedit_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */ - + ot->poll = ED_operator_graphedit_active; + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -689,6 +693,14 @@ static int graphkeys_click_insert_exec(bContext *C, wmOperator *op) short mapping_flag = ANIM_get_normalization_flags(&ac); float scale, offset; + /* preserve selection? */ + if (RNA_boolean_get(op->ptr, "extend") == false) { + /* deselect all keyframes first, so that we can immediately start manipulating the newly added one(s) + * - only affect the keyframes themselves, as we don't want channels popping in and out... + */ + deselect_graph_keys(&ac, false, SELECT_SUBTRACT, false); + } + /* get frame and value from props */ frame = RNA_float_get(op->ptr, "frame"); val = RNA_float_get(op->ptr, "value"); @@ -778,6 +790,8 @@ void GRAPH_OT_click_insert(wmOperatorType *ot) /* properties */ RNA_def_float(ot->srna, "frame", 1.0f, -FLT_MAX, FLT_MAX, "Frame Number", "Frame to insert keyframe on", 0, 100); RNA_def_float(ot->srna, "value", 1.0f, -FLT_MAX, FLT_MAX, "Value", "Value for keyframe on", 0, 100); + + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first"); } /* ******************** Copy/Paste Keyframes Operator ************************* */ @@ -789,7 +803,7 @@ static short copy_graph_keys(bAnimContext *ac) int filter, ok = 0; /* clear buffer first */ - free_anim_copybuf(); + ANIM_fcurves_copybuf_free(); /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); @@ -2374,27 +2388,29 @@ static EnumPropertyItem *graph_fmodifier_itemf(bContext *C, PointerRNA *UNUSED(p EnumPropertyItem *item = NULL; int totitem = 0; int i = 0; - + if (C == NULL) { return rna_enum_fmodifier_type_items; } - + /* start from 1 to skip the 'Invalid' modifier type */ for (i = 1; i < FMODIFIER_NUM_TYPES; i++) { const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i); int index; - + /* check if modifier is valid for this context */ if (fmi == NULL) continue; - + index = RNA_enum_from_value(rna_enum_fmodifier_type_items, fmi->type); - RNA_enum_item_add(&item, &totitem, &rna_enum_fmodifier_type_items[index]); + if (index != -1) { /* Not all types are implemented yet... */ + RNA_enum_item_add(&item, &totitem, &rna_enum_fmodifier_type_items[index]); + } } - + RNA_enum_item_end(&item, &totitem); *r_free = true; - + return item; } @@ -2435,16 +2451,15 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Modifier could not be added (see console for details)"); break; } - + ale->update |= ANIM_UPDATE_DEPS; } - + ANIM_animdata_update(&ac, &anim_data); ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ - // FIXME: this really isn't the best description for it... - WM_event_add_notifier(C, NC_ANIMATION, NULL); + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -2452,11 +2467,11 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) void GRAPH_OT_fmodifier_add(wmOperatorType *ot) { PropertyRNA *prop; - + /* identifiers */ ot->name = "Add F-Curve Modifier"; ot->idname = "GRAPH_OT_fmodifier_add"; - ot->description = "Add F-Modifiers to the selected F-Curves"; + ot->description = "Add F-Modifier to the active/selected F-Curves"; /* api callbacks */ ot->invoke = WM_menu_invoke; @@ -2470,7 +2485,7 @@ void GRAPH_OT_fmodifier_add(wmOperatorType *ot) prop = RNA_def_enum(ot->srna, "type", rna_enum_fmodifier_type_items, 0, "Type", ""); RNA_def_enum_funcs(prop, graph_fmodifier_itemf); ot->prop = prop; - + RNA_def_boolean(ot->srna, "only_active", 1, "Only Active", "Only add F-Modifier to active F-Curve"); } @@ -2487,7 +2502,7 @@ static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; /* clear buffer first */ - free_fmodifiers_copybuf(); + ANIM_fmodifiers_copybuf_free(); /* get the active F-Curve */ ale = get_active_fcurve_channel(&ac); @@ -2495,10 +2510,10 @@ static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op) /* if this exists, call the copy F-Modifiers API function */ if (ale && ale->data) { FCurve *fcu = (FCurve *)ale->data; - + /* TODO: when 'active' vs 'all' boolean is added, change last param! */ ok = ANIM_fmodifiers_copy_to_buf(&fcu->modifiers, 0); - + /* free temp data now */ MEM_freeN(ale); } @@ -2535,33 +2550,43 @@ void GRAPH_OT_fmodifier_copy(wmOperatorType *ot) static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op) { bAnimContext ac; + ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; - int filter, ok = 0; + int filter; + + const bool replace = RNA_boolean_get(op->ptr, "replace"); + bool ok = false; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; /* filter data */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT); + if (RNA_boolean_get(op->ptr, "only_active")) { + /* This should be the default (for buttons) - Just paste to the active FCurve */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + } + else { + /* This is only if the operator gets called from a hotkey or search - Paste to all visible curves */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + } + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* paste modifiers */ for (ale = anim_data.first; ale; ale = ale->next) { FCurve *fcu = (FCurve *)ale->data; int tot; - - /* TODO: do we want to replace existing modifiers? add user pref for that! */ - tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, 0); - + + tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, replace); + if (tot) { ale->update |= ANIM_UPDATE_DEPS; + ok = true; } - - ok += tot; } - + if (ok) { ANIM_animdata_update(&ac, &anim_data); } @@ -2569,7 +2594,6 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op) /* successful or not? */ if (ok) { - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -2594,6 +2618,128 @@ void GRAPH_OT_fmodifier_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "only_active", true, "Only Active", "Only paste F-Modifiers on active F-Curve"); + RNA_def_boolean(ot->srna, "replace", false, "Replace Existing", + "Replace existing F-Modifiers, instead of just appending to the end of the existing list"); +} + +/* ************************************************************************** */ +/* Drivers */ + +/* ******************** Copy Driver Vars Operator *********************** */ + +static int graph_driver_vars_copy_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + bAnimListElem *ale; + bool ok = false; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* clear buffer first */ + ANIM_driver_vars_copybuf_free(); + + /* get the active F-Curve */ + ale = get_active_fcurve_channel(&ac); + + /* if this exists, call the copy driver vars API function */ + if (ale && ale->data) { + FCurve *fcu = (FCurve *)ale->data; + + ok = ANIM_driver_vars_copy(op->reports, fcu); + + /* free temp data now */ + MEM_freeN(ale); + } + + /* successful or not? */ + if (ok) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; +} + +void GRAPH_OT_driver_variables_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Driver Variables"; + ot->idname = "GRAPH_OT_driver_variables_copy"; + ot->description = "Copy the driver variables of the active F-Curve"; + + /* api callbacks */ + ot->exec = graph_driver_vars_copy_exec; + ot->poll = graphop_active_fcurve_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************** Paste Driver Vars Operator *********************** */ + +static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + const bool replace = RNA_boolean_get(op->ptr, "replace"); + bool ok = false; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* filter data */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* paste variables */ + for (ale = anim_data.first; ale; ale = ale->next) { + FCurve *fcu = (FCurve *)ale->data; + ok |= ANIM_driver_vars_paste(op->reports, fcu, replace); + } + + /* cleanup */ + ANIM_animdata_freelist(&anim_data); + + /* successful or not? */ + if (ok) { + /* rebuild depsgraph, now that there are extra deps here */ + DAG_relations_tag_update(CTX_data_main(C)); + + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, CTX_data_scene(C)); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void GRAPH_OT_driver_variables_paste(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Paste Driver Variables"; + ot->idname = "GRAPH_OT_driver_variables_paste"; + ot->description = "Add copied driver variables to the active driver"; + + /* api callbacks */ + ot->exec = graph_driver_vars_paste_exec; + ot->poll = graphop_active_fcurve_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "replace", false, "Replace Existing", + "Replace existing driver variables, instead of just appending to the end of the existing list"); } /* ************************************************************************** */ diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index a478a86a5e2..534b712fd5e 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -56,6 +56,8 @@ void graph_draw_ghost_curves(struct bAnimContext *ac, struct SpaceIpo *sipo, str /* ***************************************** */ /* graph_select.c */ +void deselect_graph_keys(struct bAnimContext *ac, bool test, short sel, bool do_channels); + void GRAPH_OT_select_all_toggle(struct wmOperatorType *ot); void GRAPH_OT_select_border(struct wmOperatorType *ot); void GRAPH_OT_select_lasso(struct wmOperatorType *ot); @@ -148,6 +150,11 @@ void GRAPH_OT_fmodifier_paste(struct wmOperatorType *ot); /* ----------- */ +void GRAPH_OT_driver_variables_copy(struct wmOperatorType *ot); +void GRAPH_OT_driver_variables_paste(struct wmOperatorType *ot); + +/* ----------- */ + void GRAPH_OT_ghost_curves_create(struct wmOperatorType *ot); void GRAPH_OT_ghost_curves_clear(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index 59215531ac0..6b860990c10 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -458,6 +458,10 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_fmodifier_add); WM_operatortype_append(GRAPH_OT_fmodifier_copy); WM_operatortype_append(GRAPH_OT_fmodifier_paste); + + /* Drivers */ + WM_operatortype_append(GRAPH_OT_driver_variables_copy); + WM_operatortype_append(GRAPH_OT_driver_variables_paste); } void ED_operatormacros_graph(void) @@ -606,7 +610,11 @@ 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); + + kmi = WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "extend", false); + kmi = WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", ACTIONMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", true); /* 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 67274100312..eb786d872ec 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -83,7 +83,7 @@ * 2 = invert * - do_channels: whether to affect selection status of channels */ -static void deselect_graph_keys(bAnimContext *ac, short test, short sel, short do_channels) +void deselect_graph_keys(bAnimContext *ac, bool test, short sel, bool do_channels) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index f22152651e2..a2db6827b0e 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -177,7 +177,7 @@ void image_preview_event(int event) G.is_break = false; G.scene->nodetree->timecursor = set_timecursor; - G.scene->nodetree->test_break = blender_test_break; + G.scene->nodetree->test_break = BKE_blender_test_break; BIF_store_spare(); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 0ddbb1153c0..e810f4db7dd 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -197,6 +197,19 @@ void ED_image_draw_info(Scene *scene, ARegion *ar, bool color_manage, bool use_d dx += BLF_width(blf_mono_font, str, sizeof(str)); } + if (channels == 1 && (cp != NULL || fp != NULL)) { + if (fp != NULL) { + BLI_snprintf(str, sizeof(str), " Val:%-.3f |", fp[0]); + } + else if (cp != NULL) { + BLI_snprintf(str, sizeof(str), " Val:%-.3f |", cp[0] / 255.0f); + } + glColor3ub(255, 255, 255); + BLF_position(blf_mono_font, dx, dy, 0); + BLF_draw_ascii(blf_mono_font, str, sizeof(str)); + dx += BLF_width(blf_mono_font, str, sizeof(str)); + } + if (channels >= 3) { glColor3ubv(red); if (fp) @@ -526,7 +539,12 @@ static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar, } if ((sima->flag & (SI_SHOW_R | SI_SHOW_G | SI_SHOW_B)) == 0) { - glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST); + int clip_max_x, clip_max_y; + UI_view2d_view_to_region(&ar->v2d, + ar->v2d.cur.xmax, ar->v2d.cur.ymax, + &clip_max_x, &clip_max_y); + glaDrawImBuf_glsl_ctx_clipping(C, ibuf, x, y, GL_NEAREST, + 0, 0, clip_max_x, clip_max_y); } else { unsigned char *display_buffer; diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 3b57d17f9f3..69993c3be65 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -63,6 +63,7 @@ void IMAGE_OT_view_zoom(struct wmOperatorType *ot); void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot); void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot); void IMAGE_OT_view_zoom_ratio(struct wmOperatorType *ot); +void IMAGE_OT_view_zoom_border(struct wmOperatorType *ot); void IMAGE_OT_view_ndof(struct wmOperatorType *ot); void IMAGE_OT_new(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index eb74922a256..8db5a8f9bd3 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -149,6 +149,34 @@ static void sima_zoom_set_factor(SpaceImage *sima, ARegion *ar, float zoomfac, c sima_zoom_set(sima, ar, sima->zoom * zoomfac, location); } +/** + * Fits the view to the bounds exactly, caller should add margin if needed. + */ +static void sima_zoom_set_from_bounds(SpaceImage *sima, ARegion *ar, const rctf *bounds) +{ + int image_size[2]; + float aspx, aspy; + + ED_space_image_get_size(sima, &image_size[0], &image_size[1]); + ED_space_image_get_aspect(sima, &aspx, &aspy); + + image_size[0] = image_size[0] * aspx; + image_size[1] = image_size[1] * aspy; + + /* adjust offset and zoom */ + sima->xof = roundf((BLI_rctf_cent_x(bounds) - 0.5f) * image_size[0]); + sima->yof = roundf((BLI_rctf_cent_y(bounds) - 0.5f) * image_size[1]); + + float size_xy[2], size; + size_xy[0] = BLI_rcti_size_x(&ar->winrct) / (BLI_rctf_size_x(bounds) * image_size[0]); + size_xy[1] = BLI_rcti_size_y(&ar->winrct) / (BLI_rctf_size_y(bounds) * image_size[1]); + + size = min_ff(size_xy[0], size_xy[1]); + CLAMP_MAX(size, 100.0f); + + sima_zoom_set(sima, ar, size, NULL); +} + #if 0 // currently unused static int image_poll(bContext *C) { @@ -763,8 +791,6 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene; Object *obedit; Image *ima; - float size, min[2], max[2], d[2], aspx, aspy; - int width, height; /* retrieve state */ sima = CTX_wm_space_image(C); @@ -773,33 +799,28 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) obedit = CTX_data_edit_object(C); ima = ED_space_image(sima); - ED_space_image_get_size(sima, &width, &height); - ED_space_image_get_aspect(sima, &aspx, &aspy); - - width = width * aspx; - height = height * aspy; /* get bounds */ + float min[2], max[2]; if (ED_space_image_show_uvedit(sima, obedit)) { - if (!ED_uvedit_minmax(scene, ima, obedit, min, max)) + if (!ED_uvedit_minmax(scene, ima, obedit, min, max)) { return OPERATOR_CANCELLED; + } } else if (ED_space_image_check_show_maskedit(scene, sima)) { if (!ED_mask_selected_minmax(C, min, max)) { return OPERATOR_CANCELLED; } } + rctf bounds = { + .xmin = min[0], .ymin = min[1], + .xmax = max[0], .ymax = max[1], + }; - /* adjust offset and zoom */ - sima->xof = (int)(((min[0] + max[0]) * 0.5f - 0.5f) * width); - sima->yof = (int)(((min[1] + max[1]) * 0.5f - 0.5f) * height); + /* add some margin */ + BLI_rctf_scale(&bounds, 1.4f); - d[0] = max[0] - min[0]; - d[1] = max[1] - min[1]; - size = 0.5f * MAX2(d[0], d[1]) * MAX2(width, height) / 256.0f; - - if (size <= 0.01f) size = 0.01f; - sima_zoom_set(sima, ar, 0.7f / size, NULL); + sima_zoom_set_from_bounds(sima, ar, &bounds); ED_region_tag_redraw(ar); @@ -969,6 +990,62 @@ void IMAGE_OT_view_zoom_ratio(wmOperatorType *ot) "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out", -FLT_MAX, FLT_MAX); } +/********************** view border-zoom operator *********************/ + +static int image_view_zoom_border_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima = CTX_wm_space_image(C); + ARegion *ar = CTX_wm_region(C); + rctf bounds; + const int gesture_mode = RNA_int_get(op->ptr, "gesture_mode"); + + WM_operator_properties_border_to_rctf(op, &bounds); + + UI_view2d_region_to_view_rctf(&ar->v2d, &bounds, &bounds); + + const struct { + float xof; + float yof; + float zoom; + } sima_view_prev = { + .xof = sima->xof, + .yof = sima->yof, + .zoom = sima->zoom, + }; + + sima_zoom_set_from_bounds(sima, ar, &bounds); + + /* zoom out */ + if (gesture_mode == GESTURE_MODAL_OUT) { + sima->xof = sima_view_prev.xof + (sima->xof - sima_view_prev.xof); + sima->yof = sima_view_prev.yof + (sima->yof - sima_view_prev.yof); + sima->zoom = sima_view_prev.zoom * (sima_view_prev.zoom / sima->zoom); + } + + ED_region_tag_redraw(ar); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_view_zoom_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Zoom to Border"; + ot->description = "Zoom in the view to the nearest item contained in the border"; + ot->idname = "IMAGE_OT_view_zoom_border"; + + /* api callbacks */ + ot->invoke = WM_border_select_invoke; + ot->exec = image_view_zoom_border_exec; + ot->modal = WM_border_select_modal; + ot->cancel = WM_border_select_cancel; + + ot->poll = space_image_main_region_poll; + + /* rna */ + WM_operator_properties_gesture_border(ot, false); +} + /**************** load/replace/save callbacks ******************/ static void image_filesel(bContext *C, wmOperator *op, const char *path) { @@ -2749,8 +2826,8 @@ typedef struct ImageSampleInfo { int *zp; float *zfp; - int draw; - int color_manage; + bool draw; + bool color_manage; int use_default_view; } ImageSampleInfo; @@ -2824,7 +2901,7 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event if (ibuf == NULL) { ED_space_image_release_buffer(sima, ibuf, lock); - info->draw = 0; + info->draw = false; return; } @@ -2841,7 +2918,7 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event info->x = x; info->y = y; - info->draw = 1; + info->draw = true; info->channels = ibuf->channels; info->colp = NULL; @@ -2874,10 +2951,24 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event if (ibuf->rect_float) { fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); - info->colf[0] = fp[0]; - info->colf[1] = fp[1]; - info->colf[2] = fp[2]; - info->colf[3] = fp[3]; + if (ibuf->channels == 4) { + info->colf[0] = fp[0]; + info->colf[1] = fp[1]; + info->colf[2] = fp[2]; + info->colf[3] = fp[3]; + } + else if (ibuf->channels == 3) { + info->colf[0] = fp[0]; + info->colf[1] = fp[1]; + info->colf[2] = fp[2]; + info->colf[3] = 1.0f; + } + else { + info->colf[0] = fp[0]; + info->colf[1] = fp[0]; + info->colf[2] = fp[0]; + info->colf[3] = 1.0f; + } info->colfp = info->colf; copy_v4_v4(info->linearcol, info->colf); @@ -2888,10 +2979,16 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event if (ibuf->zbuf) { info->z = ibuf->zbuf[y * ibuf->x + x]; info->zp = &info->z; + if (ibuf->zbuf == (int*)ibuf->rect) { + info->colp = NULL; + } } if (ibuf->zbuf_float) { info->zf = ibuf->zbuf_float[y * ibuf->x + x]; info->zfp = &info->zf; + if (ibuf->zbuf_float == ibuf->rect_float) { + info->colfp = NULL; + } } if (curve_mapping && ibuf->channels == 4) { @@ -3140,7 +3237,7 @@ static int image_record_composite_apply(bContext *C, wmOperator *op) WM_cursor_time(CTX_wm_window(C), scene->r.cfra); - // XXX scene->nodetree->test_break = blender_test_break; + // XXX scene->nodetree->test_break = BKE_blender_test_break; // XXX scene->nodetree->test_break = NULL; BKE_image_all_free_anim_ibufs(scene->r.cfra); @@ -3554,7 +3651,7 @@ static int clear_render_border_exec(bContext *C, wmOperator *UNUSED(op)) void IMAGE_OT_clear_render_border(wmOperatorType *ot) { /* identifiers */ - ot->name = "Render Border"; + ot->name = "Clear Render Border"; ot->description = "Clear the boundaries of the border render and disable border render"; ot->idname = "IMAGE_OT_clear_render_border"; diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index ccf9e825e1b..168f9c0dfdf 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -232,6 +232,7 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_view_zoom_in); WM_operatortype_append(IMAGE_OT_view_zoom_out); WM_operatortype_append(IMAGE_OT_view_zoom_ratio); + WM_operatortype_append(IMAGE_OT_view_zoom_border); WM_operatortype_append(IMAGE_OT_view_ndof); WM_operatortype_append(IMAGE_OT_new); @@ -303,6 +304,7 @@ static void image_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom", MOUSEZOOM, 0, 0, 0); WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom", MOUSEPAN, 0, KM_CTRL, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0); /* ctrl now works as well, shift + numpad works as arrow keys on Windows */ RNA_float_set(WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_ratio", PAD8, KM_PRESS, KM_CTRL, 0)->ptr, "ratio", 8.0f); diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index dafb71b4480..8dc6c4229b2 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -44,7 +44,7 @@ #include "BLT_translation.h" #include "BKE_anim.h" -#include "BKE_blender.h" +#include "BKE_blender_version.h" #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_DerivedMesh.h" diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 5f046e3b6c6..cbdc476bee6 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -478,12 +478,10 @@ static void nla_panel_modifiers(const bContext *C, Panel *pa) row = uiLayoutRow(pa->layout, false); block = uiLayoutGetBlock(row); - // XXX for now, this will be a operator button which calls a temporary 'add modifier' operator // FIXME: we need to set the only-active property so that this will only add modifiers for the active strip (not all selected) - uiDefButO(block, UI_BTYPE_BUT, "NLA_OT_fmodifier_add", WM_OP_INVOKE_REGION_WIN, IFACE_("Add Modifier"), 10, 0, UI_UNIT_X * 7.0f, UI_UNIT_Y, - TIP_("Adds a new F-Modifier for the active NLA Strip")); + uiItemMenuEnumO(row, (bContext *)C, "NLA_OT_fmodifier_add", "type", IFACE_("Add Modifier"), ICON_NONE); - /* copy/paste (as sub-row)*/ + /* copy/paste (as sub-row) */ row = uiLayoutRow(row, true); uiItemO(row, "", ICON_COPYDOWN, "NLA_OT_fmodifier_copy"); uiItemO(row, "", ICON_PASTEDOWN, "NLA_OT_fmodifier_paste"); diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index ff71cc47158..a2159696394 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -542,6 +542,30 @@ void NLA_OT_view_selected(wmOperatorType *ot) } /* *********************************************** */ + +static int nlaedit_viewframe_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + return OPERATOR_FINISHED; +} + +void NLA_OT_view_frame(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Frame"; + ot->idname = "NLA_OT_view_frame"; + ot->description = "Reset viewable area to show range around current frame"; + + /* api callbacks */ + ot->exec = nlaedit_viewframe_exec; + ot->poll = ED_operator_nla_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* *********************************************** */ /* NLA Editing Operations (Constructive/Destructive) */ /* ******************** Add Action-Clip Operator ***************************** */ @@ -2213,19 +2237,20 @@ void NLA_OT_snap(wmOperatorType *ot) /* ******************** Add F-Modifier Operator *********************** */ -/* present a special customised popup menu for this, with some filtering */ -static int nla_fmodifier_add_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +static EnumPropertyItem *nla_fmodifier_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { - uiPopupMenu *pup; - uiLayout *layout; - int i; + EnumPropertyItem *item = NULL; + int totitem = 0; + int i = 0; - pup = UI_popup_menu_begin(C, IFACE_("Add F-Modifier"), ICON_NONE); - layout = UI_popup_menu_layout(pup); + if (C == NULL) { + return rna_enum_fmodifier_type_items; + } /* start from 1 to skip the 'Invalid' modifier type */ for (i = 1; i < FMODIFIER_NUM_TYPES; i++) { const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i); + int index; /* check if modifier is valid for this context */ if (fmi == NULL) @@ -2233,16 +2258,19 @@ static int nla_fmodifier_add_invoke(bContext *C, wmOperator *UNUSED(op), const w if (i == FMODIFIER_TYPE_CYCLES) /* we already have repeat... */ continue; - /* add entry to add this type of modifier */ - uiItemEnumO(layout, "NLA_OT_fmodifier_add", fmi->name, 0, "type", i); + index = RNA_enum_from_value(rna_enum_fmodifier_type_items, fmi->type); + if (index != -1) { /* Not all types are implemented yet... */ + RNA_enum_item_add(&item, &totitem, &rna_enum_fmodifier_type_items[index]); + } } - uiItemS(layout); - UI_popup_menu_end(C, pup); + RNA_enum_item_end(&item, &totitem); + *r_free = true; - return OPERATOR_INTERFACE; + return item; } + static int nla_fmodifier_add_exec(bContext *C, wmOperator *op) { bAnimContext ac; @@ -2316,10 +2344,10 @@ void NLA_OT_fmodifier_add(wmOperatorType *ot) /* identifiers */ ot->name = "Add F-Modifier"; ot->idname = "NLA_OT_fmodifier_add"; - ot->description = "Add a F-Modifier of the specified type to the selected NLA-Strips"; + ot->description = "Add F-Modifier to the active/selected NLA-Strips"; /* api callbacks */ - ot->invoke = nla_fmodifier_add_invoke; + ot->invoke = WM_menu_invoke; ot->exec = nla_fmodifier_add_exec; ot->poll = nlaop_poll_tweakmode_off; @@ -2328,7 +2356,9 @@ void NLA_OT_fmodifier_add(wmOperatorType *ot) /* id-props */ ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_fmodifier_type_items, 0, "Type", ""); - RNA_def_boolean(ot->srna, "only_active", 0, "Only Active", "Only add a F-Modifier of the specified type to the active strip"); + RNA_def_enum_funcs(ot->prop, nla_fmodifier_itemf); + + RNA_def_boolean(ot->srna, "only_active", true, "Only Active", "Only add a F-Modifier of the specified type to the active strip"); } /* ******************** Copy F-Modifiers Operator *********************** */ @@ -2346,7 +2376,7 @@ static int nla_fmodifier_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; /* clear buffer first */ - free_fmodifiers_copybuf(); + ANIM_fmodifiers_copybuf_free(); /* get a list of the editable tracks being shown in the NLA */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT); @@ -2408,12 +2438,15 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op) bAnimListElem *ale; int filter, ok = 0; + const bool active_only = RNA_boolean_get(op->ptr, "only_active"); + const bool replace = RNA_boolean_get(op->ptr, "replace"); + /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) return OPERATOR_CANCELLED; /* get a list of the editable tracks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* for each NLA-Track, add the specified modifier to all selected strips */ @@ -2422,8 +2455,20 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op) NlaStrip *strip; for (strip = nlt->strips.first; strip; strip = strip->next) { - // TODO: do we want to replace existing modifiers? add user pref for that! - ok += ANIM_fmodifiers_paste_from_buf(&strip->modifiers, 0); + /* can F-Modifier be added to the current strip? */ + if (active_only) { + /* if not active, cannot add since we're only adding to active strip */ + if ((strip->flag & NLASTRIP_FLAG_ACTIVE) == 0) + continue; + } + else { + /* strip must be selected, since we're not just doing active */ + if ((strip->flag & NLASTRIP_FLAG_SELECT) == 0) + continue; + } + + /* paste FModifiers from buffer */ + ok += ANIM_fmodifiers_paste_from_buf(&strip->modifiers, replace); ale->update |= ANIM_UPDATE_DEPS; } } @@ -2456,6 +2501,11 @@ void NLA_OT_fmodifier_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "only_active", true, "Only Active", "Only paste F-Modifiers on active strip"); + RNA_def_boolean(ot->srna, "replace", false, "Replace Existing", + "Replace existing F-Modifiers, instead of just appending to the end of the existing list"); } /* *********************************************** */ diff --git a/source/blender/editors/space_nla/nla_intern.h b/source/blender/editors/space_nla/nla_intern.h index 344580c0d15..806fbe90ff2 100644 --- a/source/blender/editors/space_nla/nla_intern.h +++ b/source/blender/editors/space_nla/nla_intern.h @@ -94,6 +94,7 @@ void NLA_OT_previewrange_set(wmOperatorType *ot); void NLA_OT_view_all(wmOperatorType *ot); void NLA_OT_view_selected(wmOperatorType *ot); +void NLA_OT_view_frame(wmOperatorType *ot); void NLA_OT_actionclip_add(wmOperatorType *ot); void NLA_OT_transition_add(wmOperatorType *ot); diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index 98da10470f8..386950ead3a 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -130,6 +130,7 @@ void nla_operatortypes(void) /* view */ WM_operatortype_append(NLA_OT_view_all); WM_operatortype_append(NLA_OT_view_selected); + WM_operatortype_append(NLA_OT_view_frame); WM_operatortype_append(NLA_OT_previewrange_set); @@ -243,6 +244,7 @@ static void nla_keymap_main(wmKeyConfig *keyconf, wmKeyMap *keymap) WM_keymap_add_item(keymap, "NLA_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "NLA_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "NLA_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "NLA_OT_view_frame", PAD0, KM_PRESS, 0, 0); /* editing ------------------------------------------------ */ diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index b2f3306fb62..ddbd07616bc 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1953,6 +1953,7 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C uiTemplateColorPicker(col, ptr, "offset", 1, 1, 0, 1); row = uiLayoutRow(col, false); uiItemR(row, ptr, "offset", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "offset_basis", 0, NULL, ICON_NONE); col = uiLayoutColumn(split, false); uiTemplateColorPicker(col, ptr, "power", 1, 1, 0, 1); diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 07e79c5a59c..d7249897723 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -107,7 +107,7 @@ static bool node_group_has_output_dfs(bNode *node) current_node = current_node->next) { if (current_node->type == NODE_GROUP) { - if (node_group_has_output_dfs(current_node)) { + if (current_node->id && node_group_has_output_dfs(current_node)) { return true; } } @@ -514,12 +514,10 @@ void NODE_OT_link_viewer(wmOperatorType *ot) static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag)) { -#define HEADER_LENGTH 256 - char header[HEADER_LENGTH]; + char header[UI_MAX_DRAW_STR]; - BLI_strncpy(header, IFACE_("LMB: drag node link, RMB: cancel"), HEADER_LENGTH); + BLI_strncpy(header, IFACE_("LMB: drag node link, RMB: cancel"), sizeof(header)); ED_area_headerprint(CTX_wm_area(C), header); -#undef HEADER_LENGTH } static int node_count_links(bNodeTree *ntree, bNodeSocket *sock) diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c index 0cbb7ea0220..09594ab543c 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.c @@ -32,6 +32,7 @@ #include "DNA_node_types.h" #include "DNA_screen_types.h" +#include "BLI_array.h" #include "BLI_listbase.h" #include "BLI_string.h" @@ -420,6 +421,13 @@ static int ui_compatible_sockets(int typeA, int typeB) return (typeA == typeB); } +static int ui_node_item_name_compare(const void *a, const void *b) +{ + const bNodeType *type_a = *(const bNodeType **)a; + const bNodeType *type_b = *(const bNodeType **)b; + return BLI_natstrcmp(type_a->ui_name, type_b->ui_name); +} + static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) { bNodeTree *ntree = arg->ntree; @@ -439,7 +447,26 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) compatibility = NODE_OLD_SHADING; } + /* generate array of node types sorted by UI name */ + bNodeType **sorted_ntypes = NULL; + BLI_array_declare(sorted_ntypes); + NODE_TYPES_BEGIN(ntype) { + if (compatibility && !(ntype->compatibility & compatibility)) + continue; + + if (ntype->nclass != nclass) + continue; + + BLI_array_append(sorted_ntypes, ntype); + } + NODE_TYPES_END + + qsort(sorted_ntypes, BLI_array_count(sorted_ntypes), sizeof(bNodeType *), ui_node_item_name_compare); + + /* generate UI */ + for (int j = 0; j < BLI_array_count(sorted_ntypes); j++) { + bNodeType *ntype = sorted_ntypes[j]; NodeLinkItem *items; int totitems; char name[UI_MAX_NAME_STR]; @@ -447,12 +474,6 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) int i, num = 0; int icon = ICON_NONE; - if (compatibility && !(ntype->compatibility & compatibility)) - continue; - - if (ntype->nclass != nclass) - continue; - arg->node_type = ntype; ui_node_link_items(arg, SOCK_OUT, &items, &totitems); @@ -502,7 +523,8 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) if (items) MEM_freeN(items); } - NODE_TYPES_END + + BLI_array_free(sorted_ntypes); } static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name) diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 222fe27983c..2627b978b40 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -59,6 +59,7 @@ #include "ED_outliner.h" #include "ED_screen.h" #include "ED_keyframing.h" +#include "ED_armature.h" #include "WM_api.h" #include "WM_types.h" @@ -743,14 +744,35 @@ static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) TreeElement *te; int xdelta, ytop; - - // TODO: make this get this info from context instead... - if (OBACT == NULL) + + Object *obact = OBACT; + + if (!obact) return OPERATOR_CANCELLED; - - te = outliner_find_id(so, &so->tree, (ID *)OBACT); + + + te = outliner_find_id(so, &so->tree, &obact->id); + + if (obact->type == OB_ARMATURE) { + /* traverse down the bone hierarchy in case of armature */ + TreeElement *te_obact = te; + + if (obact->mode & OB_MODE_POSE) { + bPoseChannel *pchan = CTX_data_active_pose_bone(C); + if (pchan) { + te = outliner_find_posechannel(so, &te_obact->subtree, pchan); + } + } + else if (obact->mode & OB_MODE_EDIT) { + EditBone *ebone = CTX_data_active_bone(C); + if (ebone) { + te = outliner_find_editbone(so, &te_obact->subtree, ebone); + } + } + } + if (te) { - /* open up tree to active object */ + /* open up tree to active object/bone */ if (outliner_open_back(te)) { outliner_set_coordinates(ar, so); } diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 5db4897b36d..dc81be7a8e0 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -42,6 +42,8 @@ struct bContext; struct Scene; struct ID; struct Object; +struct bPoseChannel; +struct EditBone; typedef struct TreeElement { struct TreeElement *next, *prev, *parent; @@ -128,9 +130,11 @@ typedef enum { void outliner_free_tree(ListBase *lb); void outliner_cleanup_tree(struct SpaceOops *soops); -TreeElement *outliner_find_tse(struct SpaceOops *soops, TreeStoreElem *tse); -TreeElement *outliner_find_tree_element(ListBase *lb, TreeStoreElem *store_elem); -TreeElement *outliner_find_id(struct SpaceOops *soops, ListBase *lb, struct ID *id); +TreeElement *outliner_find_tse(struct SpaceOops *soops, const TreeStoreElem *tse); +TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem); +TreeElement *outliner_find_id(struct SpaceOops *soops, ListBase *lb, const struct ID *id); +TreeElement *outliner_find_posechannel(struct SpaceOops *soops, ListBase *lb, const struct bPoseChannel *pchan); +TreeElement *outliner_find_editbone(struct SpaceOops *soops, ListBase *lb, const struct EditBone *ebone); struct ID *outliner_search_back(SpaceOops *soops, TreeElement *te, short idcode); void outliner_build_tree(struct Main *mainvar, struct Scene *scene, struct SpaceOops *soops); diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 84f8c629c5d..21bee2c7cce 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -612,6 +612,8 @@ static void tree_element_active_ebone__sel(bContext *C, Scene *scene, bArmature static eOLDrawState tree_element_active_ebone( bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tselem), const eOLSetState set, bool recursive) { + BLI_assert(scene->obedit != NULL); + bArmature *arm = scene->obedit->data; EditBone *ebone = te->directdata; eOLDrawState status = OL_DRAWSEL_NONE; @@ -915,11 +917,14 @@ static bool do_outliner_item_activate(bContext *C, Scene *scene, ARegion *ar, Sp /* name and first icon */ else if (mval[0] > te->xs + UI_UNIT_X && mval[0] < te->xend) { - /* always makes active object */ - if (tselem->type != TSE_SEQUENCE && tselem->type != TSE_SEQ_STRIP && tselem->type != TSE_SEQUENCE_DUP) + /* always makes active object, except for some specific types. + * Note about TSE_EBONE: In case of a same ID_AR datablock shared among several objects, we do not want + * to switch out of edit mode (see T48328 for details). */ + if (!ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP, TSE_EBONE)) { tree_element_set_active_object(C, scene, soops, te, (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, recursive && tselem->type == 0); + } if (tselem->type == 0) { // the lib blocks /* editmode? */ diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index d688e628967..a687f61d69f 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -199,7 +199,7 @@ void outliner_cleanup_tree(SpaceOops *soops) } /* Find specific item from the treestore */ -TreeElement *outliner_find_tree_element(ListBase *lb, TreeStoreElem *store_elem) +TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem) { TreeElement *te, *tes; for (te = lb->first; te; te = te->next) { @@ -211,7 +211,7 @@ TreeElement *outliner_find_tree_element(ListBase *lb, TreeStoreElem *store_elem) } /* tse is not in the treestore, we use its contents to find a match */ -TreeElement *outliner_find_tse(SpaceOops *soops, TreeStoreElem *tse) +TreeElement *outliner_find_tse(SpaceOops *soops, const TreeStoreElem *tse) { TreeStoreElem *tselem; @@ -226,25 +226,63 @@ TreeElement *outliner_find_tse(SpaceOops *soops, TreeStoreElem *tse) } /* Find treestore that refers to given ID */ -TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, ID *id) +TreeElement *outliner_find_id(SpaceOops *soops, ListBase *lb, const ID *id) { - TreeElement *te, *tes; - TreeStoreElem *tselem; - - for (te = lb->first; te; te = te->next) { - tselem = TREESTORE(te); + for (TreeElement *te = lb->first; te; te = te->next) { + TreeStoreElem *tselem = TREESTORE(te); if (tselem->type == 0) { - if (tselem->id == id) return te; + if (tselem->id == id) { + return te; + } /* only deeper on scene or object */ - if (te->idcode == ID_OB || te->idcode == ID_SCE || (soops->outlinevis == SO_GROUPS && te->idcode == ID_GR)) { - tes = outliner_find_id(soops, &te->subtree, id); - if (tes) return tes; + if (ELEM(te->idcode, ID_OB, ID_SCE) || + ((soops->outlinevis == SO_GROUPS) && (te->idcode == ID_GR))) + { + TreeElement *tes = outliner_find_id(soops, &te->subtree, id); + if (tes) { + return tes; + } + } + } + } + return NULL; +} + +TreeElement *outliner_find_posechannel(SpaceOops *soops, ListBase *lb, const bPoseChannel *pchan) +{ + for (TreeElement *te = lb->first; te; te = te->next) { + if (te->directdata == pchan) { + return te; + } + + TreeStoreElem *tselem = TREESTORE(te); + if (ELEM(tselem->type, TSE_POSE_BASE, TSE_POSE_CHANNEL)) { + TreeElement *tes = outliner_find_posechannel(soops, &te->subtree, pchan); + if (tes) { + return tes; } } } return NULL; } +TreeElement *outliner_find_editbone(SpaceOops *soops, ListBase *lb, const EditBone *ebone) +{ + for (TreeElement *te = lb->first; te; te = te->next) { + if (te->directdata == ebone) { + return te; + } + + TreeStoreElem *tselem = TREESTORE(te); + if (ELEM(tselem->type, 0, TSE_EBONE)) { + TreeElement *tes = outliner_find_editbone(soops, &te->subtree, ebone); + if (tes) { + return tes; + } + } + } + return NULL; +} ID *outliner_search_back(SpaceOops *UNUSED(soops), TreeElement *te, short idcode) { diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index df3e508ae0e..8ae89941bdb 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -61,6 +61,7 @@ /* for menu/popup icons etc etc*/ +#include "ED_anim_api.h" #include "ED_numinput.h" #include "ED_screen.h" #include "ED_transform.h" @@ -1505,23 +1506,20 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op) static void sequencer_slip_update_header(Scene *scene, ScrArea *sa, SlipData *data, int offset) { -#define HEADER_LENGTH 40 - char msg[HEADER_LENGTH]; + char msg[UI_MAX_DRAW_STR]; if (sa) { if (hasNumInput(&data->num_input)) { char num_str[NUM_STR_REP_LEN]; outputNumInput(&data->num_input, num_str, &scene->unit); - BLI_snprintf(msg, HEADER_LENGTH, "Trim offset: %s", num_str); + BLI_snprintf(msg, sizeof(msg), IFACE_("Trim offset: %s"), num_str); } else { - BLI_snprintf(msg, HEADER_LENGTH, "Trim offset: %d", offset); + BLI_snprintf(msg, sizeof(msg), IFACE_("Trim offset: %d"), offset); } } ED_area_headerprint(sa, msg); - -#undef HEADER_LENGTH } static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *event) @@ -2697,6 +2695,29 @@ void SEQUENCER_OT_view_all(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER; } +static int sequencer_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_view_frame(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Frame"; + ot->idname = "SEQUENCER_OT_view_frame"; + ot->description = "Reset viewable area to show range around current frame"; + + /* api callbacks */ + ot->exec = sequencer_view_frame_exec; + ot->poll = ED_operator_sequencer_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* view_all operator */ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op)) { diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 3e228fd0b31..730cc117287 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -121,6 +121,7 @@ void SEQUENCER_OT_rendersize(struct wmOperatorType *ot); void SEQUENCER_OT_view_toggle(struct wmOperatorType *ot); void SEQUENCER_OT_view_all(struct wmOperatorType *ot); void SEQUENCER_OT_view_selected(struct wmOperatorType *ot); +void SEQUENCER_OT_view_frame(struct wmOperatorType *ot); void SEQUENCER_OT_view_zoom_ratio(struct wmOperatorType *ot); void SEQUENCER_OT_view_ghost_border(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 3d08e0c5ed8..655e029cfdd 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -84,6 +84,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_view_all); WM_operatortype_append(SEQUENCER_OT_view_selected); + WM_operatortype_append(SEQUENCER_OT_view_frame); WM_operatortype_append(SEQUENCER_OT_view_all_preview); WM_operatortype_append(SEQUENCER_OT_view_toggle); WM_operatortype_append(SEQUENCER_OT_view_zoom_ratio); @@ -202,6 +203,7 @@ void sequencer_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "SEQUENCER_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "SEQUENCER_OT_view_frame", PAD0, KM_PRESS, 0, 0); kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_strip_jump", PAGEUPKEY, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "next", true); diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 87270bace9a..0a6a9a81e63 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -79,7 +79,15 @@ static SpaceLink *text_new(const bContext *UNUSED(C)) BLI_addtail(&stext->regionbase, ar); ar->regiontype = RGN_TYPE_HEADER; ar->alignment = RGN_ALIGN_BOTTOM; - + + /* properties region */ + ar = MEM_callocN(sizeof(ARegion), "properties region for text"); + + BLI_addtail(&stext->regionbase, ar); + ar->regiontype = RGN_TYPE_UI; + ar->alignment = RGN_ALIGN_LEFT; + ar->flag = RGN_FLAG_HIDDEN; + /* main region */ ar = MEM_callocN(sizeof(ARegion), "main region for text"); diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 9448f6af69f..81605a80f69 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1009,7 +1009,7 @@ static void draw_documentation(const SpaceText *st, ARegion *ar) if (lines >= DOC_HEIGHT) break; } - if (0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) { + if (0 /* XXX doc_scroll*/ /* > 0 && lines < DOC_HEIGHT */) { // XXX doc_scroll--; draw_documentation(st, ar); } diff --git a/source/blender/editors/space_time/time_ops.c b/source/blender/editors/space_time/time_ops.c index e2e861fda38..7dd45327352 100644 --- a/source/blender/editors/space_time/time_ops.c +++ b/source/blender/editors/space_time/time_ops.c @@ -39,6 +39,7 @@ #include "BKE_context.h" +#include "ED_anim_api.h" #include "ED_screen.h" #include "WM_api.h" @@ -140,18 +141,18 @@ static int time_view_all_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); ARegion *ar = CTX_wm_region(C); - View2D *v2d = (ar) ? &ar->v2d : NULL; - float extra; - + if (ELEM(NULL, scene, ar)) return OPERATOR_CANCELLED; - + + View2D *v2d = &ar->v2d; + /* set extents of view to start/end frames (Preview Range too) */ v2d->cur.xmin = (float)PSFRA; v2d->cur.xmax = (float)PEFRA; /* we need an extra "buffer" factor on either side so that the endpoints are visible */ - extra = 0.01f * BLI_rctf_size_x(&v2d->cur); + const float extra = 0.01f * BLI_rctf_size_x(&v2d->cur); v2d->cur.xmin -= extra; v2d->cur.xmax += extra; @@ -176,6 +177,31 @@ static void TIME_OT_view_all(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ************************ View Frame Operator *******************************/ + +static int time_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + + return OPERATOR_FINISHED; +} + +static void TIME_OT_view_frame(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Frame"; + ot->idname = "TIME_OT_view_frame"; + ot->description = "Reset viewable area to show range around current frame"; + + /* api callbacks */ + ot->exec = time_view_frame_exec; + ot->poll = ED_operator_timeline_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ************************** registration **********************************/ void time_operatortypes(void) @@ -183,6 +209,7 @@ void time_operatortypes(void) WM_operatortype_append(TIME_OT_start_frame_set); WM_operatortype_append(TIME_OT_end_frame_set); WM_operatortype_append(TIME_OT_view_all); + WM_operatortype_append(TIME_OT_view_frame); } void time_keymap(wmKeyConfig *keyconf) @@ -193,5 +220,6 @@ void time_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "TIME_OT_end_frame_set", EKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "TIME_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "TIME_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "TIME_OT_view_frame", PAD0, KM_PRESS, 0, 0); } diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index c6b2ccee85b..f7c1e2ee981 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -53,6 +53,7 @@ #include "BKE_global.h" #include "BKE_modifier.h" #include "BKE_nla.h" +#include "BKE_curve.h" #include "BIF_gl.h" @@ -1093,20 +1094,101 @@ static void draw_line_bone(int armflag, int boneflag, short constflag, unsigned glPopMatrix(); } -static void draw_b_bone_boxes(const short dt, bPoseChannel *pchan, float xwidth, float length, float zwidth) +/* A partial copy of b_bone_spline_setup(), with just the parts for previewing editmode curve settings + * + * This assumes that prev/next bones don't have any impact (since they should all still be in the "straight" + * position here anyway), and that we can simply apply the bbone settings to get the desired effect... + */ +static void ebone_spline_preview(EditBone *ebone, Mat4 result_array[MAX_BBONE_SUBDIV]) +{ + float h1[3], h2[3], length, hlength1, hlength2, roll1 = 0.0f, roll2 = 0.0f; + float mat3[3][3]; + float data[MAX_BBONE_SUBDIV + 1][4], *fp; + int a; + + length = ebone->length; + + hlength1 = ebone->ease1 * length * 0.390464f; /* 0.5f * sqrt(2) * kappa, the handle length for near-perfect circles */ + hlength2 = ebone->ease2 * length * 0.390464f; + + /* find the handle points, since this is inside bone space, the + * first point = (0, 0, 0) + * last point = (0, length, 0) + * + * we also just apply all the "extra effects", since they're the whole reason we're doing this... + */ + h1[0] = ebone->curveInX; + h1[1] = hlength1; + h1[2] = ebone->curveInY; + roll1 = ebone->roll1; + + h2[0] = ebone->curveOutX; + h2[1] = -hlength2; + h2[2] = ebone->curveOutY; + roll2 = ebone->roll2; + + /* make curve */ + if (ebone->segments > MAX_BBONE_SUBDIV) + ebone->segments = MAX_BBONE_SUBDIV; + + BKE_curve_forward_diff_bezier(0.0f, h1[0], h2[0], 0.0f, data[0], MAX_BBONE_SUBDIV, 4 * sizeof(float)); + BKE_curve_forward_diff_bezier(0.0f, h1[1], length + h2[1], length, data[0] + 1, MAX_BBONE_SUBDIV, 4 * sizeof(float)); + BKE_curve_forward_diff_bezier(0.0f, h1[2], h2[2], 0.0f, data[0] + 2, MAX_BBONE_SUBDIV, 4 * sizeof(float)); + BKE_curve_forward_diff_bezier(roll1, roll1 + 0.390464f * (roll2 - roll1), roll2 - 0.390464f * (roll2 - roll1), roll2, data[0] + 3, MAX_BBONE_SUBDIV, 4 * sizeof(float)); + + equalize_bbone_bezier(data[0], ebone->segments); /* note: does stride 4! */ + + /* make transformation matrices for the segments for drawing */ + for (a = 0, fp = data[0]; a < ebone->segments; a++, fp += 4) { + sub_v3_v3v3(h1, fp + 4, fp); + vec_roll_to_mat3(h1, fp[3], mat3); /* fp[3] is roll */ + + copy_m4_m3(result_array[a].mat, mat3); + copy_v3_v3(result_array[a].mat[3], fp); + + /* "extra" scale facs... */ + { + const int num_segments = ebone->segments; + + const float scaleFactorIn = 1.0f + (ebone->scaleIn - 1.0f) * ((float)(num_segments - a) / (float)num_segments); + const float scaleFactorOut = 1.0f + (ebone->scaleOut - 1.0f) * ((float)(a + 1) / (float)num_segments); + + const float scalefac = scaleFactorIn * scaleFactorOut; + float bscalemat[4][4], bscale[3]; + + bscale[0] = scalefac; + bscale[1] = 1.0f; + bscale[2] = scalefac; + + size_to_mat4(bscalemat, bscale); + + /* Note: don't multiply by inverse scale mat here, as it causes problems with scaling shearing and breaking segment chains */ + mul_m4_series(result_array[a].mat, result_array[a].mat, bscalemat); + } + } +} + +static void draw_b_bone_boxes(const short dt, bPoseChannel *pchan, EditBone *ebone, float xwidth, float length, float zwidth) { int segments = 0; if (pchan) segments = pchan->bone->segments; + else if (ebone) + segments = ebone->segments; - if ((segments > 1) && (pchan)) { + if (segments > 1) { float dlen = length / (float)segments; Mat4 bbone[MAX_BBONE_SUBDIV]; int a; - - b_bone_spline_setup(pchan, 0, bbone); - + + if (pchan) { + b_bone_spline_setup(pchan, 0, bbone); + } + else if (ebone) { + ebone_spline_preview(ebone, bbone); + } + for (a = 0; a < segments; a++) { glPushMatrix(); glMultMatrixf(bbone[a].mat); @@ -1177,7 +1259,7 @@ static void draw_b_bone(const short dt, int armflag, int boneflag, short constfl else UI_ThemeColor(TH_BONE_SOLID); - draw_b_bone_boxes(OB_SOLID, pchan, xwidth, length, zwidth); + draw_b_bone_boxes(OB_SOLID, pchan, ebone, xwidth, length, zwidth); /* disable solid drawing */ GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); @@ -1190,7 +1272,7 @@ static void draw_b_bone(const short dt, int armflag, int boneflag, short constfl if (set_pchan_glColor(PCHAN_COLOR_CONSTS, boneflag, constflag)) { glEnable(GL_BLEND); - draw_b_bone_boxes(OB_SOLID, pchan, xwidth, length, zwidth); + draw_b_bone_boxes(OB_SOLID, pchan, ebone, xwidth, length, zwidth); glDisable(GL_BLEND); } @@ -1200,7 +1282,7 @@ static void draw_b_bone(const short dt, int armflag, int boneflag, short constfl } } - draw_b_bone_boxes(OB_WIRE, pchan, xwidth, length, zwidth); + draw_b_bone_boxes(OB_WIRE, pchan, ebone, xwidth, length, zwidth); } } @@ -1892,7 +1974,9 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, } /* custom bone may draw outline double-width */ - glLineWidth(1.0f); + if (arm->flag & ARM_POSEMODE) { + glLineWidth(1.0f); + } /* wire draw over solid only in posemode */ if ((dt <= OB_WIRE) || (arm->flag & ARM_POSEMODE) || ELEM(arm->drawtype, ARM_LINE, ARM_WIRE)) { diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index 345e47940d5..a73a055c5c8 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -713,6 +713,8 @@ static void update_tface_color_layer(DerivedMesh *dm, bool use_mcol) } } } + + dm->dirty |= DM_DIRTY_MCOL_UPDATE_DRAW; } static DMDrawOption draw_tface_mapped__set_draw(void *userData, int origindex, int UNUSED(mat_nr)) @@ -999,7 +1001,16 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d else { userData.me = NULL; - update_tface_color_layer(dm, !(ob->mode & OB_MODE_TEXTURE_PAINT)); + if ((ob->mode & OB_MODE_ALL_PAINT) == 0) { + + /* Note: this isn't efficient and runs on every redraw, + * its needed so material colors are used for vertex colors. + * In the future we will likely remove 'texface' so, just avoid running this where possible, + * (when vertex paint or weight paint are used). */ + + update_tface_color_layer(dm, !(ob->mode & OB_MODE_TEXTURE_PAINT)); + } + dm->drawFacesTex( dm, draw_tface__set_draw, compareDrawOptions, &userData, dm_draw_flag); diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 6199ed85ba1..65f1f375443 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -3678,29 +3678,10 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, { RegionView3D *rv3d = ar->regiondata; Mesh *me = ob->data; - BMFace *efa_act = BM_mesh_active_face_get(em->bm, false, true); /* annoying but active faces is stored differently */ - BMEdge *eed_act = NULL; - BMVert *eve_act = NULL; - bool use_occlude_wire = (v3d->flag2 & V3D_OCCLUDE_WIRE) && (dt > OB_WIRE); + const bool use_occlude_wire = (dt > OB_WIRE) && (v3d->flag2 & V3D_OCCLUDE_WIRE); + bool use_depth_offset = false; glLineWidth(1); - - if (em->bm->selected.last) { - BMEditSelection *ese = em->bm->selected.last; - /* face is handled above */ -#if 0 - if (ese->type == BM_FACE) { - efa_act = (BMFace *)ese->data; - } - else -#endif - if (ese->htype == BM_EDGE) { - eed_act = (BMEdge *)ese->ele; - } - else if (ese->htype == BM_VERT) { - eve_act = (BMVert *)ese->ele; - } - } BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE | BM_FACE); @@ -3710,6 +3691,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, ED_view3d_polygon_offset(rv3d, 1.0); glDepthMask(0); + use_depth_offset = true; } else { glEnable(GL_DEPTH_TEST); @@ -3756,6 +3738,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, ED_view3d_polygon_offset(rv3d, 1.0); glDepthMask(0); + use_depth_offset = true; } else { if (cageDM != finalDM) { @@ -3764,145 +3747,172 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, } } - if ((me->drawflag & ME_DRAWFACES) && (use_occlude_wire == false)) { /* transp faces */ - unsigned char col1[4], col2[4], col3[4]; + if ((dt > OB_WIRE) && (v3d->flag2 & V3D_RENDER_SHADOW)) { + /* pass */ + } + else { + /* annoying but active faces is stored differently */ + BMFace *efa_act = BM_mesh_active_face_get(em->bm, false, true); + BMEdge *eed_act = NULL; + BMVert *eve_act = NULL; + + if (em->bm->selected.last) { + BMEditSelection *ese = em->bm->selected.last; + /* face is handled above */ +#if 0 + if (ese->type == BM_FACE) { + efa_act = (BMFace *)ese->data; + } + else +#endif + if (ese->htype == BM_EDGE) { + eed_act = (BMEdge *)ese->ele; + } + else if (ese->htype == BM_VERT) { + eve_act = (BMVert *)ese->ele; + } + } + + if ((me->drawflag & ME_DRAWFACES) && (use_occlude_wire == false)) { /* transp faces */ + unsigned char col1[4], col2[4], col3[4]; #ifdef WITH_FREESTYLE - unsigned char col4[4]; + unsigned char col4[4]; #endif - UI_GetThemeColor4ubv(TH_FACE, col1); - UI_GetThemeColor4ubv(TH_FACE_SELECT, col2); - UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); + UI_GetThemeColor4ubv(TH_FACE, col1); + UI_GetThemeColor4ubv(TH_FACE_SELECT, col2); + UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); #ifdef WITH_FREESTYLE - UI_GetThemeColor4ubv(TH_FREESTYLE_FACE_MARK, col4); + UI_GetThemeColor4ubv(TH_FREESTYLE_FACE_MARK, col4); #endif - glEnable(GL_BLEND); - glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ - /* don't draw unselected faces, only selected, this is MUCH nicer when texturing */ - if (check_object_draw_texture(scene, v3d, dt)) - col1[3] = 0; + /* don't draw unselected faces, only selected, this is MUCH nicer when texturing */ + if (check_object_draw_texture(scene, v3d, dt)) + col1[3] = 0; #ifdef WITH_FREESTYLE - if (!(me->drawflag & ME_DRAW_FREESTYLE_FACE) || !CustomData_has_layer(&em->bm->pdata, CD_FREESTYLE_FACE)) - col4[3] = 0; + if (!(me->drawflag & ME_DRAW_FREESTYLE_FACE) || !CustomData_has_layer(&em->bm->pdata, CD_FREESTYLE_FACE)) + col4[3] = 0; - draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act); + draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act); #else - draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act); + draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act); #endif - glDisable(GL_BLEND); - glDepthMask(1); /* restore write in zbuffer */ - } - else if (efa_act) { - /* even if draw faces is off it would be nice to draw the stipple face - * Make all other faces zero alpha except for the active */ - unsigned char col1[4], col2[4], col3[4]; + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } + else if (efa_act) { + /* even if draw faces is off it would be nice to draw the stipple face + * Make all other faces zero alpha except for the active */ + unsigned char col1[4], col2[4], col3[4]; #ifdef WITH_FREESTYLE - unsigned char col4[4]; - col4[3] = 0; /* don't draw */ + unsigned char col4[4]; + col4[3] = 0; /* don't draw */ #endif - col1[3] = col2[3] = 0; /* don't draw */ + col1[3] = col2[3] = 0; /* don't draw */ - UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); + UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); - glEnable(GL_BLEND); - glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ #ifdef WITH_FREESTYLE - draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act); + draw_dm_faces_sel(em, cageDM, col1, col2, col3, col4, efa_act); #else - draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act); + draw_dm_faces_sel(em, cageDM, col1, col2, col3, efa_act); #endif - glDisable(GL_BLEND); - glDepthMask(1); /* restore write in zbuffer */ - } + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } - /* here starts all fancy draw-extra over */ - if ((me->drawflag & ME_DRAWEDGES) == 0 && check_object_draw_texture(scene, v3d, dt)) { - /* we are drawing textures and 'ME_DRAWEDGES' is disabled, don't draw any edges */ - - /* only draw selected edges otherwise there is no way of telling if a face is selected */ - draw_em_fancy_edges(em, scene, v3d, me, cageDM, 1, eed_act); - - } - else { - if (me->drawflag & ME_DRAWSEAMS) { - UI_ThemeColor(TH_EDGE_SEAM); - glLineWidth(2); + /* here starts all fancy draw-extra over */ + if ((me->drawflag & ME_DRAWEDGES) == 0 && check_object_draw_texture(scene, v3d, dt)) { + /* we are drawing textures and 'ME_DRAWEDGES' is disabled, don't draw any edges */ - draw_dm_edges_seams(em, cageDM); + /* only draw selected edges otherwise there is no way of telling if a face is selected */ + draw_em_fancy_edges(em, scene, v3d, me, cageDM, 1, eed_act); - glColor3ub(0, 0, 0); } - - if (me->drawflag & ME_DRAWSHARP) { - UI_ThemeColor(TH_EDGE_SHARP); - glLineWidth(2); + else { + if (me->drawflag & ME_DRAWSEAMS) { + UI_ThemeColor(TH_EDGE_SEAM); + glLineWidth(2); - draw_dm_edges_sharp(em, cageDM); + draw_dm_edges_seams(em, cageDM); - glColor3ub(0, 0, 0); - } + glColor3ub(0, 0, 0); + } + + if (me->drawflag & ME_DRAWSHARP) { + UI_ThemeColor(TH_EDGE_SHARP); + glLineWidth(2); + + draw_dm_edges_sharp(em, cageDM); + + glColor3ub(0, 0, 0); + } #ifdef WITH_FREESTYLE - if (me->drawflag & ME_DRAW_FREESTYLE_EDGE && CustomData_has_layer(&em->bm->edata, CD_FREESTYLE_EDGE)) { - UI_ThemeColor(TH_FREESTYLE_EDGE_MARK); - glLineWidth(2); - - draw_dm_edges_freestyle(em, cageDM); - - glColor3ub(0, 0, 0); - } -#endif - - if (me->drawflag & ME_DRAWCREASES) { - draw_dm_creases(em, cageDM); - } - if (me->drawflag & ME_DRAWBWEIGHTS) { - draw_dm_bweights(em, scene, cageDM); - } + if (me->drawflag & ME_DRAW_FREESTYLE_EDGE && CustomData_has_layer(&em->bm->edata, CD_FREESTYLE_EDGE)) { + UI_ThemeColor(TH_FREESTYLE_EDGE_MARK); + glLineWidth(2); - glLineWidth(1); - draw_em_fancy_edges(em, scene, v3d, me, cageDM, 0, eed_act); - } + draw_dm_edges_freestyle(em, cageDM); - { - draw_em_fancy_verts(scene, v3d, ob, em, cageDM, eve_act, rv3d); + glColor3ub(0, 0, 0); + } +#endif - if (me->drawflag & ME_DRAWNORMALS) { - UI_ThemeColor(TH_NORMAL); - draw_dm_face_normals(em, scene, ob, cageDM); - } - if (me->drawflag & ME_DRAW_VNORMALS) { - UI_ThemeColor(TH_VNORMAL); - draw_dm_vert_normals(em, scene, ob, cageDM); - } - if (me->drawflag & ME_DRAW_LNORMALS) { - UI_ThemeColor(TH_LNORMAL); - draw_dm_loop_normals(em, scene, ob, cageDM); - } + if (me->drawflag & ME_DRAWCREASES) { + draw_dm_creases(em, cageDM); + } + if (me->drawflag & ME_DRAWBWEIGHTS) { + draw_dm_bweights(em, scene, cageDM); + } - if ((me->drawflag & (ME_DRAWEXTRA_EDGELEN | - ME_DRAWEXTRA_FACEAREA | - ME_DRAWEXTRA_FACEANG | - ME_DRAWEXTRA_EDGEANG)) && - !(v3d->flag2 & V3D_RENDER_OVERRIDE)) - { - draw_em_measure_stats(ar, v3d, ob, em, &scene->unit); + glLineWidth(1); + draw_em_fancy_edges(em, scene, v3d, me, cageDM, 0, eed_act); } - if ((G.debug & G_DEBUG) && (me->drawflag & ME_DRAWEXTRA_INDICES) && - !(v3d->flag2 & V3D_RENDER_OVERRIDE)) { - draw_em_indices(em); + draw_em_fancy_verts(scene, v3d, ob, em, cageDM, eve_act, rv3d); + + if (me->drawflag & ME_DRAWNORMALS) { + UI_ThemeColor(TH_NORMAL); + draw_dm_face_normals(em, scene, ob, cageDM); + } + if (me->drawflag & ME_DRAW_VNORMALS) { + UI_ThemeColor(TH_VNORMAL); + draw_dm_vert_normals(em, scene, ob, cageDM); + } + if (me->drawflag & ME_DRAW_LNORMALS) { + UI_ThemeColor(TH_LNORMAL); + draw_dm_loop_normals(em, scene, ob, cageDM); + } + + if ((me->drawflag & (ME_DRAWEXTRA_EDGELEN | + ME_DRAWEXTRA_FACEAREA | + ME_DRAWEXTRA_FACEANG | + ME_DRAWEXTRA_EDGEANG)) && + !(v3d->flag2 & V3D_RENDER_OVERRIDE)) + { + draw_em_measure_stats(ar, v3d, ob, em, &scene->unit); + } + + if ((G.debug & G_DEBUG) && (me->drawflag & ME_DRAWEXTRA_INDICES) && + !(v3d->flag2 & V3D_RENDER_OVERRIDE)) + { + draw_em_indices(em); + } } } - if (dt > OB_WIRE) { + if (use_depth_offset) { glDepthMask(1); ED_view3d_polygon_offset(rv3d, 0.0); GPU_object_material_unbind(); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 7f1a7a059fc..184b9ff5b15 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -315,12 +315,12 @@ static void drawgrid(UnitSettings *unit, ARegion *ar, View3D *v3d, const char ** if (unit->system) { /* Use GRID_MIN_PX * 2 for units because very very small grid * items are less useful when dealing with units */ - void *usys; + const void *usys; int len, i; double dx_scalar; float blend_fac; - bUnit_GetSystem(&usys, &len, unit->system, B_UNIT_LENGTH); + bUnit_GetSystem(unit->system, B_UNIT_LENGTH, &usys, &len); if (usys) { i = len; @@ -455,10 +455,10 @@ float ED_scene_grid_scale(Scene *scene, const char **grid_unit) { /* apply units */ if (scene->unit.system) { - void *usys; + const void *usys; int len; - bUnit_GetSystem(&usys, &len, scene->unit.system, B_UNIT_LENGTH); + bUnit_GetSystem(scene->unit.system, B_UNIT_LENGTH, &usys, &len); if (usys) { int i = bUnit_GetBaseUnit(usys); @@ -3976,6 +3976,7 @@ static void view3d_main_region_draw_info(const bContext *C, Scene *scene, drawviewborder(scene, ar, v3d); } else if (v3d->flag2 & V3D_RENDER_BORDER) { + glLineWidth(1.0f); setlinestyle(3); cpack(0x4040FF); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 7cd20401a53..c7a10d65071 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -37,6 +37,7 @@ #include "DNA_curve_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" #include "MEM_guardedalloc.h" @@ -74,6 +75,7 @@ #include "ED_screen.h" #include "ED_transform.h" #include "ED_mesh.h" +#include "ED_gpencil.h" #include "ED_view3d.h" #include "UI_resources.h" @@ -1229,6 +1231,8 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_PASS_THROUGH; } + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar); + /* switch from camera view when: */ if (view3d_ensure_persp(vod->v3d, vod->ar)) { /* If we're switching from camera view to the perspective one, @@ -1647,8 +1651,10 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewops_data_alloc(C, op); viewops_data_create_ex(C, op, event, (U.uiflag & USER_ORBIT_SELECTION) != 0, false); - vod = op->customdata; + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar); + v3d = vod->v3d; rv3d = vod->rv3d; @@ -1715,6 +1721,9 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev (U.uiflag & USER_ORBIT_SELECTION) != 0, false); vod = op->customdata; + + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar); + v3d = vod->v3d; rv3d = vod->rv3d; @@ -2024,6 +2033,8 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewops_data_create(C, op, event); vod = op->customdata; + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar); + if (event->type == MOUSEPAN) { /* invert it, trackpad scroll follows same principle as 2d windows this way */ viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy); @@ -2503,6 +2514,8 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewops_data_create(C, op, event); vod = op->customdata; + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar); + /* if one or the other zoom position aren't set, set from event */ if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { RNA_int_set(op->ptr, "mx", event->x); @@ -2743,6 +2756,8 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_PASS_THROUGH; } + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar); + /* needs to run before 'viewops_data_create' so the backup 'rv3d->ofs' is correct */ /* switch from camera view when: */ if (vod->rv3d->persp != RV3D_PERSP) { @@ -2838,6 +2853,8 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, float afm[3]; float size; + ED_view3d_smooth_view_force_finish(C, v3d, ar); + /* SMOOTHVIEW */ float new_ofs[3]; float new_dist; @@ -3004,6 +3021,8 @@ static int viewselected_exec(bContext *C, wmOperator *op) ARegion *ar = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); Object *ob = OBACT; Object *obedit = CTX_data_edit_object(C); float min[3], max[3]; @@ -3016,6 +3035,10 @@ static int viewselected_exec(bContext *C, wmOperator *op) INIT_MINMAX(min, max); + if (is_gp_edit) { + ob = NULL; + } + if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) { /* hard-coded exception, we look for the one selected armature */ /* this is weak code this way, we should make a generic active/selection callback interface once... */ @@ -3032,7 +3055,19 @@ static int viewselected_exec(bContext *C, wmOperator *op) } - if (obedit) { + if (is_gp_edit) { + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + /* we're only interested in selected points here... */ + if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) { + if (ED_gpencil_stroke_minmax(gps, true, min, max)) { + ok = true; + } + } + } + CTX_DATA_END; + } + else if (obedit) { ok = ED_view3d_minmax_verts(obedit, min, max); /* only selected */ } else if (ob && (ob->mode & OB_MODE_POSE)) { @@ -3196,6 +3231,8 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op) ARegion *ar = CTX_wm_region(C); const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ED_view3d_smooth_view_force_finish(C, v3d, ar); + /* non camera center */ float new_ofs[3]; negate_v3_v3(new_ofs, ED_view3d_cursor3d_get(scene, v3d)); @@ -3235,6 +3272,8 @@ static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *ev float new_ofs[3]; const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ED_view3d_smooth_view_force_finish(C, v3d, ar); + view3d_operator_needs_opengl(C); if (ED_view3d_autodist(scene, ar, v3d, event->mval, new_ofs, false, NULL)) { @@ -3840,6 +3879,8 @@ static int viewnumpad_exec(bContext *C, wmOperator *op) ED_view3d_context_user_region(C, &v3d, &ar); rv3d = ar->regiondata; + ED_view3d_smooth_view_force_finish(C, v3d, ar); + viewnum = RNA_enum_get(op->ptr, "type"); align_active = RNA_boolean_get(op->ptr, "align_active"); @@ -3990,6 +4031,8 @@ static int vieworbit_exec(bContext *C, wmOperator *op) rv3d = ar->regiondata; } + ED_view3d_smooth_view_force_finish(C, v3d, ar); + if ((rv3d->viewlock & RV3D_LOCKED) == 0 || (view_opposite != RV3D_VIEW_USER)) { if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { int smooth_viewtx = WM_operator_smooth_viewtx_get(op); @@ -4195,6 +4238,9 @@ static int viewroll_exec(bContext *C, wmOperator *op) rv3d = ar->regiondata; if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { + + ED_view3d_smooth_view_force_finish(C, v3d, ar); + int type = RNA_enum_get(op->ptr, "type"); float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle); float mousevec[3]; @@ -4247,6 +4293,8 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewops_data_create(C, op, event); vod = op->customdata; + ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar); + /* overwrite the mouse vector with the view direction */ normalize_v3_v3(vod->mousevec, vod->rv3d->viewinv[2]); negate_v3(vod->mousevec); @@ -5171,85 +5219,3 @@ void ED_view3D_lock_clear(View3D *v3d) v3d->ob_centre_cursor = false; v3d->flag2 &= ~V3D_LOCK_CAMERA; } - -/** - * Convenience function for snap ray-casting. - * - * Given a ray, cast it into the scene (snapping to faces). - * - * \return Snap success - */ -bool ED_view3d_snap_from_ray( - Scene *scene, - const float ray_start[3], const float ray_normal[3], - float r_co[3]) -{ - float r_no_dummy[3]; - float ray_dist = BVH_RAYCAST_DIST_MAX; - bool ret; - - struct Object *obedit = scene->obedit; - - /* try snap edge, then face if it fails */ - ret = snapObjectsRayEx( - scene, NULL, NULL, NULL, obedit, - NULL, SNAP_ALL, SCE_SNAP_MODE_FACE, - ray_start, ray_normal, &ray_dist, - r_co, r_no_dummy, NULL, NULL, - NULL, NULL); - - return ret; -} - -/** - * Convenience function for performing snapping. - * - * Given a 2D region value, snap to vert/edge/face. - * - * \param mval: Screenspace coordinate. - * \param dist_px: Maximum distance to snap (in pixels). - * \param use_depth: Snap to the closest element, use when using more than one snap type. - * \param use_obedit: Use editmode cage. - * \param use_vert: Snap to verts. - * \param use_edge: Snap to edges. - * \param use_face: Snap to faces. - * \param r_co: hit location. - * \param r_no: hit normal (optional). - * \return Snap success - */ -bool ED_view3d_snap_from_region( - Scene *scene, View3D *v3d, ARegion *ar, - const float mval[2], float dist_px, - bool use_depth, bool use_obedit, - bool use_vert, bool use_edge, bool use_face, - float r_co[3], float r_no[3]) -{ - float r_no_dummy[3]; - float ray_dist = BVH_RAYCAST_DIST_MAX; - bool is_hit = false; - float *r_no_ptr = r_no ? r_no : r_no_dummy; - - struct Object *obedit = use_obedit ? scene->obedit : NULL; - const int elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE}; - const bool elem_test[3] = {use_vert, use_edge, use_face}; - - BLI_assert(use_vert || use_edge || use_face); - - for (int i = 0; i < 3; i++) { - if (elem_test[i] && (is_hit == false || use_depth)) { - if (use_depth == false) { - ray_dist = BVH_RAYCAST_DIST_MAX; - } - if (snapObjectsEx( - scene, v3d, ar, NULL, obedit, - mval, SNAP_ALL, elem_type[i], - &ray_dist, - r_co, r_no_ptr, &dist_px)) - { - is_hit = true; - } - } - } - - return is_hit; -} diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index c398356e941..901c5c8d4c2 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -229,6 +229,10 @@ void ED_view3d_smooth_view( struct View3D *v3d, struct ARegion *ar, const int smooth_viewtx, const V3D_SmoothParams *sview); +void ED_view3d_smooth_view_force_finish( + struct bContext *C, + struct View3D *v3d, struct ARegion *ar); + void view3d_winmatrix_set(ARegion *ar, const View3D *v3d, const rctf *rect); void view3d_viewmatrix_set(Scene *scene, const View3D *v3d, RegionView3D *rv3d); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index a5411da131b..c30e688f1e7 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -44,7 +44,7 @@ #include "BLI_utildefines.h" #include "BKE_appdir.h" -#include "BKE_blender.h" +#include "BKE_blender_copybuffer.h" #include "BKE_context.h" #include "BKE_main.h" #include "BKE_report.h" diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index c364148c9f1..797d97586c7 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -217,8 +217,8 @@ eV3DProjStatus ED_view3d_project_float_ex(const ARegion *ar, float perspmat[4][4 float tvec[2]; eV3DProjStatus ret = ed_view3d_project__internal(ar, perspmat, is_local, co, tvec, flag); if (ret == V3D_PROJ_RET_OK) { - if (finite(tvec[0]) && - finite(tvec[1])) + if (isfinite(tvec[0]) && + isfinite(tvec[1])) { copy_v2_v2(r_co, tvec); } @@ -481,19 +481,22 @@ void ED_view3d_win_to_3d(const ARegion *ar, const float depth_pt[3], const float { RegionView3D *rv3d = ar->regiondata; - float line_sta[3]; - float line_end[3]; + float ray_origin[3]; + float ray_direction[3]; + float lambda; if (rv3d->is_persp) { - float mousevec[3], lambda; - copy_v3_v3(line_sta, rv3d->viewinv[3]); - ED_view3d_win_to_vector(ar, mval, mousevec); - add_v3_v3v3(line_end, line_sta, mousevec); + float plane[4]; + + copy_v3_v3(ray_origin, rv3d->viewinv[3]); + ED_view3d_win_to_vector(ar, mval, ray_direction); /* note, we could use isect_line_plane_v3() however we want the intersection to be infront of the * view no matter what, so apply the unsigned factor instead */ - lambda = line_plane_factor_v3(depth_pt, rv3d->viewinv[2], line_sta, line_end); - interp_v3_v3v3(out, line_sta, line_end, fabsf(lambda)); + plane_from_point_normal_v3(plane, depth_pt, rv3d->viewinv[2]); + + isect_ray_plane_v3(ray_origin, ray_direction, plane, &lambda, false); + lambda = fabsf(lambda); } else { float dx = (2.0f * mval[0] / (float)ar->winx) - 1.0f; @@ -504,13 +507,15 @@ void ED_view3d_win_to_3d(const ARegion *ar, const float depth_pt[3], const float dx += rv3d->camdx * zoomfac; dy += rv3d->camdy * zoomfac; } - line_sta[0] = (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0]; - line_sta[1] = (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1]; - line_sta[2] = (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2]; + ray_origin[0] = (rv3d->persinv[0][0] * dx) + (rv3d->persinv[1][0] * dy) + rv3d->viewinv[3][0]; + ray_origin[1] = (rv3d->persinv[0][1] * dx) + (rv3d->persinv[1][1] * dy) + rv3d->viewinv[3][1]; + ray_origin[2] = (rv3d->persinv[0][2] * dx) + (rv3d->persinv[1][2] * dy) + rv3d->viewinv[3][2]; - add_v3_v3v3(line_end, line_sta, rv3d->viewinv[2]); - closest_to_line_v3(out, depth_pt, line_sta, line_end); + copy_v3_v3(ray_direction, rv3d->viewinv[2]); + lambda = ray_point_factor_v3(depth_pt, ray_origin, ray_direction); } + + madd_v3_v3v3fl(out, ray_origin, ray_direction, lambda); } void ED_view3d_win_to_3d_int(const ARegion *ar, const float depth_pt[3], const int mval[2], float out[3]) diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index 198cc3e5703..dfa76753f64 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -35,6 +35,8 @@ #include "BLI_math.h" #include "BLI_blenlib.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_unit.h" #include "BKE_gpencil.h" @@ -46,6 +48,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "ED_transform.h" +#include "ED_transform_snap_object_context.h" #include "ED_space_api.h" #include "BLF_api.h" @@ -109,6 +113,8 @@ typedef struct RulerInfo { int state; float drag_start_co[3]; + struct SnapObjectContext *snap_context; + /* wm state */ wmWindow *win; ScrArea *sa; @@ -128,6 +134,7 @@ static RulerItem *ruler_item_add(RulerInfo *ruler_info) static void ruler_item_remove(RulerInfo *ruler_info, RulerItem *ruler_item) { BLI_remlink(&ruler_info->items, ruler_item); + MEM_freeN(ruler_item); } @@ -632,6 +639,9 @@ static void view3d_ruler_end(const struct bContext *UNUSED(C), RulerInfo *ruler_ static void view3d_ruler_free(RulerInfo *ruler_info) { BLI_freelistN(&ruler_info->items); + + ED_transform_snap_object_context_destroy(ruler_info->snap_context); + MEM_freeN(ruler_info); } @@ -642,11 +652,12 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], } /* use for mousemove events */ -static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, const int mval[2], - const bool do_thickness, const bool do_snap) +static bool view3d_ruler_item_mousemove( + RulerInfo *ruler_info, const int mval[2], + const bool do_thickness, const bool do_snap) { const float eps_bias = 0.0002f; - const float dist_px = MVAL_MAX_PX_DIST * U.pixelsize; /* snap dist */ + float dist_px = MVAL_MAX_PX_DIST * U.pixelsize; /* snap dist */ RulerItem *ruler_item = ruler_item_active_get(ruler_info); ruler_info->snap_flag &= ~RULER_SNAP_OK; @@ -657,8 +668,8 @@ static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, cons copy_v3_v3(co, ruler_info->drag_start_co); view3d_ruler_item_project(ruler_info, co, mval); if (do_thickness && ruler_item->co_index != 1) { - Scene *scene = CTX_data_scene(C); - View3D *v3d = ruler_info->sa->spacedata.first; + // Scene *scene = CTX_data_scene(C); + // View3D *v3d = ruler_info->sa->spacedata.first; const float mval_fl[2] = {UNPACK2(mval)}; float ray_normal[3]; float ray_start[3]; @@ -666,33 +677,37 @@ static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, cons co_other = ruler_item->co[ruler_item->co_index == 0 ? 2 : 0]; - if (ED_view3d_snap_from_region( - scene, v3d, ruler_info->ar, - mval_fl, dist_px, - true, false, - false, false, true, + if (ED_transform_snap_object_project_view3d_mixed( + ruler_info->snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + .snap_to_flag = SCE_SELECT_FACE, + }, + mval_fl, &dist_px, true, co, ray_normal)) { negate_v3(ray_normal); /* add some bias */ madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias); - ED_view3d_snap_from_ray( - scene, - ray_start, ray_normal, - co_other); + ED_transform_snap_object_project_ray( + ruler_info->snap_context, + ray_start, ray_normal, NULL, + co_other, NULL); } } else if (do_snap) { - Scene *scene = CTX_data_scene(C); + // Scene *scene = CTX_data_scene(C); View3D *v3d = ruler_info->sa->spacedata.first; const float mval_fl[2] = {UNPACK2(mval)}; bool use_depth = (v3d->drawtype >= OB_SOLID); - if (ED_view3d_snap_from_region( - scene, v3d, ruler_info->ar, - mval_fl, dist_px, - use_depth, false, - true, true, use_depth, + if (ED_transform_snap_object_project_view3d_mixed( + ruler_info->snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + .snap_to_flag = (SCE_SELECT_VERTEX | SCE_SELECT_EDGE) | (use_depth ? SCE_SELECT_FACE : 0), + }, + mval_fl, &dist_px, use_depth, co, NULL)) { ruler_info->snap_flag |= RULER_SNAP_OK; @@ -707,13 +722,13 @@ static bool view3d_ruler_item_mousemove(bContext *C, RulerInfo *ruler_info, cons static void view3d_ruler_header_update(ScrArea *sa) { - const char *text = "Ctrl+LMB: Add, " - "Del: Remove, " - "Ctrl+Drag: Snap, " - "Shift+Drag: Thickness, " - "Ctrl+C: Copy Value, " - "Enter: Store, " - "Esc: Cancel"; + const char *text = IFACE_("Ctrl+LMB: Add, " + "Del: Remove, " + "Ctrl+Drag: Snap, " + "Shift+Drag: Thickness, " + "Ctrl+C: Copy Value, " + "Enter: Store, " + "Esc: Cancel"); ED_area_headerprint(sa, text); } @@ -736,6 +751,10 @@ static int view3d_ruler_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE op->customdata = ruler_info; + ruler_info->snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), CTX_data_scene(C), SNAP_OBJECT_USE_CACHE, + ar, CTX_wm_view3d(C)); + ruler_info->win = win; ruler_info->sa = sa; ruler_info->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ruler_info_draw_pixel, @@ -818,7 +837,7 @@ static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) if (use_depth) { /* snap the first point added, not essential but handy */ ruler_item->co_index = 0; - view3d_ruler_item_mousemove(C, ruler_info, event->mval, false, true); + view3d_ruler_item_mousemove(ruler_info, event->mval, false, true); copy_v3_v3(ruler_info->drag_start_co, ruler_item->co[ruler_item->co_index]); } else { @@ -871,7 +890,7 @@ static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) } /* update the new location */ - view3d_ruler_item_mousemove(C, ruler_info, event->mval, + view3d_ruler_item_mousemove(ruler_info, event->mval, event->shift != 0, event->ctrl != 0); do_draw = true; } @@ -920,7 +939,7 @@ static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) case MOUSEMOVE: { if (ruler_info->state == RULER_STATE_DRAG) { - if (view3d_ruler_item_mousemove(C, ruler_info, event->mval, + if (view3d_ruler_item_mousemove(ruler_info, event->mval, event->shift != 0, event->ctrl != 0)) { do_draw = true; diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 18cb9fb728d..bedcf413bfa 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -418,11 +418,12 @@ static void do_lasso_select_objects(ViewContext *vc, const int mcords[][2], cons for (base = vc->scene->base.first; base; base = base->next) { if (BASE_SELECTABLE(vc->v3d, base)) { /* use this to avoid un-needed lasso lookups */ - ED_view3d_project_base(vc->ar, base); - if (BLI_lasso_is_point_inside(mcords, moves, base->sx, base->sy, IS_CLIPPED)) { - - ED_base_object_select(base, select ? BA_SELECT : BA_DESELECT); - base->object->flag = base->flag; + if (ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) { + if (BLI_lasso_is_point_inside(mcords, moves, base->sx, base->sy, IS_CLIPPED)) { + + ED_base_object_select(base, select ? BA_SELECT : BA_DESELECT); + base->object->flag = base->flag; + } } if (vc->obact == base->object && (base->object->mode & OB_MODE_POSE)) { do_lasso_select_pose(vc, base->object, mcords, moves, select); @@ -1096,20 +1097,22 @@ static Base *object_mouse_select_menu(bContext *C, ViewContext *vc, unsigned int /* two selection methods, the CTRL select uses max dist of 15 */ if (buffer) { - int a; - for (a = 0; a < hits; a++) { + for (int a = 0; a < hits; a++) { /* index was converted */ - if (base->selcol == buffer[(4 * a) + 3]) + if (base->selcol == (buffer[(4 * a) + 3] & ~0xFFFF0000)) { ok = true; + break; + } } } else { - int temp, dist = 15; - ED_view3d_project_base(vc->ar, base); - - temp = abs(base->sx - mval[0]) + abs(base->sy - mval[1]); - if (temp < dist) - ok = true; + const int dist = 15 * U.pixelsize; + if (ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) { + const int delta_px[2] = {base->sx - mval[0], base->sy - mval[1]}; + if (len_manhattan_v2_int(delta_px) < dist) { + ok = true; + } + } } if (ok) { @@ -1468,7 +1471,7 @@ static bool ed_object_select_pick( const bool has_bones = selectbuffer_has_bones(buffer, hits); /* note; shift+alt goes to group-flush-selecting */ - if (has_bones == 0 && enumerate) { + if (enumerate) { basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, toggle); } else { diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 305b4a3785e..c35646b9e92 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -337,17 +337,12 @@ void ED_view3d_smooth_view( } /* only meant for timer usage */ -static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *ar, bool sync_boxview) { - View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); + RegionView3D *rv3d = ar->regiondata; struct SmoothView3DStore *sms = rv3d->sms; float step, step_inv; - /* escape if not our timer */ - if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) - return OPERATOR_PASS_THROUGH; - if (sms->time_allowed != 0.0) step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed); else @@ -404,8 +399,9 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w } - if (rv3d->viewlock & RV3D_BOXVIEW) - view3d_boxview_copy(CTX_wm_area(C), CTX_wm_region(C)); + if (sync_boxview && (rv3d->viewlock & RV3D_BOXVIEW)) { + view3d_boxview_copy(CTX_wm_area(C), ar); + } /* note: this doesn't work right because the v3d->lens is now used in ortho mode r51636, * when switching camera in quad-view the other ortho views would zoom & reset. @@ -416,12 +412,47 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); } else { - ED_region_tag_redraw(CTX_wm_region(C)); + ED_region_tag_redraw(ar); } - +} + +static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + /* escape if not our timer */ + if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) { + return OPERATOR_PASS_THROUGH; + } + + view3d_smoothview_apply(C, v3d, ar, true); + return OPERATOR_FINISHED; } +/** + * Apply the smoothview immediately, use when we need to start a new view operation. + * (so we don't end up half-applying a view operation when pressing keys quickly). + */ +void ED_view3d_smooth_view_force_finish( + bContext *C, + View3D *v3d, ARegion *ar) +{ + RegionView3D *rv3d = ar->regiondata; + + if (rv3d && rv3d->sms) { + rv3d->sms->time_allowed = 0.0; /* force finishing */ + view3d_smoothview_apply(C, v3d, ar, false); + + /* force update of view matrix so tools that run immediately after + * can use them without redrawing first */ + Scene *scene = CTX_data_scene(C); + ED_view3d_update_viewmat(scene, v3d, ar, NULL, NULL); + } +} + void VIEW3D_OT_smoothview(wmOperatorType *ot) { diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 7e1202aef77..47f81678699 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -49,7 +49,7 @@ #include "ED_screen.h" #include "ED_space_api.h" -#include "ED_transform.h" +#include "ED_transform_snap_object_context.h" #include "PIL_time.h" /* smoothview */ @@ -306,6 +306,8 @@ typedef struct WalkInfo { float jump_height; /* maximum jump height */ float speed_factor; /* to use for fast/slow speeds */ + struct SnapObjectContext *snap_context; + struct View3DCameraControl *v3d_camera_control; } WalkInfo; @@ -402,12 +404,14 @@ static void walk_navigation_mode_set(bContext *C, wmOperator *op, WalkInfo *walk /** * \param r_distance Distance to the hit point */ -static bool walk_floor_distance_get(bContext *C, RegionView3D *rv3d, WalkInfo *walk, const float dvec[3], float *r_distance) +static bool walk_floor_distance_get( + RegionView3D *rv3d, WalkInfo *walk, const float dvec[3], + float *r_distance) { float ray_normal[3] = {0, 0, -1}; /* down */ float ray_start[3]; float r_location[3]; - float r_normal[3]; + float r_normal_dummy[3]; float dvec_tmp[3]; bool ret; @@ -418,12 +422,10 @@ static bool walk_floor_distance_get(bContext *C, RegionView3D *rv3d, WalkInfo *w mul_v3_v3fl(dvec_tmp, dvec, walk->grid); add_v3_v3(ray_start, dvec_tmp); - ret = snapObjectsRayEx( - CTX_data_scene(C), NULL, NULL, NULL, NULL, - NULL, SNAP_ALL, SCE_SNAP_MODE_FACE, + ret = ED_transform_snap_object_project_ray( + walk->snap_context, ray_start, ray_normal, r_distance, - r_location, r_normal, NULL, NULL, - NULL, NULL); + r_location, r_normal_dummy); /* artifically scale the distance to the scene size */ *r_distance /= walk->grid; @@ -435,34 +437,30 @@ static bool walk_floor_distance_get(bContext *C, RegionView3D *rv3d, WalkInfo *w * \param r_location Location of the hit point * \param r_normal Normal of the hit surface, transformed to always face the camera */ -static bool walk_ray_cast(bContext *C, RegionView3D *rv3d, WalkInfo *walk, float r_location[3], float r_normal[3], float *ray_distance) +static bool walk_ray_cast( + RegionView3D *rv3d, WalkInfo *walk, + float r_location[3], float r_normal[3], float *ray_distance) { - float ray_normal[3] = {0, 0, 1}; /* forward */ + float ray_normal[3] = {0, 0, -1}; /* forward */ float ray_start[3]; - float mat[3][3]; /* 3x3 copy of the view matrix so we can move along the view axis */ bool ret; *ray_distance = BVH_RAYCAST_DIST_MAX; copy_v3_v3(ray_start, rv3d->viewinv[3]); - copy_m3_m4(mat, rv3d->viewinv); - mul_m3_v3(mat, ray_normal); + mul_mat3_m4_v3(rv3d->viewinv, ray_normal); - mul_v3_fl(ray_normal, -1); normalize_v3(ray_normal); - ret = snapObjectsRayEx( - CTX_data_scene(C), NULL, NULL, NULL, NULL, - NULL, SNAP_ALL, SCE_SNAP_MODE_FACE, - ray_start, ray_normal, ray_distance, - r_location, r_normal, NULL, NULL, - NULL, NULL); - + ret = ED_transform_snap_object_project_ray( + walk->snap_context, + ray_start, ray_normal, NULL, + r_location, r_normal); /* dot is positive if both rays are facing the same direction */ if (dot_v3v3(ray_normal, r_normal) > 0) { - copy_v3_fl3(r_normal, -r_normal[0], -r_normal[1], -r_normal[2]); + negate_v3(r_normal); } /* artifically scale the distance to the scene size */ @@ -575,6 +573,9 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->rv3d->rflag |= RV3D_NAVIGATING; + walk->snap_context = ED_transform_snap_object_context_create_view3d( + CTX_data_main(C), walk->scene, SNAP_OBJECT_USE_CACHE, + walk->ar, walk->v3d); walk->v3d_camera_control = ED_view3d_cameracontrol_acquire( walk->scene, walk->v3d, walk->rv3d, @@ -625,6 +626,8 @@ static int walkEnd(bContext *C, WalkInfo *walk) ED_region_draw_cb_exit(walk->ar->type, walk->draw_handle_pixel); + ED_transform_snap_object_context_destroy(walk->snap_context); + ED_view3d_cameracontrol_release(walk->v3d_camera_control, walk->state == WALK_CANCEL); rv3d->rflag &= ~RV3D_NAVIGATING; @@ -897,7 +900,7 @@ static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent { float loc[3], nor[3]; float distance; - bool ret = walk_ray_cast(C, walk->rv3d, walk, loc, nor, &distance); + bool ret = walk_ray_cast(walk->rv3d, walk, loc, nor, &distance); /* in case we are teleporting middle way from a jump */ walk->speed_jump = 0.0f; @@ -1178,7 +1181,7 @@ static int walkApply(bContext *C, wmOperator *op, WalkInfo *walk) float difference = -100.0f; float fall_distance; - ret = walk_floor_distance_get(C, rv3d, walk, dvec, &ray_distance); + ret = walk_floor_distance_get(rv3d, walk, dvec, &ray_distance); if (ret) { difference = walk->view_height - ray_distance; @@ -1231,7 +1234,7 @@ static int walkApply(bContext *C, wmOperator *op, WalkInfo *walk) if (t > walk->teleport.duration) { /* check to see if we are landing */ - ret = walk_floor_distance_get(C, rv3d, walk, dvec, &ray_distance); + ret = walk_floor_distance_get(rv3d, walk, dvec, &ray_distance); if (ret) { difference = walk->view_height - ray_distance; diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index f3047c088a9..b7de49d8158 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -49,6 +49,7 @@ set(SRC transform_ops.c transform_orientations.c transform_snap.c + transform_snap_object.c transform.h ) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 29c152c2f6e..5c0c0bcd6c1 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -80,6 +80,7 @@ #include "WM_api.h" #include "UI_view2d.h" +#include "UI_interface.h" #include "UI_interface_icons.h" #include "UI_resources.h" @@ -93,8 +94,6 @@ /* Disabling, since when you type you know what you are doing, and being able to set it to zero is handy. */ // #define USE_NUM_NO_ZERO -#define MAX_INFO_LEN 256 - static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg); static void doEdgeSlide(TransInfo *t, float perc); static void doVertSlide(TransInfo *t, float perc); @@ -2910,7 +2909,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) float pivot[3]; float warp_end_radius[3]; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; const struct BendCustomData *data = t->custom.mode.data; const bool is_clamp = (t->flag & T_ALT_TRANSFORM) == 0; @@ -2948,13 +2947,13 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Bend Angle: %s Radius: %s Alt, Clamp %s"), + BLI_snprintf(str, sizeof(str), IFACE_("Bend Angle: %s Radius: %s Alt, Clamp %s"), &c[0], &c[NUM_STR_REP_LEN], WM_bool_as_string(is_clamp)); } else { /* default header print */ - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Bend Angle: %.3f Radius: %.4f, Alt, Clamp %s"), + BLI_snprintf(str, sizeof(str), IFACE_("Bend Angle: %.3f Radius: %.4f, Alt, Clamp %s"), RAD2DEGF(values.angle), values.scale * data->warp_init_dist, WM_bool_as_string(is_clamp)); } @@ -3104,7 +3103,7 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) float smat[3][3], tmat[3][3], totmat[3][3], persmat[3][3], persinv[3][3]; float value; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; const bool is_local_center = transdata_check_local_center(t, t->around); copy_m3_m4(persmat, t->viewmat); @@ -3124,11 +3123,11 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shear: %s %s"), c, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Shear: %s %s"), c, t->proptext); } else { /* default header print */ - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shear: %.3f %s (Press X or Y to set shear axis)"), value, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Shear: %.3f %s (Press X or Y to set shear axis)"), value, t->proptext); } unit_m3(smat); @@ -3228,7 +3227,7 @@ static void initResize(TransInfo *t) t->num.unit_type[2] = B_UNIT_NONE; } -static void headerResize(TransInfo *t, const float vec[3], char str[MAX_INFO_LEN]) +static void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) { char tvec[NUM_STR_REP_LEN * 3]; size_t ofs = 0; @@ -3244,32 +3243,32 @@ static void headerResize(TransInfo *t, const float vec[3], char str[MAX_INFO_LEN if (t->con.mode & CON_APPLY) { switch (t->num.idx_max) { case 0: - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Scale: %s%s %s"), + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Scale: %s%s %s"), &tvec[0], t->con.text, t->proptext); break; case 1: - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Scale: %s : %s%s %s"), + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Scale: %s : %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], t->con.text, t->proptext); break; case 2: - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Scale: %s : %s : %s%s %s"), &tvec[0], + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Scale: %s : %s : %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], &tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext); break; } } else { if (t->flag & T_2D_EDIT) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Scale X: %s Y: %s%s %s"), + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Scale X: %s Y: %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], t->con.text, t->proptext); } else { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Scale X: %s Y: %s Z: %s%s %s"), + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Scale X: %s Y: %s Z: %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], &tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext); } } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); } } @@ -3394,7 +3393,7 @@ static void applyResize(TransInfo *t, const int mval[2]) TransData *td; float mat[3][3]; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; if (t->flag & T_AUTOVALUES) { copy_v3_v3(t->values, t->auto_values); @@ -3512,7 +3511,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) TransData *td; float size[3], mat[3][3]; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; copy_v3_fl(size, t->values[0]); @@ -3610,7 +3609,7 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) float vec[3]; float ratio, radius; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; TransData *td = t->data; ratio = t->values[0]; @@ -3629,11 +3628,11 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("To Sphere: %s %s"), c, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("To Sphere: %s %s"), c, t->proptext); } else { /* default header print */ - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("To Sphere: %.4f %s"), ratio, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("To Sphere: %.4f %s"), ratio, t->proptext); } @@ -3964,7 +3963,7 @@ static void applyRotationValue(TransInfo *t, float angle, float axis[3]) static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) { - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; size_t ofs = 0; float final; @@ -3995,15 +3994,15 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext); } else { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Rot: %.2f%s %s"), + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Rot: %.2f%s %s"), RAD2DEGF(final), t->con.text, t->proptext); } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); } applyRotationValue(t, final, t->axis); @@ -4074,7 +4073,7 @@ static void applyTrackballValue(TransInfo *t, const float axis1[3], const float static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) { - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; size_t ofs = 0; float axis1[3], axis2[3]; #if 0 /* UNUSED */ @@ -4100,16 +4099,16 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Trackball: %s %s %s"), + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Trackball: %s %s %s"), &c[0], &c[NUM_STR_REP_LEN], t->proptext); } else { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Trackball: %.2f %.2f %s"), + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Trackball: %.2f %.2f %s"), RAD2DEGF(phi[0]), RAD2DEGF(phi[1]), t->proptext); } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); } #if 0 /* UNUSED */ @@ -4206,7 +4205,7 @@ static void initTranslation(TransInfo *t) } } -static void headerTranslation(TransInfo *t, const float vec[3], char str[MAX_INFO_LEN]) +static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) { size_t ofs = 0; char tvec[NUM_STR_REP_LEN * 3]; @@ -4268,15 +4267,15 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[MAX_INF if (t->con.mode & CON_APPLY) { switch (t->num.idx_max) { case 0: - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, "D: %s (%s)%s %s %s", + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, "D: %s (%s)%s %s %s", &tvec[0], distvec, t->con.text, t->proptext, autoik); break; case 1: - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, "D: %s D: %s (%s)%s %s %s", + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, "D: %s D: %s (%s)%s %s %s", &tvec[0], &tvec[NUM_STR_REP_LEN], distvec, t->con.text, t->proptext, autoik); break; case 2: - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, "D: %s D: %s D: %s (%s)%s %s %s", + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, "D: %s D: %s D: %s (%s)%s %s %s", &tvec[0], &tvec[NUM_STR_REP_LEN], &tvec[NUM_STR_REP_LEN * 2], distvec, t->con.text, t->proptext, autoik); break; @@ -4284,18 +4283,18 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[MAX_INF } else { if (t->flag & T_2D_EDIT) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, "Dx: %s Dy: %s (%s)%s %s", + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, "Dx: %s Dy: %s (%s)%s %s", &tvec[0], &tvec[NUM_STR_REP_LEN], distvec, t->con.text, t->proptext); } else { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", &tvec[0], &tvec[NUM_STR_REP_LEN], &tvec[NUM_STR_REP_LEN * 2], distvec, t->con.text, t->proptext, autoik); } } if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); } if (t->spacetype == SPACE_NODE) { @@ -4304,11 +4303,11 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[MAX_INF if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) { const char *str_old = BLI_strdup(str); const char *str_dir = (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT) ? IFACE_("right") : IFACE_("left"); - char str_km[MAX_INFO_LEN]; + char str_km[64]; WM_modalkeymap_items_to_string(t->keymap, TFM_MODAL_INSERTOFS_TOGGLE_DIR, true, sizeof(str_km), str_km); - ofs += BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Auto-offset set to %s - press %s to toggle direction | %s"), + ofs += BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("Auto-offset set to %s - press %s to toggle direction | %s"), str_dir, str_km, str_old); MEM_freeN((void *)str_old); @@ -4376,7 +4375,7 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) { - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; float value_final[3]; if (t->flag & T_AUTOVALUES) { @@ -4468,7 +4467,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) { float distance; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; size_t ofs = 0; TransData *td = t->data; @@ -4481,29 +4480,29 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) t->values[0] = -distance; /* header print for NumInput */ - ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Shrink/Fatten:"), MAX_INFO_LEN - ofs); + ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Shrink/Fatten:"), sizeof(str) - ofs); if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, " %s", c); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %s", c); } else { /* default header print */ - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, " %.4f", distance); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %.4f", distance); } if (t->proptext[0]) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, " %s", t->proptext); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %s", t->proptext); } - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, ", ("); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, ", ("); if (t->keymap) { wmKeyMapItem *kmi = WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_RESIZE); if (kmi) { - ofs += WM_keymap_item_to_string(kmi, false, MAX_INFO_LEN - ofs, str + ofs); + ofs += WM_keymap_item_to_string(kmi, false, sizeof(str) - ofs, str + ofs); } } - BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" or Alt) Even Thickness %s"), + BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_(" or Alt) Even Thickness %s"), WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); /* done with header string */ @@ -4563,7 +4562,7 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) { TransData *td = t->data; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; float final; @@ -4580,13 +4579,13 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Tilt: %s° %s"), &c[0], t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Tilt: %s° %s"), &c[0], t->proptext); /* XXX For some reason, this seems needed for this op, else RNA prop is not updated... :/ */ t->values[0] = final; } else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Tilt: %.2f° %s"), RAD2DEGF(final), t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Tilt: %.2f° %s"), RAD2DEGF(final), t->proptext); } for (i = 0; i < t->total; i++, td++) { @@ -4644,7 +4643,7 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) TransData *td = t->data; float ratio; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; ratio = t->values[0]; @@ -4659,10 +4658,10 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shrink/Fatten: %s"), c); + BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %s"), c); } else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shrink/Fatten: %3f"), ratio); + BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %3f"), ratio); } for (i = 0; i < t->total; i++, td++) { @@ -4724,7 +4723,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) float ratio; int i; bool initial_feather = false; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; ratio = t->values[0]; @@ -4739,10 +4738,10 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Feather Shrink/Fatten: %s"), c); + BLI_snprintf(str, sizeof(str), IFACE_("Feather Shrink/Fatten: %s"), c); } else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Feather Shrink/Fatten: %3f"), ratio); + BLI_snprintf(str, sizeof(str), IFACE_("Feather Shrink/Fatten: %3f"), ratio); } /* detect if no points have feather yet */ @@ -4824,7 +4823,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) TransData *td = t->data; float ratio; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; ratio = t->values[0]; @@ -4839,10 +4838,10 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shrink/Fatten: %s"), c); + BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %s"), c); } else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shrink/Fatten: %3f"), ratio); + BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %3f"), ratio); } for (i = 0; i < t->total; i++, td++) { @@ -4897,7 +4896,7 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) float vec[3], axis[3]; float distance; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; TransData *td = t->data; distance = t->values[0]; @@ -4914,11 +4913,11 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Push/Pull: %s%s %s"), c, t->con.text, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Push/Pull: %s%s %s"), c, t->con.text, t->proptext); } else { /* default header print */ - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Push/Pull: %.4f%s %s"), distance, t->con.text, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Push/Pull: %.4f%s %s"), distance, t->con.text, t->proptext); } if (t->con.applyRot && t->con.mode & CON_APPLY) { @@ -4989,7 +4988,7 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) TransData *td = t->data; float weight; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; weight = t->values[0]; @@ -5008,16 +5007,16 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); if (weight >= 0.0f) - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Bevel Weight: +%s %s"), c, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: +%s %s"), c, t->proptext); else - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Bevel Weight: %s %s"), c, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: %s %s"), c, t->proptext); } else { /* default header print */ if (weight >= 0.0f) - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Bevel Weight: +%.3f %s"), weight, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: +%.3f %s"), weight, t->proptext); else - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Bevel Weight: %.3f %s"), weight, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: %.3f %s"), weight, t->proptext); } for (i = 0; i < t->total; i++, td++) { @@ -5069,7 +5068,7 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) TransData *td = t->data; float crease; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; crease = t->values[0]; @@ -5088,16 +5087,16 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); if (crease >= 0.0f) - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Crease: +%s %s"), c, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Crease: +%s %s"), c, t->proptext); else - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Crease: %s %s"), c, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Crease: %s %s"), c, t->proptext); } else { /* default header print */ if (crease >= 0.0f) - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Crease: +%.3f %s"), crease, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Crease: +%.3f %s"), crease, t->proptext); else - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Crease: %.3f %s"), crease, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Crease: %.3f %s"), crease, t->proptext); } for (i = 0; i < t->total; i++, td++) { @@ -5151,7 +5150,7 @@ static void initBoneSize(TransInfo *t) t->num.unit_type[2] = B_UNIT_NONE; } -static void headerBoneSize(TransInfo *t, const float vec[3], char str[MAX_INFO_LEN]) +static void headerBoneSize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) { char tvec[NUM_STR_REP_LEN * 3]; if (hasNumInput(&t->num)) { @@ -5166,13 +5165,13 @@ static void headerBoneSize(TransInfo *t, const float vec[3], char str[MAX_INFO_L /* hmm... perhaps the y-axis values don't need to be shown? */ if (t->con.mode & CON_APPLY) { if (t->num.idx_max == 0) - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("ScaleB: %s%s %s"), &tvec[0], t->con.text, t->proptext); + BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("ScaleB: %s%s %s"), &tvec[0], t->con.text, t->proptext); else - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("ScaleB: %s : %s : %s%s %s"), + BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("ScaleB: %s : %s : %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], &tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext); } else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("ScaleB X: %s Y: %s Z: %s%s %s"), + BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("ScaleB X: %s Y: %s Z: %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN], &tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext); } } @@ -5203,7 +5202,7 @@ static void applyBoneSize(TransInfo *t, const int mval[2]) float size[3], mat[3][3]; float ratio; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; // TRANSFORM_FIX_ME MOVE TO MOUSE INPUT /* for manipulator, center handle, the scaling can't be done relative to center */ @@ -5282,7 +5281,7 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) TransData *td = t->data; float ratio; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; ratio = t->values[0]; @@ -5297,10 +5296,10 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Envelope: %s"), c); + BLI_snprintf(str, sizeof(str), IFACE_("Envelope: %s"), c); } else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Envelope: %3f"), ratio); + BLI_snprintf(str, sizeof(str), IFACE_("Envelope: %3f"), ratio); } for (i = 0; i < t->total; i++, td++) { @@ -6959,7 +6958,7 @@ static void doEdgeSlide(TransInfo *t, float perc) static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) { - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; size_t ofs = 0; float final; EdgeSlideData *sld = t->custom.mode.data; @@ -6982,20 +6981,20 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) t->values[0] = final; /* header string */ - ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Edge Slide: "), MAX_INFO_LEN - ofs); + ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Edge Slide: "), sizeof(str) - ofs); if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_strncpy_rlen(str + ofs, &c[0], MAX_INFO_LEN - ofs); + ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs); } else { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, "%.4f ", final); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final); } - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("(E)ven: %s, "), WM_bool_as_string(use_even)); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("(E)ven: %s, "), WM_bool_as_string(use_even)); if (use_even) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("(F)lipped: %s, "), WM_bool_as_string(flipped)); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("(F)lipped: %s, "), WM_bool_as_string(flipped)); } - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); /* done with header string */ /* do stuff here */ @@ -7527,7 +7526,7 @@ static void doVertSlide(TransInfo *t, float perc) static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) { - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; size_t ofs = 0; float final; VertSlideData *sld = t->custom.mode.data; @@ -7550,20 +7549,20 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) t->values[0] = final; /* header string */ - ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Vert Slide: "), MAX_INFO_LEN - ofs); + ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Vert Slide: "), sizeof(str) - ofs); if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; outputNumInput(&(t->num), c, &t->scene->unit); - ofs += BLI_strncpy_rlen(str + ofs, &c[0], MAX_INFO_LEN - ofs); + ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs); } else { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, "%.4f ", final); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final); } - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("(E)ven: %s, "), WM_bool_as_string(use_even)); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("(E)ven: %s, "), WM_bool_as_string(use_even)); if (use_even) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("(F)lipped: %s, "), WM_bool_as_string(flipped)); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("(F)lipped: %s, "), WM_bool_as_string(flipped)); } - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); + ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); /* done with header string */ /* do stuff here */ @@ -7607,7 +7606,7 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) { TransData *td = t->data; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; float final; @@ -7624,10 +7623,10 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) outputNumInput(&(t->num), c, &t->scene->unit); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Roll: %s"), &c[0]); + BLI_snprintf(str, sizeof(str), IFACE_("Roll: %s"), &c[0]); } else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Roll: %.2f"), RAD2DEGF(final)); + BLI_snprintf(str, sizeof(str), IFACE_("Roll: %.2f"), RAD2DEGF(final)); } /* set roll values */ @@ -7675,7 +7674,7 @@ static void applyBakeTime(TransInfo *t, const int mval[2]) TransData *td = t->data; float time; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; float fac = 0.1f; @@ -7704,16 +7703,16 @@ static void applyBakeTime(TransInfo *t, const int mval[2]) outputNumInput(&(t->num), c, &t->scene->unit); if (time >= 0.0f) - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Time: +%s %s"), c, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Time: +%s %s"), c, t->proptext); else - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Time: %s %s"), c, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Time: %s %s"), c, t->proptext); } else { /* default header print */ if (time >= 0.0f) - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Time: +%.3f %s"), time, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Time: +%.3f %s"), time, t->proptext); else - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Time: %.3f %s"), time, t->proptext); + BLI_snprintf(str, sizeof(str), IFACE_("Time: %.3f %s"), time, t->proptext); } for (i = 0; i < t->total; i++, td++) { @@ -7759,7 +7758,7 @@ static void applyMirror(TransInfo *t, const int UNUSED(mval[2])) TransData *td; float size[3], mat[3][3]; int i; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; /* * OPTIMIZATION: @@ -7777,7 +7776,7 @@ static void applyMirror(TransInfo *t, const int UNUSED(mval[2])) t->con.applySize(t, NULL, mat); } - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Mirror%s"), t->con.text); + BLI_snprintf(str, sizeof(str), IFACE_("Mirror%s"), t->con.text); for (i = 0, td = t->data; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) @@ -7906,7 +7905,7 @@ static void initSeqSlide(TransInfo *t) t->num.unit_type[1] = B_UNIT_NONE; } -static void headerSeqSlide(TransInfo *t, const float val[2], char str[MAX_INFO_LEN]) +static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRAW_STR]) { char tvec[NUM_STR_REP_LEN * 3]; size_t ofs = 0; @@ -7918,15 +7917,15 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[MAX_INFO_L BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.0f, %.0f", val[0], val[1]); } - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text); + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text); if (t->keymap) { wmKeyMapItem *kmi = WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_TRANSLATE); if (kmi) { - ofs += WM_keymap_item_to_string(kmi, false, MAX_INFO_LEN - ofs, str + ofs); + ofs += WM_keymap_item_to_string(kmi, false, UI_MAX_DRAW_STR - ofs, str + ofs); } } - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" or Alt) Expand to fit %s"), + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_(" or Alt) Expand to fit %s"), WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); } @@ -7948,7 +7947,7 @@ static void applySeqSlideValue(TransInfo *t, const float val[2]) static void applySeqSlide(TransInfo *t, const int mval[2]) { - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; snapSequenceBounds(t, mval); @@ -8127,7 +8126,7 @@ static void initTimeTranslate(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; } -static void headerTimeTranslate(TransInfo *t, char str[MAX_INFO_LEN]) +static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) { char tvec[NUM_STR_REP_LEN * 3]; int ofs = 0; @@ -8166,10 +8165,10 @@ static void headerTimeTranslate(TransInfo *t, char str[MAX_INFO_LEN]) BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", val); } - ofs += BLI_snprintf(str, MAX_INFO_LEN, IFACE_("DeltaX: %s"), &tvec[0]); + ofs += BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("DeltaX: %s"), &tvec[0]); if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); } } @@ -8231,7 +8230,7 @@ static void applyTimeTranslateValue(TransInfo *t) static void applyTimeTranslate(TransInfo *t, const int mval[2]) { View2D *v2d = (View2D *)t->view; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; /* calculate translation amount from mouse movement - in 'time-grid space' */ if (t->flag & T_MODAL) { @@ -8323,7 +8322,7 @@ static void initTimeSlide(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; } -static void headerTimeSlide(TransInfo *t, const float sval, char str[MAX_INFO_LEN]) +static void headerTimeSlide(TransInfo *t, const float sval, char str[UI_MAX_DRAW_STR]) { char tvec[NUM_STR_REP_LEN * 3]; @@ -8343,7 +8342,7 @@ static void headerTimeSlide(TransInfo *t, const float sval, char str[MAX_INFO_LE BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", val); } - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("TimeSlide: %s"), &tvec[0]); + BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("TimeSlide: %s"), &tvec[0]); } static void applyTimeSlideValue(TransInfo *t, float sval) @@ -8400,7 +8399,7 @@ static void applyTimeSlide(TransInfo *t, const int mval[2]) const float *range = t->custom.mode.data; float minx = range[0]; float maxx = range[1]; - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; /* calculate mouse co-ordinates */ UI_view2d_region_to_view(v2d, mval[0], mval[1], &cval[0], &cval[1]); @@ -8473,7 +8472,7 @@ static void initTimeScale(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; } -static void headerTimeScale(TransInfo *t, char str[MAX_INFO_LEN]) +static void headerTimeScale(TransInfo *t, char str[UI_MAX_DRAW_STR]) { char tvec[NUM_STR_REP_LEN * 3]; @@ -8482,7 +8481,7 @@ static void headerTimeScale(TransInfo *t, char str[MAX_INFO_LEN]) else BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", t->values[0]); - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("ScaleX: %s"), &tvec[0]); + BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("ScaleX: %s"), &tvec[0]); } static void applyTimeScaleValue(TransInfo *t) @@ -8526,7 +8525,7 @@ static void applyTimeScaleValue(TransInfo *t) static void applyTimeScale(TransInfo *t, const int UNUSED(mval[2])) { - char str[MAX_INFO_LEN]; + char str[UI_MAX_DRAW_STR]; /* handle numeric-input stuff */ t->vec[0] = t->values[0]; @@ -8558,5 +8557,3 @@ bool checkUseAxisMatrix(TransInfo *t) return false; } - -#undef MAX_INFO_LEN diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index c156e9ecec0..11151a9c65a 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -59,6 +59,7 @@ struct wmTimer; struct ARegion; struct ReportList; struct EditBone; +struct SnapObjectContext; /* transinfo->redraw */ typedef enum { @@ -101,6 +102,11 @@ typedef struct TransSnap { * where the smallest absolute value defines whats closest. */ float (*distance)(struct TransInfo *, const float p1[3], const float p2[3]); + + /** + * Re-usable snap context data. + */ + struct SnapObjectContext *object_context; } TransSnap; typedef struct TransCon { @@ -677,6 +683,7 @@ bool activeSnap(TransInfo *t); bool validSnap(TransInfo *t); void initSnapping(struct TransInfo *t, struct wmOperator *op); +void freeSnapping(struct TransInfo *t); void applyProject(TransInfo *t); void applyGridAbsolute(TransInfo *t); void applySnapping(TransInfo *t, float *vec); @@ -786,4 +793,8 @@ void projectVertSlideData(TransInfo *t, bool is_final); /* TODO. transform_queries.c */ bool checkUseAxisMatrix(TransInfo *t); +#define TRANSFORM_DIST_MAX_PX 1000.0f +#define TRANSFORM_SNAP_MAX_PX 100.0f +#define TRANSFORM_DIST_INVALID -FLT_MAX + #endif diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index beeba7cf10f..13cc0c22778 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -267,9 +267,9 @@ static void axisProjection(TransInfo *t, const float axis[3], const float in[3], /* possible some values become nan when * viewpoint and object are both zero */ - if (!finite(out[0])) out[0] = 0.0f; - if (!finite(out[1])) out[1] = 0.0f; - if (!finite(out[2])) out[2] = 0.0f; + if (!isfinite(out[0])) out[0] = 0.0f; + if (!isfinite(out[1])) out[1] = 0.0f; + if (!isfinite(out[2])) out[2] = 0.0f; } } } diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 5d3e71c5a3c..fa5e86813fa 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -820,6 +820,7 @@ static void pose_grab_with_ik_clear(Object *ob) bKinematicConstraint *data; bPoseChannel *pchan; bConstraint *con, *next; + bool need_dependency_update = false; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { /* clear all temporary lock flags */ @@ -834,6 +835,7 @@ static void pose_grab_with_ik_clear(Object *ob) data = con->data; if (data->flag & CONSTRAINT_IK_TEMP) { /* iTaSC needs clear for removed constraints */ + need_dependency_update = true; BIK_clear_data(ob->pose); BLI_remlink(&pchan->constraints, con); @@ -849,10 +851,10 @@ static void pose_grab_with_ik_clear(Object *ob) } #ifdef WITH_LEGACY_DEPSGRAPH - if (!DEG_depsgraph_use_legacy()) + if (!DEG_depsgraph_use_legacy() && need_dependency_update) #endif { - /* TODO(sergey): Consuder doing partial update only. */ + /* TODO(sergey): Consider doing partial update only. */ DAG_relations_tag_update(G.main); } } @@ -7813,7 +7815,9 @@ static void createTransGPencil(bContext *C, TransInfo *t) copy_m3_m3(td->mtx, mtx); unit_m3(td->axismtx); // XXX? } - + /* Triangulation must be calculated again, so save the stroke for recalc function */ + td->extra = gps; + td++; tail++; } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index eb6308d1c41..ed6477392d8 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -969,6 +969,16 @@ static void recalcData_sequencer(TransInfo *t) flushTransSeq(t); } +/* force recalculation of triangles during transformation */ +static void recalcData_gpencil_strokes(TransInfo *t) + { + TransData *td = t->data; + for (int i = 0; i < t->total; i++, td++) { + bGPDstroke *gps = td->extra; + gps->flag |= GP_STROKE_RECALC_CACHES; + } +} + /* called for updating while transform acts, once per redraw */ void recalcData(TransInfo *t) { @@ -983,7 +993,8 @@ void recalcData(TransInfo *t) flushTransPaintCurve(t); } else if (t->options & CTX_GPENCIL_STROKES) { - /* pass? */ + /* set recalc triangle cache flag */ + recalcData_gpencil_strokes(t); } else if (t->spacetype == SPACE_IMAGE) { recalcData_image(t); @@ -1502,6 +1513,8 @@ void postTrans(bContext *C, TransInfo *t) if (t->mouse.data) { MEM_freeN(t->mouse.data); } + + freeSnapping(t); } void applyTransObjects(TransInfo *t) diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index bb9120c337b..38f1d37acd6 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -36,8 +36,6 @@ #include "PIL_time.h" -#include "DNA_armature_types.h" -#include "DNA_curve_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_meshdata_types.h" /* Temporary, for snapping to other unselected meshes */ @@ -61,17 +59,16 @@ #include "BKE_editmesh.h" #include "BKE_sequencer.h" #include "BKE_main.h" -#include "BKE_tracking.h" #include "RNA_access.h" #include "WM_types.h" -#include "ED_armature.h" #include "ED_image.h" #include "ED_node.h" #include "ED_uvedit.h" #include "ED_view3d.h" +#include "ED_transform_snap_object_context.h" #include "UI_resources.h" #include "UI_view2d.h" @@ -83,10 +80,6 @@ /* this should be passed as an arg for use in snap functions */ #undef BASACT -#define TRANSFORM_DIST_MAX_PX 1000.0f -#define TRANSFORM_SNAP_MAX_PX 100.0f -#define TRANSFORM_DIST_INVALID -FLT_MAX - /* use half of flt-max so we can scale up without an exception */ /********************* PROTOTYPES ***********************/ @@ -331,8 +324,8 @@ void applyProject(TransInfo *t) if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { if (snapObjectsTransform( - t, mval_fl, t->tsnap.modeSelect, - loc, no, &dist_px)) + t, mval_fl, t->tsnap.modeSelect, &dist_px, + loc, no)) { // if (t->flag & (T_EDIT|T_POSE)) { // mul_m4_v3(imat, loc); @@ -493,6 +486,35 @@ bool validSnappingNormal(TransInfo *t) return false; } +static bool bm_edge_is_snap_target(BMEdge *e, void *UNUSED(user_data)) +{ + if (BM_elem_flag_test(e, BM_ELEM_SELECT | BM_ELEM_HIDDEN) || + BM_elem_flag_test(e->v1, BM_ELEM_SELECT) || + BM_elem_flag_test(e->v2, BM_ELEM_SELECT)) + { + return false; + } + + return true; +} + +static bool bm_face_is_snap_target(BMFace *f, void *UNUSED(user_data)) +{ + if (BM_elem_flag_test(f, BM_ELEM_SELECT | BM_ELEM_HIDDEN)) { + return false; + } + + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { + return false; + } + } while ((l_iter = l_iter->next) != l_first); + + return true; +} + static void initSnappingMode(TransInfo *t) { ToolSettings *ts = t->settings; @@ -573,6 +595,21 @@ static void initSnappingMode(TransInfo *t) /* Always grid outside of 3D view */ t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; } + + if (t->spacetype == SPACE_VIEW3D) { + if (t->tsnap.object_context == NULL) { + t->tsnap.object_context = ED_transform_snap_object_context_create_view3d( + G.main, t->scene, SNAP_OBJECT_USE_CACHE, + t->ar, t->view); + + ED_transform_snap_object_context_set_editmesh_callbacks( + t->tsnap.object_context, + (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled, + bm_edge_is_snap_target, + bm_face_is_snap_target, + SET_UINT_IN_POINTER((BM_ELEM_SELECT | BM_ELEM_HIDDEN))); + } + } } void initSnapping(TransInfo *t, wmOperator *op) @@ -636,6 +673,14 @@ void initSnapping(TransInfo *t, wmOperator *op) initSnappingMode(t); } +void freeSnapping(TransInfo *t) +{ + if (t->tsnap.object_context) { + ED_transform_snap_object_context_destroy(t->tsnap.object_context); + t->tsnap.object_context = NULL; + } +} + static void setSnappingCallback(TransInfo *t) { t->tsnap.calcSnap = CalcSnapGeometry; @@ -918,92 +963,16 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) mval[1] = t->mval[1]; if (t->tsnap.mode == SCE_SNAP_MODE_VOLUME) { - ListBase depth_peels; - DepthPeel *p1, *p2; - const float *last_p = NULL; - float max_dist = FLT_MAX; - float p[3] = {0.0f, 0.0f, 0.0f}; - - BLI_listbase_clear(&depth_peels); - - peelObjectsTransForm(t, mval, t->tsnap.modeSelect, &depth_peels); - -// if (LAST_SNAP_POINT_VALID) -// { -// last_p = LAST_SNAP_POINT; -// } -// else - { - last_p = t->tsnap.snapPoint; - } - - - for (p1 = depth_peels.first; p1; p1 = p1->next) { - if (p1->flag == 0) { - float vec[3]; - float new_dist; - - p2 = NULL; - p1->flag = 1; - - /* if peeling objects, take the first and last from each object */ - if (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) { - DepthPeel *peel; - for (peel = p1->next; peel; peel = peel->next) { - if (peel->ob == p1->ob) { - peel->flag = 1; - p2 = peel; - } - } - } - /* otherwise, pair first with second and so on */ - else { - for (p2 = p1->next; p2 && p2->ob != p1->ob; p2 = p2->next) { - /* nothing to do here */ - } - } - - if (p2) { - p2->flag = 1; - - add_v3_v3v3(vec, p1->p, p2->p); - mul_v3_fl(vec, 0.5f); - } - else { - copy_v3_v3(vec, p1->p); - } - - if (last_p == NULL) { - copy_v3_v3(p, vec); - max_dist = 0; - break; - } - - new_dist = len_squared_v3v3(last_p, vec); - - if (new_dist < max_dist) { - copy_v3_v3(p, vec); - max_dist = new_dist; - } - } - } - - if (max_dist != FLT_MAX) { - copy_v3_v3(loc, p); - /* XXX, is there a correct normal in this case ???, for now just z up */ - no[0] = 0.0; - no[1] = 0.0; - no[2] = 1.0; - found = true; - } - - BLI_freelistN(&depth_peels); + found = peelObjectsTransform( + t, mval, t->tsnap.modeSelect, + (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, + loc, no, NULL); } else { zero_v3(no); /* objects won't set this */ found = snapObjectsTransform( - t, mval, t->tsnap.modeSelect, - loc, no, &dist_px); + t, mval, t->tsnap.modeSelect, &dist_px, + loc, no); } if (found == true) { @@ -1237,1142 +1206,107 @@ static void TargetSnapClosest(TransInfo *t) } } -static bool snapEdge( - ARegion *ar, const float v1co[3], const short v1no[3], const float v2co[3], const short v2no[3], - float obmat[4][4], float timat[3][3], const float mval_fl[2], - const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - float intersect[3] = {0, 0, 0}, ray_end[3], dvec[3]; - int result; - bool retval = false; - - copy_v3_v3(ray_end, ray_normal_local); - mul_v3_fl(ray_end, 2000); - add_v3_v3v3(ray_end, ray_start_local, ray_end); - - result = isect_line_line_v3(v1co, v2co, ray_start_local, ray_end, intersect, dvec); /* dvec used but we don't care about result */ - - if (result) { - float edge_loc[3], vec[3]; - float mul; - - /* check for behind ray_start */ - sub_v3_v3v3(dvec, intersect, ray_start_local); - - sub_v3_v3v3(edge_loc, v1co, v2co); - sub_v3_v3v3(vec, intersect, v2co); - - mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); - - if (mul > 1) { - mul = 1; - copy_v3_v3(intersect, v1co); - } - else if (mul < 0) { - mul = 0; - copy_v3_v3(intersect, v2co); - } - - if (dot_v3v3(ray_normal_local, dvec) > 0) { - float location[3]; - float new_depth; - float screen_loc[2]; - float new_dist; - - copy_v3_v3(location, intersect); - - mul_m4_v3(obmat, location); - - new_depth = len_v3v3(location, ray_start); - - if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - new_dist = len_manhattan_v2v2(mval_fl, screen_loc); - } - else { - new_dist = TRANSFORM_DIST_MAX_PX; - } - - /* 10% threshold if edge is closer but a bit further - * this takes care of series of connected edges a bit slanted w.r.t the viewport - * otherwise, it would stick to the verts of the closest edge and not slide along merrily - * */ - if (new_dist <= *r_dist_px && new_depth < *ray_depth * 1.001f) { - float n1[3], n2[3]; - - *ray_depth = new_depth; - retval = true; - - sub_v3_v3v3(edge_loc, v1co, v2co); - sub_v3_v3v3(vec, intersect, v2co); - - mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); - - if (r_no) { - normal_short_to_float_v3(n1, v1no); - normal_short_to_float_v3(n2, v2no); - interp_v3_v3v3(r_no, n2, n1, mul); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - - copy_v3_v3(r_loc, location); - - *r_dist_px = new_dist; - } - } - } - - return retval; -} - -static bool snapVertex( - ARegion *ar, const float vco[3], const short vno[3], - float obmat[4][4], float timat[3][3], const float mval_fl[2], - const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - bool retval = false; - float dvec[3]; - - sub_v3_v3v3(dvec, vco, ray_start_local); - - if (dot_v3v3(ray_normal_local, dvec) > 0) { - float location[3]; - float new_depth; - float screen_loc[2]; - float new_dist; - - copy_v3_v3(location, vco); - - mul_m4_v3(obmat, location); - - new_depth = len_v3v3(location, ray_start); - - if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - new_dist = len_manhattan_v2v2(mval_fl, screen_loc); - } - else { - new_dist = TRANSFORM_DIST_MAX_PX; - } - - - if (new_dist <= *r_dist_px && new_depth < *ray_depth) { - *ray_depth = new_depth; - retval = true; - - copy_v3_v3(r_loc, location); - - if (r_no) { - normal_short_to_float_v3(r_no, vno); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - - *r_dist_px = new_dist; - } - } - - return retval; -} - -static bool snapArmature( - ARegion *ar, Object *ob, bArmature *arm, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float *UNUSED(r_no), float *r_dist_px) -{ - float imat[4][4]; - float ray_start_local[3], ray_normal_local[3]; - bool retval = false; - - invert_m4_m4(imat, obmat); - - mul_v3_m4v3(ray_start_local, imat, ray_start); - mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); - - if (arm->edbo) { - EditBone *eBone; - - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - if (eBone->layer & arm->layer) { - /* skip hidden or moving (selected) bones */ - if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) { - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - retval |= snapVertex(ar, eBone->head, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - retval |= snapVertex(ar, eBone->tail, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - break; - case SCE_SNAP_MODE_EDGE: - retval |= snapEdge(ar, eBone->head, NULL, eBone->tail, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - break; - } - } - } - } - } - else if (ob->pose && ob->pose->chanbase.first) { - bPoseChannel *pchan; - Bone *bone; - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - bone = pchan->bone; - /* skip hidden bones */ - if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { - const float *head_vec = pchan->pose_head; - const float *tail_vec = pchan->pose_tail; - - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - retval |= snapVertex(ar, head_vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - retval |= snapVertex(ar, tail_vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - break; - case SCE_SNAP_MODE_EDGE: - retval |= snapEdge(ar, head_vec, NULL, tail_vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - break; - } - } - } - } - - return retval; -} - -static bool snapCurve( - ARegion *ar, Object *ob, Curve *cu, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float *UNUSED(r_no), float *r_dist_px) -{ - float imat[4][4]; - float ray_start_local[3], ray_normal_local[3]; - bool retval = false; - int u; - - Nurb *nu; - - /* only vertex snapping mode (eg control points and handles) supported for now) */ - if (snap_to != SCE_SNAP_MODE_VERTEX) { - return retval; - } - - invert_m4_m4(imat, obmat); - - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); - - for (nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { - for (u = 0; u < nu->pntsu; u++) { - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - { - if (ob->mode == OB_MODE_EDIT) { - if (nu->bezt) { - /* don't snap to selected (moving) or hidden */ - if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) { - break; - } - retval |= snapVertex(ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - /* don't snap if handle is selected (moving), or if it is aligning to a moving handle */ - if (!(nu->bezt[u].f1 & SELECT) && !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT)) { - retval |= snapVertex(ar, nu->bezt[u].vec[0], NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - if (!(nu->bezt[u].f3 & SELECT) && !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) { - retval |= snapVertex(ar, nu->bezt[u].vec[2], NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - } - else { - /* don't snap to selected (moving) or hidden */ - if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) { - break; - } - retval |= snapVertex(ar, nu->bp[u].vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - } - else { - /* curve is not visible outside editmode if nurb length less than two */ - if (nu->pntsu > 1) { - if (nu->bezt) { - retval |= snapVertex(ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - else { - retval |= snapVertex(ar, nu->bp[u].vec, NULL, obmat, NULL, mval, ray_start, ray_start_local, ray_normal_local, ray_depth, r_loc, NULL, r_dist_px); - } - } - } - break; - } - default: - break; - } - } - } - return retval; -} - -static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) -{ - const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); - return index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly; +bool snapObjectsTransform( + TransInfo *t, const float mval[2], SnapSelect snap_select, + float *dist_px, + float r_loc[3], float r_no[3]) +{ + return ED_transform_snap_object_project_view3d_ex( + t->tsnap.object_context, + &(const struct SnapObjectParams){ + .snap_select = snap_select, + .snap_to = t->scene->toolsettings->snap_mode, + .use_object_edit = (t->flag & T_EDIT) != 0, + .use_object_active = (t->options & CTX_GPENCIL_STROKES) == 0, + }, + mval, dist_px, NULL, + r_loc, r_no, NULL); } -static bool snapDerivedMesh( - ARegion *ar, Object *ob, DerivedMesh *dm, BMEditMesh *em, float obmat[4][4], - const float mval[2], const short snap_to, bool do_bb, - const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index) -{ - bool retval = false; - int totvert = dm->getNumVerts(dm); - - if (totvert > 0) { - const bool do_ray_start_correction = ( - ELEM(snap_to, SCE_SNAP_MODE_FACE, SCE_SNAP_MODE_VERTEX) && - (ar && !((RegionView3D *)ar->regiondata)->is_persp)); - bool need_ray_start_correction_init = do_ray_start_correction; - - float imat[4][4]; - float timat[3][3]; /* transpose inverse matrix for normals */ - float ray_start_local[3], ray_normal_local[3]; - float local_scale, local_depth, len_diff; - - BVHTreeFromMesh treedata = {0}; - - invert_m4_m4(imat, obmat); - transpose_m3_m4(timat, imat); - - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); - - /* local scale in normal direction */ - local_scale = normalize_v3(ray_normal_local); - local_depth = *ray_depth; - if (local_depth != BVH_RAYCAST_DIST_MAX) { - local_depth *= local_scale; - } - if (do_bb) { - BoundBox *bb = BKE_object_boundbox_get(ob); - - if (bb) { - BoundBox bb_temp; - - /* We cannot aford a bbox with some null dimension, which may happen in some cases... - * Threshold is rather high, but seems to be needed to get good behavior, see T46099. */ - bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f); - - /* Exact value here is arbitrary (ideally we would scale in pixel-space based on 'r_dist_px'), - * scale up so we can snap against verts & edges on the boundbox, see T46816. */ - if (ELEM(snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { - BKE_boundbox_scale(&bb_temp, bb, 1.0f + 1e-1f); - bb = &bb_temp; - } - - /* was local_depth, see: T47838 */ - len_diff = BVH_RAYCAST_DIST_MAX; +/******************** PEELING *********************************/ - if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { - return retval; - } - need_ray_start_correction_init = false; +bool peelObjectsSnapContext( + SnapObjectContext *sctx, + const float mval[2], SnapSelect snap_select, bool use_peel_object, + /* return args */ + float r_loc[3], float r_no[3], float *r_thickness) +{ + ListBase depths_peel = {0}; + ED_transform_snap_object_project_all_view3d_ex( + sctx, + &(const struct SnapObjectParams){ + .snap_to = SCE_SNAP_MODE_FACE, + .snap_select = snap_select, + .use_object_edit = true, + }, + mval, -1.0f, false, + &depths_peel); + + if (!BLI_listbase_is_empty(&depths_peel)) { + /* At the moment we only use the hits of the first object */ + struct SnapObjectHitDepth *hit_min = depths_peel.first; + for (struct SnapObjectHitDepth *iter = hit_min->next; iter; iter = iter->next) { + if (iter->depth < hit_min->depth) { + hit_min = iter; } } + struct SnapObjectHitDepth *hit_max = NULL; - treedata.em_evil = em; - treedata.em_evil_all = false; - switch (snap_to) { - case SCE_SNAP_MODE_FACE: - bvhtree_from_mesh_looptri(&treedata, dm, 0.0f, 4, 6); - break; - case SCE_SNAP_MODE_VERTEX: - bvhtree_from_mesh_verts(&treedata, dm, 0.0f, 2, 6); - break; - } - - if (need_ray_start_correction_init) { - /* We *need* a reasonably valid len_diff in this case. - * Use BHVTree to find the closest face from ray_start_local. - */ - BVHTreeNearest nearest; - - if (treedata.tree != NULL) { - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - /* Compute and store result. */ - BLI_bvhtree_find_nearest( - treedata.tree, ray_start_local, &nearest, treedata.nearest_callback, &treedata); - if (nearest.index != -1) { - len_diff = sqrtf(nearest.dist_sq); + if (use_peel_object) { + /* if peeling objects, take the first and last from each object */ + hit_max = hit_min; + for (struct SnapObjectHitDepth *iter = depths_peel.first; iter; iter = iter->next) { + if ((iter->depth > hit_max->depth) && (iter->ob_uuid == hit_min->ob_uuid)) { + hit_max = iter; } } } - /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already - * been *inside* boundbox, leading to snap failures (see T38409). - * Note also ar might be null (see T38435), in this case we assume ray_start is ok! - */ - if (do_ray_start_correction) { - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - - /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far - * away ray_start values (as returned in case of ortho view3d), see T38358. - */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, - len_diff - len_v3v3(ray_start_local, ray_org_local)); - local_depth -= len_diff; - } else { - len_diff = 0.0f; - } - - switch (snap_to) { - case SCE_SNAP_MODE_FACE: - { - BVHTreeRayHit hit; - - hit.index = -1; - hit.dist = local_depth; - - if (treedata.tree && - BLI_bvhtree_ray_cast(treedata.tree, ray_start_local, ray_normal_local, 0.0f, - &hit, treedata.raycast_callback, &treedata) != -1) - { - hit.dist += len_diff; - hit.dist /= local_scale; - if (hit.dist <= *ray_depth) { - *ray_depth = hit.dist; - copy_v3_v3(r_loc, hit.co); - copy_v3_v3(r_no, hit.no); - - /* back to worldspace */ - mul_m4_v3(obmat, r_loc); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - - retval = true; - - if (r_index) { - *r_index = dm_looptri_to_poly_index(dm, &treedata.looptri[hit.index]); - } - } - } - break; - } - case SCE_SNAP_MODE_VERTEX: - { - BVHTreeNearest nearest; - - nearest.index = -1; - nearest.dist_sq = local_depth * local_depth; - if (treedata.tree && - BLI_bvhtree_find_nearest_to_ray( - treedata.tree, ray_start_local, ray_normal_local, - &nearest, NULL, NULL) != -1) - { - const MVert *v = &treedata.vert[nearest.index]; - retval = snapVertex( - ar, v->co, v->no, obmat, timat, mval, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, r_no, r_dist_px); - } - break; - } - case SCE_SNAP_MODE_EDGE: - { - MVert *verts = dm->getVertArray(dm); - MEdge *edges = dm->getEdgeArray(dm); - int totedge = dm->getNumEdges(dm); - const int *index_array = NULL; - int index = 0; - int i; - - if (em != NULL) { - index_array = dm->getEdgeDataArray(dm, CD_ORIGINDEX); - BM_mesh_elem_table_ensure(em->bm, BM_EDGE); - } - - for (i = 0; i < totedge; i++) { - MEdge *e = edges + i; - bool test = true; - - if (em != NULL) { - if (index_array) { - index = index_array[i]; - } - else { - index = i; - } - - if (index == ORIGINDEX_NONE) { - test = false; - } - else { - BMEdge *eed = BM_edge_at_index(em->bm, index); - - if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN) || - BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || - BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) - { - test = false; - } - } - } - - if (test) { - retval |= snapEdge( - ar, verts[e->v1].co, verts[e->v1].no, verts[e->v2].co, verts[e->v2].no, obmat, timat, - mval, ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, r_no, r_dist_px); - } - } - - break; - } - } - - free_bvhtree_from_mesh(&treedata); - } - - return retval; -} - -/* may extend later (for now just snaps to empty center) */ -static bool snapEmpty( - ARegion *ar, Object *ob, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float *UNUSED(r_no), float *r_dist_px) -{ - float imat[4][4]; - float ray_start_local[3], ray_normal_local[3]; - bool retval = false; - - if (ob->transflag & OB_DUPLI) { - return retval; - } - /* for now only vertex supported */ - if (snap_to != SCE_SNAP_MODE_VERTEX) { - return retval; - } - - invert_m4_m4(imat, obmat); - - mul_v3_m4v3(ray_start_local, imat, ray_start); - mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); - - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - { - const float zero_co[3] = {0.0f}; - retval |= snapVertex( - ar, zero_co, NULL, obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL, r_dist_px); - break; - } - default: - break; - } - - return retval; -} - -static bool snapCamera( - ARegion *ar, Scene *scene, Object *object, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float *UNUSED(r_no), float *r_dist_px) -{ - float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4]; - bool retval = false; - MovieClip *clip = BKE_object_movieclip_get(scene, object, false); - MovieTracking *tracking; - float ray_start_local[3], ray_normal_local[3]; - - if (clip == NULL) { - return retval; - } - if (object->transflag & OB_DUPLI) { - return retval; - } - - tracking = &clip->tracking; - - BKE_tracking_get_camera_object_matrix(scene, object, orig_camera_mat); - - invert_m4_m4(orig_camera_imat, orig_camera_mat); - invert_m4_m4(imat, obmat); - - switch (snap_to) { - case SCE_SNAP_MODE_VERTEX: - { - MovieTrackingObject *tracking_object; - - for (tracking_object = tracking->objects.first; - tracking_object; - tracking_object = tracking_object->next) - { - ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); - MovieTrackingTrack *track; - float reconstructed_camera_mat[4][4], - reconstructed_camera_imat[4][4]; - float (*vertex_obmat)[4]; - - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) { - BKE_tracking_camera_get_reconstructed_interpolate(tracking, tracking_object, - CFRA, reconstructed_camera_mat); - - invert_m4_m4(reconstructed_camera_imat, reconstructed_camera_mat); - } - - for (track = tracksbase->first; track; track = track->next) { - float bundle_pos[3]; - - if ((track->flag & TRACK_HAS_BUNDLE) == 0) { - continue; - } - - copy_v3_v3(bundle_pos, track->bundle_pos); - if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - mul_m4_v3(orig_camera_imat, ray_start_local); - mul_mat3_m4_v3(orig_camera_imat, ray_normal_local); - vertex_obmat = orig_camera_mat; + /* otherwise, pair first with second and so on */ + for (struct SnapObjectHitDepth *iter = depths_peel.first; iter; iter = iter->next) { + if ((iter != hit_min) && (iter->ob_uuid == hit_min->ob_uuid)) { + if (hit_max == NULL) { + hit_max = iter; } - else { - mul_m4_v3(reconstructed_camera_imat, bundle_pos); - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); - vertex_obmat = obmat; + else if (iter->depth < hit_max->depth) { + hit_max = iter; } - - retval |= snapVertex( - ar, bundle_pos, NULL, vertex_obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL, r_dist_px); } } - - break; - } - default: - break; - } - - return retval; -} - -static bool snapObject( - Scene *scene, ARegion *ar, Object *ob, float obmat[4][4], bool use_obedit, - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index, - Object **r_ob, float r_obmat[4][4]) -{ - bool retval = false; - - if (ob->type == OB_MESH) { - BMEditMesh *em; - DerivedMesh *dm; - bool do_bb = true; - - if (use_obedit) { - em = BKE_editmesh_from_object(ob); - dm = editbmesh_get_derived_cage(scene, ob, em, CD_MASK_BAREMESH); - do_bb = false; - } - else { - /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978. - * still set the 'em' to NULL, since we only want the 'dm'. */ - em = BKE_editmesh_from_object(ob); - if (em) { - editbmesh_get_derived_cage_and_final(scene, ob, em, CD_MASK_BAREMESH, &dm); - } - else { - dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + /* in this case has only one hit. treat as raycast */ + if (hit_max == NULL) { + hit_max = hit_min; } - em = NULL; } - - retval = snapDerivedMesh( - ar, ob, dm, em, obmat, mval, snap_to, do_bb, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index); - - dm->release(dm); - } - else if (ob->type == OB_ARMATURE) { - retval = snapArmature( - ar, ob, ob->data, obmat, mval, snap_to, - ray_start, ray_normal, ray_depth, - r_loc, r_no, r_dist_px); - } - else if (ob->type == OB_CURVE) { - retval = snapCurve( - ar, ob, ob->data, obmat, mval, snap_to, - ray_start, ray_normal, ray_depth, - r_loc, r_no, r_dist_px); - } - else if (ob->type == OB_EMPTY) { - retval = snapEmpty( - ar, ob, obmat, mval, snap_to, - ray_start, ray_normal, ray_depth, - r_loc, r_no, r_dist_px); - } - else if (ob->type == OB_CAMERA) { - retval = snapCamera( - ar, scene, ob, obmat, mval, snap_to, - ray_start, ray_normal, ray_depth, - r_loc, r_no, r_dist_px); - } - - if (retval) { - if (r_ob) { - *r_ob = ob; - copy_m4_m4(r_obmat, obmat); - } - } - return retval; -} + mid_v3_v3v3(r_loc, hit_min->co, hit_max->co); -static bool snapObjectsRay( - Scene *scene, View3D *v3d, ARegion *ar, Base *base_act, Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_to, - const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, - /* return args */ - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index, - Object **r_ob, float r_obmat[4][4]) -{ - Base *base; - bool retval = false; - - if (snap_select == SNAP_ALL && obedit) { - Object *ob = obedit; - - retval |= snapObject( - scene, ar, ob, ob->obmat, true, - mval, snap_to, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index, r_ob, r_obmat); - } - - /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA - * which makes the loop skip it, even the derived mesh will never change - * - * To solve that problem, we do it first as an exception. - * */ - base = base_act; - if (base && base->object && base->object->mode & OB_MODE_PARTICLE_EDIT) { - Object *ob = base->object; - retval |= snapObject( - scene, ar, ob, ob->obmat, false, - mval, snap_to, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index, r_ob, r_obmat); - } - - for (base = FIRSTBASE; base != NULL; base = base->next) { - if ((BASE_VISIBLE_BGMODE(v3d, scene, base)) && - (base->flag & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 && - - ((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL)) == 0) || - (ELEM(snap_select, SNAP_ALL, SNAP_NOT_OBEDIT) && base != base_act))) - { - Object *ob = base->object; - Object *ob_snap = ob; - bool use_obedit = false; - - /* for linked objects, use the same object but a different matrix */ - if (obedit && ob->data == obedit->data) { - use_obedit = true; - ob_snap = obedit; - } - - if (ob->transflag & OB_DUPLI) { - DupliObject *dupli_ob; - ListBase *lb = object_duplilist(G.main->eval_ctx, scene, ob); - - for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { - bool use_obedit_dupli = (obedit && dupli_ob->ob->data == obedit->data); - Object *dupli_snap = (use_obedit_dupli) ? obedit : dupli_ob->ob; - - retval |= snapObject( - scene, ar, dupli_snap, dupli_ob->mat, use_obedit_dupli, - mval, snap_to, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index, r_ob, r_obmat); - } - - free_object_duplilist(lb); - } - - retval |= snapObject( - scene, ar, ob_snap, ob->obmat, use_obedit, - mval, snap_to, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_dist_px, r_index, r_ob, r_obmat); + if (r_thickness) { + *r_thickness = hit_max->depth - hit_min->depth; } - } - - return retval; -} -static bool snapObjects( - Scene *scene, View3D *v3d, ARegion *ar, Base *base_act, Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_to, - float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index) -{ - float ray_start[3], ray_normal[3], ray_orgigin[3]; - - if (!ED_view3d_win_to_ray_ex(ar, v3d, mval, ray_orgigin, ray_normal, ray_start, true)) { - return false; - } - - return snapObjectsRay( - scene, v3d, ar, base_act, obedit, - mval, snap_select, snap_to, - ray_start, ray_normal, ray_orgigin, ray_depth, - r_loc, r_no, r_dist_px, r_index, NULL, NULL); -} -bool snapObjectsTransform( - TransInfo *t, const float mval[2], SnapSelect snap_select, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - float ray_dist = BVH_RAYCAST_DIST_MAX; - Object *obedit = NULL; - Base *base_act = NULL; + /* XXX, is there a correct normal in this case ???, for now just z up */ + r_no[0] = 0.0; + r_no[1] = 0.0; + r_no[2] = 1.0; - if (t->flag & T_EDIT) { - obedit = t->obedit; + BLI_freelistN(&depths_peel); + return true; } - - if ((t->options & CTX_GPENCIL_STROKES) == 0) { - base_act = t->scene->basact; - } - - return snapObjects( - t->scene, t->view, t->ar, base_act, obedit, - mval, snap_select, t->scene->toolsettings->snap_mode, - &ray_dist, - r_loc, r_no, r_dist_px, NULL); -} - -bool snapObjectsContext( - bContext *C, const float mval[2], SnapSelect snap_select, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - ScrArea *sa = CTX_wm_area(C); - View3D *v3d = sa->spacedata.first; - Scene *scene = CTX_data_scene(C); - ARegion *ar = CTX_wm_region(C); - Object *obedit = CTX_data_edit_object(C); - float ray_dist = BVH_RAYCAST_DIST_MAX; - - return snapObjects( - scene, v3d, ar, scene->basact, obedit, - mval, snap_select, scene->toolsettings->snap_mode, - &ray_dist, - r_loc, r_no, r_dist_px, NULL); -} - -bool snapObjectsEx( - Scene *scene, View3D *v3d, ARegion *ar, Base *base_act, Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_to, - float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px) -{ - return snapObjects( - scene, v3d, ar, base_act, obedit, - mval, snap_select, snap_to, - ray_depth, - r_loc, r_no, r_dist_px, NULL); -} -bool snapObjectsRayEx( - Scene *scene, View3D *v3d, ARegion *ar, Base *base_act, Object *obedit, - const float mval[2], SnapSelect snap_select, const short snap_to, - const float ray_start[3], const float ray_normal[3], float *ray_depth, - float r_loc[3], float r_no[3], float *r_dist_px, int *r_index, - Object **r_ob, float r_obmat[4][4]) -{ - return snapObjectsRay( - scene, v3d, ar, base_act, obedit, - mval, snap_select, snap_to, - ray_start, ray_normal, ray_start, ray_depth, - r_loc, r_no, r_dist_px, r_index, - r_ob, r_obmat); -} - -/******************** PEELING *********************************/ - - -static int cmpPeel(const void *arg1, const void *arg2) -{ - const DepthPeel *p1 = arg1; - const DepthPeel *p2 = arg2; - int val = 0; - - if (p1->depth < p2->depth) { - val = -1; - } - else if (p1->depth > p2->depth) { - val = 1; - } - - return val; -} - -static void removeDoublesPeel(ListBase *depth_peels) -{ - DepthPeel *peel; - - for (peel = depth_peels->first; peel; peel = peel->next) { - DepthPeel *next_peel = peel->next; - - if (next_peel && fabsf(peel->depth - next_peel->depth) < 0.0015f) { - peel->next = next_peel->next; - - if (next_peel->next) { - next_peel->next->prev = peel; - } - - MEM_freeN(next_peel); - } - } -} - -static void addDepthPeel(ListBase *depth_peels, float depth, float p[3], float no[3], Object *ob) -{ - DepthPeel *peel = MEM_callocN(sizeof(DepthPeel), "DepthPeel"); - - peel->depth = depth; - peel->ob = ob; - copy_v3_v3(peel->p, p); - copy_v3_v3(peel->no, no); - - BLI_addtail(depth_peels, peel); - - peel->flag = 0; -} - -struct PeelRayCast_Data { - BVHTreeFromMesh bvhdata; - - /* internal vars for adding peel */ - Object *ob; - const float (*obmat)[4]; - const float (*timat)[3]; - - const float *ray_start; /* globalspace */ - - const MLoopTri *looptri; - const float (*polynors)[3]; /* optional, can be NULL */ - - /* output list */ - ListBase *depth_peels; -}; - -static void peelRayCast_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) -{ - struct PeelRayCast_Data *data = userdata; - - data->bvhdata.raycast_callback(&data->bvhdata, index, ray, hit); - - if (hit->index != -1) { - /* get all values in worldspace */ - float location[3], normal[3]; - float depth; - - /* worldspace location */ - mul_v3_m4v3(location, (float (*)[4])data->obmat, hit->co); - depth = len_v3v3(location, data->ray_start); - - /* worldspace normal */ - copy_v3_v3(normal, data->polynors ? data->polynors[data->looptri[hit->index].poly] : hit->no); - mul_m3_v3((float (*)[3])data->timat, normal); - normalize_v3(normal); - - addDepthPeel(data->depth_peels, depth, location, normal, data->ob); - } -} - -static bool peelDerivedMesh( - Object *ob, DerivedMesh *dm, BMEditMesh *em, float obmat[4][4], - const float ray_start[3], const float ray_normal[3], const float UNUSED(mval[2]), - ListBase *depth_peels) -{ - bool retval = false; - int totvert = dm->getNumVerts(dm); - - if (totvert > 0) { - const MLoopTri *looptri = dm->getLoopTriArray(dm); - const int looptri_num = dm->getNumLoopTri(dm); - float imat[4][4]; - float timat[3][3]; /* transpose inverse matrix for normals */ - float ray_start_local[3], ray_normal_local[3]; - bool test = true; - - invert_m4_m4(imat, obmat); - - transpose_m3_m4(timat, imat); - - mul_v3_m4v3(ray_start_local, imat, ray_start); - mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); - - /* If number of vert is more than an arbitrary limit, - * test against boundbox first - * */ - if (looptri_num > 16) { - BoundBox *bb = BKE_object_boundbox_get(ob); - - if (bb) { - BoundBox bb_temp; - - /* We cannot aford a bbox with some null dimension, which may happen in some cases... - * Threshold is rather high, but seems to be needed to get good behavior, see T46099. */ - bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f); - - test = BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, NULL); - } - } - - if (test == true) { - struct PeelRayCast_Data data; - - data.bvhdata.em_evil = em; - data.bvhdata.em_evil_all = false; - bvhtree_from_mesh_looptri(&data.bvhdata, dm, 0.0f, 4, 6); - - if (data.bvhdata.tree != NULL) { - data.ob = ob; - data.obmat = (const float (*)[4])obmat; - data.timat = (const float (*)[3])timat; - data.ray_start = ray_start; - data.looptri = looptri; - data.polynors = dm->getPolyDataArray(dm, CD_NORMAL); /* can be NULL */ - data.depth_peels = depth_peels; - - BLI_bvhtree_ray_cast_all(data.bvhdata.tree, ray_start_local, ray_normal_local, 0.0f, - peelRayCast_cb, &data); - } - - free_bvhtree_from_mesh(&data.bvhdata); - } - } - - return retval; -} - -static bool peelObjects( - Scene *scene, View3D *v3d, ARegion *ar, Object *obedit, - const float mval[2], SnapSelect snap_select, - ListBase *r_depth_peels) -{ - Base *base; - bool retval = false; - float ray_start[3], ray_normal[3]; - - if (ED_view3d_win_to_ray(ar, v3d, mval, ray_start, ray_normal, true) == false) { - return false; - } - - for (base = scene->base.first; base != NULL; base = base->next) { - if (BASE_SELECTABLE(v3d, base)) { - Object *ob = base->object; - - if (ob->transflag & OB_DUPLI) { - DupliObject *dupli_ob; - ListBase *lb = object_duplilist(G.main->eval_ctx, scene, ob); - - for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { - Object *dob = dupli_ob->ob; - - if (dob->type == OB_MESH) { - BMEditMesh *em; - DerivedMesh *dm = NULL; - bool val; - - if (dob != obedit) { - dm = mesh_get_derived_final(scene, dob, CD_MASK_BAREMESH); - - val = peelDerivedMesh(dob, dm, NULL, dob->obmat, ray_start, ray_normal, mval, r_depth_peels); - } - else { - em = BKE_editmesh_from_object(dob); - dm = editbmesh_get_derived_cage(scene, obedit, em, CD_MASK_BAREMESH); - - val = peelDerivedMesh(dob, dm, em, dob->obmat, ray_start, ray_normal, mval, r_depth_peels); - } - - retval = retval || val; - - dm->release(dm); - } - } - - free_object_duplilist(lb); - } - - if (ob->type == OB_MESH) { - bool val = false; - - if (ob != obedit && ((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL)) == 0) || ELEM(snap_select, SNAP_ALL, SNAP_NOT_OBEDIT))) { - DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); - - val = peelDerivedMesh(ob, dm, NULL, ob->obmat, ray_start, ray_normal, mval, r_depth_peels); - dm->release(dm); - } - else if (ob == obedit && snap_select != SNAP_NOT_OBEDIT) { - BMEditMesh *em = BKE_editmesh_from_object(ob); - DerivedMesh *dm = editbmesh_get_derived_cage(scene, obedit, em, CD_MASK_BAREMESH); - - val = peelDerivedMesh(ob, dm, NULL, ob->obmat, ray_start, ray_normal, mval, r_depth_peels); - dm->release(dm); - } - - retval = retval || val; - - } - } - } - - BLI_listbase_sort(r_depth_peels, cmpPeel); - removeDoublesPeel(r_depth_peels); - - return retval; -} - -bool peelObjectsTransForm( - TransInfo *t, const float mval[2], SnapSelect snap_select, - ListBase *r_depth_peels) -{ - return peelObjects(t->scene, t->view, t->ar, t->obedit, mval, snap_select, r_depth_peels); + return false; } -bool peelObjectsContext( - bContext *C, const float mval[2], SnapSelect snap_select, - ListBase *r_depth_peels) +bool peelObjectsTransform( + TransInfo *t, + const float mval[2], SnapSelect snap_select, bool use_peel_object, + /* return args */ + float r_loc[3], float r_no[3], float *r_thickness) { - Scene *scene = CTX_data_scene(C); - ScrArea *sa = CTX_wm_area(C); - View3D *v3d = sa->spacedata.first; - ARegion *ar = CTX_wm_region(C); - Object *obedit = CTX_data_edit_object(C); - - return peelObjects(scene, v3d, ar, obedit, mval, snap_select, r_depth_peels); + return peelObjectsSnapContext( + t->tsnap.object_context, + mval, snap_select, use_peel_object, + r_loc, r_no, r_thickness); } /******************** NODES ***********************************/ diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c new file mode 100644 index 00000000000..62ca4e515a5 --- /dev/null +++ b/source/blender/editors/transform/transform_snap_object.c @@ -0,0 +1,1767 @@ +/* + * ***** 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/editors/transform/transform_snap_object.c + * \ingroup edtransform + */ + +#include <stdlib.h> +#include <math.h> +#include <float.h> +#include <stdio.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_kdopbvh.h" +#include "BLI_memarena.h" +#include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +#include "DNA_armature_types.h" +#include "DNA_curve_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_object.h" +#include "BKE_anim.h" /* for duplis */ +#include "BKE_editmesh.h" +#include "BKE_main.h" +#include "BKE_tracking.h" + +#include "ED_transform.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" +#include "ED_armature.h" + +#include "transform.h" + +typedef struct SnapObjectData { + enum { + SNAP_MESH = 1, + SNAP_EDIT_MESH, + } type; +} SnapObjectData; + +typedef struct SnapObjectData_Mesh { + SnapObjectData sd; + BVHTreeFromMesh *bvh_trees[2]; + +} SnapObjectData_Mesh; + +typedef struct SnapObjectData_EditMesh { + SnapObjectData sd; + BVHTreeFromEditMesh *bvh_trees[2]; + +} SnapObjectData_EditMesh; + +struct SnapObjectContext { + Main *bmain; + Scene *scene; + int flag; + + /* Optional: when performing screen-space projection. + * otherwise this doesn't take viewport into account. */ + bool use_v3d; + struct { + struct View3D *v3d; + struct ARegion *ar; + } v3d_data; + + + /* Object -> SnapObjectData map */ + struct { + GHash *object_map; + MemArena *mem_arena; + } cache; + + /* Filter data, returns true to check this value */ + struct { + struct { + bool (*test_vert_fn)(BMVert *, void *user_data); + bool (*test_edge_fn)(BMEdge *, void *user_data); + bool (*test_face_fn)(BMFace *, void *user_data); + void *user_data; + } edit_mesh; + } callbacks; + +}; + +static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt); + + +/* -------------------------------------------------------------------- */ + +/** \name Support for storing all depths, not just the first (raycast 'all') + * + * This uses a list of #SnapObjectHitDepth structs. + * + * \{ */ + +/* Store all ray-hits */ +struct RayCastAll_Data { + void *bvhdata; + + /* internal vars for adding depths */ + BVHTree_RayCastCallback raycast_callback; + + const float(*obmat)[4]; + const float(*timat)[3]; + + float len_diff; + float local_scale; + + Object *ob; + unsigned int ob_uuid; + + /* DerivedMesh only */ + DerivedMesh *dm; + const struct MLoopTri *dm_looptri; + + /* output data */ + ListBase *hit_list; + bool retval; +}; + +static struct SnapObjectHitDepth *hit_depth_create( + const float depth, const float co[3], const float no[3], int index, + Object *ob, const float obmat[4][4], unsigned int ob_uuid) +{ + struct SnapObjectHitDepth *hit = MEM_mallocN(sizeof(*hit), __func__); + + hit->depth = depth; + copy_v3_v3(hit->co, co); + copy_v3_v3(hit->no, no); + hit->index = index; + + hit->ob = ob; + copy_m4_m4(hit->obmat, (float(*)[4])obmat); + hit->ob_uuid = ob_uuid; + + return hit; +} + +static int hit_depth_cmp(const void *arg1, const void *arg2) +{ + const struct SnapObjectHitDepth *h1 = arg1; + const struct SnapObjectHitDepth *h2 = arg2; + int val = 0; + + if (h1->depth < h2->depth) { + val = -1; + } + else if (h1->depth > h2->depth) { + val = 1; + } + + return val; +} + +static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + struct RayCastAll_Data *data = userdata; + data->raycast_callback(data->bvhdata, index, ray, hit); + if (hit->index != -1) { + /* get all values in worldspace */ + float location[3], normal[3]; + float depth; + + /* worldspace location */ + mul_v3_m4v3(location, (float(*)[4])data->obmat, hit->co); + depth = (hit->dist + data->len_diff) / data->local_scale; + + /* worldspace normal */ + copy_v3_v3(normal, hit->no); + mul_m3_v3((float(*)[3])data->timat, normal); + normalize_v3(normal); + + /* currently unused, and causes issues when looptri's havn't been calculated. + * since theres some overhead in ensuring this data is valid, it may need to be optional. */ +#if 0 + if (data->dm) { + hit->index = dm_looptri_to_poly_index(data->dm, &data->dm_looptri[hit->index]); + } +#endif + + struct SnapObjectHitDepth *hit_item = hit_depth_create( + depth, location, normal, hit->index, + data->ob, data->obmat, data->ob_uuid); + BLI_addtail(data->hit_list, hit_item); + } +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Internal Object Snapping API + * \{ */ + +static bool snapEdge( + ARegion *ar, const float v1co[3], const short v1no[3], const float v2co[3], const short v2no[3], + float obmat[4][4], float timat[3][3], const float mval_fl[2], float *dist_px, + const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], float *ray_depth, + float r_loc[3], float r_no[3]) +{ + float intersect[3] = {0, 0, 0}, ray_end[3], dvec[3]; + int result; + bool retval = false; + + copy_v3_v3(ray_end, ray_normal_local); + mul_v3_fl(ray_end, 2000); + add_v3_v3v3(ray_end, ray_start_local, ray_end); + + /* dvec used but we don't care about result */ + result = isect_line_line_v3(v1co, v2co, ray_start_local, ray_end, intersect, dvec); + + if (result) { + float edge_loc[3], vec[3]; + float mul; + + /* check for behind ray_start */ + sub_v3_v3v3(dvec, intersect, ray_start_local); + + sub_v3_v3v3(edge_loc, v1co, v2co); + sub_v3_v3v3(vec, intersect, v2co); + + mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); + + if (mul > 1) { + mul = 1; + copy_v3_v3(intersect, v1co); + } + else if (mul < 0) { + mul = 0; + copy_v3_v3(intersect, v2co); + } + + if (dot_v3v3(ray_normal_local, dvec) > 0) { + float location[3]; + float new_depth; + float screen_loc[2]; + float new_dist; + + copy_v3_v3(location, intersect); + + mul_m4_v3(obmat, location); + + new_depth = len_v3v3(location, ray_start); + + if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + new_dist = len_manhattan_v2v2(mval_fl, screen_loc); + } + else { + new_dist = TRANSFORM_DIST_MAX_PX; + } + + /* 10% threshold if edge is closer but a bit further + * this takes care of series of connected edges a bit slanted w.r.t the viewport + * otherwise, it would stick to the verts of the closest edge and not slide along merrily + * */ + if (new_dist <= *dist_px && new_depth < *ray_depth * 1.001f) { + float n1[3], n2[3]; + + *ray_depth = new_depth; + retval = true; + + sub_v3_v3v3(edge_loc, v1co, v2co); + sub_v3_v3v3(vec, intersect, v2co); + + mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); + + if (r_no) { + normal_short_to_float_v3(n1, v1no); + normal_short_to_float_v3(n2, v2no); + interp_v3_v3v3(r_no, n2, n1, mul); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + + copy_v3_v3(r_loc, location); + + *dist_px = new_dist; + } + } + } + + return retval; +} + +static bool snapVertex( + ARegion *ar, const float vco[3], const float vno[3], + float obmat[4][4], float timat[3][3], const float mval_fl[2], float *dist_px, + const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], float *ray_depth, + float r_loc[3], float r_no[3]) +{ + bool retval = false; + float dvec[3]; + + sub_v3_v3v3(dvec, vco, ray_start_local); + + if (dot_v3v3(ray_normal_local, dvec) > 0) { + float location[3]; + float new_depth; + float screen_loc[2]; + float new_dist; + + copy_v3_v3(location, vco); + + mul_m4_v3(obmat, location); + + new_depth = len_v3v3(location, ray_start); + + if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + new_dist = len_manhattan_v2v2(mval_fl, screen_loc); + } + else { + new_dist = TRANSFORM_DIST_MAX_PX; + } + + + if (new_dist <= *dist_px && new_depth < *ray_depth) { + *ray_depth = new_depth; + retval = true; + + copy_v3_v3(r_loc, location); + + if (r_no) { + copy_v3_v3(r_no, vno); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + } + + *dist_px = new_dist; + } + } + + return retval; +} + +static bool snapArmature( + ARegion *ar, Object *ob, bArmature *arm, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float *UNUSED(r_no)) +{ + float imat[4][4]; + float ray_start_local[3], ray_normal_local[3]; + bool retval = false; + + invert_m4_m4(imat, obmat); + + mul_v3_m4v3(ray_start_local, imat, ray_start); + mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); + + if (arm->edbo) { + EditBone *eBone; + + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + if (eBone->layer & arm->layer) { + /* skip hidden or moving (selected) bones */ + if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) { + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + retval |= snapVertex( + ar, eBone->head, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + retval |= snapVertex( + ar, eBone->tail, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + break; + case SCE_SNAP_MODE_EDGE: + retval |= snapEdge( + ar, eBone->head, NULL, eBone->tail, NULL, + obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, + ray_depth, r_loc, NULL); + break; + } + } + } + } + } + else if (ob->pose && ob->pose->chanbase.first) { + bPoseChannel *pchan; + Bone *bone; + + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + bone = pchan->bone; + /* skip hidden bones */ + if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { + const float *head_vec = pchan->pose_head; + const float *tail_vec = pchan->pose_tail; + + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + retval |= snapVertex( + ar, head_vec, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, + ray_depth, r_loc, NULL); + retval |= snapVertex( + ar, tail_vec, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + break; + case SCE_SNAP_MODE_EDGE: + retval |= snapEdge( + ar, head_vec, NULL, tail_vec, NULL, + obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, + ray_depth, r_loc, NULL); + break; + } + } + } + } + + return retval; +} + +static bool snapCurve( + ARegion *ar, Object *ob, Curve *cu, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float *UNUSED(r_no)) +{ + float imat[4][4]; + float ray_start_local[3], ray_normal_local[3]; + bool retval = false; + int u; + + Nurb *nu; + + /* only vertex snapping mode (eg control points and handles) supported for now) */ + if (snap_to != SCE_SNAP_MODE_VERTEX) { + return retval; + } + + invert_m4_m4(imat, obmat); + + copy_v3_v3(ray_start_local, ray_start); + copy_v3_v3(ray_normal_local, ray_normal); + + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + + for (nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { + for (u = 0; u < nu->pntsu; u++) { + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + { + if (ob->mode == OB_MODE_EDIT) { + if (nu->bezt) { + /* don't snap to selected (moving) or hidden */ + if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) { + break; + } + retval |= snapVertex( + ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + /* don't snap if handle is selected (moving), or if it is aligning to a moving handle */ + if (!(nu->bezt[u].f1 & SELECT) && + !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT)) + { + retval |= snapVertex( + ar, nu->bezt[u].vec[0], NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + if (!(nu->bezt[u].f3 & SELECT) && + !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) + { + retval |= snapVertex( + ar, nu->bezt[u].vec[2], NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + } + else { + /* don't snap to selected (moving) or hidden */ + if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) { + break; + } + retval |= snapVertex( + ar, nu->bp[u].vec, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + } + else { + /* curve is not visible outside editmode if nurb length less than two */ + if (nu->pntsu > 1) { + if (nu->bezt) { + retval |= snapVertex( + ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + else { + retval |= snapVertex( + ar, nu->bp[u].vec, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + } + } + break; + } + default: + break; + } + } + } + return retval; +} + +/* may extend later (for now just snaps to empty center) */ +static bool snapEmpty( + ARegion *ar, Object *ob, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float *UNUSED(r_no)) +{ + float imat[4][4]; + float ray_start_local[3], ray_normal_local[3]; + bool retval = false; + + if (ob->transflag & OB_DUPLI) { + return retval; + } + /* for now only vertex supported */ + if (snap_to != SCE_SNAP_MODE_VERTEX) { + return retval; + } + + invert_m4_m4(imat, obmat); + + mul_v3_m4v3(ray_start_local, imat, ray_start); + mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); + + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + { + const float zero_co[3] = {0.0f}; + retval |= snapVertex( + ar, zero_co, NULL, obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + break; + } + default: + break; + } + + return retval; +} + +static bool snapCamera( + ARegion *ar, Scene *scene, Object *object, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float *UNUSED(r_no)) +{ + float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4]; + bool retval = false; + MovieClip *clip = BKE_object_movieclip_get(scene, object, false); + MovieTracking *tracking; + float ray_start_local[3], ray_normal_local[3]; + + if (clip == NULL) { + return retval; + } + if (object->transflag & OB_DUPLI) { + return retval; + } + + tracking = &clip->tracking; + + BKE_tracking_get_camera_object_matrix(scene, object, orig_camera_mat); + + invert_m4_m4(orig_camera_imat, orig_camera_mat); + invert_m4_m4(imat, obmat); + + switch (snap_to) { + case SCE_SNAP_MODE_VERTEX: + { + MovieTrackingObject *tracking_object; + + for (tracking_object = tracking->objects.first; + tracking_object; + tracking_object = tracking_object->next) + { + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); + MovieTrackingTrack *track; + float reconstructed_camera_mat[4][4], + reconstructed_camera_imat[4][4]; + float (*vertex_obmat)[4]; + + copy_v3_v3(ray_start_local, ray_start); + copy_v3_v3(ray_normal_local, ray_normal); + + if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) { + BKE_tracking_camera_get_reconstructed_interpolate(tracking, tracking_object, + CFRA, reconstructed_camera_mat); + + invert_m4_m4(reconstructed_camera_imat, reconstructed_camera_mat); + } + + for (track = tracksbase->first; track; track = track->next) { + float bundle_pos[3]; + + if ((track->flag & TRACK_HAS_BUNDLE) == 0) { + continue; + } + + copy_v3_v3(bundle_pos, track->bundle_pos); + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + mul_m4_v3(orig_camera_imat, ray_start_local); + mul_mat3_m4_v3(orig_camera_imat, ray_normal_local); + vertex_obmat = orig_camera_mat; + } + else { + mul_m4_v3(reconstructed_camera_imat, bundle_pos); + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + vertex_obmat = obmat; + } + + retval |= snapVertex( + ar, bundle_pos, NULL, vertex_obmat, NULL, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, NULL); + } + } + + break; + } + default: + break; + } + + return retval; +} + +static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) +{ + const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); + return index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly; +} + +static bool snapDerivedMesh( + SnapObjectContext *sctx, + Object *ob, DerivedMesh *dm, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, bool do_bb, + const float ray_start[3], const float ray_normal[3], const float ray_origin[3], + float *ray_depth, unsigned int ob_index, + float r_loc[3], float r_no[3], int *r_index, + ListBase *r_hit_list) +{ + ARegion *ar = sctx->v3d_data.ar; + bool retval = false; + + if (snap_to == SCE_SNAP_MODE_FACE) { + if (dm->getNumPolys(dm) == 0) { + return retval; + } + } + if (snap_to == SCE_SNAP_MODE_EDGE) { + if (dm->getNumEdges(dm) == 0) { + return retval; + } + } + else { + if (dm->getNumVerts(dm) == 0) { + return retval; + } + } + + { + const bool do_ray_start_correction = ( + ELEM(snap_to, SCE_SNAP_MODE_FACE, SCE_SNAP_MODE_VERTEX) && + (sctx->use_v3d && !((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp)); + bool need_ray_start_correction_init = do_ray_start_correction; + + float imat[4][4]; + float timat[3][3]; /* transpose inverse matrix for normals */ + float ray_start_local[3], ray_normal_local[3]; + float local_scale, local_depth, len_diff; + + invert_m4_m4(imat, obmat); + transpose_m3_m4(timat, imat); + + copy_v3_v3(ray_start_local, ray_start); + copy_v3_v3(ray_normal_local, ray_normal); + + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + + /* local scale in normal direction */ + local_scale = normalize_v3(ray_normal_local); + local_depth = *ray_depth; + if (local_depth != BVH_RAYCAST_DIST_MAX) { + local_depth *= local_scale; + } + + if (do_bb) { + BoundBox *bb = BKE_object_boundbox_get(ob); + + if (bb) { + BoundBox bb_temp; + + /* We cannot aford a bbox with some null dimension, which may happen in some cases... + * Threshold is rather high, but seems to be needed to get good behavior, see T46099. */ + bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f); + + /* Exact value here is arbitrary (ideally we would scale in pixel-space based on 'dist_px'), + * scale up so we can snap against verts & edges on the boundbox, see T46816. */ + if (ELEM(snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { + BKE_boundbox_scale(&bb_temp, bb, 1.0f + 1e-1f); + bb = &bb_temp; + } + + /* was local_depth, see: T47838 */ + len_diff = BVH_RAYCAST_DIST_MAX; + + if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { + return retval; + } + need_ray_start_correction_init = false; + } + } + + SnapObjectData_Mesh *sod = NULL; + BVHTreeFromMesh *treedata = NULL, treedata_stack; + + if (sctx->flag & SNAP_OBJECT_USE_CACHE) { + void **sod_p; + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + sod = *sod_p; + } + else { + sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); + sod->sd.type = SNAP_MESH; + } + + int tree_index = -1; + switch (snap_to) { + case SCE_SNAP_MODE_FACE: + tree_index = 1; + break; + case SCE_SNAP_MODE_VERTEX: + tree_index = 0; + break; + } + if (tree_index != -1) { + if (sod->bvh_trees[tree_index] == NULL) { + sod->bvh_trees[tree_index] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata)); + } + treedata = sod->bvh_trees[tree_index]; + + /* the tree is owned by the DM and may have been freed since we last used! */ + if (treedata && treedata->tree) { + if (treedata->cached && !bvhcache_has_tree(dm->bvhCache, treedata->tree)) { + free_bvhtree_from_mesh(treedata); + } + } + } + } + else { + if (ELEM(snap_to, SCE_SNAP_MODE_FACE, SCE_SNAP_MODE_VERTEX)) { + treedata = &treedata_stack; + memset(treedata, 0, sizeof(*treedata)); + } + } + + if (treedata && treedata->tree == NULL) { + switch (snap_to) { + case SCE_SNAP_MODE_FACE: + bvhtree_from_mesh_looptri(treedata, dm, 0.0f, 4, 6); + break; + case SCE_SNAP_MODE_VERTEX: + bvhtree_from_mesh_verts(treedata, dm, 0.0f, 2, 6); + break; + } + } + + if (need_ray_start_correction_init) { + /* We *need* a reasonably valid len_diff in this case. + * Use BHVTree to find the closest face from ray_start_local. + */ + if (treedata && treedata->tree != NULL) { + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + /* Compute and store result. */ + BLI_bvhtree_find_nearest( + treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata); + if (nearest.index != -1) { + len_diff = sqrtf(nearest.dist_sq); + } + } + } + /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already + * been *inside* boundbox, leading to snap failures (see T38409). + * Note also ar might be null (see T38435), in this case we assume ray_start is ok! + */ + if (do_ray_start_correction) { + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, ray_origin); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far + * away ray_start values (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, + len_diff - len_v3v3(ray_start_local, ray_org_local)); + local_depth -= len_diff; + } + else { + len_diff = 0.0f; + } + + switch (snap_to) { + case SCE_SNAP_MODE_FACE: + { + if (r_hit_list) { + struct RayCastAll_Data data; + + data.bvhdata = treedata; + data.raycast_callback = treedata->raycast_callback; + data.obmat = obmat; + data.timat = timat; + data.len_diff = len_diff; + data.local_scale = local_scale; + data.ob = ob; + data.ob_uuid = ob_index, + data.dm = dm; + data.hit_list = r_hit_list; + data.retval = retval; + + BLI_bvhtree_ray_cast_all( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + *ray_depth, raycast_all_cb, &data); + + retval = data.retval; + } + else { + BVHTreeRayHit hit; + + hit.index = -1; + hit.dist = local_depth; + + if (treedata->tree && + BLI_bvhtree_ray_cast( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + &hit, treedata->raycast_callback, treedata) != -1) + { + hit.dist += len_diff; + hit.dist /= local_scale; + if (hit.dist <= *ray_depth) { + *ray_depth = hit.dist; + copy_v3_v3(r_loc, hit.co); + copy_v3_v3(r_no, hit.no); + + /* back to worldspace */ + mul_m4_v3(obmat, r_loc); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + + retval = true; + + if (r_index) { + *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]); + } + } + } + } + break; + } + case SCE_SNAP_MODE_VERTEX: + { + BVHTreeNearest nearest; + + nearest.index = -1; + nearest.dist_sq = local_depth * local_depth; + if (treedata->tree && + BLI_bvhtree_find_nearest_to_ray( + treedata->tree, ray_start_local, ray_normal_local, + &nearest, NULL, NULL) != -1) + { + const MVert *v = &treedata->vert[nearest.index]; + float vno[3]; + normal_short_to_float_v3(vno, v->no); + retval = snapVertex( + ar, v->co, vno, obmat, timat, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, r_no); + } + break; + } + case SCE_SNAP_MODE_EDGE: + { + MVert *verts = dm->getVertArray(dm); + MEdge *edges = dm->getEdgeArray(dm); + int totedge = dm->getNumEdges(dm); + + for (int i = 0; i < totedge; i++) { + MEdge *e = edges + i; + retval |= snapEdge( + ar, verts[e->v1].co, verts[e->v1].no, verts[e->v2].co, verts[e->v2].no, + obmat, timat, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, r_no); + } + + break; + } + } + + if ((sctx->flag & SNAP_OBJECT_USE_CACHE) == 0) { + if (treedata) { + free_bvhtree_from_mesh(treedata); + } + } + } + + return retval; +} + + +static bool snapEditMesh( + SnapObjectContext *sctx, + Object *ob, BMEditMesh *em, float obmat[4][4], + const float mval[2], float *dist_px, const short snap_to, + const float ray_start[3], const float ray_normal[3], const float ray_origin[3], + float *ray_depth, const unsigned int ob_index, + float r_loc[3], float r_no[3], int *r_index, + ListBase *r_hit_list) +{ + ARegion *ar = sctx->v3d_data.ar; + bool retval = false; + + if (snap_to == SCE_SNAP_MODE_FACE) { + if (em->bm->totface == 0) { + return retval; + } + } + if (snap_to == SCE_SNAP_MODE_EDGE) { + if (em->bm->totedge == 0) { + return retval; + } + } + else { + if (em->bm->totvert == 0) { + return retval; + } + } + + { + const bool do_ray_start_correction = ( + ELEM(snap_to, SCE_SNAP_MODE_FACE, SCE_SNAP_MODE_VERTEX) && + (sctx->use_v3d && !((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp)); + + float imat[4][4]; + float timat[3][3]; /* transpose inverse matrix for normals */ + float ray_start_local[3], ray_normal_local[3]; + float local_scale, local_depth, len_diff; + + invert_m4_m4(imat, obmat); + transpose_m3_m4(timat, imat); + + copy_v3_v3(ray_start_local, ray_start); + copy_v3_v3(ray_normal_local, ray_normal); + + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + + /* local scale in normal direction */ + local_scale = normalize_v3(ray_normal_local); + local_depth = *ray_depth; + if (local_depth != BVH_RAYCAST_DIST_MAX) { + local_depth *= local_scale; + } + + SnapObjectData_EditMesh *sod = NULL; + + BVHTreeFromEditMesh *treedata = NULL, treedata_stack; + + if (sctx->flag & SNAP_OBJECT_USE_CACHE) { + void **sod_p; + if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + sod = *sod_p; + } + else { + sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); + sod->sd.type = SNAP_EDIT_MESH; + } + + int tree_index = -1; + switch (snap_to) { + case SCE_SNAP_MODE_FACE: + tree_index = 1; + break; + case SCE_SNAP_MODE_VERTEX: + tree_index = 0; + break; + } + if (tree_index != -1) { + if (sod->bvh_trees[tree_index] == NULL) { + sod->bvh_trees[tree_index] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata)); + } + treedata = sod->bvh_trees[tree_index]; + } + } + else { + if (ELEM(snap_to, SCE_SNAP_MODE_FACE, SCE_SNAP_MODE_VERTEX)) { + treedata = &treedata_stack; + memset(treedata, 0, sizeof(*treedata)); + } + } + + if (treedata && treedata->tree == NULL) { + switch (snap_to) { + case SCE_SNAP_MODE_FACE: + { + BLI_bitmap *looptri_mask = NULL; + int looptri_num_active = -1; + if (sctx->callbacks.edit_mesh.test_face_fn) { + looptri_mask = BLI_BITMAP_NEW(em->tottri, __func__); + looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( + em->bm, looptri_mask, + sctx->callbacks.edit_mesh.test_face_fn, sctx->callbacks.edit_mesh.user_data); + } + bvhtree_from_editmesh_looptri_ex(treedata, em, looptri_mask, looptri_num_active, 0.0f, 4, 6); + if (looptri_mask) { + MEM_freeN(looptri_mask); + } + break; + } + case SCE_SNAP_MODE_VERTEX: + { + BLI_bitmap *verts_mask = NULL; + int verts_num_active = -1; + if (sctx->callbacks.edit_mesh.test_vert_fn) { + verts_mask = BLI_BITMAP_NEW(em->bm->totvert, __func__); + verts_num_active = BM_iter_mesh_bitmap_from_filter( + BM_VERTS_OF_MESH, em->bm, verts_mask, + (bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_vert_fn, + sctx->callbacks.edit_mesh.user_data); + } + bvhtree_from_editmesh_verts_ex(treedata, em, verts_mask, verts_num_active, 0.0f, 2, 6); + if (verts_mask) { + MEM_freeN(verts_mask); + } + break; + } + } + } + + /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already + * been *inside* boundbox, leading to snap failures (see T38409). + * Note also ar might be null (see T38435), in this case we assume ray_start is ok! + */ + if (do_ray_start_correction) { + /* We *need* a reasonably valid len_diff in this case. + * Use BHVTree to find the closest face from ray_start_local. + */ + if (treedata && treedata->tree != NULL) { + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + /* Compute and store result. */ + BLI_bvhtree_find_nearest( + treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata); + if (nearest.index != -1) { + len_diff = sqrtf(nearest.dist_sq); + } + } + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, ray_origin); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far + * away ray_start values (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, + len_diff - len_v3v3(ray_start_local, ray_org_local)); + local_depth -= len_diff; + } + else { + len_diff = 0.0f; + } + + switch (snap_to) { + case SCE_SNAP_MODE_FACE: + { + if (r_hit_list) { + struct RayCastAll_Data data; + + data.bvhdata = treedata; + data.raycast_callback = treedata->raycast_callback; + data.obmat = obmat; + data.timat = timat; + data.len_diff = len_diff; + data.local_scale = local_scale; + data.ob = ob; + data.ob_uuid = ob_index; + data.dm = NULL; + data.hit_list = r_hit_list; + data.retval = retval; + + BLI_bvhtree_ray_cast_all( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + *ray_depth, raycast_all_cb, &data); + + retval = data.retval; + } + else { + BVHTreeRayHit hit; + + hit.index = -1; + hit.dist = local_depth; + + if (treedata->tree && + BLI_bvhtree_ray_cast( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + &hit, treedata->raycast_callback, treedata) != -1) + { + hit.dist += len_diff; + hit.dist /= local_scale; + if (hit.dist <= *ray_depth) { + *ray_depth = hit.dist; + copy_v3_v3(r_loc, hit.co); + copy_v3_v3(r_no, hit.no); + + /* back to worldspace */ + mul_m4_v3(obmat, r_loc); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); + + retval = true; + + if (r_index) { + *r_index = hit.index; + } + } + } + } + break; + } + case SCE_SNAP_MODE_VERTEX: + { + BVHTreeNearest nearest; + + nearest.index = -1; + nearest.dist_sq = local_depth * local_depth; + if (treedata->tree && + BLI_bvhtree_find_nearest_to_ray( + treedata->tree, ray_start_local, ray_normal_local, + &nearest, NULL, NULL) != -1) + { + const BMVert *v = BM_vert_at_index(em->bm, nearest.index); + retval = snapVertex( + ar, v->co, v->no, obmat, timat, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, r_no); + } + break; + } + case SCE_SNAP_MODE_EDGE: + { + BM_mesh_elem_table_ensure(em->bm, BM_EDGE); + int totedge = em->bm->totedge; + for (int i = 0; i < totedge; i++) { + BMEdge *eed = BM_edge_at_index(em->bm, i); + + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && + !BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) && + !BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) + { + short v1no[3], v2no[3]; + normal_float_to_short_v3(v1no, eed->v1->no); + normal_float_to_short_v3(v2no, eed->v2->no); + retval |= snapEdge( + ar, eed->v1->co, v1no, eed->v2->co, v2no, + obmat, timat, mval, dist_px, + ray_start, ray_start_local, ray_normal_local, ray_depth, + r_loc, r_no); + } + } + + break; + } + } + + if ((sctx->flag & SNAP_OBJECT_USE_CACHE) == 0) { + if (treedata) { + free_bvhtree_from_editmesh(treedata); + } + } + } + + return retval; +} + +static bool snapObject( + SnapObjectContext *sctx, + Object *ob, float obmat[4][4], bool use_obedit, const short snap_to, + const float mval[2], float *dist_px, const unsigned int ob_index, + const float ray_start[3], const float ray_normal[3], const float ray_origin[3], + float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4], + ListBase *r_hit_list) +{ + ARegion *ar = sctx->v3d_data.ar; + bool retval = false; + + if (ob->type == OB_MESH) { + BMEditMesh *em; + + if (use_obedit) { + em = BKE_editmesh_from_object(ob); + retval = snapEditMesh( + sctx, ob, em, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_origin, + ray_depth, ob_index, + r_loc, r_no, r_index, + r_hit_list); + } + else { + /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978. + * still set the 'em' to NULL, since we only want the 'dm'. */ + DerivedMesh *dm; + em = BKE_editmesh_from_object(ob); + if (em) { + editbmesh_get_derived_cage_and_final(sctx->scene, ob, em, CD_MASK_BAREMESH, &dm); + } + else { + dm = mesh_get_derived_final(sctx->scene, ob, CD_MASK_BAREMESH); + } + retval = snapDerivedMesh( + sctx, ob, dm, obmat, mval, dist_px, snap_to, true, + ray_start, ray_normal, ray_origin, + ray_depth, ob_index, + r_loc, r_no, r_index, r_hit_list); + + dm->release(dm); + } + } + else if (ob->type == OB_ARMATURE) { + retval = snapArmature( + ar, ob, ob->data, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_depth, + r_loc, r_no); + } + else if (ob->type == OB_CURVE) { + retval = snapCurve( + ar, ob, ob->data, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_depth, + r_loc, r_no); + } + else if (ob->type == OB_EMPTY) { + retval = snapEmpty( + ar, ob, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_depth, + r_loc, r_no); + } + else if (ob->type == OB_CAMERA) { + retval = snapCamera( + ar, sctx->scene, ob, obmat, mval, dist_px, snap_to, + ray_start, ray_normal, ray_depth, + r_loc, r_no); + } + + if (retval) { + if (r_ob) { + *r_ob = ob; + copy_m4_m4(r_obmat, obmat); + } + } + + return retval; +} + +static bool snapObjectsRay( + SnapObjectContext *sctx, + SnapSelect snap_select, const short snap_to, + const float mval[2], float *dist_px, + /* special handling of active and edit objects */ + Base *base_act, Object *obedit, + const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, + /* return args */ + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4], + ListBase *r_hit_list) +{ + Base *base; + bool retval = false; + bool snap_obedit_first = snap_select == SNAP_ALL && obedit; + unsigned int ob_index = 0; + + if (snap_obedit_first) { + Object *ob = obedit; + + retval |= snapObject( + sctx, ob, ob->obmat, true, snap_to, + mval, dist_px, ob_index++, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); + } + + /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA + * which makes the loop skip it, even the derived mesh will never change + * + * To solve that problem, we do it first as an exception. + * */ + base = base_act; + if (base && base->object && base->object->mode & OB_MODE_PARTICLE_EDIT) { + Object *ob = base->object; + retval |= snapObject( + sctx, ob, ob->obmat, false, snap_to, + mval, dist_px, ob_index++, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); + } + + for (base = sctx->scene->base.first; base != NULL; base = base->next) { + if ((BASE_VISIBLE_BGMODE(sctx->v3d_data.v3d, sctx->scene, base)) && + (base->flag & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 && + + ((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL)) == 0) || + (ELEM(snap_select, SNAP_ALL, SNAP_NOT_OBEDIT) && base != base_act))) + { + Object *ob = base->object; + Object *ob_snap = ob; + bool use_obedit = false; + + if (ob->transflag & OB_DUPLI) { + DupliObject *dupli_ob; + ListBase *lb = object_duplilist(sctx->bmain->eval_ctx, sctx->scene, ob); + + for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { + bool use_obedit_dupli = (obedit && dupli_ob->ob->data == obedit->data); + Object *dupli_snap = (use_obedit_dupli) ? obedit : dupli_ob->ob; + + retval |= snapObject( + sctx, dupli_snap, dupli_ob->mat, use_obedit_dupli, snap_to, + mval, dist_px, ob_index++, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); + } + + free_object_duplilist(lb); + } + + if (obedit) { + if ((ob == obedit) && + (snap_obedit_first || (snap_select == SNAP_NOT_OBEDIT))) + { + continue; + } + + if (ob->data == obedit->data) { + /* for linked objects, use the same object but a different matrix */ + use_obedit = true; + ob_snap = obedit; + } + } + + retval |= snapObject( + sctx, ob_snap, ob->obmat, use_obedit, snap_to, + mval, dist_px, ob_index++, + ray_start, ray_normal, ray_origin, ray_depth, + r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); + } + } + + return retval; +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Public Object Snapping API + * \{ */ + +SnapObjectContext *ED_transform_snap_object_context_create( + Main *bmain, Scene *scene, int flag) +{ + SnapObjectContext *sctx = MEM_callocN(sizeof(*sctx), __func__); + + sctx->flag = flag; + + sctx->bmain = bmain; + sctx->scene = scene; + + return sctx; +} + +SnapObjectContext *ED_transform_snap_object_context_create_view3d( + Main *bmain, Scene *scene, int flag, + /* extra args for view3d */ + ARegion *ar, View3D *v3d) +{ + SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, flag); + + sctx->use_v3d = true; + sctx->v3d_data.ar = ar; + sctx->v3d_data.v3d = v3d; + + if (sctx->flag & SNAP_OBJECT_USE_CACHE) { + sctx->cache.object_map = BLI_ghash_ptr_new(__func__); + sctx->cache.mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + return sctx; +} + +static void snap_object_data_free(void *sod_v) +{ + switch (((SnapObjectData *)sod_v)->type) { + case SNAP_MESH: + { + SnapObjectData_Mesh *sod = sod_v; + for (int i = 0; i < ARRAY_SIZE(sod->bvh_trees); i++) { + if (sod->bvh_trees[i]) { + free_bvhtree_from_mesh(sod->bvh_trees[i]); + } + } + break; + } + case SNAP_EDIT_MESH: + { + SnapObjectData_EditMesh *sod = sod_v; + for (int i = 0; i < ARRAY_SIZE(sod->bvh_trees); i++) { + if (sod->bvh_trees[i]) { + free_bvhtree_from_editmesh(sod->bvh_trees[i]); + } + } + break; + } + } +} + +void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx) +{ + if (sctx->flag & SNAP_OBJECT_USE_CACHE) { + BLI_ghash_free(sctx->cache.object_map, NULL, snap_object_data_free); + BLI_memarena_free(sctx->cache.mem_arena); + } + + MEM_freeN(sctx); +} + +void ED_transform_snap_object_context_set_editmesh_callbacks( + SnapObjectContext *sctx, + bool (*test_vert_fn)(BMVert *, void *user_data), + bool (*test_edge_fn)(BMEdge *, void *user_data), + bool (*test_face_fn)(BMFace *, void *user_data), + void *user_data) +{ + sctx->callbacks.edit_mesh.test_vert_fn = test_vert_fn; + sctx->callbacks.edit_mesh.test_edge_fn = test_edge_fn; + sctx->callbacks.edit_mesh.test_face_fn = test_face_fn; + + sctx->callbacks.edit_mesh.user_data = user_data; +} + +bool ED_transform_snap_object_project_ray_ex( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4]) +{ + Base *base_act = params->use_object_active ? sctx->scene->basact : NULL; + Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL; + + return snapObjectsRay( + sctx, + params->snap_select, params->snap_to, + NULL, NULL, + base_act, obedit, + ray_start, ray_normal, ray_start, ray_depth, + r_loc, r_no, r_index, + r_ob, r_obmat, NULL); +} + +/** + * Fill in a list of all hits. + * + * \param ray_depth: Only depths in this range are considered, -1.0 for maximum. + * \param sort: Optionally sort the hits by depth. + * \param r_hit_list: List of #SnapObjectHitDepth (caller must free). + */ +bool ED_transform_snap_object_project_ray_all( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float ray_start[3], const float ray_normal[3], + float ray_depth, bool sort, + ListBase *r_hit_list) +{ + Base *base_act = params->use_object_active ? sctx->scene->basact : NULL; + Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL; + + if (ray_depth == -1.0f) { + ray_depth = BVH_RAYCAST_DIST_MAX; + } + +#ifdef DEBUG + float ray_depth_prev = ray_depth; +#endif + + bool retval = snapObjectsRay( + sctx, + params->snap_select, params->snap_to, + NULL, NULL, + base_act, obedit, + ray_start, ray_normal, ray_start, &ray_depth, + NULL, NULL, NULL, NULL, NULL, + r_hit_list); + + /* meant to be readonly for 'all' hits, ensure it is */ +#ifdef DEBUG + BLI_assert(ray_depth_prev == ray_depth); +#endif + + if (sort) { + BLI_listbase_sort(r_hit_list, hit_depth_cmp); + } + + return retval; +} + +/** + * Convenience function for snap ray-casting. + * + * Given a ray, cast it into the scene (snapping to faces). + * + * \return Snap success + */ +static bool transform_snap_context_project_ray_impl( + SnapObjectContext *sctx, + const float ray_start[3], const float ray_normal[3], float *ray_depth, + float r_co[3], float r_no[3]) +{ + bool ret; + + /* try snap edge, then face if it fails */ + ret = ED_transform_snap_object_project_ray_ex( + sctx, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + .snap_to = SCE_SNAP_MODE_FACE, + .use_object_edit = (sctx->scene->obedit != NULL), + }, + ray_start, ray_normal, ray_depth, + r_co, r_no, NULL, + NULL, NULL); + + return ret; +} + +bool ED_transform_snap_object_project_ray( + SnapObjectContext *sctx, + const float ray_origin[3], const float ray_direction[3], float *ray_depth, + float r_co[3], float r_no[3]) +{ + float ray_depth_fallback; + if (ray_depth == NULL) { + ray_depth_fallback = BVH_RAYCAST_DIST_MAX; + ray_depth = &ray_depth_fallback; + } + + float no_fallback[3]; + if (r_no == NULL) { + r_no = no_fallback; + } + + return transform_snap_context_project_ray_impl( + sctx, + ray_origin, ray_direction, ray_depth, + r_co, r_no); +} + +static bool transform_snap_context_project_view3d_mixed_impl( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + bool use_depth, + float r_co[3], float r_no[3]) +{ + float ray_depth = BVH_RAYCAST_DIST_MAX; + bool is_hit = false; + + float r_no_dummy[3]; + if (r_no == NULL) { + r_no = r_no_dummy; + } + + const int elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE}; + + BLI_assert(params->snap_to_flag != 0); + BLI_assert((params->snap_to_flag & ~(1 | 2 | 4)) == 0); + + struct SnapObjectParams params_temp = *params; + + for (int i = 0; i < 3; i++) { + if ((params->snap_to_flag & (1 << i)) && (is_hit == false || use_depth)) { + if (use_depth == false) { + ray_depth = BVH_RAYCAST_DIST_MAX; + } + + params_temp.snap_to = elem_type[i]; + + if (ED_transform_snap_object_project_view3d( + sctx, + ¶ms_temp, + mval, dist_px, &ray_depth, + r_co, r_no)) + { + is_hit = true; + } + } + } + + return is_hit; +} + +/** + * Convenience function for performing snapping. + * + * Given a 2D region value, snap to vert/edge/face. + * + * \param sctx: Snap context. + * \param mval: Screenspace coordinate. + * \param dist_px: Maximum distance to snap (in pixels). + * \param use_depth: Snap to the closest element, use when using more than one snap type. + * \param r_co: hit location. + * \param r_no: hit normal (optional). + * \return Snap success + */ +bool ED_transform_snap_object_project_view3d_mixed( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval_fl[2], float *dist_px, + bool use_depth, + float r_co[3], float r_no[3]) +{ + return transform_snap_context_project_view3d_mixed_impl( + sctx, + params, + mval_fl, dist_px, use_depth, + r_co, r_no); +} + +bool ED_transform_snap_object_project_view3d_ex( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float *ray_depth, + float r_loc[3], float r_no[3], int *r_index) +{ + float ray_start[3], ray_normal[3], ray_orgigin[3]; + + float ray_depth_fallback; + if (ray_depth == NULL) { + ray_depth_fallback = BVH_RAYCAST_DIST_MAX; + ray_depth = &ray_depth_fallback; + } + + if (!ED_view3d_win_to_ray_ex( + sctx->v3d_data.ar, sctx->v3d_data.v3d, + mval, ray_orgigin, ray_normal, ray_start, true)) + { + return false; + } + + Base *base_act = params->use_object_active ? sctx->scene->basact : NULL; + Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL; + return snapObjectsRay( + sctx, + params->snap_select, params->snap_to, + mval, dist_px, + base_act, obedit, + ray_start, ray_normal, ray_orgigin, ray_depth, + r_loc, r_no, r_index, NULL, NULL, NULL); +} + +bool ED_transform_snap_object_project_view3d( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float *ray_depth, + float r_loc[3], float r_no[3]) +{ + return ED_transform_snap_object_project_view3d_ex( + sctx, + params, + mval, dist_px, + ray_depth, + r_loc, r_no, NULL); +} + +/** + * see: #ED_transform_snap_object_project_ray_all + */ +bool ED_transform_snap_object_project_all_view3d_ex( + SnapObjectContext *sctx, + const struct SnapObjectParams *params, + const float mval[2], + float ray_depth, bool sort, + ListBase *r_hit_list) +{ + float ray_start[3], ray_normal[3]; + + BLI_assert(params->snap_to == SCE_SNAP_MODE_FACE); + + if (!ED_view3d_win_to_ray_ex( + sctx->v3d_data.ar, sctx->v3d_data.v3d, + mval, NULL, ray_normal, ray_start, true)) + { + return false; + } + + return ED_transform_snap_object_project_ray_all( + sctx, + params, + ray_start, ray_normal, ray_depth, sort, + r_hit_list); +} + +/** \} */ diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 7de788dca56..c0b30f93939 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -83,6 +83,7 @@ set(SRC ../include/ED_space_api.h ../include/ED_text.h ../include/ED_transform.h + ../include/ED_transform_snap_object_context.h ../include/ED_transverts.h ../include/ED_types.h ../include/ED_util.h diff --git a/source/blender/editors/util/editmode_undo.c b/source/blender/editors/util/editmode_undo.c index bc7a8374c73..441fd446cd6 100644 --- a/source/blender/editors/util/editmode_undo.c +++ b/source/blender/editors/util/editmode_undo.c @@ -40,7 +40,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BKE_blender.h" +#include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_global.h" diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index a1853bf8daa..ee6700666c0 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -42,7 +42,7 @@ #include "BLT_translation.h" -#include "BKE_blender.h" +#include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" @@ -436,7 +436,8 @@ enum { UNDOSYSTEM_GLOBAL = 1, UNDOSYSTEM_EDITMODE = 2, UNDOSYSTEM_PARTICLE = 3, - UNDOSYSTEM_IMAPAINT = 4 + UNDOSYSTEM_IMAPAINT = 4, + UNDOSYSTEM_SCULPT = 5, }; static int get_undo_system(bContext *C) @@ -468,6 +469,10 @@ static int get_undo_system(bContext *C) if (!ED_undo_paint_empty(UNDO_PAINT_IMAGE)) return UNDOSYSTEM_IMAPAINT; } + else if (obact->mode & OB_MODE_SCULPT) { + if (!ED_undo_paint_empty(UNDO_PAINT_MESH)) + return UNDOSYSTEM_SCULPT; + } } if (U.uiflag & USER_GLOBALUNDO) return UNDOSYSTEM_GLOBAL; @@ -495,6 +500,9 @@ static EnumPropertyItem *rna_undo_itemf(bContext *C, int undosys, int *totitem) else if (undosys == UNDOSYSTEM_IMAPAINT) { name = ED_undo_paint_get_name(C, UNDO_PAINT_IMAGE, i, &active); } + else if (undosys == UNDOSYSTEM_SCULPT) { + name = ED_undo_paint_get_name(C, UNDO_PAINT_MESH, i, &active); + } else { name = BKE_undo_get_name(i, &active); } @@ -576,6 +584,9 @@ static int undo_history_exec(bContext *C, wmOperator *op) else if (undosys == UNDOSYSTEM_IMAPAINT) { ED_undo_paint_step_num(C, UNDO_PAINT_IMAGE, item); } + else if (undosys == UNDOSYSTEM_SCULPT) { + ED_undo_paint_step_num(C, UNDO_PAINT_MESH, item); + } else { ED_viewport_render_kill_jobs(CTX_wm_manager(C), CTX_data_main(C), true); BKE_undo_number(C, item); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 1071e0f12e8..193b006cf0d 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -4302,7 +4302,7 @@ void ED_keymap_uvedit(wmKeyConfig *keyconf) /* border/circle selection */ kmi = WM_keymap_add_item(keymap, "UV_OT_select_border", BKEY, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "pinned", false); - kmi = WM_keymap_add_item(keymap, "UV_OT_select_border", BKEY, KM_PRESS, KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "UV_OT_select_border", BKEY, KM_PRESS, KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "pinned", true); WM_keymap_add_item(keymap, "UV_OT_circle_select", CKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 20a8ab5c98c..59442e89787 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -46,6 +46,8 @@ #include "BLI_math_vector.h" #include "BLI_string.h" +#include "BLT_translation.h" + #include "BIF_gl.h" #include "BKE_context.h" @@ -54,6 +56,8 @@ #include "BKE_mesh_mapping.h" #include "BKE_editmesh.h" +#include "UI_interface.h" + #include "ED_mesh.h" #include "ED_uvedit.h" #include "ED_screen.h" @@ -259,25 +263,24 @@ static void stitch_preview_delete(StitchPreviewer *stitch_preview) } } -#define HEADER_LENGTH 256 - /* This function updates the header of the UV editor when the stitch tool updates its settings */ static void stitch_update_header(StitchState *state, bContext *C) { - static char str[] = + const char *str = IFACE_( "Mode(TAB) %s, " "(S)nap %s, " "(M)idpoints %s, " "(L)imit %.2f (Alt Wheel adjust) %s, " "Switch (I)sland, " - "shift select vertices"; + "shift select vertices" + ); - char msg[HEADER_LENGTH]; + char msg[UI_MAX_DRAW_STR]; ScrArea *sa = CTX_wm_area(C); if (sa) { - BLI_snprintf(msg, HEADER_LENGTH, str, - state->mode == STITCH_VERT ? "Vertex" : "Edge", + BLI_snprintf(msg, sizeof(msg), str, + state->mode == STITCH_VERT ? IFACE_("Vertex") : IFACE_("Edge"), WM_bool_as_string(state->snap_islands), WM_bool_as_string(state->midpoints), state->limit_dist, diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 768624b1968..8e4ba4c0afa 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -49,6 +49,8 @@ #include "BLI_uvproject.h" #include "BLI_string.h" +#include "BLT_translation.h" + #include "BKE_cdderivedmesh.h" #include "BKE_subsurf.h" #include "BKE_context.h" @@ -62,6 +64,8 @@ #include "PIL_time.h" +#include "UI_interface.h" + #include "ED_image.h" #include "ED_mesh.h" #include "ED_screen.h" @@ -554,12 +558,13 @@ static void minimize_stretch_iteration(bContext *C, wmOperator *op, bool interac RNA_int_set(op->ptr, "iterations", ms->i); if (interactive && (PIL_check_seconds_timer() - ms->lasttime > 0.5)) { - char str[100]; + char str[UI_MAX_DRAW_STR]; param_flush(ms->handle); if (sa) { - BLI_snprintf(str, sizeof(str), "Minimize Stretch. Blend %.2f (Press + and -, or scroll wheel to set)", ms->blend); + BLI_snprintf(str, sizeof(str), + IFACE_("Minimize Stretch. Blend %.2f (Press + and -, or scroll wheel to set)"), ms->blend); ED_area_headerprint(sa, str); } diff --git a/source/blender/freestyle/intern/application/Controller.cpp b/source/blender/freestyle/intern/application/Controller.cpp index 136fec3dd1c..beb85798223 100644 --- a/source/blender/freestyle/intern/application/Controller.cpp +++ b/source/blender/freestyle/intern/application/Controller.cpp @@ -864,10 +864,10 @@ bool Controller::getComputeSteerableViewMapFlag() const return _ComputeSteerableViewMap; } -void Controller::DrawStrokes() +int Controller::DrawStrokes() { if (_ViewMap == 0) - return; + return 0; if (G.debug & G_DEBUG_FREESTYLE) { cout << "\n=== Stroke drawing ===" << endl; @@ -875,12 +875,14 @@ void Controller::DrawStrokes() _Chrono.start(); _Canvas->Draw(); real d = _Chrono.stop(); + int strokeCount = _Canvas->getStrokeCount(); if (G.debug & G_DEBUG_FREESTYLE) { cout << "Strokes generation : " << d << endl; - cout << "Stroke count : " << _Canvas->getStrokeCount() << endl; + cout << "Stroke count : " << strokeCount << endl; } resetModified(); DeleteViewMap(); + return strokeCount; } void Controller::ResetRenderCount() diff --git a/source/blender/freestyle/intern/application/Controller.h b/source/blender/freestyle/intern/application/Controller.h index 6f3cb3b274b..154edaf1e53 100644 --- a/source/blender/freestyle/intern/application/Controller.h +++ b/source/blender/freestyle/intern/application/Controller.h @@ -75,7 +75,7 @@ public: void ComputeSteerableViewMap(); void saveSteerableViewMapImages(); void toggleEdgeTesselationNature(Nature::EdgeNature iNature); - void DrawStrokes(); + int DrawStrokes(); void ResetRenderCount(); Render *RenderStrokes(Render *re, bool render); void SwapStyleModules(unsigned i1, unsigned i2); diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index dc88f69c95b..4d8c6a66d42 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -628,15 +628,19 @@ Render *FRS_do_stroke_rendering(Render *re, SceneRenderLayer *srl, int render) re->stats_draw(re->sdh, &re->i); re->i.infostr = NULL; g_freestyle.scene = re->scene; - controller->DrawStrokes(); - freestyle_render = controller->RenderStrokes(re, true); + int strokeCount = controller->DrawStrokes(); + if (strokeCount > 0) { + freestyle_render = controller->RenderStrokes(re, true); + } controller->CloseFile(); g_freestyle.scene = NULL; // composite result - FRS_composite_result(re, srl, freestyle_render); - RE_FreeRenderResult(freestyle_render->result); - freestyle_render->result = NULL; + if (freestyle_render) { + FRS_composite_result(re, srl, freestyle_render); + RE_FreeRenderResult(freestyle_render->result); + freestyle_render->result = NULL; + } } } diff --git a/source/blender/freestyle/intern/system/PseudoNoise.cpp b/source/blender/freestyle/intern/system/PseudoNoise.cpp index 4949cb8c430..638eb6ca16a 100644 --- a/source/blender/freestyle/intern/system/PseudoNoise.cpp +++ b/source/blender/freestyle/intern/system/PseudoNoise.cpp @@ -33,7 +33,7 @@ static int modf_to_index(Freestyle::real x, unsigned int range) { - if (finite(x)) { + if (isfinite(x)) { Freestyle::real tmp; int i = abs((int)(modf(x, &tmp) * range)); BLI_assert(i >= 0 && i < range); diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 7cd05eff64e..94d52c3617c 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -1496,7 +1496,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) function = gpu_lookup_function(name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); - return 0; + return false; } node = GPU_node_begin(name); @@ -1516,7 +1516,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) gpu_material_add_node(mat, node); - return 1; + return true; } bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...) @@ -1530,7 +1530,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod function = gpu_lookup_function(name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); - return 0; + return false; } node = GPU_node_begin(name); @@ -1577,7 +1577,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod gpu_material_add_node(mat, node); - return 1; + return true; } int GPU_link_changed(GPUNodeLink *link) diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index 7534d9d0fb9..c2a2b1804ca 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -871,14 +871,15 @@ bool GPU_fx_do_composite_pass( float scale = scene->unit.system ? scene->unit.scale_length : 1.0f; /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though - * because the shader reads coordinates in world space, which is in blender units. */ + * because the shader reads coordinates in world space, which is in blender units. + * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */ float scale_camera = 0.001f / scale; /* we want radius here for the aperture number */ float aperture = 0.5f * scale_camera * fx_dof->focal_length / fx_dof->fstop; dof_params[0] = aperture * fabsf(scale_camera * fx_dof->focal_length / - ((fx_dof->focus_distance / scale) - scale_camera * fx_dof->focal_length)); - dof_params[1] = fx_dof->focus_distance / scale; + (fx_dof->focus_distance - scale_camera * fx_dof->focal_length)); + dof_params[1] = fx_dof->focus_distance; dof_params[2] = fx->gbuffer_dim[0] / (scale_camera * fx_dof->sensor); dof_params[3] = fx_dof->num_blades; diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 1c22c89abf9..e5d51772bb2 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -509,6 +509,58 @@ static void gpu_verify_reflection(Image *ima) } } +typedef struct VerifyThreadData { + ImBuf *ibuf; + float *srgb_frect; +} VerifyThreadData; + +static void gpu_verify_high_bit_srgb_buffer_slice(float *srgb_frect, + ImBuf *ibuf, + const int start_line, + const int height) +{ + size_t offset = ibuf->channels * start_line * ibuf->x; + float *current_srgb_frect = srgb_frect + offset; + float *current_rect_float = ibuf->rect_float + offset; + IMB_buffer_float_from_float(current_srgb_frect, + current_rect_float, + ibuf->channels, + IB_PROFILE_SRGB, + IB_PROFILE_LINEAR_RGB, true, + ibuf->x, height, + ibuf->x, ibuf->x); + IMB_buffer_float_unpremultiply(current_srgb_frect, ibuf->x, height); + /* Clamp buffer colors to 1.0 to avoid artifacts due to glu for hdr images. */ + IMB_buffer_float_clamp(current_srgb_frect, ibuf->x, height); +} + +static void verify_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + VerifyThreadData *data = (VerifyThreadData *)data_v; + gpu_verify_high_bit_srgb_buffer_slice(data->srgb_frect, + data->ibuf, + start_scanline, + num_scanlines); +} + +static void gpu_verify_high_bit_srgb_buffer(float *srgb_frect, + ImBuf *ibuf) +{ + if (ibuf->y < 64) { + gpu_verify_high_bit_srgb_buffer_slice(srgb_frect, + ibuf, + 0, ibuf->y); + } + else { + VerifyThreadData data; + data.ibuf = ibuf; + data.srgb_frect = srgb_frect; + IMB_processor_apply_threaded_scanlines(ibuf->y, verify_thread_do, &data); + } +} + int GPU_verify_image(Image *ima, ImageUser *iuser, int textarget, int tftile, bool compare, bool mipmap, bool is_data) { unsigned int *bind = NULL; @@ -575,17 +627,18 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int textarget, int tftile, bo * a high precision format only if it is available */ use_high_bit_depth = true; } + else if (ibuf->rect == NULL) { + IMB_rect_from_float(ibuf); + } /* we may skip this in high precision, but if not, we need to have a valid buffer here */ else if (ibuf->userflags & IB_RECT_INVALID) { IMB_rect_from_float(ibuf); } /* TODO unneeded when float images are correctly treated as linear always */ - if (!is_data) + if (!is_data) { do_color_management = true; - - if (ibuf->rect == NULL) - IMB_rect_from_float(ibuf); + } } /* currently, tpage refresh is used by ima sequences */ @@ -622,12 +675,7 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int textarget, int tftile, bo if (use_high_bit_depth) { if (do_color_management) { srgb_frect = MEM_mallocN(ibuf->x * ibuf->y * sizeof(float) * 4, "floar_buf_col_cor"); - IMB_buffer_float_from_float(srgb_frect, ibuf->rect_float, - ibuf->channels, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, true, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); - IMB_buffer_float_unpremultiply(srgb_frect, ibuf->x, ibuf->y); - /* clamp buffer colors to 1.0 to avoid artifacts due to glu for hdr images */ - IMB_buffer_float_clamp(srgb_frect, ibuf->x, ibuf->y); + gpu_verify_high_bit_srgb_buffer(srgb_frect, ibuf); frect = srgb_frect + texwinsy * ibuf->x + texwinsx; } else { @@ -650,13 +698,7 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int textarget, int tftile, bo if (use_high_bit_depth) { if (do_color_management) { frect = srgb_frect = MEM_mallocN(ibuf->x * ibuf->y * sizeof(*srgb_frect) * 4, "floar_buf_col_cor"); - IMB_buffer_float_from_float( - srgb_frect, ibuf->rect_float, - ibuf->channels, IB_PROFILE_SRGB, IB_PROFILE_LINEAR_RGB, true, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); - IMB_buffer_float_unpremultiply(srgb_frect, ibuf->x, ibuf->y); - /* clamp buffer colors to 1.0 to avoid artifacts due to glu for hdr images */ - IMB_buffer_float_clamp(srgb_frect, ibuf->x, ibuf->y); + gpu_verify_high_bit_srgb_buffer(srgb_frect, ibuf); } else frect = ibuf->rect_float; diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index 7d433a78411..3c96b628cd6 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -259,8 +259,8 @@ bool GPU_legacy_support(void) if (G.debug & G_DEBUG_GPU) { printf("GL_CONTEXT_PROFILE_MASK = %#x (%s profile)\n", (unsigned int)profile, - profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT ? "compatibility" : - profile & GL_CONTEXT_CORE_PROFILE_BIT ? "core" : "unknown"); + (profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) ? "compatibility" : + (profile & GL_CONTEXT_CORE_PROFILE_BIT) ? "core" : "unknown"); } if (profile == 0) { diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 18468c1674f..72a59e6843d 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -135,6 +135,13 @@ void linearrgb_to_srgb(vec4 col_from, out vec4 col_to) col_to.a = col_from.a; } +void color_to_normal(vec3 color, out vec3 normal) +{ + normal.x = 2.0 * ((color.r) - 0.5); + normal.y = -2.0 * ((color.g) - 0.5); + normal.z = 2.0 * ((color.b) - 0.5); +} + #define M_PI 3.14159265358979323846 #define M_1_PI 0.31830988618379069 @@ -369,6 +376,10 @@ void vec_math_average(vec3 v1, vec3 v2, out vec3 outvec, out float outval) outval = length(outvec); outvec = normalize(outvec); } +void vec_math_mix(float strength, vec3 v1, vec3 v2, out vec3 outvec) +{ + outvec = strength*v1 + (1 - strength) * v2; +} void vec_math_dot(vec3 v1, vec3 v2, out vec3 outvec, out float outval) { @@ -2701,9 +2712,12 @@ void node_object_info(out vec3 location, out float object_index, out float mater random = 0.0; } -void node_normal_map(float strength, vec4 color, vec3 N, out vec3 result) +void node_normal_map(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal) { - result = N; + vec3 B = tangent.w * cross(normal, tangent.xyz); + + outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal; + outnormal = normalize(outnormal); } void node_bump(float strength, float dist, float height, vec3 N, out vec3 result) diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h index ab822f1cd55..52febe642a0 100644 --- a/source/blender/imbuf/IMB_colormanagement.h +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -163,6 +163,16 @@ void IMB_partial_display_buffer_update(struct ImBuf *ibuf, const float *linear_b int xmin, int ymin, int xmax, int ymax, bool copy_display_to_byte_buffer); +void IMB_partial_display_buffer_update_threaded(struct ImBuf *ibuf, + const float *linear_buffer, + const unsigned char *buffer_byte, + int stride, + int offset_x, int offset_y, + const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings, + int xmin, int ymin, int xmax, int ymax, + bool copy_display_to_byte_buffer); + void IMB_partial_display_buffer_update_delayed(struct ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax); /* ** Pixel processor functions ** */ diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 043f1602a76..93d2b3e0cd0 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -130,7 +130,18 @@ void IMB_freeImBuf(struct ImBuf *ibuf); * \attention Defined in allocimbuf.c */ struct ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, - unsigned char d, unsigned int flags); + unsigned char planes, unsigned int flags); + +/** + * Initialize given ImBuf. + * + * Use in cases when temporary image buffer is allocated on stack. + * + * \attention Defined in allocimbuf.c + */ +bool IMB_initImBuf(struct ImBuf *ibuf, + unsigned int x, unsigned int y, + unsigned char planes, unsigned int flags); /** * Create a copy of a pixel buffer and wrap it to a new ImBuf @@ -213,6 +224,10 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf, unsigned short *dmask, unsigned short *curvemask, unsigned short *mmask, float mask_max, int destx, int desty, int origx, int origy, int srcx, int srcy, int width, int height, IMB_BlendMode mode, bool accumulate); +void IMB_rectblend_threaded(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf, + unsigned short *dmask, unsigned short *curvemask, unsigned short *mmask, float mask_max, + int destx, int desty, int origx, int origy, int srcx, int srcy, + int width, int height, IMB_BlendMode mode, bool accumulate); /** * @@ -439,6 +454,9 @@ void IMB_buffer_float_from_byte(float *rect_to, const unsigned char *rect_from, void IMB_buffer_float_from_float(float *rect_to, const float *rect_from, int channels_from, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from); +void IMB_buffer_float_from_float_threaded(float *rect_to, const float *rect_from, + int channels_from, int profile_to, int profile_from, bool predivide, + int width, int height, int stride_to, int stride_from); void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, int channels_from, int width, int height, int stride_to, int stride_from, char *mask); void IMB_buffer_byte_from_byte(unsigned char *rect_to, const unsigned char *rect_from, @@ -580,6 +598,14 @@ void IMB_processor_apply_threaded(int buffer_lines, int handle_size, void *init_ void *customdata), void *(do_thread) (void *)); +typedef void (*ScanlineThreadFunc) (void *custom_data, + int start_scanline, + int num_scanlines); +void IMB_processor_apply_threaded_scanlines(int total_scanlines, + ScanlineThreadFunc do_thread, + void *custom_data); + + /* ffmpeg */ void IMB_ffmpeg_init(void); const char *IMB_ffmpeg_last_error(void); diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 988f43ff9fa..ef3743d9c8a 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -446,49 +446,60 @@ ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int { ImBuf *ibuf; - ibuf = MEM_callocN(sizeof(ImBuf), "ImBuf_struct"); + ibuf = MEM_mallocN(sizeof(ImBuf), "ImBuf_struct"); if (ibuf) { - ibuf->x = x; - ibuf->y = y; - ibuf->planes = planes; - ibuf->ftype = IMB_FTYPE_PNG; - ibuf->foptions.quality = 15; /* the 15 means, set compression to low ratio but not time consuming */ - ibuf->channels = 4; /* float option, is set to other values when buffers get assigned */ - ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254f; /* IMB_DPI_DEFAULT -> pixels-per-meter */ - - if (flags & IB_rect) { - if (imb_addrectImBuf(ibuf) == false) { - IMB_freeImBuf(ibuf); - return NULL; - } + if (!IMB_initImBuf(ibuf, x, y, planes, flags)) { + IMB_freeImBuf(ibuf); + return NULL; } - - if (flags & IB_rectfloat) { - if (imb_addrectfloatImBuf(ibuf) == false) { - IMB_freeImBuf(ibuf); - return NULL; - } + } + + return (ibuf); +} + +bool IMB_initImBuf(struct ImBuf *ibuf, + unsigned int x, unsigned int y, + unsigned char planes, unsigned int flags) +{ + memset(ibuf, 0, sizeof(ImBuf)); + + ibuf->x = x; + ibuf->y = y; + ibuf->planes = planes; + ibuf->ftype = IMB_FTYPE_PNG; + ibuf->foptions.quality = 15; /* the 15 means, set compression to low ratio but not time consuming */ + ibuf->channels = 4; /* float option, is set to other values when buffers get assigned */ + ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254f; /* IMB_DPI_DEFAULT -> pixels-per-meter */ + + if (flags & IB_rect) { + if (imb_addrectImBuf(ibuf) == false) { + return false; } - - if (flags & IB_zbuf) { - if (addzbufImBuf(ibuf) == false) { - IMB_freeImBuf(ibuf); - return NULL; - } + } + + if (flags & IB_rectfloat) { + if (imb_addrectfloatImBuf(ibuf) == false) { + return false; } - - if (flags & IB_zbuffloat) { - if (addzbuffloatImBuf(ibuf) == false) { - IMB_freeImBuf(ibuf); - return NULL; - } + } + + if (flags & IB_zbuf) { + if (addzbufImBuf(ibuf) == false) { + return false; } + } - /* assign default spaces */ - colormanage_imbuf_set_default_spaces(ibuf); + if (flags & IB_zbuffloat) { + if (addzbuffloatImBuf(ibuf) == false) { + return false; + } } - return (ibuf); + + /* assign default spaces */ + colormanage_imbuf_set_default_spaces(ibuf); + + return true; } /* does no zbuffers? */ diff --git a/source/blender/imbuf/intern/cineon/dpxlib.c b/source/blender/imbuf/intern/cineon/dpxlib.c index 562bdecb842..8b4e95ac452 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.c +++ b/source/blender/imbuf/intern/cineon/dpxlib.c @@ -184,8 +184,8 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf } else { if (verbose) { - printf("DPX: Bad magic number %lu in \"%s\".\n", - (uintptr_t)header.fileHeader.magic_num, byteStuff); + printf("DPX: Bad magic number %u in \"%s\".\n", + header.fileHeader.magic_num, byteStuff); } logImageClose(dpx); return NULL; diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index e4e93d3c4da..41812872f11 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -2027,11 +2027,18 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSet if (ibuf->invalid_rect.xmin != ibuf->invalid_rect.xmax) { if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) { - IMB_partial_display_buffer_update(ibuf, ibuf->rect_float, (unsigned char *) ibuf->rect, - ibuf->x, 0, 0, applied_view_settings, display_settings, - ibuf->invalid_rect.xmin, ibuf->invalid_rect.ymin, - ibuf->invalid_rect.xmax, ibuf->invalid_rect.ymax, - false); + IMB_partial_display_buffer_update_threaded(ibuf, + ibuf->rect_float, + (unsigned char *) ibuf->rect, + ibuf->x, + 0, 0, + applied_view_settings, + display_settings, + ibuf->invalid_rect.xmin, + ibuf->invalid_rect.ymin, + ibuf->invalid_rect.xmax, + ibuf->invalid_rect.ymax, + false); } BLI_rcti_init(&ibuf->invalid_rect, 0, 0, 0, 0); @@ -2609,10 +2616,16 @@ void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *tot * the rest buffers would be marked as dirty */ -static void partial_buffer_update_rect(ImBuf *ibuf, unsigned char *display_buffer, const float *linear_buffer, - const unsigned char *byte_buffer, int display_stride, int linear_stride, - int linear_offset_x, int linear_offset_y, ColormanageProcessor *cm_processor, - const int xmin, const int ymin, const int xmax, const int ymax) +static void partial_buffer_update_rect(ImBuf *ibuf, + unsigned char *display_buffer, + const float *linear_buffer, + const unsigned char *byte_buffer, + int display_stride, + int linear_stride, + int linear_offset_x, int linear_offset_y, + ColormanageProcessor *cm_processor, + const int xmin, const int ymin, + const int xmax, const int ymax) { int x, y; int channels = ibuf->channels; @@ -2731,12 +2744,50 @@ static void partial_buffer_update_rect(ImBuf *ibuf, unsigned char *display_buffe } } -void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer, const unsigned char *byte_buffer, - int stride, int offset_x, int offset_y, - const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings, - int xmin, int ymin, int xmax, int ymax, - bool copy_display_to_byte_buffer) +typedef struct PartialThreadData { + ImBuf *ibuf; + unsigned char *display_buffer; + const float *linear_buffer; + const unsigned char *byte_buffer; + int display_stride; + int linear_stride; + int linear_offset_x, linear_offset_y; + ColormanageProcessor *cm_processor; + int xmin, ymin, xmax; +} PartialThreadData; + +static void partial_buffer_update_rect_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + PartialThreadData *data = (PartialThreadData *)data_v; + int ymin = data->ymin + start_scanline; + partial_buffer_update_rect(data->ibuf, + data->display_buffer, + data->linear_buffer, + data->byte_buffer, + data->display_stride, + data->linear_stride, + data->linear_offset_x, + data->linear_offset_y, + data->cm_processor, + data->xmin, + ymin, + data->xmax, + ymin + num_scanlines); +} + +static void imb_partial_display_buffer_update_ex(ImBuf *ibuf, + const float *linear_buffer, + const unsigned char *byte_buffer, + int stride, + int offset_x, int offset_y, + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + int xmin, int ymin, + int xmax, int ymax, + bool copy_display_to_byte_buffer, + bool do_threads) { ColormanageCacheViewSettings cache_view_settings; ColormanageCacheDisplaySettings cache_display_settings; @@ -2755,16 +2806,20 @@ void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer, BLI_lock_thread(LOCK_COLORMANAGE); - if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) - display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, &cache_handle); + if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) { + display_buffer = colormanage_cache_get(ibuf, + &cache_view_settings, + &cache_display_settings, + &cache_handle); + } - /* in some rare cases buffer's dimension could be changing directly from + /* In some rare cases buffer's dimension could be changing directly from * different thread * this i.e. happens when image editor acquires render result */ buffer_width = ibuf->x; - /* mark all other buffers as invalid */ + /* Mark all other buffers as invalid. */ memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int)); ibuf->display_buffer_flags[display_index] |= view_flag; @@ -2789,15 +2844,42 @@ void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer, * it first and byte buffer is likely to be out of date here. */ if (linear_buffer == NULL && byte_buffer != NULL) { - skip_transform = is_ibuf_rect_in_display_space(ibuf, view_settings, display_settings); + skip_transform = is_ibuf_rect_in_display_space(ibuf, + view_settings, + display_settings); } if (!skip_transform) { - cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); + cm_processor = IMB_colormanagement_display_processor_new( + view_settings, display_settings); } - partial_buffer_update_rect(ibuf, display_buffer, linear_buffer, byte_buffer, buffer_width, stride, - offset_x, offset_y, cm_processor, xmin, ymin, xmax, ymax); + if (do_threads) { + PartialThreadData data; + data.ibuf = ibuf; + data.display_buffer = display_buffer; + data.linear_buffer = linear_buffer; + data.byte_buffer = byte_buffer; + data.display_stride = buffer_width; + data.linear_stride = stride; + data.linear_offset_x = offset_x; + data.linear_offset_y = offset_y; + data.cm_processor = cm_processor; + data.xmin = xmin; + data.ymin = ymin; + data.xmax = xmax; + IMB_processor_apply_threaded_scanlines( + ymax - ymin, partial_buffer_update_rect_thread_do, &data); + } + else { + partial_buffer_update_rect(ibuf, + display_buffer, linear_buffer, byte_buffer, + buffer_width, + stride, + offset_x, offset_y, + cm_processor, + xmin, ymin, xmax, ymax); + } if (cm_processor) { IMB_colormanagement_processor_free(cm_processor); @@ -2810,11 +2892,64 @@ void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer, int y; for (y = ymin; y < ymax; y++) { size_t index = (size_t)y * buffer_width * 4; - memcpy((unsigned char *)ibuf->rect + index, display_buffer + index, (size_t)(xmax - xmin) * 4); + memcpy((unsigned char *)ibuf->rect + index, + display_buffer + index, + (size_t)(xmax - xmin) * 4); } } } +void IMB_partial_display_buffer_update(ImBuf *ibuf, + const float *linear_buffer, + const unsigned char *byte_buffer, + int stride, + int offset_x, int offset_y, + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + int xmin, int ymin, + int xmax, int ymax, + bool copy_display_to_byte_buffer) +{ + imb_partial_display_buffer_update_ex(ibuf, + linear_buffer, + byte_buffer, + stride, + offset_x, offset_y, + view_settings, + display_settings, + xmin, ymin, + xmax, ymax, + copy_display_to_byte_buffer, + false); + +} + +void IMB_partial_display_buffer_update_threaded(struct ImBuf *ibuf, + const float *linear_buffer, + const unsigned char *byte_buffer, + int stride, + int offset_x, int offset_y, + const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings, + int xmin, int ymin, int xmax, int ymax, + bool copy_display_to_byte_buffer) +{ + int width = xmax - xmin; + int height = ymax - ymin; + bool do_threads = (((size_t)width) * height >= 64 * 64); + imb_partial_display_buffer_update_ex(ibuf, + linear_buffer, + byte_buffer, + stride, + offset_x, offset_y, + view_settings, + display_settings, + xmin, ymin, + xmax, ymax, + copy_display_to_byte_buffer, + do_threads); +} + void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin, int xmax, int ymax) { if (ibuf->invalid_rect.xmin == ibuf->invalid_rect.xmax) { diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 455b78bce4d..62fd623ecfa 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -532,6 +532,76 @@ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from, } } +typedef struct FloatToFloatThreadData { + float *rect_to; + const float *rect_from; + int channels_from; + int profile_to; + int profile_from; + bool predivide; + int width; + int stride_to; + int stride_from; +} FloatToFloatThreadData; + +static void imb_buffer_float_from_float_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + FloatToFloatThreadData *data = (FloatToFloatThreadData *)data_v; + size_t offset_from = ((size_t)start_scanline) * data->stride_from * data->channels_from; + size_t offset_to = ((size_t)start_scanline) * data->stride_to * data->channels_from; + IMB_buffer_float_from_float(data->rect_to + offset_to, + data->rect_from + offset_from, + data->channels_from, + data->profile_to, + data->profile_from, + data->predivide, + data->width, + num_scanlines, + data->stride_to, + data->stride_from); +} + +void IMB_buffer_float_from_float_threaded(float *rect_to, + const float *rect_from, + int channels_from, + int profile_to, + int profile_from, + bool predivide, + int width, + int height, + int stride_to, + int stride_from) +{ + if (((size_t)width) * height < 64 * 64) { + IMB_buffer_float_from_float(rect_to, + rect_from, + channels_from, + profile_to, + profile_from, + predivide, + width, + height, + stride_to, + stride_from); + } + else { + FloatToFloatThreadData data; + data.rect_to = rect_to; + data.rect_from = rect_from; + data.channels_from = channels_from; + data.profile_to = profile_to; + data.profile_from = profile_from; + data.predivide = predivide; + data.width = width; + data.stride_to = stride_to; + data.stride_from = stride_from; + IMB_processor_apply_threaded_scanlines( + height, imb_buffer_float_from_float_thread_do, &data); + } +} + /* float to float pixels, output 4-channel RGBA */ void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, int channels_from, int width, int height, int stride_to, int stride_from, char *mask) @@ -674,25 +744,24 @@ void IMB_rect_from_float(ImBuf *ibuf) ibuf->userflags &= ~IB_RECT_INVALID; } -/* converts from linear float to sRGB byte for part of the texture, buffer will hold the changed part */ -void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w, int h, bool is_data) -{ - const float *rect_float; +typedef struct PartialThreadData { + ImBuf *ibuf; + float *buffer; uchar *rect_byte; - int profile_from = IB_PROFILE_LINEAR_RGB; - - /* verify we have a float buffer */ - if (ibuf->rect_float == NULL || buffer == NULL) - return; - - /* create byte rect if it didn't exist yet */ - if (ibuf->rect == NULL) - imb_addrectImBuf(ibuf); - - /* do conversion */ - rect_float = ibuf->rect_float + (x + ((size_t)y) * ibuf->x) * ibuf->channels; - rect_byte = (uchar *)ibuf->rect + (x + ((size_t)y) * ibuf->x) * 4; - + const float *rect_float; + int width; + bool is_data; +} PartialThreadData; + +static void partial_rect_from_float_slice(float *buffer, + uchar *rect_byte, + ImBuf *ibuf, + const float *rect_float, + const int w, + const int h, + const bool is_data) +{ + const int profile_from = IB_PROFILE_LINEAR_RGB; if (is_data) { /* exception for non-color data, just copy float */ IMB_buffer_float_from_float(buffer, rect_float, @@ -715,6 +784,58 @@ void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w 4, ibuf->dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB, 0, w, h, ibuf->x, w); } +} + +static void partial_rect_from_float_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + PartialThreadData *data = (PartialThreadData *)data_v; + ImBuf *ibuf = data->ibuf; + size_t global_offset = ((size_t)ibuf->x) * start_scanline; + size_t local_offset = ((size_t)data->width) * start_scanline; + partial_rect_from_float_slice(data->buffer + local_offset * ibuf->channels, + data->rect_byte + global_offset * 4, + ibuf, + data->rect_float + global_offset * ibuf->channels, + data->width, + num_scanlines, + data->is_data); +} + +/* converts from linear float to sRGB byte for part of the texture, buffer will hold the changed part */ +void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w, int h, bool is_data) +{ + const float *rect_float; + uchar *rect_byte; + + /* verify we have a float buffer */ + if (ibuf->rect_float == NULL || buffer == NULL) + return; + + /* create byte rect if it didn't exist yet */ + if (ibuf->rect == NULL) + imb_addrectImBuf(ibuf); + + /* do conversion */ + rect_float = ibuf->rect_float + (x + ((size_t)y) * ibuf->x) * ibuf->channels; + rect_byte = (uchar *)ibuf->rect + (x + ((size_t)y) * ibuf->x) * 4; + + if (((size_t)w) * h < 64 * 64) { + partial_rect_from_float_slice( + buffer, rect_byte, ibuf, rect_float, w, h, is_data); + } + else { + PartialThreadData data; + data.ibuf = ibuf; + data.buffer = buffer; + data.rect_byte = rect_byte; + data.rect_float = rect_float; + data.width = w; + data.is_data = is_data; + IMB_processor_apply_threaded_scanlines( + h, partial_rect_from_float_thread_do, &data); + } /* ensure user flag is reset */ ibuf->userflags &= ~IB_RECT_INVALID; diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 9e4bb957f66..0f5fd6518fb 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -374,6 +374,60 @@ void IMB_processor_apply_threaded(int buffer_lines, int handle_size, void *init_ BLI_task_pool_free(task_pool); } +typedef struct ScanlineGlobalData { + void *custom_data; + ScanlineThreadFunc do_thread; + int scanlines_per_task; + int total_scanlines; +} ScanlineGlobalData; + +typedef struct ScanlineTask { + int start_scanline; + int num_scanlines; +} ScanlineTask; + +static void processor_apply_scanline_func(TaskPool * __restrict pool, + void *taskdata, + int UNUSED(threadid)) +{ + ScanlineGlobalData *data = BLI_task_pool_userdata(pool); + int start_scanline = GET_INT_FROM_POINTER(taskdata); + int num_scanlines = min_ii(data->scanlines_per_task, + data->total_scanlines - start_scanline); + data->do_thread(data->custom_data, + start_scanline, + num_scanlines); +} + +void IMB_processor_apply_threaded_scanlines(int total_scanlines, + ScanlineThreadFunc do_thread, + void *custom_data) +{ + const int scanlines_per_task = 64; + ScanlineGlobalData data; + data.custom_data = custom_data; + data.do_thread = do_thread; + data.scanlines_per_task = scanlines_per_task; + data.total_scanlines = total_scanlines; + const int total_tasks = (total_scanlines + scanlines_per_task - 1) / scanlines_per_task; + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &data); + for (int i = 0, start_line = 0; i < total_tasks; i++) { + BLI_task_pool_push(task_pool, + processor_apply_scanline_func, + SET_INT_IN_POINTER(start_line), + false, + TASK_PRIORITY_LOW); + start_line += scanlines_per_task; + } + + /* work and wait until tasks are done */ + BLI_task_pool_work_and_wait(task_pool); + + /* Free memory. */ + BLI_task_pool_free(task_pool); +} + /* Alpha-under */ void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3]) diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index 570ca5ba06e..390f2502ee7 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -63,12 +63,12 @@ enum { DCP_CINEMA4K = 4, }; -static int check_jp2(const unsigned char *mem) /* J2K_CFMT */ +static bool check_jp2(const unsigned char *mem) /* J2K_CFMT */ { return memcmp(JP2_HEAD, mem, sizeof(JP2_HEAD)) ? 0 : 1; } -static int check_j2k(const unsigned char *mem) /* J2K_CFMT */ +static bool check_j2k(const unsigned char *mem) /* J2K_CFMT */ { return memcmp(J2K_HEAD, mem, sizeof(J2K_HEAD)) ? 0 : 1; } @@ -133,7 +133,7 @@ struct ImBuf *imb_jp2_decode(const unsigned char *mem, size_t size, int flags, c unsigned int i, i_next, w, h, planes; unsigned int y; int *r, *g, *b, *a; /* matching 'opj_image_comp.data' type */ - int is_jp2, is_j2k; + bool is_jp2, is_j2k; opj_dparameters_t parameters; /* decompression parameters */ diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 47fa4c1de0c..fdadb783815 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -38,6 +38,32 @@ #include <set> #include <errno.h> #include <algorithm> +#include <iostream> + +#include <half.h> +#include <Iex.h> +#include <ImfVersion.h> +#include <ImathBox.h> +#include <ImfArray.h> +#include <ImfIO.h> +#include <ImfChannelList.h> +#include <ImfPixelType.h> +#include <ImfInputFile.h> +#include <ImfOutputFile.h> +#include <ImfCompression.h> +#include <ImfCompressionAttribute.h> +#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> #include "DNA_scene_types.h" /* For OpenEXR compression constants */ @@ -74,33 +100,6 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #include "openexr_multi.h" } -#include <iostream> - -#include <half.h> -#include <Iex.h> -#include <ImfVersion.h> -#include <ImathBox.h> -#include <ImfArray.h> -#include <ImfIO.h> -#include <ImfChannelList.h> -#include <ImfPixelType.h> -#include <ImfInputFile.h> -#include <ImfOutputFile.h> -#include <ImfCompression.h> -#include <ImfCompressionAttribute.h> -#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> - extern "C" { #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c index c7b347cb20c..3360fd7548e 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -693,6 +693,69 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, } } +typedef struct RectBlendThreadData { + ImBuf *dbuf, *obuf, *sbuf; + unsigned short *dmask, *curvemask, *texmask; + float mask_max; + int destx, desty, origx, origy; + int srcx, srcy, width; + IMB_BlendMode mode; + bool accumulate; +} RectBlendThreadData; + +static void rectblend_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + RectBlendThreadData *data = (RectBlendThreadData *)data_v; + IMB_rectblend(data->dbuf, data->obuf, data->sbuf, + data->dmask, data->curvemask, data->texmask, + data->mask_max, + data->destx, + data->desty + start_scanline, + data->origx, + data->origy + start_scanline, + data->srcx, + data->srcy + start_scanline, + data->width, num_scanlines, + data->mode, data->accumulate); +} + +void IMB_rectblend_threaded(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, + unsigned short *dmask, unsigned short *curvemask, + unsigned short *texmask, float mask_max, + int destx, int desty, int origx, int origy, + int srcx, int srcy, int width, int height, + IMB_BlendMode mode, bool accumulate) +{ + if (((size_t)width) * height < 64 * 64) { + IMB_rectblend(dbuf, obuf, sbuf, dmask, curvemask, texmask, + mask_max, destx, desty, origx, origy, + srcx, srcy, width, height, mode, accumulate); + } + else { + RectBlendThreadData data; + data.dbuf = dbuf; + data.obuf = obuf; + data.sbuf = sbuf; + data.dmask = dmask; + data.curvemask = curvemask; + data.texmask = texmask; + data.mask_max = mask_max; + data.destx = destx; + data.desty = desty; + data.origx = origx; + data.origy = origy; + data.srcx = srcx; + data.srcy = srcy; + data.width = width; + data.mode = mode; + data.accumulate = accumulate; + IMB_processor_apply_threaded_scanlines( + height, rectblend_thread_do, &data); + } +} + /* fill */ void IMB_rectfill(ImBuf *drect, const float col[4]) diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c index 19edce3da3b..a55cef60943 100644 --- a/source/blender/imbuf/intern/stereoimbuf.c +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -719,6 +719,9 @@ ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *i 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)); + ibuf_stereo->rect_colorspace = ibuf_left->rect_colorspace; + ibuf_stereo->float_colorspace = ibuf_left->float_colorspace; + /* copy flags for IB_fields and other settings */ ibuf_stereo->flags = ibuf_left->flags; diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 96d7ec3128c..4d82e4528d6 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -199,7 +199,8 @@ typedef struct bPoseChannel { char constflag; /* for quick detecting which constraints affect this channel */ char selectflag; /* copy of bone flag, so you can work with library armatures, not for runtime use */ char drawflag; - char pad0[5]; + char bboneflag; + char pad0[4]; struct Bone *bone; /* set on read file or rebuild pose */ struct bPoseChannel *parent; /* set on read file or rebuild pose */ @@ -242,7 +243,16 @@ typedef struct bPoseChannel { float ikstretch; float ikrotweight; /* weight of joint rotation constraint */ float iklinweight; /* weight of joint stretch constraint */ - + + /* curved bones settings - these are for animating, and are applied on top of the copies in pchan->bone */ + float roll1, roll2; + float curveInX, curveInY; + float curveOutX, curveOutY; + float scaleIn, scaleOut; + + struct bPoseChannel *bbone_prev; /* next/prev bones to use as handle references when calculating bbones (optional) */ + struct bPoseChannel *bbone_next; + void *temp; /* use for outliner */ } bPoseChannel; @@ -253,17 +263,17 @@ typedef enum ePchan_Flag { POSE_LOC = (1 << 0), POSE_ROT = (1 << 1), POSE_SIZE = (1 << 2), - /* old IK/cache stuff... */ -#if 0 - POSE_IK_MAT = (1 << 3), - POSE_UNUSED2 = (1 << 4), - POSE_UNUSED3 = (1 << 5), - POSE_UNUSED4 = (1 << 6), - POSE_UNUSED5 = (1 << 7), - /* has Standard IK */ - POSE_HAS_IK = (1 << 8), -#endif - /* IK/Pose solving*/ + + /* old IK/cache stuff + * - used to be here from (1 << 3) to (1 << 8) + * but has been repurposed since 2.77.2 + * as they haven't been used in over 10 years + */ + + /* has BBone deforms */ + POSE_BBONE_SHAPE = (1 << 3), + + /* IK/Pose solving */ POSE_CHAIN = (1 << 9), POSE_DONE = (1 << 10), /* visualization */ @@ -318,6 +328,16 @@ typedef enum ePchan_DrawFlag { #define PCHAN_CUSTOM_DRAW_SIZE(pchan) \ (pchan)->custom_scale * (((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length) +/* PoseChannel->bboneflag */ +typedef enum ePchan_BBoneFlag { + /* Use custom reference bones (for roll and handle alignment), instead of immediate neighbours */ + PCHAN_BBONE_CUSTOM_HANDLES = (1 << 1), + /* Evaluate start handle as being "relative" */ + PCHAN_BBONE_CUSTOM_START_REL = (1 << 2), + /* Evaluate end handle as being "relative" */ + PCHAN_BBONE_CUSTOM_END_REL = (1 << 3), +} ePchan_BBoneFlag; + /* PoseChannel->rotmode and Object->rotmode */ typedef enum eRotationModes { /* quaternion rotations (default, and for older Blender versions) */ diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index b995e6917a9..cda6441f0ae 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -68,11 +68,18 @@ typedef struct Bone { float xwidth, length, zwidth; /* width: for block bones. keep in this order, transform! */ float ease1, ease2; /* length of bezier handles */ float rad_head, rad_tail; /* radius for head/tail sphere, defining deform as well, parent->rad_tip overrides rad_head */ - + + float roll1, roll2; /* curved bones settings - these define the "restpose" for a curved bone */ + float curveInX, curveInY; + float curveOutX, curveOutY; + float scaleIn, scaleOut; + float size[3]; /* patch for upward compat, UNUSED! */ int layer; /* layers that bone appears on */ short segments; /* for B-bones */ - short pad[1]; + + short pad1; + } Bone; typedef struct bArmature { @@ -204,7 +211,8 @@ typedef enum eBone_Flag { BONE_TRANSFORM_CHILD = (1 << 20), /* Indicates that a parent is also being transformed */ BONE_UNSELECTABLE = (1 << 21), /* bone cannot be selected */ BONE_NO_LOCAL_LOCATION = (1 << 22), /* bone location is in armature space */ - BONE_RELATIVE_PARENTING = (1 << 23) /* object child will use relative transform (like deform) */ + BONE_RELATIVE_PARENTING = (1 << 23), /* object child will use relative transform (like deform) */ + BONE_ADD_PARENT_END_ROLL = (1 << 24) /* it will add the parent end roll to the inroll */ } eBone_Flag; diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index 7f2e1aaadf9..68741578f27 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -53,6 +53,10 @@ typedef struct CameraStereoSettings { short pivot; short flag; short pad; + /* Cut-off angle at which interocular distance start to fade down. */ + float pole_merge_angle_from; + /* Cut-off angle at which interocular distance stops to fade down. */ + float pole_merge_angle_to; } CameraStereoSettings; typedef struct Camera { @@ -152,6 +156,7 @@ enum { /* stereo->flag */ enum { CAM_S3D_SPHERICAL = (1 << 0), + CAM_S3D_POLE_MERGE = (1 << 1), }; #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 86991245068..5fcd374b21f 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -516,7 +516,9 @@ typedef enum eBConstraint_Flags { /* indicates that constraint was added locally (i.e. didn't come from the proxy-lib) */ CONSTRAINT_PROXY_LOCAL = (1<<8), /* indicates that constraint is temporarily disabled (only used in GE) */ - CONSTRAINT_OFF = (1<<9) + CONSTRAINT_OFF = (1<<9), + /* use bbone curve shape when calculating headtail values */ + CONSTRAINT_BBONE_SHAPE = (1<<10), } eBConstraint_Flags; /* bConstraint->ownspace/tarspace */ diff --git a/source/blender/makesdna/DNA_fileglobal_types.h b/source/blender/makesdna/DNA_fileglobal_types.h index fc7959c0043..b2ab0d2a08d 100644 --- a/source/blender/makesdna/DNA_fileglobal_types.h +++ b/source/blender/makesdna/DNA_fileglobal_types.h @@ -58,7 +58,7 @@ typedef struct FileGlobal { /* minversion: in file, the oldest past blender version you can use compliant */ /* example: if in 2.43 the meshes lose mesh data, minversion is 2.43 then too */ /* or: in 2.42, subversion 1, same as above, minversion then is 2.42, min subversion 1 */ -/* (defines for version are in the BKE_blender.h file, for historic reasons) */ +/* (defines for version are in the BKE_blender_version.h file, for historic reasons) */ #endif diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index e5e193d479b..41f53f9f51c 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -57,6 +57,14 @@ typedef enum eGPDspoint_Flag { GP_SPOINT_TAG = (1 << 1), } eGPSPoint_Flag; +/* Grease-Pencil Annotations - 'Triangle' + * -> A triangle contains the index of three vertices for filling the stroke + * This is only used if high quality fill is enabled + */ +typedef struct bGPDtriangle { + int v1, v2, v3; /* indices for tesselated triangle used for GP Fill */ +} bGPDtriangle; + /* Grease-Pencil Annotations - 'Stroke' * -> A stroke represents a (simplified version) of the curve * drawn by the user in one 'mousedown'->'mouseup' operation @@ -69,6 +77,10 @@ typedef struct bGPDstroke { short thickness; /* thickness of stroke (currently not used) */ short flag; /* various settings about this stroke */ + + bGPDtriangle *triangles;/* tesselated triangles for GP Fill */ + int tot_triangles; /* number of triangles in array */ + int pad1, *pad2; double inittime; /* Init time of stroke */ } bGPDstroke; @@ -83,6 +95,8 @@ typedef enum eGPDstroke_Flag { GP_STROKE_2DIMAGE = (1 << 2), /* stroke is selected */ GP_STROKE_SELECT = (1 << 3), + /* Recalculate triangulation for high quality fill (when true, force a new recalc) */ + GP_STROKE_RECALC_CACHES = (1 << 4), /* only for use with stroke-buffer (while drawing eraser) */ GP_STROKE_ERASER = (1 << 15) } eGPDstroke_Flag; @@ -132,8 +146,9 @@ typedef struct bGPDlayer { * this is used for the name of the layer too and kept unique. */ float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */ + short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */ short sublevel; /* number of times to subdivide new strokes */ - short pad[5]; /* padding for compiler error */ + short pad[4]; /* padding for compiler error */ } bGPDlayer; /* bGPDlayer->flag */ @@ -160,6 +175,8 @@ typedef enum eGPDlayer_Flag { GP_LAYER_GHOST_NEXTCOL = (1 << 9), /* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */ GP_LAYER_VOLUMETRIC = (1 << 10), + /* Use high quality fill (instead of buggy legacy OpenGL Fill) */ + GP_LAYER_HQ_FILL = (1 << 11) } eGPDlayer_Flag; /* Grease-Pencil Annotations - 'DataBlock' */ diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 8790f736600..0c500e366a7 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -197,6 +197,10 @@ typedef struct Material { short tot_slots; short pad4[3]; + /* multiple tangent (Normal Map node) */ + char nmap_tangent_names[9][64]; /* [MAX_MTFACE+1][MAX_NAME]; +1 for empty name */ + int nmap_tangent_names_count, pad5; + struct TexPaintSlot *texpaintslot; /* cached slot for painting. Make sure to recalculate before use * with refresh_texpaint_image_cache */ ListBase gpumaterial; /* runtime */ @@ -305,6 +309,7 @@ typedef struct Material { /* mode2 (is int) */ #define MA_CASTSHADOW (1 << 0) #define MA_MODE2_PIPELINE (MA_CASTSHADOW) +#define MA_TANGENT_CONCRETE (1 << 1) /* mapflag */ #define MA_MAPFLAG_UVPROJECT (1 << 0) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index c75a019a28e..6cba3322135 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -689,6 +689,8 @@ typedef struct NodeColorBalance { float slope[3]; float offset[3]; float power[3]; + float offset_basis; + char _pad[4]; /* LGG parameters */ float lift[3]; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index a8f78f6bb34..39508e17be5 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1266,6 +1266,41 @@ typedef enum { UNIFIED_PAINT_BRUSH_ALPHA_PRESSURE = (1 << 4) } UnifiedPaintSettingsFlags; + +typedef struct CurvePaintSettings { + char curve_type; + char flag; + char depth_mode; + char surface_plane; + int error_threshold; + float radius_min, radius_max; + float radius_taper_start, radius_taper_end; + float surface_offset; + float corner_angle; +} CurvePaintSettings; + +/* CurvePaintSettings.flag */ +enum { + CURVE_PAINT_FLAG_CORNERS_DETECT = (1 << 0), + CURVE_PAINT_FLAG_PRESSURE_RADIUS = (1 << 1), + CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS = (1 << 2), + CURVE_PAINT_FLAG_DEPTH_STROKE_OFFSET_ABS = (1 << 3), +}; + +/* CurvePaintSettings.depth_mode */ +enum { + CURVE_PAINT_PROJECT_CURSOR = 0, + CURVE_PAINT_PROJECT_SURFACE = 1, +}; + +/* CurvePaintSettings.surface_plane */ +enum { + CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW = 0, + CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE = 1, + CURVE_PAINT_SURFACE_PLANE_VIEW = 2, +}; + + /* *************************************************************** */ /* Stats */ @@ -1417,6 +1452,8 @@ typedef struct ToolSettings { /* Unified Paint Settings */ struct UnifiedPaintSettings unified_paint_settings; + struct CurvePaintSettings curve_paint_settings; + struct MeshStatVis statvis; } ToolSettings; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 3a64890a84b..1f4e4df4660 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -274,6 +274,7 @@ typedef struct GaussianBlurVars { typedef struct TextVars { char text[512]; int text_size; + float color[4], shadow_color[4]; float loc[2]; float wrap_width; char flag; diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 651794da50d..fc94a8d9ff4 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -720,6 +720,28 @@ static int arraysize(const char *str) return mul; } +static bool check_field_alignment(int firststruct, int structtype, int type, int len, + const char *name, const char *detail) +{ + bool result = true; + if (type < firststruct && typelens_native[type] > 4 && (len % 8)) { + fprintf(stderr, "Align 8 error (%s) in struct: %s %s (add %d padding bytes)\n", + detail, types[structtype], name, len % 8); + result = false; + } + if (typelens_native[type] > 3 && (len % 4) ) { + fprintf(stderr, "Align 4 error (%s) in struct: %s %s (add %d padding bytes)\n", + detail, types[structtype], name, len % 4); + result = false; + } + if (typelens_native[type] == 2 && (len % 2) ) { + fprintf(stderr, "Align 2 error (%s) in struct: %s %s (add %d padding bytes)\n", + detail, types[structtype], name, len % 2); + result = false; + } + return result; +} + static int calculate_structlens(int firststruct) { int unknown = nr_structs, lastunknown; @@ -815,20 +837,11 @@ static int calculate_structlens(int firststruct) } } - /* 2-4-8 aligned/ */ - if (type < firststruct && typelens_native[type] > 4 && (len_native % 8)) { - fprintf(stderr, "Align 8 error in struct: %s %s (add %d padding bytes)\n", - types[structtype], cp, len_native % 8); - dna_error = 1; - } - if (typelens_native[type] > 3 && (len_native % 4) ) { - fprintf(stderr, "Align 4 error in struct: %s %s (add %d padding bytes)\n", - types[structtype], cp, len_native % 4); + /* Check 2-4-8 aligned. */ + if (!check_field_alignment(firststruct, structtype, type, len_32, cp, "32 bit")) { dna_error = 1; } - else if (typelens_native[type] == 2 && (len_native % 2) ) { - fprintf(stderr, "Align 2 error in struct: %s %s (add %d padding bytes)\n", - types[structtype], cp, len_native % 2); + if (!check_field_alignment(firststruct, structtype, type, len_64, cp, "64 bit")) { dna_error = 1; } diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 04a2361b903..d8bea93bcbc 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -301,11 +301,25 @@ typedef struct RawArray { int stride; } RawArray; +/** + * This struct is are typically defined in arrays which define an *enum* for RNA, + * which is used by the RNA API both for user-interface and the Python API. + */ typedef struct EnumPropertyItem { + /** The internal value of the enum, not exposed to users. */ int value; + /** + * Note that identifiers must be unique within the array, + * by convention they're upper case with underscores for separators. + * - An empty string is used to define menu separators. + * - NULL denotes the end of the array of items. + */ const char *identifier; + /** Optional icon, typically 'ICON_NONE' */ int icon; + /** Name displayed in the interface. */ const char *name; + /** Longer description used in the interface. */ const char *description; } EnumPropertyItem; diff --git a/source/blender/makesrna/intern/rna_actuator.c b/source/blender/makesrna/intern/rna_actuator.c index b425a454d33..a09853eaddc 100644 --- a/source/blender/makesrna/intern/rna_actuator.c +++ b/source/blender/makesrna/intern/rna_actuator.c @@ -333,7 +333,7 @@ static void rna_Actuator_constraint_detect_material_set(struct PointerRNA *ptr, bActuator *act = (bActuator *)ptr->data; bConstraintActuator *ca = act->data; - short old_value = (ca->flag & ACT_CONST_MATERIAL ? 1 : 0); + short old_value = (ca->flag & ACT_CONST_MATERIAL) ? 1 : 0; if (old_value != value) { ca->flag ^= ACT_CONST_MATERIAL; diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index a8ef4664fd7..842e220e8b5 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -482,6 +482,82 @@ static void rna_Armature_transform(struct bArmature *arm, float *mat) #else +/* Settings for curved bbone settings - The posemode values get applied over the top of the editmode ones */ +void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone) +{ +#define RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone) \ + { \ + if (is_posebone) \ + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); \ + else \ + RNA_def_property_update(prop, 0, "rna_Armature_update_data"); \ + } (void)0; + + PropertyRNA *prop; + + /* Roll In/Out */ + prop = RNA_def_property(srna, "bbone_rollin", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "roll1"); + RNA_def_property_range(prop, -M_PI * 2.0f, M_PI * 2.0f); + RNA_def_property_ui_text(prop, "Roll In", "Roll offset for the start of the B-Bone, adjusts twist"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_rollout", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "roll2"); + RNA_def_property_range(prop, -M_PI * 2.0f, M_PI * 2.0f); + RNA_def_property_ui_text(prop, "Roll Out", "Roll offset for the end of the B-Bone, adjusts twist"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + if (is_posebone == false) { + prop = RNA_def_property(srna, "use_endroll_as_inroll", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Inherit End Roll", "Use Roll Out of parent bone as Roll In of its children"); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_ADD_PARENT_END_ROLL); + RNA_def_property_update(prop, 0, "rna_Armature_update_data"); + } + + /* Curve X/Y Offsets */ + prop = RNA_def_property(srna, "bbone_curveinx", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curveInX"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_ui_text(prop, "In X", "X-axis handle offset for start of the B-Bone's curve, adjusts curvature"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_curveiny", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curveInY"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_ui_text(prop, "In Y", "Y-axis handle offset for start of the B-Bone's curve, adjusts curvature"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_curveoutx", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curveOutX"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_ui_text(prop, "Out X", "X-axis handle offset for end of the B-Bone's curve, adjusts curvature"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_curveouty", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curveOutY"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_ui_text(prop, "Out Y", "Y-axis handle offset for end of the B-Bone's curve, adjusts curvature"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + /* Scale In/Out */ + prop = RNA_def_property(srna, "bbone_scalein", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "scaleIn"); + RNA_def_property_range(prop, 0.0f, 5.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Scale In", "Scale factor for start of the B-Bone, adjusts thickness (for tapering effects)"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_scaleout", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "scaleOut"); + RNA_def_property_range(prop, 0.0f, 5.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Scale Out", "Scale factor for end of the B-Bone, adjusts thickness (for tapering effects)"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + +#undef RNA_DEF_CURVEBONE_UPDATE +} + static void rna_def_bone_common(StructRNA *srna, int editbone) { PropertyRNA *prop; @@ -653,6 +729,7 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Children", "Bones which are children of this bone"); rna_def_bone_common(srna, 0); + rna_def_bone_curved_common(srna, 0); /* XXX should we define this in PoseChannel wrapping code instead? * But PoseChannels directly get some of their flags from here... */ @@ -766,6 +843,7 @@ static void rna_def_edit_bone(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Armature_editbone_transform_update"); rna_def_bone_common(srna, 1); + rna_def_bone_curved_common(srna, 0); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_HIDDEN_A); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index e3733ff2850..ac348c1750c 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -530,10 +530,12 @@ static EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, PointerRNA *ptr, switch ((BrushMaskTool)me->mask_tool) { case BRUSH_MASK_DRAW: return prop_direction_items; - break; + case BRUSH_MASK_SMOOTH: return prop_default_items; - break; + + default: + return prop_default_items; } case SCULPT_TOOL_FLATTEN: @@ -554,7 +556,6 @@ static EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, PointerRNA *ptr, default: return prop_default_items; } - break; case ePaintTexture2D: case ePaintTextureProjective: @@ -565,7 +566,6 @@ static EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, PointerRNA *ptr, default: return prop_default_items; } - break; default: return prop_default_items; @@ -653,34 +653,34 @@ static void rna_def_brush_texture_slot(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "brush_map_mode"); RNA_def_property_enum_items(prop, prop_map_mode_items); RNA_def_property_ui_text(prop, "Mode", ""); - RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update"); + RNA_def_property_update(prop, 0, "rna_TextureSlot_update"); prop = RNA_def_property(srna, "tex_paint_map_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "brush_map_mode"); RNA_def_property_enum_items(prop, prop_tex_paint_map_mode_items); RNA_def_property_ui_text(prop, "Mode", ""); - RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update"); + RNA_def_property_update(prop, 0, "rna_TextureSlot_update"); prop = RNA_def_property(srna, "mask_map_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "brush_map_mode"); RNA_def_property_enum_items(prop, prop_mask_paint_map_mode_items); RNA_def_property_ui_text(prop, "Mode", ""); - RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update"); + RNA_def_property_update(prop, 0, "rna_TextureSlot_update"); prop = RNA_def_property(srna, "use_rake", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "brush_angle_mode", MTEX_ANGLE_RAKE); RNA_def_property_ui_text(prop, "Rake", ""); - RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update"); + RNA_def_property_update(prop, 0, "rna_TextureSlot_update"); prop = RNA_def_property(srna, "use_random", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "brush_angle_mode", MTEX_ANGLE_RANDOM); RNA_def_property_ui_text(prop, "Random", ""); - RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update"); + RNA_def_property_update(prop, 0, "rna_TextureSlot_update"); prop = RNA_def_property(srna, "random_angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_range(prop, 0, M_PI * 2); RNA_def_property_ui_text(prop, "Random Angle", "Brush texture random angle"); - RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update"); + RNA_def_property_update(prop, 0, "rna_TextureSlot_update"); TEXTURE_CAPABILITY(has_texture_angle_source, "Has Texture Angle Source"); TEXTURE_CAPABILITY(has_random_texture_angle, "Has Random Texture Angle"); diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index d5044ec9838..dcb4d65f3f9 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -132,14 +132,14 @@ static void rna_def_camera_stereo_data(BlenderRNA *brna) 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_range(prop, 0.0f, 1.f, 1, 3); 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.00001f, 15.f, 1, 2); + RNA_def_property_ui_range(prop, 0.00001f, 15.f, 1, 3); 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)"); @@ -151,6 +151,24 @@ static void rna_def_camera_stereo_data(BlenderRNA *brna) "Render every pixel rotating the camera around the " "middle of the interocular distance"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "use_pole_merge", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_S3D_POLE_MERGE); + RNA_def_property_ui_text(prop, "Use Pole Merge", + "Fade interocular distance to 0 after the given cutoff angle"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "pole_merge_angle_from", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0.0f, M_PI / 2.0f); + RNA_def_property_ui_text(prop, "Pole Merge Start Angle", + "Angle at which interocular distance starts to fade to 0"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "pole_merge_angle_to", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0.0f, M_PI / 2.0f); + RNA_def_property_ui_text(prop, "Pole Merge End Angle", + "Angle at which interocular distance is 0"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); } void RNA_def_camera(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c index b75a3f0320b..c91b6487653 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -535,6 +535,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flags", CLOTH_SIMSETTINGS_FLAG_SEW); RNA_def_property_ui_text(prop, "Sew Cloth", "Pulls loose edges together"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_cloth_update"); prop = RNA_def_property(srna, "vertex_group_bending", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs(prop, "rna_ClothSettings_bend_vgroup_get", "rna_ClothSettings_bend_vgroup_length", diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 0b5d0f3d41d..98560bf3452 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -483,6 +483,21 @@ static EnumPropertyItem constraint_distance_items[] = { }; +static void rna_def_constraint_headtail_common(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); + RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + + prop = RNA_def_property(srna, "use_bbone_shape", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, "bConstraint", "flag", CONSTRAINT_BBONE_SHAPE); + RNA_def_property_ui_text(prop, "Follow B-Bone", "Follow shape of B-Bone segments when calculating Head/Tail position"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); +} + static void rna_def_constrainttarget(BlenderRNA *brna) { StructRNA *srna; @@ -787,10 +802,7 @@ static void rna_def_constraint_track_to(BlenderRNA *brna) srna = RNA_def_struct(brna, "TrackToConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Track To Constraint", "Aim the constrained object toward the target"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bTrackToConstraint", "data"); @@ -831,10 +843,7 @@ static void rna_def_constraint_locate_like(BlenderRNA *brna) srna = RNA_def_struct(brna, "CopyLocationConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Copy Location Constraint", "Copy the location of the target"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bLocateLikeConstraint", "data"); @@ -1022,10 +1031,7 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna) srna = RNA_def_struct(brna, "CopyTransformsConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Copy Transforms Constraint", "Copy all the transforms of the target"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bTransLikeConstraint", "data"); @@ -1200,10 +1206,7 @@ static void rna_def_constraint_locked_track(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Locked Track Constraint", "Point toward the target along the track axis, while locking the other axis"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bLockTrackConstraint", "data"); @@ -1327,10 +1330,7 @@ static void rna_def_constraint_stretch_to(BlenderRNA *brna) srna = RNA_def_struct(brna, "StretchToConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Stretch To Constraint", "Stretch to meet the target object"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bStretchToConstraint", "data"); @@ -2122,10 +2122,7 @@ static void rna_def_constraint_distance_limit(BlenderRNA *brna) srna = RNA_def_struct(brna, "LimitDistanceConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Limit Distance Constraint", "Limit the distance from target object"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bDistLimitConstraint", "data"); @@ -2236,10 +2233,7 @@ static void rna_def_constraint_damped_track(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Damped Track Constraint", "Point toward target by taking the shortest rotation path"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); +rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bDampTrackConstraint", "data"); @@ -2396,10 +2390,7 @@ static void rna_def_constraint_pivot(BlenderRNA *brna) srna = RNA_def_struct(brna, "PivotConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Pivot Constraint", "Rotate around a different point"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bPivotConstraint", "data"); diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index b765ca10eec..1487dfa074e 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -61,7 +61,7 @@ EnumPropertyItem rna_enum_fmodifier_type_items[] = { {FMODIFIER_TYPE_NOISE, "NOISE", 0, "Noise", "Add pseudo-random noise on top of F-Curves"}, /*{FMODIFIER_TYPE_FILTER, "FILTER", 0, "Filter", ""},*/ /* FIXME: not implemented yet! */ - {FMODIFIER_TYPE_PYTHON, "PYTHON", 0, "Python", ""}, + /*{FMODIFIER_TYPE_PYTHON, "PYTHON", 0, "Python", ""},*/ /* FIXME: not implemented yet! */ {FMODIFIER_TYPE_LIMITS, "LIMITS", 0, "Limits", "Restrict maximum and minimum values of F-Curve"}, {FMODIFIER_TYPE_STEPPED, "STEPPED", 0, "Stepped Interpolation", @@ -300,7 +300,7 @@ static void rna_Driver_remove_variable(ChannelDriver *driver, ReportList *report return; } - driver_free_variable(driver, dvar); + driver_free_variable_ex(driver, dvar); RNA_POINTER_INVALIDATE(dvar_ptr); } @@ -580,7 +580,17 @@ static void rna_FModifier_blending_range(PointerRNA *ptr, float *min, float *max *max = fcm->efra - fcm->sfra; } -static void rna_FModifier_verify_data_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_FModifier_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->id.data; + AnimData *adt = BKE_animdata_from_id(id); + DAG_id_tag_update(id, (GS(id->name) == ID_OB) ? OB_RECALC_OB : OB_RECALC_DATA); + if (adt != NULL) { + adt->recalc |= ADT_RECALC_ANIM; + } +} + +static void rna_FModifier_verify_data_update(Main *bmain, Scene *scene, PointerRNA *ptr) { FModifier *fcm = (FModifier *)ptr->data; const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); @@ -588,9 +598,11 @@ static void rna_FModifier_verify_data_update(Main *UNUSED(bmain), Scene *UNUSED( /* call the verify callback on the modifier if applicable */ if (fmi && fmi->verify_data) fmi->verify_data(fcm); + + rna_FModifier_update(bmain, scene, ptr); } -static void rna_FModifier_active_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_FModifier_active_update(Main *bmain, Scene *scene, PointerRNA *ptr) { FModifier *fm, *fmo = (FModifier *)ptr->data; @@ -601,7 +613,8 @@ static void rna_FModifier_active_update(Main *UNUSED(bmain), Scene *UNUSED(scene for (fm = fmo->next; fm; fm = fm->next) { fm->flag &= ~FMODIFIER_FLAG_ACTIVE; } - + + rna_FModifier_update(bmain, scene, ptr); } static int rna_FModifierGenerator_coefficients_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) @@ -885,7 +898,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Additive", "Values generated by this modifier are applied on top of " "the existing values instead of overwriting them"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, generator_mode_items); @@ -933,19 +946,19 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna) /* coefficients */ prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); RNA_def_property_ui_text(prop, "Amplitude", "Scale factor determining the maximum/minimum values"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "phase_multiplier", PROP_FLOAT, PROP_NONE); RNA_def_property_ui_text(prop, "Phase Multiplier", "Scale factor determining the 'speed' of the function"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "phase_offset", PROP_FLOAT, PROP_NONE); RNA_def_property_ui_text(prop, "Phase Offset", "Constant factor to offset time by for function"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "value_offset", PROP_FLOAT, PROP_NONE); RNA_def_property_ui_text(prop, "Value Offset", "Constant factor to offset values by"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); /* flags */ prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE); @@ -953,13 +966,13 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Additive", "Values generated by this modifier are applied on top of " "the existing values instead of overwriting them"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "function_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "Type", "Type of built-in function to use"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); } /* --------- */ @@ -980,18 +993,18 @@ static void rna_def_fmodifier_envelope_ctrl(BlenderRNA *brna) prop = RNA_def_property(srna, "min", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "min"); RNA_def_property_ui_text(prop, "Minimum Value", "Lower bound of envelope at this control-point"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "max", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "max"); RNA_def_property_ui_text(prop, "Maximum Value", "Upper bound of envelope at this control-point"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); /* Frame */ prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "time"); RNA_def_property_ui_text(prop, "Frame", "Frame this control-point occurs on"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); /* TODO: */ /* - selection flags (not implemented in UI yet though) */ @@ -1047,17 +1060,17 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna) prop = RNA_def_property(srna, "reference_value", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "midval"); RNA_def_property_ui_text(prop, "Reference Value", "Value that envelope's influence is centered around / based on"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "default_min", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "min"); RNA_def_property_ui_text(prop, "Default Minimum", "Lower distance from Reference Value for 1:1 default influence"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "default_max", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "max"); RNA_def_property_ui_text(prop, "Default Maximum", "Upper distance from Reference Value for 1:1 default influence"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); } /* --------- */ @@ -1087,26 +1100,26 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "before_mode"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "Before Mode", "Cycling mode to use before first keyframe"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "cycles_before", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "before_cycles"); RNA_def_property_ui_text(prop, "Before Cycles", "Maximum number of cycles to allow before first keyframe (0 = infinite)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); /* after */ prop = RNA_def_property(srna, "mode_after", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "after_mode"); RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "After Mode", "Cycling mode to use after last keyframe"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "cycles_after", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "after_cycles"); RNA_def_property_ui_text(prop, "After Cycles", "Maximum number of cycles to allow after last keyframe (0 = infinite)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); } /* --------- */ @@ -1135,46 +1148,46 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna) prop = RNA_def_property(srna, "use_min_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMIN); RNA_def_property_ui_text(prop, "Minimum X", "Use the minimum X value"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_min_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMIN); RNA_def_property_ui_text(prop, "Minimum Y", "Use the minimum Y value"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_max_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMAX); RNA_def_property_ui_text(prop, "Maximum X", "Use the maximum X value"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_max_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMAX); RNA_def_property_ui_text(prop, "Maximum Y", "Use the maximum Y value"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "min_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.xmin"); RNA_def_property_float_funcs(prop, NULL, "rna_FModifierLimits_minx_set", "rna_FModifierLimits_minx_range"); RNA_def_property_ui_text(prop, "Minimum X", "Lowest X value to allow"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "min_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.ymin"); RNA_def_property_float_funcs(prop, NULL, "rna_FModifierLimits_miny_set", "rna_FModifierLimits_miny_range"); RNA_def_property_ui_text(prop, "Minimum Y", "Lowest Y value to allow"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "max_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.xmax"); RNA_def_property_float_funcs(prop, NULL, "rna_FModifierLimits_maxx_set", "rna_FModifierLimits_maxx_range"); RNA_def_property_ui_text(prop, "Maximum X", "Highest X value to allow"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "max_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rect.ymax"); RNA_def_property_float_funcs(prop, NULL, "rna_FModifierLimits_maxy_set", "rna_FModifierLimits_maxy_range"); RNA_def_property_ui_text(prop, "Maximum Y", "Highest Y value to allow"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); } /* --------- */ @@ -1200,33 +1213,33 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "modification"); RNA_def_property_enum_items(prop, prop_modification_items); RNA_def_property_ui_text(prop, "Blend Type", "Method of modifying the existing F-Curve"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "size"); RNA_def_property_ui_text(prop, "Scale", "Scaling (in time) of the noise"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "strength"); RNA_def_property_ui_text(prop, "Strength", "Amplitude of the noise - the amount that it modifies the underlying curve"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "phase"); RNA_def_property_ui_text(prop, "Phase", "A random seed for the noise effect"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "offset"); RNA_def_property_ui_text(prop, "Offset", "Time offset for the noise effect"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "depth", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "depth"); RNA_def_property_ui_text(prop, "Depth", "Amount of fine level detail present in the noise"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); } @@ -1247,36 +1260,36 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna) prop = RNA_def_property(srna, "frame_step", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "step_size"); RNA_def_property_ui_text(prop, "Step Size", "Number of frames to hold each value"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "frame_offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "offset"); RNA_def_property_ui_text(prop, "Offset", "Reference number of frames before frames get held " "(use to get hold for '1-3' vs '5-7' holding patterns)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_frame_start", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_BEFORE); RNA_def_property_ui_text(prop, "Use Start Frame", "Restrict modifier to only act after its 'start' frame"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "use_frame_end", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_AFTER); RNA_def_property_ui_text(prop, "Use End Frame", "Restrict modifier to only act before its 'end' frame"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "start_frame"); RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifierStepped_start_frame_range"); RNA_def_property_ui_text(prop, "Start Frame", "Frame that modifier's influence starts (if applicable)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "end_frame"); RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifierStepped_end_frame_range"); RNA_def_property_ui_text(prop, "End Frame", "Frame that modifier's influence ends (if applicable)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update"); } /* --------- */ @@ -1314,14 +1327,14 @@ static void rna_def_fmodifier(BlenderRNA *brna) prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_MUTED); RNA_def_property_ui_text(prop, "Muted", "F-Curve Modifier will not be evaluated"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); RNA_def_property_ui_icon(prop, ICON_MUTE_IPO_OFF, 1); prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FMODIFIER_FLAG_DISABLED); RNA_def_property_ui_text(prop, "Disabled", "F-Curve Modifier has invalid settings and will not be evaluated"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); /* TODO: setting this to true must ensure that all others in stack are turned off too... */ prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); @@ -1337,7 +1350,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Restrict Frame Range", "F-Curve Modifier is only applied for the specified frame range to help " "mask off effects in order to chain them"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1); /* XXX: depends on UI implementation */ prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE); @@ -1345,32 +1358,32 @@ static void rna_def_fmodifier(BlenderRNA *brna) RNA_def_property_float_funcs(prop, NULL, "rna_FModifier_start_frame_set", "rna_FModifier_start_frame_range"); RNA_def_property_ui_text(prop, "Start Frame", "Frame that modifier's influence starts (if Restrict Frame Range is in use)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "efra"); RNA_def_property_float_funcs(prop, NULL, "rna_FModifer_end_frame_set", "rna_FModifier_end_frame_range"); RNA_def_property_ui_text(prop, "End Frame", "Frame that modifier's influence ends (if Restrict Frame Range is in use)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendin"); RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range"); RNA_def_property_ui_text(prop, "Blend In", "Number of frames from start frame for influence to take effect"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); prop = RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendout"); RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range"); RNA_def_property_ui_text(prop, "Blend Out", "Number of frames from end frame for influence to fade out"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); /* influence */ prop = RNA_def_property(srna, "use_influence", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_USEINFLUENCE); RNA_def_property_ui_text(prop, "Use Influence", "F-Curve Modifier's effects will be tempered by a default factor"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1); /* XXX: depends on UI implementation */ prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR); @@ -1379,7 +1392,7 @@ static void rna_def_fmodifier(BlenderRNA *brna) RNA_def_property_float_default(prop, 1.0f); RNA_def_property_ui_text(prop, "Influence", "Amount of influence F-Curve Modifier will have when not fading in/out"); - RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update"); } /* *********************** */ @@ -1467,11 +1480,11 @@ static void rna_def_drivervar(BlenderRNA *brna) PropertyRNA *prop; static EnumPropertyItem prop_type_items[] = { - {DVAR_TYPE_SINGLE_PROP, "SINGLE_PROP", 0, "Single Property", "Use the value from some RNA property (Default)"}, - {DVAR_TYPE_TRANSFORM_CHAN, "TRANSFORMS", 0, "Transform Channel", + {DVAR_TYPE_SINGLE_PROP, "SINGLE_PROP", ICON_RNA, "Single Property", "Use the value from some RNA property (Default)"}, + {DVAR_TYPE_TRANSFORM_CHAN, "TRANSFORMS", ICON_MANIPUL, "Transform Channel", "Final transformation value of object or bone"}, - {DVAR_TYPE_ROT_DIFF, "ROTATION_DIFF", 0, "Rotational Difference", "Use the angle between two bones"}, - {DVAR_TYPE_LOC_DIFF, "LOC_DIFF", 0, "Distance", "Distance between two bones or objects"}, + {DVAR_TYPE_ROT_DIFF, "ROTATION_DIFF", ICON_PARTICLE_TIP, "Rotational Difference", "Use the angle between two bones"}, /* XXX: Icon... */ + {DVAR_TYPE_LOC_DIFF, "LOC_DIFF", ICON_FULLSCREEN_ENTER, "Distance", "Distance between two bones or objects"}, /* XXX: Icon... */ {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 3c16f086325..52c04bec743 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -721,6 +721,12 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in a volumetric effect"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Use High Quality Fill */ + prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HQ_FILL); + RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality method to avoid glitches (slower fps during animation playback)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Stroke Drawing Color */ prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_array(prop, 3); @@ -764,18 +770,18 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) prop = RNA_def_property(srna, "ghost_before_range", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "gstep"); - RNA_def_property_range(prop, 0, 120); + RNA_def_property_range(prop, -1, 120); RNA_def_property_ui_text(prop, "Frames Before", "Maximum number of frames to show before current frame " - "(0 = show only the previous sketch)"); + "(0 = show only the previous sketch, -1 = don't show any frames before current)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "ghost_after_range", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "gstep_next"); - RNA_def_property_range(prop, 0, 120); + RNA_def_property_range(prop, -1, 120); RNA_def_property_ui_text(prop, "Frames After", "Maximum number of frames to show after current frame " - "(0 = show only the next sketch)"); + "(0 = show only the next sketch, -1 = don't show any frames after current)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "use_ghost_custom_colors", PROP_BOOLEAN, PROP_NONE); @@ -801,14 +807,25 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac"); RNA_def_property_range(prop, 0.0, 2.0f); - RNA_def_property_ui_text(prop, "Smooth", "Amount of smoothing to apply to newly created strokes, to reduce jitter/noise"); + RNA_def_property_ui_text(prop, "Smooth", + "Amount of smoothing to apply to newly created strokes, to reduce jitter/noise"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Iterations of the Smoothing factor */ + prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl"); + RNA_def_property_range(prop, 1, 3); + RNA_def_property_ui_text(prop, "Iterations", + "Number of times to smooth newly created strokes " + "(smoothing strength is halved on each successive round of smoothing)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Subdivision level for new strokes */ prop = RNA_def_property(srna, "pen_subdivision_steps", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "sublevel"); RNA_def_property_range(prop, 0, 3); - RNA_def_property_ui_text(prop, "Subdivision Steps", "Number of times to subdivide newly created strokes, for less jagged strokes"); + RNA_def_property_ui_text(prop, "Subdivision Steps", + "Number of times to subdivide newly created strokes, for less jagged strokes"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Flags */ diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 72cd2ce55b8..161e19f581c 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -197,6 +197,8 @@ void rna_def_animdata_common(struct StructRNA *srna); void rna_def_animviz_common(struct StructRNA *srna); void rna_def_motionpath_common(struct StructRNA *srna); +void rna_def_bone_curved_common(struct StructRNA *srna, bool is_posebone); + void rna_def_texmat_common(struct StructRNA *srna, const char *texspace_editable); void rna_def_mtex_common(struct BlenderRNA *brna, struct StructRNA *srna, const char *begin, const char *activeget, const char *activeset, const char *activeeditable, const char *structname, @@ -234,7 +236,6 @@ int rna_object_shapekey_index_set(struct ID *id, PointerRNA value, int current); void rna_Object_internal_update_data(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr); void rna_Mesh_update_draw(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr); void rna_TextureSlot_update(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr); -void rna_TextureSlot_brush_update(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr); /* basic poll functions for object types */ int rna_Armature_object_poll(struct PointerRNA *ptr, struct PointerRNA value); diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index d09cac2a383..eb87eafe16c 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -50,9 +50,12 @@ /* needed for some of the validation stuff... */ #include "BKE_animsys.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" #include "BKE_nla.h" -#include "BKE_fcurve.h" +#include "DNA_object_types.h" + #include "ED_anim_api.h" /* temp constant defined for these funcs only... */ @@ -102,7 +105,14 @@ static char *rna_NlaStrip_path(PointerRNA *ptr) return BLI_strdup(""); } -static void rna_NlaStrip_transform_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_NlaStrip_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +{ + ID *id = ptr->id.data; + + ANIM_id_update(scene, id); +} + +static void rna_NlaStrip_transform_update(Main *bmain, Scene *scene, PointerRNA *ptr) { NlaStrip *strip = (NlaStrip *)ptr->data; @@ -117,6 +127,8 @@ static void rna_NlaStrip_transform_update(Main *UNUSED(bmain), Scene *UNUSED(sce BKE_nla_validate_state(iat->adt); } } + + rna_NlaStrip_update(bmain, scene, ptr); } static void rna_NlaStrip_start_frame_set(PointerRNA *ptr, float value) @@ -536,45 +548,45 @@ static void rna_def_nlastrip(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* XXX for now, not editable, since this is dangerous */ RNA_def_property_enum_items(prop, prop_type_items); RNA_def_property_ui_text(prop, "Type", "Type of NLA Strip"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "extrapolation", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "extendmode"); RNA_def_property_enum_items(prop, rna_enum_nla_mode_extend_items); RNA_def_property_ui_text(prop, "Extrapolation", "Action to take for gaps past the strip extents"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "blendmode"); RNA_def_property_enum_items(prop, rna_enum_nla_mode_blend_items); RNA_def_property_ui_text(prop, "Blending", "Method used for combining strip's result with accumulated result"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); /* Strip extents */ prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "start"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_start_frame_set", NULL); RNA_def_property_ui_text(prop, "Start Frame", ""); - RNA_def_property_update(prop, 0, "rna_NlaStrip_transform_update"); + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "end"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_end_frame_set", NULL); RNA_def_property_ui_text(prop, "End Frame", ""); - RNA_def_property_update(prop, 0, "rna_NlaStrip_transform_update"); + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* Blending */ prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendin"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_blend_in_set", NULL); RNA_def_property_ui_text(prop, "Blend In", "Number of frames at start of strip to fade in influence"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blendout"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_blend_out_set", NULL); RNA_def_property_ui_text(prop, "Blend Out", ""); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_auto_blend", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_AUTO_BLENDS); @@ -582,7 +594,7 @@ static void rna_def_nlastrip(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Auto Blend In/Out", "Number of frames for Blending In/Out is automatically determined from " "overlapping strips"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); /* Action */ prop = RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE); @@ -591,20 +603,20 @@ static void rna_def_nlastrip(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_editable_func(prop, "rna_NlaStrip_action_editable"); RNA_def_property_ui_text(prop, "Action", "Action referenced by this strip"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); /* Action extents */ prop = RNA_def_property(srna, "action_frame_start", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "actstart"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_action_start_frame_set", NULL); RNA_def_property_ui_text(prop, "Action Start Frame", "First frame from action to use"); - RNA_def_property_update(prop, 0, "rna_NlaStrip_transform_update"); + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); prop = RNA_def_property(srna, "action_frame_end", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "actend"); RNA_def_property_float_funcs(prop, NULL, "rna_NlaStrip_action_end_frame_set", NULL); RNA_def_property_ui_text(prop, "Action End Frame", "Last frame from action to use"); - RNA_def_property_update(prop, 0, "rna_NlaStrip_transform_update"); + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* Action Reuse */ prop = RNA_def_property(srna, "repeat", PROP_FLOAT, PROP_NONE); @@ -614,7 +626,7 @@ static void rna_def_nlastrip(BlenderRNA *brna) * (minimum should still be > 0 though) if needed... */ RNA_def_property_range(prop, 0.1f, 1000.0f); RNA_def_property_ui_text(prop, "Repeat", "Number of times to repeat the action range"); - RNA_def_property_update(prop, 0, "rna_NlaStrip_transform_update"); + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "scale"); @@ -623,7 +635,7 @@ static void rna_def_nlastrip(BlenderRNA *brna) * due to numeric errors */ RNA_def_property_range(prop, 0.0001f, 1000.0f); RNA_def_property_ui_text(prop, "Scale", "Scaling factor for action"); - RNA_def_property_update(prop, 0, "rna_NlaStrip_transform_update"); + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* Strip's F-Curves */ prop = RNA_def_property(srna, "fcurves", PROP_COLLECTION, PROP_NONE); @@ -647,11 +659,11 @@ static void rna_def_nlastrip(BlenderRNA *brna) prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Influence", "Amount the strip contributes to the current result"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "strip_time", PROP_FLOAT, PROP_TIME); RNA_def_property_ui_text(prop, "Strip Time", "Frame of referenced Action to evaluate"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); /* TODO: should the animated_influence/time settings be animatable themselves? */ prop = RNA_def_property(srna, "use_animated_influence", PROP_BOOLEAN, PROP_NONE); @@ -659,19 +671,19 @@ static void rna_def_nlastrip(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_animated_influence_set"); RNA_def_property_ui_text(prop, "Animated Influence", "Influence setting is controlled by an F-Curve rather than automatically determined"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_animated_time", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_TIME); RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaStrip_animated_time_set"); RNA_def_property_ui_text(prop, "Animated Strip Time", "Strip time is controlled by an F-Curve rather than automatically determined"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_animated_time_cyclic", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_USR_TIME_CYCLIC); RNA_def_property_ui_text(prop, "Cyclic Strip Time", "Cycle the animated time within the action start & end"); - RNA_def_property_update(prop, 0, "rna_NlaStrip_transform_update"); /* is there a better update flag? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* settings */ prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); @@ -689,21 +701,21 @@ static void rna_def_nlastrip(BlenderRNA *brna) prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_MUTED); RNA_def_property_ui_text(prop, "Muted", "NLA Strip is not evaluated"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_reverse", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_REVERSE); RNA_def_property_ui_text(prop, "Reversed", "NLA Strip is played back in reverse order (only when timing is " "automatically determined)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "use_sync_length", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_SYNC_LENGTH); RNA_def_property_ui_text(prop, "Sync Action Length", "Update range of frames referenced from action " "after tweaking strip and its keyframes"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); } static void rna_api_nlatrack_strips(BlenderRNA *brna, PropertyRNA *cprop) @@ -775,7 +787,7 @@ static void rna_def_nlatrack(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Solo", "NLA Track is evaluated itself (i.e. active Action and all other NLA Tracks in the " "same AnimData block are disabled)"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); RNA_def_property_boolean_funcs(prop, NULL, "rna_NlaTrack_solo_set"); prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); @@ -786,7 +798,7 @@ static void rna_def_nlatrack(BlenderRNA *brna) prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_MUTED); RNA_def_property_ui_text(prop, "Muted", "NLA Track is not evaluated"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, NULL); /* this will do? */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", NLATRACK_PROTECTED); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index ccbabb2b238..3221e93fe84 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5892,6 +5892,12 @@ static void def_cmp_colorbalance(StructRNA *srna) RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); RNA_def_property_ui_text(prop, "Slope", "Correction for Highlights"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_cdl"); + + prop = RNA_def_property(srna, "offset_basis", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -1.0, 1.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Basis", "Support negative color by using this as the RGB basis"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeColorBalance_update_cdl"); } static void def_cmp_huecorrect(StructRNA *srna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 031ea89cff6..9ebef9db454 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -267,7 +267,7 @@ static void rna_Object_active_shape_update(Main *bmain, Scene *scene, PointerRNA switch (ob->type) { case OB_MESH: EDBM_mesh_load(ob); - EDBM_mesh_make(scene->toolsettings, ob); + EDBM_mesh_make(scene->toolsettings, ob, true); DAG_id_tag_update(ob->data, 0); @@ -301,7 +301,7 @@ static void rna_Object_select_update(Main *UNUSED(bmain), Scene *scene, PointerR { if (scene) { Object *ob = (Object *)ptr->id.data; - short mode = ob->flag & SELECT ? BA_SELECT : BA_DESELECT; + short mode = (ob->flag & SELECT) ? BA_SELECT : BA_DESELECT; ED_base_object_select(BKE_scene_base_find(scene, ob), mode); } } @@ -309,7 +309,7 @@ static void rna_Object_select_update(Main *UNUSED(bmain), Scene *scene, PointerR static void rna_Base_select_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Base *base = (Base *)ptr->data; - short mode = base->flag & BA_SELECT ? BA_SELECT : BA_DESELECT; + short mode = (base->flag & BA_SELECT) ? BA_SELECT : BA_DESELECT; ED_base_object_select(base, mode); } @@ -1134,7 +1134,7 @@ static void rna_GameObjectSettings_state_get(PointerRNA *ptr, int *values) { Object *ob = (Object *)ptr->data; int i; - int all_states = (ob->scaflag & OB_ALLSTATE ? 1 : 0); + int all_states = (ob->scaflag & OB_ALLSTATE) ? 1 : 0; memset(values, 0, sizeof(int) * OB_MAX_STATES); for (i = 0; i < OB_MAX_STATES; i++) { diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index d8f1f944933..06823a26c77 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -43,6 +43,8 @@ #include "BLT_translation.h" +#include "UI_resources.h" + #include "WM_types.h" @@ -65,26 +67,26 @@ EnumPropertyItem rna_enum_posebone_rotmode_items[] = { /* Bone and Group Color Sets */ EnumPropertyItem rna_enum_color_sets_items[] = { {0, "DEFAULT", 0, "Default Colors", ""}, - {1, "THEME01", 0, "01 - Theme Color Set", ""}, - {2, "THEME02", 0, "02 - Theme Color Set", ""}, - {3, "THEME03", 0, "03 - Theme Color Set", ""}, - {4, "THEME04", 0, "04 - Theme Color Set", ""}, - {5, "THEME05", 0, "05 - Theme Color Set", ""}, - {6, "THEME06", 0, "06 - Theme Color Set", ""}, - {7, "THEME07", 0, "07 - Theme Color Set", ""}, - {8, "THEME08", 0, "08 - Theme Color Set", ""}, - {9, "THEME09", 0, "09 - Theme Color Set", ""}, - {10, "THEME10", 0, "10 - Theme Color Set", ""}, - {11, "THEME11", 0, "11 - Theme Color Set", ""}, - {12, "THEME12", 0, "12 - Theme Color Set", ""}, - {13, "THEME13", 0, "13 - Theme Color Set", ""}, - {14, "THEME14", 0, "14 - Theme Color Set", ""}, - {15, "THEME15", 0, "15 - Theme Color Set", ""}, - {16, "THEME16", 0, "16 - Theme Color Set", ""}, - {17, "THEME17", 0, "17 - Theme Color Set", ""}, - {18, "THEME18", 0, "18 - Theme Color Set", ""}, - {19, "THEME19", 0, "19 - Theme Color Set", ""}, - {20, "THEME20", 0, "20 - Theme Color Set", ""}, + {1, "THEME01", VICO_COLORSET_01_VEC, "01 - Theme Color Set", ""}, + {2, "THEME02", VICO_COLORSET_02_VEC, "02 - Theme Color Set", ""}, + {3, "THEME03", VICO_COLORSET_03_VEC, "03 - Theme Color Set", ""}, + {4, "THEME04", VICO_COLORSET_04_VEC, "04 - Theme Color Set", ""}, + {5, "THEME05", VICO_COLORSET_05_VEC, "05 - Theme Color Set", ""}, + {6, "THEME06", VICO_COLORSET_06_VEC, "06 - Theme Color Set", ""}, + {7, "THEME07", VICO_COLORSET_07_VEC, "07 - Theme Color Set", ""}, + {8, "THEME08", VICO_COLORSET_08_VEC, "08 - Theme Color Set", ""}, + {9, "THEME09", VICO_COLORSET_09_VEC, "09 - Theme Color Set", ""}, + {10, "THEME10", VICO_COLORSET_10_VEC, "10 - Theme Color Set", ""}, + {11, "THEME11", VICO_COLORSET_11_VEC, "11 - Theme Color Set", ""}, + {12, "THEME12", VICO_COLORSET_12_VEC, "12 - Theme Color Set", ""}, + {13, "THEME13", VICO_COLORSET_13_VEC, "13 - Theme Color Set", ""}, + {14, "THEME14", VICO_COLORSET_14_VEC, "14 - Theme Color Set", ""}, + {15, "THEME15", VICO_COLORSET_15_VEC, "15 - Theme Color Set", ""}, + {16, "THEME16", VICO_COLORSET_16_VEC, "16 - Theme Color Set", ""}, + {17, "THEME17", VICO_COLORSET_17_VEC, "17 - Theme Color Set", ""}, + {18, "THEME18", VICO_COLORSET_18_VEC, "18 - Theme Color Set", ""}, + {19, "THEME19", VICO_COLORSET_19_VEC, "19 - Theme Color Set", ""}, + {20, "THEME20", VICO_COLORSET_20_VEC, "20 - Theme Color Set", ""}, {-1, "CUSTOM", 0, "Custom Color Set", ""}, {0, NULL, 0, NULL, NULL} }; @@ -532,7 +534,7 @@ static bConstraint *rna_PoseChannel_constraints_new(bPoseChannel *pchan, int typ static void rna_PoseChannel_constraints_remove(ID *id, bPoseChannel *pchan, ReportList *reports, PointerRNA *con_ptr) { bConstraint *con = con_ptr->data; - const short is_ik = ELEM(con->type, CONSTRAINT_TYPE_KINEMATIC, CONSTRAINT_TYPE_SPLINEIK); + const bool is_ik = ELEM(con->type, CONSTRAINT_TYPE_KINEMATIC, CONSTRAINT_TYPE_SPLINEIK); Object *ob = (Object *)id; if (BLI_findindex(&pchan->constraints, con) == -1) { @@ -872,6 +874,50 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Rotation Mode", ""); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + /* Curved bones settings - Applied on top of restpose values */ + rna_def_bone_curved_common(srna, true); + + /* Custom BBone next/prev sources */ + prop = RNA_def_property(srna, "use_bbone_custom_handles", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "bboneflag", PCHAN_BBONE_CUSTOM_HANDLES); + RNA_def_property_ui_text(prop, "Use Custom Handle References", + "Use custom reference bones as handles for B-Bones instead of next/previous bones, " + "leave these blank to use only B-Bone offset properties to control the shape"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "bbone_custom_handle_start", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "bbone_prev"); + RNA_def_property_struct_type(prop, "PoseBone"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "B-Bone Start Handle", + "Bone that serves as the start handle for the B-Bone curve"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "use_bbone_relative_start_handle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "bboneflag", PCHAN_BBONE_CUSTOM_START_REL); + RNA_def_property_ui_text(prop, "Relative B-Bone Start Handle", + "Use treat custom start handle position as a relative value"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "bbone_custom_handle_end", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "bbone_next"); + RNA_def_property_struct_type(prop, "PoseBone"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "B-Bone End Handle", + "Bone that serves as the end handle for the B-Bone curve"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "use_bbone_relative_end_handle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "bboneflag", PCHAN_BBONE_CUSTOM_END_REL); + RNA_def_property_ui_text(prop, "Relative B-Bone End Handle", + "Use treat custom end handle position as a relative value"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + /* transform matrices - should be read-only since these are set directly by AnimSys evaluation */ prop = RNA_def_property(srna, "matrix_channel", PROP_FLOAT, PROP_MATRIX); RNA_def_property_float_sdna(prop, NULL, "chan_mat"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 94223f7f7da..e89edaf74ad 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -860,7 +860,7 @@ static void rna_ImageFormatSettings_file_format_set(PointerRNA *ptr, int value) { ImageFormatData *imf = (ImageFormatData *)ptr->data; ID *id = ptr->id.data; - const char is_render = (id && GS(id->name) == ID_SCE); + const bool is_render = (id && GS(id->name) == ID_SCE); /* see note below on why this is */ const char chan_flag = BKE_imtype_valid_channels(imf->imtype, true) | (is_render ? IMA_CHAN_FLAG_BW : 0); @@ -927,7 +927,7 @@ static EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf( { ImageFormatData *imf = (ImageFormatData *)ptr->data; ID *id = ptr->id.data; - const char is_render = (id && GS(id->name) == ID_SCE); + const bool is_render = (id && GS(id->name) == ID_SCE); /* note, we need to act differently for render * where 'BW' will force grayscale even if the output format writes @@ -1767,6 +1767,11 @@ static char *rna_UnifiedPaintSettings_path(PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.unified_paint_settings"); } +static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.curve_paint_settings"); +} + /* generic function to recalc geometry */ static void rna_EditMesh_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { @@ -2497,6 +2502,12 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_struct_type(prop, "UnifiedPaintSettings"); RNA_def_property_ui_text(prop, "Unified Paint Settings", NULL); + /* Curve Paint Settings */ + prop = RNA_def_property(srna, "curve_paint_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "CurvePaintSettings"); + RNA_def_property_ui_text(prop, "Curve Paint Settings", NULL); + /* Mesh Statistics */ prop = RNA_def_property(srna, "statvis", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); @@ -2595,6 +2606,100 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) "when unlocked brush size is given in pixels"); } + +static void rna_def_curve_paint_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "CurvePaintSettings", NULL); + RNA_def_struct_path_func(srna, "rna_CurvePaintSettings_path"); + RNA_def_struct_ui_text(srna, "Curve Paint Settings", ""); + + static EnumPropertyItem curve_type_items[] = { + {CU_POLY, "POLY", 0, "Poly", ""}, + {CU_BEZIER, "BEZIER", 0, "Bezier", ""}, + {0, NULL, 0, NULL, NULL}}; + + prop = RNA_def_property(srna, "curve_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "curve_type"); + RNA_def_property_enum_items(prop, curve_type_items); + RNA_def_property_ui_text(prop, "Type", "Type of curve to use for new strokes"); + + prop = RNA_def_property(srna, "use_corners_detect", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_CORNERS_DETECT); + RNA_def_property_ui_text(prop, "Detect Corners", "Detect corners and use non-aligned handles"); + + prop = RNA_def_property(srna, "use_pressure_radius", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_PRESSURE_RADIUS); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure", "Map tablet pressure to curve radius"); + + prop = RNA_def_property(srna, "use_stroke_endpoints", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS); + RNA_def_property_ui_text(prop, "Only First", "Use the start of the stroke for the depth"); + + prop = RNA_def_property(srna, "use_offset_absolute", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_DEPTH_STROKE_OFFSET_ABS); + RNA_def_property_ui_text(prop, "Absolute Offset", "Apply a fixed offset (don't scale by the radius)"); + + prop = RNA_def_property(srna, "error_threshold", PROP_INT, PROP_PIXEL); + RNA_def_property_range(prop, 1, 100); + RNA_def_property_ui_text(prop, "Tolerance", "Allow deviation for a smoother, less precise line"); + + prop = RNA_def_property(srna, "corner_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0, M_PI); + RNA_def_property_ui_text(prop, "Corner Angle", "Angles above this are considered corners"); + + prop = RNA_def_property(srna, "radius_min", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_range(prop, 0.0f, 10.0, 10, 2); + RNA_def_property_ui_text(prop, "Radius Min", + "Minimum radius when the minimum pressure is applied (also the minimum when tapering)"); + + prop = RNA_def_property(srna, "radius_max", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_range(prop, 0.0f, 10.0, 10, 2); + RNA_def_property_ui_text(prop, "Radius Max", + "Radius to use when the maximum pressure is applied (or when a tablet isn't used)"); + + prop = RNA_def_property(srna, "radius_taper_start", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_range(prop, 0.0f, 1.0, 1, 2); + RNA_def_property_ui_text(prop, "Radius Min", "Taper factor for the radius of each point along the curve"); + + prop = RNA_def_property(srna, "radius_taper_end", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_range(prop, 0.0f, 1.0, 1, 2); + RNA_def_property_ui_text(prop, "Radius Max", "Taper factor for the radius of each point along the curve"); + + prop = RNA_def_property(srna, "surface_offset", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, -10.0, 10.0); + RNA_def_property_ui_range(prop, -1.0f, 1.0, 1, 2); + RNA_def_property_ui_text(prop, "Offset", "Offset the stroke from the surface"); + + static EnumPropertyItem depth_mode_items[] = { + {CURVE_PAINT_PROJECT_CURSOR, "CURSOR", 0, "Cursor", ""}, + {CURVE_PAINT_PROJECT_SURFACE, "SURFACE", 0, "Surface", ""}, + {0, NULL, 0, NULL, NULL}}; + + prop = RNA_def_property(srna, "depth_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "depth_mode"); + RNA_def_property_enum_items(prop, depth_mode_items); + RNA_def_property_ui_text(prop, "Depth", "Method of projecting depth"); + + static EnumPropertyItem surface_plane_items[] = { + {CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW, "NORMAL_VIEW", 0, "Normal/View", "Draw perpendicular to the surface"}, + {CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE, "NORMAL_SURFACE", 0, "Normal/Surface", "Draw aligned to the surface"}, + {CURVE_PAINT_SURFACE_PLANE_VIEW, "VIEW", 0, "View", "Draw aligned to the viewport"}, + {0, NULL, 0, NULL, NULL}}; + + prop = RNA_def_property(srna, "surface_plane", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "surface_plane"); + RNA_def_property_enum_items(prop, surface_plane_items); + RNA_def_property_ui_text(prop, "Plane", "Plane for projected stroke"); +} + static void rna_def_statvis(BlenderRNA *brna) { StructRNA *srna; @@ -6720,6 +6825,7 @@ void RNA_def_scene(BlenderRNA *brna) RNA_define_animate_sdna(false); rna_def_tool_settings(brna); rna_def_unified_paint_settings(brna); + rna_def_curve_paint_settings(brna); rna_def_statvis(brna); rna_def_unit_settings(brna); rna_def_scene_image_format_data(brna); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 6d7506f3911..74fb4a08eda 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -55,6 +55,7 @@ #include "BKE_writeavi.h" #include "ED_transform.h" +#include "ED_transform_snap_object_context.h" #include "ED_uvedit.h" #ifdef WITH_PYTHON @@ -145,13 +146,22 @@ static void rna_Scene_ray_cast( { normalize_v3(direction); - if (snapObjectsRayEx( - scene, NULL, NULL, NULL, NULL, - NULL, SNAP_ALL, SCE_SNAP_MODE_FACE, + SnapObjectContext *sctx = ED_transform_snap_object_context_create( + G.main, scene, 0); + + bool ret = ED_transform_snap_object_project_ray_ex( + sctx, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + .snap_to = SCE_SNAP_MODE_FACE, + }, origin, direction, &ray_dist, - r_location, r_normal, NULL, r_index, - r_ob, (float(*)[4])r_obmat)) - { + r_location, r_normal, r_index, + r_ob, (float(*)[4])r_obmat); + + ED_transform_snap_object_context_destroy(sctx); + + if (ret) { *r_success = true; } else { diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index e8a45e6a77d..68d4f7f7e51 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2330,6 +2330,16 @@ static void rna_def_text(StructRNA *srna) RNA_def_property_ui_range(prop, 0.0f, 1000, 1, -1); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "color"); + RNA_def_property_ui_text(prop, "Color", ""); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); + + prop = RNA_def_property(srna, "shadow_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "shadow_color"); + RNA_def_property_ui_text(prop, "Shadow Color", ""); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); + prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_XYZ); RNA_def_property_float_sdna(prop, NULL, "loc"); RNA_def_property_ui_text(prop, "Location", "Location of the text"); diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c index ba3198a4843..9a31952b84b 100644 --- a/source/blender/makesrna/intern/rna_smoke.c +++ b/source/blender/makesrna/intern/rna_smoke.c @@ -191,6 +191,29 @@ static int rna_SmokeModifier_velocity_grid_get_length(PointerRNA *ptr, int lengt return length[0]; } +static int rna_SmokeModifier_heat_grid_get_length( + PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) +{ +#ifdef WITH_SMOKE + SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data; + float *heat = NULL; + int size = 0; + + /* Heat data is always low-resolution. */ + if (sds->fluid) { + size = sds->res[0] * sds->res[1] * sds->res[2]; + heat = smoke_get_heat(sds->fluid); + } + + length[0] = (heat) ? size : 0; +#else + (void)ptr; + length[0] = 0; +#endif + return length[0]; +} + static void rna_SmokeModifier_density_grid_get(PointerRNA *ptr, float *values) { #ifdef WITH_SMOKE @@ -293,6 +316,34 @@ static void rna_SmokeModifier_flame_grid_get(PointerRNA *ptr, float *values) #endif } +static void rna_SmokeModifier_heat_grid_get(PointerRNA *ptr, float *values) +{ +#ifdef WITH_SMOKE + SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data; + int length[RNA_MAX_ARRAY_DIMENSION]; + int size = rna_SmokeModifier_heat_grid_get_length(ptr, length); + float *heat; + + BLI_rw_mutex_lock(sds->fluid_mutex, THREAD_LOCK_READ); + + heat = smoke_get_heat(sds->fluid); + + if (heat != NULL) { + /* scale heat values from -2.0-2.0 to -1.0-1.0. */ + for (int i = 0; i < size; i++) { + values[i] = heat[i] * 0.5f; + } + } + else { + memset(values, 0, size * sizeof(float)); + } + + BLI_rw_mutex_unlock(sds->fluid_mutex); +#else + UNUSED_VARS(ptr, values); +#endif +} + static void rna_SmokeFlow_density_vgroup_get(PointerRNA *ptr, char *value) { SmokeFlowSettings *flow = (SmokeFlowSettings *)ptr->data; @@ -563,6 +614,14 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna) RNA_def_property_float_funcs(prop, "rna_SmokeModifier_color_grid_get", NULL, NULL); RNA_def_property_ui_text(prop, "Color Grid", "Smoke color grid"); + prop = RNA_def_property(srna, "heat_grid", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 32); + RNA_def_property_flag(prop, PROP_DYNAMIC); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_dynamic_array_funcs(prop, "rna_SmokeModifier_heat_grid_get_length"); + RNA_def_property_float_funcs(prop, "rna_SmokeModifier_heat_grid_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Heat Grid", "Smoke heat grid"); + prop = RNA_def_property(srna, "cell_size", PROP_FLOAT, PROP_XYZ); /* can change each frame when using adaptive domain */ RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "cell_size", "Cell Size"); diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index ad1ca12d4ea..1e88585a286 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -225,7 +225,7 @@ static void rna_Texture_type_set(PointerRNA *ptr, int value) BKE_texture_type_set(tex, value); } -void rna_TextureSlot_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +void rna_TextureSlot_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) { ID *id = ptr->id.data; @@ -244,8 +244,12 @@ void rna_TextureSlot_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRN WM_main_add_notifier(NC_LAMP | ND_LIGHTING_DRAW, id); break; case ID_BR: + { + MTex *mtex = ptr->data; + BKE_paint_invalidate_overlay_tex(scene, mtex->tex); WM_main_add_notifier(NC_BRUSH, id); break; + } case ID_LS: WM_main_add_notifier(NC_LINESTYLE, id); break; @@ -266,24 +270,6 @@ void rna_TextureSlot_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRN } } -void rna_TextureSlot_brush_update(Main *bmain, Scene *scene, PointerRNA *ptr) -{ - ID *id = ptr->id.data; - - DAG_id_tag_update(id, 0); - - switch (GS(id->name)) { - case ID_BR: - { - MTex *mtex = ptr->data; - BKE_paint_invalidate_overlay_tex(scene, mtex->tex); - break; - } - } - rna_TextureSlot_update(bmain, scene, ptr); -} - - char *rna_TextureSlot_path(PointerRNA *ptr) { MTex *mtex = ptr->data; @@ -675,14 +661,14 @@ static void rna_def_mtex(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "ofs"); RNA_def_property_ui_range(prop, -10, 10, 10, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_ui_text(prop, "Offset", "Fine tune of the texture mapping X, Y and Z locations"); - RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update"); + RNA_def_property_update(prop, 0, "rna_TextureSlot_update"); prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ); RNA_def_property_float_sdna(prop, NULL, "size"); RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_ui_range(prop, -100, 100, 10, 2); RNA_def_property_ui_text(prop, "Size", "Set scaling for the texture's X, Y and Z sizes"); - RNA_def_property_update(prop, 0, "rna_TextureSlot_brush_update"); + RNA_def_property_update(prop, 0, "rna_TextureSlot_update"); prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "r"); diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index a9901fd3a50..2564bdb800f 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -1777,8 +1777,8 @@ static void rna_def_trackingReconstructedCameras(BlenderRNA *brna) RNA_def_function_flag(func, FUNC_USE_SELF_ID); RNA_def_function_ui_description(func, "Return interpolated camera matrix for a given frame"); RNA_def_int(func, "frame", 1, MINFRAME, MAXFRAME, "Frame", "Frame number to find camera for", MINFRAME, MAXFRAME); - parm = RNA_def_float_matrix(func, "matrix", 4, 4, NULL, FLT_MIN, FLT_MAX, "Matrix", - "Interpolated camera matrix for a given frame", FLT_MIN, FLT_MAX); + parm = RNA_def_float_matrix(func, "matrix", 4, 4, NULL, -FLT_MAX, FLT_MAX, "Matrix", + "Interpolated camera matrix for a given frame", -FLT_MAX, FLT_MAX); RNA_def_property_flag(parm, PROP_THICK_WRAP); /* needed for string return value */ RNA_def_function_output(func, parm); } diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index efd302ce451..0fbc1ce1854 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -139,7 +139,7 @@ static void rna_userdef_dpi_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Po /* font's are stored at each DPI level, without this we can easy load 100's of fonts */ BLF_cache_clear(); - BKE_userdef_state(); + BKE_blender_userdef_refresh(); WM_main_add_notifier(NC_WINDOW, NULL); /* full redraw */ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); /* refresh region sizes */ } @@ -149,7 +149,7 @@ static void rna_userdef_virtual_pixel_update(Main *bmain, Scene *UNUSED(scene), /* font's are stored at each DPI level, without this we can easy load 100's of fonts */ BLF_cache_clear(); - BKE_userdef_state(); + BKE_blender_userdef_refresh(); /* force setting drawable again */ wmWindowManager *wm = bmain->wm.first; diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 51c410e44e1..0d11414c878 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -263,6 +263,7 @@ EnumPropertyItem rna_enum_event_type_items[] = { {QUOTEKEY, "QUOTE", 0, "\"", ""}, {ACCENTGRAVEKEY, "ACCENT_GRAVE", 0, "`", ""}, {MINUSKEY, "MINUS", 0, "-", ""}, + {PLUSKEY, "PLUS", 0, "+", ""}, {SLASHKEY, "SLASH", 0, "/", ""}, {BACKSLASHKEY, "BACK_SLASH", 0, "\\", ""}, {EQUALKEY, "EQUAL", 0, "=", ""}, diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index d9ace45453c..567505ea45d 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -124,6 +124,7 @@ static void updateDepsgraph(ModifierData *md, ArmatureModifierData *amd = (ArmatureModifierData *)md; if (amd->object != NULL) { DEG_add_object_relation(node, amd->object, DEG_OB_COMP_EVAL_POSE, "Armature Modifier"); + DEG_add_object_relation(node, amd->object, DEG_OB_COMP_TRANSFORM, "Armature Modifier"); } } @@ -206,6 +207,7 @@ ModifierTypeInfo modifierType_Armature = { /* structSize */ sizeof(ArmatureModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 32c3d41c4b6..33e5b3615d9 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -498,6 +498,7 @@ ModifierTypeInfo modifierType_Cast = { /* structSize */ sizeof(CastModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index bebfbbe9a80..ff0c6500479 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -189,7 +189,7 @@ static void deformVerts(ModifierData *md, Object *ob, /* check if GUI setting has changed for bvh */ if (collmd->bvhtree) { - if (ob->pd->pdef_sboft != BLI_bvhtree_getepsilon(collmd->bvhtree)) { + if (ob->pd->pdef_sboft != BLI_bvhtree_get_epsilon(collmd->bvhtree)) { BLI_bvhtree_free(collmd->bvhtree); collmd->bvhtree = bvhtree_build_from_mvert( collmd->current_x, diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index 6e2d746c858..9bc96e593fd 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -164,6 +164,7 @@ ModifierTypeInfo modifierType_Curve = { /* structSize */ sizeof(CurveModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index e175f4943a7..83c4ca7984c 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -139,20 +139,21 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, static void updateDepsgraph(ModifierData *md, struct Main *UNUSED(bmain), struct Scene *UNUSED(scene), - Object *UNUSED(ob), + Object *ob, struct DepsNodeHandle *node) { HookModifierData *hmd = (HookModifierData *)md; if (hmd->object != NULL) { if (hmd->subtarget[0]) { - /* TODO(sergey): Hpw do we add relation to bone here? */ - //DEG_add_object_relation(node, hmd->object, DEG_OB_COMP_EVAL_POSE, "Hook Modifier"); + DEG_add_bone_relation(node, hmd->object, hmd->subtarget, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); DEG_add_bone_relation(node, hmd->object, hmd->subtarget, DEG_OB_COMP_BONE, "Hook Modifier"); } else { DEG_add_object_relation(node, hmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); } } + /* We need own transformation as well. */ + DEG_add_object_relation(node, ob, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); } struct HookData_cb { @@ -416,6 +417,7 @@ ModifierTypeInfo modifierType_Hook = { /* structSize */ sizeof(HookModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, /* deformVerts */ deformVerts, diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index d4f02d923d3..ce3fdc4bbe8 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -339,7 +339,6 @@ static void rotateDifferentialCoordinates(LaplacianSystem *sys) pi[1] = EIG_linear_solver_variable_get(sys->context, 1, i); pi[2] = EIG_linear_solver_variable_get(sys->context, 2, i); zero_v3(ni); - num_fni = 0; num_fni = sys->ringf_map[i].count; for (fi = 0; fi < num_fni; fi++) { const unsigned int *vin; diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 0f49ce6cfbf..7d4701e3ef2 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -156,6 +156,7 @@ ModifierTypeInfo modifierType_Lattice = { /* structSize */ sizeof(LatticeModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, /* deformVerts */ deformVerts, diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 92926ed9424..aa3e3ebcf7e 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -299,6 +299,7 @@ ModifierTypeInfo modifierType_MeshCache = { /* structSize */ sizeof(MeshCacheModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index e3c94a1a4df..3f34319d25f 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -520,6 +520,7 @@ ModifierTypeInfo modifierType_MeshDeform = { /* structSize */ sizeof(MeshDeformModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index a90a51e9ddc..e77cc655c16 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -435,7 +435,7 @@ static DerivedMesh *doOcean(ModifierData *md, Object *ob, const float size_co_inv = 1.0f / (omd->size * omd->spatial_size); /* can happen in when size is small, avoid bad array lookups later and quit now */ - if (!finite(size_co_inv)) { + if (!isfinite(size_co_inv)) { return derivedData; } diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index a9919cadd16..633311c2b87 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -194,6 +194,7 @@ ModifierTypeInfo modifierType_Shrinkwrap = { /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index 588b56d6aba..c68f2651191 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -366,6 +366,7 @@ ModifierTypeInfo modifierType_SimpleDeform = { /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode, diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index d958badc33c..98a1412d0c6 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -65,6 +65,7 @@ ModifierTypeInfo modifierType_Softbody = { /* structSize */ sizeof(SoftbodyModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_RequiresOriginalData | eModifierTypeFlag_Single, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 27d3bac59ec..b38de140a91 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -374,6 +374,7 @@ ModifierTypeInfo modifierType_Warp = { /* structSize */ sizeof(WarpModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, /* deformVerts */ deformVerts, diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index f13eeb3185e..683649ed1c0 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -384,6 +384,7 @@ ModifierTypeInfo modifierType_Wave = { /* structSize */ sizeof(WaveModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, /* deformVerts */ deformVerts, diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index f126e499fc2..0968d2aa5e0 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -506,9 +506,9 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *der new_w[i] = dist; } else if (wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) { - const short use_trgt_verts = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_VERTS); - const short use_trgt_edges = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_EDGES); - const short use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES); + const bool use_trgt_verts = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_VERTS) != 0; + const bool use_trgt_edges = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_EDGES) != 0; + const bool use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES) != 0; if (use_trgt_verts || use_trgt_edges || use_trgt_faces) { DerivedMesh *target_dm = obr->derivedFinal; diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index 2f96bdbe3df..4cb39e0f13b 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -55,7 +55,6 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" -#include "BKE_blender.h" #include "BKE_colortools.h" #include "BKE_global.h" #include "BKE_image.h" diff --git a/source/blender/nodes/shader/nodes/node_shader_lamp.c b/source/blender/nodes/shader/nodes/node_shader_lamp.c index 3b000d49822..d5dac3b7ff8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_lamp.c +++ b/source/blender/nodes/shader/nodes/node_shader_lamp.c @@ -69,7 +69,7 @@ static int gpu_shader_lamp(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED( return GPU_stack_link(mat, "lamp", in, out, col, energy, lv, dist, shadow, visifac); } - return 0; + return false; } void register_node_type_sh_lamp(void) diff --git a/source/blender/nodes/shader/nodes/node_shader_mapping.c b/source/blender/nodes/shader/nodes/node_shader_mapping.c index 2af6e19565b..2044f5390cc 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mapping.c +++ b/source/blender/nodes/shader/nodes/node_shader_mapping.c @@ -42,6 +42,15 @@ static bNodeSocketTemplate sh_node_mapping_out[] = { { -1, 0, "" } }; +static void *node_shader_initexec_mapping(bNodeExecContext *UNUSED(context), + bNode *node, + bNodeInstanceKey UNUSED(key)) +{ + TexMapping *texmap = node->storage; + BKE_texture_mapping_init(texmap); + return NULL; +} + /* do the regular mapping options for blender textures */ static void node_shader_exec_mapping(void *UNUSED(data), int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out) { @@ -85,12 +94,12 @@ static int gpu_shader_mapping(GPUMaterial *mat, bNode *node, bNodeExecData *UNUS GPUNodeLink *tdomin = GPU_uniform(&domin); GPUNodeLink *tdomax = GPU_uniform(&domax); - int result = GPU_stack_link(mat, "mapping", in, out, tmat, tmin, tmax, tdomin, tdomax); + GPU_stack_link(mat, "mapping", in, out, tmat, tmin, tmax, tdomin, tdomax); - if (result && texmap->type == TEXMAP_TYPE_NORMAL) + if (texmap->type == TEXMAP_TYPE_NORMAL) GPU_link(mat, "texco_norm", out[0].link, &out[0].link); - return result; + return true; } void register_node_type_sh_mapping(void) @@ -103,7 +112,7 @@ void register_node_type_sh_mapping(void) node_type_size(&ntype, 320, 160, 360); node_type_init(&ntype, node_shader_init_mapping); node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_mapping); + node_type_exec(&ntype, node_shader_initexec_mapping, NULL, node_shader_exec_mapping); node_type_gpu(&ntype, gpu_shader_mapping); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.c b/source/blender/nodes/shader/nodes/node_shader_normal_map.c index 13bf2a0fe43..642e5b296be 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal_map.c +++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.c @@ -46,12 +46,111 @@ static void node_shader_init_normal_map(bNodeTree *UNUSED(ntree), bNode *node) node->storage = attr; } -static int gpu_shader_normal_map(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) +static void node_shader_exec_normal_map(void *data, int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out) + { + if (data) { + ShadeInput *shi = ((ShaderCallData *)data)->shi; + + NodeShaderNormalMap *nm = node->storage; + + float strength, vecIn[3]; + nodestack_get_vec(&strength, SOCK_FLOAT, in[0]); + nodestack_get_vec(vecIn, SOCK_VECTOR, in[1]); + + vecIn[0] = -2 * (vecIn[0] - 0.5f); + vecIn[1] = 2 * (vecIn[1] - 0.5f); + vecIn[2] = 2 * (vecIn[2] - 0.5f); + + CLAMP_MIN(strength, 0.0f); + + float *N = shi->vno; + int uv_index = 0; + switch (nm->space) { + case SHD_NORMAL_MAP_TANGENT: + if (nm->uv_map[0]) { + /* find uv map by name */ + for (int i = 0; i < shi->totuv; i++) { + if (STREQ(shi->uv[i].name, nm->uv_map)) { + uv_index = i; + break; + } + } + } + else { + uv_index = shi->actuv; + } + + float *T = shi->tangents[uv_index]; + + float B[3]; + cross_v3_v3v3(B, N, T); + mul_v3_fl(B, T[3]); + + for (int j = 0; j < 3; j++) + out[0]->vec[j] = vecIn[0] * T[j] + vecIn[1] * B[j] + vecIn[2] * N[j]; + interp_v3_v3v3(out[0]->vec, N, out[0]->vec, strength); + break; + + case SHD_NORMAL_MAP_OBJECT: + case SHD_NORMAL_MAP_BLENDER_OBJECT: + mul_mat3_m4_v3((float (*)[4])RE_object_instance_get_matrix(shi->obi, RE_OBJECT_INSTANCE_MATRIX_LOCALTOVIEW), vecIn); + interp_v3_v3v3(out[0]->vec, N, vecIn, strength); + break; + + case SHD_NORMAL_MAP_WORLD: + case SHD_NORMAL_MAP_BLENDER_WORLD: + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEW_MATRIX), vecIn); + interp_v3_v3v3(out[0]->vec, N, vecIn, strength); + break; + } + normalize_v3(out[0]->vec); + } +} + +static int gpu_shader_normal_map(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *normal; - GPU_link(mat, "direction_transform_m4v3", GPU_builtin(GPU_VIEW_NORMAL), GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &normal); + NodeShaderNormalMap *nm = node->storage; + GPUNodeLink *negnorm; + GPUNodeLink *realnorm; + GPUNodeLink *strength; + + float d[4] = {0, 0, 0, 0}; + + if (in[0].link) + strength = in[0].link; + else + strength = GPU_uniform(in[0].vec); + + if (in[1].link) { + GPU_link(mat, "color_to_normal", in[1].link, &realnorm); + GPU_link(mat, "mtex_negate_texnormal", realnorm, &realnorm); + } + + GPU_link(mat, "math_max", strength, GPU_uniform(d), &strength); + GPU_link(mat, "vec_math_negate", GPU_builtin(GPU_VIEW_NORMAL), &negnorm); + + if (in[1].link) { + switch (nm->space) { + case SHD_NORMAL_MAP_TANGENT: + GPU_link(mat, "node_normal_map", GPU_attribute(CD_TANGENT, nm->uv_map), negnorm, realnorm, &out[0].link); + break; + case SHD_NORMAL_MAP_OBJECT: + case SHD_NORMAL_MAP_BLENDER_OBJECT: + GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_LOC_TO_VIEW_MATRIX), &out[0].link); + break; + case SHD_NORMAL_MAP_WORLD: + case SHD_NORMAL_MAP_BLENDER_WORLD: + GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_VIEW_MATRIX), &out[0].link); + break; + } + } + + if (out[0].link) { + GPU_link(mat, "vec_math_mix", strength, out[0].link, negnorm, &out[0].link); + GPU_link(mat, "vect_normalize", out[0].link, &out[0].link); + } - return GPU_stack_link(mat, "node_normal_map", in, out, normal); + return true; } /* node type definition */ @@ -60,12 +159,13 @@ void register_node_type_sh_normal_map(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_NORMAL_MAP, "Normal Map", NODE_CLASS_OP_VECTOR, 0); - node_type_compatibility(&ntype, NODE_NEW_SHADING); + node_type_compatibility(&ntype, NODE_NEW_SHADING | NODE_OLD_SHADING); node_type_socket_templates(&ntype, sh_node_normal_map_in, sh_node_normal_map_out); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_normal_map); node_type_storage(&ntype, "NodeShaderNormalMap", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, gpu_shader_normal_map); + node_type_exec(&ntype, NULL, NULL, node_shader_exec_normal_map); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_material.c b/source/blender/nodes/shader/nodes/node_shader_output_material.c index bd98dc1279c..336536b21ee 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_material.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_material.c @@ -43,7 +43,7 @@ static int node_shader_gpu_output_material(GPUMaterial *mat, bNode *UNUSED(node) GPU_stack_link(mat, "node_output_material", in, out, &outlink); GPU_material_output_link(mat, outlink); - return 1; + return true; } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_world.c b/source/blender/nodes/shader/nodes/node_shader_output_world.c index ad7389fd56e..f95cc842720 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_world.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_world.c @@ -42,7 +42,7 @@ static int node_shader_gpu_output_world(GPUMaterial *mat, bNode *UNUSED(node), b GPU_stack_link(mat, "node_output_world", in, out, &outlink); GPU_material_output_link(mat, outlink); - return 1; + return true; } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c index 2b43667a009..2f8f95b0675 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c @@ -60,7 +60,6 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, bNode *node, bNodeE ImageUser *iuser = NULL; NodeTexImage *tex = node->storage; int isdata = tex->color_space == SHD_COLORSPACE_NONE; - int ret; if (!ima) return GPU_stack_link(mat, "node_tex_environment_empty", in, out); @@ -77,21 +76,19 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, bNode *node, bNodeE node_shader_gpu_tex_mapping(mat, node, in, out); if (tex->projection == SHD_PROJ_EQUIRECTANGULAR) - ret = GPU_stack_link(mat, "node_tex_environment_equirectangular", in, out, GPU_image(ima, iuser, isdata)); + GPU_stack_link(mat, "node_tex_environment_equirectangular", in, out, GPU_image(ima, iuser, isdata)); else - ret = GPU_stack_link(mat, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata)); + GPU_stack_link(mat, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata)); - if (ret) { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 && - GPU_material_do_color_management(mat)) - { - GPU_link(mat, "srgb_to_linearrgb", out[0].link, &out[0].link); - } - BKE_image_release_ibuf(ima, ibuf, NULL); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 && + GPU_material_do_color_management(mat)) + { + GPU_link(mat, "srgb_to_linearrgb", out[0].link, &out[0].link); } + BKE_image_release_ibuf(ima, ibuf, NULL); - return ret; + return true; } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index f87e792399b..f0a8cda045e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -60,7 +60,6 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat ImageUser *iuser = NULL; NodeTexImage *tex = node->storage; int isdata = tex->color_space == SHD_COLORSPACE_NONE; - int ret; if (!ima) return GPU_stack_link(mat, "node_tex_image_empty", in, out); @@ -70,19 +69,18 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat node_shader_gpu_tex_mapping(mat, node, in, out); - ret = GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); - if (ret) { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 && - GPU_material_do_color_management(mat)) - { - GPU_link(mat, "srgb_to_linearrgb", out[0].link, &out[0].link); - } - BKE_image_release_ibuf(ima, ibuf, NULL); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + if ((tex->color_space == SHD_COLORSPACE_COLOR) && + ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 && + GPU_material_do_color_management(mat)) + { + GPU_link(mat, "srgb_to_linearrgb", out[0].link, &out[0].link); } + BKE_image_release_ibuf(ima, ibuf, NULL); - return ret; + return true; } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_texture.c b/source/blender/nodes/shader/nodes/node_shader_texture.c index 5cdbaf444f8..6edf6c2a0b4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_texture.c +++ b/source/blender/nodes/shader/nodes/node_shader_texture.c @@ -123,22 +123,20 @@ static int gpu_shader_texture(GPUMaterial *mat, bNode *node, bNodeExecData *UNUS if (tex && tex->type == TEX_IMAGE && tex->ima) { GPUNodeLink *texlink = GPU_image(tex->ima, &tex->iuser, false); - int ret = GPU_stack_link(mat, "texture_image", in, out, texlink); + GPU_stack_link(mat, "texture_image", in, out, texlink); - if (ret) { - ImBuf *ibuf = BKE_image_acquire_ibuf(tex->ima, &tex->iuser, NULL); - if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 && - GPU_material_do_color_management(mat)) - { - GPU_link(mat, "srgb_to_linearrgb", out[1].link, &out[1].link); - } - BKE_image_release_ibuf(tex->ima, ibuf, NULL); + ImBuf *ibuf = BKE_image_acquire_ibuf(tex->ima, &tex->iuser, NULL); + if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 && + GPU_material_do_color_management(mat)) + { + GPU_link(mat, "srgb_to_linearrgb", out[1].link, &out[1].link); } + BKE_image_release_ibuf(tex->ima, ibuf, NULL); - return ret; + return true; } - else - return 0; + + return false; } void register_node_type_sh_texture(void) diff --git a/source/blender/nodes/shader/nodes/node_shader_vectMath.c b/source/blender/nodes/shader/nodes/node_shader_vectMath.c index 45a11c5f799..26045dfca04 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vectMath.c +++ b/source/blender/nodes/shader/nodes/node_shader_vectMath.c @@ -130,10 +130,10 @@ static int gpu_shader_vect_math(GPUMaterial *mat, bNode *node, bNodeExecData *UN } break; default: - return 0; + return false; } - return 1; + return true; } void register_node_type_sh_vect_math(void) diff --git a/source/blender/nodes/shader/nodes/node_shader_vectTransform.c b/source/blender/nodes/shader/nodes/node_shader_vectTransform.c index 35a12d52b2d..3c165cfcb8a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vectTransform.c +++ b/source/blender/nodes/shader/nodes/node_shader_vectTransform.c @@ -157,8 +157,6 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, bNode *node, bNodeExecDat struct GPUNodeLink *inputlink; struct GPUNodeLink *fromto; - int ret = 0; - const char *vtransform = "direction_transform_m4v3"; const char *ptransform = "point_transform_m4v3"; const char *func_name = 0; @@ -180,24 +178,24 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, bNode *node, bNodeExecDat /* For cycles we have inverted Z */ /* TODO: pass here the correct matrices */ if (nodeprop->convert_from == SHD_VECT_TRANSFORM_SPACE_CAMERA && nodeprop->convert_to != SHD_VECT_TRANSFORM_SPACE_CAMERA) { - ret = GPU_link(mat, "invert_z", inputlink, &inputlink); + GPU_link(mat, "invert_z", inputlink, &inputlink); } - ret = GPU_link(mat, func_name, inputlink, fromto, &out[0].link); + GPU_link(mat, func_name, inputlink, fromto, &out[0].link); if (nodeprop->convert_to == SHD_VECT_TRANSFORM_SPACE_CAMERA && nodeprop->convert_from != SHD_VECT_TRANSFORM_SPACE_CAMERA) { - ret = GPU_link(mat, "invert_z", out[0].link, &out[0].link); + GPU_link(mat, "invert_z", out[0].link, &out[0].link); } } else { - ret = GPU_link(mat, func_name, inputlink, fromto, &out[0].link); + GPU_link(mat, func_name, inputlink, fromto, &out[0].link); } } else - ret = GPU_link(mat, "set_rgb", inputlink, &out[0].link); + GPU_link(mat, "set_rgb", inputlink, &out[0].link); if (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_NORMAL) - return GPU_link(mat, "vect_normalize", out[0].link, &out[0].link); + GPU_link(mat, "vect_normalize", out[0].link, &out[0].link); - return ret; + return true; } void register_node_type_sh_vect_transform(void) diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_absorption.c b/source/blender/nodes/shader/nodes/node_shader_volume_absorption.c index a271162bd19..4861871e8d3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_absorption.c +++ b/source/blender/nodes/shader/nodes/node_shader_volume_absorption.c @@ -42,7 +42,7 @@ static bNodeSocketTemplate sh_node_volume_absorption_out[] = { static int node_shader_gpu_volume_absorption(GPUMaterial *UNUSED(mat), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *UNUSED(in), GPUNodeStack *UNUSED(out)) { - return 0; + return false; } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_scatter.c b/source/blender/nodes/shader/nodes/node_shader_volume_scatter.c index cb1377348e5..0c5647b4ba8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_scatter.c +++ b/source/blender/nodes/shader/nodes/node_shader_volume_scatter.c @@ -43,7 +43,7 @@ static bNodeSocketTemplate sh_node_volume_scatter_out[] = { static int node_shader_gpu_volume_scatter(GPUMaterial *UNUSED(mat), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *UNUSED(in), GPUNodeStack *UNUSED(out)) { - return 0; + return false; } /* node type definition */ diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h index 9000cbb90d8..2263c271ccf 100644 --- a/source/blender/nodes/texture/node_texture_util.h +++ b/source/blender/nodes/texture/node_texture_util.h @@ -53,7 +53,6 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" -#include "BKE_blender.h" #include "BKE_colortools.h" #include "BKE_global.h" #include "BKE_image.h" diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp index 4d114e182c0..648ef132655 100644 --- a/source/blender/physics/intern/BPH_mass_spring.cpp +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -138,7 +138,7 @@ static bool collision_response(ClothModifierData *clmd, CollisionModifierData *c bool result = false; float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3]; - float epsilon2 = BLI_bvhtree_getepsilon(collmd->bvhtree); + float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); float margin_distance = (float)collpair->distance - epsilon2; float mag_v_rel; diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index ce8f1247919..a0722af522b 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -223,7 +223,11 @@ static PyObject *bpy_bmfaceseq_get(BPy_BMesh *self, void *UNUSED(closure)) } PyDoc_STRVAR(bpy_bmloopseq_doc, -"This meshes face sequence (read-only).\n\n:type: :class:`BMLoopSeq`" +"This meshes loops (read-only).\n\n:type: :class:`BMLoopSeq`\n" +"\n" +".. note::\n" +"\n" +" Loops must be accessed via faces, this is only exposed for layer access.\n" ); static PyObject *bpy_bmloopseq_get(BPy_BMesh *self, void *UNUSED(closure)) { @@ -912,7 +916,7 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args) /* python won't ensure matching uv/mtex */ BM_mesh_cd_validate(bm); - BM_mesh_bm_to_me(bm, me, false); + BM_mesh_bm_to_me(bm, me, (&(struct BMeshToMeshParams){0})); /* we could have the user do this but if they forget blender can easy crash * since the references arrays for the objects derived meshes are now invalid */ @@ -1071,7 +1075,10 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject * bm = self->bm; - BM_mesh_bm_from_me(bm, me, use_fnorm, use_shape_key, shape_key_index + 1); + BM_mesh_bm_from_me( + bm, me, (&(struct BMeshFromMeshParams){ + .calc_face_normal = use_fnorm, .use_shapekey = use_shape_key, .active_shapekey = shape_key_index + 1, + })); Py_RETURN_NONE; } diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 0dfff9b4a7b..69f1e297b43 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -332,17 +332,21 @@ PyDoc_STRVAR(py_blf_shadow_doc, static PyObject *py_blf_shadow(PyObject *UNUSED(self), PyObject *args) { int level, fontid; - float r, g, b, a; + float rgba[4]; - if (!PyArg_ParseTuple(args, "iiffff:blf.shadow", &fontid, &level, &r, &g, &b, &a)) + if (!PyArg_ParseTuple( + args, "iiffff:blf.shadow", + &fontid, &level, &rgba[0], &rgba[1], &rgba[2], &rgba[3])) + { return NULL; + } if (level != 0 && level != 3 && level != 5) { PyErr_SetString(PyExc_TypeError, "blf.shadow expected arg to be in (0, 3, 5)"); return NULL; } - BLF_shadow(fontid, level, r, g, b, a); + BLF_shadow(fontid, level, rgba); Py_RETURN_NONE; } diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index dd32c913f78..7f13a7a4d94 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -45,7 +45,7 @@ #ifdef _WIN32 #include "BLI_path_util.h" /* BLI_setenv() */ -#include "BLI_math_base.h" /* finite() */ +#include "BLI_math_base.h" /* isfinite() */ #endif /* array utility function */ @@ -1026,7 +1026,7 @@ bool PyC_RunString_AsNumber(const char *expr, double *value, const char *filenam if (val == -1 && PyErr_Occurred()) { ok = false; } - else if (!finite(val)) { + else if (!isfinite(val)) { *value = 0.0; } else { diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index c09b39139c2..5bbfb4912e6 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -35,7 +35,7 @@ #include "BKE_appdir.h" #include "BKE_global.h" /* XXX, G.main only */ -#include "BKE_blender.h" +#include "BKE_blender_version.h" #include "BKE_bpath.h" #include "RNA_types.h" diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 595bb7b0f22..17617a231b2 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -48,7 +48,7 @@ #include "BLI_utildefines.h" #include "BKE_appdir.h" -#include "BKE_blender.h" +#include "BKE_blender_version.h" #include "BKE_global.h" #include "DNA_ID.h" diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index f9c0982a4c3..9b477e384db 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -273,6 +273,22 @@ float BPY_driver_exec(ChannelDriver *driver, const float evaltime) if (driver_arg == NULL) { driver_arg = PyFloat_FromDouble(0.0); + dvar->curval = 0.0f; + } + else { + /* no need to worry about overflow here, values from RNA are within limits. */ + if (PyFloat_CheckExact(driver_arg)) { + dvar->curval = (float)PyFloat_AsDouble(driver_arg); + } + else if (PyLong_CheckExact(driver_arg)) { + dvar->curval = (float)PyLong_AsLong(driver_arg); + } + else if (PyBool_Check(driver_arg)) { + dvar->curval = (driver_arg == Py_True); + } + else { + dvar->curval = 0.0f; + } } } else @@ -331,7 +347,7 @@ float BPY_driver_exec(ChannelDriver *driver, const float evaltime) if (use_gil) PyGILState_Release(gilstate); - if (finite(result)) { + if (isfinite(result)) { return (float)result; } else { diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index b559623f619..11af0836e1c 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -31,10 +31,6 @@ #include <Python.h> -#ifdef WIN32 -# include "BLI_math_base.h" /* finite */ -#endif - #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" diff --git a/source/blender/python/intern/bpy_library_write.c b/source/blender/python/intern/bpy_library_write.c index ab239bbfa8c..f582cebb260 100644 --- a/source/blender/python/intern/bpy_library_write.c +++ b/source/blender/python/intern/bpy_library_write.c @@ -35,7 +35,7 @@ #include "BLI_path_util.h" #include "BKE_library.h" -#include "BKE_blender.h" +#include "BKE_blendfile.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_report.h" @@ -66,13 +66,15 @@ PyDoc_STRVAR(bpy_lib_write_doc, " :type relative_remap: bool\n" " :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n" " :type fake_user: bool\n" +" :arg compress: When True, write a compressed blend file.\n" +" :type compress: bool\n" ); static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) { static const char *kwlist[] = { "filepath", "datablocks", /* optional */ - "relative_remap", "fake_user", + "relative_remap", "fake_user", "compress", NULL, }; @@ -80,15 +82,16 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject const char *filepath; char filepath_abs[FILE_MAX]; PyObject *datablocks = NULL; - bool use_relative_remap = false, use_fake_user = false; + bool use_relative_remap = false, use_fake_user = false, use_compress = false; if (!PyArg_ParseTupleAndKeywords( args, kwds, - "sO!|$O&O&:write", (char **)kwlist, + "sO!|$O&O&O&:write", (char **)kwlist, &filepath, &PySet_Type, &datablocks, PyC_ParseBool, &use_relative_remap, - PyC_ParseBool, &use_fake_user)) + PyC_ParseBool, &use_fake_user, + PyC_ParseBool, &use_compress)) { return NULL; } @@ -100,6 +103,10 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject write_flags |= G_FILE_RELATIVE_REMAP; } + if (use_compress) { + write_flags |= G_FILE_COMPRESS; + } + BLI_strncpy(filepath_abs, filepath, FILE_MAX); BLI_path_abs(filepath_abs, G.main->name); diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index ce9b3e754ac..bce1d923462 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -1314,7 +1314,7 @@ static int icon_id_from_name(const char *name) return 0; } -static EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, PyObject *def, int *defvalue, const short is_enum_flag) +static EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, PyObject *def, int *defvalue, const bool is_enum_flag) { EnumPropertyItem *items; PyObject *item; diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 75490a18ab0..33ac58dadb1 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -7363,12 +7363,8 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param PointerRNA funcptr; int err = 0, i, ret_len = 0, arg_count; int flag = RNA_function_flag(func); - const char is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); - const char is_classmethod = (flag & FUNC_NO_SELF) && (flag & FUNC_USE_SELF_TYPE); - - /* annoying!, need to check if the screen gets set to NULL which is a - * hint that the file was actually re-loaded. */ - char is_valid_wm; + const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); + const bool is_classmethod = (flag & FUNC_NO_SELF) && (flag & FUNC_USE_SELF_TYPE); PropertyRNA *pret_single = NULL; void *retdata_single = NULL; @@ -7395,7 +7391,9 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param if (C == NULL) C = BPy_GetContext(); - is_valid_wm = (CTX_wm_manager(C) != NULL); + /* annoying!, need to check if the screen gets set to NULL which is a + * hint that the file was actually re-loaded. */ + const bool is_valid_wm = (CTX_wm_manager(C) != NULL); bpy_context_set(C, &gilstate); diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index e74a5185ab0..92931eb8090 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -27,7 +27,7 @@ */ #include <Python.h> -#include <float.h> /* FLT_MIN/MAX */ +#include <float.h> /* FLT_MAX */ #include "MEM_guardedalloc.h" @@ -188,18 +188,19 @@ char pyrna_struct_keyframe_insert_doc[] = "\n" " :arg data_path: path to the property to key, analogous to the fcurve's data path.\n" " :type data_path: string\n" -" :arg index: array index of the property to key. Defaults to -1 which will key all indices or a single channel " - "if the property is not an array.\n" +" :arg index: array index of the property to key.\n" +" Defaults to -1 which will key all indices or a single channel if the property is not an array.\n" " :type index: int\n" " :arg frame: The frame on which the keyframe is inserted, defaulting to the current frame.\n" " :type frame: float\n" " :arg group: The name of the group the F-Curve should be added to if it doesn't exist yet.\n" " :type group: str\n" -" :arg options: Some optional flags:\n" -" 'NEEDED': Only insert keyframes where they're needed in the relevant F-Curves.\n" -" 'VISUAL': Insert keyframes based on 'visual transforms'.\n" -" 'XYZ_TO_RGB': Color for newly added transformation F-Curves (Location, Rotation, Scale) " - "and also Color is based on the transform axis.\n" +" :arg options: Optional flags:\n" +"\n" +" - ``INSERTKEY_NEEDED`` Only insert keyframes where they're needed in the relevant F-Curves.\n" +" - ``INSERTKEY_VISUAL`` Insert keyframes based on 'visual transforms'.\n" +" - ``INSERTKEY_XYZ_TO_RGB`` Color for newly added transformation F-Curves (Location, Rotation, Scale)\n" +" and also Color is based on the transform axis.\n" " :type flag: set\n" " :return: Success of keyframe insertion.\n" " :rtype: boolean\n" diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c index ee827c06e14..2b8ad6ccb90 100644 --- a/source/blender/python/intern/bpy_util.c +++ b/source/blender/python/intern/bpy_util.c @@ -148,4 +148,4 @@ bool BPy_errors_to_report_ex(ReportList *reports, const bool use_full, const boo bool BPy_errors_to_report(ReportList *reports) { return BPy_errors_to_report_ex(reports, true, true); -}
\ No newline at end of file +} diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index ea5732966b2..71b3cf8ddac 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -1165,9 +1165,9 @@ static void quat__axis_angle_sanitize(float axis[3], float *angle) { if (axis) { if (is_zero_v3(axis) || - !finite(axis[0]) || - !finite(axis[1]) || - !finite(axis[2])) + !isfinite(axis[0]) || + !isfinite(axis[1]) || + !isfinite(axis[2])) { axis[0] = 1.0f; axis[1] = 0.0f; @@ -1182,7 +1182,7 @@ static void quat__axis_angle_sanitize(float axis[3], float *angle) } if (angle) { - if (!finite(*angle)) { + if (!isfinite(*angle)) { *angle = 0.0f; } } diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index 12b97aedbd3..8b6dfb88b73 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -139,6 +139,7 @@ typedef struct ShadeInput { float refcol[4], displace[3]; float strandco, tang[3], nmapnorm[3], nmaptang[4], stress, winspeed[4]; float duplilo[3], dupliuv[3]; + float tangents[8][4]; /* 8 = MAX_MTFACE */ ShadeInputUV uv[8]; /* 8 = MAX_MTFACE */ ShadeInputCol col[8]; /* 8 = MAX_MCOL */ diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index cef3a073084..6de5da3795a 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -334,6 +334,8 @@ typedef struct ObjectRen { char (*mcol)[MAX_CUSTOMDATA_LAYER_NAME]; int actmtface, actmcol, bakemtface; + char tangent_mask; /* which tangent layer should be calculated */ + float obmat[4][4]; /* only used in convertblender.c, for instancing */ /* used on makeraytree */ diff --git a/source/blender/render/intern/include/renderdatabase.h b/source/blender/render/intern/include/renderdatabase.h index 167ebc58030..b576d69d806 100644 --- a/source/blender/render/intern/include/renderdatabase.h +++ b/source/blender/render/intern/include/renderdatabase.h @@ -76,7 +76,7 @@ typedef struct VlakTableNode { int *origindex; int totmtface, totmcol; float *surfnor; - float *tangent; + float *tangent_arrays[MAX_MTFACE]; struct RadFace **radface; } VlakTableNode; @@ -137,7 +137,7 @@ struct MTFace *RE_vlakren_get_tface(struct ObjectRen *obr, VlakRen *ren, int n, struct MCol *RE_vlakren_get_mcol(struct ObjectRen *obr, VlakRen *ren, int n, char **name, int verify); int *RE_vlakren_get_origindex(struct ObjectRen *obr, VlakRen *vlak, int verify); float *RE_vlakren_get_surfnor(struct ObjectRen *obr, VlakRen *ren, int verify); -float *RE_vlakren_get_nmap_tangent(struct ObjectRen *obr, VlakRen *ren, int verify); +float *RE_vlakren_get_nmap_tangent(ObjectRen *obr, VlakRen *vlak, int index, bool verify); RadFace **RE_vlakren_get_radface(struct ObjectRen *obr, VlakRen *ren, int verify); void RE_vlakren_get_normal(struct Render *re, struct ObjectInstanceRen *obi, struct VlakRen *vlr, float *nor); diff --git a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp index 00c1129fa85..02a49fc3c8f 100644 --- a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp +++ b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp @@ -109,9 +109,9 @@ void rtbuild_add(RTBuilder *b, RayObject *o) if (bb[0] > bb[3] || bb[1] > bb[4] || bb[2] > bb[5]) return; /* skip objects with inf bounding boxes */ - if (!finite(bb[0]) || !finite(bb[1]) || !finite(bb[2])) + if (!isfinite(bb[0]) || !isfinite(bb[1]) || !isfinite(bb[2])) return; - if (!finite(bb[3]) || !finite(bb[4]) || !finite(bb[5])) + if (!isfinite(bb[3]) || !isfinite(bb[4]) || !isfinite(bb[5])) return; /* skip objects with zero bounding box, they are of no use, and * will give problems in rtbuild_heuristic_object_split later */ diff --git a/source/blender/render/intern/source/bake.c b/source/blender/render/intern/source/bake.c index b2f8c79c197..31e461b4536 100644 --- a/source/blender/render/intern/source/bake.c +++ b/source/blender/render/intern/source/bake.c @@ -926,7 +926,7 @@ static void *do_bake_thread(void *bs_v) void RE_bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter) { /* must check before filtering */ - const short is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf); + const bool is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf); /* Margin */ if (filter) { diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c index eff021c9b14..1a0ef4e64d4 100644 --- a/source/blender/render/intern/source/bake_api.c +++ b/source/blender/render/intern/source/bake_api.c @@ -425,7 +425,7 @@ static TriTessFace *mesh_calc_tri_tessface( if (tangent) { DM_ensure_normals(dm); - DM_calc_loop_tangents(dm); + DM_calc_loop_tangents(dm, true, NULL, 0); tspace = dm->getLoopDataArray(dm, CD_TANGENT); BLI_assert(tspace); diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index ccf54cb6bcd..b6ee88de290 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -304,7 +304,7 @@ static void calc_tangent_vector(ObjectRen *obr, VlakRen *vlr, int do_tangent) typedef struct { ObjectRen *obr; - + int mtface_index; } SRenderMeshToTangent; /* interface */ @@ -337,7 +337,7 @@ static void GetTextureCoordinate(const SMikkTSpaceContext *pContext, float r_uv[ //assert(vert_index>=0 && vert_index<4); SRenderMeshToTangent *pMesh = (SRenderMeshToTangent *) pContext->m_pUserData; VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num); - MTFace *tface= RE_vlakren_get_tface(pMesh->obr, vlr, pMesh->obr->actmtface, NULL, 0); + MTFace *tface= RE_vlakren_get_tface(pMesh->obr, vlr, pMesh->mtface_index, NULL, 0); const float *coord; if (tface != NULL) { @@ -371,7 +371,7 @@ static void SetTSpace(const SMikkTSpaceContext *pContext, const float fvTangent[ //assert(vert_index>=0 && vert_index<4); SRenderMeshToTangent *pMesh = (SRenderMeshToTangent *) pContext->m_pUserData; VlakRen *vlr = RE_findOrAddVlak(pMesh->obr, face_num); - float *ftang = RE_vlakren_get_nmap_tangent(pMesh->obr, vlr, 1); + float *ftang = RE_vlakren_get_nmap_tangent(pMesh->obr, vlr, pMesh->mtface_index, true); if (ftang!=NULL) { copy_v3_v3(&ftang[iVert*4+0], fvTangent); ftang[iVert*4+3]=fSign; @@ -457,7 +457,12 @@ static void calc_vertexnormals(Render *UNUSED(re), ObjectRen *obr, bool do_verte sInterface.m_getNormal = GetNormal; sInterface.m_setTSpaceBasic = SetTSpace; - genTangSpaceDefault(&sContext); + for (a = 0; a < MAX_MTFACE; a++) { + if (obr->tangent_mask & 1 << a) { + mesh2tangent.mtface_index = a; + genTangSpaceDefault(&sContext); + } + } } } @@ -3113,7 +3118,8 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) float xn, yn, zn, imat[3][3], mat[4][4]; //nor[3], float *orco = NULL; short (*loop_nors)[4][3] = NULL; - bool need_orco = false, need_stress = false, need_nmap_tangent = false, need_tangent = false, need_origindex = false; + bool need_orco = false, need_stress = false, need_tangent = false, need_origindex = false; + bool need_nmap_tangent_concrete = false; int a, a1, ok, vertofs; int end, totvert = 0; bool do_autosmooth = false, do_displace = false; @@ -3148,9 +3154,11 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) if (ma->mode_l & MA_NORMAP_TANG) { if (me->mtpoly==NULL) { need_orco= 1; - need_tangent= 1; } - need_nmap_tangent= 1; + need_tangent= 1; + } + if (ma->mode2_l & MA_TANGENT_CONCRETE) { + need_nmap_tangent_concrete = true; } } } @@ -3161,7 +3169,7 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) need_orco= 1; need_tangent= 1; } - need_nmap_tangent= 1; + need_nmap_tangent_concrete = true; } /* check autosmooth and displacement, we then have to skip only-verts optimize @@ -3274,14 +3282,13 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) /* store customdata names, because DerivedMesh is freed */ RE_set_customdata_names(obr, &dm->faceData); - /* add tangent layer if we need one */ - if (need_nmap_tangent!=0 && CustomData_get_layer_index(&dm->faceData, CD_TANGENT) == -1) { - bool generate_data = false; - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { - dm->calcLoopTangents(dm); - generate_data = true; - } - DM_generate_tangent_tessface_data(dm, generate_data); + /* add tangent layers if we need */ + if ((ma->nmap_tangent_names_count && need_nmap_tangent_concrete) || need_tangent) { + dm->calcLoopTangents( + dm, need_tangent, + (const char (*)[MAX_NAME])ma->nmap_tangent_names, ma->nmap_tangent_names_count); + obr->tangent_mask = dm->tangent_mask; + DM_generate_tangent_tessface_data(dm, need_nmap_tangent_concrete || need_tangent); } /* still to do for keys: the correct local texture coordinate */ @@ -3401,7 +3408,7 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) CustomDataLayer *layer; MTFace *mtface, *mtf; MCol *mcol, *mc; - int index, mtfn= 0, mcn= 0, mtng=0, mln = 0, vindex; + int index, mtfn= 0, mcn= 0, mln = 0, vindex; char *name; int nr_verts = v4!=0 ? 4 : 3; @@ -3424,17 +3431,24 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) for (vindex=0; vindex<nr_verts; vindex++) mc[vindex]=mcol[a*4+rev_tab[vindex]]; } - else if (layer->type == CD_TANGENT && mtng < 1) { - if (need_nmap_tangent != 0) { - const float * tangent = (const float *) layer->data; - float * ftang = RE_vlakren_get_nmap_tangent(obr, vlr, 1); + else if (layer->type == CD_TANGENT) { + if (need_nmap_tangent_concrete || need_tangent) { + int uv_start = CustomData_get_layer_index(&dm->faceData, CD_MTFACE); + int uv_index = CustomData_get_named_layer_index(&dm->faceData, CD_MTFACE, layer->name); + BLI_assert(uv_start >= 0 && uv_index >= 0); + if ((uv_start < 0 || uv_index < 0)) + continue; + int n = uv_index - uv_start; + + const float *tangent = (const float *) layer->data; + float *ftang = RE_vlakren_get_nmap_tangent(obr, vlr, n, true); + for (vindex=0; vindex<nr_verts; vindex++) { copy_v4_v4(ftang+vindex*4, tangent+a*16+rev_tab[vindex]*4); mul_mat3_m4_v3(mat, ftang+vindex*4); normalize_v3(ftang+vindex*4); } } - mtng++; } else if (layer->type == CD_TESSLOOPNORMAL && mln < 1) { if (loop_nors) { @@ -3542,7 +3556,7 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) } if (recalc_normals!=0 || need_tangent!=0) - calc_vertexnormals(re, obr, recalc_normals, need_tangent, need_nmap_tangent); + calc_vertexnormals(re, obr, recalc_normals, need_tangent, need_nmap_tangent_concrete); } MEM_SAFE_FREE(loop_nors); diff --git a/source/blender/render/intern/source/multires_bake.c b/source/blender/render/intern/source/multires_bake.c index 8eb6e7000ab..8c6d9c5f951 100644 --- a/source/blender/render/intern/source/multires_bake.c +++ b/source/blender/render/intern/source/multires_bake.c @@ -456,7 +456,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, Image *ima, bool require_t if (require_tangent) { if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) - DM_calc_loop_tangents(dm); + DM_calc_loop_tangents(dm, true, NULL, 0); pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); } diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 93666bd2a48..c88e3b36e27 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -2860,7 +2860,7 @@ static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportLis SceneRenderView *srv; bool active_view = false; - if ((scene->r.scemode & R_MULTIVIEW) == 0) + if (camera == NULL || (scene->r.scemode & R_MULTIVIEW) == 0) return true; for (srv = scene->r.views.first; srv; srv = srv->next) { diff --git a/source/blender/render/intern/source/rayshade.c b/source/blender/render/intern/source/rayshade.c index 0f77cd9768d..9aac5ed1f1d 100644 --- a/source/blender/render/intern/source/rayshade.c +++ b/source/blender/render/intern/source/rayshade.c @@ -184,7 +184,7 @@ void freeraytree(Render *re) #endif } -static int is_raytraceable_vlr(Render *re, VlakRen *vlr) +static bool is_raytraceable_vlr(Render *re, VlakRen *vlr) { /* note: volumetric must be tracable, wire must not */ if ((re->flag & R_BAKE_TRACE) || (vlr->flag & R_TRACEBLE) || (vlr->mat->material_type == MA_TYPE_VOLUME)) @@ -193,7 +193,7 @@ static int is_raytraceable_vlr(Render *re, VlakRen *vlr) return 0; } -static int is_raytraceable(Render *re, ObjectInstanceRen *obi) +static bool is_raytraceable(Render *re, ObjectInstanceRen *obi) { int v; ObjectRen *obr = obi->obr; diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index e8db096c9a5..d3d26011a57 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -70,6 +70,7 @@ #include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "DNA_texture_types.h" +#include "DNA_listBase.h" #include "DNA_particle_types.h" #include "BKE_customdata.h" @@ -380,19 +381,28 @@ float *RE_vlakren_get_surfnor(ObjectRen *obr, VlakRen *vlak, int verify) return surfnor + (vlak->index & 255)*RE_SURFNOR_ELEMS; } -float *RE_vlakren_get_nmap_tangent(ObjectRen *obr, VlakRen *vlak, int verify) +float *RE_vlakren_get_nmap_tangent(ObjectRen *obr, VlakRen *vlak, int index, bool verify) { - float *tangent; + float **tangents; int nr= vlak->index>>8; - tangent= obr->vlaknodes[nr].tangent; - if (tangent==NULL) { - if (verify) - tangent= obr->vlaknodes[nr].tangent= MEM_callocN(256*RE_NMAP_TANGENT_ELEMS*sizeof(float), "tangent table"); + tangents = obr->vlaknodes[nr].tangent_arrays; + + if (index + 1 > 8) { + return NULL; + } + + index = index < 0 ? 0: index; + + if (tangents[index] == NULL) { + if (verify) { + tangents[index] = MEM_callocN(256*RE_NMAP_TANGENT_ELEMS*sizeof(float), "tangent table"); + } else return NULL; } - return tangent + (vlak->index & 255)*RE_NMAP_TANGENT_ELEMS; + + return tangents[index] + (vlak->index & 255)*RE_NMAP_TANGENT_ELEMS; } RadFace **RE_vlakren_get_radface(ObjectRen *obr, VlakRen *vlak, int verify) @@ -415,7 +425,8 @@ VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr) VlakRen *vlr1 = RE_findOrAddVlak(obr, obr->totvlak++); MTFace *mtface, *mtface1; MCol *mcol, *mcol1; - float *surfnor, *surfnor1, *tangent, *tangent1; + float *surfnor, *surfnor1; + float *tangent, *tangent1; int *origindex, *origindex1; RadFace **radface, **radface1; int i, index = vlr1->index; @@ -447,9 +458,11 @@ VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr) copy_v3_v3(surfnor1, surfnor); } - tangent= RE_vlakren_get_nmap_tangent(obr, vlr, 0); - if (tangent) { - tangent1= RE_vlakren_get_nmap_tangent(obr, vlr1, 1); + for (i=0; i < MAX_MTFACE; i++) { + tangent = RE_vlakren_get_nmap_tangent(obr, vlr, i, false); + if (!tangent) + continue; + tangent1 = RE_vlakren_get_nmap_tangent(obr, vlr1, i, true); memcpy(tangent1, tangent, sizeof(float)*RE_NMAP_TANGENT_ELEMS); } @@ -790,8 +803,10 @@ void free_renderdata_vlaknodes(VlakTableNode *vlaknodes) MEM_freeN(vlaknodes[a].origindex); if (vlaknodes[a].surfnor) MEM_freeN(vlaknodes[a].surfnor); - if (vlaknodes[a].tangent) - MEM_freeN(vlaknodes[a].tangent); + for (int b = 0; b < MAX_MTFACE; b++) { + if (vlaknodes[a].tangent_arrays[b]) + MEM_freeN(vlaknodes[a].tangent_arrays[b]); + } if (vlaknodes[a].radface) MEM_freeN(vlaknodes[a].radface); } diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index e60a5a70a7f..6e01921a6a7 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -884,7 +884,10 @@ void shade_input_set_shade_texco(ShadeInput *shi) float u = shi->u, v = shi->v; float l = 1.0f + u + v, dl; int mode = shi->mode; /* or-ed result for all nodes */ + int mode2 = shi->mode2; short texco = shi->mat->texco; + const bool need_mikk_tangent = (mode & MA_NORMAP_TANG || R.flag & R_NEED_TANGENT); + const bool need_mikk_tangent_concrete = (mode2 & MA_TANGENT_CONCRETE) != 0; /* calculate dxno */ if (shi->vlr->flag & R_SMOOTH) { @@ -905,8 +908,8 @@ void shade_input_set_shade_texco(ShadeInput *shi) } /* calc tangents */ - if (mode & (MA_TANGENT_V | MA_NORMAP_TANG) || R.flag & R_NEED_TANGENT) { - const float *tangent, *s1, *s2, *s3; + if (mode & (MA_TANGENT_V | MA_NORMAP_TANG) || mode2 & MA_TANGENT_CONCRETE || R.flag & R_NEED_TANGENT) { + const float *s1, *s2, *s3; float tl, tu, tv; if (shi->vlr->flag & R_SMOOTH) { @@ -943,14 +946,18 @@ void shade_input_set_shade_texco(ShadeInput *shi) } } - if (mode & MA_NORMAP_TANG || R.flag & R_NEED_TANGENT) { - tangent = RE_vlakren_get_nmap_tangent(obr, shi->vlr, 0); + if (need_mikk_tangent || need_mikk_tangent_concrete) { + int j1 = shi->i1, j2 = shi->i2, j3 = shi->i3; + float c0[3], c1[3], c2[3]; + int acttang = obr->actmtface; - if (tangent) { - int j1 = shi->i1, j2 = shi->i2, j3 = shi->i3; - float c0[3], c1[3], c2[3]; + vlr_set_uv_indices(shi->vlr, &j1, &j2, &j3); - vlr_set_uv_indices(shi->vlr, &j1, &j2, &j3); + /* cycle through all tangent in vlakren */ + for (int i = 0; i < MAX_MTFACE; i++) { + const float *tangent = RE_vlakren_get_nmap_tangent(obr, shi->vlr, i, false); + if (!tangent) + continue; copy_v3_v3(c0, &tangent[j1 * 4]); copy_v3_v3(c1, &tangent[j2 * 4]); @@ -966,13 +973,19 @@ void shade_input_set_shade_texco(ShadeInput *shi) /* we don't normalize the interpolated TBN tangent * corresponds better to how it's done in game engines */ - shi->nmaptang[0] = (tl * c2[0] - tu * c0[0] - tv * c1[0]); - shi->nmaptang[1] = (tl * c2[1] - tu * c0[1] - tv * c1[1]); - shi->nmaptang[2] = (tl * c2[2] - tu * c0[2] - tv * c1[2]); + shi->tangents[i][0] = (tl * c2[0] - tu * c0[0] - tv * c1[0]); + shi->tangents[i][1] = (tl * c2[1] - tu * c0[1] - tv * c1[1]); + shi->tangents[i][2] = (tl * c2[2] - tu * c0[2] - tv * c1[2]); /* the sign is the same for all 3 vertices of any * non degenerate triangle. */ - shi->nmaptang[3] = tangent[j1 * 4 + 3]; + shi->tangents[i][3] = tangent[j1 * 4 + 3]; + + if (acttang == i && need_mikk_tangent) { + for (int m = 0; m < 4; m++) { + shi->nmaptang[m] = shi->tangents[i][m]; + } + } } } } diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index af8a7cca8dd..01188cb7f65 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -58,6 +58,7 @@ set(SRC intern/wm_draw.c intern/wm_event_system.c intern/wm_files.c + intern/wm_files_link.c intern/wm_gesture.c intern/wm_init_exit.c intern/wm_jobs.c diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index d9466cbd035..d4c3928bd6c 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -253,7 +253,7 @@ static void wm_cursor_warp_relative(wmWindow *win, int x, int y) } /* give it a modal keymap one day? */ -int wm_cursor_arrow_move(wmWindow *win, wmEvent *event) +bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event) { if (win && event->val == KM_PRESS) { if (event->type == UPARROWKEY) { diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index f8a879d0485..8f15d94f538 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -43,7 +43,6 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BLI_math_base.h" #include "BIF_gl.h" diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 24d4144ec17..4f9d48450f6 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -410,15 +410,15 @@ void wm_event_do_notifiers(bContext *C) CTX_wm_window_set(C, NULL); } -static int wm_event_always_pass(wmEvent *event) +static int wm_event_always_pass(const wmEvent *event) { /* some events we always pass on, to ensure proper communication */ - return ISTIMER(event->type) || (event->type == WINDEACTIVATE) || (event->type == EVT_BUT_OPEN); + return ISTIMER(event->type) || (event->type == WINDEACTIVATE); } /* ********************* ui handler ******************* */ -static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass) +static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, const wmEvent *event, int always_pass) { ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); @@ -1526,7 +1526,7 @@ int WM_userdef_event_map(int kmitype) } -static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi) +static int wm_eventmatch(const wmEvent *winevent, wmKeyMapItem *kmi) { int kmitype = WM_userdef_event_map(kmi->type); @@ -1921,7 +1921,7 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand return action; } -static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event) +static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, const wmEvent *event) { int action = WM_HANDLER_CONTINUE; @@ -1933,7 +1933,7 @@ static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHa return wm_handler_fileselect_do(C, handlers, handler, event->val); } -static bool handler_boundbox_test(wmEventHandler *handler, wmEvent *event) +static bool handler_boundbox_test(wmEventHandler *handler, const wmEvent *event) { if (handler->bbwin) { if (handler->bblocal) { @@ -2197,12 +2197,6 @@ static int wm_event_inside_i(wmEvent *event, rcti *rect) return 1; if (BLI_rcti_isect_pt_v(rect, &event->x)) return 1; - if (event->type == MOUSEMOVE) { - if (BLI_rcti_isect_pt_v(rect, &event->prevx)) { - return 1; - } - return 0; - } return 0; } @@ -2245,7 +2239,7 @@ static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar) /* called on mousemove, check updates for paintcursors */ /* context was set on active area and region */ -static void wm_paintcursor_test(bContext *C, wmEvent *event) +static void wm_paintcursor_test(bContext *C, const wmEvent *event) { wmWindowManager *wm = CTX_wm_manager(C); @@ -2312,7 +2306,7 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even } /* filter out all events of the pie that spawned the last pie unless it's a release event */ -static bool wm_event_pie_filter(wmWindow *win, wmEvent *event) +static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event) { if (win->lock_pie_event && win->lock_pie_event == event->type) { if (event->val == KM_RELEASE) { @@ -2365,7 +2359,7 @@ void wm_event_do_handlers(bContext *C) if (is_playing_sound == 0) { const float time = BKE_sound_sync_scene(scene); - if (finite(time)) { + if (isfinite(time)) { int ncfra = time * (float)FPS + 0.5f; if (ncfra != scene->r.cfra) { scene->r.cfra = ncfra; @@ -2432,7 +2426,6 @@ void wm_event_do_handlers(bContext *C) if ((action & WM_HANDLER_BREAK) == 0) { ScrArea *sa; ARegion *ar; - int doit = 0; /* Note: setting subwin active should be done here, after modal handlers have been done */ if (event->type == MOUSEMOVE) { @@ -2484,8 +2477,6 @@ void wm_event_do_handlers(bContext *C) if (CTX_wm_window(C) == NULL) return; - doit |= (BLI_rcti_isect_pt_v(&ar->winrct, &event->x)); - if (action & WM_HANDLER_BREAK) break; } @@ -2518,18 +2509,12 @@ void wm_event_do_handlers(bContext *C) return; } - /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad? - * doing it on ghost queue gives errors when mousemoves go over area borders */ - if (doit && win->screen->subwinactive != win->screen->mainwin) { - win->eventstate->prevx = event->x; - win->eventstate->prevy = event->y; - //printf("win->eventstate->prev = %d %d\n", event->x, event->y); - } - else { - //printf("not setting prev to %d %d\n", event->x, event->y); - } } - + + /* update previous mouse position for following events to use */ + win->eventstate->prevx = event->x; + win->eventstate->prevy = event->y; + /* unlink and free here, blender-quit then frees all */ BLI_remlink(&win->queue, event); wm_event_free(event); @@ -2912,6 +2897,7 @@ static int convert_key(GHOST_TKey key) case GHOST_kKeyQuote: return QUOTEKEY; case GHOST_kKeyComma: return COMMAKEY; case GHOST_kKeyMinus: return MINUSKEY; + case GHOST_kKeyPlus: return PLUSKEY; case GHOST_kKeyPeriod: return PERIODKEY; case GHOST_kKeySlash: return SLASHKEY; @@ -3124,7 +3110,7 @@ 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) +static bool wm_event_is_double_click(wmEvent *event, const wmEvent *event_state) { if ((event->type == event_state->prevtype) && (event_state->prevval == KM_RELEASE) && diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index ce722c732e6..36b819d3495 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -26,7 +26,7 @@ /** \file blender/windowmanager/intern/wm_files.c * \ingroup wm * - * User level access for blend file read/write, file-history and userprefs. + * User level access for blend file read/write, file-history and userprefs (including relevant operators). */ @@ -62,6 +62,7 @@ #include "BLT_translation.h" +#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */ #include "DNA_object_types.h" #include "DNA_space_types.h" #include "DNA_userdef_types.h" @@ -73,6 +74,8 @@ #include "BKE_utildefines.h" #include "BKE_autoexec.h" #include "BKE_blender.h" +#include "BKE_blendfile.h" +#include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_global.h" @@ -88,6 +91,7 @@ #include "BLO_writefile.h" #include "RNA_access.h" +#include "RNA_define.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -103,6 +107,7 @@ #include "GHOST_Path-api.h" #include "UI_interface.h" +#include "UI_resources.h" #include "UI_view2d.h" #include "GPU_draw.h" @@ -338,7 +343,7 @@ static void wm_init_userdef(bContext *C, const bool from_memory) /* update tempdir from user preferences */ BKE_tempdir_init(U.tempdir); - BKE_userdef_state(); + BKE_blender_userdef_refresh(); } @@ -548,7 +553,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) /* confusing this global... */ G.relbase_valid = 1; - retval = BKE_read_file(C, filepath, reports); + retval = BKE_blendfile_read(C, filepath, reports); /* when loading startup.blend's, we can be left with a blank path */ if (G.main->name[0]) { G.save_over = 1; @@ -569,12 +574,12 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) wm_window_match_do(C, &wmbase); WM_check(C); /* opens window(s), checks keymaps */ - if (retval == BKE_READ_FILE_OK_USERPREFS) { + if (retval == BKE_BLENDFILE_READ_OK_USERPREFS) { /* in case a userdef is read from regular .blend */ wm_init_userdef(C, false); } - if (retval != BKE_READ_FILE_FAIL) { + if (retval != BKE_BLENDFILE_READ_FAIL) { if (do_history) { wm_history_file_update(); } @@ -687,7 +692,7 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c if (!from_memory) { if (BLI_access(startstr, R_OK) == 0) { - success = (BKE_read_file(C, startstr, NULL) != BKE_READ_FILE_FAIL); + success = (BKE_blendfile_read(C, startstr, NULL) != BKE_BLENDFILE_READ_FAIL); } if (BLI_listbase_is_empty(&U.themes)) { if (G.debug & G_DEBUG) @@ -702,7 +707,7 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c } if (success == 0) { - success = BKE_read_file_from_memory(C, datatoc_startup_blend, datatoc_startup_blend_size, NULL, true); + success = BKE_blendfile_read_from_memory(C, datatoc_startup_blend, datatoc_startup_blend_size, NULL, true); if (BLI_listbase_is_empty(&wmbase)) { wm_clear_default_size(C); } @@ -717,8 +722,8 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c /* check new prefs only after startup.blend was finished */ if (!from_memory && BLI_exists(prefstr)) { - int done = BKE_read_file_userdef(prefstr, NULL); - if (done != BKE_READ_FILE_FAIL) { + int done = BKE_blendfile_read_userdef(prefstr, NULL); + if (done != BKE_BLENDFILE_READ_FAIL) { read_userdef_from_memory = false; printf("Read new prefs: %s\n", prefstr); } @@ -749,44 +754,6 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c return true; } -int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) -{ - ED_file_read_bookmarks(); - wm_history_file_read(); - return OPERATOR_FINISHED; -} - -int wm_homefile_read_exec(bContext *C, wmOperator *op) -{ - const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings")); - char filepath_buf[FILE_MAX]; - const char *filepath = NULL; - - if (!from_memory) { - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath"); - - /* This can be used when loading of a start-up file should only change - * the scene content but keep the blender UI as it is. */ - wm_open_init_load_ui(op, true); - BKE_BIT_TEST_SET(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI); - - if (RNA_property_is_set(op->ptr, prop)) { - RNA_property_string_get(op->ptr, prop, filepath_buf); - filepath = filepath_buf; - if (BLI_access(filepath, R_OK)) { - BKE_reportf(op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath); - return OPERATOR_CANCELLED; - } - } - } - else { - /* always load UI for factory settings (prefs will re-init) */ - G.fileflags &= ~G_FILE_NO_UI; - } - - return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; -} - /** \name WM History File API * \{ */ @@ -1005,7 +972,7 @@ bool write_crash_blend(void) /** * \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way. */ -int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports) +static int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports) { Library *li; int len; @@ -1117,69 +1084,6 @@ int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList * return ret; } -/** - * \see #wm_file_write wraps #BLO_write_file in a similar way. - */ -int wm_homefile_write_exec(bContext *C, wmOperator *op) -{ - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - char filepath[FILE_MAX]; - int fileflags; - - BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); - - /* check current window and close it if temp */ - if (win && win->screen->temp) - wm_window_close(C, wm, win); - - /* update keymaps in user preferences */ - WM_keyconfig_update(wm); - - BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE); - printf("trying to save homefile at %s ", filepath); - - ED_editors_flush_edits(C, false); - - /* force save as regular blend file */ - fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); - - if (BLO_write_file(CTX_data_main(C), filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) { - printf("fail\n"); - return OPERATOR_CANCELLED; - } - - printf("ok\n"); - - G.save_over = 0; - - BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST); - - return OPERATOR_FINISHED; -} - -/* Only save the prefs block. operator entry */ -int wm_userpref_write_exec(bContext *C, wmOperator *op) -{ - wmWindowManager *wm = CTX_wm_manager(C); - char filepath[FILE_MAX]; - - /* update keymaps in user preferences */ - WM_keyconfig_update(wm); - - BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE); - printf("trying to save userpref at %s ", filepath); - - if (BKE_write_file_userdef(filepath, op->reports) == 0) { - printf("fail\n"); - return OPERATOR_CANCELLED; - } - - printf("ok\n"); - - return OPERATOR_FINISHED; -} - /************************ autosave ****************************/ void wm_autosave_location(char *filepath) @@ -1341,3 +1245,730 @@ void WM_file_tag_modified(const bContext *C) WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, NULL); } } + +/** \name Preferences/startup save & load. + * + * \{ */ + +/** + * \see #wm_file_write wraps #BLO_write_file in a similar way. + */ +static int wm_homefile_write_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + char filepath[FILE_MAX]; + int fileflags; + + BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE); + + /* check current window and close it if temp */ + if (win && win->screen->temp) + wm_window_close(C, wm, win); + + /* update keymaps in user preferences */ + WM_keyconfig_update(wm); + + BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_STARTUP_FILE); + printf("trying to save homefile at %s ", filepath); + + ED_editors_flush_edits(C, false); + + /* force save as regular blend file */ + fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); + + if (BLO_write_file(CTX_data_main(C), filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) { + printf("fail\n"); + return OPERATOR_CANCELLED; + } + + printf("ok\n"); + + G.save_over = 0; + + BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST); + + return OPERATOR_FINISHED; +} + +void WM_OT_save_homefile(wmOperatorType *ot) +{ + ot->name = "Save Startup File"; + ot->idname = "WM_OT_save_homefile"; + ot->description = "Make the current file the default .blend file, includes preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_write_exec; +} + +static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare"); + BLI_addtail(&U.autoexec_paths, path_cmp); + return OPERATOR_FINISHED; +} + +void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot) +{ + ot->name = "Add Autoexec Path"; + ot->idname = "WM_OT_userpref_autoexec_path_add"; + ot->description = "Add path to exclude from autoexecution"; + + ot->exec = wm_userpref_autoexec_add_exec; + + ot->flag = OPTYPE_INTERNAL; +} + +static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op) +{ + const int index = RNA_int_get(op->ptr, "index"); + bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index); + if (path_cmp) { + BLI_freelinkN(&U.autoexec_paths, path_cmp); + } + return OPERATOR_FINISHED; +} + +void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot) +{ + ot->name = "Remove Autoexec Path"; + ot->idname = "WM_OT_userpref_autoexec_path_remove"; + ot->description = "Remove path to exclude from autoexecution"; + + ot->exec = wm_userpref_autoexec_remove_exec; + + ot->flag = OPTYPE_INTERNAL; + + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000); +} + +/* Only save the prefs block. operator entry */ +static int wm_userpref_write_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + char filepath[FILE_MAX]; + + /* update keymaps in user preferences */ + WM_keyconfig_update(wm); + + BLI_make_file_string("/", filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_USERPREF_FILE); + printf("trying to save userpref at %s ", filepath); + + if (BKE_blendfile_write_userdef(filepath, op->reports) == 0) { + printf("fail\n"); + return OPERATOR_CANCELLED; + } + + printf("ok\n"); + + return OPERATOR_FINISHED; +} + +void WM_OT_save_userpref(wmOperatorType *ot) +{ + ot->name = "Save User Settings"; + ot->idname = "WM_OT_save_userpref"; + ot->description = "Save user preferences separately, overrides startup file preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_userpref_write_exec; +} + +static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + ED_file_read_bookmarks(); + wm_history_file_read(); + return OPERATOR_FINISHED; +} + +void WM_OT_read_history(wmOperatorType *ot) +{ + ot->name = "Reload History File"; + ot->idname = "WM_OT_read_history"; + ot->description = "Reloads history and bookmarks"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_history_file_read_exec; + + /* this operator is only used for loading settings from a previous blender install */ + ot->flag = OPTYPE_INTERNAL; +} + +static int wm_homefile_read_exec(bContext *C, wmOperator *op) +{ + const bool from_memory = (STREQ(op->type->idname, "WM_OT_read_factory_settings")); + char filepath_buf[FILE_MAX]; + const char *filepath = NULL; + + if (!from_memory) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath"); + + /* This can be used when loading of a start-up file should only change + * the scene content but keep the blender UI as it is. */ + wm_open_init_load_ui(op, true); + BKE_BIT_TEST_SET(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI); + + if (RNA_property_is_set(op->ptr, prop)) { + RNA_property_string_get(op->ptr, prop, filepath_buf); + filepath = filepath_buf; + if (BLI_access(filepath, R_OK)) { + BKE_reportf(op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath); + return OPERATOR_CANCELLED; + } + } + } + else { + /* always load UI for factory settings (prefs will re-init) */ + G.fileflags &= ~G_FILE_NO_UI; + } + + return wm_homefile_read(C, op->reports, from_memory, filepath) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +void WM_OT_read_homefile(wmOperatorType *ot) +{ + PropertyRNA *prop; + ot->name = "Reload Start-Up File"; + ot->idname = "WM_OT_read_homefile"; + ot->description = "Open the default file (doesn't save the current file)"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_read_exec; + + prop = RNA_def_string_file_path(ot->srna, "filepath", NULL, + FILE_MAX, "File Path", + "Path to an alternative start-up file"); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* So scripts can use an alternative start-up file without the UI */ + prop = RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", + "Load user interface setup from the .blend file"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + /* omit poll to run in background mode */ +} + +void WM_OT_read_factory_settings(wmOperatorType *ot) +{ + ot->name = "Load Factory Settings"; + ot->idname = "WM_OT_read_factory_settings"; + ot->description = "Load default file and user preferences"; + + ot->invoke = WM_operator_confirm; + ot->exec = wm_homefile_read_exec; + /* omit poll to run in background mode */ +} + +/** \} */ + +/** \name Open main .blend file. + * + * \{ */ + +/** + * Wrap #WM_file_read, shared by file reading operators. + */ +static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports, + const bool autoexec_init) +{ + bool success; + + /* XXX wm in context is not set correctly after WM_file_read -> crash */ + /* do it before for now, but is this correct with multiple windows? */ + WM_event_add_notifier(C, NC_WINDOW, NULL); + + if (autoexec_init) { + WM_file_autoexec_init(filepath); + } + + success = WM_file_read(C, filepath, reports); + + return success; +} + +/* currently fits in a pointer */ +struct FileRuntime { + bool is_untrusted; +}; + +static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + const char *openname = G.main->name; + + if (CTX_wm_window(C) == NULL) { + /* in rare cases this could happen, when trying to invoke in background + * mode on load for example. Don't use poll for this because exec() + * can still run without a window */ + BKE_report(op->reports, RPT_ERROR, "Context window not set"); + return OPERATOR_CANCELLED; + } + + /* if possible, get the name of the most recently used .blend file */ + if (G.recent_files.first) { + struct RecentFile *recent = G.recent_files.first; + openname = recent->filepath; + } + + RNA_string_set(op->ptr, "filepath", openname); + wm_open_init_load_ui(op, true); + wm_open_init_use_scripts(op, true); + op->customdata = NULL; + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_open_mainfile_exec(bContext *C, wmOperator *op) +{ + char filepath[FILE_MAX]; + bool success; + + RNA_string_get(op->ptr, "filepath", filepath); + + /* re-use last loaded setting so we can reload a file without changing */ + wm_open_init_load_ui(op, false); + wm_open_init_use_scripts(op, false); + + if (RNA_boolean_get(op->ptr, "load_ui")) + G.fileflags &= ~G_FILE_NO_UI; + else + G.fileflags |= G_FILE_NO_UI; + + if (RNA_boolean_get(op->ptr, "use_scripts")) + G.f |= G_SCRIPT_AUTOEXEC; + else + G.f &= ~G_SCRIPT_AUTOEXEC; + + success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); + + /* for file open also popup for warnings, not only errors */ + BKE_report_print_level_set(op->reports, RPT_WARNING); + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op) +{ + struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts"); + bool is_untrusted = false; + char path[FILE_MAX]; + char *lslash; + + RNA_string_get(op->ptr, "filepath", path); + + /* get the dir */ + lslash = (char *)BLI_last_slash(path); + if (lslash) *(lslash + 1) = '\0'; + + if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) { + if (BKE_autoexec_match(path) == true) { + RNA_property_boolean_set(op->ptr, prop, false); + is_untrusted = true; + } + } + + if (file_info) { + file_info->is_untrusted = is_untrusted; + } + + return is_untrusted; +} + +static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op) +{ + struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; + uiLayout *layout = op->layout; + uiLayout *col = op->layout; + const char *autoexec_text; + + uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, false); + if (file_info->is_untrusted) { + autoexec_text = IFACE_("Trusted Source [Untrusted Path]"); + uiLayoutSetActive(col, false); + uiLayoutSetEnabled(col, false); + } + else { + autoexec_text = IFACE_("Trusted Source"); + } + + uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE); +} + +void WM_OT_open_mainfile(wmOperatorType *ot) +{ + ot->name = "Open Blender File"; + ot->idname = "WM_OT_open_mainfile"; + ot->description = "Open a Blender file"; + + ot->invoke = wm_open_mainfile_invoke; + ot->exec = wm_open_mainfile_exec; + ot->check = wm_open_mainfile_check; + ot->ui = wm_open_mainfile_ui; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file"); + RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", + "Allow .blend file to execute scripts automatically, default available from system preferences"); +} + +/** \} */ + +/** \name Reload (revert) main .blend file. + * + * \{ */ + +static int wm_revert_mainfile_exec(bContext *C, wmOperator *op) +{ + bool success; + char filepath[FILE_MAX]; + + wm_open_init_use_scripts(op, false); + + if (RNA_boolean_get(op->ptr, "use_scripts")) + G.f |= G_SCRIPT_AUTOEXEC; + else + G.f &= ~G_SCRIPT_AUTOEXEC; + + BLI_strncpy(filepath, G.main->name, sizeof(filepath)); + success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int wm_revert_mainfile_poll(bContext *UNUSED(C)) +{ + return G.relbase_valid; +} + +void WM_OT_revert_mainfile(wmOperatorType *ot) +{ + ot->name = "Revert"; + ot->idname = "WM_OT_revert_mainfile"; + ot->description = "Reload the saved file"; + ot->invoke = WM_operator_confirm; + + RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", + "Allow .blend file to execute scripts automatically, default available from system preferences"); + + ot->exec = wm_revert_mainfile_exec; + ot->poll = wm_revert_mainfile_poll; +} + +/** \} */ + +/** \name Recover last session & auto-save. + * + * \{ */ + +void WM_recover_last_session(bContext *C, ReportList *reports) +{ + char filepath[FILE_MAX]; + + BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE); + /* if reports==NULL, it's called directly without operator, we add a quick check here */ + if (reports || BLI_exists(filepath)) { + G.fileflags |= G_FILE_RECOVER; + + wm_file_read_opwrap(C, filepath, reports, true); + + G.fileflags &= ~G_FILE_RECOVER; + + /* XXX bad global... fixme */ + if (G.main->name[0]) + G.file_loaded = 1; /* prevents splash to show */ + else { + G.relbase_valid = 0; + G.save_over = 0; /* start with save preference untitled.blend */ + } + + } +} + +static int wm_recover_last_session_exec(bContext *C, wmOperator *op) +{ + WM_recover_last_session(C, op->reports); + return OPERATOR_FINISHED; +} + +void WM_OT_recover_last_session(wmOperatorType *ot) +{ + ot->name = "Recover Last Session"; + ot->idname = "WM_OT_recover_last_session"; + ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")"; + ot->invoke = WM_operator_confirm; + + ot->exec = wm_recover_last_session_exec; +} + +static int wm_recover_auto_save_exec(bContext *C, wmOperator *op) +{ + char filepath[FILE_MAX]; + bool success; + + RNA_string_get(op->ptr, "filepath", filepath); + + G.fileflags |= G_FILE_RECOVER; + + success = wm_file_read_opwrap(C, filepath, op->reports, true); + + G.fileflags &= ~G_FILE_RECOVER; + + if (success) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + char filename[FILE_MAX]; + + wm_autosave_location(filename); + RNA_string_set(op->ptr, "filepath", filename); + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void WM_OT_recover_auto_save(wmOperatorType *ot) +{ + ot->name = "Recover Auto Save"; + ot->idname = "WM_OT_recover_auto_save"; + ot->description = "Open an automatically saved file to recover it"; + + ot->exec = wm_recover_auto_save_exec; + ot->invoke = wm_recover_auto_save_invoke; + + WM_operator_properties_filesel( + ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, + WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME); +} + +/** \} */ + +/** \name Save main .blend file. + * + * \{ */ + +static void wm_filepath_default(char *filepath) +{ + if (G.save_over == false) { + BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend"); + } +} + +static void save_set_compress(wmOperator *op) +{ + PropertyRNA *prop; + + prop = RNA_struct_find_property(op->ptr, "compress"); + if (!RNA_property_is_set(op->ptr, prop)) { + if (G.save_over) { /* keep flag for existing file */ + RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0); + } + else { /* use userdef for new file */ + RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0); + } + } +} + +static void save_set_filepath(wmOperator *op) +{ + PropertyRNA *prop; + char name[FILE_MAX]; + + prop = RNA_struct_find_property(op->ptr, "filepath"); + if (!RNA_property_is_set(op->ptr, prop)) { + /* if not saved before, get the name of the most recently used .blend file */ + if (G.main->name[0] == 0 && G.recent_files.first) { + struct RecentFile *recent = G.recent_files.first; + BLI_strncpy(name, recent->filepath, FILE_MAX); + } + else { + BLI_strncpy(name, G.main->name, FILE_MAX); + } + + wm_filepath_default(name); + RNA_property_string_set(op->ptr, prop, name); + } +} + +static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + + save_set_compress(op); + save_set_filepath(op); + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* function used for WM_OT_save_mainfile too */ +static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) +{ + char path[FILE_MAX]; + int fileflags; + + save_set_compress(op); + + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + RNA_string_get(op->ptr, "filepath", path); + } + else { + BLI_strncpy(path, G.main->name, FILE_MAX); + wm_filepath_default(path); + } + + fileflags = G.fileflags & ~G_FILE_USERPREFS; + + /* set compression flag */ + BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"), + G_FILE_COMPRESS); + BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), + G_FILE_RELATIVE_REMAP); + BKE_BIT_TEST_SET(fileflags, + (RNA_struct_property_is_set(op->ptr, "copy") && + RNA_boolean_get(op->ptr, "copy")), + G_FILE_SAVE_COPY); + +#ifdef USE_BMESH_SAVE_AS_COMPAT + BKE_BIT_TEST_SET(fileflags, + (RNA_struct_find_property(op->ptr, "use_mesh_compat") && + RNA_boolean_get(op->ptr, "use_mesh_compat")), + G_FILE_MESH_COMPAT); +#else +# error "don't remove by accident" +#endif + + if (wm_file_write(C, path, fileflags, op->reports) != 0) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL); + + return OPERATOR_FINISHED; +} + +/* function used for WM_OT_save_mainfile too */ +static bool blend_save_check(bContext *UNUSED(C), wmOperator *op) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + if (!BLO_has_bfile_extension(filepath)) { + /* some users would prefer BLI_replace_extension(), + * we keep getting nitpicking bug reports about this - campbell */ + BLI_ensure_extension(filepath, FILE_MAX, ".blend"); + RNA_string_set(op->ptr, "filepath", filepath); + return true; + } + return false; +} + +void WM_OT_save_as_mainfile(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Save As Blender File"; + ot->idname = "WM_OT_save_as_mainfile"; + ot->description = "Save the current file in the desired location"; + + ot->invoke = wm_save_as_mainfile_invoke; + ot->exec = wm_save_as_mainfile_exec; + ot->check = blend_save_check; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); + RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative", + "Remap relative paths when saving in a different directory"); + prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy", + "Save a copy of the actual working state but does not make saved file active"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +#ifdef USE_BMESH_SAVE_AS_COMPAT + RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format", + "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will " + "be lost (no implicit triangulation)"); +#endif +} + +static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + int ret; + + /* cancel if no active window */ + if (CTX_wm_window(C) == NULL) + return OPERATOR_CANCELLED; + + save_set_compress(op); + save_set_filepath(op); + + /* if we're saving for the first time and prefer relative paths - any existing paths will be absolute, + * enable the option to remap paths to avoid confusion [#37240] */ + if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, true); + } + } + + if (G.save_over) { + char path[FILE_MAX]; + + RNA_string_get(op->ptr, "filepath", path); + if (BLI_exists(path)) { + ret = WM_operator_confirm_message_ex(C, op, IFACE_("Save Over?"), ICON_QUESTION, path); + } + else { + ret = wm_save_as_mainfile_exec(C, op); + } + } + else { + WM_event_add_fileselect(C, op); + ret = OPERATOR_RUNNING_MODAL; + } + + return ret; +} + +void WM_OT_save_mainfile(wmOperatorType *ot) +{ + ot->name = "Save Blender File"; + ot->idname = "WM_OT_save_mainfile"; + ot->description = "Save the current Blender file"; + + ot->invoke = wm_save_mainfile_invoke; + ot->exec = wm_save_as_mainfile_exec; + ot->check = blend_save_check; + /* omit window poll so this can work in background mode */ + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, + WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); + RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative", + "Remap relative paths when saving in a different directory"); +} + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c new file mode 100644 index 00000000000..2e4a4b63b7a --- /dev/null +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -0,0 +1,520 @@ +/* + * ***** 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) 2007 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_files_link.c + * \ingroup wm + * + * Functions for dealing with append/link operators and helpers. + */ + + +#include <float.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <stddef.h> +#include <assert.h> +#include <errno.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_ID.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_windowmanager_types.h" + + + +#include "BLI_blenlib.h" +#include "BLI_bitmap.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + +#include "BLO_readfile.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_library.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "BKE_idcode.h" + + +#include "IMB_colormanagement.h" + +#include "ED_screen.h" + +#include "GPU_material.h" + +#include "RNA_access.h" +#include "RNA_define.h" + + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_files.h" + +/* **************** link/append *************** */ + +static int wm_link_append_poll(bContext *C) +{ + if (WM_operator_winactive(C)) { + /* linking changes active object which is pretty useful in general, + * but which totally confuses edit mode (i.e. it becoming not so obvious + * to leave from edit mode and invalid tools in toolbar might be displayed) + * so disable link/append when in edit mode (sergey) */ + if (CTX_data_edit_object(C)) + return 0; + + return 1; + } + + return 0; +} + +static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + return WM_operator_call_notest(C, op); + } + else { + /* XXX TODO solve where to get last linked library from */ + if (G.lib[0] != '\0') { + RNA_string_set(op->ptr, "filepath", G.lib); + } + else if (G.relbase_valid) { + char path[FILE_MAX]; + BLI_strncpy(path, G.main->name, sizeof(G.main->name)); + BLI_parent_dir(path); + RNA_string_set(op->ptr, "filepath", path); + } + WM_event_add_fileselect(C, op); + return OPERATOR_RUNNING_MODAL; + } +} + +static short wm_link_append_flag(wmOperator *op) +{ + PropertyRNA *prop; + short flag = 0; + + if (RNA_boolean_get(op->ptr, "autoselect")) + flag |= FILE_AUTOSELECT; + if (RNA_boolean_get(op->ptr, "active_layer")) + flag |= FILE_ACTIVELAY; + if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) + flag |= FILE_RELPATH; + if (RNA_boolean_get(op->ptr, "link")) + flag |= FILE_LINK; + if (RNA_boolean_get(op->ptr, "instance_groups")) + flag |= FILE_GROUP_INSTANCE; + + return flag; +} + +typedef struct WMLinkAppendDataItem { + char *name; + BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ + short idcode; + + ID *new_id; + void *customdata; +} WMLinkAppendDataItem; + +typedef struct WMLinkAppendData { + LinkNodePair libraries; + LinkNodePair items; + int num_libraries; + int num_items; + short flag; + + /* Internal 'private' data */ + MemArena *memarena; +} WMLinkAppendData; + +static WMLinkAppendData *wm_link_append_data_new(const int flag) +{ + MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data)); + + lapp_data->flag = flag; + lapp_data->memarena = ma; + + return lapp_data; +} + +static void wm_link_append_data_free(WMLinkAppendData *lapp_data) +{ + BLI_memarena_free(lapp_data->memarena); +} + +/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */ + +static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname) +{ + size_t len = strlen(libname) + 1; + char *libpath = BLI_memarena_alloc(lapp_data->memarena, len); + + BLI_strncpy(libpath, libname, len); + BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena); + lapp_data->num_libraries++; +} + +static WMLinkAppendDataItem *wm_link_append_data_item_add( + WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata) +{ + WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item)); + size_t len = strlen(idname) + 1; + + item->name = BLI_memarena_alloc(lapp_data->memarena, len); + BLI_strncpy(item->name, idname, len); + item->idcode = idcode; + item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); + + item->new_id = NULL; + item->customdata = customdata; + + BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); + lapp_data->num_items++; + + return item; +} + +static void wm_link_do( + WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) +{ + Main *mainl; + BlendHandle *bh; + Library *lib; + + const int flag = lapp_data->flag; + + LinkNode *liblink, *itemlink; + int lib_idx, item_idx; + + BLI_assert(lapp_data->num_items && lapp_data->num_libraries); + + for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) { + char *libname = liblink->link; + + bh = BLO_blendhandle_from_file(libname, reports); + + if (bh == NULL) { + /* Unlikely since we just browsed it, but possible + * Error reports will have been made by BLO_blendhandle_from_file() */ + continue; + } + + /* here appending/linking starts */ + mainl = BLO_library_link_begin(bmain, &bh, libname); + lib = mainl->curlib; + BLI_assert(lib); + UNUSED_VARS_NDEBUG(lib); + + if (mainl->versionfile < 250) { + BKE_reportf(reports, RPT_WARNING, + "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will " + "be done! You may want to re-save your lib file with current Blender", + mainl->versionfile, mainl->subversionfile); + } + + /* For each lib file, we try to link all items belonging to that lib, + * and tag those successful to not try to load them again with the other libs. */ + for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *new_id; + + if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) { + continue; + } + + new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); + if (new_id) { + /* If the link is sucessful, clear item's libs 'todo' flags. + * This avoids trying to link same item with other libraries to come. */ + BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries); + item->new_id = new_id; + } + } + + BLO_library_link_end(mainl, &bh, flag, scene, v3d); + BLO_blendhandle_close(bh); + } +} + +static int wm_link_append_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + PropertyRNA *prop; + WMLinkAppendData *lapp_data; + char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; + char *group, *name; + int totfiles = 0; + short flag; + + RNA_string_get(op->ptr, "filename", relname); + RNA_string_get(op->ptr, "directory", root); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + /* test if we have a valid data */ + if (!BLO_library_path_explode(path, libname, &group, &name)) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path); + return OPERATOR_CANCELLED; + } + else if (!group) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + else if (BLI_path_cmp(bmain->name, libname) == 0) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path); + return OPERATOR_CANCELLED; + } + + /* check if something is indicated for append/link */ + prop = RNA_struct_find_property(op->ptr, "files"); + if (prop) { + totfiles = RNA_property_collection_length(op->ptr, prop); + if (totfiles == 0) { + if (!name) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + } + } + else if (!name) { + BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); + return OPERATOR_CANCELLED; + } + + flag = wm_link_append_flag(op); + + /* sanity checks for flag */ + if (scene && scene->id.lib) { + BKE_reportf(op->reports, RPT_WARNING, + "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2); + flag &= ~FILE_GROUP_INSTANCE; + scene = NULL; + } + + /* from here down, no error returns */ + + if (scene && RNA_boolean_get(op->ptr, "autoselect")) { + BKE_scene_base_deselect_all(scene); + } + + /* tag everything, all untagged data can be made local + * its also generally useful to know what is new + * + * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); + + /* We define our working data... + * Note that here, each item 'uses' one library, and only one. */ + lapp_data = wm_link_append_data_new(flag); + if (totfiles != 0) { + GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); + int lib_idx = 0; + + RNA_BEGIN (op->ptr, itemptr, "files") + { + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLO_library_path_explode(path, libname, &group, &name)) { + if (!group || !name) { + continue; + } + + if (!BLI_ghash_haskey(libraries, libname)) { + BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); + lib_idx++; + wm_link_append_data_library_add(lapp_data, libname); + } + } + } + RNA_END; + + RNA_BEGIN (op->ptr, itemptr, "files") + { + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLO_library_path_explode(path, libname, &group, &name)) { + WMLinkAppendDataItem *item; + if (!group || !name) { + printf("skipping %s\n", path); + continue; + } + + lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname)); + + item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); + BLI_BITMAP_ENABLE(item->libraries, lib_idx); + } + } + RNA_END; + + BLI_ghash_free(libraries, MEM_freeN, NULL); + } + else { + WMLinkAppendDataItem *item; + + wm_link_append_data_library_add(lapp_data, libname); + item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); + BLI_BITMAP_ENABLE(item->libraries, 0); + } + + /* XXX We'd need re-entrant locking on Main for this to work... */ + /* BKE_main_lock(bmain); */ + + wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C)); + + /* BKE_main_unlock(bmain); */ + + /* mark all library linked objects to be updated */ + BKE_main_lib_objects_recalc_all(bmain); + IMB_colormanagement_check_file_config(bmain); + + /* append, rather than linking */ + if ((flag & FILE_LINK) == 0) { + const bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); + const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive"); + + if (use_recursive) { + BKE_library_make_local(bmain, NULL, true, set_fake); + } + else { + LinkNode *itemlink; + GSet *done_libraries = BLI_gset_new_ex(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, + __func__, lapp_data->num_libraries); + + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + ID *new_id = ((WMLinkAppendDataItem *)(itemlink->link))->new_id; + + if (new_id && !BLI_gset_haskey(done_libraries, new_id->lib)) { + BKE_library_make_local(bmain, new_id->lib, true, set_fake); + BLI_gset_insert(done_libraries, new_id->lib); + } + } + + BLI_gset_free(done_libraries, NULL); + } + } + + wm_link_append_data_free(lapp_data); + + /* important we unset, otherwise these object wont + * link into other scenes from this blend file */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + + /* recreate dependency graph to include new objects */ + DAG_scene_relations_rebuild(bmain, scene); + + /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ + GPU_materials_free(); + + /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ + BLI_strncpy(G.lib, root, FILE_MAX); + + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) +{ + PropertyRNA *prop; + + /* better not save _any_ settings for this operator */ + /* properties */ + prop = RNA_def_boolean(ot->srna, "link", is_link, + "Link", "Link the objects or datablocks rather than appending"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "autoselect", true, + "Select", "Select new objects"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "active_layer", true, + "Active Layer", "Put new objects on the active layer"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "instance_groups", is_link, + "Instance Groups", "Create Dupli-Group instances for each group"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +void WM_OT_link(wmOperatorType *ot) +{ + ot->name = "Link from Library"; + ot->idname = "WM_OT_link"; + ot->description = "Link from a Library .blend file"; + + ot->invoke = wm_link_append_invoke; + ot->exec = wm_link_append_exec; + ot->poll = wm_link_append_poll; + + ot->flag |= OPTYPE_UNDO; + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + wm_link_append_properties_common(ot, true); +} + +void WM_OT_append(wmOperatorType *ot) +{ + ot->name = "Append from Library"; + ot->idname = "WM_OT_append"; + ot->description = "Append from a Library .blend file"; + + ot->invoke = wm_link_append_invoke; + ot->exec = wm_link_append_exec; + ot->poll = wm_link_append_poll; + + ot->flag |= OPTYPE_UNDO; + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + wm_link_append_properties_common(ot, false); + RNA_def_boolean(ot->srna, "set_fake", false, "Fake User", + "Set Fake User for appended items (except Objects and Groups)"); + RNA_def_boolean(ot->srna, "use_recursive", true, "Localize All", + "Localize all appended data, including those indirectly linked from other libraries"); +} diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 4dc60a9b729..6300d2ed3c7 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -54,6 +54,7 @@ #include "BLO_writefile.h" #include "BKE_blender.h" +#include "BKE_blender_undo.h" #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_DerivedMesh.h" @@ -97,9 +98,11 @@ #include "wm_files.h" #include "wm_window.h" +#include "ED_anim_api.h" #include "ED_armature.h" #include "ED_gpencil.h" #include "ED_keyframing.h" +#include "ED_keyframes_edit.h" #include "ED_node.h" #include "ED_render.h" #include "ED_space_api.h" @@ -399,13 +402,6 @@ static void free_openrecent(void) } -/* bad stuff*/ - -// XXX copy/paste buffer stuff... -extern void free_anim_copybuf(void); -extern void free_anim_drivers_copybuf(void); -extern void free_fmodifiers_copybuf(void); - #ifdef WIN32 /* Read console events until there is a key event. Also returns on any error. */ static void wait_for_console_key(void) @@ -497,10 +493,10 @@ void WM_exit_ext(bContext *C, const bool do_python) RE_FreeAllRender(); RE_engines_exit(); - ED_preview_free_dbase(); /* frees a Main dbase, before free_blender! */ + ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */ if (C && wm) - wm_free_reports(C); /* before free_blender! - since the ListBases get freed there */ + wm_free_reports(C); /* before BKE_blender_free! - since the ListBases get freed there */ BKE_sequencer_free_clipboard(); /* sequencer.c */ BKE_tracking_clipboard_free(); @@ -511,11 +507,12 @@ void WM_exit_ext(bContext *C, const bool do_python) COM_deinitialize(); #endif - free_blender(); /* blender.c, does entire library and spacetypes */ + BKE_blender_free(); /* blender.c, does entire library and spacetypes */ // free_matcopybuf(); - free_anim_copybuf(); - free_anim_drivers_copybuf(); - free_fmodifiers_copybuf(); + ANIM_fcurves_copybuf_free(); + ANIM_drivers_copybuf_free(); + ANIM_driver_vars_copybuf_free(); + ANIM_fmodifiers_copybuf_free(); ED_gpencil_anim_copybuf_free(); ED_gpencil_strokes_copybuf_free(); ED_clipboard_posebuf_free(); @@ -538,10 +535,10 @@ void WM_exit_ext(bContext *C, const bool do_python) /* option not to close python so we can use 'atexit' */ if (do_python) { /* XXX - old note */ - /* before free_blender so py's gc happens while library still exists */ + /* before BKE_blender_free so py's gc happens while library still exists */ /* needed at least for a rare sigsegv that can happen in pydrivers */ - /* Update for blender 2.5, move after free_blender because blender now holds references to PyObject's + /* Update for blender 2.5, move after BKE_blender_free because blender now holds references to PyObject's * so decref'ing them after python ends causes bad problems every time * the pyDriver bug can be fixed if it happens again we can deal with it then */ BPY_python_end(); @@ -566,7 +563,7 @@ void WM_exit_ext(bContext *C, const bool do_python) ED_file_exit(); /* for fsmenu */ UI_exit(); - BKE_userdef_free(); + BKE_blender_userdef_free(); RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index b5ad027148a..6ef8965a408 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -52,27 +52,22 @@ #include "DNA_scene_types.h" #include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" -#include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */ #include "BLT_translation.h" #include "PIL_time.h" #include "BLI_blenlib.h" -#include "BLI_bitmap.h" #include "BLI_dial.h" #include "BLI_dynstr.h" /*for WM_operator_pystring */ -#include "BLI_linklist.h" #include "BLI_math.h" -#include "BLI_memarena.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BLO_readfile.h" #include "BKE_appdir.h" -#include "BKE_autoexec.h" -#include "BKE_blender.h" +#include "BKE_blender_version.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_depsgraph.h" @@ -88,14 +83,12 @@ #include "BKE_scene.h" #include "BKE_screen.h" /* BKE_ST_MAXNAME */ #include "BKE_unit.h" -#include "BKE_utildefines.h" #include "BKE_idcode.h" #include "BIF_glutil.h" /* for paint cursor */ #include "BLF_api.h" -#include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -105,7 +98,6 @@ #include "ED_view3d.h" #include "GPU_basic_shader.h" -#include "GPU_material.h" #include "RNA_access.h" #include "RNA_define.h" @@ -2073,1025 +2065,6 @@ static void WM_OT_window_duplicate(wmOperatorType *ot) ot->poll = wm_operator_winactive_normal; } -static void WM_OT_save_homefile(wmOperatorType *ot) -{ - ot->name = "Save Startup File"; - ot->idname = "WM_OT_save_homefile"; - ot->description = "Make the current file the default .blend file, includes preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_write_exec; -} - -static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) -{ - bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare"); - BLI_addtail(&U.autoexec_paths, path_cmp); - return OPERATOR_FINISHED; -} - -static void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot) -{ - ot->name = "Add Autoexec Path"; - ot->idname = "WM_OT_userpref_autoexec_path_add"; - ot->description = "Add path to exclude from autoexecution"; - - ot->exec = wm_userpref_autoexec_add_exec; - - ot->flag = OPTYPE_INTERNAL; -} - -static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op) -{ - const int index = RNA_int_get(op->ptr, "index"); - bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index); - if (path_cmp) { - BLI_freelinkN(&U.autoexec_paths, path_cmp); - } - return OPERATOR_FINISHED; -} - -static void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot) -{ - ot->name = "Remove Autoexec Path"; - ot->idname = "WM_OT_userpref_autoexec_path_remove"; - ot->description = "Remove path to exclude from autoexecution"; - - ot->exec = wm_userpref_autoexec_remove_exec; - - ot->flag = OPTYPE_INTERNAL; - - RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000); -} - -static void WM_OT_save_userpref(wmOperatorType *ot) -{ - ot->name = "Save User Settings"; - ot->idname = "WM_OT_save_userpref"; - ot->description = "Save user preferences separately, overrides startup file preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_userpref_write_exec; -} - -static void WM_OT_read_history(wmOperatorType *ot) -{ - ot->name = "Reload History File"; - ot->idname = "WM_OT_read_history"; - ot->description = "Reloads history and bookmarks"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_history_file_read_exec; - - /* this operator is only used for loading settings from a previous blender install */ - ot->flag = OPTYPE_INTERNAL; -} - -static void WM_OT_read_homefile(wmOperatorType *ot) -{ - PropertyRNA *prop; - ot->name = "Reload Start-Up File"; - ot->idname = "WM_OT_read_homefile"; - ot->description = "Open the default file (doesn't save the current file)"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_read_exec; - - prop = RNA_def_string_file_path(ot->srna, "filepath", NULL, - FILE_MAX, "File Path", - "Path to an alternative start-up file"); - RNA_def_property_flag(prop, PROP_HIDDEN); - - /* So scripts can use an alternative start-up file without the UI */ - prop = RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", - "Load user interface setup from the .blend file"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - /* omit poll to run in background mode */ -} - -static void WM_OT_read_factory_settings(wmOperatorType *ot) -{ - ot->name = "Load Factory Settings"; - ot->idname = "WM_OT_read_factory_settings"; - ot->description = "Load default file and user preferences"; - - ot->invoke = WM_operator_confirm; - ot->exec = wm_homefile_read_exec; - /* omit poll to run in background mode */ -} - -/* *************** open file **************** */ - -/** - * Wrap #WM_file_read, shared by file reading operators. - */ -static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports, - const bool autoexec_init) -{ - bool success; - - /* XXX wm in context is not set correctly after WM_file_read -> crash */ - /* do it before for now, but is this correct with multiple windows? */ - WM_event_add_notifier(C, NC_WINDOW, NULL); - - if (autoexec_init) { - WM_file_autoexec_init(filepath); - } - - success = WM_file_read(C, filepath, reports); - - return success; -} - -/* currently fits in a pointer */ -struct FileRuntime { - bool is_untrusted; -}; - -static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - const char *openname = G.main->name; - - if (CTX_wm_window(C) == NULL) { - /* in rare cases this could happen, when trying to invoke in background - * mode on load for example. Don't use poll for this because exec() - * can still run without a window */ - BKE_report(op->reports, RPT_ERROR, "Context window not set"); - return OPERATOR_CANCELLED; - } - - /* if possible, get the name of the most recently used .blend file */ - if (G.recent_files.first) { - struct RecentFile *recent = G.recent_files.first; - openname = recent->filepath; - } - - RNA_string_set(op->ptr, "filepath", openname); - wm_open_init_load_ui(op, true); - wm_open_init_use_scripts(op, true); - op->customdata = NULL; - - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static int wm_open_mainfile_exec(bContext *C, wmOperator *op) -{ - char filepath[FILE_MAX]; - bool success; - - RNA_string_get(op->ptr, "filepath", filepath); - - /* re-use last loaded setting so we can reload a file without changing */ - wm_open_init_load_ui(op, false); - wm_open_init_use_scripts(op, false); - - if (RNA_boolean_get(op->ptr, "load_ui")) - G.fileflags &= ~G_FILE_NO_UI; - else - G.fileflags |= G_FILE_NO_UI; - - if (RNA_boolean_get(op->ptr, "use_scripts")) - G.f |= G_SCRIPT_AUTOEXEC; - else - G.f &= ~G_SCRIPT_AUTOEXEC; - - success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); - - /* for file open also popup for warnings, not only errors */ - BKE_report_print_level_set(op->reports, RPT_WARNING); - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op) -{ - struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts"); - bool is_untrusted = false; - char path[FILE_MAX]; - char *lslash; - - RNA_string_get(op->ptr, "filepath", path); - - /* get the dir */ - lslash = (char *)BLI_last_slash(path); - if (lslash) *(lslash + 1) = '\0'; - - if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) { - if (BKE_autoexec_match(path) == true) { - RNA_property_boolean_set(op->ptr, prop, false); - is_untrusted = true; - } - } - - if (file_info) { - file_info->is_untrusted = is_untrusted; - } - - return is_untrusted; -} - -static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op) -{ - struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata; - uiLayout *layout = op->layout; - uiLayout *col = op->layout; - const char *autoexec_text; - - uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - if (file_info->is_untrusted) { - autoexec_text = IFACE_("Trusted Source [Untrusted Path]"); - uiLayoutSetActive(col, false); - uiLayoutSetEnabled(col, false); - } - else { - autoexec_text = IFACE_("Trusted Source"); - } - - uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE); -} - -static void WM_OT_open_mainfile(wmOperatorType *ot) -{ - ot->name = "Open Blender File"; - ot->idname = "WM_OT_open_mainfile"; - ot->description = "Open a Blender file"; - - ot->invoke = wm_open_mainfile_invoke; - ot->exec = wm_open_mainfile_exec; - ot->check = wm_open_mainfile_check; - ot->ui = wm_open_mainfile_ui; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - RNA_def_boolean(ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file"); - RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", - "Allow .blend file to execute scripts automatically, default available from system preferences"); -} - - -/* *************** revert file **************** */ - -static int wm_revert_mainfile_exec(bContext *C, wmOperator *op) -{ - bool success; - - wm_open_init_use_scripts(op, false); - - if (RNA_boolean_get(op->ptr, "use_scripts")) - G.f |= G_SCRIPT_AUTOEXEC; - else - G.f &= ~G_SCRIPT_AUTOEXEC; - - success = wm_file_read_opwrap(C, G.main->name, op->reports, !(G.f & G_SCRIPT_AUTOEXEC)); - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static int wm_revert_mainfile_poll(bContext *UNUSED(C)) -{ - return G.relbase_valid; -} - -static void WM_OT_revert_mainfile(wmOperatorType *ot) -{ - ot->name = "Revert"; - ot->idname = "WM_OT_revert_mainfile"; - ot->description = "Reload the saved file"; - ot->invoke = WM_operator_confirm; - - RNA_def_boolean(ot->srna, "use_scripts", true, "Trusted Source", - "Allow .blend file to execute scripts automatically, default available from system preferences"); - - ot->exec = wm_revert_mainfile_exec; - ot->poll = wm_revert_mainfile_poll; -} - -/* **************** link/append *************** */ - -static int wm_link_append_poll(bContext *C) -{ - if (WM_operator_winactive(C)) { - /* linking changes active object which is pretty useful in general, - * but which totally confuses edit mode (i.e. it becoming not so obvious - * to leave from edit mode and invalid tools in toolbar might be displayed) - * so disable link/append when in edit mode (sergey) */ - if (CTX_data_edit_object(C)) - return 0; - - return 1; - } - - return 0; -} - -static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - if (RNA_struct_property_is_set(op->ptr, "filepath")) { - return WM_operator_call_notest(C, op); - } - else { - /* XXX TODO solve where to get last linked library from */ - if (G.lib[0] != '\0') { - RNA_string_set(op->ptr, "filepath", G.lib); - } - else if (G.relbase_valid) { - char path[FILE_MAX]; - BLI_strncpy(path, G.main->name, sizeof(G.main->name)); - BLI_parent_dir(path); - RNA_string_set(op->ptr, "filepath", path); - } - WM_event_add_fileselect(C, op); - return OPERATOR_RUNNING_MODAL; - } -} - -static short wm_link_append_flag(wmOperator *op) -{ - PropertyRNA *prop; - short flag = 0; - - if (RNA_boolean_get(op->ptr, "autoselect")) - flag |= FILE_AUTOSELECT; - if (RNA_boolean_get(op->ptr, "active_layer")) - flag |= FILE_ACTIVELAY; - if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) - flag |= FILE_RELPATH; - if (RNA_boolean_get(op->ptr, "link")) - flag |= FILE_LINK; - if (RNA_boolean_get(op->ptr, "instance_groups")) - flag |= FILE_GROUP_INSTANCE; - - return flag; -} - -typedef struct WMLinkAppendDataItem { - char *name; - BLI_bitmap *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ - short idcode; - - ID *new_id; - void *customdata; -} WMLinkAppendDataItem; - -typedef struct WMLinkAppendData { - LinkNodePair libraries; - LinkNodePair items; - int num_libraries; - int num_items; - short flag; - - /* Internal 'private' data */ - MemArena *memarena; -} WMLinkAppendData; - -static WMLinkAppendData *wm_link_append_data_new(const int flag) -{ - MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data)); - - lapp_data->flag = flag; - lapp_data->memarena = ma; - - return lapp_data; -} - -static void wm_link_append_data_free(WMLinkAppendData *lapp_data) -{ - BLI_memarena_free(lapp_data->memarena); -} - -/* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */ - -static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname) -{ - size_t len = strlen(libname) + 1; - char *libpath = BLI_memarena_alloc(lapp_data->memarena, len); - - BLI_strncpy(libpath, libname, len); - BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena); - lapp_data->num_libraries++; -} - -static WMLinkAppendDataItem *wm_link_append_data_item_add( - WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata) -{ - WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item)); - size_t len = strlen(idname) + 1; - - item->name = BLI_memarena_alloc(lapp_data->memarena, len); - BLI_strncpy(item->name, idname, len); - item->idcode = idcode; - item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); - - item->new_id = NULL; - item->customdata = customdata; - - BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); - lapp_data->num_items++; - - return item; -} - -static void wm_link_do( - WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) -{ - Main *mainl; - BlendHandle *bh; - Library *lib; - - const int flag = lapp_data->flag; - - LinkNode *liblink, *itemlink; - int lib_idx, item_idx; - - BLI_assert(lapp_data->num_items && lapp_data->num_libraries); - - for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) { - char *libname = liblink->link; - - bh = BLO_blendhandle_from_file(libname, reports); - - if (bh == NULL) { - /* Unlikely since we just browsed it, but possible - * Error reports will have been made by BLO_blendhandle_from_file() */ - continue; - } - - /* here appending/linking starts */ - mainl = BLO_library_link_begin(bmain, &bh, libname); - lib = mainl->curlib; - BLI_assert(lib); - UNUSED_VARS_NDEBUG(lib); - - if (mainl->versionfile < 250) { - BKE_reportf(reports, RPT_WARNING, - "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will " - "be done! You may want to re-save your lib file with current Blender", - mainl->versionfile, mainl->subversionfile); - } - - /* For each lib file, we try to link all items belonging to that lib, - * and tag those successful to not try to load them again with the other libs. */ - for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { - WMLinkAppendDataItem *item = itemlink->link; - ID *new_id; - - if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) { - continue; - } - - new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); - if (new_id) { - /* If the link is sucessful, clear item's libs 'todo' flags. - * This avoids trying to link same item with other libraries to come. */ - BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries); - item->new_id = new_id; - } - } - - BLO_library_link_end(mainl, &bh, flag, scene, v3d); - BLO_blendhandle_close(bh); - } -} - -static int wm_link_append_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - PropertyRNA *prop; - WMLinkAppendData *lapp_data; - char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; - char *group, *name; - int totfiles = 0; - short flag; - - RNA_string_get(op->ptr, "filename", relname); - RNA_string_get(op->ptr, "directory", root); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - /* test if we have a valid data */ - if (!BLO_library_path_explode(path, libname, &group, &name)) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path); - return OPERATOR_CANCELLED; - } - else if (!group) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - else if (BLI_path_cmp(bmain->name, libname) == 0) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path); - return OPERATOR_CANCELLED; - } - - /* check if something is indicated for append/link */ - prop = RNA_struct_find_property(op->ptr, "files"); - if (prop) { - totfiles = RNA_property_collection_length(op->ptr, prop); - if (totfiles == 0) { - if (!name) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - } - } - else if (!name) { - BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path); - return OPERATOR_CANCELLED; - } - - flag = wm_link_append_flag(op); - - /* sanity checks for flag */ - if (scene && scene->id.lib) { - BKE_reportf(op->reports, RPT_WARNING, - "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2); - flag &= ~FILE_GROUP_INSTANCE; - scene = NULL; - } - - /* from here down, no error returns */ - - if (scene && RNA_boolean_get(op->ptr, "autoselect")) { - BKE_scene_base_deselect_all(scene); - } - - /* tag everything, all untagged data can be made local - * its also generally useful to know what is new - * - * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); - - /* We define our working data... - * Note that here, each item 'uses' one library, and only one. */ - lapp_data = wm_link_append_data_new(flag); - if (totfiles != 0) { - GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); - int lib_idx = 0; - - RNA_BEGIN (op->ptr, itemptr, "files") - { - RNA_string_get(&itemptr, "name", relname); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - if (BLO_library_path_explode(path, libname, &group, &name)) { - if (!group || !name) { - continue; - } - - if (!BLI_ghash_haskey(libraries, libname)) { - BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx)); - lib_idx++; - wm_link_append_data_library_add(lapp_data, libname); - } - } - } - RNA_END; - - RNA_BEGIN (op->ptr, itemptr, "files") - { - RNA_string_get(&itemptr, "name", relname); - - BLI_join_dirfile(path, sizeof(path), root, relname); - - if (BLO_library_path_explode(path, libname, &group, &name)) { - WMLinkAppendDataItem *item; - if (!group || !name) { - printf("skipping %s\n", path); - continue; - } - - lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname)); - - item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); - BLI_BITMAP_ENABLE(item->libraries, lib_idx); - } - } - RNA_END; - - BLI_ghash_free(libraries, MEM_freeN, NULL); - } - else { - WMLinkAppendDataItem *item; - - wm_link_append_data_library_add(lapp_data, libname); - item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL); - BLI_BITMAP_ENABLE(item->libraries, 0); - } - - /* XXX We'd need re-entrant locking on Main for this to work... */ - /* BKE_main_lock(bmain); */ - - wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C)); - - /* BKE_main_unlock(bmain); */ - - wm_link_append_data_free(lapp_data); - - /* mark all library linked objects to be updated */ - BKE_main_lib_objects_recalc_all(bmain); - IMB_colormanagement_check_file_config(bmain); - - /* append, rather than linking */ - if ((flag & FILE_LINK) == 0) { - bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); - BKE_library_make_local(bmain, NULL, true, set_fake); - } - - /* important we unset, otherwise these object wont - * link into other scenes from this blend file */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); - - /* recreate dependency graph to include new objects */ - DAG_scene_relations_rebuild(bmain, scene); - - /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ - GPU_materials_free(); - - /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ - BLI_strncpy(G.lib, root, FILE_MAX); - - WM_event_add_notifier(C, NC_WINDOW, NULL); - - return OPERATOR_FINISHED; -} - -static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) -{ - PropertyRNA *prop; - - /* better not save _any_ settings for this operator */ - /* properties */ - prop = RNA_def_boolean(ot->srna, "link", is_link, - "Link", "Link the objects or datablocks rather than appending"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); - prop = RNA_def_boolean(ot->srna, "autoselect", true, - "Select", "Select new objects"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "active_layer", true, - "Active Layer", "Put new objects on the active layer"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "instance_groups", is_link, - "Instance Groups", "Create Dupli-Group instances for each group"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -static void WM_OT_link(wmOperatorType *ot) -{ - ot->name = "Link from Library"; - ot->idname = "WM_OT_link"; - ot->description = "Link from a Library .blend file"; - - ot->invoke = wm_link_append_invoke; - ot->exec = wm_link_append_exec; - ot->poll = wm_link_append_poll; - - ot->flag |= OPTYPE_UNDO; - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES, - FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - wm_link_append_properties_common(ot, true); -} - -static void WM_OT_append(wmOperatorType *ot) -{ - ot->name = "Append from Library"; - ot->idname = "WM_OT_append"; - ot->description = "Append from a Library .blend file"; - - ot->invoke = wm_link_append_invoke; - ot->exec = wm_link_append_exec; - ot->poll = wm_link_append_poll; - - ot->flag |= OPTYPE_UNDO; - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES, - FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - - wm_link_append_properties_common(ot, false); - RNA_def_boolean(ot->srna, "set_fake", false, "Fake User", "Set Fake User for appended items (except Objects and Groups)"); -} - -/* *************** recover last session **************** */ - -void WM_recover_last_session(bContext *C, ReportList *reports) -{ - char filepath[FILE_MAX]; - - BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE); - /* if reports==NULL, it's called directly without operator, we add a quick check here */ - if (reports || BLI_exists(filepath)) { - G.fileflags |= G_FILE_RECOVER; - - wm_file_read_opwrap(C, filepath, reports, true); - - G.fileflags &= ~G_FILE_RECOVER; - - /* XXX bad global... fixme */ - if (G.main->name[0]) - G.file_loaded = 1; /* prevents splash to show */ - else { - G.relbase_valid = 0; - G.save_over = 0; /* start with save preference untitled.blend */ - } - - } -} - -static int wm_recover_last_session_exec(bContext *C, wmOperator *op) -{ - WM_recover_last_session(C, op->reports); - return OPERATOR_FINISHED; -} - -static void WM_OT_recover_last_session(wmOperatorType *ot) -{ - ot->name = "Recover Last Session"; - ot->idname = "WM_OT_recover_last_session"; - ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")"; - ot->invoke = WM_operator_confirm; - - ot->exec = wm_recover_last_session_exec; -} - -/* *************** recover auto save **************** */ - -static int wm_recover_auto_save_exec(bContext *C, wmOperator *op) -{ - char filepath[FILE_MAX]; - bool success; - - RNA_string_get(op->ptr, "filepath", filepath); - - G.fileflags |= G_FILE_RECOVER; - - success = wm_file_read_opwrap(C, filepath, op->reports, true); - - G.fileflags &= ~G_FILE_RECOVER; - - if (success) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } -} - -static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - char filename[FILE_MAX]; - - wm_autosave_location(filename); - RNA_string_set(op->ptr, "filepath", filename); - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static void WM_OT_recover_auto_save(wmOperatorType *ot) -{ - ot->name = "Recover Auto Save"; - ot->idname = "WM_OT_recover_auto_save"; - ot->description = "Open an automatically saved file to recover it"; - - ot->exec = wm_recover_auto_save_exec; - ot->invoke = wm_recover_auto_save_invoke; - - WM_operator_properties_filesel( - ot, FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH, FILE_LONGDISPLAY, FILE_SORT_TIME); -} - -/* *************** save file as **************** */ - -static void wm_filepath_default(char *filepath) -{ - if (G.save_over == false) { - BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend"); - } -} - -static void save_set_compress(wmOperator *op) -{ - PropertyRNA *prop; - - prop = RNA_struct_find_property(op->ptr, "compress"); - if (!RNA_property_is_set(op->ptr, prop)) { - if (G.save_over) { /* keep flag for existing file */ - RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0); - } - else { /* use userdef for new file */ - RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0); - } - } -} - -static void save_set_filepath(wmOperator *op) -{ - PropertyRNA *prop; - char name[FILE_MAX]; - - prop = RNA_struct_find_property(op->ptr, "filepath"); - if (!RNA_property_is_set(op->ptr, prop)) { - /* if not saved before, get the name of the most recently used .blend file */ - if (G.main->name[0] == 0 && G.recent_files.first) { - struct RecentFile *recent = G.recent_files.first; - BLI_strncpy(name, recent->filepath, FILE_MAX); - } - else { - BLI_strncpy(name, G.main->name, FILE_MAX); - } - - wm_filepath_default(name); - RNA_property_string_set(op->ptr, prop, name); - } -} - -static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - - save_set_compress(op); - save_set_filepath(op); - - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -/* function used for WM_OT_save_mainfile too */ -static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) -{ - char path[FILE_MAX]; - int fileflags; - - save_set_compress(op); - - if (RNA_struct_property_is_set(op->ptr, "filepath")) { - RNA_string_get(op->ptr, "filepath", path); - } - else { - BLI_strncpy(path, G.main->name, FILE_MAX); - wm_filepath_default(path); - } - - fileflags = G.fileflags & ~G_FILE_USERPREFS; - - /* set compression flag */ - BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"), - G_FILE_COMPRESS); - BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), - G_FILE_RELATIVE_REMAP); - BKE_BIT_TEST_SET(fileflags, - (RNA_struct_property_is_set(op->ptr, "copy") && - RNA_boolean_get(op->ptr, "copy")), - G_FILE_SAVE_COPY); - -#ifdef USE_BMESH_SAVE_AS_COMPAT - BKE_BIT_TEST_SET(fileflags, - (RNA_struct_find_property(op->ptr, "use_mesh_compat") && - RNA_boolean_get(op->ptr, "use_mesh_compat")), - G_FILE_MESH_COMPAT); -#else -# error "don't remove by accident" -#endif - - if (wm_file_write(C, path, fileflags, op->reports) != 0) - return OPERATOR_CANCELLED; - - WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL); - - return OPERATOR_FINISHED; -} - -/* function used for WM_OT_save_mainfile too */ -static bool blend_save_check(bContext *UNUSED(C), wmOperator *op) -{ - char filepath[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filepath); - if (!BLO_has_bfile_extension(filepath)) { - /* some users would prefer BLI_replace_extension(), - * we keep getting nitpicking bug reports about this - campbell */ - BLI_ensure_extension(filepath, FILE_MAX, ".blend"); - RNA_string_set(op->ptr, "filepath", filepath); - return true; - } - return false; -} - -static void WM_OT_save_as_mainfile(wmOperatorType *ot) -{ - PropertyRNA *prop; - - ot->name = "Save As Blender File"; - ot->idname = "WM_OT_save_as_mainfile"; - ot->description = "Save the current file in the desired location"; - - ot->invoke = wm_save_as_mainfile_invoke; - ot->exec = wm_save_as_mainfile_exec; - ot->check = blend_save_check; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); - RNA_def_boolean(ot->srna, "relative_remap", true, "Remap Relative", - "Remap relative paths when saving in a different directory"); - prop = RNA_def_boolean(ot->srna, "copy", false, "Save Copy", - "Save a copy of the actual working state but does not make saved file active"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -#ifdef USE_BMESH_SAVE_AS_COMPAT - RNA_def_boolean(ot->srna, "use_mesh_compat", false, "Legacy Mesh Format", - "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will " - "be lost (no implicit triangulation)"); -#endif -} - -/* *************** save file directly ******** */ - -static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - int ret; - - /* cancel if no active window */ - if (CTX_wm_window(C) == NULL) - return OPERATOR_CANCELLED; - - save_set_compress(op); - save_set_filepath(op); - - /* if we're saving for the first time and prefer relative paths - any existing paths will be absolute, - * enable the option to remap paths to avoid confusion [#37240] */ - if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) { - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(op->ptr, prop, true); - } - } - - if (G.save_over) { - char path[FILE_MAX]; - - RNA_string_get(op->ptr, "filepath", path); - if (BLI_exists(path)) { - ret = WM_operator_confirm_message_ex(C, op, IFACE_("Save Over?"), ICON_QUESTION, path); - } - else { - ret = wm_save_as_mainfile_exec(C, op); - } - } - else { - WM_event_add_fileselect(C, op); - ret = OPERATOR_RUNNING_MODAL; - } - - return ret; -} - -static void WM_OT_save_mainfile(wmOperatorType *ot) -{ - ot->name = "Save Blender File"; - ot->idname = "WM_OT_save_mainfile"; - ot->description = "Save the current Blender file"; - - ot->invoke = wm_save_mainfile_invoke; - ot->exec = wm_save_as_mainfile_exec; - ot->check = blend_save_check; - /* omit window poll so this can work in background mode */ - - WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_SAVE, - WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); - RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file"); - RNA_def_boolean(ot->srna, "relative_remap", false, "Remap Relative", - "Remap relative paths when saving in a different directory"); -} - static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot) { ot->name = "Toggle Window Fullscreen"; @@ -3872,7 +2845,6 @@ void WM_OT_straightline_gesture(wmOperatorType *ot) #define WM_RADIAL_CONTROL_DISPLAY_SIZE (200 * U.pixelsize) #define WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE (35 * U.pixelsize) #define WM_RADIAL_CONTROL_DISPLAY_WIDTH (WM_RADIAL_CONTROL_DISPLAY_SIZE - WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE) -#define WM_RADIAL_CONTROL_HEADER_LENGTH 180 #define WM_RADIAL_MAX_STR 10 typedef struct { @@ -3898,7 +2870,7 @@ typedef struct { static void radial_control_update_header(wmOperator *op, bContext *C) { RadialControl *rc = op->customdata; - char msg[WM_RADIAL_CONTROL_HEADER_LENGTH]; + char msg[UI_MAX_DRAW_STR]; ScrArea *sa = CTX_wm_area(C); Scene *scene = CTX_data_scene(C); @@ -3906,34 +2878,33 @@ static void radial_control_update_header(wmOperator *op, bContext *C) if (hasNumInput(&rc->num_input)) { char num_str[NUM_STR_REP_LEN]; outputNumInput(&rc->num_input, num_str, &scene->unit); - BLI_snprintf(msg, WM_RADIAL_CONTROL_HEADER_LENGTH, "%s: %s", RNA_property_ui_name(rc->prop), num_str); - ED_area_headerprint(sa, msg); + BLI_snprintf(msg, sizeof(msg), "%s: %s", RNA_property_ui_name(rc->prop), num_str); } else { const char *ui_name = RNA_property_ui_name(rc->prop); switch (rc->subtype) { case PROP_NONE: case PROP_DISTANCE: - BLI_snprintf(msg, WM_RADIAL_CONTROL_HEADER_LENGTH, "%s: %0.4f", ui_name, rc->current_value); + BLI_snprintf(msg, sizeof(msg), "%s: %0.4f", ui_name, rc->current_value); break; case PROP_PIXEL: - BLI_snprintf(msg, WM_RADIAL_CONTROL_HEADER_LENGTH, "%s: %d", ui_name, (int)rc->current_value); /* XXX: round to nearest? */ + BLI_snprintf(msg, sizeof(msg), "%s: %d", ui_name, (int)rc->current_value); /* XXX: round to nearest? */ break; case PROP_PERCENTAGE: - BLI_snprintf(msg, WM_RADIAL_CONTROL_HEADER_LENGTH, "%s: %3.1f%%", ui_name, rc->current_value); + BLI_snprintf(msg, sizeof(msg), "%s: %3.1f%%", ui_name, rc->current_value); break; case PROP_FACTOR: - BLI_snprintf(msg, WM_RADIAL_CONTROL_HEADER_LENGTH, "%s: %1.3f", ui_name, rc->current_value); + BLI_snprintf(msg, sizeof(msg), "%s: %1.3f", ui_name, rc->current_value); break; case PROP_ANGLE: - BLI_snprintf(msg, WM_RADIAL_CONTROL_HEADER_LENGTH, "%s: %3.2f", ui_name, RAD2DEGF(rc->current_value)); + BLI_snprintf(msg, sizeof(msg), "%s: %3.2f", ui_name, RAD2DEGF(rc->current_value)); break; default: - BLI_snprintf(msg, WM_RADIAL_CONTROL_HEADER_LENGTH, "%s", ui_name); /* XXX: No value? */ + BLI_snprintf(msg, sizeof(msg), "%s", ui_name); /* XXX: No value? */ break; } - ED_area_headerprint(sa, msg); } + ED_area_headerprint(sa, msg); } } @@ -4151,7 +3122,7 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd BLF_size(fontid, 1.5 * fstyle_points, 1.0f / U.dpi); BLF_enable(fontid, BLF_SHADOW); - BLF_shadow(fontid, 3, 0.0f, 0.0f, 0.0f, 0.5f); + BLF_shadow(fontid, 3, (const float[4]){0.0f, 0.0f, 0.0f, 0.5f}); BLF_shadow_offset(fontid, 1, -1); /* draw value */ @@ -5323,6 +4294,7 @@ static void gesture_border_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "VIEW3D_OT_select_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom_border"); /* XXX TODO: zoom border should perhaps map rightmouse to zoom out instead of in+cancel */ WM_modalkeymap_assign(keymap, "IMAGE_OT_render_border"); + WM_modalkeymap_assign(keymap, "IMAGE_OT_view_zoom_border"); WM_modalkeymap_assign(keymap, "GPENCIL_OT_select_border"); } @@ -5357,6 +4329,7 @@ static void gesture_zoom_border_modal_keymap(wmKeyConfig *keyconf) /* assign map to operators */ WM_modalkeymap_assign(keymap, "VIEW2D_OT_zoom_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom_border"); + WM_modalkeymap_assign(keymap, "IMAGE_OT_view_zoom_border"); } /* default keymap for windows and screens, only call once per WM */ diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 675958cf0a3..6bf7bcc2934 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -897,6 +897,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void) } break; case GHOST_kKeyEqual: + case GHOST_kKeyPlus: case GHOST_kKeyNumpadPlus: { if (val == 0) break; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 688be21cdd0..42f6585f152 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -457,7 +457,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm /* displays with larger native pixels, like Macbook. Used to scale dpi with */ /* needed here, because it's used before it reads userdef */ U.pixelsize = wm_window_pixelsize(win); - BKE_userdef_state(); + BKE_blender_userdef_refresh(); wm_window_swap_buffers(win); @@ -834,7 +834,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) /* this can change per window */ U.pixelsize = wm_window_pixelsize(win); - BKE_userdef_state(); + BKE_blender_userdef_refresh(); } } @@ -1197,7 +1197,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr // printf("change, pixel size %f\n", GHOST_GetNativePixelSize(win->ghostwin)); U.pixelsize = wm_window_pixelsize(win); - BKE_userdef_state(); + BKE_blender_userdef_refresh(); WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); diff --git a/source/blender/windowmanager/wm_cursors.h b/source/blender/windowmanager/wm_cursors.h index e99f80f53cd..c695a12f52c 100644 --- a/source/blender/windowmanager/wm_cursors.h +++ b/source/blender/windowmanager/wm_cursors.h @@ -111,7 +111,7 @@ enum { struct wmWindow; struct wmEvent; -int wm_cursor_arrow_move(struct wmWindow *win, struct wmEvent *event); +bool wm_cursor_arrow_move(struct wmWindow *win, const struct wmEvent *event); #endif /* __WM_CURSORS_H__ */ diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index c32ded28126..449612bc5fd 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -167,6 +167,7 @@ enum { QUOTEKEY = 0x00e4, /* 228 */ ACCENTGRAVEKEY = 0x00e5, /* 229 */ MINUSKEY = 0x00e6, /* 230 */ + PLUSKEY = 0x00e7, /* 231 */ SLASHKEY = 0x00e8, /* 232 */ BACKSLASHKEY = 0x00e9, /* 233 */ EQUALKEY = 0x00ea, /* 234 */ diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 4b35f662a99..2eae9cdb012 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -31,15 +31,33 @@ #ifndef __WM_FILES_H__ #define __WM_FILES_H__ +struct wmOperatorType; + +/* wm_files.c */ void wm_history_file_read(void); -int wm_history_file_read_exec(bContext *C, wmOperator *op); -int wm_file_write(struct bContext *C, const char *target, int fileflags, struct ReportList *reports); -int wm_homefile_read_exec(struct bContext *C, struct wmOperator *op); int wm_homefile_read(struct bContext *C, struct ReportList *reports, bool from_memory, const char *filepath); -int wm_homefile_write_exec(struct bContext *C, struct wmOperator *op); -int wm_userpref_write_exec(struct bContext *C, struct wmOperator *op); void wm_file_read_report(bContext *C); +void WM_OT_save_homefile(struct wmOperatorType *ot); +void WM_OT_userpref_autoexec_path_add(struct wmOperatorType *ot); +void WM_OT_userpref_autoexec_path_remove(struct wmOperatorType *ot); +void WM_OT_save_userpref(struct wmOperatorType *ot); +void WM_OT_read_history(struct wmOperatorType *ot); +void WM_OT_read_homefile(struct wmOperatorType *ot); +void WM_OT_read_factory_settings(struct wmOperatorType *ot); + +void WM_OT_open_mainfile(struct wmOperatorType *ot); + +void WM_OT_revert_mainfile(struct wmOperatorType *ot); +void WM_OT_recover_last_session(struct wmOperatorType *ot); +void WM_OT_recover_auto_save(struct wmOperatorType *ot); + +void WM_OT_save_as_mainfile(struct wmOperatorType *ot); +void WM_OT_save_mainfile(struct wmOperatorType *ot); + +/* wm_files_link.c */ +void WM_OT_link(struct wmOperatorType *ot); +void WM_OT_append(struct wmOperatorType *ot); #endif /* __WM_FILES_H__ */ |