diff options
author | Clément Fukhaut <turjuque@gmail.com> | 2016-05-30 21:09:02 +0300 |
---|---|---|
committer | Clément Fukhaut <turjuque@gmail.com> | 2016-05-30 21:09:02 +0300 |
commit | 6ebbef4724dd7d6f09677d5cd871042a44f8008c (patch) | |
tree | f238502b654d063ed9b88d3f70e0c3b878a15c93 /source/blender | |
parent | bcf5e1c7f574c55b92c739d36eb97554366956d0 (diff) | |
parent | 7e120f3a740897edb4362fb5e1ba69e869b5b7d9 (diff) |
Merge branch 'master' of git://git.blender.org/blender
# Conflicts:
# source/blender/gpu/intern/gpu_draw.c
# source/blender/gpu/shaders/gpu_shader_material.glsl
# source/blender/makesdna/DNA_view3d_types.h
# source/blender/nodes/shader/nodes/node_shader_normal_map.c
# source/blender/nodes/shader/nodes/node_shader_tex_environment.c
# source/blender/nodes/shader/nodes/node_shader_tex_image.c
# source/blender/nodes/shader/nodes/node_shader_tex_noise.c
# source/blender/nodes/shader/nodes/node_shader_texture.c
Diffstat (limited to 'source/blender')
508 files changed, 26778 insertions, 13920 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_dynamicpaint.h b/source/blender/blenkernel/BKE_dynamicpaint.h index 00256176d98..5abb53d4c52 100644 --- a/source/blender/blenkernel/BKE_dynamicpaint.h +++ b/source/blender/blenkernel/BKE_dynamicpaint.h @@ -82,7 +82,7 @@ void dynamicPaint_resetPreview(struct DynamicPaintCanvasSettings *canvas); struct DynamicPaintSurface *get_activeSurface(struct DynamicPaintCanvasSettings *canvas); /* image sequence baking */ -int dynamicPaint_createUVSurface(struct Scene *scene, struct DynamicPaintSurface *surface); +int dynamicPaint_createUVSurface(struct Scene *scene, struct DynamicPaintSurface *surface, float *progress, short *do_update); int dynamicPaint_calculateFrame(struct DynamicPaintSurface *surface, struct Scene *scene, struct Object *cObject, int frame); void dynamicPaint_outputSurfaceImage(struct DynamicPaintSurface *surface, char *filename, short output_layer); 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_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index d7dd9ed3ac5..dff79b6cc22 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -114,6 +114,11 @@ void BKE_mesh_vert_loop_map_create( MeshElemMap **r_map, int **r_mem, const struct MPoly *mface, const struct MLoop *mloop, int totvert, int totface, int totloop); +void BKE_mesh_vert_looptri_map_create( + MeshElemMap **r_map, int **r_mem, + const struct MVert *mvert, const int totvert, + const struct MLoopTri *mlooptri, const int totlooptri, + const struct MLoop *mloop, const int totloop); void BKE_mesh_vert_edge_map_create( MeshElemMap **r_map, int **r_mem, const struct MEdge *medge, int totvert, int totedge); 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..bb5cc9cb067 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) @@ -2000,15 +2003,10 @@ static void mesh_calc_modifiers( DM_add_edge_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); DM_add_poly_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); -#pragma omp parallel sections if (dm->numVertData + dm->numEdgeData + dm->numPolyData >= BKE_MESH_OMP_LIMIT) - { -#pragma omp section - { range_vn_i(DM_get_vert_data_layer(dm, CD_ORIGINDEX), dm->numVertData, 0); } -#pragma omp section - { range_vn_i(DM_get_edge_data_layer(dm, CD_ORIGINDEX), dm->numEdgeData, 0); } -#pragma omp section - { range_vn_i(DM_get_poly_data_layer(dm, CD_ORIGINDEX), dm->numPolyData, 0); } - } + /* Not worth parallelizing this, gives less than 0.1% overall speedup in best of best cases... */ + range_vn_i(DM_get_vert_data_layer(dm, CD_ORIGINDEX), dm->numVertData, 0); + range_vn_i(DM_get_edge_data_layer(dm, CD_ORIGINDEX), dm->numEdgeData, 0); + range_vn_i(DM_get_poly_data_layer(dm, CD_ORIGINDEX), dm->numPolyData, 0); } } @@ -3207,96 +3205,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 +3237,212 @@ 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 */ + int layer_index = CustomData_get_layer_index(loopData, CD_MLOOPUV); + *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 + layer_index].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 + layer_index].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,23 +3616,50 @@ 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 */ + 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_TANGENT) { - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { - dm->calcLoopTangents(dm); + int type = gattribs->layer[b].type; + layer = -1; + if (type == CD_AUTO_FROM_NAME) { + /* We need to deduct what exact layer is used. + * + * We do it based on the specified name. + */ + if (gattribs->layer[b].name[0]) { + layer = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, gattribs->layer[b].name); + type = CD_TANGENT; + if (layer == -1) { + layer = CustomData_get_named_layer_index(ldata, CD_MLOOPCOL, gattribs->layer[b].name); + type = CD_MCOL; + } + if (layer == -1) { + layer = CustomData_get_named_layer_index(ldata, CD_MLOOPUV, gattribs->layer[b].name); + type = CD_MTFACE; + } + if (layer == -1) { + continue; + } + } + else { + /* Fall back to the UV layer, which matches old behavior. */ + type = CD_MTFACE; } - break; } - } - - for (b = 0; b < gattribs->totlayer; b++) { - if (gattribs->layer[b].type == CD_MTFACE) { + if (type == CD_MTFACE) { /* uv coordinates */ - if (gattribs->layer[b].name[0]) - layer = CustomData_get_named_layer_index(ldata, CD_MLOOPUV, gattribs->layer[b].name); - else - layer = CustomData_get_active_layer_index(ldata, CD_MLOOPUV); + if (layer == -1) { + if (gattribs->layer[b].name[0]) + layer = CustomData_get_named_layer_index(ldata, CD_MLOOPUV, gattribs->layer[b].name); + else + layer = CustomData_get_active_layer_index(ldata, CD_MLOOPUV); + } a = attribs->tottface++; @@ -3519,11 +3675,13 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, attribs->tface[a].gl_index = gattribs->layer[b].glindex; attribs->tface[a].gl_texco = gattribs->layer[b].gltexco; } - else if (gattribs->layer[b].type == CD_MCOL) { - if (gattribs->layer[b].name[0]) - layer = CustomData_get_named_layer_index(ldata, CD_MLOOPCOL, gattribs->layer[b].name); - else - layer = CustomData_get_active_layer_index(ldata, CD_MLOOPCOL); + else if (type == CD_MCOL) { + if (layer == -1) { + if (gattribs->layer[b].name[0]) + layer = CustomData_get_named_layer_index(ldata, CD_MLOOPCOL, gattribs->layer[b].name); + else + layer = CustomData_get_active_layer_index(ldata, CD_MLOOPCOL); + } a = attribs->totmcol++; @@ -3539,26 +3697,33 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, attribs->mcol[a].gl_index = gattribs->layer[b].glindex; } - else if (gattribs->layer[b].type == CD_TANGENT) { + else if (type == CD_TANGENT) { /* note, even with 'is_editmesh' this uses the derived-meshes loop data */ - layer = CustomData_get_layer_index(&dm->loopData, CD_TANGENT); + if (layer == -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); + } - attribs->tottang = 1; + 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) { + else if (type == CD_ORCO) { /* original coordinates */ - layer = CustomData_get_layer_index(vdata, CD_ORCO); + if (layer == -1) { + layer = CustomData_get_layer_index(vdata, CD_ORCO); + } attribs->totorco = 1; if (layer != -1) { @@ -3622,24 +3787,26 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, /* vertex colors */ for (b = 0; b < attribs->totmcol; b++) { - GLubyte col[4]; + GLfloat col[4]; if (attribs->mcol[b].array) { const MLoopCol *cp = &attribs->mcol[b].array[loop]; - copy_v4_v4_uchar(col, &cp->r); + rgba_uchar_to_float(col, &cp->r); } else { - col[0] = 0; col[1] = 0; col[2] = 0; col[3] = 0; + zero_v4(col); } - glVertexAttrib4ubv(attribs->mcol[b].gl_index, col); + glVertexAttrib4fv(attribs->mcol[b].gl_index, col); } /* 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 +4030,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..df9b9683687 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; @@ -596,7 +598,16 @@ void BKE_pose_copy_data(bPose **dst, bPose *src, const bool copy_constraints) outPose = MEM_callocN(sizeof(bPose), "pose"); BLI_duplicatelist(&outPose->chanbase, &src->chanbase); - + + /* Rebuild ghash here too, so that name lookups below won't be too bad... + * BUT this will have the penalty that the ghash will be built twice + * if BKE_pose_rebuild() gets called after this... + */ + if (outPose->chanbase.first != outPose->chanbase.last) { + outPose->chanhash = NULL; + BKE_pose_channels_hash_make(outPose); + } + outPose->iksolver = src->iksolver; outPose->ikdata = NULL; outPose->ikparam = MEM_dupallocN(src->ikparam); @@ -608,10 +619,16 @@ void BKE_pose_copy_data(bPose **dst, bPose *src, const bool copy_constraints) id_us_plus(&pchan->custom->id); } - /* warning, O(n2) here, but it's a rarely used feature. */ + /* warning, O(n2) here, if done without the hash, but these are rarely used features. */ if (pchan->custom_tx) { pchan->custom_tx = BKE_pose_channel_find_name(outPose, pchan->custom_tx->name); } + if (pchan->bbone_prev) { + pchan->bbone_prev = BKE_pose_channel_find_name(outPose, pchan->bbone_prev->name); + } + if (pchan->bbone_next) { + pchan->bbone_next = BKE_pose_channel_find_name(outPose, pchan->bbone_next->name); + } if (copy_constraints) { BKE_constraints_copy(&listb, &pchan->constraints, true); // BKE_constraints_copy NULLs listb @@ -726,7 +743,7 @@ void BKE_pose_channels_remove( Object *ob, bool (*filter_fn)(const char *bone_name, void *user_data), void *user_data) { - /* First erase any associated pose channel */ + /* Erase any associated pose channel, along with any references to them */ if (ob->pose) { bPoseChannel *pchan, *pchan_next; bConstraint *con; @@ -735,6 +752,7 @@ void BKE_pose_channels_remove( pchan_next = pchan->next; if (filter_fn(pchan->name, user_data)) { + /* Bone itself is being removed */ BKE_pose_channel_free(pchan); if (ob->pose->chanhash) { BLI_ghash_remove(ob->pose->chanhash, pchan->name, NULL, NULL); @@ -742,6 +760,7 @@ void BKE_pose_channels_remove( BLI_freelinkN(&ob->pose->chanbase, pchan); } else { + /* Maybe something the bone references is being removed instead? */ for (con = pchan->constraints.first; con; con = con->next) { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; @@ -765,6 +784,20 @@ void BKE_pose_channels_remove( cti->flush_constraint_targets(con, &targets, 0); } } + + if (pchan->bbone_prev) { + if (filter_fn(pchan->bbone_prev->name, user_data)) + pchan->bbone_prev = NULL; + } + if (pchan->bbone_next) { + if (filter_fn(pchan->bbone_next->name, user_data)) + pchan->bbone_next = NULL; + } + + if (pchan->custom_tx) { + if (filter_fn(pchan->custom_tx->name, user_data)) + pchan->custom_tx = NULL; + } } } } @@ -824,26 +857,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); } @@ -869,6 +911,15 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan copy_m4_m4(pchan->pose_mat, (float(*)[4])chan->pose_mat); pchan->flag = chan->flag; + pchan->roll1 = chan->roll1; + pchan->roll2 = chan->roll2; + pchan->curveInX = chan->curveInX; + pchan->curveInY = chan->curveInY; + pchan->curveOutX = chan->curveOutX; + pchan->curveOutY = chan->curveOutY; + pchan->scaleIn = chan->scaleIn; + pchan->scaleOut = chan->scaleOut; + con = chan->constraints.first; for (pcon = pchan->constraints.first; pcon && con; pcon = pcon->next, con = con->next) { pcon->enforce = con->enforce; @@ -879,7 +930,7 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan /** * Copy the internal members of each pose channel including constraints * and ID-Props, used when duplicating bones in editmode. - * (unlike copy_pose_channel_data which only). + * (unlike copy_pose_channel_data which only does posing-related stuff). * * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify) */ @@ -902,6 +953,11 @@ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_f pchan->ikstretch = pchan_from->ikstretch; pchan->ikrotweight = pchan_from->ikrotweight; pchan->iklinweight = pchan_from->iklinweight; + + /* bbone settings (typically not animated) */ + pchan->bboneflag = pchan_from->bboneflag; + pchan->bbone_next = pchan_from->bbone_next; + pchan->bbone_prev = pchan_from->bbone_prev; /* constraints */ BKE_constraints_copy(&pchan->constraints, &pchan_from->constraints, true); @@ -1271,6 +1327,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 :) */ @@ -1330,8 +1398,13 @@ void BKE_pose_rest(bPose *pose) unit_qt(pchan->quat); unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f; - - pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE); + + pchan->roll1 = pchan->roll2 = 0.0f; + pchan->curveInX = pchan->curveInY = 0.0f; + pchan->curveOutX = pchan->curveOutY = 0.0f; + pchan->scaleIn = pchan->scaleOut = 1.0f; + + pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); } } @@ -1366,9 +1439,19 @@ bool BKE_pose_copy_result(bPose *to, bPose *from) copy_v3_v3(pchanto->pose_head, pchanfrom->pose_head); copy_v3_v3(pchanto->pose_tail, pchanfrom->pose_tail); + pchanto->roll1 = pchanfrom->roll1; + pchanto->roll2 = pchanfrom->roll2; + pchanto->curveInX = pchanfrom->curveInX; + pchanto->curveInY = pchanfrom->curveInY; + pchanto->curveOutX = pchanfrom->curveOutX; + pchanto->curveOutY = pchanfrom->curveOutY; + pchanto->scaleIn = pchanfrom->scaleIn; + pchanto->scaleOut = pchanfrom->scaleOut; + pchanto->rotmode = pchanfrom->rotmode; pchanto->flag = pchanfrom->flag; pchanto->protectflag = pchanfrom->protectflag; + pchanto->bboneflag = pchanfrom->bboneflag; } } return true; @@ -1414,6 +1497,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..04b4733fd44 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]; @@ -473,7 +475,7 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB { bPoseChannel *next, *prev; Bone *bone = pchan->bone; - float h1[3], h2[3], scale[3], length, hlength1, hlength2, roll1 = 0.0f, roll2; + float h1[3], h2[3], scale[3], length, roll1 = 0.0f, roll2; float mat3[3][3], imat[4][4], posemat[4][4], scalemat[4][4], iscalemat[4][4]; float data[MAX_BBONE_SUBDIV + 1][4], *fp; int a; @@ -494,16 +496,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 +530,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) { @@ -536,7 +560,7 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB } normalize_v3(h1); - mul_v3_fl(h1, -hlength1); + negate_v3(h1); if (prev->bone->segments == 1) { /* find the previous roll to interpolate */ @@ -555,17 +579,34 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB } } else { - h1[0] = 0.0f; h1[1] = hlength1; h1[2] = 0.0f; + h1[0] = 0.0f; h1[1] = 1.0; h1[2] = 0.0f; roll1 = 0.0f; } if (next) { 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 */ @@ -591,14 +632,66 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB roll2 = atan2f(mat3[2][0], mat3[2][2]); - /* and only now negate handle */ - mul_v3_fl(h2, -hlength2); } else { - h2[0] = 0.0f; h2[1] = -hlength2; h2[2] = 0.0f; + h2[0] = 0.0f; h2[1] = 1.0f; h2[2] = 0.0f; roll2 = 0.0; } + { + const float circle_factor = length * (cubic_tangent_factor_circle_v3(h1, h2) / 0.75f); + const float hlength1 = bone->ease1 * circle_factor; + const float hlength2 = bone->ease2 * circle_factor; + + /* and only now negate h2 */ + mul_v3_fl(h1, hlength1); + mul_v3_fl(h2, -hlength2); + } + + /* 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 +701,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 +751,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 +785,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 +798,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 +971,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 +1010,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 +1040,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..af1ad4900b3 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -495,11 +495,12 @@ static void cdDM_drawFacesTex_common( CDDerivedMesh *cddm = (CDDerivedMesh *) dm; const MPoly *mpoly = cddm->mpoly; MTexPoly *mtexpoly = DM_get_poly_data_layer(dm, CD_MTEXPOLY); - const MLoopCol *mloopcol; + const MLoopCol *mloopcol = NULL; int i; int colType, start_element, tot_drawn; const bool use_hide = (flag & DM_DRAW_SKIP_HIDDEN) != 0; const bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_colors = (flag & DM_DRAW_USE_COLORS) != 0; int totpoly; int next_actualFace; int mat_index; @@ -529,15 +530,17 @@ static void cdDM_drawFacesTex_common( } } - colType = CD_TEXTURE_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - if (!mloopcol) { - colType = CD_PREVIEW_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - } - if (!mloopcol) { - colType = CD_MLOOPCOL; + if (use_colors) { + colType = CD_TEXTURE_MLOOPCOL; mloopcol = dm->getLoopDataArray(dm, colType); + if (!mloopcol) { + colType = CD_PREVIEW_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } + if (!mloopcol) { + colType = CD_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } } GPU_vertex_setup(dm); @@ -1049,11 +1052,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 +1110,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/colortools.c b/source/blender/blenkernel/intern/colortools.c index bac59c8c62d..c1f1f0128f5 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -43,6 +43,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BKE_colortools.h" @@ -53,10 +54,6 @@ #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" -#ifdef _OPENMP -# include <omp.h> -#endif - /* ********************************* color curve ********************* */ /* ***************** operations on full struct ************* */ @@ -1089,31 +1086,170 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorM } /* if view_settings, it also applies this to byte buffers */ +typedef struct ScopesUpdateData { + Scopes *scopes; + const ImBuf *ibuf; + struct ColormanageProcessor *cm_processor; + const unsigned char *display_buffer; + const int ycc_mode; + + unsigned int *bin_lum, *bin_r, *bin_g, *bin_b, *bin_a; +} ScopesUpdateData; + +typedef struct ScopesUpdateDataChunk { + unsigned int bin_lum[256]; + unsigned int bin_r[256]; + unsigned int bin_g[256]; + unsigned int bin_b[256]; + unsigned int bin_a[256]; + float min[3], max[3]; +} ScopesUpdateDataChunk; + +static void scopes_update_cb(void *userdata, void *userdata_chunk, const int y, const int UNUSED(threadid)) +{ + const ScopesUpdateData *data = userdata; + + Scopes *scopes = data->scopes; + const ImBuf *ibuf = data->ibuf; + struct ColormanageProcessor *cm_processor = data->cm_processor; + const unsigned char *display_buffer = data->display_buffer; + const int ycc_mode = data->ycc_mode; + + ScopesUpdateDataChunk *data_chunk = userdata_chunk; + unsigned int *bin_lum = data_chunk->bin_lum; + unsigned int *bin_r = data_chunk->bin_r; + unsigned int *bin_g = data_chunk->bin_g; + unsigned int *bin_b = data_chunk->bin_b; + unsigned int *bin_a = data_chunk->bin_a; + float *min = data_chunk->min; + float *max = data_chunk->max; + + const float *rf = NULL; + const unsigned char *rc = NULL; + const int rows_per_sample_line = ibuf->y / scopes->sample_lines; + const int savedlines = y / rows_per_sample_line; + const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0; + const bool is_float = (ibuf->rect_float != NULL); + + if (is_float) + rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels; + else { + rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels; + } + + for (int x = 0; x < ibuf->x; x++) { + float rgba[4], ycc[3], luma; + + if (is_float) { + switch (ibuf->channels) { + case 4: + copy_v4_v4(rgba, rf); + IMB_colormanagement_processor_apply_v4(cm_processor, rgba); + break; + case 3: + copy_v3_v3(rgba, rf); + IMB_colormanagement_processor_apply_v3(cm_processor, rgba); + rgba[3] = 1.0f; + break; + case 2: + copy_v3_fl(rgba, rf[0]); + rgba[3] = rf[1]; + break; + case 1: + copy_v3_fl(rgba, rf[0]); + rgba[3] = 1.0f; + break; + default: + BLI_assert(0); + } + } + else { + for (int c = 4; c--;) + rgba[c] = rc[c] * INV_255; + } + + /* we still need luma for histogram */ + luma = IMB_colormanagement_get_luminance(rgba); + + /* check for min max */ + if (ycc_mode == -1) { + minmax_v3v3_v3(min, max, rgba); + } + else { + rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode); + mul_v3_fl(ycc, INV_255); + minmax_v3v3_v3(min, max, ycc); + } + /* increment count for histo*/ + bin_lum[get_bin_float(luma)]++; + bin_r[get_bin_float(rgba[0])]++; + bin_g[get_bin_float(rgba[1])]++; + bin_b[get_bin_float(rgba[2])]++; + bin_a[get_bin_float(rgba[3])]++; + + /* save sample if needed */ + if (do_sample_line) { + const float fx = (float)x / (float)ibuf->x; + const int idx = 2 * (ibuf->x * savedlines + x); + save_sample_line(scopes, idx, fx, rgba, ycc); + } + + rf += ibuf->channels; + rc += ibuf->channels; + } +} + +static void scopes_update_finalize(void *userdata, void *userdata_chunk) +{ + const ScopesUpdateData *data = userdata; + const ScopesUpdateDataChunk *data_chunk = userdata_chunk; + + unsigned int *bin_lum = data->bin_lum; + unsigned int *bin_r = data->bin_r; + unsigned int *bin_g = data->bin_g; + unsigned int *bin_b = data->bin_b; + unsigned int *bin_a = data->bin_a; + const unsigned int *bin_lum_c = data_chunk->bin_lum; + const unsigned int *bin_r_c = data_chunk->bin_r; + const unsigned int *bin_g_c = data_chunk->bin_g; + const unsigned int *bin_b_c = data_chunk->bin_b; + const unsigned int *bin_a_c = data_chunk->bin_a; + + float (*minmax)[2] = data->scopes->minmax; + const float *min = data_chunk->min; + const float *max = data_chunk->max; + + for (int b = 256; b--;) { + bin_lum[b] += bin_lum_c[b]; + bin_r[b] += bin_r_c[b]; + bin_g[b] += bin_g_c[b]; + bin_b[b] += bin_b_c[b]; + bin_a[b] += bin_a_c[b]; + } + + for (int c = 3; c--;) { + if (min[c] < minmax[c][0]) + minmax[c][0] = min[c]; + if (max[c] > minmax[c][1]) + minmax[c][1] = max[c]; + } +} + void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { -#ifdef _OPENMP - const int num_threads = BLI_system_thread_count(); -#endif - int a, y; + int a; unsigned int nl, na, nr, ng, nb; double divl, diva, divr, divg, divb; - unsigned char *display_buffer; + const unsigned char *display_buffer = NULL; unsigned int bin_lum[256] = {0}, bin_r[256] = {0}, bin_g[256] = {0}, bin_b[256] = {0}, bin_a[256] = {0}; - unsigned int bin_lum_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_r_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_g_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_b_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_a_t[BLENDER_MAX_THREADS][256] = {{0}}; int ycc_mode = -1; - const bool is_float = (ibuf->rect_float != NULL); void *cache_handle = NULL; struct ColormanageProcessor *cm_processor = NULL; - int rows_per_sample_line; if (ibuf->rect == NULL && ibuf->rect_float == NULL) return; @@ -1151,7 +1287,6 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings * scopes->sample_lines = ibuf->y; /* scan the image */ - rows_per_sample_line = ibuf->y / scopes->sample_lines; for (a = 0; a < 3; a++) { scopes->minmax[a][0] = 25500.0f; scopes->minmax[a][1] = -25500.0f; @@ -1177,129 +1312,21 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings * cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); } else { - display_buffer = (unsigned char *)IMB_display_buffer_acquire(ibuf, - view_settings, - display_settings, - &cache_handle); + display_buffer = (const unsigned char *)IMB_display_buffer_acquire( + ibuf, view_settings, display_settings, &cache_handle); } /* Keep number of threads in sync with the merge parts below. */ -#pragma omp parallel for private(y) schedule(static) num_threads(num_threads) if (ibuf->y > 256) - for (y = 0; y < ibuf->y; y++) { -#ifdef _OPENMP - const int thread_idx = omp_get_thread_num(); -#else - const int thread_idx = 0; -#endif - const float *rf = NULL; - const unsigned char *rc = NULL; - const int savedlines = y / rows_per_sample_line; - const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0; - float min[3] = { FLT_MAX, FLT_MAX, FLT_MAX}, - max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX}; - int x, c; - if (is_float) - rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels; - else { - rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels; - } - for (x = 0; x < ibuf->x; x++) { - float rgba[4], ycc[3], luma; - if (is_float) { - - switch (ibuf->channels) { - case 4: - copy_v4_v4(rgba, rf); - IMB_colormanagement_processor_apply_v4(cm_processor, rgba); - break; - case 3: - copy_v3_v3(rgba, rf); - IMB_colormanagement_processor_apply_v3(cm_processor, rgba); - rgba[3] = 1.0f; - break; - case 2: - copy_v3_fl(rgba, rf[0]); - rgba[3] = rf[1]; - break; - case 1: - copy_v3_fl(rgba, rf[0]); - rgba[3] = 1.0f; - break; - default: - BLI_assert(0); - } - } - else { - for (c = 0; c < 4; c++) - rgba[c] = rc[c] * INV_255; - } - - /* we still need luma for histogram */ - luma = IMB_colormanagement_get_luminance(rgba); - - /* check for min max */ - if (ycc_mode == -1) { - for (c = 0; c < 3; c++) { - if (rgba[c] < min[c]) min[c] = rgba[c]; - if (rgba[c] > max[c]) max[c] = rgba[c]; - } - } - else { - rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode); - for (c = 0; c < 3; c++) { - ycc[c] *= INV_255; - if (ycc[c] < min[c]) min[c] = ycc[c]; - if (ycc[c] > max[c]) max[c] = ycc[c]; - } - } - /* increment count for histo*/ - bin_lum_t[thread_idx][get_bin_float(luma)] += 1; - bin_r_t[thread_idx][get_bin_float(rgba[0])] += 1; - bin_g_t[thread_idx][get_bin_float(rgba[1])] += 1; - bin_b_t[thread_idx][get_bin_float(rgba[2])] += 1; - bin_a_t[thread_idx][get_bin_float(rgba[3])] += 1; - - /* save sample if needed */ - if (do_sample_line) { - const float fx = (float)x / (float)ibuf->x; - const int idx = 2 * (ibuf->x * savedlines + x); - save_sample_line(scopes, idx, fx, rgba, ycc); - } - - rf += ibuf->channels; - rc += ibuf->channels; - } -#pragma omp critical - { - for (c = 0; c < 3; c++) { - if (min[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = min[c]; - if (max[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = max[c]; - } - } - } - -#ifdef _OPENMP - if (ibuf->y > 256) { - for (a = 0; a < num_threads; a++) { - int b; - for (b = 0; b < 256; b++) { - bin_lum[b] += bin_lum_t[a][b]; - bin_r[b] += bin_r_t[a][b]; - bin_g[b] += bin_g_t[a][b]; - bin_b[b] += bin_b_t[a][b]; - bin_a[b] += bin_a_t[a][b]; - } - } - } - else -#endif - { - memcpy(bin_lum, bin_lum_t[0], sizeof(bin_lum)); - memcpy(bin_r, bin_r_t[0], sizeof(bin_r)); - memcpy(bin_g, bin_g_t[0], sizeof(bin_g)); - memcpy(bin_b, bin_b_t[0], sizeof(bin_b)); - memcpy(bin_a, bin_a_t[0], sizeof(bin_a)); - } + ScopesUpdateData data = { + .scopes = scopes, . ibuf = ibuf, + .cm_processor = cm_processor, .display_buffer = display_buffer, .ycc_mode = ycc_mode, + .bin_lum = bin_lum, .bin_r = bin_r, .bin_g = bin_g, .bin_b = bin_b, .bin_a = bin_a, + }; + ScopesUpdateDataChunk data_chunk = {0}; + INIT_MINMAX(data_chunk.min, data_chunk.max); + + BLI_task_parallel_range_finalize(0, ibuf->y, &data, &data_chunk, sizeof(data_chunk), + scopes_update_cb, scopes_update_finalize, ibuf->y > 256, false); /* test for nicer distribution even - non standard, leave it out for a while */ #if 0 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..2d531e32f69 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -32,6 +32,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_kdtree.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -64,6 +65,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_mesh_mapping.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" @@ -79,13 +81,11 @@ #include "RE_render_ext.h" #include "RE_shader_ext.h" -#ifdef _OPENMP -# include <omp.h> -#endif +#include "atomic_ops.h" /* 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))) : @@ -138,19 +138,20 @@ typedef struct Bounds2D { } Bounds2D; typedef struct Bounds3D { - int valid; float min[3], max[3]; + bool valid; } 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) */ + int *temp_t_index; } VolumeGrid; typedef struct Vec3f { @@ -165,21 +166,21 @@ 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; + float dim[3]; /* 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,11 +189,10 @@ 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. */ - } PaintBakeData; /* UV Image sequence format point */ @@ -201,8 +201,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 +213,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 +237,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 +250,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 +273,9 @@ void dynamicPaint_resetPreview(DynamicPaintCanvasSettings *canvas) surface->flags |= MOD_DPAINT_PREVIEW; done = true; } - else + else { surface->flags &= ~MOD_DPAINT_PREVIEW; + } } } @@ -325,8 +307,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 +317,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 +336,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 +409,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 && @@ -434,8 +421,10 @@ static int surface_totalSamples(DynamicPaintSurface *surface) return surface->data->total_points; } -static void blendColors(const float t_color[3], float t_alpha, const float s_color[3], float s_alpha, float result[4]) +static void blendColors( + const float t_color[3], const float t_alpha, const float s_color[3], const 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 +497,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 +509,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; @@ -540,56 +529,58 @@ static int surface_getBrushFlags(DynamicPaintSurface *surface, const Scene *scen return flags; } -static int brush_usesMaterial(DynamicPaintBrushSettings *brush, Scene *scene) +static int brush_usesMaterial(const DynamicPaintBrushSettings *brush, const Scene *scene) { return ((brush->flags & MOD_DPAINT_USE_MATERIAL) && (!BKE_scene_use_new_shading_nodes(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; - } - 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]; - } + b->valid = true; + return; } + + minmax_v3v3_v3(b->min, b->max, point); } static float getSurfaceDimension(PaintSurfaceData *sData) @@ -612,65 +603,123 @@ static void freeGrid(PaintSurfaceData *data) bData->grid = NULL; } +static void grid_bound_insert_cb_ex(void *userdata, void *userdata_chunk, const int i, const int UNUSED(thread_id)) +{ + PaintBakeData *bData = userdata; + + Bounds3D *grid_bound = userdata_chunk; + + boundInsert(grid_bound, bData->realCoord[bData->s_pos[i]].v); +} + +static void grid_bound_insert_finalize(void *userdata, void *userdata_chunk) +{ + PaintBakeData *bData = userdata; + VolumeGrid *grid = bData->grid; + + Bounds3D *grid_bound = userdata_chunk; + + boundInsert(&grid->grid_bounds, grid_bound->min); + boundInsert(&grid->grid_bounds, grid_bound->max); +} + +static void grid_cell_points_cb_ex(void *userdata, void *userdata_chunk, const int i, const int UNUSED(thread_id)) +{ + PaintBakeData *bData = userdata; + VolumeGrid *grid = bData->grid; + int *temp_t_index = grid->temp_t_index; + int *s_num = userdata_chunk; + + int co[3]; + + for (int j = 3; j--;) { + co[j] = (int)floorf((bData->realCoord[bData->s_pos[i]].v[j] - grid->grid_bounds.min[j]) / + bData->dim[j] * grid->dim[j]); + CLAMP(co[j], 0, grid->dim[j] - 1); + } + + temp_t_index[i] = co[0] + co[1] * grid->dim[0] + co[2] * grid->dim[0] * grid->dim[1]; + s_num[temp_t_index[i]]++; +} + +static void grid_cell_points_finalize(void *userdata, void *userdata_chunk) +{ + PaintBakeData *bData = userdata; + VolumeGrid *grid = bData->grid; + const int grid_cells = grid->dim[0] * grid->dim[1] * grid->dim[2]; + + int *s_num = userdata_chunk; + + /* calculate grid indexes */ + for (int i = 0; i < grid_cells; i++) { + grid->s_num[i] += s_num[i]; + } +} + +static void grid_cell_bounds_cb(void *userdata, const int x) +{ + PaintBakeData *bData = userdata; + VolumeGrid *grid = bData->grid; + float *dim = bData->dim; + int *grid_dim = grid->dim; + + for (int y = 0; y < grid_dim[1]; y++) { + for (int z = 0; z < grid_dim[2]; z++) { + const int b_index = x + y * grid_dim[0] + z * grid_dim[0] * grid_dim[1]; + /* set bounds */ + for (int j = 3; j--;) { + const int s = (j == 0) ? x : ((j == 1) ? y : z); + grid->bounds[b_index].min[j] = grid->grid_bounds.min[j] + dim[j] / grid_dim[j] * s; + grid->bounds[b_index].max[j] = grid->grid_bounds.min[j] + dim[j] / grid_dim[j] * (s + 1); + } + grid->bounds[b_index].valid = true; + } + } +} + static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) { PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; - Bounds3D *grid_bounds; VolumeGrid *grid; int grid_cells, axis = 3; int *temp_t_index = NULL; int *temp_s_num = NULL; -#ifdef _OPENMP - int num_of_threads = omp_get_max_threads(); -#else - int num_of_threads = 1; -#endif - if (bData->grid) freeGrid(sData); - /* allocate separate bounds for each thread */ - grid_bounds = MEM_callocN(sizeof(Bounds3D) * num_of_threads, "Grid Bounds"); 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]; float min_dim; /* calculate canvas dimensions */ -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { -#ifdef _OPENMP - int id = omp_get_thread_num(); - boundInsert(&grid_bounds[id], (bData->realCoord[bData->s_pos[i]].v)); -#else - boundInsert(&grid_bounds[0], (bData->realCoord[bData->s_pos[i]].v)); -#endif - } - - /* get final dimensions */ - for (i = 0; i < num_of_threads; i++) { - boundInsert(&grid->grid_bounds, grid_bounds[i].min); - boundInsert(&grid->grid_bounds, grid_bounds[i].max); - } + /* Important to init correctly our ref grid_bound... */ + boundInsert(&grid->grid_bounds, bData->realCoord[bData->s_pos[0]].v); + BLI_task_parallel_range_finalize( + 0, sData->total_points, bData, &grid->grid_bounds, sizeof(grid->grid_bounds), + grid_bound_insert_cb_ex, grid_bound_insert_finalize, sData->total_points > 1000, false); /* get dimensions */ sub_v3_v3v3(dim, grid->grid_bounds.max, grid->grid_bounds.min); copy_v3_v3(td, dim); + copy_v3_v3(bData->dim, dim); min_dim = max_fff(td[0], td[1], td[2]) / 1000.f; /* 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; @@ -692,10 +741,11 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) /* allocate memory for grids */ grid->bounds = MEM_callocN(sizeof(Bounds3D) * grid_cells, "Surface Grid Bounds"); grid->s_pos = MEM_callocN(sizeof(int) * grid_cells, "Surface Grid Position"); - grid->s_num = MEM_callocN(sizeof(int) * grid_cells * num_of_threads, "Surface Grid Points"); + + grid->s_num = MEM_callocN(sizeof(int) * grid_cells, "Surface Grid Points"); temp_s_num = MEM_callocN(sizeof(int) * grid_cells, "Temp Surface Grid Points"); grid->t_index = MEM_callocN(sizeof(int) * sData->total_points, "Surface Grid Target Ids"); - temp_t_index = MEM_callocN(sizeof(int) * sData->total_points, "Temp Surface Grid Target Ids"); + grid->temp_t_index = temp_t_index = MEM_callocN(sizeof(int) * sData->total_points, "Temp Surface Grid Target Ids"); /* in case of an allocation failure abort here */ if (!grid->bounds || !grid->s_pos || !grid->s_num || !grid->t_index || !temp_s_num || !temp_t_index) @@ -703,33 +753,12 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) if (!error) { /* calculate number of points withing each cell */ -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - int co[3], j; - for (j = 0; j < 3; j++) { - co[j] = (int)floor((bData->realCoord[bData->s_pos[i]].v[j] - grid->grid_bounds.min[j]) / dim[j] * grid->dim[j]); - CLAMP(co[j], 0, grid->dim[j] - 1); - } - - temp_t_index[i] = co[0] + co[1] * grid->dim[0] + co[2] * grid->dim[0] * grid->dim[1]; -#ifdef _OPENMP - grid->s_num[temp_t_index[i] + omp_get_thread_num() * grid_cells]++; -#else - grid->s_num[temp_t_index[i]]++; -#endif - } + BLI_task_parallel_range_finalize( + 0, sData->total_points, bData, grid->s_num, sizeof(*grid->s_num) * grid_cells, + grid_cell_points_cb_ex, grid_cell_points_finalize, sData->total_points > 1000, false); - /* for first cell only calc s_num */ - for (i = 1; i < num_of_threads; i++) { - grid->s_num[0] += grid->s_num[i * grid_cells]; - } - - /* calculate grid indexes */ + /* calculate grid indexes (not needed for first cell, which is zero). */ for (i = 1; i < grid_cells; i++) { - int id; - for (id = 1; id < num_of_threads; id++) { - grid->s_num[i] += grid->s_num[i + id * grid_cells]; - } grid->s_pos[i] = grid->s_pos[i - 1] + grid->s_num[i - 1]; } @@ -742,41 +771,20 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) } /* calculate cell bounds */ - { - int x; -#pragma omp parallel for schedule(static) - for (x = 0; x < grid->dim[0]; x++) { - int y; - for (y = 0; y < grid->dim[1]; y++) { - int z; - for (z = 0; z < grid->dim[2]; z++) { - int j, b_index = x + y * grid->dim[0] + z * grid->dim[0] * grid->dim[1]; - /* set bounds */ - for (j = 0; j < 3; j++) { - int s = (j == 0) ? x : ((j == 1) ? y : z); - grid->bounds[b_index].min[j] = grid->grid_bounds.min[j] + dim[j] / grid->dim[j] * s; - grid->bounds[b_index].max[j] = grid->grid_bounds.min[j] + dim[j] / grid->dim[j] * (s + 1); - } - grid->bounds[b_index].valid = 1; - } - } - } - } + BLI_task_parallel_range(0, grid->dim[0], bData, grid_cell_bounds_cb, grid_cells > 1000); } - 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); + if (temp_s_num) + MEM_freeN(temp_s_num); + if (temp_t_index) + MEM_freeN(temp_t_index); + grid->temp_t_index = NULL; if (error || !grid->s_num) { setError(surface->canvas, N_("Not enough free memory")); freeGrid(sData); } } - - if (grid_bounds) MEM_freeN(grid_bounds); } /***************************** Freeing data ******************************/ @@ -791,10 +799,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 +810,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 +827,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 +854,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 +866,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 +881,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); @@ -1035,7 +1056,7 @@ bool dynamicPaint_createType(struct DynamicPaintModifierData *pmd, int type, str brush->flags = MOD_DPAINT_ABS_ALPHA | MOD_DPAINT_RAMP_ALPHA; brush->collision = MOD_DPAINT_COL_VOLUME; - + brush->mat = NULL; brush->r = 0.15f; brush->g = 0.4f; @@ -1224,31 +1245,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 +1270,24 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for int *temp_data; int neigh_points = 0; - if (!surface_usesAdjData(surface) && !force_init) return; + if (!force_init && !surface_usesAdjData(surface)) + 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 +1298,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 +1327,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,13 +1335,11 @@ 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; } - /* reset temp data */ + /* reset temp data */ temp_data[i] = 0; } @@ -1357,6 +1373,117 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for MEM_freeN(temp_data); } +typedef struct DynamicPaintSetInitColorData { + const DynamicPaintSurface *surface; + + const MLoop *mloop; + const MLoopUV *mloopuv; + const MLoopTri *mlooptri; + const MLoopCol *mloopcol; + struct ImagePool *pool; + + const bool scene_color_manage; +} DynamicPaintSetInitColorData; + +static void dynamic_paint_set_init_color_tex_to_vcol_cb(void *userdata, const int i) +{ + const DynamicPaintSetInitColorData *data = userdata; + + const PaintSurfaceData *sData = data->surface->data; + PaintPoint *pPoint = (PaintPoint *)sData->type_data; + + const MLoop *mloop = data->mloop; + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + struct ImagePool *pool = data->pool; + Tex *tex = data->surface->init_texture; + + const bool scene_color_manage = data->scene_color_manage; + + float uv[3] = {0.0f}; + + for (int j = 3; j--;) { + TexResult texres = {0}; + const unsigned int vert = mloop[mlooptri[i].tri[j]].v; + + /* remap to [-1.0, 1.0] */ + uv[0] = mloopuv[mlooptri[i].tri[j]].uv[0] * 2.0f - 1.0f; + uv[1] = mloopuv[mlooptri[i].tri[j]].uv[1] * 2.0f - 1.0f; + + multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage, false); + + if (texres.tin > pPoint[vert].color[3]) { + copy_v3_v3(pPoint[vert].color, &texres.tr); + pPoint[vert].color[3] = texres.tin; + } + } +} + +static void dynamic_paint_set_init_color_tex_to_imseq_cb(void *userdata, const int i) +{ + const DynamicPaintSetInitColorData *data = userdata; + + const PaintSurfaceData *sData = data->surface->data; + PaintPoint *pPoint = (PaintPoint *)sData->type_data; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + Tex *tex = data->surface->init_texture; + ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; + const int samples = (data->surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + + const bool scene_color_manage = data->scene_color_manage; + + float uv[9] = {0.0f}; + float uv_final[3] = {0.0f}; + + TexResult texres = {0}; + + /* collect all uvs */ + for (int j = 3; j--;) { + copy_v2_v2(&uv[j * 3], mloopuv[mlooptri[f_data->uv_p[i].tri_index].tri[j]].uv); + } + + /* interpolate final uv pos */ + interp_v3_v3v3v3(uv_final, &uv[0], &uv[3], &uv[6], f_data->barycentricWeights[i * samples].v); + /* remap to [-1.0, 1.0] */ + uv_final[0] = uv_final[0] * 2.0f - 1.0f; + uv_final[1] = uv_final[1] * 2.0f - 1.0f; + + multitex_ext_safe(tex, uv_final, &texres, NULL, scene_color_manage, false); + + /* apply color */ + copy_v3_v3(pPoint[i].color, &texres.tr); + pPoint[i].color[3] = texres.tin; +} + +static void dynamic_paint_set_init_color_vcol_to_imseq_cb(void *userdata, const int i) +{ + const DynamicPaintSetInitColorData *data = userdata; + + const PaintSurfaceData *sData = data->surface->data; + PaintPoint *pPoint = (PaintPoint *)sData->type_data; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopCol *mloopcol = data->mloopcol; + ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; + const int samples = (data->surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + + const int tri_idx = f_data->uv_p[i].tri_index; + float colors[3][4]; + float final_color[4]; + + /* collect color values */ + for (int j = 3; j--;) { + rgba_uchar_to_float(colors[j], (const unsigned char *)&mloopcol[mlooptri[tri_idx].tri[j]].r); + } + + /* interpolate final color */ + interp_v4_v4v4v4(final_color, UNPACK3(colors), f_data->barycentricWeights[i * samples].v); + + copy_v4_v4(pPoint[i].color, final_color); +} + static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface *surface) { PaintSurfaceData *sData = surface->data; @@ -1370,10 +1497,10 @@ 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++) { copy_v4_v4(pPoint[i].color, surface->init_color); } @@ -1389,69 +1516,36 @@ 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 */ if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { struct ImagePool *pool = BKE_image_pool_new(); -#pragma omp parallel for schedule(static) shared(pool) - for (i = 0; i < tottri; i++) { - float uv[3] = {0.0f}; - int j; - for (j = 0; j < 3; j++) { - TexResult texres = {0}; - unsigned int vert = mloop[mlooptri[i].tri[j]].v; - - /* remap to -1.0 to 1.0 */ - uv[0] = mloopuv[mlooptri[i].tri[j]].uv[0] * 2.0f - 1.0f; - uv[1] = mloopuv[mlooptri[i].tri[j]].uv[1] * 2.0f - 1.0f; - - multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage, false); - - if (texres.tin > pPoint[vert].color[3]) { - copy_v3_v3(pPoint[vert].color, &texres.tr); - pPoint[vert].color[3] = texres.tin; - } - } - } + DynamicPaintSetInitColorData data = { + .surface = surface, + .mloop = mloop, .mlooptri = mlooptri, .mloopuv = mloopuv, .pool = pool, + .scene_color_manage = scene_color_manage + }; + BLI_task_parallel_range(0, tottri, &data, dynamic_paint_set_init_color_tex_to_vcol_cb, tottri > 1000); BKE_image_pool_free(pool); } else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; - int samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; - -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - float uv[9] = {0.0f}; - float uv_final[3] = {0.0f}; - int j; - TexResult texres = {0}; - - /* collect all uvs */ - for (j = 0; j < 3; j++) { - copy_v2_v2(&uv[j * 3], mloopuv[mlooptri[f_data->uv_p[i].tri_index].tri[j]].uv); - } - - /* interpolate final uv pos */ - interp_v3_v3v3v3(uv_final, &uv[0], &uv[3], &uv[6], - f_data->barycentricWeights[i * samples].v); - /* remap to -1.0 to 1.0 */ - uv_final[0] = uv_final[0] * 2.0f - 1.0f; - uv_final[1] = uv_final[1] * 2.0f - 1.0f; - - multitex_ext_safe(tex, uv_final, &texres, NULL, scene_color_manage, false); - - /* apply color */ - copy_v3_v3(pPoint[i].color, &texres.tr); - pPoint[i].color[3] = texres.tin; - } + DynamicPaintSetInitColorData data = { + .surface = surface, + .mlooptri = mlooptri, .mloopuv = mloopuv, + .scene_color_manage = scene_color_manage + }; + BLI_task_parallel_range(0, sData->total_points, &data, dynamic_paint_set_init_color_tex_to_imseq_cb, + sData->total_points > 1000); } } /* vertex color layer */ @@ -1462,37 +1556,25 @@ 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++) { rgba_uchar_to_float(pPoint[mloop[i].v].color, (const unsigned char *)&col[mloop[i].v].r); } } else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { const MLoopTri *mlooptri = dm->getLoopTriArray(dm); - 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; - -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - int tri_ind = f_data->uv_p[i].tri_index; - float colors[3][4]; - float final_color[4]; - int j; - - /* collect color values */ - for (j = 0; j < 3; j++) { - rgba_uchar_to_float(colors[j], (const unsigned char *)&col[mlooptri[tri_ind].tri[j]].r); - } - - /* interpolate final color */ - interp_v4_v4v4v4(final_color, UNPACK3(colors), f_data->barycentricWeights[i * samples].v); - - copy_v4_v4(pPoint[i].color, final_color); - } + if (!col) + return; + + DynamicPaintSetInitColorData data = { + .surface = surface, + .mlooptri = mlooptri, .mloopcol = col, + }; + BLI_task_parallel_range(0, sData->total_points, &data, dynamic_paint_set_init_color_vcol_to_imseq_cb, + sData->total_points > 1000); } } } @@ -1527,20 +1609,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) @@ -1561,39 +1647,153 @@ static bool dynamicPaint_checkSurfaceData(const Scene *scene, DynamicPaintSurfac /***************************** Modifier processing ******************************/ +typedef struct DynamicPaintModifierApplyData { + const DynamicPaintSurface *surface; + Object *ob; + + MVert *mvert; + const MLoop *mloop; + const MPoly *mpoly; + + float (*fcolor)[4]; + MLoopCol *mloopcol; + MLoopCol *mloopcol_wet; + MLoopCol *mloopcol_preview; +} DynamicPaintModifierApplyData; + +static void dynamic_paint_apply_surface_displace_cb(void *userdata, const int i) +{ + const DynamicPaintModifierApplyData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + MVert *mvert = data->mvert; + + float normal[3]; + const float *value = (float *)surface->data->type_data; + const float val = value[i] * surface->disp_factor; + + normal_short_to_float_v3(normal, mvert[i].no); + + /* same as 'mvert[i].co[0] -= normal[0] * val' etc. */ + madd_v3_v3fl(mvert[i].co, normal, -val); +} /* apply displacing vertex surface to the derived mesh */ static void dynamicPaint_applySurfaceDisplace(DynamicPaintSurface *surface, DerivedMesh *result) { 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) { MVert *mvert = result->getVertArray(result); - int i; - const float *value = (float *)sData->type_data; -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - float normal[3], val = value[i] * surface->disp_factor; - normal_short_to_float_v3(normal, mvert[i].no); - normalize_v3(normal); + DynamicPaintModifierApplyData data = {.surface = surface, .mvert = mvert}; + BLI_task_parallel_range(0, sData->total_points, &data, dynamic_paint_apply_surface_displace_cb, + sData->total_points > 10000); + } +} + +static void dynamic_paint_apply_surface_vpaint_blend_cb(void *userdata, const int i) +{ + const DynamicPaintModifierApplyData *data = userdata; + + PaintPoint *pPoint = (PaintPoint *)data->surface->data->type_data; + float (*fcolor)[4] = data->fcolor; + + /* blend dry and wet layer */ + blendColors(pPoint[i].color, pPoint[i].color[3], pPoint[i].e_color, pPoint[i].e_color[3], fcolor[i]); +} + +static void dynamic_paint_apply_surface_vpaint_cb(void *userdata, const int p_index) +{ + const DynamicPaintModifierApplyData *data = userdata; + Object *ob = data->ob; + + const MLoop *mloop = data->mloop; + const MPoly *mpoly = data->mpoly; + + const DynamicPaintSurface *surface = data->surface; + PaintPoint *pPoint = (PaintPoint *)surface->data->type_data; + float (*fcolor)[4] = data->fcolor; + + MLoopCol *mloopcol = data->mloopcol; + MLoopCol *mloopcol_wet = data->mloopcol_wet; + MLoopCol *mloopcol_preview = data->mloopcol_preview; - mvert[i].co[0] -= normal[0] * val; - mvert[i].co[1] -= normal[1] * val; - mvert[i].co[2] -= normal[2] * val; + const Material *material = mloopcol_preview ? + give_current_material(ob, mpoly[p_index].mat_nr + 1) : NULL; + + for (int j = 0; j < mpoly[p_index].totloop; j++) { + const int l_index = mpoly[p_index].loopstart + j; + const int v_index = mloop[l_index].v; + + /* save layer data to output layer */ + /* apply color */ + if (mloopcol) { + rgba_float_to_uchar((unsigned char *)&mloopcol[l_index].r, fcolor[v_index]); + } + /* apply wetness */ + if (mloopcol_wet) { + const char c = FTOCHAR(pPoint[v_index].wetness); + mloopcol_wet[l_index].r = c; + mloopcol_wet[l_index].g = c; + mloopcol_wet[l_index].b = c; + mloopcol_wet[l_index].a = 255; + } + + /* viewport preview */ + if (mloopcol_preview) { + if (surface->preview_id == MOD_DPAINT_SURFACE_PREV_PAINT) { + float c[3]; + + /* Apply material color as base vertex color for preview */ + mloopcol_preview[l_index].a = 255; + if (material) { + c[0] = material->r; + c[1] = material->g; + c[2] = material->b; + } + else { /* default gray */ + c[0] = 0.65f; + c[1] = 0.65f; + c[2] = 0.65f; + } + /* mix surface color */ + interp_v3_v3v3(c, c, fcolor[v_index], fcolor[v_index][3]); + + rgb_float_to_uchar((unsigned char *)&mloopcol_preview[l_index].r, c); + } + else { + const char c = FTOCHAR(pPoint[v_index].wetness); + mloopcol_preview[l_index].r = c; + mloopcol_preview[l_index].g = c; + mloopcol_preview[l_index].b = c; + mloopcol_preview[l_index].a = 255; + } } } } +static void dynamic_paint_apply_surface_wave_cb(void *userdata, const int i) +{ + const DynamicPaintModifierApplyData *data = userdata; + + PaintWavePoint *wPoint = (PaintWavePoint *)data->surface->data->type_data; + MVert *mvert = data->mvert; + float normal[3]; + + normal_short_to_float_v3(normal, mvert[i].no); + madd_v3_v3fl(mvert[i].co, normal, wPoint[i].height); +} + /* * 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,129 +1807,65 @@ 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) { /* vertex color paint */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - - int i; - PaintPoint *pPoint = (PaintPoint *)sData->type_data; - MLoopCol *col = NULL; MLoop *mloop = CDDM_get_loops(result); - int totloop = result->numLoopData; + const int totloop = result->numLoopData; + MPoly *mpoly = CDDM_get_polys(result); + const int totpoly = result->numPolyData; /* paint is stored on dry and wet layers, so mix final color first */ - float *fcolor = MEM_callocN(sizeof(float) * sData->total_points * 4, "Temp paint color"); + float (*fcolor)[4] = MEM_callocN(sizeof(*fcolor) * sData->total_points, "Temp paint color"); -#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]); - } - - /* viewport preview */ - if (surface->flags & MOD_DPAINT_PREVIEW) { - MPoly *mp = CDDM_get_polys(result); - int totpoly = result->numPolyData; - -#if 0 - /* XXX We have to create a CD_PREVIEW_MCOL, else it might sigsev - * (after a SubSurf mod, eg)... */ - if (!result->getTessFaceDataArray(result, CD_PREVIEW_MCOL)) { - int numFaces = result->getNumTessFaces(result); - CustomData_add_layer(&result->faceData, CD_PREVIEW_MCOL, CD_CALLOC, NULL, numFaces); - } -#endif - - /* 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) { -#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++) { - int l_index = mp[i].loopstart + j; - int v_index = mloop[l_index].v; - - if (surface->preview_id == MOD_DPAINT_SURFACE_PREV_PAINT) { - float c[3]; - v_index *= 4; - - /* Apply material color as base vertex color for preview */ - col[l_index].a = 255; - if (material) { - c[0] = material->r; - c[1] = material->g; - c[2] = material->b; - } - else { /* default gray */ - c[0] = 0.65f; - c[1] = 0.65f; - c[2] = 0.65f; - } - /* mix surface color */ - interp_v3_v3v3(c, c, &fcolor[v_index], fcolor[v_index + 3]); - - 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); - col[l_index].a = 255; - } - } - } - } - } - - - /* save layer data to output layer */ + DynamicPaintModifierApplyData data = {.surface = surface, .fcolor = fcolor}; + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_apply_surface_vpaint_blend_cb, + sData->total_points > 1000); /* paint layer */ - col = CustomData_get_layer_named(&result->loopData, CD_MLOOPCOL, surface->output_name); + MLoopCol *mloopcol = 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); - /* 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? */ - } + if (!mloopcol && dynamicPaint_outputLayerExists(surface, ob, 0)) { + mloopcol = CustomData_add_layer_named( + &result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name); } - - MEM_freeN(fcolor); /* wet layer */ - col = CustomData_get_layer_named(&result->loopData, CD_MLOOPCOL, surface->output_name2); + MLoopCol *mloopcol_wet = 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); - /* 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); - col[i].a = 255; + if (!mloopcol_wet && dynamicPaint_outputLayerExists(surface, ob, 1)) { + mloopcol_wet = CustomData_add_layer_named( + &result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name2); + } + + /* Save preview results to weight layer to be able to share same drawing methods */ + MLoopCol *mloopcol_preview = NULL; + if (surface->flags & MOD_DPAINT_PREVIEW) { + mloopcol_preview = CustomData_get_layer(&result->loopData, CD_PREVIEW_MLOOPCOL); + if (!mloopcol_preview) { + mloopcol_preview = CustomData_add_layer( + &result->loopData, CD_PREVIEW_MLOOPCOL, CD_CALLOC, NULL, totloop); } } + data.ob = ob; + data.mloop = mloop; + data.mpoly = mpoly; + data.mloopcol = mloopcol; + data.mloopcol_wet = mloopcol_wet; + data.mloopcol_preview = mloopcol_preview; + + BLI_task_parallel_range(0, totpoly, &data, dynamic_paint_apply_surface_vpaint_cb, + totpoly > 1000); + + MEM_freeN(fcolor); + /* Mark tessellated CD layers as dirty. */ result->dirty |= DM_DIRTY_TESS_CDLAYERS; } @@ -1748,9 +1884,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 +1896,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); @@ -1774,15 +1910,10 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, /* wave simulation */ else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { MVert *mvert = result->getVertArray(result); - int i; - PaintWavePoint *wPoint = (PaintWavePoint *)sData->type_data; - -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - float normal[3]; - normal_short_to_float_v3(normal, mvert[i].no); - madd_v3_v3fl(mvert[i].co, normal, wPoint[i].height); - } + + DynamicPaintModifierApplyData data = {.surface = surface, .mvert = mvert}; + BLI_task_parallel_range(0, sData->total_points, &data, dynamic_paint_apply_surface_wave_cb, + sData->total_points > 1000); update_normals = true; } @@ -1801,7 +1932,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 +1971,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 +1983,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; @@ -1907,263 +2043,462 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene /* Modifier call. Processes dynamic paint modifier step. */ DerivedMesh *dynamicPaint_Modifier_do(DynamicPaintModifierData *pmd, Scene *scene, Object *ob, DerivedMesh *dm) { - /* For now generate looptris in every case */ - DM_ensure_looptri(dm); + if (pmd->canvas) { + DerivedMesh *ret; + + /* For now generate looptris in every case */ + DM_ensure_looptri(dm); - /* Update canvas data for a new frame */ - dynamicPaint_frameUpdate(pmd, scene, ob, dm); + /* Update canvas data for a new frame */ + dynamicPaint_frameUpdate(pmd, scene, ob, dm); - /* Return output mesh */ - return dynamicPaint_Modifier_apply(pmd, ob, dm); + /* Return output mesh */ + ret = dynamicPaint_Modifier_apply(pmd, ob, dm); + + return ret; + } + else { + /* For now generate looptris in every case */ + DM_ensure_looptri(dm); + + /* Update canvas data for a new frame */ + dynamicPaint_frameUpdate(pmd, scene, ob, dm); + + /* Return output mesh */ + return dynamicPaint_Modifier_apply(pmd, ob, dm); + } } /***************************** Image Sequence / UV Image Surface Calls ******************************/ /* - * Tries to find the neighboring pixel in given (uv space) direction. - * Result is used by effect system to move paint on the surface. + * Create a surface for uv image sequence format + */ +#define JITTER_SAMPLES { \ + 0.0f, 0.0f, \ + -0.2f, -0.4f, \ + 0.2f, 0.4f, \ + 0.4f, -0.2f, \ + -0.4f, 0.3f, \ +} + +typedef struct DynamicPaintCreateUVSurfaceData { + const DynamicPaintSurface *surface; + + PaintUVPoint *tempPoints; + Vec3f *tempWeights; + + const MLoopTri *mlooptri; + const MLoopUV *mloopuv; + const MLoop *mloop; + const int tottri; + + const Bounds2D *faceBB; + uint32_t *active_points; +} DynamicPaintCreateUVSurfaceData; + +static void dynamic_paint_create_uv_surface_direct_cb(void *userdata, const int ty) +{ + const DynamicPaintCreateUVSurfaceData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + PaintUVPoint *tempPoints = data->tempPoints; + Vec3f *tempWeights = data->tempWeights; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + const MLoop *mloop = data->mloop; + const int tottri = data->tottri; + + const Bounds2D *faceBB = data->faceBB; + + const float jitter5sample[10] = JITTER_SAMPLES; + const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + const int w = surface->image_resolution; + const int h = w; + + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; + PaintUVPoint *tPoint = &tempPoints[index]; + float point[5][2]; + + /* Init per pixel settings */ + tPoint->tri_index = -1; + tPoint->neighbour_pixel = -1; + tPoint->pixel_index = index; + + /* Actual pixel center, used when collision is found */ + point[0][0] = ((float)tx + 0.5f) / w; + point[0][1] = ((float)ty + 0.5f) / h; + + /* + * A pixel middle sample isn't enough to find very narrow polygons + * So using 4 samples of each corner too + */ + point[1][0] = ((float)tx) / w; + point[1][1] = ((float)ty) / h; + + point[2][0] = ((float)tx + 1) / w; + point[2][1] = ((float)ty) / h; + + point[3][0] = ((float)tx) / w; + point[3][1] = ((float)ty + 1) / h; + + point[4][0] = ((float)tx + 1) / w; + point[4][1] = ((float)ty + 1) / h; + + + /* Loop through samples, starting from middle point */ + for (int sample = 0; sample < 5; sample++) { + /* Loop through every face in the mesh */ + /* XXX TODO This is *horrible* with big meshes, should use a 2D BVHTree over UV tris here! */ + for (int i = 0; i < tottri; i++) { + /* Check uv bb */ + 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; + } + + 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; + + /* 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 (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(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v); + } + + /* Set surface point face values */ + tPoint->tri_index = i; + + /* save vertex indexes */ + tPoint->v1 = mloop[mlooptri[i].tri[0]].v; + tPoint->v2 = mloop[mlooptri[i].tri[1]].v; + tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + + sample = 5; /* make sure we exit sample loop as well */ + break; + } + } + } + } +} + +static void dynamic_paint_create_uv_surface_neighbor_cb(void *userdata, const int ty) +{ + const DynamicPaintCreateUVSurfaceData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + PaintUVPoint *tempPoints = data->tempPoints; + Vec3f *tempWeights = data->tempWeights; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + const MLoop *mloop = data->mloop; + + uint32_t *active_points = data->active_points; + + const float jitter5sample[10] = JITTER_SAMPLES; + const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + const int w = surface->image_resolution; + const int h = w; + + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; + PaintUVPoint *tPoint = &tempPoints[index]; + + /* If point isn't on canvas mesh */ + if (tPoint->tri_index == -1) { + float point[2]; + + /* get loop area */ + const int u_min = (tx > 0) ? -1 : 0; + const int u_max = (tx < (w - 1)) ? 1 : 0; + const int v_min = (ty > 0) ? -1 : 0; + const int v_max = (ty < (h - 1)) ? 1 : 0; + + point[0] = ((float)tx + 0.5f) / w; + point[1] = ((float)ty + 0.5f) / h; + + /* search through defined area for neighbor */ + for (int u = u_min; u <= u_max; u++) { + for (int v = v_min; v <= v_max; v++) { + /* if not this pixel itself */ + if (u != 0 || v != 0) { + const int ind = (tx + u) + w * (ty + v); + + /* if neighbor has index */ + if (tempPoints[ind].neighbour_pixel == -1 && tempPoints[ind].tri_index != -1) { + 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; + + /* tri index */ + /* There is a low possibility of actually having a neighbor point which tri is + * already set from another neighbor in a separate thread here. + * Cheking for both tri_index and neighbour_pixel above reduces that probability + * but it remains possible. + * That atomic op (and its memory fence) ensures tPoint->neighbour_pixel is set + * to non--1 *before* its tri_index is set (i.e. that it cannot be used a neighbour). + */ + tPoint->neighbour_pixel = ind - 1; + atomic_add_uint32(&tPoint->neighbour_pixel, 1); + tPoint->tri_index = i; + + /* Now calculate pixel data for this pixel as it was on polygon surface */ + /* Add b-weights per anti-aliasing sample */ + 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(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v); + } + + /* save vertex indexes */ + tPoint->v1 = mloop[mlooptri[i].tri[0]].v; + tPoint->v2 = mloop[mlooptri[i].tri[1]].v; + tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + + u = u_max + 1; /* make sure we exit outer loop as well */ + break; + } + } + } + } + } + + /* Increase the final number of active surface points if relevant. */ + if (tPoint->tri_index != -1) + atomic_add_uint32(active_points, 1); + } +} + +#undef JITTER_SAMPLES + +/* Tries to find the neighboring pixel in given (uv space) direction. + * Result is used by effect system to move paint on the surface. * - * px, py : origin pixel x and y - * n_index : lookup direction index (use neighX, neighY to get final index) + * px, py : origin pixel x and y + * n_index : lookup direction index (use neighX, neighY to get final index) */ -static int dynamicPaint_findNeighbourPixel(PaintUVPoint *tempPoints, DerivedMesh *dm, - const char *uvname, int w, int h, int px, int py, int n_index) +static int dynamic_paint_find_neighbour_pixel( + const DynamicPaintCreateUVSurfaceData *data, const MeshElemMap *vert_to_looptri_map, + const int w, const int h, const int px, const int py, const int n_index) { /* Note: Current method only uses polygon edges to detect neighboring pixels. - * -> It doesn't always lead to the optimum pixel but is accurate enough - * and faster/simpler than including possible face tip point links) + * -> It doesn't always lead to the optimum pixel but is accurate enough + * and faster/simpler than including possible face tip point links) */ - int x, y; - PaintUVPoint *tPoint = NULL; - PaintUVPoint *cPoint = NULL; - /* shift position by given n_index */ - x = px + neighX[n_index]; - y = py + neighY[n_index]; + const int x = px + neighX[n_index]; + const int 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 */ + const PaintUVPoint *tempPoints = data->tempPoints; + const PaintUVPoint *tPoint = &tempPoints[x + w * y]; /* UV neighbor */ + const PaintUVPoint *cPoint = &tempPoints[px + w * py]; /* Origin point */ - /* - * Check if shifted point is on same face -> it's a correct neighbor - * (and if it isn't marked as an "edge pixel") - */ + /* Check if shifted point is on same face -> it's a correct neighbor (and if it isn't marked as an "edge pixel") */ if ((tPoint->tri_index == cPoint->tri_index) && (tPoint->neighbour_pixel == -1)) return (x + w * y); - /* - * Even if shifted point is on another face - * -> use this point. + /* Even if shifted point is on another face + * -> use this point. * - * !! Replace with "is uv faces linked" check !! - * This should work fine as long as uv island - * margin is > 1 pixel. + * !! Replace with "is uv faces linked" check !! + * This should work fine as long as uv island margin is > 1 pixel. */ if ((tPoint->tri_index != -1) && (tPoint->neighbour_pixel == -1)) { return (x + w * y); } - /* - * If we get here, the actual neighboring pixel - * is located on a non-linked uv face, and we have to find - * it's "real" position. + /* If we get here, the actual neighboring pixel is located on a non-linked uv face, + * and we have to find its "real" position. * - * Simple neighboring face finding algorithm: - * - find closest uv edge to shifted pixel and get - * the another face that shares that edge - * - find corresponding position of that new face edge - * in uv space + * Simple neighboring face finding algorithm: + * - find closest uv edge to shifted pixel and get the another face that shares that edge + * - find corresponding position of that new face edge in uv space * - * TODO: Implement something more accurate / optimized? + * TODO: Implement something more accurate / optimized? */ { - const MLoop *mloop = dm->getLoopArray(dm); - const MLoopTri *mlooptri = dm->getLoopTriArray(dm); - const int tottri = dm->getNumLoopTri(dm); - const MLoopUV *mloopuv = CustomData_get_layer_named(&dm->loopData, CD_MLOOPUV, uvname); + const MLoop *mloop = data->mloop; + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; /* Get closest edge to that subpixel on UV map */ - { - float pixel[2]; - /* distances only used for comparison */ - float dist_squared, t_dist_squared; - - int i, edge1_index, edge2_index, - 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; - - const float *s_uv1, *s_uv2, *t_uv1, *t_uv2; - - pixel[0] = ((float)(px + neighX[n_index]) + 0.5f) / (float)w; - pixel[1] = ((float)(py + neighY[n_index]) + 0.5f) / (float)h; - - /* - * Find closest edge to that pixel - */ - - /* Dist to first edge */ - e1_index = cPoint->v1; - e2_index = cPoint->v2; - edge1_index = 0; - edge2_index = 1; - dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv); - - /* Dist to second edge */ - t_dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv); - if (t_dist_squared < dist_squared) { - e1_index = cPoint->v2; - e2_index = cPoint->v3; - edge1_index = 1; - edge2_index = 2; - dist_squared = t_dist_squared; - } - /* Dist to third edge */ - t_dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv); - if (t_dist_squared < dist_squared) { - e1_index = cPoint->v3; - e2_index = cPoint->v1; - edge1_index = 2; - edge2_index = 0; - dist_squared = t_dist_squared; - } + float pixel[2]; + /* distances only used for comparison */ + float dist_squared, t_dist_squared; + int 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; - /* - * Now find another face that is linked to that edge - */ - target_tri = -1; + const float *s_uv1, *s_uv2, *t_uv1, *t_uv2; - for (i = 0; i < tottri; i++) { - /* - * 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; + pixel[0] = ((float)(px + neighX[n_index]) + 0.5f) / (float)w; + pixel[1] = ((float)(py + neighY[n_index]) + 0.5f) / (float)h; - target_tri = i; + /* + * Find closest edge to that pixel + */ - /* - * 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; + /* Dist to first edge */ + e1_index = cPoint->v1; + e2_index = cPoint->v2; + edge1_index = 0; + edge2_index = 1; + dist_squared = dist_squared_to_line_segment_v2( + pixel, + mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv, + mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv); + + /* Dist to second edge */ + t_dist_squared = dist_squared_to_line_segment_v2( + pixel, + mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv, + mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv); + if (t_dist_squared < dist_squared) { + e1_index = cPoint->v2; + e2_index = cPoint->v3; + edge1_index = 1; + edge2_index = 2; + dist_squared = t_dist_squared; + } - 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; + /* Dist to third edge */ + t_dist_squared = dist_squared_to_line_segment_v2( + pixel, + mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv, + mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv); + if (t_dist_squared < dist_squared) { + e1_index = cPoint->v3; + e2_index = cPoint->v1; + edge1_index = 2; + edge2_index = 0; + dist_squared = t_dist_squared; + } - break; - } - } + /* + * Now find another face that is linked to that edge + */ + target_tri = -1; - /* If none found pixel is on mesh edge */ - if (target_tri == -1) return ON_MESH_EDGE; + /* Use a pre-computed vert-to-looptri mapping, speeds up things a lot compared to looping over all loopti. */ + for (int i = 0; i < vert_to_looptri_map[e1_index].count; i++) { + const int lt_index = vert_to_looptri_map[e1_index].indices[i]; + const int v0 = mloop[mlooptri[lt_index].tri[0]].v; + const int v1 = mloop[mlooptri[lt_index].tri[1]].v; + const int v2 = mloop[mlooptri[lt_index].tri[2]].v; - /* - * If target face is connected in UV space as well, just use original index - */ - s_uv1 = mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv; - s_uv2 = mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv; - t_uv1 = mloopuv[mlooptri[target_tri].tri[target_uv1]].uv; - t_uv2 = mloopuv[mlooptri[target_tri].tri[target_uv2]].uv; + BLI_assert(ELEM(e1_index, v0, v1, v2)); - //printf("connected UV : %f,%f & %f,%f - %f,%f & %f,%f\n", s_uv1[0], s_uv1[1], s_uv2[0], s_uv2[1], t_uv1[0], t_uv1[1], t_uv2[0], t_uv2[1]); + if (ELEM(e2_index, v0, v1, v2)) { + if (lt_index == cPoint->tri_index) + continue; - if (((s_uv1[0] == t_uv1[0] && s_uv1[1] == t_uv1[1]) && - (s_uv2[0] == t_uv2[0] && s_uv2[1] == t_uv2[1]) ) || - ((s_uv2[0] == t_uv1[0] && s_uv2[1] == t_uv1[1]) && - (s_uv1[0] == t_uv2[0] && s_uv1[1] == t_uv2[1]) )) - { - return ((px + neighX[n_index]) + w * (py + neighY[n_index])); - } + target_tri = lt_index; - /* - * Find a point that is relatively at same edge position - * on this other face UV - */ - lambda = closest_to_line_v2( - 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; - - sub_v2_v2v2( - dir_vec, - mloopuv[mlooptri[target_tri].tri[target_uv2]].uv, - mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); - - mul_v2_fl(dir_vec, lambda); - - copy_v2_v2(pixel, mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); - add_v2_v2(pixel, dir_vec); - 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]); - - /* 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; - - 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 found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ - if (tempPoints[final_index].tri_index != target_tri) { - return NOT_FOUND; + /* 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 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 none found pixel is on mesh edge */ + if (target_tri == -1) + return ON_MESH_EDGE; - return final_index; + /* + * If target face is connected in UV space as well, just use original index + */ + s_uv1 = mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv; + s_uv2 = mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv; + t_uv1 = mloopuv[mlooptri[target_tri].tri[target_uv1]].uv; + t_uv2 = mloopuv[mlooptri[target_tri].tri[target_uv2]].uv; + + //printf("connected UV : %f,%f & %f,%f - %f,%f & %f,%f\n", s_uv1[0], s_uv1[1], s_uv2[0], s_uv2[1], t_uv1[0], t_uv1[1], t_uv2[0], t_uv2[1]); + + if (((s_uv1[0] == t_uv1[0] && s_uv1[1] == t_uv1[1]) && + (s_uv2[0] == t_uv2[0] && s_uv2[1] == t_uv2[1])) || + ((s_uv2[0] == t_uv1[0] && s_uv2[1] == t_uv1[1]) && + (s_uv1[0] == t_uv2[0] && s_uv1[1] == t_uv2[1]))) + { + return ((px + neighX[n_index]) + w * (py + neighY[n_index])); } + + /* + * Find a point that is relatively at same edge position + * on this other face UV + */ + lambda = closest_to_line_v2( + closest_point, pixel, + mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv, + mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv); + CLAMP(lambda, 0.0f, 1.0f); + + sub_v2_v2v2( + dir_vec, + mloopuv[mlooptri[target_tri].tri[target_uv2]].uv, + mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); + + mul_v2_fl(dir_vec, lambda); + + copy_v2_v2(pixel, mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); + add_v2_v2(pixel, dir_vec); + pixel[0] = (pixel[0] * (float)w) - 0.5f; + pixel[1] = (pixel[1] * (float)h) - 0.5f; + + 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 || 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 found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ + 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; + + return final_index; } } -/* - * Create a surface for uv image sequence format - */ -int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) +int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface, float *progress, short *do_update) { /* Antialias jitter point relative coords */ - const float jitter5sample[10] = { - 0.0f, 0.0f, - -0.2f, -0.4f, - 0.2f, 0.4f, - 0.4f, -0.2f, - -0.4f, 0.3f, - }; - int ty; - int w, h; - int tottri; + const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - int active_points = 0; - int error = 0; + uint32_t active_points = 0; + bool error = false; PaintSurfaceData *sData; DynamicPaintCanvasSettings *canvas = surface->canvas; @@ -2177,7 +2512,9 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) Bounds2D *faceBB = NULL; int *final_index; - int aa_samples; + + *progress = 0.0f; + *do_update = true; if (!dm) return setError(canvas, N_("Canvas mesh not updated")); @@ -2186,7 +2523,7 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) mloop = dm->getLoopArray(dm); mlooptri = dm->getLoopTriArray(dm); - tottri = dm->getNumLoopTri(dm); + const int tottri = dm->getNumLoopTri(dm); /* get uv map */ if (CustomData_has_layer(&dm->loopData, CD_MLOOPUV)) { @@ -2200,7 +2537,8 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) if (surface->image_resolution < 16 || surface->image_resolution > 8192) return setError(canvas, N_("Invalid resolution")); - w = h = surface->image_resolution; + const int w = surface->image_resolution; + const int h = w; /* * Start generating the surface @@ -2208,156 +2546,60 @@ 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; + tempPoints = MEM_callocN(w * h * sizeof(*tempPoints), "Temp PaintUVPoint"); + if (!tempPoints) + error = true; - final_index = (int *) MEM_callocN(w * h * sizeof(int), "Temp UV Final Indexes"); - if (!final_index) error = 1; + final_index = MEM_callocN(w * h * sizeof(*final_index), "Temp UV Final Indexes"); + if (!final_index) + error = true; - tempWeights = (struct Vec3f *) MEM_mallocN(w * h * aa_samples * sizeof(struct Vec3f), "Temp bWeights"); - if (!tempWeights) error = 1; + tempWeights = MEM_mallocN(w * h * aa_samples * sizeof(*tempWeights), "Temp bWeights"); + if (!tempWeights) + error = true; /* * Generate a temporary bounding box array for UV faces to optimize * the pixel-inside-a-face search. */ if (!error) { - faceBB = (struct Bounds2D *) MEM_mallocN(tottri * sizeof(struct Bounds2D), "MPCanvasFaceBB"); - if (!faceBB) error = 1; + faceBB = MEM_mallocN(tottri * sizeof(*faceBB), "MPCanvasFaceBB"); + if (!faceBB) + error = true; } - if (!error) - for (ty = 0; ty < tottri; ty++) { - int i; + *progress = 0.01f; + *do_update = true; - copy_v2_v2(faceBB[ty].min, mloopuv[mlooptri[ty].tri[0]].uv); - copy_v2_v2(faceBB[ty].max, mloopuv[mlooptri[ty].tri[0]].uv); + if (!error) { + for (int i = 0; i < tottri; i++) { + copy_v2_v2(faceBB[i].min, mloopuv[mlooptri[i].tri[0]].uv); + copy_v2_v2(faceBB[i].max, mloopuv[mlooptri[i].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]); + for (int j = 1; j < 3; j++) { + minmax_v2v2_v2(faceBB[i].min, faceBB[i].max, mloopuv[mlooptri[i].tri[j]].uv); } } - /* - * Loop through every pixel and check - * if pixel is uv-mapped on a canvas face. - */ - if (!error) { -#pragma omp parallel for schedule(static) - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - 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; - - /* Init per pixel settings */ - tPoint->tri_index = -1; - tPoint->neighbour_pixel = -1; - tPoint->pixel_index = index; - - /* Actual pixel center, used when collision is found */ - point[0][0] = ((float)tx + 0.5f) / w; - point[0][1] = ((float)ty + 0.5f) / h; - - /* - * A pixel middle sample isn't enough to find very narrow polygons - * So using 4 samples of each corner too - */ - point[1][0] = ((float)tx) / w; - point[1][1] = ((float)ty) / h; - - point[2][0] = ((float)tx + 1) / w; - point[2][1] = ((float)ty) / h; + *progress = 0.02f; + *do_update = true; - point[3][0] = ((float)tx) / w; - point[3][1] = ((float)ty + 1) / h; + /* Loop through every pixel and check if pixel is uv-mapped on a canvas face. */ + DynamicPaintCreateUVSurfaceData data = { + .surface = surface, .tempPoints = tempPoints, .tempWeights = tempWeights, + .mlooptri = mlooptri, .mloopuv = mloopuv, .mloop = mloop, .tottri = tottri, + .faceBB = faceBB, + }; + BLI_task_parallel_range(0, h, &data, dynamic_paint_create_uv_surface_direct_cb, h > 64 || tottri > 1000); - point[4][0] = ((float)tx + 1) / w; - point[4][1] = ((float)ty + 1) / h; - - - /* 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) { - - float uv1co[2], uv2co[2], uv3co[2], uv[2]; - int j; - - /* 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); - - /* Add b-weights per anti-aliasing sample */ - for (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); - } - - /* Set surface point face values */ - tPoint->tri_index = i; - - /* save vertex indexes */ - tPoint->v1 = mloop[mlooptri[i].tri[0]].v; - tPoint->v2 = mloop[mlooptri[i].tri[1]].v; - tPoint->v3 = mloop[mlooptri[i].tri[2]].v; - - sample = 5; /* make sure we exit sample loop as well */ - break; - } - } - } /* sample loop */ - } - } + *progress = 0.04f; + *do_update = true; /* * Now loop through every pixel that was left without index @@ -2365,87 +2607,11 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) * If so use that polygon as pixel surface. * (To avoid seams on uv island edges) */ -#pragma omp parallel for schedule(static) - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - int index = tx + w * ty; - PaintUVPoint *tPoint = (&tempPoints[index]); - - /* If point isn't't on canvas mesh */ - if (tPoint->tri_index == -1) { - int u_min, u_max, v_min, v_max; - int u, v, ind; - float point[2]; - - /* get loop area */ - u_min = (tx > 0) ? -1 : 0; - u_max = (tx < (w - 1)) ? 1 : 0; - v_min = (ty > 0) ? -1 : 0; - v_max = (ty < (h - 1)) ? 1 : 0; - - point[0] = ((float)tx + 0.5f) / w; - point[1] = ((float)ty + 0.5f) / h; - - /* search through defined area for neighbor */ - for (u = u_min; u <= u_max; u++) - for (v = v_min; v <= v_max; v++) { - /* if not this pixel itself */ - if (u != 0 || v != 0) { - ind = (tx + u) + w * (ty + v); - - /* 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; - - /* 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++) { - - 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); - } - - /* Set values */ - tPoint->neighbour_pixel = ind; /* tri index */ - - /* save vertex indexes */ - tPoint->v1 = mloop[mlooptri[i].tri[0]].v; - tPoint->v2 = mloop[mlooptri[i].tri[1]].v; - tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + data.active_points = &active_points; + BLI_task_parallel_range(0, h, &data, dynamic_paint_create_uv_surface_neighbor_cb, h > 64); - u = u_max + 1; /* make sure we exit outer loop as well */ - break; - } - } - } - } - } - } - - /* - * When base loop is over convert found neighbor indexes to real ones - * Also count the final number of active surface points - */ - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - int index = tx + w * ty; - PaintUVPoint *tPoint = &tempPoints[index]; - - if (tPoint->tri_index == -1 && tPoint->neighbour_pixel != -1) - tPoint->tri_index = tempPoints[tPoint->neighbour_pixel].tri_index; - if (tPoint->tri_index != -1) - active_points++; - } - } + *progress = 0.06f; + *do_update = true; /* Generate surface adjacency data. */ { @@ -2461,25 +2627,31 @@ 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; - unsigned int n_pos = 0; - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - int index = tx + w * ty; + int n_pos = 0; + + MeshElemMap *vert_to_looptri_map; + int *vert_to_looptri_map_mem; + + BKE_mesh_vert_looptri_map_create( + &vert_to_looptri_map, &vert_to_looptri_map_mem, + dm->getVertArray(dm), dm->getNumVerts(dm), mlooptri, tottri, mloop, dm->getNumLoops(dm)); + + for (int ty = 0; ty < h; ty++) { + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; if (tempPoints[index].tri_index != -1) { ed->n_index[final_index[index]] = n_pos; ed->n_num[final_index[index]] = 0; for (int i = 0; i < 8; i++) { - - /* Try to find a neighboring pixel in defined direction - * If not found, -1 is returned */ - int n_target = dynamicPaint_findNeighbourPixel(tempPoints, dm, uvname, w, h, tx, ty, i); + /* Try to find a neighboring pixel in defined direction. If not found, -1 is returned */ + const int n_target = dynamic_paint_find_neighbour_pixel( + &data, vert_to_looptri_map, w, h, tx, ty, i); if (n_target >= 0) { ed->n_target[n_pos] = final_index[n_target]; @@ -2493,43 +2665,50 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) } } } + + MEM_freeN(vert_to_looptri_map); + MEM_freeN(vert_to_looptri_map_mem); } } + *progress = 0.08f; + *do_update = true; + /* Create final surface data without inactive points */ - { - ImgSeqFormatData *f_data = MEM_callocN(sizeof(struct ImgSeqFormatData), "ImgSeqFormatData"); - if (f_data) { - 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"); + ImgSeqFormatData *f_data = MEM_callocN(sizeof(*f_data), "ImgSeqFormatData"); + if (f_data) { + f_data->uv_p = MEM_callocN(active_points * sizeof(*f_data->uv_p), "PaintUVPoint"); + f_data->barycentricWeights = MEM_callocN(active_points * aa_samples * sizeof(*f_data->barycentricWeights), + "PaintUVPoint"); - if (!f_data->uv_p || !f_data->barycentricWeights) error = 1; - } - else { + if (!f_data->uv_p || !f_data->barycentricWeights) error = 1; - } + } + else { + error = 1; + } - sData->total_points = active_points; - - /* 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); - MEM_freeN(f_data); - } + /* 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); + MEM_freeN(f_data); } - else { - int index, cursor = 0; - sData->total_points = active_points; - sData->format_data = f_data; - - 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); - cursor++; - } + sData->total_points = 0; + } + else { + sData->total_points = (int)active_points; + sData->format_data = f_data; + + for (int index = 0, cursor = 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(*tempWeights) * aa_samples); + cursor++; } } } @@ -2537,10 +2716,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) { @@ -2550,38 +2733,126 @@ 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; /* 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[0] = 1.0f; + pPoint->color[1] = (float)(uvPoint->tri_index % 255) / 256.0f; + } } - #endif + dynamicPaint_setInitialColor(scene, surface); } + *progress = 0.09f; + *do_update = true; + return (error == 0); } /* * Outputs an image file from uv surface data. */ +typedef struct DynamicPaintOutputSurfaceImageData { + const DynamicPaintSurface *surface; + ImBuf *ibuf; +} DynamicPaintOutputSurfaceImageData; + +static void dynamic_paint_output_surface_image_paint_cb(void *userdata, const int index) +{ + const DynamicPaintOutputSurfaceImageData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintPoint *point = &((PaintPoint *)surface->data->type_data)[index]; + + ImBuf *ibuf = data->ibuf; + /* image buffer position */ + const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4; + + /* blend wet and dry layers */ + blendColors(point->color, point->color[3], point->e_color, point->e_color[3], &ibuf->rect_float[pos]); + + /* Multiply color by alpha if enabled */ + if (surface->flags & MOD_DPAINT_MULALPHA) { + mul_v3_fl(&ibuf->rect_float[pos], ibuf->rect_float[pos + 3]); + } +} + +static void dynamic_paint_output_surface_image_displace_cb(void *userdata, const int index) +{ + const DynamicPaintOutputSurfaceImageData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + float depth = ((float *)surface->data->type_data)[index]; + + ImBuf *ibuf = data->ibuf; + /* image buffer position */ + const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4; + + if (surface->depth_clamp) + depth /= surface->depth_clamp; + + if (surface->disp_type == MOD_DPAINT_DISP_DISPLACE) { + depth = (0.5f - depth / 2.0f); + } + + CLAMP(depth, 0.0f, 1.0f); + + copy_v3_fl(&ibuf->rect_float[pos], depth); + ibuf->rect_float[pos + 3] = 1.0f; +} + +static void dynamic_paint_output_surface_image_wave_cb(void *userdata, const int index) +{ + const DynamicPaintOutputSurfaceImageData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintWavePoint *wPoint = &((PaintWavePoint *)surface->data->type_data)[index]; + float depth = wPoint->height; + + ImBuf *ibuf = data->ibuf; + /* image buffer position */ + const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4; + + if (surface->depth_clamp) + depth /= surface->depth_clamp; + + depth = (0.5f + depth / 2.0f); + CLAMP(depth, 0.0f, 1.0f); + + copy_v3_fl(&ibuf->rect_float[pos], depth); + ibuf->rect_float[pos + 3] = 1.0f; +} + +static void dynamic_paint_output_surface_image_wetmap_cb(void *userdata, const int index) +{ + const DynamicPaintOutputSurfaceImageData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintPoint *point = &((PaintPoint *)surface->data->type_data)[index]; + + ImBuf *ibuf = data->ibuf; + /* image buffer position */ + const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4; + + copy_v3_fl(&ibuf->rect_float[pos], (point->wetness > 1.0f) ? 1.0f : point->wetness); + ibuf->rect_float[pos + 3] = 1.0f; +} + void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filename, short output_layer) { - int index; ImBuf *ibuf = NULL; PaintSurfaceData *sData = surface->data; - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; /* OpenEXR or PNG */ int format = (surface->image_fileformat & MOD_DPAINT_IMGFORMAT_OPENEXR) ? R_IMF_IMTYPE_OPENEXR : R_IMF_IMTYPE_PNG; char output_file[FILE_MAX]; @@ -2592,7 +2863,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); @@ -2608,82 +2880,66 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam return; } -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int pos = f_data->uv_p[index].pixel_index * 4; /* image buffer position */ - - /* Set values of preferred type */ - if (output_layer == 1) { - /* wetmap */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - 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; - ibuf->rect_float[pos + 3] = 1.0f; - } - } - else if (output_layer == 0) { - /* Paintmap */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - PaintPoint *point = &((PaintPoint *)sData->type_data)[index]; - - /* blend wet and dry layers */ - blendColors(point->color, point->color[3], point->e_color, point->e_color[3], &ibuf->rect_float[pos]); - - /* 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]; - } + DynamicPaintOutputSurfaceImageData data = {.surface = surface, .ibuf = ibuf}; + switch(surface->type) { + case MOD_DPAINT_SURFACE_T_PAINT: + switch (output_layer) { + case 0: + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_output_surface_image_paint_cb, sData->total_points > 10000); + break; + case 1: + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_output_surface_image_wetmap_cb, sData->total_points > 10000); + break; + default: + BLI_assert(0); + break; } - /* displace */ - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { - float depth = ((float *)sData->type_data)[index]; - if (surface->depth_clamp) - depth /= surface->depth_clamp; - - if (surface->disp_type == MOD_DPAINT_DISP_DISPLACE) { - 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; - ibuf->rect_float[pos + 3] = 1.0f; + break; + case MOD_DPAINT_SURFACE_T_DISPLACE: + switch (output_layer) { + case 0: + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_output_surface_image_displace_cb, sData->total_points > 10000); + break; + case 1: + break; + default: + BLI_assert(0); + break; } - /* waves */ - else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { - PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; - float depth = wPoint->height; - if (surface->depth_clamp) - depth /= surface->depth_clamp; - 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; - ibuf->rect_float[pos + 3] = 1.0f; + break; + case MOD_DPAINT_SURFACE_T_WAVE: + switch (output_layer) { + case 0: + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_output_surface_image_wave_cb, sData->total_points > 10000); + break; + case 1: + break; + default: + BLI_assert(0); + break; } - } + break; + default: + BLI_assert(0); + break; } /* Set output format, png in case exr isn't supported */ - ibuf->ftype = IMB_FTYPE_PNG; - ibuf->foptions.quality = 15; - #ifdef WITH_OPENEXR if (format == R_IMF_IMTYPE_OPENEXR) { /* OpenEXR 32-bit float */ ibuf->ftype = IMB_FTYPE_OPENEXR; ibuf->foptions.flag |= OPENEXR_COMPRESS; } + else #endif + { + ibuf->ftype = IMB_FTYPE_PNG; + ibuf->foptions.quality = 15; + } /* Save image */ IMB_saveiff(ibuf, output_file, IB_rectfloat); @@ -2749,7 +3005,7 @@ static void dynamicPaint_freeBrushMaterials(BrushMaterials *bMats) * Get material diffuse color and alpha (including linked textures) in given coordinates */ static void dynamicPaint_doMaterialTex( - BrushMaterials *bMats, float color[3], float *alpha, Object *brushOb, + const BrushMaterials *bMats, float color[3], float *alpha, Object *brushOb, const float volume_co[3], const float surface_co[3], int triIndex, DerivedMesh *orcoDm) { @@ -2762,9 +3018,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 +3035,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 +3061,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. @@ -2849,15 +3106,15 @@ static void mesh_tris_nearest_point_dp(void *userdata, int index, const float co * operations when using substeps */ static void dynamicPaint_mixPaintColors( - DynamicPaintSurface *surface, int index, int paintFlags, - const float paintColor[3], float *paintAlpha, float *paintWetness, float *timescale) + const DynamicPaintSurface *surface, const int index, const int paintFlags, + const float paintColor[3], const float paintAlpha, const float paintWetness, const float timescale) { PaintPoint *pPoint = &((PaintPoint *)surface->data->type_data)[index]; /* Add paint */ if (!(paintFlags & MOD_DPAINT_ERASE)) { float mix[4]; - float temp_alpha = (*paintAlpha) * ((paintFlags & MOD_DPAINT_ABS_ALPHA) ? 1.0f : (*timescale)); + float temp_alpha = paintAlpha * ((paintFlags & MOD_DPAINT_ABS_ALPHA) ? 1.0f : timescale); /* mix brush color with wet layer color */ blendColors(pPoint->e_color, pPoint->e_color[3], paintColor, temp_alpha, mix); @@ -2866,11 +3123,11 @@ static void dynamicPaint_mixPaintColors( /* mix wetness and alpha depending on selected alpha mode */ if (paintFlags & MOD_DPAINT_ABS_ALPHA) { /* update values to the brush level unless theyre higher already */ - CLAMP_MIN(pPoint->e_color[3], *paintAlpha); - CLAMP_MIN(pPoint->wetness, *paintWetness); + CLAMP_MIN(pPoint->e_color[3], paintAlpha); + CLAMP_MIN(pPoint->wetness, paintWetness); } else { - float wetness = (*paintWetness); + float wetness = paintWetness; CLAMP(wetness, 0.0f, 1.0f); pPoint->e_color[3] = mix[3]; pPoint->wetness = pPoint->wetness * (1.0f - wetness) + wetness; @@ -2884,7 +3141,7 @@ static void dynamicPaint_mixPaintColors( else { float a_ratio, a_highest; float wetness; - float invFact = 1.0f - (*paintAlpha); + float invFact = 1.0f - paintAlpha; /* * Make highest alpha to match erased value @@ -2900,47 +3157,57 @@ static void dynamicPaint_mixPaintColors( } } else { - pPoint->e_color[3] -= (*paintAlpha) * (*timescale); + pPoint->e_color[3] -= paintAlpha * timescale; CLAMP_MIN(pPoint->e_color[3], 0.0f); - pPoint->color[3] -= (*paintAlpha) * (*timescale); + pPoint->color[3] -= paintAlpha * timescale; CLAMP_MIN(pPoint->color[3], 0.0f); } - wetness = (1.0f - (*paintWetness)) * pPoint->e_color[3]; + wetness = (1.0f - paintWetness) * pPoint->e_color[3]; CLAMP_MAX(pPoint->wetness, wetness); } } /* applies given brush intersection value for wave surface */ -static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, DynamicPaintBrushSettings *brush, float isect_height) +static void dynamicPaint_mixWaveHeight( + PaintWavePoint *wPoint, const DynamicPaintBrushSettings *brush, float isect_height) { - float isect_change = isect_height - wPoint->brush_isect; - int hit = 0; + const float isect_change = isect_height - wPoint->brush_isect; + const float wave_factor = brush->wave_factor; + bool hit = false; + /* intersection marked regardless of brush type or hit */ wPoint->brush_isect = isect_height; wPoint->state = DPAINT_WAVE_ISECT_CHANGED; - isect_height *= brush->wave_factor; + isect_height *= wave_factor; /* determine hit depending on wave_factor */ - if (brush->wave_factor > 0.0f && wPoint->height > isect_height) - hit = 1; - else if (brush->wave_factor < 0.0f && wPoint->height < isect_height) - hit = 1; + if (wave_factor > 0.0f && wPoint->height > isect_height) + hit = true; + else if (wave_factor < 0.0f && wPoint->height < isect_height) + hit = true; if (hit) { - if (brush->wave_type == MOD_DPAINT_WAVEB_DEPTH) { - wPoint->height = isect_height; - wPoint->state = DPAINT_WAVE_OBSTACLE; - wPoint->velocity = 0.0f; - } - else if (brush->wave_type == MOD_DPAINT_WAVEB_FORCE) - wPoint->velocity = isect_height; - else if (brush->wave_type == MOD_DPAINT_WAVEB_REFLECT) - wPoint->state = DPAINT_WAVE_REFLECT_ONLY; - else if (brush->wave_type == MOD_DPAINT_WAVEB_CHANGE) { - if (isect_change < 0.0f) - wPoint->height += isect_change * brush->wave_factor; + switch (brush->wave_type) { + case MOD_DPAINT_WAVEB_DEPTH: + wPoint->height = isect_height; + wPoint->state = DPAINT_WAVE_OBSTACLE; + wPoint->velocity = 0.0f; + break; + case MOD_DPAINT_WAVEB_FORCE: + wPoint->velocity = isect_height; + break; + case MOD_DPAINT_WAVEB_REFLECT: + wPoint->state = DPAINT_WAVE_REFLECT_ONLY; + break; + case MOD_DPAINT_WAVEB_CHANGE: + if (isect_change < 0.0f) + wPoint->height += isect_change * wave_factor; + break; + default: + BLI_assert(0); + break; } } } @@ -2948,8 +3215,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( + const DynamicPaintSurface *surface, const int index, const DynamicPaintBrushSettings *brush, + float paint[3], float influence, float depth, float vel_factor, const float timescale) { PaintSurfaceData *sData = surface->data; float strength; @@ -2969,9 +3237,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 +3248,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); - + dynamicPaint_mixPaintColors(surface, index, brush->flags, paint, paintAlpha, paintWetness, timescale); } /* displace surface */ else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { @@ -3002,10 +3266,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 +3278,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 +3290,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,19 +3300,57 @@ 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) +typedef struct DynamicPaintBrushVelocityData { + Vec3f *brush_vel; + + const MVert *mvert_p; + const MVert *mvert_c; + + float (*obmat)[4]; + float (*prev_obmat)[4]; + + const float timescale; +} DynamicPaintBrushVelocityData; + +static void dynamic_paint_brush_velocity_compute_cb(void *userdata, const int i) +{ + const DynamicPaintBrushVelocityData *data = userdata; + + Vec3f *brush_vel = data->brush_vel; + + const MVert *mvert_p = data->mvert_p; + const MVert *mvert_c = data->mvert_c; + + float (*obmat)[4] = data->obmat; + float (*prev_obmat)[4] = data->prev_obmat; + + const float timescale = data->timescale; + + float p1[3], p2[3]; + + copy_v3_v3(p1, mvert_p[i].co); + mul_m4_v3(prev_obmat, p1); + + copy_v3_v3(p2, mvert_c[i].co); + mul_m4_v3(obmat, p2); + + sub_v3_v3v3(brush_vel[i].v, p2, p1); + mul_v3_fl(brush_vel[i].v, 1.0f / timescale); +} + +static void dynamicPaint_brushMeshCalculateVelocity( + Scene *scene, Object *ob, DynamicPaintBrushSettings *brush, Vec3f **brushVel, float timescale) { - int i; float prev_obmat[4][4]; DerivedMesh *dm_p, *dm_c; MVert *mvert_p, *mvert_c; @@ -3069,7 +3370,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,33 +3381,27 @@ 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 */ + /* if mesh is constructive -> num of verts has changed, only use current frame derived mesh */ if (numOfVerts_p != numOfVerts_c) mvert_p = mvert_c; /* calculate speed */ -#pragma omp parallel for schedule(static) - for (i = 0; i < numOfVerts_c; i++) { - float p1[3], p2[3]; - - copy_v3_v3(p1, mvert_p[i].co); - mul_m4_v3(prev_obmat, p1); - - copy_v3_v3(p2, mvert_c[i].co); - mul_m4_v3(ob->obmat, p2); - - sub_v3_v3v3((*brushVel)[i].v, p2, p1); - mul_v3_fl((*brushVel)[i].v, 1.0f / timescale); - } + DynamicPaintBrushVelocityData data = { + .brush_vel = *brushVel, + .mvert_p = mvert_p, .mvert_c = mvert_c, .obmat = ob->obmat, .prev_obmat = prev_obmat, + .timescale = timescale, + }; + BLI_task_parallel_range(0, numOfVerts_c, &data, dynamic_paint_brush_velocity_compute_cb, numOfVerts_c > 10000); dm_p->release(dm_p); } @@ -3129,13 +3425,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); @@ -3145,9 +3443,366 @@ static void dynamicPaint_brushObjectCalculateVelocity(Scene *scene, Object *ob, mul_v3_fl(brushVel->v, 1.0f / timescale); } +typedef struct DynamicPaintPaintData { + const DynamicPaintSurface *surface; + const DynamicPaintBrushSettings *brush; + Object *brushOb; + const BrushMaterials *bMats; + const Scene *scene; + const float timescale; + const int c_index; + + DerivedMesh *dm; + const MVert *mvert; + const MLoop *mloop; + const MLoopTri *mlooptri; + const float brush_radius; + const float *avg_brushNor; + const Vec3f *brushVelocity; + + const ParticleSystem *psys; + const float solidradius; + + void *treeData; + + float *pointCoord; +} DynamicPaintPaintData; + /* * Paint a brush object mesh to the surface */ +static void dynamic_paint_paint_mesh_cell_point_cb_ex( + void *userdata, void *UNUSED(userdata_chunk), const int id, const int UNUSED(threadid)) +{ + const DynamicPaintPaintData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; + VolumeGrid *grid = bData->grid; + + const DynamicPaintBrushSettings *brush = data->brush; + Object *brushOb = data->brushOb; + const BrushMaterials *bMats = data->bMats; + + const Scene *scene = data->scene; + const float timescale = data->timescale; + const int c_index = data->c_index; + + DerivedMesh *dm = data->dm; + const MVert *mvert = data->mvert; + const MLoop *mloop = data->mloop; + const MLoopTri *mlooptri = data->mlooptri; + const float brush_radius = data->brush_radius; + const float *avg_brushNor = data->avg_brushNor; + const Vec3f *brushVelocity = data->brushVelocity; + + BVHTreeFromMesh *treeData = data->treeData; + + const int index = grid->t_index[grid->s_pos[c_index] + id]; + const int samples = bData->s_num[index]; + int ss; + float total_sample = (float)samples; + float brushStrength = 0.0f; /* brush influence factor */ + float depth = 0.0f; /* brush intersection depth */ + float velocity_val = 0.0f; + + float paintColor[3] = {0.0f}; + int numOfHits = 0; + + /* for image sequence anti-aliasing, use gaussian factors */ + if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + total_sample = gaussianTotal; + + /* Supersampling */ + for (ss = 0; ss < samples; ss++) { + float ray_start[3], ray_dir[3]; + float sample_factor = 0.0f; + float sampleStrength = 0.0f; + BVHTreeRayHit hit; + BVHTreeNearest nearest; + short hit_found = 0; + + /* volume sample */ + float volume_factor = 0.0f; + /* proximity sample */ + float proximity_factor = 0.0f; + float prox_colorband[4] = {0.0f}; + const bool inner_proximity = (brush->flags & MOD_DPAINT_INVERSE_PROX && + brush->collision == MOD_DPAINT_COL_VOLDIST); + + /* hit data */ + float hitCoord[3]; + int hitTri = -1; + + /* Supersampling factor */ + if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + sample_factor = gaussianFactors[ss]; + else + sample_factor = 1.0f; + + /* Get current sample position in world coordinates */ + copy_v3_v3(ray_start, bData->realCoord[bData->s_pos[index] + ss].v); + copy_v3_v3(ray_dir, bData->bNormal[index].invNorm); + + /* a simple hack to minimize chance of ray leaks at identical ray <-> edge locations */ + add_v3_fl(ray_start, 0.001f); + + hit.index = -1; + hit.dist = BVH_RAYCAST_DIST_MAX; + nearest.index = -1; + nearest.dist_sq = brush_radius * brush_radius; /* find_nearest uses squared distance */ + + /* Check volume collision */ + if (ELEM(brush->collision, MOD_DPAINT_COL_VOLUME, MOD_DPAINT_COL_VOLDIST)) { + 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 */ + const int vtri[3] = { + mloop[mlooptri[hit.index].tri[0]].v, + mloop[mlooptri[hit.index].tri[1]].v, + mloop[mlooptri[hit.index].tri[2]].v, + }; + float dot; + + normal_tri_v3(hit.no, mvert[vtri[0]].co, mvert[vtri[1]].co, mvert[vtri[2]].co); + dot = dot_v3v3(ray_dir, hit.no); + + /* If ray and hit face normal are facing same direction + * hit point is inside a closed mesh. */ + if (dot >= 0.0f) { + const float dist = hit.dist; + const int f_index = hit.index; + + /* Also cast a ray in opposite direction to make sure + * point is at least surrounded by two brush faces */ + negate_v3(ray_dir); + 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); + + if (hit.index != -1) { + /* Add factor on supersample filter */ + volume_factor = 1.0f; + hit_found = HIT_VOLUME; + + /* Mark hit info */ + madd_v3_v3v3fl(hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ + depth += dist * sample_factor; + hitTri = f_index; + } + } + } + } + + /* Check proximity collision */ + if (ELEM(brush->collision, MOD_DPAINT_COL_DIST, MOD_DPAINT_COL_VOLDIST) && + (!hit_found || (brush->flags & MOD_DPAINT_INVERSE_PROX))) + { + float proxDist = -1.0f; + float hitCo[3] = {0.0f, 0.0f, 0.0f}; + int tri = 0; + + /* if inverse prox and no hit found, skip this sample */ + if (inner_proximity && !hit_found) + continue; + + /* If pure distance proximity, find the nearest point on the mesh */ + if (!(brush->flags & MOD_DPAINT_PROX_PROJECT)) { + 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; + } + } + else { /* else cast a ray in defined projection direction */ + float proj_ray[3] = {0.0f}; + + if (brush->ray_dir == MOD_DPAINT_RAY_CANVAS) { + copy_v3_v3(proj_ray, bData->bNormal[index].invNorm); + negate_v3(proj_ray); + } + else if (brush->ray_dir == MOD_DPAINT_RAY_BRUSH_AVG) { + copy_v3_v3(proj_ray, avg_brushNor); + } + else { /* MOD_DPAINT_RAY_ZPLUS */ + proj_ray[2] = 1.0f; + } + hit.index = -1; + hit.dist = brush_radius; + + /* Do a face normal directional raycast, and use that distance */ + 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; + } + } + + /* If a hit was found, calculate required values */ + if (proxDist >= 0.0f && proxDist <= brush_radius) { + proximity_factor = proxDist / brush_radius; + CLAMP(proximity_factor, 0.0f, 1.0f); + if (!inner_proximity) + proximity_factor = 1.0f - proximity_factor; + + hit_found = HIT_PROXIMITY; + + /* if no volume hit, use prox point face info */ + if (hitTri == -1) { + copy_v3_v3(hitCoord, hitCo); + hitTri = tri; + } + } + } + + /* 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) + { + volume_factor = 1.0f - volume_factor; + if (inner_proximity) + proximity_factor = 1.0f - proximity_factor; + } + + /* apply final sample depending on final hit type */ + if (hit_found == HIT_VOLUME) { + sampleStrength = volume_factor; + } + 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)) + { + proximity_factor = prox_colorband[3]; + } + 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; + } + + sampleStrength *= sample_factor; + } + else { + continue; + } + + /* velocity brush, only do on main sample */ + if (brush->flags & MOD_DPAINT_USES_VELOCITY && ss == 0 && brushVelocity) { + float weights[4]; + float brushPointVelocity[3]; + float velocity[3]; + + const int v1 = mloop[mlooptri[hitTri].tri[0]].v; + const int v2 = mloop[mlooptri[hitTri].tri[1]].v; + const int v3 = mloop[mlooptri[hitTri].tri[2]].v; + + /* calculate barycentric weights for hit point */ + interp_weights_face_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, NULL, hitCoord); + + /* simple check based on brush surface velocity, + * todo: perhaps implement something that handles volume movement as well */ + + /* interpolate vertex speed vectors to get hit point velocity */ + interp_v3_v3v3v3(brushPointVelocity, + brushVelocity[v1].v, + brushVelocity[v2].v, + brushVelocity[v3].v, weights); + + /* substract canvas point velocity */ + if (bData->velocity) { + sub_v3_v3v3(velocity, brushPointVelocity, bData->velocity[index].v); + } + else { + copy_v3_v3(velocity, brushPointVelocity); + } + velocity_val = normalize_v3(velocity); + + /* if brush has smudge enabled store 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); + bData->brush_velocity[index * 4 + 3] = velocity_val; + } + } + + /* + * Process hit color and alpha + */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + float sampleColor[3]; + float alpha_factor = 1.0f; + + sampleColor[0] = brush->r; + sampleColor[1] = brush->g; + 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, dm); + } + + /* Sample proximity colorband if required */ + 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]; + sampleColor[2] = prox_colorband[2]; + } + } + + /* Add AA sample */ + paintColor[0] += sampleColor[0]; + paintColor[1] += sampleColor[1]; + paintColor[2] += sampleColor[2]; + sampleStrength *= alpha_factor; + numOfHits++; + } + + /* apply sample strength */ + brushStrength += sampleStrength; + } // end supersampling + + + /* if any sample was inside paint range */ + if (brushStrength > 0.0f || depth > 0.0f) { + /* apply supersampling results */ + if (samples > 1) { + brushStrength /= total_sample; + } + CLAMP(brushStrength, 0.0f, 1.0f); + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + /* Get final pixel color and alpha */ + paintColor[0] /= numOfHits; + paintColor[1] /= numOfHits; + paintColor[2] /= numOfHits; + } + /* get final object space depth */ + else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + depth /= bData->bNormal[index].normal_scale * total_sample; + } + + dynamicPaint_updatePointData(surface, index, brush, paintColor, brushStrength, depth, velocity_val, timescale); + } +} + static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, DynamicPaintBrushSettings *brush, Object *brushOb, @@ -3166,11 +3821,13 @@ 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}; - float brush_radius = brush->paint_distance * surface->radius_scale; + const float brush_radius = brush->paint_distance * surface->radius_scale; int numOfVerts; int ii; Bounds3D mesh_bb = {0}; @@ -3217,324 +3874,211 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, /* loop through space partitioning grid */ for (c_index = 0; c_index < total_cells; c_index++) { - 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) - for (id = 0; id < grid->s_num[c_index]; id++) { - int index = grid->t_index[grid->s_pos[c_index] + id]; - int ss, samples = bData->s_num[index]; - float total_sample = (float)samples; - float brushStrength = 0.0f; /* brush influence factor */ - float depth = 0.0f; /* brush intersection depth */ - float velocity_val = 0.0f; - - float paintColor[3] = {0.0f}; - int numOfHits = 0; - - /* for image sequence anti-aliasing, use gaussian factors */ - if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) - total_sample = gaussianTotal; - - /* Supersampling */ - for (ss = 0; ss < samples; ss++) { - - float ray_start[3], ray_dir[3]; - float sample_factor = 0.0f; - float sampleStrength = 0.0f; - BVHTreeRayHit hit; - BVHTreeNearest nearest; - short hit_found = 0; - - /* volume sample */ - float volume_factor = 0.0f; - /* proximity sample */ - float proximity_factor = 0.0f; - float prox_colorband[4] = {0.0f}; - int inner_proximity = (brush->flags & MOD_DPAINT_INVERSE_PROX && - brush->collision == MOD_DPAINT_COL_VOLDIST); - - /* hit data */ - float hitCoord[3]; - int hitTri = -1; - - /* Supersampling factor */ - if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) - sample_factor = gaussianFactors[ss]; - else - sample_factor = 1.0f; - - /* Get current sample position in world coordinates */ - copy_v3_v3(ray_start, bData->realCoord[bData->s_pos[index] + ss].v); - copy_v3_v3(ray_dir, bData->bNormal[index].invNorm); - - /* a simple hack to minimize chance of ray leaks at identical ray <-> edge locations */ - add_v3_fl(ray_start, 0.001f); - - hit.index = -1; - hit.dist = BVH_RAYCAST_DIST_MAX; - nearest.index = -1; - nearest.dist_sq = brush_radius * brush_radius; /* find_nearest uses squared distance */ - - /* 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) { - /* 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 */ - const int vtri[3] = { - mloop[mlooptri[hit.index].tri[0]].v, - mloop[mlooptri[hit.index].tri[1]].v, - mloop[mlooptri[hit.index].tri[2]].v, - }; - float dot; - - normal_tri_v3(hit.no, mvert[vtri[0]].co, mvert[vtri[1]].co, mvert[vtri[2]].co); - dot = dot_v3v3(ray_dir, hit.no); - - /* If ray and hit face normal are facing same direction - * hit point is inside a closed mesh. */ - if (dot >= 0) { - float dist = hit.dist; - int f_index = hit.index; - - /* Also cast a ray in opposite direction to make sure - * point is at least surrounded by two brush faces */ - negate_v3(ray_dir); - 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); - - if (hit.index != -1) { - /* Add factor on supersample filter */ - volume_factor = 1.0f; - hit_found = HIT_VOLUME; - - /* Mark hit info */ - madd_v3_v3v3fl(hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ - depth += dist * sample_factor; - hitTri = f_index; - } - } - } + DynamicPaintPaintData data = { + .surface = surface, + .brush = brush, .brushOb = brushOb, .bMats = bMats, + .scene = scene, .timescale = timescale, .c_index = c_index, + .dm = dm, .mvert = mvert, .mloop = mloop, .mlooptri = mlooptri, + .brush_radius = brush_radius, .avg_brushNor = avg_brushNor, .brushVelocity = brushVelocity, + .treeData = &treeData + }; + BLI_task_parallel_range_ex(0, grid->s_num[c_index], &data, NULL, 0, + dynamic_paint_paint_mesh_cell_point_cb_ex, + grid->s_num[c_index] > 250, true); + } + } + } + /* free bvh tree */ + free_bvhtree_from_mesh(&treeData); + dm->release(dm); - /* Check proximity collision */ - if ((brush->collision == MOD_DPAINT_COL_DIST || brush->collision == MOD_DPAINT_COL_VOLDIST) && - (!hit_found || (brush->flags & MOD_DPAINT_INVERSE_PROX))) - { - float proxDist = -1.0f; - float hitCo[3] = {0.0f, 0.0f, 0.0f}; - int tri = 0; - - /* if inverse prox and no hit found, skip this sample */ - if (inner_proximity && !hit_found) continue; - - /* 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) { - proxDist = sqrtf(nearest.dist_sq); - copy_v3_v3(hitCo, nearest.co); - tri = nearest.index; - } - } - else { /* else cast a ray in defined projection direction */ - float proj_ray[3] = {0.0f}; + } - if (brush->ray_dir == MOD_DPAINT_RAY_CANVAS) { - copy_v3_v3(proj_ray, bData->bNormal[index].invNorm); - negate_v3(proj_ray); - } - else if (brush->ray_dir == MOD_DPAINT_RAY_BRUSH_AVG) { - copy_v3_v3(proj_ray, avg_brushNor); - } - else { /* MOD_DPAINT_RAY_ZPLUS */ - proj_ray[2] = 1.0f; - } - hit.index = -1; - 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) { - proxDist = hit.dist; - madd_v3_v3v3fl(hitCo, ray_start, proj_ray, hit.dist); /* Calculate final hit coordinates */ - tri = hit.index; - } - } + /* free brush velocity data */ + if (brushVelocity) + MEM_freeN(brushVelocity); - /* If a hit was found, calculate required values */ - if (proxDist >= 0.0f && proxDist <= brush_radius) { - proximity_factor = proxDist / brush_radius; - CLAMP(proximity_factor, 0.0f, 1.0f); - if (!inner_proximity) - proximity_factor = 1.0f - proximity_factor; + return 1; +} - hit_found = HIT_PROXIMITY; +/* + * Paint a particle system to the surface + */ +static void dynamic_paint_paint_particle_cell_point_cb_ex( + void *userdata, void *UNUSED(userdata_chunk), const int id, const int UNUSED(threadid)) +{ + const DynamicPaintPaintData *data = userdata; - /* if no volume hit, use prox point face info */ - if (hitTri == -1) { - copy_v3_v3(hitCoord, hitCo); - hitTri = tri; - } - } - } + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; + VolumeGrid *grid = bData->grid; - /* 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) { - volume_factor = 1.0f - volume_factor; - if (inner_proximity) - proximity_factor = 1.0f - proximity_factor; - } + const DynamicPaintBrushSettings *brush = data->brush; - /* apply final sample depending on final hit type */ - if (hit_found == HIT_VOLUME) { - sampleStrength = volume_factor; - } - 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)) - proximity_factor = prox_colorband[3]; - 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; - } + const ParticleSystem *psys = data->psys; - sampleStrength *= sample_factor; - } - else { - continue; - } + const float timescale = data->timescale; + const int c_index = data->c_index; - /* velocity brush, only do on main sample */ - if (brush->flags & MOD_DPAINT_USES_VELOCITY && ss == 0 && brushVelocity) { - int v1, v2, v3; - float weights[4]; - float brushPointVelocity[3]; - float velocity[3]; - - v1 = mloop[mlooptri[hitTri].tri[0]].v; - v2 = mloop[mlooptri[hitTri].tri[1]].v; - v3 = mloop[mlooptri[hitTri].tri[2]].v; - - /* calculate barycentric weights for hit point */ - interp_weights_face_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, NULL, hitCoord); - - /* simple check based on brush surface velocity, - * todo: perhaps implement something that handles volume movement as well */ - - /* interpolate vertex speed vectors to get hit point velocity */ - interp_v3_v3v3v3(brushPointVelocity, - brushVelocity[v1].v, - brushVelocity[v2].v, - brushVelocity[v3].v, weights); - - /* substract canvas point velocity */ - if (bData->velocity) { - sub_v3_v3v3(velocity, brushPointVelocity, bData->velocity[index].v); - } - else { - copy_v3_v3(velocity, brushPointVelocity); - } - velocity_val = len_v3(velocity); - - /* if brush has smudge enabled store 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); - bData->brush_velocity[index * 4 + 3] = velocity_val; - } - } + KDTree *tree = data->treeData; - /* - * Process hit color and alpha - */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - float sampleColor[3]; - float alpha_factor = 1.0f; - - sampleColor[0] = brush->r; - sampleColor[1] = brush->g; - 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); - - /* Sample proximity colorband if required */ - 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]; - sampleColor[2] = prox_colorband[2]; - } - } + const float solidradius = data->solidradius; + const float smooth = brush->particle_smooth * surface->radius_scale; + const float range = solidradius + smooth; + const float particle_timestep = 0.04f * psys->part->timetweak; - /* Add AA sample */ - paintColor[0] += sampleColor[0]; - paintColor[1] += sampleColor[1]; - paintColor[2] += sampleColor[2]; - sampleStrength *= alpha_factor; - numOfHits++; - } + const int index = grid->t_index[grid->s_pos[c_index] + id]; + float disp_intersect = 0.0f; + float radius = 0.0f; + float strength = 0.0f; + int part_index = -1; - /* apply sample strength */ - brushStrength += sampleStrength; - } // end supersampling + /* + * With predefined radius, there is no variation between particles. + * It's enough to just find the nearest one. + */ + { + KDTreeNearest nearest; + float smooth_range, part_solidradius; + /* 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) + return; - /* if any sample was inside paint range */ - if (brushStrength > 0.0f || depth > 0.0f) { + if (brush->flags & MOD_DPAINT_PART_RAD) { + /* use particles individual size */ + ParticleData *pa = psys->particles + nearest.index; + part_solidradius = pa->size; + } + else { + part_solidradius = solidradius; + } + radius = part_solidradius + smooth; + if (nearest.dist < radius) { + /* distances inside solid radius has maximum influence -> dist = 0 */ + smooth_range = max_ff(0.0f, (nearest.dist - part_solidradius)); + /* do smoothness if enabled */ + if (smooth) + smooth_range /= smooth; + + strength = 1.0f - smooth_range; + disp_intersect = radius - nearest.dist; + part_index = nearest.index; + } + } + /* If using random per particle radius and closest particle didn't give max influence */ + if (brush->flags & MOD_DPAINT_PART_RAD && strength < 1.0f && psys->part->randsize > 0.0f) { + /* + * If we use per particle radius, we have to sample all particles + * within max radius range + */ + KDTreeNearest *nearest; - /* apply supersampling results */ - if (samples > 1) { - brushStrength /= total_sample; - } - CLAMP(brushStrength, 0.0f, 1.0f); + float smooth_range = smooth * (1.0f - strength), dist; + /* calculate max range that can have particles with higher influence than the nearest one */ + const float max_range = smooth - strength * smooth + solidradius; + /* Make gcc happy! */ + dist = max_range; - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - /* Get final pixel color and alpha */ - paintColor[0] /= numOfHits; - paintColor[1] /= numOfHits; - paintColor[2] /= numOfHits; - } - /* get final object space depth */ - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) - { - depth /= bData->bNormal[index].normal_scale * total_sample; - } + const int particles = BLI_kdtree_range_search( + tree, bData->realCoord[bData->s_pos[index]].v, &nearest, max_range); - dynamicPaint_updatePointData(surface, index, brush, paintColor, brushStrength, depth, velocity_val, timescale); - } - } - } + /* Find particle that produces highest influence */ + for (int n = 0; n < particles; n++) { + ParticleData *pa = &psys->particles[nearest[n].index]; + + /* skip if out of range */ + if (nearest[n].dist > (pa->size + smooth)) + continue; + + /* update hit data */ + const float s_range = nearest[n].dist - pa->size; + /* skip if higher influence is already found */ + if (smooth_range < s_range) + continue; + + /* update hit data */ + smooth_range = s_range; + dist = nearest[n].dist; + part_index = nearest[n].index; + + /* If inside solid range and no disp depth required, no need to seek further */ + if ((s_range < 0.0f) && !ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + break; } } - /* free bvh tree */ - free_bvhtree_from_mesh(&treeData); - dm->release(dm); + if (nearest) + MEM_freeN(nearest); + + /* now calculate influence for this particle */ + const float rad = radius + smooth; + if ((rad - dist) > disp_intersect) { + disp_intersect = radius - dist; + radius = rad; + } + + /* do smoothness if enabled */ + CLAMP_MIN(smooth_range, 0.0f); + if (smooth) + smooth_range /= smooth; + + const float str = 1.0f - smooth_range; + /* if influence is greater, use this one */ + if (str > strength) + strength = str; } - /* free brush velocity data */ - if (brushVelocity) - MEM_freeN(brushVelocity); + if (strength > 0.001f) { + float paintColor[4] = {0.0f}; + float depth = 0.0f; + float velocity_val = 0.0f; - return 1; + /* apply velocity */ + if ((brush->flags & MOD_DPAINT_USES_VELOCITY) && (part_index != -1)) { + float velocity[3]; + ParticleData *pa = psys->particles + part_index; + mul_v3_v3fl(velocity, pa->state.vel, particle_timestep); + + /* substract canvas point velocity */ + if (bData->velocity) { + sub_v3_v3(velocity, bData->velocity[index].v); + } + velocity_val = normalize_v3(velocity); + + /* store brush velocity for smudge */ + 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); + bData->brush_velocity[index * 4 + 3] = velocity_val; + } + } + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + copy_v3_v3(paintColor, &brush->r); + } + else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + /* get displace depth */ + disp_intersect = (1.0f - sqrtf(disp_intersect / radius)) * radius; + depth = max_ff(0.0f, (radius - disp_intersect) / bData->bNormal[index].normal_scale); + } + + dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); + } } -/* - * Paint a particle system to the surface - */ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, ParticleSystem *psys, DynamicPaintBrushSettings *brush, @@ -3543,22 +4087,23 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, ParticleSettings *part = psys->part; PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; - VolumeGrid *grid = bData->grid; + VolumeGrid *grid = bData->grid; KDTree *tree; int particlesAdded = 0; int invalidParticles = 0; int p = 0; - 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; + const float solidradius = surface->radius_scale * + ((brush->flags & MOD_DPAINT_PART_RAD) ? part->size : brush->particle_radius); + const float smooth = brush->particle_smooth * surface->radius_scale; - float range = solidradius + smooth; - float particle_timestep = 0.04f * part->timetweak; + const float range = solidradius + smooth; 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 +4113,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); @@ -3604,14 +4155,12 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, if (boundsIntersectDist(&grid->grid_bounds, &part_bb, range)) { int c_index; int total_cells = grid->dim[0] * grid->dim[1] * grid->dim[2]; - + /* balance tree */ BLI_kdtree_balance(tree); /* loop through space partitioning grid */ for (c_index = 0; c_index < total_cells; c_index++) { - int id; - /* check cell bounding box */ if (!grid->s_num[c_index] || !boundsIntersectDist(&grid->bounds[c_index], &part_bb, range)) @@ -3620,156 +4169,15 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, } /* loop through cell points */ -#pragma omp parallel for schedule(static) - for (id = 0; id < grid->s_num[c_index]; id++) { - int index = grid->t_index[grid->s_pos[c_index] + id]; - float disp_intersect = 0.0f; - float radius = 0.0f; - float strength = 0.0f; - float velocity_val = 0.0f; - int part_index = -1; - - /* - * With predefined radius, there is no variation between particles. - * It's enough to just find the nearest one. - */ - { - KDTreeNearest nearest; - float smooth_range, part_solidradius; - - /* 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 (brush->flags & MOD_DPAINT_PART_RAD) { - /* use particles individual size */ - ParticleData *pa = psys->particles + nearest.index; - part_solidradius = pa->size; - } - else { - part_solidradius = solidradius; - } - radius = part_solidradius + smooth; - if (nearest.dist < radius) { - /* distances inside solid radius has maximum influence -> dist = 0 */ - smooth_range = (nearest.dist - part_solidradius); - if (smooth_range < 0.0f) smooth_range = 0.0f; - /* do smoothness if enabled */ - if (smooth) smooth_range /= smooth; - - strength = 1.0f - smooth_range; - disp_intersect = radius - nearest.dist; - part_index = nearest.index; - } - } - /* If using random per particle radius and closest particle didn't give max influence */ - if (brush->flags & MOD_DPAINT_PART_RAD && strength < 1.0f && psys->part->randsize > 0.0f) { - /* - * If we use per particle radius, we have to sample all particles - * within max radius range - */ - KDTreeNearest *nearest; - - int n, particles; - float smooth_range = smooth * (1.0f - strength), dist; - /* calculate max range that can have particles with higher influence than the nearest one */ - float max_range = smooth - strength * smooth + solidradius; - /* Make gcc happy! */ - dist = max_range; - - particles = BLI_kdtree_range_search(tree, bData->realCoord[bData->s_pos[index]].v, - &nearest, max_range); - - /* Find particle that produces highest influence */ - for (n = 0; n < particles; n++) { - ParticleData *pa = psys->particles + nearest[n].index; - float s_range; - - /* skip if out of range */ - if (nearest[n].dist > (pa->size + smooth)) - continue; - - /* update hit data */ - s_range = nearest[n].dist - pa->size; - /* skip if higher influence is already found */ - if (smooth_range < s_range) - continue; - - /* update hit data */ - smooth_range = s_range; - dist = nearest[n].dist; - part_index = nearest[n].index; - - /* If inside solid range and no disp depth required, no need to seek further */ - if ( (s_range < 0.0f) && - (surface->type != MOD_DPAINT_SURFACE_T_DISPLACE) && - (surface->type != MOD_DPAINT_SURFACE_T_WAVE)) - { - break; - } - } - - if (nearest) MEM_freeN(nearest); - - /* now calculate influence for this particle */ - { - float rad = radius + smooth, str; - if ((rad - dist) > disp_intersect) { - disp_intersect = radius - dist; - radius = rad; - } - - /* do smoothness if enabled */ - 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 (strength > 0.001f) { - float paintColor[4] = {0.0f}; - float depth = 0.0f; - - /* apply velocity */ - if ((brush->flags & MOD_DPAINT_USES_VELOCITY) && (part_index != -1)) { - float velocity[3]; - ParticleData *pa = psys->particles + part_index; - mul_v3_v3fl(velocity, pa->state.vel, particle_timestep); - - /* substract canvas point velocity */ - if (bData->velocity) { - sub_v3_v3(velocity, bData->velocity[index].v); - } - 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)) - { - copy_v3_v3(&bData->brush_velocity[index * 4], velocity); - mul_v3_fl(&bData->brush_velocity[index * 4], 1.0f / velocity_val); - bData->brush_velocity[index * 4 + 3] = velocity_val; - } - } - - 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)) - { - /* get displace depth */ - disp_intersect = (1.0f - sqrtf(disp_intersect / radius)) * radius; - depth = (radius - disp_intersect) / bData->bNormal[index].normal_scale; - if (depth < 0.0f) depth = 0.0f; - } - - dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); - } - } + DynamicPaintPaintData data = { + .surface = surface, + .brush = brush, .psys = psys, + .solidradius = solidradius, .timescale = timescale, .c_index = c_index, + .treeData = tree, + }; + BLI_task_parallel_range_ex(0, grid->s_num[c_index], &data, NULL, 0, + dynamic_paint_paint_particle_cell_point_cb_ex, + grid->s_num[c_index] > 250, true); } } BLI_end_threaded_malloc(); @@ -3779,110 +4187,142 @@ 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 void dynamic_paint_paint_single_point_cb_ex( + void *userdata, void *UNUSED(userdata_chunk), const int index, const int UNUSED(threadid)) { - int index; - float brush_radius = brush->paint_distance * surface->radius_scale; - PaintSurfaceData *sData = surface->data; - PaintBakeData *bData = sData->bData; - Vec3f brushVel; + const DynamicPaintPaintData *data = userdata; - if (brush->flags & MOD_DPAINT_USES_VELOCITY) - dynamicPaint_brushObjectCalculateVelocity(scene, brushOb, &brushVel, timescale); + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; - /* - * Loop through every surface point - */ -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - float distance = len_v3v3(pointCoord, bData->realCoord[bData->s_pos[index]].v); - float colorband[4] = {0.0f}; - float strength; + const DynamicPaintBrushSettings *brush = data->brush; + Object *brushOb = data->brushOb; + const BrushMaterials *bMats = data->bMats; - if (distance > brush_radius) continue; + const Scene *scene = data->scene; + const float timescale = data->timescale; - /* Smooth range or color ramp */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH || - brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP) - { - strength = 1.0f - distance / brush_radius; - CLAMP(strength, 0.0f, 1.0f); - } - else { - strength = 1.0f; - } + const MVert *mvert = data->mvert; + const float brush_radius = data->brush_radius; + const Vec3f *brushVelocity = data->brushVelocity; - if (strength >= 0.001f) { - float paintColor[3] = {0.0f}; - float depth = 0.0f; - float velocity_val = 0.0f; + float *pointCoord = data->pointCoord; - /* material */ - if (brush_usesMaterial(brush, scene)) { - float alpha_factor = 1.0f; - float hit_coord[3]; - MVert *mvert = brush->dm->getVertArray(brush->dm); - /* use dummy coord of first vertex */ - 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); - } + const float distance = len_v3v3(pointCoord, bData->realCoord[bData->s_pos[index]].v); + float colorband[4] = {0.0f}; + float strength; + + if (distance > brush_radius) + return; + + /* Smooth range or color ramp */ + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH || + brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP) + { + strength = 1.0f - distance / brush_radius; + CLAMP(strength, 0.0f, 1.0f); + } + else { + strength = 1.0f; + } - /* color ramp */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && do_colorband(brush->paint_ramp, (1.0f - strength), colorband)) - strength = colorband[3]; + if (strength >= 0.001f) { + float paintColor[3] = {0.0f}; + float depth = 0.0f; + float velocity_val = 0.0f; - if (brush->flags & MOD_DPAINT_USES_VELOCITY) { - float velocity[3]; + /* material */ + if (brush_usesMaterial(brush, scene)) { + float alpha_factor = 1.0f; + float hit_coord[3]; + /* use dummy coord of first vertex */ + mul_v3_m4v3(hit_coord, brushOb->obmat, mvert[0].co); - /* substract canvas point velocity */ - if (bData->velocity) { - sub_v3_v3v3(velocity, brushVel.v, bData->velocity[index].v); - } - else { - copy_v3_v3(velocity, brushVel.v); - } - velocity_val = len_v3(velocity); + dynamicPaint_doMaterialTex(bMats, paintColor, &alpha_factor, brushOb, + bData->realCoord[bData->s_pos[index]].v, hit_coord, 0, brush->dm); + } - /* store brush velocity for smudge */ - 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); - bData->brush_velocity[index * 4 + 3] = velocity_val; - } + /* color ramp */ + 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]; + + /* substract canvas point velocity */ + if (bData->velocity) { + sub_v3_v3v3(velocity, brushVelocity->v, bData->velocity[index].v); } + else { + copy_v3_v3(velocity, brushVelocity->v); + } + velocity_val = len_v3(velocity); - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && - !(brush->flags & MOD_DPAINT_RAMP_ALPHA)) - { - paintColor[0] = colorband[0]; - paintColor[1] = colorband[1]; - paintColor[2] = colorband[2]; - } - else { - if (!brush_usesMaterial(brush, scene)) { - paintColor[0] = brush->r; - paintColor[1] = brush->g; - paintColor[2] = brush->b; - } - } + /* store brush velocity for smudge */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && + brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity) + { + mul_v3_v3fl(&bData->brush_velocity[index * 4], velocity, 1.0f / velocity_val); + bData->brush_velocity[index * 4 + 3] = velocity_val; } - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) + } + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && + !(brush->flags & MOD_DPAINT_RAMP_ALPHA)) { - /* 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; + paintColor[0] = colorband[0]; + paintColor[1] = colorband[1]; + paintColor[2] = colorband[2]; + } + else { + if (!brush_usesMaterial(brush, scene)) { + paintColor[0] = brush->r; + paintColor[1] = brush->g; + paintColor[2] = brush->b; + } } - dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); } + else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + /* get displace depth */ + const float disp_intersect = (1.0f - sqrtf((brush_radius - distance) / brush_radius)) * brush_radius; + depth = max_ff(0.0f, (brush_radius - disp_intersect) / bData->bNormal[index].normal_scale); + } + dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); } +} + +static int dynamicPaint_paintSinglePoint( + DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush, + Object *brushOb, BrushMaterials *bMats, Scene *scene, float timescale) +{ + PaintSurfaceData *sData = surface->data; + float brush_radius = brush->paint_distance * surface->radius_scale; + Vec3f brushVel; + + if (brush->flags & MOD_DPAINT_USES_VELOCITY) + dynamicPaint_brushObjectCalculateVelocity(scene, brushOb, &brushVel, timescale); + + const MVert *mvert = brush->dm->getVertArray(brush->dm); + + /* + * Loop through every surface point + */ + DynamicPaintPaintData data = { + .surface = surface, + .brush = brush, .brushOb = brushOb, .bMats = bMats, + .scene = scene, .timescale = timescale, + .mvert = mvert, + .brush_radius = brush_radius, .brushVelocity = &brushVel, + .pointCoord = pointCoord, + }; + BLI_task_parallel_range_ex(0, sData->total_points, &data, NULL, 0, + dynamic_paint_paint_single_point_cb_ex, + sData->total_points > 1000, true); return 1; } @@ -3893,57 +4333,68 @@ 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 dynamic_paint_prepare_adjacency_cb(void *userdata, const int index) +{ + PaintSurfaceData *sData = userdata; + PaintBakeData *bData = sData->bData; + BakeAdjPoint *bNeighs = bData->bNeighs; + PaintAdjData *adj_data = sData->adj_data; + Vec3f *realCoord = bData->realCoord; + + const int num_neighs = adj_data->n_num[index]; + + for (int i = 0; i < num_neighs; i++) { + const int n_index = adj_data->n_index[index] + i; + const int t_index = adj_data->n_target[n_index]; + + /* dir vec */ + sub_v3_v3v3(bNeighs[n_index].dir, realCoord[bData->s_pos[t_index]].v, realCoord[bData->s_pos[index]].v); + /* dist */ + bNeighs[n_index].dist = normalize_v3(bNeighs[n_index].dir); + } +} + +static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, const bool force_init) { PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; BakeAdjPoint *bNeighs; PaintAdjData *adj_data = sData->adj_data; - 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); - bNeighs = bData->bNeighs = MEM_mallocN(sData->adj_data->total_targets * sizeof(struct BakeAdjPoint), "PaintEffectBake"); - if (!bNeighs) return; + if (bData->bNeighs) + MEM_freeN(bData->bNeighs); + bNeighs = bData->bNeighs = MEM_mallocN(sData->adj_data->total_targets * sizeof(*bNeighs), "PaintEffectBake"); + if (!bNeighs) + return; -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int i; - int numOfNeighs = adj_data->n_num[index]; + BLI_task_parallel_range( + 0, sData->total_points, sData, dynamic_paint_prepare_adjacency_cb, sData->total_points > 1000); - for (i = 0; i < numOfNeighs; i++) { - int n_index = adj_data->n_index[index] + i; - int t_index = adj_data->n_target[n_index]; - - /* dir vec */ - sub_v3_v3v3(bNeighs[n_index].dir, realCoord[bData->s_pos[t_index]].v, realCoord[bData->s_pos[index]].v); - /* 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); - } - } - - /* calculate average values (single thread) */ - bData->average_dist = 0.0f; + /* calculate average values (single thread). + * Note: tried to put this in threaded callback (using _finalize feature), but gave ~30% slower result! */ + bData->average_dist = 0.0; for (index = 0; index < sData->total_points; index++) { - int i; int numOfNeighs = adj_data->n_num[index]; - for (i = 0; i < numOfNeighs; i++) { + for (int i = 0; i < numOfNeighs; i++) { bData->average_dist += (double)bNeighs[adj_data->n_index[index] + i].dist; } } - bData->average_dist /= adj_data->total_targets; + bData->average_dist /= adj_data->total_targets; } /* 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( + const PaintSurfaceData *sData, const int index, const float force[3], float closest_d[2], int closest_id[2]) { BakeAdjPoint *bNeighs = sData->bData->bNeighs; - int numOfNeighs = sData->adj_data->n_num[index]; + const int numOfNeighs = sData->adj_data->n_num[index]; int i; closest_id[0] = closest_id[1] = -1; @@ -3951,35 +4402,42 @@ static void surface_determineForceTargetPoints(PaintSurfaceData *sData, int inde /* find closest neigh */ for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); + const int n_index = sData->adj_data->n_index[index] + i; + const 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++) { - int n_index = sData->adj_data->n_index[index] + i; - float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); - float closest_dot = dot_v3v3(bNeighs[n_index].dir, bNeighs[closest_id[0]].dir); + const int n_index = sData->adj_data->n_index[index] + i; - if (n_index == closest_id[0]) continue; + if (n_index == closest_id[0]) + continue; + + const float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); + const float closest_dot = dot_v3v3(bNeighs[n_index].dir, bNeighs[closest_id[0]].dir); /* 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 */ if (dir_dot > closest_d[1] && closest_dot < closest_d[0] && dir_dot > 0.0f) { - closest_d[1] = dir_dot; closest_id[1] = n_index; + closest_d[1] = dir_dot; + closest_id[1] = n_index; } } - /* if two valid neighs found, calculate how force effect is divided - * evenly between them (so that d[0]+d[1] = 1.0)*/ + /* if two valid neighs found, calculate how force effect is divided evenly between them + * (so that d[0] + d[1] = 1.0) */ if (closest_id[1] != -1) { float force_proj[3]; float tangent[3]; - float neigh_diff = acosf(dot_v3v3(bNeighs[closest_id[0]].dir, bNeighs[closest_id[1]].dir)); + const float neigh_diff = acosf(dot_v3v3(bNeighs[closest_id[0]].dir, bNeighs[closest_id[1]].dir)); float force_intersect; float temp; @@ -4016,12 +4474,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 +4488,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,8 +4497,9 @@ 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 +4512,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); @@ -4072,11 +4532,83 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus } } +typedef struct DynamicPaintEffectData { + const DynamicPaintSurface *surface; + Scene *scene; + + float *force; + ListBase *effectors; + const void *prevPoint; + const float eff_scale; + + uint8_t *point_locks; + + const float wave_speed; + const float wave_scale; + const float wave_max_slope; + + const float dt; + const float min_dist; + const float damp_factor; + const bool reset_wave; +} DynamicPaintEffectData; + /* * 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 void dynamic_paint_prepare_effect_cb(void *userdata, const int index) +{ + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; + Vec3f *realCoord = bData->realCoord; + + Scene *scene = data->scene; + + float *force = data->force; + ListBase *effectors = data->effectors; + + float forc[3] = {0}; + float vel[3] = {0}; + + /* apply force fields */ + if (effectors) { + EffectedPoint epoint; + pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint); + epoint.vel_to_sec = 1.0f; + pdDoEffectors(effectors, NULL, surface->effector_weights, &epoint, forc, NULL); + } + + /* if global gravity is enabled, add it too */ + if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) + /* also divide by 10 to about match default grav + * with default force strength (1.0) */ + madd_v3_v3fl(forc, scene->physics_settings.gravity, + surface->effector_weights->global_gravity * surface->effector_weights->weight[0] / 10.f); + + /* add surface point velocity and acceleration if enabled */ + if (bData->velocity) { + if (surface->drip_vel) + madd_v3_v3fl(forc, bData->velocity[index].v, surface->drip_vel * (-1.0f)); + + /* acceleration */ + if (bData->prev_velocity && surface->drip_acc) { + float acc[3]; + copy_v3_v3(acc, bData->velocity[index].v); + sub_v3_v3(acc, bData->prev_velocity[index].v); + madd_v3_v3fl(forc, acc, surface->drip_acc * (-1.0f)); + } + } + + /* force strength, and normalize force vec */ + force[index * 4 + 3] = normalize_v3_v3(&force[index * 4], forc); +} + +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; @@ -4084,60 +4616,24 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s int steps; PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; - Vec3f *realCoord = bData->realCoord; - int index; /* Init force data if required */ if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { - float vel[3] = {0}; ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true); /* allocate memory for force data (dir vector + strength) */ *force = MEM_mallocN(sData->total_points * 4 * sizeof(float), "PaintEffectForces"); if (*force) { -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - float forc[3] = {0}; - - /* apply force fields */ - if (effectors) { - EffectedPoint epoint; - pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint); - epoint.vel_to_sec = 1.0f; - pdDoEffectors(effectors, NULL, surface->effector_weights, &epoint, forc, NULL); - } - - /* if global gravity is enabled, add it too */ - if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) - /* also divide by 10 to about match default grav - * with default force strength (1.0) */ - madd_v3_v3fl(forc, scene->physics_settings.gravity, - surface->effector_weights->global_gravity * surface->effector_weights->weight[0] / 10.f); - - /* add surface point velocity and acceleration if enabled */ - if (bData->velocity) { - if (surface->drip_vel) - madd_v3_v3fl(forc, bData->velocity[index].v, surface->drip_vel * (-1.0f)); - - /* acceleration */ - if (bData->prev_velocity && surface->drip_acc) { - float acc[3]; - copy_v3_v3(acc, bData->velocity[index].v); - sub_v3_v3(acc, bData->prev_velocity[index].v); - madd_v3_v3fl(forc, acc, surface->drip_acc * (-1.0f)); - } - } - - /* 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]); - copy_v3_v3(&((*force)[index * 4]), forc); - } + DynamicPaintEffectData data = { + .surface = surface, .scene = scene, + .force = *force, .effectors = effectors, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_prepare_effect_cb, sData->total_points > 1000); /* calculate average values (single thread) */ - for (index = 0; index < sData->total_points; index++) { + for (int index = 0; index < sData->total_points; index++) { average_force += (*force)[index * 4 + 3]; } average_force /= sData->total_points; @@ -4157,7 +4653,7 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s fastest_effect = max_fff(spread_speed, shrink_speed, average_force); avg_dist = bData->average_dist * CANVAS_REL_SIZE / getSurfaceDimension(sData); - steps = (int)ceil(1.5f * EFF_MOVEMENT_PER_FRAME * fastest_effect / avg_dist * timescale); + steps = (int)ceilf(1.5f * EFF_MOVEMENT_PER_FRAME * fastest_effect / avg_dist * timescale); CLAMP(steps, 1, 20); return steps; @@ -4166,166 +4662,337 @@ 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 dynamic_paint_effect_spread_cb(void *userdata, const int index) { - PaintSurfaceData *sData = surface->data; + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + + const int numOfNeighs = sData->adj_data->n_num[index]; BakeAdjPoint *bNeighs = sData->bData->bNeighs; - float distance_scale = getSurfaceDimension(sData) / CANVAS_REL_SIZE; - int index; + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + const PaintPoint *prevPoint = data->prevPoint; + const float eff_scale = data->eff_scale; + + const int *n_index = sData->adj_data->n_index; + const int *n_target = sData->adj_data->n_target; + + /* Loop through neighboring points */ + for (int i = 0; i < numOfNeighs; i++) { + const int n_idx = n_index[index] + i; + float w_factor; + const PaintPoint *pPoint_prev = &prevPoint[n_target[n_idx]]; + const float speed_scale = (bNeighs[n_idx].dist < eff_scale) ? 1.0f : eff_scale / bNeighs[n_idx].dist; + const float color_mix = min_fff(pPoint_prev->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], pPoint_prev->e_color, pPoint_prev->e_color[3], color_mix); + + /* Only continue if surrounding point has higher wetness */ + if (pPoint_prev->wetness < pPoint->wetness || pPoint_prev->wetness < MIN_WETNESS) + continue; + + w_factor = 1.0f / numOfNeighs * min_ff(pPoint_prev->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 * pPoint_prev->wetness; + pPoint->e_color[3] = mixColors(pPoint->e_color, pPoint->e_color[3], + pPoint_prev->e_color, pPoint_prev->e_color[3], w_factor); + } +} + +static void dynamic_paint_effect_shrink_cb(void *userdata, const int index) +{ + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + + const int numOfNeighs = sData->adj_data->n_num[index]; + BakeAdjPoint *bNeighs = sData->bData->bNeighs; + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + const PaintPoint *prevPoint = data->prevPoint; + const float eff_scale = data->eff_scale; + float totalAlpha = 0.0f; + + const int *n_index = sData->adj_data->n_index; + const int *n_target = sData->adj_data->n_target; + + /* Loop through neighboring points */ + for (int i = 0; i < numOfNeighs; i++) { + const int n_idx = n_index[index] + i; + const float speed_scale = (bNeighs[n_idx].dist < eff_scale) ? 1.0f : eff_scale / bNeighs[n_idx].dist; + const PaintPoint *pPoint_prev = &prevPoint[n_target[n_idx]]; + float a_factor, ea_factor, w_factor; + + totalAlpha += pPoint_prev->e_color[3]; + + /* 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; + + /* decrease factor for dry paint alpha */ + a_factor = max_ff((1.0f - pPoint_prev->color[3]) / numOfNeighs * (pPoint->color[3] - pPoint_prev->color[3]) * speed_scale, 0.0f); + /* decrease factor for wet paint alpha */ + ea_factor = max_ff((1.0f - pPoint_prev->e_color[3]) / 8 * (pPoint->e_color[3] - pPoint_prev->e_color[3]) * speed_scale, 0.0f); + /* decrease factor for paint wetness */ + w_factor = max_ff((1.0f - pPoint_prev->wetness) / 8 * (pPoint->wetness - pPoint_prev->wetness) * speed_scale, 0.0f); + + pPoint->color[3] -= a_factor; + CLAMP_MIN(pPoint->color[3], 0.0f); + pPoint->e_color[3] -= ea_factor; + CLAMP_MIN(pPoint->e_color[3], 0.0f); + pPoint->wetness -= w_factor; + CLAMP_MIN(pPoint->wetness, 0.0f); + } +} + +static void dynamic_paint_effect_drip_cb(void *userdata, const int index) +{ + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + BakeAdjPoint *bNeighs = sData->bData->bNeighs; + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + const PaintPoint *prevPoint = data->prevPoint; + const PaintPoint *pPoint_prev = &prevPoint[index]; + const float *force = data->force; + const float eff_scale = data->eff_scale; + + const int *n_target = sData->adj_data->n_target; + + uint8_t *point_locks = data->point_locks; + + int closest_id[2]; + float closest_d[2]; + + /* adjust drip speed depending on wetness */ + float w_factor = pPoint_prev->wetness - 0.025f; + if (w_factor <= 0) + return; + CLAMP(w_factor, 0.0f, 1.0f); + + /* get force affect points */ + surface_determineForceTargetPoints(sData, index, &force[index * 4], closest_d, closest_id); + + /* Apply movement towards those two points */ + for (int i = 0; i < 2; i++) { + const int n_idx = closest_id[i]; + if (n_idx != -1 && closest_d[i] > 0.0f) { + const float dir_dot = closest_d[i]; + + /* just skip if angle is too extreme */ + if (dir_dot <= 0.0f) + continue; + + float dir_factor, a_factor; + const float speed_scale = eff_scale * force[index * 4 + 3] / bNeighs[n_idx].dist; + + const unsigned int n_trgt = (unsigned int)n_target[n_idx]; + + /* Sort of spinlock, but only for given ePoint. + * Since the odds a same ePoint is modified at the same time by several threads is very low, this is + * much more eficient than a global spin lock. */ + const unsigned int pointlock_idx = n_trgt / 8; + const uint8_t pointlock_bitmask = 1 << (n_trgt & 7); /* 7 == 0b111 */ + while (atomic_fetch_and_or_uint8(&point_locks[pointlock_idx], pointlock_bitmask) & pointlock_bitmask); + + PaintPoint *ePoint = &((PaintPoint *)sData->type_data)[n_trgt]; + const float e_wet = ePoint->wetness; + + dir_factor = min_ff(0.5f, dir_dot * min_ff(speed_scale, 1.0f) * w_factor); + + /* mix new wetness */ + ePoint->wetness += dir_factor; + CLAMP(ePoint->wetness, 0.0f, MAX_WETNESS); + + /* 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); + /* 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]; + CLAMP_MAX(ePoint->e_color[3], pPoint_prev->e_color[3]); + } + + /* decrease paint wetness on current point */ + pPoint->wetness -= (ePoint->wetness - e_wet); + CLAMP(pPoint->wetness, 0.0f, MAX_WETNESS); + +#ifndef NDEBUG + uint8_t ret = atomic_fetch_and_and_uint8(&point_locks[pointlock_idx], ~pointlock_bitmask); + BLI_assert(ret & pointlock_bitmask); +#else + atomic_fetch_and_and_uint8(&point_locks[pointlock_idx], ~pointlock_bitmask); +#endif + } + } +} + +static void dynamicPaint_doEffectStep( + DynamicPaintSurface *surface, float *force, PaintPoint *prevPoint, float timescale, float steps) +{ + PaintSurfaceData *sData = surface->data; + + const float distance_scale = getSurfaceDimension(sData) / CANVAS_REL_SIZE; timescale /= steps; - if (!sData->adj_data) return; + if (!sData->adj_data) + return; /* * Spread Effect */ if (surface->effect & MOD_DPAINT_EFFECT_DO_SPREAD) { - float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->spread_speed * timescale; + const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->spread_speed * timescale; /* Copy current surface to the previous points array to read unmodified values */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(struct PaintPoint)); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int i; - int numOfNeighs = sData->adj_data->n_num[index]; - PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; - - /* Only reads values from the surface copy (prevPoint[]), - * so this one is thread safe */ - - /* Loop through neighboring points */ - for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float w_factor; - PaintPoint *ePoint = &prevPoint[sData->adj_data->n_target[n_index]]; - float speed_scale = (bNeighs[n_index].dist < eff_scale) ? 1.0f : eff_scale / bNeighs[n_index].dist; - 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); - - /* Only continue if surrounding point has higher wetness */ - 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); - } - } + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, .eff_scale = eff_scale, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_effect_spread_cb, sData->total_points > 1000); } /* * Shrink Effect */ if (surface->effect & MOD_DPAINT_EFFECT_DO_SHRINK) { - float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->shrink_speed * timescale; + const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->shrink_speed * timescale; /* Copy current surface to the previous points array to read unmodified values */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(struct PaintPoint)); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int i; - int numOfNeighs = sData->adj_data->n_num[index]; - float totalAlpha = 0.0f; - PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; - - for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float speed_scale = (bNeighs[n_index].dist < eff_scale) ? 1.0f : eff_scale / bNeighs[n_index].dist; - PaintPoint *ePoint = &prevPoint[sData->adj_data->n_target[n_index]]; - float a_factor, ea_factor, w_factor; - - totalAlpha += ePoint->e_color[3]; - - /* 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; - - /* decrease factor for dry paint alpha */ - a_factor = (1.0f - ePoint->color[3]) / numOfNeighs * (pPoint->color[3] - ePoint->color[3]) * speed_scale; - CLAMP_MIN(a_factor, 0.0f); - /* decrease factor for wet paint alpha */ - ea_factor = (1.0f - ePoint->e_color[3]) / 8 * (pPoint->e_color[3] - ePoint->e_color[3]) * speed_scale; - CLAMP_MIN(ea_factor, 0.0f); - /* decrease factor for paint wetness */ - w_factor = (1.0f - ePoint->wetness) / 8 * (pPoint->wetness - ePoint->wetness) * speed_scale; - CLAMP_MIN(w_factor, 0.0f); - - pPoint->color[3] -= a_factor; - CLAMP_MIN(pPoint->color[3], 0.0f); - pPoint->e_color[3] -= ea_factor; - CLAMP_MIN(pPoint->e_color[3], 0.0f); - pPoint->wetness -= w_factor; - CLAMP_MIN(pPoint->wetness, 0.0f); - } - } + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, .eff_scale = eff_scale, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_effect_shrink_cb, sData->total_points > 1000); } /* * Drip Effect */ if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP && force) { - float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * timescale / 2.0f; + const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * timescale / 2.0f; + + /* Same as BLI_bitmask, but handled atomicaly as 'ePoint' locks. */ + const size_t point_locks_size = (sData->total_points / 8) + 1; + uint8_t *point_locks = MEM_callocN(sizeof(*point_locks) * point_locks_size, __func__); + /* Copy current surface to the previous points array to read unmodified values */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(struct PaintPoint)); - for (index = 0; index < sData->total_points; index++) { - int i; - PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; - PaintPoint *pPoint_prev = &prevPoint[index]; + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, + .eff_scale = eff_scale, .force = force, + .point_locks = point_locks, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_effect_drip_cb, sData->total_points > 1000); - int closest_id[2]; - float closest_d[2]; + MEM_freeN(point_locks); + } +} - /* adjust drip speed depending on wetness */ - float w_factor = pPoint_prev->wetness - 0.025f; - if (w_factor <= 0) continue; - CLAMP(w_factor, 0.0f, 1.0f); +static void dynamic_paint_wave_step_cb(void *userdata, const int index) +{ + const DynamicPaintEffectData *data = userdata; - /* get force affect points */ - surface_determineForceTargetPoints(sData, index, &force[index * 4], closest_d, closest_id); + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + BakeAdjPoint *bNeighs = sData->bData->bNeighs; + const PaintWavePoint *prevPoint = data->prevPoint; - /* Apply movement towards those two points */ - for (i = 0; i < 2; i++) { - int n_index = closest_id[i]; - if (n_index != -1 && closest_d[i] > 0.0f) { - float dir_dot = closest_d[i], dir_factor, a_factor; - float speed_scale = eff_scale * force[index * 4 + 3] / bNeighs[n_index].dist; - PaintPoint *ePoint = &((PaintPoint *)sData->type_data)[sData->adj_data->n_target[n_index]]; - float e_wet = ePoint->wetness; + const float wave_speed = data->wave_speed; + const float wave_scale = data->wave_scale; + const float wave_max_slope = data->wave_max_slope; - /* just skip if angle is too extreme */ - if (dir_dot <= 0.0f) continue; - - dir_factor = dir_dot * MIN2(speed_scale, 1.0f) * w_factor; - CLAMP_MAX(dir_factor, 0.5f); - - /* mix new wetness */ - ePoint->wetness += dir_factor; - CLAMP(ePoint->wetness, 0.0f, MAX_WETNESS); - - /* 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); - /* 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]; - CLAMP_MAX(ePoint->e_color[3], pPoint_prev->e_color[3]); - } + const float dt = data->dt; + const float min_dist = data->min_dist; + const float damp_factor = data->damp_factor; - /* decrease paint wetness on current point */ - pPoint->wetness -= (ePoint->wetness - e_wet); - CLAMP(pPoint->wetness, 0.0f, MAX_WETNESS); - } - } + PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; + const int numOfNeighs = sData->adj_data->n_num[index]; + float force = 0.0f, avg_dist = 0.0f, avg_height = 0.0f, avg_n_height = 0.0f; + int numOfN = 0, numOfRN = 0; + + if (wPoint->state > 0) + return; + + const int *n_index = sData->adj_data->n_index; + const int *n_target = sData->adj_data->n_target; + const int *adj_flags = sData->adj_data->flags; + + /* calculate force from surrounding points */ + for (int i = 0; i < numOfNeighs; i++) { + const int n_idx = n_index[index] + i; + float dist = bNeighs[n_idx].dist * wave_scale; + const PaintWavePoint *tPoint = &prevPoint[n_target[n_idx]]; + + if (!dist || tPoint->state > 0) + continue; + + CLAMP_MIN(dist, min_dist); + avg_dist += dist; + numOfN++; + + /* count average height for edge points for open borders */ + if (!(adj_flags[n_target[n_idx]] & ADJ_ON_MESH_EDGE)) { + avg_n_height += tPoint->height; + numOfRN++; + } + + force += (tPoint->height - wPoint->height) / (dist * dist); + avg_height += tPoint->height; + } + avg_dist = (numOfN) ? avg_dist / numOfN : 0.0f; + + if (surface->flags & MOD_DPAINT_WAVE_OPEN_BORDERS && adj_flags[index] & ADJ_ON_MESH_EDGE) { + /* 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); + } + /* else do wave eq */ + else { + /* add force towards zero height based on average dist */ + if (avg_dist) + force += (0.0f - wPoint->height) * surface->wave_spring / (avg_dist * avg_dist) / 2.0f; + + /* change point velocity */ + wPoint->velocity += force * dt * wave_speed * wave_speed; + /* damping */ + wPoint->velocity *= damp_factor; + /* and new height */ + wPoint->height += wPoint->velocity * dt; + + /* limit wave slope steepness */ + if (wave_max_slope && avg_dist) { + const float max_offset = wave_max_slope * avg_dist; + const float offset = (numOfN) ? (avg_height / numOfN - wPoint->height) : 0.0f; + if (offset > max_offset) + wPoint->height += offset - max_offset; + else if (offset < -max_offset) + wPoint->height += offset + max_offset; } } + + if (data->reset_wave) { + /* if there wasnt any brush intersection, clear isect height */ + if (wPoint->state == DPAINT_WAVE_NONE) { + wPoint->brush_isect = 0.0f; + } + wPoint->state = DPAINT_WAVE_NONE; + } } static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescale) @@ -4335,15 +5002,16 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal int index; int steps, ss; float dt, min_dist, damp_factor; - float wave_speed = surface->wave_speed; - float wave_max_slope = (surface->wave_smoothness >= 0.01f) ? (0.5f / surface->wave_smoothness) : 0.0f; + const float wave_speed = surface->wave_speed; + const float wave_max_slope = (surface->wave_smoothness >= 0.01f) ? (0.5f / surface->wave_smoothness) : 0.0f; double average_dist = 0.0f; const float canvas_size = getSurfaceDimension(sData); - float wave_scale = CANVAS_REL_SIZE / canvas_size; + const float wave_scale = CANVAS_REL_SIZE / canvas_size; /* 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,170 +5035,120 @@ 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)); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; - int numOfNeighs = sData->adj_data->n_num[index]; - float force = 0.0f, avg_dist = 0.0f, avg_height = 0.0f, avg_n_height = 0.0f; - int numOfN = 0, numOfRN = 0; - int i; + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, + .wave_speed = wave_speed, .wave_scale = wave_scale, .wave_max_slope = wave_max_slope, + .dt = dt, .min_dist = min_dist, .damp_factor = damp_factor, .reset_wave = (ss == steps - 1), + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_wave_step_cb, sData->total_points > 1000); + } - if (wPoint->state > 0) continue; + MEM_freeN(prevPoint); +} - /* calculate force from surrounding points */ - for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float dist = bNeighs[n_index].dist * wave_scale; - PaintWavePoint *tPoint = &prevPoint[sData->adj_data->n_target[n_index]]; +/* Do dissolve and fading effects */ +static bool dynamic_paint_surface_needs_dry_dissolve(DynamicPaintSurface *surface) +{ + return (((surface->type == MOD_DPAINT_SURFACE_T_PAINT) && + (surface->flags & (MOD_DPAINT_USE_DRYING | MOD_DPAINT_DISSOLVE))) || + (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WEIGHT) && + (surface->flags & MOD_DPAINT_DISSOLVE))); +} - if (!dist || tPoint->state > 0) continue; - if (dist < min_dist) dist = min_dist; - avg_dist += dist; - numOfN++; +typedef struct DynamicPaintDissolveDryData { + const DynamicPaintSurface *surface; + const float timescale; +} DynamicPaintDissolveDryData; - /* count average height for edge points for open borders */ - if (!(sData->adj_data->flags[sData->adj_data->n_target[n_index]] & ADJ_ON_MESH_EDGE)) { - avg_n_height += tPoint->height; - numOfRN++; - } +static void dynamic_paint_surface_pre_step_cb(void *userdata, const int index) +{ + const DynamicPaintDissolveDryData *data = userdata; - force += (tPoint->height - wPoint->height) / (dist * dist); - avg_height += tPoint->height; - } - avg_dist = (numOfN) ? avg_dist / numOfN : 0.0f; + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const float timescale = data->timescale; - if (surface->flags & MOD_DPAINT_WAVE_OPEN_BORDERS && - sData->adj_data->flags[index] & ADJ_ON_MESH_EDGE) - { - /* 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); - } - /* else do wave eq */ - else { - /* add force towards zero height based on average dist */ - if (avg_dist) - force += (0.0f - wPoint->height) * surface->wave_spring / (avg_dist * avg_dist) / 2.0f; - - /* change point velocity */ - wPoint->velocity += force * dt * wave_speed * wave_speed; - /* damping */ - wPoint->velocity *= damp_factor; - /* and new height */ - wPoint->height += wPoint->velocity * dt; - - /* limit wave slope steepness */ - 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; - } - } - } - } - - /* reset state */ -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; - /* if there wasnt any brush intersection, clear isect height */ - if (wPoint->state == DPAINT_WAVE_NONE) { - wPoint->brush_isect = 0.0f; - } - wPoint->state = DPAINT_WAVE_NONE; - } + /* Do drying dissolve effects */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + /* drying */ + if (surface->flags & MOD_DPAINT_USE_DRYING) { + if (pPoint->wetness >= MIN_WETNESS) { + 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) != 0); + CLAMP_MIN(pPoint->wetness, 0.0f); - MEM_freeN(prevPoint); -} + if (pPoint->wetness < surface->color_dry_threshold) { + dry_ratio = pPoint->wetness / p_wetness; -/* Do dissolve and fading effects */ -static void dynamicPaint_surfacePreStep(DynamicPaintSurface *surface, float timescale) -{ - PaintSurfaceData *sData = surface->data; - int index; + /* + * Slowly "shift" paint from wet layer to dry layer as it drys: + */ + /* make sure alpha values are within proper range */ + CLAMP(pPoint->color[3], 0.0f, 1.0f); + CLAMP(pPoint->e_color[3], 0.0f, 1.0f); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - /* Do drying dissolve effects */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; - /* drying */ - if (surface->flags & MOD_DPAINT_USE_DRYING) { - if (pPoint->wetness >= MIN_WETNESS) { - 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; - - if (pPoint->wetness < surface->color_dry_threshold) { - dry_ratio = pPoint->wetness / p_wetness; - - /* - * Slowly "shift" paint from wet layer to dry layer as it drys: - */ - /* make sure alpha values are within proper range */ - CLAMP(pPoint->color[3], 0.0f, 1.0f); - CLAMP(pPoint->e_color[3], 0.0f, 1.0f); - - /* get current final blended color of these layers */ - blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color); - /* reduce wet layer alpha by dry factor */ - pPoint->e_color[3] *= dry_ratio; - - /* now calculate new alpha for dry layer that keeps final blended color unchanged */ - pPoint->color[3] = (f_color[3] - pPoint->e_color[3]) / (1.0f - pPoint->e_color[3]); - /* for each rgb component, calculate a new dry layer color that keeps the final blend color - * 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])); - } + /* get current final blended color of these layers */ + blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color); + /* reduce wet layer alpha by dry factor */ + pPoint->e_color[3] *= dry_ratio; + + /* now calculate new alpha for dry layer that keeps final blended color unchanged */ + pPoint->color[3] = (f_color[3] - pPoint->e_color[3]) / (1.0f - pPoint->e_color[3]); + /* for each rgb component, calculate a new dry layer color that keeps the final blend color + * 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->state = DPAINT_PAINT_WET; } - /* in case of just dryed paint, just mix it to the dry layer and mark it empty */ - else if (pPoint->state > 0) { - float f_color[4]; - blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color); - copy_v4_v4(pPoint->color, f_color); - /* clear wet layer */ - pPoint->wetness = 0.0f; - pPoint->e_color[3] = 0.0f; - pPoint->state = DPAINT_PAINT_DRY; - } - } - if (surface->flags & MOD_DPAINT_DISSOLVE) { - value_dissolve(&pPoint->color[3], surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); - CLAMP_MIN(pPoint->color[3], 0.0f); - - value_dissolve(&pPoint->e_color[3], surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); - CLAMP_MIN(pPoint->e_color[3], 0.0f); + pPoint->state = DPAINT_PAINT_WET; + } + /* in case of just dryed paint, just mix it to the dry layer and mark it empty */ + else if (pPoint->state > 0) { + float f_color[4]; + blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color); + copy_v4_v4(pPoint->color, f_color); + /* clear wet layer */ + pPoint->wetness = 0.0f; + pPoint->e_color[3] = 0.0f; + pPoint->state = DPAINT_PAINT_DRY; } } - /* 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)) - { - float *point = &((float *)sData->type_data)[index]; - /* log or linear */ - value_dissolve(point, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); - CLAMP_MIN(*point, 0.0f); + if (surface->flags & MOD_DPAINT_DISSOLVE) { + 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) != 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)) + { + float *point = &((float *)sData->type_data)[index]; + /* log or linear */ + 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,67 +5157,159 @@ static int dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob int numOfVerts = dm->getNumVerts(dm); int i; - int ret = 0; - 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)) { + return true; + } } - return ret; + return false; } -static int surface_needsVelocityData(DynamicPaintSurface *surface, const Scene *scene) -{ - if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) - return 1; +/* Prepare for surface step by creating PaintBakeNormal data */ +typedef struct DynamicPaintGenerateBakeData { + const DynamicPaintSurface *surface; + Object *ob; - if (surface_getBrushFlags(surface, scene) & BRUSH_USES_VELOCITY) - return 1; + const MVert *mvert; + const Vec3f *canvas_verts; - return 0; -} + const bool do_velocity_data; + const bool new_bdata; +} DynamicPaintGenerateBakeData; -static int surface_needsAccelerationData(DynamicPaintSurface *surface) +static void dynamic_paint_generate_bake_data_cb(void *userdata, const int index) { - if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) - return 1; + const DynamicPaintGenerateBakeData *data = userdata; - return 0; + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintAdjData *adj_data = sData->adj_data; + const PaintBakeData *bData = sData->bData; + + Object *ob = data->ob; + + const MVert *mvert = data->mvert; + const Vec3f *canvas_verts = data->canvas_verts; + + const bool do_velocity_data = data->do_velocity_data; + const bool new_bdata = data->new_bdata; + + float prev_point[3] = {0.0f, 0.0f, 0.0f}; + float temp_nor[3]; + + if (do_velocity_data && !new_bdata) { + copy_v3_v3(prev_point, bData->realCoord[bData->s_pos[index]].v); + } + + /* + * Calculate current 3D-position and normal of each surface point + */ + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { + float n1[3], n2[3], n3[3]; + const ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; + const PaintUVPoint *tPoint = &((PaintUVPoint *)f_data->uv_p)[index]; + + bData->s_num[index] = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + bData->s_pos[index] = index * bData->s_num[index]; + + /* per sample coordinates */ + for (int ss = 0; ss < bData->s_num[index]; ss++) { + interp_v3_v3v3v3(bData->realCoord[bData->s_pos[index] + ss].v, + canvas_verts[tPoint->v1].v, + canvas_verts[tPoint->v2].v, + canvas_verts[tPoint->v3].v, + f_data->barycentricWeights[index * bData->s_num[index] + ss].v); + } + + /* Calculate current pixel surface normal */ + 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); + normalize_v3(temp_nor); + if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + /* Prepare surface normal directional scale to easily convert + * brush intersection amount between global and local space */ + float scaled_nor[3]; + mul_v3_v3v3(scaled_nor, temp_nor, ob->size); + bData->bNormal[index].normal_scale = len_v3(scaled_nor); + } + mul_mat3_m4_v3(ob->obmat, temp_nor); + normalize_v3(temp_nor); + negate_v3_v3(bData->bNormal[index].invNorm, temp_nor); + } + else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + int ss; + if (surface->flags & MOD_DPAINT_ANTIALIAS && adj_data) { + bData->s_num[index] = adj_data->n_num[index] + 1; + bData->s_pos[index] = adj_data->n_index[index] + index; + } + else { + bData->s_num[index] = 1; + bData->s_pos[index] = index; + } + + /* calculate position for each sample */ + for (ss = 0; ss < bData->s_num[index]; ss++) { + /* first sample is always point center */ + copy_v3_v3(bData->realCoord[bData->s_pos[index] + ss].v, canvas_verts[index].v); + if (ss > 0) { + 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); + } + } + + /* normal */ + normal_short_to_float_v3(temp_nor, mvert[index].no); + if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + /* Prepare surface normal directional scale to easily convert + * brush intersection amount between global and local space */ + float scaled_nor[3]; + mul_v3_v3v3(scaled_nor, temp_nor, ob->size); + bData->bNormal[index].normal_scale = len_v3(scaled_nor); + } + mul_mat3_m4_v3(ob->obmat, temp_nor); + normalize_v3(temp_nor); + negate_v3_v3(bData->bNormal[index].invNorm, temp_nor); + } + + /* calculate speed vector */ + if (do_velocity_data && !new_bdata && !bData->clear) { + sub_v3_v3v3(bData->velocity[index].v, bData->realCoord[bData->s_pos[index]].v, prev_point); + } } -/* Prepare for surface step by creating PaintBakeNormal data */ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Scene *scene, Object *ob) { PaintSurfaceData *sData = surface->data; - PaintAdjData *adj_data = sData->adj_data; 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); + int index; + bool new_bdata = false; + 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 +5325,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,16 +5346,21 @@ 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")); } - new_bdata = 1; + new_bdata = true; } if (do_velocity_data && !bData->velocity) { @@ -4659,7 +5376,7 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce /* * Make a transformed copy of canvas derived mesh vertices to avoid recalculation. */ - bData->mesh_bounds.valid = 0; + bData->mesh_bounds.valid = false; for (index = 0; index < canvasNumOfVerts; index++) { copy_v3_v3(canvas_verts[index].v, mvert[index].co); mul_m4_v3(ob->obmat, canvas_verts[index].v); @@ -4669,112 +5386,20 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce /* * Prepare each surface point for a new step */ -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - float prev_point[3] = {0.0f, 0.0f, 0.0f}; - if (do_velocity_data && !new_bdata) { - copy_v3_v3(prev_point, bData->realCoord[bData->s_pos[index]].v); - } - /* - * Calculate current 3D-position and normal of each surface point - */ - if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { - float n1[3], n2[3], n3[3]; - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; - PaintUVPoint *tPoint = &((PaintUVPoint *)f_data->uv_p)[index]; - int ss; - - bData->s_num[index] = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; - bData->s_pos[index] = index * bData->s_num[index]; - - /* per sample coordinates */ - for (ss = 0; ss < bData->s_num[index]; ss++) { - interp_v3_v3v3v3(bData->realCoord[bData->s_pos[index] + ss].v, - canvas_verts[tPoint->v1].v, - canvas_verts[tPoint->v2].v, - canvas_verts[tPoint->v3].v, - f_data->barycentricWeights[index * bData->s_num[index] + ss].v); - } - - /* Calculate current pixel surface normal */ - 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(bData->bNormal[index].invNorm, - n1, n2, n3, f_data->barycentricWeights[index * bData->s_num[index]].v); - mul_mat3_m4_v3(ob->obmat, bData->bNormal[index].invNorm); - normalize_v3(bData->bNormal[index].invNorm); - negate_v3(bData->bNormal[index].invNorm); - } - else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - int ss; - if (surface->flags & MOD_DPAINT_ANTIALIAS && adj_data) { - bData->s_num[index] = adj_data->n_num[index] + 1; - bData->s_pos[index] = adj_data->n_index[index] + index; - } - else { - bData->s_num[index] = 1; - bData->s_pos[index] = index; - } - - /* calculate position for each sample */ - for (ss = 0; ss < bData->s_num[index]; ss++) { - /* first sample is always point center */ - copy_v3_v3(bData->realCoord[bData->s_pos[index] + ss].v, canvas_verts[index].v); - if (ss > 0) { - 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); - } - } - - /* normal */ - normal_short_to_float_v3(bData->bNormal[index].invNorm, mvert[index].no); - mul_mat3_m4_v3(ob->obmat, bData->bNormal[index].invNorm); - normalize_v3(bData->bNormal[index].invNorm); - negate_v3(bData->bNormal[index].invNorm); - } - - /* Prepare surface normal directional scale to easily convert - * brush intersection amount between global and local space */ - if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) - { - float temp_nor[3]; - if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - normal_short_to_float_v3(temp_nor, mvert[index].no); - normalize_v3(temp_nor); - } - else { - float n1[3], n2[3], n3[3]; - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; - PaintUVPoint *tPoint = &((PaintUVPoint *)f_data->uv_p)[index]; - - 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); - } - - mul_v3_v3(temp_nor, ob->size); - bData->bNormal[index].normal_scale = len_v3(temp_nor); - } - - /* calculate speed vector */ - if (do_velocity_data && !new_bdata && !bData->clear) { - sub_v3_v3v3(bData->velocity[index].v, bData->realCoord[bData->s_pos[index]].v, prev_point); - } - } + DynamicPaintGenerateBakeData data = { + .surface = surface, .ob = ob, + .mvert = mvert, .canvas_verts = canvas_verts, + .do_velocity_data = do_velocity_data, .new_bdata = new_bdata, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_generate_bake_data_cb, sData->total_points > 1000); MEM_freeN(canvas_verts); /* 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,9 +5419,16 @@ 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; - dynamicPaint_surfacePreStep(surface, timescale); + if (sData->total_points < 1) + return 0; + + if (dynamic_paint_surface_needs_dry_dissolve(surface)) { + DynamicPaintDissolveDryData data = {.surface = surface, .timescale = timescale}; + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_surface_pre_step_cb, sData->total_points > 1000); + } + /* * Loop through surface's target paint objects and do painting */ @@ -4820,24 +5452,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 +5483,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 +5501,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 +5527,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 +5568,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); } } @@ -4954,8 +5590,8 @@ int dynamicPaint_calculateFrame(DynamicPaintSurface *surface, Scene *scene, Obje dynamicPaint_applySurfaceDisplace(surface, surface->canvas->dm); /* update bake data */ - dynamicPaint_generateBakeData(surface, scene, cObject); - + dynamicPaint_generateBakeData(surface, scene, cObject); + /* don't do substeps for first frame */ if (surface->substeps && (frame != surface->start_frame)) { int st; @@ -4963,7 +5599,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..1ae7ca189fb 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; + + 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); + } +} - if (source == IMA_SRC_VIEWER) - ima->flag |= IMA_VIEW_AS_RENDER; +static Image *image_alloc(Main *bmain, const char *name, short source, short type) +{ + Image *ima; - BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); - ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); + 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; } @@ -1741,6 +1748,7 @@ typedef struct StampData { char scene[STAMP_NAME_SIZE]; char strip[STAMP_NAME_SIZE]; char rendertime[STAMP_NAME_SIZE]; + char memory[STAMP_NAME_SIZE]; } StampData; #undef STAMP_NAME_SIZE @@ -1862,6 +1870,13 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d else { stamp_data->rendertime[0] = '\0'; } + + if (stats && (scene->r.stamp & R_STAMP_MEMORY)) { + BLI_snprintf(stamp_data->memory, sizeof(stamp_data->memory), do_prefix ? "Peak Memory %.2fM" : "%.2fM", stats->mem_peak); + } + else { + stamp_data->memory[0] = '\0'; + } } } @@ -1936,6 +1951,12 @@ static void stampdata_from_template(StampData *stamp_data, else { stamp_data->rendertime[0] = '\0'; } + if (scene->r.stamp & R_STAMP_MEMORY) { + BLI_snprintf(stamp_data->memory, sizeof(stamp_data->memory), "Peak Memory %s", stamp_data_template->memory); + } + else { + stamp_data->memory[0] = '\0'; + } } void BKE_image_stamp_buf( @@ -1992,7 +2013,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 */ @@ -2049,6 +2070,21 @@ void BKE_image_stamp_buf( } /* Top left corner, below File, Date, Rendertime */ + if (TEXT_SIZE_CHECK(stamp_data.memory, w, h)) { + y -= h; + + /* and space for background. */ + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, + 0, y - BUFF_MARGIN_Y, w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); + + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.memory, BLF_DRAW_STR_DUMMY_MAX); + + /* the extra pixel for background. */ + y -= BUFF_MARGIN_Y * 2; + } + + /* Top left corner, below File, Date, Memory, Rendertime */ BLF_enable(mono, BLF_WORD_WRAP); if (TEXT_SIZE_CHECK_WORD_WRAP(stamp_data.note, w, h)) { y -= h; @@ -2212,6 +2248,7 @@ void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCall CALL(scene, "Scene"); CALL(strip, "Strip"); CALL(rendertime, "RenderTime"); + CALL(memory, "Memory"); #undef CALL } @@ -4494,13 +4531,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 +4555,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_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index c8bb2e0e758..a472b6e5bc1 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -262,6 +262,51 @@ void BKE_mesh_vert_loop_map_create(MeshElemMap **r_map, int **r_mem, } /** + * Generates a map where the key is the edge and the value is a list of looptris that use that edge. + * The lists are allocated from one memory pool. + */ +void BKE_mesh_vert_looptri_map_create( + MeshElemMap **r_map, int **r_mem, + const MVert *UNUSED(mvert), const int totvert, + const MLoopTri *mlooptri, const int totlooptri, + const MLoop *mloop, const int UNUSED(totloop)) +{ + MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, __func__); + int *indices = MEM_mallocN(sizeof(int) * (size_t)totlooptri * 3, __func__); + int *index_step; + const MLoopTri *mlt; + int i; + + /* count face users */ + for (i = 0, mlt = mlooptri; i < totlooptri; mlt++, i++) { + for (int j = 3; j--;) { + map[mloop[mlt->tri[j]].v].count++; + } + } + + /* create offsets */ + index_step = indices; + for (i = 0; i < totvert; i++) { + map[i].indices = index_step; + index_step += map[i].count; + + /* re-count, using this as an index below */ + map[i].count = 0; + } + + /* assign looptri-edge users */ + for (i = 0, mlt = mlooptri; i < totlooptri; mlt++, i++) { + for (int j = 3; j--;) { + MeshElemMap *map_ele = &map[mloop[mlt->tri[j]].v]; + map_ele->indices[map_ele->count++] = i; + } + } + + *r_map = map; + *r_mem = indices; +} + +/** * Generates a map where the key is the vertex and the value is a list of edges that use that vertex as an endpoint. * The lists are allocated from one memory pool. */ 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 ae7c72a74d3..e44318c6633 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -245,6 +245,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..d307ba1811b 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"; @@ -550,7 +545,7 @@ void BKE_scene_init(Scene *sce) sce->r.bake.im_format.compress = 15; sce->r.scemode = R_DOCOMP | R_DOSEQ | R_EXTENSION; - sce->r.stamp = R_STAMP_TIME | R_STAMP_FRAME | R_STAMP_DATE | R_STAMP_CAMERA | R_STAMP_SCENE | R_STAMP_FILENAME | R_STAMP_RENDERTIME; + sce->r.stamp = R_STAMP_TIME | R_STAMP_FRAME | R_STAMP_DATE | R_STAMP_CAMERA | R_STAMP_SCENE | R_STAMP_FILENAME | R_STAMP_RENDERTIME | R_STAMP_MEMORY; sce->r.stamp_font_id = 12; sce->r.fg_stamp[0] = sce->r.fg_stamp[1] = sce->r.fg_stamp[2] = 0.8f; sce->r.fg_stamp[3] = 1.0f; @@ -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..b9f7afb7365 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -44,6 +44,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_task.h" #include "BKE_shrinkwrap.h" #include "BKE_DerivedMesh.h" @@ -58,7 +59,7 @@ /* for timing... */ #if 0 -# include "PIL_time.h" +# include "PIL_time_utildefines.h" #else # define TIMEIT_BENCH(expr, id) (expr) #endif @@ -66,16 +67,88 @@ /* Util macros */ #define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n")) +typedef struct ShrinkwrapCalcCBData { + ShrinkwrapCalcData *calc; + + void *treeData; + void *auxData; + BVHTree *targ_tree; + BVHTree *aux_tree; + void *targ_callback; + void *aux_callback; + + float *proj_axis; + SpaceTransform *local2aux; +} ShrinkwrapCalcCBData; + /* * Shrinkwrap to the nearest vertex * * it builds a kdtree of vertexs we can attach to and then * for each vertex performs a nearest vertex search on the tree */ -static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) +static void shrinkwrap_calc_nearest_vertex_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) { - int i; + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + BVHTreeFromMesh *treeData = data->treeData; + BVHTreeNearest *nearest = userdata_chunk; + + float *co = calc->vertexCos[i]; + float tmp_co[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + /* Convert the vertex to tree coordinates */ + if (calc->vert) { + copy_v3_v3(tmp_co, calc->vert[i].co); + } + else { + copy_v3_v3(tmp_co, co); + } + BLI_space_transform_apply(&calc->local2target, tmp_co); + + /* Use local proximity heuristics (to reduce the nearest search) + * + * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex + * so we can initiate the "nearest.dist" with the expected value to that last hit. + * This will lead in pruning of the search tree. */ + if (nearest->index != -1) + nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co); + else + nearest->dist_sq = FLT_MAX; + + BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData); + + + /* Found the nearest vertex */ + if (nearest->index != -1) { + /* Adjusting the vertex weight, + * so that after interpolating it keeps a certain distance from the nearest position */ + if (nearest->dist_sq > FLT_EPSILON) { + const float dist = sqrtf(nearest->dist_sq); + weight *= (dist - calc->keepDist) / dist; + } + + /* Convert the coordinates back to mesh coordinates */ + copy_v3_v3(tmp_co, nearest->co); + BLI_space_transform_invert(&calc->local2target, tmp_co); + + interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ + } +} +static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) +{ BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; @@ -89,61 +162,11 @@ static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) /* Setup nearest */ nearest.index = -1; nearest.dist_sq = FLT_MAX; -#ifndef __APPLE__ -#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(treeData, calc) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) { - continue; - } - - - /* Convert the vertex to tree coordinates */ - if (calc->vert) { - copy_v3_v3(tmp_co, calc->vert[i].co); - } - else { - copy_v3_v3(tmp_co, co); - } - BLI_space_transform_apply(&calc->local2target, tmp_co); - - /* Use local proximity heuristics (to reduce the nearest search) - * - * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex - * so we can initiate the "nearest.dist" with the expected value to that last hit. - * This will lead in pruning of the search tree. */ - if (nearest.index != -1) - nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co); - else - nearest.dist_sq = FLT_MAX; - - BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); - - - /* Found the nearest vertex */ - if (nearest.index != -1) { - /* Adjusting the vertex weight, - * so that after interpolating it keeps a certain distance from the nearest position */ - if (nearest.dist_sq > FLT_EPSILON) { - const float dist = sqrtf(nearest.dist_sq); - weight *= (dist - calc->keepDist) / dist; - } - /* Convert the coordinates back to mesh coordinates */ - copy_v3_v3(tmp_co, nearest.co); - BLI_space_transform_invert(&calc->local2target, tmp_co); - - interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ - } - } + ShrinkwrapCalcCBData data = {.calc = calc, .treeData = &treeData}; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &nearest, sizeof(nearest), shrinkwrap_calc_nearest_vertex_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); free_bvhtree_from_mesh(&treeData); } @@ -230,13 +253,109 @@ bool BKE_shrinkwrap_project_normal( return false; } +static void shrinkwrap_calc_normal_projection_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) +{ + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + void *treeData = data->treeData; + void *auxData = data->auxData; + BVHTree *targ_tree = data->targ_tree; + BVHTree *aux_tree = data->aux_tree; + void *targ_callback = data->targ_callback; + void *aux_callback = data->aux_callback; + + float *proj_axis = data->proj_axis; + SpaceTransform *local2aux = data->local2aux; + + BVHTreeRayHit *hit = userdata_chunk; + + const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; + float *co = calc->vertexCos[i]; + float tmp_co[3], tmp_no[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + if (calc->vert) { + /* calc->vert contains verts from derivedMesh */ + /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ + /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ + if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { + copy_v3_v3(tmp_co, calc->vert[i].co); + normal_short_to_float_v3(tmp_no, calc->vert[i].no); + } + else { + copy_v3_v3(tmp_co, co); + copy_v3_v3(tmp_no, proj_axis); + } + } + else { + copy_v3_v3(tmp_co, co); + copy_v3_v3(tmp_no, proj_axis); + } + + + hit->index = -1; + hit->dist = BVH_RAYCAST_DIST_MAX; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ + + /* Project over positive direction of axis */ + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { + 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, targ_tree, hit, + targ_callback, treeData); + } + + /* Project over negative direction of axis */ + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { + float inv_no[3]; + negate_v3_v3(inv_no, tmp_no); + + 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, targ_tree, hit, + targ_callback, treeData); + } + + /* don't set the initial dist (which is more efficient), + * because its calculated in the targets space, we want the dist in our own space */ + if (proj_limit_squared != 0.0f) { + if (len_squared_v3v3(hit->co, co) > proj_limit_squared) { + hit->index = -1; + } + } + + if (hit->index != -1) { + madd_v3_v3v3fl(hit->co, hit->co, tmp_no, calc->keepDist); + interp_v3_v3v3(co, co, hit->co, weight); + } +} static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for_render) { - int i; - /* Options about projection direction */ - const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; /* Raycast and tree stuff */ @@ -245,11 +364,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,106 +404,64 @@ 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))) - { - -#ifndef __APPLE__ -#pragma omp parallel for private(i, hit) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3], tmp_no[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) { - continue; - } - - if (calc->vert) { - /* calc->vert contains verts from derivedMesh */ - /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ - /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ - if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { - copy_v3_v3(tmp_co, calc->vert[i].co); - normal_short_to_float_v3(tmp_no, calc->vert[i].no); - } - else { - copy_v3_v3(tmp_co, co); - copy_v3_v3(tmp_no, proj_axis); + if (targ_tree) { + BVHTree *aux_tree = NULL; + void *aux_callback = NULL; + 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 { - copy_v3_v3(tmp_co, co); - copy_v3_v3(tmp_no, proj_axis); - } - - - hit.index = -1; - hit.dist = BVH_RAYCAST_DIST_MAX; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ - - /* 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); - } - - BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, tmp_no, - &calc->local2target, treeData.tree, &hit, - treeData.raycast_callback, &treeData); - } - - /* Project over negative direction of axis */ - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { - 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); - } - - BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, inv_no, - &calc->local2target, treeData.tree, &hit, - treeData.raycast_callback, &treeData); - } - - /* don't set the initial dist (which is more efficient), - * because its calculated in the targets space, we want the dist in our own space */ - if (proj_limit_squared != 0.0f) { - if (len_squared_v3v3(hit.co, co) > proj_limit_squared) { - hit.index = -1; + 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; } } - - if (hit.index != -1) { - madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); - interp_v3_v3v3(co, co, hit.co, weight); - } } + /* After sucessufuly build the trees, start projection vertexs */ + ShrinkwrapCalcCBData data = { + .calc = calc, + .treeData = treeData, .targ_tree = targ_tree, .targ_callback = targ_callback, + .auxData = auxData, .aux_tree = aux_tree, .aux_callback = aux_callback, + .proj_axis = proj_axis, .local2aux = &local2aux, + }; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &hit, sizeof(hit), shrinkwrap_calc_normal_projection_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); } /* 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); + } } /* @@ -393,10 +470,75 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for * it builds a BVHTree from the target mesh and then performs a * NN matches for each vertex */ -static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) +static void shrinkwrap_calc_nearest_surface_point_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) { - int i; + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + BVHTreeFromMesh *treeData = data->treeData; + BVHTreeNearest *nearest = userdata_chunk; + + float *co = calc->vertexCos[i]; + float tmp_co[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + /* Convert the vertex to tree coordinates */ + if (calc->vert) { + copy_v3_v3(tmp_co, calc->vert[i].co); + } + else { + copy_v3_v3(tmp_co, co); + } + BLI_space_transform_apply(&calc->local2target, tmp_co); + + /* Use local proximity heuristics (to reduce the nearest search) + * + * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex + * so we can initiate the "nearest.dist" with the expected value to that last hit. + * This will lead in pruning of the search tree. */ + if (nearest->index != -1) + nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co); + else + nearest->dist_sq = FLT_MAX; + + BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData); + + /* Found the nearest vertex */ + if (nearest->index != -1) { + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { + /* Make the vertex stay on the front side of the face */ + madd_v3_v3v3fl(tmp_co, nearest->co, nearest->no, calc->keepDist); + } + else { + /* Adjusting the vertex weight, + * so that after interpolating it keeps a certain distance from the nearest position */ + const float dist = sasqrt(nearest->dist_sq); + if (dist > FLT_EPSILON) { + /* linear interpolation */ + interp_v3_v3v3(tmp_co, tmp_co, nearest->co, (dist - calc->keepDist) / dist); + } + else { + copy_v3_v3(tmp_co, nearest->co); + } + } + /* Convert the coordinates back to mesh coordinates */ + BLI_space_transform_invert(&calc->local2target, tmp_co); + interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ + } +} + +static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) +{ BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; @@ -411,67 +553,11 @@ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) nearest.index = -1; nearest.dist_sq = FLT_MAX; - /* Find the nearest vertex */ -#ifndef __APPLE__ -#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(calc, treeData) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) continue; - - /* Convert the vertex to tree coordinates */ - if (calc->vert) { - copy_v3_v3(tmp_co, calc->vert[i].co); - } - else { - copy_v3_v3(tmp_co, co); - } - BLI_space_transform_apply(&calc->local2target, tmp_co); - - /* Use local proximity heuristics (to reduce the nearest search) - * - * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex - * so we can initiate the "nearest.dist" with the expected value to that last hit. - * This will lead in pruning of the search tree. */ - if (nearest.index != -1) - nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co); - else - nearest.dist_sq = FLT_MAX; - - BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); - - /* Found the nearest vertex */ - if (nearest.index != -1) { - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { - /* Make the vertex stay on the front side of the face */ - madd_v3_v3v3fl(tmp_co, nearest.co, nearest.no, calc->keepDist); - } - else { - /* Adjusting the vertex weight, - * so that after interpolating it keeps a certain distance from the nearest position */ - const float dist = sasqrt(nearest.dist_sq); - if (dist > FLT_EPSILON) { - /* linear interpolation */ - interp_v3_v3v3(tmp_co, tmp_co, nearest.co, (dist - calc->keepDist) / dist); - } - else { - copy_v3_v3(tmp_co, nearest.co); - } - } - - /* Convert the coordinates back to mesh coordinates */ - BLI_space_transform_invert(&calc->local2target, tmp_co); - interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ - } - } + ShrinkwrapCalcCBData data = {.calc = calc, .treeData = &treeData}; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &nearest, sizeof(nearest), shrinkwrap_calc_nearest_surface_point_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); free_bvhtree_from_mesh(&treeData); } 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..5fd418fadfc 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; @@ -3366,13 +3370,14 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, CCGSubSurf *ss = ccgdm->ss; CCGKey key; int colType; - const MLoopCol *mloopcol; + const MLoopCol *mloopcol = NULL; MTexPoly *mtexpoly = DM_get_poly_data_layer(dm, CD_MTEXPOLY); DMFlagMat *faceFlags = ccgdm->faceFlags; DMDrawOption draw_option; int i, totpoly; bool flush; - bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_colors = (flag & DM_DRAW_USE_COLORS) != 0; unsigned int next_actualFace; unsigned int gridFaces = ccgSubSurf_getGridSize(ss) - 1; int mat_index; @@ -3391,15 +3396,17 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, CCG_key_top_level(&key, ss); ccgdm_pbvh_update(ccgdm); - colType = CD_TEXTURE_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - if (!mloopcol) { - colType = CD_PREVIEW_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - } - if (!mloopcol) { - colType = CD_MLOOPCOL; + if (use_colors) { + colType = CD_TEXTURE_MLOOPCOL; mloopcol = dm->getLoopDataArray(dm, colType); + if (!mloopcol) { + colType = CD_PREVIEW_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } + if (!mloopcol) { + colType = CD_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } } GPU_vertex_setup(dm); @@ -4361,6 +4368,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 +4399,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_store.h b/source/blender/blenlib/BLI_array_store.h new file mode 100644 index 00000000000..f4cbc07bf26 --- /dev/null +++ b/source/blender/blenlib/BLI_array_store.h @@ -0,0 +1,66 @@ +/* + * ***** 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 __BLI_ARRAY_STORE_H__ +#define __BLI_ARRAY_STORE_H__ + +/** \file BLI_array_store.h + * \ingroup bli + * \brief Efficient in-memory storage of multiple similar arrays. + */ + +typedef struct BArrayStore BArrayStore; +typedef struct BArrayState BArrayState; + +BArrayStore *BLI_array_store_create( + unsigned int stride, unsigned int chunk_count); +void BLI_array_store_destroy( + BArrayStore *bs); +void BLI_array_store_clear( + BArrayStore *bs); + +/* find the memory used by all states (expanded & real) */ +size_t BLI_array_store_calc_size_expanded_get( + const BArrayStore *bs); +size_t BLI_array_store_calc_size_compacted_get( + const BArrayStore *bs); + +BArrayState *BLI_array_store_state_add( + BArrayStore *bs, + const void *data, const size_t data_len, + const BArrayState *state_reference); +void BLI_array_store_state_remove( + BArrayStore *bs, + BArrayState *state); + +size_t BLI_array_store_state_size_get( + BArrayState *state); +void BLI_array_store_state_data_get( + BArrayState *state, + void *data); +void *BLI_array_store_state_data_get_alloc( + BArrayState *state, + size_t *r_data_len); + +/* only for tests */ +bool BLI_array_store_is_valid( + BArrayStore *bs); + +#endif /* __BLI_ARRAY_STORE_H__ */ 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..54b824c91ac 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); @@ -464,6 +470,10 @@ MINLINE float shell_v2v2_normalized_to_dist(const float a[2], const float b[2]); MINLINE float shell_v3v3_mid_normalized_to_dist(const float a[3], const float b[3]); MINLINE float shell_v2v2_mid_normalized_to_dist(const float a[2], const float b[2]); +/********************************* Cubic (Bezier) *******************************/ + +float cubic_tangent_factor_circle_v3(const float tan_l[3], const float tan_r[3]); + /**************************** Inline Definitions ******************************/ #if BLI_MATH_DO_INLINE 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/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 944ba60eb58..42d958774d8 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC intern/BLI_memarena.c intern/BLI_mempool.c intern/DLRB_tree.c + intern/array_store.c intern/array_utils.c intern/astar.c intern/boxpack2d.c @@ -120,6 +121,7 @@ set(SRC BLI_alloca.h BLI_args.h BLI_array.h + BLI_array_store.h BLI_array_utils.h BLI_astar.h BLI_bitmap.h diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 06946e520a8..05f2d9221ef 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -1287,7 +1287,7 @@ bool BLI_ghashutil_paircmp(const void *a, const void *b) const GHashPair *A = a; const GHashPair *B = b; - return (BLI_ghashutil_ptrcmp(A->first, B->first) || + return (BLI_ghashutil_ptrcmp(A->first, B->first) && BLI_ghashutil_ptrcmp(A->second, B->second)); } 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_store.c b/source/blender/blenlib/intern/array_store.c new file mode 100644 index 00000000000..7f657f4a048 --- /dev/null +++ b/source/blender/blenlib/intern/array_store.c @@ -0,0 +1,1731 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenlib/intern/array_store.c + * \ingroup bli + * \brief Array storage to minimize duplication. + * + * This is done by splitting arrays into chunks and using copy-on-write (COW), + * to de-duplicate chunks, + * from the users perspective this is an implementation detail. + * + * + * Overview + * ======== + * + * + * Data Structure + * -------------- + * + * This diagram is an overview of the structure of a single array-store. + * + * \note The only 2 structues here which are referenced externally are the. + * + * - BArrayStore: The whole array store. + * - BArrayState: Represents a single state (array) of data. + * These can be add using a reference state, while this could be considered the previous or parent state. + * no relationship is kept, so the caller is free to add any state from the same BArrayStore as a reference. + * + * <pre> + * <+> BArrayStore: root data-structure, + * | can store many 'states', which share memory. + * | + * | This can store many arrays, however they must share the same 'stride'. + * | Arrays of different types will need to use a new BArrayStore. + * | + * +- <+> states (Collection of BArrayState's): + * | | Each represents an array added by the user of this API. + * | | and references a chunk_list (each state is a chunk_list user). + * | | Note that the list order has no significance. + * | | + * | +- <+> chunk_list (BChunkList): + * | | The chunks that make up this state. + * | | Each state is a chunk_list user, + * | | avoids duplicating lists when there is no change between states. + * | | + * | +- chunk_refs (List of BChunkRef): Each chunk_ref links to a a BChunk. + * | Each reference is a chunk user, + * | avoids duplicating smaller chunks of memory found in multiple states. + * | + * +- info (BArrayInfo): + * | Sizes and offsets for this array-store. + * | Also caches some variables for reuse. + * | + * +- <+> memory (BArrayMemory): + * | Memory pools for storing BArrayStore data. + * | + * +- chunk_list (Pool of BChunkList): + * | All chunk_lists, (reference counted, used by BArrayState). + * | + * +- chunk_ref (Pool of BChunkRef): + * | All chunk_refs (link between BChunkList & BChunk). + * | + * +- chunks (Pool of BChunk): + * All chunks, (reference counted, used by BChunkList). + * These have their headers hashed for reuse so we can quickly check for duplicates. + * </pre> + * + * + * De-Duplication + * -------------- + * + * When creating a new state, a previous state can be given as a reference, + * matching chunks from this state are re-used in the new state. + * + * First matches at either end of the array are detected. + * For identical arrays this is all thats needed. + * + * De-duplication is performed on any remaining chunks, by hasing the first few bytes of the chunk + * (see: BCHUNK_HASH_TABLE_ACCUMULATE_STEPS). + * + * \note This is cached for reuse since the referenced data never changes. + * + * An array is created to store hash values at every 'stride', + * then stepped over to search for matching chunks. + * + * Once a match is found, there is a high chance next chunks match too, + * so this is checked to avoid performing so many hash-lookups. + * Otherwise new chunks are created. + */ + +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_mempool.h" + +#include "BLI_strict_flags.h" + +#include "BLI_array_store.h" /* own include */ + +/* only for BLI_array_store_is_valid */ +#include "BLI_ghash.h" + +/** \name Defines + * + * Some of the logic for merging is quite involved, + * support disabling some parts of this. + * \{ */ + +/* Scan first chunks (happy path when beginning of the array matches). + * When the array is a perfect match, we can re-use the entire list. + * + * Note that disabling makes some tests fail that check for output-size. + */ +#define USE_FASTPATH_CHUNKS_FIRST + +/* Scan last chunks (happy path when end of the array matches). + * When the end of the array matches, we can quickly add these chunks. + * note that we will add contiguous matching chunks + * so this isn't as useful as USE_FASTPATH_CHUNKS_FIRST, + * however it avoids adding matching chunks into the lookup table, + * so creating the lookup table won't be as expensive. + */ +#ifdef USE_FASTPATH_CHUNKS_FIRST +# define USE_FASTPATH_CHUNKS_LAST +#endif + +/* For arrays of matching length, test that *enough* of the chunks are aligned, + * and simply step over both arrays, using matching chunks. + * This avoids overhead of using a lookup table for cases when we can assume they're mostly aligned. + */ +#define USE_ALIGN_CHUNKS_TEST + +/* Accumulate hashes from right to left so we can create a hash for the chunk-start. + * This serves to increase uniqueness and will help when there is many values which are the same. + */ +#define USE_HASH_TABLE_ACCUMULATE + +#ifdef USE_HASH_TABLE_ACCUMULATE +/* Number of times to propagate hashes back. + * Effectively a 'triangle-number'. + * so 4 -> 7, 5 -> 10, 6 -> 15... etc. + */ +# define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS 4 +#else +/* How many items to hash (multiplied by stride) + */ +# define BCHUNK_HASH_LEN 4 +#endif + +/* Calculate the key once and reuse it + */ +#define USE_HASH_TABLE_KEY_CACHE +#ifdef USE_HASH_TABLE_KEY_CACHE +# define HASH_TABLE_KEY_UNSET ((uint64_t)-1) +# define HASH_TABLE_KEY_FALLBACK ((uint64_t)-2) +#endif + +/* How much larger the table is then the total number of chunks. + */ +#define BCHUNK_HASH_TABLE_MUL 3 + +/* Merge too small/large chunks: + * + * Using this means chunks below a threshold will be merged together. + * Even though short term this uses more memory, + * long term the overhead of maintaining many small chunks is reduced. + * This is defined by setting the minimum chunk size (as a fraction of the regular chunk size). + * + * Chunks may also become too large (when incrementally growing an array), + * this also enables chunk splitting. + */ +#define USE_MERGE_CHUNKS + +#ifdef USE_MERGE_CHUNKS +/* Merge chunks smaller then: (chunk_size / BCHUNK_MIN_SIZE_DIV) + */ +# define BCHUNK_SIZE_MIN_DIV 8 + +/* Disallow chunks bigger then the regular chunk size scaled by this value + * note: must be at least 2! + * however, this code runs wont run in tests unless its ~1.1 ugh. + * so lower only to check splitting works. + */ +# define BCHUNK_SIZE_MAX_MUL 2 +#endif /* USE_MERGE_CHUNKS */ + +/* slow (keep disabled), but handy for debugging */ +// #define USE_VALIDATE_LIST_SIZE + +// #define USE_VALIDATE_LIST_DATA_PARTIAL + +// #define USE_PARANOID_CHECKS + +/** \} */ + + +/** \name Internal Structs + * \{ */ + +typedef unsigned int uint; +typedef unsigned char ubyte; + +typedef uint64_t hash_key; + + +typedef struct BArrayInfo { + size_t chunk_stride; + uint chunk_count; + + /* pre-calculated */ + size_t chunk_byte_size; + size_t chunk_byte_size_min; + + size_t accum_read_ahead_bytes; +#ifdef USE_HASH_TABLE_ACCUMULATE + size_t accum_steps; + size_t accum_read_ahead_len; +#endif +} BArrayInfo; + +typedef struct BArrayMemory { + BLI_mempool *chunk_list; /* BChunkList */ + BLI_mempool *chunk_ref; /* BChunkRef */ + BLI_mempool *chunk; /* BChunk */ +} BArrayMemory; + +/** + * Main storage for all states + */ +typedef struct BArrayStore { + /* static */ + BArrayInfo info; + + /* memory storage */ + BArrayMemory memory; + + /** + * #BArrayState may be in any order (logic should never depend on state order). + */ + ListBase states; +} BArrayStore; + +/** + * A single instance of an array. + * + * This is how external API's hold a reference to an in-memory state, + * although the struct is private. + * + * \note Currently each 'state' is allocated separately. + * While this could be moved to a memory pool, + * it makes it easier to trace invalid usage, so leave as-is for now. + */ +typedef struct BArrayState { + /** linked list in #BArrayStore.states */ + struct BArrayState *next, *prev; + + struct BChunkList *chunk_list; /* BChunkList's */ + +} BArrayState; + +typedef struct BChunkList { + ListBase chunk_refs; /* BChunkRef's */ + uint chunk_refs_len; /* BLI_listbase_count(chunks), store for reuse. */ + size_t total_size; /* size of all chunks */ + + /** number of #BArrayState using this. */ + int users; +} BChunkList; + +/* a chunk of an array */ +typedef struct BChunk { + const ubyte *data; + size_t data_len; + /** number of #BChunkList using this. */ + int users; + +#ifdef USE_HASH_TABLE_KEY_CACHE + hash_key key; +#endif +} BChunk; + +/** + * Links to store #BChunk data in #BChunkList.chunks. + */ +typedef struct BChunkRef { + struct BChunkRef *next, *prev; + BChunk *link; +} BChunkRef; + +/** + * Single linked list used when putting chunks into a temporary table, + * used for lookups. + * + * Point to the #BChunkRef, not the #BChunk, + * to allow talking down the chunks in-order until a mis-match is found, + * this avoids having to do so many table lookups. + */ +typedef struct BTableRef { + struct BTableRef *next; + const BChunkRef *cref; +} BTableRef; + +/** \} */ + + +static size_t bchunk_list_size(const BChunkList *chunk_list); + + +/** \name Internal BChunk API + * \{ */ + +static BChunk *bchunk_new( + BArrayMemory *bs_mem, const ubyte *data, const size_t data_len) +{ + BChunk *chunk = BLI_mempool_alloc(bs_mem->chunk); + chunk->data = data; + chunk->data_len = data_len; + chunk->users = 0; +#ifdef USE_HASH_TABLE_KEY_CACHE + chunk->key = HASH_TABLE_KEY_UNSET; +#endif + return chunk; +} + +static BChunk *bchunk_new_copydata( + BArrayMemory *bs_mem, const ubyte *data, const size_t data_len) +{ + ubyte *data_copy = MEM_mallocN(data_len, __func__); + memcpy(data_copy, data, data_len); + return bchunk_new(bs_mem, data_copy, data_len); +} + +static void bchunk_decref( + BArrayMemory *bs_mem, BChunk *chunk) +{ + BLI_assert(chunk->users > 0); + if (chunk->users == 1) { + MEM_freeN((void *)chunk->data); + BLI_mempool_free(bs_mem->chunk, chunk); + } + else { + chunk->users -= 1; + } +} + +static bool bchunk_data_compare( + const BChunk *chunk, + const ubyte *data_base, const size_t data_base_len, + const size_t offset) +{ + if (offset + (size_t)chunk->data_len <= data_base_len) { + return (memcmp(&data_base[offset], chunk->data, chunk->data_len) == 0); + } + else { + return false; + } +} + +/** \} */ + + +/** \name Internal BChunkList API + * \{ */ + +static BChunkList *bchunk_list_new( + BArrayMemory *bs_mem, size_t total_size) +{ + BChunkList *chunk_list = BLI_mempool_alloc(bs_mem->chunk_list); + + BLI_listbase_clear(&chunk_list->chunk_refs); + chunk_list->chunk_refs_len = 0; + chunk_list->total_size = total_size; + chunk_list->users = 0; + return chunk_list; +} + +static void bchunk_list_decref( + BArrayMemory *bs_mem, BChunkList *chunk_list) +{ + BLI_assert(chunk_list->users > 0); + if (chunk_list->users == 1) { + for (BChunkRef *cref = chunk_list->chunk_refs.first, *cref_next; cref; cref = cref_next) { + cref_next = cref->next; + bchunk_decref(bs_mem, cref->link); + BLI_mempool_free(bs_mem->chunk_ref, cref); + } + + BLI_mempool_free(bs_mem->chunk_list, chunk_list); + } + else { + chunk_list->users -= 1; + } +} + +#ifdef USE_VALIDATE_LIST_SIZE +# ifndef NDEBUG +# define ASSERT_CHUNKLIST_SIZE(chunk_list, n) BLI_assert(bchunk_list_size(chunk_list) == n) +# endif +#endif +#ifndef ASSERT_CHUNKLIST_SIZE +# define ASSERT_CHUNKLIST_SIZE(chunk_list, n) (EXPR_NOP(chunk_list), EXPR_NOP(n)) +#endif + + +#ifdef USE_VALIDATE_LIST_DATA_PARTIAL +static size_t bchunk_list_data_check( + const BChunkList *chunk_list, const ubyte *data) +{ + size_t total_size = 0; + for (BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + if (memcmp(&data[total_size], cref->link->data, cref->link->data_len) != 0) { + return false; + } + total_size += cref->link->data_len; + } + return true; +} +# define ASSERT_CHUNKLIST_DATA(chunk_list, data) BLI_assert(bchunk_list_data_check(chunk_list, data)) +#else +# define ASSERT_CHUNKLIST_DATA(chunk_list, data) (EXPR_NOP(chunk_list), EXPR_NOP(data)) +#endif + + +#ifdef USE_MERGE_CHUNKS +static void bchunk_list_ensure_min_size_last( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list) +{ + BChunkRef *cref = chunk_list->chunk_refs.last; + if (cref && cref->prev) { + /* both are decref'd after use (end of this block) */ + BChunk *chunk_curr = cref->link; + BChunk *chunk_prev = cref->prev->link; + + if (MIN2(chunk_prev->data_len, chunk_curr->data_len) < info->chunk_byte_size_min) { + const size_t data_merge_len = chunk_prev->data_len + chunk_curr->data_len; + /* we could pass, but no need */ + if (data_merge_len <= (info->chunk_byte_size * BCHUNK_SIZE_MAX_MUL)) { + /* we have enough space to merge */ + + /* remove last from linklist */ + BLI_assert(chunk_list->chunk_refs.last != chunk_list->chunk_refs.first); + cref->prev->next = NULL; + chunk_list->chunk_refs.last = cref->prev; + chunk_list->chunk_refs_len -= 1; + + ubyte *data_merge = MEM_mallocN(data_merge_len, __func__); + memcpy(data_merge, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_merge[chunk_prev->data_len], chunk_curr->data, chunk_curr->data_len); + + cref->prev->link = bchunk_new(bs_mem, data_merge, data_merge_len); + cref->prev->link->users += 1; + + BLI_mempool_free(bs_mem->chunk_ref, cref); + } + else { + /* If we always merge small slices, we should _almost_ never end up having very large chunks. + * Gradual expanding on contracting will cause this. + * + * if we do, the code below works (test by setting 'BCHUNK_SIZE_MAX_MUL = 1.2') */ + + /* keep chunk on the left hand side a regular size */ + const size_t split = info->chunk_byte_size; + + /* merge and split */ + const size_t data_prev_len = split; + const size_t data_curr_len = data_merge_len - split; + ubyte *data_prev = MEM_mallocN(data_prev_len, __func__); + ubyte *data_curr = MEM_mallocN(data_curr_len, __func__); + + if (data_prev_len <= chunk_prev->data_len) { + const size_t data_curr_shrink_len = chunk_prev->data_len - data_prev_len; + + /* setup 'data_prev' */ + memcpy(data_prev, chunk_prev->data, data_prev_len); + + /* setup 'data_curr' */ + memcpy(data_curr, &chunk_prev->data[data_prev_len], data_curr_shrink_len); + memcpy(&data_curr[data_curr_shrink_len], chunk_curr->data, chunk_curr->data_len); + } + else { + BLI_assert(data_curr_len <= chunk_curr->data_len); + BLI_assert(data_prev_len >= chunk_prev->data_len); + + const size_t data_prev_grow_len = data_prev_len - chunk_prev->data_len; + + /* setup 'data_prev' */ + memcpy(data_prev, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_prev[chunk_prev->data_len], chunk_curr->data, data_prev_grow_len); + + /* setup 'data_curr' */ + memcpy(data_curr, &chunk_curr->data[data_prev_grow_len], data_curr_len); + } + + cref->prev->link = bchunk_new(bs_mem, data_prev, data_prev_len); + cref->prev->link->users += 1; + + cref->link = bchunk_new(bs_mem, data_curr, data_curr_len); + cref->link->users += 1; + } + + /* free zero users */ + bchunk_decref(bs_mem, chunk_curr); + bchunk_decref(bs_mem, chunk_prev); + } + } +} +#endif /* USE_MERGE_CHUNKS */ + +/** + * Append and don't manage merging small chunks. + */ +static bool bchunk_list_append_only( + BArrayMemory *bs_mem, + BChunkList *chunk_list, BChunk *chunk) +{ + BChunkRef *cref = BLI_mempool_alloc(bs_mem->chunk_ref); + BLI_addtail(&chunk_list->chunk_refs, cref); + cref->link = chunk; + chunk_list->chunk_refs_len += 1; + chunk->users += 1; + return chunk; +} + +static void bchunk_list_append_data( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + const ubyte *data, const size_t data_len) +{ + BLI_assert(data_len != 0); + // printf("data_len: %d\n", data_len); +#ifdef USE_MERGE_CHUNKS + if (!BLI_listbase_is_empty(&chunk_list->chunk_refs)) { + BChunkRef *cref = chunk_list->chunk_refs.last; + BChunk *chunk_prev = cref->link; + + if (MIN2(chunk_prev->data_len, data_len) < info->chunk_byte_size_min) { + const size_t data_merge_len = chunk_prev->data_len + data_len; + /* realloc for single user */ + if (cref->link->users == 1) { + ubyte *data_merge = MEM_reallocN((void *)cref->link->data, data_merge_len); + memcpy(&data_merge[chunk_prev->data_len], data, data_len); + cref->link->data = data_merge; + cref->link->data_len = data_merge_len; + } + else { + ubyte *data_merge = MEM_mallocN(data_merge_len, __func__); + memcpy(data_merge, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_merge[chunk_prev->data_len], data, data_len); + cref->link = bchunk_new(bs_mem, data_merge, data_merge_len); + cref->link->users += 1; + bchunk_decref(bs_mem, chunk_prev); + } + return; + } + } +#else + UNUSED_VARS(info); +#endif /* USE_MERGE_CHUNKS */ + + BChunk *chunk = bchunk_new_copydata(bs_mem, data, data_len); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + + /* don't run this, instead preemptively avoid creating a chunk only to merge it (above). */ +#if 0 +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list, chunk_size_min); +#endif +#endif +} + +static void bchunk_list_append( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + BChunk *chunk) +{ + bchunk_list_append_only(bs_mem, chunk_list, chunk); + +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list); +#else + UNUSED_VARS(info); +#endif +} + +static void bchunk_list_fill_from_array( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + const ubyte *data, + const size_t data_len) +{ + BLI_assert(BLI_listbase_is_empty(&chunk_list->chunk_refs)); + + size_t data_last_chunk_len = 0; + size_t data_trim_len = data_len; + +#ifdef USE_MERGE_CHUNKS + /* avoid creating too-small chunks + * more efficient then merging after */ + if (data_len > info->chunk_byte_size) { + data_last_chunk_len = (data_trim_len % info->chunk_byte_size); + data_trim_len = data_trim_len - data_last_chunk_len; + if (data_last_chunk_len) { + if (data_last_chunk_len < info->chunk_byte_size_min) { + /* may be zero and thats OK */ + data_trim_len -= info->chunk_byte_size; + data_last_chunk_len += info->chunk_byte_size; + } + } + } + else { + data_trim_len = 0; + data_last_chunk_len = data_len; + } +#else + data_last_chunk_len = (data_trim_len % info->chunk_byte_size); + data_trim_len = data_trim_len - data_last_chunk_len; +#endif + + + BLI_assert(data_trim_len + data_last_chunk_len == data_len); + + size_t i_prev = 0; + while (i_prev < data_trim_len) { + const size_t i = i_prev + info->chunk_byte_size; + BChunk *chunk = bchunk_new_copydata(bs_mem, &data[i_prev], i - i_prev); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + i_prev = i; + } + + if (data_last_chunk_len) { + BChunk *chunk = bchunk_new_copydata(bs_mem, &data[i_prev], data_last_chunk_len); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + // i_prev = data_len; + } + +#ifdef USE_MERGE_CHUNKS + if (data_len > info->chunk_byte_size) { + BLI_assert(((BChunkRef *)chunk_list->chunk_refs.last)->link->data_len >= info->chunk_byte_size_min); + } +#endif + + /* works but better avoid redundant re-alloc */ +#if 0 +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list); +#endif +#endif + + ASSERT_CHUNKLIST_SIZE(chunk_list, data_len); + ASSERT_CHUNKLIST_DATA(chunk_list, data); +} + + +/* --------------------------------------------------------------------------- + * Internal Table Lookup Functions + */ + +/** \name Internal Hashing/De-Duplication API + * + * Only used by #bchunk_list_from_data_merge + * \{ */ + +#define HASH_INIT (5381) + +BLI_INLINE uint hash_data_single(const ubyte p) +{ + return (HASH_INIT << 5) + HASH_INIT + (unsigned int)p; +} + +/* hash bytes, from BLI_ghashutil_strhash_n */ +static uint hash_data(const ubyte *key, size_t n) +{ + const signed char *p; + unsigned int h = HASH_INIT; + + for (p = (const signed char *)key; n--; p++) { + h = (h << 5) + h + (unsigned int)*p; + } + + return h; +} + +#undef HASH_INIT + + +#ifdef USE_HASH_TABLE_ACCUMULATE +static void hash_array_from_data( + const BArrayInfo *info, const ubyte *data_slice, const size_t data_slice_len, + hash_key *hash_array) +{ + if (info->chunk_stride != 1) { + for (size_t i = 0, i_step = 0; i_step < data_slice_len; i++, i_step += info->chunk_stride) { + hash_array[i] = hash_data(&data_slice[i_step], info->chunk_stride); + } + } + else { + /* fast-path for bytes */ + for (size_t i = 0; i < data_slice_len; i++) { + hash_array[i] = hash_data_single(data_slice[i]); + } + } +} + +/* + * Similar to hash_array_from_data, + * but able to step into the next chunk if we run-out of data. + */ +static void hash_array_from_cref( + const BArrayInfo *info, const BChunkRef *cref, const size_t data_len, + hash_key *hash_array) +{ + const size_t hash_array_len = data_len / info->chunk_stride; + size_t i = 0; + do { + size_t i_next = hash_array_len - i; + size_t data_trim_len = i_next * info->chunk_stride; + if (data_trim_len > cref->link->data_len) { + data_trim_len = cref->link->data_len; + i_next = data_trim_len / info->chunk_stride; + } + BLI_assert(data_trim_len <= cref->link->data_len); + hash_array_from_data(info, cref->link->data, data_trim_len, &hash_array[i]); + i += i_next; + cref = cref->next; + } while ((i < hash_array_len) && (cref != NULL)); + + /* If this isn't equal, the caller didn't properly check + * that there was enough data left in all chunks */ + BLI_assert(i == hash_array_len); +} + +static void hash_accum(hash_key *hash_array, const size_t hash_array_len, size_t iter_steps) +{ + /* _very_ unlikely, can happen if you select a chunk-size of 1 for example. */ + if (UNLIKELY((iter_steps > hash_array_len))) { + iter_steps = hash_array_len; + } + + const size_t hash_array_search_len = hash_array_len - iter_steps; + while (iter_steps != 0) { + const size_t hash_offset = iter_steps; + for (uint i = 0; i < hash_array_search_len; i++) { + hash_array[i] += (hash_array[i + hash_offset]) * ((hash_array[i] & 0xff) + 1); + } + iter_steps -= 1; + } +} + +/** + * When we only need a single value, can use a small optimization. + * we can avoid accumulating the tail of the array a little, each iteration. + */ +static void hash_accum_single(hash_key *hash_array, const size_t hash_array_len, size_t iter_steps) +{ + BLI_assert(iter_steps <= hash_array_len); + if (UNLIKELY(!(iter_steps <= hash_array_len))) { + /* while this shouldn't happen, avoid crashing */ + iter_steps = hash_array_len; + } + /* We can increase this value each step to avoid accumulating quite as much + * while getting the same results as hash_accum */ + size_t iter_steps_sub = iter_steps; + + while (iter_steps != 0) { + const size_t hash_array_search_len = hash_array_len - iter_steps_sub; + const size_t hash_offset = iter_steps; + for (uint i = 0; i < hash_array_search_len; i++) { + hash_array[i] += (hash_array[i + hash_offset]) * ((hash_array[i] & 0xff) + 1); + } + iter_steps -= 1; + iter_steps_sub += iter_steps; + } +} + +static hash_key key_from_chunk_ref( + const BArrayInfo *info, const BChunkRef *cref, + /* avoid reallicating each time */ + hash_key *hash_store, const size_t hash_store_len) +{ + /* in C, will fill in a reusable array */ + BChunk *chunk = cref->link; + BLI_assert(info->accum_read_ahead_bytes * info->chunk_stride); + + if (info->accum_read_ahead_bytes <= chunk->data_len) { + hash_key key; + +#ifdef USE_HASH_TABLE_KEY_CACHE + key = chunk->key; + if (key != HASH_TABLE_KEY_UNSET) { + /* Using key cache! + * avoids calculating every time */ + } + else { + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + key = hash_store[0]; + + /* cache the key */ + if (key == HASH_TABLE_KEY_UNSET) { + key = HASH_TABLE_KEY_FALLBACK; + } + chunk->key = key; + } +#else + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + key = hash_store[0]; +#endif + return key; + } + else { + /* corner case - we're too small, calculate the key each time. */ + + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + hash_key key = hash_store[0]; + +#ifdef USE_HASH_TABLE_KEY_CACHE + if (UNLIKELY(key == HASH_TABLE_KEY_UNSET)) { + key = HASH_TABLE_KEY_FALLBACK; + } +#endif + return key; + } +} + +static const BChunkRef *table_lookup( + const BArrayInfo *info, BTableRef **table, const size_t table_len, const size_t i_table_start, + const ubyte *data, const size_t data_len, const size_t offset, const hash_key *table_hash_array) +{ + size_t size_left = data_len - offset; + hash_key key = table_hash_array[((offset - i_table_start) / info->chunk_stride)]; + size_t key_index = (size_t)(key % (hash_key)table_len); + for (BTableRef *tref = table[key_index]; tref; tref = tref->next) { + const BChunkRef *cref = tref->cref; +#ifdef USE_HASH_TABLE_KEY_CACHE + if (cref->link->key == key) +#endif + { + BChunk *chunk_test = cref->link; + if (chunk_test->data_len <= size_left) { + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + /* we could remove the chunk from the table, to avoid multiple hits */ + return cref; + } + } + } + } + return NULL; +} + +#else /* USE_HASH_TABLE_ACCUMULATE */ + +/* NON USE_HASH_TABLE_ACCUMULATE code (simply hash each chunk) */ + +static hash_key key_from_chunk_ref(const BArrayInfo *info, const BChunkRef *cref) +{ + const size_t data_hash_len = BCHUNK_HASH_LEN * info->chunk_stride; + hash_key key; + BChunk *chunk = cref->link; + +#ifdef USE_HASH_TABLE_KEY_CACHE + key = chunk->key; + if (key != HASH_TABLE_KEY_UNSET) { + /* Using key cache! + * avoids calculating every time */ + } + else { + /* cache the key */ + key = hash_data(chunk->data, data_hash_len); + if (key == HASH_TABLE_KEY_UNSET) { + key = HASH_TABLE_KEY_FALLBACK; + } + chunk->key = key; + } +#else + key = hash_data(chunk->data, data_hash_len); +#endif + + return key; +} + +static const BChunkRef *table_lookup( + const BArrayInfo *info, BTableRef **table, const size_t table_len, const uint UNUSED(i_table_start), + const ubyte *data, const size_t data_len, const size_t offset, const hash_key *UNUSED(table_hash_array)) +{ + const size_t data_hash_len = BCHUNK_HASH_LEN * info->chunk_stride; /* TODO, cache */ + + size_t size_left = data_len - offset; + hash_key key = hash_data(&data[offset], MIN2(data_hash_len, size_left)); + size_t key_index = (size_t)(key % (hash_key)table_len); + for (BTableRef *tref = table[key_index]; tref; tref = tref->next) { + const BChunkRef *cref = tref->cref; +#ifdef USE_HASH_TABLE_KEY_CACHE + if (cref->link->key == key) +#endif + { + BChunk *chunk_test = cref->link; + if (chunk_test->data_len <= size_left) { + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + /* we could remove the chunk from the table, to avoid multiple hits */ + return cref; + } + } + } + } + return NULL; +} + +#endif /* USE_HASH_TABLE_ACCUMULATE */ + +/* End Table Lookup + * ---------------- */ + +/** \} */ + +/** + * \param data: Data to store in the returned value. + * \param data_len_original: Length of data in bytes. + * \param chunk_list_reference: Reuse this list or chunks within it, don't modify its content. + * \note Caller is responsible for adding the user. + */ +static BChunkList *bchunk_list_from_data_merge( + const BArrayInfo *info, BArrayMemory *bs_mem, + const ubyte *data, const size_t data_len_original, + const BChunkList *chunk_list_reference) +{ + ASSERT_CHUNKLIST_SIZE(chunk_list_reference, chunk_list_reference->total_size); + + /* ----------------------------------------------------------------------- + * Fast-Path for exact match + * Check for exact match, if so, return the current list. + */ + + const BChunkRef *cref_match_first = NULL; + + uint chunk_list_reference_skip_len = 0; + size_t chunk_list_reference_skip_bytes = 0; + size_t i_prev = 0; + +#ifdef USE_FASTPATH_CHUNKS_FIRST + bool full_match = false; + + { + full_match = true; + + const BChunkRef *cref = chunk_list_reference->chunk_refs.first; + while (i_prev < data_len_original) { + if (cref != NULL && bchunk_data_compare(cref->link, data, data_len_original, i_prev)) { + cref_match_first = cref; + chunk_list_reference_skip_len += 1; + chunk_list_reference_skip_bytes += cref->link->data_len; + i_prev += cref->link->data_len; + cref = cref->next; + } + else { + full_match = false; + break; + } + } + + if (full_match) { + if (chunk_list_reference->total_size == data_len_original) { + return (BChunkList *)chunk_list_reference; + } + } + } + + /* End Fast-Path (first) + * --------------------- */ + +#endif /* USE_FASTPATH_CHUNKS_FIRST */ + + /* Copy until we have a mismatch */ + BChunkList *chunk_list = bchunk_list_new(bs_mem, data_len_original); + if (cref_match_first != NULL) { + size_t chunk_size_step = 0; + const BChunkRef *cref = chunk_list_reference->chunk_refs.first; + while (true) { + BChunk *chunk = cref->link; + chunk_size_step += chunk->data_len; + bchunk_list_append_only(bs_mem, chunk_list, chunk); + ASSERT_CHUNKLIST_SIZE(chunk_list, chunk_size_step); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + if (cref == cref_match_first) { + break; + } + else { + cref = cref->next; + } + } + /* happens when bytes are removed from the end of the array */ + if (chunk_size_step == data_len_original) { + return chunk_list; + } + + i_prev = chunk_size_step; + } + else { + i_prev = 0; + } + + /* ------------------------------------------------------------------------ + * Fast-Path for end chunks + * + * Check for trailing chunks + */ + + /* In this case use 'chunk_list_reference_last' to define the last index + * index_match_last = -1 */ + + /* warning, from now on don't use len(data) + * since we want to ignore chunks already matched */ + size_t data_len = data_len_original; +#define data_len_original invalid_usage +#ifdef data_len_original /* quiet warning */ +#endif + + const BChunkRef *chunk_list_reference_last = NULL; + +#ifdef USE_FASTPATH_CHUNKS_LAST + if (!BLI_listbase_is_empty(&chunk_list_reference->chunk_refs)) { + const BChunkRef *cref = chunk_list_reference->chunk_refs.last; + while ((cref->prev != NULL) && + (cref != cref_match_first) && + (cref->link->data_len <= data_len - i_prev)) + { + BChunk *chunk_test = cref->link; + size_t offset = data_len - chunk_test->data_len; + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + data_len = offset; + chunk_list_reference_last = cref; + chunk_list_reference_skip_len += 1; + chunk_list_reference_skip_bytes += cref->link->data_len; + cref = cref->prev; + } + else { + break; + } + } + } + + /* End Fast-Path (last) + * -------------------- */ +#endif /* USE_FASTPATH_CHUNKS_LAST */ + + /* ----------------------------------------------------------------------- + * Check for aligned chunks + * + * This saves a lot of searching, so use simple heuristics to detect aligned arrays. + * (may need to tweak exact method). + */ + + bool use_aligned = false; + +#ifdef USE_ALIGN_CHUNKS_TEST + if (chunk_list->total_size == chunk_list_reference->total_size) { + /* if we're already a quarter aligned */ + if (data_len - i_prev <= chunk_list->total_size / 4) { + use_aligned = true; + } + else { + /* TODO, walk over chunks and check if some arbitrary amount align */ + } + } +#endif /* USE_ALIGN_CHUNKS_TEST */ + + /* End Aligned Chunk Case + * ----------------------- */ + + if (use_aligned) { + /* Copy matching chunks, creates using the same 'layout' as the reference */ + const BChunkRef *cref = cref_match_first ? cref_match_first->next : chunk_list_reference->chunk_refs.first; + while (i_prev != data_len) { + const size_t i = i_prev + cref->link->data_len; + BLI_assert(i != i_prev); + + if ((cref != chunk_list_reference_last) && + bchunk_data_compare(cref->link, data, data_len, i_prev)) + { + bchunk_list_append(info, bs_mem, chunk_list, cref->link); + ASSERT_CHUNKLIST_SIZE(chunk_list, i); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + else { + bchunk_list_append_data(info, bs_mem, chunk_list, &data[i_prev], i - i_prev); + ASSERT_CHUNKLIST_SIZE(chunk_list, i); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + + cref = cref->next; + + i_prev = i; + } + } + else if ((data_len - i_prev >= info->chunk_byte_size) && + (chunk_list_reference->chunk_refs_len >= chunk_list_reference_skip_len) && + (chunk_list_reference->chunk_refs.first != NULL)) + { + + /* -------------------------------------------------------------------- + * Non-Aligned Chunk De-Duplication */ + + /* only create a table if we have at least one chunk to search + * otherwise just make a new one. + * + * Support re-arranged chunks */ + +#ifdef USE_HASH_TABLE_ACCUMULATE + size_t i_table_start = i_prev; + const size_t table_hash_array_len = (data_len - i_prev) / info->chunk_stride; + hash_key *table_hash_array = MEM_mallocN(sizeof(*table_hash_array) * table_hash_array_len, __func__); + hash_array_from_data(info, &data[i_prev], data_len - i_prev, table_hash_array); + + hash_accum(table_hash_array, table_hash_array_len, info->accum_steps); +#else + /* dummy vars */ + uint i_table_start = 0; + hash_key *table_hash_array = NULL; +#endif + + const uint chunk_list_reference_remaining_len = + (chunk_list_reference->chunk_refs_len - chunk_list_reference_skip_len) + 1; + BTableRef *table_ref_stack = MEM_mallocN(chunk_list_reference_remaining_len * sizeof(BTableRef), __func__); + uint table_ref_stack_n = 0; + + const size_t table_len = chunk_list_reference_remaining_len * BCHUNK_HASH_TABLE_MUL; + BTableRef **table = MEM_callocN(table_len * sizeof(*table), __func__); + + /* table_make - inline + * include one matching chunk, to allow for repeating values */ + { +#ifdef USE_HASH_TABLE_ACCUMULATE + const size_t hash_store_len = info->accum_read_ahead_len; + hash_key *hash_store = MEM_mallocN(sizeof(hash_key) * hash_store_len, __func__); +#endif + + const BChunkRef *cref; + size_t chunk_list_reference_bytes_remaining = + chunk_list_reference->total_size - chunk_list_reference_skip_bytes; + + if (cref_match_first) { + cref = cref_match_first; + chunk_list_reference_bytes_remaining += cref->link->data_len; + } + else { + cref = chunk_list_reference->chunk_refs.first; + } + +#ifdef USE_PARANOID_CHECKS + { + size_t test_bytes_len = 0; + const BChunkRef *cr = cref; + while (cr != chunk_list_reference_last) { + test_bytes_len += cr->link->data_len; + cr = cr->next; + } + BLI_assert(test_bytes_len == chunk_list_reference_bytes_remaining); + } +#endif + + while ((cref != chunk_list_reference_last) && + (chunk_list_reference_bytes_remaining >= info->accum_read_ahead_bytes)) + { + hash_key key = key_from_chunk_ref(info, cref + +#ifdef USE_HASH_TABLE_ACCUMULATE + , hash_store, hash_store_len +#endif + ); + size_t key_index = (size_t)(key % (hash_key)table_len); + BTableRef *tref_prev = table[key_index]; + BLI_assert(table_ref_stack_n < chunk_list_reference_remaining_len); + BTableRef *tref = &table_ref_stack[table_ref_stack_n++]; + tref->cref = cref; + tref->next = tref_prev; + table[key_index] = tref; + + chunk_list_reference_bytes_remaining -= cref->link->data_len; + cref = cref->next; + } + + BLI_assert(table_ref_stack_n <= chunk_list_reference_remaining_len); + +#ifdef USE_HASH_TABLE_ACCUMULATE + MEM_freeN(hash_store); +#endif + } + /* done making the table */ + + BLI_assert(i_prev <= data_len); + for (size_t i = i_prev; i < data_len; ) { + /* Assumes exiting chunk isnt a match! */ + + const BChunkRef *cref_found = table_lookup( + info, + table, table_len, i_table_start, + data, data_len, i, table_hash_array); + if (cref_found != NULL) { + BLI_assert(i < data_len); + if (i != i_prev) { + size_t i_step = MIN2(i_prev + info->chunk_byte_size, data_len); + BLI_assert(i_step <= data_len); + + while (i_prev != i) { + i_step = MIN2(i_step, i); + const ubyte *data_slice = &data[i_prev]; + const size_t data_slice_len = i_step - i_prev; + /* First add all previous chunks! */ + i_prev += data_slice_len; + bchunk_list_append_data(info, bs_mem, chunk_list, data_slice, data_slice_len); + BLI_assert(i_prev <= data_len); + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + i_step += info->chunk_byte_size; + } + } + + /* now add the reference chunk */ + { + BChunk *chunk_found = cref_found->link; + i += chunk_found->data_len; + bchunk_list_append(info, bs_mem, chunk_list, chunk_found); + } + i_prev = i; + BLI_assert(i_prev <= data_len); + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + /* its likely that the next chunk in the list will be a match, so check it! */ + while ((cref_found->next != NULL) && + (cref_found->next != chunk_list_reference_last)) + { + cref_found = cref_found->next; + BChunk *chunk_found = cref_found->link; + + if (bchunk_data_compare(chunk_found, data, data_len, i_prev)) { + /* may be useful to remove table data, assuming we dont have repeating memory + * where it would be useful to re-use chunks. */ + i += chunk_found->data_len; + bchunk_list_append(info, bs_mem, chunk_list, chunk_found); + /* chunk_found may be freed! */ + i_prev = i; + BLI_assert(i_prev <= data_len); + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + else { + break; + } + } + } + else { + i = i + info->chunk_stride; + } + } + +#ifdef USE_HASH_TABLE_ACCUMULATE + MEM_freeN(table_hash_array); +#endif + MEM_freeN(table); + MEM_freeN(table_ref_stack); + + /* End Table Lookup + * ---------------- */ + } + + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + /* ----------------------------------------------------------------------- + * No Duplicates to copy, write new chunks + * + * Trailing chunks, no matches found in table lookup above. + * Write all new data. */ + BLI_assert(i_prev <= data_len); + while (i_prev != data_len) { + size_t i = i_prev + info->chunk_byte_size; + i = MIN2(i, data_len); + BLI_assert(i != i_prev); + bchunk_list_append_data(info, bs_mem, chunk_list, &data[i_prev], i - i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + i_prev = i; + } + + BLI_assert(i_prev == data_len); + +#ifdef USE_FASTPATH_CHUNKS_LAST + if (chunk_list_reference_last != NULL) { + /* write chunk_list_reference_last since it hasn't been written yet */ + const BChunkRef *cref = chunk_list_reference_last; + while (cref != NULL) { + BChunk *chunk = cref->link; + // BLI_assert(bchunk_data_compare(chunk, data, data_len, i_prev)); + i_prev += chunk->data_len; + /* use simple since we assume the references chunks have already been sized correctly. */ + bchunk_list_append_only(bs_mem, chunk_list, chunk); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + cref = cref->next; + } + } +#endif + +#undef data_len_original + + BLI_assert(i_prev == data_len_original); + + /* check we're the correct size and that we didn't accidentally modify the reference */ + ASSERT_CHUNKLIST_SIZE(chunk_list, data_len_original); + ASSERT_CHUNKLIST_SIZE(chunk_list_reference, chunk_list_reference->total_size); + + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + return chunk_list; +} +/* end private API */ + +/** \} */ + + +/** \name Main Array Storage API + * \{ */ + + +/** + * Create a new array store, which can store any number of arrays + * as long as their stride matches. + * + * \param stride: ``sizeof()`` each element, + * + * \note while a stride of ``1`` will always work, + * its less efficient since duplicate chunks of memory will be searched + * at positions unaligned with the array data. + * + * \param chunk_count: Number of elements to split each chunk into. + * - A small value increases the ability to de-duplicate chunks, + * but adds overhead by increasing the number of chunks to look-up when searching for duplicates, + * as well as some overhead constructing the original array again, with more calls to ``memcpy``. + * - Larger values reduce the *book keeping* overhead, + * but increase the chance a small, isolated change will cause a larger amount of data to be duplicated. + * + * \return A new array store, to be freed with #BLI_array_store_destroy. + */ +BArrayStore *BLI_array_store_create( + uint stride, + uint chunk_count) +{ + BArrayStore *bs = MEM_callocN(sizeof(BArrayStore), __func__); + + bs->info.chunk_stride = stride; + bs->info.chunk_count = chunk_count; + + bs->info.chunk_byte_size = chunk_count * stride; +#ifdef USE_MERGE_CHUNKS + bs->info.chunk_byte_size_min = MAX2(1u, chunk_count / BCHUNK_SIZE_MIN_DIV) * stride; +#endif + +#ifdef USE_HASH_TABLE_ACCUMULATE + bs->info.accum_steps = BCHUNK_HASH_TABLE_ACCUMULATE_STEPS - 1; + /* Triangle number, identifying now much read-ahead we need: + * https://en.wikipedia.org/wiki/Triangular_number (+ 1) */ + bs->info.accum_read_ahead_len = (uint)((((bs->info.accum_steps * (bs->info.accum_steps + 1))) / 2) + 1); + bs->info.accum_read_ahead_bytes = bs->info.accum_read_ahead_len * stride; +#else + bs->info.accum_read_ahead_bytes = BCHUNK_HASH_LEN * stride; +#endif + + bs->memory.chunk_list = BLI_mempool_create(sizeof(BChunkList), 0, 512, BLI_MEMPOOL_NOP); + bs->memory.chunk_ref = BLI_mempool_create(sizeof(BChunkRef), 0, 512, BLI_MEMPOOL_NOP); + /* allow iteration to simplify freeing, otherwise its not needed + * (we could loop over all states as an alternative). */ + bs->memory.chunk = BLI_mempool_create(sizeof(BChunk), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + + return bs; +} + +static void array_store_free_data(BArrayStore *bs) +{ + /* free chunk data */ + { + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + BLI_assert(chunk->users > 0); + MEM_freeN((void *)chunk->data); + } + } + + /* free states */ + for (BArrayState *state = bs->states.first, *state_next; state; state = state_next) { + state_next = state->next; + MEM_freeN(state); + } +} + +/** + * Free the #BArrayStore, including all states and chunks. + */ +void BLI_array_store_destroy( + BArrayStore *bs) +{ + array_store_free_data(bs); + + BLI_mempool_destroy(bs->memory.chunk_list); + BLI_mempool_destroy(bs->memory.chunk_ref); + BLI_mempool_destroy(bs->memory.chunk); + + MEM_freeN(bs); +} + +/** + * Clear all contents, allowing reuse of \a bs. + */ +void BLI_array_store_clear( + BArrayStore *bs) +{ + array_store_free_data(bs); + + BLI_listbase_clear(&bs->states); + + BLI_mempool_clear(bs->memory.chunk_list); + BLI_mempool_clear(bs->memory.chunk_ref); + BLI_mempool_clear(bs->memory.chunk); +} + +/** \name BArrayStore Statistics + * \{ */ + +/** + * \return the total amount of memory that would be used by getting the arrays for all states. + */ +size_t BLI_array_store_calc_size_expanded_get( + const BArrayStore *bs) +{ + size_t size_accum = 0; + for (const BArrayState *state = bs->states.first; state; state = state->next) { + size_accum += state->chunk_list->total_size; + } + return size_accum; +} + +/** + * \return the amount of memory used by all #BChunk.data + * (duplicate chunks are only counted once). + */ +size_t BLI_array_store_calc_size_compacted_get( + const BArrayStore *bs) +{ + size_t size_total = 0; + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + BLI_assert(chunk->users > 0); + size_total += (size_t)chunk->data_len; + } + return size_total; +} + +/** \} */ + + +/** \name BArrayState Access + * \{ */ + +/** + * + * \param data: Data used to create + * \param state_reference: The state to use as a reference when adding the new state, + * typically this is the previous state, + * however it can be any previously created state from this \a bs. + * + * \return The new state, which is used by the caller as a handle to get back the contents of \a data. + * This may be removed using #BLI_array_store_state_remove, + * otherwise it will be removed with #BLI_array_store_destroy. + */ +BArrayState *BLI_array_store_state_add( + BArrayStore *bs, + const void *data, const size_t data_len, + const BArrayState *state_reference) +{ + /* ensure we're aligned to the stride */ + BLI_assert((data_len % bs->info.chunk_stride) == 0); + +#ifdef USE_PARANOID_CHECKS + if (state_reference) { + BLI_assert(BLI_findindex(&bs->states, state_reference) != -1); + } +#endif + + BChunkList *chunk_list; + if (state_reference) { + chunk_list = bchunk_list_from_data_merge( + &bs->info, &bs->memory, + (const ubyte *)data, data_len, + /* re-use reference chunks */ + state_reference->chunk_list); + } + else { + chunk_list = bchunk_list_new(&bs->memory, data_len); + bchunk_list_fill_from_array( + &bs->info, &bs->memory, + chunk_list, + (const ubyte *)data, data_len); + } + + chunk_list->users += 1; + + BArrayState *state = MEM_callocN(sizeof(BArrayState), __func__); + state->chunk_list = chunk_list; + + BLI_addtail(&bs->states, state); + +#ifdef USE_PARANOID_CHECKS + { + size_t data_test_len; + void *data_test = BLI_array_store_state_data_get_alloc(state, &data_test_len); + BLI_assert(data_test_len == data_len); + BLI_assert(memcmp(data_test, data, data_len) == 0); + MEM_freeN(data_test); + } +#endif + + return state; +} + +/** + * Remove a state and free any unused #BChunk data. + * + * The states can be freed in any order. + */ +void BLI_array_store_state_remove( + BArrayStore *bs, + BArrayState *state) +{ +#ifdef USE_PARANOID_CHECKS + BLI_assert(BLI_findindex(&bs->states, state) != -1); +#endif + + bchunk_list_decref(&bs->memory, state->chunk_list); + BLI_remlink(&bs->states, state); + + MEM_freeN(state); +} + +/** + * \return the expanded size of the array, + * use this to know how much memory to allocate #BLI_array_store_state_data_get's argument. + */ +size_t BLI_array_store_state_size_get( + BArrayState *state) +{ + return state->chunk_list->total_size; +} + +/** + * Fill in existing allocated memory with the contents of \a state. + */ +void BLI_array_store_state_data_get( + BArrayState *state, + void *data) +{ +#ifdef USE_PARANOID_CHECKS + size_t data_test_len = 0; + for (BChunkRef *cref = state->chunk_list->chunk_refs.first; cref; cref = cref->next) { + data_test_len += cref->link->data_len; + } + BLI_assert(data_test_len == state->chunk_list->total_size); +#endif + + ubyte *data_step = (ubyte *)data; + for (BChunkRef *cref = state->chunk_list->chunk_refs.first; cref; cref = cref->next) { + BLI_assert(cref->link->users > 0); + memcpy(data_step, cref->link->data, cref->link->data_len); + data_step += cref->link->data_len; + } +} + +/** + * Allocate an array for \a state and return it. + */ +void *BLI_array_store_state_data_get_alloc( + BArrayState *state, + size_t *r_data_len) +{ + void *data = MEM_mallocN(state->chunk_list->total_size, __func__); + BLI_array_store_state_data_get(state, data); + *r_data_len = state->chunk_list->total_size; + return data; +} + +/** \} */ + + +/** \name Debigging API (for testing). + * \{ */ + +/* only for test validation */ +static size_t bchunk_list_size(const BChunkList *chunk_list) +{ + size_t total_size = 0; + for (BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + total_size += cref->link->data_len; + } + return total_size; +} + +bool BLI_array_store_is_valid( + BArrayStore *bs) +{ + bool ok = true; + + /* Check Length + * ------------ */ + + for (BArrayState *state = bs->states.first; state; state = state->next) { + BChunkList *chunk_list = state->chunk_list; + if (!(bchunk_list_size(chunk_list) == chunk_list->total_size)) { + return false; + } + } + + { + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + if (!(MEM_allocN_len(chunk->data) >= chunk->data_len)) { + return false; + } + } + } + + /* Check User Count & Lost References + * ---------------------------------- */ + { + GHashIterator gh_iter; + +#define GHASH_PTR_ADD_USER(gh, pt) \ + { \ + void **val; \ + if (BLI_ghash_ensure_p((gh), (pt), &val)) { \ + *((int *)val) += 1; \ + } \ + else { \ + *((int *)val) = 1; \ + } \ + } ((void)0) + + + /* count chunk_list's */ + int totrefs = 0; + GHash *chunk_list_map = BLI_ghash_ptr_new(__func__); + for (BArrayState *state = bs->states.first; state; state = state->next) { + GHASH_PTR_ADD_USER(chunk_list_map, state->chunk_list); + } + GHASH_ITER (gh_iter, chunk_list_map) { + const struct BChunkList *chunk_list = BLI_ghashIterator_getKey(&gh_iter); + const int users = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); + if (!(chunk_list->users == users)) { + ok = false; + goto user_finally; + } + } + if (!(BLI_mempool_count(bs->memory.chunk_list) == (int)BLI_ghash_size(chunk_list_map))) { + ok = false; + goto user_finally; + } + + /* count chunk's */ + GHash *chunk_map = BLI_ghash_ptr_new(__func__); + GHASH_ITER (gh_iter, chunk_list_map) { + const struct BChunkList *chunk_list = BLI_ghashIterator_getKey(&gh_iter); + for (const BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + GHASH_PTR_ADD_USER(chunk_map, cref->link); + totrefs += 1; + } + } + if (!(BLI_mempool_count(bs->memory.chunk) == (int)BLI_ghash_size(chunk_map))) { + ok = false; + goto user_finally; + } + if (!(BLI_mempool_count(bs->memory.chunk_ref) == totrefs)) { + ok = false; + goto user_finally; + } + + GHASH_ITER (gh_iter, chunk_map) { + const struct BChunk *chunk = BLI_ghashIterator_getKey(&gh_iter); + const int users = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); + if (!(chunk->users == users)) { + ok = false; + goto user_finally; + } + } + +#undef GHASH_PTR_ADD_USER + +user_finally: + BLI_ghash_free(chunk_list_map, NULL, NULL); + BLI_ghash_free(chunk_map, NULL, NULL); + } + + + return ok; + /* TODO, dangling pointer checks */ +} + +/** \} */ 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..370e8bb0035 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 } @@ -4972,3 +4988,37 @@ int is_quad_flip_v3(const float v1[3], const float v2[3], const float v3[3], con return ret; } + +/** + * Return the value which the distance between points will need to be scaled by, + * to define a handle, given both points are on a perfect circle. + * + * Use when we want a bezier curve to match a circle as closely as possible. + * + * \note the return value will need to be divided by 0.75 for correct results. + */ +float cubic_tangent_factor_circle_v3(const float tan_l[3], const float tan_r[3]) +{ + BLI_ASSERT_UNIT_V3(tan_l); + BLI_ASSERT_UNIT_V3(tan_r); + + const float eps = 1e-7f; + const float tan_dot = dot_v3v3(tan_l, tan_r); + if (tan_dot > 1.0f - eps) { + /* no angle difference (use fallback, length wont make any difference) */ + return (1.0f / 3.0f) * 0.75f; + } + else if (tan_dot < -1.0f + eps) { + /* parallele tangents (half-circle) */ + return (1.0f / 2.0f); + } + else { + /* non-aligned tangents, calculate handle length */ + const float angle = acosf(tan_dot) / 2.0f; + + /* could also use 'angle_sin = len_vnvn(tan_l, tan_r, dims) / 2.0' */ + const float angle_sin = sinf(angle); + const float angle_cos = cosf(angle); + return ((1.0f - angle_cos) / (angle_sin * 2.0f)) / angle_sin; + } +} 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..58475b7f835 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,16 +852,17 @@ 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); } for (i = start; i < stop; ++i) { - func_ex(userdata, userdata_chunk, i, 0); + func_ex(userdata, userdata_chunk_local, i, 0); + } + + if (func_finalize) { + func_finalize(userdata, userdata_chunk_local); } MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size); @@ -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 1a11e78ec32..10f603c9f6b 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4933,6 +4933,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); @@ -6168,6 +6171,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; } } } @@ -6937,6 +6945,7 @@ static bool direct_link_screen(FileData *fd, bScreen *sc) /* render can be quite heavy, set to solid on load */ if (v3d->drawtype == OB_RENDER) v3d->drawtype = OB_SOLID; + v3d->prev_drawtype = OB_SOLID; if (v3d->fx_settings.dof) v3d->fx_settings.dof = newdataadr(fd, v3d->fx_settings.dof); @@ -7464,9 +7473,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); @@ -7477,6 +7490,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); @@ -7494,7 +7515,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; } } @@ -8223,7 +8244,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 533f62b088b..d81cd6a6c60 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)) { @@ -1063,6 +1098,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) { @@ -1095,5 +1139,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 f2b39391eee..d3c418dd72c 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..bb61f66e267 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,20 +794,20 @@ 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; me->totselect = BLI_listbase_count(&(bm->selected)); - if (me->mselect) MEM_freeN(me->mselect); - - me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); - + MEM_SAFE_FREE(me->mselect); + if (me->totselect != 0) { + me->mselect = MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + } for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { if (selected->htype == BM_VERT) { 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_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 5a7788c0b62..f7e3622e53c 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -345,6 +345,36 @@ static EdgeHalf *next_bev(BevVert *bv, EdgeHalf *from_e) return NULL; } +/* return count of edges between e1 and e2 when going around bv CCW */ +static int count_ccw_edges_between(EdgeHalf *e1, EdgeHalf *e2) +{ + int cnt = 0; + EdgeHalf *e = e1; + + do { + if (e == e2) + break; + e = e->next; + cnt++; + } while (e != e1); + return cnt; +} + +/* Assume bme1 and bme2 both share some vert. Do they share a face? + * If they share a face then there is some loop around bme1 that is in a face + * where the next or previous edge in the face must be bme2. */ +static bool edges_face_connected_at_vert(BMEdge *bme1, BMEdge *bme2) +{ + BMLoop *l; + BMIter iter; + + BM_ITER_ELEM(l, &iter, bme1, BM_LOOPS_OF_EDGE) { + if (l->prev->e == bme2 || l->next->e == bme2) + return true; + } + return false; +} + /* Return a good representative face (for materials, etc.) for faces * created around/near BoundVert v. * Sometimes care about a second choice, if there is one. @@ -1557,7 +1587,7 @@ static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool constr if (construct) { v = add_new_bound_vert(bp->mem_arena, vm, co); v->efirst = v->elast = e; - e->leftv = v; + e->leftv = e->rightv = v; } else { adjust_bound_vert(e->leftv, co); @@ -1637,7 +1667,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf v->efirst = e->prev; v->elast = v->ebev = e; e->leftv = v; - e->prev->leftv = v; + e->prev->leftv = e->prev->rightv = v; } else { adjust_bound_vert(e->leftv, co); @@ -1648,7 +1678,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf v = add_new_bound_vert(mem_arena, vm, co); v->efirst = e->prev; v->elast = e; - e->leftv = v; + e->leftv = e->rightv = v; e->prev->rightv = v; } else { @@ -1661,7 +1691,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf if (construct) { v = add_new_bound_vert(mem_arena, vm, co); v->efirst = v->elast = e; - e->leftv = v; + e->leftv = e->rightv = v; } else { adjust_bound_vert(e->leftv, co); @@ -3237,6 +3267,11 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv) if (!weld) create_mesh_bmvert(bm, vm, i, 0, k, bv->v); } + else if (n == 2 && !v->ebev && vm->mesh_kind != M_ADJ) { + /* case of one edge beveled and this is the v without ebev */ + /* want to copy the verts from other v, in reverse order */ + copy_mesh_vert(vm, i, 0, k, 1 - i, 0, ns - k); + } } } while ((v = v->next) != vm->boundstart); @@ -3305,6 +3340,219 @@ static float edge_face_angle(EdgeHalf *e) #define BM_BEVEL_EDGE_TAG_DISABLE(bme) BM_ELEM_API_FLAG_DISABLE( (bme), _FLAG_OVERLAP) #define BM_BEVEL_EDGE_TAG_TEST(bme) BM_ELEM_API_FLAG_TEST( (bme), _FLAG_OVERLAP) +/* Try to extend the bv->edges[] array beyond i by finding more successor edges. + * This is a possibly exponential-time search, but it is only exponential in the number + * of "internal faces" at a vertex -- i.e., faces that bridge between the edges that naturally + * form a manifold cap around bv. It is rare to have more than one of these, so unlikely + * that the exponential time case will be hit in practice. + * Returns the new index i' where bv->edges[i'] ends the best path found. + * The path will have the tags of all of its edges set. */ +static int bevel_edge_order_extend(BMesh *bm, BevVert *bv, int i) +{ + BMEdge *bme, *bme2, *nextbme; + BMLoop *l; + BMIter iter; + int j, tryj, bestj, nsucs, sucindex, k; + BMEdge **sucs = NULL; + BMEdge **save_path = NULL; + BLI_array_staticdeclare(sucs, 4); /* likely very few faces attached to same edge */ + BLI_array_staticdeclare(save_path, BM_DEFAULT_NGON_STACK_SIZE); + + bme = bv->edges[i].e; + /* fill sucs with all unmarked edges of bmes */ + BM_ITER_ELEM(l, &iter, bme, BM_LOOPS_OF_EDGE) { + bme2 = (l->v == bv->v) ? l->prev->e : l->next->e; + if (!BM_BEVEL_EDGE_TAG_TEST(bme2)) { + BLI_array_append(sucs, bme2); + } + } + nsucs = BLI_array_count(sucs); + + bestj = j = i; + for (sucindex = 0; sucindex < nsucs; sucindex++) { + nextbme = sucs[sucindex]; + BLI_assert(nextbme != NULL); + BLI_assert(!BM_BEVEL_EDGE_TAG_TEST(nextbme)); + BLI_assert(j + 1 < bv->edgecount); + bv->edges[j + 1].e = nextbme; + BM_BEVEL_EDGE_TAG_ENABLE(nextbme); + tryj = bevel_edge_order_extend(bm, bv, j + 1); + if (tryj > bestj || (tryj == bestj && edges_face_connected_at_vert(bv->edges[tryj].e, bv->edges[0].e))) { + bestj = tryj; + BLI_array_empty(save_path); + for (k = j + 1; k <= bestj; k++) { + BLI_array_append(save_path, bv->edges[k].e); + } + } + /* now reset to path only-going-to-j state */ + for (k = j + 1; k <= tryj; k++) { + BM_BEVEL_EDGE_TAG_DISABLE(bv->edges[k].e); + bv->edges[k].e = NULL; + } + } + /* at this point we should be back at invariant on entrance: path up to j */ + if (bestj > j) { + /* save_path should have from j + 1 to bestj inclusive edges to add to edges[] before returning */ + for (k = j + 1; k <= bestj; k++) { + BLI_assert(save_path[k - (j + 1)] != NULL); + bv->edges[k].e = save_path[k - (j + 1)]; + BM_BEVEL_EDGE_TAG_ENABLE(bv->edges[k].e); + } + } + BLI_array_free(sucs); + BLI_array_free(save_path); + return bestj; +} + +/* See if we have usual case for bevel edge order: + * there is an ordering such that all the faces are between + * successive edges and form a manifold "cap" at bv. + * If this is the case, set bv->edges to such an order + * and return true; else return unmark any partial path and return false. + * Assume the first edge is already in bv->edges[0].e and it is tagged. */ +#ifdef FASTER_FASTORDER +/* The alternative older code is O(n^2) where n = # of edges incident to bv->v. + * This implementation is O(n * m) where m = average number of faces attached to an edge incident to bv->v, + * which is almost certainly a small constant except in very strange cases. But this code produces different + * choices of ordering than the legacy system, leading to differences in vertex orders etc. in user models, + * so for now will continue to use the legacy code. */ +static bool fast_bevel_edge_order(BevVert *bv) +{ + int j, k, nsucs; + BMEdge *bme, *bme2, *bmenext; + BMIter iter; + BMLoop *l; + + for (j = 1; j < bv->edgecount; j++) { + bme = bv->edges[j - 1].e; + bmenext = NULL; + nsucs = 0; + BM_ITER_ELEM(l, &iter, bme, BM_LOOPS_OF_EDGE) { + bme2 = (l->v == bv->v) ? l->prev->e : l->next->e; + if (!BM_BEVEL_EDGE_TAG_TEST(bme2)) { + nsucs++; + if (bmenext == NULL) + bmenext = bme2; + } + } + if (nsucs == 0 || (nsucs == 2 && j != 1) || nsucs > 2 || + (j == bv->edgecount - 1 && !edges_face_connected_at_vert(bmenext, bv->edges[0].e))) + { + for (k = 1; k < j; k++) { + BM_BEVEL_EDGE_TAG_DISABLE(bv->edges[k].e); + bv->edges[k].e = NULL; + } + return false; + } + bv->edges[j].e = bmenext; + BM_BEVEL_EDGE_TAG_ENABLE(bmenext); + } + return true; +} +#else +static bool fast_bevel_edge_order(BevVert *bv) +{ + BMEdge *bme, *bme2, *first_suc; + BMIter iter, iter2; + BMFace *f; + EdgeHalf *e; + int i, k, ntot, num_shared_face; + + ntot = bv->edgecount; + + /* add edges to bv->edges in order that keeps adjacent edges sharing + * a unique face, if possible */ + e = &bv->edges[0]; + bme = e->e; + if (!bme->l) + return false; + for (i = 1; i < ntot; i++) { + /* find an unflagged edge bme2 that shares a face f with previous bme */ + num_shared_face = 0; + first_suc = NULL; /* keep track of first successor to match legacy behavior */ + BM_ITER_ELEM (bme2, &iter, bv->v, BM_EDGES_OF_VERT) { + if (BM_BEVEL_EDGE_TAG_TEST(bme2)) + continue; + BM_ITER_ELEM (f, &iter2, bme2, BM_FACES_OF_EDGE) { + if (BM_face_edge_share_loop(f, bme)) { + num_shared_face++; + if (first_suc == NULL) + first_suc = bme2; + } + } + if (num_shared_face >= 3) + break; + } + if (num_shared_face == 1 || (i == 1 && num_shared_face == 2)) { + e = &bv->edges[i]; + e->e = bme = first_suc; + BM_BEVEL_EDGE_TAG_ENABLE(bme); + } + else { + for (k = 1; k < i; k++) { + BM_BEVEL_EDGE_TAG_DISABLE(bv->edges[k].e); + bv->edges[k].e = NULL; + } + return false; + } + } + return true; +} +#endif + +/* Fill in bv->edges with a good ordering of non-wire edges around bv->v. + * Use only edges where BM_BEVEL_EDGE_TAG is disabled so far + * (if edge beveling, others are wire). + * first_bme is a good edge to start with.*/ +static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme) +{ + BMEdge *bme, *bme2; + BMIter iter; + BMFace *f; + EdgeHalf *e; + EdgeHalf *e2; + BMLoop *l; + int i, ntot; + + ntot = bv->edgecount; + i = 0; + for (;;) { + BLI_assert(first_bme != NULL); + bv->edges[i].e = first_bme; + BM_BEVEL_EDGE_TAG_ENABLE(first_bme); + if (fast_bevel_edge_order(bv)) + break; + i = bevel_edge_order_extend(bm, bv, i); + i++; + if (i >= bv->edgecount) + break; + /* Not done yet: find a new first_bme */ + first_bme = NULL; + BM_ITER_ELEM(bme, &iter, bv->v, BM_EDGES_OF_VERT) { + if (BM_BEVEL_EDGE_TAG_TEST(bme)) + continue; + if (!first_bme) + first_bme = bme; + if (BM_edge_face_count(bme) == 1) { + first_bme = bme; + break; + } + } + } + /* now fill in the faces ... */ + for (i = 0; i < ntot; i++) { + e = &bv->edges[i]; + e2 = (i == bv->edgecount - 1) ? &bv->edges[0] : &bv->edges[i + 1]; + bme = e->e; + bme2 = e2->e; + BM_ITER_ELEM(l, &iter, bme, BM_LOOPS_OF_EDGE) { + f = l->f; + if ((l->prev->e == bme2 || l->next->e == bme2) && !e->fnext && !e2->fprev) + e->fnext = e2->fprev = f; + } + } +} + /* * Construction around the vertex */ @@ -3312,13 +3560,12 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) { BMEdge *bme; BevVert *bv; - BMEdge *bme2, *unflagged_bme, *first_bme; - BMFace *f; + BMEdge *first_bme; BMVert *v1, *v2; - BMIter iter, iter2; + BMIter iter; EdgeHalf *e; float weight, z; - int i, found_shared_face, ccw_test_sum; + int i, ccw_test_sum; int nsel = 0; int ntot = 0; int nwire = 0; @@ -3398,47 +3645,12 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) } BLI_ghash_insert(bp->vert_hash, v, bv); - /* add edges to bv->edges in order that keeps adjacent edges sharing - * a face, if possible */ - i = 0; + find_bevel_edge_order(bm, bv, first_bme); - bme = first_bme; - BM_BEVEL_EDGE_TAG_ENABLE(bme); - e = &bv->edges[0]; - e->e = bme; + /* fill in other attributes of EdgeHalfs */ for (i = 0; i < ntot; i++) { - if (i > 0) { - /* find an unflagged edge bme2 that shares a face f with previous bme */ - found_shared_face = 0; - unflagged_bme = NULL; - BM_ITER_ELEM (bme2, &iter, v, BM_EDGES_OF_VERT) { - if (BM_BEVEL_EDGE_TAG_TEST(bme2)) - continue; - if (!unflagged_bme) - unflagged_bme = bme2; - if (!bme->l) - continue; - BM_ITER_ELEM (f, &iter2, bme2, BM_FACES_OF_EDGE) { - if (BM_face_edge_share_loop(f, bme)) { - found_shared_face = 1; - break; - } - } - if (found_shared_face) - break; - } - e = &bv->edges[i]; - if (found_shared_face) { - e->e = bme2; - e->fprev = f; - bv->edges[i - 1].fnext = f; - } - else { - e->e = unflagged_bme; - } - } + e = &bv->edges[i]; bme = e->e; - BM_BEVEL_EDGE_TAG_ENABLE(bme); if (BM_elem_flag_test(bme, BM_ELEM_TAG) && !bp->vertex_only) { e->is_bev = true; e->seg = bp->seg; @@ -3449,16 +3661,6 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) } e->is_rev = (bme->v2 == v); } - /* find wrap-around shared face */ - BM_ITER_ELEM (f, &iter2, bme, BM_FACES_OF_EDGE) { - if (bv->edges[0].e->l && BM_face_edge_share_loop(f, bv->edges[0].e)) { - if (bv->edges[0].fnext == f) - continue; /* if two shared faces, want the other one now */ - bv->edges[ntot - 1].fnext = f; - bv->edges[0].fprev = f; - break; - } - } /* now done with tag flag */ BM_ITER_ELEM (bme, &iter, v, BM_EDGES_OF_VERT) { @@ -3586,6 +3788,7 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) VMesh *vm; int i, k, n; bool do_rebuild = false; + bool go_ccw, corner3special; BMVert *bmv; BMEdge *bme, *bme_new, *bme_prev; BMFace *f_new; @@ -3600,47 +3803,88 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { lprev = l->prev; bv = find_bevvert(bp, l->v); + vm = bv->vmesh; e = find_edge_half(bv, l->e); bme = e->e; eprev = find_edge_half(bv, lprev->e); BLI_assert(e != NULL && eprev != NULL); - vstart = eprev->leftv; - if (e->is_bev) - vend = e->rightv; - else + + /* which direction around our vertex do we travel to match orientation of f? */ + if (e->prev == eprev) { + if (eprev->prev == e) { + /* valence 2 vertex: use f is one of e->fnext or e->fprev to break tie */ + go_ccw = (e->fnext != f); + } + else { + go_ccw = true; /* going ccw around bv to trace this corner */ + } + } + else if (eprev->prev == e) { + go_ccw = false; /* going cw around bv to trace this corner */ + } + else { + /* edges in face are non-contiguous in our ordering around bv. + * Which way should we go when going from eprev to e? */ + if (count_ccw_edges_between(eprev, e) < count_ccw_edges_between(e, eprev)) { + /* go counterclockewise from eprev to e */ + go_ccw = true; + } + else { + /* go clockwise from eprev to e */ + go_ccw = false; + } + } + if (go_ccw) { + vstart = eprev->rightv; vend = e->leftv; + } + else { + vstart = eprev->leftv; + vend = e->rightv; + } + BLI_assert(vstart != NULL && vend != NULL); v = vstart; - vm = bv->vmesh; BLI_array_append(vv, v->nv.v); BLI_array_append(ee, bme); + /* check for special case: multisegment 3rd face opposite a beveled edge with no vmesh */ + corner3special = (vm->mesh_kind == M_NONE && v->ebev != e && v->ebev != eprev); while (v != vend) { - if (vm->mesh_kind == M_NONE && v->ebev && v->ebev->seg > 1 && v->ebev != e && v->ebev != eprev) { - /* case of 3rd face opposite a beveled edge, with no vmesh */ - i = v->index; - e = v->ebev; - for (k = 1; k < e->seg; k++) { - bmv = mesh_vert(vm, i, 0, k)->v; - BLI_array_append(vv, bmv); - BLI_array_append(ee, bme); - /* may want to merge UVs of these later */ - if (!e->is_seam) - BLI_array_append(vv_fix, bmv); + if (go_ccw) { + if (vm->seg > 1) { + if (vm->mesh_kind == M_ADJ || bp->vertex_only || corner3special) { + i = v->index; + for (k = 1; k < vm->seg; k++) { + bmv = mesh_vert(vm, i, 0, k)->v; + BLI_array_append(vv, bmv); + BLI_array_append(ee, bme); /* TODO: maybe better edge here */ + if (corner3special && v->ebev && !v->ebev->is_seam) + BLI_array_append(vv_fix, bmv); + } + } } + v = v->next; } - else if ((vm->mesh_kind == M_ADJ || bp->vertex_only) && vm->seg > 1 && !e->is_bev && !eprev->is_bev) { - BLI_assert(v->prev == vend); - i = vend->index; - for (k = vm->seg - 1; k > 0; k--) { - bmv = mesh_vert(vm, i, 0, k)->v; - BLI_array_append(vv, bmv); - BLI_array_append(ee, bme); + else { + /* going cw */ + if (vm->seg > 1) { + if (vm->mesh_kind == M_ADJ || bp->vertex_only || + (vm->mesh_kind == M_NONE && v->ebev != e && v->ebev != eprev)) + { + i = v->prev->index; + for (k = vm->seg - 1; k > 0; k--) { + bmv = mesh_vert(vm, i, 0, k)->v; + BLI_array_append(vv, bmv); + BLI_array_append(ee, bme); + if (corner3special && v->ebev && !v->ebev->is_seam) + BLI_array_append(vv_fix, bmv); + } + } } + v = v->prev; } - v = v->prev; BLI_array_append(vv, v->nv.v); BLI_array_append(ee, bme); } - do_rebuild = true; } else { 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/ArmatureExporter.cpp b/source/blender/collada/ArmatureExporter.cpp index 36ab85b9b5b..4f5cf83f5ca 100644 --- a/source/blender/collada/ArmatureExporter.cpp +++ b/source/blender/collada/ArmatureExporter.cpp @@ -67,12 +67,19 @@ void ArmatureExporter::add_armature_bones(Object *ob_arm, Scene *sce, std::list<Object *>& child_objects) { // write bone nodes + + bArmature * armature = (bArmature *)ob_arm->data; + ED_armature_to_edit(armature); + bArmature *arm = (bArmature *)ob_arm->data; for (Bone *bone = (Bone *)arm->bonebase.first; bone; bone = bone->next) { // start from root bones if (!bone->parent) add_bone_node(bone, ob_arm, sce, se, child_objects); } + + ED_armature_from_edit(armature); + ED_armature_edit_free(armature); } void ArmatureExporter::write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone) @@ -167,12 +174,30 @@ void ArmatureExporter::add_bone_node(Bone *bone, Object *ob_arm, Scene *sce, node.setNodeName(node_name); node.setNodeSid(node_sid); -#if 0 - if (BLI_listbase_is_empty(&bone->childbase) || BLI_listbase_count_ex(&bone->childbase, 2) == 2) { - add_blender_leaf_bone( bone, ob_arm, node); + if (this->export_settings->use_blender_profile) + { + if (bone->parent) { + if (bone->flag & BONE_CONNECTED) { + node.addExtraTechniqueParameter("blender", "connect", true); + } + } + std::string layers = BoneExtended::get_bone_layers(bone->layer); + node.addExtraTechniqueParameter("blender", "layer", layers); + + bArmature *armature = (bArmature *)ob_arm->data; + EditBone *ebone = bc_get_edit_bone(armature, bone->name); + if (ebone && ebone->roll != 0) + { + node.addExtraTechniqueParameter("blender", "roll", ebone->roll); + } + if (bc_is_leaf_bone(bone)) + { + node.addExtraTechniqueParameter("blender", "tip_x", bone->arm_tail[0] - bone->arm_head[0]); + node.addExtraTechniqueParameter("blender", "tip_y", bone->arm_tail[1] - bone->arm_head[1]); + node.addExtraTechniqueParameter("blender", "tip_z", bone->arm_tail[2] - bone->arm_head[2]); + } } - else { -#endif + node.start(); add_bone_transform(ob_arm, bone, node); @@ -227,25 +252,6 @@ void ArmatureExporter::add_bone_node(Bone *bone, Object *ob_arm, Scene *sce, } } -//#if 1 -void ArmatureExporter::add_blender_leaf_bone(Bone *bone, Object *ob_arm, COLLADASW::Node& node) -{ - node.start(); - - add_bone_transform(ob_arm, bone, node); - - node.addExtraTechniqueParameter("blender", "tip_x", bone->tail[0]); - node.addExtraTechniqueParameter("blender", "tip_y", bone->tail[1]); - node.addExtraTechniqueParameter("blender", "tip_z", bone->tail[2]); - - /*for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - add_bone_node(child, ob_arm, sce, se, child_objects); - }*/ - node.end(); - -} -//#endif - void ArmatureExporter::add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node) { //bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name); diff --git a/source/blender/collada/ArmatureExporter.h b/source/blender/collada/ArmatureExporter.h index 931cc5d2988..883a6aca847 100644 --- a/source/blender/collada/ArmatureExporter.h +++ b/source/blender/collada/ArmatureExporter.h @@ -92,8 +92,6 @@ private: void add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node); - void add_blender_leaf_bone(Bone *bone, Object *ob_arm, COLLADASW::Node& node); - std::string get_controller_id(Object *ob_arm, Object *ob); void write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone); diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp index fd08e1ebfab..fca9b9ffa55 100644 --- a/source/blender/collada/ArmatureImporter.cpp +++ b/source/blender/collada/ArmatureImporter.cpp @@ -50,19 +50,6 @@ static const char *bc_get_joint_name(T *node) return id.size() ? id.c_str() : node->getOriginalId().c_str(); } -static EditBone *get_edit_bone(bArmature * armature, char *name) { - EditBone *eBone; - - for (eBone = (EditBone *)armature->edbo->first; eBone; eBone = eBone->next) { - if (STREQ(name, eBone->name)) - return eBone; - } - - return NULL; - -} - - ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, Scene *sce, const ImportSettings *import_settings) : import_settings(import_settings), @@ -110,7 +97,7 @@ JointData *ArmatureImporter::get_joint_data(COLLADAFW::Node *node); #endif int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBone *parent, int totchild, - float parent_mat[4][4], bArmature *arm) + float parent_mat[4][4], bArmature *arm, std::vector<std::string> &layer_labels) { float mat[4][4]; float joint_inv_bind_mat[4][4]; @@ -157,22 +144,41 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon if (parent) bone->parent = parent; float loc[3], size[3], rot[3][3]; - float angle; - float vec[3] = {0.0f, 0.5f, 0.0f}; - mat4_to_loc_rot_size(loc, rot, size, mat); - //copy_m3_m4(bonemat,mat); - mat3_to_vec_roll(rot, vec, &angle); - - bone->roll = angle; - // set head - copy_v3_v3(bone->head, mat[3]); - // set tail, don't set it to head because 0-length bones are not allowed - add_v3_v3v3(bone->tail, bone->head, vec); + BoneExtended &be = add_bone_extended(bone, node, layer_labels); + int layer = be.get_bone_layers(); + if (layer) bone->layer = layer; + arm->layer |= layer; // ensure that all populated bone layers are visible after import + + float *tail = be.get_tail(); + int use_connect = be.get_use_connect(); + + switch (use_connect) { + case 1: bone->flag |= BONE_CONNECTED; + break; + case 0: bone->flag &= ~BONE_CONNECTED; + case -1: break; // not defined + } + + if (be.has_roll()) { + bone->roll = be.get_roll(); + } + else { + float angle; + mat4_to_loc_rot_size(loc, rot, size, mat); + mat3_to_vec_roll(rot, NULL, &angle); + } + + copy_v3_v3(bone->head, mat[3]); + add_v3_v3v3(bone->tail, bone->head, tail); //tail must be non zero /* find smallest bone length in armature (used later for leaf bone length) */ if (parent) { + if (use_connect == 1) { + copy_v3_v3(parent->tail, bone->head); + } + /* guess reasonable leaf bone length */ float length = len_v3v3(parent->head, bone->head); if ((length < leaf_bone_length || totbone == 0) && length > MINIMUM_BONE_LENGTH) { @@ -182,11 +188,8 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon COLLADAFW::NodePointerArray& children = node->getChildNodes(); - BoneExtended &be = add_bone_extended(bone, node); - be.set_leaf_bone(true); - for (unsigned int i = 0; i < children.getCount(); i++) { - int cl = create_bone(skin, children[i], bone, children.getCount(), mat, arm); + int cl = create_bone(skin, children[i], bone, children.getCount(), mat, arm, layer_labels); if (cl > chain_length) chain_length = cl; } @@ -209,22 +212,23 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon **/ void ArmatureImporter::fix_leaf_bones(bArmature *armature, Bone *bone) { - /* armature has no bones */ if (bone == NULL) return; - BoneExtended *be = extended_bones[bone->name]; - if (be != NULL && be->is_leaf_bone() ) { - /* Collada only knows Joints, Here we guess a reasonable leaf bone length */ - float leaf_length = (leaf_bone_length == FLT_MAX) ? 1.0 : leaf_bone_length; + if (bc_is_leaf_bone(bone)) { + + BoneExtended *be = extended_bones[bone->name]; + if (be == NULL || !be->has_tail()) { - EditBone *ebone = get_edit_bone(armature, bone->name); - float vec[3]; + /* Collada only knows Joints, Here we guess a reasonable leaf bone length */ + float leaf_length = (leaf_bone_length == FLT_MAX) ? 1.0 : leaf_bone_length; + + EditBone *ebone = bc_get_edit_bone(armature, bone->name); + float vec[3]; - if (this->import_settings->fix_orientation) { if (ebone->parent != NULL) { EditBone *parent = ebone->parent; - sub_v3_v3v3(vec, ebone->head, parent->tail); + sub_v3_v3v3(vec, ebone->head, parent->head); if (len_squared_v3(vec) < MINIMUM_BONE_LENGTH) { sub_v3_v3v3(vec, parent->tail, parent->head); @@ -234,19 +238,31 @@ void ArmatureImporter::fix_leaf_bones(bArmature *armature, Bone *bone) vec[2] = 0.1f; sub_v3_v3v3(vec, ebone->tail, ebone->head); } - } - else { - sub_v3_v3v3(vec, ebone->tail, ebone->head); - } - normalize_v3_v3(vec, vec); - mul_v3_fl(vec, leaf_length); - add_v3_v3v3(ebone->tail, ebone->head, vec); + normalize_v3_v3(vec, vec); + mul_v3_fl(vec, leaf_length); + add_v3_v3v3(ebone->tail, ebone->head, vec); + } } for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { fix_leaf_bones(armature, child); } +} + +void ArmatureImporter::fix_parent_connect(bArmature *armature, Bone *bone) +{ + /* armature has no bones */ + if (bone == NULL) + return; + + if (bone->parent && bone->flag & BONE_CONNECTED) { + copy_v3_v3(bone->parent->tail, bone->head); + } + + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + fix_parent_connect(armature, child); + } } @@ -281,8 +297,8 @@ void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone BoneExtended *pbe = extended_bones[parentbone->name]; if (dominant_child != NULL) { /* Found a valid chain. Now connect current bone with that chain.*/ - EditBone *pebone = get_edit_bone(armature, parentbone->name); - EditBone *cebone = get_edit_bone(armature, dominant_child->get_name()); + EditBone *pebone = bc_get_edit_bone(armature, parentbone->name); + EditBone *cebone = bc_get_edit_bone(armature, dominant_child->get_name()); if (pebone && !(cebone->flag & BONE_CONNECTED)) { float vec[3]; @@ -421,6 +437,7 @@ ArmatureJoints& ArmatureImporter::get_armature_joints(Object *ob_arm) void ArmatureImporter::create_armature_bones( ) { std::vector<COLLADAFW::Node *>::iterator ri; + std::vector<std::string> layer_labels; leaf_bone_length = FLT_MAX; //if there is an armature created for root_joint next root_joint @@ -445,8 +462,9 @@ void ArmatureImporter::create_armature_bones( ) clear_extended_boneset(); ED_armature_to_edit(armature); + armature->layer = 0; // layer is set according to imported bone set in create_bone() - create_bone(NULL, *ri , NULL, (*ri)->getChildNodes().getCount(), NULL, armature); + create_bone(NULL, *ri , NULL, (*ri)->getChildNodes().getCount(), NULL, armature, layer_labels); /* exit armature edit mode to populate the Armature object */ ED_armature_from_edit(armature); @@ -455,14 +473,23 @@ void ArmatureImporter::create_armature_bones( ) /* and step back to edit mode to fix the leaf nodes */ ED_armature_to_edit(armature); - connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - fix_leaf_bones(armature, (Bone *)armature->bonebase.first); + if (this->import_settings->fix_orientation || this->import_settings->find_chains) { + + if (this->import_settings->find_chains) + connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - // exit armature edit mode - unskinned_armature_map[(*ri)->getUniqueId()] = ob_arm; + if (this->import_settings->fix_orientation) + fix_leaf_bones(armature, (Bone *)armature->bonebase.first); + + // exit armature edit mode + unskinned_armature_map[(*ri)->getUniqueId()] = ob_arm; + } + + fix_parent_connect(armature, (Bone *)armature->bonebase.first); ED_armature_from_edit(armature); ED_armature_edit_free(armature); + DAG_id_tag_update(&ob_arm->id, OB_RECALC_OB | OB_RECALC_DATA); } } @@ -513,6 +540,7 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) SkinInfo *a = &skin; Object *shared = NULL; std::vector<COLLADAFW::Node *> skin_root_joints; + std::vector<std::string> layer_labels; std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { @@ -578,7 +606,7 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) // since root_joints may contain joints for multiple controllers, we need to filter if (skin.uses_joint_or_descendant(*ri)) { - create_bone(&skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, armature); + create_bone(&skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, armature, layer_labels); if (joint_parent_map.find((*ri)->getUniqueId()) != joint_parent_map.end() && !skin.get_parent()) skin.set_parent(joint_parent_map[(*ri)->getUniqueId()]); @@ -594,8 +622,8 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) if (armature->bonebase.first) { /* Do this only if Armature has bones */ - connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - fix_leaf_bones(armature, (Bone *)armature->bonebase.first); + //connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); + //fix_leaf_bones(armature, (Bone *)armature->bonebase.first); } // exit armature edit mode ED_armature_from_edit(armature); @@ -894,70 +922,44 @@ bool ArmatureImporter::get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint) return found; } - -/** - * BoneExtended is a helper class needed for the Bone chain finder - * See ArmatureImporter::fix_leaf_bones() - * and ArmatureImporter::connect_bone_chains() - **/ - -BoneExtended::BoneExtended(EditBone *aBone) -{ - this->set_name(aBone->name); - this->chain_length = 0; - this->is_leaf = false; -} - -char *BoneExtended::get_name() -{ - return name; -} - -void BoneExtended::set_name(char *aName) -{ - BLI_strncpy(name, aName, MAXBONENAME); -} - -int BoneExtended::get_chain_length() -{ - return chain_length; -} - -void BoneExtended::set_chain_length(const int aLength) -{ - chain_length = aLength; -} - - -void BoneExtended::set_leaf_bone(bool state) -{ - is_leaf = state; -} - -bool BoneExtended::is_leaf_bone() -{ - return is_leaf; -} - -BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Node *node) +BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Node *node, std::vector<std::string> &layer_labels) { + BoneExtended *be = new BoneExtended(bone); + extended_bones[bone->name] = be; TagsMap::iterator etit; ExtraTags *et = 0; etit = uid_tags_map.find(node->getUniqueId().toAscii()); if (etit != uid_tags_map.end()) { - float x, y, z; + + float tail[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; + float roll = 0; + int use_connect = -1; + std::string layers; et = etit->second; - et->setData("tip_x", &x); - et->setData("tip_y", &y); - et->setData("tip_z", &z); - float vec[3] = { x, y, z }; - copy_v3_v3(bone->tail, bone->head); - add_v3_v3v3(bone->tail, bone->head, vec); + + bool has_tail = false; + has_tail |= et->setData("tip_x", &tail[0]); + has_tail |= et->setData("tip_y", &tail[1]); + has_tail |= et->setData("tip_z", &tail[2]); + + bool has_connect = et->setData("connect", &use_connect); + bool has_roll = et->setData("roll", &roll); + + layers = et->setData("layer", layers); + + if (has_tail && !has_connect) + { + use_connect = 0; // got a bone tail definition but no connect info -> bone is not connected + } + + be->set_bone_layers(layers, layer_labels); + if (has_tail) be->set_tail(tail); + if (has_roll) be->set_roll(roll); + be->set_use_connect(use_connect); } + be->set_leaf_bone(true); - BoneExtended *be = new BoneExtended(bone); - extended_bones[bone->name] = be; return *be; } diff --git a/source/blender/collada/ArmatureImporter.h b/source/blender/collada/ArmatureImporter.h index 732fda80ff1..f38bd1a6c66 100644 --- a/source/blender/collada/ArmatureImporter.h +++ b/source/blender/collada/ArmatureImporter.h @@ -59,25 +59,6 @@ extern "C" { #define UNLIMITED_CHAIN_MAX INT_MAX #define MINIMUM_BONE_LENGTH 0.000001f -class BoneExtended { - -private: - char name[MAXBONENAME]; - int chain_length; - bool is_leaf; - -public: - - BoneExtended(EditBone *aBone); - char *get_name(); - int get_chain_length(); - - void set_name(char *aName); - void set_chain_length(const int aLength); - void set_leaf_bone(bool state); - bool is_leaf_bone(); -}; - class ArmatureImporter : private TransformReader { private: @@ -125,12 +106,13 @@ private: #endif int create_bone(SkinInfo* skin, COLLADAFW::Node *node, EditBone *parent, int totchild, - float parent_mat[4][4], bArmature *arm); + float parent_mat[4][4], bArmature *arm, std::vector<std::string> &layer_labels); - BoneExtended &add_bone_extended(EditBone *bone, COLLADAFW::Node * node); + BoneExtended &add_bone_extended(EditBone *bone, COLLADAFW::Node * node, std::vector<std::string> &layer_labels); void clear_extended_boneset(); void fix_leaf_bones(bArmature *armature, Bone *bone); + void fix_parent_connect(bArmature *armature, Bone *bone); void connect_bone_chains(bArmature *armature, Bone *bone, const int max_chain_length); void set_pose( Object *ob_arm, COLLADAFW::Node *root_node, const char *parentname, float parent_mat[4][4]); 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/ExportSettings.h b/source/blender/collada/ExportSettings.h index 3dc7e74379e..9451cac9dae 100644 --- a/source/blender/collada/ExportSettings.h +++ b/source/blender/collada/ExportSettings.h @@ -48,6 +48,7 @@ public: bool triangulate; bool use_object_instantiation; + bool use_blender_profile; bool sort_by_name; BC_export_transformation_type export_transformation_type; bool open_sim; diff --git a/source/blender/collada/ExtraTags.cpp b/source/blender/collada/ExtraTags.cpp index 6af61432fda..ea225d8a4ae 100644 --- a/source/blender/collada/ExtraTags.cpp +++ b/source/blender/collada/ExtraTags.cpp @@ -85,32 +85,45 @@ std::string ExtraTags::asString(std::string tag, bool *ok) } -void ExtraTags::setData(std::string tag, short *data) +bool ExtraTags::setData(std::string tag, short *data) { bool ok = false; int tmp = asInt(tag, &ok); if (ok) *data = (short)tmp; + return ok; } -void ExtraTags::setData(std::string tag, int *data) + +bool ExtraTags::setData(std::string tag, int *data) { bool ok = false; int tmp = asInt(tag, &ok); if (ok) *data = tmp; + return ok; } -void ExtraTags::setData(std::string tag, float *data) + +bool ExtraTags::setData(std::string tag, float *data) { bool ok = false; float tmp = asFloat(tag, &ok); if (ok) *data = tmp; + return ok; } -void ExtraTags::setData(std::string tag, char *data) + +bool ExtraTags::setData(std::string tag, char *data) { bool ok = false; int tmp = asInt(tag, &ok); if (ok) *data = (char)tmp; + return ok; +} + +std::string ExtraTags::setData(std::string tag, std::string &data) +{ + bool ok = false; + std::string tmp = asString(tag, &ok); + return (ok) ? tmp : data; } - diff --git a/source/blender/collada/ExtraTags.h b/source/blender/collada/ExtraTags.h index 03a311a7e86..ad272dcba65 100644 --- a/source/blender/collada/ExtraTags.h +++ b/source/blender/collada/ExtraTags.h @@ -43,17 +43,18 @@ public: bool addTag(std::string tag, std::string data); /** Set given short pointer to value of tag, if it exists. */ - void setData(std::string tag, short *data); + bool setData(std::string tag, short *data); /** Set given int pointer to value of tag, if it exists. */ - void setData(std::string tag, int *data); + bool setData(std::string tag, int *data); /** Set given float pointer to value of tag, if it exists. */ - void setData(std::string tag, float *data); + bool setData(std::string tag, float *data); /** Set given char pointer to value of tag, if it exists. */ - void setData(std::string tag, char *data); - + bool setData(std::string tag, char *data); + std::string setData(std::string tag, std::string &data); + /** Return true if the extra tags is for specified profile. */ bool isProfile(std::string profile); diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp index 64df42fc823..76f24545248 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" @@ -260,7 +259,8 @@ bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has su COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); const std::string &name = bc_get_dae_name(mesh); - + int hole_count = 0; + for (unsigned i = 0; i < prim_arr.getCount(); i++) { COLLADAFW::MeshPrimitive *mp = prim_arr[i]; @@ -276,13 +276,21 @@ bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has su for (unsigned int j = 0; j < vca.getCount(); j++) { int count = vca[j]; - if (count < 3) { - fprintf(stderr, "Primitive %s in %s has at least one face with vertex count < 3\n", + if (abs(count) < 3) { + fprintf(stderr, "ERROR: Primitive %s in %s has at least one face with vertex count < 3\n", type_str, name.c_str()); return false; } + if (count < 0) + { + hole_count ++; + } + } + + if (hole_count > 0) + { + fprintf(stderr, "WARNING: Primitive %s in %s: %d holes not imported (unsupported)\n", type_str, name.c_str(), hole_count); } - } else if (type == COLLADAFW::MeshPrimitive::LINES) { @@ -290,13 +298,13 @@ bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has su } else if (type != COLLADAFW::MeshPrimitive::TRIANGLES && type != COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { - fprintf(stderr, "Primitive type %s is not supported.\n", type_str); + fprintf(stderr, "ERROR: Primitive type %s is not supported.\n", type_str); return false; } } if (mesh->getPositions().empty()) { - fprintf(stderr, "Mesh %s has no vertices.\n", name.c_str()); + fprintf(stderr, "ERROR: Mesh %s has no vertices.\n", name.c_str()); return false; } @@ -410,11 +418,18 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me) size_t prim_poly_count = mpvc->getFaceCount(); size_t prim_loop_count = 0; - for (int index=0; index < prim_poly_count; index++) { - prim_loop_count += get_vertex_count(mpvc, index); + for (int index=0; index < prim_poly_count; index++) + { + int vcount = get_vertex_count(mpvc, index); + if (vcount > 0) { + prim_loop_count += vcount; + total_poly_count++; + } + else { + // TODO: this is a hole and not another polygon! + } } - total_poly_count += prim_poly_count; total_loop_count += prim_loop_count; break; @@ -684,9 +699,12 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) COLLADAFW::IndexListArray& index_list_array_vcolor = mp->getColorIndicesArray(); for (unsigned int j = 0; j < prim_totpoly; j++) { - + // Vertices in polygon: int vcount = get_vertex_count(mpvc, j); + if (vcount < 0) { + continue; // TODO: add support for holes + } set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount); diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index b1cbc01a9a6..e1b8a2dd30a 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -81,8 +81,9 @@ int collada_export(Scene *sce, int use_texture_copies, int triangulate, - int use_object_instantiation, - int sort_by_name, + int use_object_instantiation, + int use_blender_profile, + int sort_by_name, BC_export_transformation_type export_transformation_type, int open_sim) { @@ -105,6 +106,7 @@ int collada_export(Scene *sce, export_settings.triangulate = triangulate != 0; export_settings.use_object_instantiation = use_object_instantiation != 0; + export_settings.use_blender_profile = use_blender_profile != 0; export_settings.sort_by_name = sort_by_name != 0; export_settings.export_transformation_type = export_transformation_type; export_settings.open_sim = open_sim != 0; @@ -118,12 +120,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.h b/source/blender/collada/collada.h index 6819a62fdf0..db8ea884222 100644 --- a/source/blender/collada/collada.h +++ b/source/blender/collada/collada.h @@ -78,6 +78,7 @@ int collada_export(struct Scene *sce, int triangulate, int use_object_instantiation, + int use_blender_profile, int sort_by_name, BC_export_transformation_type export_transformation_type, int open_sim); diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp index efdfaadd3e2..f0984fbc127 100644 --- a/source/blender/collada/collada_utils.cpp +++ b/source/blender/collada/collada_utils.cpp @@ -54,6 +54,8 @@ extern "C" { #include "BKE_scene.h" #include "BKE_DerivedMesh.h" +#include "ED_armature.h" + #include "WM_api.h" // XXX hrm, see if we can do without this #include "WM_types.h" @@ -357,9 +359,221 @@ 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); } + +/* +* A bone is a leaf when it has no children or all children are not connected. +*/ +bool bc_is_leaf_bone(Bone *bone) +{ + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + if (child->flag & BONE_CONNECTED) + return false; + } + return true; +} + +EditBone *bc_get_edit_bone(bArmature * armature, char *name) { + EditBone *eBone; + + for (eBone = (EditBone *)armature->edbo->first; eBone; eBone = eBone->next) { + if (STREQ(name, eBone->name)) + return eBone; + } + + return NULL; + +} +int bc_set_layer(int bitfield, int layer) +{ + return bc_set_layer(bitfield, layer, true); /* enable */ +} + +int bc_set_layer(int bitfield, int layer, bool enable) +{ + int bit = 1u << layer; + + if (enable) + bitfield |= bit; + else + bitfield &= ~bit; + + return bitfield; +} + +/** +* BoneExtended is a helper class needed for the Bone chain finder +* See ArmatureImporter::fix_leaf_bones() +* and ArmatureImporter::connect_bone_chains() +**/ + +BoneExtended::BoneExtended(EditBone *aBone) +{ + this->set_name(aBone->name); + this->chain_length = 0; + this->is_leaf = false; + this->tail[0] = 0.0f; + this->tail[1] = 0.5f; + this->tail[2] = 0.0f; + this->use_connect = -1; + this->roll = 0; + this->bone_layers = 0; + + this->has_custom_tail = false; + this->has_custom_roll = false; +} + +char *BoneExtended::get_name() +{ + return name; +} + +void BoneExtended::set_name(char *aName) +{ + BLI_strncpy(name, aName, MAXBONENAME); +} + +int BoneExtended::get_chain_length() +{ + return chain_length; +} + +void BoneExtended::set_chain_length(const int aLength) +{ + chain_length = aLength; +} + +void BoneExtended::set_leaf_bone(bool state) +{ + is_leaf = state; +} + +bool BoneExtended::is_leaf_bone() +{ + return is_leaf; +} + +void BoneExtended::set_roll(float roll) +{ + this->roll = roll; + this->has_custom_roll = true; +} + +bool BoneExtended::has_roll() +{ + return this->has_custom_roll; +} + +float BoneExtended::get_roll() +{ + return this->roll; +} + +void BoneExtended::set_tail(float vec[]) +{ + this->tail[0] = vec[0]; + this->tail[1] = vec[1]; + this->tail[2] = vec[2]; + this->has_custom_tail = true; +} + +bool BoneExtended::has_tail() +{ + return this->has_custom_tail; +} + +float *BoneExtended::get_tail() +{ + return this->tail; +} + +inline bool isInteger(const std::string & s) +{ + if (s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false; + + char * p; + strtol(s.c_str(), &p, 10); + + return (*p == 0); +} + +void BoneExtended::set_bone_layers(std::string layerString, std::vector<std::string> &layer_labels) +{ + std::stringstream ss(layerString); + std::string layer; + int pos; + + while (ss >> layer) { + + /* Blender uses numbers to specify layers*/ + if (isInteger(layer)) + { + pos = atoi(layer.c_str()); + if (pos >= 0 && pos < 32) { + this->bone_layers = bc_set_layer(this->bone_layers, pos); + continue; + } + } + + /* layer uses labels (not supported by blender). Map to layer numbers:*/ + pos = find(layer_labels.begin(), layer_labels.end(), layer) - layer_labels.begin(); + if (pos >= layer_labels.size()) { + layer_labels.push_back(layer); /* remember layer number for future usage*/ + } + + if (pos > 31) + { + fprintf(stderr, "Too many layers in Import. Layer %s mapped to Blender layer 31\n", layer.c_str()); + pos = 31; + } + + /* If numeric layers and labeled layers are used in parallel (unlikely), + we get a potential mixup. Just leave as is for now. + */ + this->bone_layers = bc_set_layer(this->bone_layers, pos); + + } +} + +std::string BoneExtended::get_bone_layers(int bitfield) +{ + std::string result = ""; + std::string sep = ""; + int bit = 1u; + + std::ostringstream ss; + for (int i = 0; i < 32; i++) + { + if (bit & bitfield) + { + ss << sep << i; + sep = " "; + } + bit = bit << 1; + } + return ss.str(); +} + +int BoneExtended::get_bone_layers() +{ + return (bone_layers == 0) ? 1 : bone_layers; // ensure that the bone is in at least one bone layer! +} + + +void BoneExtended::set_use_connect(int use_connect) +{ + this->use_connect = use_connect; +} + +int BoneExtended::get_use_connect() +{ + return this->use_connect; +} diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h index 4bc2f55cf33..74f8dca1492 100644 --- a/source/blender/collada/collada_utils.h +++ b/source/blender/collada/collada_utils.h @@ -34,6 +34,7 @@ #include <vector> #include <map> +#include <algorithm> extern "C" { #include "DNA_object_types.h" @@ -46,6 +47,7 @@ extern "C" { #include "BLI_linklist.h" #include "BLI_utildefines.h" +#include "BLI_string.h" #include "BKE_context.h" #include "BKE_object.h" @@ -87,7 +89,10 @@ extern void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_sce extern void bc_match_scale(std::vector<Object *> *objects_done, UnitConverter &unit_converter, bool scale_to_scene); extern void bc_triangulate_mesh(Mesh *me); - +extern bool bc_is_leaf_bone(Bone *bone); +extern EditBone *bc_get_edit_bone(bArmature * armature, char *name); +extern int bc_set_layer(int bitfield, int layer, bool enable); +extern int bc_set_layer(int bitfield, int layer); class BCPolygonNormalsIndices { @@ -105,4 +110,48 @@ class BCPolygonNormalsIndices }; +class BoneExtended { + +private: + char name[MAXBONENAME]; + int chain_length; + bool is_leaf; + float tail[3]; + float roll; + + int bone_layers; + bool use_connect; + bool has_custom_tail; + bool has_custom_roll; + +public: + + BoneExtended(EditBone *aBone); + + void set_name(char *aName); + char *get_name(); + + void set_chain_length(const int aLength); + int get_chain_length(); + + void set_leaf_bone(bool state); + bool is_leaf_bone(); + + void set_bone_layers(std::string layers, std::vector<std::string> &layer_labels); + int get_bone_layers(); + static std::string get_bone_layers(int bitfield); + + void set_roll(float roll); + bool has_roll(); + float get_roll(); + + void set_tail(float *vec); + float *get_tail(); + bool has_tail(); + + void set_use_connect(int use_connect); + int get_use_connect(); +}; + + #endif 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..fd2a521bec5 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -25,8 +25,6 @@ set(INC . - intern - util ../blenkernel ../blenlib ../bmesh @@ -42,48 +40,55 @@ set(INC_SYS ) set(SRC + intern/builder/deg_builder.cc + intern/builder/deg_builder_cycle.cc + intern/builder/deg_builder_nodes.cc + intern/builder/deg_builder_pchanmap.cc + intern/builder/deg_builder_relations.cc + intern/builder/deg_builder_transitive.cc + intern/debug/deg_debug_graphviz.cc + intern/eval/deg_eval.cc + intern/eval/deg_eval_debug.cc + intern/eval/deg_eval_flush.cc + intern/nodes/deg_node.cc + intern/nodes/deg_node_component.cc + intern/nodes/deg_node_operation.cc intern/depsgraph.cc - intern/depsnode.cc - intern/depsnode_component.cc - intern/depsnode_operation.cc intern/depsgraph_build.cc - intern/depsgraph_build_nodes.cc - intern/depsgraph_build_relations.cc intern/depsgraph_debug.cc intern/depsgraph_eval.cc intern/depsgraph_query.cc - intern/depsgraph_queue.cc intern/depsgraph_tag.cc intern/depsgraph_type_defines.cc - util/depsgraph_util_cycle.cc - util/depsgraph_util_pchanmap.cc - util/depsgraph_util_transitive.cc DEG_depsgraph.h DEG_depsgraph_build.h DEG_depsgraph_debug.h DEG_depsgraph_query.h + + intern/builder/deg_builder.h + intern/builder/deg_builder_cycle.h + intern/builder/deg_builder_nodes.h + intern/builder/deg_builder_pchanmap.h + intern/builder/deg_builder_relations.h + intern/builder/deg_builder_transitive.h + intern/eval/deg_eval.h + intern/eval/deg_eval_debug.h + intern/eval/deg_eval_flush.h + intern/nodes/deg_node.h + intern/nodes/deg_node_component.h + intern/nodes/deg_node_operation.h intern/depsgraph.h - intern/depsnode.h - intern/depsnode_component.h - intern/depsnode_operation.h - intern/depsnode_opcodes.h - intern/depsgraph_build.h - intern/depsgraph_debug.h intern/depsgraph_intern.h - intern/depsgraph_queue.h intern/depsgraph_types.h - util/depsgraph_util_cycle.h - util/depsgraph_util_function.h - util/depsgraph_util_hash.h - util/depsgraph_util_map.h - util/depsgraph_util_pchanmap.h - util/depsgraph_util_set.h - util/depsgraph_util_transitive.h + util/deg_util_function.h + util/deg_util_hash.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/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index f37ba71ab65..d1de83ec8a9 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -131,9 +131,6 @@ void DEG_ids_clear_recalc(struct Main *bmain); /* Update Flushing ------------------------------- */ -/* Flush updates */ -void DEG_graph_flush_updates(struct Main *bmain, Depsgraph *graph); - /* Flush updates for all IDs */ void DEG_ids_flush_tagged(struct Main *bmain); @@ -144,11 +141,6 @@ void DEG_ids_check_recalc(struct Main *bmain, struct Scene *scene, bool time); -/* Clear all update tags - * - For aborted updates, or after successful evaluation - */ -void DEG_graph_clear_tags(Depsgraph *graph); - /* ************************************************ */ /* Evaluation Engine API */ diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index f680c47247a..49b648c7dae 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -43,9 +43,6 @@ struct Depsgraph; struct Main; struct Scene; -struct PointerRNA; -struct PropertyRNA; - #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/depsgraph/DEG_depsgraph_debug.h b/source/blender/depsgraph/DEG_depsgraph_debug.h index 374fad63c34..0d19b8e1e97 100644 --- a/source/blender/depsgraph/DEG_depsgraph_debug.h +++ b/source/blender/depsgraph/DEG_depsgraph_debug.h @@ -39,13 +39,10 @@ extern "C" { #endif -struct DepsgraphSettings; struct GHash; struct ID; struct Depsgraph; -struct DepsNode; -struct DepsRelation; /* ************************************************ */ /* Statistics */ diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index 60d673d4c5b..ccd204a2083 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -19,7 +19,7 @@ * All rights reserved. * * Original Author: Joshua Leung - * Contributor(s): None Yet + * Contributor(s): Sergey Sharybin * * ***** END GPL LICENSE BLOCK ***** */ @@ -33,155 +33,14 @@ #ifndef __DEG_DEPSGRAPH_QUERY_H__ #define __DEG_DEPSGRAPH_QUERY_H__ -struct ListBase; struct ID; struct Depsgraph; -struct DepsNode; -struct DepsRelation; #ifdef __cplusplus extern "C" { #endif -/* ************************************************ */ -/* Type Defines */ - -/* FilterPredicate Callback - * - * Defines a callback function which can be supplied to check whether a - * node is relevant or not. - * - * < graph: Depsgraph that we're traversing - * < node: The node to check - * < userdata: FilterPredicate state data (as needed) - * > returns: True if node is relevant - */ -typedef bool (*DEG_FilterPredicate)(const struct Depsgraph *graph, const struct DepsNode *node, void *userdata); - - -/* Node Operation - * - * Performs some action on the given node, provided that the node was - * deemed to be relevant to operate on. - * - * < graph: Depsgraph that we're traversing - * < node: The node to perform operation on/with - * < userdata: Node Operation's state data (as needed) - * > returns: True if traversal should be aborted at this point - */ -typedef bool (*DEG_NodeOperation)(const struct Depsgraph *graph, struct DepsNode *node, void *userdata); - -/* ************************************************ */ -/* Low-Level Filtering API */ - -/* Create a filtered copy of the given graph which contains only the - * nodes which fulfill the criteria specified using the FilterPredicate - * passed in. - * - * < graph: The graph to be copied and filtered - * < filter: FilterPredicate used to check which nodes should be included - * (If null, full graph is copied as-is) - * < userdata: State data for filter (as necessary) - * - * > returns: a full copy of all the relevant nodes - the matching subgraph - */ -// XXX: is there any need for extra settings/options for how the filtering goes? -Depsgraph *DEG_graph_filter(const struct Depsgraph *graph, DEG_FilterPredicate *filter, void *userdata); - - -/* Traverse nodes in graph which are deemed relevant, - * performing the provided operation on the nodes. - * - * < graph: The graph to perform operations on - * < filter: FilterPredicate used to check which nodes should be included - * (If null, all nodes are considered valid targets) - * < filter_data: Custom state data for FilterPredicate - * (Note: This can be the same as op_data, where appropriate) - * < op: NodeOperation to perform on each node - * (If null, no graph traversal is performed for efficiency) - * < op_data: Custom state data for NodeOperation - * (Note: This can be the same as filter_data, where appropriate) - */ -void DEG_graph_traverse(const struct Depsgraph *graph, - DEG_FilterPredicate *filter, void *filter_data, - DEG_NodeOperation *op, void *op_data); - -/* ************************************************ */ -/* Node-Based Operations */ -// XXX: do we want to be able to attach conditional requirements here? - -/* Find an (outer) node matching given conditions - * ! Assumes that there will only be one such node, or that only the first one matters - * - * < graph: a dependency graph which may or may not contain a node matching these requirements - * < query: query conditions for the criteria that the node must satisfy - */ -//DepsNode *DEG_node_find(const Depsgraph *graph, DEG_QueryConditions *query); - -/* Topology Queries (Direct) ---------------------- */ - -/* Get list of nodes which directly depend on given node - * - * > result: list to write results to - * < node: the node to find the children/dependents of - */ -void DEG_node_get_children(struct ListBase *result, const struct DepsNode *node); - - -/* Get list of nodes which given node directly depends on - * - * > result: list to write results to - * < node: the node to find the dependencies of - */ -void DEG_node_get_dependencies(struct ListBase *result, const struct DepsNode *node); - - -/* Topology Queries (Subgraph) -------------------- */ -// XXX: given that subgraphs potentially involve many interconnected nodes, we currently -// just spit out a copy of the subgraph which matches. This works well for the cases -// where these are used - mostly for efficient updating of subsets of the nodes. - -// XXX: allow supplying a filter predicate to provide further filtering/pruning? - - -/* Get all descendants of a node - * - * That is, get the subgraph / subset of nodes which are dependent - * on the results of the given node. - */ -Depsgraph *DEG_node_get_descendants(const struct Depsgraph *graph, const struct DepsNode *node); - - -/* Get all ancestors of a node - * - * That is, get the subgraph / subset of nodes which the given node - * is dependent on in order to be evaluated. - */ -Depsgraph *DEG_node_get_ancestors(const struct Depsgraph *graph, const struct DepsNode *node); - -/* ************************************************ */ -/* Higher-Level Queries */ - -/* Get ID-blocks which would be affected if specified ID is modified - * < only_direct: True = Only ID-blocks with direct relationships to ID-block will be returned - * - * > result: (LinkData : ID) a list of ID-blocks matching the specified criteria - * > returns: number of matching ID-blocks - */ -size_t DEG_query_affected_ids(struct ListBase *result, const struct ID *id, const bool only_direct); - - -/* Get ID-blocks which are needed to update/evaluate specified ID - * < only_direct: True = Only ID-blocks with direct relationships to ID-block will be returned - * - * > result: (LinkData : ID) a list of ID-blocks matching the specified criteria - * > returns: number of matching ID-blocks - */ -size_t DEG_query_required_ids(struct ListBase *result, const struct ID *id, const bool only_direct); - -/* ************************************************ */ - /* Check if given ID type was tagged for update. */ bool DEG_id_type_tagged(struct Main *bmain, short idtype); diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc new file mode 100644 index 00000000000..9f80c21a6a4 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -0,0 +1,129 @@ +/* + * ***** 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. + * + * Original Author: Sergey Sharybin + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/build/deg_builder.cc + * \ingroup depsgraph + */ + +#include "intern/builder/deg_builder.h" + +// TODO(sergey): Use own wrapper over STD. +#include <stack> + +#include "DNA_anim_types.h" + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + +#include "intern/depsgraph.h" +#include "intern/depsgraph_types.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "util/deg_util_foreach.h" + +namespace DEG { + +string deg_fcurve_id_name(const FCurve *fcu) +{ + char index_buf[32]; + // TODO(sergey): Use int-to-string utility or so. + BLI_snprintf(index_buf, sizeof(index_buf), "[%d]", fcu->array_index); + return string(fcu->rna_path) + index_buf; +} + +void deg_graph_build_finalize(Depsgraph *graph) +{ + std::stack<OperationDepsNode *> stack; + + foreach (OperationDepsNode *node, graph->operations) { + IDDepsNode *id_node = node->owner->owner; + node->done = 0; + node->num_links_pending = 0; + foreach (DepsRelation *rel, node->outlinks) { + if ((rel->from->type == DEPSNODE_TYPE_OPERATION) && + (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) + { + ++node->num_links_pending; + } + } + if (node->num_links_pending == 0) { + stack.push(node); + node->done = 1; + } + node->owner->layers = id_node->layers; + id_node->id->tag |= LIB_TAG_DOIT; + } + + while (!stack.empty()) { + OperationDepsNode *node = stack.top(); + stack.pop(); + /* Flush layers to parents. */ + foreach (DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEPSNODE_TYPE_OPERATION) { + OperationDepsNode *from = (OperationDepsNode *)rel->from; + from->owner->layers |= node->owner->layers; + } + } + /* Schedule parent nodes. */ + foreach (DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEPSNODE_TYPE_OPERATION) { + OperationDepsNode *from = (OperationDepsNode *)rel->from; + if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { + BLI_assert(from->num_links_pending > 0); + --from->num_links_pending; + } + if (from->num_links_pending == 0 && from->done == 0) { + stack.push(from); + from->done = 1; + } + } + } + } + + /* Re-tag IDs for update if it was tagged before the relations update tag. */ + GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash) + { + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp, id_node->components) + { + id_node->layers |= comp->layers; + } + GHASH_FOREACH_END(); + + 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; + } + id_node->finalize_build(); + } + GHASH_FOREACH_END(); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h new file mode 100644 index 00000000000..7ecb4b20684 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder.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. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Original Author: Sergey Sharybin + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/build/deg_builder.h + * \ingroup depsgraph + */ + +#pragma once + +#include "intern/depsgraph_types.h" + +struct FCurve; + +namespace DEG { + +struct Depsgraph; + +/* Get unique identifier for FCurves and Drivers */ +string deg_fcurve_id_name(const FCurve *fcu); + +void deg_graph_build_finalize(struct Depsgraph *graph); + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc index 5eae8c087ad..225cc64ae4d 100644 --- a/source/blender/depsgraph/util/depsgraph_util_cycle.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc @@ -23,28 +23,30 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_cycle.cc +/** \file blender/depsgraph/intern/builder/deg_builder_cycle.cc * \ingroup depsgraph */ +#include "intern/builder/deg_builder_cycle.h" + +// TOO(sergey): Use some wrappers over those? #include <cstdio> #include <cstdlib> #include <stack> extern "C" { #include "BLI_utildefines.h" +} -#include "DNA_ID.h" +#include "util/deg_util_foreach.h" -#include "RNA_access.h" -#include "RNA_types.h" -} +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" -#include "depsgraph_util_cycle.h" -#include "depsgraph.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" +#include "intern/depsgraph.h" + +namespace DEG { struct StackEntry { OperationDepsNode *node; @@ -62,17 +64,9 @@ void deg_graph_detect_cycles(Depsgraph *graph) const int NODE_IN_STACK = 2; std::stack<StackEntry> traversal_stack; - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + foreach (OperationDepsNode *node, graph->operations) { bool has_inlinks = false; - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; + foreach (DepsRelation *rel, node->inlinks) { if (rel->from->type == DEPSNODE_TYPE_OPERATION) { has_inlinks = true; } @@ -94,11 +88,7 @@ void deg_graph_detect_cycles(Depsgraph *graph) StackEntry &entry = traversal_stack.top(); OperationDepsNode *node = entry.node; bool all_child_traversed = true; - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; + foreach (DepsRelation *rel, node->outlinks) { if (rel->to->type == DEPSNODE_TYPE_OPERATION) { OperationDepsNode *to = (OperationDepsNode *)rel->to; if (to->done == NODE_IN_STACK) { @@ -138,3 +128,5 @@ void deg_graph_detect_cycles(Depsgraph *graph) } } } + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_cycle.h b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h index fac38b61057..386fbd80d19 100644 --- a/source/blender/depsgraph/util/depsgraph_util_cycle.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h @@ -23,15 +23,18 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_cycle.h +/** \file blender/depsgraph/intern/builder/deg_builder_cycle.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_CYCLE_H__ -#define __DEPSGRAPH_UTIL_CYCLE_H__ + +#pragma once + +namespace DEG { struct Depsgraph; +/* Detect and solve dependency cycles. */ void deg_graph_detect_cycles(Depsgraph *graph); -#endif /* __DEPSGRAPH_UTIL_CYCLE_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 0a5235a6d11..5359cc8754a 100644 --- a/source/blender/depsgraph/intern/depsgraph_build_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -24,12 +24,14 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/depsgraph_build_nodes.cc +/** \file blender/depsgraph/intern/builder/deg_build_nodes.cc * \ingroup depsgraph * * Methods for constructing depsgraph's nodes */ +#include "intern/builder/deg_builder_nodes.h" + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -97,12 +99,14 @@ extern "C" { #include "RNA_types.h" } /* extern "C" */ -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_types.h" -#include "depsgraph_build.h" -#include "depsgraph_intern.h" +#include "intern/builder/deg_builder.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_types.h" +#include "intern/depsgraph_intern.h" + +namespace DEG { /* ************ */ /* Node Builder */ @@ -217,6 +221,17 @@ OperationDepsNode *DepsgraphNodeBuilder::add_operation_node( return add_operation_node(comp_node, optype, op, opcode, description); } +OperationDepsNode *DepsgraphNodeBuilder::add_operation_node( + ID *id, + eDepsNode_Type comp_type, + eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string& description) +{ + return add_operation_node(id, comp_type, "", optype, op, opcode, description); +} + bool DepsgraphNodeBuilder::has_operation_node(ID *id, eDepsNode_Type comp_type, const string &comp_name, @@ -237,6 +252,14 @@ OperationDepsNode *DepsgraphNodeBuilder::find_operation_node( return comp_node->has_operation(opcode, description); } +OperationDepsNode *DepsgraphNodeBuilder::find_operation_node( + ID *id, + eDepsNode_Type comp_type, + eDepsOperation_Code opcode, + const string& description) +{ + return find_operation_node(id, comp_type, "", opcode, description); +} /* **** Build functions for entity nodes **** */ @@ -248,6 +271,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); @@ -336,7 +366,7 @@ SubgraphDepsNode *DepsgraphNodeBuilder::build_subgraph(Group *group) return NULL; /* create new subgraph's data */ - Depsgraph *subgraph = DEG_graph_new(); + Depsgraph *subgraph = reinterpret_cast<Depsgraph *>(DEG_graph_new()); DepsgraphNodeBuilder subgraph_builder(m_bmain, subgraph); @@ -374,11 +404,11 @@ void DepsgraphNodeBuilder::build_object(Scene *scene, Base *base, Object *ob) IDDepsNode *id_node = add_id_node(&ob->id); id_node->layers = base->lay; + ob->customdata_mask = 0; /* standard components */ build_object_transform(scene, ob); - /* object data */ if (ob->data) { /* type-specific data... */ @@ -778,6 +808,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 +910,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, @@ -1211,3 +1257,5 @@ void DepsgraphNodeBuilder::build_gpencil(bGPdata *gpd) */ build_animdata(gpd_id); } + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h new file mode 100644 index 00000000000..6ee0b8406a1 --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -0,0 +1,153 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Lukas Toenne + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/builder/deg_builder_nodes.h + * \ingroup depsgraph + */ + +#pragma once + +#include "intern/depsgraph_types.h" + +struct Base; +struct bGPdata; +struct ListBase; +struct GHash; +struct ID; +struct FCurve; +struct Group; +struct Key; +struct Main; +struct Material; +struct MTex; +struct bNodeTree; +struct Object; +struct bPoseChannel; +struct bConstraint; +struct Scene; +struct Tex; +struct World; + +struct PropertyRNA; + +namespace DEG { + +struct Depsgraph; +struct DepsNode; +struct RootDepsNode; +struct SubgraphDepsNode; +struct IDDepsNode; +struct TimeSourceDepsNode; +struct ComponentDepsNode; +struct OperationDepsNode; + +struct DepsgraphNodeBuilder { + DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph); + ~DepsgraphNodeBuilder(); + + RootDepsNode *add_root_node(); + IDDepsNode *add_id_node(ID *id); + TimeSourceDepsNode *add_time_source(ID *id); + + ComponentDepsNode *add_component_node(ID *id, + eDepsNode_Type comp_type, + const string& comp_name = ""); + + OperationDepsNode *add_operation_node(ComponentDepsNode *comp_node, + eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string& description = ""); + OperationDepsNode *add_operation_node(ID *id, + eDepsNode_Type comp_type, + const string& comp_name, + eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string& description = ""); + OperationDepsNode *add_operation_node(ID *id, + eDepsNode_Type comp_type, + eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string& description = ""); + + bool has_operation_node(ID *id, + eDepsNode_Type comp_type, + const string& comp_name, + eDepsOperation_Code opcode, + const string& description = ""); + + OperationDepsNode *find_operation_node(ID *id, + eDepsNode_Type comp_type, + const string &comp_name, + eDepsOperation_Code opcode, + const string &description = ""); + + OperationDepsNode *find_operation_node(ID *id, + eDepsNode_Type comp_type, + eDepsOperation_Code opcode, + const string &description = ""); + + void build_scene(Main *bmain, Scene *scene); + SubgraphDepsNode *build_subgraph(Group *group); + void build_group(Scene *scene, Base *base, Group *group); + void build_object(Scene *scene, Base *base, Object *ob); + void build_object_transform(Scene *scene, Object *ob); + void build_object_constraints(Scene *scene, Object *ob); + void build_pose_constraints(Object *ob, bPoseChannel *pchan); + void build_rigidbody(Scene *scene); + void build_particles(Scene *scene, Object *ob); + void build_animdata(ID *id); + OperationDepsNode *build_driver(ID *id, FCurve *fcurve); + void build_ik_pose(Scene *scene, + Object *ob, + bPoseChannel *pchan, + bConstraint *con); + void build_splineik_pose(Scene *scene, + Object *ob, + bPoseChannel *pchan, + bConstraint *con); + void build_rig(Scene *scene, Object *ob); + void build_proxy_rig(Object *ob); + void build_shapekeys(Key *key); + void build_obdata_geom(Scene *scene, Object *ob); + void build_camera(Object *ob); + void build_lamp(Object *ob); + void build_nodetree(DepsNode *owner_node, bNodeTree *ntree); + void build_material(DepsNode *owner_node, Material *ma); + void build_texture(DepsNode *owner_node, Tex *tex); + void build_texture_stack(DepsNode *owner_node, MTex **texture_stack); + void build_world(World *world); + void build_compositor(Scene *scene); + void build_gpencil(bGPdata *gpd); + +protected: + Main *m_bmain; + Depsgraph *m_graph; +}; + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_pchanmap.cc b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc index 80b37ec622d..0e78df52ff8 100644 --- a/source/blender/depsgraph/util/depsgraph_util_pchanmap.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc @@ -24,11 +24,11 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_pchanmap.cc +/** \file blender/depsgraph/intern/builder/deg_builder_pchanmap.h * \ingroup depsgraph */ -#include "depsgraph_util_pchanmap.h" +#include "intern/builder/deg_builder_pchanmap.h" #include <stdio.h> #include <string.h> @@ -38,6 +38,8 @@ extern "C" { #include "BLI_ghash.h" } +namespace DEG { + static void free_rootpchanmap_valueset(void *val) { /* Just need to free the set itself - the names stored are all references. */ @@ -48,13 +50,13 @@ static void free_rootpchanmap_valueset(void *val) RootPChanMap::RootPChanMap() { /* Just create empty map. */ - m_map = BLI_ghash_str_new("RootPChanMap"); + map_ = BLI_ghash_str_new("RootPChanMap"); } RootPChanMap::~RootPChanMap() { /* Free the map, and all the value sets. */ - BLI_ghash_free(m_map, NULL, free_rootpchanmap_valueset); + BLI_ghash_free(map_, NULL, free_rootpchanmap_valueset); } /* Debug contents of map */ @@ -64,7 +66,7 @@ void RootPChanMap::print_debug() GSetIterator it2; printf("Root PChan Map:\n"); - GHASH_ITER(it1, m_map) { + GHASH_ITER(it1, map_) { const char *item = (const char *)BLI_ghashIterator_getKey(&it1); GSet *values = (GSet *)BLI_ghashIterator_getValue(&it1); @@ -80,11 +82,11 @@ void RootPChanMap::print_debug() /* Add a mapping. */ void RootPChanMap::add_bone(const char *bone, const char *root) { - if (BLI_ghash_haskey(m_map, bone)) { + if (BLI_ghash_haskey(map_, bone)) { /* Add new entry, but only add the root if it doesn't already * exist in there. */ - GSet *values = (GSet *)BLI_ghash_lookup(m_map, bone); + GSet *values = (GSet *)BLI_ghash_lookup(map_, bone); BLI_gset_add(values, (void *)root); } else { @@ -92,7 +94,7 @@ void RootPChanMap::add_bone(const char *bone, const char *root) GSet *values = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "RootPChanMap Value Set"); - BLI_ghash_insert(m_map, (void *)bone, (void *)values); + BLI_ghash_insert(map_, (void *)bone, (void *)values); /* Add new entry now. */ BLI_gset_insert(values, (void *)root); @@ -103,20 +105,20 @@ void RootPChanMap::add_bone(const char *bone, const char *root) bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) { /* Ensure that both are in the map... */ - if (BLI_ghash_haskey(m_map, bone1) == false) { + if (BLI_ghash_haskey(map_, bone1) == false) { //fprintf("RootPChanMap: bone1 '%s' not found (%s => %s)\n", bone1, bone1, bone2); //print_debug(); return false; } - if (BLI_ghash_haskey(m_map, bone2) == false) { + if (BLI_ghash_haskey(map_, bone2) == false) { //fprintf("RootPChanMap: bone2 '%s' not found (%s => %s)\n", bone2, bone1, bone2); //print_debug(); return false; } - GSet *bone1_roots = (GSet *)BLI_ghash_lookup(m_map, (void *)bone1); - GSet *bone2_roots = (GSet *)BLI_ghash_lookup(m_map, (void *)bone2); + GSet *bone1_roots = (GSet *)BLI_ghash_lookup(map_, (void *)bone1); + GSet *bone2_roots = (GSet *)BLI_ghash_lookup(map_, (void *)bone2); GSetIterator it1, it2; GSET_ITER(it1, bone1_roots) { @@ -134,3 +136,5 @@ bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) //fprintf("RootPChanMap: No common root found (%s => %s)\n", bone1, bone2); return false; } + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h index b7f4c495933..233d8602fce 100644 --- a/source/blender/depsgraph/util/depsgraph_util_pchanmap.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h @@ -24,12 +24,15 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_pchanmap.h +/** \file blender/depsgraph/intern/builder/deg_builder_pchanmap.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_PCHANMAP_H__ -#define __DEPSGRAPH_UTIL_PCHANMAP_H__ +#pragma once + +struct GHash; + +namespace DEG { struct RootPChanMap { /* ctor and dtor - Create and free the internal map respectively. */ @@ -45,7 +48,7 @@ struct RootPChanMap { /* Check if there's a common root bone between two bones. */ bool has_common_root(const char *bone1, const char *bone2); -private: +protected: /* The actual map: * - Keys are "strings" (const char *) - not dynamically allocated. * - Values are "sets" (const char *) - not dynamically allocated. @@ -53,7 +56,7 @@ private: * We don't use the C++ maps here, as it's more convenient to use * Blender's GHash and be able to compare by-value instead of by-ref. */ - struct GHash *m_map; + struct GHash *map_; }; -#endif /* __DEPSGRAPH_UTIL_PCHANMAP_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 226991e7b11..10aebb75fbf 100644 --- a/source/blender/depsgraph/intern/depsgraph_build_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -24,12 +24,14 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/depsgraph_build_relations.cc +/** \file blender/depsgraph/intern/builder/deg_builder_relations.cc * \ingroup depsgraph * * Methods for constructing depsgraph */ +#include "intern/builder/deg_builder_relations.h" + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -93,15 +95,19 @@ extern "C" { #include "RNA_types.h" } /* extern "C" */ -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_build.h" -#include "depsgraph_debug.h" -#include "depsgraph_intern.h" -#include "depsgraph_types.h" +#include "intern/builder/deg_builder.h" +#include "intern/builder/deg_builder_pchanmap.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "intern/depsgraph_types.h" -#include "depsgraph_util_pchanmap.h" +#include "util/deg_util_foreach.h" + +namespace DEG { /* ***************** */ /* Relations Builder */ @@ -244,6 +250,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... @@ -297,6 +310,19 @@ void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) if (scene->gpd) { build_gpencil(&scene->id, scene->gpd); } + + for (Depsgraph::OperationNodes::const_iterator it_op = m_graph->operations.begin(); + it_op != m_graph->operations.end(); + ++it_op) + { + OperationDepsNode *node = *it_op; + IDDepsNode *id_node = node->owner->owner; + ID *id = id_node->id; + if (GS(id->name) == ID_OB) { + Object *object = (Object *)id; + object->customdata_mask |= node->customdata_mask; + } + } } void DepsgraphRelationBuilder::build_group(Main *bmain, @@ -466,8 +492,12 @@ void DepsgraphRelationBuilder::build_object_parent(Object *ob) { ComponentKey parent_key(&ob->parent->id, DEPSNODE_TYPE_GEOMETRY); add_relation(parent_key, ob_key, DEPSREL_TYPE_GEOMETRY_EVAL, "Vertex Parent"); + /* XXX not sure what this is for or how you could be done properly - lukas */ - //parent_node->customdata_mask |= CD_MASK_ORIGINDEX; + OperationDepsNode *parent_node = find_operation_node(parent_key); + if (parent_node != NULL) { + parent_node->customdata_mask |= CD_MASK_ORIGINDEX; + } ComponentKey transform_key(&ob->parent->id, DEPSNODE_TYPE_TRANSFORM); add_relation(transform_key, ob_key, DEPSREL_TYPE_TRANSFORM, "Vertex Parent TFM"); @@ -618,7 +648,10 @@ void DepsgraphRelationBuilder::build_constraints(Scene *scene, ID *id, eDepsNode add_relation(target_key, constraint_op_key, DEPSREL_TYPE_GEOMETRY_EVAL, cti->name); if (ct->tar->type == OB_MESH) { - //node2->customdata_mask |= CD_MASK_MDEFORMVERT; + OperationDepsNode *node2 = find_operation_node(target_key); + if (node2 != NULL) { + node2->customdata_mask |= CD_MASK_MDEFORMVERT; + } } } else if (con->type == CONSTRAINT_TYPE_SHRINKWRAP) { @@ -759,8 +792,7 @@ void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu) if (arm_node && bone_name) { /* find objects which use this, and make their eval callbacks depend on this */ - DEPSNODE_RELATIONS_ITER_BEGIN(arm_node->outlinks, rel) - { + foreach (DepsRelation *rel, arm_node->outlinks) { IDDepsNode *to_node = (IDDepsNode *)rel->to; /* we only care about objects with pose data which use this... */ @@ -774,7 +806,6 @@ void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu) } } } - DEPSNODE_RELATIONS_ITER_END; /* free temp data */ MEM_freeN(bone_name); @@ -1195,7 +1226,10 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *ob, add_relation(target_key, solver_key, DEPSREL_TYPE_GEOMETRY_EVAL, con->name); if (data->tar->type == OB_MESH) { - //node2->customdata_mask |= CD_MASK_MDEFORMVERT; + OperationDepsNode *node2 = find_operation_node(target_key); + if (node2 != NULL) { + node2->customdata_mask |= CD_MASK_MDEFORMVERT; + } } } else { @@ -1227,7 +1261,10 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *ob, add_relation(target_key, solver_key, DEPSREL_TYPE_GEOMETRY_EVAL, con->name); if (data->poletar->type == OB_MESH) { - //node2->customdata_mask |= CD_MASK_MDEFORMVERT; + OperationDepsNode *node2 = find_operation_node(target_key); + if (node2 != NULL) { + node2->customdata_mask |= CD_MASK_MDEFORMVERT; + } } } else { @@ -1603,13 +1640,18 @@ void DepsgraphRelationBuilder::build_obdata_geom(Main *bmain, Scene *scene, Obje if (mti->updateDepsgraph) { DepsNodeHandle handle = create_node_handle(mod_key); - mti->updateDepsgraph(md, bmain, scene, ob, &handle); + mti->updateDepsgraph( + md, + bmain, + scene, + ob, + reinterpret_cast< ::DepsNodeHandle* >(&handle)); } if (BKE_object_modifier_use_time(ob, md)) { TimeSourceKey time_src_key; add_relation(time_src_key, mod_key, DEPSREL_TYPE_TIME, "Time Source"); - + /* Hacky fix for T45633 (Animated modifiers aren't updated) * * This check works because BKE_object_modifier_use_time() tests @@ -1683,7 +1725,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; } @@ -1917,3 +1961,4 @@ bool DepsgraphRelationBuilder::needs_animdata_node(ID *id) return false; } +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index c5b04ec299c..c0bf82becda 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -24,12 +24,27 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/depsgraph_build.h +/** \file blender/depsgraph/intern/builder/deg_builder_relations.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_BUILD_H__ -#define __DEPSGRAPH_BUILD_H__ +#pragma once + +#include <cstdio> + +#include "intern/depsgraph_types.h" + +#include "DNA_ID.h" + +#include "RNA_access.h" +#include "RNA_types.h" + +#include "BLI_utildefines.h" +#include "BLI_string.h" + +#include "intern/depsgraph_types.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_operation.h" struct Base; struct bGPdata; @@ -52,6 +67,8 @@ struct World; struct PropertyRNA; +namespace DEG { + struct Depsgraph; struct DepsNode; struct DepsNodeHandle; @@ -63,75 +80,6 @@ struct ComponentDepsNode; struct OperationDepsNode; struct RootPChanMap; -struct DepsgraphNodeBuilder { - DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph); - ~DepsgraphNodeBuilder(); - - RootDepsNode *add_root_node(); - IDDepsNode *add_id_node(ID *id); - TimeSourceDepsNode *add_time_source(ID *id); - - ComponentDepsNode *add_component_node(ID *id, eDepsNode_Type comp_type, const string &comp_name = ""); - - OperationDepsNode *add_operation_node(ComponentDepsNode *comp_node, eDepsOperation_Type optype, - DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &description = ""); - OperationDepsNode *add_operation_node(ID *id, eDepsNode_Type comp_type, const string &comp_name, eDepsOperation_Type optype, - DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &description = ""); - OperationDepsNode *add_operation_node(ID *id, eDepsNode_Type comp_type, eDepsOperation_Type optype, - DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &description = "") - { - return add_operation_node(id, comp_type, "", optype, op, opcode, description); - } - - bool has_operation_node(ID *id, eDepsNode_Type comp_type, const string &comp_name, - eDepsOperation_Code opcode, const string &description = ""); - - OperationDepsNode *find_operation_node(ID *id, - eDepsNode_Type comp_type, - const string &comp_name, - eDepsOperation_Code opcode, - const string &description = ""); - - OperationDepsNode *find_operation_node(ID *id, - eDepsNode_Type comp_type, - eDepsOperation_Code opcode, - const string &description = "") - { - return find_operation_node(id, comp_type, "", opcode, description); - } - - void build_scene(Main *bmain, Scene *scene); - SubgraphDepsNode *build_subgraph(Group *group); - void build_group(Scene *scene, Base *base, Group *group); - void build_object(Scene *scene, Base *base, Object *ob); - void build_object_transform(Scene *scene, Object *ob); - void build_object_constraints(Scene *scene, Object *ob); - void build_pose_constraints(Object *ob, bPoseChannel *pchan); - void build_rigidbody(Scene *scene); - void build_particles(Scene *scene, Object *ob); - void build_animdata(ID *id); - OperationDepsNode *build_driver(ID *id, FCurve *fcurve); - void build_ik_pose(Scene *scene, Object *ob, bPoseChannel *pchan, bConstraint *con); - void build_splineik_pose(Scene *scene, Object *ob, bPoseChannel *pchan, bConstraint *con); - void build_rig(Scene *scene, Object *ob); - void build_proxy_rig(Object *ob); - void build_shapekeys(Key *key); - void build_obdata_geom(Scene *scene, Object *ob); - void build_camera(Object *ob); - void build_lamp(Object *ob); - void build_nodetree(DepsNode *owner_node, bNodeTree *ntree); - void build_material(DepsNode *owner_node, Material *ma); - void build_texture(DepsNode *owner_node, Tex *tex); - void build_texture_stack(DepsNode *owner_node, MTex **texture_stack); - void build_world(World *world); - void build_compositor(Scene *scene); - void build_gpencil(bGPdata *gpd); - -private: - Main *m_bmain; - Depsgraph *m_graph; -}; - struct RootKey { RootKey() {} @@ -164,7 +112,7 @@ struct ComponentKey const char *idname = (id) ? id->name : "<None>"; char typebuf[5]; - sprintf(typebuf, "%d", type); + BLI_snprintf(typebuf, sizeof(typebuf), "%d", type); return string("ComponentKey(") + idname + ", " + typebuf + ", '" + name + "')"; } @@ -204,7 +152,7 @@ struct OperationKey string identifier() const { char typebuf[5]; - sprintf(typebuf, "%d", component_type); + BLI_snprintf(typebuf, sizeof(typebuf), "%d", component_type); return string("OperationKey(") + "t: " + typebuf + ", cn: '" + component_name + "', c: " + DEG_OPNAMES[opcode] + ", n: '" + name + "')"; } @@ -245,30 +193,45 @@ struct DepsgraphRelationBuilder DepsgraphRelationBuilder(Depsgraph *graph); template <typename KeyFrom, typename KeyTo> - void add_relation(const KeyFrom &key_from, const KeyTo &key_to, - eDepsRelation_Type type, const char *description); + void add_relation(const KeyFrom& key_from, + const KeyTo& key_to, + eDepsRelation_Type type, + const char *description); template <typename KeyTo> - void add_relation(const TimeSourceKey &key_from, const KeyTo &key_to, - eDepsRelation_Type type, const char *description); + void add_relation(const TimeSourceKey& key_from, + const KeyTo& key_to, + eDepsRelation_Type type, + const char *description); template <typename KeyType> - void add_node_handle_relation(const KeyType &key_from, const DepsNodeHandle *handle, - eDepsRelation_Type type, const char *description); + void add_node_handle_relation(const KeyType& key_from, + const DepsNodeHandle *handle, + eDepsRelation_Type type, + const char *description); void build_scene(Main *bmain, Scene *scene); void build_group(Main *bmain, Scene *scene, Object *object, Group *group); void build_object(Main *bmain, Scene *scene, Object *ob); void build_object_parent(Object *ob); - void build_constraints(Scene *scene, ID *id, eDepsNode_Type component_type, const char *component_subdata, - ListBase *constraints, RootPChanMap *root_map); + void build_constraints(Scene *scene, ID *id, + eDepsNode_Type component_type, + const char *component_subdata, + ListBase *constraints, + RootPChanMap *root_map); void build_animdata(ID *id); void build_driver(ID *id, FCurve *fcurve); void build_world(World *world); void build_rigidbody(Scene *scene); void build_particles(Scene *scene, Object *ob); - void build_ik_pose(Object *ob, bPoseChannel *pchan, bConstraint *con, RootPChanMap *root_map); - void build_splineik_pose(Object *ob, bPoseChannel *pchan, bConstraint *con, RootPChanMap *root_map); + void build_ik_pose(Object *ob, + bPoseChannel *pchan, + bConstraint *con, + RootPChanMap *root_map); + void build_splineik_pose(Object *ob, + bPoseChannel *pchan, + bConstraint *con, + RootPChanMap *root_map); void build_rig(Scene *scene, Object *ob); void build_proxy_rig(Object *ob); void build_shapekeys(ID *obdata, Key *key); @@ -282,6 +245,9 @@ struct DepsgraphRelationBuilder void build_compositor(Scene *scene); void build_gpencil(ID *owner, bGPdata *gpd); + template <typename KeyType> + OperationDepsNode *find_operation_node(const KeyType &key); + protected: RootDepsNode *find_node(const RootKey &key) const; TimeSourceDepsNode *find_node(const TimeSourceKey &key) const; @@ -290,12 +256,17 @@ protected: DepsNode *find_node(const RNAPathKey &key) const; OperationDepsNode *has_node(const OperationKey &key) const; - void add_time_relation(TimeSourceDepsNode *timesrc, DepsNode *node_to, const char *description); - void add_operation_relation(OperationDepsNode *node_from, OperationDepsNode *node_to, - eDepsRelation_Type type, const char *description); + void add_time_relation(TimeSourceDepsNode *timesrc, + DepsNode *node_to, + const char *description); + void add_operation_relation(OperationDepsNode *node_from, + OperationDepsNode *node_to, + eDepsRelation_Type type, + const char *description); template <typename KeyType> - DepsNodeHandle create_node_handle(const KeyType &key, const string &default_name = ""); + DepsNodeHandle create_node_handle(const KeyType& key, + const string& default_name = ""); bool needs_animdata_node(ID *id); @@ -320,8 +291,11 @@ struct DepsNodeHandle /* Utilities for Builders ----------------------------------------------------- */ -/* Get unique identifier for FCurves and Drivers */ -string deg_fcurve_id_name(const FCurve *fcu); +template <typename KeyType> +OperationDepsNode *DepsgraphRelationBuilder::find_operation_node(const KeyType& key) { + DepsNode *node = find_node(key); + return node != NULL ? node->get_exit_operation() : NULL; +} template <typename KeyFrom, typename KeyTo> void DepsgraphRelationBuilder::add_relation(const KeyFrom &key_from, @@ -377,10 +351,11 @@ void DepsgraphRelationBuilder::add_relation(const TimeSourceKey &key_from, } template <typename KeyType> -void DepsgraphRelationBuilder::add_node_handle_relation(const KeyType &key_from, - const DepsNodeHandle *handle, - eDepsRelation_Type type, - const char *description) +void DepsgraphRelationBuilder::add_node_handle_relation( + const KeyType &key_from, + const DepsNodeHandle *handle, + eDepsRelation_Type type, + const char *description) { DepsNode *node_from = find_node(key_from); OperationDepsNode *op_from = node_from ? node_from->get_exit_operation() : NULL; @@ -399,10 +374,11 @@ void DepsgraphRelationBuilder::add_node_handle_relation(const KeyType &key_from, } template <typename KeyType> -DepsNodeHandle DepsgraphRelationBuilder::create_node_handle(const KeyType &key, - const string &default_name) +DepsNodeHandle DepsgraphRelationBuilder::create_node_handle( + const KeyType &key, + const string &default_name) { return DepsNodeHandle(this, find_node(key), default_name); } -#endif /* __DEPSGRAPH_BUILD_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_transitive.cc b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc index 98192a9540f..0322ef7fa1d 100644 --- a/source/blender/depsgraph/util/depsgraph_util_transitive.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc @@ -24,26 +24,25 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_transitive.cc +/** \file blender/depsgraph/intern/builder/deg_builder_transitive.cc * \ingroup depsgraph */ +#include "intern/builder/deg_builder_transitive.h" + extern "C" { #include "MEM_guardedalloc.h" +} -#include "BLI_utildefines.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" -#include "DNA_ID.h" +#include "intern/depsgraph.h" -#include "RNA_access.h" -#include "RNA_types.h" -} +#include "util/deg_util_foreach.h" -#include "depsgraph_util_transitive.h" -#include "depsgraph.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" +namespace DEG { /* -------------------------------------------------- */ @@ -67,16 +66,11 @@ enum { static void deg_graph_tag_paths_recursive(DepsNode *node) { - if (node->done & OP_VISITED) + if (node->done & OP_VISITED) { return; + } node->done |= OP_VISITED; - - for (OperationDepsNode::Relations::const_iterator it = node->inlinks.begin(); - it != node->inlinks.end(); - ++it) - { - DepsRelation *rel = *it; - + foreach (DepsRelation *rel, node->inlinks) { deg_graph_tag_paths_recursive(rel->from); /* Do this only in inlinks loop, so the target node does not get * flagged. @@ -87,18 +81,9 @@ static void deg_graph_tag_paths_recursive(DepsNode *node) void deg_graph_transitive_reduction(Depsgraph *graph) { - for (Depsgraph::OperationNodes::const_iterator it_target = graph->operations.begin(); - it_target != graph->operations.end(); - ++it_target) - { - OperationDepsNode *target = *it_target; - + foreach (OperationDepsNode *target, graph->operations) { /* Clear tags. */ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; + foreach (OperationDepsNode *node, graph->operations) { node->done = 0; } @@ -107,19 +92,14 @@ void deg_graph_transitive_reduction(Depsgraph *graph) * flagged. */ target->done |= OP_VISITED; - for (OperationDepsNode::Relations::const_iterator it = target->inlinks.begin(); - it != target->inlinks.end(); - ++it) - { - DepsRelation *rel = *it; - + foreach (DepsRelation *rel, target->inlinks) { deg_graph_tag_paths_recursive(rel->from); } - /* Eemove redundant paths to the target. */ + /* Remove redundant paths to the target. */ for (DepsNode::Relations::const_iterator it_rel = target->inlinks.begin(); it_rel != target->inlinks.end(); - ) + ) { DepsRelation *rel = *it_rel; /* Increment in advance, so we can safely remove the relation. */ @@ -137,3 +117,5 @@ void deg_graph_transitive_reduction(Depsgraph *graph) } } } + +} // namespace DEG diff --git a/source/blender/depsgraph/util/depsgraph_util_transitive.h b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h index a80a1d783d7..be9d7c3ca9c 100644 --- a/source/blender/depsgraph/util/depsgraph_util_transitive.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h @@ -24,15 +24,17 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_transitive.h +/** \file blender/depsgraph/intern/builder/deg_builder_transitive.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_TRANSITIVE_H__ -#define __DEPSGRAPH_UTIL_TRANSITIVE_H__ +#pragma once + +namespace DEG { struct Depsgraph; +/* Performs a transitive reduction to remove redundant relations. */ void deg_graph_transitive_reduction(Depsgraph *graph); -#endif /* __DEPSGRAPH_UTIL_TRANSITIVE_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc new file mode 100644 index 00000000000..5ce84ee29db --- /dev/null +++ b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc @@ -0,0 +1,588 @@ +/* + * ***** 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) 2014 Blender Foundation. + * All rights reserved. + * + * Original Author: Lukas Toenne + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/debug/deg_debug_graphviz.cc + * \ingroup depsgraph + * + * Implementation of tools for debugging the depsgraph + */ + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + +extern "C" { +#include "DNA_listBase.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_debug.h" +} /* extern "C" */ + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" + +/* ****************** */ +/* Graphviz Debugging */ + +namespace DEG { + +#define NL "\r\n" + +/* Only one should be enabled, defines whether graphviz nodes + * get colored by individual types or classes. + */ +#define COLOR_SCHEME_NODE_CLASS 1 +//#define COLOR_SCHEME_NODE_TYPE 2 + +static const char *deg_debug_graphviz_fontname = "helvetica"; +static float deg_debug_graphviz_graph_label_size = 20.0f; +static float deg_debug_graphviz_node_label_size = 14.0f; +static const int deg_debug_max_colors = 12; +#ifdef COLOR_SCHEME_NODE_TYPE +static const char *deg_debug_colors[] = { + "#a6cee3", "#1f78b4", "#b2df8a", + "#33a02c", "#fb9a99", "#e31a1c", + "#fdbf6f", "#ff7f00", "#cab2d6", + "#6a3d9a", "#ffff99", "#b15928", +}; +#endif +static const char *deg_debug_colors_light[] = { + "#8dd3c7", "#ffffb3", "#bebada", + "#fb8072", "#80b1d3", "#fdb462", + "#b3de69", "#fccde5", "#d9d9d9", + "#bc80bd", "#ccebc5", "#ffed6f", +}; + +#ifdef COLOR_SCHEME_NODE_TYPE +static const int deg_debug_node_type_color_map[][2] = { + {DEPSNODE_TYPE_ROOT, 0}, + {DEPSNODE_TYPE_TIMESOURCE, 1}, + {DEPSNODE_TYPE_ID_REF, 2}, + {DEPSNODE_TYPE_SUBGRAPH, 3}, + + /* Outer Types */ + {DEPSNODE_TYPE_PARAMETERS, 4}, + {DEPSNODE_TYPE_PROXY, 5}, + {DEPSNODE_TYPE_ANIMATION, 6}, + {DEPSNODE_TYPE_TRANSFORM, 7}, + {DEPSNODE_TYPE_GEOMETRY, 8}, + {DEPSNODE_TYPE_SEQUENCER, 9}, + {DEPSNODE_TYPE_SHADING, 10}, + {-1, 0} +}; +#endif + +static int deg_debug_node_color_index(const DepsNode *node) +{ +#ifdef COLOR_SCHEME_NODE_CLASS + /* Some special types. */ + switch (node->type) { + case DEPSNODE_TYPE_ID_REF: + return 5; + case DEPSNODE_TYPE_OPERATION: + { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->is_noop()) + return 8; + break; + } + + default: + break; + } + /* Do others based on class. */ + switch (node->tclass) { + case DEPSNODE_CLASS_OPERATION: + return 4; + case DEPSNODE_CLASS_COMPONENT: + return 1; + default: + return 9; + } +#endif + +#ifdef COLOR_SCHEME_NODE_TYPE + const int (*pair)[2]; + for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) { + if ((*pair)[0] == node->type) { + return (*pair)[1]; + } + } + return -1; +#endif +} + +struct DebugContext { + FILE *file; + bool show_tags; + bool show_eval_priority; +}; + +static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3); +static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(ctx.file, fmt, args); + va_end(args); +} + +static void deg_debug_graphviz_legend_color(const DebugContext &ctx, + const char *name, + const char *color) +{ + deg_debug_fprintf(ctx, "<TR>"); + deg_debug_fprintf(ctx, "<TD>%s</TD>", name); + deg_debug_fprintf(ctx, "<TD BGCOLOR=\"%s\"></TD>", color); + deg_debug_fprintf(ctx, "</TR>" NL); +} + +static void deg_debug_graphviz_legend(const DebugContext &ctx) +{ + deg_debug_fprintf(ctx, "{" NL); + deg_debug_fprintf(ctx, "rank = sink;" NL); + deg_debug_fprintf(ctx, "Legend [shape=none, margin=0, label=<" NL); + deg_debug_fprintf(ctx, " <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL); + deg_debug_fprintf(ctx, "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>" NL); + +#ifdef COLOR_SCHEME_NODE_CLASS + const char **colors = deg_debug_colors_light; + deg_debug_graphviz_legend_color(ctx, "Operation", colors[4]); + deg_debug_graphviz_legend_color(ctx, "Component", colors[1]); + deg_debug_graphviz_legend_color(ctx, "ID Node", colors[5]); + deg_debug_graphviz_legend_color(ctx, "NOOP", colors[8]); +#endif + +#ifdef COLOR_SCHEME_NODE_TYPE + const int (*pair)[2]; + for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) { + DepsNodeFactory *nti = DEG_get_node_factory((eDepsNode_Type)(*pair)[0]); + deg_debug_graphviz_legend_color(ctx, + nti->tname().c_str(), + deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors]); + } +#endif + + deg_debug_fprintf(ctx, "</TABLE>" NL); + deg_debug_fprintf(ctx, ">" NL); + deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); + deg_debug_fprintf(ctx, "];" NL); + deg_debug_fprintf(ctx, "}" NL); +} + +static void deg_debug_graphviz_node_color(const DebugContext &ctx, + const DepsNode *node) +{ + const char *color_default = "black"; + const char *color_modified = "orangered4"; + const char *color_update = "dodgerblue3"; + const char *color = color_default; + if (ctx.show_tags) { + if (node->tclass == DEPSNODE_CLASS_OPERATION) { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) { + color = color_modified; + } + else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { + color = color_update; + } + } + } + deg_debug_fprintf(ctx, "\"%s\"", color); +} + +static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx, + const DepsNode *node) +{ + float penwidth_default = 1.0f; + float penwidth_modified = 4.0f; + float penwidth_update = 4.0f; + float penwidth = penwidth_default; + if (ctx.show_tags) { + if (node->tclass == DEPSNODE_CLASS_OPERATION) { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) { + penwidth = penwidth_modified; + } + else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { + penwidth = penwidth_update; + } + } + } + deg_debug_fprintf(ctx, "\"%f\"", penwidth); +} + +static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx, + const DepsNode *node) +{ + const char *defaultcolor = "gainsboro"; + int color_index = deg_debug_node_color_index(node); + const char *fillcolor = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors]; + deg_debug_fprintf(ctx, "\"%s\"", fillcolor); +} + +static void deg_debug_graphviz_relation_color(const DebugContext &ctx, + const DepsRelation *rel) +{ + const char *color_default = "black"; + const char *color_error = "red4"; + const char *color = color_default; + if (rel->flag & DEPSREL_FLAG_CYCLIC) { + color = color_error; + } + deg_debug_fprintf(ctx, "%s", color); +} + +static void deg_debug_graphviz_node_style(const DebugContext &ctx, const DepsNode *node) +{ + const char *base_style = "filled"; /* default style */ + if (ctx.show_tags) { + if (node->tclass == DEPSNODE_CLASS_OPERATION) { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->flag & (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE)) { + base_style = "striped"; + } + } + } + switch (node->tclass) { + case DEPSNODE_CLASS_GENERIC: + deg_debug_fprintf(ctx, "\"%s\"", base_style); + break; + case DEPSNODE_CLASS_COMPONENT: + deg_debug_fprintf(ctx, "\"%s\"", base_style); + break; + case DEPSNODE_CLASS_OPERATION: + deg_debug_fprintf(ctx, "\"%s,rounded\"", base_style); + break; + } +} + +static void deg_debug_graphviz_node_single(const DebugContext &ctx, + const DepsNode *node) +{ + const char *shape = "box"; + string name = node->identifier(); + float priority = -1.0f; + if (node->type == DEPSNODE_TYPE_ID_REF) { + IDDepsNode *id_node = (IDDepsNode *)node; + char buf[256]; + BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers); + name += buf; + } + if (ctx.show_eval_priority && node->tclass == DEPSNODE_CLASS_OPERATION) { + priority = ((OperationDepsNode *)node)->eval_priority; + } + deg_debug_fprintf(ctx, "// %s\n", name.c_str()); + deg_debug_fprintf(ctx, "\"node_%p\"", node); + deg_debug_fprintf(ctx, "["); +// deg_debug_fprintf(ctx, "label=<<B>%s</B>>", name); + if (priority >= 0.0f) { + deg_debug_fprintf(ctx, "label=<%s<BR/>(<I>%.2f</I>)>", + name.c_str(), + priority); + } + else { + deg_debug_fprintf(ctx, "label=<%s>", name.c_str()); + } + deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); + deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_node_label_size); + deg_debug_fprintf(ctx, ",shape=%s", shape); + deg_debug_fprintf(ctx, ",style="); deg_debug_graphviz_node_style(ctx, node); + deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_node_color(ctx, node); + deg_debug_fprintf(ctx, ",fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); + deg_debug_fprintf(ctx, ",penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); + deg_debug_fprintf(ctx, "];" NL); + deg_debug_fprintf(ctx, NL); +} + +static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx, + const DepsNode *node) +{ + string name = node->identifier().c_str(); + if (node->type == DEPSNODE_TYPE_ID_REF) { + IDDepsNode *id_node = (IDDepsNode *)node; + char buf[256]; + BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers); + name += buf; + } + deg_debug_fprintf(ctx, "// %s\n", name.c_str()); + deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node); +// deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name); + deg_debug_fprintf(ctx, "label=<%s>;" NL, name.c_str()); + deg_debug_fprintf(ctx, "fontname=\"%s\";" NL, deg_debug_graphviz_fontname); + deg_debug_fprintf(ctx, "fontsize=%f;" NL, deg_debug_graphviz_node_label_size); + deg_debug_fprintf(ctx, "margin=\"%d\";" NL, 16); + deg_debug_fprintf(ctx, "style="); deg_debug_graphviz_node_style(ctx, node); deg_debug_fprintf(ctx, ";" NL); + deg_debug_fprintf(ctx, "color="); deg_debug_graphviz_node_color(ctx, node); deg_debug_fprintf(ctx, ";" NL); + deg_debug_fprintf(ctx, "fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); deg_debug_fprintf(ctx, ";" NL); + deg_debug_fprintf(ctx, "penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); deg_debug_fprintf(ctx, ";" NL); + /* dummy node, so we can add edges between clusters */ + deg_debug_fprintf(ctx, "\"node_%p\"", node); + deg_debug_fprintf(ctx, "["); + deg_debug_fprintf(ctx, "shape=%s", "point"); + deg_debug_fprintf(ctx, ",style=%s", "invis"); + deg_debug_fprintf(ctx, "];" NL); + deg_debug_fprintf(ctx, NL); +} + +static void deg_debug_graphviz_node_cluster_end(const DebugContext &ctx) +{ + deg_debug_fprintf(ctx, "}" NL); + deg_debug_fprintf(ctx, NL); +} + +static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, + const Depsgraph *graph); +static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, + const Depsgraph *graph); + +static void deg_debug_graphviz_node(const DebugContext &ctx, + const DepsNode *node) +{ + switch (node->type) { + case DEPSNODE_TYPE_ID_REF: + { + const IDDepsNode *id_node = (const IDDepsNode *)node; + if (BLI_ghash_size(id_node->components) == 0) { + deg_debug_graphviz_node_single(ctx, node); + } + else { + deg_debug_graphviz_node_cluster_begin(ctx, node); + GHASH_FOREACH_BEGIN(const ComponentDepsNode *, comp, id_node->components) + { + deg_debug_graphviz_node(ctx, comp); + } + GHASH_FOREACH_END(); + deg_debug_graphviz_node_cluster_end(ctx); + } + break; + } + case DEPSNODE_TYPE_SUBGRAPH: + { + SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; + if (sub_node->graph) { + deg_debug_graphviz_node_cluster_begin(ctx, node); + deg_debug_graphviz_graph_nodes(ctx, sub_node->graph); + deg_debug_graphviz_node_cluster_end(ctx); + } + else { + deg_debug_graphviz_node_single(ctx, node); + } + break; + } + case DEPSNODE_TYPE_PARAMETERS: + case DEPSNODE_TYPE_ANIMATION: + case DEPSNODE_TYPE_TRANSFORM: + case DEPSNODE_TYPE_PROXY: + case DEPSNODE_TYPE_GEOMETRY: + case DEPSNODE_TYPE_SEQUENCER: + case DEPSNODE_TYPE_EVAL_POSE: + case DEPSNODE_TYPE_BONE: + case DEPSNODE_TYPE_SHADING: + case DEPSNODE_TYPE_EVAL_PARTICLES: + { + ComponentDepsNode *comp_node = (ComponentDepsNode *)node; + if (!comp_node->operations.empty()) { + deg_debug_graphviz_node_cluster_begin(ctx, node); + foreach (DepsNode *op_node, comp_node->operations) { + deg_debug_graphviz_node(ctx, op_node); + } + deg_debug_graphviz_node_cluster_end(ctx); + } + else { + deg_debug_graphviz_node_single(ctx, node); + } + break; + } + default: + deg_debug_graphviz_node_single(ctx, node); + break; + } +} + +static bool deg_debug_graphviz_is_cluster(const DepsNode *node) +{ + switch (node->type) { + case DEPSNODE_TYPE_ID_REF: + { + const IDDepsNode *id_node = (const IDDepsNode *)node; + return BLI_ghash_size(id_node->components) > 0; + } + case DEPSNODE_TYPE_SUBGRAPH: + { + SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; + return sub_node->graph != NULL; + } + case DEPSNODE_TYPE_PARAMETERS: + case DEPSNODE_TYPE_ANIMATION: + case DEPSNODE_TYPE_TRANSFORM: + case DEPSNODE_TYPE_PROXY: + case DEPSNODE_TYPE_GEOMETRY: + case DEPSNODE_TYPE_SEQUENCER: + case DEPSNODE_TYPE_EVAL_POSE: + case DEPSNODE_TYPE_BONE: + { + ComponentDepsNode *comp_node = (ComponentDepsNode *)node; + return !comp_node->operations.empty(); + } + default: + return false; + } +} + +static bool deg_debug_graphviz_is_owner(const DepsNode *node, + const DepsNode *other) +{ + switch (node->tclass) { + case DEPSNODE_CLASS_COMPONENT: + { + ComponentDepsNode *comp_node = (ComponentDepsNode *)node; + if (comp_node->owner == other) + return true; + break; + } + case DEPSNODE_CLASS_OPERATION: + { + OperationDepsNode *op_node = (OperationDepsNode *)node; + if (op_node->owner == other) + return true; + else if (op_node->owner->owner == other) + return true; + break; + } + default: break; + } + return false; +} + +static void deg_debug_graphviz_node_relations(const DebugContext &ctx, + const DepsNode *node) +{ + foreach (DepsRelation *rel, node->inlinks) { + float penwidth = 2.0f; + + const DepsNode *tail = rel->to; /* same as node */ + const DepsNode *head = rel->from; + deg_debug_fprintf(ctx, "// %s -> %s\n", + head->identifier().c_str(), + tail->identifier().c_str()); + deg_debug_fprintf(ctx, "\"node_%p\"", head); + deg_debug_fprintf(ctx, " -> "); + deg_debug_fprintf(ctx, "\"node_%p\"", tail); + + deg_debug_fprintf(ctx, "["); + /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */ + deg_debug_fprintf(ctx, "id=\"%s\"", rel->name); + deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_relation_color(ctx, rel); + deg_debug_fprintf(ctx, ",penwidth=\"%f\"", penwidth); + /* NOTE: edge from node to own cluster is not possible and gives graphviz + * warning, avoid this here by just linking directly to the invisible + * placeholder node + */ + if (deg_debug_graphviz_is_cluster(tail) && !deg_debug_graphviz_is_owner(head, tail)) { + deg_debug_fprintf(ctx, ",ltail=\"cluster_%p\"", tail); + } + if (deg_debug_graphviz_is_cluster(head) && !deg_debug_graphviz_is_owner(tail, head)) { + deg_debug_fprintf(ctx, ",lhead=\"cluster_%p\"", head); + } + deg_debug_fprintf(ctx, "];" NL); + deg_debug_fprintf(ctx, NL); + } +} + +static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, + const Depsgraph *graph) +{ + if (graph->root_node) { + deg_debug_graphviz_node(ctx, graph->root_node); + } + GHASH_FOREACH_BEGIN (DepsNode *, node, graph->id_hash) + { + deg_debug_graphviz_node(ctx, node); + } + GHASH_FOREACH_END(); + TimeSourceDepsNode *time_source = graph->find_time_source(NULL); + if (time_source != NULL) { + deg_debug_graphviz_node(ctx, time_source); + } +} + +static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, + const Depsgraph *graph) +{ + GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash) + { + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components) + { + foreach (OperationDepsNode *op_node, comp_node->operations) { + deg_debug_graphviz_node_relations(ctx, op_node); + } + } + GHASH_FOREACH_END(); + } + GHASH_FOREACH_END(); + + TimeSourceDepsNode *time_source = graph->find_time_source(NULL); + if (time_source != NULL) { + deg_debug_graphviz_node_relations(ctx, time_source); + } +} + +} // namespace DEG + +void DEG_debug_graphviz(const Depsgraph *graph, FILE *f, const char *label, bool show_eval) +{ + if (!graph) { + return; + } + + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + + DEG::DebugContext ctx; + ctx.file = f; + ctx.show_tags = show_eval; + ctx.show_eval_priority = show_eval; + + DEG::deg_debug_fprintf(ctx, "digraph depgraph {" NL); + DEG::deg_debug_fprintf(ctx, "rankdir=LR;" NL); + DEG::deg_debug_fprintf(ctx, "graph ["); + DEG::deg_debug_fprintf(ctx, "compound=true"); + DEG::deg_debug_fprintf(ctx, ",labelloc=\"t\""); + DEG::deg_debug_fprintf(ctx, ",fontsize=%f", DEG::deg_debug_graphviz_graph_label_size); + DEG::deg_debug_fprintf(ctx, ",fontname=\"%s\"", DEG::deg_debug_graphviz_fontname); + DEG::deg_debug_fprintf(ctx, ",label=\"%s\"", label); + DEG::deg_debug_fprintf(ctx, ",splines=ortho"); + DEG::deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato + DEG::deg_debug_fprintf(ctx, "];" NL); + + DEG::deg_debug_graphviz_graph_nodes(ctx, deg_graph); + DEG::deg_debug_graphviz_graph_relations(ctx, deg_graph); + + DEG::deg_debug_graphviz_legend(ctx); + + DEG::deg_debug_fprintf(ctx, "}" NL); +} + +#undef NL diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 18c7c5bd8e1..2b7c63767ab 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -30,10 +30,14 @@ * Core routines for how the Depsgraph works. */ +#include "intern/depsgraph.h" /* own include */ + #include <string.h> #include "MEM_guardedalloc.h" +#include "BLI_utildefines.h" +#include "BLI_ghash.h" #include "BLI_listbase.h" extern "C" { @@ -50,11 +54,15 @@ extern "C" { } #include "DEG_depsgraph.h" -#include "depsgraph.h" /* own include */ -#include "depsnode.h" -#include "depsnode_operation.h" -#include "depsnode_component.h" -#include "depsgraph_intern.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" + +namespace DEG { static DEG_EditorUpdateIDCb deg_editor_update_id_cb = NULL; static DEG_EditorUpdateSceneCb deg_editor_update_scene_cb = NULL; @@ -66,6 +74,9 @@ Depsgraph::Depsgraph() layers(0) { BLI_spin_init(&lock); + id_hash = BLI_ghash_ptr_new("Depsgraph id hash"); + subgraphs = BLI_gset_ptr_new("Depsgraph subgraphs"); + entry_tags = BLI_gset_ptr_new("Depsgraph entry_tags"); } Depsgraph::~Depsgraph() @@ -73,6 +84,9 @@ Depsgraph::~Depsgraph() /* Free root node - it won't have been freed yet... */ clear_id_nodes(); clear_subgraph_nodes(); + BLI_ghash_free(id_hash, NULL, NULL); + BLI_gset_free(subgraphs, NULL); + BLI_gset_free(entry_tags, NULL); if (this->root_node != NULL) { OBJECT_GUARDED_DELETE(this->root_node, RootDepsNode); } @@ -235,10 +249,16 @@ DepsNode *Depsgraph::find_node_from_pointer(const PointerRNA *ptr, /* Node Management ---------------------------- */ +static void id_node_deleter(void *value) +{ + IDDepsNode *id_node = reinterpret_cast<IDDepsNode *>(value); + OBJECT_GUARDED_DELETE(id_node, IDDepsNode); +} + RootDepsNode *Depsgraph::add_root_node() { if (!root_node) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_ROOT); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_ROOT); root_node = (RootDepsNode *)factory->create_node(NULL, "", "Root (Scene)"); } return root_node; @@ -267,12 +287,12 @@ TimeSourceDepsNode *Depsgraph::find_time_source(const ID *id) const SubgraphDepsNode *Depsgraph::add_subgraph_node(const ID *id) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_SUBGRAPH); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_SUBGRAPH); SubgraphDepsNode *subgraph_node = (SubgraphDepsNode *)factory->create_node(id, "", id->name + 2); /* Add to subnodes list. */ - this->subgraphs.insert(subgraph_node); + BLI_gset_insert(subgraphs, subgraph_node); /* if there's an ID associated, add to ID-nodes lookup too */ if (id) { @@ -289,37 +309,34 @@ SubgraphDepsNode *Depsgraph::add_subgraph_node(const ID *id) void Depsgraph::remove_subgraph_node(SubgraphDepsNode *subgraph_node) { - subgraphs.erase(subgraph_node); + BLI_gset_remove(subgraphs, subgraph_node, NULL); OBJECT_GUARDED_DELETE(subgraph_node, SubgraphDepsNode); } void Depsgraph::clear_subgraph_nodes() { - for (Subgraphs::iterator it = subgraphs.begin(); - it != subgraphs.end(); - ++it) + GSET_FOREACH_BEGIN(SubgraphDepsNode *, subgraph_node, subgraphs) { - SubgraphDepsNode *subgraph_node = *it; OBJECT_GUARDED_DELETE(subgraph_node, SubgraphDepsNode); } - subgraphs.clear(); + GSET_FOREACH_END(); + BLI_gset_clear(subgraphs, NULL); } IDDepsNode *Depsgraph::find_id_node(const ID *id) const { - IDNodeMap::const_iterator it = this->id_hash.find(id); - return it != this->id_hash.end() ? it->second : NULL; + return reinterpret_cast<IDDepsNode *>(BLI_ghash_lookup(id_hash, id)); } IDDepsNode *Depsgraph::add_id_node(ID *id, const string &name) { IDDepsNode *id_node = find_id_node(id); if (!id_node) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_ID_REF); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_ID_REF); id_node = (IDDepsNode *)factory->create_node(id, "", name); id->tag |= LIB_TAG_DOIT; /* register */ - this->id_hash[id] = id_node; + BLI_ghash_insert(id_hash, id, id_node); } return id_node; } @@ -329,21 +346,14 @@ void Depsgraph::remove_id_node(const ID *id) IDDepsNode *id_node = find_id_node(id); if (id_node) { /* unregister */ - this->id_hash.erase(id); + BLI_ghash_remove(id_hash, id, NULL, NULL); OBJECT_GUARDED_DELETE(id_node, IDDepsNode); } } void Depsgraph::clear_id_nodes() { - for (IDNodeMap::const_iterator it = id_hash.begin(); - it != id_hash.end(); - ++it) - { - IDDepsNode *id_node = it->second; - OBJECT_GUARDED_DELETE(id_node, IDDepsNode); - } - id_hash.clear(); + BLI_ghash_clear(id_hash, NULL, id_node_deleter); } /* Add new relationship between two nodes. */ @@ -413,18 +423,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 -------------------------------------- */ @@ -439,33 +459,52 @@ void Depsgraph::add_entry_tag(OperationDepsNode *node) /* Add to graph-level set of directly modified nodes to start searching from. * NOTE: this is necessary since we have several thousand nodes to play with... */ - this->entry_tags.insert(node); + BLI_gset_insert(entry_tags, node); } void Depsgraph::clear_all_nodes() { clear_id_nodes(); clear_subgraph_nodes(); - id_hash.clear(); + BLI_ghash_clear(id_hash, NULL, NULL); if (this->root_node) { OBJECT_GUARDED_DELETE(this->root_node, RootDepsNode); root_node = NULL; } } +void deg_editors_id_update(Main *bmain, ID *id) +{ + if (deg_editor_update_id_cb != NULL) { + deg_editor_update_id_cb(bmain, id); + } +} + +void deg_editors_scene_update(Main *bmain, Scene *scene, bool updated) +{ + if (deg_editor_update_scene_cb != NULL) { + deg_editor_update_scene_cb(bmain, scene, updated); + } +} + +} // namespace DEG + /* **************** */ /* Public Graph API */ /* Initialize a new Depsgraph */ Depsgraph *DEG_graph_new() { - return OBJECT_GUARDED_NEW(Depsgraph); + DEG::Depsgraph *deg_depsgraph = OBJECT_GUARDED_NEW(DEG::Depsgraph); + return reinterpret_cast<Depsgraph *>(deg_depsgraph); } /* Free graph's contents and graph itself */ void DEG_graph_free(Depsgraph *graph) { - OBJECT_GUARDED_DELETE(graph, Depsgraph); + using DEG::Depsgraph; + DEG::Depsgraph *deg_depsgraph = reinterpret_cast<DEG::Depsgraph *>(graph); + OBJECT_GUARDED_DELETE(deg_depsgraph, Depsgraph); } /* Set callbacks which are being called when depsgraph changes. */ @@ -473,28 +512,14 @@ void DEG_editors_set_update_cb(DEG_EditorUpdateIDCb id_func, DEG_EditorUpdateSceneCb scene_func, DEG_EditorUpdateScenePreCb scene_pre_func) { - deg_editor_update_id_cb = id_func; - deg_editor_update_scene_cb = scene_func; - deg_editor_update_scene_pre_cb = scene_pre_func; + DEG::deg_editor_update_id_cb = id_func; + DEG::deg_editor_update_scene_cb = scene_func; + DEG::deg_editor_update_scene_pre_cb = scene_pre_func; } void DEG_editors_update_pre(Main *bmain, Scene *scene, bool time) { - if (deg_editor_update_scene_pre_cb != NULL) { - deg_editor_update_scene_pre_cb(bmain, scene, time); - } -} - -void deg_editors_id_update(Main *bmain, ID *id) -{ - if (deg_editor_update_id_cb != NULL) { - deg_editor_update_id_cb(bmain, id); - } -} - -void deg_editors_scene_update(Main *bmain, Scene *scene, bool updated) -{ - if (deg_editor_update_scene_cb != NULL) { - deg_editor_update_scene_cb(bmain, scene, updated); + if (DEG::deg_editor_update_scene_pre_cb != NULL) { + DEG::deg_editor_update_scene_pre_cb(bmain, scene, time); } } diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index 9533fbd10d5..213bb304d73 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -34,19 +34,20 @@ * in the graph. */ -#ifndef __DEPSGRAPH_H__ -#define __DEPSGRAPH_H__ +#pragma once #include "BLI_threads.h" /* for SpinLock */ -#include "depsgraph_types.h" - -#include "depsgraph_util_map.h" -#include "depsgraph_util_set.h" +#include "intern/depsgraph_types.h" +struct ID; +struct GHash; +struct GSet; struct PointerRNA; struct PropertyRNA; +namespace DEG { + struct DepsNode; struct RootDepsNode; struct TimeSourceDepsNode; @@ -94,9 +95,6 @@ struct DepsRelation { /* Dependency Graph object */ struct Depsgraph { - typedef unordered_map<const ID *, IDDepsNode *> IDNodeMap; - typedef unordered_set<SubgraphDepsNode *> Subgraphs; - typedef unordered_set<OperationDepsNode *> EntryTags; typedef vector<OperationDepsNode *> OperationNodes; Depsgraph(); @@ -163,13 +161,13 @@ struct Depsgraph { /* <ID : IDDepsNode> mapping from ID blocks to nodes representing these blocks * (for quick lookups). */ - IDNodeMap id_hash; + GHash *id_hash; /* "root" node - the one where all evaluation enters from. */ RootDepsNode *root_node; /* Subgraphs referenced in tree. */ - Subgraphs subgraphs; + GSet *subgraphs; /* Indicates whether relations needs to be updated. */ bool need_update; @@ -177,7 +175,7 @@ struct Depsgraph { /* Quick-Access Temp Data ............. */ /* Nodes which have been tagged as "directly modified". */ - EntryTags entry_tags; + GSet *entry_tags; /* Convenience Data ................... */ @@ -198,27 +196,4 @@ struct Depsgraph { // XXX: additional stuff like eval contexts, mempools for allocating nodes from, etc. }; -/** - * Helper macros for iterating over set of relationship links - * incident on each node. - * - * \note it is safe to perform removal operations here... - * - * relations_set[in]: (DepsNode::Relations) set of relationships (in/out links) - * relation[out]: (DepsRelation *) identifier where DepsRelation that we're - * currently accessing comes up - */ -#define DEPSNODE_RELATIONS_ITER_BEGIN(relations_set_, relation_) \ - { \ - OperationDepsNode::Relations::const_iterator __rel_iter = relations_set_.begin(); \ - while (__rel_iter != relations_set_.end()) { \ - DepsRelation *relation_ = *__rel_iter; \ - ++__rel_iter; \ - - /* ... code for iterator body can be written here ... */ - -#define DEPSNODE_RELATIONS_ITER_END \ - } \ - } ((void)0) - -#endif /* __DEPSGRAPH_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index a62b23bde68..b1271c39851 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -30,137 +30,128 @@ * Methods for constructing depsgraph. */ -#include <stack> - #include "MEM_guardedalloc.h" extern "C" { -#include "BLI_blenlib.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "DNA_action_types.h" -#include "DNA_anim_types.h" -#include "DNA_armature_types.h" -#include "DNA_camera_types.h" -#include "DNA_constraint_types.h" -#include "DNA_curve_types.h" -#include "DNA_effect_types.h" -#include "DNA_group_types.h" -#include "DNA_key_types.h" -#include "DNA_lamp_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -#include "DNA_node_types.h" -#include "DNA_particle_types.h" #include "DNA_object_types.h" -#include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" -#include "DNA_texture_types.h" -#include "DNA_world_types.h" - -#include "BKE_action.h" -#include "BKE_armature.h" -#include "BKE_animsys.h" -#include "BKE_constraint.h" -#include "BKE_curve.h" -#include "BKE_effect.h" -#include "BKE_fcurve.h" -#include "BKE_group.h" -#include "BKE_key.h" -#include "BKE_library.h" + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + #include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_mball.h" -#include "BKE_modifier.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_particle.h" -#include "BKE_rigidbody.h" -#include "BKE_sound.h" -#include "BKE_texture.h" -#include "BKE_tracking.h" -#include "BKE_world.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" #include "DEG_depsgraph_build.h" -#include "RNA_access.h" -#include "RNA_types.h" } /* extern "C" */ -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsgraph_debug.h" -#include "depsnode_operation.h" -#include "depsgraph_types.h" -#include "depsgraph_build.h" -#include "depsgraph_intern.h" +#include "builder/deg_builder.h" +#include "builder/deg_builder_cycle.h" +#include "builder/deg_builder_nodes.h" +#include "builder/deg_builder_relations.h" +#include "builder/deg_builder_transitive.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" -#include "depsgraph_util_cycle.h" -#include "depsgraph_util_transitive.h" +#include "intern/depsgraph_types.h" +#include "intern/depsgraph_intern.h" + +#include "util/deg_util_foreach.h" /* ****************** */ /* External Build API */ -static eDepsNode_Type deg_build_scene_component_type(eDepsSceneComponentType component) +static DEG::eDepsNode_Type deg_build_scene_component_type( + eDepsSceneComponentType component) { switch (component) { - case DEG_SCENE_COMP_PARAMETERS: return DEPSNODE_TYPE_PARAMETERS; - case DEG_SCENE_COMP_ANIMATION: return DEPSNODE_TYPE_ANIMATION; - case DEG_SCENE_COMP_SEQUENCER: return DEPSNODE_TYPE_SEQUENCER; + case DEG_SCENE_COMP_PARAMETERS: return DEG::DEPSNODE_TYPE_PARAMETERS; + case DEG_SCENE_COMP_ANIMATION: return DEG::DEPSNODE_TYPE_ANIMATION; + case DEG_SCENE_COMP_SEQUENCER: return DEG::DEPSNODE_TYPE_SEQUENCER; } - return DEPSNODE_TYPE_UNDEFINED; + return DEG::DEPSNODE_TYPE_UNDEFINED; } -static eDepsNode_Type deg_build_object_component_type(eDepsObjectComponentType component) +static DEG::eDepsNode_Type deg_build_object_component_type( + eDepsObjectComponentType component) { switch (component) { - case DEG_OB_COMP_PARAMETERS: return DEPSNODE_TYPE_PARAMETERS; - case DEG_OB_COMP_PROXY: return DEPSNODE_TYPE_PROXY; - case DEG_OB_COMP_ANIMATION: return DEPSNODE_TYPE_ANIMATION; - case DEG_OB_COMP_TRANSFORM: return DEPSNODE_TYPE_TRANSFORM; - case DEG_OB_COMP_GEOMETRY: return DEPSNODE_TYPE_GEOMETRY; - case DEG_OB_COMP_EVAL_POSE: return DEPSNODE_TYPE_EVAL_POSE; - case DEG_OB_COMP_BONE: return DEPSNODE_TYPE_BONE; - case DEG_OB_COMP_EVAL_PARTICLES: return DEPSNODE_TYPE_EVAL_PARTICLES; - case DEG_OB_COMP_SHADING: return DEPSNODE_TYPE_SHADING; + case DEG_OB_COMP_PARAMETERS: return DEG::DEPSNODE_TYPE_PARAMETERS; + case DEG_OB_COMP_PROXY: return DEG::DEPSNODE_TYPE_PROXY; + case DEG_OB_COMP_ANIMATION: return DEG::DEPSNODE_TYPE_ANIMATION; + case DEG_OB_COMP_TRANSFORM: return DEG::DEPSNODE_TYPE_TRANSFORM; + case DEG_OB_COMP_GEOMETRY: return DEG::DEPSNODE_TYPE_GEOMETRY; + case DEG_OB_COMP_EVAL_POSE: return DEG::DEPSNODE_TYPE_EVAL_POSE; + case DEG_OB_COMP_BONE: return DEG::DEPSNODE_TYPE_BONE; + case DEG_OB_COMP_EVAL_PARTICLES: return DEG::DEPSNODE_TYPE_EVAL_PARTICLES; + case DEG_OB_COMP_SHADING: return DEG::DEPSNODE_TYPE_SHADING; } - return DEPSNODE_TYPE_UNDEFINED; + return DEG::DEPSNODE_TYPE_UNDEFINED; } -void DEG_add_scene_relation(DepsNodeHandle *handle, struct Scene *scene, eDepsSceneComponentType component, const char *description) +static DEG::DepsNodeHandle *get_handle(DepsNodeHandle *handle) { - eDepsNode_Type type = deg_build_scene_component_type(component); - ComponentKey comp_key(&scene->id, type); - handle->builder->add_node_handle_relation(comp_key, handle, DEPSREL_TYPE_GEOMETRY_EVAL, description); + return reinterpret_cast<DEG::DepsNodeHandle *>(handle); } -void DEG_add_object_relation(DepsNodeHandle *handle, struct Object *ob, eDepsObjectComponentType component, const char *description) +void DEG_add_scene_relation(DepsNodeHandle *handle, + Scene *scene, + eDepsSceneComponentType component, + const char *description) { - eDepsNode_Type type = deg_build_object_component_type(component); - ComponentKey comp_key(&ob->id, type); - handle->builder->add_node_handle_relation(comp_key, handle, DEPSREL_TYPE_GEOMETRY_EVAL, description); + DEG::eDepsNode_Type type = deg_build_scene_component_type(component); + DEG::ComponentKey comp_key(&scene->id, type); + DEG::DepsNodeHandle *deg_handle = get_handle(handle); + deg_handle->builder->add_node_handle_relation(comp_key, + deg_handle, + DEG::DEPSREL_TYPE_GEOMETRY_EVAL, + description); } -void DEG_add_bone_relation(DepsNodeHandle *handle, struct Object *ob, const char *bone_name, eDepsObjectComponentType component, const char *description) +void DEG_add_object_relation(DepsNodeHandle *handle, + Object *ob, + eDepsObjectComponentType component, + const char *description) { - eDepsNode_Type type = deg_build_object_component_type(component); - ComponentKey comp_key(&ob->id, type, bone_name); + DEG::eDepsNode_Type type = deg_build_object_component_type(component); + DEG::ComponentKey comp_key(&ob->id, type); + DEG::DepsNodeHandle *deg_handle = get_handle(handle); + deg_handle->builder->add_node_handle_relation(comp_key, + deg_handle, + DEG::DEPSREL_TYPE_GEOMETRY_EVAL, + description); +} - // XXX: "Geometry Eval" might not always be true, but this only gets called from modifier building now - handle->builder->add_node_handle_relation(comp_key, handle, DEPSREL_TYPE_GEOMETRY_EVAL, description); +void DEG_add_bone_relation(DepsNodeHandle *handle, + Object *ob, + const char *bone_name, + eDepsObjectComponentType component, + const char *description) +{ + DEG::eDepsNode_Type type = deg_build_object_component_type(component); + DEG::ComponentKey comp_key(&ob->id, type, bone_name); + DEG::DepsNodeHandle *deg_handle = get_handle(handle); + /* XXX: "Geometry Eval" might not always be true, but this only gets called + * from modifier building now. + */ + deg_handle->builder->add_node_handle_relation(comp_key, + deg_handle, + DEG::DEPSREL_TYPE_GEOMETRY_EVAL, + description); } void DEG_add_special_eval_flag(Depsgraph *graph, ID *id, short flag) { + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); if (graph == NULL) { BLI_assert(!"Graph should always be valid"); return; } - IDDepsNode *id_node = graph->find_id_node(id); + DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); if (id_node == NULL) { BLI_assert(!"ID should always be valid"); return; @@ -168,106 +159,21 @@ void DEG_add_special_eval_flag(Depsgraph *graph, ID *id, short flag) id_node->eval_flags |= flag; } -/* ********************** */ -/* Utilities for Builders */ - -/* Get unique identifier for FCurves and Drivers */ -string deg_fcurve_id_name(const FCurve *fcu) -{ - char index_buf[32]; - sprintf(index_buf, "[%d]", fcu->array_index); - - return string(fcu->rna_path) + index_buf; -} - -static void deg_graph_build_finalize(Depsgraph *graph) -{ - std::stack<OperationDepsNode *> stack; - - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; - node->done = 0; - node->num_links_pending = 0; - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if ((rel->from->type == DEPSNODE_TYPE_OPERATION) && - (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) - { - ++node->num_links_pending; - } - } - if (node->num_links_pending == 0) { - stack.push(node); - } - IDDepsNode *id_node = node->owner->owner; - id_node->id->tag |= LIB_TAG_DOIT; - } - - while (!stack.empty()) { - OperationDepsNode *node = stack.top(); - if (node->done == 0 && node->outlinks.size() != 0) { - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if (rel->to->type == DEPSNODE_TYPE_OPERATION) { - OperationDepsNode *to = (OperationDepsNode *)rel->to; - if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - BLI_assert(to->num_links_pending > 0); - --to->num_links_pending; - } - if (to->num_links_pending == 0) { - stack.push(to); - } - } - } - node->done = 1; - } - else { - stack.pop(); - IDDepsNode *id_node = node->owner->owner; - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if (rel->to->type == DEPSNODE_TYPE_OPERATION) { - OperationDepsNode *to = (OperationDepsNode *)rel->to; - IDDepsNode *id_to = to->owner->owner; - 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; - } - } - } -} - /* ******************** */ /* Graph Building API's */ -/* Build depsgraph for the given scene, and dump results in given graph container */ -// XXX: assume that this is called from outside, given the current scene as the "main" scene +/* Build depsgraph for the given scene, and dump results in given + * graph container. + */ +/* XXX: assume that this is called from outside, given the current scene as + * the "main" scene. + */ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) { + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + /* 1) Generate all the nodes in the graph first */ - DepsgraphNodeBuilder node_builder(bmain, graph); + DEG::DepsgraphNodeBuilder node_builder(bmain, deg_graph); /* create root node for scene first * - this way it should be the first in the graph, * reflecting its role as the entrypoint @@ -275,29 +181,40 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) node_builder.add_root_node(); node_builder.build_scene(bmain, scene); - /* 2) Hook up relationships between operations - to determine evaluation order */ - DepsgraphRelationBuilder relation_builder(graph); - /* hook scene up to the root node as entrypoint to graph */ + /* 2) Hook up relationships between operations - to determine evaluation + * order. + */ + DEG::DepsgraphRelationBuilder relation_builder(deg_graph); + /* Hook scene up to the root node as entrypoint to graph. */ /* XXX what does this relation actually mean? - * it doesnt add any operations anyway and is not clear what part of the scene is to be connected. + * it doesnt add any operations anyway and is not clear what part of the + * scene is to be connected. */ - //relation_builder.add_relation(RootKey(), IDKey(scene), DEPSREL_TYPE_ROOT_TO_ACTIVE, "Root to Active Scene"); +#if 0 + relation_builder.add_relation(RootKey(), + IDKey(scene), + DEPSREL_TYPE_ROOT_TO_ACTIVE, + "Root to Active Scene"); +#endif relation_builder.build_scene(bmain, scene); /* Detect and solve cycles. */ - deg_graph_detect_cycles(graph); + DEG::deg_graph_detect_cycles(deg_graph); - /* 3) Simplify the graph by removing redundant relations (to optimise traversal later) */ - // TODO: it would be useful to have an option to disable this in cases where it is causing trouble + /* 3) Simplify the graph by removing redundant relations (to optimize + * traversal later). */ + /* TODO: it would be useful to have an option to disable this in cases where + * it is causing trouble. + */ if (G.debug_value == 799) { - deg_graph_transitive_reduction(graph); + DEG::deg_graph_transitive_reduction(deg_graph); } /* 4) Flush visibility layer and re-schedule nodes for update. */ - deg_graph_build_finalize(graph); + DEG::deg_graph_build_finalize(deg_graph); #if 0 - if (!DEG_debug_consistency_check(graph)) { + if (!DEG_debug_consistency_check(deg_graph)) { printf("Consistency validation failed, ABORTING!\n"); abort(); } @@ -307,7 +224,8 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) /* Tag graph relations for update. */ void DEG_graph_tag_relations_update(Depsgraph *graph) { - graph->need_update = true; + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + deg_graph->need_update = true; } /* Tag all relations for update. */ @@ -335,7 +253,7 @@ void DEG_scene_relations_update(Main *bmain, Scene *scene) return; } - Depsgraph *graph = scene->depsgraph; + DEG::Depsgraph *graph = reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph); if (!graph->need_update) { /* Graph is up to date, nothing to do. */ return; @@ -344,10 +262,12 @@ void DEG_scene_relations_update(Main *bmain, Scene *scene) /* Clear all previous nodes and operations. */ graph->clear_all_nodes(); graph->operations.clear(); - graph->entry_tags.clear(); + BLI_gset_clear(graph->entry_tags, NULL); /* Build new nodes and relations. */ - DEG_graph_build_from_scene(graph, bmain, scene); + DEG_graph_build_from_scene(reinterpret_cast< ::Depsgraph * >(graph), + bmain, + scene); graph->need_update = false; } diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc index efb3e330857..d3b48930779 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -30,956 +30,39 @@ * Implementation of tools for debugging the depsgraph */ -//#include <stdlib.h> -#include <string.h> - -extern "C" { #include "BLI_utildefines.h" -#include "BLI_listbase.h" #include "BLI_ghash.h" -#include "BLI_string.h" +extern "C" { #include "DNA_scene_types.h" -#include "DNA_userdef_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" #include "DEG_depsgraph_build.h" - -#include "WM_api.h" -#include "WM_types.h" } /* extern "C" */ -#include "depsgraph_debug.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_intern.h" - -/* ****************** */ -/* Graphviz Debugging */ - -#define NL "\r\n" - -/* Only one should be enabled, defines whether graphviz nodes - * get colored by individual types or classes. - */ -#define COLOR_SCHEME_NODE_CLASS 1 -//#define COLOR_SCHEME_NODE_TYPE 2 - -static const char *deg_debug_graphviz_fontname = "helvetica"; -static float deg_debug_graphviz_graph_label_size = 20.0f; -static float deg_debug_graphviz_node_label_size = 14.0f; -static const int deg_debug_max_colors = 12; -#if 0 -static const char *deg_debug_colors_dark[] = { - "#6e8997", "#144f77", "#76945b", - "#216a1d", "#a76665", "#971112", - "#a87f49", "#0a9540", "#86768e", - "#462866", "#a9a965", "#753b1a", -}; -#endif -#ifdef COLOR_SCHEME_NODE_TYPE -static const char *deg_debug_colors[] = { - "#a6cee3", "#1f78b4", "#b2df8a", - "#33a02c", "#fb9a99", "#e31a1c", - "#fdbf6f", "#ff7f00", "#cab2d6", - "#6a3d9a", "#ffff99", "#b15928", -}; -#endif -static const char *deg_debug_colors_light[] = { - "#8dd3c7", "#ffffb3", "#bebada", - "#fb8072", "#80b1d3", "#fdb462", - "#b3de69", "#fccde5", "#d9d9d9", - "#bc80bd", "#ccebc5", "#ffed6f", -}; - -#ifdef COLOR_SCHEME_NODE_TYPE -static const int deg_debug_node_type_color_map[][2] = { - {DEPSNODE_TYPE_ROOT, 0}, - {DEPSNODE_TYPE_TIMESOURCE, 1}, - {DEPSNODE_TYPE_ID_REF, 2}, - {DEPSNODE_TYPE_SUBGRAPH, 3}, - - /* Outer Types */ - {DEPSNODE_TYPE_PARAMETERS, 4}, - {DEPSNODE_TYPE_PROXY, 5}, - {DEPSNODE_TYPE_ANIMATION, 6}, - {DEPSNODE_TYPE_TRANSFORM, 7}, - {DEPSNODE_TYPE_GEOMETRY, 8}, - {DEPSNODE_TYPE_SEQUENCER, 9}, - {DEPSNODE_TYPE_SHADING, 10}, - {-1, 0} -}; -#endif - -#if 0 /* unused */ -static const int deg_debug_relation_type_color_map[][2] = { - {DEPSREL_TYPE_STANDARD, 0}, - {DEPSREL_TYPE_ROOT_TO_ACTIVE, 1}, - {DEPSREL_TYPE_DATABLOCK, 2}, - {DEPSREL_TYPE_TIME, 3}, - {DEPSREL_TYPE_COMPONENT_ORDER, 4}, - {DEPSREL_TYPE_OPERATION, 5}, - {DEPSREL_TYPE_DRIVER, 6}, - {DEPSREL_TYPE_DRIVER_TARGET, 7}, - {DEPSREL_TYPE_TRANSFORM, 8}, - {DEPSREL_TYPE_GEOMETRY_EVAL, 9}, - {DEPSREL_TYPE_UPDATE, 10}, - {DEPSREL_TYPE_UPDATE_UI, 11}, - {-1, 0} -}; -#endif - -static int deg_debug_node_color_index(const DepsNode *node) -{ -#ifdef COLOR_SCHEME_NODE_CLASS - /* Some special types. */ - switch (node->type) { - case DEPSNODE_TYPE_ID_REF: - return 5; - case DEPSNODE_TYPE_OPERATION: - { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->is_noop()) - return 8; - break; - } - - default: - break; - } - /* Do others based on class. */ - switch (node->tclass) { - case DEPSNODE_CLASS_OPERATION: - return 4; - case DEPSNODE_CLASS_COMPONENT: - return 1; - default: - return 9; - } -#endif - -#ifdef COLOR_SCHEME_NODE_TYPE - const int (*pair)[2]; - for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) { - if ((*pair)[0] == node->type) { - return (*pair)[1]; - } - } - return -1; -#endif -} - -struct DebugContext { - FILE *file; - bool show_tags; - bool show_eval_priority; -}; - -static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3); -static void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vfprintf(ctx.file, fmt, args); - va_end(args); -} - -static void deg_debug_graphviz_legend_color(const DebugContext &ctx, - const char *name, - const char *color) -{ - deg_debug_fprintf(ctx, "<TR>"); - deg_debug_fprintf(ctx, "<TD>%s</TD>", name); - deg_debug_fprintf(ctx, "<TD BGCOLOR=\"%s\"></TD>", color); - deg_debug_fprintf(ctx, "</TR>" NL); -} - -#if 0 -static void deg_debug_graphviz_legend_line(const DebugContext &ctx, - const char *name, - const char *color, - const char *style) -{ - /* XXX TODO */ - deg_debug_fprintf(ctx, "" NL); -} - -static void deg_debug_graphviz_legend_cluster(const DebugContext &ctx, - const char *name, - const char *color, - const char *style) -{ - deg_debug_fprintf(ctx, "<TR>"); - deg_debug_fprintf(ctx, "<TD>%s</TD>", name); - deg_debug_fprintf(ctx, "<TD CELLPADDING=\"4\"><TABLE BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">"); - deg_debug_fprintf(ctx, "<TR><TD BGCOLOR=\"%s\"></TD></TR>", color); - deg_debug_fprintf(ctx, "</TABLE></TD>"); - deg_debug_fprintf(ctx, "</TR>" NL); -} -#endif - -static void deg_debug_graphviz_legend(const DebugContext &ctx) -{ - deg_debug_fprintf(ctx, "{" NL); - deg_debug_fprintf(ctx, "rank = sink;" NL); - deg_debug_fprintf(ctx, "Legend [shape=none, margin=0, label=<" NL); - deg_debug_fprintf(ctx, " <TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">" NL); - deg_debug_fprintf(ctx, "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>" NL); - -#ifdef COLOR_SCHEME_NODE_CLASS - const char **colors = deg_debug_colors_light; - deg_debug_graphviz_legend_color(ctx, "Operation", colors[4]); - deg_debug_graphviz_legend_color(ctx, "Component", colors[1]); - deg_debug_graphviz_legend_color(ctx, "ID Node", colors[5]); - deg_debug_graphviz_legend_color(ctx, "NOOP", colors[8]); -#endif - -#ifdef COLOR_SCHEME_NODE_TYPE - const int (*pair)[2]; - for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; ++pair) { - DepsNodeFactory *nti = DEG_get_node_factory((eDepsNode_Type)(*pair)[0]); - deg_debug_graphviz_legend_color(ctx, - nti->tname().c_str(), - deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors]); - } -#endif - - deg_debug_fprintf(ctx, "</TABLE>" NL); - deg_debug_fprintf(ctx, ">" NL); - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, "}" NL); -} - -#if 0 /* unused */ -static int deg_debug_relation_type_color_index(eDepsRelation_Type type) -{ - const int (*pair)[2]; - for (pair = deg_debug_relation_type_color_map; (*pair)[0] >= 0; ++pair) { - if ((*pair)[0] == type) { - return (*pair)[1]; - } - } - return -1; -} -#endif - -static void deg_debug_graphviz_node_color(const DebugContext &ctx, - const DepsNode *node) -{ - const char *color_default = "black"; - const char *color_modified = "orangered4"; - const char *color_update = "dodgerblue3"; - const char *color = color_default; - if (ctx.show_tags) { - if (node->tclass == DEPSNODE_CLASS_OPERATION) { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) { - color = color_modified; - } - else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { - color = color_update; - } - } - } - deg_debug_fprintf(ctx, "\"%s\"", color); -} - -static void deg_debug_graphviz_node_penwidth(const DebugContext &ctx, - const DepsNode *node) -{ - float penwidth_default = 1.0f; - float penwidth_modified = 4.0f; - float penwidth_update = 4.0f; - float penwidth = penwidth_default; - if (ctx.show_tags) { - if (node->tclass == DEPSNODE_CLASS_OPERATION) { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) { - penwidth = penwidth_modified; - } - else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { - penwidth = penwidth_update; - } - } - } - deg_debug_fprintf(ctx, "\"%f\"", penwidth); -} - -static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx, - const DepsNode *node) -{ - const char *defaultcolor = "gainsboro"; - int color_index = deg_debug_node_color_index(node); - const char *fillcolor = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors]; - deg_debug_fprintf(ctx, "\"%s\"", fillcolor); -} - -#if 0 /* implementation using stripes, a bit too noisy ... */ -static void deg_debug_graphviz_node_fillcolor(const DebugContext &ctx, - const DepsNode *node) -{ - const char *defaultcolor = "gainsboro"; - const char *color_needs_update = "orange"; - const int num_stripes = 10; - int color_index = deg_debug_node_color_index(node); - const char *base_color = color_index < 0 ? defaultcolor : deg_debug_colors_light[color_index % deg_debug_max_colors]; - if (ctx.show_tags && - (node->flag & (DEPSNODE_FLAG_DIRECTLY_MODIFIED | DEPSNODE_FLAG_NEEDS_UPDATE))) - { - deg_debug_fprintf(ctx, "\""); - for (int i = 0; i < num_stripes; ++i) { - if (i > 0) { - deg_debug_fprintf(ctx, ":"); - } - deg_debug_fprintf(ctx, "%s:%s", base_color, color_needs_update); - } - deg_debug_fprintf(ctx, "\""); - } - else { - deg_debug_fprintf(ctx, "\"%s\"", base_color); - } -} -#endif - -static void deg_debug_graphviz_relation_color(const DebugContext &ctx, - const DepsRelation *rel) -{ - const char *color_default = "black"; - const char *color_error = "red4"; - const char *color = color_default; -#if 0 /* disabled for now, edge colors are hardly distinguishable */ - int color = deg_debug_relation_type_color_index(rel->type); - if (color < 0) { - deg_debug_fprintf(ctx, "%s", defaultcolor); - } - else { - deg_debug_fprintf(ctx, "\"%s\"", deg_debug_colors_dark[color % deg_debug_max_colors]); - } -#else - if (rel->flag & DEPSREL_FLAG_CYCLIC) - color = color_error; - - deg_debug_fprintf(ctx, "%s", color); -#endif -} - -static void deg_debug_graphviz_node_style(const DebugContext &ctx, const DepsNode *node) -{ - const char *base_style = "filled"; /* default style */ - if (ctx.show_tags) { - if (node->tclass == DEPSNODE_CLASS_OPERATION) { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->flag & (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE)) { - base_style = "striped"; - } - } - } - switch (node->tclass) { - case DEPSNODE_CLASS_GENERIC: - deg_debug_fprintf(ctx, "\"%s\"", base_style); - break; - case DEPSNODE_CLASS_COMPONENT: - deg_debug_fprintf(ctx, "\"%s\"", base_style); - break; - case DEPSNODE_CLASS_OPERATION: - deg_debug_fprintf(ctx, "\"%s,rounded\"", base_style); - break; - } -} - -static void deg_debug_graphviz_node_single(const DebugContext &ctx, - const DepsNode *node) -{ - const char *shape = "box"; - string name = node->identifier(); - float priority = -1.0f; - if (node->type == DEPSNODE_TYPE_ID_REF) { - IDDepsNode *id_node = (IDDepsNode *)node; - char buf[256]; - BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers); - name += buf; - } - if (ctx.show_eval_priority && node->tclass == DEPSNODE_CLASS_OPERATION) { - priority = ((OperationDepsNode *)node)->eval_priority; - } - deg_debug_fprintf(ctx, "// %s\n", name.c_str()); - deg_debug_fprintf(ctx, "\"node_%p\"", node); - deg_debug_fprintf(ctx, "["); -// deg_debug_fprintf(ctx, "label=<<B>%s</B>>", name); - if (priority >= 0.0f) { - deg_debug_fprintf(ctx, "label=<%s<BR/>(<I>%.2f</I>)>", - name.c_str(), - priority); - } - else { - deg_debug_fprintf(ctx, "label=<%s>", name.c_str()); - } - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_node_label_size); - deg_debug_fprintf(ctx, ",shape=%s", shape); - deg_debug_fprintf(ctx, ",style="); deg_debug_graphviz_node_style(ctx, node); - deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_node_color(ctx, node); - deg_debug_fprintf(ctx, ",fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); - deg_debug_fprintf(ctx, ",penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); -} - -static void deg_debug_graphviz_node_cluster_begin(const DebugContext &ctx, - const DepsNode *node) -{ - string name = node->identifier().c_str(); - if (node->type == DEPSNODE_TYPE_ID_REF) { - IDDepsNode *id_node = (IDDepsNode *)node; - char buf[256]; - BLI_snprintf(buf, sizeof(buf), " (Layers: %d)", id_node->layers); - name += buf; - } - deg_debug_fprintf(ctx, "// %s\n", name.c_str()); - deg_debug_fprintf(ctx, "subgraph \"cluster_%p\" {" NL, node); -// deg_debug_fprintf(ctx, "label=<<B>%s</B>>;" NL, name); - deg_debug_fprintf(ctx, "label=<%s>;" NL, name.c_str()); - deg_debug_fprintf(ctx, "fontname=\"%s\";" NL, deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, "fontsize=%f;" NL, deg_debug_graphviz_node_label_size); - deg_debug_fprintf(ctx, "margin=\"%d\";" NL, 16); - deg_debug_fprintf(ctx, "style="); deg_debug_graphviz_node_style(ctx, node); deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "color="); deg_debug_graphviz_node_color(ctx, node); deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "fillcolor="); deg_debug_graphviz_node_fillcolor(ctx, node); deg_debug_fprintf(ctx, ";" NL); - deg_debug_fprintf(ctx, "penwidth="); deg_debug_graphviz_node_penwidth(ctx, node); deg_debug_fprintf(ctx, ";" NL); - /* dummy node, so we can add edges between clusters */ - deg_debug_fprintf(ctx, "\"node_%p\"", node); - deg_debug_fprintf(ctx, "["); - deg_debug_fprintf(ctx, "shape=%s", "point"); - deg_debug_fprintf(ctx, ",style=%s", "invis"); - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); -} - -static void deg_debug_graphviz_node_cluster_end(const DebugContext &ctx) -{ - deg_debug_fprintf(ctx, "}" NL); - deg_debug_fprintf(ctx, NL); -} - -static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, - const Depsgraph *graph); -static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, - const Depsgraph *graph); - -static void deg_debug_graphviz_node(const DebugContext &ctx, - const DepsNode *node) -{ - switch (node->type) { - case DEPSNODE_TYPE_ID_REF: - { - const IDDepsNode *id_node = (const IDDepsNode *)node; - if (id_node->components.empty()) { - deg_debug_graphviz_node_single(ctx, node); - } - else { - deg_debug_graphviz_node_cluster_begin(ctx, node); - for (IDDepsNode::ComponentMap::const_iterator it = id_node->components.begin(); - it != id_node->components.end(); - ++it) - { - const ComponentDepsNode *comp = it->second; - deg_debug_graphviz_node(ctx, comp); - } - deg_debug_graphviz_node_cluster_end(ctx); - } - break; - } - case DEPSNODE_TYPE_SUBGRAPH: - { - SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; - if (sub_node->graph) { - deg_debug_graphviz_node_cluster_begin(ctx, node); - deg_debug_graphviz_graph_nodes(ctx, sub_node->graph); - deg_debug_graphviz_node_cluster_end(ctx); - } - else { - deg_debug_graphviz_node_single(ctx, node); - } - break; - } - case DEPSNODE_TYPE_PARAMETERS: - case DEPSNODE_TYPE_ANIMATION: - case DEPSNODE_TYPE_TRANSFORM: - case DEPSNODE_TYPE_PROXY: - case DEPSNODE_TYPE_GEOMETRY: - case DEPSNODE_TYPE_SEQUENCER: - case DEPSNODE_TYPE_EVAL_POSE: - case DEPSNODE_TYPE_BONE: - case DEPSNODE_TYPE_SHADING: - case DEPSNODE_TYPE_EVAL_PARTICLES: - { - ComponentDepsNode *comp_node = (ComponentDepsNode *)node; - if (!comp_node->operations.empty()) { - deg_debug_graphviz_node_cluster_begin(ctx, node); - for (ComponentDepsNode::OperationMap::const_iterator it = comp_node->operations.begin(); - it != comp_node->operations.end(); - ++it) - { - const DepsNode *op_node = it->second; - deg_debug_graphviz_node(ctx, op_node); - } - deg_debug_graphviz_node_cluster_end(ctx); - } - else { - deg_debug_graphviz_node_single(ctx, node); - } - break; - } - default: - deg_debug_graphviz_node_single(ctx, node); - break; - } -} - -static bool deg_debug_graphviz_is_cluster(const DepsNode *node) -{ - switch (node->type) { - case DEPSNODE_TYPE_ID_REF: - { - const IDDepsNode *id_node = (const IDDepsNode *)node; - return !id_node->components.empty(); - } - case DEPSNODE_TYPE_SUBGRAPH: - { - SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; - return sub_node->graph != NULL; - } - case DEPSNODE_TYPE_PARAMETERS: - case DEPSNODE_TYPE_ANIMATION: - case DEPSNODE_TYPE_TRANSFORM: - case DEPSNODE_TYPE_PROXY: - case DEPSNODE_TYPE_GEOMETRY: - case DEPSNODE_TYPE_SEQUENCER: - case DEPSNODE_TYPE_EVAL_POSE: - case DEPSNODE_TYPE_BONE: - { - ComponentDepsNode *comp_node = (ComponentDepsNode *)node; - return !comp_node->operations.empty(); - } - default: - return false; - } -} - -static bool deg_debug_graphviz_is_owner(const DepsNode *node, - const DepsNode *other) -{ - switch (node->tclass) { - case DEPSNODE_CLASS_COMPONENT: - { - ComponentDepsNode *comp_node = (ComponentDepsNode *)node; - if (comp_node->owner == other) - return true; - break; - } - case DEPSNODE_CLASS_OPERATION: - { - OperationDepsNode *op_node = (OperationDepsNode *)node; - if (op_node->owner == other) - return true; - else if (op_node->owner->owner == other) - return true; - break; - } - default: break; - } - return false; -} - -static void deg_debug_graphviz_node_relations(const DebugContext &ctx, - const DepsNode *node) -{ - DEPSNODE_RELATIONS_ITER_BEGIN(node->inlinks, rel) - { - float penwidth = 2.0f; - - const DepsNode *tail = rel->to; /* same as node */ - const DepsNode *head = rel->from; - deg_debug_fprintf(ctx, "// %s -> %s\n", - head->identifier().c_str(), - tail->identifier().c_str()); - deg_debug_fprintf(ctx, "\"node_%p\"", head); - deg_debug_fprintf(ctx, " -> "); - deg_debug_fprintf(ctx, "\"node_%p\"", tail); - - deg_debug_fprintf(ctx, "["); - /* XXX labels on relations are not very helpful: - * - they tend to appear too far away to be associated with the edge lines - * - names are mostly redundant, reflecting simply their from/to nodes - * - no behavior or typing of relations themselves to justify labels - */ -#if 0 - deg_debug_fprintf(ctx, "label=\"%s\"", rel->name); - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); -#else - /* Note: without label an id seem necessary to avoid bugs in graphviz/dot */ - deg_debug_fprintf(ctx, "id=\"%s\"", rel->name); -#endif - deg_debug_fprintf(ctx, ",color="); deg_debug_graphviz_relation_color(ctx, rel); - deg_debug_fprintf(ctx, ",penwidth=\"%f\"", penwidth); - /* NOTE: edge from node to own cluster is not possible and gives graphviz - * warning, avoid this here by just linking directly to the invisible - * placeholder node - */ - if (deg_debug_graphviz_is_cluster(tail) && !deg_debug_graphviz_is_owner(head, tail)) { - deg_debug_fprintf(ctx, ",ltail=\"cluster_%p\"", tail); - } - if (deg_debug_graphviz_is_cluster(head) && !deg_debug_graphviz_is_owner(tail, head)) { - deg_debug_fprintf(ctx, ",lhead=\"cluster_%p\"", head); - } - deg_debug_fprintf(ctx, "];" NL); - deg_debug_fprintf(ctx, NL); - } - DEPSNODE_RELATIONS_ITER_END; - -#if 0 - if (node->tclass == DEPSNODE_CLASS_COMPONENT) { - const ComponentDepsNode *comp_node = (const ComponentDepsNode *)node; - for (ComponentDepsNode::OperationMap::const_iterator it = comp_node->operations.begin(); - it != comp_node->operations.end(); - ++it) - { - OperationDepsNode *op_node = it->second; - deg_debug_graphviz_node_relations(ctx, op_node); - } - } - else if (node->type == DEPSNODE_TYPE_ID_REF) { - const IDDepsNode *id_node = (const IDDepsNode *)node; - for (IDDepsNode::ComponentMap::const_iterator it = id_node->components.begin(); - it != id_node->components.end(); - ++it) - { - const ComponentDepsNode *comp = it->second; - deg_debug_graphviz_node_relations(ctx, comp); - } - } - else if (node->type == DEPSNODE_TYPE_SUBGRAPH) { - SubgraphDepsNode *sub_node = (SubgraphDepsNode *)node; - if (sub_node->graph) { - deg_debug_graphviz_graph_relations(ctx, sub_node->graph); - } - } -#endif -} - -static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, - const Depsgraph *graph) -{ - if (graph->root_node) { - deg_debug_graphviz_node(ctx, graph->root_node); - } - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) - { - DepsNode *node = it->second; - deg_debug_graphviz_node(ctx, node); - } - TimeSourceDepsNode *time_source = graph->find_time_source(NULL); - if (time_source != NULL) { - deg_debug_graphviz_node(ctx, time_source); - } -} - -static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, - const Depsgraph *graph) -{ -#if 0 - if (graph->root_node) { - deg_debug_graphviz_node_relations(ctx, graph->root_node); - } - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) - { - DepsNode *id_node = it->second; - deg_debug_graphviz_node_relations(ctx, id_node); - } -#else - /* XXX not in use yet */ -// for (Depsgraph::OperationNodes::const_iterator it = graph->all_opnodes.begin(); -// it != graph->all_opnodes.end(); -// ++it) -// { -// OperationDepsNode *op_node = *it; -// deg_debug_graphviz_node_relations(ctx, op_node); -// } - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) - { - IDDepsNode *id_node = it->second; - for (IDDepsNode::ComponentMap::const_iterator it = id_node->components.begin(); - it != id_node->components.end(); - ++it) - { - ComponentDepsNode *comp_node = it->second; - for (ComponentDepsNode::OperationMap::const_iterator it = comp_node->operations.begin(); - it != comp_node->operations.end(); - ++it) - { - OperationDepsNode *op_node = it->second; - deg_debug_graphviz_node_relations(ctx, op_node); - } - } - } - - TimeSourceDepsNode *time_source = graph->find_time_source(NULL); - if (time_source != NULL) { - deg_debug_graphviz_node_relations(ctx, time_source); - } -#endif -} - -void DEG_debug_graphviz(const Depsgraph *graph, FILE *f, const char *label, bool show_eval) -{ -#if 0 /* generate shaded color set */ - static char colors[][3] = {{0xa6, 0xce, 0xe3},{0x1f, 0x78, 0xb4},{0xb2, 0xdf, 0x8a},{0x33, 0xa0, 0x2c}, - {0xfb, 0x9a, 0x99},{0xe3, 0x1a, 0x1c},{0xfd, 0xbf, 0x6f},{0xff, 0x7f, 0x00}, - {0xca, 0xb2, 0xd6},{0x6a, 0x3d, 0x9a},{0xff, 0xff, 0x99},{0xb1, 0x59, 0x28}}; - int i; - const float factor = 0.666f; - for (i=0; i < 12; ++i) - printf("\"#%x%x%x\"\n", (char)(colors[i][0] * factor), (char)(colors[i][1] * factor), (char)(colors[i][2] * factor)); -#endif - - if (!graph) { - return; - } - - DebugContext ctx; - ctx.file = f; - ctx.show_tags = show_eval; - ctx.show_eval_priority = show_eval; - - deg_debug_fprintf(ctx, "digraph depgraph {" NL); - deg_debug_fprintf(ctx, "rankdir=LR;" NL); - deg_debug_fprintf(ctx, "graph ["); - deg_debug_fprintf(ctx, "compound=true"); - deg_debug_fprintf(ctx, ",labelloc=\"t\""); - deg_debug_fprintf(ctx, ",fontsize=%f", deg_debug_graphviz_graph_label_size); - deg_debug_fprintf(ctx, ",fontname=\"%s\"", deg_debug_graphviz_fontname); - deg_debug_fprintf(ctx, ",label=\"%s\"", label); - deg_debug_fprintf(ctx, ",splines=ortho"); - deg_debug_fprintf(ctx, ",overlap=scalexy"); // XXX: only when using neato - deg_debug_fprintf(ctx, "];" NL); - - deg_debug_graphviz_graph_nodes(ctx, graph); - deg_debug_graphviz_graph_relations(ctx, graph); - - deg_debug_graphviz_legend(ctx); - - deg_debug_fprintf(ctx, "}" NL); -} - -#undef NL +#include "intern/eval/deg_eval_debug.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" /* ************************************************ */ -static string get_component_name(eDepsNode_Type type, const string &name = "") -{ - DepsNodeFactory *factory = DEG_get_node_factory(type); - if (name.empty()) { - return string(factory->tname()); - } - else { - return string(factory->tname()) + " | " + name; - } -} - -static void times_clear(DepsgraphStatsTimes ×) -{ - times.duration_last = 0.0f; -} - -static void times_add(DepsgraphStatsTimes ×, float time) -{ - times.duration_last += time; -} - -void DepsgraphDebug::eval_begin(const EvaluationContext *UNUSED(eval_ctx)) -{ - /* TODO(sergey): Stats are currently globally disabled. */ - /* verify_stats(); */ - reset_stats(); -} - -void DepsgraphDebug::eval_end(const EvaluationContext *UNUSED(eval_ctx)) -{ - WM_main_add_notifier(NC_SPACE | ND_SPACE_INFO_REPORT, NULL); -} - -void DepsgraphDebug::eval_step(const EvaluationContext *UNUSED(eval_ctx), - const char *message) -{ -#ifdef DEG_DEBUG_BUILD - if (deg_debug_eval_cb) - deg_debug_eval_cb(deg_debug_eval_userdata, message); -#else - (void)message; /* Ignored. */ -#endif -} - -void DepsgraphDebug::task_started(Depsgraph *graph, - const OperationDepsNode *node) -{ - if (stats) { - BLI_spin_lock(&graph->lock); - - ComponentDepsNode *comp = node->owner; - ID *id = comp->owner->id; - - DepsgraphStatsID *id_stats = get_id_stats(id, true); - times_clear(id_stats->times); - - /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */ - if (0) { - /* XXX component name usage needs cleanup! currently mixes identifier and description strings! */ - DepsgraphStatsComponent *comp_stats = get_component_stats(id, get_component_name(comp->type, comp->name), true); - times_clear(comp_stats->times); - } - - BLI_spin_unlock(&graph->lock); - } -} - -void DepsgraphDebug::task_completed(Depsgraph *graph, - const OperationDepsNode *node, - double time) -{ - if (stats) { - BLI_spin_lock(&graph->lock); - - ComponentDepsNode *comp = node->owner; - ID *id = comp->owner->id; - - DepsgraphStatsID *id_stats = get_id_stats(id, true); - times_add(id_stats->times, time); - - /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */ - if (0) { - /* XXX component name usage needs cleanup! currently mixes identifier and description strings! */ - DepsgraphStatsComponent *comp_stats = get_component_stats(id, get_component_name(comp->type, comp->name), true); - times_add(comp_stats->times, time); - } - - BLI_spin_unlock(&graph->lock); - } -} - -/* ********** */ -/* Statistics */ - -DepsgraphStats *DepsgraphDebug::stats = NULL; - -/* GHash callback */ -static void deg_id_stats_free(void *val) -{ - DepsgraphStatsID *id_stats = (DepsgraphStatsID *)val; - - if (id_stats) { - BLI_freelistN(&id_stats->components); - MEM_freeN(id_stats); - } -} - -void DepsgraphDebug::stats_init() -{ - if (!stats) { - stats = (DepsgraphStats *)MEM_callocN(sizeof(DepsgraphStats), "Depsgraph Stats"); - stats->id_stats = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "Depsgraph ID Stats Hash"); - } -} - -void DepsgraphDebug::stats_free() -{ - if (stats) { - BLI_ghash_free(stats->id_stats, NULL, deg_id_stats_free); - MEM_freeN(stats); - stats = NULL; - } -} - -void DepsgraphDebug::verify_stats() -{ - stats_init(); -} - -void DepsgraphDebug::reset_stats() -{ - if (!stats) { - return; - } - - /* XXX this doesn't work, will immediately clear all info, - * since most depsgraph updates have none or very few updates to handle. - * - * Could consider clearing only zero-user ID blocks here - */ -// BLI_ghash_clear(stats->id_stats, NULL, deg_id_stats_free); -} - -DepsgraphStatsID *DepsgraphDebug::get_id_stats(ID *id, bool create) -{ - DepsgraphStatsID *id_stats = (DepsgraphStatsID *)BLI_ghash_lookup(stats->id_stats, id); - - if (!id_stats && create) { - id_stats = (DepsgraphStatsID *)MEM_callocN(sizeof(DepsgraphStatsID), "Depsgraph ID Stats"); - id_stats->id = id; - - BLI_ghash_insert(stats->id_stats, id, id_stats); - } - - return id_stats; -} - -DepsgraphStatsComponent *DepsgraphDebug::get_component_stats( - DepsgraphStatsID *id_stats, - const string &name, - bool create) -{ - DepsgraphStatsComponent *comp_stats; - for (comp_stats = (DepsgraphStatsComponent *)id_stats->components.first; - comp_stats != NULL; - comp_stats = comp_stats->next) - { - if (STREQ(comp_stats->name, name.c_str())) - break; - } - if (!comp_stats && create) { - comp_stats = (DepsgraphStatsComponent *)MEM_callocN(sizeof(DepsgraphStatsComponent), "Depsgraph Component Stats"); - BLI_strncpy(comp_stats->name, name.c_str(), sizeof(comp_stats->name)); - BLI_addtail(&id_stats->components, comp_stats); - } - return comp_stats; -} - -/* ------------------------------------------------ */ - DepsgraphStats *DEG_stats(void) { - return DepsgraphDebug::stats; + return DEG::DepsgraphDebug::stats; } void DEG_stats_verify() { - DepsgraphDebug::verify_stats(); + DEG::DepsgraphDebug::verify_stats(); } DepsgraphStatsID *DEG_stats_id(ID *id) { - if (!DepsgraphDebug::stats) { + if (!DEG::DepsgraphDebug::stats) { return NULL; } - return DepsgraphDebug::get_id_stats(id, false); + return DEG::DepsgraphDebug::get_id_stats(id, false); } bool DEG_debug_compare(const struct Depsgraph *graph1, @@ -987,7 +70,9 @@ bool DEG_debug_compare(const struct Depsgraph *graph1, { BLI_assert(graph1 != NULL); BLI_assert(graph2 != NULL); - if (graph1->operations.size() != graph2->operations.size()) { + const DEG::Depsgraph *deg_graph1 = reinterpret_cast<const DEG::Depsgraph *>(graph1); + const DEG::Depsgraph *deg_graph2 = reinterpret_cast<const DEG::Depsgraph *>(graph2); + if (deg_graph1->operations.size() != deg_graph2->operations.size()) { return false; } /* TODO(sergey): Currently we only do real stupid check, @@ -1017,34 +102,21 @@ bool DEG_debug_scene_relations_validate(Main *bmain, bool DEG_debug_consistency_check(Depsgraph *graph) { - /* Validate links exists in both directions. */ - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + /* Validate links exists in both directions. */ + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { + foreach (DEG::DepsRelation *rel, node->outlinks) { int counter1 = 0; - for (OperationDepsNode::Relations::const_iterator tmp_rel = node->outlinks.begin(); - tmp_rel != node->outlinks.end(); - ++tmp_rel) - { - if (*tmp_rel == rel) { + foreach (DEG::DepsRelation *tmp_rel, node->outlinks) { + if (tmp_rel == rel) { ++counter1; } } int counter2 = 0; - for (OperationDepsNode::Relations::const_iterator tmp_rel = rel->to->inlinks.begin(); - tmp_rel != rel->to->inlinks.end(); - ++tmp_rel) - { - if (*tmp_rel == rel) { + foreach (DEG::DepsRelation *tmp_rel, rel->to->inlinks) { + if (tmp_rel == rel) { ++counter2; } } @@ -1057,33 +129,18 @@ bool DEG_debug_consistency_check(Depsgraph *graph) } } - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { + foreach (DEG::DepsRelation *rel, node->inlinks) { int counter1 = 0; - for (OperationDepsNode::Relations::const_iterator tmp_rel = node->inlinks.begin(); - tmp_rel != node->inlinks.end(); - ++tmp_rel) - { - if (*tmp_rel == rel) { + foreach (DEG::DepsRelation *tmp_rel, node->inlinks) { + if (tmp_rel == rel) { ++counter1; } } int counter2 = 0; - for (OperationDepsNode::Relations::const_iterator tmp_rel = rel->from->outlinks.begin(); - tmp_rel != rel->from->outlinks.end(); - ++tmp_rel) - { - if (*tmp_rel == rel) { + foreach (DEG::DepsRelation *tmp_rel, rel->from->outlinks) { + if (tmp_rel == rel) { ++counter2; } } @@ -1096,32 +153,20 @@ bool DEG_debug_consistency_check(Depsgraph *graph) } /* Validate node valency calculated in both directions. */ - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { node->num_links_pending = 0; node->done = 0; } - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { if (node->done) { printf("Node %s is twice in the operations!\n", node->identifier().c_str()); return false; } - for (OperationDepsNode::Relations::const_iterator it_rel = node->outlinks.begin(); - it_rel != node->outlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if (rel->to->type == DEPSNODE_TYPE_OPERATION) { - OperationDepsNode *to = (OperationDepsNode *)rel->to; + foreach (DEG::DepsRelation *rel, node->outlinks) { + if (rel->to->type == DEG::DEPSNODE_TYPE_OPERATION) { + DEG::OperationDepsNode *to = (DEG::OperationDepsNode *)rel->to; BLI_assert(to->num_links_pending < to->inlinks.size()); ++to->num_links_pending; } @@ -1129,18 +174,10 @@ bool DEG_debug_consistency_check(Depsgraph *graph) node->done = 1; } - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - OperationDepsNode *node = *it_op; + foreach (DEG::OperationDepsNode *node, deg_graph->operations) { int num_links_pending = 0; - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - DepsRelation *rel = *it_rel; - if (rel->from->type == DEPSNODE_TYPE_OPERATION) { + foreach (DEG::DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEG::DEPSNODE_TYPE_OPERATION) { ++num_links_pending; } } @@ -1166,12 +203,14 @@ bool DEG_debug_consistency_check(Depsgraph *graph) void DEG_stats_simple(const Depsgraph *graph, size_t *r_outer, size_t *r_operations, size_t *r_relations) { + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(graph); + /* number of operations */ if (r_operations) { /* All operations should be in this list, allowing us to count the total * number of nodes. */ - *r_operations = graph->operations.size(); + *r_operations = deg_graph->operations.size(); } /* Count number of outer nodes and/or relations between these. */ @@ -1179,29 +218,21 @@ void DEG_stats_simple(const Depsgraph *graph, size_t *r_outer, size_t tot_outer = 0; size_t tot_rels = 0; - for (Depsgraph::IDNodeMap::const_iterator it = graph->id_hash.begin(); - it != graph->id_hash.end(); - ++it) + GHASH_FOREACH_BEGIN(DEG::IDDepsNode *, id_node, deg_graph->id_hash) { - IDDepsNode *id_node = it->second; tot_outer++; - for (IDDepsNode::ComponentMap::const_iterator it = id_node->components.begin(); - it != id_node->components.end(); - ++it) + GHASH_FOREACH_BEGIN(DEG::ComponentDepsNode *, comp_node, id_node->components) { - ComponentDepsNode *comp_node = it->second; tot_outer++; - for (ComponentDepsNode::OperationMap::const_iterator it = comp_node->operations.begin(); - it != comp_node->operations.end(); - ++it) - { - OperationDepsNode *op_node = it->second; + foreach (DEG::OperationDepsNode *op_node, comp_node->operations) { tot_rels += op_node->inlinks.size(); } } + GHASH_FOREACH_END(); } + GHASH_FOREACH_END(); - TimeSourceDepsNode *time_source = graph->find_time_source(NULL); + DEG::TimeSourceDepsNode *time_source = deg_graph->find_time_source(NULL); if (time_source != NULL) { tot_rels += time_source->inlinks.size(); } @@ -1210,4 +241,3 @@ void DEG_stats_simple(const Depsgraph *graph, size_t *r_outer, if (r_outer) *r_outer = tot_outer; } } - diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index e8065766332..f8d40d0e6a8 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -32,11 +32,9 @@ #include "MEM_guardedalloc.h" -#include "PIL_time.h" - extern "C" { #include "BLI_utildefines.h" -#include "BLI_task.h" +#include "BLI_ghash.h" #include "BKE_depsgraph.h" #include "BKE_scene.h" @@ -44,18 +42,21 @@ extern "C" { #include "DEG_depsgraph.h" } /* extern "C" */ -#include "atomic_ops.h" +#include "intern/eval/deg_eval.h" +#include "intern/eval/deg_eval_flush.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_operation.h" -#include "depsgraph.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_debug.h" +#include "intern/depsgraph.h" #ifdef WITH_LEGACY_DEPSGRAPH 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 @@ -115,263 +116,16 @@ void DEG_evaluation_context_free(EvaluationContext *eval_ctx) MEM_freeN(eval_ctx); } -/* ********************** */ -/* Evaluation Entrypoints */ - -/* Forward declarations. */ -static void schedule_children(TaskPool *pool, - Depsgraph *graph, - OperationDepsNode *node, - const int layers); - -struct DepsgraphEvalState { - EvaluationContext *eval_ctx; - Depsgraph *graph; - int layers; -}; - -static void deg_task_run_func(TaskPool *pool, - void *taskdata, - int UNUSED(threadid)) -{ - DepsgraphEvalState *state = (DepsgraphEvalState *)BLI_task_pool_userdata(pool); - OperationDepsNode *node = (OperationDepsNode *)taskdata; - - if (!node->is_noop()) { - /* Get context. */ - // TODO: who initialises this? "Init" operations aren't able to initialise it!!! - /* TODO(sergey): Wedon'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); - - /* Note how long this took. */ - double end_time = PIL_check_seconds_timer(); - DepsgraphDebug::task_completed(state->graph, - node, - end_time - start_time); - } - - schedule_children(pool, state->graph, node, state->layers); -} - -static void calculate_pending_parents(Depsgraph *graph, int layers) -{ - for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); - it_op != graph->operations.end(); - ++it_op) - { - 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) - { - for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); - it_rel != node->inlinks.end(); - ++it_rel) - { - 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) - { - ++node->num_links_pending; - } - } - } - } - } -} - -static void calculate_eval_priority(OperationDepsNode *node) -{ - if (node->done) { - return; - } - node->done = 1; - - if (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { - /* XXX standard cost of a node, could be estimated somewhat later on */ - const float cost = 1.0f; - /* NOOP nodes have no cost */ - node->eval_priority = node->is_noop() ? cost : 0.0f; - - for (OperationDepsNode::Relations::const_iterator it = node->outlinks.begin(); - it != node->outlinks.end(); - ++it) - { - DepsRelation *rel = *it; - OperationDepsNode *to = (OperationDepsNode *)rel->to; - BLI_assert(to->type == DEPSNODE_TYPE_OPERATION); - calculate_eval_priority(to); - node->eval_priority += to->eval_priority; - } - } - else { - node->eval_priority = 0.0f; - } -} - -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; - } - } - BLI_spin_unlock(&graph->lock); -} - -static void schedule_children(TaskPool *pool, - Depsgraph *graph, - OperationDepsNode *node, - const int layers) -{ - for (OperationDepsNode::Relations::const_iterator it = node->outlinks.begin(); - it != node->outlinks.end(); - ++it) - { - 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); - } - } - } - } -} - -/** - * Evaluate all nodes tagged for updating, - * \warning This is usually done as part of main loop, but may also be - * called from frame-change update. - * - * \note Time sources should be all valid! - */ -void DEG_evaluate_on_refresh_ex(EvaluationContext *eval_ctx, - Depsgraph *graph, - const int layers) -{ - /* Generate base evaluation context, upon which all the others are derived. */ - // TODO: this needs both main and scene access... - - /* Nothing to update, early out. */ - if (graph->entry_tags.size() == 0) { - return; - } - - /* Set time for the current graph evaluation context. */ - TimeSourceDepsNode *time_src = graph->find_time_source(); - eval_ctx->ctime = time_src->cfra; - - /* XXX could use a separate pool for each eval context */ - DepsgraphEvalState state; - state.eval_ctx = eval_ctx; - state.graph = graph; - state.layers = layers; - - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &state); - - if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { - BLI_pool_set_num_threads(task_pool, 1); - } - - calculate_pending_parents(graph, layers); - - /* Clear tags. */ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; - node->done = 0; - } - - /* Calculate priority for operation nodes. */ - for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); - it != graph->operations.end(); - ++it) - { - OperationDepsNode *node = *it; - calculate_eval_priority(node); - } - - DepsgraphDebug::eval_begin(eval_ctx); - - schedule_graph(task_pool, graph, layers); - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - - DepsgraphDebug::eval_end(eval_ctx); - - /* Clear any uncleared tags - just in case. */ - DEG_graph_clear_tags(graph); -} - /* Evaluate all nodes tagged for updating. */ void DEG_evaluate_on_refresh(EvaluationContext *eval_ctx, Depsgraph *graph, Scene *scene) { + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); /* Update time on primary timesource. */ - TimeSourceDepsNode *tsrc = graph->find_time_source(); + DEG::TimeSourceDepsNode *tsrc = deg_graph->find_time_source(); tsrc->cfra = BKE_scene_frame_get(scene); - - DEG_evaluate_on_refresh_ex(eval_ctx, graph, graph->layers); + DEG::deg_evaluate_on_refresh(eval_ctx, deg_graph, deg_graph->layers); } /* Frame-change happened for root scene that graph belongs to. */ @@ -381,19 +135,18 @@ void DEG_evaluate_on_framechange(EvaluationContext *eval_ctx, float ctime, const int layers) { + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); /* Update time on primary timesource. */ - TimeSourceDepsNode *tsrc = graph->find_time_source(); + DEG::TimeSourceDepsNode *tsrc = deg_graph->find_time_source(); tsrc->cfra = ctime; - - tsrc->tag_update(graph); - - DEG_graph_flush_updates(bmain, graph); - + tsrc->tag_update(deg_graph); + DEG::deg_graph_flush_updates(bmain, deg_graph); /* Perform recalculation updates. */ - DEG_evaluate_on_refresh_ex(eval_ctx, graph, layers); + DEG::deg_evaluate_on_refresh(eval_ctx, deg_graph, layers); } bool DEG_needs_eval(Depsgraph *graph) { - return graph->entry_tags.size() != 0; + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + return BLI_gset_size(deg_graph->entry_tags) != 0; } diff --git a/source/blender/depsgraph/intern/depsgraph_intern.h b/source/blender/depsgraph/intern/depsgraph_intern.h index 7fdc2454564..e5d3d1f5861 100644 --- a/source/blender/depsgraph/intern/depsgraph_intern.h +++ b/source/blender/depsgraph/intern/depsgraph_intern.h @@ -31,65 +31,26 @@ * - Also, defines for "Node Type Info" */ -#ifndef __DEPSGRAPH_INTERN_H__ -#define __DEPSGRAPH_INTERN_H__ +#pragma once #include <cstdlib> #include "MEM_guardedalloc.h" -#include "depsgraph.h" -#include "depsnode.h" +extern "C" { +#include "BKE_global.h" +} + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph.h" struct Main; struct Group; struct Scene; -/* Graph Building ======================================================== */ - -/** - * Build depsgraph for the given group, and dump results in given graph container - * This is usually used for building subgraphs for groups to use... - */ -void DEG_graph_build_from_group(Depsgraph *graph, struct Main *bmain, struct Group *group); - -/* Build subgraph for group */ -DepsNode *DEG_graph_build_group_subgraph(Depsgraph *graph_main, struct Main *bmain, struct Group *group); - -/* Graph Copying ========================================================= */ -/* (Part of the Filtering API) */ - -/** - * Depsgraph Copying Context (dcc) - * - * Keeps track of node relationships/links/etc. during the copy - * operation so that they can be safely remapped... - */ -typedef struct DepsgraphCopyContext { - struct GHash *nodes_hash; /* <DepsNode, DepsNode> mapping from src node to dst node */ - struct GHash *rels_hash; // XXX: same for relationships? - - // XXX: filtering criteria... -} DepsgraphCopyContext; - -/* Internal Filtering API ---------------------------------------------- */ - -/* Create filtering context */ -// XXX: needs params for conditions? -DepsgraphCopyContext *DEG_filter_init(void); - -/* Free filtering context once filtering is done */ -void DEG_filter_cleanup(DepsgraphCopyContext *dcc); - - -/* Data Copy Operations ------------------------------------------------ */ - -/** - * Make a (deep) copy of provided node and it's little subgraph - * \warning Newly created node is not added to the existing graph - * \param dcc: Context info for helping resolve links - */ -DepsNode *DEG_copy_node(DepsgraphCopyContext *dcc, const DepsNode *src); +namespace DEG { /* Node Types Handling ================================================= */ @@ -101,8 +62,9 @@ struct DepsNodeFactory { virtual eDepsNode_Class tclass() const = 0; virtual const char *tname() const = 0; - virtual DepsNode *create_node(const ID *id, const string &subdata, const string &name) const = 0; - virtual DepsNode *copy_node(DepsgraphCopyContext *dcc, const DepsNode *copy) const = 0; + virtual DepsNode *create_node(const ID *id, + const string &subdata, + const string &name) const = 0; }; template <class NodeType> @@ -130,34 +92,18 @@ struct DepsNodeFactoryImpl : public DepsNodeFactory { return node; } - - virtual DepsNode *copy_node(DepsgraphCopyContext *dcc, const DepsNode *copy) const - { - BLI_assert(copy->type == type()); - DepsNode *node = OBJECT_GUARDED_NEW(NodeType); - - /* populate base node settings */ - node->type = type(); - node->tclass = tclass(); - // XXX: need to review the name here, as we can't have exact duplicates... - node->name = copy->name; - - node->copy(dcc, static_cast<const NodeType *>(copy)); - - return node; - } }; /* Typeinfo Management -------------------------------------------------- */ /* Register typeinfo */ -void DEG_register_node_typeinfo(DepsNodeFactory *factory); +void deg_register_node_typeinfo(DepsNodeFactory *factory); /* Get typeinfo for specified type */ -DepsNodeFactory *DEG_get_node_factory(const eDepsNode_Type type); +DepsNodeFactory *deg_get_node_factory(const eDepsNode_Type type); /* Get typeinfo for provided node */ -DepsNodeFactory *DEG_node_get_factory(const DepsNode *node); +DepsNodeFactory *deg_node_get_factory(const DepsNode *node); /* Editors Integration -------------------------------------------------- */ @@ -165,4 +111,11 @@ void deg_editors_id_update(struct Main *bmain, struct ID *id); void deg_editors_scene_update(struct Main *bmain, struct Scene *scene, bool updated); -#endif /* __DEPSGRAPH_INTERN_H__ */ +#define DEG_DEBUG_PRINTF(...) \ + do { \ + if (G.debug & G_DEBUG_DEPSGRAPH) { \ + fprintf(stderr, __VA_ARGS__); \ + } \ + } while (0) + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 73193747b93..cac4eaae215 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -33,162 +33,12 @@ #include "MEM_guardedalloc.h" extern "C" { -#include "BLI_utildefines.h" -#include "BLI_ghash.h" - #include "BKE_main.h" #include "DEG_depsgraph_query.h" } /* extern "C" */ -#include "depsgraph_queue.h" -#include "depsnode.h" -#include "depsnode_operation.h" -#include "depsgraph_intern.h" - -/* ************************* */ -/* Low-Level Graph Traversal */ - -#if 0 -/* Prepare for graph traversal, by tagging nodes, etc. */ -static void DEG_graph_traverse_begin(Depsgraph * /*graph*/) -{ - /* go over all nodes, initialising the valence counts */ - // XXX: this will end up being O(|V|), which is bad when we're just updating a few nodes... -} - -/* Perform a traversal of graph from given starting node (in execution order) */ -// TODO: additional flags for controlling the process? -void DEG_graph_traverse_from_node(Depsgraph *graph, OperationDepsNode *start_node, - DEG_FilterPredicate filter, void *filter_data, - DEG_NodeOperation op, void *operation_data) -{ - DepsgraphQueue *q; - - /* sanity checks */ - if (ELEM(NULL, graph, start_node, op)) - return; - - /* add node as starting node to be evaluated, with value of 0 */ - q = DEG_queue_new(); - - start_node->num_links_pending = 0; - DEG_queue_push(q, start_node, 0.0f); - - /* while we still have nodes in the queue, grab and work on next one */ - do { - /* grab item at front of queue */ - // XXX: in practice, we may need to wait until one becomes available... - OperationDepsNode *node = (OperationDepsNode *)DEG_queue_pop(q); - - /* perform operation on node */ - op(graph, node, operation_data); - - /* schedule up operations which depend on this */ - DEPSNODE_RELATIONS_ITER_BEGIN(node->outlinks, rel) - { - /* ensure that relationship is not tagged for ignoring (i.e. cyclic, etc.) */ - // TODO: cyclic refs should probably all get clustered towards the end, so that we can just stop on the first one - if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - OperationDepsNode *child_node = (OperationDepsNode *)rel->to; - - /* only visit node if the filtering function agrees */ - if ((filter == NULL) || filter(graph, child_node, filter_data)) { - /* schedule up node... */ - child_node->num_links_pending--; - DEG_queue_push(q, child_node, (float)child_node->num_links_pending); - } - } - } - DEPSNODE_RELATIONS_ITER_END; - } while (DEG_queue_is_empty(q) == false); - - /* cleanup */ - DEG_queue_free(q); -} -#endif - -/* ************************************************************** */ -/* Filtering API - Basically, making a copy of the existing graph */ - -/* Create filtering context */ -// TODO: allow passing in a number of criteria? -DepsgraphCopyContext *DEG_filter_init() -{ - DepsgraphCopyContext *dcc = (DepsgraphCopyContext *)MEM_callocN(sizeof(DepsgraphCopyContext), "DepsgraphCopyContext"); - - /* init hashes for easy lookups */ - dcc->nodes_hash = BLI_ghash_ptr_new("Depsgraph Filter NodeHash"); - dcc->rels_hash = BLI_ghash_ptr_new("Depsgraph Filter Relationship Hash"); // XXX? - - /* store filtering criteria? */ - // xxx... - - return dcc; -} - -/* Cleanup filtering context */ -void DEG_filter_cleanup(DepsgraphCopyContext *dcc) -{ - /* sanity check */ - if (dcc == NULL) - return; - - /* free hashes - contents are weren't copied, so are ok... */ - BLI_ghash_free(dcc->nodes_hash, NULL, NULL); - BLI_ghash_free(dcc->rels_hash, NULL, NULL); - - /* clear filtering criteria */ - // ... - - /* free dcc itself */ - MEM_freeN(dcc); -} - -/* -------------------------------------------------- */ - -/* Create a copy of provided node */ -// FIXME: the handling of sub-nodes and links will need to be subject to filtering options... -// XXX: perhaps this really shouldn't be exposed, as it will just be a sub-step of the evaluation process? -DepsNode *DEG_copy_node(DepsgraphCopyContext *dcc, const DepsNode *src) -{ - /* sanity check */ - if (src == NULL) - return NULL; - - DepsNodeFactory *factory = DEG_get_node_factory(src->type); - BLI_assert(factory != NULL); - DepsNode *dst = factory->copy_node(dcc, src); - - /* add this node-pair to the hash... */ - BLI_ghash_insert(dcc->nodes_hash, (DepsNode *)src, dst); - -#if 0 /* XXX TODO */ - /* now, fix up any links in standard "node header" (i.e. DepsNode struct, that all - * all others are derived from) that are now corrupt - */ - { - /* relationships to other nodes... */ - // FIXME: how to handle links? We may only have partial set of all nodes still? - // XXX: the exact details of how to handle this are really part of the querying API... - - // XXX: BUT, for copying subgraphs, we'll need to define an API for doing this stuff anyways - // (i.e. for resolving and patching over links that exist within subtree...) - dst->inlinks.clear(); - dst->outlinks.clear(); - - /* clear traversal data */ - dst->num_links_pending = 0; - dst->lasttime = 0; - } - - /* fix links */ - // XXX... -#endif - - /* return copied node */ - return dst; -} +#include "intern/depsgraph_intern.h" bool DEG_id_type_tagged(Main *bmain, short idtype) { @@ -207,7 +57,9 @@ short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) return 0; } - IDDepsNode *id_node = graph->find_id_node(id); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + + DEG::IDDepsNode *id_node = deg_graph->find_id_node(id); if (id_node == NULL) { /* TODO(sergey): Does it mean we need to check set scene? */ return 0; diff --git a/source/blender/depsgraph/intern/depsgraph_queue.cc b/source/blender/depsgraph/intern/depsgraph_queue.cc deleted file mode 100644 index da60d73bc46..00000000000 --- a/source/blender/depsgraph/intern/depsgraph_queue.cc +++ /dev/null @@ -1,177 +0,0 @@ -/* - * ***** 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) 2013 Blender Foundation. - * All rights reserved. - * - * Original Author: Joshua Leung - * Contributor(s): None Yet - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/intern/depsgraph_queue.cc - * \ingroup depsgraph - * - * Implementation of special queue type for use in Depsgraph traversals. - */ - -#include <stdlib.h> - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "BLI_utildefines.h" -#include "BLI_heap.h" -#include "BLI_ghash.h" -} /* extern "C" */ - -#include "depsgraph_queue.h" - -/* ****************************** */ -/* Depsgraph Queue implementation */ - -/* Data Management ----------------------------------------- */ - -DepsgraphQueue *DEG_queue_new(void) -{ - DepsgraphQueue *q = (DepsgraphQueue *)MEM_callocN(sizeof(DepsgraphQueue), "DEG_queue_new()"); - - /* init data structures for use here */ - q->pending_heap = BLI_heap_new(); - q->pending_hash = BLI_ghash_ptr_new("DEG Queue Pending Hash"); - - q->ready_heap = BLI_heap_new(); - - /* init settings */ - q->idx = 0; - q->tot = 0; - - /* return queue */ - return q; -} - -void DEG_queue_free(DepsgraphQueue *q) -{ - /* free data structures */ - BLI_assert(BLI_heap_size(q->pending_heap) == 0); - BLI_assert(BLI_heap_size(q->ready_heap) == 0); - BLI_assert(BLI_ghash_size(q->pending_hash) == 0); - - BLI_heap_free(q->pending_heap, NULL); - BLI_heap_free(q->ready_heap, NULL); - BLI_ghash_free(q->pending_hash, NULL, NULL); - - /* free queue itself */ - MEM_freeN(q); -} - -/* Statistics --------------------------------------------- */ - -/* Get the number of nodes which are we should visit, but are not able to yet */ -size_t DEG_queue_num_pending(DepsgraphQueue *q) -{ - return BLI_heap_size(q->pending_heap); -} - -/* Get the number of nodes which are now ready to be visited */ -size_t DEG_queue_num_ready(DepsgraphQueue *q) -{ - return BLI_heap_size(q->ready_heap); -} - -/* Get total size of queue */ -size_t DEG_queue_size(DepsgraphQueue *q) -{ - return DEG_queue_num_pending(q) + DEG_queue_num_ready(q); -} - -/* Check if queue has any items in it (still passing through) */ -bool DEG_queue_is_empty(DepsgraphQueue *q) -{ - return DEG_queue_size(q) == 0; -} - -/* Queue Operations --------------------------------------- */ - -/** - * Add DepsNode to the queue - * \param dnode: ``(DepsNode *)`` node to add to the queue - * Each node is only added once to the queue; Subsequent pushes - * merely update its status (e.g. moving it from "pending" to "ready") - * \param cost: new "num_links_pending" count for node *after* it has encountered - * via an outlink from the node currently being visited - * (i.e. we're one of the dependencies which may now be able to be processed) - */ -void DEG_queue_push(DepsgraphQueue *q, void *dnode, float cost) -{ - HeapNode *hnode = NULL; - - /* Shortcut: Directly add to ready if node isn't waiting on anything now... */ - if (cost == 0) { - /* node is now ready to be visited - schedule it up for such */ - if (BLI_ghash_haskey(q->pending_hash, dnode)) { - /* remove from pending queue - we're moving it to the scheduling queue */ - hnode = (HeapNode *)BLI_ghash_lookup(q->pending_hash, dnode); - BLI_heap_remove(q->pending_heap, hnode); - - BLI_ghash_remove(q->pending_hash, dnode, NULL, NULL); - } - - /* schedule up node using latest count (of ready nodes) */ - BLI_heap_insert(q->ready_heap, (float)q->idx, dnode); - q->idx++; - } - else { - /* node is still waiting on some other ancestors, - * so add it to the pending heap in the meantime... - */ - // XXX: is this even necessary now? - if (BLI_ghash_haskey(q->pending_hash, dnode)) { - /* just update cost on pending node */ - hnode = (HeapNode *)BLI_ghash_lookup(q->pending_hash, dnode); - BLI_heap_remove(q->pending_heap, hnode); - BLI_heap_insert(q->pending_heap, cost, hnode); - } - else { - /* add new node to pending queue, and increase size of overall queue */ - hnode = BLI_heap_insert(q->pending_heap, cost, dnode); - q->tot++; - } - } -} - -/* Grab a "ready" node from the queue */ -void *DEG_queue_pop(DepsgraphQueue *q) -{ - /* sanity check: if there are no "ready" nodes, - * start pulling from "pending" to keep things moving, - * but throw a warning so that we know that something's up here... - */ - if (BLI_heap_is_empty(q->ready_heap)) { - // XXX: this should never happen - // XXX: if/when it does happen, we may want instead to just wait until something pops up here... - printf("DepsgraphHeap Warning: No more ready nodes available. Trying from pending (idx = %d, tot = %d, pending = %d, ready = %d)\n", - (int)q->idx, (int)q->tot, (int)DEG_queue_num_pending(q), (int)DEG_queue_num_ready(q)); - - return BLI_heap_popmin(q->pending_heap); - } - else { - /* only grab "ready" nodes */ - return BLI_heap_popmin(q->ready_heap); - } -} diff --git a/source/blender/depsgraph/intern/depsgraph_queue.h b/source/blender/depsgraph/intern/depsgraph_queue.h deleted file mode 100644 index b85d46bd173..00000000000 --- a/source/blender/depsgraph/intern/depsgraph_queue.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ***** 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) 2013 Blender Foundation. - * All rights reserved. - * - * Original Author: Joshua Leung - * Contributor(s): None Yet - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/intern/depsgraph_queue.h - * \ingroup depsgraph - * - * Defines for special queue type for use in Depsgraph traversals. - */ - -#ifndef __DEPSGRAPH_QUEUE_H__ -#define __DEPSGRAPH_QUEUE_H__ - -struct DepsNode; - -struct Heap; -struct GHash; - -/* *********************************************** */ -/* Dependency Graph Traversal Queue - * - * There are two parts to this: - * a) "Pending" Nodes - This part contains the set of nodes - * which are related to those which have been visited - * previously, but are not yet ready to actually be visited. - * b) "Scheduled" Nodes - These are the nodes whose ancestors - * have all been evaluated already, which means that any - * or all of them can be picked (in practically in order) to - * be visited immediately. - * - * Internally, the queue makes sure that each node in the graph - * only gets added to the queue once. This is because there can - * be multiple inlinks to each node given the way that the relations - * work. - */ - -/* Depsgraph Queue Type */ -typedef struct DepsgraphQueue { - /* Pending */ - struct Heap *pending_heap; /* (valence:int, DepsNode*) */ - struct GHash *pending_hash; /* (DepsNode* : HeapNode*>) */ - - /* Ready to be visited - fifo */ - struct Heap *ready_heap; /* (idx:int, DepsNode*) */ - - /* Size/Order counts */ - size_t idx; /* total number of nodes which are/have been ready so far (including those already visited) */ - size_t tot; /* total number of nodes which have passed through queue; mainly for debug */ -} DepsgraphQueue; - -/* ************************** */ -/* Depsgraph Queue Operations */ - -/* Data management */ -DepsgraphQueue *DEG_queue_new(void); -void DEG_queue_free(DepsgraphQueue *q); - -/* Statistics */ -size_t DEG_queue_num_pending(DepsgraphQueue *q); -size_t DEG_queue_num_ready(DepsgraphQueue *q); - -size_t DEG_queue_size(DepsgraphQueue *q); -bool DEG_queue_is_empty(DepsgraphQueue *q); - -/* Operations */ -void DEG_queue_push(DepsgraphQueue *q, void *dnode, float cost = 0.0f); -void *DEG_queue_pop(DepsgraphQueue *q); - -#endif /* DEPSGRAPH_QUEUE_H */ diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 486526ed46c..ea5afaab3f7 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" @@ -53,11 +55,14 @@ extern "C" { #include "DEG_depsgraph.h" } /* extern "C" */ -#include "depsgraph_debug.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_intern.h" +#include "intern/eval/deg_eval_flush.h" + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" /* *********************** */ /* Update Tagging/Flushing */ @@ -152,19 +157,21 @@ void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, short flag) */ void DEG_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id) { - IDDepsNode *node = graph->find_id_node(id); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + DEG::IDDepsNode *node = deg_graph->find_id_node(id); lib_id_recalc_tag(bmain, id); if (node != NULL) { - node->tag_update(graph); + node->tag_update(deg_graph); } } /* Tag nodes related to a specific piece of data */ void DEG_graph_data_tag_update(Depsgraph *graph, const PointerRNA *ptr) { - DepsNode *node = graph->find_node_from_pointer(ptr, NULL); - if (node) { - node->tag_update(graph); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + DEG::DepsNode *node = deg_graph->find_node_from_pointer(ptr, NULL); + if (node != NULL) { + node->tag_update(deg_graph); } else { printf("Missing node in %s\n", __func__); @@ -177,9 +184,10 @@ void DEG_graph_property_tag_update(Depsgraph *graph, const PointerRNA *ptr, const PropertyRNA *prop) { - DepsNode *node = graph->find_node_from_pointer(ptr, prop); - if (node) { - node->tag_update(graph); + DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); + DEG::DepsNode *node = deg_graph->find_node_from_pointer(ptr, prop); + if (node != NULL) { + node->tag_update(deg_graph); } else { printf("Missing node in %s\n", __func__); @@ -257,117 +265,6 @@ void DEG_id_type_tag(Main *bmain, short idtype) bmain->id_tag_update[((unsigned char *)&idtype)[0]] = 1; } -/* Update Flushing ---------------------------------- */ - -/* FIFO queue for tagged nodes that need flushing */ -/* XXX This may get a dedicated implementation later if needed - lukas */ -typedef std::queue<OperationDepsNode *> FlushQueue; - -/* Flush updates from tagged nodes outwards until all affected nodes are tagged. */ -void DEG_graph_flush_updates(Main *bmain, Depsgraph *graph) -{ - /* sanity check */ - if (graph == NULL) - return; - - /* Nothing to update, early out. */ - if (graph->entry_tags.size() == 0) { - return; - } - - /* 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; - } - - FlushQueue queue; - /* Starting from the tagged "entry" nodes, flush outwards... */ - /* NOTE: Also need to ensure that for each of these, there is a path back to - * root, or else they won't be done. - * NOTE: Count how many nodes we need to handle - entry nodes may be - * component nodes which don't count for this purpose! - */ - for (Depsgraph::EntryTags::const_iterator it = graph->entry_tags.begin(); - it != graph->entry_tags.end(); - ++it) - { - OperationDepsNode *node = *it; - IDDepsNode *id_node = node->owner->owner; - queue.push(node); - deg_editors_id_update(bmain, id_node->id); - node->scheduled = true; - } - - while (!queue.empty()) { - OperationDepsNode *node = queue.front(); - queue.pop(); - - IDDepsNode *id_node = node->owner->owner; - lib_id_recalc_tag(bmain, id_node->id); - /* TODO(sergey): For until we've got proper data nodes in the graph. */ - lib_id_recalc_data_tag(bmain, id_node->id); - - ID *id = id_node->id; - /* This code is used to preserve those areas which does direct - * object update, - * - * Plus it ensures visibility changes and relations and layers - * visibility update has proper flags to work with. - */ - if (GS(id->name) == ID_OB) { - Object *object = (Object *)id; - ComponentDepsNode *comp_node = node->owner; - if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { - object->recalc |= OB_RECALC_TIME; - } - else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { - object->recalc |= OB_RECALC_OB; - } - else { - object->recalc |= OB_RECALC_DATA; - } - } - - /* Flush to nodes along links... */ - for (OperationDepsNode::Relations::const_iterator it = node->outlinks.begin(); - it != node->outlinks.end(); - ++it) - { - DepsRelation *rel = *it; - OperationDepsNode *to_node = (OperationDepsNode *)rel->to; - if (to_node->scheduled == false) { - to_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - queue.push(to_node); - to_node->scheduled = true; - deg_editors_id_update(bmain, id_node->id); - } - } - - /* TODO(sergey): For until incremental updates are possible - * witin a component at least we tag the whole component - * for update. - */ - ComponentDepsNode *component = node->owner; - if ((component->flags & DEPSCOMP_FULLY_SCHEDULED) == 0) { - for (ComponentDepsNode::OperationMap::iterator it = component->operations.begin(); - it != node->owner->operations.end(); - ++it) - { - OperationDepsNode *op = it->second; - op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - } - component->flags |= DEPSCOMP_FULLY_SCHEDULED; - } - } -} - /* Recursively push updates out to all nodes dependent on this, * until all affected are tagged and/or scheduled up for eval */ @@ -379,36 +276,17 @@ void DEG_ids_flush_tagged(Main *bmain) { /* TODO(sergey): Only visible scenes? */ if (scene->depsgraph != NULL) { - DEG_graph_flush_updates(bmain, scene->depsgraph); + DEG::deg_graph_flush_updates( + bmain, + reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph)); } } } -/* 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; - } - - /* Clear any entry tags which haven't been flushed. */ - graph->entry_tags.clear(); -} - /* Update dependency graph when visible scenes/layers changes. */ void DEG_graph_on_visible_update(Main *bmain, Scene *scene) { - Depsgraph *graph = scene->depsgraph; + DEG::Depsgraph *graph = reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph); wmWindowManager *wm = (wmWindowManager *)bmain->wm.first; int old_layers = graph->layers; if (wm != NULL) { @@ -436,12 +314,8 @@ 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(); - ++it) + GHASH_FOREACH_BEGIN(DEG::IDDepsNode *, id_node, graph->id_hash) { - OperationDepsNode *node = *it; - IDDepsNode *id_node = node->owner->owner; ID *id = id_node->id; if ((id->tag & LIB_TAG_ID_RECALC_ALL) != 0 || (id_node->layers & scene->lay_updated) == 0) @@ -459,14 +333,15 @@ void DEG_graph_on_visible_update(Main *bmain, Scene *scene) (object->recalc & OB_RECALC_ALL) != 0) { id_node->tag_update(graph); - ComponentDepsNode *anim_comp = - id_node->find_component(DEPSNODE_TYPE_ANIMATION); + DEG::ComponentDepsNode *anim_comp = + id_node->find_component(DEG::DEPSNODE_TYPE_ANIMATION); if (anim_comp != NULL && object->recalc & OB_RECALC_TIME) { anim_comp->tag_update(graph); } } } } + GHASH_FOREACH_END(); } scene->lay_updated |= graph->layers; } @@ -507,7 +382,7 @@ void DEG_ids_check_recalc(Main *bmain, Scene *scene, bool time) } } - deg_editors_scene_update(bmain, scene, (updated || time)); + DEG::deg_editors_scene_update(bmain, scene, (updated || time)); } void DEG_ids_clear_recalc(Main *bmain) diff --git a/source/blender/depsgraph/intern/depsgraph_type_defines.cc b/source/blender/depsgraph/intern/depsgraph_type_defines.cc index 5a3048a4aa3..97208939e57 100644 --- a/source/blender/depsgraph/intern/depsgraph_type_defines.cc +++ b/source/blender/depsgraph/intern/depsgraph_type_defines.cc @@ -30,6 +30,8 @@ * Defines and code for core node types. */ +#include <cstdlib> // for BLI_assert() + extern "C" { #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -37,10 +39,13 @@ extern "C" { #include "DEG_depsgraph.h" } /* extern "C" */ -#include "depsgraph_intern.h" -#include "depsnode.h" -#include "depsnode_component.h" -#include "depsnode_operation.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" + +namespace DEG { /* ************ */ /* External API */ @@ -59,44 +64,108 @@ static GHash *_depsnode_typeinfo_registry = NULL; /* Registration ------------------------------------------- */ /* Register node type */ -void DEG_register_node_typeinfo(DepsNodeFactory *factory) +void deg_register_node_typeinfo(DepsNodeFactory *factory) { BLI_assert(factory != NULL); BLI_ghash_insert(_depsnode_typeinfo_registry, SET_INT_IN_POINTER(factory->type()), factory); } -/* Register all node types */ -void DEG_register_node_types(void) -{ - /* initialise registry */ - _depsnode_typeinfo_registry = BLI_ghash_int_new("Depsgraph Node Type Registry"); - - /* register node types */ - DEG_register_base_depsnodes(); - DEG_register_component_depsnodes(); - DEG_register_operation_depsnodes(); -} - -/* Free registry on exit */ -void DEG_free_node_types(void) -{ - BLI_ghash_free(_depsnode_typeinfo_registry, NULL, NULL); -} - /* Getters ------------------------------------------------- */ /* Get typeinfo for specified type */ -DepsNodeFactory *DEG_get_node_factory(const eDepsNode_Type type) +DepsNodeFactory *deg_get_node_factory(const eDepsNode_Type type) { /* look up type - at worst, it doesn't exist in table yet, and we fail */ return (DepsNodeFactory *)BLI_ghash_lookup(_depsnode_typeinfo_registry, SET_INT_IN_POINTER(type)); } /* Get typeinfo for provided node */ -DepsNodeFactory *DEG_node_get_factory(const DepsNode *node) +DepsNodeFactory *deg_node_get_factory(const DepsNode *node) { - if (!node) + if (node != NULL) { return NULL; + } + return deg_get_node_factory(node->type); +} + +/* Stringified opcodes ------------------------------------- */ + +DepsOperationStringifier DEG_OPNAMES; - return DEG_get_node_factory(node->type); +static const char *stringify_opcode(eDepsOperation_Code opcode) +{ + switch (opcode) { +#define STRINGIFY_OPCODE(name) case DEG_OPCODE_##name: return #name + STRINGIFY_OPCODE(OPERATION); + STRINGIFY_OPCODE(PLACEHOLDER); + STRINGIFY_OPCODE(NOOP); + STRINGIFY_OPCODE(ANIMATION); + STRINGIFY_OPCODE(DRIVER); + //STRINGIFY_OPCODE(PROXY); + STRINGIFY_OPCODE(TRANSFORM_LOCAL); + STRINGIFY_OPCODE(TRANSFORM_PARENT); + STRINGIFY_OPCODE(TRANSFORM_CONSTRAINTS); + //STRINGIFY_OPCODE(TRANSFORM_CONSTRAINTS_INIT); + //STRINGIFY_OPCODE(TRANSFORM_CONSTRAINT); + //STRINGIFY_OPCODE(TRANSFORM_CONSTRAINTS_DONE); + STRINGIFY_OPCODE(RIGIDBODY_REBUILD); + STRINGIFY_OPCODE(RIGIDBODY_SIM); + STRINGIFY_OPCODE(TRANSFORM_RIGIDBODY); + STRINGIFY_OPCODE(TRANSFORM_FINAL); + STRINGIFY_OPCODE(OBJECT_UBEREVAL); + STRINGIFY_OPCODE(GEOMETRY_UBEREVAL); + STRINGIFY_OPCODE(GEOMETRY_MODIFIER); + STRINGIFY_OPCODE(GEOMETRY_PATH); + STRINGIFY_OPCODE(POSE_INIT); + STRINGIFY_OPCODE(POSE_DONE); + STRINGIFY_OPCODE(POSE_IK_SOLVER); + STRINGIFY_OPCODE(POSE_SPLINE_IK_SOLVER); + STRINGIFY_OPCODE(BONE_LOCAL); + STRINGIFY_OPCODE(BONE_POSE_PARENT); + STRINGIFY_OPCODE(BONE_CONSTRAINTS); + //STRINGIFY_OPCODE(BONE_CONSTRAINTS_INIT); + //STRINGIFY_OPCODE(BONE_CONSTRAINT); + //STRINGIFY_OPCODE(BONE_CONSTRAINTS_DONE); + STRINGIFY_OPCODE(BONE_READY); + STRINGIFY_OPCODE(BONE_DONE); + STRINGIFY_OPCODE(PSYS_EVAL); + + case DEG_NUM_OPCODES: return "SpecialCase"; +#undef STRINGIFY_OPCODE + } + return "UNKNOWN"; +} + +DepsOperationStringifier::DepsOperationStringifier() { + for (int i = 0; i < DEG_NUM_OPCODES; ++i) { + names_[i] = stringify_opcode((eDepsOperation_Code)i); + } +} + +const char *DepsOperationStringifier::operator[](eDepsOperation_Code opcode) { + BLI_assert((opcode > 0) && (opcode < DEG_NUM_OPCODES)); + if (opcode >= 0 && opcode < DEG_NUM_OPCODES) { + return names_[opcode]; + } + return "UnknownOpcode"; +} + +} // namespace DEG + +/* Register all node types */ +void DEG_register_node_types(void) +{ + /* initialise registry */ + DEG::_depsnode_typeinfo_registry = BLI_ghash_int_new("Depsgraph Node Type Registry"); + + /* register node types */ + DEG::deg_register_base_depsnodes(); + DEG::deg_register_component_depsnodes(); + DEG::deg_register_operation_depsnodes(); +} + +/* Free registry on exit */ +void DEG_free_node_types(void) +{ + BLI_ghash_free(DEG::_depsnode_typeinfo_registry, NULL, NULL); } diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h index f5fbf0bcc76..7516ccbfdc2 100644 --- a/source/blender/depsgraph/intern/depsgraph_types.h +++ b/source/blender/depsgraph/intern/depsgraph_types.h @@ -34,10 +34,9 @@ * in the graph. */ -#ifndef __DEPSGRAPH_TYPES_H__ -#define __DEPSGRAPH_TYPES_H__ +#pragma once -#include "depsgraph_util_function.h" +#include "util/deg_util_function.h" /* TODO(sergey): Ideally we'll just use char* and statically allocated strings * to avoid any possible overhead caused by string (re)allocation/formatting. @@ -55,69 +54,232 @@ struct PointerRNA; struct EvaluationContext; struct FCurve; +namespace DEG { + /* Evaluation Operation for atomic operation */ // XXX: move this to another header that can be exposed? typedef function<void(struct EvaluationContext *)> DepsEvalOperationCb; -/* Metatype of Nodes - The general "level" in the graph structure the node serves */ +/* Metatype of Nodes - The general "level" in the graph structure + * the node serves. + */ typedef enum eDepsNode_Class { - DEPSNODE_CLASS_GENERIC = 0, /* Types generally unassociated with user-visible entities, but needed for graph functioning */ - - DEPSNODE_CLASS_COMPONENT = 1, /* [Outer Node] An "aspect" of evaluating/updating an ID-Block, requiring certain types of evaluation behaviours */ - DEPSNODE_CLASS_OPERATION = 2, /* [Inner Node] A glorified function-pointer/callback for scheduling up evaluation operations for components, subject to relationship requirements */ + /* Types generally unassociated with user-visible entities, + * but needed for graph functioning. + */ + DEPSNODE_CLASS_GENERIC = 0, + /* [Outer Node] An "aspect" of evaluating/updating an ID-Block, requiring + * certain types of evaluation behavior. + */ + DEPSNODE_CLASS_COMPONENT = 1, + /* [Inner Node] A glorified function-pointer/callback for scheduling up + * evaluation operations for components, subject to relationship + * requirements. + */ + DEPSNODE_CLASS_OPERATION = 2, } eDepsNode_Class; /* Types of Nodes */ typedef enum eDepsNode_Type { - DEPSNODE_TYPE_UNDEFINED = -1, /* fallback type for invalid return value */ + /* Fallback type for invalid return value */ + DEPSNODE_TYPE_UNDEFINED = -1, + /* Inner Node (Operation) */ + DEPSNODE_TYPE_OPERATION = 0, + + /* **** Generic Types **** */ + + /* "Current Scene" - basically whatever kicks off the evaluation process. */ + DEPSNODE_TYPE_ROOT = 1, + /* Time-Source */ + DEPSNODE_TYPE_TIMESOURCE = 2, + /* ID-Block reference - used as landmarks/collection point for components, + * but not usually part of main graph. + */ + DEPSNODE_TYPE_ID_REF = 3, + /* Isolated sub-graph - used for keeping instanced data separate from + * instances using them. + */ + DEPSNODE_TYPE_SUBGRAPH = 4, - DEPSNODE_TYPE_OPERATION = 0, /* Inner Node (Operation) */ + /* **** Outer Types **** */ - /* Generic Types */ - DEPSNODE_TYPE_ROOT = 1, /* "Current Scene" - basically whatever kicks off the evaluation process */ - DEPSNODE_TYPE_TIMESOURCE = 2, /* Time-Source */ + /* Parameters Component - Default when nothing else fits + * (i.e. just SDNA property setting). + */ + DEPSNODE_TYPE_PARAMETERS = 11, + /* Generic "Proxy-Inherit" Component + * XXX: Also for instancing of subgraphs? + */ + DEPSNODE_TYPE_PROXY = 12, + /* Animation Component + * + * XXX: merge in with parameters? + */ + DEPSNODE_TYPE_ANIMATION = 13, + /* Transform Component (Parenting/Constraints) */ + DEPSNODE_TYPE_TRANSFORM = 14, + /* Geometry Component (DerivedMesh/Displist) */ + DEPSNODE_TYPE_GEOMETRY = 15, + /* Sequencer Component (Scene Only) */ + DEPSNODE_TYPE_SEQUENCER = 16, + + /* **** Evaluation-Related Outer Types (with Subdata) **** */ + + /* Pose Component - Owner/Container of Bones Eval */ + DEPSNODE_TYPE_EVAL_POSE = 21, + /* Bone Component - Child/Subcomponent of Pose */ + DEPSNODE_TYPE_BONE = 22, + /* Particle Systems Component */ + DEPSNODE_TYPE_EVAL_PARTICLES = 23, + /* Material Shading Component */ + DEPSNODE_TYPE_SHADING = 24, +} eDepsNode_Type; - DEPSNODE_TYPE_ID_REF = 3, /* ID-Block reference - used as landmarks/collection point for components, but not usually part of main graph */ - DEPSNODE_TYPE_SUBGRAPH = 4, /* Isolated sub-graph - used for keeping instanced data separate from instances using them */ +/* Identifiers for common operations (as an enum). */ +typedef enum eDepsOperation_Code { + /* Generic Operations ------------------------------ */ - /* Outer Types */ - DEPSNODE_TYPE_PARAMETERS = 11, /* Parameters Component - Default when nothing else fits (i.e. just SDNA property setting) */ - DEPSNODE_TYPE_PROXY = 12, /* Generic "Proxy-Inherit" Component */ // XXX: Also for instancing of subgraphs? - DEPSNODE_TYPE_ANIMATION = 13, /* Animation Component */ // XXX: merge in with parameters? - DEPSNODE_TYPE_TRANSFORM = 14, /* Transform Component (Parenting/Constraints) */ - DEPSNODE_TYPE_GEOMETRY = 15, /* Geometry Component (DerivedMesh/Displist) */ - DEPSNODE_TYPE_SEQUENCER = 16, /* Sequencer Component (Scene Only) */ + /* Placeholder for operations which don't need special mention */ + DEG_OPCODE_OPERATION = 0, - /* Evaluation-Related Outer Types (with Subdata) */ - DEPSNODE_TYPE_EVAL_POSE = 21, /* Pose Component - Owner/Container of Bones Eval */ - DEPSNODE_TYPE_BONE = 22, /* Bone Component - Child/Subcomponent of Pose */ + // XXX: Placeholder while porting depsgraph code + DEG_OPCODE_PLACEHOLDER, - DEPSNODE_TYPE_EVAL_PARTICLES = 23, /* Particle Systems Component */ - DEPSNODE_TYPE_SHADING = 24, /* Material Shading Component */ -} eDepsNode_Type; + DEG_OPCODE_NOOP, -/* Identifiers for common operations (as an enum) */ -typedef enum eDepsOperation_Code { -#define DEF_DEG_OPCODE(label) DEG_OPCODE_##label, -#include "depsnode_opcodes.h" -#undef DEF_DEG_OPCODE + /* Animation, Drivers, etc. ------------------------ */ + + /* NLA + Action */ + DEG_OPCODE_ANIMATION, + + /* Driver */ + DEG_OPCODE_DRIVER, + + /* Proxy Inherit? */ + //DEG_OPCODE_PROXY, + + /* Transform --------------------------------------- */ + + /* Transform entry point - local transforms only */ + DEG_OPCODE_TRANSFORM_LOCAL, + + /* Parenting */ + DEG_OPCODE_TRANSFORM_PARENT, + + /* Constraints */ + DEG_OPCODE_TRANSFORM_CONSTRAINTS, + //DEG_OPCODE_TRANSFORM_CONSTRAINTS_INIT, + //DEG_OPCODE_TRANSFORM_CONSTRAINT, + //DEG_OPCODE_TRANSFORM_CONSTRAINTS_DONE, + + /* Rigidbody Sim - Perform Sim */ + DEG_OPCODE_RIGIDBODY_REBUILD, + DEG_OPCODE_RIGIDBODY_SIM, + + /* Rigidbody Sim - Copy Results to Object */ + DEG_OPCODE_TRANSFORM_RIGIDBODY, + + /* Transform exitpoint */ + DEG_OPCODE_TRANSFORM_FINAL, + + /* XXX: ubereval is for temporary porting purposes only */ + DEG_OPCODE_OBJECT_UBEREVAL, + + /* Geometry ---------------------------------------- */ + + /* XXX: Placeholder - UberEval */ + DEG_OPCODE_GEOMETRY_UBEREVAL, + + /* Modifier */ + DEG_OPCODE_GEOMETRY_MODIFIER, + + /* Curve Objects - Path Calculation (used for path-following tools, */ + DEG_OPCODE_GEOMETRY_PATH, + + /* Pose -------------------------------------------- */ + + /* Init IK Trees, etc. */ + DEG_OPCODE_POSE_INIT, + + /* Free IK Trees + Compute Deform Matrices */ + DEG_OPCODE_POSE_DONE, + + /* IK/Spline Solvers */ + DEG_OPCODE_POSE_IK_SOLVER, + DEG_OPCODE_POSE_SPLINE_IK_SOLVER, + + /* Bone -------------------------------------------- */ + + /* Bone local transforms - Entrypoint */ + DEG_OPCODE_BONE_LOCAL, + + /* Pose-space conversion (includes parent + restpose, */ + DEG_OPCODE_BONE_POSE_PARENT, + + /* Constraints */ + DEG_OPCODE_BONE_CONSTRAINTS, + //DEG_OPCODE_BONE_CONSTRAINTS_INIT, + //DEG_OPCODE_BONE_CONSTRAINT, + //DEG_OPCODE_BONE_CONSTRAINTS_DONE, + + /* Bone transforms are ready + * + * - "READY" This (internal, noop is used to signal that all pre-IK + * operations are done. Its role is to help mediate situations + * where cyclic relations may otherwise form (i.e. one bone in + * chain targetting another in same chain, + * + * - "DONE" This noop is used to signal that the bone's final pose + * transform can be read by others + */ + // TODO: deform mats could get calculated in the final_transform ops... + DEG_OPCODE_BONE_READY, + DEG_OPCODE_BONE_DONE, + + /* Particles --------------------------------------- */ + + /* XXX: placeholder - Particle System eval */ + DEG_OPCODE_PSYS_EVAL, + + DEG_NUM_OPCODES, } eDepsOperation_Code; -/* String defines for these opcodes, defined in depsnode_operation.cpp */ -extern const char *DEG_OPNAMES[]; +/* Some magic to stringify operation codes. */ +class DepsOperationStringifier { +public: + DepsOperationStringifier(); + const char *operator[](eDepsOperation_Code opcodex); +protected: + const char *names_[DEG_NUM_OPCODES]; +}; +/* String defines for these opcodes, defined in depsgraph_type_defines.cpp */ +extern DepsOperationStringifier DEG_OPNAMES; /* Type of operation */ typedef enum eDepsOperation_Type { - /* Primary operation types */ - DEPSOP_TYPE_INIT = 0, /* initialise evaluation data */ - DEPSOP_TYPE_EXEC = 1, /* standard evaluation step */ - DEPSOP_TYPE_POST = 2, /* cleanup evaluation data + flush results */ - - /* Additional operation types */ - DEPSOP_TYPE_OUT = 3, /* indicator for outputting a temporary result that other components can use */ // XXX? - DEPSOP_TYPE_SIM = 4, /* indicator for things like IK Solvers and Rigidbody Sim steps which modify final results of separate entities at once */ - DEPSOP_TYPE_REBUILD = 5, /* rebuild internal evaluation data - used for Rigidbody Reset and Armature Rebuild-On-Load */ + /* **** Primary operation types **** */ + + /* Initialise evaluation data */ + DEPSOP_TYPE_INIT = 0, + /* Standard evaluation step */ + DEPSOP_TYPE_EXEC = 1, + /* Cleanup evaluation data + flush results */ + DEPSOP_TYPE_POST = 2, + + /* **** Additional operation types **** */ + /* Indicator for outputting a temporary result that other components + * can use. // XXX? + */ + DEPSOP_TYPE_OUT = 3, + /* Indicator for things like IK Solvers and Rigidbody Sim steps which + * modify final results of separate entities at once. + */ + DEPSOP_TYPE_SIM = 4, + /* Rebuild internal evaluation data - used for Rigidbody Reset and + * Armature Rebuild-On-Load. + */ + DEPSOP_TYPE_REBUILD = 5, } eDepsOperation_Type; /* Types of relationships between nodes @@ -170,4 +332,4 @@ typedef enum eDepsRelation_Type { DEPSREL_TYPE_UPDATE_UI, } eDepsRelation_Type; -#endif /* __DEPSGRAPH_TYPES_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_opcodes.h b/source/blender/depsgraph/intern/depsnode_opcodes.h deleted file mode 100644 index b81822c0ac5..00000000000 --- a/source/blender/depsgraph/intern/depsnode_opcodes.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * ***** 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) 2014 Blender Foundation. - * All rights reserved. - * - * Original Author: Joshua Leung - * Contributor(s): None Yet - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/intern/depsnode_opcodes.h - * \ingroup depsgraph - * - * \par OpCodes for OperationDepsNodes - * - * This file defines all the "operation codes" (opcodes) used to identify - * common operation node types. The intention of these defines is to have - * a fast and reliable way of identifying the relevant nodes within a component - * without having to use fragile dynamic strings. - * - * This file is meant to be used like UI_icons.h. That is, before including - * the file, the host file must define the DEG_OPCODE(_label) macro, which - * is responsible for converting the define into whatever form is suitable. - * Therefore, it intentionally doesn't have header guards. - */ - - -/* Example macro define: */ -/* #define DEF_DEG_OPCODE(label) DEG_OPCODE_##label, */ - -/* Generic Operations ------------------------------ */ - -/* Placeholder for operations which don't need special mention */ -DEF_DEG_OPCODE(OPERATION) - -// XXX: Placeholder while porting depsgraph code -DEF_DEG_OPCODE(PLACEHOLDER) - -DEF_DEG_OPCODE(NOOP) - -/* Animation, Drivers, etc. ------------------------ */ - -/* NLA + Action */ -DEF_DEG_OPCODE(ANIMATION) - -/* Driver */ -DEF_DEG_OPCODE(DRIVER) - -/* Proxy Inherit? */ -//DEF_DEG_OPCODE(PROXY) - -/* Transform --------------------------------------- */ - -/* Transform entry point - local transforms only */ -DEF_DEG_OPCODE(TRANSFORM_LOCAL) - -/* Parenting */ -DEF_DEG_OPCODE(TRANSFORM_PARENT) - -/* Constraints */ -DEF_DEG_OPCODE(TRANSFORM_CONSTRAINTS) -//DEF_DEG_OPCODE(TRANSFORM_CONSTRAINTS_INIT) -//DEF_DEG_OPCODE(TRANSFORM_CONSTRAINT) -//DEF_DEG_OPCODE(TRANSFORM_CONSTRAINTS_DONE) - -/* Rigidbody Sim - Perform Sim */ -DEF_DEG_OPCODE(RIGIDBODY_REBUILD) -DEF_DEG_OPCODE(RIGIDBODY_SIM) - -/* Rigidbody Sim - Copy Results to Object */ -DEF_DEG_OPCODE(TRANSFORM_RIGIDBODY) - -/* Transform exitpoint */ -DEF_DEG_OPCODE(TRANSFORM_FINAL) - -/* XXX: ubereval is for temporary porting purposes only */ -DEF_DEG_OPCODE(OBJECT_UBEREVAL) - -/* Geometry ---------------------------------------- */ - -/* XXX: Placeholder - UberEval */ -DEF_DEG_OPCODE(GEOMETRY_UBEREVAL) - -/* Modifier */ -DEF_DEG_OPCODE(GEOMETRY_MODIFIER) - -/* Curve Objects - Path Calculation (used for path-following tools) */ -DEF_DEG_OPCODE(GEOMETRY_PATH) - -/* Pose -------------------------------------------- */ - -/* Init IK Trees, etc. */ -DEF_DEG_OPCODE(POSE_INIT) - -/* Free IK Trees + Compute Deform Matrices */ -DEF_DEG_OPCODE(POSE_DONE) - -/* IK/Spline Solvers */ -DEF_DEG_OPCODE(POSE_IK_SOLVER) -DEF_DEG_OPCODE(POSE_SPLINE_IK_SOLVER) - -/* Bone -------------------------------------------- */ - -/* Bone local transforms - Entrypoint */ -DEF_DEG_OPCODE(BONE_LOCAL) - -/* Pose-space conversion (includes parent + restpose) */ -DEF_DEG_OPCODE(BONE_POSE_PARENT) - -/* Constraints */ -DEF_DEG_OPCODE(BONE_CONSTRAINTS) -//DEF_DEG_OPCODE(BONE_CONSTRAINTS_INIT) -//DEF_DEG_OPCODE(BONE_CONSTRAINT) -//DEF_DEG_OPCODE(BONE_CONSTRAINTS_DONE) - -/* Bone transforms are ready - * - "READY" This (internal) noop is used to signal that all pre-IK operations are done. - * Its role is to help mediate situations where cyclic relations may otherwise form - * (i.e. one bone in chain targetting another in same chain) - * - "DONE" This noop is used to signal that the bone's final pose transform can be read by others - */ -// TODO: deform mats could get calculated in the final_transform ops... -DEF_DEG_OPCODE(BONE_READY) -DEF_DEG_OPCODE(BONE_DONE) - -/* Particles --------------------------------------- */ - -/* XXX: placeholder - Particle System eval */ -DEF_DEG_OPCODE(PSYS_EVAL) diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc new file mode 100644 index 00000000000..198cd349002 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -0,0 +1,409 @@ +/* + * ***** 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) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/depsgraph_eval.cc + * \ingroup depsgraph + * + * Evaluation engine entrypoints for Depsgraph Engine. + */ + +#include "intern/eval/deg_eval.h" + +#include "PIL_time.h" + +extern "C" { +#include "BLI_utildefines.h" +#include "BLI_task.h" +#include "BLI_ghash.h" + +#include "BKE_depsgraph.h" +#include "BKE_global.h" + +#include "DEG_depsgraph.h" +} /* extern "C" */ + +#include "atomic_ops.h" + +#include "intern/eval/deg_eval_debug.h" +#include "intern/eval/deg_eval_flush.h" +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph.h" +#include "util/deg_util_foreach.h" + +/* Unfinished and unused, and takes quite some pre-processing time. */ +#undef USE_EVAL_PRIORITY + +/* Use integrated debugger to keep track how much each of the nodes was + * evaluating. + */ +#undef USE_DEBUGGER + +namespace DEG { + +/* ********************** */ +/* Evaluation Entrypoints */ + +/* Forward declarations. */ +static void schedule_children(TaskPool *pool, + Depsgraph *graph, + OperationDepsNode *node, + const int layers, + const int thread_id); + +struct DepsgraphEvalState { + EvaluationContext *eval_ctx; + Depsgraph *graph; + int layers; +}; + +static void deg_task_run_func(TaskPool *pool, + void *taskdata, + int thread_id) +{ + DepsgraphEvalState *state = + reinterpret_cast<DepsgraphEvalState *>(BLI_task_pool_userdata(pool)); + OperationDepsNode *node = reinterpret_cast<OperationDepsNode *>(taskdata); + + 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): We don't use component contexts at this moment. */ + /* ComponentDepsNode *comp = node->owner; */ + BLI_assert(node->owner != NULL); + + /* 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. */ +#ifdef USE_DEBUGGER + double start_time = PIL_check_seconds_timer(); + DepsgraphDebug::task_started(state->graph, node); +#endif + + /* Perform operation. */ + node->evaluate(state->eval_ctx); + + /* Note how long this took. */ +#ifdef USE_DEBUGGER + double end_time = PIL_check_seconds_timer(); + DepsgraphDebug::task_completed(state->graph, + node, + end_time - start_time); +#endif + } + + /* 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; + } + } +} + +typedef struct CalculatePengindData { + Depsgraph *graph; + int layers; +} CalculatePengindData; + +static void calculate_pending_func(void *data_v, int i) +{ + 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) + { + foreach (DepsRelation *rel, node->inlinks) { + 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) + { + ++node->num_links_pending; + } + } + } + } +} + +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) { + return; + } + node->done = 1; + + if (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) { + /* XXX standard cost of a node, could be estimated somewhat later on */ + const float cost = 1.0f; + /* NOOP nodes have no cost */ + node->eval_priority = node->is_noop() ? cost : 0.0f; + + foreach (DepsRelation *rel, node->outlinks) { + OperationDepsNode *to = (OperationDepsNode *)rel->to; + BLI_assert(to->type == DEPSNODE_TYPE_OPERATION); + calculate_eval_priority(to); + node->eval_priority += to->eval_priority; + } + } + else { + 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) +{ + foreach (OperationDepsNode *node, graph->operations) { + schedule_node(pool, graph, layers, node, false, 0); + } +} + +static void schedule_children(TaskPool *pool, + Depsgraph *graph, + OperationDepsNode *node, + const int layers, + const int thread_id) +{ + foreach (DepsRelation *rel, node->outlinks) { + OperationDepsNode *child = (OperationDepsNode *)rel->to; + BLI_assert(child->type == DEPSNODE_TYPE_OPERATION); + if (child->scheduled) { + /* Happens when having cyclic dependencies. */ + continue; + } + schedule_node(pool, + graph, + layers, + child, + (rel->flag & DEPSREL_FLAG_CYCLIC) == 0, + thread_id); + } +} + +/** + * Evaluate all nodes tagged for updating, + * \warning This is usually done as part of main loop, but may also be + * called from frame-change update. + * + * \note Time sources should be all valid! + */ +void deg_evaluate_on_refresh(EvaluationContext *eval_ctx, + Depsgraph *graph, + const int layers) +{ + /* Generate base evaluation context, upon which all the others are derived. */ + // TODO: this needs both main and scene access... + + /* Nothing to update, early out. */ + if (BLI_gset_size(graph->entry_tags) == 0) { + return; + } + + /* Set time for the current graph evaluation context. */ + TimeSourceDepsNode *time_src = graph->find_time_source(); + eval_ctx->ctime = time_src->cfra; + + /* XXX could use a separate pool for each eval context */ + DepsgraphEvalState state; + state.eval_ctx = eval_ctx; + state.graph = graph; + state.layers = layers; + + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &state); + + if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { + BLI_pool_set_num_threads(task_pool, 1); + } + + calculate_pending_parents(graph, layers); + + /* Clear tags. */ + foreach (OperationDepsNode *node, graph->operations) { + node->done = 0; + } + + /* Calculate priority for operation nodes. */ +#ifdef USE_EVAL_PRIORITY + foreach (OperationDepsNode *node, graph->operations) { + calculate_eval_priority(node); + } +#endif + + DepsgraphDebug::eval_begin(eval_ctx); + + schedule_graph(task_pool, graph, layers); + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + DepsgraphDebug::eval_end(eval_ctx); + + /* Clear any uncleared tags - just in case. */ + deg_graph_clear_tags(graph); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval.h b/source/blender/depsgraph/intern/eval/deg_eval.h new file mode 100644 index 00000000000..0d42f63433f --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval.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. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/eval/deg_eval.cc + * \ingroup depsgraph + * + * Evaluation engine entrypoints for Depsgraph Engine. + */ + +#pragma once + +struct EvaluationContext; + +namespace DEG { + +struct Depsgraph; + +/** + * Evaluate all nodes tagged for updating, + * \warning This is usually done as part of main loop, but may also be + * called from frame-change update. + * + * \note Time sources should be all valid! + */ +void deg_evaluate_on_refresh(EvaluationContext *eval_ctx, + Depsgraph *graph, + const int layers); + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_debug.cc b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc new file mode 100644 index 00000000000..67d64aae8bf --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc @@ -0,0 +1,249 @@ +/* + * ***** 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) 2014 Blender Foundation. + * All rights reserved. + * + * Original Author: Lukas Toenne + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/eval/deg_eval_debug.cc + * \ingroup depsgraph + * + * Implementation of tools for debugging the depsgraph + */ + +#include <cstring> + +#include "intern/eval/deg_eval_debug.h" + +extern "C" { +#include "BLI_listbase.h" +#include "BLI_ghash.h" + +#include "DEG_depsgraph_debug.h" + +#include "WM_api.h" +#include "WM_types.h" +} /* extern "C" */ + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_intern.h" + +namespace DEG { + +DepsgraphStats *DepsgraphDebug::stats = NULL; + +static string get_component_name(eDepsNode_Type type, const string &name = "") +{ + DepsNodeFactory *factory = deg_get_node_factory(type); + if (name.empty()) { + return string(factory->tname()); + } + else { + return string(factory->tname()) + " | " + name; + } +} + +static void times_clear(DepsgraphStatsTimes ×) +{ + times.duration_last = 0.0f; +} + +static void times_add(DepsgraphStatsTimes ×, float time) +{ + times.duration_last += time; +} + +void DepsgraphDebug::eval_begin(const EvaluationContext *UNUSED(eval_ctx)) +{ + /* TODO(sergey): Stats are currently globally disabled. */ + /* verify_stats(); */ + reset_stats(); +} + +void DepsgraphDebug::eval_end(const EvaluationContext *UNUSED(eval_ctx)) +{ + WM_main_add_notifier(NC_SPACE | ND_SPACE_INFO_REPORT, NULL); +} + +void DepsgraphDebug::eval_step(const EvaluationContext *UNUSED(eval_ctx), + const char *message) +{ +#ifdef DEG_DEBUG_BUILD + if (deg_debug_eval_cb) + deg_debug_eval_cb(deg_debug_eval_userdata, message); +#else + (void)message; /* Ignored. */ +#endif +} + +void DepsgraphDebug::task_started(Depsgraph *graph, + const OperationDepsNode *node) +{ + if (stats) { + BLI_spin_lock(&graph->lock); + + ComponentDepsNode *comp = node->owner; + ID *id = comp->owner->id; + + DepsgraphStatsID *id_stats = get_id_stats(id, true); + times_clear(id_stats->times); + + /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */ + if (0) { + /* XXX component name usage needs cleanup! currently mixes identifier + * and description strings! + */ + DepsgraphStatsComponent *comp_stats = + get_component_stats(id, get_component_name(comp->type, + comp->name), + true); + times_clear(comp_stats->times); + } + + BLI_spin_unlock(&graph->lock); + } +} + +void DepsgraphDebug::task_completed(Depsgraph *graph, + const OperationDepsNode *node, + double time) +{ + if (stats) { + BLI_spin_lock(&graph->lock); + + ComponentDepsNode *comp = node->owner; + ID *id = comp->owner->id; + + DepsgraphStatsID *id_stats = get_id_stats(id, true); + times_add(id_stats->times, time); + + /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */ + if (0) { + /* XXX component name usage needs cleanup! currently mixes identifier + * and description strings! + */ + DepsgraphStatsComponent *comp_stats = + get_component_stats(id, + get_component_name(comp->type, + comp->name), + true); + times_add(comp_stats->times, time); + } + + BLI_spin_unlock(&graph->lock); + } +} + +/* ********** */ +/* Statistics */ + + +/* GHash callback */ +static void deg_id_stats_free(void *val) +{ + DepsgraphStatsID *id_stats = (DepsgraphStatsID *)val; + + if (id_stats) { + BLI_freelistN(&id_stats->components); + MEM_freeN(id_stats); + } +} + +void DepsgraphDebug::stats_init() +{ + if (!stats) { + stats = (DepsgraphStats *)MEM_callocN(sizeof(DepsgraphStats), + "Depsgraph Stats"); + stats->id_stats = BLI_ghash_new(BLI_ghashutil_ptrhash, + BLI_ghashutil_ptrcmp, + "Depsgraph ID Stats Hash"); + } +} + +void DepsgraphDebug::stats_free() +{ + if (stats) { + BLI_ghash_free(stats->id_stats, NULL, deg_id_stats_free); + MEM_freeN(stats); + stats = NULL; + } +} + +void DepsgraphDebug::verify_stats() +{ + stats_init(); +} + +void DepsgraphDebug::reset_stats() +{ + if (!stats) { + return; + } + + /* XXX this doesn't work, will immediately clear all info, + * since most depsgraph updates have none or very few updates to handle. + * + * Could consider clearing only zero-user ID blocks here + */ +// BLI_ghash_clear(stats->id_stats, NULL, deg_id_stats_free); +} + +DepsgraphStatsID *DepsgraphDebug::get_id_stats(ID *id, bool create) +{ + DepsgraphStatsID *id_stats = (DepsgraphStatsID *)BLI_ghash_lookup(stats->id_stats, id); + + if (!id_stats && create) { + id_stats = (DepsgraphStatsID *)MEM_callocN(sizeof(DepsgraphStatsID), + "Depsgraph ID Stats"); + id_stats->id = id; + + BLI_ghash_insert(stats->id_stats, id, id_stats); + } + + return id_stats; +} + +DepsgraphStatsComponent *DepsgraphDebug::get_component_stats( + DepsgraphStatsID *id_stats, + const string &name, + bool create) +{ + DepsgraphStatsComponent *comp_stats; + for (comp_stats = (DepsgraphStatsComponent *)id_stats->components.first; + comp_stats != NULL; + comp_stats = comp_stats->next) + { + if (STREQ(comp_stats->name, name.c_str())) + break; + } + if (!comp_stats && create) { + comp_stats = (DepsgraphStatsComponent *)MEM_callocN(sizeof(DepsgraphStatsComponent), + "Depsgraph Component Stats"); + BLI_strncpy(comp_stats->name, name.c_str(), sizeof(comp_stats->name)); + BLI_addtail(&id_stats->components, comp_stats); + } + return comp_stats; +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_debug.h b/source/blender/depsgraph/intern/eval/deg_eval_debug.h index 64b97855f57..9109019eb2d 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_debug.h @@ -24,27 +24,26 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/intern/depsgraph_debug.h +/** \file blender/depsgraph/intern/eval/deg_eval_debug.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_DEBUG_H__ -#define __DEPSGRAPH_DEBUG_H__ +#pragma once -#include "depsgraph_types.h" +#include "intern/depsgraph_types.h" -extern "C" { -#include "BKE_global.h" -} +struct ID; +struct EvaluationContext; struct DepsgraphStats; struct DepsgraphStatsID; struct DepsgraphStatsComponent; -struct DepsgraphSettings; -struct EvaluationContext; -struct OperationDepsNode; + +namespace DEG { struct Depsgraph; +struct DepsgraphSettings; +struct OperationDepsNode; struct DepsgraphDebug { static DepsgraphStats *stats; @@ -77,11 +76,4 @@ struct DepsgraphDebug { } }; -#define DEG_DEBUG_PRINTF(...) \ - { \ - if (G.debug & G_DEBUG_DEPSGRAPH) { \ - fprintf(stderr, __VA_ARGS__); \ - } \ - } \ - -#endif /* __DEPSGRAPH_DEBUG_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc new file mode 100644 index 00000000000..af68f5c55c4 --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -0,0 +1,224 @@ +/* + * ***** 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) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/depsgraph_tag.cc + * \ingroup depsgraph + * + * Core routines for how the Depsgraph works. + */ + +#include "intern/eval/deg_eval_flush.h" + +// TODO(sergey): Use some sort of wrapper. +#include <queue> + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_utildefines.h" +#include "BLI_task.h" +#include "BLI_ghash.h" + +#include "DEG_depsgraph.h" +} /* extern "C" */ + +#include "intern/nodes/deg_node.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" + +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" + +namespace DEG { + +namespace { + +// TODO(sergey): De-duplicate with depsgraph_tag,cc +void lib_id_recalc_tag(Main *bmain, ID *id) +{ + id->tag |= LIB_TAG_ID_RECALC; + DEG_id_type_tag(bmain, GS(id->name)); +} + +void lib_id_recalc_data_tag(Main *bmain, ID *id) +{ + id->tag |= LIB_TAG_ID_RECALC_DATA; + DEG_id_type_tag(bmain, GS(id->name)); +} + +} /* namespace */ + +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) +{ + /* Sanity check. */ + if (graph == NULL) { + return; + } + + /* Nothing to update, early out. */ + if (BLI_gset_size(graph->entry_tags) == 0) { + return; + } + + /* TODO(sergey): With a bit of flag magic we can get rid of this + * extra loop. + */ + 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... */ + /* NOTE: Also need to ensure that for each of these, there is a path back to + * root, or else they won't be done. + * NOTE: Count how many nodes we need to handle - entry nodes may be + * component nodes which don't count for this purpose! + */ + GSET_FOREACH_BEGIN(OperationDepsNode *, node, graph->entry_tags) + { + queue.push(node); + node->scheduled = true; + } + GSET_FOREACH_END(); + + while (!queue.empty()) { + OperationDepsNode *node = queue.front(); + queue.pop(); + + for (;;) { + node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + + IDDepsNode *id_node = node->owner->owner; + + if (id_node->done == 0) { + deg_editors_id_update(bmain, id_node->id); + id_node->done = 1; + } + + lib_id_recalc_tag(bmain, id_node->id); + /* TODO(sergey): For until we've got proper data nodes in the graph. */ + lib_id_recalc_data_tag(bmain, id_node->id); + + ID *id = id_node->id; + /* This code is used to preserve those areas which does direct + * object update, + * + * Plus it ensures visibility changes and relations and layers + * visibility update has proper flags to work with. + */ + if (GS(id->name) == ID_OB) { + Object *object = (Object *)id; + ComponentDepsNode *comp_node = node->owner; + if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { + object->recalc |= OB_RECALC_TIME; + } + else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { + object->recalc |= OB_RECALC_OB; + } + else { + object->recalc |= OB_RECALC_DATA; + } + } + + /* TODO(sergey): For until incremental updates are possible + * witin a component at least we tag the whole component + * for update. + */ + ComponentDepsNode *component = node->owner; + if ((component->flags & DEPSCOMP_FULLY_SCHEDULED) == 0) { + foreach (OperationDepsNode *op, component->operations) { + op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + } + component->flags |= DEPSCOMP_FULLY_SCHEDULED; + } + + /* Flush to nodes along links... */ + if (node->outlinks.size() == 1) { + OperationDepsNode *to_node = (OperationDepsNode *)node->outlinks[0]->to; + if (to_node->scheduled == false) { + to_node->scheduled = true; + node = to_node; + } + else { + break; + } + } + else { + foreach (DepsRelation *rel, node->outlinks) { + OperationDepsNode *to_node = (OperationDepsNode *)rel->to; + if (to_node->scheduled == false) { + queue.push(to_node); + to_node->scheduled = true; + } + } + break; + } + } + } +} + +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. */ + 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. */ + BLI_gset_clear(graph->entry_tags, NULL); +} + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h new file mode 100644 index 00000000000..8912aebee7d --- /dev/null +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h @@ -0,0 +1,49 @@ +/* + * ***** 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) 2013 Blender Foundation. + * All rights reserved. + * + * Original Author: Joshua Leung + * Contributor(s): None Yet + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/intern/eval/deg_eval_flush.cc + * \ingroup depsgraph + * + * Core routines for how the Depsgraph works. + */ + +#pragma once + +struct Main; + +namespace DEG { + +struct Depsgraph; + +/* Flush updates from tagged nodes outwards until all affected nodes + * are tagged. + */ +void deg_graph_flush_updates(struct Main *bmain, struct Depsgraph *graph); + +/* Clear tags from all operation nodes. */ +void deg_graph_clear_tags(struct Depsgraph *graph); + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode.cc b/source/blender/depsgraph/intern/nodes/deg_node.cc index 7d4d6890c83..78293f7f483 100644 --- a/source/blender/depsgraph/intern/depsnode.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node.cc @@ -28,10 +28,13 @@ * \ingroup depsgraph */ +#include "intern/nodes/deg_node.h" + #include <stdio.h> #include <string.h> #include "BLI_utildefines.h" +#include "BLI_ghash.h" extern "C" { #include "DNA_ID.h" @@ -42,10 +45,13 @@ extern "C" { #include "DEG_depsgraph.h" } -#include "depsnode.h" /* own include */ -#include "depsnode_component.h" -#include "depsnode_operation.h" -#include "depsgraph_intern.h" +#include "intern/nodes/deg_node_component.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" +#include "util/deg_util_hash.h" + +namespace DEG { /* *************** */ /* Node Management */ @@ -66,26 +72,19 @@ DepsNode::TypeInfo::TypeInfo(eDepsNode_Type type, const char *tname) DepsNode::DepsNode() { - this->name[0] = '\0'; + name[0] = '\0'; } 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) - { + foreach (DepsRelation *rel, inlinks) { 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; } @@ -105,11 +104,7 @@ string DepsNode::identifier() const void TimeSourceDepsNode::tag_update(Depsgraph *graph) { - for (DepsNode::Relations::const_iterator it = outlinks.begin(); - it != outlinks.end(); - ++it) - { - DepsRelation *rel = *it; + foreach (DepsRelation *rel, outlinks) { DepsNode *node = rel->to; node->tag_update(graph); } @@ -130,7 +125,7 @@ RootDepsNode::~RootDepsNode() TimeSourceDepsNode *RootDepsNode::add_time_source(const string &name) { if (!time_source) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_TIMESOURCE); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_TIMESOURCE); time_source = (TimeSourceDepsNode *)factory->create_node(NULL, "", name); /*time_source->owner = this;*/ // XXX } @@ -147,6 +142,36 @@ static DepsNodeFactoryImpl<TimeSourceDepsNode> DNTI_TIMESOURCE; /* ID Node ================================================ */ +static unsigned int id_deps_node_hash_key(const void *key_v) +{ + const IDDepsNode::ComponentIDKey *key = + reinterpret_cast<const IDDepsNode::ComponentIDKey *>(key_v); + return hash_combine(BLI_ghashutil_uinthash(key->type), + BLI_ghashutil_strhash_p(key->name.c_str())); +} + +static bool id_deps_node_hash_key_cmp(const void *a, const void *b) +{ + const IDDepsNode::ComponentIDKey *key_a = + reinterpret_cast<const IDDepsNode::ComponentIDKey *>(a); + const IDDepsNode::ComponentIDKey *key_b = + reinterpret_cast<const IDDepsNode::ComponentIDKey *>(b); + return !(*key_a == *key_b); +} + +static void id_deps_node_hash_key_free(void *key_v) +{ + typedef IDDepsNode::ComponentIDKey ComponentIDKey; + ComponentIDKey *key = reinterpret_cast<ComponentIDKey *>(key_v); + OBJECT_GUARDED_DELETE(key, ComponentIDKey); +} + +static void id_deps_node_hash_value_free(void *value_v) +{ + ComponentDepsNode *comp_node = reinterpret_cast<ComponentDepsNode *>(value_v); + OBJECT_GUARDED_DELETE(comp_node, ComponentDepsNode); +} + /* Initialize 'id' node - from pointer data given. */ void IDDepsNode::init(const ID *id, const string &UNUSED(subdata)) { @@ -156,6 +181,10 @@ void IDDepsNode::init(const ID *id, const string &UNUSED(subdata)) this->layers = (1 << 20) - 1; this->eval_flags = 0; + components = BLI_ghash_new(id_deps_node_hash_key, + id_deps_node_hash_key_cmp, + "Depsgraph id components hash"); + /* NOTE: components themselves are created if/when needed. * This prevents problems with components getting added * twice if an ID-Ref needs to be created to house it... @@ -166,51 +195,27 @@ void IDDepsNode::init(const ID *id, const string &UNUSED(subdata)) IDDepsNode::~IDDepsNode() { clear_components(); -} - -/* Copy 'id' node. */ -void IDDepsNode::copy(DepsgraphCopyContext *dcc, const IDDepsNode *src) -{ - (void)src; /* Ignored. */ - /* Iterate over items in original hash, adding them to new hash. */ - for (IDDepsNode::ComponentMap::const_iterator it = this->components.begin(); - it != this->components.end(); - ++it) - { - /* Get current <type : component> mapping. */ - ComponentIDKey c_key = it->first; - DepsNode *old_component = it->second; - - /* Make a copy of component. */ - ComponentDepsNode *component = (ComponentDepsNode *)DEG_copy_node(dcc, old_component); - - /* Add new node to hash... */ - this->components[c_key] = component; - } - - // TODO: perform a second loop to fix up links? - BLI_assert(!"Not expected to be used"); + BLI_ghash_free(components, id_deps_node_hash_key_free, NULL); } ComponentDepsNode *IDDepsNode::find_component(eDepsNode_Type type, const string &name) const { ComponentIDKey key(type, name); - ComponentMap::const_iterator it = components.find(key); - return it != components.end() ? it->second : NULL; + return reinterpret_cast<ComponentDepsNode *>(BLI_ghash_lookup(components, &key)); } ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type, const string &name) { - ComponentIDKey key(type, name); ComponentDepsNode *comp_node = find_component(type, name); if (!comp_node) { - DepsNodeFactory *factory = DEG_get_node_factory(type); + DepsNodeFactory *factory = deg_get_node_factory(type); comp_node = (ComponentDepsNode *)factory->create_node(this->id, "", name); /* Register. */ - this->components[key] = comp_node; + ComponentIDKey *key = OBJECT_GUARDED_NEW(ComponentIDKey, type, name); + BLI_ghash_insert(components, key, comp_node); comp_node->owner = this; } return comp_node; @@ -218,34 +223,28 @@ ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type, void IDDepsNode::remove_component(eDepsNode_Type type, const string &name) { - ComponentIDKey key(type, name); ComponentDepsNode *comp_node = find_component(type, name); if (comp_node) { /* Unregister. */ - this->components.erase(key); - OBJECT_GUARDED_DELETE(comp_node, ComponentDepsNode); + ComponentIDKey key(type, name); + BLI_ghash_remove(components, + &key, + id_deps_node_hash_key_free, + id_deps_node_hash_value_free); } } void IDDepsNode::clear_components() { - for (ComponentMap::const_iterator it = components.begin(); - it != components.end(); - ++it) - { - ComponentDepsNode *comp_node = it->second; - OBJECT_GUARDED_DELETE(comp_node, ComponentDepsNode); - } - components.clear(); + BLI_ghash_clear(components, + id_deps_node_hash_key_free, + id_deps_node_hash_value_free); } void IDDepsNode::tag_update(Depsgraph *graph) { - for (ComponentMap::const_iterator it = components.begin(); - it != components.end(); - ++it) + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components) { - ComponentDepsNode *comp_node = it->second; /* TODO(sergey): What about drievrs? */ bool do_component_tag = comp_node->type != DEPSNODE_TYPE_ANIMATION; if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { @@ -259,6 +258,16 @@ void IDDepsNode::tag_update(Depsgraph *graph) comp_node->tag_update(graph); } } + GHASH_FOREACH_END(); +} + +void IDDepsNode::finalize_build() +{ + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components) + { + comp_node->finalize_build(); + } + GHASH_FOREACH_END(); } DEG_DEPSNODE_DEFINE(IDDepsNode, DEPSNODE_TYPE_ID_REF, "ID Node"); @@ -286,31 +295,21 @@ SubgraphDepsNode::~SubgraphDepsNode() // XXX: prune these flags a bit... if ((this->flag & SUBGRAPH_FLAG_FIRSTREF) || !(this->flag & SUBGRAPH_FLAG_SHARED)) { /* Free the referenced graph. */ - DEG_graph_free(this->graph); - this->graph = NULL; + DEG_graph_free(reinterpret_cast< ::Depsgraph* >(graph)); + graph = NULL; } } -/* Copy 'subgraph' node - Assume that the subgraph doesn't get copied for now... */ -void SubgraphDepsNode::copy(DepsgraphCopyContext * /*dcc*/, - const SubgraphDepsNode * /*src*/) -{ - //const SubgraphDepsNode *src_node = (const SubgraphDepsNode *)src; - //SubgraphDepsNode *dst_node = (SubgraphDepsNode *)dst; - - /* for now, subgraph itself isn't copied... */ - BLI_assert(!"Not expected to be used"); -} - DEG_DEPSNODE_DEFINE(SubgraphDepsNode, DEPSNODE_TYPE_SUBGRAPH, "Subgraph Node"); static DepsNodeFactoryImpl<SubgraphDepsNode> DNTI_SUBGRAPH; - -void DEG_register_base_depsnodes() +void deg_register_base_depsnodes() { - DEG_register_node_typeinfo(&DNTI_ROOT); - DEG_register_node_typeinfo(&DNTI_TIMESOURCE); + deg_register_node_typeinfo(&DNTI_ROOT); + deg_register_node_typeinfo(&DNTI_TIMESOURCE); - DEG_register_node_typeinfo(&DNTI_ID_REF); - DEG_register_node_typeinfo(&DNTI_SUBGRAPH); + deg_register_node_typeinfo(&DNTI_ID_REF); + deg_register_node_typeinfo(&DNTI_SUBGRAPH); } + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode.h b/source/blender/depsgraph/intern/nodes/deg_node.h index 53826ae8e71..d79d3d2348d 100644 --- a/source/blender/depsgraph/intern/depsnode.h +++ b/source/blender/depsgraph/intern/nodes/deg_node.h @@ -28,21 +28,18 @@ * \ingroup depsgraph */ -#ifndef __DEPSNODE_H__ -#define __DEPSNODE_H__ +#pragma once -#include "depsgraph_types.h" - -#include "depsgraph_util_hash.h" -#include "depsgraph_util_map.h" -#include "depsgraph_util_set.h" +#include "intern/depsgraph_types.h" struct ID; +struct GHash; struct Scene; +namespace DEG { + struct Depsgraph; struct DepsRelation; -struct DepsgraphCopyContext; struct OperationDepsNode; /* *********************************** */ @@ -73,7 +70,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; @@ -94,8 +91,6 @@ struct DepsNode { virtual void init(const ID * /*id*/, const string &/*subdata*/) {} - virtual void copy(DepsgraphCopyContext * /*dcc*/, - const DepsNode * /*src*/) {} virtual void tag_update(Depsgraph * /*graph*/) {} @@ -160,24 +155,7 @@ struct IDDepsNode : public DepsNode { string name; }; - /* XXX can't specialize std::hash for this purpose, because ComponentIDKey is - * a nested type ... - * - * http://stackoverflow.com/a/951245 - */ - struct component_key_hash { - bool operator() (const ComponentIDKey &key) const - { - return hash_combine(hash<int>()(key.type), hash<string>()(key.name)); - } - }; - - typedef unordered_map<ComponentIDKey, - ComponentDepsNode *, - component_key_hash> ComponentMap; - void init(const ID *id, const string &subdata); - void copy(DepsgraphCopyContext *dcc, const IDDepsNode *src); ~IDDepsNode(); ComponentDepsNode *find_component(eDepsNode_Type type, @@ -189,11 +167,13 @@ struct IDDepsNode : public DepsNode { void tag_update(Depsgraph *graph); + void finalize_build(); + /* ID Block referenced. */ ID *id; /* Hash to make it faster to look up components. */ - ComponentMap components; + GHash *components; /* Layers of this node with accumulated layers of it's output relations. */ int layers; @@ -210,7 +190,6 @@ struct IDDepsNode : public DepsNode { /* Subgraph Reference. */ struct SubgraphDepsNode : public DepsNode { void init(const ID *id, const string &subdata); - void copy(DepsgraphCopyContext *dcc, const SubgraphDepsNode *src); ~SubgraphDepsNode(); /* Instanced graph. */ @@ -243,6 +222,6 @@ typedef enum eSubgraphRef_Flag { SUBGRAPH_FLAG_FIRSTREF = (1 << 1), } eSubgraphRef_Flag; -void DEG_register_base_depsnodes(); +void deg_register_base_depsnodes(); -#endif /* __DEPSNODE_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc index a47a0d29228..c529e2ecfe6 100644 --- a/source/blender/depsgraph/intern/depsnode_component.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc @@ -28,6 +28,8 @@ * \ingroup depsgraph */ +#include "intern/nodes/deg_node_component.h" + #include <stdio.h> #include <string.h> @@ -39,20 +41,57 @@ extern "C" { #include "BKE_action.h" } /* extern "C" */ -#include "depsnode_component.h" /* own include */ -#include "depsnode_operation.h" -#include "depsgraph_intern.h" +#include "intern/nodes/deg_node_operation.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_foreach.h" +#include "util/deg_util_hash.h" + +namespace DEG { /* *********** */ /* Outer Nodes */ /* Standard Component Methods ============================= */ +static unsigned int comp_node_hash_key(const void *key_v) +{ + const ComponentDepsNode::OperationIDKey *key = + reinterpret_cast<const ComponentDepsNode::OperationIDKey *>(key_v); + return hash_combine(BLI_ghashutil_uinthash(key->opcode), + BLI_ghashutil_strhash_p(key->name.c_str())); +} + +static bool comp_node_hash_key_cmp(const void *a, const void *b) +{ + const ComponentDepsNode::OperationIDKey *key_a = + reinterpret_cast<const ComponentDepsNode::OperationIDKey *>(a); + const ComponentDepsNode::OperationIDKey *key_b = + reinterpret_cast<const ComponentDepsNode::OperationIDKey *>(b); + return !(*key_a == *key_b); +} + +static void comp_node_hash_key_free(void *key_v) +{ + typedef ComponentDepsNode::OperationIDKey OperationIDKey; + OperationIDKey *key = reinterpret_cast<OperationIDKey *>(key_v); + OBJECT_GUARDED_DELETE(key, OperationIDKey); +} + +static void comp_node_hash_value_free(void *value_v) +{ + OperationDepsNode *op_node = reinterpret_cast<OperationDepsNode *>(value_v); + OBJECT_GUARDED_DELETE(op_node, OperationDepsNode); +} + ComponentDepsNode::ComponentDepsNode() : entry_operation(NULL), exit_operation(NULL), - flags(0) + flags(0), + layers(0) { + operations_map = BLI_ghash_new(comp_node_hash_key, + comp_node_hash_key_cmp, + "Depsgraph id hash"); } /* Initialize 'component' node - from pointer data given */ @@ -63,37 +102,15 @@ void ComponentDepsNode::init(const ID * /*id*/, // XXX: maybe this needs a special API? } -/* Copy 'component' node */ -void ComponentDepsNode::copy(DepsgraphCopyContext * /*dcc*/, - const ComponentDepsNode * /*src*/) -{ -#if 0 // XXX: remove all this - /* duplicate list of operation nodes */ - this->operations.clear(); - - for (OperationMap::const_iterator it = src->operations.begin(); it != src->operations.end(); ++it) { - const string &pchan_name = it->first; - OperationDepsNode *src_op = it->second; - - /* recursive copy */ - DepsNodeFactory *factory = DEG_node_get_factory(src_op); - OperationDepsNode *dst_op = (OperationDepsNode *)factory->copy_node(dcc, src_op); - this->operations[pchan_name] = dst_op; - - /* fix links... */ - // ... - } - - /* copy evaluation contexts */ - // -#endif - BLI_assert(!"Not expected to be called"); -} - /* Free 'component' node */ ComponentDepsNode::~ComponentDepsNode() { clear_operations(); + if (operations_map != NULL) { + BLI_ghash_free(operations_map, + comp_node_hash_key_free, + comp_node_hash_value_free); + } } string ComponentDepsNode::identifier() const @@ -103,15 +120,17 @@ string ComponentDepsNode::identifier() const char typebuf[7]; sprintf(typebuf, "(%d)", type); - return string(typebuf) + name + " : " + idname; + char layers[7]; + sprintf(layers, "%d", this->layers); + + return string(typebuf) + name + " : " + idname + " (Layers: " + layers + ")"; } OperationDepsNode *ComponentDepsNode::find_operation(OperationIDKey key) const { - OperationMap::const_iterator it = this->operations.find(key); - - if (it != this->operations.end()) { - return it->second; + OperationDepsNode *node = reinterpret_cast<OperationDepsNode *>(BLI_ghash_lookup(operations_map, &key)); + if (node != NULL) { + return node; } else { fprintf(stderr, "%s: find_operation(%s) failed\n", @@ -129,11 +148,7 @@ OperationDepsNode *ComponentDepsNode::find_operation(eDepsOperation_Code opcode, OperationDepsNode *ComponentDepsNode::has_operation(OperationIDKey key) const { - OperationMap::const_iterator it = this->operations.find(key); - if (it != this->operations.end()) { - return it->second; - } - return NULL; + return reinterpret_cast<OperationDepsNode *>(BLI_ghash_lookup(operations_map, &key)); } OperationDepsNode *ComponentDepsNode::has_operation(eDepsOperation_Code opcode, @@ -147,12 +162,12 @@ OperationDepsNode *ComponentDepsNode::add_operation(eDepsOperation_Type optype, { OperationDepsNode *op_node = has_operation(opcode, name); if (!op_node) { - DepsNodeFactory *factory = DEG_get_node_factory(DEPSNODE_TYPE_OPERATION); + DepsNodeFactory *factory = deg_get_node_factory(DEPSNODE_TYPE_OPERATION); op_node = (OperationDepsNode *)factory->create_node(this->owner->id, "", name); /* register opnode in this component's operation set */ - OperationIDKey key(opcode, name); - this->operations[key] = op_node; + OperationIDKey *key = OBJECT_GUARDED_NEW(OperationIDKey, opcode, name); + BLI_ghash_insert(operations_map, key, op_node); /* set as entry/exit node of component (if appropriate) */ if (optype == DEPSOP_TYPE_INIT) { @@ -185,18 +200,22 @@ OperationDepsNode *ComponentDepsNode::add_operation(eDepsOperation_Type optype, void ComponentDepsNode::remove_operation(eDepsOperation_Code opcode, const string &name) { - OperationDepsNode *op_node = find_operation(opcode, name); - if (op_node) { - /* unregister */ - this->operations.erase(OperationIDKey(opcode, name)); - OBJECT_GUARDED_DELETE(op_node, OperationDepsNode); - } + /* unregister */ + OperationIDKey key(opcode, name); + BLI_ghash_remove(operations_map, + &key, + comp_node_hash_key_free, + comp_node_hash_key_free); } void ComponentDepsNode::clear_operations() { - for (OperationMap::const_iterator it = operations.begin(); it != operations.end(); ++it) { - OperationDepsNode *op_node = it->second; + if (operations_map != NULL) { + BLI_ghash_clear(operations_map, + comp_node_hash_key_free, + comp_node_hash_value_free); + } + foreach (OperationDepsNode *op_node, operations) { OBJECT_GUARDED_DELETE(op_node, OperationDepsNode); } operations.clear(); @@ -208,30 +227,71 @@ void ComponentDepsNode::tag_update(Depsgraph *graph) if (entry_op != NULL && entry_op->flag & DEPSOP_FLAG_NEEDS_UPDATE) { return; } - for (OperationMap::const_iterator it = operations.begin(); it != operations.end(); ++it) { - OperationDepsNode *op_node = it->second; + foreach (OperationDepsNode *op_node, operations) { op_node->tag_update(graph); } } OperationDepsNode *ComponentDepsNode::get_entry_operation() { - if (entry_operation) + if (entry_operation) { return entry_operation; - else if (operations.size() == 1) - return operations.begin()->second; + } + else if (operations_map != NULL && BLI_ghash_size(operations_map) == 1) { + OperationDepsNode *op_node = NULL; + /* TODO(sergey): This is somewhat slow. */ + GHASH_FOREACH_BEGIN(OperationDepsNode *, tmp, operations_map) + { + op_node = tmp; + } + GHASH_FOREACH_END(); + /* Cache for the subsequent usage. */ + entry_operation = op_node; + return op_node; + } + else if(operations.size() == 1) { + return operations[0]; + } return NULL; } OperationDepsNode *ComponentDepsNode::get_exit_operation() { - if (exit_operation) + if (exit_operation) { return exit_operation; - else if (operations.size() == 1) - return operations.begin()->second; + } + else if (operations_map != NULL && BLI_ghash_size(operations_map) == 1) { + OperationDepsNode *op_node = NULL; + /* TODO(sergey): This is somewhat slow. */ + GHASH_FOREACH_BEGIN(OperationDepsNode *, tmp, operations_map) + { + op_node = tmp; + } + GHASH_FOREACH_END(); + /* Cache for the subsequent usage. */ + exit_operation = op_node; + return op_node; + } + else if(operations.size() == 1) { + return operations[0]; + } return NULL; } +void ComponentDepsNode::finalize_build() +{ + operations.reserve(BLI_ghash_size(operations_map)); + GHASH_FOREACH_BEGIN(OperationDepsNode *, op_node, operations_map) + { + operations.push_back(op_node); + } + GHASH_FOREACH_END(); + BLI_ghash_free(operations_map, + comp_node_hash_key_free, + NULL); + operations_map = NULL; +} + /* Parameter Component Defines ============================ */ DEG_DEPSNODE_DEFINE(ParametersComponentDepsNode, DEPSNODE_TYPE_PARAMETERS, "Parameters Component"); @@ -302,18 +362,20 @@ static DepsNodeFactoryImpl<ShadingComponentDepsNode> DNTI_SHADING; /* Node Types Register =================================== */ -void DEG_register_component_depsnodes() +void deg_register_component_depsnodes() { - DEG_register_node_typeinfo(&DNTI_PARAMETERS); - DEG_register_node_typeinfo(&DNTI_PROXY); - DEG_register_node_typeinfo(&DNTI_ANIMATION); - DEG_register_node_typeinfo(&DNTI_TRANSFORM); - DEG_register_node_typeinfo(&DNTI_GEOMETRY); - DEG_register_node_typeinfo(&DNTI_SEQUENCER); - - DEG_register_node_typeinfo(&DNTI_EVAL_POSE); - DEG_register_node_typeinfo(&DNTI_BONE); - - DEG_register_node_typeinfo(&DNTI_EVAL_PARTICLES); - DEG_register_node_typeinfo(&DNTI_SHADING); + deg_register_node_typeinfo(&DNTI_PARAMETERS); + deg_register_node_typeinfo(&DNTI_PROXY); + deg_register_node_typeinfo(&DNTI_ANIMATION); + deg_register_node_typeinfo(&DNTI_TRANSFORM); + deg_register_node_typeinfo(&DNTI_GEOMETRY); + deg_register_node_typeinfo(&DNTI_SEQUENCER); + + deg_register_node_typeinfo(&DNTI_EVAL_POSE); + deg_register_node_typeinfo(&DNTI_BONE); + + deg_register_node_typeinfo(&DNTI_EVAL_PARTICLES); + deg_register_node_typeinfo(&DNTI_SHADING); } + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_component.h b/source/blender/depsgraph/intern/nodes/deg_node_component.h index 7f44c0ed03f..df321ea9299 100644 --- a/source/blender/depsgraph/intern/depsnode_component.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h @@ -28,21 +28,22 @@ * \ingroup depsgraph */ -#ifndef __DEPSNODE_COMPONENT_H__ -#define __DEPSNODE_COMPONENT_H__ +#pragma once -#include "depsnode.h" +#include "intern/nodes/deg_node.h" -#include "depsgraph_util_hash.h" -#include "depsgraph_util_map.h" -#include "depsgraph_util_set.h" +#include "BLI_utildefines.h" +#include "BLI_string.h" struct ID; struct bPoseChannel; +struct GHash; -struct Depsgraph; -struct DepsgraphCopyContext; struct EvaluationContext; + +namespace DEG { + +struct Depsgraph; struct OperationDepsNode; struct BoneComponentDepsNode; @@ -75,7 +76,7 @@ struct ComponentDepsNode : public DepsNode { string identifier() const { char codebuf[5]; - sprintf(codebuf, "%d", opcode); + BLI_snprintf(codebuf, sizeof(codebuf), "%d", opcode); return string("OperationIDKey(") + codebuf + ", " + name + ")"; } @@ -86,47 +87,41 @@ struct ComponentDepsNode : public DepsNode { } }; - /* XXX can't specialize std::hash for this purpose, because ComponentKey is a nested type ... - * http://stackoverflow.com/a/951245 - */ - struct operation_key_hash { - bool operator() (const OperationIDKey &key) const - { - return hash_combine(hash<int>()(key.opcode), hash<string>()(key.name)); - } - }; - /* Typedef for container of operations */ - typedef unordered_map<OperationIDKey, OperationDepsNode *, operation_key_hash> OperationMap; - - ComponentDepsNode(); ~ComponentDepsNode(); void init(const ID *id, const string &subdata); - void copy(DepsgraphCopyContext *dcc, const ComponentDepsNode *src); string identifier() const; /* Find an existing operation, will throw an assert() if it does not exist. */ OperationDepsNode *find_operation(OperationIDKey key) const; - OperationDepsNode *find_operation(eDepsOperation_Code opcode, const string &name) const; + OperationDepsNode *find_operation(eDepsOperation_Code opcode, + const string &name) const; /* Check operation exists and return it. */ OperationDepsNode *has_operation(OperationIDKey key) const; - OperationDepsNode *has_operation(eDepsOperation_Code opcode, const string &name) const; + OperationDepsNode *has_operation(eDepsOperation_Code opcode, + const string &name) const; /** * Create a new node for representing an operation and add this to graph - * \warning If an existing node is found, it will be modified. This helps when node may - * have been partially created earlier (e.g. parent ref before parent item is added) + * \warning If an existing node is found, it will be modified. This helps + * when node may have been partially created earlier (e.g. parent ref before + * parent item is added) * - * \param type: Operation node type (corresponding to context/component that it operates in) - * \param optype: Role that operation plays within component (i.e. where in eval process) + * \param type: Operation node type (corresponding to context/component that + * it operates in) + * \param optype: Role that operation plays within component + * (i.e. where in eval process) * \param op: The operation to perform * \param name: Identifier for operation - used to find/locate it again */ - OperationDepsNode *add_operation(eDepsOperation_Type optype, DepsEvalOperationCb op, eDepsOperation_Code opcode, const string &name); + OperationDepsNode *add_operation(eDepsOperation_Type optype, + DepsEvalOperationCb op, + eDepsOperation_Code opcode, + const string &name); void remove_operation(eDepsOperation_Code opcode, const string &name); void clear_operations(); @@ -135,9 +130,13 @@ struct ComponentDepsNode : public DepsNode { /* Evaluation Context Management .................. */ - /* Initialize component's evaluation context used for the specified purpose */ + /* Initialize component's evaluation context used for the specified + * purpose. + */ virtual bool eval_context_init(EvaluationContext * /*eval_ctx*/) { return false; } - /* Free data in component's evaluation context which is used for the specified purpose + /* Free data in component's evaluation context which is used for + * the specified purpose + * * NOTE: this does not free the actual context in question */ virtual void eval_context_free(EvaluationContext * /*eval_ctx*/) {} @@ -145,15 +144,31 @@ struct ComponentDepsNode : public DepsNode { OperationDepsNode *get_entry_operation(); OperationDepsNode *get_exit_operation(); + void finalize_build(); + IDDepsNode *owner; - OperationMap operations; /* inner nodes for this component */ + /* ** Inner nodes for this component ** */ + + /* Operations stored as a hash map, for faster build. + * This hash map will be freed when graph is fully built. + */ + GHash *operations_map; + + /* This is a "normal" list of operations, used by evaluation + * and other routines after construction. + */ + vector<OperationDepsNode *> operations; + OperationDepsNode *entry_operation; OperationDepsNode *exit_operation; // XXX: a poll() callback to check if component's first node can be started? int flags; + + /* Temporary bitmask, used during graph construction. */ + int layers; }; /* ---------------------------------------- */ @@ -204,6 +219,6 @@ struct ShadingComponentDepsNode : public ComponentDepsNode { }; -void DEG_register_component_depsnodes(); +void deg_register_component_depsnodes(); -#endif /* __DEPSNODE_COMPONENT_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_operation.cc b/source/blender/depsgraph/intern/nodes/deg_node_operation.cc index 6aeb163356b..a9f9703bb3b 100644 --- a/source/blender/depsgraph/intern/depsnode_operation.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_operation.cc @@ -28,35 +28,27 @@ * \ingroup depsgraph */ +#include "intern/nodes/deg_node_operation.h" + #include "MEM_guardedalloc.h" extern "C" { #include "BLI_utildefines.h" } /* extern "C" */ -#include "depsnode_operation.h" /* own include */ -#include "depsnode_component.h" -#include "depsgraph.h" -#include "depsgraph_intern.h" - -/* ******************************************************************* */ -/* OpNode Identifiers Array - Exported to other depsgraph files too... */ +#include "intern/depsgraph.h" +#include "intern/depsgraph_intern.h" +#include "util/deg_util_hash.h" -/* identifiers for operations */ -const char *DEG_OPNAMES[] = { -#define DEF_DEG_OPCODE(label) #label, -#include "depsnode_opcodes.h" -#undef DEF_DEG_OPCODE - - "<Invalid>" -}; +namespace DEG { /* *********** */ /* Inner Nodes */ OperationDepsNode::OperationDepsNode() : eval_priority(0.0f), - flag(0) + flag(0), + customdata_mask(0) { } @@ -66,7 +58,6 @@ OperationDepsNode::~OperationDepsNode() string OperationDepsNode::identifier() const { - BLI_assert((opcode > 0) && (opcode < ARRAY_SIZE(DEG_OPNAMES))); return string(DEG_OPNAMES[opcode]) + "(" + name + ")"; } @@ -98,7 +89,9 @@ void OperationDepsNode::tag_update(Depsgraph *graph) DEG_DEPSNODE_DEFINE(OperationDepsNode, DEPSNODE_TYPE_OPERATION, "Operation"); static DepsNodeFactoryImpl<OperationDepsNode> DNTI_OPERATION; -void DEG_register_operation_depsnodes() +void deg_register_operation_depsnodes() { - DEG_register_node_typeinfo(&DNTI_OPERATION); + deg_register_node_typeinfo(&DNTI_OPERATION); } + +} // namespace DEG diff --git a/source/blender/depsgraph/intern/depsnode_operation.h b/source/blender/depsgraph/intern/nodes/deg_node_operation.h index 1119e10805d..f03078fc3db 100644 --- a/source/blender/depsgraph/intern/depsnode_operation.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_operation.h @@ -28,15 +28,15 @@ * \ingroup depsgraph */ -#ifndef __DEPSNODE_OPERATION_H__ -#define __DEPSNODE_OPERATION_H__ +#pragma once -#include "depsnode.h" +#include "intern/nodes/deg_node.h" struct ID; struct Depsgraph; -struct DepsgraphCopyContext; + +namespace DEG { /* Flags for Depsgraph Nodes */ typedef enum eDepsOperation_Flag { @@ -44,10 +44,14 @@ typedef enum eDepsOperation_Flag { DEPSOP_FLAG_NEEDS_UPDATE = (1 << 0), /* node was directly modified, causing need for update */ - /* XXX: intention is to make it easier to tell when we just need to take subgraphs */ + /* XXX: intention is to make it easier to tell when we just need to + * take subgraphs. + */ DEPSOP_FLAG_DIRECTLY_MODIFIED = (1 << 1), - /* Operation is evaluated using CPython; has GIL and security implications... */ + /* Operation is evaluated using CPython; has GIL and security + * implications... + */ DEPSOP_FLAG_USES_PYTHON = (1 << 2), } eDepsOperation_Flag; @@ -68,23 +72,33 @@ struct OperationDepsNode : public DepsNode { OperationDepsNode *get_entry_operation() { return this; } OperationDepsNode *get_exit_operation() { return this; } - ComponentDepsNode *owner; /* component that contains the operation */ + /* Component that contains the operation. */ + ComponentDepsNode *owner; - DepsEvalOperationCb evaluate; /* callback for operation */ + /* Callback for operation. */ + DepsEvalOperationCb evaluate; - uint32_t num_links_pending; /* how many inlinks are we still waiting on before we can be evaluated... */ + /* How many inlinks are we still waiting on before we can be evaluated. */ + uint32_t num_links_pending; float eval_priority; bool scheduled; - short optype; /* (eDepsOperation_Type) stage of evaluation */ - int opcode; /* (eDepsOperation_Code) identifier for the operation being performed */ + /* Stage of evaluation */ + eDepsOperation_Type optype; + + /* Identifier for the operation being performed. */ + eDepsOperation_Code opcode; + + /* (eDepsOperation_Flag) extra settings affecting evaluation. */ + int flag; - int flag; /* (eDepsOperation_Flag) extra settings affecting evaluation */ + /* Extra customdata mask which needs to be evaluated for the object. */ + uint64_t customdata_mask; DEG_DEPSNODE_DECLARE; }; -void DEG_register_operation_depsnodes(); +void deg_register_operation_depsnodes(); -#endif /* __DEPSNODE_OPERATION_H__ */ +} // namespace DEG diff --git a/source/blender/depsgraph/util/deg_util_foreach.h b/source/blender/depsgraph/util/deg_util_foreach.h new file mode 100644 index 00000000000..14cf4fc11ed --- /dev/null +++ b/source/blender/depsgraph/util/deg_util_foreach.h @@ -0,0 +1,68 @@ +/* + * ***** 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. + * + * Original Author: Sergey Sharybin + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/depsgraph/util/deg_util_foreach.h + * \ingroup depsgraph + */ + +#pragma once + +#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800) +# define foreach(x, y) for(x : y) +#elif defined(HAVE_BOOST_FUNCTION_BINDINGS) +# include <boost/foreach.hpp> +# define foreach BOOST_FOREACH +#else +#pragma message("No available foreach() implementation. Using stub instead, disabling new depsgraph") + +#ifndef WITH_LEGACY_DEPSGRAPH +# error "Unable to build new depsgraph and legacy one is disabled." +#endif + +#define DISABLE_NEW_DEPSGRAPH + +# define foreach(x, y) for (x; false; (void)y) +#endif + +#define GHASH_FOREACH_BEGIN(type, var, what) \ + do { \ + GHashIterator gh_iter##var; \ + GHASH_ITER(gh_iter##var, what) { \ + type var = reinterpret_cast<type>(BLI_ghashIterator_getValue(&gh_iter##var)); \ + +#define GHASH_FOREACH_END() \ + } \ + } while(0) + +#define GSET_FOREACH_BEGIN(type, var, what) \ + do { \ + GSetIterator gh_iter##var; \ + GSET_ITER(gh_iter##var, what) { \ + type var = reinterpret_cast<type>(BLI_gsetIterator_getKey(&gh_iter##var)); \ + +#define GSET_FOREACH_END() \ + } \ + } while(0) diff --git a/source/blender/depsgraph/util/depsgraph_util_function.h b/source/blender/depsgraph/util/deg_util_function.h index a4301833408..1e34ae04d9a 100644 --- a/source/blender/depsgraph/util/depsgraph_util_function.h +++ b/source/blender/depsgraph/util/deg_util_function.h @@ -24,12 +24,11 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_function.h +/** \file blender/depsgraph/util/deg_util_function.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_FUNCTION_H__ -#define __DEPSGRAPH_UTIL_FUNCTION_H__ +#pragma once #if (__cplusplus > 199711L) @@ -57,6 +56,7 @@ using boost::function; #define DISABLE_NEW_DEPSGRAPH +#include "BLI_utildefines.h" #include <cstdlib> template<typename T> @@ -108,5 +108,3 @@ void *function_bind(T func, #define _4 Wrap() #endif - -#endif /* __DEPSGRAPH_UTIL_FUNCTION_H__ */ diff --git a/source/blender/depsgraph/util/depsgraph_util_set.h b/source/blender/depsgraph/util/deg_util_hash.h index 008ec6b74ca..e490be1a7a1 100644 --- a/source/blender/depsgraph/util/depsgraph_util_set.h +++ b/source/blender/depsgraph/util/deg_util_hash.h @@ -24,43 +24,18 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/depsgraph/util/depsgraph_util_set.h +/** \file blender/depsgraph/util/deg_util_hash.h * \ingroup depsgraph */ -#ifndef __DEPSGRAPH_UTIL_SET_H__ -#define __DEPSGRAPH_UTIL_SET_H__ +#pragma once -#include <set> +#include "BLI_utildefines.h" -#include "depsgraph_util_hash.h" +#include "BLI_ghash.h" -using std::set; - -#if defined(DEG_NO_UNORDERED_MAP) -# include <set> -typedef std::set unordered_set; -#endif - -#if defined(DEG_TR1_UNORDERED_MAP) -# include <tr1/unordered_set> -using std::tr1::unordered_set; -#endif - -#if defined(DEG_STD_UNORDERED_MAP) -# include <unordered_set> -using std::unordered_set; -#endif - -#if defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) -# include <unordered_set> -using std::tr1::unordered_set; -#endif - -#if !defined(DEG_NO_UNORDERED_MAP) && !defined(DEG_TR1_UNORDERED_MAP) && \ - !defined(DEG_STD_UNORDERED_MAP) && !defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) // NOLINT -# error One of: DEG_NO_UNORDERED_MAP, DEG_TR1_UNORDERED_MAP,\ - DEG_STD_UNORDERED_MAP, DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE must be defined! // NOLINT -#endif - -#endif /* __DEPSGRAPH_UTIL_SET_H__ */ +/* XXX this might require 2 different variants for sizeof(size_t) (32 vs 64 bit) */ +BLI_INLINE size_t hash_combine(size_t hash_a, size_t hash_b) +{ + return hash_a ^ (hash_b + 0x9e3779b9 + (hash_a << 6) + (hash_a >> 2)); +} diff --git a/source/blender/depsgraph/util/depsgraph_util_hash.h b/source/blender/depsgraph/util/depsgraph_util_hash.h deleted file mode 100644 index bc75627a026..00000000000 --- a/source/blender/depsgraph/util/depsgraph_util_hash.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * ***** 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) 2014 Blender Foundation. - * All rights reserved. - * - * Original Author: Brecht van Lommel - * Contributor(s): Lukas Toenne - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/util/depsgraph_util_hash.h - * \ingroup depsgraph - */ - -#ifndef __DEPSGRAPH_UTIL_HASH_H__ -#define __DEPSGRAPH_UTIL_HASH_H__ - -#if defined(DEG_NO_UNORDERED_MAP) -# define DEG_HASH_NAMESPACE_BEGIN -# define DEG_HASH_NAMESPACE_END -#endif - -#if defined(DEG_TR1_UNORDERED_MAP) -# include <tr1/unordered_map> -# define DEG_HASH_NAMESPACE_BEGIN namespace std { namespace tr1 { -# define DEG_HASH_NAMESPACE_END } } -using std::tr1::hash; -#endif - -#if defined(DEG_STD_UNORDERED_MAP) -# include <unordered_map> -# define DEG_HASH_NAMESPACE_BEGIN namespace std { -# define DEG_HASH_NAMESPACE_END } -using std::hash; -#endif - -#if defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) -# include <unordered_map> -# define DEG_HASH_NAMESPACE_BEGIN namespace std { namespace tr1 { -# define DEG_HASH_NAMESPACE_END } } -using std::tr1::hash; -#endif - -#if !defined(DEG_NO_UNORDERED_MAP) && !defined(DEG_TR1_UNORDERED_MAP) && \ - !defined(DEG_STD_UNORDERED_MAP) && !defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) // NOLINT -# error One of: DEG_NO_UNORDERED_MAP, DEG_TR1_UNORDERED_MAP,\ - DEG_STD_UNORDERED_MAP, DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE must be defined! // NOLINT -#endif - -/* XXX this might require 2 different variants for sizeof(size_t) (32 vs 64 bit) */ -inline size_t hash_combine(size_t hash_a, size_t hash_b) -{ - return hash_a ^ (hash_b + 0x9e3779b9 + (hash_a << 6) + (hash_a >> 2)); -} - -#endif /* __DEPSGRAPH_UTIL_HASH_H__ */ diff --git a/source/blender/depsgraph/util/depsgraph_util_map.h b/source/blender/depsgraph/util/depsgraph_util_map.h deleted file mode 100644 index 0eae1d79e34..00000000000 --- a/source/blender/depsgraph/util/depsgraph_util_map.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * ***** 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) 2014 Blender Foundation. - * All rights reserved. - * - * Original Author: Brecht van Lommel - * Contributor(s): Lukas Toenne - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/depsgraph/util/depsgraph_util_map.h - * \ingroup depsgraph - */ - -#ifndef __DEPSGRAPH_UTIL_MAP_H__ -#define __DEPSGRAPH_UTIL_MAP_H__ - -#include <map> - -#include "depsgraph_util_hash.h" - -using std::map; -using std::pair; - -#if defined(DEG_NO_UNORDERED_MAP) -# include <map> -typedef std::map unordered_map; -#endif - -#if defined(DEG_TR1_UNORDERED_MAP) -# include <tr1/unordered_map> -using std::tr1::unordered_map; -#endif - -#if defined(DEG_STD_UNORDERED_MAP) -# include <unordered_map> -using std::unordered_map; -#endif - -#if defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) -# include <unordered_map> -using std::tr1::unordered_map; -#endif - -#if !defined(DEG_NO_UNORDERED_MAP) && !defined(DEG_TR1_UNORDERED_MAP) && \ - !defined(DEG_STD_UNORDERED_MAP) && !defined(DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) // NOLINT -# error One of: DEG_NO_UNORDERED_MAP, DEG_TR1_UNORDERED_MAP,\ - DEG_STD_UNORDERED_MAP, DEG_STD_UNORDERED_MAP_IN_TR1_NAMESPACE must be defined! // NOLINT -#endif - -#endif /* __DEPSGRAPH_UTIL_MAP_H__ */ 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_deps.c b/source/blender/editors/animation/anim_deps.c index 5665ce59783..437dd2b2de2 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -399,7 +399,13 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) ANIM_list_elem_update(ac->scene, ale); } } - + else if (ale->datatype == ALE_NLASTRIP) { + if (ale->update & ANIM_UPDATE_DEPS) { + ale->update &= ~ANIM_UPDATE_DEPS; + ANIM_list_elem_update(ac->scene, ale); + } + } + BLI_assert(ale->update == 0); } } 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/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 6a19cf679be..172f2b9069e 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2024,17 +2024,25 @@ bool autokeyframe_cfra_can_key(Scene *scene, ID *id) /* only filter if auto-key mode requires this */ if (IS_AUTOKEY_ON(scene) == 0) return false; - - if (IS_AUTOKEY_MODE(scene, NORMAL)) { - /* can insert anytime we like... */ - return true; - } - else { /* REPLACE */ - /* for whole block - only key if there's a keyframe on that frame already - * this is a valid assumption when we're blocking + tweaking + + if (IS_AUTOKEY_MODE(scene, EDITKEYS)) { + /* Replace Mode: + * For whole block, only key if there's a keyframe on that frame already + * This is a valid assumption when we're blocking + tweaking */ return id_frame_has_keyframe(id, cfra, ANIMFILTER_KEYS_LOCAL); } + else { + /* Normal Mode (or treat as being normal mode): + * + * Just in case the flags are't set properly (i.e. only on/off is set, without a mode) + * let's set the "normal" flag too, so that it will all be sane everywhere... + */ + scene->toolsettings->autokey_mode = AUTOKEY_MODE_NORMAL; + + /* Can insert anytime we like... */ + return true; + } } /* ******************************************* */ diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 218f215a350..559d93c7eb1 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,13 @@ 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); + } + if (pchan_src->bbone_prev) { + pchan_dst->bbone_prev = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_prev); + } + if (pchan_src->bbone_next) { + pchan_dst->bbone_next = pchan_duplicate_map(ob->pose, name_map, pchan_src->bbone_next); } } } @@ -682,7 +696,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 +905,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_edit.c b/source/blender/editors/armature/armature_edit.c index b1c23fb4cac..354b748e129 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -317,9 +317,10 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op) float cursor_local[3]; const float *cursor = ED_view3d_cursor3d_get(scene, v3d); - + invert_m4_m4(ob->imat, ob->obmat); copy_v3_v3(cursor_local, cursor); - mul_m3_v3(imat, cursor_local); + mul_m4_v3(ob->imat, cursor_local); + /* cursor */ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { 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..6306926e0b2 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; } @@ -368,6 +367,8 @@ void transform_armature_mirror_update(Object *obedit) eboflip->tail[2] = ebo->tail[2]; eboflip->rad_tail = ebo->rad_tail; eboflip->roll = -ebo->roll; + eboflip->curveOutX = -ebo->curveOutX; + eboflip->roll2 = -ebo->roll2; /* Also move connected children, in case children's name aren't mirrored properly */ for (children = arm->edbo->first; children; children = children->next) { @@ -383,6 +384,8 @@ void transform_armature_mirror_update(Object *obedit) eboflip->head[2] = ebo->head[2]; eboflip->rad_head = ebo->rad_head; eboflip->roll = -ebo->roll; + eboflip->curveInX = -ebo->curveInX; + eboflip->roll1 = -ebo->roll1; /* Also move connected parent, in case parent's name isn't mirrored properly */ if (eboflip->parent && eboflip->flag & BONE_CONNECTED) { @@ -396,6 +399,11 @@ void transform_armature_mirror_update(Object *obedit) eboflip->roll = -ebo->roll; eboflip->xwidth = ebo->xwidth; eboflip->zwidth = ebo->zwidth; + + eboflip->curveInX = -ebo->curveInX; + eboflip->curveOutX = -ebo->curveOutX; + eboflip->roll1 = -ebo->roll1; + eboflip->roll2 = -ebo->roll2; } } } @@ -457,7 +465,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 +629,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..0271afd6827 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 @@ -865,14 +866,18 @@ static void gp_brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customda glTranslatef((float)x, (float)y, 0.0f); - /* TODO: toggle between add and remove? */ - glColor4ub(255, 255, 255, 128); - glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); + /* Inner Ring: Light color for action of the brush */ + /* TODO: toggle between add and remove? */ + glColor4ub(255, 255, 255, 200); glutil_draw_lined_arc(0.0, M_PI * 2.0, brush->size, 40); + /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ + glColor3ub(30, 30, 30); + glutil_draw_lined_arc(0.0, M_PI * 2.0, brush->size + 1, 40); + glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); @@ -905,7 +910,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 +1289,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..405b673c42b 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -102,6 +102,10 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) /* Pie Menu - For standard tools */ WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_tool_palette", QKEY, KM_PRESS, 0, DKEY); WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_settings_palette", WKEY, KM_PRESS, 0, DKEY); + + /* Delete Active Frame - For easier video tutorials/review sessions */ + /* NOTE: This works even when not in EditMode */ + WM_keymap_add_item(keymap, "GPENCIL_OT_active_frame_delete", XKEY, KM_PRESS, 0, DKEY); } /* ==================== */ @@ -140,6 +144,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 +194,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); @@ -191,10 +234,12 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) /* delete */ WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_gpencil_delete", XKEY, KM_PRESS, 0, 0); WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_gpencil_delete", DELKEY, KM_PRESS, 0, 0); - + WM_keymap_add_item(keymap, "GPENCIL_OT_dissolve", XKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_dissolve", DELKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "GPENCIL_OT_active_frame_delete", XKEY, KM_PRESS, KM_SHIFT, 0); + /* copy + paste */ WM_keymap_add_item(keymap, "GPENCIL_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0); @@ -229,36 +274,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 +329,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 ebfafc2835d..54b4acec864 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..0a25a8fb3c6 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((unsigned char *)cs->solid); + glRecti(x, y, a, y + h); + + /* selected */ + glColor3ubv((unsigned char *)cs->select); + glRecti(a, y, b, y + h); + + /* active */ + glColor3ubv((unsigned char *)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..2bbd5b8dde8 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"); @@ -2400,7 +2418,7 @@ static void ui_block_colorpicker(uiBlock *block, float rgba[4], PointerRNA *ptr, BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", UNPACK3_EX((unsigned int), rgb_gamma_uchar, )); yco = -3.0f * UI_UNIT_Y; - bt = uiDefBut(block, UI_BTYPE_TEXT, 0, IFACE_("Hex: "), 0, yco, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, TIP_("Hex triplet for color (#RRGGBB)")); + bt = uiDefBut(block, UI_BTYPE_TEXT, 0, IFACE_("Hex: "), 0, yco, butwidth, UI_UNIT_Y, hexcol, 0, 7, 0, 0, TIP_("Hex triplet for color (#RRGGBB)")); UI_but_func_set(bt, ui_colorpicker_hex_rna_cb, bt, hexcol); bt->custom_data = cpicker; uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("(Gamma Corrected)"), 0, yco - UI_UNIT_Y, butwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); @@ -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/io/io_collada.c b/source/blender/editors/io/io_collada.c index d4c976fb544..acb8e8e7512 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -94,6 +94,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) int triangulate; int use_object_instantiation; + int use_blender_profile; int sort_by_name; int export_transformation_type; int open_sim; @@ -142,6 +143,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) triangulate = RNA_boolean_get(op->ptr, "triangulate"); use_object_instantiation = RNA_boolean_get(op->ptr, "use_object_instantiation"); + use_blender_profile = RNA_boolean_get(op->ptr, "use_blender_profile"); sort_by_name = RNA_boolean_get(op->ptr, "sort_by_name"); export_transformation_type = RNA_enum_get(op->ptr, "export_transformation_type_selection"); open_sim = RNA_boolean_get(op->ptr, "open_sim"); @@ -167,6 +169,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) triangulate, use_object_instantiation, + use_blender_profile, sort_by_name, export_transformation_type, open_sim); @@ -256,6 +259,8 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr) uiItemR(row, imfptr, "triangulate", 0, NULL, ICON_NONE); row = uiLayoutRow(box, false); uiItemR(row, imfptr, "use_object_instantiation", 0, NULL, ICON_NONE); + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "use_blender_profile", 0, NULL, ICON_NONE); row = uiLayoutRow(box, false); split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT); @@ -349,7 +354,10 @@ void WM_OT_collada_export(wmOperatorType *ot) "Export Polygons (Quads & NGons) as Triangles"); RNA_def_boolean(ot->srna, "use_object_instantiation", 1, "Use Object Instances", - "Instantiate multiple Objects from same Data"); + "Instantiate multiple Objects from same Data"); + + RNA_def_boolean(ot->srna, "use_blender_profile", 1, "Use Blender Profile", + "Export additional Blender specific information (for material, shaders, bones, etc.)"); RNA_def_boolean(ot->srna, "sort_by_name", 0, "Sort by Object name", "Sort exported data by Object name"); 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/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 0280f662a26..8783367ef7e 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -55,6 +55,7 @@ set(SRC editmesh_rip_edge.c editmesh_select.c editmesh_tools.c + editmesh_undo.c editmesh_utils.c mesh_data.c mesh_ops.c 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_undo.c b/source/blender/editors/mesh/editmesh_undo.c new file mode 100644 index 00000000000..b9d3fd6c8be --- /dev/null +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -0,0 +1,726 @@ +/* + * ***** 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/mesh/editmesh_undo.c + * \ingroup edmesh + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_key_types.h" + +#include "BLI_listbase.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_context.h" +#include "BKE_key.h" +#include "BKE_mesh.h" +#include "BKE_editmesh.h" + +#include "ED_mesh.h" +#include "ED_util.h" + +#define USE_ARRAY_STORE + +#ifdef USE_ARRAY_STORE +// # define DEBUG_PRINT +// # define DEBUG_TIME +# ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +# endif + +# include "BLI_array_store.h" +# include "BLI_math_base.h" + /* check on best size later... */ +# define ARRAY_CHUNK_SIZE 256 + +# define USE_ARRAY_STORE_THREAD +#endif + +#ifdef USE_ARRAY_STORE_THREAD +# include "BLI_task.h" +#endif + + +#ifdef USE_ARRAY_STORE + +/* Single linked list of layers stored per type */ +typedef struct BArrayCustomData { + struct BArrayCustomData *next; + CustomDataType type; + int states_len; /* number of layers for each type */ + BArrayState *states[0]; +} BArrayCustomData; + +#endif + +typedef struct UndoMesh { + Mesh me; + int selectmode; + + /** \note + * this isn't a prefect solution, if you edit keys and change shapes this works well (fixing [#32442]), + * but editing shape keys, going into object mode, removing or changing their order, + * then go back into editmode and undo will give issues - where the old index will be out of sync + * with the new object index. + * + * There are a few ways this could be made to work but for now its a known limitation with mixing + * object and editmode operations - Campbell */ + int shapenr; + +#ifdef USE_ARRAY_STORE + /* NULL arrays are considered empty */ + struct { + /* most data is stored as 'custom' data */ + BArrayCustomData *vdata, *edata, *ldata, *pdata; + BArrayState **keyblocks; + BArrayState *mselect; + } store; +#endif /* USE_ARRAY_STORE */ +} UndoMesh; + + +#ifdef USE_ARRAY_STORE + +/** \name Array Store + * \{ */ + +static struct { + BArrayStore **bs_all; + int bs_all_len; + int users; + + /* We could have the undo API pass in the previous state, for now store a local list */ + ListBase local_links; + +#ifdef USE_ARRAY_STORE_THREAD + TaskPool *task_pool; +#endif + +} um_arraystore = {NULL}; + +static BArrayStore *array_store_at_size_ensure(const int stride) +{ + if (um_arraystore.bs_all_len < stride) { + um_arraystore.bs_all_len = stride; + um_arraystore.bs_all = MEM_recallocN(um_arraystore.bs_all, sizeof(*um_arraystore.bs_all) * stride); + } + BArrayStore **bs_p = &um_arraystore.bs_all[stride - 1]; + + if ((*bs_p) == NULL) { +#if 0 + unsigned int chunk_count = ARRAY_CHUNK_SIZE; +#else + /* calculate best chunk-count to fit a power of two */ + unsigned int chunk_count = ARRAY_CHUNK_SIZE; + { + unsigned int size = chunk_count * stride; + size = power_of_2_max_u(size); + size = MEM_SIZE_OPTIMAL(size); + chunk_count = size / stride; + } +#endif + + (*bs_p) = BLI_array_store_create(stride, chunk_count); + } + return *bs_p; +} + +static BArrayStore *array_store_at_size_get(const int stride) +{ + BLI_assert(stride > 0 && stride <= um_arraystore.bs_all_len); + return um_arraystore.bs_all[stride - 1]; +} + +#ifdef DEBUG_PRINT +static void um_arraystore_memory_usage(size_t *r_size_expanded, size_t *r_size_compacted) +{ + size_t size_compacted = 0; + size_t size_expanded = 0; + for (int i = 0; i < um_arraystore.bs_all_len; i++) { + BArrayStore *bs = um_arraystore.bs_all[i]; + if (bs) { + size_compacted += BLI_array_store_calc_size_compacted_get(bs); + size_expanded += BLI_array_store_calc_size_expanded_get(bs); + } + } + + *r_size_expanded = size_expanded; + *r_size_compacted = size_compacted; +} +#endif + +static void um_arraystore_cd_compact( + struct CustomData *cdata, const size_t data_len, + bool create, + const BArrayCustomData *bcd_reference, + BArrayCustomData **r_bcd_first) +{ + if (data_len == 0) { + if (create) { + *r_bcd_first = NULL; + } + } + + const BArrayCustomData *bcd_reference_current = bcd_reference; + BArrayCustomData *bcd = NULL, *bcd_first = NULL, *bcd_prev = NULL; + for (int layer_start = 0, layer_end; layer_start < cdata->totlayer; layer_start = layer_end) { + const CustomDataType type = cdata->layers[layer_start].type; + + layer_end = layer_start + 1; + while ((layer_end < cdata->totlayer) && + (type == cdata->layers[layer_end].type)) + { + layer_end++; + } + + const int stride = CustomData_sizeof(type); + BArrayStore *bs = create ? array_store_at_size_ensure(stride) : NULL; + const int layer_len = layer_end - layer_start; + + if (create) { + if (bcd_reference_current && (bcd_reference_current->type == type)) { + /* common case, the reference is aligned */ + } + else { + bcd_reference_current = NULL; + + /* do a full lookup when un-alligned */ + if (bcd_reference) { + const BArrayCustomData *bcd_iter = bcd_reference; + while (bcd_iter) { + if (bcd_iter->type == type) { + bcd_reference_current = bcd_iter; + break; + } + bcd_iter = bcd_iter->next; + } + } + } + } + + if (create) { + bcd = MEM_callocN(sizeof(BArrayCustomData) + (layer_len * sizeof(BArrayState *)), __func__); + bcd->next = NULL; + bcd->type = type; + bcd->states_len = layer_end - layer_start; + + if (bcd_prev) { + bcd_prev->next = bcd; + bcd_prev = bcd; + } + else { + bcd_first = bcd; + bcd_prev = bcd; + } + } + + CustomDataLayer *layer = &cdata->layers[layer_start]; + for (int i = 0; i < layer_len; i++, layer++) { + if (create) { + if (layer->data) { + BArrayState *state_reference = + (bcd_reference_current && i < bcd_reference_current->states_len) ? + bcd_reference_current->states[i] : NULL; + bcd->states[i] = BLI_array_store_state_add( + bs, layer->data, (size_t)data_len * stride, state_reference); + } + else { + bcd->states[i] = NULL; + } + } + + if (layer->data) { + MEM_freeN(layer->data); + layer->data = NULL; + } + } + + if (create) { + if (bcd_reference_current) { + bcd_reference_current = bcd_reference_current->next; + } + } + } + + if (create) { + *r_bcd_first = bcd_first; + } +} + +/** + * \note There is no room for data going out of sync here. + * The layers and the states are stored together so this can be kept working. + */ +static void um_arraystore_cd_expand( + const BArrayCustomData *bcd, struct CustomData *cdata, const size_t data_len) +{ + CustomDataLayer *layer = cdata->layers; + while (bcd) { + const int stride = CustomData_sizeof(bcd->type); + for (int i = 0; i < bcd->states_len; i++) { + BLI_assert(bcd->type == layer->type); + if (bcd->states[i]) { + size_t state_len; + layer->data = BLI_array_store_state_data_get_alloc(bcd->states[i], &state_len); + BLI_assert(stride * data_len == state_len); + UNUSED_VARS_NDEBUG(stride, data_len); + } + else { + layer->data = NULL; + } + layer++; + } + bcd = bcd->next; + } +} + +static void um_arraystore_cd_free(BArrayCustomData *bcd) +{ + while (bcd) { + BArrayCustomData *bcd_next = bcd->next; + const int stride = CustomData_sizeof(bcd->type); + BArrayStore *bs = array_store_at_size_get(stride); + for (int i = 0; i < bcd->states_len; i++) { + if (bcd->states[i]) { + BLI_array_store_state_remove(bs, bcd->states[i]); + } + } + MEM_freeN(bcd); + bcd = bcd_next; + } +} + +/** + * \param create: When false, only free the arrays. + * This is done since when reading from an undo state, they must be temporarily expanded. + * then discarded afterwards, having this argument avoids having 2x code paths. + */ +static void um_arraystore_compact_ex( + UndoMesh *um, const UndoMesh *um_ref, + bool create) +{ + Mesh *me = &um->me; + + um_arraystore_cd_compact(&me->vdata, me->totvert, create, um_ref ? um_ref->store.vdata : NULL, &um->store.vdata); + um_arraystore_cd_compact(&me->edata, me->totedge, create, um_ref ? um_ref->store.edata : NULL, &um->store.edata); + um_arraystore_cd_compact(&me->ldata, me->totloop, create, um_ref ? um_ref->store.ldata : NULL, &um->store.ldata); + um_arraystore_cd_compact(&me->pdata, me->totpoly, create, um_ref ? um_ref->store.pdata : NULL, &um->store.pdata); + + if (me->key && me->key->totkey) { + const size_t stride = me->key->elemsize; + BArrayStore *bs = create ? array_store_at_size_ensure(stride) : NULL; + if (create) { + um->store.keyblocks = MEM_mallocN(me->key->totkey * sizeof(*um->store.keyblocks), __func__); + } + KeyBlock *keyblock = me->key->block.first; + for (int i = 0; i < me->key->totkey; i++, keyblock = keyblock->next) { + if (create) { + BArrayState *state_reference = + (um_ref && um_ref->me.key && (i < um_ref->me.key->totkey)) ? + um_ref->store.keyblocks[i] : NULL; + um->store.keyblocks[i] = BLI_array_store_state_add( + bs, keyblock->data, (size_t)keyblock->totelem * stride, + state_reference); + } + + if (keyblock->data) { + MEM_freeN(keyblock->data); + keyblock->data = NULL; + } + } + } + + if (me->mselect && me->totselect) { + BLI_assert(create == (um->store.mselect == NULL)); + if (create) { + BArrayState *state_reference = um_ref ? um_ref->store.mselect : NULL; + const size_t stride = sizeof(*me->mselect); + BArrayStore *bs = array_store_at_size_ensure(stride); + um->store.mselect = BLI_array_store_state_add( + bs, me->mselect, (size_t)me->totselect * stride, state_reference); + } + + /* keep me->totselect for validation */ + MEM_freeN(me->mselect); + me->mselect = NULL; + } + + if (create) { + um_arraystore.users += 1; + } + + BKE_mesh_update_customdata_pointers(me, false); +} + +/** + * Move data from allocated arrays to de-duplicated states and clear arrays. + */ +static void um_arraystore_compact(UndoMesh *um, const UndoMesh *um_ref) +{ + um_arraystore_compact_ex(um, um_ref, true); +} + +static void um_arraystore_compact_with_info(UndoMesh *um, const UndoMesh *um_ref) +{ +#ifdef DEBUG_PRINT + size_t size_expanded_prev, size_compacted_prev; + um_arraystore_memory_usage(&size_expanded_prev, &size_compacted_prev); +#endif + +#ifdef DEBUG_TIME + TIMEIT_START(mesh_undo_compact); +#endif + + um_arraystore_compact(um, um_ref); + +#ifdef DEBUG_TIME + TIMEIT_END(mesh_undo_compact); +#endif + +#ifdef DEBUG_PRINT + { + size_t size_expanded, size_compacted; + um_arraystore_memory_usage(&size_expanded, &size_compacted); + + const double percent_total = size_expanded ? + (((double)size_compacted / (double)size_expanded) * 100.0) : -1.0; + + size_t size_expanded_step = size_expanded - size_expanded_prev; + size_t size_compacted_step = size_compacted - size_compacted_prev; + const double percent_step = size_expanded_step ? + (((double)size_compacted_step / (double)size_expanded_step) * 100.0) : -1.0; + + printf("overall memory use: %.8f%% of expanded size\n", percent_total); + printf("step memory use: %.8f%% of expanded size\n", percent_step); + } +#endif +} + +#ifdef USE_ARRAY_STORE_THREAD + +struct UMArrayData { + UndoMesh *um; + const UndoMesh *um_ref; /* can be NULL */ +}; +static void um_arraystore_compact_cb(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + struct UMArrayData *um_data = taskdata; + um_arraystore_compact_with_info(um_data->um, um_data->um_ref); +} + +#endif /* USE_ARRAY_STORE_THREAD */ + +/** + * Remove data we only expanded for temporary use. + */ +static void um_arraystore_expand_clear(UndoMesh *um) +{ + um_arraystore_compact_ex(um, NULL, false); +} + +static void um_arraystore_expand(UndoMesh *um) +{ + Mesh *me = &um->me; + + um_arraystore_cd_expand(um->store.vdata, &me->vdata, me->totvert); + um_arraystore_cd_expand(um->store.edata, &me->edata, me->totedge); + um_arraystore_cd_expand(um->store.ldata, &me->ldata, me->totloop); + um_arraystore_cd_expand(um->store.pdata, &me->pdata, me->totpoly); + + if (um->store.keyblocks) { + const size_t stride = me->key->elemsize; + KeyBlock *keyblock = me->key->block.first; + for (int i = 0; i < me->key->totkey; i++, keyblock = keyblock->next) { + BArrayState *state = um->store.keyblocks[i]; + size_t state_len; + keyblock->data = BLI_array_store_state_data_get_alloc(state, &state_len); + BLI_assert(keyblock->totelem == (state_len / stride)); + UNUSED_VARS_NDEBUG(stride); + } + } + + if (um->store.mselect) { + const size_t stride = sizeof(*me->mselect); + BArrayState *state = um->store.mselect; + size_t state_len; + me->mselect = BLI_array_store_state_data_get_alloc(state, &state_len); + BLI_assert(me->totselect == (state_len / stride)); + UNUSED_VARS_NDEBUG(stride); + } + + /* not essential, but prevents accidental dangling pointer access */ + BKE_mesh_update_customdata_pointers(me, false); +} + +static void um_arraystore_free(UndoMesh *um) +{ + Mesh *me = &um->me; + + um_arraystore_cd_free(um->store.vdata); + um_arraystore_cd_free(um->store.edata); + um_arraystore_cd_free(um->store.ldata); + um_arraystore_cd_free(um->store.pdata); + + if (um->store.keyblocks) { + const size_t stride = me->key->elemsize; + BArrayStore *bs = array_store_at_size_get(stride); + for (int i = 0; i < me->key->totkey; i++) { + BArrayState *state = um->store.keyblocks[i]; + BLI_array_store_state_remove(bs, state); + } + MEM_freeN(um->store.keyblocks); + um->store.keyblocks = NULL; + } + + if (um->store.mselect) { + const size_t stride = sizeof(*me->mselect); + BArrayStore *bs = array_store_at_size_get(stride); + BArrayState *state = um->store.mselect; + BLI_array_store_state_remove(bs, state); + um->store.mselect = NULL; + } + + um_arraystore.users -= 1; + + BLI_assert(um_arraystore.users >= 0); + + if (um_arraystore.users == 0) { +#ifdef DEBUG_PRINT + printf("mesh undo store: freeing all data!\n"); +#endif + for (int i = 0; i < um_arraystore.bs_all_len; i += 1) { + if (um_arraystore.bs_all[i]) { + BLI_array_store_destroy(um_arraystore.bs_all[i]); + } + } + + MEM_freeN(um_arraystore.bs_all); + um_arraystore.bs_all = NULL; + um_arraystore.bs_all_len = 0; + +#ifdef USE_ARRAY_STORE_THREAD + BLI_task_pool_free(um_arraystore.task_pool); + um_arraystore.task_pool = NULL; +#endif + } + +} + +/** \} */ + +#endif /* USE_ARRAY_STORE */ + + +/* for callbacks */ +/* undo simply makes copies of a bmesh */ +static void *editbtMesh_to_undoMesh(void *emv, void *obdata) +{ + +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + if (um_arraystore.task_pool) { + BLI_task_pool_work_and_wait(um_arraystore.task_pool); + } +#endif + + BMEditMesh *em = emv; + Mesh *obme = obdata; + + UndoMesh *um = MEM_callocN(sizeof(UndoMesh), "undo Mesh"); + + /* make sure shape keys work */ + um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL; + + /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ + + 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; + +#ifdef USE_ARRAY_STORE + { + /* We could be more clever here, + * the previous undo state may be from a separate mesh. */ + const UndoMesh *um_ref = um_arraystore.local_links.last ? + ((LinkData *)um_arraystore.local_links.last)->data : NULL; + + /* add oursrlves */ + BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um)); + +#ifdef USE_ARRAY_STORE_THREAD + if (um_arraystore.task_pool == NULL) { + TaskScheduler *scheduler = BLI_task_scheduler_get(); + um_arraystore.task_pool = BLI_task_pool_create_background(scheduler, NULL); + } + + struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__); + um_data->um = um; + um_data->um_ref = um_ref; + + BLI_task_pool_push( + um_arraystore.task_pool, + um_arraystore_compact_cb, um_data, true, TASK_PRIORITY_LOW); +#else + um_arraystore_compact_with_info(um, um_ref); +#endif + } +#endif + + return um; +} + +static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata) +{ + BMEditMesh *em = em_v, *em_tmp; + Object *ob = em->ob; + UndoMesh *um = um_v; + BMesh *bm; + Key *key = ((Mesh *) obdata)->key; + +#ifdef USE_ARRAY_STORE +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + BLI_task_pool_work_and_wait(um_arraystore.task_pool); +#endif + +#ifdef DEBUG_TIME + TIMEIT_START(mesh_undo_expand); +#endif + + um_arraystore_expand(um); + +#ifdef DEBUG_TIME + TIMEIT_END(mesh_undo_expand); +#endif +#endif /* USE_ARRAY_STORE */ + + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&um->me); + + em->bm->shapenr = um->shapenr; + + EDBM_mesh_free(em); + + bm = BM_mesh_create(&allocsize); + + 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; + + em->selectmode = um->selectmode; + bm->selectmode = um->selectmode; + em->ob = ob; + + /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens + * if the active is a basis for any other. */ + if (key && (key->type == KEY_RELATIVE)) { + /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume + * shapenr from restored bmesh and keyblock indices are in sync. */ + const int kb_act_idx = ob->shapenr - 1; + + /* If it is, let's patch the current mesh key block to its restored value. + * Else, the offsets won't be computed and it won't matter. */ + if (BKE_keyblock_is_basis(key, kb_act_idx)) { + KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx); + + if (kb_act->totelem != um->me.totvert) { + /* The current mesh has some extra/missing verts compared to the undo, adjust. */ + MEM_SAFE_FREE(kb_act->data); + kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__); + kb_act->totelem = um->me.totvert; + } + + BKE_keyblock_update_from_mesh(&um->me, kb_act); + } + } + + ob->shapenr = um->shapenr; + + MEM_freeN(em_tmp); + +#ifdef USE_ARRAY_STORE + um_arraystore_expand_clear(um); +#endif +} + +static void free_undo(void *um_v) +{ + UndoMesh *um = um_v; + Mesh *me = &um->me; + +#ifdef USE_ARRAY_STORE + +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + BLI_task_pool_work_and_wait(um_arraystore.task_pool); +#endif + + /* we need to expand so any allocations in custom-data are freed with the mesh */ + um_arraystore_expand(um); + + { + LinkData *link = BLI_findptr(&um_arraystore.local_links, um, offsetof(LinkData, data)); + BLI_remlink(&um_arraystore.local_links, link); + MEM_freeN(link); + } + um_arraystore_free(um); +#endif + + if (me->key) { + BKE_key_free(me->key); + MEM_freeN(me->key); + } + + BKE_mesh_free(me, false); + MEM_freeN(me); +} + +static void *getEditMesh(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_MESH) { + Mesh *me = obedit->data; + return me->edit_btmesh; + } + return NULL; +} + +/* and this is all the undo system needs to know */ +void undo_push_mesh(bContext *C, const char *name) +{ + /* em->ob gets out of date and crashes on mesh undo, + * this is an easy way to ensure its OK + * though we could investigate the matter further. */ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + em->ob = obedit; + + undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL); +} diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 33f8455e614..99be37845ee 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -45,7 +45,6 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_depsgraph.h" -#include "BKE_key.h" #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" @@ -60,7 +59,6 @@ #include "ED_mesh.h" #include "ED_screen.h" -#include "ED_util.h" #include "ED_view3d.h" #include "mesh_intern.h" /* own include */ @@ -347,7 +345,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 +354,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 +393,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); @@ -491,134 +489,6 @@ void EDBM_flag_enable_all(BMEditMesh *em, const char hflag) BM_mesh_elem_hflag_enable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, hflag, true); } -/**************-------------- Undo ------------*****************/ - -/* for callbacks */ - -static void *getEditMesh(bContext *C) -{ - Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_MESH) { - Mesh *me = obedit->data; - return me->edit_btmesh; - } - return NULL; -} - -typedef struct UndoMesh { - Mesh me; - int selectmode; - - /** \note - * this isn't a prefect solution, if you edit keys and change shapes this works well (fixing [#32442]), - * but editing shape keys, going into object mode, removing or changing their order, - * then go back into editmode and undo will give issues - where the old index will be out of sync - * with the new object index. - * - * There are a few ways this could be made to work but for now its a known limitation with mixing - * object and editmode operations - Campbell */ - int shapenr; -} UndoMesh; - -/* undo simply makes copies of a bmesh */ -static void *editbtMesh_to_undoMesh(void *emv, void *obdata) -{ - BMEditMesh *em = emv; - Mesh *obme = obdata; - - UndoMesh *um = MEM_callocN(sizeof(UndoMesh), "undo Mesh"); - - /* make sure shape keys work */ - um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL; - - /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ - - BM_mesh_bm_to_me(em->bm, &um->me, false); - - um->selectmode = em->selectmode; - um->shapenr = em->bm->shapenr; - - return um; -} - -static void undoMesh_to_editbtMesh(void *umv, void *em_v, void *obdata) -{ - BMEditMesh *em = em_v, *em_tmp; - Object *ob = em->ob; - UndoMesh *um = umv; - BMesh *bm; - Key *key = ((Mesh *) obdata)->key; - - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&um->me); - - em->bm->shapenr = um->shapenr; - - EDBM_mesh_free(em); - - bm = BM_mesh_create(&allocsize); - - BM_mesh_bm_from_me(bm, &um->me, true, false, um->shapenr); - - em_tmp = BKE_editmesh_create(bm, true); - *em = *em_tmp; - - em->selectmode = um->selectmode; - bm->selectmode = um->selectmode; - em->ob = ob; - - /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens - * if the active is a basis for any other. */ - if (key && (key->type == KEY_RELATIVE)) { - /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume - * shapenr from restored bmesh and keyblock indices are in sync. */ - const int kb_act_idx = ob->shapenr - 1; - - /* If it is, let's patch the current mesh key block to its restored value. - * Else, the offsets won't be computed and it won't matter. */ - if (BKE_keyblock_is_basis(key, kb_act_idx)) { - KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx); - - if (kb_act->totelem != um->me.totvert) { - /* The current mesh has some extra/missing verts compared to the undo, adjust. */ - MEM_SAFE_FREE(kb_act->data); - kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__); - kb_act->totelem = um->me.totvert; - } - - BKE_keyblock_update_from_mesh(&um->me, kb_act); - } - } - - ob->shapenr = um->shapenr; - - MEM_freeN(em_tmp); -} - -static void free_undo(void *me_v) -{ - Mesh *me = me_v; - if (me->key) { - BKE_key_free(me->key); - MEM_freeN(me->key); - } - - BKE_mesh_free(me, false); - MEM_freeN(me); -} - -/* and this is all the undo system needs to know */ -void undo_push_mesh(bContext *C, const char *name) -{ - /* em->ob gets out of date and crashes on mesh undo, - * this is an easy way to ensure its OK - * though we could investigate the matter further. */ - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - em->ob = obedit; - - undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL); -} - /** * Return a new UVVertMap from the editmesh */ 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..3d7a45843cc 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" @@ -351,6 +350,9 @@ static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job) return; } + /* Show progress bar. */ + *(job->do_update) = true; + /* Set frame to start point (also inits modifier data) */ frame = surface->start_frame; orig_frame = scene->r.cfra; @@ -358,14 +360,15 @@ static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job) ED_update_for_newframe(job->bmain, scene, 1); /* Init surface */ - if (!dynamicPaint_createUVSurface(scene, surface)) { + if (!dynamicPaint_createUVSurface(scene, surface, job->progress, job->do_update)) { job->success = 0; return; } /* Loop through selected frames */ for (frame = surface->start_frame; frame <= surface->end_frame; frame++) { - float progress = (frame - surface->start_frame) / (float)frames; + /* The first 10% are for createUVSurface... */ + const float progress = 0.1f + 0.9f * (frame - surface->start_frame) / (float)frames; surface->current_frame = frame; /* If user requested stop, quit baking */ @@ -426,7 +429,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 cbb448c7a84..a716cae56dd 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/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index 778ccf902d1..6dce962ee02 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -28,6 +28,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/atomic ../../../../intern/guardedalloc ../../../../intern/glew-mx ) 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_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index c197aabedfd..80cb42c0b3d 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -30,11 +30,14 @@ #include <string.h> #include "BLI_utildefines.h" +#include "BLI_task.h" #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "atomic_ops.h" + #include "sequencer_intern.h" /* XXX, why is this function better then BLI_math version? @@ -450,41 +453,57 @@ static void draw_histogram_bar(ImBuf *ibuf, int x, float val, int col) #define HIS_STEPS 512 -static ImBuf *make_histogram_view_from_ibuf_byte(ImBuf *ibuf) +typedef struct MakeHistogramViewData { + const ImBuf *ibuf; + uint32_t (*bins)[HIS_STEPS]; +} MakeHistogramViewData; + +static void make_histogram_view_from_ibuf_byte_cb_ex( + void *userdata, void *userdata_chunk, const int y, const int UNUSED(threadid)) { - ImBuf *rval = IMB_allocImBuf(515, 128, 32, IB_rect); - int x, y; - unsigned int nr, ng, nb; + MakeHistogramViewData *data = userdata; + const ImBuf *ibuf = data->ibuf; const unsigned char *src = (unsigned char *)ibuf->rect; - unsigned int bins[3][HIS_STEPS]; - - memset(bins, 0, sizeof(bins)); + uint32_t (*cur_bins)[HIS_STEPS] = userdata_chunk; -#pragma omp parallel for shared(bins, src, ibuf) private(x, y) if (ibuf->y >= 256) - for (y = 0; y < ibuf->y; y++) { - unsigned int cur_bins[3][HIS_STEPS]; + for (int x = 0; x < ibuf->x; x++) { + const unsigned char *pixel = src + (y * ibuf->x + x) * 4; - memset(cur_bins, 0, sizeof(cur_bins)); + for (int j = 3; j--;) { + cur_bins[j][pixel[j]]++; + } + } +} - for (x = 0; x < ibuf->x; x++) { - const unsigned char *pixel = src + (y * ibuf->x + x) * 4; +static void make_histogram_view_from_ibuf_finalize(void *userdata, void *userdata_chunk) +{ + MakeHistogramViewData *data = userdata; + uint32_t (*bins)[HIS_STEPS] = data->bins; - cur_bins[0][pixel[0]]++; - cur_bins[1][pixel[1]]++; - cur_bins[2][pixel[2]]++; - } + uint32_t (*cur_bins)[HIS_STEPS] = userdata_chunk; -#pragma omp critical - { - int i; - for (i = 0; i < HIS_STEPS; i++) { - bins[0][i] += cur_bins[0][i]; - bins[1][i] += cur_bins[1][i]; - bins[2][i] += cur_bins[2][i]; - } + for (int j = 3; j--;) { + for (int i = 0; i < HIS_STEPS; i++) { + bins[j][i] += cur_bins[j][i]; } } +} + +static ImBuf *make_histogram_view_from_ibuf_byte(ImBuf *ibuf) +{ + ImBuf *rval = IMB_allocImBuf(515, 128, 32, IB_rect); + int x; + unsigned int nr, ng, nb; + + unsigned int bins[3][HIS_STEPS]; + + memset(bins, 0, sizeof(bins)); + + MakeHistogramViewData data = {.ibuf = ibuf, .bins = bins}; + BLI_task_parallel_range_finalize( + 0, ibuf->y, &data, bins, sizeof(bins), make_histogram_view_from_ibuf_byte_cb_ex, + make_histogram_view_from_ibuf_finalize, ibuf->y >= 256, false); nr = nb = ng = 0; for (x = 0; x < HIS_STEPS; x++) { @@ -528,40 +547,38 @@ BLI_INLINE int get_bin_float(float f) return (int) (((f + 0.25f) / 1.5f) * 512); } -static ImBuf *make_histogram_view_from_ibuf_float(ImBuf *ibuf) +static void make_histogram_view_from_ibuf_float_cb_ex( + void *userdata, void *userdata_chunk, const int y, const int UNUSED(threadid)) { - ImBuf *rval = IMB_allocImBuf(515, 128, 32, IB_rect); - int nr, ng, nb, x, y; + const MakeHistogramViewData *data = userdata; + const ImBuf *ibuf = data->ibuf; const float *src = ibuf->rect_float; - unsigned int bins[3][HIS_STEPS]; + uint32_t (*cur_bins)[HIS_STEPS] = userdata_chunk; - memset(bins, 0, sizeof(bins)); + for (int x = 0; x < ibuf->x; x++) { + const float *pixel = src + (y * ibuf->x + x) * 4; -#pragma omp parallel for shared(bins, src, ibuf) private(x, y) if (ibuf->y >= 256) - for (y = 0; y < ibuf->y; y++) { - unsigned int cur_bins[3][HIS_STEPS]; + for (int j = 3; j--;) { + cur_bins[j][get_bin_float(pixel[j])]++; + } + } +} - memset(cur_bins, 0, sizeof(cur_bins)); +static ImBuf *make_histogram_view_from_ibuf_float(ImBuf *ibuf) +{ + ImBuf *rval = IMB_allocImBuf(515, 128, 32, IB_rect); + int nr, ng, nb; + int x; - for (x = 0; x < ibuf->x; x++) { - const float *pixel = src + (y * ibuf->x + x) * 4; + unsigned int bins[3][HIS_STEPS]; - cur_bins[0][get_bin_float(pixel[0])]++; - cur_bins[1][get_bin_float(pixel[1])]++; - cur_bins[2][get_bin_float(pixel[2])]++; - } + memset(bins, 0, sizeof(bins)); -#pragma omp critical - { - int i; - for (i = 0; i < HIS_STEPS; i++) { - bins[0][i] += cur_bins[0][i]; - bins[1][i] += cur_bins[1][i]; - bins[2][i] += cur_bins[2][i]; - } - } - } + MakeHistogramViewData data = {.ibuf = ibuf, .bins = bins}; + BLI_task_parallel_range_finalize( + 0, ibuf->y, &data, bins, sizeof(bins), make_histogram_view_from_ibuf_float_cb_ex, + make_histogram_view_from_ibuf_finalize, ibuf->y >= 256, false); nr = nb = ng = 0; for (x = 0; x < HIS_STEPS; x++) { 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 4e6a72b3d3c..0bc6447d028 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); @@ -1183,7 +1194,8 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, set_face_cb = NULL; /* test if we can use glsl */ - bool glsl = (v3d->drawtype == OB_MATERIAL) && !picking; + const int drawtype = view3d_effective_drawtype(v3d); + bool glsl = (drawtype == OB_MATERIAL) && !picking; GPU_begin_object_materials(v3d, rv3d, scene, ob, glsl, NULL); diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 7df74650780..5768a37a0d9 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -227,16 +227,25 @@ static void ob_wire_color_blend_theme_id(const unsigned char ob_wire_col[4], con glColor3fv(col); } +int view3d_effective_drawtype(const struct View3D *v3d) +{ + if (v3d->drawtype == OB_RENDER) { + return v3d->prev_drawtype; + } + return v3d->drawtype; +} + /* this condition has been made more complex since editmode can draw textures */ bool check_object_draw_texture(Scene *scene, View3D *v3d, const char drawtype) { + const int v3d_drawtype = view3d_effective_drawtype(v3d); /* texture and material draw modes */ - if (ELEM(v3d->drawtype, OB_TEXTURE, OB_MATERIAL) && drawtype > OB_SOLID) { + if (ELEM(v3d_drawtype, OB_TEXTURE, OB_MATERIAL) && drawtype > OB_SOLID) { return true; } /* textured solid */ - if ((v3d->drawtype == OB_SOLID) && + if ((v3d_drawtype == OB_SOLID) && (v3d->flag2 & V3D_SOLID_TEX) && (BKE_scene_use_new_shading_nodes(scene) == false)) { @@ -298,7 +307,7 @@ bool draw_glsl_material(Scene *scene, Object *ob, View3D *v3d, const char dt) if (v3d->flag2 & V3D_SHOW_SOLID_MATCAP) return true; - + if (v3d->drawtype == OB_TEXTURE) return (scene->gm.matmode == GAME_MAT_GLSL && !BKE_scene_use_new_shading_nodes(scene)); else if (v3d->drawtype == OB_MATERIAL && dt > OB_SOLID) @@ -1669,7 +1678,8 @@ static void draw_viewport_object_reconstruction( v3d->bundle_size / 0.05f / camera_size[1], v3d->bundle_size / 0.05f / camera_size[2]); - if (v3d->drawtype == OB_WIRE) { + const int v3d_drawtype = view3d_effective_drawtype(v3d); + if (v3d_drawtype == OB_WIRE) { if ((dflag & DRAW_CONSTCOLOR) == 0) { if (selected && (track->flag & TRACK_CUSTOMCOLOR) == 0) { glColor3ubv(ob_wire_col); @@ -1681,7 +1691,7 @@ static void draw_viewport_object_reconstruction( drawaxes(rv3d->viewmatob, 0.05f, v3d->bundle_drawtype); } - else if (v3d->drawtype > OB_WIRE) { + else if (v3d_drawtype > OB_WIRE) { if (v3d->bundle_drawtype == OB_EMPTY_SPHERE) { /* selection outline */ if (selected) { @@ -3686,29 +3696,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); @@ -3718,6 +3709,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); @@ -3764,6 +3756,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) { @@ -3772,145 +3765,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(); @@ -7595,7 +7615,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short /* maximum drawtype */ char dt = v3d->drawtype; - if (dt == OB_RENDER) dt = OB_SOLID; + if (dt == OB_RENDER) dt = v3d->prev_drawtype; dt = MIN2(dt, ob->dt); if (v3d->zbuf == 0 && dt > OB_WIRE) dt = OB_WIRE; short dtx = 0; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 9b179645279..187772a305a 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -317,12 +317,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; @@ -457,10 +457,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); @@ -2983,19 +2983,20 @@ static void gpu_pbr_update(Scene *scene, View3D *v3d, ARegion *ar) CustomDataMask ED_view3d_datamask(const Scene *scene, const View3D *v3d) { CustomDataMask mask = 0; + const int drawtype = view3d_effective_drawtype(v3d); - if (ELEM(v3d->drawtype, OB_TEXTURE, OB_MATERIAL) || - ((v3d->drawtype == OB_SOLID) && (v3d->flag2 & V3D_SOLID_TEX))) + if (ELEM(drawtype, OB_TEXTURE, OB_MATERIAL) || + ((drawtype == OB_SOLID) && (v3d->flag2 & V3D_SOLID_TEX))) { mask |= CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL; if (BKE_scene_use_new_shading_nodes(scene)) { - if (v3d->drawtype == OB_MATERIAL) + if (drawtype == OB_MATERIAL) mask |= CD_MASK_ORCO; } else { - if ((scene->gm.matmode == GAME_MAT_GLSL && v3d->drawtype == OB_TEXTURE) || - (v3d->drawtype == OB_MATERIAL)) + if ((scene->gm.matmode == GAME_MAT_GLSL && drawtype == OB_TEXTURE) || + (drawtype == OB_MATERIAL)) { mask |= CD_MASK_ORCO; } @@ -4411,6 +4412,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..155c7503acf 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); @@ -4800,6 +4848,35 @@ void VIEW3D_OT_enable_manipulator(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/* ************************* Toggle rendered shading *********************** */ + +static int toggle_render_exec(bContext *C, wmOperator *UNUSED(op)) +{ + View3D *v3d = CTX_wm_view3d(C); + if (v3d->drawtype == OB_RENDER) { + v3d->drawtype = v3d->prev_drawtype; + } + else { + v3d->prev_drawtype = v3d->drawtype; + v3d->drawtype = OB_RENDER; + } + ED_view3d_shade_update(CTX_data_main(C), CTX_data_scene(C), v3d, CTX_wm_area(C)); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); + return OPERATOR_FINISHED; +} + +void VIEW3D_OT_toggle_render(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Rendered Shading"; + ot->description = "Toggle rendered shading mode of the viewport"; + ot->idname = "VIEW3D_OT_toggle_render"; + + /* api callbacks */ + ot->exec = toggle_render_exec; + ot->poll = ED_operator_view3d_active; +} + /* ************************* below the line! *********************** */ @@ -5171,85 +5248,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..6d831c667a8 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -103,6 +103,7 @@ void VIEW3D_OT_enable_manipulator(struct wmOperatorType *ot); void VIEW3D_OT_render_border(struct wmOperatorType *ot); void VIEW3D_OT_clear_render_border(struct wmOperatorType *ot); void VIEW3D_OT_zoom_border(struct wmOperatorType *ot); +void VIEW3D_OT_toggle_render(struct wmOperatorType *ot); void view3d_boxview_copy(ScrArea *sa, ARegion *ar); @@ -158,6 +159,8 @@ enum { V3D_CACHE_TEXT_LOCALCLIP = (1 << 4) }; +int view3d_effective_drawtype(const struct View3D *v3d); + /* drawarmature.c */ bool draw_armature(Scene *scene, View3D *v3d, ARegion *ar, Base *base, const short dt, const short dflag, const unsigned char ob_wire_col[4], @@ -229,6 +232,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..1c84ce3c985 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" @@ -218,7 +218,9 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_snap_cursor_to_center); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_selected); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_active); - + + WM_operatortype_append(VIEW3D_OT_toggle_render); + transform_operatortypes(); } @@ -417,10 +419,7 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_string_set(kmi->ptr, "value_1", "SOLID"); RNA_string_set(kmi->ptr, "value_2", "TEXTURED"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_PRESS, KM_SHIFT, 0); - RNA_string_set(kmi->ptr, "data_path", "space_data.viewport_shade"); - RNA_string_set(kmi->ptr, "value_1", "SOLID"); - RNA_string_set(kmi->ptr, "value_2", "RENDERED"); + WM_keymap_add_item(keymap, "VIEW3D_OT_toggle_render", ZKEY, KM_PRESS, KM_SHIFT, 0); /* selection*/ kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); 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/GPU_material.h b/source/blender/gpu/GPU_material.h index 5bb7f6c042a..2756216d1cd 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -351,6 +351,7 @@ typedef struct GPUInputUniform { GPUDataType datatype; /* type of uniform data */ struct Object *lamp; /* when type=GPU_DYNAMIC_LAMP_... or GPU_DYNAMIC_SAMPLER_2DSHADOW */ struct Image *image; /* when type=GPU_DYNAMIC_SAMPLER_2DIMAGE */ + struct Material *material;/* when type=GPU_DYNAMIC_MAT_... */ int texnumber; /* when type=GPU_DYNAMIC_SAMPLER, texture number: 0.. */ unsigned char *texpixels; /* for internally generated texture, pixel data in RGBA format */ int texsize; /* size in pixel of the texture in texpixels buffer: diff --git a/source/blender/gpu/intern/gpu_basic_shader.c b/source/blender/gpu/intern/gpu_basic_shader.c index b6fe40a13ee..088dac6f6cc 100644 --- a/source/blender/gpu/intern/gpu_basic_shader.c +++ b/source/blender/gpu/intern/gpu_basic_shader.c @@ -552,7 +552,8 @@ void GPU_basic_shader_light_set(int light_num, GPULightData *light) GPU_MATERIAL_STATE.lights_directional |= light_bit; } else { - if (USE_GLSL) { + /* TODO(sergey): Needs revisit. */ + if (USE_GLSL || true) { /* glsl shader needs these zero to skip them */ const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 072ff5235bf..09d0a383426 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -823,7 +823,7 @@ void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numda for (i = 0; i < numdata; i++) { glEnableVertexAttribArray(data[i].index); glVertexAttribPointer(data[i].index, data[i].size, data[i].type, - GL_FALSE, elementsize, BUFFER_OFFSET(offset)); + GL_TRUE, elementsize, BUFFER_OFFSET(offset)); offset += data[i].size * GPU_typesize(data[i].type); attribData[i].index = data[i].index; diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 98e8ad32af1..d4c296ebd2f 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -1542,7 +1542,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); @@ -1562,7 +1562,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, ...) @@ -1576,7 +1576,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); @@ -1623,7 +1623,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 1923d5a40fd..d015f7c715a 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -898,14 +898,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 3b1b84d2991..79f0bbc6d6d 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -515,7 +515,60 @@ static void gpu_verify_reflection(Image *ima) } } -int GPU_verify_image(Image *ima, ImageUser *iuser, int textarget, int tftile, bool compare, bool mipmap, bool is_data, bool is_envmap) +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. */ + if (!GTS.gpu_mipmap && ibuf->miptot == 1) + 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, bool is_envmap) { unsigned int *bind = NULL; int tpx = 0, tpy = 0; @@ -581,17 +634,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 */ @@ -628,13 +682,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 */ - if (!GTS.gpu_mipmap && ibuf->miptot == 1) - 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 { @@ -657,14 +705,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 */ - if (!GTS.gpu_mipmap && ibuf->miptot == 1) - 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 95eada9772b..567925a130e 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/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c index 574a06a1287..1d6fb613769 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.c @@ -590,7 +590,7 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) glFramebufferTexture2DEXT( GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + ofs->color->fb_attachment, GL_TEXTURE_2D_MULTISAMPLE, ofs->color->bindcode, 0); - status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER_EXT); + status = glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { goto finally; } @@ -598,11 +598,11 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) /* write into new single-sample buffer */ glGenFramebuffersEXT(1, &fbo_blit); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, fbo_blit); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo_blit); glFramebufferTexture2DEXT( - GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, + GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex_blit, 0); - status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER_EXT); + status = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { goto finally; } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index ff45c6bb9de..9a30aafef41 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -2105,22 +2105,22 @@ void GPU_shadeinput_set(GPUMaterial *mat, Material *ma, GPUShadeInput *shi) shi->gpumat = mat; shi->mat = ma; - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->r, GPU_DYNAMIC_MAT_DIFFRGB, NULL), &shi->rgb); - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->specr, GPU_DYNAMIC_MAT_SPECRGB, NULL), &shi->specrgb); - GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->mirr, GPU_DYNAMIC_MAT_MIR, NULL), &shi->mir); + GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->r, GPU_DYNAMIC_MAT_DIFFRGB, ma), &shi->rgb); + GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->specr, GPU_DYNAMIC_MAT_SPECRGB, ma), &shi->specrgb); + GPU_link(mat, "set_rgb", GPU_dynamic_uniform(&ma->mirr, GPU_DYNAMIC_MAT_MIR, ma), &shi->mir); GPU_link(mat, "set_rgba_zero", &shi->refcol); GPU_link(mat, "shade_norm", GPU_builtin(GPU_VIEW_NORMAL), &shi->vn); if (mat->alpha) - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->alpha, GPU_DYNAMIC_MAT_ALPHA, NULL), &shi->alpha); + GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->alpha, GPU_DYNAMIC_MAT_ALPHA, ma), &shi->alpha); else GPU_link(mat, "set_value", GPU_uniform(&one), &shi->alpha); - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->ref, GPU_DYNAMIC_MAT_REF, NULL), &shi->refl); - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->spec, GPU_DYNAMIC_MAT_SPEC, NULL), &shi->spec); - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->emit, GPU_DYNAMIC_MAT_EMIT, NULL), &shi->emit); - GPU_link(mat, "set_value", GPU_dynamic_uniform((float *)&ma->har, GPU_DYNAMIC_MAT_HARD, NULL), &shi->har); - GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->amb, GPU_DYNAMIC_MAT_AMB, NULL), &shi->amb); + GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->ref, GPU_DYNAMIC_MAT_REF, ma), &shi->refl); + GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->spec, GPU_DYNAMIC_MAT_SPEC, ma), &shi->spec); + GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->emit, GPU_DYNAMIC_MAT_EMIT, ma), &shi->emit); + GPU_link(mat, "set_value", GPU_dynamic_uniform((float *)&ma->har, GPU_DYNAMIC_MAT_HARD, ma), &shi->har); + GPU_link(mat, "set_value", GPU_dynamic_uniform(&ma->amb, GPU_DYNAMIC_MAT_AMB, ma), &shi->amb); GPU_link(mat, "set_value", GPU_uniform(&ma->spectra), &shi->spectra); GPU_link(mat, "shade_view", GPU_builtin(GPU_VIEW_POSITION), &shi->view); GPU_link(mat, "vcol_attribute", GPU_attribute(CD_MCOL, ""), &shi->vcol); @@ -3752,6 +3752,9 @@ GPUShaderExport *GPU_shader_export(struct Scene *scene, struct Material *ma) if (GPU_DYNAMIC_GROUP_FROM_TYPE(uniform->type) == GPU_DYNAMIC_GROUP_LAMP) uniform->lamp = input->dynamicdata; + + if (GPU_DYNAMIC_GROUP_FROM_TYPE(uniform->type) == GPU_DYNAMIC_GROUP_MAT) + uniform->material = input->dynamicdata; } if (uniform->type != GPU_DYNAMIC_NONE) diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c index 734f962110e..d58d34b868b 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader.c @@ -223,6 +223,10 @@ static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH], if (GPU_bicubic_bump_support()) strcat(defines, "#define BUMP_BICUBIC\n"); + if (GLEW_VERSION_3_0) { + strcat(defines, "#define BIT_OPERATIONS\n"); + } + #ifdef WITH_OPENSUBDIV /* TODO(sergey): Check whether we actually compiling shader for * the OpenSubdiv mesh. diff --git a/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl b/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl index c7b29ee5707..6b6679b60df 100644 --- a/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl @@ -55,83 +55,83 @@ void main() { #if defined(USE_STIPPLE) #if defined(DRAW_LINE) - /* GLSL 1.3 */ - if (!bool((1 << int(mod(t, 16))) & stipple_pattern)) - discard; + /* GLSL 1.3 */ + if (!bool((1 << int(mod(t, 16))) & stipple_pattern)) + discard; #else - /* We have to use mod function and integer casting. - * This can be optimized further with the bitwise operations - * when GLSL 1.3 is supported. */ - if (stipple_id == STIPPLE_HALFTONE || - stipple_id == STIPPLE_S3D_INTERLACE_CHECKERBOARD || - stipple_id == STIPPLE_S3D_INTERLACE_CHECKERBOARD_SWAP) - { - int result = int(mod(gl_FragCoord.x + gl_FragCoord.y, 2)); - bool dis = result == 0; - if (stipple_id == STIPPLE_S3D_INTERLACE_CHECKERBOARD_SWAP) - dis = !dis; - if (dis) - discard; - } - else if (stipple_id == STIPPLE_QUARTTONE) { - int mody = int(mod(gl_FragCoord.y, 4)); - int modx = int(mod(gl_FragCoord.x, 4)); - if (mody == 0) { - if (modx != 2) - discard; - } - else if (mody == 2){ - if (modx != 0) - discard; - } - else - discard; - } - else if (stipple_id == STIPPLE_CHECKER_8PX) { - int result = int(mod(int(gl_FragCoord.x)/8 + int(gl_FragCoord.y)/8, 2)); - if (result != 0) - discard; - } - else if (stipple_id == STIPPLE_DIAG_STRIPES) { - int mody = int(mod(gl_FragCoord.y, 16)); - int modx = int(mod(gl_FragCoord.x, 16)); - if ((16 - modx > mody && mody > 8 - modx) || mody > 24 - modx) - discard; - } - else if (stipple_id == STIPPLE_DIAG_STRIPES_SWAP) { - int mody = int(mod(gl_FragCoord.y, 16)); - int modx = int(mod(gl_FragCoord.x, 16)); - if (!((16 - modx > mody && mody > 8 - modx) || mody > 24 - modx)) - discard; - } - else if (stipple_id == STIPPLE_S3D_INTERLACE_ROW || stipple_id == STIPPLE_S3D_INTERLACE_ROW_SWAP) { - int result = int(mod(gl_FragCoord.y, 2)); - bool dis = result == 0; - if (stipple_id == STIPPLE_S3D_INTERLACE_ROW_SWAP) - dis = !dis; - if (dis) - discard; - } - else if (stipple_id == STIPPLE_S3D_INTERLACE_COLUMN || stipple_id == STIPPLE_S3D_INTERLACE_COLUMN_SWAP) { - int result = int(mod(gl_FragCoord.x, 2)); - bool dis = result != 0; - if (stipple_id == STIPPLE_S3D_INTERLACE_COLUMN_SWAP) - dis = !dis; - if (dis) - discard; - } - else if (stipple_id == STIPPLE_HEXAGON) { - int mody = int(mod(gl_FragCoord.y, 2)); - int modx = int(mod(gl_FragCoord.x, 4)); - if (mody != 0) { - if (modx != 1) - discard; - } - else { - if (modx != 3) - discard; - } - } + /* We have to use mod function and integer casting. + * This can be optimized further with the bitwise operations + * when GLSL 1.3 is supported. */ + if (stipple_id == STIPPLE_HALFTONE || + stipple_id == STIPPLE_S3D_INTERLACE_CHECKERBOARD || + stipple_id == STIPPLE_S3D_INTERLACE_CHECKERBOARD_SWAP) + { + int result = int(mod(gl_FragCoord.x + gl_FragCoord.y, 2)); + bool dis = result == 0; + if (stipple_id == STIPPLE_S3D_INTERLACE_CHECKERBOARD_SWAP) + dis = !dis; + if (dis) + discard; + } + else if (stipple_id == STIPPLE_QUARTTONE) { + int mody = int(mod(gl_FragCoord.y, 4)); + int modx = int(mod(gl_FragCoord.x, 4)); + if (mody == 0) { + if (modx != 2) + discard; + } + else if (mody == 2) { + if (modx != 0) + discard; + } + else + discard; + } + else if (stipple_id == STIPPLE_CHECKER_8PX) { + int result = int(mod(int(gl_FragCoord.x) / 8 + int(gl_FragCoord.y) / 8, 2)); + if (result != 0) + discard; + } + else if (stipple_id == STIPPLE_DIAG_STRIPES) { + int mody = int(mod(gl_FragCoord.y, 16)); + int modx = int(mod(gl_FragCoord.x, 16)); + if ((16 - modx > mody && mody > 8 - modx) || mody > 24 - modx) + discard; + } + else if (stipple_id == STIPPLE_DIAG_STRIPES_SWAP) { + int mody = int(mod(gl_FragCoord.y, 16)); + int modx = int(mod(gl_FragCoord.x, 16)); + if (!((16 - modx > mody && mody > 8 - modx) || mody > 24 - modx)) + discard; + } + else if (stipple_id == STIPPLE_S3D_INTERLACE_ROW || stipple_id == STIPPLE_S3D_INTERLACE_ROW_SWAP) { + int result = int(mod(gl_FragCoord.y, 2)); + bool dis = result == 0; + if (stipple_id == STIPPLE_S3D_INTERLACE_ROW_SWAP) + dis = !dis; + if (dis) + discard; + } + else if (stipple_id == STIPPLE_S3D_INTERLACE_COLUMN || stipple_id == STIPPLE_S3D_INTERLACE_COLUMN_SWAP) { + int result = int(mod(gl_FragCoord.x, 2)); + bool dis = result != 0; + if (stipple_id == STIPPLE_S3D_INTERLACE_COLUMN_SWAP) + dis = !dis; + if (dis) + discard; + } + else if (stipple_id == STIPPLE_HEXAGON) { + int mody = int(mod(gl_FragCoord.y, 2)); + int modx = int(mod(gl_FragCoord.x, 4)); + if (mody != 0) { + if (modx != 1) + discard; + } + else { + if (modx != 3) + discard; + } + } #endif /* !DRAW_LINE */ #endif /* USE_STIPPLE */ @@ -158,7 +158,7 @@ void main() /* diffuse light */ vec3 light_diffuse = gl_LightSource[i].diffuse.rgb; float diffuse_bsdf = max(dot(N, light_direction), 0.0); - L_diffuse += light_diffuse*diffuse_bsdf; + L_diffuse += light_diffuse * diffuse_bsdf; #ifndef NO_SPECULAR /* specular light */ @@ -166,7 +166,7 @@ void main() vec3 H = gl_LightSource[i].halfVector.xyz; float specular_bsdf = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess); - L_specular += light_specular*specular_bsdf; + L_specular += light_specular * specular_bsdf; #endif } #else @@ -174,7 +174,7 @@ void main() #ifndef NO_SPECULAR /* view vector computation, depends on orthographics or perspective */ - vec3 V = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(varying_position): vec3(0.0, 0.0, -1.0); + vec3 V = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(varying_position) : vec3(0.0, 0.0, -1.0); #endif for (int i = 0; i < NUM_SCENE_LIGHTS; i++) { @@ -205,14 +205,14 @@ void main() float distance = length(d); intensity /= gl_LightSource[i].constantAttenuation + - gl_LightSource[i].linearAttenuation * distance + - gl_LightSource[i].quadraticAttenuation * distance * distance; + gl_LightSource[i].linearAttenuation * distance + + gl_LightSource[i].quadraticAttenuation * distance * distance; } /* diffuse light */ vec3 light_diffuse = gl_LightSource[i].diffuse.rgb; float diffuse_bsdf = max(dot(N, light_direction), 0.0); - L_diffuse += light_diffuse*diffuse_bsdf*intensity; + L_diffuse += light_diffuse * diffuse_bsdf * intensity; #ifndef NO_SPECULAR /* specular light */ @@ -220,7 +220,7 @@ void main() vec3 H = normalize(light_direction - V); float specular_bsdf = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess); - L_specular += light_specular*specular_bsdf*intensity; + L_specular += light_specular * specular_bsdf * intensity; #endif } #endif @@ -250,7 +250,7 @@ void main() vec3 L = gl_FrontLightModelProduct.sceneColor.rgb + L_diffuse; #ifndef NO_SPECULAR - L += L_specular*gl_FrontMaterial.specular.rgb; + L += L_specular * gl_FrontMaterial.specular.rgb; #endif /* write out fragment color */ diff --git a/source/blender/gpu/shaders/gpu_shader_basic_geom.glsl b/source/blender/gpu/shaders/gpu_shader_basic_geom.glsl index ffd747ab1eb..a88681a5fd3 100644 --- a/source/blender/gpu/shaders/gpu_shader_basic_geom.glsl +++ b/source/blender/gpu/shaders/gpu_shader_basic_geom.glsl @@ -1,6 +1,6 @@ /* -* Used the implementation of wide lines of Timo Suoranta (http://neure.dy.fi/wideline.html) -*/ + * Used the implementation of wide lines of Timo Suoranta (http://neure.dy.fi/wideline.html) + */ #define PASSTHROUGH 0 @@ -24,72 +24,72 @@ uniform int stipple_factor; void main(void) { - vec2 window_size = viewport.zw; - vec4 start = gl_in[0].gl_Position; - vec4 end = gl_in[1].gl_Position; + vec2 window_size = viewport.zw; + vec4 start = gl_in[0].gl_Position; + vec4 end = gl_in[1].gl_Position; #if PASSTHROUGH - gl_Position = start; EmitVertex(); - gl_Position = end; EmitVertex(); - EndPrimitive(); - return; + gl_Position = start; EmitVertex(); + gl_Position = end; EmitVertex(); + EndPrimitive(); + return; #endif -/* t = 0 t = ~(len(end - start) + 2*line_width) - * A-------------------------------------B - * | | | | - * | side | | - * | | | | - * |--axis--*start--------------*end-----| - * | | | | - * | | | | - * | | | | - * D-------------------------------------C - */ - - /* Clip the line before homogenization. - * Compute line start and end distances to nearplane in clipspace - * Distances are t0 = dot(start, plane) and t1 = dot(end, plane) - */ - float t0 = start.z + start.w; - float t1 = end.z + end.w; - if(t0 < 0.0) { - if(t1 < 0.0) { - return; - } - start = mix(start, end, (0 - t0) / (t1 - t0)); - } - if(t1 < 0.0) { - end = mix(start, end, (0 - t0) / (t1 - t0)); - } - - /* Compute line axis and side vector in screen space */ - vec2 startInNDC = start.xy / start.w; /* clip to NDC: homogenize and drop z */ - vec2 endInNDC = end.xy / end.w; - vec2 lineInNDC = endInNDC - startInNDC; - vec2 lineInScreen = lineInNDC * window_size; /* ndc to screen (direction vector) */ - - vec2 axisInScreen = normalize(lineInScreen); - vec2 sideInScreen = vec2(-axisInScreen.y, axisInScreen.x); /* rotate */ - vec2 axisInNDC = axisInScreen / window_size; /* screen to NDC */ - vec2 sideInNDC = sideInScreen / window_size; - vec4 axis = vec4(axisInNDC, 0.0, 0.0) * line_width; /* NDC to clip (delta vector) */ - vec4 side = vec4(sideInNDC, 0.0, 0.0) * line_width; - - vec4 A = (start + (side - axis) * start.w); - vec4 B = (end + (side + axis) * end.w); - vec4 C = (end - (side - axis) * end.w); - vec4 D = (start - (side + axis) * start.w); - - /* There is no relation between lines yet */ - /* TODO Pass here t0 to make continuous pattern. */ - t0 = 0; - t1 = (length(lineInScreen) + 2*line_width)/ (2*line_width * stipple_factor); - - gl_Position = A; t = t0; varying_vertex_color = varying_vertex_color_line[0]; EmitVertex(); - gl_Position = D; t = t0; varying_vertex_color = varying_vertex_color_line[0]; EmitVertex(); - gl_Position = B; t = t1; varying_vertex_color = varying_vertex_color_line[1]; EmitVertex(); - gl_Position = C; t = t1; varying_vertex_color = varying_vertex_color_line[1]; EmitVertex(); - EndPrimitive(); + /* t = 0 t = ~(len(end - start) + 2*line_width) + * A-------------------------------------B + * | | | | + * | side | | + * | | | | + * |--axis--*start--------------*end-----| + * | | | | + * | | | | + * | | | | + * D-------------------------------------C + */ + + /* Clip the line before homogenization. + * Compute line start and end distances to nearplane in clipspace + * Distances are t0 = dot(start, plane) and t1 = dot(end, plane) + */ + float t0 = start.z + start.w; + float t1 = end.z + end.w; + if (t0 < 0.0) { + if (t1 < 0.0) { + return; + } + start = mix(start, end, (0 - t0) / (t1 - t0)); + } + if (t1 < 0.0) { + end = mix(start, end, (0 - t0) / (t1 - t0)); + } + + /* Compute line axis and side vector in screen space */ + vec2 startInNDC = start.xy / start.w; /* clip to NDC: homogenize and drop z */ + vec2 endInNDC = end.xy / end.w; + vec2 lineInNDC = endInNDC - startInNDC; + vec2 lineInScreen = lineInNDC * window_size; /* ndc to screen (direction vector) */ + + vec2 axisInScreen = normalize(lineInScreen); + vec2 sideInScreen = vec2(-axisInScreen.y, axisInScreen.x); /* rotate */ + vec2 axisInNDC = axisInScreen / window_size; /* screen to NDC */ + vec2 sideInNDC = sideInScreen / window_size; + vec4 axis = vec4(axisInNDC, 0.0, 0.0) * line_width; /* NDC to clip (delta vector) */ + vec4 side = vec4(sideInNDC, 0.0, 0.0) * line_width; + + vec4 A = (start + (side - axis) * start.w); + vec4 B = (end + (side + axis) * end.w); + vec4 C = (end - (side - axis) * end.w); + vec4 D = (start - (side + axis) * start.w); + + /* There is no relation between lines yet */ + /* TODO Pass here t0 to make continuous pattern. */ + t0 = 0; + t1 = (length(lineInScreen) + 2 * line_width) / (2 * line_width * stipple_factor); + + gl_Position = A; t = t0; varying_vertex_color = varying_vertex_color_line[0]; EmitVertex(); + gl_Position = D; t = t0; varying_vertex_color = varying_vertex_color_line[0]; EmitVertex(); + gl_Position = B; t = t1; varying_vertex_color = varying_vertex_color_line[1]; EmitVertex(); + gl_Position = C; t = t1; varying_vertex_color = varying_vertex_color_line[1]; EmitVertex(); + EndPrimitive(); } #else diff --git a/source/blender/gpu/shaders/gpu_shader_basic_vert.glsl b/source/blender/gpu/shaders/gpu_shader_basic_vert.glsl index 04900001998..cef28ea3026 100644 --- a/source/blender/gpu/shaders/gpu_shader_basic_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_basic_vert.glsl @@ -39,7 +39,7 @@ void main() #ifdef CLIP_WORKAROUND int i; - for(i = 0; i < 6; i++) + for (i = 0; i < 6; i++) gl_ClipDistance[i] = dot(co, gl_ClipPlane[i]); #elif !defined(GPU_ATI) // Setting gl_ClipVertex is necessary to get glClipPlane working on NVIDIA diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl index e9dab04de5d..338ef6d51a7 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_frag.glsl @@ -115,9 +115,9 @@ void second_pass() color += texture2D(colorbuffer, uvcoordsvar.xy + invrendertargetdim) * 0.234375; color += texture2D(colorbuffer, uvcoordsvar.xy + 2.5 * invrendertargetdim) * 0.09375; color += texture2D(colorbuffer, uvcoordsvar.xy + 4.5 * invrendertargetdim) * 0.015625; - color += texture2D(colorbuffer, uvcoordsvar.xy -invrendertargetdim) * 0.234375; - color += texture2D(colorbuffer, uvcoordsvar.xy -2.5 * invrendertargetdim) * 0.09375; - color += texture2D(colorbuffer, uvcoordsvar.xy -4.5 * invrendertargetdim) * 0.015625; + color += texture2D(colorbuffer, uvcoordsvar.xy - invrendertargetdim) * 0.234375; + color += texture2D(colorbuffer, uvcoordsvar.xy - 2.5 * invrendertargetdim) * 0.09375; + color += texture2D(colorbuffer, uvcoordsvar.xy - 4.5 * invrendertargetdim) * 0.015625; gl_FragColor = color; } @@ -128,7 +128,7 @@ void third_pass() { vec4 color = texture2D(colorbuffer, uvcoordsvar.xy); vec4 color_blurred = texture2D(blurredcolorbuffer, uvcoordsvar.xy); - float coc = 2.0 * max(color_blurred.a, color.a); - color.a; + float coc = 2.0 * max(color_blurred.a, color.a); -color.a; gl_FragColor = vec4(color.rgb, coc); } @@ -146,7 +146,7 @@ void fourth_pass() vec4 small_sample_blur(in sampler2D colorbuffer, in vec2 uv, in vec4 color) { - float weight = 1.0/ 17.0; + float weight = 1.0 / 17.0; vec4 result = weight * color; weight *= 4.0; diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl index e315d2fb97a..182113367d3 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_hq_frag.glsl @@ -102,7 +102,8 @@ void accumulate_pass(void) { if (dof_params.w == 0.0) r = 1.0; else - r = cos(M_PI / dof_params.w) / (cos(theta - (2.0 * M_PI / dof_params.w) * floor((dof_params.w * theta + M_PI) / (2.0 * M_PI)))); + r = cos(M_PI / dof_params.w) / + (cos(theta - (2.0 * M_PI / dof_params.w) * floor((dof_params.w * theta + M_PI) / (2.0 * M_PI)))); if (dot(particlecoord, particlecoord) > r * r) discard; diff --git a/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl b/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl index a2ef990c4e8..63b57d5775c 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_dof_vert.glsl @@ -38,8 +38,12 @@ void vert_dof_first_pass() void vert_dof_fourth_pass() { vec4 halfpixel = vec4(-0.5, 0.5, -0.5, 0.5); - uvcoordsvar = gl_MultiTexCoord0.xxyy + halfpixel * vec4(invrendertargetdim.x, - invrendertargetdim.x, invrendertargetdim.y, invrendertargetdim.y); + uvcoordsvar = gl_MultiTexCoord0.xxyy + + halfpixel * + vec4(invrendertargetdim.x, + invrendertargetdim.x, + invrendertargetdim.y, + invrendertargetdim.y); gl_Position = gl_Vertex; } diff --git a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl index 494a74dcdf8..054a2f795ee 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl @@ -44,7 +44,8 @@ float calculate_ssao_factor(float depth) vec3 position = get_view_space_from_depth(uvcoordsvar.xy, viewvecs[0].xyz, viewvecs[1].xyz, depth); vec3 normal = calculate_view_space_normal(position); - // find the offset in screen space by multiplying a point in camera space at the depth of the point by the projection matrix. + /* find the offset in screen space by multiplying a point + * in camera space at the depth of the point by the projection matrix. */ vec2 offset; float homcoord = gl_ProjectionMatrix[2][3] * position.z + gl_ProjectionMatrix[3][3]; offset.x = gl_ProjectionMatrix[0][0] * ssao_params.x / homcoord; @@ -76,7 +77,7 @@ float calculate_ssao_factor(float depth) /* use minor bias here to avoid self shadowing */ if (f > 0.05 * len + 0.0001) - factor += f * 1.0/(len * (1.0 + len * len * ssao_params.z)); + factor += f * 1.0 / (len * (1.0 + len * len * ssao_params.z)); } } diff --git a/source/blender/gpu/shaders/gpu_shader_geometry.glsl b/source/blender/gpu/shaders/gpu_shader_geometry.glsl index 16fba0dd055..1663915549c 100644 --- a/source/blender/gpu/shaders/gpu_shader_geometry.glsl +++ b/source/blender/gpu/shaders/gpu_shader_geometry.glsl @@ -49,7 +49,7 @@ void emit_flat(int index, vec3 normal) varposition = outpt.v.position.xyz; /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1)); + vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); vec2 st = quadst[index]; INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); @@ -70,7 +70,7 @@ void emit_smooth(int index) varposition = outpt.v.position.xyz; /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1)); + vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); vec2 st = quadst[index]; INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 63bd065fa81..07810579b22 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -3,7 +3,7 @@ float convert_rgba_to_float(vec4 color) { #ifdef USE_NEW_SHADING - return color.r*0.2126 + color.g*0.7152 + color.b*0.0722; + return color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722; #else return (color.r + color.g + color.b) / 3.0; #endif @@ -21,17 +21,17 @@ float exp_blender(float f) float compatible_pow(float x, float y) { - if(y == 0.0) /* x^0 -> 1, including 0^0 */ + if (y == 0.0) /* x^0 -> 1, including 0^0 */ return 1.0; /* glsl pow doesn't accept negative x */ - if(x < 0.0) { - if(mod(-y, 2.0) == 0.0) + if (x < 0.0) { + if (mod(-y, 2.0) == 0.0) return pow(-x, y); else return -pow(-x, y); } - else if(x == 0.0) + else if (x == 0.0) return 0.0; return pow(x, y); @@ -44,11 +44,11 @@ void rgb_to_hsv(vec4 rgb, out vec4 outcol) cmax = max(rgb[0], max(rgb[1], rgb[2])); cmin = min(rgb[0], min(rgb[1], rgb[2])); - cdelta = cmax-cmin; + cdelta = cmax - cmin; v = cmax; - if (cmax!=0.0) - s = cdelta/cmax; + if (cmax != 0.0) + s = cdelta / cmax; else { s = 0.0; h = 0.0; @@ -58,15 +58,15 @@ void rgb_to_hsv(vec4 rgb, out vec4 outcol) h = 0.0; } else { - c = (vec3(cmax, cmax, cmax) - rgb.xyz)/cdelta; + c = (vec3(cmax, cmax, cmax) - rgb.xyz) / cdelta; - if (rgb.x==cmax) h = c[2] - c[1]; - else if (rgb.y==cmax) h = 2.0 + c[0] - c[2]; + if (rgb.x == cmax) h = c[2] - c[1]; + else if (rgb.y == cmax) h = 2.0 + c[0] - c[2]; else h = 4.0 + c[1] - c[0]; h /= 6.0; - if (h<0.0) + if (h < 0.0) h += 1.0; } @@ -82,21 +82,21 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol) s = hsv[1]; v = hsv[2]; - if(s==0.0) { + if (s == 0.0) { rgb = vec3(v, v, v); } else { - if(h==1.0) + if (h == 1.0) h = 0.0; - + h *= 6.0; i = floor(h); f = h - i; rgb = vec3(f, f, f); - p = v*(1.0-s); - q = v*(1.0-(s*f)); - t = v*(1.0-(s*(1.0-f))); - + p = v * (1.0 - s); + q = v * (1.0 - (s * f)); + t = v * (1.0 - (s * (1.0 - f))); + if (i == 0.0) rgb = vec3(v, t, p); else if (i == 1.0) rgb = vec3(q, v, p); else if (i == 2.0) rgb = vec3(p, v, t); @@ -110,18 +110,18 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol) float srgb_to_linearrgb(float c) { - if(c < 0.04045) - return (c < 0.0) ? 0.0: c * (1.0 / 12.92); + if (c < 0.04045) + return (c < 0.0) ? 0.0 : c * (1.0 / 12.92); else - return pow((c + 0.055)*(1.0/1.055), 2.4); + return pow((c + 0.055) * (1.0 / 1.055), 2.4); } float linearrgb_to_srgb(float c) { - if(c < 0.0031308) - return (c < 0.0) ? 0.0: c * 12.92; + if (c < 0.0031308) + return (c < 0.0) ? 0.0 : c * 12.92; else - return 1.055 * pow(c, 1.0/2.4) - 0.055; + return 1.055 * pow(c, 1.0 / 2.4) - 0.055; } void srgb_to_linearrgb(vec4 col_from, out vec4 col_to) @@ -140,6 +140,27 @@ 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); +} + +void color_to_normal_new_shading(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); +} + +void color_to_blender_normal_new_shading(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_PI_2 1.57079632679489661923 #define M_2PI 6.28318530717958647 @@ -153,38 +174,44 @@ void linearrgb_to_srgb(vec4 col_from, out vec4 col_to) void vcol_attribute(vec4 attvcol, out vec4 vcol) { - vcol = vec4(attvcol.x/255.0, attvcol.y/255.0, attvcol.z/255.0, 1.0); + vcol = vec4(attvcol.x, attvcol.y, attvcol.z, 1.0); } void uv_attribute(vec2 attuv, out vec3 uv) { - uv = vec3(attuv*2.0 - vec2(1.0, 1.0), 0.0); + uv = vec3(attuv * 2.0 - vec2(1.0, 1.0), 0.0); } -void geom(vec3 co, vec3 nor, mat4 viewinvmat, vec3 attorco, vec2 attuv, vec4 attvcol, out vec3 global, out vec3 local, out vec3 view, out vec3 orco, out vec3 uv, out vec3 normal, out vec4 vcol, out float vcol_alpha, out float frontback) +void geom( + vec3 co, vec3 nor, mat4 viewinvmat, vec3 attorco, vec2 attuv, vec4 attvcol, + out vec3 global, out vec3 local, out vec3 view, out vec3 orco, out vec3 uv, + out vec3 normal, out vec4 vcol, out float vcol_alpha, out float frontback) { local = co; - view = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(local): vec3(0.0, 0.0, -1.0); - global = (viewinvmat*vec4(local, 1.0)).xyz; + view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(local) : vec3(0.0, 0.0, -1.0); + global = (viewinvmat * vec4(local, 1.0)).xyz; orco = attorco; uv_attribute(attuv, uv); - normal = -normalize(nor); /* blender render normal is negated */ + normal = -normalize(nor); /* blender render normal is negated */ vcol_attribute(attvcol, vcol); srgb_to_linearrgb(vcol, vcol); vcol_alpha = attvcol.a; - frontback = (gl_FrontFacing)? 1.0: 0.0; + frontback = (gl_FrontFacing) ? 1.0 : 0.0; } -void particle_info(vec4 sprops, vec3 loc, vec3 vel, vec3 avel, out float index, out float age, out float life_time, out vec3 location, out float size, out vec3 velocity, out vec3 angular_velocity) +void particle_info( + vec4 sprops, vec3 loc, vec3 vel, vec3 avel, + out float index, out float age, out float life_time, out vec3 location, + out float size, out vec3 velocity, out vec3 angular_velocity) { - index = sprops.x; - age = sprops.y; - life_time = sprops.z; - size = sprops.w; + index = sprops.x; + age = sprops.y; + life_time = sprops.z; + size = sprops.w; - location = loc; - velocity = vel; - angular_velocity = avel; + location = loc; + velocity = vel; + angular_velocity = avel; } void vect_normalize(vec3 vin, out vec3 vout) @@ -194,20 +221,56 @@ void vect_normalize(vec3 vin, out vec3 vout) void direction_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) { - vout = (mat*vec4(vin, 0.0)).xyz; + vout = (mat * vec4(vin, 0.0)).xyz; } void point_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) { - vout = (mat*vec4(vin, 1.0)).xyz; + vout = (mat * vec4(vin, 1.0)).xyz; +} + +void point_texco_remap_square(vec3 vin, out vec3 vout) +{ + vout = vec3(vin - vec3(0.5, 0.5, 0.5)) * 2.0; +} + +void point_map_to_sphere(vec3 vin, out vec3 vout) +{ + float len = length(vin); + float v, u; + if (len > 0.0) { + if (vin.x == 0.0 && vin.y == 0.0) + u = 0.0; + else + u = (1.0 - atan(vin.x, vin.y) / M_PI) / 2.0; + + v = 1.0 - acos(vin.z / len) / M_PI; + } + else + v = u = 0.0; + + vout = vec3(u, v, 0.0); +} + +void point_map_to_tube(vec3 vin, out vec3 vout) +{ + float u, v; + v = (vin.z + 1.0) * 0.5; + float len = sqrt(vin.x * vin.x + vin.y * vin[1]); + if (len > 0.0) + u = (1.0 - (atan(vin.x / len, vin.y / len) / M_PI)) * 0.5; + else + v = u = 0.0; + + vout = vec3(u, v, 0.0); } void mapping(vec3 vec, mat4 mat, vec3 minvec, vec3 maxvec, float domin, float domax, out vec3 outvec) { outvec = (mat * vec4(vec, 1.0)).xyz; - if(domin == 1.0) + if (domin == 1.0) outvec = max(outvec, minvec); - if(domax == 1.0) + if (domax == 1.0) outvec = min(outvec, maxvec); } @@ -218,7 +281,9 @@ void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist) outview = normalize(co*vec3(1.0,1.0,-1.0)); } -void lamp(vec4 col, float energy, vec3 lv, float dist, vec3 shadow, float visifac, out vec4 outcol, out vec3 outlv, out float outdist, out vec4 outshadow, out float outvisifac) +void lamp( + vec4 col, float energy, vec3 lv, float dist, vec3 shadow, float visifac, + out vec4 outcol, out vec3 outlv, out float outdist, out vec4 outshadow, out float outvisifac) { outcol = col * energy; outlv = lv; @@ -303,10 +368,10 @@ void math_pow(float val1, float val2, out float outval) void math_log(float val1, float val2, out float outval) { - if(val1 > 0.0 && val2 > 0.0) - outval= log2(val1) / log2(val2); + if (val1 > 0.0 && val2 > 0.0) + outval = log2(val1) / log2(val2); else - outval= 0.0; + outval = 0.0; } void math_max(float val1, float val2, out float outval) @@ -321,12 +386,12 @@ void math_min(float val1, float val2, out float outval) void math_round(float val, out float outval) { - outval= floor(val + 0.5); + outval = floor(val + 0.5); } void math_less_than(float val1, float val2, out float outval) { - if(val1 < val2) + if (val1 < val2) outval = 1.0; else outval = 0.0; @@ -334,7 +399,7 @@ void math_less_than(float val1, float val2, out float outval) void math_greater_than(float val1, float val2, out float outval) { - if(val1 > val2) + if (val1 > val2) outval = 1.0; else outval = 0.0; @@ -354,24 +419,24 @@ void math_modulo(float val1, float val2, out float outval) void math_abs(float val1, out float outval) { - outval = abs(val1); + outval = abs(val1); } void squeeze(float val, float width, float center, out float outval) { - outval = 1.0/(1.0 + pow(2.71828183, -((val-center)*width))); + outval = 1.0 / (1.0 + pow(2.71828183, -((val - center) * width))); } void vec_math_add(vec3 v1, vec3 v2, out vec3 outvec, out float outval) { outvec = v1 + v2; - outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2]))/3.0; + outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2])) / 3.0; } void vec_math_sub(vec3 v1, vec3 v2, out vec3 outvec, out float outval) { outvec = v1 - v2; - outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2]))/3.0; + outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2])) / 3.0; } void vec_math_average(vec3 v1, vec3 v2, out vec3 outvec, out float outval) @@ -380,6 +445,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) { @@ -425,12 +494,12 @@ void normal_new_shading(vec3 dir, vec3 nor, out vec3 outnor, out float outdot) void curves_vec(float fac, vec3 vec, sampler2D curvemap, out vec3 outvec) { - outvec.x = texture2D(curvemap, vec2((vec.x + 1.0)*0.5, 0.0)).x; - outvec.y = texture2D(curvemap, vec2((vec.y + 1.0)*0.5, 0.0)).y; - outvec.z = texture2D(curvemap, vec2((vec.z + 1.0)*0.5, 0.0)).z; + outvec.x = texture2D(curvemap, vec2((vec.x + 1.0) * 0.5, 0.0)).x; + outvec.y = texture2D(curvemap, vec2((vec.y + 1.0) * 0.5, 0.0)).y; + outvec.z = texture2D(curvemap, vec2((vec.z + 1.0) * 0.5, 0.0)).z; if (fac != 1.0) - outvec = (outvec*fac) + (vec*(1.0-fac)); + outvec = (outvec * fac) + (vec * (1.0 - fac)); } @@ -441,7 +510,7 @@ void curves_rgb(float fac, vec4 col, sampler2D curvemap, out vec4 outcol) outcol.b = texture2D(curvemap, vec2(texture2D(curvemap, vec2(col.b, 0.0)).a, 0.0)).b; if (fac != 1.0) - outcol = (outcol*fac) + (col*(1.0-fac)); + outcol = (outcol * fac) + (col * (1.0 - fac)); outcol.a = col.a; } @@ -494,11 +563,11 @@ void set_rgba_one(out vec4 outval) void brightness_contrast(vec4 col, float brightness, float contrast, out vec4 outcol) { float a = 1.0 + contrast; - float b = brightness - contrast*0.5; + float b = brightness - contrast * 0.5; - outcol.r = max(a*col.r + b, 0.0); - outcol.g = max(a*col.g + b, 0.0); - outcol.b = max(a*col.b + b, 0.0); + outcol.r = max(a * col.r + b, 0.0); + outcol.g = max(a * col.g + b, 0.0); + outcol.b = max(a * col.b + b, 0.0); outcol.a = col.a; } @@ -528,7 +597,7 @@ void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol) fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; - outcol = vec4(1.0) - (vec4(facm) + fac*(vec4(1.0) - col2))*(vec4(1.0) - col1); + outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1); outcol.a = col1.a; } @@ -539,20 +608,20 @@ void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol) outcol = col1; - if(outcol.r < 0.5) - outcol.r *= facm + 2.0*fac*col2.r; + if (outcol.r < 0.5) + outcol.r *= facm + 2.0 * fac * col2.r; else - outcol.r = 1.0 - (facm + 2.0*fac*(1.0 - col2.r))*(1.0 - outcol.r); + outcol.r = 1.0 - (facm + 2.0 * fac * (1.0 - col2.r)) * (1.0 - outcol.r); - if(outcol.g < 0.5) - outcol.g *= facm + 2.0*fac*col2.g; + if (outcol.g < 0.5) + outcol.g *= facm + 2.0 * fac * col2.g; else - outcol.g = 1.0 - (facm + 2.0*fac*(1.0 - col2.g))*(1.0 - outcol.g); + outcol.g = 1.0 - (facm + 2.0 * fac * (1.0 - col2.g)) * (1.0 - outcol.g); - if(outcol.b < 0.5) - outcol.b *= facm + 2.0*fac*col2.b; + if (outcol.b < 0.5) + outcol.b *= facm + 2.0 * fac * col2.b; else - outcol.b = 1.0 - (facm + 2.0*fac*(1.0 - col2.b))*(1.0 - outcol.b); + outcol.b = 1.0 - (facm + 2.0 * fac * (1.0 - col2.b)) * (1.0 - outcol.b); } void mix_sub(float fac, vec4 col1, vec4 col2, out vec4 outcol) @@ -569,9 +638,9 @@ void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol) outcol = col1; - if(col2.r != 0.0) outcol.r = facm*outcol.r + fac*outcol.r/col2.r; - if(col2.g != 0.0) outcol.g = facm*outcol.g + fac*outcol.g/col2.g; - if(col2.b != 0.0) outcol.b = facm*outcol.b + fac*outcol.b/col2.b; + if (col2.r != 0.0) outcol.r = facm * outcol.r + fac * outcol.r / col2.r; + if (col2.g != 0.0) outcol.g = facm * outcol.g + fac * outcol.g / col2.g; + if (col2.b != 0.0) outcol.b = facm * outcol.b + fac * outcol.b / col2.b; } void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol) @@ -584,14 +653,14 @@ void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_dark(float fac, vec4 col1, vec4 col2, out vec4 outcol) { fac = clamp(fac, 0.0, 1.0); - outcol.rgb = min(col1.rgb, col2.rgb*fac); + outcol.rgb = min(col1.rgb, col2.rgb * fac); outcol.a = col1.a; } void mix_light(float fac, vec4 col1, vec4 col2, out vec4 outcol) { fac = clamp(fac, 0.0, 1.0); - outcol.rgb = max(col1.rgb, col2.rgb*fac); + outcol.rgb = max(col1.rgb, col2.rgb * fac); outcol.a = col1.a; } @@ -600,29 +669,29 @@ void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol) fac = clamp(fac, 0.0, 1.0); outcol = col1; - if(outcol.r != 0.0) { - float tmp = 1.0 - fac*col2.r; - if(tmp <= 0.0) + if (outcol.r != 0.0) { + float tmp = 1.0 - fac * col2.r; + if (tmp <= 0.0) outcol.r = 1.0; - else if((tmp = outcol.r/tmp) > 1.0) + else if ((tmp = outcol.r / tmp) > 1.0) outcol.r = 1.0; else outcol.r = tmp; } - if(outcol.g != 0.0) { - float tmp = 1.0 - fac*col2.g; - if(tmp <= 0.0) + if (outcol.g != 0.0) { + float tmp = 1.0 - fac * col2.g; + if (tmp <= 0.0) outcol.g = 1.0; - else if((tmp = outcol.g/tmp) > 1.0) + else if ((tmp = outcol.g / tmp) > 1.0) outcol.g = 1.0; else outcol.g = tmp; } - if(outcol.b != 0.0) { - float tmp = 1.0 - fac*col2.b; - if(tmp <= 0.0) + if (outcol.b != 0.0) { + float tmp = 1.0 - fac * col2.b; + if (tmp <= 0.0) outcol.b = 1.0; - else if((tmp = outcol.b/tmp) > 1.0) + else if ((tmp = outcol.b / tmp) > 1.0) outcol.b = 1.0; else outcol.b = tmp; @@ -636,32 +705,32 @@ void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol) outcol = col1; - tmp = facm + fac*col2.r; - if(tmp <= 0.0) + tmp = facm + fac * col2.r; + if (tmp <= 0.0) outcol.r = 0.0; - else if((tmp = (1.0 - (1.0 - outcol.r)/tmp)) < 0.0) + else if ((tmp = (1.0 - (1.0 - outcol.r) / tmp)) < 0.0) outcol.r = 0.0; - else if(tmp > 1.0) + else if (tmp > 1.0) outcol.r = 1.0; else outcol.r = tmp; - tmp = facm + fac*col2.g; - if(tmp <= 0.0) + tmp = facm + fac * col2.g; + if (tmp <= 0.0) outcol.g = 0.0; - else if((tmp = (1.0 - (1.0 - outcol.g)/tmp)) < 0.0) + else if ((tmp = (1.0 - (1.0 - outcol.g) / tmp)) < 0.0) outcol.g = 0.0; - else if(tmp > 1.0) + else if (tmp > 1.0) outcol.g = 1.0; else outcol.g = tmp; - tmp = facm + fac*col2.b; - if(tmp <= 0.0) + tmp = facm + fac * col2.b; + if (tmp <= 0.0) outcol.b = 0.0; - else if((tmp = (1.0 - (1.0 - outcol.b)/tmp)) < 0.0) + else if ((tmp = (1.0 - (1.0 - outcol.b) / tmp)) < 0.0) outcol.b = 0.0; - else if(tmp > 1.0) + else if (tmp > 1.0) outcol.b = 1.0; else outcol.b = tmp; @@ -677,10 +746,10 @@ void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol) vec4 hsv, hsv2, tmp; rgb_to_hsv(col2, hsv2); - if(hsv2.y != 0.0) { + if (hsv2.y != 0.0) { rgb_to_hsv(outcol, hsv); hsv.x = hsv2.x; - hsv_to_rgb(hsv, tmp); + hsv_to_rgb(hsv, tmp); outcol = mix(outcol, tmp, fac); outcol.a = col1.a; @@ -697,10 +766,10 @@ void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol) vec4 hsv, hsv2; rgb_to_hsv(outcol, hsv); - if(hsv.y != 0.0) { + if (hsv.y != 0.0) { rgb_to_hsv(col2, hsv2); - hsv.y = facm*hsv.y + fac*hsv2.y; + hsv.y = facm * hsv.y + fac * hsv2.y; hsv_to_rgb(hsv, outcol); } } @@ -714,7 +783,7 @@ void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol) rgb_to_hsv(col1, hsv); rgb_to_hsv(col2, hsv2); - hsv.z = facm*hsv.z + fac*hsv2.z; + hsv.z = facm * hsv.z + fac * hsv2.z; hsv_to_rgb(hsv, outcol); } @@ -728,11 +797,11 @@ void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol) vec4 hsv, hsv2, tmp; rgb_to_hsv(col2, hsv2); - if(hsv2.y != 0.0) { + if (hsv2.y != 0.0) { rgb_to_hsv(outcol, hsv); hsv.x = hsv2.x; hsv.y = hsv2.y; - hsv_to_rgb(hsv, tmp); + hsv_to_rgb(hsv, tmp); outcol = mix(outcol, tmp, fac); outcol.a = col1.a; @@ -744,16 +813,16 @@ void mix_soft(float fac, vec4 col1, vec4 col2, out vec4 outcol) fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; - vec4 one= vec4(1.0); - vec4 scr= one - (one - col2)*(one - col1); - outcol = facm*col1 + fac*((one - col1)*col2*col1 + col1*scr); + vec4 one = vec4(1.0); + vec4 scr = one - (one - col2) * (one - col1); + outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr); } void mix_linear(float fac, vec4 col1, vec4 col2, out vec4 outcol) { fac = clamp(fac, 0.0, 1.0); - outcol = col1 + fac*(2.0*(col2 - vec4(0.5))); + outcol = col1 + fac * (2.0 * (col2 - vec4(0.5))); } void valtorgb(float fac, sampler2D colormap, out vec4 outcol, out float outalpha) @@ -762,12 +831,12 @@ void valtorgb(float fac, sampler2D colormap, out vec4 outcol, out float outalpha outalpha = outcol.a; } -void rgbtobw(vec4 color, out float outval) +void rgbtobw(vec4 color, out float outval) { #ifdef USE_NEW_SHADING - outval = color.r*0.2126 + color.g*0.7152 + color.b*0.0722; + outval = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722; #else - outval = color.r*0.35 + color.g*0.45 + color.b*0.2; /* keep these factors in sync with texture.h:RGBTOBW */ + outval = color.r * 0.35 + color.g * 0.45 + color.b * 0.2; /* keep these factors in sync with texture.h:RGBTOBW */ #endif } @@ -794,11 +863,11 @@ void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 ou rgb_to_hsv(col, hsv); hsv[0] += (hue - 0.5); - if(hsv[0]>1.0) hsv[0]-=1.0; else if(hsv[0]<0.0) hsv[0]+= 1.0; + if (hsv[0] > 1.0) hsv[0] -= 1.0; else if (hsv[0] < 0.0) hsv[0] += 1.0; hsv[1] *= sat; - if(hsv[1]>1.0) hsv[1]= 1.0; else if(hsv[1]<0.0) hsv[1]= 0.0; + if (hsv[1] > 1.0) hsv[1] = 1.0; else if (hsv[1] < 0.0) hsv[1] = 0.0; hsv[2] *= value; - if(hsv[2]>1.0) hsv[2]= 1.0; else if(hsv[2]<0.0) hsv[2]= 0.0; + if (hsv[2] > 1.0) hsv[2] = 1.0; else if (hsv[2] < 0.0) hsv[2] = 0.0; hsv_to_rgb(hsv, outcol); @@ -858,19 +927,19 @@ void texture_flip_blend(vec3 vec, out vec3 outvec) void texture_blend_lin(vec3 vec, out float outval) { - outval = (1.0+vec.x)/2.0; + outval = (1.0 + vec.x) / 2.0; } void texture_blend_quad(vec3 vec, out float outval) { - outval = max((1.0+vec.x)/2.0, 0.0); + outval = max((1.0 + vec.x) / 2.0, 0.0); outval *= outval; } void texture_wood_sin(vec3 vec, out float value, out vec4 color, out vec3 normal) { - float a = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z)*20.0; - float wi = 0.5 + 0.5*sin(a); + float a = sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) * 20.0; + float wi = 0.5 + 0.5 * sin(a); value = wi; color = vec4(wi, wi, wi, 1.0); @@ -879,12 +948,12 @@ void texture_wood_sin(vec3 vec, out float value, out vec4 color, out vec3 normal void texture_image(vec3 vec, sampler2D ima, out float value, out vec4 color, out vec3 normal) { - color = texture2D(ima, (vec.xy + vec2(1.0, 1.0))*0.5); + color = texture2D(ima, (vec.xy + vec2(1.0, 1.0)) * 0.5); value = color.a; - normal.x = 2.0*(color.r - 0.5); - normal.y = 2.0*(0.5 - color.g); - normal.z = 2.0*(color.b - 0.5); + normal.x = 2.0 * (color.r - 0.5); + normal.y = 2.0 * (0.5 - color.g); + normal.z = 2.0 * (color.b - 0.5); } /************* MTEX *****************/ @@ -915,17 +984,17 @@ void texco_tangent(vec4 tangent, out vec3 outtangent) void texco_global(mat4 viewinvmat, vec3 co, out vec3 global) { - global = (viewinvmat*vec4(co, 1.0)).xyz; + global = (viewinvmat * vec4(co, 1.0)).xyz; } void texco_object(mat4 viewinvmat, mat4 obinvmat, vec3 co, out vec3 object) { - object = (obinvmat*(viewinvmat*vec4(co, 1.0))).xyz; + object = (obinvmat * (viewinvmat * vec4(co, 1.0))).xyz; } void texco_refl(vec3 vn, vec3 view, out vec3 ref) { - ref = view - 2.0*dot(vn, view)*vn; + ref = view - 2.0 * dot(vn, view) * vn; } void shade_norm(vec3 normal, out vec3 outnormal) @@ -936,7 +1005,7 @@ void shade_norm(vec3 normal, out vec3 outnormal) void mtex_mirror(vec3 tcol, vec4 refcol, float tin, float colmirfac, out vec4 outrefcol) { - outrefcol = mix(refcol, vec4(1.0, tcol), tin*colmirfac); + outrefcol = mix(refcol, vec4(1.0, tcol), tin * colmirfac); } void mtex_rgb_blend(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -944,9 +1013,9 @@ void mtex_rgb_blend(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 i float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - incol = fact*texcol + facm*outcol; + incol = fact * texcol + facm * outcol; } void mtex_rgb_mul(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -954,9 +1023,9 @@ void mtex_rgb_mul(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - incol = (facm + fact*texcol)*outcol; + incol = (facm + fact * texcol) * outcol; } void mtex_rgb_screen(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -964,9 +1033,9 @@ void mtex_rgb_screen(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - incol = vec3(1.0) - (vec3(facm) + fact*(vec3(1.0) - texcol))*(vec3(1.0) - outcol); + incol = vec3(1.0) - (vec3(facm) + fact * (vec3(1.0) - texcol)) * (vec3(1.0) - outcol); } void mtex_rgb_overlay(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -974,32 +1043,32 @@ void mtex_rgb_overlay(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - if(outcol.r < 0.5) - incol.r = outcol.r*(facm + 2.0*fact*texcol.r); + if (outcol.r < 0.5) + incol.r = outcol.r * (facm + 2.0 * fact * texcol.r); else - incol.r = 1.0 - (facm + 2.0*fact*(1.0 - texcol.r))*(1.0 - outcol.r); + incol.r = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.r)) * (1.0 - outcol.r); - if(outcol.g < 0.5) - incol.g = outcol.g*(facm + 2.0*fact*texcol.g); + if (outcol.g < 0.5) + incol.g = outcol.g * (facm + 2.0 * fact * texcol.g); else - incol.g = 1.0 - (facm + 2.0*fact*(1.0 - texcol.g))*(1.0 - outcol.g); + incol.g = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.g)) * (1.0 - outcol.g); - if(outcol.b < 0.5) - incol.b = outcol.b*(facm + 2.0*fact*texcol.b); + if (outcol.b < 0.5) + incol.b = outcol.b * (facm + 2.0 * fact * texcol.b); else - incol.b = 1.0 - (facm + 2.0*fact*(1.0 - texcol.b))*(1.0 - outcol.b); + incol.b = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.b)) * (1.0 - outcol.b); } void mtex_rgb_sub(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) { - incol = -fact*facg*texcol + outcol; + incol = -fact * facg * texcol + outcol; } void mtex_rgb_add(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) { - incol = fact*facg*texcol + outcol; + incol = fact * facg * texcol + outcol; } void mtex_rgb_div(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -1007,11 +1076,11 @@ void mtex_rgb_div(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - if(texcol.r != 0.0) incol.r = facm*outcol.r + fact*outcol.r/texcol.r; - if(texcol.g != 0.0) incol.g = facm*outcol.g + fact*outcol.g/texcol.g; - if(texcol.b != 0.0) incol.b = facm*outcol.b + fact*outcol.b/texcol.b; + if (texcol.r != 0.0) incol.r = facm * outcol.r + fact * outcol.r / texcol.r; + if (texcol.g != 0.0) incol.g = facm * outcol.g + fact * outcol.g / texcol.g; + if (texcol.b != 0.0) incol.b = facm * outcol.b + fact * outcol.b / texcol.b; } void mtex_rgb_diff(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -1019,9 +1088,9 @@ void mtex_rgb_diff(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 in float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; - incol = facm*outcol + fact*abs(texcol - outcol); + incol = facm * outcol + fact * abs(texcol - outcol); } void mtex_rgb_dark(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) @@ -1029,7 +1098,7 @@ void mtex_rgb_dark(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 in float facm, col; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; incol.r = min(outcol.r, texcol.r) * fact + outcol.r * facm; incol.g = min(outcol.g, texcol.g) * fact + outcol.g * facm; @@ -1042,19 +1111,19 @@ void mtex_rgb_light(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 i fact *= facg; - col = fact*texcol.r; - if(col > outcol.r) incol.r = col; else incol.r = outcol.r; - col = fact*texcol.g; - if(col > outcol.g) incol.g = col; else incol.g = outcol.g; - col = fact*texcol.b; - if(col > outcol.b) incol.b = col; else incol.b = outcol.b; + col = fact * texcol.r; + if (col > outcol.r) incol.r = col; else incol.r = outcol.r; + col = fact * texcol.g; + if (col > outcol.g) incol.g = col; else incol.g = outcol.g; + col = fact * texcol.b; + if (col > outcol.b) incol.b = col; else incol.b = outcol.b; } void mtex_rgb_hue(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) { vec4 col; - mix_hue(fact*facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); + mix_hue(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); incol.rgb = col.rgb; } @@ -1062,7 +1131,7 @@ void mtex_rgb_sat(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc { vec4 col; - mix_sat(fact*facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); + mix_sat(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); incol.rgb = col.rgb; } @@ -1070,7 +1139,7 @@ void mtex_rgb_val(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 inc { vec4 col; - mix_val(fact*facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); + mix_val(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); incol.rgb = col.rgb; } @@ -1078,7 +1147,7 @@ void mtex_rgb_color(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 i { vec4 col; - mix_color(fact*facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); + mix_color(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col); incol.rgb = col.rgb; } @@ -1087,39 +1156,39 @@ void mtex_rgb_soft(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 in float facm; fact *= facg; - facm = 1.0-fact; + facm = 1.0 - fact; vec3 one = vec3(1.0); - vec3 scr = one - (one - texcol)*(one - outcol); - incol = facm*outcol + fact*((one - texcol)*outcol*texcol + outcol*scr); + vec3 scr = one - (one - texcol) * (one - outcol); + incol = facm * outcol + fact * ((one - texcol) * outcol * texcol + outcol * scr); } void mtex_rgb_linear(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol) { fact *= facg; - if(texcol.r > 0.5) - incol.r = outcol.r + fact*(2.0*(texcol.r - 0.5)); + if (texcol.r > 0.5) + incol.r = outcol.r + fact * (2.0 * (texcol.r - 0.5)); else - incol.r = outcol.r + fact*(2.0*(texcol.r) - 1.0); + incol.r = outcol.r + fact * (2.0 * (texcol.r) - 1.0); - if(texcol.g > 0.5) - incol.g = outcol.g + fact*(2.0*(texcol.g - 0.5)); + if (texcol.g > 0.5) + incol.g = outcol.g + fact * (2.0 * (texcol.g - 0.5)); else - incol.g = outcol.g + fact*(2.0*(texcol.g) - 1.0); + incol.g = outcol.g + fact * (2.0 * (texcol.g) - 1.0); - if(texcol.b > 0.5) - incol.b = outcol.b + fact*(2.0*(texcol.b - 0.5)); + if (texcol.b > 0.5) + incol.b = outcol.b + fact * (2.0 * (texcol.b - 0.5)); else - incol.b = outcol.b + fact*(2.0*(texcol.b) - 1.0); + incol.b = outcol.b + fact * (2.0 * (texcol.b) - 1.0); } void mtex_value_vars(inout float fact, float facg, out float facm) { fact *= abs(facg); - facm = 1.0-fact; + facm = 1.0 - fact; - if(facg < 0.0) { + if (facg < 0.0) { float tmp = fact; fact = facm; facm = tmp; @@ -1131,7 +1200,7 @@ void mtex_value_blend(float outcol, float texcol, float fact, float facg, out fl float facm; mtex_value_vars(fact, facg, facm); - incol = fact*texcol + facm*outcol; + incol = fact * texcol + facm * outcol; } void mtex_value_mul(float outcol, float texcol, float fact, float facg, out float incol) @@ -1140,7 +1209,7 @@ void mtex_value_mul(float outcol, float texcol, float fact, float facg, out floa mtex_value_vars(fact, facg, facm); facm = 1.0 - facg; - incol = (facm + fact*texcol)*outcol; + incol = (facm + fact * texcol) * outcol; } void mtex_value_screen(float outcol, float texcol, float fact, float facg, out float incol) @@ -1149,7 +1218,7 @@ void mtex_value_screen(float outcol, float texcol, float fact, float facg, out f mtex_value_vars(fact, facg, facm); facm = 1.0 - facg; - incol = 1.0 - (facm + fact*(1.0 - texcol))*(1.0 - outcol); + incol = 1.0 - (facm + fact * (1.0 - texcol)) * (1.0 - outcol); } void mtex_value_sub(float outcol, float texcol, float fact, float facg, out float incol) @@ -1158,7 +1227,7 @@ void mtex_value_sub(float outcol, float texcol, float fact, float facg, out floa mtex_value_vars(fact, facg, facm); fact = -fact; - incol = fact*texcol + outcol; + incol = fact * texcol + outcol; } void mtex_value_add(float outcol, float texcol, float fact, float facg, out float incol) @@ -1167,7 +1236,7 @@ void mtex_value_add(float outcol, float texcol, float fact, float facg, out floa mtex_value_vars(fact, facg, facm); fact = fact; - incol = fact*texcol + outcol; + incol = fact * texcol + outcol; } void mtex_value_div(float outcol, float texcol, float fact, float facg, out float incol) @@ -1175,8 +1244,8 @@ void mtex_value_div(float outcol, float texcol, float fact, float facg, out floa float facm; mtex_value_vars(fact, facg, facm); - if(texcol != 0.0) - incol = facm*outcol + fact*outcol/texcol; + if (texcol != 0.0) + incol = facm * outcol + fact * outcol / texcol; else incol = 0.0; } @@ -1186,7 +1255,7 @@ void mtex_value_diff(float outcol, float texcol, float fact, float facg, out flo float facm; mtex_value_vars(fact, facg, facm); - incol = facm*outcol + fact*abs(texcol - outcol); + incol = facm * outcol + fact * abs(texcol - outcol); } void mtex_value_dark(float outcol, float texcol, float fact, float facg, out float incol) @@ -1194,7 +1263,7 @@ void mtex_value_dark(float outcol, float texcol, float fact, float facg, out flo float facm; mtex_value_vars(fact, facg, facm); - incol = facm*outcol + fact*min(outcol, texcol); + incol = facm * outcol + fact * min(outcol, texcol); } void mtex_value_light(float outcol, float texcol, float fact, float facg, out float incol) @@ -1202,8 +1271,8 @@ void mtex_value_light(float outcol, float texcol, float fact, float facg, out fl float facm; mtex_value_vars(fact, facg, facm); - float col = fact*texcol; - if(col > outcol) incol = col; else incol = outcol; + float col = fact * texcol; + if (col > outcol) incol = col; else incol = outcol; } void mtex_value_clamp_positive(float fac, out float outfac) @@ -1218,15 +1287,15 @@ void mtex_value_clamp(float fac, out float outfac) void mtex_har_divide(float har, out float outhar) { - outhar = har/128.0; + outhar = har / 128.0; } void mtex_har_multiply_clamp(float har, out float outhar) { har *= 128.0; - if(har < 1.0) outhar = 1.0; - else if(har > 511.0) outhar = 511.0; + if (har < 1.0) outhar = 1.0; + else if (har > 511.0) outhar = 511.0; else outhar = har; } @@ -1242,7 +1311,7 @@ void mtex_alpha_to_col(vec4 col, float alpha, out vec4 outcol) void mtex_alpha_multiply_value(vec4 col, float value, out vec4 outcol) { - outcol = vec4(col.rgb, col.a * value); + outcol = vec4(col.rgb, col.a * value); } void mtex_rgbtoint(vec4 rgb, out float intensity) @@ -1263,15 +1332,15 @@ void mtex_rgb_invert(vec4 inrgb, out vec4 outrgb) void mtex_value_stencil(float stencil, float intensity, out float outstencil, out float outintensity) { float fact = intensity; - outintensity = intensity*stencil; - outstencil = stencil*fact; + outintensity = intensity * stencil; + outstencil = stencil * fact; } void mtex_rgb_stencil(float stencil, vec4 rgb, out float outstencil, out vec4 outrgb) { float fact = rgb.a; - outrgb = vec4(rgb.rgb, rgb.a*stencil); - outstencil = stencil*fact; + outrgb = vec4(rgb.rgb, rgb.a * stencil); + outstencil = stencil * fact; } void mtex_mapping_ofs(vec3 texco, vec3 ofs, out vec3 outtexco) @@ -1281,17 +1350,17 @@ void mtex_mapping_ofs(vec3 texco, vec3 ofs, out vec3 outtexco) void mtex_mapping_size(vec3 texco, vec3 size, out vec3 outtexco) { - outtexco = size*texco; + outtexco = size * texco; } void mtex_2d_mapping(vec3 vec, out vec3 outvec) { - outvec = vec3(vec.xy*0.5 + vec2(0.5), vec.z); + outvec = vec3(vec.xy * 0.5 + vec2(0.5), vec.z); } vec3 mtex_2d_mapping(vec3 vec) { - return vec3(vec.xy*0.5 + vec2(0.5), vec.z); + return vec3(vec.xy * 0.5 + vec2(0.5), vec.z); } void mtex_cube_map(vec3 co, samplerCube ima, out float value, out vec4 color) @@ -1300,7 +1369,9 @@ void mtex_cube_map(vec3 co, samplerCube ima, out float value, out vec4 color) value = 1.0; } -void mtex_cube_map_refl(samplerCube ima, vec3 vp, vec3 vn, mat4 viewmatrixinverse, mat4 viewmatrix, out float value, out vec4 color) +void mtex_cube_map_refl( + samplerCube ima, vec3 vp, vec3 vn, mat4 viewmatrixinverse, mat4 viewmatrix, + out float value, out vec4 color) { vec3 viewdirection = vec3(viewmatrixinverse * vec4(vp, 0.0)); vec3 normaldirection = normalize(vec3(vec4(vn, 0.0) * viewmatrix)); @@ -1323,10 +1394,10 @@ void mtex_normal(vec3 texco, sampler2D ima, out vec3 normal) // the normal used points inward. // Should this ever change this negate must be removed. vec4 color = texture2D(ima, texco.xy); - normal = 2.0*(vec3(-color.r, color.g, color.b) - vec3(-0.5, 0.5, 0.5)); + normal = 2.0 * (vec3(-color.r, color.g, color.b) - vec3(-0.5, 0.5, 0.5)); } -void mtex_bump_normals_init( vec3 vN, out vec3 vNorg, out vec3 vNacc, out float fPrevMagnitude ) +void mtex_bump_normals_init(vec3 vN, out vec3 vNorg, out vec3 vNacc, out float fPrevMagnitude) { vNorg = vN; vNacc = vN; @@ -1343,171 +1414,175 @@ mat3 to_mat3(mat4 m4) return m3; } -void mtex_bump_init_objspace( vec3 surf_pos, vec3 surf_norm, - mat4 mView, mat4 mViewInv, mat4 mObj, mat4 mObjInv, - float fPrevMagnitude_in, vec3 vNacc_in, - out float fPrevMagnitude_out, out vec3 vNacc_out, - out vec3 vR1, out vec3 vR2, out float fDet ) +void mtex_bump_init_objspace( + vec3 surf_pos, vec3 surf_norm, + mat4 mView, mat4 mViewInv, mat4 mObj, mat4 mObjInv, + float fPrevMagnitude_in, vec3 vNacc_in, + out float fPrevMagnitude_out, out vec3 vNacc_out, + out vec3 vR1, out vec3 vR2, out float fDet) { mat3 obj2view = to_mat3(gl_ModelViewMatrix); mat3 view2obj = to_mat3(gl_ModelViewMatrixInverse); - - vec3 vSigmaS = view2obj * dFdx( surf_pos ); - vec3 vSigmaT = view2obj * dFdy( surf_pos ); - vec3 vN = normalize( surf_norm * obj2view ); - vR1 = cross( vSigmaT, vN ); - vR2 = cross( vN, vSigmaS ) ; - fDet = dot ( vSigmaS, vR1 ); - + vec3 vSigmaS = view2obj * dFdx(surf_pos); + vec3 vSigmaT = view2obj * dFdy(surf_pos); + vec3 vN = normalize(surf_norm * obj2view); + + vR1 = cross(vSigmaT, vN); + vR2 = cross(vN, vSigmaS); + fDet = dot(vSigmaS, vR1); + /* pretransform vNacc (in mtex_bump_apply) using the inverse transposed */ vR1 = vR1 * view2obj; vR2 = vR2 * view2obj; vN = vN * view2obj; - + float fMagnitude = abs(fDet) * length(vN); vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in); fPrevMagnitude_out = fMagnitude; } -void mtex_bump_init_texturespace( vec3 surf_pos, vec3 surf_norm, - float fPrevMagnitude_in, vec3 vNacc_in, - out float fPrevMagnitude_out, out vec3 vNacc_out, - out vec3 vR1, out vec3 vR2, out float fDet ) +void mtex_bump_init_texturespace( + vec3 surf_pos, vec3 surf_norm, + float fPrevMagnitude_in, vec3 vNacc_in, + out float fPrevMagnitude_out, out vec3 vNacc_out, + out vec3 vR1, out vec3 vR2, out float fDet) { - vec3 vSigmaS = dFdx( surf_pos ); - vec3 vSigmaT = dFdy( surf_pos ); + vec3 vSigmaS = dFdx(surf_pos); + vec3 vSigmaT = dFdy(surf_pos); vec3 vN = surf_norm; /* normalized interpolated vertex normal */ - - vR1 = normalize( cross( vSigmaT, vN ) ); - vR2 = normalize( cross( vN, vSigmaS ) ); - fDet = sign( dot(vSigmaS, vR1) ); - + + vR1 = normalize(cross(vSigmaT, vN)); + vR2 = normalize(cross(vN, vSigmaS)); + fDet = sign(dot(vSigmaS, vR1)); + float fMagnitude = abs(fDet); vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in); fPrevMagnitude_out = fMagnitude; } -void mtex_bump_init_viewspace( vec3 surf_pos, vec3 surf_norm, - float fPrevMagnitude_in, vec3 vNacc_in, - out float fPrevMagnitude_out, out vec3 vNacc_out, - out vec3 vR1, out vec3 vR2, out float fDet ) +void mtex_bump_init_viewspace( + vec3 surf_pos, vec3 surf_norm, + float fPrevMagnitude_in, vec3 vNacc_in, + out float fPrevMagnitude_out, out vec3 vNacc_out, + out vec3 vR1, out vec3 vR2, out float fDet) { - vec3 vSigmaS = dFdx( surf_pos ); - vec3 vSigmaT = dFdy( surf_pos ); + vec3 vSigmaS = dFdx(surf_pos); + vec3 vSigmaT = dFdy(surf_pos); vec3 vN = surf_norm; /* normalized interpolated vertex normal */ - - vR1 = cross( vSigmaT, vN ); - vR2 = cross( vN, vSigmaS ) ; - fDet = dot ( vSigmaS, vR1 ); - + + vR1 = cross(vSigmaT, vN); + vR2 = cross(vN, vSigmaS); + fDet = dot(vSigmaS, vR1); + float fMagnitude = abs(fDet); vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in); fPrevMagnitude_out = fMagnitude; } -void mtex_bump_tap3( vec3 texco, sampler2D ima, float hScale, - out float dBs, out float dBt ) +void mtex_bump_tap3( + vec3 texco, sampler2D ima, float hScale, + out float dBs, out float dBt) { vec2 STll = texco.xy; - vec2 STlr = texco.xy + dFdx(texco.xy) ; - vec2 STul = texco.xy + dFdy(texco.xy) ; - - float Hll,Hlr,Hul; - rgbtobw( texture2D(ima, STll), Hll ); - rgbtobw( texture2D(ima, STlr), Hlr ); - rgbtobw( texture2D(ima, STul), Hul ); - + vec2 STlr = texco.xy + dFdx(texco.xy); + vec2 STul = texco.xy + dFdy(texco.xy); + + float Hll, Hlr, Hul; + rgbtobw(texture2D(ima, STll), Hll); + rgbtobw(texture2D(ima, STlr), Hlr); + rgbtobw(texture2D(ima, STul), Hul); + dBs = hScale * (Hlr - Hll); dBt = hScale * (Hul - Hll); } #ifdef BUMP_BICUBIC -void mtex_bump_bicubic( vec3 texco, sampler2D ima, float hScale, - out float dBs, out float dBt ) +void mtex_bump_bicubic( + vec3 texco, sampler2D ima, float hScale, + out float dBs, out float dBt ) { float Hl; float Hr; float Hd; float Hu; - + vec2 TexDx = dFdx(texco.xy); vec2 TexDy = dFdy(texco.xy); - - vec2 STl = texco.xy - 0.5 * TexDx ; - vec2 STr = texco.xy + 0.5 * TexDx ; - vec2 STd = texco.xy - 0.5 * TexDy ; - vec2 STu = texco.xy + 0.5 * TexDy ; - + + vec2 STl = texco.xy - 0.5 * TexDx; + vec2 STr = texco.xy + 0.5 * TexDx; + vec2 STd = texco.xy - 0.5 * TexDy; + vec2 STu = texco.xy + 0.5 * TexDy; + rgbtobw(texture2D(ima, STl), Hl); rgbtobw(texture2D(ima, STr), Hr); rgbtobw(texture2D(ima, STd), Hd); rgbtobw(texture2D(ima, STu), Hu); - + vec2 dHdxy = vec2(Hr - Hl, Hu - Hd); - float fBlend = clamp(1.0-textureQueryLOD(ima, texco.xy).x, 0.0, 1.0); - if(fBlend!=0.0) - { + float fBlend = clamp(1.0 - textureQueryLOD(ima, texco.xy).x, 0.0, 1.0); + if (fBlend != 0.0) { // the derivative of the bicubic sampling of level 0 ivec2 vDim; vDim = textureSize(ima, 0); // taking the fract part of the texture coordinate is a hardcoded wrap mode. - // this is acceptable as textures use wrap mode exclusively in 3D view elsewhere in blender. + // this is acceptable as textures use wrap mode exclusively in 3D view elsewhere in blender. // this is done so that we can still get a valid texel with uvs outside the 0,1 range // by texelFetch below, as coordinates are clamped when using this function. - vec2 fTexLoc = vDim*fract(texco.xy) - vec2(0.5, 0.5); + vec2 fTexLoc = vDim * fract(texco.xy) - vec2(0.5, 0.5); ivec2 iTexLoc = ivec2(floor(fTexLoc)); - vec2 t = clamp(fTexLoc - iTexLoc, 0.0, 1.0); // sat just to be pedantic + vec2 t = clamp(fTexLoc - iTexLoc, 0.0, 1.0); // sat just to be pedantic /******************************************************************************************* * This block will replace the one below when one channel textures are properly supported. * ******************************************************************************************* - vec4 vSamplesUL = textureGather(ima, (iTexLoc+ivec2(-1,-1) + vec2(0.5,0.5))/vDim ); - vec4 vSamplesUR = textureGather(ima, (iTexLoc+ivec2(1,-1) + vec2(0.5,0.5))/vDim ); - vec4 vSamplesLL = textureGather(ima, (iTexLoc+ivec2(-1,1) + vec2(0.5,0.5))/vDim ); - vec4 vSamplesLR = textureGather(ima, (iTexLoc+ivec2(1,1) + vec2(0.5,0.5))/vDim ); + vec4 vSamplesUL = textureGather(ima, (iTexLoc+ivec2(-1,-1) + vec2(0.5,0.5))/vDim); + vec4 vSamplesUR = textureGather(ima, (iTexLoc+ivec2(1,-1) + vec2(0.5,0.5))/vDim); + vec4 vSamplesLL = textureGather(ima, (iTexLoc+ivec2(-1,1) + vec2(0.5,0.5))/vDim); + vec4 vSamplesLR = textureGather(ima, (iTexLoc+ivec2(1,1) + vec2(0.5,0.5))/vDim); mat4 H = mat4(vSamplesUL.w, vSamplesUL.x, vSamplesLL.w, vSamplesLL.x, - vSamplesUL.z, vSamplesUL.y, vSamplesLL.z, vSamplesLL.y, - vSamplesUR.w, vSamplesUR.x, vSamplesLR.w, vSamplesLR.x, - vSamplesUR.z, vSamplesUR.y, vSamplesLR.z, vSamplesLR.y); -*/ + vSamplesUL.z, vSamplesUL.y, vSamplesLL.z, vSamplesLL.y, + vSamplesUR.w, vSamplesUR.x, vSamplesLR.w, vSamplesLR.x, + vSamplesUR.z, vSamplesUR.y, vSamplesLR.z, vSamplesLR.y); + */ ivec2 iTexLocMod = iTexLoc + ivec2(-1, -1); mat4 H; - - for(int i = 0; i < 4; i++) { - for(int j = 0; j < 4; j++) { - ivec2 iTexTmp = iTexLocMod + ivec2(i,j); - + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + ivec2 iTexTmp = iTexLocMod + ivec2(i, j); + // wrap texture coordinates manually for texelFetch to work on uvs oitside the 0,1 range. // this is guaranteed to work since we take the fractional part of the uv above. - iTexTmp.x = (iTexTmp.x < 0)? iTexTmp.x + vDim.x : ((iTexTmp.x >= vDim.x)? iTexTmp.x - vDim.x : iTexTmp.x); - iTexTmp.y = (iTexTmp.y < 0)? iTexTmp.y + vDim.y : ((iTexTmp.y >= vDim.y)? iTexTmp.y - vDim.y : iTexTmp.y); + iTexTmp.x = (iTexTmp.x < 0) ? iTexTmp.x + vDim.x : ((iTexTmp.x >= vDim.x) ? iTexTmp.x - vDim.x : iTexTmp.x); + iTexTmp.y = (iTexTmp.y < 0) ? iTexTmp.y + vDim.y : ((iTexTmp.y >= vDim.y) ? iTexTmp.y - vDim.y : iTexTmp.y); rgbtobw(texelFetch(ima, iTexTmp, 0), H[i][j]); } } - + float x = t.x, y = t.y; float x2 = x * x, x3 = x2 * x, y2 = y * y, y3 = y2 * y; - vec4 X = vec4(-0.5*(x3+x)+x2, 1.5*x3-2.5*x2+1, -1.5*x3+2*x2+0.5*x, 0.5*(x3-x2)); - vec4 Y = vec4(-0.5*(y3+y)+y2, 1.5*y3-2.5*y2+1, -1.5*y3+2*y2+0.5*y, 0.5*(y3-y2)); - vec4 dX = vec4(-1.5*x2+2*x-0.5, 4.5*x2-5*x, -4.5*x2+4*x+0.5, 1.5*x2-x); - vec4 dY = vec4(-1.5*y2+2*y-0.5, 4.5*y2-5*y, -4.5*y2+4*y+0.5, 1.5*y2-y); - + vec4 X = vec4(-0.5 * (x3 + x) + x2, 1.5 * x3 - 2.5 * x2 + 1, -1.5 * x3 + 2 * x2 + 0.5 * x, 0.5 * (x3 - x2)); + vec4 Y = vec4(-0.5 * (y3 + y) + y2, 1.5 * y3 - 2.5 * y2 + 1, -1.5 * y3 + 2 * y2 + 0.5 * y, 0.5 * (y3 - y2)); + vec4 dX = vec4(-1.5 * x2 + 2 * x - 0.5, 4.5 * x2 - 5 * x, -4.5 * x2 + 4 * x + 0.5, 1.5 * x2 - x); + vec4 dY = vec4(-1.5 * y2 + 2 * y - 0.5, 4.5 * y2 - 5 * y, -4.5 * y2 + 4 * y + 0.5, 1.5 * y2 - y); + // complete derivative in normalized coordinates (mul by vDim) vec2 dHdST = vDim * vec2(dot(Y, H * dX), dot(dY, H * X)); // transform derivative to screen-space - vec2 dHdxy_bicubic = vec2( dHdST.x * TexDx.x + dHdST.y * TexDx.y, - dHdST.x * TexDy.x + dHdST.y * TexDy.y ); + vec2 dHdxy_bicubic = vec2(dHdST.x * TexDx.x + dHdST.y * TexDx.y, + dHdST.x * TexDy.x + dHdST.y * TexDy.y); // blend between the two - dHdxy = dHdxy*(1-fBlend) + dHdxy_bicubic*fBlend; + dHdxy = dHdxy * (1 - fBlend) + dHdxy_bicubic * fBlend; } dBs = hScale * dHdxy.x; @@ -1516,67 +1591,71 @@ void mtex_bump_bicubic( vec3 texco, sampler2D ima, float hScale, #endif -void mtex_bump_tap5( vec3 texco, sampler2D ima, float hScale, - out float dBs, out float dBt ) +void mtex_bump_tap5( + vec3 texco, sampler2D ima, float hScale, + out float dBs, out float dBt) { vec2 TexDx = dFdx(texco.xy); vec2 TexDy = dFdy(texco.xy); vec2 STc = texco.xy; - vec2 STl = texco.xy - 0.5 * TexDx ; - vec2 STr = texco.xy + 0.5 * TexDx ; - vec2 STd = texco.xy - 0.5 * TexDy ; - vec2 STu = texco.xy + 0.5 * TexDy ; - - float Hc,Hl,Hr,Hd,Hu; - rgbtobw( texture2D(ima, STc), Hc ); - rgbtobw( texture2D(ima, STl), Hl ); - rgbtobw( texture2D(ima, STr), Hr ); - rgbtobw( texture2D(ima, STd), Hd ); - rgbtobw( texture2D(ima, STu), Hu ); - + vec2 STl = texco.xy - 0.5 * TexDx; + vec2 STr = texco.xy + 0.5 * TexDx; + vec2 STd = texco.xy - 0.5 * TexDy; + vec2 STu = texco.xy + 0.5 * TexDy; + + float Hc, Hl, Hr, Hd, Hu; + rgbtobw(texture2D(ima, STc), Hc); + rgbtobw(texture2D(ima, STl), Hl); + rgbtobw(texture2D(ima, STr), Hr); + rgbtobw(texture2D(ima, STd), Hd); + rgbtobw(texture2D(ima, STu), Hu); + dBs = hScale * (Hr - Hl); dBt = hScale * (Hu - Hd); } -void mtex_bump_deriv( vec3 texco, sampler2D ima, float ima_x, float ima_y, float hScale, - out float dBs, out float dBt ) +void mtex_bump_deriv( + vec3 texco, sampler2D ima, float ima_x, float ima_y, float hScale, + out float dBs, out float dBt) { - float s = 1.0; // negate this if flipped texture coordinate + float s = 1.0; // negate this if flipped texture coordinate vec2 TexDx = dFdx(texco.xy); vec2 TexDy = dFdy(texco.xy); - + // this variant using a derivative map is described here // http://mmikkelsen3d.blogspot.com/2011/07/derivative-maps.html vec2 dim = vec2(ima_x, ima_y); - vec2 dBduv = hScale*dim*(2.0*texture2D(ima, texco.xy).xy-1.0); - - dBs = dBduv.x*TexDx.x + s*dBduv.y*TexDx.y; - dBt = dBduv.x*TexDy.x + s*dBduv.y*TexDy.y; + vec2 dBduv = hScale * dim * (2.0 * texture2D(ima, texco.xy).xy - 1.0); + + dBs = dBduv.x * TexDx.x + s * dBduv.y * TexDx.y; + dBt = dBduv.x * TexDy.x + s * dBduv.y * TexDy.y; } -void mtex_bump_apply( float fDet, float dBs, float dBt, vec3 vR1, vec3 vR2, vec3 vNacc_in, - out vec3 vNacc_out, out vec3 perturbed_norm ) +void mtex_bump_apply( + float fDet, float dBs, float dBt, vec3 vR1, vec3 vR2, vec3 vNacc_in, + out vec3 vNacc_out, out vec3 perturbed_norm) { - vec3 vSurfGrad = sign(fDet) * ( dBs * vR1 + dBt * vR2 ); - + vec3 vSurfGrad = sign(fDet) * (dBs * vR1 + dBt * vR2); + vNacc_out = vNacc_in - vSurfGrad; - perturbed_norm = normalize( vNacc_out ); + perturbed_norm = normalize(vNacc_out); } -void mtex_bump_apply_texspace( float fDet, float dBs, float dBt, vec3 vR1, vec3 vR2, - sampler2D ima, vec3 texco, float ima_x, float ima_y, vec3 vNacc_in, - out vec3 vNacc_out, out vec3 perturbed_norm ) +void mtex_bump_apply_texspace( + float fDet, float dBs, float dBt, vec3 vR1, vec3 vR2, + sampler2D ima, vec3 texco, float ima_x, float ima_y, vec3 vNacc_in, + out vec3 vNacc_out, out vec3 perturbed_norm) { vec2 TexDx = dFdx(texco.xy); vec2 TexDy = dFdy(texco.xy); - vec3 vSurfGrad = sign(fDet) * ( - dBs / length( vec2(ima_x*TexDx.x, ima_y*TexDx.y) ) * vR1 + - dBt / length( vec2(ima_x*TexDy.x, ima_y*TexDy.y) ) * vR2 ); - + vec3 vSurfGrad = sign(fDet) * ( + dBs / length(vec2(ima_x * TexDx.x, ima_y * TexDx.y)) * vR1 + + dBt / length(vec2(ima_x * TexDy.x, ima_y * TexDy.y)) * vR2); + vNacc_out = vNacc_in - vSurfGrad; - perturbed_norm = normalize( vNacc_out ); + perturbed_norm = normalize(vNacc_out); } void mtex_negate_texnormal(vec3 normal, out vec3 outnormal) @@ -1588,13 +1667,13 @@ void mtex_nspace_tangent(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 out { vec3 B = tangent.w * cross(normal, tangent.xyz); - outnormal = texnormal.x*tangent.xyz + texnormal.y*B + texnormal.z*normal; + outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal; outnormal = normalize(outnormal); } void mtex_nspace_world(mat4 viewmat, vec3 texnormal, out vec3 outnormal) { - outnormal = normalize((viewmat*vec4(texnormal, 0.0)).xyz); + outnormal = normalize((viewmat * vec4(texnormal, 0.0)).xyz); } void mtex_nspace_object(vec3 texnormal, out vec3 outnormal) @@ -1604,7 +1683,7 @@ void mtex_nspace_object(vec3 texnormal, out vec3 outnormal) void mtex_blend_normal(float norfac, vec3 normal, vec3 newnormal, out vec3 outnormal) { - outnormal = (1.0 - norfac)*normal + norfac*newnormal; + outnormal = (1.0 - norfac) * normal + norfac * newnormal; outnormal = normalize(outnormal); } @@ -1625,34 +1704,28 @@ void lamp_visibility_other(vec3 co, vec3 lampco, out vec3 lv, out float dist, ou visifac = 1.0; } -void lamp_falloff_realistic(float lampdist, float dist, out float visifac) -{ - // visifac = 1.0/(dist*dist + 1e-8); - visifac = 1.0; -} - void lamp_falloff_invlinear(float lampdist, float dist, out float visifac) { - visifac = lampdist/(lampdist + dist); + visifac = lampdist / (lampdist + dist); } void lamp_falloff_invsquare(float lampdist, float dist, out float visifac) { - visifac = lampdist/(lampdist + dist*dist); + visifac = lampdist / (lampdist + dist * dist); } void lamp_falloff_sliders(float lampdist, float ld1, float ld2, float dist, out float visifac) { - float lampdistkw = lampdist*lampdist; + float lampdistkw = lampdist * lampdist; - visifac = lampdist/(lampdist + ld1*dist); - visifac *= lampdistkw/(lampdistkw + ld2*dist*dist); + visifac = lampdist / (lampdist + ld1 * dist); + visifac *= lampdistkw / (lampdistkw + ld2 * dist * dist); } void lamp_falloff_invcoefficients(float coeff_const, float coeff_lin, float coeff_quad, float dist, out float visifac) { vec3 coeff = vec3(coeff_const, coeff_lin, coeff_quad); - vec3 d_coeff = vec3(1.0, dist, dist*dist); + vec3 d_coeff = vec3(1.0, dist, dist * dist); float visifac_r = dot(coeff, d_coeff); if (visifac_r > 0.0) visifac = 1.0 / visifac_r; @@ -1662,25 +1735,25 @@ void lamp_falloff_invcoefficients(float coeff_const, float coeff_lin, float coef void lamp_falloff_curve(float lampdist, sampler2D curvemap, float dist, out float visifac) { - visifac = texture2D(curvemap, vec2(dist/lampdist, 0.0)).x; + visifac = texture2D(curvemap, vec2(dist / lampdist, 0.0)).x; } void lamp_visibility_sphere(float lampdist, float dist, float visifac, out float outvisifac) { - float t= lampdist - dist; + float t = lampdist - dist; - outvisifac= visifac*max(t, 0.0)/lampdist; + outvisifac = visifac * max(t, 0.0) / lampdist; } void lamp_visibility_spot_square(vec3 lampvec, mat4 lampimat, vec2 scale, vec3 lv, out float inpr) { - if(dot(lv, lampvec) > 0.0) { - vec3 lvrot = (lampimat*vec4(lv, 0.0)).xyz; + if (dot(lv, lampvec) > 0.0) { + vec3 lvrot = (lampimat * vec4(lv, 0.0)).xyz; /* without clever non-uniform scale, we could do: */ // float x = max(abs(lvrot.x / lvrot.z), abs(lvrot.y / lvrot.z)); float x = max(abs((lvrot.x / scale.x) / lvrot.z), abs((lvrot.y / scale.y) / lvrot.z)); - inpr = 1.0/sqrt(1.0 + x*x); + inpr = 1.0 / sqrt(1.0 + x * x); } else inpr = 0.0; @@ -1707,23 +1780,23 @@ void lamp_visibility_spot(float spotsi, float spotbl, float inpr, float visifac, { float t = spotsi; - if(inpr <= t) { + if (inpr <= t) { outvisifac = 0.0; } else { t = inpr - t; /* soft area */ - if(spotbl != 0.0) - inpr *= smoothstep(0.0, 1.0, t/spotbl); + if (spotbl != 0.0) + inpr *= smoothstep(0.0, 1.0, t / spotbl); - outvisifac = visifac*inpr; + outvisifac = visifac * inpr; } } void lamp_visibility_clamp(float visifac, out float outvisifac) { - outvisifac = (visifac < 0.001)? 0.0: visifac; + outvisifac = (visifac < 0.001) ? 0.0 : visifac; } void world_paper_view(vec3 vec, out vec3 outvec) @@ -1763,7 +1836,7 @@ void world_blend(vec3 vec, out float blend) void shade_view(vec3 co, out vec3 view) { /* handle perspective/orthographic */ - view = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(co): vec3(0.0, 0.0, -1.0); + view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(co) : vec3(0.0, 0.0, -1.0); } void shade_tangent_v(vec3 lv, vec3 tang, out vec3 vn) @@ -1786,68 +1859,52 @@ void shade_is_no_diffuse(out float is) void shade_is_hemi(float inp, out float is) { - is = 0.5*inp + 0.5; + is = 0.5 * inp + 0.5; } -float rectangleSolidAngle(vec3 worldPos ,vec3 p0 ,vec3 p1 ,vec3 p2 ,vec3 p3 ) +float area_lamp_energy(mat4 area, vec3 co, vec3 vn) { - vec3 v0 = p0 - worldPos ; - vec3 v1 = p1 - worldPos ; - vec3 v2 = p2 - worldPos ; - vec3 v3 = p3 - worldPos ; - - vec3 n0 = normalize ( cross (v0 , v1 )); - vec3 n1 = normalize ( cross (v1 , v2 )); - vec3 n2 = normalize ( cross (v2 , v3 )); - vec3 n3 = normalize ( cross (v3 , v0 )); + vec3 vec[4], c[4]; + float rad[4], fac; - float g0 = acos ( dot (-n0 , n1 )); - float g1 = acos ( dot (-n1 , n2 )); - float g2 = acos ( dot (-n2 , n3 )); - float g3 = acos ( dot (-n3 , n0 )); + vec[0] = normalize(co - area[0].xyz); + vec[1] = normalize(co - area[1].xyz); + vec[2] = normalize(co - area[2].xyz); + vec[3] = normalize(co - area[3].xyz); - return g0 + g1 + g2 + g3 - 2.0 * M_PI; -} + c[0] = normalize(cross(vec[0], vec[1])); + c[1] = normalize(cross(vec[1], vec[2])); + c[2] = normalize(cross(vec[2], vec[3])); + c[3] = normalize(cross(vec[3], vec[0])); -float area_lamp_energy(mat4 lampmat, vec3 co, vec3 n, vec3 lampco, vec2 areasize) -{ - n = -n; + rad[0] = acos(dot(vec[0], vec[1])); + rad[1] = acos(dot(vec[1], vec[2])); + rad[2] = acos(dot(vec[2], vec[3])); + rad[3] = acos(dot(vec[3], vec[0])); - vec3 right = normalize(vec3(lampmat*vec4(1.0,0.0,0.0,0.0))); //lamp right axis - vec3 up = normalize(vec3(lampmat*vec4(0.0,1.0,0.0,0.0))); //lamp up axis - vec3 lampv = normalize(vec3(lampmat*vec4(0.0,0.0,-1.0,0.0)));; //lamp projection axis + fac = rad[0] * dot(vn, c[0]); + fac += rad[1] * dot(vn, c[1]); + fac += rad[2] * dot(vn, c[2]); + fac += rad[3] * dot(vn, c[3]); - float illuminance = 0.0; - - if ( dot ( co - lampco , lampv ) > 0.0 ) - { - float width = areasize.x / 2.0; - float height = areasize.y / 2.0; - - vec3 p0 = lampco + right * -width + up * height; - vec3 p1 = lampco + right * -width + up * -height; - vec3 p2 = lampco + right * width + up * -height; - vec3 p3 = lampco + right * width + up * height; + return max(fac, 0.0); +} - float solidAngle = rectangleSolidAngle ( co , p0 , p1 , p2 , p3 ); +void shade_inp_area( + vec3 position, vec3 lampco, vec3 lampvec, vec3 vn, mat4 area, float areasize, float k, + out float inp) +{ + vec3 co = position; + vec3 vec = co - lampco; - illuminance = solidAngle * 0.2 * ( - max(0.0, dot( normalize ( p0 - co ) , n ) )+ - max(0.0, dot( normalize ( p1 - co ) , n ) )+ - max(0.0, dot( normalize ( p2 - co ) , n ) )+ - max(0.0, dot( normalize ( p3 - co ) , n ) )+ - max(0.0, dot( normalize ( lampco - co ) , n ))); + if (dot(vec, lampvec) < 0.0) { + inp = 0.0; } + else { + float intens = area_lamp_energy(area, co, vn); - float normalization = 1 / (areasize.x * areasize.y); - - return illuminance * normalization * M_PI; -} - -void shade_inp_area(vec3 co, vec3 lampco, vec3 lampvec, vec3 vn, mat4 lampmat, float areasize, float areasizey, out float inp) -{ - vec2 asize = vec2(areasize,areasizey); - inp = area_lamp_energy(lampmat, co, vn, lampco, asize); + inp = pow(intens * areasize, k); + } } void shade_diffuse_oren_nayer(float nl, vec3 n, vec3 l, vec3 v, float rough, out float is) @@ -1857,10 +1914,10 @@ void shade_diffuse_oren_nayer(float nl, vec3 n, vec3 l, vec3 v, float rough, out float nv = max(dot(n, v), 0.0); float realnl = dot(n, l); - if(realnl < 0.0) { + if (realnl < 0.0) { is = 0.0; } - else if(nl < 0.0) { + else if (nl < 0.0) { is = 0.0; } else { @@ -1868,14 +1925,14 @@ void shade_diffuse_oren_nayer(float nl, vec3 n, vec3 l, vec3 v, float rough, out float Lit_A = acos(realnl); float View_A = acos(nv); - vec3 Lit_B = normalize(l - realnl*n); - vec3 View_B = normalize(v - nv*n); + vec3 Lit_B = normalize(l - realnl * n); + vec3 View_B = normalize(v - nv * n); float t = max(dot(Lit_B, View_B), 0.0); float a, b; - if(Lit_A > View_A) { + if (Lit_A > View_A) { a = Lit_A; b = View_A; } @@ -1884,11 +1941,11 @@ void shade_diffuse_oren_nayer(float nl, vec3 n, vec3 l, vec3 v, float rough, out b = Lit_A; } - float A = 1.0 - (0.5*((rough*rough)/((rough*rough) + 0.33))); - float B = 0.45*((rough*rough)/((rough*rough) + 0.09)); + float A = 1.0 - (0.5 * ((rough * rough) / ((rough * rough) + 0.33))); + float B = 0.45 * ((rough * rough) / ((rough * rough) + 0.09)); b *= 0.95; - is = nl*(A + (B * t * sin(a) * tan(b))); + is = nl * (A + (B * t * sin(a) * tan(b))); } } @@ -1897,23 +1954,23 @@ void shade_diffuse_toon(vec3 n, vec3 l, vec3 v, float size, float tsmooth, out f float rslt = dot(n, l); float ang = acos(rslt); - if(ang < size) is = 1.0; - else if(ang > (size + tsmooth) || tsmooth == 0.0) is = 0.0; - else is = 1.0 - ((ang - size)/tsmooth); + if (ang < size) is = 1.0; + else if (ang > (size + tsmooth) || tsmooth == 0.0) is = 0.0; + else is = 1.0 - ((ang - size) / tsmooth); } void shade_diffuse_minnaert(float nl, vec3 n, vec3 v, float darkness, out float is) { - if(nl <= 0.0) { + if (nl <= 0.0) { is = 0.0; } else { float nv = max(dot(n, v), 0.0); - if(darkness <= 1.0) - is = nl*pow(max(nv*nl, 0.1), darkness - 1.0); + if (darkness <= 1.0) + is = nl * pow(max(nv * nl, 0.1), darkness - 1.0); else - is = nl*pow(1.0001 - nv, darkness - 1.0); + is = nl * pow(1.0001 - nv, darkness - 1.0); } } @@ -1922,18 +1979,18 @@ float fresnel_fac(vec3 view, vec3 vn, float grad, float fac) float t1, t2; float ffac; - if(fac==0.0) { + if (fac == 0.0) { ffac = 1.0; } else { - t1= dot(view, vn); - if(t1>0.0) t2= 1.0+t1; - else t2= 1.0-t1; + t1 = dot(view, vn); + if (t1 > 0.0) t2 = 1.0 + t1; + else t2 = 1.0 - t1; - t2= grad + (1.0-grad)*pow(t2, fac); + t2 = grad + (1.0 - grad) * pow(t2, fac); - if(t2<0.0) ffac = 0.0; - else if(t2>1.0) ffac = 1.0; + if (t2 < 0.0) ffac = 0.0; + else if (t2 > 1.0) ffac = 1.0; else ffac = t2; } @@ -1947,18 +2004,18 @@ void shade_diffuse_fresnel(vec3 vn, vec3 lv, vec3 view, float fac_i, float fac, void shade_cubic(float is, out float outis) { - if(is>0.0 && is<1.0) - outis= smoothstep(0.0, 1.0, is); + if (is > 0.0 && is < 1.0) + outis = smoothstep(0.0, 1.0, is); else - outis= is; + outis = is; } void shade_visifac(float i, float visifac, float refl, out float outi) { - /*if(i > 0.0)*/ - outi = max(i*visifac*refl, 0.0); + /*if (i > 0.0)*/ + outi = max(i * visifac * refl, 0.0); /*else - outi = i;*/ + outi = i;*/ } void shade_tangent_v_spec(vec3 tang, out vec3 vn) @@ -1968,8 +2025,8 @@ void shade_tangent_v_spec(vec3 tang, out vec3 vn) void shade_add_to_diffuse(float i, vec3 lampcol, vec3 col, out vec3 outcol) { - if(i > 0.0) - outcol = i*lampcol*col; + if (i > 0.0) + outcol = i * lampcol * col; else outcol = vec3(0.0, 0.0, 0.0); } @@ -1980,9 +2037,9 @@ void shade_hemi_spec(vec3 vn, vec3 lv, vec3 view, float spec, float hard, float lv = normalize(lv); t = dot(vn, lv); - t = 0.5*t + 0.5; + t = 0.5 * t + 0.5; - t = visifac*spec*pow(t, hard); + t = visifac * spec * pow(t, hard); } void shade_phong_spec(vec3 n, vec3 l, vec3 v, float hard, out float specfac) @@ -1998,61 +2055,63 @@ void shade_cooktorr_spec(vec3 n, vec3 l, vec3 v, float hard, out float specfac) vec3 h = normalize(v + l); float nh = dot(n, h); - if(nh < 0.0) { + if (nh < 0.0) { specfac = 0.0; } else { float nv = max(dot(n, v), 0.0); float i = pow(nh, hard); - i = i/(0.1+nv); + i = i / (0.1 + nv); specfac = i; } } void shade_blinn_spec(vec3 n, vec3 l, vec3 v, float refrac, float spec_power, out float specfac) { - if(refrac < 1.0) { + if (refrac < 1.0) { specfac = 0.0; } - else if(spec_power == 0.0) { + else if (spec_power == 0.0) { specfac = 0.0; } else { - if(spec_power<100.0) - spec_power= sqrt(1.0/spec_power); + if (spec_power < 100.0) + spec_power = sqrt(1.0 / spec_power); else - spec_power= 10.0/spec_power; + spec_power = 10.0 / spec_power; vec3 h = normalize(v + l); float nh = dot(n, h); - if(nh < 0.0) { + if (nh < 0.0) { specfac = 0.0; } else { float nv = max(dot(n, v), 0.01); float nl = dot(n, l); - if(nl <= 0.01) { + if (nl <= 0.01) { specfac = 0.0; } else { float vh = max(dot(v, h), 0.01); float a = 1.0; - float b = (2.0*nh*nv)/vh; - float c = (2.0*nh*nl)/vh; + float b = (2.0 * nh * nv) / vh; + float c = (2.0 * nh * nl) / vh; float g = 0.0; - if(a < b && a < c) g = a; - else if(b < a && b < c) g = b; - else if(c < a && c < b) g = c; + if (a < b && a < c) g = a; + else if (b < a && b < c) g = b; + else if (c < a && c < b) g = c; - float p = sqrt(((refrac * refrac)+(vh*vh)-1.0)); - float f = (((p-vh)*(p-vh))/((p+vh)*(p+vh)))*(1.0+((((vh*(p+vh))-1.0)*((vh*(p+vh))-1.0))/(((vh*(p-vh))+1.0)*((vh*(p-vh))+1.0)))); + float p = sqrt(((refrac * refrac) + (vh * vh) - 1.0)); + float f = ((((p - vh) * (p - vh)) / ((p + vh) * (p + vh))) * + (1.0 + ((((vh * (p + vh)) - 1.0) * ((vh * (p + vh)) - 1.0)) / + (((vh * (p - vh)) + 1.0) * ((vh * (p - vh)) + 1.0))))); float ang = acos(nh); - specfac = max(f*g*exp_blender((-(ang*ang)/(2.0*spec_power*spec_power))), 0.0); + specfac = max(f * g * exp_blender((-(ang * ang) / (2.0 * spec_power * spec_power))), 0.0); } } } @@ -2067,7 +2126,7 @@ void shade_wardiso_spec(vec3 n, vec3 l, vec3 v, float rms, out float specfac) float angle = tan(acos(nh)); float alpha = max(rms, 0.001); - specfac= nl * (1.0/(4.0*M_PI*alpha*alpha))*(exp_blender(-(angle*angle)/(alpha*alpha))/(sqrt(nv*nl))); + specfac = nl * (1.0 / (4.0 * M_PI * alpha * alpha)) * (exp_blender(-(angle * angle) / (alpha * alpha)) / (sqrt(nv * nl))); } void shade_toon_spec(vec3 n, vec3 l, vec3 v, float size, float tsmooth, out float specfac) @@ -2076,31 +2135,31 @@ void shade_toon_spec(vec3 n, vec3 l, vec3 v, float size, float tsmooth, out floa float rslt = dot(h, n); float ang = acos(rslt); - if(ang < size) rslt = 1.0; - else if(ang >= (size + tsmooth) || tsmooth == 0.0) rslt = 0.0; - else rslt = 1.0 - ((ang - size)/tsmooth); + if (ang < size) rslt = 1.0; + else if (ang >= (size + tsmooth) || tsmooth == 0.0) rslt = 0.0; + else rslt = 1.0 - ((ang - size) / tsmooth); specfac = rslt; } void shade_spec_area_inp(float specfac, float inp, out float outspecfac) { - outspecfac = specfac*inp; + outspecfac = specfac * inp; } void shade_spec_t(float shadfac, float spec, float visifac, float specfac, out float t) { - t = shadfac*spec*visifac*specfac; + t = shadfac * spec * visifac * specfac; } void shade_add_spec(float t, vec3 lampcol, vec3 speccol, out vec3 outcol) { - outcol = t*lampcol*speccol; + outcol = t * lampcol * speccol; } void shade_add_mirror(vec3 mir, vec4 refcol, vec3 combined, out vec3 result) { - result = mir*refcol.gba + (vec3(1.0) - mir*refcol.rrr)*combined; + result = mir * refcol.gba + (vec3(1.0) - mir * refcol.rrr) * combined; } void alpha_spec_correction(vec3 spec, float spectra, float alpha, out float outalpha) @@ -2121,7 +2180,7 @@ void shade_add(vec4 col1, vec4 col2, out vec4 outcol) void shade_madd(vec4 col, vec4 col1, vec4 col2, out vec4 outcol) { - outcol = col + col1*col2; + outcol = col + col1 * col2; } void shade_add_clamped(vec4 col1, vec4 col2, out vec4 outcol) @@ -2131,52 +2190,52 @@ void shade_add_clamped(vec4 col1, vec4 col2, out vec4 outcol) void shade_madd_clamped(vec4 col, vec4 col1, vec4 col2, out vec4 outcol) { - outcol = col + max(col1*col2, vec4(0.0, 0.0, 0.0, 0.0)); + outcol = col + max(col1 * col2, vec4(0.0, 0.0, 0.0, 0.0)); } void shade_maddf(vec4 col, float f, vec4 col1, out vec4 outcol) { - outcol = col + f*col1; + outcol = col + f * col1; } void shade_mul(vec4 col1, vec4 col2, out vec4 outcol) { - outcol = col1*col2; + outcol = col1 * col2; } void shade_mul_value(float fac, vec4 col, out vec4 outcol) { - outcol = col*fac; + outcol = col * fac; } void shade_mul_value_v3(float fac, vec3 col, out vec3 outcol) { - outcol = col*fac; + outcol = col * fac; } void shade_obcolor(vec4 col, vec4 obcol, out vec4 outcol) { - outcol = vec4(col.rgb*obcol.rgb, col.a); + outcol = vec4(col.rgb * obcol.rgb, col.a); } void ramp_rgbtobw(vec3 color, out float outval) { - outval = color.r*0.3 + color.g*0.58 + color.b*0.12; + outval = color.r * 0.3 + color.g * 0.58 + color.b * 0.12; } void shade_only_shadow(float i, float shadfac, float energy, vec3 shadcol, out vec3 outshadrgb) { - outshadrgb = i*energy*(1.0 - shadfac)*(vec3(1.0)-shadcol); + outshadrgb = i * energy * (1.0 - shadfac) * (vec3(1.0) - shadcol); } void shade_only_shadow_diffuse(vec3 shadrgb, vec3 rgb, vec4 diff, out vec4 outdiff) { - outdiff = diff - vec4(rgb*shadrgb, 0.0); + outdiff = diff - vec4(rgb * shadrgb, 0.0); } void shade_only_shadow_specular(vec3 shadrgb, vec3 specrgb, vec4 spec, out vec4 outspec) { - outspec = spec - vec4(specrgb*shadrgb, 0.0); + outspec = spec - vec4(specrgb * shadrgb, 0.0); } void shade_clamp_positive(vec4 col, out vec4 outcol) @@ -2184,51 +2243,68 @@ void shade_clamp_positive(vec4 col, out vec4 outcol) outcol = max(col, vec4(0.0)); } -void test_shadowbuf(vec3 rco, sampler2DShadow shadowmap, mat4 shadowpersmat, float shadowbias, float inp, out float result) +void test_shadowbuf( + vec3 rco, sampler2DShadow shadowmap, mat4 shadowpersmat, float shadowbias, float inp, + out float result) { - vec4 co = shadowpersmat*vec4(rco, 1.0); + if (inp <= 0.0) { + result = 0.0; + } + else { + vec4 co = shadowpersmat * vec4(rco, 1.0); - //float bias = (1.5 - inp*inp)*shadowbias; - co.z -= shadowbias*co.w; + //float bias = (1.5 - inp*inp)*shadowbias; + co.z -= shadowbias * co.w; - if (co.w > 0.0 && co.x > 0.0 && co.x/co.w < 1.0 && co.y > 0.0 && co.y/co.w < 1.0) - result = shadow2DProj(shadowmap, co).x; - else - result = 1.0; + if (co.w > 0.0 && co.x > 0.0 && co.x / co.w < 1.0 && co.y > 0.0 && co.y / co.w < 1.0) + result = shadow2DProj(shadowmap, co).x; + else + result = 1.0; + } } -void test_shadowbuf_vsm(vec3 rco, sampler2D shadowmap, mat4 shadowpersmat, float shadowbias, float bleedbias, float inp, out float result) +void test_shadowbuf_vsm( + vec3 rco, sampler2D shadowmap, mat4 shadowpersmat, float shadowbias, float bleedbias, float inp, + out float result) { - vec4 co = shadowpersmat*vec4(rco, 1.0); - if (co.w > 0.0 && co.x > 0.0 && co.x/co.w < 1.0 && co.y > 0.0 && co.y/co.w < 1.0) { - vec2 moments = texture2DProj(shadowmap, co).rg; - float dist = co.z/co.w; - float p = 0.0; + if (inp <= 0.0) { + result = 0.0; + } + else { + vec4 co = shadowpersmat * vec4(rco, 1.0); + if (co.w > 0.0 && co.x > 0.0 && co.x / co.w < 1.0 && co.y > 0.0 && co.y / co.w < 1.0) { + vec2 moments = texture2DProj(shadowmap, co).rg; + float dist = co.z / co.w; + float p = 0.0; - if(dist <= moments.x) - p = 1.0; + if (dist <= moments.x) + p = 1.0; - float variance = moments.y - (moments.x*moments.x); - variance = max(variance, shadowbias/10.0); + float variance = moments.y - (moments.x * moments.x); + variance = max(variance, shadowbias / 10.0); - float d = moments.x - dist; - float p_max = variance / (variance + d*d); + float d = moments.x - dist; + float p_max = variance / (variance + d * d); - // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] - p_max = clamp((p_max-bleedbias)/(1.0-bleedbias), 0.0, 1.0); + // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1] + p_max = clamp((p_max - bleedbias) / (1.0 - bleedbias), 0.0, 1.0); - result = max(p, p_max); - } - else { - result = 1.0; + result = max(p, p_max); + } + else { + result = 1.0; + } } } -void shadows_only(vec3 rco, sampler2DShadow shadowmap, mat4 shadowpersmat, float shadowbias, vec3 shadowcolor, float inp, out vec3 result) +void shadows_only( + vec3 rco, sampler2DShadow shadowmap, mat4 shadowpersmat, + float shadowbias, vec3 shadowcolor, float inp, + out vec3 result) { result = vec3(1.0); - if(inp > 0.0) { + if (inp > 0.0) { float shadfac; test_shadowbuf(rco, shadowmap, shadowpersmat, shadowbias, inp, shadfac); @@ -2236,11 +2312,14 @@ void shadows_only(vec3 rco, sampler2DShadow shadowmap, mat4 shadowpersmat, float } } -void shadows_only_vsm(vec3 rco, sampler2D shadowmap, mat4 shadowpersmat, float shadowbias, float bleedbias, vec3 shadowcolor, float inp, out vec3 result) +void shadows_only_vsm( + vec3 rco, sampler2D shadowmap, mat4 shadowpersmat, + float shadowbias, float bleedbias, vec3 shadowcolor, float inp, + out vec3 result) { result = vec3(1.0); - if(inp > 0.0) { + if (inp > 0.0) { float shadfac; test_shadowbuf_vsm(rco, shadowmap, shadowpersmat, shadowbias, bleedbias, inp, shadfac); @@ -2251,26 +2330,28 @@ void shadows_only_vsm(vec3 rco, sampler2D shadowmap, mat4 shadowpersmat, float s void shade_light_texture(vec3 rco, sampler2D cookie, mat4 shadowpersmat, out vec4 result) { - vec4 co = shadowpersmat*vec4(rco, 1.0); + vec4 co = shadowpersmat * vec4(rco, 1.0); result = texture2DProj(cookie, co); } void shade_exposure_correct(vec3 col, float linfac, float logfac, out vec3 outcol) { - outcol = linfac*(1.0 - exp(col*logfac)); + outcol = linfac * (1.0 - exp(col * logfac)); } -void shade_mist_factor(vec3 co, float enable, float miststa, float mistdist, float misttype, float misi, out float outfac) +void shade_mist_factor( + vec3 co, float enable, float miststa, float mistdist, float misttype, float misi, + out float outfac) { - if(enable == 1.0) { + if (enable == 1.0) { float fac, zcor; - zcor = (gl_ProjectionMatrix[3][3] == 0.0)? length(co): -co[2]; - + zcor = (gl_ProjectionMatrix[3][3] == 0.0) ? length(co) : -co[2]; + fac = clamp((zcor - miststa) / mistdist, 0.0, 1.0); - if(misttype == 0.0) fac *= fac; - else if(misttype == 1.0); + if (misttype == 0.0) fac *= fac; + else if (misttype == 1.0) ; else fac = sqrt(fac); outfac = 1.0 - (1.0 - fac) * (1.0 - misi); @@ -2293,7 +2374,7 @@ void shade_alpha_opaque(vec4 col, out vec4 outcol) void shade_alpha_obcolor(vec4 col, vec4 obcol, out vec4 outcol) { - outcol = vec4(col.rgb, col.a*obcol.a); + outcol = vec4(col.rgb, col.a * obcol.a); } /* ********************** matcap style render ******************** */ @@ -2304,7 +2385,7 @@ void material_preview_matcap(vec4 color, sampler2D ima, vec4 N, vec4 mask, out v vec2 tex; #ifndef USE_OPENSUBDIV - /* remap to 0.0 - 1.0 range. This is done because OpenGL 2.0 clamps colors + /* remap to 0.0 - 1.0 range. This is done because OpenGL 2.0 clamps colors * between shader stages and we want the full range of the normal */ normal = vec3(2.0, 2.0, 2.0) * vec3(N.x, N.y, N.z) - vec3(1.0, 1.0, 1.0); if (normal.z < 0.0) { diff --git a/source/blender/gpu/shaders/gpu_shader_material_bsdf_velvet.glsl b/source/blender/gpu/shaders/gpu_shader_material_bsdf_velvet.glsl index b1af6a8ba85..553627edead 100644 --- a/source/blender/gpu/shaders/gpu_shader_material_bsdf_velvet.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material_bsdf_velvet.glsl @@ -152,5 +152,5 @@ void env_sampling_velvet( } } - result = out_radiance.rgb * unfbsdfsamples.y * specular_occlusion(NV, ao_factor, a2); + result = out_radiance.rgb * unfbsdfsamples.y * ao_factor; } diff --git a/source/blender/gpu/shaders/gpu_shader_material_new_shading.glsl b/source/blender/gpu/shaders/gpu_shader_material_new_shading.glsl index c7743b7ff04..c06e7f54205 100644 --- a/source/blender/gpu/shaders/gpu_shader_material_new_shading.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material_new_shading.glsl @@ -138,25 +138,19 @@ void background_transform_to_world(vec3 viewvec, out vec3 worldvec) vec4 co_homogenous = (gl_ProjectionMatrixInverse * v); vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0); - worldvec = normalize( (gl_ModelViewMatrixInverse * co).xyz ); -} - -void background_sampling_default(vec3 viewvec, mat4 viewinvmat, out vec3 worldvec) -{ - viewvec = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(viewvec): vec3(0.0, 0.0, -1.0); - worldvec = normalize( (viewinvmat * vec4(viewvec,0.0)).xyz ); + worldvec = (gl_ModelViewMatrixInverse * co).xyz; } void node_background(vec4 color, float strength, vec3 N, out vec4 result) { - result = vec4(color.rgb * strength, 1.0); + result = color * strength; } /* closures */ void node_mix_shader(float fac, vec4 shader1, vec4 shader2, out vec4 shader) { - shader = mix(shader1, shader2, clamp(fac, 0.0, 1.0)); + shader = mix(shader1, shader2, saturate(fac)); } void node_add_shader(vec4 shader1, vec4 shader2, out vec4 shader) @@ -293,10 +287,11 @@ void node_geometry_lamp(vec3 N, vec4 P, vec3 I, mat4 toworld, pointiness = 0.0; } -void node_tex_coord(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac, - vec3 attr_orco, vec3 attr_uv, - out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object, - out vec3 camera, out vec3 window, out vec3 reflection) +void node_tex_coord( + vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac, + vec3 attr_orco, vec3 attr_uv, + out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object, + out vec3 camera, out vec3 window, out vec3 reflection) { generated = attr_orco * 0.5 + vec3(0.5); normal = normalize((obinvmat*(viewinvmat*vec4(N, 0.0))).xyz); @@ -312,10 +307,11 @@ void node_tex_coord(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 cameraf reflection = (viewinvmat*vec4(view_reflection, 0.0)).xyz; } -void node_tex_coord_background(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac, - vec3 attr_orco, vec3 attr_uv, - out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object, - out vec3 camera, out vec3 window, out vec3 reflection) +void node_tex_coord_background( + vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, vec4 camerafac, + vec3 attr_orco, vec3 attr_uv, + out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object, + out vec3 camera, out vec3 window, out vec3 reflection) { vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); vec4 co_homogenous = (gl_ProjectionMatrixInverse * v); @@ -340,22 +336,126 @@ void node_tex_coord_background(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, v /* textures */ -void node_tex_gradient(vec3 co, out vec4 color, out float fac) +float calc_gradient(vec3 p, int gradient_type) { - color = vec4(1.0); - fac = 1.0; + float x, y, z; + x = p.x; + y = p.y; + z = p.z; + if (gradient_type == 0) { /* linear */ + return x; + } + else if (gradient_type == 1) { /* quadratic */ + float r = max(x, 0.0); + return r * r; + } + else if (gradient_type == 2) { /* easing */ + float r = min(max(x, 0.0), 1.0); + float t = r * r; + return (3.0 * t - 2.0 * t * r); + } + else if (gradient_type == 3) { /* diagonal */ + return (x + y) * 0.5; + } + else if (gradient_type == 4) { /* radial */ + return atan(y, x) / (M_PI * 2) + 0.5; + } + else { + float r = max(1.0 - sqrt(x * x + y * y + z * z), 0.0); + if (gradient_type == 5) { /* quadratic sphere */ + return r * r; + } + else if (gradient_type == 6) { /* sphere */ + return r; + } + } + return 0.0; +} + +void node_tex_gradient(vec3 co, float gradient_type, out vec4 color, out float fac) +{ + float f = calc_gradient(co, int(gradient_type)); + f = clamp(f, 0.0, 1.0); + + color = vec4(f, f, f, 1.0); + fac = f; } void node_tex_checker(vec3 co, vec4 color1, vec4 color2, float scale, out vec4 color, out float fac) { - color = vec4(1.0); - fac = 1.0; + vec3 p = co * scale; + + /* Prevent precision issues on unit coordinates. */ + p.x = (p.x + 0.000001) * 0.999999; + p.y = (p.y + 0.000001) * 0.999999; + p.z = (p.z + 0.000001) * 0.999999; + + int xi = abs(int(floor(p.x))); + int yi = abs(int(floor(p.y))); + int zi = abs(int(floor(p.z))); + + bool check = ((xi % 2 == yi % 2) == bool(zi % 2)); + + color = check ? color1 : color2; + fac = check ? 1.0 : 0.0; } -void node_tex_brick(vec3 co, vec4 color1, vec4 color2, vec4 mortar, float scale, float mortar_size, float bias, float brick_width, float row_height, out vec4 color, out float fac) +#ifdef BIT_OPERATIONS +vec2 calc_brick_texture(vec3 p, float mortar_size, float bias, + float brick_width, float row_height, + float offset_amount, int offset_frequency, + float squash_amount, int squash_frequency) { + int bricknum, rownum; + float offset = 0.0; + float x, y; + + rownum = floor_to_int(p.y / row_height); + + if (offset_frequency != 0 && squash_frequency != 0) { + brick_width *= (rownum % squash_frequency != 0) ? 1.0 : squash_amount; /* squash */ + offset = (rownum % offset_frequency != 0) ? 0.0 : (brick_width * offset_amount); /* offset */ + } + + bricknum = floor_to_int((p.x + offset) / brick_width); + + x = (p.x + offset) - brick_width * bricknum; + y = p.y - row_height * rownum; + + return vec2(clamp((integer_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias), 0.0, 1.0), + (x < mortar_size || y < mortar_size || + x > (brick_width - mortar_size) || + y > (row_height - mortar_size)) ? 1.0 : 0.0); +} +#endif + +void node_tex_brick(vec3 co, + vec4 color1, vec4 color2, + vec4 mortar, float scale, + float mortar_size, float bias, + float brick_width, float row_height, + float offset_amount, float offset_frequency, + float squash_amount, float squash_frequency, + out vec4 color, out float fac) +{ +#ifdef BIT_OPERATIONS + vec2 f2 = calc_brick_texture(co * scale, + mortar_size, bias, + brick_width, row_height, + offset_amount, int(offset_frequency), + squash_amount, int(squash_frequency)); + float tint = f2.x; + float f = f2.y; + if (f != 1.0) { + float facm = 1.0 - tint; + color1 = facm * color1 + tint * color2; + } + color = (f == 1.0) ? mortar : color1; + fac = f; +#else color = vec4(1.0); fac = 1.0; +#endif } void node_tex_clouds(vec3 co, float size, out vec4 color, out float fac) @@ -367,8 +467,8 @@ void node_tex_clouds(vec3 co, float size, out vec4 color, out float fac) void node_tex_environment_equirectangular(vec3 co, sampler2D ima, out vec4 color) { vec3 nco = normalize(co); - float u = -atan(nco.y, nco.x)/(M_2PI) + 0.5; - float v = atan(nco.z, hypot(nco.x, nco.y))/M_PI + 0.5; + float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5; + float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5; color = texture2D(ima, vec2(u, v)); } @@ -400,213 +500,233 @@ void node_tex_image(vec3 co, sampler2D ima, out vec4 color, out float alpha) alpha = color.a; } -void node_tex_image_empty(vec3 co, out vec4 color, out float alpha) -{ - color = vec4(0.0); - alpha = 0.0; -} +void node_tex_image_box(vec3 texco, + vec3 nob, + sampler2D ima, + float blend, + out vec4 color, + out float alpha) +{ + /* project from direction vector to barycentric coordinates in triangles */ + nob = vec3(abs(nob.x), abs(nob.y), abs(nob.z)); + nob /= (nob.x + nob.y + nob.z); + + /* basic idea is to think of this as a triangle, each corner representing + * one of the 3 faces of the cube. in the corners we have single textures, + * in between we blend between two textures, and in the middle we a blend + * between three textures. + * + * the Nxyz values are the barycentric coordinates in an equilateral + * triangle, which in case of blending, in the middle has a smaller + * equilateral triangle where 3 textures blend. this divides things into + * 7 zones, with an if () test for each zone */ + + vec3 weight = vec3(0.0, 0.0, 0.0); + float limit = 0.5 * (1.0 + blend); + + /* first test for corners with single texture */ + if (nob.x > limit * (nob.x + nob.y) && nob.x > limit * (nob.x + nob.z)) { + weight.x = 1.0; + } + else if (nob.y > limit * (nob.x + nob.y) && nob.y > limit * (nob.y + nob.z)) { + weight.y = 1.0; + } + else if (nob.z > limit * (nob.x + nob.z) && nob.z > limit * (nob.y + nob.z)) { + weight.z = 1.0; + } + else if (blend > 0.0) { + /* in case of blending, test for mixes between two textures */ + if (nob.z < (1.0 - limit) * (nob.y + nob.x)) { + weight.x = nob.x / (nob.x + nob.y); + weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.y = 1.0 - weight.x; + } + else if (nob.x < (1.0 - limit) * (nob.y + nob.z)) { + weight.y = nob.y / (nob.y + nob.z); + weight.y = clamp((weight.y - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.z = 1.0 - weight.y; + } + else if (nob.y < (1.0 - limit) * (nob.x + nob.z)) { + weight.x = nob.x / (nob.x + nob.z); + weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.z = 1.0 - weight.x; + } + else { + /* last case, we have a mix between three */ + weight.x = ((2.0 - limit) * nob.x + (limit - 1.0)) / (2.0 * limit - 1.0); + weight.y = ((2.0 - limit) * nob.y + (limit - 1.0)) / (2.0 * limit - 1.0); + weight.z = ((2.0 - limit) * nob.z + (limit - 1.0)) / (2.0 * limit - 1.0); + } + } + else { + /* Desperate mode, no valid choice anyway, fallback to one side.*/ + weight.x = 1.0; + } -void node_tex_magic(vec3 p, float scale, float distortion, out vec4 color, out float fac) -{ - color = vec4(1.0); - fac = 1.0; + if (weight.x > 0.0) { + color += weight.x * texture2D(ima, texco.yz); + } + if (weight.y > 0.0) { + color += weight.y * texture2D(ima, texco.xz); + } + if (weight.z > 0.0) { + color += weight.z * texture2D(ima, texco.yx); + } + + alpha = color.a; } -void node_tex_musgrave(vec3 co, float scale, float detail, float dimension, float lacunarity, float offset, float gain, out vec4 color, out float fac) +void node_tex_image_empty(vec3 co, out vec4 color, out float alpha) { - color = vec4(1.0); - fac = 1.0; + color = vec4(0.0); + alpha = 0.0; } -#define TEX_NOISE_PERLIN 0 -#define TEX_NOISE_VORONOI_F1 1 -#define TEX_NOISE_VORONOI_F2 2 -#define TEX_NOISE_VORONOI_F3 3 -#define TEX_NOISE_VORONOI_F4 4 -#define TEX_NOISE_VORONOI_F2_F1 5 -#define TEX_NOISE_VORONOI_CRACKLE 6 -#define TEX_NOISE_CELL_NOISE 7 +void node_tex_magic(vec3 co, float scale, float distortion, float depth, out vec4 color, out float fac) +{ + vec3 p = co * scale; + float x = sin((p.x + p.y + p.z) * 5.0); + float y = cos((-p.x + p.y - p.z) * 5.0); + float z = -cos((-p.x - p.y + p.z) * 5.0); + + if (depth > 0) { + x *= distortion; + y *= distortion; + z *= distortion; + y = -cos(x - y + z); + y *= distortion; + if (depth > 1) { + x = cos(x - y - z); + x *= distortion; + if (depth > 2) { + z = sin(-x - y - z); + z *= distortion; + if (depth > 3) { + x = -cos(-x + y - z); + x *= distortion; + if (depth > 4) { + y = -sin(-x + y + z); + y *= distortion; + if (depth > 5) { + y = -cos(-x + y + z); + y *= distortion; + if (depth > 6) { + x = cos(x + y + z); + x *= distortion; + if (depth > 7) { + z = sin(x + y - z); + z *= distortion; + if (depth > 8) { + x = -cos(-x - y + z); + x *= distortion; + if (depth > 9) { + y = -sin(x - y + z); + y *= distortion; + } + } + } + } + } + } + } + } + } + } + if (distortion != 0.0) { + distortion *= 2.0; + x /= distortion; + y /= distortion; + z /= distortion; + } -int texture_quick_floor(float x) -{ - return int(x) - ((x < 0) ? 1 : 0); + color = vec4(0.5 - x, 0.5 - y, 0.5 - z, 1.0); + fac = (color.x + color.y + color.z) / 3.0; } -float texture_fade(float t) +#ifdef BIT_OPERATIONS +float noise_fade(float t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } -float texture_scale3(float result) +float noise_scale3(float result) { return 0.9820 * result; } -float texture_nerp(float t, float a, float b) +float noise_nerp(float t, float a, float b) { return (1.0 - t) * a + t * b; } -float texture_grad(int hash, float x, float y, float z) +float noise_grad(uint hash, float x, float y, float z) { - // use vectors pointing to the edges of the cube - int h = hash & 15; - float u = h<8 ? x : y; - float v = h<4 ? y : h == 12 || h == 14 ? x : z; - return (bool(h&1) ? -u : u) + (bool(h&2) ? -v : v); + uint h = hash & 15u; + float u = h < 8u ? x : y; + float vt = ((h == 12u) || (h == 14u)) ? x : z; + float v = h < 4u ? y : vt; + return (((h & 1u) != 0u) ? -u : u) + (((h & 2u) != 0u) ? -v : v); } -uint texture_rot(uint x, uint k) +float noise_perlin(float x, float y, float z) { - return (x<<k) | (x>>(uint(32)-k)); -} - -uint texture_hash(int kx, int ky, int kz) -{ - uint deadbeef = uint(0xdeadbeef); - uint len = uint(3); + int X; float fx = floorfrac(x, X); + int Y; float fy = floorfrac(y, Y); + int Z; float fz = floorfrac(z, Z); - uint a = deadbeef + uint(len << 2u) + uint(13); - uint b = a; - uint c = a; + float u = noise_fade(fx); + float v = noise_fade(fy); + float w = noise_fade(fz); - c += uint(kz); - b += uint(ky); - a += uint(kx); + float result; - c ^= b; - c -= texture_rot(b,uint(14)); - a ^= c; - a -= texture_rot(c,uint(11)); - b ^= a; - b -= texture_rot(a,uint(25)); - c ^= b; - c -= texture_rot(b,uint(16)); - a ^= c; - a -= texture_rot(c,uint(4)); - b ^= a; - b -= texture_rot(a,uint(14)); - c ^= b; - c -= texture_rot(b,uint(24)); - - return c; + result = noise_nerp(w, noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z), fx, fy, fz), + noise_grad(hash(X + 1, Y, Z), fx - 1.0, fy, fz)), + noise_nerp(u, noise_grad(hash(X, Y + 1, Z), fx, fy - 1.0, fz), + noise_grad(hash(X + 1, Y + 1, Z), fx - 1.0, fy - 1.0, fz))), + noise_nerp(v, noise_nerp(u, noise_grad(hash(X, Y, Z + 1), fx, fy, fz - 1.0), + noise_grad(hash(X + 1, Y, Z + 1), fx - 1.0, fy, fz - 1.0)), + noise_nerp(u, noise_grad(hash(X, Y + 1, Z + 1), fx, fy - 1.0, fz - 1.0), + noise_grad(hash(X + 1, Y + 1, Z + 1), fx - 1.0, fy - 1.0, fz - 1.0)))); + return noise_scale3(result); } -float texture_perlin (float x, float y, float z) { - - int X = texture_quick_floor(x); - float fx = x-float(X); - int Y = texture_quick_floor(y); - float fy = y-float(Y); - int Z = texture_quick_floor(z); - float fz = z-float(Z); - - float u = texture_fade(fx); - float v = texture_fade(fy); - float w = texture_fade(fz); - - float result = texture_nerp (w, texture_nerp (v, texture_nerp (u, texture_grad (int(texture_hash (X , Y , Z )), fx ,fy , fz ), - texture_grad (int(texture_hash (X+1, Y , Z )), fx-1.0, fy , fz )), - texture_nerp (u, texture_grad (int(texture_hash (X , Y+1, Z )), fx ,fy-1.0 , fz ), - texture_grad (int(texture_hash (X+1, Y+1, Z )), fx-1.0, fy-1.0 , fz ))), - texture_nerp (v, texture_nerp (u, texture_grad (int(texture_hash (X , Y , Z+1)), fx ,fy , fz-1.0 ), - texture_grad (int(texture_hash (X+1, Y , Z+1)), fx-1.0, fy , fz-1.0 )), - texture_nerp (u, texture_grad (int(texture_hash (X , Y+1, Z+1)), fx ,fy-1.0 , fz-1.0 ), - texture_grad (int(texture_hash (X+1, Y+1, Z+1)), fx-1.0, fy-1.0 , fz-1.0 )))); - return texture_scale3(result); -} - -// perlin noise in range -1..1 -float texture_snoise(vec3 p) +float noise(vec3 p) { - return texture_perlin(p.x, p.y, p.z); + return 0.5 * noise_perlin(p.x, p.y, p.z) + 0.5; } -// perlin noise in range 0..1 -float texture_noise(vec3 p) +float snoise(vec3 p) { - float r = texture_perlin(p.x, p.y, p.z); - return 0.5*r + 0.5; + return noise_perlin(p.x, p.y, p.z); } -float texture_safe_noise(vec3 p, int type) +float noise_turbulence(vec3 p, float octaves, int hard) { - float f = 0.0; - - /* Perlin noise in range -1..1 */ - if (type == 0) - f = texture_snoise(p); - - /* Perlin noise in range 0..1 */ - else - f = texture_noise(p); - - /* can happen for big coordinates, things even out to 0.5 then anyway */ - if (f > 1.18118254e+36 || f < -1.18118254e+36) - return 0.5; - - return f; -} - -float texture_noise_basis(vec3 p, int basis) -{ - if (basis == TEX_NOISE_PERLIN) - return texture_safe_noise(p, 1); - /* not supported in cycles? */ - /* - if (basis == TEX_NOISE_VORONOI_F1) - return voronoi_F1S(p); - if (basis == TEX_NOISE_VORONOI_F2) - return voronoi_F2S(p); - if (basis == TEX_NOISE_VORONOI_F3) - return voronoi_F3S(p); - if (basis == TEX_NOISE_VORONOI_F4) - return voronoi_F4S(p); - if (basis == TEX_NOISE_VORONOI_F2_F1) - return voronoi_F1F2S(p); - if (basis == TEX_NOISE_VORONOI_CRACKLE) - return voronoi_CrS(p); - if (basis == TEX_NOISE_CELL_NOISE) - return texture_cellnoise(p); - */ - return 0.0; -} - -float texture_noise_turbulence(vec3 p, int basis, float details, int hard) -{ - float fscale = 1.0; float amp = 1.0; float sum = 0.0; int i, n; - - float octaves = clamp(details, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 16.0); n = int(octaves); - for (i = 0; i <= n; i++) { - float t = texture_noise_basis(fscale * p, basis); - - if (hard==1) + float t = noise(fscale * p); + if (hard != 0) { t = abs(2.0 * t - 1.0); - + } sum += t * amp; amp *= 0.5; fscale *= 2.0; } - float rmd = octaves - floor(octaves); - - if (rmd != 0.0) { - float t = texture_noise_basis(fscale * p, basis); - - if (bool(hard)) + if (rmd != 0.0) { + float t = noise(fscale * p); + if (hard != 0) { t = abs(2.0 * t - 1.0); - + } float sum2 = sum + t * amp; - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1)); - return (1.0 - rmd) * sum + rmd * sum2; } else { @@ -614,29 +734,246 @@ float texture_noise_turbulence(vec3 p, int basis, float details, int hard) return sum; } } +#endif // BIT_OPERATIONS void node_tex_noise(vec3 co, float scale, float detail, float distortion, out vec4 color, out float fac) { - int basis = TEX_NOISE_PERLIN; +#ifdef BIT_OPERATIONS + vec3 p = co * scale; int hard = 0; + if (distortion != 0.0) { + vec3 r, offset = vec3(13.5, 13.5, 13.5); + r.x = noise(p + offset) * distortion; + r.y = noise(p) * distortion; + r.z = noise(p - offset) * distortion; + p += r; + } + + fac = noise_turbulence(p, detail, hard); + color = vec4(fac, + noise_turbulence(vec3(p.y, p.x, p.z), detail, hard), + noise_turbulence(vec3(p.y, p.z, p.x), detail, hard), + 1); +#else // BIT_OPERATIONS + color = vec4(1.0); + fac = 1.0; +#endif // BIT_OPERATIONS +} + - co *= scale; +#ifdef BIT_OPERATIONS - if(distortion != 0.0) { - vec3 r, offset = vec3(13.5); +/* Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ - r.x = texture_noise_basis(co + offset, basis) * distortion; - r.y = texture_noise_basis(co, basis) * distortion; - r.z = texture_noise_basis(co - offset, basis) * distortion; +float noise_musgrave_fBm(vec3 p, float H, float lacunarity, float octaves) +{ + float rmd; + float value = 0.0; + float pwr = 1.0; + float pwHL = pow(lacunarity, -H); + int i; - co += r; + for (i = 0; i < int(octaves); i++) { + value += snoise(p) * pwr; + pwr *= pwHL; + p *= lacunarity; } - fac = texture_noise_turbulence(co, basis, detail, hard); - color = vec4(fac, - texture_noise_turbulence(vec3(co.y, co.x, co.z), basis, detail, hard), - texture_noise_turbulence(vec3(co.y, co.z, co.x), basis, detail, hard), - 1.0); + rmd = octaves - floor(octaves); + if (rmd != 0.0) + value += rmd * snoise(p) * pwr; + + return value; +} + +/* Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float noise_musgrave_multi_fractal(vec3 p, float H, float lacunarity, float octaves) +{ + float rmd; + float value = 1.0; + float pwr = 1.0; + float pwHL = pow(lacunarity, -H); + int i; + + for (i = 0; i < int(octaves); i++) { + value *= (pwr * snoise(p) + 1.0); + pwr *= pwHL; + p *= lacunarity; + } + + rmd = octaves - floor(octaves); + if (rmd != 0.0) + value *= (rmd * pwr * snoise(p) + 1.0); /* correct? */ + + return value; +} + +/* Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float noise_musgrave_hetero_terrain(vec3 p, float H, float lacunarity, float octaves, float offset) +{ + float value, increment, rmd; + float pwHL = pow(lacunarity, -H); + float pwr = pwHL; + int i; + + /* first unscaled octave of function; later octaves are scaled */ + value = offset + snoise(p); + p *= lacunarity; + + for (i = 1; i < int(octaves); i++) { + increment = (snoise(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + rmd = octaves - floor(octaves); + if (rmd != 0.0) { + increment = (snoise(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float noise_musgrave_hybrid_multi_fractal(vec3 p, float H, float lacunarity, float octaves, float offset, float gain) +{ + float result, signal, weight, rmd; + float pwHL = pow(lacunarity, -H); + float pwr = pwHL; + int i; + + result = snoise(p) + offset; + weight = gain * result; + p *= lacunarity; + + for (i = 1; (weight > 0.001f) && (i < int(octaves)); i++) { + if (weight > 1.0) + weight = 1.0; + + signal = (snoise(p) + offset) * pwr; + pwr *= pwHL; + result += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + rmd = octaves - floor(octaves); + if (rmd != 0.0) + result += rmd * ((snoise(p) + offset) * pwr); + + return result; +} + +/* Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float noise_musgrave_ridged_multi_fractal(vec3 p, float H, float lacunarity, float octaves, float offset, float gain) +{ + float result, signal, weight; + float pwHL = pow(lacunarity, -H); + float pwr = pwHL; + int i; + + signal = offset - abs(snoise(p)); + signal *= signal; + result = signal; + weight = 1.0; + + for (i = 1; i < int(octaves); i++) { + p *= lacunarity; + weight = clamp(signal * gain, 0.0, 1.0); + signal = offset - abs(snoise(p)); + signal *= signal; + signal *= weight; + result += signal * pwr; + pwr *= pwHL; + } + + return result; +} + +float svm_musgrave(int type, + float dimension, + float lacunarity, + float octaves, + float offset, + float intensity, + float gain, + vec3 p) +{ + if (type == 0 /*NODE_MUSGRAVE_MULTIFRACTAL*/) + return intensity * noise_musgrave_multi_fractal(p, dimension, lacunarity, octaves); + else if (type == 1 /*NODE_MUSGRAVE_FBM*/) + return intensity * noise_musgrave_fBm(p, dimension, lacunarity, octaves); + else if (type == 2 /*NODE_MUSGRAVE_HYBRID_MULTIFRACTAL*/) + return intensity * noise_musgrave_hybrid_multi_fractal(p, dimension, lacunarity, octaves, offset, gain); + else if (type == 3 /*NODE_MUSGRAVE_RIDGED_MULTIFRACTAL*/) + return intensity * noise_musgrave_ridged_multi_fractal(p, dimension, lacunarity, octaves, offset, gain); + else if (type == 4 /*NODE_MUSGRAVE_HETERO_TERRAIN*/) + return intensity * noise_musgrave_hetero_terrain(p, dimension, lacunarity, octaves, offset); + return 0.0; +} +#endif // #ifdef BIT_OPERATIONS + +void node_tex_musgrave(vec3 co, + float scale, + float detail, + float dimension, + float lacunarity, + float offset, + float gain, + float type, + out vec4 color, + out float fac) +{ +#ifdef BIT_OPERATIONS + fac = svm_musgrave(int(type), + dimension, + lacunarity, + detail, + offset, + 1.0, + gain, + co * scale); +#else + fac = 1.0; +#endif + + color = vec4(fac, fac, fac, 1.0); } void node_tex_sky(vec3 co, out vec4 color) @@ -644,16 +981,117 @@ void node_tex_sky(vec3 co, out vec4 color) color = vec4(1.0); } -void node_tex_voronoi(vec3 co, float scale, out vec4 color, out float fac) -{ +void node_tex_voronoi(vec3 co, float scale, float coloring, out vec4 color, out float fac) +{ +#ifdef BIT_OPERATIONS + vec3 p = co * scale; + int xx, yy, zz, xi, yi, zi; + float da[4]; + vec3 pa[4]; + + xi = floor_to_int(p[0]); + yi = floor_to_int(p[1]); + zi = floor_to_int(p[2]); + + da[0] = 1e+10; + da[1] = 1e+10; + da[2] = 1e+10; + da[3] = 1e+10; + + for (xx = xi - 1; xx <= xi + 1; xx++) { + for (yy = yi - 1; yy <= yi + 1; yy++) { + for (zz = zi - 1; zz <= zi + 1; zz++) { + vec3 ip = vec3(xx, yy, zz); + vec3 vp = cellnoise_color(ip); + vec3 pd = p - (vp + ip); + float d = dot(pd, pd); + vp += vec3(xx, yy, zz); + if (d < da[0]) { + da[3] = da[2]; + da[2] = da[1]; + da[1] = da[0]; + da[0] = d; + pa[3] = pa[2]; + pa[2] = pa[1]; + pa[1] = pa[0]; + pa[0] = vp; + } + else if (d < da[1]) { + da[3] = da[2]; + da[2] = da[1]; + da[1] = d; + + pa[3] = pa[2]; + pa[2] = pa[1]; + pa[1] = vp; + } + else if (d < da[2]) { + da[3] = da[2]; + da[2] = d; + + pa[3] = pa[2]; + pa[2] = vp; + } + else if (d < da[3]) { + da[3] = d; + pa[3] = vp; + } + } + } + } + + if (coloring == 0.0) { + fac = abs(da[0]); + color = vec4(fac, fac, fac, 1); + } + else { + color = vec4(cellnoise_color(pa[0]), 1); + fac = (color.x + color.y + color.z) * (1.0 / 3.0); + } +#else // BIT_OPERATIONS color = vec4(1.0); fac = 1.0; +#endif // BIT_OPERATIONS } -void node_tex_wave(vec3 co, float scale, float distortion, float detail, float detail_scale, out vec4 color, out float fac) +#ifdef BIT_OPERATIONS +float calc_wave(vec3 p, float distortion, float detail, float detail_scale, int wave_type, int wave_profile) { + float n; + + if (wave_type == 0) /* type bands */ + n = (p.x + p.y + p.z) * 10.0; + else /* type rings */ + n = length(p) * 20.0; + + if (distortion != 0.0) + n += distortion * noise_turbulence(p * detail_scale, detail, 0); + + if (wave_profile == 0) { /* profile sin */ + return 0.5 + 0.5 * sin(n); + } + else { /* profile saw */ + n /= 2.0 * M_PI; + n -= int(n); + return (n < 0.0) ? n + 1.0 : n; + } +} +#endif // BIT_OPERATIONS + +void node_tex_wave( + vec3 co, float scale, float distortion, float detail, float detail_scale, float wave_type, float wave_profile, + out vec4 color, out float fac) +{ +#ifdef BIT_OPERATIONS + float f; + f = calc_wave(co * scale, distortion, detail, detail_scale, int(wave_type), int(wave_profile)); + + color = vec4(f, f, f, 1.0); + fac = f; +#else // BIT_OPERATIONS color = vec4(1.0); - fac = 1.0; + fac = 1; +#endif // BIT_OPERATIONS } /* light path */ @@ -706,55 +1144,38 @@ void node_object_info(mat4 objmat, out vec3 location, out float object_index, ou random = 0.0; } -void node_normal_map_tangent(float strength, vec4 color, vec3 N, vec4 T, mat4 viewmat, mat4 obmat, mat4 viewinvmat, mat4 obinvmat, out vec3 result) +void node_normal_map(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal) { - color = ( color - vec4(0.5))*vec4(2.0); - N = normalize((obinvmat*(viewinvmat*vec4(N, 0.0))).xyz); - T = vec4( normalize((obinvmat*(viewinvmat*vec4(T.xyz, 0.0))).xyz), T.w); - //vec3 No = color.xyz * vec3(1.0, -1.0, 1.0); - vec3 B = T.w * cross(N, T.xyz); - vec3 No = from_tangent_to_world(color.xyz, N, T.xyz, B); - result = normalize(N + (No - N) * max(strength, 0.0)); - result = normalize((obmat*vec4(result,0.0)).xyz); -} + vec3 B = tangent.w * cross(normal, tangent.xyz); -void node_normal_map_object(float strength, vec4 color, vec3 N, vec4 T, mat4 viewmat, mat4 obmat, mat4 viewinvmat, mat4 obinvmat, out vec3 result) -{ - color = ( color - vec4(0.5))*vec4(2.0); - vec3 No = color.xyz * vec3(1.0, -1.0, 1.0); - N = normalize((obinvmat*(viewinvmat*vec4(N, 0.0))).xyz); - result = normalize(N + (No - N) * max(strength, 0.0)); - result = normalize((obmat*vec4(result,0.0)).xyz); + outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal; + outnormal = normalize(outnormal); } -void node_normal_map_world(float strength, vec4 color, vec3 N, vec4 T, mat4 viewmat, mat4 obmat, mat4 viewinvmat, mat4 obinvmat, out vec3 result) +void node_bump(float strength, float dist, float height, vec3 N, vec3 surf_pos, float invert, out vec3 result) { - color = ( color - vec4(0.5))*vec4(2.0); - vec3 No = color.xyz * vec3(1.0, -1.0, 1.0); - N = normalize((viewinvmat*vec4(N, 0.0)).xyz); - result = normalize(N + (No - N) * max(strength, 0.0)); -} + if (invert != 0.0) { + dist *= -1.0; + } + vec3 dPdx = dFdx(surf_pos); + vec3 dPdy = dFdy(surf_pos); -void node_normal_map_blend_object(float strength, vec4 color, vec3 N, vec4 T, mat4 viewmat, mat4 obmat, mat4 viewinvmat, mat4 obinvmat, out vec3 result) -{ - color = ( color - vec4(0.5))*vec4(2.0); - vec3 No = color.xzy * vec3(1.0, -1.0, 1.0); - N = normalize((obinvmat*(viewinvmat*vec4(N, 0.0))).xyz); - result = normalize(N + (No - N) * max(strength, 0.0)); - result = normalize((obmat*vec4(result,0.0)).xyz); -} + /* Get surface tangents from normal. */ + vec3 Rx = cross(dPdy, N); + vec3 Ry = cross(N, dPdx); -void node_normal_map_blend_world(float strength, vec4 color, vec3 N, vec4 T, mat4 viewmat, mat4 obmat, mat4 viewinvmat, mat4 obinvmat, out vec3 result) -{ - color = ( color - vec4(0.5))*vec4(2.0); - vec3 No = color.xzy * vec3(1.0, -1.0, 1.0); - N = normalize((viewinvmat*vec4(N, 0.0)).xyz); - result = normalize(N + (No - N) * max(strength, 0.0)); -} + /* Compute surface gradient and determinant. */ + float det = dot(dPdx, Rx); + float absdet = abs(det); -void node_bump(float strength, float dist, float height, vec3 N, out vec3 result) -{ - result = N; + float dHdx = dFdx(height); + float dHdy = dFdy(height); + vec3 surfgrad = dHdx * Rx + dHdy * Ry; + + strength = max(strength, 0.0); + + result = normalize(absdet * N - dist * sign(det) * surfgrad); + result = normalize(strength * result + (1.0 - strength) * N); } /* output */ diff --git a/source/blender/gpu/shaders/gpu_shader_material_utils.glsl b/source/blender/gpu/shaders/gpu_shader_material_utils.glsl index 3393f35e91a..d1037daa784 100644 --- a/source/blender/gpu/shaders/gpu_shader_material_utils.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material_utils.glsl @@ -68,6 +68,96 @@ float hypot(float x, float y) { return sqrt(x*x + y*y); } float inverse_distance(vec3 V) { return max( 1 / length(V), 1e-8); } +/* --------- Noise Utils Functions --------- */ + +void generated_from_orco(vec3 orco, out vec3 generated) +{ + generated = orco * 0.5 + vec3(0.5); +} + +int floor_to_int(float x) +{ + return int(floor(x)); +} + +int quick_floor(float x) +{ + return int(x) - ((x < 0) ? 1 : 0); +} + +#ifdef BIT_OPERATIONS +float integer_noise(int n) +{ + int nn; + n = (n + 1013) & 0x7fffffff; + n = (n >> 13) ^ n; + nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; + return 0.5 * (float(nn) / 1073741824.0); +} + +uint hash(uint kx, uint ky, uint kz) +{ +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) +#define final(a, b, c) \ +{ \ + c ^= b; c -= rot(b, 14); \ + a ^= c; a -= rot(c, 11); \ + b ^= a; b -= rot(a, 25); \ + c ^= b; c -= rot(b, 16); \ + a ^= c; a -= rot(c, 4); \ + b ^= a; b -= rot(a, 14); \ + c ^= b; c -= rot(b, 24); \ +} + // now hash the data! + uint a, b, c, len = 3u; + a = b = c = 0xdeadbeefu + (len << 2u) + 13u; + + c += kz; + b += ky; + a += kx; + final (a, b, c); + + return c; +#undef rot +#undef final +} + +uint hash(int kx, int ky, int kz) +{ + return hash(uint(kx), uint(ky), uint(kz)); +} + +float bits_to_01(uint bits) +{ + float x = float(bits) * (1.0 / float(0xffffffffu)); + return x; +} + +float cellnoise(vec3 p) +{ + int ix = quick_floor(p.x); + int iy = quick_floor(p.y); + int iz = quick_floor(p.z); + + return bits_to_01(hash(uint(ix), uint(iy), uint(iz))); +} + +vec3 cellnoise_color(vec3 p) +{ + float r = cellnoise(p); + float g = cellnoise(vec3(p.y, p.x, p.z)); + float b = cellnoise(vec3(p.y, p.z, p.x)); + + return vec3(r, g, b); +} +#endif // BIT_OPERATIONS + +float floorfrac(float x, out int i) +{ + i = floor_to_int(x); + return x - i; +} + /* --------- Geometric Utils Functions --------- */ float linear_depth(float z) diff --git a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_frag.glsl b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_frag.glsl index 5f406c959f1..b485d2cce86 100644 --- a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_frag.glsl @@ -4,13 +4,13 @@ uniform sampler2D textureSource; void main() { vec4 color = vec4(0.0); - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(-3.0 * ScaleU.x, -3.0 * ScaleU.y ) ) * 0.015625; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(-2.0 * ScaleU.x, -2.0 * ScaleU.y ) ) * 0.09375; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(-1.0 * ScaleU.x, -1.0 * ScaleU.y ) ) * 0.234375; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(0.0, 0.0)) * 0.3125; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(1.0 * ScaleU.x, 1.0 * ScaleU.y ) ) * 0.234375; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(2.0 * ScaleU.x, 2.0 * ScaleU.y ) ) * 0.09375; - color += texture2D( textureSource, gl_TexCoord[0].st + vec2(3.0 * ScaleU.x, 3.0 * ScaleU.y ) ) * 0.015625; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(-3.0 * ScaleU.x, -3.0 * ScaleU.y)) * 0.015625; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(-2.0 * ScaleU.x, -2.0 * ScaleU.y)) * 0.09375; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(-1.0 * ScaleU.x, -1.0 * ScaleU.y)) * 0.234375; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(0.0, 0.0)) * 0.3125; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(1.0 * ScaleU.x, 1.0 * ScaleU.y)) * 0.234375; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(2.0 * ScaleU.x, 2.0 * ScaleU.y)) * 0.09375; + color += texture2D(textureSource, gl_TexCoord[0].st + vec2(3.0 * ScaleU.x, 3.0 * ScaleU.y)) * 0.015625; gl_FragColor = color; } diff --git a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl index 9bb2e7ad469..5d00108b052 100644 --- a/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_sep_gaussian_blur_vert.glsl @@ -1,6 +1,6 @@ void main() { - gl_Position = ftransform(); - gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = ftransform(); + gl_TexCoord[0] = gl_MultiTexCoord0; } diff --git a/source/blender/gpu/shaders/gpu_shader_vertex.glsl b/source/blender/gpu/shaders/gpu_shader_vertex.glsl index 7e332706695..5824d5a80db 100644 --- a/source/blender/gpu/shaders/gpu_shader_vertex.glsl +++ b/source/blender/gpu/shaders/gpu_shader_vertex.glsl @@ -29,7 +29,7 @@ void main() #ifdef CLIP_WORKAROUND int i; - for(i = 0; i < 6; i++) + for (i = 0; i < 6; i++) gl_ClipDistance[i] = dot(co, gl_ClipPlane[i]); #elif !defined(GPU_ATI) // Setting gl_ClipVertex is necessary to get glClipPlane working on NVIDIA diff --git a/source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl b/source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl index 4838289ff9e..3761bf350eb 100644 --- a/source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_vsm_store_frag.glsl @@ -15,7 +15,7 @@ void main() // Adjusting moments using partial derivative float dx = dFdx(depth); float dy = dFdy(depth); - moment2 += 0.25*(dx*dx+dy*dy); + moment2 += 0.25 * (dx * dx + dy * dy); gl_FragColor = vec4(moment1, moment2, 0.0, 0.0); } 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_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 3807bb296fd..2d1ffaa53eb 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -75,6 +75,12 @@ typedef struct CustomData { /* CustomData.type */ typedef enum CustomDataType { + /* Used by GLSL attributes in the cases when we need a delayed CD type + * assignment (in the cases when we don't know in advance which layer + * we are addressing). + */ + CD_AUTO_FROM_NAME = -1, + CD_MVERT = 0, #ifdef DNA_DEPRECATED CD_MSTICKY = 1, /* DEPRECATED */ 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 03516f0890f..354314ddc65 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..1bf044ffecb 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; @@ -1677,9 +1714,10 @@ typedef struct Scene { #define R_STAMP_RENDERTIME 0x0400 #define R_STAMP_CAMERALENS 0x0800 #define R_STAMP_STRIPMETA 0x1000 +#define R_STAMP_MEMORY 0x2000 #define R_STAMP_ALL (R_STAMP_TIME|R_STAMP_FRAME|R_STAMP_DATE|R_STAMP_CAMERA|R_STAMP_SCENE| \ R_STAMP_NOTE|R_STAMP_MARKER|R_STAMP_FILENAME|R_STAMP_SEQSTRIP| \ - R_STAMP_RENDERTIME|R_STAMP_CAMERALENS) + R_STAMP_RENDERTIME|R_STAMP_CAMERALENS|R_STAMP_MEMORY) /* alphamode */ #define R_ADDSKY 0 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/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 5ff059bc74b..eb3204e0018 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -243,6 +243,13 @@ typedef struct View3D { struct Object *probe_source; /* runtime : the probe that is being updated when V3D_PROBE_CAPTURE */ struct GPUPBRSettings pbr_settings; struct GPUPBR *pbr; /* holds all pbr specific textures */ + + /* Previous viewport draw type. + * Runtime-only, set in the rendered viewport otggle operator. + */ + short prev_drawtype; + short pad1; + float pad2; } View3D; 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_access.h b/source/blender/makesrna/RNA_access.h index 3a19211ab39..c3d25ed2972 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -979,6 +979,7 @@ bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBas char *RNA_path_from_ID_to_struct(PointerRNA *ptr); char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop); +char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, PropertyRNA *prop, int array_dim, int index); char *RNA_path_resolve_from_type_to_property( struct PointerRNA *ptr, struct PropertyRNA *prop, 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_access.c b/source/blender/makesrna/intern/rna_access.c index 97c71715349..c49a6197a4e 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -4642,7 +4642,47 @@ char *RNA_path_from_ID_to_struct(PointerRNA *ptr) return ptrpath; } -char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) +static void rna_path_array_multi_from_flat_index( + const int dimsize[RNA_MAX_ARRAY_LENGTH], const int totdims, + const int index_dim, int index, + int r_index_multi[RNA_MAX_ARRAY_LENGTH]) +{ + int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1]; + int i = totdims - 1; + dimsize_step[i + 1] = 1; + dimsize_step[i] = dimsize[i]; + while (--i != -1) { + dimsize_step[i] = dimsize[i] * dimsize_step[i + 1]; + } + while (++i != index_dim) { + int index_round = index / dimsize_step[i + 1]; + r_index_multi[i] = index_round; + index -= (index_round * dimsize_step[i + 1]); + } + BLI_assert(index == 0); +} + +static void rna_path_array_multi_string_from_flat_index( + PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, + char *index_str, int index_str_len) +{ + int dimsize[RNA_MAX_ARRAY_LENGTH]; + int totdims = RNA_property_array_dimension(ptr, prop, dimsize); + int index_multi[RNA_MAX_ARRAY_LENGTH]; + + rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi); + + for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) { + offset += BLI_snprintf_rlen(&index_str[offset], index_str_len - offset, "[%d]", index_multi[i]); + } +} + +/** + * \param index_dim: The dimensiuon to show, 0 disables. 1 for 1d array, 2 for 2d. etc. + * \param index: The *flattened* index to use when \a ``index_dim > 0``, + * this is expanded when used with multi-dimensional arrays. + */ +char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index) { const bool is_rna = (prop->magic == RNA_MAGIC); const char *propname; @@ -4656,25 +4696,36 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) propname = RNA_property_identifier(prop); + /* support indexing w/ multi-dimensional arrays */ + char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1]; + if (index_dim == 0) { + index_str[0] = '\0'; + } + else { + rna_path_array_multi_string_from_flat_index( + ptr, prop, index_dim, index, + index_str, sizeof(index_str)); + } + if (ptrpath) { if (is_rna) { - path = BLI_sprintfN("%s.%s", ptrpath, propname); + path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str); } else { char propname_esc[MAX_IDPROP_NAME * 2]; BLI_strescape(propname_esc, propname, sizeof(propname_esc)); - path = BLI_sprintfN("%s[\"%s\"]", ptrpath, propname_esc); + path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str); } MEM_freeN(ptrpath); } else if (RNA_struct_is_ID(ptr->type)) { if (is_rna) { - path = BLI_strdup(propname); + path = BLI_sprintfN("%s%s", propname, index_str); } else { char propname_esc[MAX_IDPROP_NAME * 2]; BLI_strescape(propname_esc, propname, sizeof(propname_esc)); - path = BLI_sprintfN("[\"%s\"]", propname_esc); + path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str); } } else { @@ -4684,6 +4735,11 @@ char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) return path; } +char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) +{ + return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1); +} + /** * \return the path to given ptr/prop from the closest ancestor of given type, if any (else return NULL). */ 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..678b0ac8f1f 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -131,15 +131,15 @@ static void rna_def_camera_stereo_data(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); prop = RNA_def_property(srna, "interocular_distance", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_range(prop, 0.0f, 100.0f); - RNA_def_property_ui_range(prop, 0.0f, 1.f, 1, 2); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 1e4f, 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..bf7e4634c2f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -803,6 +803,7 @@ static bNodeLink *rna_NodeTree_link_new(bNodeTree *ntree, ReportList *reports, ntreeUpdateTree(G.main, ntree); + ED_node_tag_update_nodetree(G.main, ntree, ret->tonode); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } return ret; @@ -5892,6 +5893,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 941e2607c3c..55b3fd905dc 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -345,7 +345,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); @@ -379,7 +379,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); } } @@ -387,7 +387,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); } @@ -1212,7 +1212,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_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index ed765f1b5c6..a8d8407fe74 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -199,6 +199,8 @@ static void rna_Cache_idname_change(Main *UNUSED(bmain), Scene *UNUSED(scene), P } if (use_new_name) { + BLI_filename_make_safe(cache->name); + if (pid2 && cache->flag & PTCACHE_DISK_CACHE) { char old_name[80]; char new_name[80]; 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 acefef55fb7..c8e0b7b914c 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; @@ -5873,6 +5978,11 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Strip Metadata", "Use metadata from the strips in the sequencer"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "use_stamp_memory", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_MEMORY); + RNA_def_property_ui_text(prop, "Stamp Peak Memory", "Include the peak memory usage in image metadata"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "stamp_font_size", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "stamp_font_id"); RNA_def_property_range(prop, 8, 64); @@ -6730,6 +6840,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..e1216e3c85f 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 { @@ -186,6 +196,7 @@ static void rna_Scene_collada_export( int use_ngons, int use_object_instantiation, + int use_blender_profile, int sort_by_name, int export_transformation_type, int open_sim) @@ -193,7 +204,7 @@ static void rna_Scene_collada_export( collada_export(scene, filepath, apply_modifiers, export_mesh_type, selected, include_children, include_armatures, include_shapekeys, deform_bones_only, active_uv_only, include_uv_textures, include_material_textures, - use_texture_copies, use_ngons, use_object_instantiation, sort_by_name, export_transformation_type, open_sim); + use_texture_copies, use_ngons, use_object_instantiation, use_blender_profile, sort_by_name, export_transformation_type, open_sim); } #endif @@ -276,6 +287,7 @@ void RNA_api_scene(StructRNA *srna) parm = RNA_def_boolean(func, "use_ngons", 1, "Use NGons", "Keep NGons in Export"); parm = RNA_def_boolean(func, "use_object_instantiation", 1, "Use Object Instances", "Instantiate multiple Objects from same Data"); + parm = RNA_def_boolean(func, "use_blender_profile", 1, "Use Blender Profile", "Export additional Blender specific information (for material, shaders, bones, etc.)"); parm = RNA_def_boolean(func, "sort_by_name", 0, "Sort by Object name", "Sort exported data by Object name"); parm = RNA_def_boolean(func, "open_sim", 0, "Export for SL/OpenSim", "Compatibility mode for SL, OpenSim and similar online worlds"); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index e8a45e6a77d..c3a66058888 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2327,9 +2327,20 @@ static void rna_def_text(StructRNA *srna) prop = RNA_def_property(srna, "font_size", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "text_size"); RNA_def_property_ui_text(prop, "Size", "Size of the text"); + RNA_def_property_range(prop, 0.0, 2000); 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_space.c b/source/blender/makesrna/intern/rna_space.c index 33f655b1e4c..272d027d490 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -708,6 +708,15 @@ static int rna_SpaceView3D_viewport_shade_get(PointerRNA *ptr) return drawtype; } +static void rna_SpaceView3D_viewport_shade_set(PointerRNA *ptr, int value) +{ + View3D *v3d = (View3D *)ptr->data; + if (value != v3d->drawtype && value == OB_RENDER) { + v3d->prev_drawtype = v3d->drawtype; + } + v3d->drawtype = value; +} + static EnumPropertyItem *rna_SpaceView3D_viewport_shade_itemf(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { @@ -2542,7 +2551,7 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop = RNA_def_property(srna, "viewport_shade", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "drawtype"); RNA_def_property_enum_items(prop, rna_enum_viewport_shade_items); - RNA_def_property_enum_funcs(prop, "rna_SpaceView3D_viewport_shade_get", NULL, + RNA_def_property_enum_funcs(prop, "rna_SpaceView3D_viewport_shade_get", "rna_SpaceView3D_viewport_shade_set", "rna_SpaceView3D_viewport_shade_itemf"); RNA_def_property_ui_text(prop, "Viewport Shading", "Method to display/shade objects in the 3D View"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_SpaceView3D_viewport_shade_update"); 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_tree.c b/source/blender/nodes/shader/node_shader_tree.c index c4ec55c8d06..29b1e5bc5c7 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -199,12 +199,172 @@ void register_node_tree_type_sh(void) /* GPU material from shader nodes */ +/* Find an output node of the shader tree. + * + * NOTE: it will only return output which is NOT in the group, which isn't how + * render engines works but it's how the GPU shader compilation works. This we + * can change in the future and make it a generic function, but for now it stays + * private here. + */ +static bNode *ntree_shader_output_node(bNodeTree *ntree) +{ + /* Make sure we only have single node tagged as output. */ + ntreeSetOutput(ntree); + for (bNode *node = ntree->nodes.first; node != NULL; node = node->next) { + if (node->flag & NODE_DO_OUTPUT) { + return node; + } + } + return NULL; +} + +/* Find socket with a specified identifier. */ +static bNodeSocket *ntree_shader_node_find_socket(ListBase *sockets, + const char *identifier) +{ + for (bNodeSocket *sock = sockets->first; sock != NULL; sock = sock->next) { + if (STREQ(sock->identifier, identifier)) { + return sock; + } + } + return NULL; +} + +/* Find input socket with a specified identifier. */ +static bNodeSocket *ntree_shader_node_find_input(bNode *node, + const char *identifier) +{ + return ntree_shader_node_find_socket(&node->inputs, identifier); +} + +/* Find output socket with a specified identifier. */ +static bNodeSocket *ntree_shader_node_find_output(bNode *node, + const char *identifier) +{ + return ntree_shader_node_find_socket(&node->outputs, identifier); +} + +/* Check whether shader has a displacement. + * + * Will also return a node and it's socket which is connected to a displacement + * output. Additionally, link which is attached to the displacement output is + * also returned. + */ +static bool ntree_shader_has_displacement(bNodeTree *ntree, + bNode **r_node, + bNodeSocket **r_socket, + bNodeLink **r_link) +{ + bNode *output_node = ntree_shader_output_node(ntree); + if (output_node == NULL) { + /* We can't have displacement without output node, apparently. */ + return false; + } + /* Make sure sockets links pointers are correct. */ + ntreeUpdateTree(G.main, ntree); + bNodeSocket *displacement = ntree_shader_node_find_input(output_node, + "Displacement"); + + if (displacement == NULL) { + /* Non-cycles node is used as an output. */ + return false; + } + if (displacement->link != NULL) { + *r_node = displacement->link->fromnode; + *r_socket = displacement->link->fromsock; + *r_link = displacement->link; + } + return displacement->link != NULL; +} + +/* Use specified node and socket as an input for unconnected normal sockets. */ +static void ntree_shader_link_builtin_normal(bNodeTree *ntree, + bNode *node_from, + bNodeSocket *socket_from) +{ + for (bNode *node = ntree->nodes.first; node != NULL; node = node->next) { + if (node == node_from) { + /* Don't connect node itself! */ + continue; + } + bNodeSocket *sock = ntree_shader_node_find_input(node, "Normal"); + /* TODO(sergey): Can we do something smarter here than just a name-based + * matching? + */ + if (sock == NULL) { + /* There's no Normal input, nothing to link. */ + continue; + } + if (sock->link != NULL) { + /* Something is linked to the normal input already. can't + * use other input for that. + */ + continue; + } + /* Create connection between specified node and the normal input. */ + nodeAddLink(ntree, node_from, socket_from, node, sock); + } +} + +/* Re-link displacement output to unconnected normal sockets via bump node. + * This way material with have proper displacement in the viewport. + */ +static void ntree_shader_relink_displacement(bNodeTree *ntree, + short compatibility) +{ + if (compatibility != NODE_NEW_SHADING) { + /* We can only deal with new shading system here. */ + return; + } + bNode *displacement_node; + bNodeSocket *displacement_socket; + bNodeLink *displacement_link; + if (!ntree_shader_has_displacement(ntree, + &displacement_node, + &displacement_socket, + &displacement_link)) + { + /* There is no displacement output connected, nothing to re-link. */ + return; + } + /* We have to disconnect displacement output socket, otherwise we'll have + * cycles in the Cycles material :) + */ + nodeRemLink(ntree, displacement_link); + /* We can't connect displacement to normal directly, use bump node for that + * and hope that it gives good enough approximation. + */ + bNode *bump_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BUMP); + bNodeSocket *bump_input_socket = ntree_shader_node_find_input(bump_node, "Height"); + bNodeSocket *bump_output_socket = ntree_shader_node_find_output(bump_node, "Normal"); + BLI_assert(bump_input_socket != NULL); + BLI_assert(bump_output_socket != NULL); + /* Connect bump node to where displacement output was originally + * connected to. + */ + nodeAddLink(ntree, + displacement_node, displacement_socket, + bump_node, bump_input_socket); + /* Connect all free-standing Normal inputs. */ + ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket); + /* TODO(sergey): Reconnect Geometry Info->Normal sockets to the new + * bump node. + */ + /* We modified the tree, it needs to be updated now. */ + ntreeUpdateTree(G.main, ntree); +} + void ntreeGPUMaterialNodes(bNodeTree *ntree, GPUMaterial *mat, short compatibility) { /* localize tree to create links for reroute and mute */ bNodeTree *localtree = ntreeLocalize(ntree); bNodeTreeExec *exec; + /* Perform all needed modifications on the tree in order to support + * displacement/bump mapping. + */ + ntree_shader_relink_displacement(localtree, compatibility); + exec = ntreeShaderBeginExecTree(localtree); ntreeExecGPUNodes(exec, mat, 1, compatibility); ntreeShaderEndExecTree(exec); 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_attribute.c b/source/blender/nodes/shader/nodes/node_shader_attribute.c index 61bdfb3dfd6..0a69593cf07 100644 --- a/source/blender/nodes/shader/nodes/node_shader_attribute.c +++ b/source/blender/nodes/shader/nodes/node_shader_attribute.c @@ -45,9 +45,9 @@ static void node_shader_init_attribute(bNodeTree *UNUSED(ntree), bNode *node) static int node_shader_gpu_attribute(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { NodeShaderAttribute *attr = node->storage; - GPUNodeLink *mtface = GPU_attribute(CD_MTFACE, attr->name); + GPUNodeLink *cd_attr = GPU_attribute(CD_AUTO_FROM_NAME, attr->name); - return GPU_stack_link(mat, "node_attribute", in, out, mtface); + return GPU_stack_link(mat, "node_attribute", in, out, cd_attr); } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.c b/source/blender/nodes/shader/nodes/node_shader_bump.c index 22027d58385..b39ca5d90ee 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bump.c +++ b/source/blender/nodes/shader/nodes/node_shader_bump.c @@ -45,14 +45,21 @@ static bNodeSocketTemplate sh_node_bump_out[] = { { -1, 0, "" } }; -static int gpu_shader_bump(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) +static int gpu_shader_bump(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { if (!in[3].link) in[3].link = GPU_builtin(GPU_VIEW_NORMAL); else GPU_link(mat, "direction_transform_m4v3", in[3].link, GPU_builtin(GPU_VIEW_MATRIX), &in[3].link); - - return GPU_stack_link(mat, "node_bump", in, out); + float invert = node->custom1; + GPU_stack_link(mat, "node_bump", in, out, GPU_builtin(GPU_VIEW_POSITION), GPU_uniform(&invert)); + /* Other nodes are applying view matrix if the input Normal has a link. + * We don't want normal to have view matrix applied twice, so we cancel it here. + * + * TODO(sergey): This is an extra multiplication which cancels each other, + * better avoid this but that requires bigger refactor. + */ + return GPU_link(mat, "direction_transform_m4v3", out[0].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[0].link); } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_camera.c b/source/blender/nodes/shader/nodes/node_shader_camera.c index 49ebb15d7a4..3bdb5c36d69 100644 --- a/source/blender/nodes/shader/nodes/node_shader_camera.c +++ b/source/blender/nodes/shader/nodes/node_shader_camera.c @@ -54,7 +54,15 @@ static void node_shader_exec_camera(void *data, int UNUSED(thread), bNode *UNUSE static int gpu_shader_camera(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - return GPU_stack_link(mat, "camera", in, out, GPU_builtin(GPU_VIEW_POSITION)); + GPUNodeLink *viewvec; + + viewvec = GPU_builtin(GPU_VIEW_POSITION); + + /* Blender has negative Z, Cycles positive Z convention */ + if (GPU_material_use_new_shading_nodes(mat)) + GPU_link(mat, "invert_z", viewvec, &viewvec); + + return GPU_stack_link(mat, "camera", in, out, viewvec); } void register_node_type_sh_camera(void) diff --git a/source/blender/nodes/shader/nodes/node_shader_lamp.c b/source/blender/nodes/shader/nodes/node_shader_lamp.c index a06536bf1bf..6ff5cb0e8c4 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 4c8db5bff7f..85e2c77662d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal_map.c +++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.c @@ -46,16 +46,149 @@ static void node_shader_init_normal_map(bNodeTree *UNUSED(ntree), bNode *node) node->storage = attr; } +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) { - NodeShaderNormalMap *storage = node->storage; - static const char *space[] = {"node_normal_map_tangent", - "node_normal_map_object", - "node_normal_map_world", - "node_normal_map_blend_object", - "node_normal_map_blend_world"}; - - return GPU_stack_link(mat, space[storage->space], in, out, GPU_builtin(GPU_VIEW_NORMAL), GPU_attribute(CD_TANGENT, ""), GPU_builtin(GPU_VIEW_MATRIX), GPU_builtin(GPU_OBJECT_MATRIX) , GPU_builtin(GPU_INVERSE_VIEW_MATRIX), GPU_builtin(GPU_INVERSE_OBJECT_MATRIX)); + 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) + realnorm = in[1].link; + else + realnorm = GPU_uniform(in[1].vec); + + negnorm = GPU_builtin(GPU_VIEW_NORMAL); + GPU_link(mat, "math_max", strength, GPU_uniform(d), &strength); + + if (GPU_material_use_new_shading_nodes(mat)) { + + /* **************** CYCLES ******************** */ + + switch (nm->space) { + case SHD_NORMAL_MAP_TANGENT: + GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); + GPU_link(mat, "node_normal_map", GPU_attribute(CD_TANGENT, nm->uv_map), negnorm, realnorm, &realnorm); + GPU_link(mat, "vec_math_mix", strength, realnorm, GPU_builtin(GPU_VIEW_NORMAL), &out[0].link); + /* for uniform scale this is sufficient to match Cycles */ + GPU_link(mat, "direction_transform_m4v3", out[0].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[0].link); + GPU_link(mat, "vect_normalize", out[0].link, &out[0].link); + return true; + case SHD_NORMAL_MAP_OBJECT: + GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); + GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); + GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_OBJECT_MATRIX), &realnorm); + break; + case SHD_NORMAL_MAP_BLENDER_OBJECT: + GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); + GPU_link(mat, "color_to_blender_normal_new_shading", realnorm, &realnorm); + GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_OBJECT_MATRIX), &realnorm); + break; + case SHD_NORMAL_MAP_WORLD: + GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); + GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); + break; + case SHD_NORMAL_MAP_BLENDER_WORLD: + GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); + GPU_link(mat, "color_to_blender_normal_new_shading", realnorm, &realnorm); + break; + } + + } else { + + /* *********** BLENDER INTERNAL *************** */ + + GPU_link(mat, "color_to_normal", realnorm, &realnorm); + GPU_link(mat, "mtex_negate_texnormal", realnorm, &realnorm); + GPU_link(mat, "vec_math_negate", negnorm, &negnorm); + + switch (nm->space) { + case SHD_NORMAL_MAP_TANGENT: + GPU_link(mat, "node_normal_map", GPU_attribute(CD_TANGENT, nm->uv_map), negnorm, realnorm, &realnorm); + 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), &realnorm); + break; + case SHD_NORMAL_MAP_WORLD: + case SHD_NORMAL_MAP_BLENDER_WORLD: + GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_VIEW_MATRIX), &realnorm); + break; + } + } + + GPU_link(mat, "vec_math_mix", strength, realnorm, negnorm, &out[0].link); + GPU_link(mat, "vect_normalize", out[0].link, &out[0].link); + + return true; } /* node type definition */ @@ -64,13 +197,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_brick.c b/source/blender/nodes/shader/nodes/node_shader_tex_brick.c index 569eaf5ff9b..bb7f2166a8a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.c @@ -64,12 +64,19 @@ static void node_shader_init_tex_brick(bNodeTree *UNUSED(ntree), bNode *node) static int node_shader_gpu_tex_brick(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - if (!in[0].link) + if (!in[0].link) { in[0].link = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "generated_from_orco", in[0].link, &in[0].link); + } node_shader_gpu_tex_mapping(mat, node, in, out); - - return GPU_stack_link(mat, "node_tex_brick", in, out); + NodeTexBrick *tex = (NodeTexBrick *)node->storage; + float offset_freq = tex->offset_freq; + float squash_freq = tex->squash_freq; + return GPU_stack_link(mat, "node_tex_brick", + in, out, + GPU_uniform(&tex->offset), GPU_uniform(&offset_freq), + GPU_uniform(&tex->squash), GPU_uniform(&squash_freq)); } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.c b/source/blender/nodes/shader/nodes/node_shader_tex_checker.c index b7498df1706..77edd0e115e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.c @@ -54,8 +54,10 @@ static void node_shader_init_tex_checker(bNodeTree *UNUSED(ntree), bNode *node) static int node_shader_gpu_tex_checker(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - if (!in[0].link) + if (!in[0].link) { in[0].link = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "generated_from_orco", in[0].link, &in[0].link); + } node_shader_gpu_tex_mapping(mat, node, in, out); 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 de34d65ae29..4e431374490 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c @@ -60,9 +60,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, bNode *node, bNodeE ImageUser *iuser = NULL; NodeTexEnvironment *tex = node->storage; int isdata = tex->color_space == SHD_COLORSPACE_NONE; - int ret; GPUMatType type = GPU_material_get_type(mat); - GPUBrdfInput *brdf = NULL; if (!ima) return GPU_stack_link(mat, "node_tex_environment_empty", in, out); @@ -77,21 +75,18 @@ 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, true)); + GPU_stack_link(mat, "node_tex_environment_equirectangular", in, out, GPU_image(ima, iuser, isdata, true)); else - ret = GPU_stack_link(mat, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata, true)); - - 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); + GPU_stack_link(mat, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata, false)); + + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + if (!isdata) + { + 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_gradient.c b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.c index 24916e8f013..18a8065fb57 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.c @@ -52,12 +52,16 @@ static void node_shader_init_tex_gradient(bNodeTree *UNUSED(ntree), bNode *node) static int node_shader_gpu_tex_gradient(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - if (!in[0].link) + if (!in[0].link) { in[0].link = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "generated_from_orco", in[0].link, &in[0].link); + } node_shader_gpu_tex_mapping(mat, node, in, out); - return GPU_stack_link(mat, "node_tex_gradient", in, out); + NodeTexGradient *tex = (NodeTexGradient *)node->storage; + float gradient_type = tex->gradient_type; + return GPU_stack_link(mat, "node_tex_gradient", in, out, GPU_uniform(&gradient_type)); } /* 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 9c803c3e90c..5bcad70d88d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -59,32 +59,57 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat Image *ima = (Image *)node->id; ImageUser *iuser = NULL; NodeTexImage *tex = node->storage; + GPUNodeLink *norm; int isdata = tex->color_space == SHD_COLORSPACE_NONE; bool do_clip = tex->extension == SHD_IMAGE_EXTENSION_CLIP; - int ret; + float blend = tex->projection_blend; if (!ima) return GPU_stack_link(mat, "node_tex_image_empty", in, out); - + if (!in[0].link) in[0].link = GPU_attribute(CD_MTFACE, ""); node_shader_gpu_tex_mapping(mat, node, in, out); - ret = GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata, do_clip)); - - 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) - !isdata - ) - { - GPU_link(mat, "srgb_to_linearrgb", out[0].link, &out[0].link); - } - BKE_image_release_ibuf(ima, ibuf, NULL); + switch (tex->projection) { + case SHD_PROJ_FLAT: + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata, do_clip)); + break; + case SHD_PROJ_BOX: + GPU_link(mat, "direction_transform_m4v3", GPU_builtin(GPU_VIEW_NORMAL), + GPU_builtin(GPU_INVERSE_VIEW_MATRIX), + &norm); + GPU_link(mat, "direction_transform_m4v3", norm, + GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), + &norm); + GPU_link(mat, "node_tex_image_box", in[0].link, + norm, + GPU_image(ima, iuser, isdata, do_clip), + GPU_uniform(&blend), + &out[0].link, + &out[1].link); + break; + case SHD_PROJ_SPHERE: + GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); + GPU_link(mat, "point_map_to_sphere", in[0].link, &in[0].link); + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata, do_clip)); + break; + case SHD_PROJ_TUBE: + GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); + GPU_link(mat, "point_map_to_tube", in[0].link, &in[0].link); + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata, do_clip)); + break; + } + + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + if (!isdata) + { + 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_magic.c b/source/blender/nodes/shader/nodes/node_shader_tex_magic.c index 80904e376bc..8700d7954e7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.c @@ -57,8 +57,10 @@ static int node_shader_gpu_tex_magic(GPUMaterial *mat, bNode *node, bNodeExecDat NodeTexMagic *tex = (NodeTexMagic *)node->storage; float depth = tex->depth; - if (!in[0].link) + if (!in[0].link) { in[0].link = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "generated_from_orco", in[0].link, &in[0].link); + } node_shader_gpu_tex_mapping(mat, node, in, out); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c index 825ba7eb3c1..51d6699fadd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c @@ -58,12 +58,17 @@ static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) static int node_shader_gpu_tex_musgrave(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - if (!in[0].link) + if (!in[0].link) { in[0].link = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "generated_from_orco", in[0].link, &in[0].link); + } node_shader_gpu_tex_mapping(mat, node, in, out); - return GPU_stack_link(mat, "node_tex_musgrave", in, out); + NodeTexMusgrave *tex = (NodeTexMusgrave *)node->storage; + float type = tex->musgrave_type; + + return GPU_stack_link(mat, "node_tex_musgrave", in, out, GPU_uniform(&type)); } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_noise.c index e65f55dbd90..91015b3db25 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.c @@ -54,8 +54,10 @@ static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) static int node_shader_gpu_tex_noise(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - if (!in[0].link) - GPU_link(mat, "default_coordinates", GPU_attribute(CD_ORCO, ""), &in[0].link); + if (!in[0].link) { + in[0].link = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "generated_from_orco", in[0].link, &in[0].link); + } node_shader_gpu_tex_mapping(mat, node, in, out); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c index 88596a4a72f..c994798e2da 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c @@ -53,12 +53,17 @@ static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) static int node_shader_gpu_tex_voronoi(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - if (!in[0].link) + if (!in[0].link) { in[0].link = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "generated_from_orco", in[0].link, &in[0].link); + } node_shader_gpu_tex_mapping(mat, node, in, out); - return GPU_stack_link(mat, "node_tex_voronoi", in, out); + NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage; + float coloring = tex->coloring; + + return GPU_stack_link(mat, "node_tex_voronoi", in, out, GPU_uniform(&coloring)); } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c b/source/blender/nodes/shader/nodes/node_shader_tex_wave.c index 100510641e8..1194874e06c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.c @@ -56,12 +56,18 @@ static void node_shader_init_tex_wave(bNodeTree *UNUSED(ntree), bNode *node) static int node_shader_gpu_tex_wave(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - if (!in[0].link) + if (!in[0].link) { in[0].link = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "generated_from_orco", in[0].link, &in[0].link); + } node_shader_gpu_tex_mapping(mat, node, in, out); - return GPU_stack_link(mat, "node_tex_wave", in, out); + NodeTexWave *tex = (NodeTexWave *)node->storage; + float wave_type = tex->wave_type; + float wave_profile = tex->wave_profile; + + return GPU_stack_link(mat, "node_tex_wave", in, out, GPU_uniform(&wave_type), GPU_uniform(&wave_profile)); } /* 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 609a0e5a260..d7f67a97895 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, 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..49b806347d6 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -973,7 +973,9 @@ static PyObject *pyrna_prop_str(BPy_PropertyRNA *self) RNA_property_identifier(self->prop)); } -static PyObject *pyrna_prop_repr(BPy_PropertyRNA *self) +static PyObject *pyrna_prop_repr_ex( + BPy_PropertyRNA *self, + const int index_dim, const int index) { ID *id = self->ptr.id.data; PyObject *tmp_str; @@ -987,7 +989,8 @@ static PyObject *pyrna_prop_repr(BPy_PropertyRNA *self) tmp_str = PyUnicode_FromString(id->name + 2); - path = RNA_path_from_ID_to_property(&self->ptr, self->prop); + path = RNA_path_from_ID_to_property_index(&self->ptr, self->prop, index_dim, index); + if (path) { const char *data_delim = (path[0] == '[') ? "" : "."; if (GS(id->name) == ID_NT) { /* nodetree paths are not accurate */ @@ -1015,6 +1018,15 @@ static PyObject *pyrna_prop_repr(BPy_PropertyRNA *self) return ret; } +static PyObject *pyrna_prop_repr(BPy_PropertyRNA *self) +{ + return pyrna_prop_repr_ex(self, 0, -1); +} + +static PyObject *pyrna_prop_array_repr(BPy_PropertyArrayRNA *self) +{ + return pyrna_prop_repr_ex((BPy_PropertyRNA *)self, self->arraydim, self->arrayoffset); +} static PyObject *pyrna_func_repr(BPy_FunctionRNA *self) { @@ -5841,7 +5853,7 @@ PyTypeObject pyrna_prop_array_Type = { NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */ - NULL, /* subclassed */ /* tp_repr */ + (reprfunc)pyrna_prop_array_repr, /* tp_repr */ /* Method suites for standard classes */ @@ -7363,12 +7375,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 +7403,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/intern/gpu.c b/source/blender/python/intern/gpu.c index 66c1ddcc352..c3bb588f7eb 100644 --- a/source/blender/python/intern/gpu.c +++ b/source/blender/python/intern/gpu.c @@ -261,6 +261,9 @@ static PyObject *GPU_export_shader(PyObject *UNUSED(self), PyObject *args, PyObj if (uniform->lamp) { PY_DICT_ADD_ID(dict, uniform, lamp); } + if (uniform->material) { + PY_DICT_ADD_ID(dict, uniform, material); + } if (uniform->image) { PY_DICT_ADD_ID(dict, uniform, image); } 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__ */ |