diff options
author | Lukas Tönne <lukas.toenne@gmail.com> | 2015-02-10 19:09:00 +0300 |
---|---|---|
committer | Lukas Tönne <lukas.toenne@gmail.com> | 2015-02-10 19:09:00 +0300 |
commit | e89fd502d7b1ee5a4c17286a78ae0116f03510b7 (patch) | |
tree | 5c68b5cb560b3cc5ba62046167d364b2183abb11 /source | |
parent | 6cd3ebfc9fa4838a74192f2e5cc82b5d5d2e6fe7 (diff) | |
parent | 227a94077f508a47fe7595b9091ab86aecde4ad5 (diff) |
Merge branch 'master' into alembic_pointcache
Conflicts:
CMakeLists.txt
build_files/build_environment/install_deps.sh
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/SConscript
source/blender/blenkernel/intern/particle.c
source/blender/blenkernel/intern/pointcache.c
source/blender/editors/physics/particle_edit.c
source/blender/editors/space_outliner/outliner_draw.c
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesrna/SConscript
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/SConscript
source/blender/makesrna/intern/rna_modifier.c
source/blender/makesrna/intern/rna_object_force.c
source/blender/modifiers/MOD_modifiertypes.h
source/blender/modifiers/intern/MOD_util.c
source/blender/render/intern/source/voxeldata.c
Diffstat (limited to 'source')
579 files changed, 23152 insertions, 8969 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index e2bf5930275..52273ce3047 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -103,6 +103,7 @@ add_subdirectory(render) add_subdirectory(blenfont) add_subdirectory(blenloader) add_subdirectory(ikplugin) +add_subdirectory(physics) add_subdirectory(gpu) add_subdirectory(imbuf) add_subdirectory(nodes) diff --git a/source/blender/SConscript b/source/blender/SConscript index 34de8a16cf1..a0ad4576e57 100644 --- a/source/blender/SConscript +++ b/source/blender/SConscript @@ -41,6 +41,7 @@ SConscript(['avi/SConscript', 'nodes/SConscript', 'modifiers/SConscript', 'ikplugin/SConscript', + 'physics/SConscript', 'windowmanager/SConscript', 'blenfont/SConscript', 'pointcache/SConscript']) diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index cdccbe044bb..127826f98c8 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -63,6 +63,9 @@ */ #define BLF_MAX_FONT 16 +/* call BLF_default_set first! */ +#define ASSERT_DEFAULT_SET BLI_assert(global_font_default != -1) + /* Font array. */ static FontBLF *global_font[BLF_MAX_FONT] = {NULL}; @@ -134,7 +137,7 @@ static int blf_search(const char *name) for (i = 0; i < BLF_MAX_FONT; i++) { font = global_font[i]; - if (font && (!strcmp(font->name, name))) + if (font && (STREQ(font->name, name))) return i; } @@ -160,21 +163,6 @@ void BLF_default_set(int fontid) } } -static int blf_global_font_init(void) -{ - if (global_font_default == -1) { - global_font_default = blf_search("default"); - } - - if (global_font_default == -1) { - printf("Warning: Can't find default font!\n"); - return 0; - } - else { - return 1; - } -} - int BLF_load(const char *name) { FontBLF *font; @@ -336,7 +324,7 @@ void BLF_unload(const char *name) for (i = 0; i < BLF_MAX_FONT; i++) { font = global_font[i]; - if (font && (!strcmp(font->name, name))) { + if (font && (STREQ(font->name, name))) { blf_font_free(font); global_font[i] = NULL; } @@ -477,8 +465,7 @@ void BLF_blur(int fontid, int size) void BLF_draw_default(float x, float y, float z, const char *str, size_t len) { - if (!blf_global_font_init()) - return; + ASSERT_DEFAULT_SET; BLF_size(global_font_default, global_font_points, global_font_dpi); BLF_position(global_font_default, x, y, z); @@ -488,8 +475,7 @@ void BLF_draw_default(float x, float y, float z, const char *str, size_t len) /* same as above but call 'BLF_draw_ascii' */ void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len) { - if (!blf_global_font_init()) - return; + ASSERT_DEFAULT_SET; BLF_size(global_font_default, global_font_points, global_font_dpi); BLF_position(global_font_default, x, y, z); @@ -670,10 +656,7 @@ void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_widt void BLF_width_and_height_default(const char *str, size_t len, float *r_width, float *r_height) { - if (!blf_global_font_init()) { - *r_width = *r_height = 0.0f; - return; - } + ASSERT_DEFAULT_SET; BLF_size(global_font_default, global_font_points, global_font_dpi); BLF_width_and_height(global_font_default, str, len, r_width, r_height); @@ -703,8 +686,7 @@ float BLF_fixed_width(int fontid) float BLF_width_default(const char *str, size_t len) { - if (!blf_global_font_init()) - return 0.0f; + ASSERT_DEFAULT_SET; BLF_size(global_font_default, global_font_points, global_font_dpi); return BLF_width(global_font_default, str, len); @@ -767,8 +749,7 @@ float BLF_ascender(int fontid) float BLF_height_default(const char *str, size_t len) { - if (!blf_global_font_init()) - return 0.0f; + ASSERT_DEFAULT_SET; BLF_size(global_font_default, global_font_points, global_font_dpi); diff --git a/source/blender/blenfont/intern/blf_lang.c b/source/blender/blenfont/intern/blf_lang.c index 10614e8ca59..99e1aa5d3e3 100644 --- a/source/blender/blenfont/intern/blf_lang.c +++ b/source/blender/blenfont/intern/blf_lang.c @@ -33,6 +33,10 @@ #include <stdlib.h> #include <string.h> +#ifndef _WIN32 +# include <locale.h> +#endif + #include "RNA_types.h" #include "BLF_translation.h" /* own include */ @@ -189,7 +193,36 @@ void BLF_lang_init(void) { #ifdef WITH_INTERNATIONAL const char * const messagepath = BKE_appdir_folder_id(BLENDER_DATAFILES, "locale"); +#endif + + /* Make sure LANG is correct and wouldn't cause std::rumtime_error. */ +#ifndef _WIN32 + /* TODO(sergey): This code only ensures LANG is set properly, so later when + * Cycles will try to use file system API from boost there'll be no runtime + * exception generated by std::locale() which _requires_ having proper LANG + * set in the environment. + * + * Ideally we also need to ensure LC_ALL, LC_MESSAGES and others are also + * set to a proper value, but currently it's not a huge deal and doesn't + * cause any headache. + * + * Would also be good to find nicer way to check if LANG is correct. + */ + const char *lang = getenv("LANG"); + if (lang != NULL) { + char *old_locale = setlocale(LC_ALL, NULL); + /* Make a copy so subsequenct setlocale() doesn't interfere. */ + old_locale = BLI_strdup(old_locale); + if (setlocale(LC_ALL, lang) == NULL) { + setenv("LANG", "C", 1); + printf("Warning: Falling back to the standard locale (\"C\")\n"); + } + setlocale(LC_ALL, old_locale); + MEM_freeN(old_locale); + } +#endif +#ifdef WITH_INTERNATIONAL if (messagepath) { bl_locale_init(messagepath, TEXT_DOMAIN_NAME); fill_locales(); diff --git a/source/blender/blenfont/intern/blf_translation.c b/source/blender/blenfont/intern/blf_translation.c index e7d2c14b16f..2a4a152f0eb 100644 --- a/source/blender/blenfont/intern/blf_translation.c +++ b/source/blender/blenfont/intern/blf_translation.c @@ -133,7 +133,7 @@ void BLF_free_unifont_mono(void) bool BLF_is_default_context(const char *msgctxt) { /* We use the "short" test, a more complete one could be: - * return (!msgctxt || !msgctxt[0] || !strcmp(msgctxt == BLF_I18NCONTEXT_DEFAULT_BPYRNA)) + * return (!msgctxt || !msgctxt[0] || STREQ(msgctxt, BLF_I18NCONTEXT_DEFAULT_BPYRNA)) */ /* Note: trying without the void string check for now, it *should* not be necessary... */ return (!msgctxt || msgctxt[0] == BLF_I18NCONTEXT_DEFAULT_BPYRNA[0]); diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index da4fcb4eca4..04251641d9b 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -88,6 +88,7 @@ struct MTFace; struct Object; struct Scene; struct Mesh; +struct MLoopNorSpaceArray; struct BMEditMesh; struct KeyBlock; struct ModifierData; @@ -197,7 +198,11 @@ struct DerivedMesh { void (*calcNormals)(DerivedMesh *dm); /** Calculate loop (split) normals */ - void (*calcLoopNormals)(DerivedMesh *dm, const float split_angle); + void (*calcLoopNormals)(DerivedMesh *dm, const bool use_split_normals, const float split_angle); + + /** Calculate loop (split) normals, and returns split loop normal spacearr. */ + void (*calcLoopNormalsSpaceArray)(DerivedMesh *dm, const bool use_split_normals, const float split_angle, + struct MLoopNorSpaceArray *r_lnors_spacearr); /** Recalculates mesh tessellation */ void (*recalcTessellation)(DerivedMesh *dm); @@ -746,6 +751,8 @@ typedef struct DMVertexAttribs { void DM_vertex_attributes_from_gpu(DerivedMesh *dm, struct GPUVertexAttribs *gattribs, DMVertexAttribs *attribs); +void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert); + void DM_add_tangent_layer(DerivedMesh *dm); void DM_calc_auto_bump_scale(DerivedMesh *dm); diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index a5109acfa56..0acfd40a110 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -73,6 +73,20 @@ bool BKE_copy_animdata_id(struct ID *id_to, struct ID *id_from, const bool do_ac /* Copy AnimData Actions */ void BKE_copy_animdata_id_action(struct ID *id); +/* Merge copies of data from source AnimData block */ +typedef enum eAnimData_MergeCopy_Modes { + /* Keep destination action */ + ADT_MERGECOPY_KEEP_DST = 0, + + /* Use src action (make a new copy) */ + ADT_MERGECOPY_SRC_COPY = 1, + + /* Use src action (but just reference the existing version) */ + ADT_MERGECOPY_SRC_REF = 2 +} eAnimData_MergeCopy_Modes; + +void BKE_animdata_merge_copy(struct ID *dst_id, struct ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers); + /* Make Local */ void BKE_animdata_make_local(struct AnimData *adt); @@ -106,6 +120,10 @@ void BKE_keyingsets_free(struct ListBase *list); /* ************************************* */ /* Path Fixing API */ +/* Get a "fixed" version of the given path (oldPath) */ +char *BKE_animsys_fix_rna_path_rename(ID *owner_id, char *old_path, const char *prefix, const char *oldName, + const char *newName, int oldSubscript, int newSubscript, bool verify_paths); + /* Fix all the paths for the given ID + Action */ void BKE_action_fix_paths_rename(struct ID *owner_id, struct bAction *act, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths); diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 0215becd2a0..2c53a247f6d 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -42,7 +42,7 @@ extern "C" { * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 273 -#define BLENDER_SUBVERSION 2 +#define BLENDER_SUBVERSION 6 /* 262 was the last editmesh release but it has compatibility code for bmesh data */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 5 @@ -88,7 +88,7 @@ void BKE_userdef_free(void); void BKE_userdef_state(void); /* set this callback when a UI is running */ -void set_blender_test_break_cb(void (*func)(void) ); +void set_blender_test_break_cb(void (*func)(void)); int blender_test_break(void); #define BKE_UNDO_STR_MAX 64 diff --git a/source/blender/blenkernel/BKE_cdderivedmesh.h b/source/blender/blenkernel/BKE_cdderivedmesh.h index b0ade7bacdf..59ec316a403 100644 --- a/source/blender/blenkernel/BKE_cdderivedmesh.h +++ b/source/blender/blenkernel/BKE_cdderivedmesh.h @@ -40,6 +40,7 @@ struct DerivedMesh; struct BMEditMesh; struct Mesh; +struct MLoopNorSpaceArray; struct Object; /* creates a new CDDerivedMesh */ @@ -106,7 +107,9 @@ void CDDM_calc_normals_mapping(struct DerivedMesh *dm); void CDDM_calc_normals(struct DerivedMesh *dm); void CDDM_calc_normals_tessface(struct DerivedMesh *dm); -void CDDM_calc_loop_normals(struct DerivedMesh *dm, const float split_angle); +void CDDM_calc_loop_normals(struct DerivedMesh *dm, const bool use_split_normals, const float split_angle); +void CDDM_calc_loop_normals_spacearr(struct DerivedMesh *dm, const bool use_split_normals, const float split_angle, + struct MLoopNorSpaceArray *r_lnors_spacearr); /* calculates edges for a CDDerivedMesh (from face data) * this completely replaces the current edge data in the DerivedMesh diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index 757d63e6ec1..beb4f226aea 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -41,7 +41,10 @@ struct Scene; struct MFace; struct DerivedMesh; struct ClothModifierData; +struct CollisionModifierData; struct CollisionTree; +struct VoxelData; +struct PartDeflect; #define DO_INLINE MALWAYS_INLINE @@ -53,8 +56,26 @@ struct CollisionTree; #define ALMOST_ZERO FLT_EPSILON /* Bits to or into the ClothVertex.flags. */ -#define CLOTH_VERT_FLAG_PINNED 1 -#define CLOTH_VERT_FLAG_NOSELFCOLL 2 /* vertex NOT used for self collisions */ +typedef enum eClothVertexFlag { + CLOTH_VERT_FLAG_PINNED = 1, + CLOTH_VERT_FLAG_NOSELFCOLL = 2, /* vertex NOT used for self collisions */ +} eClothVertexFlag; + +typedef struct ClothHairData { + float loc[3]; + float rot[3][3]; + float rest_target[3]; /* rest target direction for each segment */ + float radius; + float bending_stiffness; +} ClothHairData; + +typedef struct ClothSolverResult { + int status; + + int max_iterations, min_iterations; + float avg_iterations; + float max_error, min_error, avg_error; +} ClothSolverResult; /** * This structure describes a cloth object against which the @@ -79,7 +100,6 @@ typedef struct Cloth { struct BVHTree *bvhselftree; /* collision tree for this cloth object */ struct MFace *mfaces; struct Implicit_Data *implicit; /* our implicit solver connects to this pointer */ - struct Implicit_Data *implicitEM; /* our implicit solver connects to this pointer */ struct EdgeSet *edgeset; /* used for selfcollisions */ int last_frame, pad4; } Cloth; @@ -116,8 +136,8 @@ ClothVertex; typedef struct ClothSpring { int ij; /* Pij from the paper, one end of the spring. */ int kl; /* Pkl from the paper, one end of the spring. */ + int mn; float restlen; /* The original length of the spring. */ - int matrix_index; /* needed for implicit solver (fast lookup) */ int type; /* types defined in BKE_cloth.h ("springType") */ int flags; /* defined in BKE_cloth.h, e.g. deactivated due to tearing */ float dfdx[3][3]; @@ -125,6 +145,9 @@ typedef struct ClothSpring { float f[3]; float stiffness; /* stiffness factor from the vertex groups */ float editrestlen; + + /* angular bending spring target and derivatives */ + float target[3]; } ClothSpring; @@ -158,6 +181,7 @@ typedef enum { typedef enum { CLOTH_COLLSETTINGS_FLAG_ENABLED = ( 1 << 1 ), /* enables cloth - object collisions */ CLOTH_COLLSETTINGS_FLAG_SELF = ( 1 << 2 ), /* enables selfcollisions */ + CLOTH_COLLSETTINGS_FLAG_POINTS = ( 1 << 3 ), /* enables point collisions (hair) */ } CLOTH_COLLISIONSETTINGS_FLAGS; /* Spring types as defined in the paper.*/ @@ -166,7 +190,8 @@ typedef enum { CLOTH_SPRING_TYPE_SHEAR = (1 << 2), CLOTH_SPRING_TYPE_BENDING = (1 << 3), CLOTH_SPRING_TYPE_GOAL = (1 << 4), - CLOTH_SPRING_TYPE_SEWING = (1 << 5) + CLOTH_SPRING_TYPE_SEWING = (1 << 5), + CLOTH_SPRING_TYPE_BENDING_ANG = (1 << 6), } CLOTH_SPRING_TYPES; /* SPRING FLAGS */ @@ -180,21 +205,25 @@ typedef enum { // collision.c //////////////////////////////////////////////// +struct CollPair; + +typedef struct ColliderContacts { + struct Object *ob; + struct CollisionModifierData *collmd; + + struct CollPair *collisions; + int totcollisions; +} ColliderContacts; + // needed for implicit.c int cloth_bvh_objcollision (struct Object *ob, struct ClothModifierData *clmd, float step, float dt ); +int cloth_points_objcollision(struct Object *ob, struct ClothModifierData *clmd, float step, float dt); -//////////////////////////////////////////////// - +void cloth_find_point_contacts(struct Object *ob, struct ClothModifierData *clmd, float step, float dt, + ColliderContacts **r_collider_contacts, int *r_totcolliders); +void cloth_free_contacts(ColliderContacts *collider_contacts, int totcolliders); //////////////////////////////////////////////// -// implicit.c -//////////////////////////////////////////////// - -// needed for cloth.c -int implicit_init (struct Object *ob, struct ClothModifierData *clmd ); -int implicit_free (struct ClothModifierData *clmd ); -int implicit_solver (struct Object *ob, float frame, struct ClothModifierData *clmd, struct ListBase *effectors ); -void implicit_set_positions (struct ClothModifierData *clmd ); ///////////////////////////////////////////////// // cloth.c @@ -218,27 +247,9 @@ void cloth_clear_cache (struct Object *ob, struct ClothModifierData *clmd, float // needed for cloth.c int cloth_add_spring (struct ClothModifierData *clmd, unsigned int indexA, unsigned int indexB, float restlength, int spring_type); -//////////////////////////////////////////////// - - -/* This enum provides the IDs for our solvers. */ -// only one available in the moment -typedef enum { - CM_IMPLICIT = 0, -} CM_SOLVER_ID; - - -/* This structure defines how to call the solver. - */ -typedef struct { - const char *name; - CM_SOLVER_ID id; - int ( *init ) (struct Object *ob, struct ClothModifierData *clmd ); - int ( *solver ) (struct Object *ob, float framenr, struct ClothModifierData *clmd, struct ListBase *effectors ); - int ( *free ) (struct ClothModifierData *clmd ); -} -CM_SOLVER_DEF; +void cloth_parallel_transport_hair_frame(float mat[3][3], const float dir_old[3], const float dir_new[3]); +//////////////////////////////////////////////// #endif diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h index ec257a2f394..b81b8f04817 100644 --- a/source/blender/blenkernel/BKE_collision.h +++ b/source/blender/blenkernel/BKE_collision.h @@ -81,6 +81,8 @@ typedef struct CollPair { float pa[3], pb[3]; // collision point p1 on face1, p2 on face2 int flag; float time; // collision time, from 0 up to 1 + + /* mesh-mesh collision */ #ifdef WITH_ELTOPO /*either ap* or bp* can be set, but not both*/ float bary[3]; int ap1, ap2, ap3, collp, bp1, bp2, bp3; @@ -135,6 +137,8 @@ void bvhtree_update_from_mvert(BVHTree *bvhtree, struct MFace *faces, int numfac // defined in collisions.c void collision_move_object(struct CollisionModifierData *collmd, float step, float prevstep); +void collision_get_collider_velocity(float vel_old[3], float vel_new[3], struct CollisionModifierData *collmd, struct CollPair *collpair); + ///////////////////////////////////////////////// // used in effect.c ///////////////////////////////////////////////// diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index 13a1468aee8..60cbf8b302e 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -176,6 +176,7 @@ void BKE_nurb_bezt_calc_plane(struct Nurb *nu, struct BezTriple *bezt, float r_p void BKE_nurb_handle_calc(struct BezTriple *bezt, struct BezTriple *prev, struct BezTriple *next, const bool is_fcurve); void BKE_nurb_handle_calc_simple(struct Nurb *nu, struct BezTriple *bezt); +void BKE_nurb_handle_calc_simple_auto(struct Nurb *nu, struct BezTriple *bezt); void BKE_nurb_handles_calc(struct Nurb *nu); void BKE_nurb_handles_autocalc(struct Nurb *nu, int flag); diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index f11aad27e4b..f3f1e0a407e 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -397,6 +397,8 @@ enum { CD_FAKE_BWEIGHT = CD_FAKE | CD_BWEIGHT, /* *sigh*. */ CD_FAKE_UV = CD_FAKE | CD_MLOOPUV, /* UV flag, because we handle both loop's UVs and poly's textures. */ + CD_FAKE_LNOR = CD_FAKE | CD_CUSTOMLOOPNORMAL, /* Because we play with clnor and temp lnor layers here. */ + CD_FAKE_SHARP = CD_FAKE | 200, /* Sharp flag for edges, smooth flag for faces. */ }; diff --git a/source/blender/blenkernel/BKE_customdata_file.h b/source/blender/blenkernel/BKE_customdata_file.h index 978db8a6c1a..242897f968f 100644 --- a/source/blender/blenkernel/BKE_customdata_file.h +++ b/source/blender/blenkernel/BKE_customdata_file.h @@ -40,14 +40,14 @@ void cdf_free(CDataFile *cdf); /* File read/write/remove */ -int cdf_read_open(CDataFile *cdf, const char *filename); -int cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay); -int cdf_read_data(CDataFile *cdf, unsigned int size, void *data); +bool cdf_read_open(CDataFile *cdf, const char *filename); +bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay); +bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data); void cdf_read_close(CDataFile *cdf); -int cdf_write_open(CDataFile *cdf, const char *filename); -int cdf_write_layer(CDataFile *cdf, CDataFileLayer *blay); -int cdf_write_data(CDataFile *cdf, unsigned int size, void *data); +bool cdf_write_open(CDataFile *cdf, const char *filename); +bool cdf_write_layer(CDataFile *cdf, CDataFileLayer *blay); +bool cdf_write_data(CDataFile *cdf, unsigned int size, void *data); void cdf_write_close(CDataFile *cdf); void cdf_remove(const char *filename); diff --git a/source/blender/blenkernel/BKE_data_transfer.h b/source/blender/blenkernel/BKE_data_transfer.h index d31804b3cae..cea093adca4 100644 --- a/source/blender/blenkernel/BKE_data_transfer.h +++ b/source/blender/blenkernel/BKE_data_transfer.h @@ -57,6 +57,7 @@ enum { DT_TYPE_FREESTYLE_EDGE = 1 << 12, DT_TYPE_VCOL = 1 << 16, + DT_TYPE_LNOR = 1 << 17, DT_TYPE_UV = 1 << 24, DT_TYPE_SHARP_FACE = 1 << 25, @@ -67,7 +68,7 @@ enum { DT_TYPE_VERT_ALL = DT_TYPE_MDEFORMVERT | DT_TYPE_SHAPEKEY | DT_TYPE_SKIN | DT_TYPE_BWEIGHT_VERT, DT_TYPE_EDGE_ALL = DT_TYPE_SHARP_EDGE | DT_TYPE_SEAM | DT_TYPE_CREASE | DT_TYPE_BWEIGHT_EDGE | DT_TYPE_FREESTYLE_EDGE, - DT_TYPE_LOOP_ALL = DT_TYPE_VCOL | DT_TYPE_UV, + DT_TYPE_LOOP_ALL = DT_TYPE_VCOL | DT_TYPE_LNOR | DT_TYPE_UV, DT_TYPE_POLY_ALL = DT_TYPE_UV | DT_TYPE_SHARP_FACE | DT_TYPE_FREESTYLE_FACE, }; @@ -85,7 +86,7 @@ int BKE_object_data_transfer_dttype_to_srcdst_index(const int dtdata_type); #define DT_DATATYPE_IS_EDGE(_dt) \ ELEM(_dt, DT_TYPE_CREASE, DT_TYPE_SHARP_EDGE, DT_TYPE_SEAM, DT_TYPE_BWEIGHT_EDGE, DT_TYPE_FREESTYLE_EDGE) #define DT_DATATYPE_IS_LOOP(_dt) \ - ELEM(_dt, DT_TYPE_UV, DT_TYPE_VCOL) + ELEM(_dt, DT_TYPE_UV, DT_TYPE_VCOL, DT_TYPE_LNOR) #define DT_DATATYPE_IS_POLY(_dt) \ ELEM(_dt, DT_TYPE_UV, DT_TYPE_SHARP_FACE, DT_TYPE_FREESTYLE_FACE) diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h index d5e54d849cd..c4c27e1060d 100644 --- a/source/blender/blenkernel/BKE_effect.h +++ b/source/blender/blenkernel/BKE_effect.h @@ -32,8 +32,11 @@ * \since March 2001 * \author nzc */ + #include "DNA_modifier_types.h" +#include "BLI_utildefines.h" + struct Object; struct Scene; struct Effect; @@ -136,6 +139,88 @@ int get_effector_data(struct EffectorCache *eff, struct EffectorData *efd, struc /* EffectorData->flag */ #define PE_VELOCITY_TO_IMPULSE 1 +/* ======== Simulation Debugging ======== */ + +#define SIM_DEBUG_HASH_BASE 5381 + +unsigned int BKE_sim_debug_data_hash(int i); +unsigned int BKE_sim_debug_data_hash_combine(unsigned int kx, unsigned int ky); + +/* _VA_SIM_DEBUG_HASH#(i, ...): combined hash value of multiple integers */ +/* internal helpers*/ +#define _VA_SIM_DEBUG_HASH1(a) \ + (BKE_sim_debug_data_hash(a)) +#define _VA_SIM_DEBUG_HASH2(a, b) \ + (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH1(b))) +#define _VA_SIM_DEBUG_HASH3(a, b, c) \ + (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH2(b, c))) +#define _VA_SIM_DEBUG_HASH4(a, b, c, d) \ + (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH3(b, c, d))) +#define _VA_SIM_DEBUG_HASH5(a, b, c, d, e) \ + (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH4(b, c, d, e))) +#define _VA_SIM_DEBUG_HASH6(a, b, c, d, e, f) \ + (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH5(b, c, d, e, f))) +#define _VA_SIM_DEBUG_HASH7(a, b, c, d, e, f, g) \ + (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH6(b, c, d, e, f, g))) +#define _VA_SIM_DEBUG_HASH8(a, b, c, d, e, f, g, h) \ + (BKE_sim_debug_data_hash_combine(BKE_sim_debug_data_hash(a), _VA_SIM_DEBUG_HASH7(b, c, d, e, f, g, h))) + +#define SIM_DEBUG_HASH(...) VA_NARGS_CALL_OVERLOAD(_VA_SIM_DEBUG_HASH, __VA_ARGS__) + +typedef struct SimDebugElement { + unsigned int category_hash; + unsigned int hash; + + int type; + float color[3]; + + float v1[3], v2[3]; +} SimDebugElement; + +typedef enum eSimDebugElement_Type { + SIM_DEBUG_ELEM_DOT, + SIM_DEBUG_ELEM_CIRCLE, + SIM_DEBUG_ELEM_LINE, + SIM_DEBUG_ELEM_VECTOR, +} eSimDebugElement_Type; + +typedef struct SimDebugData { + struct GHash *gh; +} SimDebugData; + +extern SimDebugData *_sim_debug_data; + +void BKE_sim_debug_data_set_enabled(bool enable); +bool BKE_sim_debug_data_get_enabled(void); +void BKE_sim_debug_data_free(void); + +void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[3], + float r, float g, float b, const char *category, unsigned int hash); +void BKE_sim_debug_data_remove_element(unsigned int hash); + +#define BKE_sim_debug_data_add_dot(p, r, g, b, category, ...) { \ + const float v2[3] = { 0.0f, 0.0f, 0.0f }; \ + BKE_sim_debug_data_add_element(SIM_DEBUG_ELEM_DOT, p, v2, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \ +} + +#define BKE_sim_debug_data_add_circle(p, radius, r, g, b, category, ...) { \ + const float v2[3] = { radius, 0.0f, 0.0f }; \ + BKE_sim_debug_data_add_element(SIM_DEBUG_ELEM_CIRCLE, p, v2, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \ +} + +#define BKE_sim_debug_data_add_line(p1, p2, r, g, b, category, ...) { \ + BKE_sim_debug_data_add_element(SIM_DEBUG_ELEM_LINE, p1, p2, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \ +} + +#define BKE_sim_debug_data_add_vector(p, d, r, g, b, category, ...) { \ + BKE_sim_debug_data_add_element(SIM_DEBUG_ELEM_VECTOR, p, d, r, g, b, category, SIM_DEBUG_HASH(__VA_ARGS__)); \ +} + +#define BKE_sim_debug_data_remove(...) \ + BKE_sim_debug_data_remove_element(SIM_DEBUG_HASH(__VA_ARGS__)) + +void BKE_sim_debug_data_clear(void); +void BKE_sim_debug_data_clear_category(const char *category); #endif diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index a7aa4c6969a..57003ffc3aa 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -70,7 +70,6 @@ typedef struct Global { bool factory_startup; short moving; - short winpos, displaymode; /* used to be in Render */ /* to indicate render is busy, prevent renderwindow events etc */ bool is_rendering; @@ -90,9 +89,6 @@ typedef struct Global { /* this variable is written to / read from FileGlobal->fileflags */ int fileflags; - /* save the allowed windowstate of blender when using -W or -w (GHOST_TWindowState) */ - int windowstate; - /* message to use when autoexec fails */ char autoexec_fail[200]; } Global; @@ -130,6 +126,7 @@ enum { G_DEBUG_JOBS = (1 << 6), /* jobs time profiling */ G_DEBUG_FREESTYLE = (1 << 7), /* freestyle messages */ G_DEBUG_DEPSGRAPH = (1 << 8), /* depsgraph messages */ + G_DEBUG_SIMDATA = (1 << 9), /* sim debug data display */ }; #define G_DEBUG_ALL (G_DEBUG | G_DEBUG_FFMPEG | G_DEBUG_PYTHON | G_DEBUG_EVENTS | G_DEBUG_WM | G_DEBUG_JOBS | \ diff --git a/source/blender/blenkernel/BKE_group.h b/source/blender/blenkernel/BKE_group.h index f528fe8c7f9..820e1ea1494 100644 --- a/source/blender/blenkernel/BKE_group.h +++ b/source/blender/blenkernel/BKE_group.h @@ -50,6 +50,7 @@ bool BKE_group_object_add(struct Group *group, struct Object *ob, struc bool BKE_group_object_unlink(struct Group *group, struct Object *ob, struct Scene *scene, struct Base *base); struct Group *BKE_group_object_find(struct Group *group, struct Object *ob); bool BKE_group_object_exists(struct Group *group, struct Object *ob); +bool BKE_group_object_cyclic_check(struct Main *bmain, struct Object *object, struct Group *group); bool BKE_group_is_animated(struct Group *group, struct Object *parent); void BKE_group_tag_recalc(struct Group *group); diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index a225a27a00b..2c9ecef4c2d 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -57,19 +57,25 @@ void BKE_image_free_buffers(struct Image *image); void BKE_image_free(struct Image *image); void BKE_imbuf_stamp_info(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf); -void BKE_stamp_buf(struct Scene *scene, struct Object *camera, unsigned char *rect, float *rectf, int width, int height, int channels); +void BKE_image_stamp_buf( + struct Scene *scene, struct Object *camera, + unsigned char *rect, float *rectf, int width, int height, int channels); bool BKE_imbuf_alpha_test(struct ImBuf *ibuf); int BKE_imbuf_write_stamp(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf); int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf); int BKE_imbuf_write_as(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf, const bool is_copy); -void BKE_makepicstring(char *string, const char *base, const char *relbase, int frame, - const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames); -void BKE_makepicstring_from_type(char *string, const char *base, const char *relbase, int frame, - const char imtype, const bool use_ext, const bool use_frames); -int BKE_add_image_extension(char *string, const struct ImageFormatData *im_format); -int BKE_add_image_extension_from_type(char *string, const char imtype); -char BKE_ftype_to_imtype(const int ftype); -int BKE_imtype_to_ftype(const char imtype); + +void BKE_image_path_from_imformat( + char *string, const char *base, const char *relbase, int frame, + const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames); +void BKE_image_path_from_imtype( + char *string, const char *base, const char *relbase, int frame, + const char imtype, const bool use_ext, const bool use_frames); + +bool BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); +bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype); +char BKE_image_ftype_to_imtype(const int ftype); +int BKE_image_imtype_to_ftype(const char imtype); bool BKE_imtype_is_movie(const char imtype); int BKE_imtype_supports_zbuf(const char imtype); @@ -85,13 +91,13 @@ void BKE_imformat_defaults(struct ImageFormatData *im_format); void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const struct ImBuf *imbuf); struct anim *openanim(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]); +struct anim *openanim_noload(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]); void BKE_image_de_interlace(struct Image *ima, int odd); void BKE_image_make_local(struct Image *ima); void BKE_image_tag_time(struct Image *ima); -void free_old_images(void); /* ********************************** NEW IMAGE API *********************** */ @@ -180,9 +186,6 @@ void BKE_image_walk_all_users(const struct Main *mainp, void *customdata, /* ensures an Image exists for viewing nodes or render */ struct Image *BKE_image_verify_viewer(int type, const char *name); -/* force an ImBuf to become part of Image */ -void BKE_image_assign_ibuf(struct Image *ima, struct ImBuf *ibuf); - /* called on frame change or before render */ void BKE_image_user_frame_calc(struct ImageUser *iuser, int cfra, int fieldnr); void BKE_image_user_check_frame_calc(struct ImageUser *iuser, int cfra, int fieldnr); diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 500c2813a75..e88a4e88209 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -107,8 +107,8 @@ void BKE_library_make_local(struct Main *bmain, struct Library *lib, bool untagg struct ID *BKE_libblock_find_name_ex(struct Main *bmain, const short type, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); struct ID *BKE_libblock_find_name(const short type, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -void set_free_windowmanager_cb(void (*func)(struct bContext *, struct wmWindowManager *) ); -void set_free_notifier_reference_cb(void (*func)(const void *) ); +void set_free_windowmanager_cb(void (*func)(struct bContext *, struct wmWindowManager *)); +void set_free_notifier_reference_cb(void (*func)(const void *)); /* use when "" is given to new_id() */ #define ID_FALLBACK_NAME N_("Untitled") diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index b2b9e37f500..224be0f3685 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -36,6 +36,9 @@ struct BoundBox; struct DispList; struct EdgeHash; struct ListBase; +struct LinkNode; +struct BLI_Stack; +struct MemArena; struct BMEditMesh; struct BMesh; struct Main; @@ -174,10 +177,6 @@ void BKE_mesh_calc_normals_tessface( struct MVert *mverts, int numVerts, struct MFace *mfaces, int numFaces, float (*r_faceNors)[3]); -void BKE_mesh_normals_loop_split( - struct MVert *mverts, const int numVerts, struct MEdge *medges, const int numEdges, - struct MLoop *mloops, float (*r_loopnors)[3], const int numLoops, - struct MPoly *mpolys, float (*polynors)[3], const int numPolys, float split_angle); void BKE_mesh_loop_tangents_ex( struct MVert *mverts, const int numVerts, struct MLoop *mloops, float (*r_looptangent)[4], float (*loopnors)[3], struct MLoopUV *loopuv, const int numLoops, struct MPoly *mpolys, const int numPolys, @@ -185,6 +184,56 @@ void BKE_mesh_loop_tangents_ex( void BKE_mesh_loop_tangents( struct Mesh *mesh, const char *uvmap, float (*r_looptangents)[4], struct ReportList *reports); +/** + * References a contiguous loop-fan with normal offset vars. + */ +typedef struct MLoopNorSpace { + float vec_lnor[3]; /* Automatically computed loop normal. */ + float vec_ref[3]; /* Reference vector, orthogonal to vec_lnor. */ + float vec_ortho[3]; /* Third vector, orthogonal to vec_lnor and vec_ref. */ + float ref_alpha; /* Reference angle, around vec_ortho, in ]0, pi] range (0.0 marks that space as invalid). */ + float ref_beta; /* Reference angle, around vec_lnor, in ]0, 2pi] range (0.0 marks that space as invalid). */ + struct LinkNode *loops; /* All indices (uint_in_ptr) of loops using this lnor space (i.e. smooth fan of loops). */ +} MLoopNorSpace; +/** + * Collection of #MLoopNorSpace basic storage & pre-allocation. + */ +typedef struct MLoopNorSpaceArray { + MLoopNorSpace **lspacearr; /* MLoop aligned array */ + struct LinkNode *loops_pool; /* Allocated once, avoids to call BLI_linklist_prepend_arena() for each loop! */ + struct MemArena *mem; +} MLoopNorSpaceArray; +void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, const int numLoops); +void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr); +void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr); +MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr); +void BKE_lnor_space_define( + MLoopNorSpace *lnor_space, const float lnor[3], float vec_ref[3], float vec_other[3], + struct BLI_Stack *edge_vectors); +void BKE_lnor_space_add_loop( + MLoopNorSpaceArray *lnors_spacearr, MLoopNorSpace *lnor_space, const int ml_index, const bool add_to_list); +void BKE_lnor_space_custom_data_to_normal(MLoopNorSpace *lnor_space, const short clnor_data[2], float r_custom_lnor[3]); +void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2]); + +bool BKE_mesh_has_custom_loop_normals(struct Mesh *me); + +void BKE_mesh_normals_loop_split(struct MVert *mverts, const int numVerts, struct MEdge *medges, const int numEdges, + struct MLoop *mloops, float (*r_loopnors)[3], const int numLoops, + struct MPoly *mpolys, const float (*polynors)[3], const int numPolys, + const bool use_split_normals, float split_angle, + MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], int *r_loop_to_poly); + +void BKE_mesh_normals_loop_custom_set( + struct MVert *mverts, const int numVerts, struct MEdge *medges, const int numEdges, + struct MLoop *mloops, float (*custom_loopnors)[3], const int numLoops, + struct MPoly *mpolys, const float (*polynors)[3], const int numPolys, + short (*r_clnors_data)[2]); +void BKE_mesh_normals_loop_custom_from_vertices_set( + struct MVert *mverts, float (*custom_vertnors)[3], const int numVerts, + struct MEdge *medges, const int numEdges, struct MLoop *mloops, const int numLoops, + struct MPoly *mpolys, const float (*polynors)[3], const int numPolys, + short (*r_clnors_data)[2]); + void BKE_mesh_calc_poly_normal( struct MPoly *mpoly, struct MLoop *loopstart, struct MVert *mvarray, float no[3]); @@ -286,7 +335,7 @@ void BKE_mesh_calc_relative_deform( /* *** mesh_validate.c *** */ -int BKE_mesh_validate(struct Mesh *me, const int do_verbose); +int BKE_mesh_validate(struct Mesh *me, const int do_verbose, const int cddata_check_mask); void BKE_mesh_cd_validate(struct Mesh *me); int BKE_mesh_validate_material_indices(struct Mesh *me); diff --git a/source/blender/blenkernel/BKE_mesh_remap.h b/source/blender/blenkernel/BKE_mesh_remap.h index e1f37a63ff0..752270a8120 100644 --- a/source/blender/blenkernel/BKE_mesh_remap.h +++ b/source/blender/blenkernel/BKE_mesh_remap.h @@ -158,8 +158,8 @@ void BKE_mesh_remap_calc_loops_from_dm( struct MVert *verts_dst, const int numverts_dst, struct MEdge *edges_dst, const int numedges_dst, struct MLoop *loops_dst, const int numloops_dst, struct MPoly *polys_dst, const int numpolys_dst, struct CustomData *ldata_dst, struct CustomData *pdata_dst, - const float split_angle_dst, const bool dirty_nors_dst, - struct DerivedMesh *dm_src, + const bool use_split_nors_dst, const float split_angle_dst, const bool dirty_nors_dst, + struct DerivedMesh *dm_src, const bool use_split_nors_src, const float split_angle_src, MeshRemapIslandsCalc gen_islands_src, const float islands_precision_src, struct MeshPairRemap *r_map); void BKE_mesh_remap_calc_polys_from_dm( diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 1bdbc339f21..0ea460d1a60 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -57,6 +57,7 @@ void BKE_object_workob_calc_parent(struct Scene *scene, struct Object *ob, struc void BKE_object_transform_copy(struct Object *ob_tar, const struct Object *ob_src); struct SoftBody *copy_softbody(struct SoftBody *sb, bool copy_caches); struct BulletSoftBody *copy_bulletsoftbody(struct BulletSoftBody *sb); +struct ParticleSystem *BKE_object_copy_particlesystem(struct ParticleSystem *psys); void BKE_object_copy_particlesystems(struct Object *obn, struct Object *ob); void BKE_object_copy_softbody(struct Object *obn, struct Object *ob); void BKE_object_free_particlesystems(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index e56add721eb..a769ce94c90 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -39,6 +39,8 @@ #include "DNA_particle_types.h" #include "DNA_object_types.h" +#include "BKE_customdata.h" + struct ParticleSystemModifierData; struct ParticleSystem; struct ParticleKey; @@ -110,7 +112,7 @@ typedef struct ParticleTexture { float ivel; /* used in reset */ float time, life, exist, size; /* used in init */ float damp, gravity, field; /* used in physics */ - float length, clump, kink, effector; /* used in path caching */ + float length, clump, kink_freq, kink_amp, effector; /* used in path caching */ float rough1, rough2, roughe; /* used in path caching */ } ParticleTexture; @@ -126,7 +128,7 @@ typedef struct ParticleCacheKey { float rot[4]; float col[3]; float time; - int steps; + int segments; } ParticleCacheKey; typedef struct ParticleThreadContext { @@ -145,12 +147,12 @@ typedef struct ParticleThreadContext { float maxweight; int *index, *skip, jitlevel; - int from, cfrom, distr; + int cfrom, distr; struct ParticleData *tpars; /* path caching */ - int editupdate, between, steps; + int editupdate, between, segments, extra_segments; int totchild, totparent, parent_pass; float cfra; @@ -160,11 +162,11 @@ typedef struct ParticleThreadContext { float *vg_effector; } ParticleThreadContext; -typedef struct ParticleThread { +typedef struct ParticleTask { ParticleThreadContext *ctx; struct RNG *rng, *rng_path; - int num, tot; -} ParticleThread; + int begin, end; +} ParticleTask; typedef struct ParticleBillboardData { struct Object *ob; @@ -279,6 +281,9 @@ BLI_INLINE void psys_frand_vec(ParticleSystem *psys, unsigned int seed, float ve int count_particles(struct ParticleSystem *psys); int count_particles_mod(struct ParticleSystem *psys, int totgr, int cur); +int psys_get_child_number(struct Scene *scene, struct ParticleSystem *psys); +int psys_get_tot_child(struct Scene *scene, struct ParticleSystem *psys); + struct ParticleSystem *psys_get_current(struct Object *ob); /* for rna */ short psys_get_current_num(struct Object *ob); @@ -302,7 +307,6 @@ void psys_free(struct Object *ob, struct ParticleSystem *psys); void psys_render_set(struct Object *ob, struct ParticleSystem *psys, float viewmat[4][4], float winmat[4][4], int winx, int winy, int timeoffset); void psys_render_restore(struct Object *ob, struct ParticleSystem *psys); -int psys_render_simplify_distribution(struct ParticleThreadContext *ctx, int tot); bool psys_render_simplify_params(struct ParticleSystem *psys, struct ChildParticle *cpa, float *params); void psys_interpolate_uvs(const struct MTFace *tface, int quad, const float w[4], float uvco[2]); @@ -310,6 +314,7 @@ void psys_interpolate_mcol(const struct MCol *mcol, int quad, const float w[4], void copy_particle_key(struct ParticleKey *to, struct ParticleKey *from, int time); +CustomDataMask psys_emitter_customdata_mask(struct ParticleSystem *psys); void psys_particle_on_emitter(struct ParticleSystemModifierData *psmd, int distr, int index, int index_dmcache, float fuv[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3], float orco[3], float ornor[3]); @@ -328,7 +333,7 @@ void psys_find_parents(struct ParticleSimulationData *sim); void psys_cache_paths(struct ParticleSimulationData *sim, float cfra); void psys_cache_edit_paths(struct Scene *scene, struct Object *ob, struct PTCacheEdit *edit, float cfra); void psys_cache_child_paths(struct ParticleSimulationData *sim, float cfra, int editupdate); -int do_guides(struct ListBase *effectors, ParticleKey *state, int pa_num, float time); +int do_guides(struct ParticleSettings *part, struct ListBase *effectors, ParticleKey *state, int pa_num, float time); void precalc_guides(struct ParticleSimulationData *sim, struct ListBase *effectors); float psys_get_timestep(struct ParticleSimulationData *sim); float psys_get_child_time(struct ParticleSystem *psys, struct ChildParticle *cpa, float cfra, float *birthtime, float *dietime); @@ -336,6 +341,13 @@ float psys_get_child_size(struct ParticleSystem *psys, struct ChildParticle *cpa void psys_get_particle_on_path(struct ParticleSimulationData *sim, int pa_num, struct ParticleKey *state, const bool vel); int psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, int always); +/* child paths */ +void BKE_particlesettings_clump_curve_init(struct ParticleSettings *part); +void BKE_particlesettings_rough_curve_init(struct ParticleSettings *part); +void psys_apply_child_modifiers(struct ParticleThreadContext *ctx, struct ListBase *modifiers, + struct ChildParticle *cpa, struct ParticleTexture *ptex, const float orco[3], const float ornor[3], float hairmat[4][4], + struct ParticleCacheKey *keys, struct ParticleCacheKey *parent_keys, const float parent_orco[3]); + void psys_sph_init(struct ParticleSimulationData *sim, struct SPHData *sphdata); void psys_sph_finalise(struct SPHData *sphdata); void psys_sph_density(struct BVHTree *tree, struct SPHData *data, float co[3], float vars[2]); @@ -347,8 +359,10 @@ void psys_get_dupli_texture(struct ParticleSystem *psys, struct ParticleSettings void psys_get_dupli_path_transform(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ChildParticle *cpa, struct ParticleCacheKey *cache, float mat[4][4], float *scale); -ParticleThread *psys_threads_create(struct ParticleSimulationData *sim); -void psys_threads_free(ParticleThread *threads); +void psys_thread_context_init(struct ParticleThreadContext *ctx, struct ParticleSimulationData *sim); +void psys_thread_context_free(struct ParticleThreadContext *ctx); +void psys_tasks_create(struct ParticleThreadContext *ctx, int totpart, struct ParticleTask **r_tasks, int *r_numtasks); +void psys_tasks_free(struct ParticleTask *tasks, int numtasks); void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3], float zvec[3], float center[3]); void psys_apply_hair_lattice(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys); @@ -403,6 +417,7 @@ void psys_particle_on_dm(struct DerivedMesh *dm, int from, int index, int index_ float orco[3], float ornor[3]); /* particle_system.c */ +void distribute_particles(struct ParticleSimulationData *sim, int from); void initialize_particle(struct ParticleSimulationData *sim, struct ParticleData *pa); void psys_calc_dmcache(struct Object *ob, struct DerivedMesh *dm, struct ParticleSystem *psys); int psys_particle_dm_face_lookup(struct Object *ob, struct DerivedMesh *dm, int index, const float fw[4], struct LinkNode *node); @@ -411,6 +426,33 @@ void reset_particle(struct ParticleSimulationData *sim, struct ParticleData *pa, float psys_get_current_display_percentage(struct ParticleSystem *psys); +typedef struct ParticleRenderElem { + int curchild, totchild, reduce; + float lambda, t, scalemin, scalemax; +} ParticleRenderElem; + +typedef struct ParticleRenderData { + ChildParticle *child; + ParticleCacheKey **pathcache; + ParticleCacheKey **childcache; + ListBase pathcachebufs, childcachebufs; + int totchild, totcached, totchildcache; + struct DerivedMesh *dm; + int totdmvert, totdmedge, totdmface; + + float mat[4][4]; + float viewmat[4][4], winmat[4][4]; + int winx, winy; + + int do_simplify; + int timeoffset; + ParticleRenderElem *elems; + + /* ORIGINDEX */ + const int *index_mf_to_mpoly; + const int *index_mp_to_orig; +} ParticleRenderData; + /* psys_reset */ #define PSYS_RESET_ALL 1 #define PSYS_RESET_DEPSGRAPH 2 diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index 280d85c0882..05cc068848d 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -224,7 +224,6 @@ typedef struct PTCacheEdit { /* particles stuff */ struct ParticleSystem *psys; - struct ParticleData *particles; struct KDTree *emitter_field; float *emitter_cosnos; /* localspace face centers and normals (average of its verts), from the derived mesh */ int *mirror_cache; diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index eac4f6a56a5..149472db8fa 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -39,6 +39,7 @@ extern "C" { struct AviCodecData; struct Base; +struct DisplaySafeAreas; struct EvaluationContext; struct bglMats; struct Main; diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 4c11ec9923d..3eb6ba7b6fa 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -265,7 +265,7 @@ struct SpaceType *BKE_spacetype_from_id(int spaceid); struct ARegionType *BKE_regiontype_from_id(struct SpaceType *st, int regionid); const struct ListBase *BKE_spacetypes_list(void); void BKE_spacetype_register(struct SpaceType *st); -int BKE_spacetype_exists(int spaceid); +bool BKE_spacetype_exists(int spaceid); void BKE_spacetypes_free(void); /* only for quitting blender */ /* spacedata */ @@ -282,6 +282,7 @@ struct ARegion *BKE_area_find_region_type(struct ScrArea *sa, int type); struct ARegion *BKE_area_find_region_active_win(struct ScrArea *sa); struct ScrArea *BKE_screen_find_area_from_space(struct bScreen *sc, struct SpaceLink *sl) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2); struct ScrArea *BKE_screen_find_big_area(struct bScreen *sc, const int spacetype, const short min); +struct ScrArea *BKE_screen_find_area_xy(struct bScreen *sc, const int spacetype, int x, int y); unsigned int BKE_screen_view3d_layer_active_ex( const struct View3D *v3d, const struct Scene *scene, bool use_localvd) ATTR_NONNULL(2); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 24e8d6362c1..6b9c4faa3d2 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -34,6 +34,7 @@ struct bContext; struct EvaluationContext; struct StripColorBalance; struct Editing; +struct GSet; struct ImBuf; struct Main; struct Mask; @@ -236,10 +237,11 @@ struct StripElem *BKE_sequencer_give_stripelem(struct Sequence *seq, int cfra); void BKE_sequencer_update_changed_seq_and_deps(struct Scene *scene, struct Sequence *changed_seq, int len_change, int ibuf_change); bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context, struct Sequence *seq, float cfra); -struct SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq); +struct SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list); void BKE_sequencer_proxy_rebuild(struct SeqIndexBuildContext *context, short *stop, short *do_update, float *progress); void BKE_sequencer_proxy_rebuild_finish(struct SeqIndexBuildContext *context, bool stop); +void BKE_sequencer_proxy_set(struct Sequence *seq, bool value); /* ********************************************************************** * seqcache.c * diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 328c465f07b..ef35cea9f39 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -36,6 +36,7 @@ set(INC ../bmesh ../modifiers ../nodes + ../physics ../pointcache ../render/extern/include ../../../intern/ghost @@ -46,8 +47,8 @@ set(INC ../../../intern/mikktspace ../../../intern/raskter ../../../intern/smoke/extern - ../../../extern/libmv ../../../intern/atomic + ../../../extern/libmv # XXX - BAD LEVEL CALL WM_api.h ../windowmanager @@ -107,7 +108,6 @@ set(SRC intern/idprop.c intern/image.c intern/image_gen.c - intern/implicit.c intern/ipo.c intern/key.c intern/lamp.c @@ -138,6 +138,8 @@ set(SRC intern/packedFile.c intern/paint.c intern/particle.c + intern/particle_child.c + intern/particle_distribute.c intern/particle_system.c intern/pbvh.c intern/pbvh_bmesh.c diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index 6de55e276a1..8c5d245b69c 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -65,6 +65,7 @@ incs = [ '../makesrna', '../modifiers', '../nodes', + '../physics', '../pointcache', '../render/extern/include', '../windowmanager', diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 9cc99a07353..72326d39c70 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -394,9 +394,9 @@ void DM_ensure_normals(DerivedMesh *dm) BLI_assert((dm->dirty & DM_DIRTY_NORMALS) == 0); } -static void DM_calc_loop_normals(DerivedMesh *dm, float split_angle) +static void DM_calc_loop_normals(DerivedMesh *dm, const bool use_split_normals, float split_angle) { - dm->calcLoopNormals(dm, split_angle); + dm->calcLoopNormals(dm, use_split_normals, split_angle); dm->dirty |= DM_DIRTY_TESS_CDLAYERS; } @@ -1553,7 +1553,7 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos /* XXX Same as above... For now, only weights preview in WPaint mode. */ const bool do_mod_wmcol = do_init_wmcol; - const bool do_loop_normals = (me->flag & ME_AUTOSMOOTH); + const bool do_loop_normals = (me->flag & ME_AUTOSMOOTH) != 0; const float loop_normals_split_angle = me->smoothresh; VirtualModifierData virtualModifierData; @@ -1950,7 +1950,7 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos if (do_loop_normals) { /* Compute loop normals (note: will compute poly and vert normals as well, if needed!) */ - DM_calc_loop_normals(finaldm, loop_normals_split_angle); + DM_calc_loop_normals(finaldm, do_loop_normals, loop_normals_split_angle); } { @@ -2050,7 +2050,7 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D const bool do_mod_wmcol = do_init_wmcol; VirtualModifierData virtualModifierData; - const bool do_loop_normals = (((Mesh *)(ob->data))->flag & ME_AUTOSMOOTH); + const bool do_loop_normals = (((Mesh *)(ob->data))->flag & ME_AUTOSMOOTH) != 0; const float loop_normals_split_angle = ((Mesh *)(ob->data))->smoothresh; modifiers_clearErrors(ob); @@ -2266,9 +2266,9 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D if (do_loop_normals) { /* Compute loop normals */ - DM_calc_loop_normals(*final_r, loop_normals_split_angle); + DM_calc_loop_normals(*final_r, do_loop_normals, loop_normals_split_angle); if (cage_r && *cage_r && (*cage_r != *final_r)) { - DM_calc_loop_normals(*cage_r, loop_normals_split_angle); + DM_calc_loop_normals(*cage_r, do_loop_normals, loop_normals_split_angle); } } @@ -3124,6 +3124,69 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, } } +/* Set vertex shader attribute inputs for a particular tessface vert + * + * a: tessface index + * index: vertex index + * vert: corner index (0, 1, 2, 3) + */ +void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert) +{ + const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + int b; + + /* orco texture coordinates */ + if (attribs->totorco) { + /*const*/ float (*array)[3] = attribs->orco.array; + const float *orco = (array) ? array[index] : zero; + + if (attribs->orco.gl_texco) + glTexCoord3fv(orco); + else + glVertexAttrib3fvARB(attribs->orco.gl_index, orco); + } + + /* uv texture coordinates */ + for (b = 0; b < attribs->tottface; b++) { + const float *uv; + + if (attribs->tface[b].array) { + MTFace *tf = &attribs->tface[b].array[a]; + uv = tf->uv[vert]; + } + else { + uv = zero; + } + + if (attribs->tface[b].gl_texco) + glTexCoord2fv(uv); + else + glVertexAttrib2fvARB(attribs->tface[b].gl_index, uv); + } + + /* vertex colors */ + for (b = 0; b < attribs->totmcol; b++) { + GLubyte col[4]; + + if (attribs->mcol[b].array) { + MCol *cp = &attribs->mcol[b].array[a * 4 + vert]; + col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a; + } + else { + col[0] = 0; col[1] = 0; col[2] = 0; col[3] = 0; + } + + glVertexAttrib4ubvARB(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[a * 4 + vert] : zero; + glVertexAttrib4fvARB(attribs->tang.gl_index, tang); + } +} + /* Set object's bounding box based on DerivedMesh min/max data */ void DM_set_object_boundbox(Object *ob, DerivedMesh *dm) { @@ -3466,7 +3529,7 @@ bool DM_is_valid(DerivedMesh *dm) dm->getEdgeDataLayout(dm), dm->getLoopDataLayout(dm), dm->getPolyDataLayout(dm), - 0, /* setting mask here isn't useful, gives false positives */ + false, /* setting mask here isn't useful, gives false positives */ do_verbose, do_fixes, &changed); is_valid &= BKE_mesh_validate_arrays( diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index f94f1e72638..1bae65a7fb4 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1002,8 +1002,12 @@ void BKE_pose_remove_group(bPose *pose, bActionGroup *grp, const int index) /* now, remove it from the pose */ BLI_freelinkN(&pose->agroups, grp); if (pose->active_group >= idx) { + const bool has_groups = !BLI_listbase_is_empty(&pose->agroups); pose->active_group--; - if (pose->active_group < 0 || BLI_listbase_is_empty(&pose->agroups)) { + if (pose->active_group == 0 && has_groups) { + pose->active_group = 1; + } + else if (pose->active_group < 0 || !has_groups) { pose->active_group = 0; } } diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 8e36449f214..8e2b3de9586 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -318,6 +318,77 @@ void BKE_copy_animdata_id_action(ID *id) } } +/* Merge copies of the data from the src AnimData into the destination AnimData */ +void BKE_animdata_merge_copy(ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers) +{ + AnimData *src = BKE_animdata_from_id(src_id); + AnimData *dst = BKE_animdata_from_id(dst_id); + + /* sanity checks */ + if (ELEM(NULL, dst, src)) + return; + + // TODO: we must unset all "tweakmode" flags + if ((src->flag & ADT_NLA_EDIT_ON) || (dst->flag & ADT_NLA_EDIT_ON)) { + printf("ERROR: Merging AnimData blocks while editing NLA is dangerous as it may cause data corruption\n"); + return; + } + + /* handle actions... */ + if (action_mode == ADT_MERGECOPY_SRC_COPY) { + /* make a copy of the actions */ + dst->action = BKE_action_copy(src->action); + dst->tmpact = BKE_action_copy(src->tmpact); + } + else if (action_mode == ADT_MERGECOPY_SRC_REF) { + /* make a reference to it */ + dst->action = src->action; + id_us_plus((ID *)dst->action); + + dst->tmpact = src->tmpact; + id_us_plus((ID *)dst->tmpact); + } + + /* duplicate NLA data */ + if (src->nla_tracks.first) { + ListBase tracks = {NULL, NULL}; + + copy_nladata(&tracks, &src->nla_tracks); + BLI_movelisttolist(&dst->nla_tracks, &tracks); + } + + /* duplicate drivers (F-Curves) */ + if (src->drivers.first) { + ListBase drivers = {NULL, NULL}; + + copy_fcurves(&drivers, &src->drivers); + + /* Fix up all driver targets using the old target id + * - This assumes that the src ID is being merged into the dst ID + */ + if (fix_drivers) { + FCurve *fcu; + + for (fcu = drivers.first; fcu; fcu = fcu->next) { + ChannelDriver *driver = fcu->driver; + DriverVar *dvar; + + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + DRIVER_TARGETS_USED_LOOPER(dvar) + { + if (dtar->id == src_id) { + dtar->id = dst_id; + } + } + DRIVER_TARGETS_LOOPER_END + } + } + } + + BLI_movelisttolist(&dst->drivers, &drivers); + } +} + /* Make Local -------------------------------------------- */ static void make_local_strips(ListBase *strips) @@ -720,7 +791,7 @@ static void fcurves_path_rename_fix(ID *owner_id, const char *prefix, const char if (fcu->rna_path != old_path) { bActionGroup *agrp = fcu->grp; - if ((agrp) && strcmp(oldName, agrp->name) == 0) { + if ((agrp) && STREQ(oldName, agrp->name)) { BLI_strncpy(agrp->name, newName, sizeof(agrp->name)); } } @@ -757,7 +828,7 @@ static void drivers_path_rename_fix(ID *owner_id, ID *ref_id, const char *prefix /* also fix the bone-name (if applicable) */ if (strstr(prefix, "bones")) { if ( ((dtar->id) && (GS(dtar->id->name) == ID_OB) && (!ref_id || ((Object *)(dtar->id))->data == ref_id)) && - (dtar->pchan_name[0]) && (strcmp(oldName, dtar->pchan_name) == 0) ) + (dtar->pchan_name[0]) && STREQ(oldName, dtar->pchan_name) ) { BLI_strncpy(dtar->pchan_name, newName, sizeof(dtar->pchan_name)); } @@ -787,6 +858,60 @@ static void nlastrips_path_rename_fix(ID *owner_id, const char *prefix, const ch } } +/* ----------------------- */ + + +/* Fix up the given RNA-Path + * + * This is just an external wrapper for the RNA-Path fixing function, + * with input validity checks on top of the basic method. + * + * NOTE: it is assumed that the structure we're replacing is <prefix><["><name><"]> + * i.e. pose.bones["Bone"] + */ +char *BKE_animsys_fix_rna_path_rename(ID *owner_id, char *old_path, const char *prefix, const char *oldName, + const char *newName, int oldSubscript, int newSubscript, bool verify_paths) +{ + char *oldN, *newN; + char *result; + + /* if no action, no need to proceed */ + if (ELEM(NULL, owner_id, old_path)) { + printf("early abort\n"); + return old_path; + } + + /* Name sanitation logic - copied from BKE_animdata_fix_paths_rename() */ + if ((oldName != NULL) && (newName != NULL)) { + /* pad the names with [" "] so that only exact matches are made */ + const size_t name_old_len = strlen(oldName); + const size_t name_new_len = strlen(newName); + char *name_old_esc = BLI_array_alloca(name_old_esc, (name_old_len * 2) + 1); + char *name_new_esc = BLI_array_alloca(name_new_esc, (name_new_len * 2) + 1); + + BLI_strescape(name_old_esc, oldName, (name_old_len * 2) + 1); + BLI_strescape(name_new_esc, newName, (name_new_len * 2) + 1); + oldN = BLI_sprintfN("[\"%s\"]", name_old_esc); + newN = BLI_sprintfN("[\"%s\"]", name_new_esc); + } + else { + oldN = BLI_sprintfN("[%d]", oldSubscript); + newN = BLI_sprintfN("[%d]", newSubscript); + } + + /* fix given path */ + printf("%s | %s | oldpath = %p ", oldN, newN, old_path); + result = rna_path_rename_fix(owner_id, prefix, oldN, newN, old_path, verify_paths); + printf("result = %p\n", result); + + /* free the temp names */ + MEM_freeN(oldN); + MEM_freeN(newN); + + /* return the resulting path - may be the same path again if nothing changed */ + return result; +} + /* Fix all RNA_Paths in the given Action, relative to the given ID block * * This is just an external wrapper for the F-Curve fixing function, @@ -1156,7 +1281,7 @@ KS_Path *BKE_keyingset_find_path(KeyingSet *ks, ID *id, const char group_name[], eq_id = 0; /* path */ - if ((ksp->rna_path == NULL) || strcmp(rna_path, ksp->rna_path)) + if ((ksp->rna_path == NULL) || !STREQ(rna_path, ksp->rna_path)) eq_path = 0; /* index - need to compare whole-array setting too... */ @@ -1192,6 +1317,7 @@ KeyingSet *BKE_keyingset_add(ListBase *list, const char idname[], const char nam ks->flag = flag; ks->keyingflag = keyingflag; + ks->keyingoverride = keyingflag; /* NOTE: assume that if one is set one way, the other should be too, so that it'll work */ /* add KeyingSet to list */ BLI_addtail(list, ks); diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 50f9a9be05d..60e81003c40 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -17,7 +17,7 @@ * */ -/** \file blender/blenlib/intern/appdir.c +/** \file blender/blenkernel/intern/appdir.c * \ingroup bke * * Access to application level directories. @@ -641,7 +641,7 @@ static void bli_where_am_i(char *fullname, const size_t maxlen, const char *name } } #if defined(DEBUG) - if (strcmp(name, fullname)) { + if (!STREQ(name, fullname)) { printf("guessing '%s' == '%s'\n", name, fullname); } #endif diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 431c24d3ed0..31695b05352 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -233,7 +233,7 @@ static Bone *get_named_bone_bonechildren(Bone *bone, const char *name) { Bone *curBone, *rbone; - if (!strcmp(bone->name, name)) + if (STREQ(bone->name, name)) return bone; for (curBone = bone->childbase.first; curBone; curBone = curBone->next) { @@ -2206,7 +2206,7 @@ static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *o float range = bulge_max - 1.0f; float scale = (range > 0.0f) ? 1.0f / range : 0.0f; - float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (0.5f * M_PI); + float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2; bulge = interpf(soft, hard, ikData->bulge_smooth); } @@ -2218,7 +2218,7 @@ static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *o float range = 1.0f - bulge_min; float scale = (range > 0.0f) ? 1.0f / range : 0.0f; - float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (0.5f * M_PI); + float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2; bulge = interpf(soft, hard, ikData->bulge_smooth); } @@ -2417,7 +2417,7 @@ static void do_strip_modifiers(Scene *scene, Object *armob, Bone *bone, bPoseCha /* validate first */ if (amod->ob && amod->ob->type == OB_CURVE && amod->channel[0]) { - if (strcmp(pchan->name, amod->channel) == 0) { + if (STREQ(pchan->name, amod->channel)) { float mat4[4][4], mat3[3][3]; curve_deform_vector(scene, amod->ob, armob, bone->arm_mat[3], pchan->pose_mat[3], mat3, amod->no_rot_axis); @@ -2430,7 +2430,7 @@ static void do_strip_modifiers(Scene *scene, Object *armob, Bone *bone, bPoseCha break; case ACTSTRIP_MOD_NOISE: { - if (strcmp(pchan->name, amod->channel) == 0) { + if (STREQ(pchan->name, amod->channel)) { float nor[3], loc[3], ofs; float eul[3], size[3], eulo[3], sizeo[3]; diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 96f769587d9..dda790ea700 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -146,10 +146,6 @@ void initglobals(void) else BLI_snprintf(versionstr, sizeof(versionstr), "v%d.%02d", BLENDER_VERSION / 100, BLENDER_VERSION % 100); -#ifdef _WIN32 - G.windowstate = 0; -#endif - #ifndef WITH_PYTHON_SECURITY /* default */ G.f |= G_SCRIPT_AUTOEXEC; #else @@ -189,6 +185,17 @@ static void clean_paths(Main *main) } } +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 */ @@ -232,24 +239,54 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, const char *filepath /* 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 + */ + bool track_undo_scene; + /* comes from readfile.c */ SWAP(ListBase, G.main->wm, bfd->main->wm); SWAP(ListBase, G.main->screen, bfd->main->screen); SWAP(ListBase, G.main->script, bfd->main->script); - /* 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 && bfd->main->wm.first); + if (track_undo_scene) { + curscene = curscreen->scene; + } + else { + /* but use new Scene pointer */ + curscene = bfd->curscene; + } + 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"); - + /* and we enforce curscene to be in current screen */ if (curscreen) curscreen->scene = curscene; /* can run in bgmode */ /* clear_global will free G.main, here we can still restore pointers */ blo_lib_link_screen_restore(bfd->main, curscreen, curscene); + 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; + } + } } /* free G.main Main database */ @@ -271,6 +308,17 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, const char *filepath 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); } @@ -280,8 +328,6 @@ static void setup_app_data(bContext *C, BlendFileData *bfd, const char *filepath CTX_data_scene_set(C, curscene); } else { - G.winpos = bfd->winpos; - G.displaymode = bfd->displaymode; G.fileflags = bfd->fileflags; CTX_wm_manager_set(C, G.main->wm.first); CTX_wm_screen_set(C, bfd->curscreen); @@ -568,7 +614,7 @@ int BKE_write_file_userdef(const char *filepath, ReportList *reports) static void (*blender_test_break_cb)(void) = NULL; -void set_blender_test_break_cb(void (*func)(void) ) +void set_blender_test_break_cb(void (*func)(void)) { blender_test_break_cb = func; } diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c index 4a564614ff3..11879c7973c 100644 --- a/source/blender/blenkernel/intern/boids.c +++ b/source/blender/blenkernel/intern/boids.c @@ -994,7 +994,7 @@ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) } case eBoidRulesetType_Random: { - /* use random rule for each particle (allways same for same particle though) */ + /* use random rule for each particle (always same for same particle though) */ rule = BLI_findlink(&state->rules, rand % BLI_listbase_count(&state->rules)); apply_boid_rule(bbd, rule, &val, pa, -1.0); diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 964d876a9a0..aa92b67c737 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -229,7 +229,7 @@ static int findFileRecursive(char *filename_new, while ((de = readdir(dir)) != NULL) { - if (STREQ(".", de->d_name) || STREQ("..", de->d_name)) + if (FILENAME_IS_CURRPAR(de->d_name)) continue; BLI_join_dirfile(path, sizeof(path), dirname, de->d_name); @@ -285,7 +285,7 @@ static bool findMissingFiles_visit_cb(void *userdata, char *path_dst, const char filename_new[0] = '\0'; found = findFileRecursive(filename_new, - data->searchdir, BLI_path_basename((char *)path_src), + data->searchdir, BLI_path_basename(path_src), &filesize, &recur_depth); if (filesize == -1) { /* could not open dir */ @@ -297,7 +297,7 @@ static bool findMissingFiles_visit_cb(void *userdata, char *path_dst, const char else if (found == false) { BKE_reportf(data->reports, RPT_WARNING, "Could not find '%s' in '%s'", - BLI_path_basename((char *)path_src), data->searchdir); + BLI_path_basename(path_src), data->searchdir); return false; } else { diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 55d347a4fb2..10d77921515 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -542,7 +542,7 @@ float BKE_brush_sample_tex_3D(const Scene *scene, Brush *br, /* Get strength by feeding the vertex * location directly into a texture */ hasrgb = externtex(mtex, point, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool); + rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false); } else if (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) { float rotation = -mtex->rot; @@ -573,7 +573,7 @@ float BKE_brush_sample_tex_3D(const Scene *scene, Brush *br, co[2] = 0.0f; hasrgb = externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool); + rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false); } else { float rotation = -mtex->rot; @@ -630,7 +630,7 @@ float BKE_brush_sample_tex_3D(const Scene *scene, Brush *br, co[2] = 0.0f; hasrgb = externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool); + rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false); } intensity += br->texture_sample_bias; @@ -690,7 +690,7 @@ float BKE_brush_sample_masktex(const Scene *scene, Brush *br, co[2] = 0.0f; externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool); + rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false); } else { float rotation = -mtex->rot; @@ -747,7 +747,7 @@ float BKE_brush_sample_masktex(const Scene *scene, Brush *br, co[2] = 0.0f; externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool); + rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false); } CLAMP(intensity, 0.0f, 1.0f); @@ -810,6 +810,9 @@ void BKE_brush_size_set(Scene *scene, Brush *brush, int size) size = (int)((float)size / U.pixelsize); + /* make sure range is sane */ + CLAMP(size, 1, MAX_BRUSH_PIXEL_RADIUS); + if (ups->flag & UNIFIED_PAINT_SIZE) ups->size = size; else @@ -1007,7 +1010,7 @@ unsigned int *BKE_brush_gen_texture_cache(Brush *br, int half_side, bool use_sec /* This is copied from displace modifier code */ /* TODO(sergey): brush are always cacheing with CM enabled for now. */ externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL); + rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false); ((char *)texcache)[(iy * side + ix) * 4] = ((char *)texcache)[(iy * side + ix) * 4 + 1] = diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index a1bf255c239..2554151c99d 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -869,59 +869,7 @@ static void cdDM_drawMappedFacesTex(DerivedMesh *dm, static void cddm_draw_attrib_vertex(DMVertexAttribs *attribs, const MVert *mvert, int a, int index, int vert, const short (*lnor)[3], const bool smoothnormal) { - const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - int b; - - /* orco texture coordinates */ - if (attribs->totorco) { - /*const*/ float (*array)[3] = attribs->orco.array; - const float *orco = (array) ? array[index] : zero; - - if (attribs->orco.gl_texco) - glTexCoord3fv(orco); - else - glVertexAttrib3fvARB(attribs->orco.gl_index, orco); - } - - /* uv texture coordinates */ - for (b = 0; b < attribs->tottface; b++) { - const float *uv; - - if (attribs->tface[b].array) { - MTFace *tf = &attribs->tface[b].array[a]; - uv = tf->uv[vert]; - } - else { - uv = zero; - } - - if (attribs->tface[b].gl_texco) - glTexCoord2fv(uv); - else - glVertexAttrib2fvARB(attribs->tface[b].gl_index, uv); - } - - /* vertex colors */ - for (b = 0; b < attribs->totmcol; b++) { - GLubyte col[4]; - - if (attribs->mcol[b].array) { - MCol *cp = &attribs->mcol[b].array[a * 4 + vert]; - col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a; - } - else { - col[0] = 0; col[1] = 0; col[2] = 0; col[3] = 0; - } - - glVertexAttrib4ubvARB(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[a * 4 + vert] : zero; - glVertexAttrib4fvARB(attribs->tang.gl_index, tang); - } + DM_draw_attrib_vertex(attribs, a, index, vert); /* vertex normal */ if (lnor) { @@ -1592,6 +1540,7 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->calcNormals = CDDM_calc_normals; dm->calcLoopNormals = CDDM_calc_loop_normals; + dm->calcLoopNormalsSpaceArray = CDDM_calc_loop_normals_spacearr; dm->recalcTessellation = CDDM_recalc_tessellation; dm->getVertCos = cdDM_getVertCos; @@ -2208,7 +2157,15 @@ void CDDM_calc_normals(DerivedMesh *dm) #endif -void CDDM_calc_loop_normals(DerivedMesh *dm, const float split_angle) +void CDDM_calc_loop_normals(DerivedMesh *dm, const bool use_split_normals, const float split_angle) +{ + CDDM_calc_loop_normals_spacearr(dm, use_split_normals, split_angle, NULL); +} + +/* #define DEBUG_CLNORS */ + +void CDDM_calc_loop_normals_spacearr( + DerivedMesh *dm, const bool use_split_normals, const float split_angle, MLoopNorSpaceArray *r_lnors_spacearr) { MVert *mverts = dm->getVertArray(dm); MEdge *medges = dm->getEdgeArray(dm); @@ -2218,6 +2175,7 @@ void CDDM_calc_loop_normals(DerivedMesh *dm, const float split_angle) CustomData *ldata, *pdata; float (*lnors)[3]; + short (*clnor_data)[2]; float (*pnors)[3]; const int numVerts = dm->getNumVerts(dm); @@ -2245,8 +2203,37 @@ void CDDM_calc_loop_normals(DerivedMesh *dm, const float split_angle) dm->dirty &= ~DM_DIRTY_NORMALS; + clnor_data = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); + BKE_mesh_normals_loop_split(mverts, numVerts, medges, numEdges, mloops, lnors, numLoops, - mpolys, pnors, numPolys, split_angle); + mpolys, (const float (*)[3])pnors, numPolys, + use_split_normals, split_angle, + r_lnors_spacearr, clnor_data, NULL); +#ifdef DEBUG_CLNORS + if (r_lnors_spacearr) { + int i; + for (i = 0; i < numLoops; i++) { + if (r_lnors_spacearr->lspacearr[i]->ref_alpha != 0.0f) { + LinkNode *loops = r_lnors_spacearr->lspacearr[i]->loops; + printf("Loop %d uses lnor space %p:\n", i, r_lnors_spacearr->lspacearr[i]); + print_v3("\tfinal lnor", lnors[i]); + print_v3("\tauto lnor", r_lnors_spacearr->lspacearr[i]->vec_lnor); + print_v3("\tref_vec", r_lnors_spacearr->lspacearr[i]->vec_ref); + printf("\talpha: %f\n\tbeta: %f\n\tloops: %p\n", r_lnors_spacearr->lspacearr[i]->ref_alpha, + r_lnors_spacearr->lspacearr[i]->ref_beta, r_lnors_spacearr->lspacearr[i]->loops); + printf("\t\t(shared with loops"); + while (loops) { + printf(" %d", GET_INT_FROM_POINTER(loops->link)); + loops = loops->next; + } + printf(")\n"); + } + else { + printf("Loop %d has no lnor space\n", i); + } + } + } +#endif } diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index e1b5dd6d39d..765986cc82b 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -47,16 +47,9 @@ #include "BKE_modifier.h" #include "BKE_pointcache.h" -// #include "PIL_time.h" /* timing for debug prints */ +#include "BPH_mass_spring.h" -/* Our available solvers. */ -// 255 is the magic reserved number, so NEVER try to put 255 solvers in here! -// 254 = MAX! -static CM_SOLVER_DEF solvers [] = -{ - { "Implicit", CM_IMPLICIT, implicit_init, implicit_solver, implicit_free }, - // { "Implicit C++", CM_IMPLICITCPP, implicitcpp_init, implicitcpp_solver, implicitcpp_free }, -}; +// #include "PIL_time.h" /* timing for debug prints */ /* ********** cloth engine ******* */ /* Prototypes for internal functions. @@ -68,7 +61,6 @@ static void cloth_update_springs( ClothModifierData *clmd ); static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ); static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ); - /****************************************************************************** * * External interface called by modifier.c clothModifier functions. @@ -89,6 +81,7 @@ void cloth_init(ClothModifierData *clmd ) clmd->sim_parms->structural = 15.0; clmd->sim_parms->shear = 15.0; clmd->sim_parms->bending = 0.5; + clmd->sim_parms->bending_damping = 0.5; clmd->sim_parms->Cdis = 5.0; clmd->sim_parms->Cvi = 1.0; clmd->sim_parms->mass = 0.3f; @@ -130,6 +123,8 @@ void cloth_init(ClothModifierData *clmd ) clmd->sim_parms->goalfrict = 0.0f; clmd->sim_parms->velocity_smooth = 0.0f; + clmd->sim_parms->voxel_cell_size = 0.1f; + if (!clmd->sim_parms->effector_weights) clmd->sim_parms->effector_weights = BKE_add_effector_weights(NULL); @@ -343,7 +338,7 @@ static int do_init_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul return 0; } - implicit_set_positions(clmd); + BKE_cloth_solver_set_positions(clmd); clmd->clothObject->last_frame= MINFRAME-1; } @@ -385,8 +380,7 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul // TIMEIT_START(cloth_step) /* call the solver. */ - if (solvers [clmd->sim_parms->solver_type].solver) - ret = solvers[clmd->sim_parms->solver_type].solver(ob, framenr, clmd, effectors); + ret = BPH_cloth_solve(ob, framenr, clmd, effectors); // TIMEIT_END(cloth_step) @@ -531,7 +525,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe); if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) { - implicit_set_positions(clmd); + BKE_cloth_solver_set_positions(clmd); cloth_to_object (ob, clmd, vertexCos); BKE_ptcache_validate(cache, framenr); @@ -544,7 +538,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived return; } else if (cache_result==PTCACHE_READ_OLD) { - implicit_set_positions(clmd); + BKE_cloth_solver_set_positions(clmd); } if (framenr!=clmd->clothObject->last_frame+1) @@ -581,10 +575,7 @@ void cloth_free_modifier(ClothModifierData *clmd ) if ( cloth ) { - // If our solver provides a free function, call it - if ( solvers [clmd->sim_parms->solver_type].free ) { - solvers [clmd->sim_parms->solver_type].free ( clmd ); - } + BPH_cloth_solver_free(clmd); // Free the verts. if ( cloth->verts != NULL ) @@ -650,10 +641,7 @@ void cloth_free_modifier_extern(ClothModifierData *clmd ) if (G.debug_value > 0) printf("cloth_free_modifier_extern in\n"); - // If our solver provides a free function, call it - if ( solvers [clmd->sim_parms->solver_type].free ) { - solvers [clmd->sim_parms->solver_type].free ( clmd ); - } + BPH_cloth_solver_free(clmd); // Free the verts. if ( cloth->verts != NULL ) @@ -931,12 +919,10 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d } // init our solver - if ( solvers [clmd->sim_parms->solver_type].init ) { - solvers [clmd->sim_parms->solver_type].init ( ob, clmd ); - } + BPH_cloth_solver_init(ob, clmd); if (!first) - implicit_set_positions(clmd); + BKE_cloth_solver_set_positions(clmd); clmd->clothObject->bvhtree = bvhtree_build_from_cloth ( clmd, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel) ); @@ -1068,6 +1054,135 @@ static void cloth_free_errorsprings(Cloth *cloth, LinkNode **edgelist) } } +static void cloth_hair_update_bending_targets(ClothModifierData *clmd) +{ + Cloth *cloth = clmd->clothObject; + LinkNode *search = NULL; + float hair_frame[3][3], dir_old[3], dir_new[3]; + int prev_mn; /* to find hair chains */ + + if (!clmd->hairdata) + return; + + /* XXX Note: we need to propagate frames from the root up, + * but structural hair springs are stored in reverse order. + * The bending springs however are then inserted in the same + * order as vertices again ... + * This messy situation can be resolved when solver data is + * generated directly from a dedicated hair system. + */ + + prev_mn = -1; + for (search = cloth->springs; search; search = search->next) { + ClothSpring *spring = search->link; + ClothHairData *hair_ij, *hair_kl; + bool is_root = spring->kl != prev_mn; + + if (spring->type != CLOTH_SPRING_TYPE_BENDING_ANG) { + continue; + } + + hair_ij = &clmd->hairdata[spring->ij]; + hair_kl = &clmd->hairdata[spring->kl]; + if (is_root) { + /* initial hair frame from root orientation */ + copy_m3_m3(hair_frame, hair_ij->rot); + /* surface normal is the initial direction, + * parallel transport then keeps it aligned to the hair direction + */ + copy_v3_v3(dir_new, hair_frame[2]); + } + + copy_v3_v3(dir_old, dir_new); + sub_v3_v3v3(dir_new, cloth->verts[spring->mn].x, cloth->verts[spring->kl].x); + normalize_v3(dir_new); + +#if 0 + if (clmd->debug_data && (spring->ij == 0 || spring->ij == 1)) { + float a[3], b[3]; + + copy_v3_v3(a, cloth->verts[spring->kl].x); +// BKE_sim_debug_data_add_dot(clmd->debug_data, cloth_vert ? cloth_vert->x : key->co, 1, 1, 0, "frames", 8246, p, k); + + mul_v3_v3fl(b, hair_frame[0], clmd->sim_parms->avg_spring_len); + BKE_sim_debug_data_add_vector(clmd->debug_data, a, b, 1, 0, 0, "frames", 8247, spring->kl, spring->mn); + + mul_v3_v3fl(b, hair_frame[1], clmd->sim_parms->avg_spring_len); + BKE_sim_debug_data_add_vector(clmd->debug_data, a, b, 0, 1, 0, "frames", 8248, spring->kl, spring->mn); + + mul_v3_v3fl(b, hair_frame[2], clmd->sim_parms->avg_spring_len); + BKE_sim_debug_data_add_vector(clmd->debug_data, a, b, 0, 0, 1, "frames", 8249, spring->kl, spring->mn); + } +#endif + + /* get local targets for kl/mn vertices by putting rest targets into the current frame, + * then multiply with the rest length to get the actual goals + */ + + mul_v3_m3v3(spring->target, hair_frame, hair_kl->rest_target); + mul_v3_fl(spring->target, spring->restlen); + + /* move frame to next hair segment */ + cloth_parallel_transport_hair_frame(hair_frame, dir_old, dir_new); + + prev_mn = spring->mn; + } +} + +static void cloth_hair_update_bending_rest_targets(ClothModifierData *clmd) +{ + Cloth *cloth = clmd->clothObject; + LinkNode *search = NULL; + float hair_frame[3][3], dir_old[3], dir_new[3]; + int prev_mn; /* to find hair roots */ + + if (!clmd->hairdata) + return; + + /* XXX Note: we need to propagate frames from the root up, + * but structural hair springs are stored in reverse order. + * The bending springs however are then inserted in the same + * order as vertices again ... + * This messy situation can be resolved when solver data is + * generated directly from a dedicated hair system. + */ + + prev_mn = -1; + for (search = cloth->springs; search; search = search->next) { + ClothSpring *spring = search->link; + ClothHairData *hair_ij, *hair_kl; + bool is_root = spring->kl != prev_mn; + + if (spring->type != CLOTH_SPRING_TYPE_BENDING_ANG) { + continue; + } + + hair_ij = &clmd->hairdata[spring->ij]; + hair_kl = &clmd->hairdata[spring->kl]; + if (is_root) { + /* initial hair frame from root orientation */ + copy_m3_m3(hair_frame, hair_ij->rot); + /* surface normal is the initial direction, + * parallel transport then keeps it aligned to the hair direction + */ + copy_v3_v3(dir_new, hair_frame[2]); + } + + copy_v3_v3(dir_old, dir_new); + sub_v3_v3v3(dir_new, cloth->verts[spring->mn].xrest, cloth->verts[spring->kl].xrest); + normalize_v3(dir_new); + + /* dir expressed in the hair frame defines the rest target direction */ + copy_v3_v3(hair_kl->rest_target, dir_new); + mul_transposed_m3_v3(hair_frame, hair_kl->rest_target); + + /* move frame to next hair segment */ + cloth_parallel_transport_hair_frame(hair_frame, dir_old, dir_new); + + prev_mn = spring->mn; + } +} + /* update stiffness if vertex group values are changing from frame to frame */ static void cloth_update_springs( ClothModifierData *clmd ) { @@ -1089,6 +1204,16 @@ static void cloth_update_springs( ClothModifierData *clmd ) else if (spring->type == CLOTH_SPRING_TYPE_BENDING) { spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f; } + else if (spring->type == CLOTH_SPRING_TYPE_BENDING_ANG) { + ClothVertex *v1 = &cloth->verts[spring->ij]; + ClothVertex *v2 = &cloth->verts[spring->kl]; + if (clmd->hairdata) { + /* copy extra hair data to generic cloth vertices */ + v1->bend_stiff = clmd->hairdata[spring->ij].bending_stiffness; + v2->bend_stiff = clmd->hairdata[spring->kl].bending_stiffness; + } + spring->stiffness = (v1->bend_stiff + v2->bend_stiff) / 2.0f; + } else if (spring->type == CLOTH_SPRING_TYPE_GOAL) { /* Warning: Appending NEW goal springs does not work because implicit solver would need reset! */ @@ -1105,8 +1230,43 @@ static void cloth_update_springs( ClothModifierData *clmd ) search = search->next; } + + cloth_hair_update_bending_targets(clmd); +} +BLI_INLINE void cross_identity_v3(float r[3][3], const float v[3]) +{ + zero_m3(r); + r[0][1] = v[2]; + r[0][2] = -v[1]; + r[1][0] = -v[2]; + r[1][2] = v[0]; + r[2][0] = v[1]; + r[2][1] = -v[0]; +} +BLI_INLINE void madd_m3_m3fl(float r[3][3], float m[3][3], float f) +{ + r[0][0] += m[0][0] * f; + r[0][1] += m[0][1] * f; + r[0][2] += m[0][2] * f; + r[1][0] += m[1][0] * f; + r[1][1] += m[1][1] * f; + r[1][2] += m[1][2] * f; + r[2][0] += m[2][0] * f; + r[2][1] += m[2][1] * f; + r[2][2] += m[2][2] * f; +} + +void cloth_parallel_transport_hair_frame(float mat[3][3], const float dir_old[3], const float dir_new[3]) +{ + float rot[3][3]; + + /* rotation between segments */ + rotation_between_vecs_to_mat3(rot, dir_old, dir_new); + + /* rotate the frame */ + mul_m3_m3m3(mat, rot, mat); } static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) @@ -1283,40 +1443,74 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) } } else if (struct_springs > 2) { - /* bending springs for hair strands */ - /* The current algorightm only goes through the edges in order of the mesh edges list */ - /* and makes springs between the outer vert of edges sharing a vertice. This works just */ - /* fine for hair, but not for user generated string meshes. This could/should be later */ - /* extended to work with non-ordered edges so that it can be used for general "rope */ - /* dynamics" without the need for the vertices or edges to be ordered through the length*/ - /* of the strands. -jahka */ - search = cloth->springs; - search2 = search->next; - while (search && search2) { - tspring = search->link; - tspring2 = search2->link; - - if (tspring->ij == tspring2->kl) { - spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); + if (G.debug_value != 1112) { + search = cloth->springs; + search2 = search->next; + while (search && search2) { + tspring = search->link; + tspring2 = search2->link; - if (!spring) { - cloth_free_errorsprings(cloth, edgelist); - return 0; + if (tspring->ij == tspring2->kl) { + spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); + + if (!spring) { + cloth_free_errorsprings(cloth, edgelist); + return 0; + } + + spring->ij = tspring2->ij; + spring->kl = tspring->ij; + spring->mn = tspring->kl; + spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest); + spring->type = CLOTH_SPRING_TYPE_BENDING_ANG; + spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f; + bend_springs++; + + BLI_linklist_prepend ( &cloth->springs, spring ); } - - spring->ij = tspring2->ij; - spring->kl = tspring->kl; - spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest); - spring->type = CLOTH_SPRING_TYPE_BENDING; - spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f; - bend_springs++; - - BLI_linklist_prepend ( &cloth->springs, spring ); + + search = search->next; + search2 = search2->next; + } + } + else { + /* bending springs for hair strands */ + /* The current algorightm only goes through the edges in order of the mesh edges list */ + /* and makes springs between the outer vert of edges sharing a vertice. This works just */ + /* fine for hair, but not for user generated string meshes. This could/should be later */ + /* extended to work with non-ordered edges so that it can be used for general "rope */ + /* dynamics" without the need for the vertices or edges to be ordered through the length*/ + /* of the strands. -jahka */ + search = cloth->springs; + search2 = search->next; + while (search && search2) { + tspring = search->link; + tspring2 = search2->link; + + if (tspring->ij == tspring2->kl) { + spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); + + if (!spring) { + cloth_free_errorsprings(cloth, edgelist); + return 0; + } + + spring->ij = tspring2->ij; + spring->kl = tspring->kl; + spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest); + spring->type = CLOTH_SPRING_TYPE_BENDING; + spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f; + bend_springs++; + + BLI_linklist_prepend ( &cloth->springs, spring ); + } + + search = search->next; + search2 = search2->next; } - - search = search->next; - search2 = search2->next; } + + cloth_hair_update_bending_rest_targets(clmd); } /* note: the edges may already exist so run reinsert */ diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 3030ee4b4a4..5266c5b323d 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -33,6 +33,7 @@ #include "MEM_guardedalloc.h" #include "DNA_cloth_types.h" +#include "DNA_effect_types.h" #include "DNA_group_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" @@ -45,6 +46,7 @@ #include "BLI_edgehash.h" #include "BKE_cloth.h" +#include "BKE_effect.h" #include "BKE_modifier.h" #include "BKE_scene.h" @@ -652,8 +654,8 @@ static void cloth_bvh_objcollisions_nearcheck ( ClothModifierData * clmd, Collis *collisions_index = *collisions; for ( i = 0; i < numresult; i++ ) { - *collisions_index = cloth_collision ( (ModifierData *)clmd, (ModifierData *)collmd, - overlap+i, *collisions_index, dt ); + *collisions_index = cloth_collision((ModifierData *)clmd, (ModifierData *)collmd, + overlap+i, *collisions_index, dt); } } @@ -913,3 +915,557 @@ int cloth_bvh_objcollision(Object *ob, ClothModifierData *clmd, float step, floa return 1|MIN2 ( ret, 1 ); } + +BLI_INLINE void max_v3_v3v3(float r[3], const float a[3], const float b[3]) +{ + r[0] = max_ff(a[0], b[0]); + r[1] = max_ff(a[1], b[1]); + r[2] = max_ff(a[2], b[2]); +} + +void collision_get_collider_velocity(float vel_old[3], float vel_new[3], CollisionModifierData *collmd, CollPair *collpair) +{ + float u1, u2, u3; + + /* compute barycentric coordinates */ + collision_compute_barycentric(collpair->pb, + collmd->current_x[collpair->bp1].co, + collmd->current_x[collpair->bp2].co, + collmd->current_x[collpair->bp3].co, + &u1, &u2, &u3); + + collision_interpolateOnTriangle(vel_new, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, collmd->current_v[collpair->bp3].co, u1, u2, u3); + /* XXX assume constant velocity of the collider for now */ + copy_v3_v3(vel_old, vel_new); +} + +static bool cloth_points_collision_response_static(ClothModifierData *clmd, CollisionModifierData *collmd, PartDeflect *pd, + CollPair *collpair, CollPair *collision_end, float dt) +{ + bool result = false; + float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - pd->pdef_sbdamp); + float inv_dt = 1.0f / dt; + Cloth *cloth1 = clmd->clothObject; + + float w1, w2, 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 ); + + for ( ; collpair != collision_end; collpair++ ) { + float margin_distance = collpair->distance - epsilon2; + float impulse[3]; + float mag_v_rel; + + if (margin_distance > 0.0f) + continue; + + zero_v3(impulse); + + /* only handle static collisions here */ + if ( collpair->flag & COLLISION_IN_FUTURE ) + continue; + + /* compute barycentric coordinates for both collision points */ + w1 = 1.0f - collpair->time; + w2 = collpair->time; + + /* was: txold */ + collision_compute_barycentric ( collpair->pb, + collmd->current_x[collpair->bp1].co, + collmd->current_x[collpair->bp2].co, + collmd->current_x[collpair->bp3].co, + &u1, &u2, &u3 ); + + /* Calculate relative velocity */ + copy_v3_v3(v1, cloth1->verts[collpair->ap1].tv); + + collision_interpolateOnTriangle ( v2_new, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, collmd->current_v[collpair->bp3].co, u1, u2, u3 ); + /* XXX assume constant velocity of the collider for now */ + copy_v3_v3(v2_old, v2_new); + + sub_v3_v3v3(v_rel_old, v1, v2_old); + sub_v3_v3v3(v_rel_new, v1, v2_new); + + /* normal component of the relative velocity */ + mag_v_rel = dot_v3v3(v_rel_old, collpair->normal); + + /**** DEBUG ****/ + BKE_sim_debug_data_add_dot(collpair->pa, 0.9, 0.2, 0.2, "collision", 833, collpair->face1, collpair->face2); + BKE_sim_debug_data_add_dot(collpair->pb, 0.2, 0.9, 0.2, "collision", 834, collpair->face1, collpair->face2); + BKE_sim_debug_data_add_line(collpair->pa, collpair->pb, 0.8, 0.8, 0.8, "collision", 835, collpair->face1, collpair->face2); + /********/ + + if (mag_v_rel < -ALMOST_ZERO) { + float v_nor_old, v_nor_new; + float v_tan_old[3], v_tan_new[3]; + float bounce, repulse; + + /* Collision response based on + * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005) + * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf + */ + + v_nor_old = mag_v_rel; + v_nor_new = dot_v3v3(v_rel_new, collpair->normal); + + madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old); + madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new); + + repulse = -margin_distance * inv_dt + dot_v3v3(v1, collpair->normal); + + if (margin_distance < -epsilon2) { + bounce = -v_nor_new + v_nor_old * restitution; + mul_v3_v3fl(impulse, collpair->normal, max_ff(repulse, bounce)); + } + else { + bounce = 0.0f; + mul_v3_v3fl(impulse, collpair->normal, repulse); + } + cloth1->verts[collpair->ap1].impulse_count++; + + result = true; + } + + if (result) { + int i = 0; + + for (i = 0; i < 3; i++) { + if (cloth1->verts[collpair->ap1].impulse_count > 0 && fabsf(cloth1->verts[collpair->ap1].impulse[i]) < fabsf(impulse[i])) + cloth1->verts[collpair->ap1].impulse[i] = impulse[i]; + } + } + } + return result; +} + +BLI_INLINE bool cloth_point_face_collision_params(const float p1[3], const float p2[3], const float v0[3], const float v1[3], const float v2[3], + float r_nor[3], float *r_lambda, float r_w[4]) +{ + float edge1[3], edge2[3], p2face[3], p1p2[3], v0p2[3]; + float nor_v0p2, nor_p1p2; + + sub_v3_v3v3(edge1, v1, v0); + sub_v3_v3v3(edge2, v2, v0); + cross_v3_v3v3(r_nor, edge1, edge2); + normalize_v3(r_nor); + + nor_v0p2 = dot_v3v3(v0p2, r_nor); + madd_v3_v3v3fl(p2face, p2, r_nor, -nor_v0p2); + interp_weights_face_v3(r_w, v0, v1, v2, NULL, p2face); + + sub_v3_v3v3(p1p2, p2, p1); + sub_v3_v3v3(v0p2, p2, v0); + nor_p1p2 = dot_v3v3(p1p2, r_nor); + *r_lambda = (nor_p1p2 != 0.0f ? nor_v0p2 / nor_p1p2 : 0.0f); + + return r_w[1] >= 0.0f && r_w[2] >= 0.0f && r_w[1] + r_w[2] <= 1.0f; + +#if 0 /* XXX this method uses the intersection point, but is broken and doesn't work well in general */ + float p[3], vec1[3], line[3], edge1[3], edge2[3], q[3]; + float a, f, u, v; + + sub_v3_v3v3(edge1, v1, v0); + sub_v3_v3v3(edge2, v2, v0); + sub_v3_v3v3(line, p2, p1); + + cross_v3_v3v3(p, line, edge2); + a = dot_v3v3(edge1, p); + if (a == 0.0f) return 0; + f = 1.0f / a; + + sub_v3_v3v3(vec1, p1, v0); + + u = f * dot_v3v3(vec1, p); + if ((u < 0.0f) || (u > 1.0f)) + return false; + + cross_v3_v3v3(q, vec1, edge1); + + v = f * dot_v3v3(line, q); + if ((v < 0.0f) || ((u + v) > 1.0f)) + return false; + + *r_lambda = f * dot_v3v3(edge2, q); + /* don't care about 0..1 lambda range here */ + /*if ((*r_lambda < 0.0f) || (*r_lambda > 1.0f)) + * return 0; + */ + + r_w[0] = 1.0f - u - v; + r_w[1] = u; + r_w[2] = v; + r_w[3] = 0.0f; + + cross_v3_v3v3(r_nor, edge1, edge2); + normalize_v3(r_nor); + + return true; +#endif +} + +static CollPair *cloth_point_collpair(float p1[3], float p2[3], MVert *mverts, int bp1, int bp2, int bp3, + int index_cloth, int index_coll, float epsilon, CollPair *collpair) +{ + float *co1 = mverts[bp1].co, *co2 = mverts[bp2].co, *co3 = mverts[bp3].co; + float lambda, distance1, distance2; + float facenor[3], v1p1[3], v1p2[3]; + float w[4]; + + if (!cloth_point_face_collision_params(p1, p2, co1, co2, co3, facenor, &lambda, w)) + return collpair; + + sub_v3_v3v3(v1p1, p1, co1); + distance1 = dot_v3v3(v1p1, facenor); + sub_v3_v3v3(v1p2, p2, co1); + distance2 = dot_v3v3(v1p2, facenor); +// if (distance2 > epsilon || (distance1 < 0.0f && distance2 < 0.0f)) + if (distance2 > epsilon) + return collpair; + + collpair->face1 = index_cloth; /* XXX actually not a face, but equivalent index for point */ + collpair->face2 = index_coll; + collpair->ap1 = index_cloth; + collpair->ap2 = collpair->ap3 = -1; /* unused */ + collpair->bp1 = bp1; + collpair->bp2 = bp2; + collpair->bp3 = bp3; + + /* note: using the second point here, which is + * the current updated position that needs to be corrected + */ + copy_v3_v3(collpair->pa, p2); + collpair->distance = distance2; + mul_v3_v3fl(collpair->vector, facenor, -distance2); + + interp_v3_v3v3v3(collpair->pb, co1, co2, co3, w); + + copy_v3_v3(collpair->normal, facenor); + collpair->time = lambda; + collpair->flag = 0; + + collpair++; + return collpair; +} + +//Determines collisions on overlap, collisions are written to collpair[i] and collision+number_collision_found is returned +static CollPair* cloth_point_collision(ModifierData *md1, ModifierData *md2, + BVHTreeOverlap *overlap, float epsilon, CollPair *collpair, float UNUSED(dt)) +{ + ClothModifierData *clmd = (ClothModifierData *)md1; + CollisionModifierData *collmd = (CollisionModifierData *) md2; + /* Cloth *cloth = clmd->clothObject; */ /* UNUSED */ + ClothVertex *vert = NULL; + MFace *face = NULL; + MVert *mverts = collmd->current_x; + + vert = &clmd->clothObject->verts[overlap->indexA]; + face = &collmd->mfaces[overlap->indexB]; + + collpair = cloth_point_collpair(vert->tx, vert->x, mverts, face->v1, face->v2, face->v3, overlap->indexA, overlap->indexB, epsilon, collpair); + if (face->v4) + collpair = cloth_point_collpair(vert->tx, vert->x, mverts, face->v3, face->v4, face->v1, overlap->indexA, overlap->indexB, epsilon, collpair); + + return collpair; +} + +static void cloth_points_objcollisions_nearcheck(ClothModifierData * clmd, CollisionModifierData *collmd, + CollPair **collisions, CollPair **collisions_index, + int numresult, BVHTreeOverlap *overlap, float epsilon, double dt) +{ + int i; + + /* can return 2 collisions in total */ + *collisions = (CollPair *) MEM_mallocN(sizeof(CollPair) * numresult * 2, "collision array" ); + *collisions_index = *collisions; + + for ( i = 0; i < numresult; i++ ) { + *collisions_index = cloth_point_collision((ModifierData *)clmd, (ModifierData *)collmd, + overlap+i, epsilon, *collisions_index, dt); + } +} + +static int cloth_points_objcollisions_resolve(ClothModifierData * clmd, CollisionModifierData *collmd, PartDeflect *pd, + CollPair *collisions, CollPair *collisions_index, float dt) +{ + Cloth *cloth = clmd->clothObject; + int i=0, numverts = clmd->clothObject->numverts; + ClothVertex *verts = cloth->verts; + int ret = 0; + + // process all collisions + if ( collmd->bvhtree ) { + bool result = cloth_points_collision_response_static(clmd, collmd, pd, collisions, collisions_index, dt); + + // apply impulses in parallel + if (result) { + for (i = 0; i < numverts; i++) { + // calculate "velocities" (just xnew = xold + v; no dt in v) + if (verts[i].impulse_count) { + // VECADDMUL ( verts[i].tv, verts[i].impulse, 1.0f / verts[i].impulse_count ); + VECADD ( verts[i].tv, verts[i].tv, verts[i].impulse); + zero_v3(verts[i].impulse); + verts[i].impulse_count = 0; + + ret++; + } + } + } + } + + return ret; +} + +// cloth - object collisions +int cloth_points_objcollision(Object *ob, ClothModifierData *clmd, float step, float dt) +{ + Cloth *cloth= clmd->clothObject; + BVHTree *cloth_bvh; + int rounds = 0; // result counts applied collisions; ic is for debug output; + float round_dt = dt / (float)clmd->coll_parms->loop_count; + unsigned int i=0, numverts = 0; + ClothVertex *verts = NULL; + int ret = 0, ret2 = 0; + Object **collobjs = NULL; + unsigned int numcollobj = 0; + + verts = cloth->verts; + numverts = cloth->numverts; + + //////////////////////////////////////////////////////////// + // static collisions + //////////////////////////////////////////////////////////// + + // create temporary cloth points bvh + cloth_bvh = BLI_bvhtree_new(numverts, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel), 4, 6); + /* fill tree */ + for (i = 0; i < numverts; i++) { + float co[6]; + + copy_v3_v3(&co[0*3], verts[i].x); + copy_v3_v3(&co[1*3], verts[i].tx); + + BLI_bvhtree_insert(cloth_bvh, i, co, 2); + } + /* balance tree */ + BLI_bvhtree_balance(cloth_bvh); + + collobjs = get_collisionobjects(clmd->scene, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision); + if (!collobjs) + return 0; + + /* move object to position (step) in time */ + for (i = 0; i < numcollobj; i++) { + Object *collob= collobjs[i]; + CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(collob, eModifierType_Collision); + if (!collmd->bvhtree) + continue; + + /* move object to position (step) in time */ + collision_move_object ( collmd, step + dt, step ); + } + + do { + CollPair **collisions, **collisions_index; + + ret2 = 0; + + collisions = MEM_callocN(sizeof(CollPair *) *numcollobj, "CollPair"); + collisions_index = MEM_callocN(sizeof(CollPair *) *numcollobj, "CollPair"); + + // check all collision objects + for (i = 0; i < numcollobj; i++) { + Object *collob= collobjs[i]; + CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(collob, eModifierType_Collision); + BVHTreeOverlap *overlap = NULL; + unsigned int result = 0; + float epsilon; + + if (!collmd->bvhtree) + continue; + + /* search for overlapping collision pairs */ + overlap = BLI_bvhtree_overlap ( cloth_bvh, collmd->bvhtree, &result ); + epsilon = BLI_bvhtree_getepsilon(collmd->bvhtree); + + // go to next object if no overlap is there + if (result && overlap) { + /* check if collisions really happen (costly near check) */ + cloth_points_objcollisions_nearcheck(clmd, collmd, &collisions[i], &collisions_index[i], + result, overlap, epsilon, round_dt); + + // resolve nearby collisions + ret += cloth_points_objcollisions_resolve(clmd, collmd, collob->pd, collisions[i], collisions_index[i], round_dt); + ret2 += ret; + } + + if (overlap) + MEM_freeN ( overlap ); + } + rounds++; + + for (i = 0; i < numcollobj; i++) { + if (collisions[i]) + MEM_freeN(collisions[i]); + } + + MEM_freeN(collisions); + MEM_freeN(collisions_index); + + //////////////////////////////////////////////////////////// + // update positions + // this is needed for bvh_calc_DOP_hull_moving() [kdop.c] + //////////////////////////////////////////////////////////// + + // verts come from clmd + for ( i = 0; i < numverts; i++ ) { + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL ) { + if ( verts [i].flags & CLOTH_VERT_FLAG_PINNED ) { + continue; + } + } + + VECADD ( verts[i].tx, verts[i].txold, verts[i].tv ); + } + //////////////////////////////////////////////////////////// + } + while ( ret2 && ( clmd->coll_parms->loop_count>rounds ) ); + + if (collobjs) + MEM_freeN(collobjs); + + BLI_bvhtree_free(cloth_bvh); + + return 1|MIN2 ( ret, 1 ); +} + +void cloth_find_point_contacts(Object *ob, ClothModifierData *clmd, float step, float dt, + ColliderContacts **r_collider_contacts, int *r_totcolliders) +{ + Cloth *cloth= clmd->clothObject; + BVHTree *cloth_bvh; + unsigned int i=0, numverts = 0; + ClothVertex *verts = NULL; + + ColliderContacts *collider_contacts; + + Object **collobjs = NULL; + unsigned int numcollobj = 0; + + verts = cloth->verts; + numverts = cloth->numverts; + + //////////////////////////////////////////////////////////// + // static collisions + //////////////////////////////////////////////////////////// + + // create temporary cloth points bvh + cloth_bvh = BLI_bvhtree_new(numverts, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel), 4, 6); + /* fill tree */ + for (i = 0; i < numverts; i++) { + float co[6]; + + copy_v3_v3(&co[0*3], verts[i].x); + copy_v3_v3(&co[1*3], verts[i].tx); + + BLI_bvhtree_insert(cloth_bvh, i, co, 2); + } + /* balance tree */ + BLI_bvhtree_balance(cloth_bvh); + + collobjs = get_collisionobjects(clmd->scene, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision); + if (!collobjs) { + *r_collider_contacts = NULL; + *r_totcolliders = 0; + return; + } + + /* move object to position (step) in time */ + for (i = 0; i < numcollobj; i++) { + Object *collob= collobjs[i]; + CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(collob, eModifierType_Collision); + if (!collmd->bvhtree) + continue; + + /* move object to position (step) in time */ + collision_move_object ( collmd, step + dt, step ); + } + + collider_contacts = MEM_callocN(sizeof(ColliderContacts) * numcollobj, "CollPair"); + + // check all collision objects + for (i = 0; i < numcollobj; i++) { + ColliderContacts *ct = collider_contacts + i; + Object *collob= collobjs[i]; + CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(collob, eModifierType_Collision); + BVHTreeOverlap *overlap; + unsigned int result = 0; + float epsilon; + + ct->ob = collob; + ct->collmd = collmd; + ct->collisions = NULL; + ct->totcollisions = 0; + + if (!collmd->bvhtree) + continue; + + /* search for overlapping collision pairs */ + overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result); + epsilon = BLI_bvhtree_getepsilon(collmd->bvhtree); + + // go to next object if no overlap is there + if (result && overlap) { + CollPair *collisions_index; + + /* check if collisions really happen (costly near check) */ + cloth_points_objcollisions_nearcheck(clmd, collmd, &ct->collisions, &collisions_index, + result, overlap, epsilon, dt); + ct->totcollisions = (int)(collisions_index - ct->collisions); + + // resolve nearby collisions +// ret += cloth_points_objcollisions_resolve(clmd, collmd, collob->pd, collisions[i], collisions_index[i], dt); + } + + if (overlap) + MEM_freeN(overlap); + } + + if (collobjs) + MEM_freeN(collobjs); + + BLI_bvhtree_free(cloth_bvh); + + //////////////////////////////////////////////////////////// + // update positions + // this is needed for bvh_calc_DOP_hull_moving() [kdop.c] + //////////////////////////////////////////////////////////// + + // verts come from clmd + for (i = 0; i < numverts; i++) { + if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { + if (verts [i].flags & CLOTH_VERT_FLAG_PINNED) { + continue; + } + } + + VECADD(verts[i].tx, verts[i].txold, verts[i].tv); + } + //////////////////////////////////////////////////////////// + + *r_collider_contacts = collider_contacts; + *r_totcolliders = numcollobj; +} + +void cloth_free_contacts(ColliderContacts *collider_contacts, int totcolliders) +{ + if (collider_contacts) { + int i; + for (i = 0; i < totcolliders; ++i) { + ColliderContacts *ct = collider_contacts + i; + if (ct->collisions) { + MEM_freeN(ct->collisions); + } + } + MEM_freeN(collider_contacts); + } +} diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index f591fe857f8..668b78a08d4 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2715,7 +2715,7 @@ static void stretchto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t float range = bulge_max - 1.0f; float scale = (range > 0.0f) ? 1.0f / range : 0.0f; - float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (0.5f * M_PI); + float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2; bulge = interpf(soft, hard, data->bulge_smooth); } @@ -2727,7 +2727,7 @@ static void stretchto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t float range = 1.0f - bulge_min; float scale = (range > 0.0f) ? 1.0f / range : 0.0f; - float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (0.5f * M_PI); + float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2; bulge = interpf(soft, hard, data->bulge_smooth); } diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 8f6c9735aaf..7142c092583 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -454,7 +454,7 @@ static void data_dir_add(ListBase *lb, const char *member, const bool use_all) { LinkData *link; - if ((use_all == false) && strcmp(member, "scene") == 0) /* exception */ + if ((use_all == false) && STREQ(member, "scene")) /* exception */ return; if (BLI_findstring(lb, member, offsetof(LinkData, data))) @@ -546,7 +546,7 @@ ListBase CTX_data_dir_get(const bContext *C) bool CTX_data_equals(const char *member, const char *str) { - return (strcmp(member, str) == 0); + return (STREQ(member, str)); } bool CTX_data_dir(const char *member) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 3abe3c58562..e88a8ecde8e 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -593,6 +593,10 @@ Nurb *BKE_nurb_copy(Nurb *src, int pntsu, int pntsv) newnu->pntsu = pntsu; newnu->pntsv = pntsv; + /* caller can manually handle these arrays */ + newnu->knotsu = NULL; + newnu->knotsv = NULL; + if (src->bezt) { newnu->bezt = (BezTriple *)MEM_mallocN(pntsu * pntsv * sizeof(BezTriple), "copyNurb2"); } @@ -1738,7 +1742,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp, /* half a circle */ fp = dl->verts; - dangle = (0.5 * M_PI / (dnr - 1)); + dangle = ((float)M_PI_2 / (dnr - 1)); angle = -(nr - 1) * dangle; for (a = 0; a < nr; a++) { @@ -1797,7 +1801,7 @@ void BKE_curve_bevel_make(Scene *scene, Object *ob, ListBase *disp, /* half a circle */ fp = dl->verts; angle = 0.0; - dangle = (0.5 * M_PI / (dnr - 1)); + dangle = ((float)M_PI_2 / (dnr - 1)); for (a = 0; a < nr; a++) { fp[0] = 0.0; @@ -1939,7 +1943,7 @@ static void calc_bevel_sin_cos(float x1, float y1, float x2, float y2, t02 = x1 * x2 + y1 * y2; if (fabsf(t02) >= 1.0f) - t02 = 0.5 * M_PI; + t02 = M_PI_2; else t02 = (saacos(t02)) / 2.0f; @@ -2472,7 +2476,7 @@ static void make_bevel_list_2D(BevList *bl) /* first */ bevp = bl->bevpoints; - angle = atan2f(bevp->dir[0], bevp->dir[1]) - (float)(M_PI / 2.0f); + angle = atan2f(bevp->dir[0], bevp->dir[1]) - (float)M_PI_2; bevp->sina = sinf(angle); bevp->cosa = cosf(angle); vec_to_quat(bevp->quat, bevp->dir, 5, 1); @@ -2480,7 +2484,7 @@ static void make_bevel_list_2D(BevList *bl) /* last */ bevp = bl->bevpoints; bevp += (bl->nr - 1); - angle = atan2f(bevp->dir[0], bevp->dir[1]) - (float)(M_PI / 2.0f); + angle = atan2f(bevp->dir[0], bevp->dir[1]) - (float)M_PI_2; bevp->sina = sinf(angle); bevp->cosa = cosf(angle); vec_to_quat(bevp->quat, bevp->dir, 5, 1); @@ -3347,6 +3351,21 @@ void BKE_nurb_handle_calc_simple(Nurb *nu, BezTriple *bezt) } } +void BKE_nurb_handle_calc_simple_auto(Nurb *nu, BezTriple *bezt) +{ + if (nu->pntsu > 1) { + const char h1_back = bezt->h1, h2_back = bezt->h2; + + bezt->h1 = bezt->h2 = HD_AUTO; + + /* Override handle types to HD_AUTO and recalculate */ + BKE_nurb_handle_calc_simple(nu, bezt); + + bezt->h1 = h1_back; + bezt->h2 = h2_back; + } +} + /** * Use when something has changed handle positions. * @@ -3604,24 +3623,12 @@ void BKE_nurbList_handles_recalculate(ListBase *editnurb, const bool calc_length if (h1_select || h2_select) { - /* Override handle types to HD_AUTO and recalculate */ - - char h1_back, h2_back; float co1_back[3], co2_back[3]; - h1_back = bezt->h1; - h2_back = bezt->h2; - - bezt->h1 = HD_AUTO; - bezt->h2 = HD_AUTO; - copy_v3_v3(co1_back, bezt->vec[0]); copy_v3_v3(co2_back, bezt->vec[2]); - BKE_nurb_handle_calc_simple(nu, bezt); - - bezt->h1 = h1_back; - bezt->h2 = h2_back; + BKE_nurb_handle_calc_simple_auto(nu, bezt); if (h1_select) { if (!calc_length) { diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 89ebed258a3..0436ec0ef22 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -300,6 +300,49 @@ static void layerInterp_mdeformvert(void **sources, const float *weights, } } +static void layerInterp_normal(void **sources, const float *weights, + const float *UNUSED(sub_weights), int count, void *dest) +{ + float no[3] = {0.0f}; + + while (count--) { + madd_v3_v3fl(no, (float *)sources[count], weights[count]); + } + + copy_v3_v3((float *)dest, no); +} + +static void layerCopyValue_normal(const void *source, void *dest, const int mixmode, const float mixfactor) +{ + const float *no_src = source; + float *no_dst = dest; + float no_tmp[3]; + + if (ELEM(mixmode, CDT_MIX_NOMIX, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) { + /* Above/below threshold modes are not supported here, fallback to nomix (just in case). */ + copy_v3_v3(no_dst, no_src); + } + else { /* Modes that support 'real' mix factor. */ + /* Since we normalize in the end, MIX and ADD are the same op here. */ + if (ELEM(mixmode, CDT_MIX_MIX, CDT_MIX_ADD)) { + add_v3_v3v3(no_tmp, no_dst, no_src); + normalize_v3(no_tmp); + } + else if (mixmode == CDT_MIX_SUB) { + sub_v3_v3v3(no_tmp, no_dst, no_src); + normalize_v3(no_tmp); + } + else if (mixmode == CDT_MIX_MUL) { + mul_v3_v3v3(no_tmp, no_dst, no_src); + normalize_v3(no_tmp); + } + else { + copy_v3_v3(no_tmp, no_src); + } + interp_v3_v3v3_slerp_safe(no_dst, no_dst, no_tmp, mixfactor); + } +} + static void layerCopy_tface(const void *source, void *dest, int count) { const MTFace *source_tf = (const MTFace *)source; @@ -1165,7 +1208,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, layerDefault_origindex}, /* 8: CD_NORMAL */ /* 3 floats per normal vector */ - {sizeof(float) * 3, "vec3f", 1, NULL, NULL, NULL, NULL, NULL, NULL}, + {sizeof(float) * 3, "vec3f", 1, NULL, NULL, NULL, layerInterp_normal, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, layerCopyValue_normal}, /* 9: CD_POLYINDEX (deprecated) */ {sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, NULL}, /* 10: CD_PROP_FLT */ @@ -1255,6 +1299,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { {sizeof(float[4]), "", 0, NULL, NULL, NULL, NULL, NULL, NULL}, /* 40: CD_TESSLOOPNORMAL */ {sizeof(short[4][3]), "", 0, NULL, NULL, NULL, NULL, layerSwap_flnor, NULL}, + /* 41: CD_CUSTOMLOOPNORMAL */ + {sizeof(short[2]), "vec2s", 1, NULL, NULL, NULL, NULL, NULL, NULL}, }; /* note, numbers are from trunk and need updating for bmesh */ @@ -1270,7 +1316,8 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { /* 25-29 */ "CDMPoly", "CDMLoop", "CDShapeKeyIndex", "CDShapeKey", "CDBevelWeight", /* 30-34 */ "CDSubSurfCrease", "CDOrigSpaceLoop", "CDPreviewLoopCol", "CDBMElemPyPtr", "CDPaintMask", /* 35-36 */ "CDGridPaintMask", "CDMVertSkin", - /* 37-40 */ "CDFreestyleEdge", "CDFreestyleFace", "CDMLoopTangent", "CDTessLoopNormal", + /* 37-38 */ "CDFreestyleEdge", "CDFreestyleFace", + /* 39-41 */ "CDMLoopTangent", "CDTessLoopNormal", "CDCustomLoopNormal", }; @@ -1282,26 +1329,29 @@ const CustomDataMask CD_MASK_MESH = CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MPOLY | CD_MASK_MLOOP | CD_MASK_MTEXPOLY | CD_MASK_RECAST | CD_MASK_PAINT_MASK | - CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE; + CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE | + CD_MASK_CUSTOMLOOPNORMAL; const CustomDataMask CD_MASK_EDITMESH = CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS | CD_MASK_SHAPEKEY | CD_MASK_RECAST | CD_MASK_PAINT_MASK | - CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN; + CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_CUSTOMLOOPNORMAL; const CustomDataMask CD_MASK_DERIVEDMESH = CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_CLOTH_ORCO | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_ORCO | CD_MASK_TANGENT | CD_MASK_PREVIEW_MCOL | CD_MASK_SHAPEKEY | CD_MASK_RECAST | - CD_MASK_ORIGINDEX | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE; + CD_MASK_ORIGINDEX | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE | + CD_MASK_CUSTOMLOOPNORMAL; const CustomDataMask CD_MASK_BMESH = CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS | CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_RECAST | CD_MASK_PAINT_MASK | - CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE; + CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE | + CD_MASK_CUSTOMLOOPNORMAL; const CustomDataMask CD_MASK_FACECORNERS = /* XXX Not used anywhere! */ CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_NORMAL | CD_MASK_MLOOPTANGENT; @@ -1316,7 +1366,7 @@ const CustomDataMask CD_MASK_EVERYTHING = /* BMESH ONLY END */ CD_MASK_PAINT_MASK | CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE | - CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL; + CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_CUSTOMLOOPNORMAL; static const LayerTypeInfo *layerType_getInfo(int type) { @@ -1553,7 +1603,7 @@ int CustomData_get_named_layer_index(const CustomData *data, int type, const cha for (i = 0; i < data->totlayer; ++i) if (data->layers[i].type == type) - if (strcmp(data->layers[i].name, name) == 0) + if (STREQ(data->layers[i].name, name)) return i; return -1; @@ -2690,7 +2740,7 @@ void CustomData_bmesh_copy_data(const CustomData *source, CustomData *dest, /* if we found a matching layer, copy the data */ if (dest->layers[dest_i].type == source->layers[src_i].type && - strcmp(dest->layers[dest_i].name, source->layers[src_i].name) == 0) + STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { const char *src_data = (char *)src_block + source->layers[src_i].offset; char *dest_data = (char *)*dest_block + dest->layers[dest_i].offset; @@ -3163,12 +3213,12 @@ static bool cd_layer_find_dupe(CustomData *data, const char *name, int type, int CustomDataLayer *layer = &data->layers[i]; if (CustomData_is_property_layer(type)) { - if (CustomData_is_property_layer(layer->type) && strcmp(layer->name, name) == 0) { + if (CustomData_is_property_layer(layer->type) && STREQ(layer->name, name)) { return true; } } else { - if (i != index && layer->type == type && strcmp(layer->name, name) == 0) { + if (i != index && layer->type == type && STREQ(layer->name, name)) { return true; } } diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c index c72eea50e40..41579aaa568 100644 --- a/source/blender/blenkernel/intern/customdata_file.c +++ b/source/blender/blenkernel/intern/customdata_file.c @@ -276,7 +276,7 @@ static int cdf_write_header(CDataFile *cdf) return 1; } -int cdf_read_open(CDataFile *cdf, const char *filename) +bool cdf_read_open(CDataFile *cdf, const char *filename) { FILE *f; @@ -299,7 +299,7 @@ int cdf_read_open(CDataFile *cdf, const char *filename) return 1; } -int cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay) +bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay) { size_t offset; int a; @@ -316,7 +316,7 @@ int cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay) return (fseek(cdf->readf, offset, SEEK_SET) == 0); } -int cdf_read_data(CDataFile *cdf, unsigned int size, void *data) +bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data) { /* read data */ if (!fread(data, size, 1, cdf->readf)) @@ -338,7 +338,7 @@ void cdf_read_close(CDataFile *cdf) } } -int cdf_write_open(CDataFile *cdf, const char *filename) +bool cdf_write_open(CDataFile *cdf, const char *filename) { CDataFileHeader *header; CDataFileImageHeader *image; @@ -380,12 +380,12 @@ int cdf_write_open(CDataFile *cdf, const char *filename) return 1; } -int cdf_write_layer(CDataFile *UNUSED(cdf), CDataFileLayer *UNUSED(blay)) +bool cdf_write_layer(CDataFile *UNUSED(cdf), CDataFileLayer *UNUSED(blay)) { return 1; } -int cdf_write_data(CDataFile *cdf, unsigned int size, void *data) +bool cdf_write_data(CDataFile *cdf, unsigned int size, void *data) { /* write data */ if (!fwrite(data, size, 1, cdf->writef)) @@ -417,7 +417,7 @@ CDataFileLayer *cdf_layer_find(CDataFile *cdf, int type, const char *name) for (a = 0; a < cdf->totlayer; a++) { layer = &cdf->layer[a]; - if (layer->type == type && strcmp(layer->name, name) == 0) + if (layer->type == type && STREQ(layer->name, name)) return layer; } diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index d41c74770f1..5ac7b928d96 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -47,6 +47,7 @@ #include "BKE_data_transfer.h" #include "BKE_deform.h" #include "BKE_DerivedMesh.h" +#include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_remap.h" #include "BKE_object.h" @@ -79,6 +80,9 @@ CustomDataMask BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types else if (cddata_type == CD_FAKE_UV) { cddata_mask |= CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV; } + else if (cddata_type == CD_FAKE_LNOR) { + cddata_mask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; + } } return cddata_mask; @@ -143,6 +147,10 @@ bool BKE_object_data_transfer_get_dttypes_capacity( *r_threshold = true; ret = true; break; + case DT_TYPE_LNOR: + *r_advanced_mixing = true; + ret = true; + break; case DT_TYPE_SHARP_FACE: *r_threshold = true; ret = true; @@ -217,6 +225,8 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) case DT_TYPE_VCOL: return CD_MLOOPCOL; + case DT_TYPE_LNOR: + return CD_FAKE_LNOR; default: BLI_assert(0); @@ -242,6 +252,105 @@ int BKE_object_data_transfer_dttype_to_srcdst_index(const int dtdata_type) /* ********** */ +/* Generic pre/post processing, only used by custom loop normals currently. */ + +static void data_transfer_dtdata_type_preprocess( + Object *UNUSED(ob_src), Object *UNUSED(ob_dst), DerivedMesh *dm_src, DerivedMesh *dm_dst, Mesh *me_dst, + const int dtdata_type, const bool dirty_nors_dst, const bool use_split_nors_src, const float split_angle_src) +{ + if (dtdata_type == DT_TYPE_LNOR) { + /* Compute custom normals into regular loop normals, which will be used for the transfer. */ + MVert *verts_dst = dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert; + const int num_verts_dst = dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert; + MEdge *edges_dst = dm_dst ? dm_dst->getEdgeArray(dm_dst) : me_dst->medge; + const int num_edges_dst = dm_dst ? dm_dst->getNumEdges(dm_dst) : me_dst->totedge; + MPoly *polys_dst = dm_dst ? dm_dst->getPolyArray(dm_dst) : me_dst->mpoly; + const int num_polys_dst = dm_dst ? dm_dst->getNumPolys(dm_dst) : me_dst->totpoly; + MLoop *loops_dst = dm_dst ? dm_dst->getLoopArray(dm_dst) : me_dst->mloop; + const int num_loops_dst = dm_dst ? dm_dst->getNumLoops(dm_dst) : me_dst->totloop; + CustomData *pdata_dst = dm_dst ? dm_dst->getPolyDataLayout(dm_dst) : &me_dst->pdata; + CustomData *ldata_dst = dm_dst ? dm_dst->getLoopDataLayout(dm_dst) : &me_dst->ldata; + + const bool use_split_nors_dst = (me_dst->flag & ME_AUTOSMOOTH) != 0; + const float split_angle_dst = me_dst->smoothresh; + + dm_src->calcLoopNormals(dm_src, use_split_nors_src, split_angle_src); + + if (dm_dst) { + dm_dst->calcLoopNormals(dm_dst, use_split_nors_dst, split_angle_dst); + } + else { + float (*poly_nors_dst)[3]; + float (*loop_nors_dst)[3]; + short (*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL); + + /* Cache poly nors into a temp CDLayer. */ + poly_nors_dst = CustomData_get_layer(pdata_dst, CD_NORMAL); + if (dirty_nors_dst || !poly_nors_dst) { + if (!poly_nors_dst) { + poly_nors_dst = CustomData_add_layer(pdata_dst, CD_NORMAL, CD_CALLOC, NULL, num_polys_dst); + CustomData_set_layer_flag(pdata_dst, CD_NORMAL, CD_FLAG_TEMPORARY); + } + BKE_mesh_calc_normals_poly(verts_dst, num_verts_dst, loops_dst, polys_dst, + num_loops_dst, num_polys_dst, poly_nors_dst, true); + } + /* Cache loop nors into a temp CDLayer. */ + loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL); + if (dirty_nors_dst || loop_nors_dst) { + if (!loop_nors_dst) { + loop_nors_dst = CustomData_add_layer(ldata_dst, CD_NORMAL, CD_CALLOC, NULL, num_loops_dst); + CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY); + } + BKE_mesh_normals_loop_split(verts_dst, num_verts_dst, edges_dst, num_edges_dst, + loops_dst, loop_nors_dst, num_loops_dst, + polys_dst, (const float (*)[3])poly_nors_dst, num_polys_dst, + use_split_nors_dst, split_angle_dst, NULL, custom_nors_dst, NULL); + } + } + } +} + +static void data_transfer_dtdata_type_postprocess( + Object *UNUSED(ob_src), Object *UNUSED(ob_dst), DerivedMesh *UNUSED(dm_src), DerivedMesh *dm_dst, Mesh *me_dst, + const int dtdata_type, const bool changed) +{ + if (dtdata_type == DT_TYPE_LNOR) { + /* Bake edited destination loop normals into custom normals again. */ + MVert *verts_dst = dm_dst ? dm_dst->getVertArray(dm_dst) : me_dst->mvert; + const int num_verts_dst = dm_dst ? dm_dst->getNumVerts(dm_dst) : me_dst->totvert; + MEdge *edges_dst = dm_dst ? dm_dst->getEdgeArray(dm_dst) : me_dst->medge; + const int num_edges_dst = dm_dst ? dm_dst->getNumEdges(dm_dst) : me_dst->totedge; + MPoly *polys_dst = dm_dst ? dm_dst->getPolyArray(dm_dst) : me_dst->mpoly; + const int num_polys_dst = dm_dst ? dm_dst->getNumPolys(dm_dst) : me_dst->totpoly; + MLoop *loops_dst = dm_dst ? dm_dst->getLoopArray(dm_dst) : me_dst->mloop; + const int num_loops_dst = dm_dst ? dm_dst->getNumLoops(dm_dst) : me_dst->totloop; + CustomData *pdata_dst = dm_dst ? dm_dst->getPolyDataLayout(dm_dst) : &me_dst->pdata; + CustomData *ldata_dst = dm_dst ? dm_dst->getLoopDataLayout(dm_dst) : &me_dst->ldata; + + const float (*poly_nors_dst)[3] = CustomData_get_layer(pdata_dst, CD_NORMAL); + float (*loop_nors_dst)[3] = CustomData_get_layer(ldata_dst, CD_NORMAL); + short (*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL); + + BLI_assert(poly_nors_dst); + + if (!changed) { + return; + } + + if (!custom_nors_dst) { + custom_nors_dst = CustomData_add_layer(ldata_dst, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, num_loops_dst); + } + + /* Note loop_nors_dst contains our custom normals as transferred from source... */ + BKE_mesh_normals_loop_custom_set(verts_dst, num_verts_dst, edges_dst, num_edges_dst, + loops_dst, loop_nors_dst, num_loops_dst, + polys_dst, poly_nors_dst, num_polys_dst, + custom_nors_dst); + } +} + +/* ********** */ + static MeshRemapIslandsCalc data_transfer_get_loop_islands_generator(const int cddata_type) { switch (cddata_type) { @@ -690,13 +799,19 @@ static bool data_transfer_layersmapping_generate( return true; } else if (cddata_type == CD_FAKE_MDEFORMVERT) { + bool ret; + cd_src = dm_src->getVertDataLayout(dm_src); cd_dst = dm_dst ? dm_dst->getVertDataLayout(dm_dst) : &me_dst->vdata; - return data_transfer_layersmapping_vgroups(r_map, mix_mode, mix_factor, mix_weights, - num_elem_dst, use_create, use_delete, - ob_src, ob_dst, cd_src, cd_dst, dm_dst != NULL, - fromlayers, tolayers); + ret = data_transfer_layersmapping_vgroups(r_map, mix_mode, mix_factor, mix_weights, + num_elem_dst, use_create, use_delete, + ob_src, ob_dst, cd_src, cd_dst, dm_dst != NULL, + fromlayers, tolayers); + + /* Mesh stores its dvert in a specific pointer too. :( */ + me_dst->dvert = CustomData_get_layer(&me_dst->vdata, CD_MDEFORMVERT); + return ret; } else if (cddata_type == CD_FAKE_SHAPEKEY) { /* TODO: leaving shapekeys asside for now, quite specific case, since we can't access them from MVert :/ */ @@ -799,6 +914,10 @@ static bool data_transfer_layersmapping_generate( if (cddata_type == CD_FAKE_UV) { cddata_type = CD_MLOOPUV; } + else if (cddata_type == CD_FAKE_LNOR) { + /* Preprocess should have generated it, Postprocess will convert it back to CD_CUSTOMLOOPNORMAL. */ + cddata_type = CD_NORMAL; + } if (!(cddata_type & CD_FAKE)) { cd_src = dm_src->getLoopDataLayout(dm_src); @@ -957,7 +1076,7 @@ bool BKE_object_data_transfer_dm( #define DATAMAX 4 DerivedMesh *dm_src; - Mesh *me_dst; + Mesh *me_dst, *me_src; bool dirty_nors_dst = true; /* Assumed always true if not using a dm as destination. */ int i; @@ -967,7 +1086,7 @@ bool BKE_object_data_transfer_dm( MeshPairRemap geom_map[DATAMAX] = {{0}}; bool geom_map_init[DATAMAX] = {0}; - ListBase lay_map = {0}; + ListBase lay_map = {NULL}; bool changed = false; const bool use_delete = false; /* We never delete data layers from destination here. */ @@ -977,6 +1096,7 @@ bool BKE_object_data_transfer_dm( BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH)); me_dst = ob_dst->data; + me_src = ob_src->data; if (dm_dst) { dirty_nors_dst = (dm_dst->dirty & DM_DIRTY_NORMALS) != 0; use_create = false; /* Never create needed custom layers on DM (modifier case). */ @@ -1016,6 +1136,10 @@ bool BKE_object_data_transfer_dm( continue; } + data_transfer_dtdata_type_preprocess(ob_src, ob_dst, dm_src, dm_dst, me_dst, + dtdata_type, dirty_nors_dst, + (me_src->flag & ME_AUTOSMOOTH) != 0, me_src->smoothresh); + cddata_type = BKE_object_data_transfer_dttype_to_cdtype(dtdata_type); fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(dtdata_type); @@ -1133,8 +1257,10 @@ bool BKE_object_data_transfer_dm( map_loop_mode, space_transform, max_distance, ray_radius, verts_dst, num_verts_dst, edges_dst, num_edges_dst, loops_dst, num_loops_dst, polys_dst, num_polys_dst, - ldata_dst, pdata_dst, me_dst->smoothresh, dirty_nors_dst, - dm_src, island_callback, islands_handling_precision, &geom_map[LDATA]); + ldata_dst, pdata_dst, + (me_dst->flag & ME_AUTOSMOOTH) != 0, me_dst->smoothresh, dirty_nors_dst, + dm_src, (me_src->flag & ME_AUTOSMOOTH) != 0, me_src->smoothresh, + island_callback, islands_handling_precision, &geom_map[LDATA]); geom_map_init[LDATA] = true; } @@ -1208,6 +1334,8 @@ bool BKE_object_data_transfer_dm( BLI_freelistN(&lay_map); } } + + data_transfer_dtdata_type_postprocess(ob_src, ob_dst, dm_src, dm_dst, me_dst, dtdata_type, changed); } for (i = 0; i < DATAMAX; i++) { diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index 80db62801d6..e2bb326b226 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -542,7 +542,7 @@ static bool defgroup_find_name_dupe(const char *name, bDeformGroup *dg, Object * for (curdef = ob->defbase.first; curdef; curdef = curdef->next) { if (dg != curdef) { - if (!strcmp(curdef->name, name)) { + if (STREQ(curdef->name, name)) { return true; } } diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 58a7702828c..e268f95b8ac 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -370,7 +370,7 @@ static bool surface_duplicateNameExists(void *arg, const char *name) DynamicPaintSurface *surface = t_surface->canvas->surfaces.first; for (; surface; surface = surface->next) { - if (surface != t_surface && !strcmp(name, surface->name)) return true; + if (surface != t_surface && STREQ(name, surface->name)) return true; } return false; } @@ -1502,7 +1502,7 @@ static void dynamicPaint_setInitialColor(Scene *scene, DynamicPaintSurface *surf uv[0] = tface[i].uv[j][0] * 2.0f - 1.0f; uv[1] = tface[i].uv[j][1] * 2.0f - 1.0f; - multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage); + multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage, false); if (texres.tin > pPoint[*vert].alpha) { copy_v3_v3(pPoint[*vert].color, &texres.tr); @@ -1536,7 +1536,7 @@ static void dynamicPaint_setInitialColor(Scene *scene, DynamicPaintSurface *surf 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); + multitex_ext_safe(tex, uv_final, &texres, NULL, scene_color_manage, false); /* apply color */ copy_v3_v3(pPoint[i].color, &texres.tr); @@ -2707,7 +2707,7 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam if (format == R_IMF_IMTYPE_OPENEXR) format = R_IMF_IMTYPE_PNG; #endif BLI_strncpy(output_file, filename, sizeof(output_file)); - BKE_add_image_extension_from_type(output_file, format); + BKE_image_path_ensure_ext_from_imtype(output_file, format); /* Validate output file path */ BLI_path_abs(output_file, G.main->name); diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index eb7c78c2760..082edb01efd 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -169,12 +169,25 @@ static void emDM_calcNormals(DerivedMesh *dm) dm->dirty &= ~DM_DIRTY_NORMALS; } -static void emDM_calcLoopNormals(DerivedMesh *dm, const float split_angle) +static void emDM_calcLoopNormalsSpaceArray( + DerivedMesh *dm, const bool use_split_normals, const float split_angle, MLoopNorSpaceArray *r_lnors_spacearr); + +static void emDM_calcLoopNormals(DerivedMesh *dm, const bool use_split_normals, const float split_angle) +{ + emDM_calcLoopNormalsSpaceArray(dm, use_split_normals, split_angle, NULL); +} + +/* #define DEBUG_CLNORS */ + +static void emDM_calcLoopNormalsSpaceArray( + DerivedMesh *dm, const bool use_split_normals, const float split_angle, MLoopNorSpaceArray *r_lnors_spacearr) { EditDerivedBMesh *bmdm = (EditDerivedBMesh *)dm; BMesh *bm = bmdm->em->bm; const float (*vertexCos)[3], (*vertexNos)[3], (*polyNos)[3]; float (*loopNos)[3]; + short (*clnors_data)[2]; + int cd_loop_clnors_offset; /* calculate loop normals from poly and vertex normals */ emDM_ensureVertNormals(bmdm); @@ -191,7 +204,37 @@ static void emDM_calcLoopNormals(DerivedMesh *dm, const float split_angle) loopNos = dm->getLoopDataArray(dm, CD_NORMAL); } - BM_loops_calc_normal_vcos(bm, vertexCos, vertexNos, polyNos, split_angle, loopNos); + /* We can have both, give priority to dm's data, and fallback to bm's ones. */ + clnors_data = dm->getLoopDataArray(dm, CD_CUSTOMLOOPNORMAL); + cd_loop_clnors_offset = clnors_data ? -1 : CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + + BM_loops_calc_normal_vcos(bm, vertexCos, vertexNos, polyNos, use_split_normals, split_angle, loopNos, + r_lnors_spacearr, clnors_data, cd_loop_clnors_offset); +#ifdef DEBUG_CLNORS + if (r_lnors_spacearr) { + int i; + for (i = 0; i < numLoops; i++) { + if (r_lnors_spacearr->lspacearr[i]->ref_alpha != 0.0f) { + LinkNode *loops = r_lnors_spacearr->lspacearr[i]->loops; + printf("Loop %d uses lnor space %p:\n", i, r_lnors_spacearr->lspacearr[i]); + print_v3("\tfinal lnor:", loopNos[i]); + print_v3("\tauto lnor:", r_lnors_spacearr->lspacearr[i]->vec_lnor); + print_v3("\tref_vec:", r_lnors_spacearr->lspacearr[i]->vec_ref); + printf("\talpha: %f\n\tbeta: %f\n\tloops: %p\n", r_lnors_spacearr->lspacearr[i]->ref_alpha, + r_lnors_spacearr->lspacearr[i]->ref_beta, r_lnors_spacearr->lspacearr[i]->loops); + printf("\t\t(shared with loops"); + while (loops) { + printf(" %d", GET_INT_FROM_POINTER(loops->link)); + loops = loops->next; + } + printf(")\n"); + } + else { + printf("Loop %d has no lnor space\n", i); + } + } + } +#endif } static void emDM_recalcTessellation(DerivedMesh *UNUSED(dm)) @@ -1764,6 +1807,7 @@ DerivedMesh *getEditDerivedBMesh(BMEditMesh *em, bmdm->dm.calcNormals = emDM_calcNormals; bmdm->dm.calcLoopNormals = emDM_calcLoopNormals; + bmdm->dm.calcLoopNormalsSpaceArray = emDM_calcLoopNormalsSpaceArray; bmdm->dm.recalcTessellation = emDM_recalcTessellation; bmdm->dm.foreachMappedVert = emDM_foreachMappedVert; diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index ced9da8d0b1..76a39fed3f6 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -30,6 +30,7 @@ */ #include <stddef.h> +#include <stdarg.h> #include <math.h> #include <stdlib.h> @@ -51,6 +52,7 @@ #include "BLI_noise.h" #include "BLI_rand.h" #include "BLI_utildefines.h" +#include "BLI_ghash.h" #include "PIL_time.h" @@ -61,6 +63,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_cdderivedmesh.h" #include "BKE_effect.h" +#include "BKE_global.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" @@ -754,7 +757,7 @@ static void do_texture_effector(EffectorCache *eff, EffectorData *efd, EffectedP scene_color_manage = BKE_scene_check_color_management_enabled(eff->scene); - hasrgb = multitex_ext(eff->pd->tex, tex_co, NULL, NULL, 0, result, NULL, scene_color_manage); + hasrgb = multitex_ext(eff->pd->tex, tex_co, NULL, NULL, 0, result, NULL, scene_color_manage, false); if (hasrgb && mode==PFIELD_TEX_RGB) { force[0] = (0.5f - result->tr) * strength; @@ -765,15 +768,15 @@ static void do_texture_effector(EffectorCache *eff, EffectorData *efd, EffectedP strength/=nabla; tex_co[0] += nabla; - multitex_ext(eff->pd->tex, tex_co, NULL, NULL, 0, result+1, NULL, scene_color_manage); + multitex_ext(eff->pd->tex, tex_co, NULL, NULL, 0, result+1, NULL, scene_color_manage, false); tex_co[0] -= nabla; tex_co[1] += nabla; - multitex_ext(eff->pd->tex, tex_co, NULL, NULL, 0, result+2, NULL, scene_color_manage); + multitex_ext(eff->pd->tex, tex_co, NULL, NULL, 0, result+2, NULL, scene_color_manage, false); tex_co[1] -= nabla; tex_co[2] += nabla; - multitex_ext(eff->pd->tex, tex_co, NULL, NULL, 0, result+3, NULL, scene_color_manage); + multitex_ext(eff->pd->tex, tex_co, NULL, NULL, 0, result+3, NULL, scene_color_manage, false); if (mode == PFIELD_TEX_GRAD || !hasrgb) { /* if we don't have rgb fall back to grad */ /* generate intensity if texture only has rgb value */ @@ -1024,3 +1027,160 @@ void pdDoEffectors(ListBase *effectors, ListBase *colliders, EffectorWeights *we } } } + +/* ======== Simulation Debugging ======== */ + +SimDebugData *_sim_debug_data = NULL; + +unsigned int BKE_sim_debug_data_hash(int i) +{ + return BLI_ghashutil_uinthash((unsigned int)i); +} + +unsigned int BKE_sim_debug_data_hash_combine(unsigned int kx, unsigned int ky) +{ +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + + unsigned int a, b, c; + + a = b = c = 0xdeadbeef + (2 << 2) + 13; + a += kx; + b += ky; + + 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); + + return c; + +#undef rot +} + +static unsigned int debug_element_hash(const void *key) +{ + const SimDebugElement *elem = key; + return elem->hash; +} + +static bool debug_element_compare(const void *a, const void *b) +{ + const SimDebugElement *elem1 = a; + const SimDebugElement *elem2 = b; + + if (elem1->hash == elem2->hash) { + return 0; + } + return 1; +} + +static void debug_element_free(void *val) +{ + SimDebugElement *elem = val; + MEM_freeN(elem); +} + +void BKE_sim_debug_data_set_enabled(bool enable) +{ + if (enable) { + if (!_sim_debug_data) { + _sim_debug_data = MEM_callocN(sizeof(SimDebugData), "sim debug data"); + _sim_debug_data->gh = BLI_ghash_new(debug_element_hash, debug_element_compare, "sim debug element hash"); + } + } + else { + BKE_sim_debug_data_free(); + } +} + +bool BKE_sim_debug_data_get_enabled(void) +{ + return _sim_debug_data != NULL; +} + +void BKE_sim_debug_data_free(void) +{ + if (_sim_debug_data) { + if (_sim_debug_data->gh) + BLI_ghash_free(_sim_debug_data->gh, NULL, debug_element_free); + MEM_freeN(_sim_debug_data); + } +} + +static void debug_data_insert(SimDebugData *debug_data, SimDebugElement *elem) +{ + SimDebugElement *old_elem = BLI_ghash_lookup(debug_data->gh, elem); + if (old_elem) { + *old_elem = *elem; + MEM_freeN(elem); + } + else + BLI_ghash_insert(debug_data->gh, elem, elem); +} + +void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[3], float r, float g, float b, const char *category, unsigned int hash) +{ + unsigned int category_hash = BLI_ghashutil_strhash_p(category); + SimDebugElement *elem; + + if (!_sim_debug_data) { + if (G.debug & G_DEBUG_SIMDATA) + BKE_sim_debug_data_set_enabled(true); + else + return; + } + + elem = MEM_callocN(sizeof(SimDebugElement), "sim debug data element"); + elem->type = type; + elem->category_hash = category_hash; + elem->hash = hash; + elem->color[0] = r; + elem->color[1] = g; + elem->color[2] = b; + copy_v3_v3(elem->v1, v1); + copy_v3_v3(elem->v2, v2); + + debug_data_insert(_sim_debug_data, elem); +} + +void BKE_sim_debug_data_remove_element(unsigned int hash) +{ + SimDebugElement dummy; + if (!_sim_debug_data) + return; + + dummy.hash = hash; + BLI_ghash_remove(_sim_debug_data->gh, &dummy, NULL, debug_element_free); +} + +void BKE_sim_debug_data_clear(void) +{ + if (!_sim_debug_data) + return; + + if (_sim_debug_data->gh) + BLI_ghash_clear(_sim_debug_data->gh, NULL, debug_element_free); +} + +void BKE_sim_debug_data_clear_category(const char *category) +{ + int category_hash = (int)BLI_ghashutil_strhash_p(category); + + if (!_sim_debug_data) + return; + + if (_sim_debug_data->gh) { + GHashIterator iter; + BLI_ghashIterator_init(&iter, _sim_debug_data->gh); + while (!BLI_ghashIterator_done(&iter)) { + SimDebugElement *elem = BLI_ghashIterator_getValue(&iter); + BLI_ghashIterator_step(&iter); /* removing invalidates the current iterator, so step before removing */ + + if (elem->category_hash == category_hash) + BLI_ghash_remove(_sim_debug_data->gh, elem, NULL, debug_element_free); + } + } +} diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index b50e563775e..765917794e8 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -230,7 +230,7 @@ FCurve *list_find_fcurve(ListBase *list, const char rna_path[], const int array_ /* check paths of curves, then array indices... */ for (fcu = list->first; fcu; fcu = fcu->next) { /* simple string-compare (this assumes that they have the same root...) */ - if (fcu->rna_path && !strcmp(fcu->rna_path, rna_path)) { + if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { /* now check indices */ if (fcu->array_index == array_index) return fcu; @@ -253,7 +253,7 @@ FCurve *iter_step_fcurve(FCurve *fcu_iter, const char rna_path[]) /* check paths of curves, then array indices... */ for (fcu = fcu_iter; fcu; fcu = fcu->next) { /* simple string-compare (this assumes that they have the same root...) */ - if (fcu->rna_path && !strcmp(fcu->rna_path, rna_path)) { + if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { return fcu; } } @@ -290,7 +290,7 @@ int list_find_data_fcurves(ListBase *dst, ListBase *src, const char *dataPrefix, if (quotedName) { /* check if the quoted name matches the required name */ - if (strcmp(quotedName, dataName) == 0) { + if (STREQ(quotedName, dataName)) { LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); ld->data = fcu; @@ -820,7 +820,7 @@ void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSample printf("Error: No F-Curve with F-Curve Modifiers to Bake\n"); return; } - if (start >= end) { + if (start > end) { printf("Error: Frame range for Sampled F-Curve creation is inappropriate\n"); return; } diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 8d4bb7ec058..2adf8ebc615 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -245,7 +245,7 @@ VFont *BKE_vfont_load(Main *bmain, const char *name) } /* Do not add FO_BUILTIN_NAME to temporary listbase */ - if (strcmp(filename, FO_BUILTIN_NAME)) { + if (!STREQ(filename, FO_BUILTIN_NAME)) { vfont->temp_pf = temp_pf; } } diff --git a/source/blender/blenkernel/intern/group.c b/source/blender/blenkernel/intern/group.c index 1f9cf2e11b7..ae3ab833a87 100644 --- a/source/blender/blenkernel/intern/group.c +++ b/source/blender/blenkernel/intern/group.c @@ -219,6 +219,43 @@ static int group_object_unlink_internal(Group *group, Object *ob) return removed; } +static bool group_object_cyclic_check_internal(Object *object, Group *group) +{ + if (object->dup_group) { + Group *dup_group = object->dup_group; + if ((dup_group->id.flag & LIB_DOIT) == 0) { + /* Cycle already exists in groups, let's prevent further crappyness */ + return true; + } + /* flag the object to identify cyclic dependencies in further dupli groups */ + dup_group->id.flag &= ~LIB_DOIT; + + if (dup_group == group) + return true; + else { + GroupObject *gob; + for (gob = dup_group->gobject.first; gob; gob = gob->next) { + if (group_object_cyclic_check_internal(gob->ob, group)) { + return true; + } + } + } + + /* un-flag the object, it's allowed to have the same group multiple times in parallel */ + dup_group->id.flag |= LIB_DOIT; + } + + return false; +} + +bool BKE_group_object_cyclic_check(Main *bmain, Object *object, Group *group) +{ + /* first flag all groups */ + BKE_main_id_tag_listbase(&bmain->group, true); + + return group_object_cyclic_check_internal(object, group); +} + bool BKE_group_object_unlink(Group *group, Object *object, Scene *scene, Base *base) { if (group_object_unlink_internal(group, object)) { diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 2b99b5f4620..4d83f8cf916 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -804,7 +804,7 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is case IDP_STRING: { return (((prop1->len == prop2->len) && - strncmp(IDP_String(prop1), IDP_String(prop2), (size_t)prop1->len) == 0)); + STREQLEN(IDP_String(prop1), IDP_String(prop2), (size_t)prop1->len))); } case IDP_ARRAY: if (prop1->len == prop2->len && prop1->subtype == prop2->subtype) { diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 870c077ff78..131a19b8108 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -803,7 +803,9 @@ Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int hei return ima; } -/* creates an image image owns the imbuf passed */ +/* Create an image image from ibuf. The refcount of ibuf is increased, + * caller should take care to drop its reference by calling + * IMB_freeImBuf if needed. */ Image *BKE_image_add_from_imbuf(ImBuf *ibuf) { /* on save, type is changed to FILE in editsima.c */ @@ -860,14 +862,14 @@ void BKE_image_memorypack(Image *ima) void BKE_image_tag_time(Image *ima) { - ima->lastused = (int)PIL_check_seconds_timer(); + ima->lastused = PIL_check_seconds_timer_i(); } #if 0 static void tag_all_images_time() { Image *ima; - int ctime = (int)PIL_check_seconds_timer(); + int ctime = PIL_check_seconds_timer_i(); ima = G.main->image.first; while (ima) { @@ -1011,7 +1013,7 @@ void BKE_image_all_free_anim_ibufs(int cfra) /* *********** READ AND WRITE ************** */ -int BKE_imtype_to_ftype(const char imtype) +int BKE_image_imtype_to_ftype(const char imtype) { if (imtype == R_IMF_IMTYPE_TARGA) return TGA; @@ -1051,7 +1053,7 @@ int BKE_imtype_to_ftype(const char imtype) return JPG | 90; } -char BKE_ftype_to_imtype(const int ftype) +char BKE_image_ftype_to_imtype(const int ftype) { if (ftype == 0) return R_IMF_IMTYPE_TARGA; @@ -1223,42 +1225,42 @@ char BKE_imtype_valid_depths(const char imtype) * creator.c help info */ char BKE_imtype_from_arg(const char *imtype_arg) { - if (!strcmp(imtype_arg, "TGA")) return R_IMF_IMTYPE_TARGA; - else if (!strcmp(imtype_arg, "IRIS")) return R_IMF_IMTYPE_IRIS; + if (STREQ(imtype_arg, "TGA")) return R_IMF_IMTYPE_TARGA; + else if (STREQ(imtype_arg, "IRIS")) return R_IMF_IMTYPE_IRIS; #ifdef WITH_DDS - else if (!strcmp(imtype_arg, "DDS")) return R_IMF_IMTYPE_DDS; + else if (STREQ(imtype_arg, "DDS")) return R_IMF_IMTYPE_DDS; #endif - else if (!strcmp(imtype_arg, "JPEG")) return R_IMF_IMTYPE_JPEG90; - else if (!strcmp(imtype_arg, "IRIZ")) return R_IMF_IMTYPE_IRIZ; - else if (!strcmp(imtype_arg, "RAWTGA")) return R_IMF_IMTYPE_RAWTGA; - else if (!strcmp(imtype_arg, "AVIRAW")) return R_IMF_IMTYPE_AVIRAW; - else if (!strcmp(imtype_arg, "AVIJPEG")) return R_IMF_IMTYPE_AVIJPEG; - else if (!strcmp(imtype_arg, "PNG")) return R_IMF_IMTYPE_PNG; - else if (!strcmp(imtype_arg, "QUICKTIME")) return R_IMF_IMTYPE_QUICKTIME; - else if (!strcmp(imtype_arg, "BMP")) return R_IMF_IMTYPE_BMP; + else if (STREQ(imtype_arg, "JPEG")) return R_IMF_IMTYPE_JPEG90; + else if (STREQ(imtype_arg, "IRIZ")) return R_IMF_IMTYPE_IRIZ; + else if (STREQ(imtype_arg, "RAWTGA")) return R_IMF_IMTYPE_RAWTGA; + else if (STREQ(imtype_arg, "AVIRAW")) return R_IMF_IMTYPE_AVIRAW; + else if (STREQ(imtype_arg, "AVIJPEG")) return R_IMF_IMTYPE_AVIJPEG; + else if (STREQ(imtype_arg, "PNG")) return R_IMF_IMTYPE_PNG; + else if (STREQ(imtype_arg, "QUICKTIME")) return R_IMF_IMTYPE_QUICKTIME; + else if (STREQ(imtype_arg, "BMP")) return R_IMF_IMTYPE_BMP; #ifdef WITH_HDR - else if (!strcmp(imtype_arg, "HDR")) return R_IMF_IMTYPE_RADHDR; + else if (STREQ(imtype_arg, "HDR")) return R_IMF_IMTYPE_RADHDR; #endif #ifdef WITH_TIFF - else if (!strcmp(imtype_arg, "TIFF")) return R_IMF_IMTYPE_TIFF; + else if (STREQ(imtype_arg, "TIFF")) return R_IMF_IMTYPE_TIFF; #endif #ifdef WITH_OPENEXR - else if (!strcmp(imtype_arg, "EXR")) return R_IMF_IMTYPE_OPENEXR; - else if (!strcmp(imtype_arg, "MULTILAYER")) return R_IMF_IMTYPE_MULTILAYER; + else if (STREQ(imtype_arg, "EXR")) return R_IMF_IMTYPE_OPENEXR; + else if (STREQ(imtype_arg, "MULTILAYER")) return R_IMF_IMTYPE_MULTILAYER; #endif - else if (!strcmp(imtype_arg, "MPEG")) return R_IMF_IMTYPE_FFMPEG; - else if (!strcmp(imtype_arg, "FRAMESERVER")) return R_IMF_IMTYPE_FRAMESERVER; + else if (STREQ(imtype_arg, "MPEG")) return R_IMF_IMTYPE_FFMPEG; + else if (STREQ(imtype_arg, "FRAMESERVER")) return R_IMF_IMTYPE_FRAMESERVER; #ifdef WITH_CINEON - else if (!strcmp(imtype_arg, "CINEON")) return R_IMF_IMTYPE_CINEON; - else if (!strcmp(imtype_arg, "DPX")) return R_IMF_IMTYPE_DPX; + else if (STREQ(imtype_arg, "CINEON")) return R_IMF_IMTYPE_CINEON; + else if (STREQ(imtype_arg, "DPX")) return R_IMF_IMTYPE_DPX; #endif #ifdef WITH_OPENJPEG - else if (!strcmp(imtype_arg, "JP2")) return R_IMF_IMTYPE_JP2; + else if (STREQ(imtype_arg, "JP2")) return R_IMF_IMTYPE_JP2; #endif else return R_IMF_IMTYPE_INVALID; } -static bool do_add_image_extension(char *string, const char imtype, const ImageFormatData *im_format) +static bool image_path_ensure_ext(char *string, const char imtype, const ImageFormatData *im_format) { const char *extension = NULL; const char *extension_test; @@ -1368,14 +1370,14 @@ static bool do_add_image_extension(char *string, const char imtype, const ImageF } } -int BKE_add_image_extension(char *string, const ImageFormatData *im_format) +bool BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) { - return do_add_image_extension(string, im_format->imtype, im_format); + return image_path_ensure_ext(string, im_format->imtype, im_format); } -int BKE_add_image_extension_from_type(char *string, const char imtype) +bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) { - return do_add_image_extension(string, imtype, NULL); + return image_path_ensure_ext(string, imtype, NULL); } void BKE_imformat_defaults(ImageFormatData *im_format) @@ -1651,7 +1653,9 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d } } -void BKE_stamp_buf(Scene *scene, Object *camera, unsigned char *rect, float *rectf, int width, int height, int channels) +void BKE_image_stamp_buf( + Scene *scene, Object *camera, + unsigned char *rect, float *rectf, int width, int height, int channels) { struct StampData stamp_data; float w, h, pad; @@ -2076,8 +2080,9 @@ int BKE_imbuf_write_stamp(Scene *scene, struct Object *camera, ImBuf *ibuf, cons } -static void do_makepicstring(char *string, const char *base, const char *relbase, int frame, const char imtype, - const ImageFormatData *im_format, const short use_ext, const short use_frames) +static void image_path_makepicstring( + char *string, const char *base, const char *relbase, int frame, const char imtype, + const ImageFormatData *im_format, const short use_ext, const short use_frames) { if (string == NULL) return; BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ @@ -2087,19 +2092,29 @@ static void do_makepicstring(char *string, const char *base, const char *relbase BLI_path_frame(string, frame, 4); if (use_ext) - do_add_image_extension(string, imtype, im_format); + image_path_ensure_ext(string, imtype, im_format); } -void BKE_makepicstring(char *string, const char *base, const char *relbase, int frame, - const ImageFormatData *im_format, const bool use_ext, const bool use_frames) +void BKE_image_path_from_imformat( + char *string, const char *base, const char *relbase, int frame, + const ImageFormatData *im_format, const bool use_ext, const bool use_frames) { - do_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames); + image_path_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames); } -void BKE_makepicstring_from_type(char *string, const char *base, const char *relbase, int frame, - const char imtype, const bool use_ext, const bool use_frames) +void BKE_image_path_from_imtype( + char *string, const char *base, const char *relbase, int frame, + const char imtype, const bool use_ext, const bool use_frames) { - do_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames); + image_path_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames); +} + +struct anim *openanim_noload(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]) +{ + struct anim *anim; + + anim = IMB_open_anim(name, flags, streamindex, colorspace); + return anim; } /* used by sequencer too */ @@ -2167,11 +2182,6 @@ Image *BKE_image_verify_viewer(int type, const char *name) return ima; } -void BKE_image_assign_ibuf(Image *ima, ImBuf *ibuf) -{ - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); -} - void BKE_image_walk_all_users(const Main *mainp, void *customdata, void callback(Image *ima, ImageUser *iuser, void *customdata)) { @@ -2595,11 +2605,15 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) if (ima->anim == NULL) { char str[FILE_MAX]; + int flags = IB_rect; + if (ima->flag & IMA_DEINTERLACE) { + flags |= IB_animdeinterlace; + } BKE_image_user_file_path(iuser, ima, str); /* FIXME: make several stream accessible in image editor, too*/ - ima->anim = openanim(str, IB_rect, 0, ima->colorspace_settings.name); + ima->anim = openanim(str, flags, 0, ima->colorspace_settings.name); /* let's initialize this user */ if (ima->anim && iuser && iuser->frames == 0) diff --git a/source/blender/blenkernel/intern/implicit.c b/source/blender/blenkernel/intern/implicit.c deleted file mode 100644 index fe64af184a9..00000000000 --- a/source/blender/blenkernel/intern/implicit.c +++ /dev/null @@ -1,1770 +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) Blender Foundation - * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): none yet. - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/blenkernel/intern/implicit.c - * \ingroup bke - */ - - -#include "MEM_guardedalloc.h" - -#include "DNA_scene_types.h" -#include "DNA_object_types.h" -#include "DNA_object_force.h" -#include "DNA_meshdata_types.h" - -#include "BLI_math.h" -#include "BLI_linklist.h" -#include "BLI_utildefines.h" - -#include "BKE_cloth.h" -#include "BKE_collision.h" -#include "BKE_effect.h" -#include "BKE_global.h" - - -#ifdef __GNUC__ -# pragma GCC diagnostic ignored "-Wtype-limits" -#endif - -#ifdef _OPENMP -# define CLOTH_OPENMP_LIMIT 512 -#endif - -#if 0 /* debug timing */ -#ifdef _WIN32 -#include <windows.h> -static LARGE_INTEGER _itstart, _itend; -static LARGE_INTEGER ifreq; -static void itstart(void) -{ - static int first = 1; - if (first) { - QueryPerformanceFrequency(&ifreq); - first = 0; - } - QueryPerformanceCounter(&_itstart); -} -static void itend(void) -{ - QueryPerformanceCounter(&_itend); -} -double itval(void) -{ - return ((double)_itend.QuadPart - - (double)_itstart.QuadPart)/((double)ifreq.QuadPart); -} -#else -#include <sys/time.h> -// intrinsics need better compile flag checking -// #include <xmmintrin.h> -// #include <pmmintrin.h> -// #include <pthread.h> - -static struct timeval _itstart, _itend; -static struct timezone itz; -static void itstart(void) -{ - gettimeofday(&_itstart, &itz); -} -static void itend(void) -{ - gettimeofday(&_itend, &itz); -} -static double itval(void) -{ - double t1, t2; - t1 = (double)_itstart.tv_sec + (double)_itstart.tv_usec/(1000*1000); - t2 = (double)_itend.tv_sec + (double)_itend.tv_usec/(1000*1000); - return t2-t1; -} -#endif -#endif /* debug timing */ - -static float I[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; -static float ZERO[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - -/* -#define C99 -#ifdef C99 -#defineDO_INLINE inline -#else -#defineDO_INLINE static -#endif -*/ -struct Cloth; - -////////////////////////////////////////// -/* fast vector / matrix library, enhancements are welcome :) -dg */ -///////////////////////////////////////// - -/* DEFINITIONS */ -typedef float lfVector[3]; -typedef struct fmatrix3x3 { - float m[3][3]; /* 3x3 matrix */ - unsigned int c, r; /* column and row number */ - /* int pinned; // is this vertex allowed to move? */ - float n1, n2, n3; /* three normal vectors for collision constrains */ - unsigned int vcount; /* vertex count */ - unsigned int scount; /* spring count */ -} fmatrix3x3; - -/////////////////////////// -// float[3] vector -/////////////////////////// -/* simple vector code */ -/* STATUS: verified */ -DO_INLINE void mul_fvector_S(float to[3], float from[3], float scalar) -{ - to[0] = from[0] * scalar; - to[1] = from[1] * scalar; - to[2] = from[2] * scalar; -} -/* simple v^T * v product ("outer product") */ -/* STATUS: HAS TO BE verified (*should* work) */ -DO_INLINE void mul_fvectorT_fvector(float to[3][3], float vectorA[3], float vectorB[3]) -{ - mul_fvector_S(to[0], vectorB, vectorA[0]); - mul_fvector_S(to[1], vectorB, vectorA[1]); - mul_fvector_S(to[2], vectorB, vectorA[2]); -} -/* simple v^T * v product with scalar ("outer product") */ -/* STATUS: HAS TO BE verified (*should* work) */ -DO_INLINE void mul_fvectorT_fvectorS(float to[3][3], float vectorA[3], float vectorB[3], float aS) -{ - mul_fvectorT_fvector(to, vectorA, vectorB); - - mul_fvector_S(to[0], to[0], aS); - mul_fvector_S(to[1], to[1], aS); - mul_fvector_S(to[2], to[2], aS); -} - -#if 0 -/* printf vector[3] on console: for debug output */ -static void print_fvector(float m3[3]) -{ - printf("%f\n%f\n%f\n\n", m3[0], m3[1], m3[2]); -} - -/////////////////////////// -// long float vector float (*)[3] -/////////////////////////// -/* print long vector on console: for debug output */ -DO_INLINE void print_lfvector(float (*fLongVector)[3], unsigned int verts) -{ - unsigned int i = 0; - for (i = 0; i < verts; i++) { - print_fvector(fLongVector[i]); - } -} -#endif - -/* create long vector */ -DO_INLINE lfVector *create_lfvector(unsigned int verts) -{ - /* TODO: check if memory allocation was successful */ - return (lfVector *)MEM_callocN(verts * sizeof(lfVector), "cloth_implicit_alloc_vector"); - // return (lfVector *)cloth_aligned_malloc(&MEMORY_BASE, verts * sizeof(lfVector)); -} -/* delete long vector */ -DO_INLINE void del_lfvector(float (*fLongVector)[3]) -{ - if (fLongVector != NULL) { - MEM_freeN(fLongVector); - // cloth_aligned_free(&MEMORY_BASE, fLongVector); - } -} -/* copy long vector */ -DO_INLINE void cp_lfvector(float (*to)[3], float (*from)[3], unsigned int verts) -{ - memcpy(to, from, verts * sizeof(lfVector)); -} -/* init long vector with float[3] */ -DO_INLINE void init_lfvector(float (*fLongVector)[3], float vector[3], unsigned int verts) -{ - unsigned int i = 0; - for (i = 0; i < verts; i++) { - copy_v3_v3(fLongVector[i], vector); - } -} -/* zero long vector with float[3] */ -DO_INLINE void zero_lfvector(float (*to)[3], unsigned int verts) -{ - memset(to, 0.0f, verts * sizeof(lfVector)); -} -/* multiply long vector with scalar*/ -DO_INLINE void mul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, unsigned int verts) -{ - unsigned int i = 0; - - for (i = 0; i < verts; i++) { - mul_fvector_S(to[i], fLongVector[i], scalar); - } -} -/* multiply long vector with scalar*/ -/* A -= B * float */ -DO_INLINE void submul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, unsigned int verts) -{ - unsigned int i = 0; - for (i = 0; i < verts; i++) { - VECSUBMUL(to[i], fLongVector[i], scalar); - } -} -/* dot product for big vector */ -DO_INLINE float dot_lfvector(float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) -{ - long i = 0; - float temp = 0.0; -// XXX brecht, disabled this for now (first schedule line was already disabled), -// due to non-commutative nature of floating point ops this makes the sim give -// different results each time you run it! -// schedule(guided, 2) -//#pragma omp parallel for reduction(+: temp) if (verts > CLOTH_OPENMP_LIMIT) - for (i = 0; i < (long)verts; i++) { - temp += dot_v3v3(fLongVectorA[i], fLongVectorB[i]); - } - return temp; -} -/* A = B + C --> for big vector */ -DO_INLINE void add_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) -{ - unsigned int i = 0; - - for (i = 0; i < verts; i++) { - VECADD(to[i], fLongVectorA[i], fLongVectorB[i]); - } - -} -/* A = B + C * float --> for big vector */ -DO_INLINE void add_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], float bS, unsigned int verts) -{ - unsigned int i = 0; - - for (i = 0; i < verts; i++) { - VECADDS(to[i], fLongVectorA[i], fLongVectorB[i], bS); - - } -} -/* A = B * float + C * float --> for big vector */ -DO_INLINE void add_lfvectorS_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float aS, float (*fLongVectorB)[3], float bS, unsigned int verts) -{ - unsigned int i = 0; - - for (i = 0; i < verts; i++) { - VECADDSS(to[i], fLongVectorA[i], aS, fLongVectorB[i], bS); - } -} -/* A = B - C * float --> for big vector */ -DO_INLINE void sub_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], float bS, unsigned int verts) -{ - unsigned int i = 0; - for (i = 0; i < verts; i++) { - VECSUBS(to[i], fLongVectorA[i], fLongVectorB[i], bS); - } - -} -/* A = B - C --> for big vector */ -DO_INLINE void sub_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) -{ - unsigned int i = 0; - - for (i = 0; i < verts; i++) { - sub_v3_v3v3(to[i], fLongVectorA[i], fLongVectorB[i]); - } - -} -/////////////////////////// -// 3x3 matrix -/////////////////////////// -#if 0 -/* printf 3x3 matrix on console: for debug output */ -static void print_fmatrix(float m3[3][3]) -{ - printf("%f\t%f\t%f\n", m3[0][0], m3[0][1], m3[0][2]); - printf("%f\t%f\t%f\n", m3[1][0], m3[1][1], m3[1][2]); - printf("%f\t%f\t%f\n\n", m3[2][0], m3[2][1], m3[2][2]); -} -#endif - -/* copy 3x3 matrix */ -DO_INLINE void cp_fmatrix(float to[3][3], float from[3][3]) -{ - // memcpy(to, from, sizeof (float) * 9); - copy_v3_v3(to[0], from[0]); - copy_v3_v3(to[1], from[1]); - copy_v3_v3(to[2], from[2]); -} - -/* copy 3x3 matrix */ -DO_INLINE void initdiag_fmatrixS(float to[3][3], float aS) -{ - cp_fmatrix(to, ZERO); - - to[0][0] = aS; - to[1][1] = aS; - to[2][2] = aS; -} - -#if 0 -/* calculate determinant of 3x3 matrix */ -DO_INLINE float det_fmatrix(float m[3][3]) -{ - return m[0][0]*m[1][1]*m[2][2] + m[1][0]*m[2][1]*m[0][2] + m[0][1]*m[1][2]*m[2][0] - -m[0][0]*m[1][2]*m[2][1] - m[0][1]*m[1][0]*m[2][2] - m[2][0]*m[1][1]*m[0][2]; -} - -DO_INLINE void inverse_fmatrix(float to[3][3], float from[3][3]) -{ - unsigned int i, j; - float d; - - if ((d=det_fmatrix(from)) == 0) { - printf("can't build inverse"); - exit(0); - } - for (i=0;i<3;i++) { - for (j=0;j<3;j++) { - int i1=(i+1)%3; - int i2=(i+2)%3; - int j1=(j+1)%3; - int j2=(j+2)%3; - // reverse indexs i&j to take transpose - to[j][i] = (from[i1][j1]*from[i2][j2]-from[i1][j2]*from[i2][j1])/d; - /* - if (i==j) - to[i][j] = 1.0f / from[i][j]; - else - to[i][j] = 0; - */ - } - } - -} -#endif - -/* 3x3 matrix multiplied by a scalar */ -/* STATUS: verified */ -DO_INLINE void mul_fmatrix_S(float matrix[3][3], float scalar) -{ - mul_fvector_S(matrix[0], matrix[0], scalar); - mul_fvector_S(matrix[1], matrix[1], scalar); - mul_fvector_S(matrix[2], matrix[2], scalar); -} - -/* a vector multiplied by a 3x3 matrix */ -/* STATUS: verified */ -DO_INLINE void mul_fvector_fmatrix(float *to, float *from, float matrix[3][3]) -{ - to[0] = matrix[0][0]*from[0] + matrix[1][0]*from[1] + matrix[2][0]*from[2]; - to[1] = matrix[0][1]*from[0] + matrix[1][1]*from[1] + matrix[2][1]*from[2]; - to[2] = matrix[0][2]*from[0] + matrix[1][2]*from[1] + matrix[2][2]*from[2]; -} - -/* 3x3 matrix multiplied by a vector */ -/* STATUS: verified */ -DO_INLINE void mul_fmatrix_fvector(float *to, float matrix[3][3], float from[3]) -{ - to[0] = dot_v3v3(matrix[0], from); - to[1] = dot_v3v3(matrix[1], from); - to[2] = dot_v3v3(matrix[2], from); -} -/* 3x3 matrix addition with 3x3 matrix */ -DO_INLINE void add_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) -{ - VECADD(to[0], matrixA[0], matrixB[0]); - VECADD(to[1], matrixA[1], matrixB[1]); - VECADD(to[2], matrixA[2], matrixB[2]); -} -/* A -= B*x + C*y (3x3 matrix sub-addition with 3x3 matrix) */ -DO_INLINE void subadd_fmatrixS_fmatrixS(float to[3][3], float matrixA[3][3], float aS, float matrixB[3][3], float bS) -{ - VECSUBADDSS(to[0], matrixA[0], aS, matrixB[0], bS); - VECSUBADDSS(to[1], matrixA[1], aS, matrixB[1], bS); - VECSUBADDSS(to[2], matrixA[2], aS, matrixB[2], bS); -} -/* A = B - C (3x3 matrix subtraction with 3x3 matrix) */ -DO_INLINE void sub_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) -{ - sub_v3_v3v3(to[0], matrixA[0], matrixB[0]); - sub_v3_v3v3(to[1], matrixA[1], matrixB[1]); - sub_v3_v3v3(to[2], matrixA[2], matrixB[2]); -} -///////////////////////////////////////////////////////////////// -// special functions -///////////////////////////////////////////////////////////////// -/* 3x3 matrix multiplied+added by a vector */ -/* STATUS: verified */ -DO_INLINE void muladd_fmatrix_fvector(float to[3], float matrix[3][3], float from[3]) -{ - to[0] += dot_v3v3(matrix[0], from); - to[1] += dot_v3v3(matrix[1], from); - to[2] += dot_v3v3(matrix[2], from); -} -///////////////////////////////////////////////////////////////// - -/////////////////////////// -// SPARSE SYMMETRIC big matrix with 3x3 matrix entries -/////////////////////////// -/* printf a big matrix on console: for debug output */ -#if 0 -static void print_bfmatrix(fmatrix3x3 *m3) -{ - unsigned int i = 0; - - for (i = 0; i < m3[0].vcount + m3[0].scount; i++) - { - print_fmatrix(m3[i].m); - } -} -#endif - -/* create big matrix */ -DO_INLINE fmatrix3x3 *create_bfmatrix(unsigned int verts, unsigned int springs) -{ - // TODO: check if memory allocation was successful */ - fmatrix3x3 *temp = (fmatrix3x3 *)MEM_callocN(sizeof(fmatrix3x3) * (verts + springs), "cloth_implicit_alloc_matrix"); - temp[0].vcount = verts; - temp[0].scount = springs; - return temp; -} -/* delete big matrix */ -DO_INLINE void del_bfmatrix(fmatrix3x3 *matrix) -{ - if (matrix != NULL) { - MEM_freeN(matrix); - } -} - -/* copy big matrix */ -DO_INLINE void cp_bfmatrix(fmatrix3x3 *to, fmatrix3x3 *from) -{ - // TODO bounds checking - memcpy(to, from, sizeof(fmatrix3x3) * (from[0].vcount+from[0].scount)); -} - -/* init big matrix */ -// slow in parallel -DO_INLINE void init_bfmatrix(fmatrix3x3 *matrix, float m3[3][3]) -{ - unsigned int i; - - for (i = 0; i < matrix[0].vcount+matrix[0].scount; i++) { - cp_fmatrix(matrix[i].m, m3); - } -} - -/* init the diagonal of big matrix */ -// slow in parallel -DO_INLINE void initdiag_bfmatrix(fmatrix3x3 *matrix, float m3[3][3]) -{ - unsigned int i, j; - float tmatrix[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - - for (i = 0; i < matrix[0].vcount; i++) { - cp_fmatrix(matrix[i].m, m3); - } - for (j = matrix[0].vcount; j < matrix[0].vcount+matrix[0].scount; j++) { - cp_fmatrix(matrix[j].m, tmatrix); - } -} - -/* SPARSE SYMMETRIC multiply big matrix with long vector*/ -/* STATUS: verified */ -DO_INLINE void mul_bfmatrix_lfvector( float (*to)[3], fmatrix3x3 *from, lfVector *fLongVector) -{ - unsigned int i = 0; - unsigned int vcount = from[0].vcount; - lfVector *temp = create_lfvector(vcount); - - zero_lfvector(to, vcount); - -#pragma omp parallel sections private(i) if (vcount > CLOTH_OPENMP_LIMIT) - { -#pragma omp section - { - for (i = from[0].vcount; i < from[0].vcount+from[0].scount; i++) { - muladd_fmatrix_fvector(to[from[i].c], from[i].m, fLongVector[from[i].r]); - } - } -#pragma omp section - { - for (i = 0; i < from[0].vcount+from[0].scount; i++) { - muladd_fmatrix_fvector(temp[from[i].r], from[i].m, fLongVector[from[i].c]); - } - } - } - add_lfvector_lfvector(to, to, temp, from[0].vcount); - - del_lfvector(temp); - - -} - -/* SPARSE SYMMETRIC sub big matrix with big matrix*/ -/* A -= B * float + C * float --> for big matrix */ -/* VERIFIED */ -DO_INLINE void subadd_bfmatrixS_bfmatrixS( fmatrix3x3 *to, fmatrix3x3 *from, float aS, fmatrix3x3 *matrix, float bS) -{ - unsigned int i = 0; - - /* process diagonal elements */ - for (i = 0; i < matrix[0].vcount+matrix[0].scount; i++) { - subadd_fmatrixS_fmatrixS(to[i].m, from[i].m, aS, matrix[i].m, bS); - } - -} - -/////////////////////////////////////////////////////////////////// -// simulator start -/////////////////////////////////////////////////////////////////// -typedef struct Implicit_Data { - lfVector *X, *V, *Xnew, *Vnew, *olddV, *F, *B, *dV, *z; - fmatrix3x3 *A, *dFdV, *dFdX, *S, *P, *Pinv, *bigI, *M; -} Implicit_Data; - -/* Init constraint matrix */ -static void update_matrixS(ClothVertex *verts, int numverts, fmatrix3x3 *S) -{ - unsigned int pinned = 0; - int i = 0; - - /* Clear matrix from old vertex constraints */ - for (i = 0; i < S[0].vcount; i++) - S[i].c = S[i].r = 0; - - /* Set new vertex constraints */ - for (i = 0; i < numverts; i++) { - if (verts [i].flags & CLOTH_VERT_FLAG_PINNED) { - S[pinned].c = S[pinned].r = i; - pinned++; - } - } - - // S is special and needs specific vcount and scount - S[0].vcount = pinned; - S[0].scount = 0; -} - -int implicit_init(Object *UNUSED(ob), ClothModifierData *clmd) -{ - unsigned int i = 0; - Cloth *cloth = NULL; - ClothVertex *verts = NULL; - ClothSpring *spring = NULL; - Implicit_Data *id = NULL; - LinkNode *search = NULL; - - if (G.debug_value > 0) - printf("implicit_init\n"); - - // init memory guard - // BLI_listbase_clear(&MEMORY_BASE); - - cloth = (Cloth *)clmd->clothObject; - verts = cloth->verts; - - // create implicit base - id = (Implicit_Data *)MEM_callocN(sizeof(Implicit_Data), "implicit vecmat"); - cloth->implicit = id; - - /* process diagonal elements */ - id->A = create_bfmatrix(cloth->numverts, cloth->numsprings); - id->dFdV = create_bfmatrix(cloth->numverts, cloth->numsprings); - id->dFdX = create_bfmatrix(cloth->numverts, cloth->numsprings); - id->S = create_bfmatrix(cloth->numverts, 0); - id->Pinv = create_bfmatrix(cloth->numverts, cloth->numsprings); - id->P = create_bfmatrix(cloth->numverts, cloth->numsprings); - id->bigI = create_bfmatrix(cloth->numverts, cloth->numsprings); // TODO 0 springs - id->M = create_bfmatrix(cloth->numverts, cloth->numsprings); - id->X = create_lfvector(cloth->numverts); - id->Xnew = create_lfvector(cloth->numverts); - id->V = create_lfvector(cloth->numverts); - id->Vnew = create_lfvector(cloth->numverts); - id->olddV = create_lfvector(cloth->numverts); - zero_lfvector(id->olddV, cloth->numverts); - id->F = create_lfvector(cloth->numverts); - id->B = create_lfvector(cloth->numverts); - id->dV = create_lfvector(cloth->numverts); - id->z = create_lfvector(cloth->numverts); - - id->S[0].vcount = 0; - update_matrixS(verts, cloth->numverts, id->S); - - for (i = 0; i < cloth->numverts; i++) { - id->A[i].r = id->A[i].c = id->dFdV[i].r = id->dFdV[i].c = id->dFdX[i].r = id->dFdX[i].c = id->P[i].c = id->P[i].r = id->Pinv[i].c = id->Pinv[i].r = id->bigI[i].c = id->bigI[i].r = id->M[i].r = id->M[i].c = i; - - initdiag_fmatrixS(id->M[i].m, verts[i].mass); - } - - // init springs - search = cloth->springs; - for (i = 0; i < cloth->numsprings; i++) { - spring = search->link; - - // dFdV_start[i].r = big_I[i].r = big_zero[i].r = - id->A[i+cloth->numverts].r = id->dFdV[i+cloth->numverts].r = id->dFdX[i+cloth->numverts].r = - id->P[i+cloth->numverts].r = id->Pinv[i+cloth->numverts].r = id->bigI[i+cloth->numverts].r = id->M[i+cloth->numverts].r = spring->ij; - - // dFdV_start[i].c = big_I[i].c = big_zero[i].c = - id->A[i+cloth->numverts].c = id->dFdV[i+cloth->numverts].c = id->dFdX[i+cloth->numverts].c = - id->P[i+cloth->numverts].c = id->Pinv[i+cloth->numverts].c = id->bigI[i+cloth->numverts].c = id->M[i+cloth->numverts].c = spring->kl; - - spring->matrix_index = i + cloth->numverts; - - search = search->next; - } - - initdiag_bfmatrix(id->bigI, I); - - for (i = 0; i < cloth->numverts; i++) { - copy_v3_v3(id->X[i], verts[i].x); - } - - return 1; -} - -int implicit_free(ClothModifierData *clmd) -{ - Implicit_Data *id; - Cloth *cloth; - cloth = (Cloth *)clmd->clothObject; - - if (cloth) { - id = cloth->implicit; - - if (id) { - del_bfmatrix(id->A); - del_bfmatrix(id->dFdV); - del_bfmatrix(id->dFdX); - del_bfmatrix(id->S); - del_bfmatrix(id->P); - del_bfmatrix(id->Pinv); - del_bfmatrix(id->bigI); - del_bfmatrix(id->M); - - del_lfvector(id->X); - del_lfvector(id->Xnew); - del_lfvector(id->V); - del_lfvector(id->Vnew); - del_lfvector(id->olddV); - del_lfvector(id->F); - del_lfvector(id->B); - del_lfvector(id->dV); - del_lfvector(id->z); - - MEM_freeN(id); - } - } - - return 1; -} - -DO_INLINE float fb(float length, float L) -{ - float x = length / L; - float xx = x * x; - float xxx = xx * x; - float xxxx = xxx * x; - return (-11.541f * xxxx + 34.193f * xxx - 39.083f * xx + 23.116f * x - 9.713f); -} - -DO_INLINE float fbderiv(float length, float L) -{ - float x = length/L; - float xx = x * x; - float xxx = xx * x; - return (-46.164f * xxx + 102.579f * xx - 78.166f * x + 23.116f); -} - -DO_INLINE float fbstar(float length, float L, float kb, float cb) -{ - float tempfb_fl = kb * fb(length, L); - float fbstar_fl = cb * (length - L); - - if (tempfb_fl < fbstar_fl) - return fbstar_fl; - else - return tempfb_fl; -} - -// function to calculae bending spring force (taken from Choi & Co) -DO_INLINE float fbstar_jacobi(float length, float L, float kb, float cb) -{ - float tempfb_fl = kb * fb(length, L); - float fbstar_fl = cb * (length - L); - - if (tempfb_fl < fbstar_fl) { - return cb; - } - else { - return kb * fbderiv(length, L); - } -} - -DO_INLINE void filter(lfVector *V, fmatrix3x3 *S) -{ - unsigned int i=0; - - for (i = 0; i < S[0].vcount; i++) { - mul_fvector_fmatrix(V[S[i].r], V[S[i].r], S[i].m); - } -} - -static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S) -{ - // Solves for unknown X in equation AX=B - unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100; - float conjgrad_epsilon=0.0001f /* , conjgrad_lasterror=0 */ /* UNUSED */; - lfVector *q, *d, *tmp, *r; - float s, starget, a, s_prev; - unsigned int numverts = lA[0].vcount; - q = create_lfvector(numverts); - d = create_lfvector(numverts); - tmp = create_lfvector(numverts); - r = create_lfvector(numverts); - - // zero_lfvector(ldV, CLOTHPARTICLES); - filter(ldV, S); - - add_lfvector_lfvector(ldV, ldV, z, numverts); - - // r = B - Mul(tmp, A, X); // just use B if X known to be zero - cp_lfvector(r, lB, numverts); - mul_bfmatrix_lfvector(tmp, lA, ldV); - sub_lfvector_lfvector(r, r, tmp, numverts); - - filter(r, S); - - cp_lfvector(d, r, numverts); - - s = dot_lfvector(r, r, numverts); - starget = s * sqrtf(conjgrad_epsilon); - - while (s>starget && conjgrad_loopcount < conjgrad_looplimit) { - // Mul(q, A, d); // q = A*d; - mul_bfmatrix_lfvector(q, lA, d); - - filter(q, S); - - a = s/dot_lfvector(d, q, numverts); - - // X = X + d*a; - add_lfvector_lfvectorS(ldV, ldV, d, a, numverts); - - // r = r - q*a; - sub_lfvector_lfvectorS(r, r, q, a, numverts); - - s_prev = s; - s = dot_lfvector(r, r, numverts); - - //d = r+d*(s/s_prev); - add_lfvector_lfvectorS(d, r, d, (s/s_prev), numverts); - - filter(d, S); - - conjgrad_loopcount++; - } - /* conjgrad_lasterror = s; */ /* UNUSED */ - - del_lfvector(q); - del_lfvector(d); - del_lfvector(tmp); - del_lfvector(r); - // printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount); - - return conjgrad_loopcount<conjgrad_looplimit; // true means we reached desired accuracy in given time - ie stable -} - -#if 0 -// block diagonalizer -DO_INLINE void BuildPPinv(fmatrix3x3 *lA, fmatrix3x3 *P, fmatrix3x3 *Pinv) -{ - unsigned int i = 0; - - // Take only the diagonal blocks of A -// #pragma omp parallel for private(i) if (lA[0].vcount > CLOTH_OPENMP_LIMIT) - for (i = 0; i<lA[0].vcount; i++) { - // block diagonalizer - cp_fmatrix(P[i].m, lA[i].m); - inverse_fmatrix(Pinv[i].m, P[i].m); - - } -} -/* -// version 1.3 -static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, fmatrix3x3 *P, fmatrix3x3 *Pinv) -{ - unsigned int numverts = lA[0].vcount, iterations = 0, conjgrad_looplimit=100; - float delta0 = 0, deltaNew = 0, deltaOld = 0, alpha = 0; - float conjgrad_epsilon=0.0001; // 0.2 is dt for steps=5 - lfVector *r = create_lfvector(numverts); - lfVector *p = create_lfvector(numverts); - lfVector *s = create_lfvector(numverts); - lfVector *h = create_lfvector(numverts); - - BuildPPinv(lA, P, Pinv); - - filter(dv, S); - add_lfvector_lfvector(dv, dv, z, numverts); - - mul_bfmatrix_lfvector(r, lA, dv); - sub_lfvector_lfvector(r, lB, r, numverts); - filter(r, S); - - mul_prevfmatrix_lfvector(p, Pinv, r); - filter(p, S); - - deltaNew = dot_lfvector(r, p, numverts); - - delta0 = deltaNew * sqrt(conjgrad_epsilon); - - // itstart(); - - while ((deltaNew > delta0) && (iterations < conjgrad_looplimit)) - { - iterations++; - - mul_bfmatrix_lfvector(s, lA, p); - filter(s, S); - - alpha = deltaNew / dot_lfvector(p, s, numverts); - - add_lfvector_lfvectorS(dv, dv, p, alpha, numverts); - - add_lfvector_lfvectorS(r, r, s, -alpha, numverts); - - mul_prevfmatrix_lfvector(h, Pinv, r); - filter(h, S); - - deltaOld = deltaNew; - - deltaNew = dot_lfvector(r, h, numverts); - - add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts); - - filter(p, S); - - } - - // itend(); - // printf("cg_filtered_pre time: %f\n", (float)itval()); - - del_lfvector(h); - del_lfvector(s); - del_lfvector(p); - del_lfvector(r); - - printf("iterations: %d\n", iterations); - - return iterations<conjgrad_looplimit; -} -*/ -// version 1.4 -static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, fmatrix3x3 *P, fmatrix3x3 *Pinv, fmatrix3x3 *bigI) -{ - unsigned int numverts = lA[0].vcount, iterations = 0, conjgrad_looplimit=100; - float delta0 = 0, deltaNew = 0, deltaOld = 0, alpha = 0, tol = 0; - lfVector *r = create_lfvector(numverts); - lfVector *p = create_lfvector(numverts); - lfVector *s = create_lfvector(numverts); - lfVector *h = create_lfvector(numverts); - lfVector *bhat = create_lfvector(numverts); - lfVector *btemp = create_lfvector(numverts); - - BuildPPinv(lA, P, Pinv); - - initdiag_bfmatrix(bigI, I); - sub_bfmatrix_Smatrix(bigI, bigI, S); - - // x = Sx_0+(I-S)z - filter(dv, S); - add_lfvector_lfvector(dv, dv, z, numverts); - - // b_hat = S(b-A(I-S)z) - mul_bfmatrix_lfvector(r, lA, z); - mul_bfmatrix_lfvector(bhat, bigI, r); - sub_lfvector_lfvector(bhat, lB, bhat, numverts); - - // r = S(b-Ax) - mul_bfmatrix_lfvector(r, lA, dv); - sub_lfvector_lfvector(r, lB, r, numverts); - filter(r, S); - - // p = SP^-1r - mul_prevfmatrix_lfvector(p, Pinv, r); - filter(p, S); - - // delta0 = bhat^TP^-1bhat - mul_prevfmatrix_lfvector(btemp, Pinv, bhat); - delta0 = dot_lfvector(bhat, btemp, numverts); - - // deltaNew = r^TP - deltaNew = dot_lfvector(r, p, numverts); - - /* - filter(dv, S); - add_lfvector_lfvector(dv, dv, z, numverts); - - mul_bfmatrix_lfvector(r, lA, dv); - sub_lfvector_lfvector(r, lB, r, numverts); - filter(r, S); - - mul_prevfmatrix_lfvector(p, Pinv, r); - filter(p, S); - - deltaNew = dot_lfvector(r, p, numverts); - - delta0 = deltaNew * sqrt(conjgrad_epsilon); - */ - - // itstart(); - - tol = (0.01*0.2); - - while ((deltaNew > delta0*tol*tol) && (iterations < conjgrad_looplimit)) - { - iterations++; - - mul_bfmatrix_lfvector(s, lA, p); - filter(s, S); - - alpha = deltaNew / dot_lfvector(p, s, numverts); - - add_lfvector_lfvectorS(dv, dv, p, alpha, numverts); - - add_lfvector_lfvectorS(r, r, s, -alpha, numverts); - - mul_prevfmatrix_lfvector(h, Pinv, r); - filter(h, S); - - deltaOld = deltaNew; - - deltaNew = dot_lfvector(r, h, numverts); - - add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts); - - filter(p, S); - - } - - // itend(); - // printf("cg_filtered_pre time: %f\n", (float)itval()); - - del_lfvector(btemp); - del_lfvector(bhat); - del_lfvector(h); - del_lfvector(s); - del_lfvector(p); - del_lfvector(r); - - // printf("iterations: %d\n", iterations); - - return iterations<conjgrad_looplimit; -} -#endif - -DO_INLINE void dfdx_spring_type2(float to[3][3], float dir[3], float length, float L, float k, float cb) -{ - // return outerprod(dir, dir)*fbstar_jacobi(length, L, k, cb); - mul_fvectorT_fvectorS(to, dir, dir, fbstar_jacobi(length, L, k, cb)); -} - -DO_INLINE void dfdv_damp(float to[3][3], float dir[3], float damping) -{ - // derivative of force wrt velocity. - mul_fvectorT_fvectorS(to, dir, dir, damping); - -} - -DO_INLINE void dfdx_spring(float to[3][3], float dir[3], float length, float L, float k) -{ - // dir is unit length direction, rest is spring's restlength, k is spring constant. - //return ( (I-outerprod(dir, dir))*Min(1.0f, rest/length) - I) * -k; - mul_fvectorT_fvector(to, dir, dir); - sub_fmatrix_fmatrix(to, I, to); - - mul_fmatrix_S(to, (L/length)); - sub_fmatrix_fmatrix(to, to, I); - mul_fmatrix_S(to, -k); -} - -DO_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, lfVector *UNUSED(lF), lfVector *X, lfVector *V, fmatrix3x3 *UNUSED(dFdV), fmatrix3x3 *UNUSED(dFdX), float time) -{ - Cloth *cloth = clmd->clothObject; - ClothVertex *verts = cloth->verts; - float extent[3]; - float length = 0, dot = 0; - float dir[3] = {0, 0, 0}; - float vel[3]; - float k = 0.0f; - float L = s->restlen; - float cb; /* = clmd->sim_parms->structural; */ /*UNUSED*/ - - float nullf[3] = {0, 0, 0}; - float stretch_force[3] = {0, 0, 0}; - float bending_force[3] = {0, 0, 0}; - float damping_force[3] = {0, 0, 0}; - float nulldfdx[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - - float scaling = 0.0; - - int no_compress = clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS; - - copy_v3_v3(s->f, nullf); - cp_fmatrix(s->dfdx, nulldfdx); - cp_fmatrix(s->dfdv, nulldfdx); - - // calculate elonglation - sub_v3_v3v3(extent, X[s->kl], X[s->ij]); - sub_v3_v3v3(vel, V[s->kl], V[s->ij]); - dot = dot_v3v3(extent, extent); - length = sqrtf(dot); - - s->flags &= ~CLOTH_SPRING_FLAG_NEEDED; - - if (length > ALMOST_ZERO) { - /* - if (length>L) - { - if ((clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED) && - ((((length-L)*100.0f/L) > clmd->sim_parms->maxspringlen))) // cut spring! - { - s->flags |= CSPRING_FLAG_DEACTIVATE; - return; - } - } - */ - mul_fvector_S(dir, extent, 1.0f/length); - } - else { - mul_fvector_S(dir, extent, 0.0f); - } - - // calculate force of structural + shear springs - if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR) || (s->type & CLOTH_SPRING_TYPE_SEWING) ) { - if (length > L || no_compress) { - s->flags |= CLOTH_SPRING_FLAG_NEEDED; - - k = clmd->sim_parms->structural; - - scaling = k + s->stiffness * fabsf(clmd->sim_parms->max_struct - k); - - k = scaling / (clmd->sim_parms->avg_spring_len + FLT_EPSILON); - - // TODO: verify, half verified (couldn't see error) - if (s->type & CLOTH_SPRING_TYPE_SEWING) { - // sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects - float force = k*(length-L); - if (force > clmd->sim_parms->max_sewing) { - force = clmd->sim_parms->max_sewing; - } - mul_fvector_S(stretch_force, dir, force); - } - else { - mul_fvector_S(stretch_force, dir, k * (length - L)); - } - - VECADD(s->f, s->f, stretch_force); - - // Ascher & Boxman, p.21: Damping only during elonglation - // something wrong with it... - mul_fvector_S(damping_force, dir, clmd->sim_parms->Cdis * dot_v3v3(vel, dir)); - VECADD(s->f, s->f, damping_force); - - /* VERIFIED */ - dfdx_spring(s->dfdx, dir, length, L, k); - - /* VERIFIED */ - dfdv_damp(s->dfdv, dir, clmd->sim_parms->Cdis); - - } - } - else if (s->type & CLOTH_SPRING_TYPE_GOAL) { - float tvect[3]; - - s->flags |= CLOTH_SPRING_FLAG_NEEDED; - - // current_position = xold + t * (newposition - xold) - sub_v3_v3v3(tvect, verts[s->ij].xconst, verts[s->ij].xold); - mul_fvector_S(tvect, tvect, time); - VECADD(tvect, tvect, verts[s->ij].xold); - - sub_v3_v3v3(extent, X[s->ij], tvect); - - // SEE MSG BELOW (these are UNUSED) - // dot = dot_v3v3(extent, extent); - // length = sqrt(dot); - - k = clmd->sim_parms->goalspring; - - scaling = k + s->stiffness * fabsf(clmd->sim_parms->max_struct - k); - - k = verts [s->ij].goal * scaling / (clmd->sim_parms->avg_spring_len + FLT_EPSILON); - - VECADDS(s->f, s->f, extent, -k); - - mul_fvector_S(damping_force, dir, clmd->sim_parms->goalfrict * 0.01f * dot_v3v3(vel, dir)); - VECADD(s->f, s->f, damping_force); - - // HERE IS THE PROBLEM!!!! - // dfdx_spring(s->dfdx, dir, length, 0.0, k); - // dfdv_damp(s->dfdv, dir, MIN2(1.0, (clmd->sim_parms->goalfrict/100.0))); - } - else { /* calculate force of bending springs */ - if (length < L) { - s->flags |= CLOTH_SPRING_FLAG_NEEDED; - - k = clmd->sim_parms->bending; - - scaling = k + s->stiffness * fabsf(clmd->sim_parms->max_bend - k); - cb = k = scaling / (20.0f * (clmd->sim_parms->avg_spring_len + FLT_EPSILON)); - - mul_fvector_S(bending_force, dir, fbstar(length, L, k, cb)); - VECADD(s->f, s->f, bending_force); - - dfdx_spring_type2(s->dfdx, dir, length, L, k, cb); - } - } -} - -DO_INLINE void cloth_apply_spring_force(ClothModifierData *UNUSED(clmd), ClothSpring *s, lfVector *lF, lfVector *UNUSED(X), lfVector *UNUSED(V), fmatrix3x3 *dFdV, fmatrix3x3 *dFdX) -{ - if (s->flags & CLOTH_SPRING_FLAG_NEEDED) { - if (!(s->type & CLOTH_SPRING_TYPE_BENDING)) { - sub_fmatrix_fmatrix(dFdV[s->ij].m, dFdV[s->ij].m, s->dfdv); - sub_fmatrix_fmatrix(dFdV[s->kl].m, dFdV[s->kl].m, s->dfdv); - add_fmatrix_fmatrix(dFdV[s->matrix_index].m, dFdV[s->matrix_index].m, s->dfdv); - } - - VECADD(lF[s->ij], lF[s->ij], s->f); - - if (!(s->type & CLOTH_SPRING_TYPE_GOAL)) - sub_v3_v3v3(lF[s->kl], lF[s->kl], s->f); - - sub_fmatrix_fmatrix(dFdX[s->kl].m, dFdX[s->kl].m, s->dfdx); - sub_fmatrix_fmatrix(dFdX[s->ij].m, dFdX[s->ij].m, s->dfdx); - add_fmatrix_fmatrix(dFdX[s->matrix_index].m, dFdX[s->matrix_index].m, s->dfdx); - } -} - - -static void CalcFloat( float *v1, float *v2, float *v3, float *n) -{ - float n1[3], n2[3]; - - n1[0] = v1[0]-v2[0]; - n2[0] = v2[0]-v3[0]; - n1[1] = v1[1]-v2[1]; - n2[1] = v2[1]-v3[1]; - n1[2] = v1[2]-v2[2]; - n2[2] = v2[2]-v3[2]; - n[0] = n1[1]*n2[2]-n1[2]*n2[1]; - n[1] = n1[2]*n2[0]-n1[0]*n2[2]; - n[2] = n1[0]*n2[1]-n1[1]*n2[0]; -} - -static void CalcFloat4( float *v1, float *v2, float *v3, float *v4, float *n) -{ - /* real cross! */ - float n1[3], n2[3]; - - n1[0] = v1[0]-v3[0]; - n1[1] = v1[1]-v3[1]; - n1[2] = v1[2]-v3[2]; - - n2[0] = v2[0]-v4[0]; - n2[1] = v2[1]-v4[1]; - n2[2] = v2[2]-v4[2]; - - n[0] = n1[1]*n2[2]-n1[2]*n2[1]; - n[1] = n1[2]*n2[0]-n1[0]*n2[2]; - n[2] = n1[0]*n2[1]-n1[1]*n2[0]; -} - -static float calculateVertexWindForce(const float wind[3], const float vertexnormal[3]) -{ - return dot_v3v3(wind, vertexnormal); -} - -typedef struct HairGridVert { - float velocity[3]; - float density; -} HairGridVert; -#define HAIR_GRID_INDEX(vec, min, max, axis) (int)((vec[axis] - min[axis]) / (max[axis] - min[axis]) * 9.99f) -/* Smoothing of hair velocities: - * adapted from - * Volumetric Methods for Simulation and Rendering of Hair - * by Lena Petrovic, Mark Henne and John Anderson - * Pixar Technical Memo #06-08, Pixar Animation Studios - */ -static void hair_velocity_smoothing(ClothModifierData *clmd, lfVector *lF, lfVector *lX, lfVector *lV, unsigned int numverts) -{ - /* TODO: This is an initial implementation and should be made much better in due time. - * What should at least be implemented is a grid size parameter and a smoothing kernel - * for bigger grids. - */ - - /* 10x10x10 grid gives nice initial results */ - HairGridVert grid[10][10][10]; - HairGridVert colg[10][10][10]; - ListBase *colliders = get_collider_cache(clmd->scene, NULL, NULL); - ColliderCache *col = NULL; - float gmin[3], gmax[3], density; - /* 2.0f is an experimental value that seems to give good results */ - float smoothfac = 2.0f * clmd->sim_parms->velocity_smooth; - float collfac = 2.0f * clmd->sim_parms->collider_friction; - unsigned int v = 0; - int i = 0; - int j = 0; - int k = 0; - - INIT_MINMAX(gmin, gmax); - - for (i = 0; i < numverts; i++) - DO_MINMAX(lX[i], gmin, gmax); - - /* initialize grid */ - for (i = 0; i < 10; i++) { - for (j = 0; j < 10; j++) { - for (k = 0; k < 10; k++) { - grid[i][j][k].velocity[0] = 0.0f; - grid[i][j][k].velocity[1] = 0.0f; - grid[i][j][k].velocity[2] = 0.0f; - grid[i][j][k].density = 0.0f; - - colg[i][j][k].velocity[0] = 0.0f; - colg[i][j][k].velocity[1] = 0.0f; - colg[i][j][k].velocity[2] = 0.0f; - colg[i][j][k].density = 0.0f; - } - } - } - - /* gather velocities & density */ - if (smoothfac > 0.0f) for (v = 0; v < numverts; v++) { - i = HAIR_GRID_INDEX(lX[v], gmin, gmax, 0); - j = HAIR_GRID_INDEX(lX[v], gmin, gmax, 1); - k = HAIR_GRID_INDEX(lX[v], gmin, gmax, 2); - if (i < 0 || j < 0 || k < 0 || i >= 10 || j >= 10 || k >= 10) - continue; - - grid[i][j][k].velocity[0] += lV[v][0]; - grid[i][j][k].velocity[1] += lV[v][1]; - grid[i][j][k].velocity[2] += lV[v][2]; - grid[i][j][k].density += 1.0f; - } - - /* gather colliders */ - if (colliders && collfac > 0.0f) for (col = colliders->first; col; col = col->next) { - MVert *loc0 = col->collmd->x; - MVert *loc1 = col->collmd->xnew; - float vel[3]; - - for (v=0; v<col->collmd->numverts; v++, loc0++, loc1++) { - i = HAIR_GRID_INDEX(loc1->co, gmin, gmax, 0); - - if (i>=0 && i<10) { - j = HAIR_GRID_INDEX(loc1->co, gmin, gmax, 1); - - if (j>=0 && j<10) { - k = HAIR_GRID_INDEX(loc1->co, gmin, gmax, 2); - - if (k>=0 && k<10) { - sub_v3_v3v3(vel, loc1->co, loc0->co); - - colg[i][j][k].velocity[0] += vel[0]; - colg[i][j][k].velocity[1] += vel[1]; - colg[i][j][k].velocity[2] += vel[2]; - colg[i][j][k].density += 1.0f; - } - } - } - } - } - - - /* divide velocity with density */ - for (i = 0; i < 10; i++) { - for (j = 0; j < 10; j++) { - for (k = 0; k < 10; k++) { - density = grid[i][j][k].density; - if (density > 0.0f) { - grid[i][j][k].velocity[0] /= density; - grid[i][j][k].velocity[1] /= density; - grid[i][j][k].velocity[2] /= density; - } - - density = colg[i][j][k].density; - if (density > 0.0f) { - colg[i][j][k].velocity[0] /= density; - colg[i][j][k].velocity[1] /= density; - colg[i][j][k].velocity[2] /= density; - } - } - } - } - - /* calculate forces */ - for (v = 0; v < numverts; v++) { - i = HAIR_GRID_INDEX(lX[v], gmin, gmax, 0); - j = HAIR_GRID_INDEX(lX[v], gmin, gmax, 1); - k = HAIR_GRID_INDEX(lX[v], gmin, gmax, 2); - if (i < 0 || j < 0 || k < 0 || i >= 10 || j >= 10 || k >= 10) - continue; - - lF[v][0] += smoothfac * (grid[i][j][k].velocity[0] - lV[v][0]); - lF[v][1] += smoothfac * (grid[i][j][k].velocity[1] - lV[v][1]); - lF[v][2] += smoothfac * (grid[i][j][k].velocity[2] - lV[v][2]); - - if (colg[i][j][k].density > 0.0f) { - lF[v][0] += collfac * (colg[i][j][k].velocity[0] - lV[v][0]); - lF[v][1] += collfac * (colg[i][j][k].velocity[1] - lV[v][1]); - lF[v][2] += collfac * (colg[i][j][k].velocity[2] - lV[v][2]); - } - } - - free_collider_cache(&colliders); -} - -static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), lfVector *lF, lfVector *lX, lfVector *lV, fmatrix3x3 *dFdV, fmatrix3x3 *dFdX, ListBase *effectors, float time, fmatrix3x3 *M) -{ - /* Collect forces and derivatives: F, dFdX, dFdV */ - Cloth *cloth = clmd->clothObject; - unsigned int i = 0; - float spring_air = clmd->sim_parms->Cvi * 0.01f; /* viscosity of air scaled in percent */ - float gravity[3] = {0.0f, 0.0f, 0.0f}; - float tm2[3][3] = {{0}}; - MFace *mfaces = cloth->mfaces; - unsigned int numverts = cloth->numverts; - LinkNode *search; - lfVector *winvec; - EffectedPoint epoint; - - tm2[0][0] = tm2[1][1] = tm2[2][2] = -spring_air; - - /* global acceleration (gravitation) */ - if (clmd->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { - copy_v3_v3(gravity, clmd->scene->physics_settings.gravity); - mul_fvector_S(gravity, gravity, 0.001f * clmd->sim_parms->effector_weights->global_gravity); /* scale gravity force */ - } - - /* set dFdX jacobi matrix to zero */ - init_bfmatrix(dFdX, ZERO); - /* set dFdX jacobi matrix diagonal entries to -spring_air */ - initdiag_bfmatrix(dFdV, tm2); - - init_lfvector(lF, gravity, numverts); - - if (clmd->sim_parms->velocity_smooth > 0.0f || clmd->sim_parms->collider_friction > 0.0f) - hair_velocity_smoothing(clmd, lF, lX, lV, numverts); - - /* multiply lF with mass matrix - * force = mass * acceleration (in this case: gravity) - */ - for (i = 0; i < numverts; i++) { - float temp[3]; - copy_v3_v3(temp, lF[i]); - mul_fmatrix_fvector(lF[i], M[i].m, temp); - } - - submul_lfvectorS(lF, lV, spring_air, numverts); - - /* handle external forces like wind */ - if (effectors) { - // 0 = force, 1 = normalized force - winvec = create_lfvector(cloth->numverts); - - if (!winvec) - printf("winvec: out of memory in implicit.c\n"); - - // precalculate wind forces - for (i = 0; i < cloth->numverts; i++) { - pd_point_from_loc(clmd->scene, (float*)lX[i], (float*)lV[i], i, &epoint); - pdDoEffectors(effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL); - } - - for (i = 0; i < cloth->numfaces; i++) { - float trinormal[3] = {0, 0, 0}; // normalized triangle normal - float triunnormal[3] = {0, 0, 0}; // not-normalized-triangle normal - float tmp[3] = {0, 0, 0}; - float factor = (mfaces[i].v4) ? 0.25 : 1.0 / 3.0; - factor *= 0.02f; - - // calculate face normal - if (mfaces[i].v4) - CalcFloat4(lX[mfaces[i].v1], lX[mfaces[i].v2], lX[mfaces[i].v3], lX[mfaces[i].v4], triunnormal); - else - CalcFloat(lX[mfaces[i].v1], lX[mfaces[i].v2], lX[mfaces[i].v3], triunnormal); - - normalize_v3_v3(trinormal, triunnormal); - - // add wind from v1 - copy_v3_v3(tmp, trinormal); - mul_v3_fl(tmp, calculateVertexWindForce(winvec[mfaces[i].v1], triunnormal)); - VECADDS(lF[mfaces[i].v1], lF[mfaces[i].v1], tmp, factor); - - // add wind from v2 - copy_v3_v3(tmp, trinormal); - mul_v3_fl(tmp, calculateVertexWindForce(winvec[mfaces[i].v2], triunnormal)); - VECADDS(lF[mfaces[i].v2], lF[mfaces[i].v2], tmp, factor); - - // add wind from v3 - copy_v3_v3(tmp, trinormal); - mul_v3_fl(tmp, calculateVertexWindForce(winvec[mfaces[i].v3], triunnormal)); - VECADDS(lF[mfaces[i].v3], lF[mfaces[i].v3], tmp, factor); - - // add wind from v4 - if (mfaces[i].v4) { - copy_v3_v3(tmp, trinormal); - mul_v3_fl(tmp, calculateVertexWindForce(winvec[mfaces[i].v4], triunnormal)); - VECADDS(lF[mfaces[i].v4], lF[mfaces[i].v4], tmp, factor); - } - } - - /* Hair has only edges */ - if (cloth->numfaces == 0) { - ClothSpring *spring; - float edgevec[3] = {0, 0, 0}; //edge vector - float edgeunnormal[3] = {0, 0, 0}; // not-normalized-edge normal - float tmp[3] = {0, 0, 0}; - float factor = 0.01; - - search = cloth->springs; - while (search) { - spring = search->link; - - if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) { - sub_v3_v3v3(edgevec, (float*)lX[spring->ij], (float*)lX[spring->kl]); - - project_v3_v3v3(tmp, winvec[spring->ij], edgevec); - sub_v3_v3v3(edgeunnormal, winvec[spring->ij], tmp); - /* hair doesn't stretch too much so we can use restlen pretty safely */ - VECADDS(lF[spring->ij], lF[spring->ij], edgeunnormal, spring->restlen * factor); - - project_v3_v3v3(tmp, winvec[spring->kl], edgevec); - sub_v3_v3v3(edgeunnormal, winvec[spring->kl], tmp); - VECADDS(lF[spring->kl], lF[spring->kl], edgeunnormal, spring->restlen * factor); - } - - search = search->next; - } - } - - del_lfvector(winvec); - } - - // calculate spring forces - search = cloth->springs; - while (search) { - // only handle active springs - ClothSpring *spring = search->link; - if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE)) - cloth_calc_spring_force(clmd, search->link, lF, lX, lV, dFdV, dFdX, time); - - search = search->next; - } - - // apply spring forces - search = cloth->springs; - while (search) { - // only handle active springs - ClothSpring *spring = search->link; - if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE)) - cloth_apply_spring_force(clmd, search->link, lF, lX, lV, dFdV, dFdX); - search = search->next; - } - // printf("\n"); -} - -static void simulate_implicit_euler(lfVector *Vnew, lfVector *UNUSED(lX), lfVector *lV, lfVector *lF, fmatrix3x3 *dFdV, fmatrix3x3 *dFdX, float dt, fmatrix3x3 *A, lfVector *B, lfVector *dV, fmatrix3x3 *S, lfVector *z, lfVector *olddV, fmatrix3x3 *UNUSED(P), fmatrix3x3 *UNUSED(Pinv), fmatrix3x3 *M, fmatrix3x3 *UNUSED(bigI)) -{ - unsigned int numverts = dFdV[0].vcount; - - lfVector *dFdXmV = create_lfvector(numverts); - zero_lfvector(dV, numverts); - - cp_bfmatrix(A, M); - - subadd_bfmatrixS_bfmatrixS(A, dFdV, dt, dFdX, (dt*dt)); - - mul_bfmatrix_lfvector(dFdXmV, dFdX, lV); - - add_lfvectorS_lfvectorS(B, lF, dt, dFdXmV, (dt*dt), numverts); - - // itstart(); - - cg_filtered(dV, A, B, z, S); /* conjugate gradient algorithm to solve Ax=b */ - // cg_filtered_pre(dV, A, B, z, S, P, Pinv, bigI); - - // itend(); - // printf("cg_filtered calc time: %f\n", (float)itval()); - - cp_lfvector(olddV, dV, numverts); - - // advance velocities - add_lfvector_lfvector(Vnew, lV, dV, numverts); - - - del_lfvector(dFdXmV); -} - -/* computes where the cloth would be if it were subject to perfectly stiff edges - * (edge distance constraints) in a lagrangian solver. then add forces to help - * guide the implicit solver to that state. this function is called after - * collisions*/ -static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothModifierData *clmd, float (*initial_cos)[3], float UNUSED(step), float dt) -{ - Cloth *cloth= clmd->clothObject; - float (*cos)[3] = MEM_callocN(sizeof(float)*3*cloth->numverts, "cos cloth_calc_helper_forces"); - float *masses = MEM_callocN(sizeof(float)*cloth->numverts, "cos cloth_calc_helper_forces"); - LinkNode *node; - ClothSpring *spring; - ClothVertex *cv; - int i, steps; - - cv = cloth->verts; - for (i=0; i<cloth->numverts; i++, cv++) { - copy_v3_v3(cos[i], cv->tx); - - if (cv->goal == 1.0f || len_squared_v3v3(initial_cos[i], cv->tx) != 0.0f) { - masses[i] = 1e+10; - } - else { - masses[i] = cv->mass; - } - } - - steps = 55; - for (i=0; i<steps; i++) { - for (node=cloth->springs; node; node=node->next) { - /* ClothVertex *cv1, *cv2; */ /* UNUSED */ - int v1, v2; - float len, c, l, vec[3]; - - spring = node->link; - if (spring->type != CLOTH_SPRING_TYPE_STRUCTURAL && spring->type != CLOTH_SPRING_TYPE_SHEAR) - continue; - - v1 = spring->ij; v2 = spring->kl; - /* cv1 = cloth->verts + v1; */ /* UNUSED */ - /* cv2 = cloth->verts + v2; */ /* UNUSED */ - len = len_v3v3(cos[v1], cos[v2]); - - sub_v3_v3v3(vec, cos[v1], cos[v2]); - normalize_v3(vec); - - c = (len - spring->restlen); - if (c == 0.0f) - continue; - - l = c / ((1.0f / masses[v1]) + (1.0f / masses[v2])); - - mul_v3_fl(vec, -(1.0f / masses[v1]) * l); - add_v3_v3(cos[v1], vec); - - sub_v3_v3v3(vec, cos[v2], cos[v1]); - normalize_v3(vec); - - mul_v3_fl(vec, -(1.0f / masses[v2]) * l); - add_v3_v3(cos[v2], vec); - } - } - - cv = cloth->verts; - for (i=0; i<cloth->numverts; i++, cv++) { - float vec[3]; - - /*compute forces*/ - sub_v3_v3v3(vec, cos[i], cv->tx); - mul_v3_fl(vec, cv->mass*dt*20.0f); - add_v3_v3(cv->tv, vec); - //copy_v3_v3(cv->tx, cos[i]); - } - - MEM_freeN(cos); - MEM_freeN(masses); - - return 1; -} - -int implicit_solver(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors) -{ - unsigned int i=0; - float step=0.0f, tf=clmd->sim_parms->timescale; - Cloth *cloth = clmd->clothObject; - ClothVertex *verts = cloth->verts, *cv; - unsigned int numverts = cloth->numverts; - float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame; - float spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale; - float (*initial_cos)[3] = MEM_callocN(sizeof(float)*3*cloth->numverts, "initial_cos implicit.c"); - Implicit_Data *id = cloth->implicit; - int do_extra_solve; - - if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */ - - /* Update vertex constraints for pinned vertices */ - update_matrixS(verts, cloth->numverts, id->S); - - for (i = 0; i < numverts; i++) { - // update velocities with constrained velocities from pinned verts - if (verts [i].flags & CLOTH_VERT_FLAG_PINNED) { - sub_v3_v3v3(id->V[i], verts[i].xconst, verts[i].xold); - // mul_v3_fl(id->V[i], clmd->sim_parms->stepsPerFrame); - } - } - } - - while (step < tf) { - // damping velocity for artistic reasons - mul_lfvectorS(id->V, id->V, clmd->sim_parms->vel_damping, numverts); - - // calculate forces - cloth_calc_force(clmd, frame, id->F, id->X, id->V, id->dFdV, id->dFdX, effectors, step, id->M); - - // calculate new velocity - simulate_implicit_euler(id->Vnew, id->X, id->V, id->F, id->dFdV, id->dFdX, dt, id->A, id->B, id->dV, id->S, id->z, id->olddV, id->P, id->Pinv, id->M, id->bigI); - - // advance positions - add_lfvector_lfvectorS(id->Xnew, id->X, id->Vnew, dt, numverts); - - /* move pinned verts to correct position */ - for (i = 0; i < numverts; i++) { - if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { - if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) { - float tvect[3] = {0.0f, 0.0f, 0.0f}; - sub_v3_v3v3(tvect, verts[i].xconst, verts[i].xold); - mul_fvector_S(tvect, tvect, step+dt); - VECADD(tvect, tvect, verts[i].xold); - copy_v3_v3(id->Xnew[i], tvect); - } - } - - copy_v3_v3(verts[i].txold, id->X[i]); - } - - if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED && clmd->clothObject->bvhtree) { - // collisions - // itstart(); - - // update verts to current positions - for (i = 0; i < numverts; i++) { - copy_v3_v3(verts[i].tx, id->Xnew[i]); - - sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold); - copy_v3_v3(verts[i].v, verts[i].tv); - } - - for (i=0, cv=cloth->verts; i<cloth->numverts; i++, cv++) { - copy_v3_v3(initial_cos[i], cv->tx); - } - - // call collision function - // TODO: check if "step" or "step+dt" is correct - dg - do_extra_solve = cloth_bvh_objcollision(ob, clmd, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale); - - // copy corrected positions back to simulation - for (i = 0; i < numverts; i++) { - // correct velocity again, just to be sure we had to change it due to adaptive collisions - sub_v3_v3v3(verts[i].tv, verts[i].tx, id->X[i]); - } - - //if (do_extra_solve) - // cloth_calc_helper_forces(ob, clmd, initial_cos, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale); - - if (do_extra_solve) { - for (i = 0; i < numverts; i++) { - if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED)) - continue; - - copy_v3_v3(id->Xnew[i], verts[i].tx); - copy_v3_v3(id->Vnew[i], verts[i].tv); - mul_v3_fl(id->Vnew[i], spf); - } - } - - // X = Xnew; - cp_lfvector(id->X, id->Xnew, numverts); - - // if there were collisions, advance the velocity from v_n+1/2 to v_n+1 - - if (do_extra_solve) { - // V = Vnew; - cp_lfvector(id->V, id->Vnew, numverts); - - // calculate - cloth_calc_force(clmd, frame, id->F, id->X, id->V, id->dFdV, id->dFdX, effectors, step+dt, id->M); - - simulate_implicit_euler(id->Vnew, id->X, id->V, id->F, id->dFdV, id->dFdX, dt / 2.0f, id->A, id->B, id->dV, id->S, id->z, id->olddV, id->P, id->Pinv, id->M, id->bigI); - } - } - else { - // X = Xnew; - cp_lfvector(id->X, id->Xnew, numverts); - } - - // itend(); - // printf("collision time: %f\n", (float)itval()); - - // V = Vnew; - cp_lfvector(id->V, id->Vnew, numverts); - - step += dt; - } - - for (i = 0; i < numverts; i++) { - if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED)) { - copy_v3_v3(verts[i].txold, verts[i].xconst); // TODO: test --> should be .x - copy_v3_v3(verts[i].x, verts[i].xconst); - copy_v3_v3(verts[i].v, id->V[i]); - } - else { - copy_v3_v3(verts[i].txold, id->X[i]); - copy_v3_v3(verts[i].x, id->X[i]); - copy_v3_v3(verts[i].v, id->V[i]); - } - } - - MEM_freeN(initial_cos); - - return 1; -} - -void implicit_set_positions(ClothModifierData *clmd) -{ - Cloth *cloth = clmd->clothObject; - ClothVertex *verts = cloth->verts; - unsigned int numverts = cloth->numverts, i; - Implicit_Data *id = cloth->implicit; - - for (i = 0; i < numverts; i++) { - copy_v3_v3(id->X[i], verts[i].x); - copy_v3_v3(id->V[i], verts[i].v); - } - if (G.debug_value > 0) - printf("implicit_set_positions\n"); -} - diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 34877c7559c..c748568b4c5 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -161,21 +161,24 @@ static AdrBit2Path ma_mode_bits[] = { { \ *tot = sizeof(items) / sizeof(AdrBit2Path); \ return items; \ - } + } (void)0 /* This function checks if a Blocktype+Adrcode combo, returning a mapping table */ static AdrBit2Path *adrcode_bitmaps_to_paths(int blocktype, int adrcode, int *tot) { /* Object layers */ - if ((blocktype == ID_OB) && (adrcode == OB_LAY)) - RET_ABP(ob_layer_bits) - else if ((blocktype == ID_MA) && (adrcode == MA_MODE)) - RET_ABP(ma_mode_bits) + if ((blocktype == ID_OB) && (adrcode == OB_LAY)) { + RET_ABP(ob_layer_bits); + } + else if ((blocktype == ID_MA) && (adrcode == MA_MODE)) { + RET_ABP(ma_mode_bits); + } // XXX TODO: add other types... /* Normal curve */ return NULL; } +#undef RET_ABP /* *************************************************** */ /* ADRCODE to RNA-Path Conversion Code - Standard */ @@ -919,11 +922,11 @@ static char *get_rna_access(int blocktype, int adrcode, char actname[], char con BLI_snprintf(buf, sizeof(buf), "pose.bones[\"%s\"].constraints[\"%s\"]", actname, constname); } else if (actname && actname[0]) { - if ((blocktype == ID_OB) && strcmp(actname, "Object") == 0) { + if ((blocktype == ID_OB) && STREQ(actname, "Object")) { /* Actionified "Object" IPO's... no extra path stuff needed */ buf[0] = '\0'; /* empty string */ } - else if ((blocktype == ID_KE) && strcmp(actname, "Shape") == 0) { + else if ((blocktype == ID_KE) && STREQ(actname, "Shape")) { /* Actionified "Shape" IPO's - these are forced onto object level via the action container there... */ strcpy(buf, "data.shape_keys"); } @@ -1323,7 +1326,7 @@ static void icu_to_fcurves(ID *id, ListBase *groups, ListBase *list, IpoCurve *i * - we now need as 'frames' */ if ( (id) && (icu->blocktype == GS(id->name)) && - (fcu->rna_path && strcmp(fcu->rna_path, "eval_time") == 0) ) + (fcu->rna_path && STREQ(fcu->rna_path, "eval_time")) ) { Curve *cu = (Curve *)id; @@ -1400,9 +1403,9 @@ static void ipo_to_animato(ID *id, Ipo *ipo, char actname[], char constname[], S * F-Curves for bones). This may be added later... for now let's just dump without them... */ if (actname) { - if ((ipo->blocktype == ID_OB) && (strcmp(actname, "Object") == 0)) + if ((ipo->blocktype == ID_OB) && STREQ(actname, "Object")) actname = NULL; - else if ((ipo->blocktype == ID_OB) && (strcmp(actname, "Shape") == 0)) + else if ((ipo->blocktype == ID_OB) && STREQ(actname, "Shape")) actname = NULL; } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 752c2961f28..fa4f9c6ed52 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -378,7 +378,8 @@ bool id_copy(ID *id, ID **newid, bool test) case ID_WM: return false; /* can't be copied from here */ case ID_GD: - return false; /* not implemented */ + if (!test) *newid = (ID *)gpencil_data_duplicate((bGPdata *)id, false); + return true; case ID_MSK: if (!test) *newid = (ID *)BKE_mask_copy((Mask *)id); return true; @@ -1202,7 +1203,7 @@ static ID *is_dupid(ListBase *lb, ID *id, const char *name) /* do not test alphabetic! */ /* optimized */ if (idtest->name[2] == name[0]) { - if (strcmp(name, idtest->name + 2) == 0) break; + if (STREQ(name, idtest->name + 2)) break; } } } @@ -1260,7 +1261,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name) if ( (id != idtest) && (idtest->lib == NULL) && (*name == *(idtest->name + 2)) && - (strncmp(name, idtest->name + 2, left_len) == 0) && + STREQLEN(name, idtest->name + 2, left_len) && (BLI_split_name_num(leftest, &nrtest, idtest->name + 2, '.') == left_len) ) { @@ -1571,7 +1572,7 @@ void test_idbutton(char *name) ID *idtest; - lb = which_libbase(G.main, GS(name) ); + lb = which_libbase(G.main, GS(name)); if (lb == NULL) return; /* search for id */ @@ -1590,7 +1591,7 @@ void rename_id(ID *id, const char *name) ListBase *lb; BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2); - lb = which_libbase(G.main, GS(id->name) ); + lb = which_libbase(G.main, GS(id->name)); new_id(lb, id, name); } diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 981064bb22a..9b682f101ad 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1027,7 +1027,7 @@ static void do_init_render_material(Material *ma, int r_mode, float *amb) Group *group; for (group = G.main->group.first; group; group = group->id.next) { - if (!group->id.lib && strcmp(group->id.name, ma->group->id.name) == 0) { + if (!group->id.lib && STREQ(group->id.name, ma->group->id.name)) { ma->group = group; } } @@ -1317,6 +1317,26 @@ static bool get_mtex_slot_valid_texpaint(struct MTex *mtex) mtex->tex->ima); } +static bNode *nodetree_uv_node_recursive(bNode *node) +{ + bNode *inode; + bNodeSocket *sock; + + for (sock = node->inputs.first; sock; sock = sock->next) { + if (sock->link) { + inode = sock->link->fromnode; + if (inode->typeinfo->nclass == NODE_CLASS_INPUT && inode->typeinfo->type == SH_NODE_UVMAP) { + return inode; + } + else { + return nodetree_uv_node_recursive(inode); + } + } + } + + return NULL; +} + void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma) { MTex **mtex; @@ -1368,7 +1388,27 @@ void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma) if (node->typeinfo->nclass == NODE_CLASS_TEXTURE && node->typeinfo->type == SH_NODE_TEX_IMAGE && node->id) { if (active_node == node) ma->paint_active_slot = index; - ma->texpaintslot[index++].ima = (Image *)node->id; + ma->texpaintslot[index].ima = (Image *)node->id; + + /* for new renderer, we need to traverse the treeback in search of a UV node */ + if (use_nodes) { + bNode *uvnode = nodetree_uv_node_recursive(node); + + if (uvnode) { + NodeShaderUVMap *storage = (NodeShaderUVMap *)uvnode->storage; + ma->texpaintslot[index].uvname = storage->uv_map; + /* set a value to index so UI knows that we have a valid pointer for the mesh */ + ma->texpaintslot[index].index = 0; + } + else { + /* just invalidate the index here so UV map does not get displayed on the UI */ + ma->texpaintslot[index].index = -1; + } + } + else { + ma->texpaintslot[index].index = -1; + } + index++; } } } diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 34ab2a85593..ce20636233a 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -468,7 +468,7 @@ bool BKE_mball_is_basis_for(Object *ob1, Object *ob2) BLI_split_name_num(basis1name, &basis1nr, ob1->id.name + 2, '.'); BLI_split_name_num(basis2name, &basis2nr, ob2->id.name + 2, '.'); - if (!strcmp(basis1name, basis2name)) { + if (STREQ(basis1name, basis2name)) { return BKE_mball_is_basis(ob1); } else { @@ -503,7 +503,7 @@ void BKE_mball_properties_copy(Scene *scene, Object *active_object) /* Object ob has to be in same "group" ... it means, that it has to have * same base of its name */ - if (strcmp(obname, basisname) == 0) { + if (STREQ(obname, basisname)) { MetaBall *mb = ob->data; /* Copy properties from selected/edited metaball */ @@ -545,7 +545,7 @@ Object *BKE_mball_basis_find(Scene *scene, Object *basis) BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); /* object ob has to be in same "group" ... it means, that it has to have same base of its name */ - if (strcmp(obname, basisname) == 0) { + if (STREQ(obname, basisname)) { if (obnr < basisnr) { basis = ob; basisnr = obnr; @@ -913,7 +913,11 @@ static void docube(PROCESS *process, CUBE *cube, MetaBall *mb) CORNER *c1, *c2; int i, index = 0, count, indexar[8]; - for (i = 0; i < 8; i++) if (cube->corners[i]->value > 0.0f) index += (1 << i); + for (i = 0; i < 8; i++) { + if (cube->corners[i]->value > 0.0f) { + index += (1 << i); + } + } for (polys = cubetable[index]; polys; polys = polys->next) { INTLIST *edges; @@ -1675,7 +1679,7 @@ static float init_meta(EvaluationContext *eval_ctx, PROCESS *process, Scene *sce int nr; BLI_split_name_num(name, &nr, bob->id.name + 2, '.'); - if (strcmp(obname, name) == 0) { + if (STREQ(obname, name)) { mb = bob->data; if (mb->editelems) ml = mb->editelems->first; @@ -2239,7 +2243,7 @@ static void mball_count(EvaluationContext *eval_ctx, PROCESS *process, Scene *sc /* object ob has to be in same "group" ... it means, that it has to have * same base of its name */ - if (strcmp(obname, basisname) == 0) { + if (STREQ(obname, basisname)) { MetaBall *mb = ob->data; /* if object is in edit mode, then dynamic list of all MetaElems diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index c47d294cfb2..7757babaca2 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -414,6 +414,16 @@ void BKE_mesh_update_customdata_pointers(Mesh *me, const bool do_ensure_tess_cd) me->mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV); } +bool BKE_mesh_has_custom_loop_normals(Mesh *me) +{ + if (me->edit_btmesh) { + return CustomData_has_layer(&me->edit_btmesh->bm->ldata, CD_CUSTOMLOOPNORMAL); + } + else { + return CustomData_has_layer(&me->ldata, CD_CUSTOMLOOPNORMAL); + } +} + /* Note: unlinking is called when me->id.us is 0, question remains how * much unlinking of Library data in Mesh should be done... probably * we need a more generic method, like the expand() functions in @@ -703,7 +713,7 @@ bool BKE_mesh_uv_cdlayer_rename_index(Mesh *me, const int poly_index, const int } /* Loop until we do have exactly the same name for all layers! */ - for (i = 1; (strcmp(cdlp->name, cdlu->name) != 0 || (cdlf && strcmp(cdlp->name, cdlf->name) != 0)); i++) { + for (i = 1; !STREQ(cdlp->name, cdlu->name) || (cdlf && !STREQ(cdlp->name, cdlf->name)); i++) { switch (i % step) { case 0: BLI_strncpy(cdlp->name, cdlu->name, sizeof(cdlp->name)); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 915abdb6766..40a09eba658 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -39,6 +39,7 @@ #include "BLI_utildefines.h" #include "BLI_memarena.h" +#include "BLI_mempool.h" #include "BLI_math.h" #include "BLI_edgehash.h" #include "BLI_bitmap.h" @@ -46,6 +47,8 @@ #include "BLI_linklist.h" #include "BLI_linklist_stack.h" #include "BLI_alloca.h" +#include "BLI_stack.h" +#include "BLI_task.h" #include "BKE_customdata.h" #include "BKE_mesh.h" @@ -58,8 +61,8 @@ // #define DEBUG_TIME +#include "PIL_time.h" #ifdef DEBUG_TIME -# include "PIL_time.h" # include "PIL_time_utildefines.h" #endif @@ -316,19 +319,783 @@ void BKE_mesh_calc_normals_tessface(MVert *mverts, int numVerts, MFace *mfaces, MEM_freeN(fnors); } -/** - * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals'). - * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry (splitting edges). +void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, const int numLoops) +{ + if (!(lnors_spacearr->lspacearr && lnors_spacearr->loops_pool)) { + MemArena *mem; + + if (!lnors_spacearr->mem) { + lnors_spacearr->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + mem = lnors_spacearr->mem; + lnors_spacearr->lspacearr = BLI_memarena_calloc(mem, sizeof(MLoopNorSpace *) * (size_t)numLoops); + lnors_spacearr->loops_pool = BLI_memarena_alloc(mem, sizeof(LinkNode) * (size_t)numLoops); + } +} + +void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr) +{ + BLI_memarena_clear(lnors_spacearr->mem); + lnors_spacearr->lspacearr = NULL; + lnors_spacearr->loops_pool = NULL; +} + +void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr) +{ + BLI_memarena_free(lnors_spacearr->mem); + lnors_spacearr->lspacearr = NULL; + lnors_spacearr->loops_pool = NULL; + lnors_spacearr->mem = NULL; +} + +MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr) +{ + return BLI_memarena_calloc(lnors_spacearr->mem, sizeof(MLoopNorSpace)); +} + +/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */ +#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-6f) + +/* Should only be called once. + * Beware, this modifies ref_vec and other_vec in place! + * In case no valid space can be generated, ref_alpha and ref_beta are set to zero (which means 'use auto lnors'). */ -void BKE_mesh_normals_loop_split(MVert *mverts, const int UNUSED(numVerts), MEdge *medges, const int numEdges, - MLoop *mloops, float (*r_loopnors)[3], const int numLoops, - MPoly *mpolys, float (*polynors)[3], const int numPolys, float split_angle) +void BKE_lnor_space_define(MLoopNorSpace *lnor_space, const float lnor[3], + float vec_ref[3], float vec_other[3], BLI_Stack *edge_vectors) +{ + const float pi2 = (float)M_PI * 2.0f; + float tvec[3], dtp; + const float dtp_ref = dot_v3v3(vec_ref, lnor); + const float dtp_other = dot_v3v3(vec_other, lnor); + + if (UNLIKELY(fabsf(dtp_ref) >= LNOR_SPACE_TRIGO_THRESHOLD || fabsf(dtp_other) >= LNOR_SPACE_TRIGO_THRESHOLD)) { + /* If vec_ref or vec_other are too much aligned with lnor, we can't build lnor space, + * tag it as invalid and abort. */ + lnor_space->ref_alpha = lnor_space->ref_beta = 0.0f; + return; + } + + copy_v3_v3(lnor_space->vec_lnor, lnor); + + /* Compute ref alpha, average angle of all available edge vectors to lnor. */ + if (edge_vectors) { + float alpha = 0.0f; + int nbr = 0; + while (!BLI_stack_is_empty(edge_vectors)) { + const float *vec = BLI_stack_peek(edge_vectors); + alpha += saacosf(dot_v3v3(vec, lnor)); + BLI_stack_discard(edge_vectors); + nbr++; + } + BLI_assert(nbr > 2); /* This piece of code shall only be called for more than one loop... */ + lnor_space->ref_alpha = alpha / (float)nbr; + } + else { + lnor_space->ref_alpha = (saacosf(dot_v3v3(vec_ref, lnor)) + saacosf(dot_v3v3(vec_other, lnor))) / 2.0f; + } + + /* Project vec_ref on lnor's ortho plane. */ + mul_v3_v3fl(tvec, lnor, dtp_ref); + sub_v3_v3(vec_ref, tvec); + normalize_v3_v3(lnor_space->vec_ref, vec_ref); + + cross_v3_v3v3(tvec, lnor, lnor_space->vec_ref); + normalize_v3_v3(lnor_space->vec_ortho, tvec); + + /* Project vec_other on lnor's ortho plane. */ + mul_v3_v3fl(tvec, lnor, dtp_other); + sub_v3_v3(vec_other, tvec); + normalize_v3(vec_other); + + /* Beta is angle between ref_vec and other_vec, around lnor. */ + dtp = dot_v3v3(lnor_space->vec_ref, vec_other); + if (LIKELY(dtp < LNOR_SPACE_TRIGO_THRESHOLD)) { + const float beta = saacos(dtp); + lnor_space->ref_beta = (dot_v3v3(lnor_space->vec_ortho, vec_other) < 0.0f) ? pi2 - beta : beta; + } + else { + lnor_space->ref_beta = pi2; + } +} + +void BKE_lnor_space_add_loop(MLoopNorSpaceArray *lnors_spacearr, MLoopNorSpace *lnor_space, const int ml_index, + const bool do_add_loop) +{ + lnors_spacearr->lspacearr[ml_index] = lnor_space; + if (do_add_loop) { + BLI_linklist_prepend_nlink(&lnor_space->loops, SET_INT_IN_POINTER(ml_index), &lnors_spacearr->loops_pool[ml_index]); + } +} + +MINLINE float unit_short_to_float(const short val) +{ + return (float)val / (float)SHRT_MAX; +} + +MINLINE short unit_float_to_short(const float val) +{ + /* Rounding... */ + return (short)floorf(val * (float)SHRT_MAX + 0.5f); +} + +void BKE_lnor_space_custom_data_to_normal(MLoopNorSpace *lnor_space, const short clnor_data[2], float r_custom_lnor[3]) +{ + /* NOP custom normal data or invalid lnor space, return. */ + if (clnor_data[0] == 0 || lnor_space->ref_alpha == 0.0f || lnor_space->ref_beta == 0.0f) { + copy_v3_v3(r_custom_lnor, lnor_space->vec_lnor); + return; + } + + { + /* TODO Check whether using sincosf() gives any noticeable benefit + * (could not even get it working under linux though)! */ + const float pi2 = (float)(M_PI * 2.0); + const float alphafac = unit_short_to_float(clnor_data[0]); + const float alpha = (alphafac > 0.0f ? lnor_space->ref_alpha : pi2 - lnor_space->ref_alpha) * alphafac; + const float betafac = unit_short_to_float(clnor_data[1]); + + mul_v3_v3fl(r_custom_lnor, lnor_space->vec_lnor, cosf(alpha)); + + if (betafac == 0.0f) { + madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinf(alpha)); + } + else { + const float sinalpha = sinf(alpha); + const float beta = (betafac > 0.0f ? lnor_space->ref_beta : pi2 - lnor_space->ref_beta) * betafac; + madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ref, sinalpha * cosf(beta)); + madd_v3_v3fl(r_custom_lnor, lnor_space->vec_ortho, sinalpha * sinf(beta)); + } + } +} + +void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2]) { + /* We use null vector as NOP custom normal (can be simpler than giving autocomputed lnor...). */ + if (is_zero_v3(custom_lnor) || compare_v3v3(lnor_space->vec_lnor, custom_lnor, 1e-4f)) { + r_clnor_data[0] = r_clnor_data[1] = 0; + return; + } + + { + const float pi2 = (float)(M_PI * 2.0); + const float cos_alpha = dot_v3v3(lnor_space->vec_lnor, custom_lnor); + float vec[3], cos_beta; + float alpha; + + alpha = saacosf(cos_alpha); + if (alpha > lnor_space->ref_alpha) { + /* Note we could stick to [0, pi] range here, but makes decoding more complex, not worth it. */ + r_clnor_data[0] = unit_float_to_short(-(pi2 - alpha) / (pi2 - lnor_space->ref_alpha)); + } + else { + r_clnor_data[0] = unit_float_to_short(alpha / lnor_space->ref_alpha); + } + + /* Project custom lnor on (vec_ref, vec_ortho) plane. */ + mul_v3_v3fl(vec, lnor_space->vec_lnor, -cos_alpha); + add_v3_v3(vec, custom_lnor); + normalize_v3(vec); + + cos_beta = dot_v3v3(lnor_space->vec_ref, vec); + + if (cos_beta < LNOR_SPACE_TRIGO_THRESHOLD) { + float beta = saacosf(cos_beta); + if (dot_v3v3(lnor_space->vec_ortho, vec) < 0.0f) { + beta = pi2 - beta; + } + + if (beta > lnor_space->ref_beta) { + r_clnor_data[1] = unit_float_to_short(-(pi2 - beta) / (pi2 - lnor_space->ref_beta)); + } + else { + r_clnor_data[1] = unit_float_to_short(beta / lnor_space->ref_beta); + } + } + else { + r_clnor_data[1] = 0; + } + } +} + +#define LOOP_SPLIT_TASK_BLOCK_SIZE 1024 + +typedef struct LoopSplitTaskData { + /* Specific to each instance (each task). */ + MLoopNorSpace *lnor_space; /* We have to create those outside of tasks, since afaik memarena is not threadsafe. */ + float (*lnor)[3]; + const MLoop *ml_curr; + const MLoop *ml_prev; + int ml_curr_index; + int ml_prev_index; + const int *e2l_prev; /* Also used a flag to switch between single or fan process! */ + int mp_index; + + /* This one is special, it's owned and managed by worker tasks, avoid to have to create it for each fan! */ + BLI_Stack *edge_vectors; + + char pad_c; +} LoopSplitTaskData; + +typedef struct LoopSplitTaskDataCommon { + /* Read/write. + * Note we do not need to protect it, though, since two different tasks will *always* affect different + * elements in the arrays. */ + MLoopNorSpaceArray *lnors_spacearr; + BLI_bitmap *sharp_verts; + float (*loopnors)[3]; + short (*clnors_data)[2]; + + /* Read-only. */ + const MVert *mverts; + const MEdge *medges; + const MLoop *mloops; + const MPoly *mpolys; + const int (*edge_to_loops)[2]; + const int *loop_to_poly; + const float (*polynors)[3]; + + int numPolys; + + /* ***** Workers communication. ***** */ + ThreadQueue *task_queue; + +} LoopSplitTaskDataCommon; + #define INDEX_UNSET INT_MIN #define INDEX_INVALID -1 /* See comment about edge_to_loops below. */ #define IS_EDGE_SHARP(_e2l) (ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID)) +static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) +{ + MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; + short (*clnors_data)[2] = common_data->clnors_data; + + const MVert *mverts = common_data->mverts; + const MEdge *medges = common_data->medges; + const float (*polynors)[3] = common_data->polynors; + + MLoopNorSpace *lnor_space = data->lnor_space; + float (*lnor)[3] = data->lnor; + const MLoop *ml_curr = data->ml_curr; + const MLoop *ml_prev = data->ml_prev; + const int ml_curr_index = data->ml_curr_index; +#if 0 /* Not needed for 'single' loop. */ + const int ml_prev_index = data->ml_prev_index; + const int *e2l_prev = data->e2l_prev; +#endif + const int mp_index = data->mp_index; + + /* Simple case (both edges around that vertex are sharp in current polygon), + * this loop just takes its poly normal. + */ + copy_v3_v3(*lnor, polynors[mp_index]); + + /* printf("BASIC: handling loop %d / edge %d / vert %d\n", ml_curr_index, ml_curr->e, ml_curr->v); */ + + /* If needed, generate this (simple!) lnor space. */ + if (lnors_spacearr) { + float vec_curr[3], vec_prev[3]; + + const unsigned int mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const MVert *mv_pivot = &mverts[mv_pivot_index]; + const MEdge *me_curr = &medges[ml_curr->e]; + const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : &mverts[me_curr->v1]; + const MEdge *me_prev = &medges[ml_prev->e]; + const MVert *mv_3 = (me_prev->v1 == mv_pivot_index) ? &mverts[me_prev->v2] : &mverts[me_prev->v1]; + + sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co); + normalize_v3(vec_curr); + sub_v3_v3v3(vec_prev, mv_3->co, mv_pivot->co); + normalize_v3(vec_prev); + + BKE_lnor_space_define(lnor_space, *lnor, vec_curr, vec_prev, NULL); + /* We know there is only one loop in this space, no need to create a linklist in this case... */ + BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, ml_curr_index, false); + + if (clnors_data) { + BKE_lnor_space_custom_data_to_normal(lnor_space, clnors_data[ml_curr_index], *lnor); + } + } +} + +static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) +{ + MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; + float (*loopnors)[3] = common_data->loopnors; + short (*clnors_data)[2] = common_data->clnors_data; + + const MVert *mverts = common_data->mverts; + const MEdge *medges = common_data->medges; + const MLoop *mloops = common_data->mloops; + const MPoly *mpolys = common_data->mpolys; + const int (*edge_to_loops)[2] = common_data->edge_to_loops; + const int *loop_to_poly = common_data->loop_to_poly; + const float (*polynors)[3] = common_data->polynors; + + MLoopNorSpace *lnor_space = data->lnor_space; +#if 0 /* Not needed for 'fan' loops. */ + float (*lnor)[3] = data->lnor; +#endif + const MLoop *ml_curr = data->ml_curr; + const MLoop *ml_prev = data->ml_prev; + const int ml_curr_index = data->ml_curr_index; + const int ml_prev_index = data->ml_prev_index; + const int mp_index = data->mp_index; + const int *e2l_prev = data->e2l_prev; + + BLI_Stack *edge_vectors = data->edge_vectors; + + /* Gah... We have to fan around current vertex, until we find the other non-smooth edge, + * and accumulate face normals into the vertex! + * Note in case this vertex has only one sharp edges, this is a waste because the normal is the same as + * the vertex normal, but I do not see any easy way to detect that (would need to count number + * of sharp edges per vertex, I doubt the additional memory usage would be worth it, especially as + * it should not be a common case in real-life meshes anyway). + */ + const unsigned int mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const MVert *mv_pivot = &mverts[mv_pivot_index]; + const MEdge *me_org = &medges[ml_curr->e]; /* ml_curr would be mlfan_prev if we needed that one */ + const int *e2lfan_curr; + float vec_curr[3], vec_prev[3], vec_org[3]; + const MLoop *mlfan_curr, *mlfan_next; + const MPoly *mpfan_next; + float lnor[3] = {0.0f, 0.0f, 0.0f}; + /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ + int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; + + /* We validate clnors data on the fly - cheapest way to do! */ + int clnors_avg[2] = {0, 0}; + short (*clnor_ref)[2] = NULL; + int clnors_nbr = 0; + bool clnors_invalid = false; + + /* Temp loop normal stack. */ + BLI_SMALLSTACK_DECLARE(normal, float *); + /* Temp clnors stack. */ + BLI_SMALLSTACK_DECLARE(clnors, short *); + + e2lfan_curr = e2l_prev; + mlfan_curr = ml_prev; + mlfan_curr_index = ml_prev_index; + mlfan_vert_index = ml_curr_index; + mpfan_curr_index = mp_index; + + BLI_assert(mlfan_curr_index >= 0); + BLI_assert(mlfan_vert_index >= 0); + BLI_assert(mpfan_curr_index >= 0); + + /* Only need to compute previous edge's vector once, then we can just reuse old current one! */ + { + const MVert *mv_2 = (me_org->v1 == mv_pivot_index) ? &mverts[me_org->v2] : &mverts[me_org->v1]; + + sub_v3_v3v3(vec_org, mv_2->co, mv_pivot->co); + normalize_v3(vec_org); + copy_v3_v3(vec_prev, vec_org); + + if (lnors_spacearr) { + BLI_stack_push(edge_vectors, vec_org); + } + } + + /* printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); */ + + while (true) { + const MEdge *me_curr = &medges[mlfan_curr->e]; + /* Compute edge vectors. + * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing them + * twice (or more) here. However, time gained is not worth memory and time lost, + * given the fact that this code should not be called that much in real-life meshes... + */ + { + const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : &mverts[me_curr->v1]; + + sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co); + normalize_v3(vec_curr); + } + + /* printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); */ + + { + /* Code similar to accumulate_vertex_normals_poly. */ + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(dot_v3v3(vec_curr, vec_prev)); + /* Accumulate */ + madd_v3_v3fl(lnor, polynors[mpfan_curr_index], fac); + + if (clnors_data) { + /* Accumulate all clnors, if they are not all equal we have to fix that! */ + short (*clnor)[2] = &clnors_data[mlfan_vert_index]; + if (clnors_nbr) { + clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]); + } + else { + clnor_ref = clnor; + } + clnors_avg[0] += (*clnor)[0]; + clnors_avg[1] += (*clnor)[1]; + clnors_nbr++; + /* We store here a pointer to all custom lnors processed. */ + BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); + } + } + + /* We store here a pointer to all loop-normals processed. */ + BLI_SMALLSTACK_PUSH(normal, (float *)(loopnors[mlfan_vert_index])); + + if (lnors_spacearr) { + /* Assign current lnor space to current 'vertex' loop. */ + BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, mlfan_vert_index, true); + if (me_curr != me_org) { + /* We store here all edges-normalized vectors processed. */ + BLI_stack_push(edge_vectors, vec_curr); + } + } + + if (IS_EDGE_SHARP(e2lfan_curr) || (me_curr == me_org)) { + /* Current edge is sharp and we have finished with this fan of faces around this vert, + * or this vert is smooth, and we have completed a full turn around it. + */ + /* printf("FAN: Finished!\n"); */ + break; + } + + copy_v3_v3(vec_prev, vec_curr); + + /* Warning! This is rather complex! + * We have to find our next edge around the vertex (fan mode). + * First we find the next loop, which is either previous or next to mlfan_curr_index, depending + * whether both loops using current edge are in the same direction or not, and whether + * mlfan_curr_index actually uses the vertex we are fanning around! + * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one + * (i.e. not the future mlfan_curr)... + */ + mlfan_curr_index = (e2lfan_curr[0] == mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; + mpfan_curr_index = loop_to_poly[mlfan_curr_index]; + + BLI_assert(mlfan_curr_index >= 0); + BLI_assert(mpfan_curr_index >= 0); + + mlfan_next = &mloops[mlfan_curr_index]; + mpfan_next = &mpolys[mpfan_curr_index]; + if ((mlfan_curr->v == mlfan_next->v && mlfan_curr->v == mv_pivot_index) || + (mlfan_curr->v != mlfan_next->v && mlfan_curr->v != mv_pivot_index)) + { + /* We need the previous loop, but current one is our vertex's loop. */ + mlfan_vert_index = mlfan_curr_index; + if (--mlfan_curr_index < mpfan_next->loopstart) { + mlfan_curr_index = mpfan_next->loopstart + mpfan_next->totloop - 1; + } + } + else { + /* We need the next loop, which is also our vertex's loop. */ + if (++mlfan_curr_index >= mpfan_next->loopstart + mpfan_next->totloop) { + mlfan_curr_index = mpfan_next->loopstart; + } + mlfan_vert_index = mlfan_curr_index; + } + mlfan_curr = &mloops[mlfan_curr_index]; + /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */ + + e2lfan_curr = edge_to_loops[mlfan_curr->e]; + } + + { + float lnor_len = normalize_v3(lnor); + + /* If we are generating lnor spacearr, we can now define the one for this fan, + * and optionally compute final lnor from custom data too! + */ + if (lnors_spacearr) { + if (UNLIKELY(lnor_len == 0.0f)) { + /* Use vertex normal as fallback! */ + copy_v3_v3(lnor, loopnors[mlfan_vert_index]); + lnor_len = 1.0f; + } + + BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_curr, edge_vectors); + + if (clnors_data) { + if (clnors_invalid) { + short *clnor; + + clnors_avg[0] /= clnors_nbr; + clnors_avg[1] /= clnors_nbr; + /* Fix/update all clnors of this fan with computed average value. */ + printf("Invalid clnors in this fan!\n"); + while ((clnor = BLI_SMALLSTACK_POP(clnors))) { + //print_v2("org clnor", clnor); + clnor[0] = (short)clnors_avg[0]; + clnor[1] = (short)clnors_avg[1]; + } + //print_v2("new clnors", clnors_avg); + } + /* Extra bonus: since smallstack is local to this func, no more need to empty it at all cost! */ + + BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); + } + } + + /* In case we get a zero normal here, just use vertex normal already set! */ + if (LIKELY(lnor_len != 0.0f)) { + /* Copy back the final computed normal into all related loop-normals. */ + float *nor; + + while ((nor = BLI_SMALLSTACK_POP(normal))) { + copy_v3_v3(nor, lnor); + } + } + /* Extra bonus: since smallstack is local to this func, no more need to empty it at all cost! */ + } +} + +static void loop_split_worker_do( + LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data, BLI_Stack *edge_vectors) +{ + BLI_assert(data->ml_curr); + if (data->e2l_prev) { + BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors)); + data->edge_vectors = edge_vectors; + split_loop_nor_fan_do(common_data, data); + } + else { + /* No need for edge_vectors for 'single' case! */ + split_loop_nor_single_do(common_data, data); + } +} + +static void loop_split_worker(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + LoopSplitTaskDataCommon *common_data = taskdata; + LoopSplitTaskData *data_buff; + + /* Temp edge vectors stack, only used when computing lnor spacearr. */ + BLI_Stack *edge_vectors = common_data->lnors_spacearr ? BLI_stack_new(sizeof(float[3]), __func__) : NULL; + +#ifdef DEBUG_TIME + TIMEIT_START(loop_split_worker); +#endif + + while ((data_buff = BLI_thread_queue_pop(common_data->task_queue))) { + LoopSplitTaskData *data = data_buff; + int i; + + for (i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) { + /* A NULL ml_curr is used to tag ended data! */ + if (data->ml_curr == NULL) { + break; + } + loop_split_worker_do(common_data, data, edge_vectors); + } + + MEM_freeN(data_buff); + } + + if (edge_vectors) { + BLI_stack_free(edge_vectors); + } + +#ifdef DEBUG_TIME + TIMEIT_END(loop_split_worker); +#endif +} + +/* Note we use data_buff to detect whether we are in threaded context or not, in later case it is NULL. */ +static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const bool threaded) +{ + MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; + BLI_bitmap *sharp_verts = common_data->sharp_verts; + float (*loopnors)[3] = common_data->loopnors; + + const MLoop *mloops = common_data->mloops; + const MPoly *mpolys = common_data->mpolys; + const int (*edge_to_loops)[2] = common_data->edge_to_loops; + const int numPolys = common_data->numPolys; + + const MPoly *mp; + int mp_index; + + LoopSplitTaskData *data, *data_buff = NULL, data_mem; + int data_idx = 0; + + /* Temp edge vectors stack, only used when computing lnor spacearr (and we are not multi-threading). */ + BLI_Stack *edge_vectors = (lnors_spacearr && !data_buff) ? BLI_stack_new(sizeof(float[3]), __func__) : NULL; + +#ifdef DEBUG_TIME + TIMEIT_START(loop_split_generator); +#endif + + if (!threaded) { + memset(&data_mem, 0, sizeof(data_mem)); + data = &data_mem; + } + + /* We now know edges that can be smoothed (with their vector, and their two loops), and edges that will be hard! + * Now, time to generate the normals. + */ + for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { + const MLoop *ml_curr, *ml_prev; + float (*lnors)[3]; + const int ml_last_index = (mp->loopstart + mp->totloop) - 1; + int ml_curr_index = mp->loopstart; + int ml_prev_index = ml_last_index; + + ml_curr = &mloops[ml_curr_index]; + ml_prev = &mloops[ml_prev_index]; + lnors = &loopnors[ml_curr_index]; + + for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++, lnors++) { + const int *e2l_curr = edge_to_loops[ml_curr->e]; + const int *e2l_prev = edge_to_loops[ml_prev->e]; + + if (!IS_EDGE_SHARP(e2l_curr) && (!lnors_spacearr || BLI_BITMAP_TEST_BOOL(sharp_verts, ml_curr->v))) { + /* A smooth edge, and we are not generating lnor_spacearr, or the related vertex is sharp. + * We skip it because it is either: + * - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit + * one of its ends, i.e. one of its two sharp edges), or... + * - the related vertex is a "full smooth" one, in which case pre-populated normals from vertex + * are just fine (or it has already be handled in a previous loop in case of needed lnors spacearr)! + */ + /* printf("Skipping loop %d / edge %d / vert %d(%d)\n", ml_curr_index, ml_curr->e, ml_curr->v, sharp_verts[ml_curr->v]); */ + } + else { + if (threaded) { + if (data_idx == 0) { + data_buff = MEM_callocN(sizeof(*data_buff) * LOOP_SPLIT_TASK_BLOCK_SIZE, __func__); + } + data = &data_buff[data_idx]; + } + + if (IS_EDGE_SHARP(e2l_curr) && IS_EDGE_SHARP(e2l_prev)) { + data->lnor = lnors; + data->ml_curr = ml_curr; + data->ml_prev = ml_prev; + data->ml_curr_index = ml_curr_index; +#if 0 /* Not needed for 'single' loop. */ + data->ml_prev_index = ml_prev_index; + data->e2l_prev = NULL; /* Tag as 'single' task. */ +#endif + data->mp_index = mp_index; + if (lnors_spacearr) { + data->lnor_space = BKE_lnor_space_create(lnors_spacearr); + } + } + /* We *do not need* to check/tag loops as already computed! + * Due to the fact a loop only links to one of its two edges, a same fan *will never be walked + * more than once!* + * Since we consider edges having neighbor polys with inverted (flipped) normals as sharp, we are sure + * that no fan will be skipped, even only considering the case (sharp curr_edge, smooth prev_edge), + * and not the alternative (smooth curr_edge, sharp prev_edge). + * All this due/thanks to link between normals and loop ordering (i.e. winding). + */ + else { +#if 0 /* Not needed for 'fan' loops. */ + data->lnor = lnors; +#endif + data->ml_curr = ml_curr; + data->ml_prev = ml_prev; + data->ml_curr_index = ml_curr_index; + data->ml_prev_index = ml_prev_index; + data->e2l_prev = e2l_prev; /* Also tag as 'fan' task. */ + data->mp_index = mp_index; + if (lnors_spacearr) { + data->lnor_space = BKE_lnor_space_create(lnors_spacearr); + /* Tag related vertex as sharp, to avoid fanning around it again (in case it was a smooth one). + * This *has* to be done outside of workers tasks! */ + BLI_BITMAP_ENABLE(sharp_verts, ml_curr->v); + } + } + + if (threaded) { + data_idx++; + if (data_idx == LOOP_SPLIT_TASK_BLOCK_SIZE) { + BLI_thread_queue_push(common_data->task_queue, data_buff); + data_idx = 0; + } + } + else { + loop_split_worker_do(common_data, data, edge_vectors); + memset(data, 0, sizeof(data_mem)); + } + } + + ml_prev = ml_curr; + ml_prev_index = ml_curr_index; + } + } + + if (threaded) { + /* Last block of data... Since it is calloc'ed and we use first NULL item as stopper, everything is fine. */ + if (LIKELY(data_idx)) { + BLI_thread_queue_push(common_data->task_queue, data_buff); + } + + /* This will signal all other worker threads to wake up and finish! */ + BLI_thread_queue_nowait(common_data->task_queue); + } + + if (edge_vectors) { + BLI_stack_free(edge_vectors); + } + +#ifdef DEBUG_TIME + TIMEIT_END(loop_split_generator); +#endif +} + +static void loop_split_generator(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + LoopSplitTaskDataCommon *common_data = taskdata; + + loop_split_generator_do(common_data, true); +} + +/** + * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals'). + * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry (splitting edges). + */ +void BKE_mesh_normals_loop_split( + MVert *mverts, const int numVerts, MEdge *medges, const int numEdges, + MLoop *mloops, float (*r_loopnors)[3], const int numLoops, + MPoly *mpolys, const float (*polynors)[3], const int numPolys, + const bool use_split_normals, float split_angle, + MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], int *r_loop_to_poly) +{ + + /* For now this is not supported. If we do not use split normals, we do not generate anything fancy! */ + BLI_assert(use_split_normals || !(r_lnors_spacearr)); + + if (!use_split_normals) { + /* In this case, we simply fill lnors with vnors (or fnors for flat faces), quite simple! + * Note this is done here to keep some logic and consistency in this quite complex code, + * since we may want to use lnors even when mesh's 'autosmooth' is disabled (see e.g. mesh mapping code). + * As usual, we could handle that on case-by-case basis, but simpler to keep it well confined here. + */ + int mp_index; + + for (mp_index = 0; mp_index < numPolys; mp_index++) { + MPoly *mp = &mpolys[mp_index]; + int ml_index = mp->loopstart; + const int ml_index_end = ml_index + mp->totloop; + const bool is_poly_flat = ((mp->flag & ME_SMOOTH) == 0); + + for (; ml_index < ml_index_end; ml_index++) { + if (r_loop_to_poly) { + r_loop_to_poly[ml_index] = mp_index; + } + if (is_poly_flat) { + copy_v3_v3(r_loopnors[ml_index], polynors[mp_index]); + } + else { + normal_short_to_float_v3(r_loopnors[ml_index], mverts[mloops[ml_index].v].no); + } + } + } + return; + } + + { + /* Mapping edge -> loops. * If that edge is used by more than two loops (polys), it is always sharp (and tagged as such, see below). * We also use the second loop index as a kind of flag: smooth edge: > 0, @@ -341,21 +1108,39 @@ void BKE_mesh_normals_loop_split(MVert *mverts, const int UNUSED(numVerts), MEdg int (*edge_to_loops)[2] = MEM_callocN(sizeof(int[2]) * (size_t)numEdges, __func__); /* Simple mapping from a loop to its polygon index. */ - int *loop_to_poly = MEM_mallocN(sizeof(int) * (size_t)numLoops, __func__); + int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly : MEM_mallocN(sizeof(int) * (size_t)numLoops, __func__); MPoly *mp; - int mp_index; - const bool check_angle = (split_angle < (float)M_PI); + int mp_index, me_index; + bool check_angle = (split_angle < (float)M_PI); + int i; - /* Temp normal stack. */ - BLI_SMALLSTACK_DECLARE(normal, float *); + BLI_bitmap *sharp_verts = NULL; + MLoopNorSpaceArray _lnors_spacearr = {NULL}; + + LoopSplitTaskDataCommon common_data = {NULL}; #ifdef DEBUG_TIME TIMEIT_START(BKE_mesh_normals_loop_split); #endif if (check_angle) { - split_angle = cosf(split_angle); + /* When using custom loop normals, disable the angle feature! */ + if (clnors_data) { + check_angle = false; + } + else { + split_angle = cosf(split_angle); + } + } + + if (!r_lnors_spacearr && clnors_data) { + /* We need to compute lnor spacearr if some custom lnor data are given to us! */ + r_lnors_spacearr = &_lnors_spacearr; + } + if (r_lnors_spacearr) { + BKE_lnor_spacearr_init(r_lnors_spacearr, numLoops); + sharp_verts = BLI_BITMAP_NEW((size_t)numVerts, __func__); } /* This first loop check which edges are actually smooth, and compute edge vectors. */ @@ -409,187 +1194,271 @@ void BKE_mesh_normals_loop_split(MVert *mverts, const int UNUSED(numVerts), MEdg } } - /* We now know edges that can be smoothed (with their vector, and their two loops), and edges that will be hard! - * Now, time to generate the normals. - */ - for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { - MLoop *ml_curr, *ml_prev; - float (*lnors)[3]; - const int ml_last_index = (mp->loopstart + mp->totloop) - 1; - int ml_curr_index = mp->loopstart; - int ml_prev_index = ml_last_index; + if (r_lnors_spacearr) { + /* Tag vertices that have at least one sharp edge as 'sharp' (used for the lnor spacearr computation). + * XXX This third loop over edges is a bit disappointing, could not find any other way yet. + * Not really performance-critical anyway. + */ + for (me_index = 0; me_index < numEdges; me_index++) { + const int *e2l = edge_to_loops[me_index]; + const MEdge *me = &medges[me_index]; + if (IS_EDGE_SHARP(e2l)) { + BLI_BITMAP_ENABLE(sharp_verts, me->v1); + BLI_BITMAP_ENABLE(sharp_verts, me->v2); + } + } + } - ml_curr = &mloops[ml_curr_index]; - ml_prev = &mloops[ml_prev_index]; - lnors = &r_loopnors[ml_curr_index]; + /* Init data common to all tasks. */ + common_data.lnors_spacearr = r_lnors_spacearr; + common_data.loopnors = r_loopnors; + common_data.clnors_data = clnors_data; - for (; ml_curr_index <= ml_last_index; ml_curr++, ml_curr_index++, lnors++) { - const int *e2l_curr = edge_to_loops[ml_curr->e]; - const int *e2l_prev = edge_to_loops[ml_prev->e]; + common_data.mverts = mverts; + common_data.medges = medges; + common_data.mloops = mloops; + common_data.mpolys = mpolys; + common_data.sharp_verts = sharp_verts; + common_data.edge_to_loops = (const int(*)[2])edge_to_loops; + common_data.loop_to_poly = loop_to_poly; + common_data.polynors = polynors; + common_data.numPolys = numPolys; - if (!IS_EDGE_SHARP(e2l_curr)) { - /* A smooth edge. - * We skip it because it is either: - * - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit - * one of its ends, i.e. one of its two sharp edges), or... - * - the related vertex is a "full smooth" one, in which case pre-populated normals from vertex - * are just fine! - */ - } - else if (IS_EDGE_SHARP(e2l_prev)) { - /* Simple case (both edges around that vertex are sharp in current polygon), - * this vertex just takes its poly normal. - */ - copy_v3_v3(*lnors, polynors[mp_index]); - /* No need to mark loop as done here, we won't run into it again anyway! */ - } - /* We *do not need* to check/tag loops as already computed! - * Due to the fact a loop only links to one of its two edges, a same fan *will never be walked more than - * once!* - * Since we consider edges having neighbor polys with inverted (flipped) normals as sharp, we are sure that - * no fan will be skipped, even only considering the case (sharp curr_edge, smooth prev_edge), and not the - * alternative (smooth curr_edge, sharp prev_edge). - * All this due/thanks to link between normals and loop ordering. - */ - else { - /* Gah... We have to fan around current vertex, until we find the other non-smooth edge, - * and accumulate face normals into the vertex! - * Note in case this vertex has only one sharp edges, this is a waste because the normal is the same as - * the vertex normal, but I do not see any easy way to detect that (would need to count number - * of sharp edges per vertex, I doubt the additional memory usage would be worth it, especially as - * it should not be a common case in real-life meshes anyway). - */ - const unsigned int mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ - const MVert *mv_pivot = &mverts[mv_pivot_index]; - const int *e2lfan_curr; - float vec_curr[3], vec_prev[3]; - MLoop *mlfan_curr, *mlfan_next; - MPoly *mpfan_next; - float lnor[3] = {0.0f, 0.0f, 0.0f}; - /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ - int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; - - e2lfan_curr = e2l_prev; - mlfan_curr = ml_prev; - mlfan_curr_index = ml_prev_index; - mlfan_vert_index = ml_curr_index; - mpfan_curr_index = mp_index; - - BLI_assert(mlfan_curr_index >= 0); - BLI_assert(mlfan_vert_index >= 0); - BLI_assert(mpfan_curr_index >= 0); - - /* Only need to compute previous edge's vector once, then we can just reuse old current one! */ - { - const MEdge *me_prev = &medges[ml_curr->e]; /* ml_curr would be mlfan_prev if we needed that one */ - const MVert *mv_2 = (me_prev->v1 == mv_pivot_index) ? &mverts[me_prev->v2] : &mverts[me_prev->v1]; + if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) { + /* Not enough loops to be worth the whole threading overhead... */ + loop_split_generator_do(&common_data, false); + } + else { + TaskScheduler *task_scheduler; + TaskPool *task_pool; + int nbr_workers; - sub_v3_v3v3(vec_prev, mv_2->co, mv_pivot->co); - normalize_v3(vec_prev); - } + common_data.task_queue = BLI_thread_queue_init(); - while (true) { - /* Compute edge vectors. - * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing them - * twice (or more) here. However, time gained is not worth memory and time lost, - * given the fact that this code should not be called that much in real-life meshes... - */ - { - const MEdge *me_curr = &medges[mlfan_curr->e]; - const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : - &mverts[me_curr->v1]; - - sub_v3_v3v3(vec_curr, mv_2->co, mv_pivot->co); - normalize_v3(vec_curr); - } + task_scheduler = BLI_task_scheduler_get(); + task_pool = BLI_task_pool_create(task_scheduler, NULL); - { - /* Code similar to accumulate_vertex_normals_poly. */ - /* Calculate angle between the two poly edges incident on this vertex. */ - const float fac = saacos(dot_v3v3(vec_curr, vec_prev)); - /* Accumulate */ - madd_v3_v3fl(lnor, polynors[mpfan_curr_index], fac); - } + nbr_workers = max_ii(2, BLI_task_scheduler_num_threads(task_scheduler)); + for (i = 1; i < nbr_workers; i++) { + BLI_task_pool_push(task_pool, loop_split_worker, &common_data, false, TASK_PRIORITY_HIGH); + } + BLI_task_pool_push(task_pool, loop_split_generator, &common_data, false, TASK_PRIORITY_HIGH); + BLI_task_pool_work_and_wait(task_pool); - /* We store here a pointer to all loop-normals processed. */ - BLI_SMALLSTACK_PUSH(normal, &(r_loopnors[mlfan_vert_index][0])); + BLI_task_pool_free(task_pool); - if (IS_EDGE_SHARP(e2lfan_curr)) { - /* Current edge is sharp, we have finished with this fan of faces around this vert! */ - break; - } + BLI_thread_queue_free(common_data.task_queue); + } + + MEM_freeN(edge_to_loops); + if (!r_loop_to_poly) { + MEM_freeN(loop_to_poly); + } + + if (r_lnors_spacearr) { + MEM_freeN(sharp_verts); + if (r_lnors_spacearr == &_lnors_spacearr) { + BKE_lnor_spacearr_free(r_lnors_spacearr); + } + } + +#ifdef DEBUG_TIME + TIMEIT_END(BKE_mesh_normals_loop_split); +#endif - copy_v3_v3(vec_prev, vec_curr); - - /* Warning! This is rather complex! - * We have to find our next edge around the vertex (fan mode). - * First we find the next loop, which is either previous or next to mlfan_curr_index, depending - * whether both loops using current edge are in the same direction or not, and whether - * mlfan_curr_index actually uses the vertex we are fanning around! - * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one - * (i.e. not the future mlfan_curr)... - */ - mlfan_curr_index = (e2lfan_curr[0] == mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; - mpfan_curr_index = loop_to_poly[mlfan_curr_index]; - - BLI_assert(mlfan_curr_index >= 0); - BLI_assert(mpfan_curr_index >= 0); - - mlfan_next = &mloops[mlfan_curr_index]; - mpfan_next = &mpolys[mpfan_curr_index]; - if ((mlfan_curr->v == mlfan_next->v && mlfan_curr->v == mv_pivot_index) || - (mlfan_curr->v != mlfan_next->v && mlfan_curr->v != mv_pivot_index)) - { - /* We need the previous loop, but current one is our vertex's loop. */ - mlfan_vert_index = mlfan_curr_index; - if (--mlfan_curr_index < mpfan_next->loopstart) { - mlfan_curr_index = mpfan_next->loopstart + mpfan_next->totloop - 1; - } + } +} + +#undef INDEX_UNSET +#undef INDEX_INVALID +#undef IS_EDGE_SHARP + +/** + * Compute internal representation of given custom normals (as an array of float[2]). + * It also makes sure the mesh matches those custom normals, by setting sharp edges flag as needed to get a + * same custom lnor for all loops sharing a same smooth fan. + * If use_vertices if true, custom_loopnors is assumed to be per-vertex, not per-loop + * (this allows to set whole vert's normals at once, useful in some cases). + */ +static void mesh_normals_loop_custom_set( + MVert *mverts, const int numVerts, MEdge *medges, const int numEdges, + MLoop *mloops, float (*custom_loopnors)[3], const int numLoops, + MPoly *mpolys, const float (*polynors)[3], const int numPolys, + short (*r_clnors_data)[2], const bool use_vertices) +{ + /* We *may* make that poor BKE_mesh_normals_loop_split() even more complex by making it handling that + * feature too, would probably be more efficient in absolute. + * However, this function *is not* performance-critical, since it is mostly expected to be called + * by io addons when importing custom normals, and modifier (and perhaps from some editing tools later?). + * So better to keep some simplicity here, and just call BKE_mesh_normals_loop_split() twice! + */ + MLoopNorSpaceArray lnors_spacearr = {NULL}; + BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)numLoops, __func__); + float (*lnors)[3] = MEM_callocN(sizeof(*lnors) * (size_t)numLoops, __func__); + int *loop_to_poly = MEM_mallocN(sizeof(int) * (size_t)numLoops, __func__); + /* In this case we always consider split nors as ON, and do not want to use angle to define smooth fans! */ + const bool use_split_normals = true; + const float split_angle = (float)M_PI; + int i; + + BLI_SMALLSTACK_DECLARE(clnors_data, short *); + + /* Compute current lnor spacearr. */ + BKE_mesh_normals_loop_split(mverts, numVerts, medges, numEdges, mloops, lnors, numLoops, + mpolys, polynors, numPolys, use_split_normals, split_angle, + &lnors_spacearr, NULL, loop_to_poly); + + /* Now, check each current smooth fan (one lnor space per smooth fan!), and if all its matching custom lnors + * are not (enough) equal, add sharp edges as needed. + * This way, next time we run BKE_mesh_normals_loop_split(), we'll get lnor spacearr/smooth fans matching + * given custom lnors. + * Note this code *will never* unsharp edges! + * And quite obviously, when we set custom normals per vertices, running this is absolutely useless. + */ + if (!use_vertices) { + for (i = 0; i < numLoops; i++) { + if (!lnors_spacearr.lspacearr[i]) { + /* This should not happen in theory, but in some rare case (probably ugly geometry) + * we can get some NULL loopspacearr at this point. :/ + * Maybe we should set those loops' edges as sharp? + */ + BLI_BITMAP_ENABLE(done_loops, i); + printf("WARNING! Getting invalid NULL loop space for loop %d!\n", i); + continue; + } + + if (!BLI_BITMAP_TEST(done_loops, i)) { + /* Notes: + * * In case of mono-loop smooth fan, loops is NULL, so everything is fine (we have nothing to do). + * * Loops in this linklist are ordered (in reversed order compared to how they were discovered by + * BKE_mesh_normals_loop_split(), but this is not a problem). Which means if we find a + * mismatching clnor, we know all remaining loops will have to be in a new, different smooth fan/ + * lnor space. + * * In smooth fan case, we compare each clnor against a ref one, to avoid small differences adding + * up into a real big one in the end! + */ + LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; + MLoop *prev_ml = NULL; + const float *org_nor = NULL; + + while (loops) { + const int lidx = GET_INT_FROM_POINTER(loops->link); + MLoop *ml = &mloops[lidx]; + const int nidx = lidx; + float *nor = custom_loopnors[nidx]; + + if (!org_nor) { + org_nor = nor; } - else { - /* We need the next loop, which is also our vertex's loop. */ - if (++mlfan_curr_index >= mpfan_next->loopstart + mpfan_next->totloop) { - mlfan_curr_index = mpfan_next->loopstart; - } - mlfan_vert_index = mlfan_curr_index; + else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { + /* Current normal differs too much from org one, we have to tag the edge between + * previous loop's face and current's one as sharp. + * We know those two loops do not point to the same edge, since we do not allow reversed winding + * in a same smooth fan. + */ + const MPoly *mp = &mpolys[loop_to_poly[lidx]]; + const MLoop *mlp = &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; + medges[(prev_ml->e == mlp->e) ? prev_ml->e : ml->e].flag |= ME_SHARP; + + org_nor = nor; } - mlfan_curr = &mloops[mlfan_curr_index]; - /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */ - e2lfan_curr = edge_to_loops[mlfan_curr->e]; + prev_ml = ml; + loops = loops->next; + BLI_BITMAP_ENABLE(done_loops, lidx); } + BLI_BITMAP_ENABLE(done_loops, i); /* For single loops, where lnors_spacearr.lspacearr[i]->loops is NULL. */ + } + } - /* In case we get a zero normal here, just use vertex normal already set! */ - if (LIKELY(normalize_v3(lnor) != 0.0f)) { - /* Copy back the final computed normal into all related loop-normals. */ - float *nor; - while ((nor = BLI_SMALLSTACK_POP(normal))) { - copy_v3_v3(nor, lnor); - } + /* And now, recompute our new auto lnors and lnor spacearr! */ + BKE_lnor_spacearr_clear(&lnors_spacearr); + BKE_mesh_normals_loop_split(mverts, numVerts, medges, numEdges, mloops, lnors, numLoops, + mpolys, polynors, numPolys, use_split_normals, split_angle, + &lnors_spacearr, NULL, loop_to_poly); + } + else { + BLI_BITMAP_SET_ALL(done_loops, true, (size_t)numLoops); + } + + /* And we just have to convert plain object-space custom normals to our lnor space-encoded ones. */ + for (i = 0; i < numLoops; i++) { + if (!lnors_spacearr.lspacearr[i]) { + BLI_BITMAP_DISABLE(done_loops, i); + printf("WARNING! Still getting invalid NULL loop space in second loop for loop %d!\n", i); + continue; + } + + if (BLI_BITMAP_TEST_BOOL(done_loops, i)) { + /* Note we accumulate and average all custom normals in current smooth fan, to avoid getting different + * clnors data (tiny differences in plain custom normals can give rather huge differences in + * computed 2D factors). + */ + LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; + if (loops) { + int nbr_nors = 0; + float avg_nor[3]; + short clnor_data_tmp[2], *clnor_data; + + zero_v3(avg_nor); + while (loops) { + const int lidx = GET_INT_FROM_POINTER(loops->link); + const int nidx = use_vertices ? (int)mloops[lidx].v : lidx; + float *nor = custom_loopnors[nidx]; + + nbr_nors++; + add_v3_v3(avg_nor, nor); + BLI_SMALLSTACK_PUSH(clnors_data, (short *)r_clnors_data[lidx]); + + loops = loops->next; + BLI_BITMAP_DISABLE(done_loops, lidx); } - else { - /* We still have to clear the stack! */ - while (BLI_SMALLSTACK_POP(normal)); + + mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors); + BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], avg_nor, clnor_data_tmp); + + while ((clnor_data = BLI_SMALLSTACK_POP(clnors_data))) { + clnor_data[0] = clnor_data_tmp[0]; + clnor_data[1] = clnor_data_tmp[1]; } } + else { + const int nidx = use_vertices ? (int)mloops[i].v : i; + float *nor = custom_loopnors[nidx]; - ml_prev = ml_curr; - ml_prev_index = ml_curr_index; + BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], nor, r_clnors_data[i]); + BLI_BITMAP_DISABLE(done_loops, i); + } } } - MEM_freeN(edge_to_loops); + MEM_freeN(lnors); MEM_freeN(loop_to_poly); + MEM_freeN(done_loops); + BKE_lnor_spacearr_free(&lnors_spacearr); +} -#ifdef DEBUG_TIME - TIMEIT_END(BKE_mesh_normals_loop_split); -#endif +void BKE_mesh_normals_loop_custom_set( + MVert *mverts, const int numVerts, MEdge *medges, const int numEdges, + MLoop *mloops, float (*custom_loopnors)[3], const int numLoops, + MPoly *mpolys, const float (*polynors)[3], const int numPolys, + short (*r_clnors_data)[2]) +{ + mesh_normals_loop_custom_set(mverts, numVerts, medges, numEdges, mloops, custom_loopnors, numLoops, + mpolys, polynors, numPolys, r_clnors_data, false); +} -#undef INDEX_UNSET -#undef INDEX_INVALID -#undef IS_EDGE_SHARP +void BKE_mesh_normals_loop_custom_from_vertices_set( + MVert *mverts, float (*custom_vertnors)[3], const int numVerts, + MEdge *medges, const int numEdges, MLoop *mloops, const int numLoops, + MPoly *mpolys, const float (*polynors)[3], const int numPolys, + short (*r_clnors_data)[2]) +{ + mesh_normals_loop_custom_set(mverts, numVerts, medges, numEdges, mloops, custom_vertnors, numLoops, + mpolys, polynors, numPolys, r_clnors_data, true); } +#undef LNOR_SPACE_TRIGO_THRESHOLD /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index 1deb7cfdd7f..aca72614094 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -933,8 +933,9 @@ void BKE_mesh_remap_calc_loops_from_dm( const int mode, const SpaceTransform *space_transform, const float max_dist, const float ray_radius, MVert *verts_dst, const int numverts_dst, MEdge *edges_dst, const int numedges_dst, MLoop *loops_dst, const int numloops_dst, MPoly *polys_dst, const int numpolys_dst, - CustomData *ldata_dst, CustomData *pdata_dst, const float split_angle_dst, const bool dirty_nors_dst, - DerivedMesh *dm_src, + CustomData *ldata_dst, CustomData *pdata_dst, + const bool use_split_nors_dst, const float split_angle_dst, const bool dirty_nors_dst, + DerivedMesh *dm_src, const bool use_split_nors_src, const float split_angle_src, MeshRemapIslandsCalc gen_islands_src, const float islands_precision_src, MeshPairRemap *r_map) { const float full_weight = 1.0f; @@ -1046,21 +1047,24 @@ void BKE_mesh_remap_calc_loops_from_dm( } } if (need_lnors_dst) { + short (*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL); + /* Cache poly nors into a temp CDLayer. */ loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL); - if (!loop_nors_dst) { - loop_nors_dst = CustomData_add_layer(ldata_dst, CD_NORMAL, CD_CALLOC, NULL, numloops_dst); - CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY); - } - if (dirty_nors_dst) { + if (dirty_nors_dst || !loop_nors_dst) { + if (!loop_nors_dst) { + loop_nors_dst = CustomData_add_layer(ldata_dst, CD_NORMAL, CD_CALLOC, NULL, numloops_dst); + CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY); + } BKE_mesh_normals_loop_split(verts_dst, numverts_dst, edges_dst, numedges_dst, loops_dst, loop_nors_dst, numloops_dst, - polys_dst, poly_nors_dst, numpolys_dst, split_angle_dst); + polys_dst, (const float (*)[3])poly_nors_dst, numpolys_dst, + use_split_nors_dst, split_angle_dst, NULL, custom_nors_dst, NULL); } } if (need_pnors_src || need_lnors_src) { /* Simpler for now, calcNormals never stores pnors :( */ - dm_src->calcLoopNormals(dm_src, /* TODO */ (float)M_PI); + dm_src->calcLoopNormals(dm_src, use_split_nors_src, split_angle_src); if (need_pnors_src) { poly_nors_src = dm_src->getPolyDataArray(dm_src, CD_NORMAL); @@ -1425,7 +1429,7 @@ void BKE_mesh_remap_calc_loops_from_dm( best_island = use_islands ? island_store.islands[best_island_index] : NULL; as_graph = &as_graphdata[best_island_index]; poly_island_index_map = (int *)as_graph->custom_data; - BLI_astar_solution_init(as_graph, &as_solution, false); + BLI_astar_solution_init(as_graph, &as_solution, NULL); } for (plidx_dst = 0; plidx_dst < mp_dst->totloop; plidx_dst++) { @@ -1448,8 +1452,18 @@ void BKE_mesh_remap_calc_loops_from_dm( pidx_src = loop_to_poly_map_src[lidx_src]; /* If prev and curr poly are the same, no need to do anything more!!! */ if (!ELEM(pidx_src_prev, -1, pidx_src) && isld_steps_src) { + int pidx_isld_src, pidx_isld_src_prev; + if (poly_island_index_map) { + pidx_isld_src = poly_island_index_map[pidx_src]; + pidx_isld_src_prev = poly_island_index_map[pidx_src_prev]; + } + else { + pidx_isld_src = pidx_src; + pidx_isld_src_prev = pidx_src_prev; + } + BLI_astar_graph_solve( - as_graph, poly_island_index_map[pidx_src_prev], poly_island_index_map[pidx_src], + as_graph, pidx_isld_src_prev, pidx_isld_src, mesh_remap_calc_loops_astar_f_cost, &as_solution, isld_steps_src); if (GET_INT_FROM_POINTER(as_solution.custom_data) && (as_solution.steps > 0)) { /* Find first 'cutting edge' on path, and bring back lidx_src on poly just @@ -1459,7 +1473,6 @@ void BKE_mesh_remap_calc_loops_from_dm( * but this is one more level of complexity, better to first see if * simple solution works! */ - int pidx_isld_src = poly_island_index_map[pidx_src]; int last_valid_pidx_isld_src = -1; /* Note we go backward here, from dest to src poly. */ for (i = as_solution.steps - 1; i--;) { @@ -1525,8 +1538,18 @@ void BKE_mesh_remap_calc_loops_from_dm( /* If prev and curr poly are the same, no need to do anything more!!! */ if (!ELEM(pidx_src_prev, -1, pidx_src) && isld_steps_src) { + int pidx_isld_src, pidx_isld_src_prev; + if (poly_island_index_map) { + pidx_isld_src = poly_island_index_map[pidx_src]; + pidx_isld_src_prev = poly_island_index_map[pidx_src_prev]; + } + else { + pidx_isld_src = pidx_src; + pidx_isld_src_prev = pidx_src_prev; + } + BLI_astar_graph_solve( - as_graph, poly_island_index_map[pidx_src_prev], poly_island_index_map[pidx_src], + as_graph, pidx_isld_src_prev, pidx_isld_src, mesh_remap_calc_loops_astar_f_cost, &as_solution, isld_steps_src); if (GET_INT_FROM_POINTER(as_solution.custom_data) && (as_solution.steps > 0)) { /* Find first 'cutting edge' on path, and bring back lidx_src on poly just @@ -1536,7 +1559,6 @@ void BKE_mesh_remap_calc_loops_from_dm( * but this is one more level of complexity, better to first see if * simple solution works! */ - int pidx_isld_src = poly_island_index_map[pidx_src]; int last_valid_pidx_isld_src = -1; /* Note we go backward here, from dest to src poly. */ for (i = as_solution.steps - 1; i--;) { diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index 3a6c949fe9c..4b2c2aba691 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -963,7 +963,7 @@ bool BKE_mesh_validate_all_customdata(CustomData *vdata, CustomData *edata, * * \returns true if a change is made. */ -int BKE_mesh_validate(Mesh *me, const int do_verbose) +int BKE_mesh_validate(Mesh *me, const int do_verbose, const int cddata_check_mask) { bool is_valid = true; bool changed; @@ -974,7 +974,7 @@ int BKE_mesh_validate(Mesh *me, const int do_verbose) is_valid &= BKE_mesh_validate_all_customdata( &me->vdata, &me->edata, &me->ldata, &me->pdata, - true, + cddata_check_mask, do_verbose, true, &changed); diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index fe38fedd7aa..491fb46bd1f 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -621,7 +621,7 @@ MovieClip *BKE_movieclip_file_add(Main *bmain, const char *name) BLI_strncpy(strtest, clip->name, sizeof(clip->name)); BLI_path_abs(strtest, G.main->name); - if (strcmp(strtest, str) == 0) { + if (STREQ(strtest, str)) { BLI_strncpy(clip->name, name, sizeof(clip->name)); /* for stringcode */ clip->id.us++; /* officially should not, it doesn't link here! */ diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 0adc65bd806..220d0f7c604 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -1641,7 +1641,8 @@ void multires_free(Multires *mr) lvl = lvl->next; } - MEM_freeN(mr->verts); + /* mr->verts may be NULL when loading old files, see direct_link_mesh() in readfile.c, and T43560. */ + MEM_SAFE_FREE(mr->verts); BLI_freelistN(&mr->levels); diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 039af949d81..f30e3b699dd 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -2250,7 +2250,7 @@ StructRNA *ntreeInterfaceTypeGet(bNodeTree *ntree, int create) ntree_interface_identifier_base(ntree, base); /* RNA identifier may have a number suffix, but should start with the idbase string */ - if (strncmp(RNA_struct_identifier(srna), base, sizeof(base)) != 0) { + if (!STREQLEN(RNA_struct_identifier(srna), base, sizeof(base))) { /* generate new unique RNA identifier from the ID name */ ntree_interface_identifier(ntree, base, identifier, sizeof(identifier), name, description); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 41f7721d59f..7e0228ac09c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -684,6 +684,7 @@ void BKE_object_unlink(Object *ob) if (sce->camera == ob) sce->camera = NULL; if (sce->toolsettings->skgen_template == ob) sce->toolsettings->skgen_template = NULL; if (sce->toolsettings->particle.object == ob) sce->toolsettings->particle.object = NULL; + if (sce->toolsettings->particle.shape_object == ob) sce->toolsettings->particle.shape_object = NULL; #ifdef DURIAN_CAMERA_SWITCH { @@ -1234,7 +1235,7 @@ BulletSoftBody *copy_bulletsoftbody(BulletSoftBody *bsb) return bsbn; } -static ParticleSystem *copy_particlesystem(ParticleSystem *psys) +ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys) { ParticleSystem *psysn; ParticleData *pa; @@ -1314,7 +1315,7 @@ void BKE_object_copy_particlesystems(Object *obn, Object *ob) BLI_listbase_clear(&obn->particlesystem); for (psys = ob->particlesystem.first; psys; psys = psys->next) { - npsys = copy_particlesystem(psys); + npsys = BKE_object_copy_particlesystem(psys); BLI_addtail(&obn->particlesystem, npsys); diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index 2bee6b76ad6..c77f65f69e1 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -969,9 +969,9 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem } /* some hair paths might be non-existent so they can't be used for duplication */ - if (hair && - ((a < totpart && psys->pathcache[a]->steps < 0) || - (a >= totpart && psys->childcache[a - totpart]->steps < 0))) + if (hair && psys->pathcache && + ((a < totpart && psys->pathcache[a]->segments < 0) || + (a >= totpart && psys->childcache[a - totpart]->segments < 0))) { continue; } diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 373ad34e7bd..12e82d3a34f 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -1002,7 +1002,7 @@ static void cache_filename(char *string, const char *path, const char *relbase, BLI_join_dirfile(cachepath, sizeof(cachepath), path, fname); - BKE_makepicstring_from_type(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true); + BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true); } /* silly functions but useful to inline when the args do a lot of indirections */ diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 78a54e5c6ac..03a25301e3c 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -55,6 +55,7 @@ #include "BLI_utildefines.h" #include "BLI_kdtree.h" #include "BLI_rand.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_linklist.h" @@ -65,6 +66,7 @@ #include "BKE_boids.h" #include "BKE_cloth.h" +#include "BKE_colortools.h" #include "BKE_effect.h" #include "BKE_global.h" #include "BKE_group.h" @@ -103,9 +105,9 @@ void psys_init_rng(void) static void get_child_modifier_parameters(ParticleSettings *part, ParticleThreadContext *ctx, ChildParticle *cpa, short cpa_from, int cpa_num, float *cpa_fuv, float *orco, ParticleTexture *ptex); -static void do_child_modifiers(ParticleSimulationData *sim, - ParticleTexture *ptex, ParticleKey *par, float *par_rot, ChildParticle *cpa, - float *orco, float mat[4][4], ParticleKey *state, float t); +extern void do_child_modifiers(ParticleSimulationData *sim, + ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3], + ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t); /* few helpers for countall etc. */ int count_particles(ParticleSystem *psys) @@ -139,7 +141,7 @@ int count_particles_mod(ParticleSystem *psys, int totgr, int cur) #define PATH_CACHE_BUF_SIZE 1024 -static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot, int steps) +static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot, int totkeys) { LinkData *buf; ParticleCacheKey **cache; @@ -152,10 +154,10 @@ static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot, while (totkey < tot) { totbufkey = MIN2(tot - totkey, PATH_CACHE_BUF_SIZE); buf = MEM_callocN(sizeof(LinkData), "PathCacheLinkData"); - buf->data = MEM_callocN(sizeof(ParticleCacheKey) * totbufkey * steps, "ParticleCacheKey"); + buf->data = MEM_callocN(sizeof(ParticleCacheKey) * totbufkey * totkeys, "ParticleCacheKey"); for (i = 0; i < totbufkey; i++) - cache[totkey + i] = ((ParticleCacheKey *)buf->data) + i * steps; + cache[totkey + i] = ((ParticleCacheKey *)buf->data) + i * totkeys; totkey += totbufkey; BLI_addtail(bufs, buf); @@ -374,6 +376,12 @@ void BKE_particlesettings_free(ParticleSettings *part) MTex *mtex; int a; BKE_free_animdata(&part->id); + + if (part->clumpcurve) + curvemapping_free(part->clumpcurve); + if (part->roughcurve) + curvemapping_free(part->roughcurve); + free_partdeflect(part->pd); free_partdeflect(part->pd2); @@ -408,6 +416,7 @@ void free_hair(Object *UNUSED(ob), ParticleSystem *psys, int dynamics) if (psys->clmd) { if (dynamics) { BKE_ptcache_free(psys->pointcache); + psys->clmd->point_cache = psys->pointcache = NULL; modifier_free((ModifierData *)psys->clmd); @@ -591,89 +600,6 @@ void psys_free(Object *ob, ParticleSystem *psys) * rendering, to make different render settings possible without * removing the previous data. this should be solved properly once */ -typedef struct ParticleRenderElem { - int curchild, totchild, reduce; - float lambda, t, scalemin, scalemax; -} ParticleRenderElem; - -typedef struct ParticleRenderData { - ChildParticle *child; - ParticleCacheKey **pathcache; - ParticleCacheKey **childcache; - ListBase pathcachebufs, childcachebufs; - int totchild, totcached, totchildcache; - DerivedMesh *dm; - int totdmvert, totdmedge, totdmface; - - float mat[4][4]; - float viewmat[4][4], winmat[4][4]; - int winx, winy; - - int do_simplify; - int timeoffset; - ParticleRenderElem *elems; - - /* ORIGINDEX */ - const int *index_mf_to_mpoly; - const int *index_mp_to_orig; -} ParticleRenderData; - -static float psys_render_viewport_falloff(double rate, float dist, float width) -{ - return pow(rate, dist / width); -} - -static float psys_render_projected_area(ParticleSystem *psys, const float center[3], float area, double vprate, float *viewport) -{ - ParticleRenderData *data = psys->renderdata; - float co[4], view[3], ortho1[3], ortho2[3], w, dx, dy, radius; - - /* transform to view space */ - copy_v3_v3(co, center); - co[3] = 1.0f; - mul_m4_v4(data->viewmat, co); - - /* compute two vectors orthogonal to view vector */ - normalize_v3_v3(view, co); - ortho_basis_v3v3_v3(ortho1, ortho2, view); - - /* compute on screen minification */ - w = co[2] * data->winmat[2][3] + data->winmat[3][3]; - dx = data->winx * ortho2[0] * data->winmat[0][0]; - dy = data->winy * ortho2[1] * data->winmat[1][1]; - w = sqrtf(dx * dx + dy * dy) / w; - - /* w squared because we are working with area */ - area = area * w * w; - - /* viewport of the screen test */ - - /* project point on screen */ - mul_m4_v4(data->winmat, co); - if (co[3] != 0.0f) { - co[0] = 0.5f * data->winx * (1.0f + co[0] / co[3]); - co[1] = 0.5f * data->winy * (1.0f + co[1] / co[3]); - } - - /* screen space radius */ - radius = sqrtf(area / (float)M_PI); - - /* make smaller using fallof once over screen edge */ - *viewport = 1.0f; - - if (co[0] + radius < 0.0f) - *viewport *= psys_render_viewport_falloff(vprate, -(co[0] + radius), data->winx); - else if (co[0] - radius > data->winx) - *viewport *= psys_render_viewport_falloff(vprate, (co[0] - radius) - data->winx, data->winx); - - if (co[1] + radius < 0.0f) - *viewport *= psys_render_viewport_falloff(vprate, -(co[1] + radius), data->winy); - else if (co[1] - radius > data->winy) - *viewport *= psys_render_viewport_falloff(vprate, (co[1] - radius) - data->winy, data->winy); - - return area; -} - void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[4][4], float winmat[4][4], int winx, int winy, int timeoffset) { ParticleRenderData *data; @@ -788,191 +714,6 @@ void psys_render_restore(Object *ob, ParticleSystem *psys) } } -/* BMESH_TODO, for orig face data, we need to use MPoly */ - -int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) -{ - DerivedMesh *dm = ctx->dm; - Mesh *me = (Mesh *)(ctx->sim.ob->data); - MFace *mf, *mface; - MVert *mvert; - ParticleRenderData *data; - ParticleRenderElem *elems, *elem; - ParticleSettings *part = ctx->sim.psys->part; - float *facearea, (*facecenter)[3], size[3], fac, powrate, scaleclamp; - float co1[3], co2[3], co3[3], co4[3], lambda, arearatio, t, area, viewport; - double vprate; - int *facetotvert; - int a, b, totorigface, totface, newtot, skipped; - - /* double lookup */ - const int *index_mf_to_mpoly; - const int *index_mp_to_orig; - - if (part->ren_as != PART_DRAW_PATH || !(part->draw & PART_DRAW_REN_STRAND)) - return tot; - if (!ctx->sim.psys->renderdata) - return tot; - - data = ctx->sim.psys->renderdata; - if (data->timeoffset) - return 0; - if (!(part->simplify_flag & PART_SIMPLIFY_ENABLE)) - return tot; - - mvert = dm->getVertArray(dm); - mface = dm->getTessFaceArray(dm); - totface = dm->getNumTessFaces(dm); - totorigface = me->totpoly; - - if (totface == 0 || totorigface == 0) - return tot; - - index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); - index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); - if (index_mf_to_mpoly == NULL) { - index_mp_to_orig = NULL; - } - - facearea = MEM_callocN(sizeof(float) * totorigface, "SimplifyFaceArea"); - facecenter = MEM_callocN(sizeof(float[3]) * totorigface, "SimplifyFaceCenter"); - facetotvert = MEM_callocN(sizeof(int) * totorigface, "SimplifyFaceArea"); - elems = MEM_callocN(sizeof(ParticleRenderElem) * totorigface, "SimplifyFaceElem"); - - if (data->elems) - MEM_freeN(data->elems); - - data->do_simplify = true; - data->elems = elems; - data->index_mf_to_mpoly = index_mf_to_mpoly; - data->index_mp_to_orig = index_mp_to_orig; - - /* compute number of children per original face */ - for (a = 0; a < tot; a++) { - b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, ctx->index[a]) : ctx->index[a]; - if (b != ORIGINDEX_NONE) { - elems[b].totchild++; - } - } - - /* compute areas and centers of original faces */ - for (mf = mface, a = 0; a < totface; a++, mf++) { - b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a) : a; - - if (b != ORIGINDEX_NONE) { - copy_v3_v3(co1, mvert[mf->v1].co); - copy_v3_v3(co2, mvert[mf->v2].co); - copy_v3_v3(co3, mvert[mf->v3].co); - - add_v3_v3(facecenter[b], co1); - add_v3_v3(facecenter[b], co2); - add_v3_v3(facecenter[b], co3); - - if (mf->v4) { - copy_v3_v3(co4, mvert[mf->v4].co); - add_v3_v3(facecenter[b], co4); - facearea[b] += area_quad_v3(co1, co2, co3, co4); - facetotvert[b] += 4; - } - else { - facearea[b] += area_tri_v3(co1, co2, co3); - facetotvert[b] += 3; - } - } - } - - for (a = 0; a < totorigface; a++) - if (facetotvert[a] > 0) - mul_v3_fl(facecenter[a], 1.0f / facetotvert[a]); - - /* for conversion from BU area / pixel area to reference screen size */ - BKE_mesh_texspace_get(me, 0, 0, size); - fac = ((size[0] + size[1] + size[2]) / 3.0f) / part->simplify_refsize; - fac = fac * fac; - - powrate = log(0.5f) / log(part->simplify_rate * 0.5f); - if (part->simplify_flag & PART_SIMPLIFY_VIEWPORT) - vprate = pow(1.0f - part->simplify_viewport, 5.0); - else - vprate = 1.0; - - /* set simplification parameters per original face */ - for (a = 0, elem = elems; a < totorigface; a++, elem++) { - area = psys_render_projected_area(ctx->sim.psys, facecenter[a], facearea[a], vprate, &viewport); - arearatio = fac * area / facearea[a]; - - if ((arearatio < 1.0f || viewport < 1.0f) && elem->totchild) { - /* lambda is percentage of elements to keep */ - lambda = (arearatio < 1.0f) ? powf(arearatio, powrate) : 1.0f; - lambda *= viewport; - - lambda = MAX2(lambda, 1.0f / elem->totchild); - - /* compute transition region */ - t = part->simplify_transition; - elem->t = (lambda - t < 0.0f) ? lambda : (lambda + t > 1.0f) ? 1.0f - lambda : t; - elem->reduce = 1; - - /* scale at end and beginning of the transition region */ - elem->scalemax = (lambda + t < 1.0f) ? 1.0f / lambda : 1.0f / (1.0f - elem->t * elem->t / t); - elem->scalemin = (lambda + t < 1.0f) ? 0.0f : elem->scalemax * (1.0f - elem->t / t); - - elem->scalemin = sqrtf(elem->scalemin); - elem->scalemax = sqrtf(elem->scalemax); - - /* clamp scaling */ - scaleclamp = (float)min_ii(elem->totchild, 10); - elem->scalemin = MIN2(scaleclamp, elem->scalemin); - elem->scalemax = MIN2(scaleclamp, elem->scalemax); - - /* extend lambda to include transition */ - lambda = lambda + elem->t; - if (lambda > 1.0f) - lambda = 1.0f; - } - else { - lambda = arearatio; - - elem->scalemax = 1.0f; //sqrt(lambda); - elem->scalemin = 1.0f; //sqrt(lambda); - elem->reduce = 0; - } - - elem->lambda = lambda; - elem->scalemin = sqrtf(elem->scalemin); - elem->scalemax = sqrtf(elem->scalemax); - elem->curchild = 0; - } - - MEM_freeN(facearea); - MEM_freeN(facecenter); - MEM_freeN(facetotvert); - - /* move indices and set random number skipping */ - ctx->skip = MEM_callocN(sizeof(int) * tot, "SimplificationSkip"); - - skipped = 0; - for (a = 0, newtot = 0; a < tot; a++) { - b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, ctx->index[a]) : ctx->index[a]; - - if (b != ORIGINDEX_NONE) { - if (elems[b].curchild++ < ceil(elems[b].lambda * elems[b].totchild)) { - ctx->index[newtot] = ctx->index[a]; - ctx->skip[newtot] = skipped; - skipped = 0; - newtot++; - } - else skipped++; - } - else skipped++; - } - - for (a = 0, elem = elems; a < totorigface; a++, elem++) - elem->curchild = 0; - - return newtot; -} - bool psys_render_simplify_params(ParticleSystem *psys, ChildParticle *cpa, float *params) { ParticleRenderData *data; @@ -1399,9 +1140,9 @@ static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCach ParticleCacheKey *cur = first; /* scale the requested time to fit the entire path even if the path is cut early */ - t *= (first + first->steps)->time; + t *= (first + first->segments)->time; - while (i < first->steps && cur->time < t) + while (i < first->segments && cur->time < t) cur++; if (cur->time == t) @@ -1906,6 +1647,42 @@ static void psys_particle_on_shape(int UNUSED(distr), int UNUSED(index), /************************************************/ /* Particles on emitter */ /************************************************/ + +CustomDataMask psys_emitter_customdata_mask(ParticleSystem *psys) +{ + CustomDataMask dataMask = 0; + MTex *mtex; + int i; + + if (!psys->part) + return 0; + + for (i = 0; i < MAX_MTEX; i++) { + mtex = psys->part->mtex[i]; + if (mtex && mtex->mapto && (mtex->texco & TEXCO_UV)) + dataMask |= CD_MASK_MTFACE; + } + + if (psys->part->tanfac != 0.0f) + dataMask |= CD_MASK_MTFACE; + + /* ask for vertexgroups if we need them */ + for (i = 0; i < PSYS_TOT_VG; i++) { + if (psys->vgroup[i]) { + dataMask |= CD_MASK_MDEFORMVERT; + break; + } + } + + /* particles only need this if they are after a non deform modifier, and + * the modifier stack will only create them in that case. */ + dataMask |= CD_MASK_ORIGSPACE_MLOOP | CD_MASK_ORIGINDEX; + + dataMask |= CD_MASK_ORCO; + + return dataMask; +} + void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int index, int index_dmcache, float fuv[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3], float orco[3], float ornor[3]) @@ -1930,202 +1707,11 @@ void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int in /* Path Cache */ /************************************************/ -static void do_kink(ParticleKey *state, ParticleKey *par, float *par_rot, float time, float freq, float shape, float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start) -{ - float kink[3] = {1.f, 0.f, 0.f}, par_vec[3], q1[4] = {1.f, 0.f, 0.f, 0.f}; - float t, dt = 1.f, result[3]; - - if (par == NULL || type == PART_KINK_NO) - return; - - CLAMP(time, 0.f, 1.f); - - if (shape != 0.0f && type != PART_KINK_BRAID) { - if (shape < 0.0f) - time = (float)pow(time, 1.f + shape); - else - time = (float)pow(time, 1.f / (1.f - shape)); - } - - t = time * freq * (float)M_PI; - - if (smooth_start) { - dt = fabsf(t); - /* smooth the beginning of kink */ - CLAMP(dt, 0.f, (float)M_PI); - dt = sinf(dt / 2.f); - } - - if (type != PART_KINK_RADIAL) { - float temp[3]; - - kink[axis] = 1.f; - - if (obmat) - mul_mat3_m4_v3(obmat, kink); - - if (par_rot) - mul_qt_v3(par_rot, kink); - - /* make sure kink is normal to strand */ - project_v3_v3v3(temp, kink, par->vel); - sub_v3_v3(kink, temp); - normalize_v3(kink); - } - - copy_v3_v3(result, state->co); - sub_v3_v3v3(par_vec, par->co, state->co); - - switch (type) { - case PART_KINK_CURL: - { - negate_v3(par_vec); - - if (flat > 0.f) { - float proj[3]; - project_v3_v3v3(proj, par_vec, par->vel); - madd_v3_v3fl(par_vec, proj, -flat); - - project_v3_v3v3(proj, par_vec, kink); - madd_v3_v3fl(par_vec, proj, -flat); - } - - axis_angle_to_quat(q1, kink, (float)M_PI / 2.f); - - mul_qt_v3(q1, par_vec); - - madd_v3_v3fl(par_vec, kink, amplitude); - - /* rotate kink vector around strand tangent */ - if (t != 0.f) { - axis_angle_to_quat(q1, par->vel, t); - mul_qt_v3(q1, par_vec); - } - - add_v3_v3v3(result, par->co, par_vec); - break; - } - case PART_KINK_RADIAL: - { - if (flat > 0.f) { - float proj[3]; - /* flatten along strand */ - project_v3_v3v3(proj, par_vec, par->vel); - madd_v3_v3fl(result, proj, flat); - } - - madd_v3_v3fl(result, par_vec, -amplitude * sinf(t)); - break; - } - case PART_KINK_WAVE: - { - madd_v3_v3fl(result, kink, amplitude * sinf(t)); - - if (flat > 0.f) { - float proj[3]; - /* flatten along wave */ - project_v3_v3v3(proj, par_vec, kink); - madd_v3_v3fl(result, proj, flat); - - /* flatten along strand */ - project_v3_v3v3(proj, par_vec, par->vel); - madd_v3_v3fl(result, proj, flat); - } - break; - } - case PART_KINK_BRAID: - { - float y_vec[3] = {0.f, 1.f, 0.f}; - float z_vec[3] = {0.f, 0.f, 1.f}; - float vec_one[3], state_co[3]; - float inp_y, inp_z, length; - - if (par_rot) { - mul_qt_v3(par_rot, y_vec); - mul_qt_v3(par_rot, z_vec); - } - - negate_v3(par_vec); - normalize_v3_v3(vec_one, par_vec); - - inp_y = dot_v3v3(y_vec, vec_one); - inp_z = dot_v3v3(z_vec, vec_one); - - if (inp_y > 0.5f) { - copy_v3_v3(state_co, y_vec); - - mul_v3_fl(y_vec, amplitude * cosf(t)); - mul_v3_fl(z_vec, amplitude / 2.f * sinf(2.f * t)); - } - else if (inp_z > 0.0f) { - mul_v3_v3fl(state_co, z_vec, sinf((float)M_PI / 3.f)); - madd_v3_v3fl(state_co, y_vec, -0.5f); - - mul_v3_fl(y_vec, -amplitude * cosf(t + (float)M_PI / 3.f)); - mul_v3_fl(z_vec, amplitude / 2.f * cosf(2.f * t + (float)M_PI / 6.f)); - } - else { - mul_v3_v3fl(state_co, z_vec, -sinf((float)M_PI / 3.f)); - madd_v3_v3fl(state_co, y_vec, -0.5f); - - mul_v3_fl(y_vec, amplitude * -sinf(t + (float)M_PI / 6.f)); - mul_v3_fl(z_vec, amplitude / 2.f * -sinf(2.f * t + (float)M_PI / 3.f)); - } +extern void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat, + short type, short axis, float obmat[4][4], int smooth_start); +extern float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump, + bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve); - mul_v3_fl(state_co, amplitude); - add_v3_v3(state_co, par->co); - sub_v3_v3v3(par_vec, state->co, state_co); - - length = normalize_v3(par_vec); - mul_v3_fl(par_vec, MIN2(length, amplitude / 2.f)); - - add_v3_v3v3(state_co, par->co, y_vec); - add_v3_v3(state_co, z_vec); - add_v3_v3(state_co, par_vec); - - shape = 2.f * (float)M_PI * (1.f + shape); - - if (t < shape) { - shape = t / shape; - shape = (float)sqrt((double)shape); - interp_v3_v3v3(result, result, state_co, shape); - } - else { - copy_v3_v3(result, state_co); - } - break; - } - } - - /* blend the start of the kink */ - if (dt < 1.f) - interp_v3_v3v3(state->co, state->co, result, dt); - else - copy_v3_v3(state->co, result); -} - -static float do_clump(ParticleKey *state, ParticleKey *par, float time, float clumpfac, float clumppow, float pa_clump) -{ - float clump = 0.f; - - if (par && clumpfac != 0.0f) { - float cpow; - - if (clumppow < 0.0f) - cpow = 1.0f + clumppow; - else - cpow = 1.0f + 9.0f * clumppow; - - if (clumpfac < 0.0f) /* clump roots instead of tips */ - clump = -clumpfac * pa_clump * (float)pow(1.0 - (double)time, (double)cpow); - else - clump = clumpfac * pa_clump * (float)pow((double)time, (double)cpow); - - interp_v3_v3v3(state->co, state->co, par->co, clump); - } - - return clump; -} void precalc_guides(ParticleSimulationData *sim, ListBase *effectors) { EffectedPoint point; @@ -2166,12 +1752,14 @@ void precalc_guides(ParticleSimulationData *sim, ListBase *effectors) } } } -int do_guides(ListBase *effectors, ParticleKey *state, int index, float time) + +int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, int index, float time) { + CurveMapping *clumpcurve = (part->child_flag & PART_CHILD_USE_CLUMP_CURVE) ? part->clumpcurve : NULL; + CurveMapping *roughcurve = (part->child_flag & PART_CHILD_USE_ROUGH_CURVE) ? part->roughcurve : NULL; EffectorCache *eff; PartDeflect *pd; Curve *cu; - ParticleKey key, par; GuideEffectorData *data; float effect[3] = {0.0f, 0.0f, 0.0f}, veffect[3] = {0.0f, 0.0f, 0.0f}; @@ -2180,78 +1768,92 @@ int do_guides(ListBase *effectors, ParticleKey *state, int index, float time) float vec_to_point[3]; if (effectors) for (eff = effectors->first; eff; eff = eff->next) { - pd = eff->pd; - - if (pd->forcefield != PFIELD_GUIDE) - continue; - - data = eff->guide_data + index; - - if (data->strength <= 0.0f) - continue; - - guidetime = time / (1.0f - pd->free_end); - - if (guidetime > 1.0f) - continue; - - cu = (Curve *)eff->ob->data; - - if (pd->flag & PFIELD_GUIDE_PATH_ADD) { - if (where_on_path(eff->ob, data->strength * guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0) - return 0; - } - else { - if (where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0) - return 0; - } - - mul_m4_v3(eff->ob->obmat, guidevec); - mul_mat3_m4_v3(eff->ob->obmat, guidedir); - - normalize_v3(guidedir); - - copy_v3_v3(vec_to_point, data->vec_to_point); - - if (guidetime != 0.0f) { - /* curve direction */ - cross_v3_v3v3(temp, eff->guide_dir, guidedir); - angle = dot_v3v3(eff->guide_dir, guidedir) / (len_v3(eff->guide_dir)); - angle = saacos(angle); - axis_angle_to_quat(rot2, temp, angle); - mul_qt_v3(rot2, vec_to_point); - - /* curve tilt */ - axis_angle_to_quat(rot2, guidedir, guidevec[3] - eff->guide_loc[3]); - mul_qt_v3(rot2, vec_to_point); - } - - /* curve taper */ - if (cu->taperobj) - mul_v3_fl(vec_to_point, BKE_displist_calc_taper(eff->scene, cu->taperobj, (int)(data->strength * guidetime * 100.0f), 100)); - - else { /* curve size*/ - if (cu->flag & CU_PATH_RADIUS) { - mul_v3_fl(vec_to_point, radius); - } + pd = eff->pd; + + if (pd->forcefield != PFIELD_GUIDE) + continue; + + data = eff->guide_data + index; + + if (data->strength <= 0.0f) + continue; + + guidetime = time / (1.0f - pd->free_end); + + if (guidetime > 1.0f) + continue; + + cu = (Curve *)eff->ob->data; + + if (pd->flag & PFIELD_GUIDE_PATH_ADD) { + if (where_on_path(eff->ob, data->strength * guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0) + return 0; + } + else { + if (where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0) + return 0; + } + + mul_m4_v3(eff->ob->obmat, guidevec); + mul_mat3_m4_v3(eff->ob->obmat, guidedir); + + normalize_v3(guidedir); + + copy_v3_v3(vec_to_point, data->vec_to_point); + + if (guidetime != 0.0f) { + /* curve direction */ + cross_v3_v3v3(temp, eff->guide_dir, guidedir); + angle = dot_v3v3(eff->guide_dir, guidedir) / (len_v3(eff->guide_dir)); + angle = saacos(angle); + axis_angle_to_quat(rot2, temp, angle); + mul_qt_v3(rot2, vec_to_point); + + /* curve tilt */ + axis_angle_to_quat(rot2, guidedir, guidevec[3] - eff->guide_loc[3]); + mul_qt_v3(rot2, vec_to_point); + } + + /* curve taper */ + if (cu->taperobj) + mul_v3_fl(vec_to_point, BKE_displist_calc_taper(eff->scene, cu->taperobj, (int)(data->strength * guidetime * 100.0f), 100)); + + else { /* curve size*/ + if (cu->flag & CU_PATH_RADIUS) { + mul_v3_fl(vec_to_point, radius); } - par.co[0] = par.co[1] = par.co[2] = 0.0f; + } + + if (clumpcurve) + curvemapping_changed_all(clumpcurve); + if (roughcurve) + curvemapping_changed_all(roughcurve); + + { + ParticleKey key; + float par_co[3] = {0.0f, 0.0f, 0.0f}; + float par_vel[3] = {0.0f, 0.0f, 0.0f}; + float par_rot[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + float orco_offset[3] = {0.0f, 0.0f, 0.0f}; + copy_v3_v3(key.co, vec_to_point); - do_kink(&key, &par, 0, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, 0.f, pd->kink, pd->kink_axis, 0, 0); - do_clump(&key, &par, guidetime, pd->clump_fac, pd->clump_pow, 1.0f); + do_kink(&key, par_co, par_vel, par_rot, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, 0.f, pd->kink, pd->kink_axis, 0, 0); + do_clump(&key, par_co, guidetime, orco_offset, pd->clump_fac, pd->clump_pow, 1.0f, + part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve); copy_v3_v3(vec_to_point, key.co); - - add_v3_v3(vec_to_point, guidevec); - - //sub_v3_v3v3(pa_loc, pa_loc, pa_zero); - madd_v3_v3fl(effect, vec_to_point, data->strength); - madd_v3_v3fl(veffect, guidedir, data->strength); - totstrength += data->strength; - - if (pd->flag & PFIELD_GUIDE_PATH_WEIGHT) - totstrength *= weight; } - + + add_v3_v3(vec_to_point, guidevec); + + //sub_v3_v3v3(pa_loc, pa_loc, pa_zero); + madd_v3_v3fl(effect, vec_to_point, data->strength); + madd_v3_v3fl(veffect, guidedir, data->strength); + totstrength += data->strength; + + if (pd->flag & PFIELD_GUIDE_PATH_WEIGHT) + totstrength *= weight; + } + if (totstrength != 0.0f) { if (totstrength > 1.0f) mul_v3_fl(effect, 1.0f / totstrength); @@ -2266,41 +1868,7 @@ int do_guides(ListBase *effectors, ParticleKey *state, int index, float time) } return 0; } -static void do_rough(float *loc, float mat[4][4], float t, float fac, float size, float thres, ParticleKey *state) -{ - float rough[3]; - float rco[3]; - - if (thres != 0.0f) { - if (fabsf((float)(-1.5f + loc[0] + loc[1] + loc[2])) < 1.5f * thres) { - return; - } - } - - copy_v3_v3(rco, loc); - mul_v3_fl(rco, t); - rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2, 0, 2); - rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2, 0, 2); - rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2, 0, 2); - madd_v3_v3fl(state->co, mat[0], fac * rough[0]); - madd_v3_v3fl(state->co, mat[1], fac * rough[1]); - madd_v3_v3fl(state->co, mat[2], fac * rough[2]); -} -static void do_rough_end(float *loc, float mat[4][4], float t, float fac, float shape, ParticleKey *state) -{ - float rough[2]; - float roughfac; - - roughfac = fac * (float)pow((double)t, shape); - copy_v2_v2(rough, loc); - rough[0] = -1.0f + 2.0f * rough[0]; - rough[1] = -1.0f + 2.0f * rough[1]; - mul_v2_fl(rough, roughfac); - - madd_v3_v3fl(state->co, mat[0], rough[0]); - madd_v3_v3fl(state->co, mat[1], rough[1]); -} static void do_path_effectors(ParticleSimulationData *sim, int i, ParticleCacheKey *ca, int k, int steps, float *UNUSED(rootco), float effector, float UNUSED(dfra), float UNUSED(cfra), float *length, float *vec) { float force[3] = {0.0f, 0.0f, 0.0f}; @@ -2332,20 +1900,6 @@ static void do_path_effectors(ParticleSimulationData *sim, int i, ParticleCacheK if (k < steps) *length = len_v3(vec); } -static int check_path_length(int k, ParticleCacheKey *keys, ParticleCacheKey *state, float max_length, float *cur_length, float length, float *dvec) -{ - if (*cur_length + length > max_length) { - mul_v3_fl(dvec, (max_length - *cur_length) / length); - add_v3_v3v3(state->co, (state - 1)->co, dvec); - keys->steps = k; - /* something over the maximum step value */ - return k = 100000; - } - else { - *cur_length += length; - return k; - } -} static void offset_child(ChildParticle *cpa, ParticleKey *par, float *par_rot, ParticleKey *child, float flat, float radius) { copy_v3_v3(child->co, cpa->fuv); @@ -2424,49 +1978,15 @@ void psys_find_parents(ParticleSimulationData *sim) BLI_kdtree_free(tree); } -static void get_strand_normal(Material *ma, const float surfnor[3], float surfdist, float nor[3]) +static bool psys_thread_context_init_path(ParticleThreadContext *ctx, ParticleSimulationData *sim, Scene *scene, float cfra, int editupdate) { - float cross[3], nstrand[3], vnor[3], blend; - - if (!((ma->mode & MA_STR_SURFDIFF) || (ma->strand_surfnor > 0.0f))) - return; - - if (ma->mode & MA_STR_SURFDIFF) { - cross_v3_v3v3(cross, surfnor, nor); - cross_v3_v3v3(nstrand, nor, cross); - - blend = dot_v3v3(nstrand, surfnor); - CLAMP(blend, 0.0f, 1.0f); - - interp_v3_v3v3(vnor, nstrand, surfnor, blend); - normalize_v3(vnor); - } - else { - copy_v3_v3(vnor, nor); - } - - if (ma->strand_surfnor > 0.0f) { - if (ma->strand_surfnor > surfdist) { - blend = (ma->strand_surfnor - surfdist) / ma->strand_surfnor; - interp_v3_v3v3(vnor, vnor, surfnor, blend); - normalize_v3(vnor); - } - } - - copy_v3_v3(nor, vnor); -} - -static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float cfra, int editupdate) -{ - ParticleThreadContext *ctx = threads[0].ctx; -/* Object *ob = ctx->sim.ob; */ - ParticleSystem *psys = ctx->sim.psys; + ParticleSystem *psys = sim->psys; ParticleSettings *part = psys->part; -/* ParticleEditSettings *pset = &scene->toolsettings->particle; */ int totparent = 0, between = 0; - int steps = (int)pow(2.0, (double)part->draw_step); + int segments = 1 << part->draw_step; int totchild = psys->totchild; - int i, seed, totthread = threads[0].tot; + + psys_thread_context_init(ctx, sim); /*---start figuring out what is actually wanted---*/ if (psys_in_edit_mode(scene, psys)) { @@ -2475,7 +1995,7 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c if (psys->renderdata == 0 && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0) totchild = 0; - steps = (int)pow(2.0, (double)pset->draw_step); + segments = 1 << pset->draw_step; } if (totchild && part->childtype == PART_CHILD_FACES) { @@ -2489,28 +2009,22 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c } if (psys->renderdata) - steps = (int)pow(2.0, (double)part->ren_step); + segments = 1 << part->ren_step; else { totchild = (int)((float)totchild * (float)part->disp / 100.0f); totparent = MIN2(totparent, totchild); } - if (totchild == 0) return 0; - - /* init random number generator */ - seed = 31415926 + ctx->sim.psys->seed; - - if (ctx->editupdate || totchild < 10000) - totthread = 1; - - for (i = 0; i < totthread; i++) { - threads[i].rng_path = BLI_rng_new(seed); - threads[i].tot = totthread; - } + if (totchild == 0) + return false; /* fill context values */ ctx->between = between; - ctx->steps = steps; + ctx->segments = segments; + if (ELEM(part->kink, PART_KINK_SPIRAL)) + ctx->extra_segments = max_ii(part->kink_extra_steps, 1); + else + ctx->extra_segments = 0; ctx->totchild = totchild; ctx->totparent = totparent; ctx->parent_pass = 0; @@ -2529,31 +2043,36 @@ static int psys_threads_init_path(ParticleThread *threads, Scene *scene, float c if (psys->part->flag & PART_CHILD_EFFECT) ctx->vg_effector = psys_cache_vgroup(ctx->dm, psys, PSYS_VG_EFFECTOR); - /* set correct ipo timing */ -#if 0 // XXX old animation system - if (part->flag & PART_ABS_TIME && part->ipo) { - calc_ipo(part->ipo, cfra); - execute_ipo((ID *)part, part->ipo); - } -#endif // XXX old animation system + /* prepare curvemapping tables */ + if ((part->child_flag & PART_CHILD_USE_CLUMP_CURVE) && part->clumpcurve) + curvemapping_changed_all(part->clumpcurve); + if ((part->child_flag & PART_CHILD_USE_ROUGH_CURVE) && part->roughcurve) + curvemapping_changed_all(part->roughcurve); - return 1; + return true; +} + +static void psys_task_init_path(ParticleTask *task, ParticleSimulationData *sim) +{ + /* init random number generator */ + int seed = 31415926 + sim->psys->seed; + + task->rng_path = BLI_rng_new(seed); } /* note: this function must be thread safe, except for branching! */ -static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) +static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i) { - ParticleThreadContext *ctx = thread->ctx; + ParticleThreadContext *ctx = task->ctx; Object *ob = ctx->sim.ob; ParticleSystem *psys = ctx->sim.psys; ParticleSettings *part = psys->part; ParticleCacheKey **cache = psys->childcache; - ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) ? psys->edit->pathcache : psys->pathcache; - ParticleCacheKey *child, *par = NULL, *key[4]; + ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache; + ParticleCacheKey *child, *key[4]; ParticleTexture ptex; float *cpa_fuv = 0, *par_rot = 0, rot[4]; - float orco[3], ornor[3], hairmat[4][4], t, dvec[3], off1[4][3], off2[4][3]; - float length, max_length = 1.0f, cur_length = 0.0f; + float orco[3], ornor[3], hairmat[4][4], dvec[3], off1[4][3], off2[4][3]; float eff_length, eff_vec[3], weight[4]; int k, cpa_num; short cpa_from; @@ -2585,7 +2104,7 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle if (!needupdate) return; else - memset(child_keys, 0, sizeof(*child_keys) * (ctx->steps + 1)); + memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1)); } /* get parent paths */ @@ -2608,14 +2127,14 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle if (part->flag & PART_CHILD_LONG_HAIR) { /* For long hair use tip distance/root distance as parting factor instead of root to tip angle. */ float d1 = len_v3v3(key[0]->co, key[w]->co); - float d2 = len_v3v3((key[0] + key[0]->steps - 1)->co, (key[w] + key[w]->steps - 1)->co); + float d2 = len_v3v3((key[0] + key[0]->segments - 1)->co, (key[w] + key[w]->segments - 1)->co); d = d1 > 0.f ? d2 / d1 - 1.f : 10000.f; } else { float v1[3], v2[3]; - sub_v3_v3v3(v1, (key[0] + key[0]->steps - 1)->co, key[0]->co); - sub_v3_v3v3(v2, (key[w] + key[w]->steps - 1)->co, key[w]->co); + sub_v3_v3v3(v1, (key[0] + key[0]->segments - 1)->co, key[0]->co); + sub_v3_v3v3(v2, (key[w] + key[w]->segments - 1)->co, key[w]->co); normalize_v3(v1); normalize_v3(v2); @@ -2663,7 +2182,7 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle if (!(psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC)) return; - memset(child_keys, 0, sizeof(*child_keys) * (ctx->steps + 1)); + memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1)); } /* get the parent path */ @@ -2682,18 +2201,18 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat); } - child_keys->steps = ctx->steps; + child_keys->segments = ctx->segments; /* get different child parameters from textures & vgroups */ get_child_modifier_parameters(part, ctx, cpa, cpa_from, cpa_num, cpa_fuv, orco, &ptex); if (ptex.exist < psys_frand(psys, i + 24)) { - child_keys->steps = -1; + child_keys->segments = -1; return; } /* create the child path */ - for (k = 0, child = child_keys; k <= ctx->steps; k++, child++) { + for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) { if (ctx->between) { int w = 0; @@ -2711,7 +2230,7 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle /* Fade the effect of rotation for even lengths in the end */ project_v3_v3v3(dvec, off2[w], (key[w] + k)->vel); - madd_v3_v3fl(off2[w], dvec, -(float)k / (float)ctx->steps); + madd_v3_v3fl(off2[w], dvec, -(float)k / (float)ctx->segments); } add_v3_v3(off2[w], (key[w] + k)->co); @@ -2735,14 +2254,14 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle offset_child(cpa, (ParticleKey *)(key[0] + k), par_rot, (ParticleKey *)child, part->childflat, part->childrad); } - child->time = (float)k / (float)ctx->steps; + child->time = (float)k / (float)ctx->segments; } /* apply effectors */ if (part->flag & PART_CHILD_EFFECT) { - for (k = 0, child = child_keys; k <= ctx->steps; k++, child++) { + for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) { if (k) { - do_path_effectors(&ctx->sim, cpa->pa[0], child, k, ctx->steps, child_keys->co, ptex.effector, 0.0f, ctx->cfra, &eff_length, eff_vec); + do_path_effectors(&ctx->sim, cpa->pa[0], child, k, ctx->segments, child_keys->co, ptex.effector, 0.0f, ctx->cfra, &eff_length, eff_vec); } else { sub_v3_v3v3(eff_vec, (child + 1)->co, child->co); @@ -2751,147 +2270,119 @@ static void psys_thread_create_path(ParticleThread *thread, struct ChildParticle } } - for (k = 0, child = child_keys; k <= ctx->steps; k++, child++) { - t = (float)k / (float)ctx->steps; - - if (ctx->totparent) - /* this is now threadsafe, virtual parents are calculated before rest of children */ - par = (i >= ctx->totparent) ? cache[cpa->parent] : NULL; - else if (cpa->parent >= 0) - par = pcache[cpa->parent]; - - if (par) { - if (k) { - mul_qt_qtqt(rot, (par + k)->rot, par->rot); - par_rot = rot; - } - else { - par_rot = par->rot; + { + ParticleData *pa = NULL; + ParticleCacheKey *par = NULL; + float par_co[3]; + float par_orco[3]; + + if (ctx->totparent) { + if (i >= ctx->totparent) { + pa = &psys->particles[cpa->parent]; + /* this is now threadsafe, virtual parents are calculated before rest of children */ + par = cache[cpa->parent]; } - par += k; } - - /* apply different deformations to the child path */ - do_child_modifiers(&ctx->sim, &ptex, (ParticleKey *)par, par_rot, cpa, orco, hairmat, (ParticleKey *)child, t); - - /* we have to correct velocity because of kink & clump */ - if (k > 1) { - sub_v3_v3v3((child - 1)->vel, child->co, (child - 2)->co); - mul_v3_fl((child - 1)->vel, 0.5); - - if (ctx->ma && (part->draw_col == PART_DRAW_COL_MAT)) - get_strand_normal(ctx->ma, ornor, cur_length, (child - 1)->vel); - } - - if (k == ctx->steps) - sub_v3_v3v3(child->vel, child->co, (child - 1)->co); - - /* check if path needs to be cut before actual end of data points */ - if (k) { - sub_v3_v3v3(dvec, child->co, (child - 1)->co); - length = 1.0f / (float)ctx->steps; - k = check_path_length(k, child_keys, child, max_length, &cur_length, length, dvec); - } - else { - /* initialize length calculation */ - max_length = ptex.length; - cur_length = 0.0f; + else if (cpa->parent >= 0) { + pa = &psys->particles[cpa->parent]; + par = pcache[cpa->parent]; } - - if (ctx->ma && (part->draw_col == PART_DRAW_COL_MAT)) { - copy_v3_v3(child->col, &ctx->ma->r); - get_strand_normal(ctx->ma, ornor, cur_length, child->vel); + + if (pa) { + ListBase modifiers; + BLI_listbase_clear(&modifiers); + + psys_particle_on_emitter(ctx->sim.psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, + par_co, NULL, NULL, NULL, par_orco, NULL); + + psys_apply_child_modifiers(ctx, &modifiers, cpa, &ptex, orco, ornor, hairmat, child_keys, par, par_orco); } + else + zero_v3(par_orco); } /* Hide virtual parents */ if (i < ctx->totparent) - child_keys->steps = -1; + child_keys->segments = -1; } -static void *exec_child_path_cache(void *data) +static void exec_child_path_cache(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) { - ParticleThread *thread = (ParticleThread *)data; - ParticleThreadContext *ctx = thread->ctx; + ParticleTask *task = taskdata; + ParticleThreadContext *ctx = task->ctx; ParticleSystem *psys = ctx->sim.psys; ParticleCacheKey **cache = psys->childcache; ChildParticle *cpa; - int i, totchild = ctx->totchild, first = 0; + int i; - if (thread->tot > 1) { - first = ctx->parent_pass ? 0 : ctx->totparent; - totchild = ctx->parent_pass ? ctx->totparent : ctx->totchild; + cpa = psys->child + task->begin; + for (i = task->begin; i < task->end; ++i, ++cpa) { + psys_thread_create_path(task, cpa, cache[i], i); } - - cpa = psys->child + first + thread->num; - for (i = first + thread->num; i < totchild; i += thread->tot, cpa += thread->tot) - psys_thread_create_path(thread, cpa, cache[i], i); - - return 0; } void psys_cache_child_paths(ParticleSimulationData *sim, float cfra, int editupdate) { - ParticleThread *pthreads; - ParticleThreadContext *ctx; - ListBase threads; - int i, totchild, totparent, totthread; - + TaskScheduler *task_scheduler; + TaskPool *task_pool; + ParticleThreadContext ctx; + ParticleTask *tasks_parent, *tasks_child; + int numtasks_parent, numtasks_child; + int i, totchild, totparent; + if (sim->psys->flag & PSYS_GLOBAL_HAIR) return; - - pthreads = psys_threads_create(sim); - - if (!psys_threads_init_path(pthreads, sim->scene, cfra, editupdate)) { - psys_threads_free(pthreads); + + /* create a task pool for child path tasks */ + if (!psys_thread_context_init_path(&ctx, sim, sim->scene, cfra, editupdate)) return; - } - - ctx = pthreads[0].ctx; - totchild = ctx->totchild; - totparent = ctx->totparent; - + + task_scheduler = BLI_task_scheduler_get(); + task_pool = BLI_task_pool_create(task_scheduler, &ctx); + totchild = ctx.totchild; + totparent = ctx.totparent; + if (editupdate && sim->psys->childcache && totchild == sim->psys->totchildcache) { ; /* just overwrite the existing cache */ } else { /* clear out old and create new empty path cache */ free_child_path_cache(sim->psys); - sim->psys->childcache = psys_alloc_path_cache_buffers(&sim->psys->childcachebufs, totchild, ctx->steps + 1); + + sim->psys->childcache = psys_alloc_path_cache_buffers(&sim->psys->childcachebufs, totchild, ctx.segments + ctx.extra_segments + 1); sim->psys->totchildcache = totchild; } - - totthread = pthreads[0].tot; - - if (totthread > 1) { - - /* make virtual child parents thread safe by calculating them first */ - if (totparent) { - BLI_init_threads(&threads, exec_child_path_cache, totthread); - - for (i = 0; i < totthread; i++) { - pthreads[i].ctx->parent_pass = 1; - BLI_insert_thread(&threads, &pthreads[i]); - } - - BLI_end_threads(&threads); - - for (i = 0; i < totthread; i++) - pthreads[i].ctx->parent_pass = 0; - } - - BLI_init_threads(&threads, exec_child_path_cache, totthread); - - for (i = 0; i < totthread; i++) - BLI_insert_thread(&threads, &pthreads[i]); - - BLI_end_threads(&threads); + + /* cache parent paths */ + ctx.parent_pass = 1; + psys_tasks_create(&ctx, totparent, &tasks_parent, &numtasks_parent); + for (i = 0; i < numtasks_parent; ++i) { + ParticleTask *task = &tasks_parent[i]; + + psys_task_init_path(task, sim); + BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW); } - else - exec_child_path_cache(&pthreads[0]); + BLI_task_pool_work_and_wait(task_pool); + + /* cache child paths */ + ctx.parent_pass = 0; + psys_tasks_create(&ctx, totchild, &tasks_child, &numtasks_child); + for (i = 0; i < numtasks_child; ++i) { + ParticleTask *task = &tasks_child[i]; + + psys_task_init_path(task, sim); + BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW); + } + BLI_task_pool_work_and_wait(task_pool); - psys_threads_free(pthreads); + BLI_task_pool_free(task_pool); + + psys_tasks_free(tasks_parent, numtasks_parent); + psys_tasks_free(tasks_child, numtasks_child); + + psys_thread_context_free(&ctx); } + /* figure out incremental rotations along path starting from unit quat */ static void cache_key_incremental_rotation(ParticleCacheKey *key0, ParticleCacheKey *key1, ParticleCacheKey *key2, float *prev_tangent, int i) { @@ -2959,7 +2450,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) float prev_tangent[3] = {0.0f, 0.0f, 0.0f}, hairmat[4][4]; float rotmat[3][3]; int k; - int steps = (int)pow(2.0, (double)(psys->renderdata ? part->ren_step : part->draw_step)); + int segments = (int)pow(2.0, (double)(psys->renderdata ? part->ren_step : part->draw_step)); int totpart = psys->totpart; float length, vec[3]; float *vg_effector = NULL; @@ -2979,7 +2470,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) /* clear out old and create new empty path cache */ psys_free_path_cache(psys, psys->edit); - cache = psys->pathcache = psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, steps + 1); + cache = psys->pathcache = psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, segments + 1); psys->lattice_deform_data = psys_create_lattice_deform_data(sim); ma = give_current_material(sim->ob, psys->part->omat); @@ -3014,9 +2505,9 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) pind.bspline = (psys->part->flag & PART_HAIR_BSPLINE); pind.dm = hair_dm; - memset(cache[p], 0, sizeof(*cache[p]) * (steps + 1)); + memset(cache[p], 0, sizeof(*cache[p]) * (segments + 1)); - cache[p]->steps = steps; + cache[p]->segments = segments; /*--get the first data points--*/ init_particle_interpolation(sim->ob, sim->psys, pa, &pind); @@ -3038,15 +2529,15 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) } if (birthtime >= dietime) { - cache[p]->steps = -1; + cache[p]->segments = -1; continue; } dietime = birthtime + pa_length * (dietime - birthtime); /*--interpolate actual path from data points--*/ - for (k = 0, ca = cache[p]; k <= steps; k++, ca++) { - time = (float)k / (float)steps; + for (k = 0, ca = cache[p]; k <= segments; k++, ca++) { + time = (float)k / (float)segments; t = birthtime + time * (dietime - birthtime); result.time = -t; do_particle_interpolation(psys, p, pa, t, &pind, &result); @@ -3061,7 +2552,15 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) copy_v3_v3(ca->col, col); } - + + if (part->type == PART_HAIR) { + HairKey *hkey; + + for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) { + mul_v3_m4v3(hkey->world_co, hairmat, hkey->co); + } + } + /*--modify paths and calculate rotation & velocity--*/ if (!(psys->flag & PSYS_GLOBAL_HAIR)) { @@ -3074,29 +2573,29 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) sub_v3_v3v3(vec, (cache[p] + 1)->co, cache[p]->co); length = len_v3(vec); - for (k = 1, ca = cache[p] + 1; k <= steps; k++, ca++) - do_path_effectors(sim, p, ca, k, steps, cache[p]->co, effector, dfra, cfra, &length, vec); + for (k = 1, ca = cache[p] + 1; k <= segments; k++, ca++) + do_path_effectors(sim, p, ca, k, segments, cache[p]->co, effector, dfra, cfra, &length, vec); } /* apply guide curves to path data */ if (sim->psys->effectors && (psys->part->flag & PART_CHILD_EFFECT) == 0) { - for (k = 0, ca = cache[p]; k <= steps; k++, ca++) + for (k = 0, ca = cache[p]; k <= segments; k++, ca++) /* ca is safe to cast, since only co and vel are used */ - do_guides(sim->psys->effectors, (ParticleKey *)ca, p, (float)k / (float)steps); + do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)ca, p, (float)k / (float)segments); } /* lattices have to be calculated separately to avoid mixups between effector calculations */ if (psys->lattice_deform_data) { - for (k = 0, ca = cache[p]; k <= steps; k++, ca++) + for (k = 0, ca = cache[p]; k <= segments; k++, ca++) calc_latt_deform(psys->lattice_deform_data, ca->co, 1.0f); } } /* finally do rotation & velocity */ - for (k = 1, ca = cache[p] + 1; k <= steps; k++, ca++) { + for (k = 1, ca = cache[p] + 1; k <= segments; k++, ca++) { cache_key_incremental_rotation(ca, ca - 1, ca - 2, prev_tangent, k); - if (k == steps) + if (k == segments) copy_qt_qt(ca->rot, (ca - 1)->rot); /* set velocity */ @@ -3105,7 +2604,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra) if (k == 1) copy_v3_v3((ca - 1)->vel, ca->vel); - ca->time = (float)k / (float)steps; + ca->time = (float)k / (float)segments; } /* First rotation is based on emitting face orientation. * This is way better than having flipping rotations resulting @@ -3149,17 +2648,17 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf float t, time = 0.0f, keytime = 0.0f /*, frs_sec */; float hairmat[4][4], rotmat[3][3], prev_tangent[3] = {0.0f, 0.0f, 0.0f}; int k, i; - int steps = (int)pow(2.0, (double)pset->draw_step); + int segments = 1 << pset->draw_step; int totpart = edit->totpoint, recalc_set = 0; float sel_col[3]; float nosel_col[3]; - steps = MAX2(steps, 4); + segments = MAX2(segments, 4); if (!cache || edit->totpoint != edit->totcached) { /* clear out old and create new empty path cache */ psys_free_path_cache(edit->psys, edit); - cache = edit->pathcache = psys_alloc_path_cache_buffers(&edit->pathcachebufs, totpart, steps + 1); + cache = edit->pathcache = psys_alloc_path_cache_buffers(&edit->pathcachebufs, totpart, segments + 1); /* set flag for update (child particles check this too) */ for (i = 0, point = edit->points; i < totpart; i++, point++) @@ -3206,9 +2705,9 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf } - memset(cache[i], 0, sizeof(*cache[i]) * (steps + 1)); + memset(cache[i], 0, sizeof(*cache[i]) * (segments + 1)); - cache[i]->steps = steps; + cache[i]->segments = segments; /*--get the first data points--*/ init_particle_interpolation(ob, psys, pa, &pind); @@ -3224,13 +2723,13 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf dietime = pind.dietime; if (birthtime >= dietime) { - cache[i]->steps = -1; + cache[i]->segments = -1; continue; } /*--interpolate actual path from data points--*/ - for (k = 0, ca = cache[i]; k <= steps; k++, ca++) { - time = (float)k / (float)steps; + for (k = 0, ca = cache[i]; k <= segments; k++, ca++) { + time = (float)k / (float)segments; t = birthtime + time * (dietime - birthtime); result.time = -t; do_particle_interpolation(psys, i, pa, t, &pind, &result); @@ -3243,7 +2742,7 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf if (k) { cache_key_incremental_rotation(ca, ca - 1, ca - 2, prev_tangent, k); - if (k == steps) + if (k == segments) copy_qt_qt(ca->rot, (ca - 1)->rot); /* set velocity */ @@ -3277,7 +2776,7 @@ void psys_cache_edit_paths(Scene *scene, Object *ob, PTCacheEdit *edit, float cf /* at the moment this is only used for weight painting. * will need to move out of this check if its used elsewhere. */ - t2 = birthtime + ((float)k / (float)steps) * (dietime - birthtime); + t2 = birthtime + ((float)k / (float)segments) * (dietime - birthtime); while (pind.hkey[1]->time < t2) pind.hkey[1]++; pind.hkey[0] = pind.hkey[1] - 1; @@ -3581,6 +3080,7 @@ void object_remove_particle_system(Scene *UNUSED(scene), Object *ob) DAG_relations_tag_update(G.main); DAG_id_tag_update(&ob->id, OB_RECALC_DATA); } + static void default_particle_settings(ParticleSettings *part) { part->type = PART_EMITTER; @@ -3611,6 +3111,8 @@ static void default_particle_settings(ParticleSettings *part) part->adapt_pix = 3; part->kink_axis = 2; part->kink_amp_clump = 1.f; + part->kink_extra_steps = 4; + part->clump_noise_size = 1.0f; part->reactevent = PART_EVENT_DEATH; part->disp = 100; part->from = PART_FROM_FACE; @@ -3678,6 +3180,30 @@ ParticleSettings *psys_new_settings(const char *name, Main *main) return part; } +void BKE_particlesettings_clump_curve_init(ParticleSettings *part) +{ + CurveMapping *cumap = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + cumap->cm[0].curve[0].x = 0.0f; + cumap->cm[0].curve[0].y = 1.0f; + cumap->cm[0].curve[1].x = 1.0f; + cumap->cm[0].curve[1].y = 1.0f; + + part->clumpcurve = cumap; +} + +void BKE_particlesettings_rough_curve_init(ParticleSettings *part) +{ + CurveMapping *cumap = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + cumap->cm[0].curve[0].x = 0.0f; + cumap->cm[0].curve[0].y = 1.0f; + cumap->cm[0].curve[1].x = 1.0f; + cumap->cm[0].curve[1].y = 1.0f; + + part->roughcurve = cumap; +} + ParticleSettings *BKE_particlesettings_copy(ParticleSettings *part) { ParticleSettings *partn; @@ -3689,6 +3215,11 @@ ParticleSettings *BKE_particlesettings_copy(ParticleSettings *part) partn->effector_weights = MEM_dupallocN(part->effector_weights); partn->fluid = MEM_dupallocN(part->fluid); + if (part->clumpcurve) + partn->clumpcurve = curvemapping_copy(part->clumpcurve); + if (part->roughcurve) + partn->roughcurve = curvemapping_copy(part->roughcurve); + partn->boids = boid_copy_settings(part->boids); for (a = 0; a < MAX_MTEX; a++) { @@ -3840,7 +3371,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti float value, rgba[4], texvec[3]; ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp = - ptex->gravity = ptex->field = ptex->time = ptex->clump = ptex->kink = + ptex->gravity = ptex->field = ptex->time = ptex->clump = ptex->kink_freq = ptex->kink_amp = ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f; ptex->length = 1.0f - part->randlength * psys_frand(psys, child_index + 26); @@ -3880,21 +3411,23 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti break; } - externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL); + externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false); if ((event & mtex->mapto) & PAMAP_ROUGH) ptex->rough1 = ptex->rough2 = ptex->roughe = texture_value_blend(def, ptex->rough1, value, mtex->roughfac, blend); SET_PARTICLE_TEXTURE(PAMAP_LENGTH, ptex->length, mtex->lengthfac); SET_PARTICLE_TEXTURE(PAMAP_CLUMP, ptex->clump, mtex->clumpfac); - SET_PARTICLE_TEXTURE(PAMAP_KINK, ptex->kink, mtex->kinkfac); + SET_PARTICLE_TEXTURE(PAMAP_KINK_AMP, ptex->kink_amp, mtex->kinkampfac); + SET_PARTICLE_TEXTURE(PAMAP_KINK_FREQ, ptex->kink_freq, mtex->kinkfac); SET_PARTICLE_TEXTURE(PAMAP_DENS, ptex->exist, mtex->padensfac); } } CLAMP_PARTICLE_TEXTURE_POS(PAMAP_LENGTH, ptex->length); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_CLUMP, ptex->clump); - CLAMP_PARTICLE_TEXTURE_POS(PAMAP_KINK, ptex->kink); + CLAMP_PARTICLE_TEXTURE_POS(PAMAP_KINK_AMP, ptex->kink_amp); + CLAMP_PARTICLE_TEXTURE_POS(PAMAP_KINK_FREQ, ptex->kink_freq); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_ROUGH, ptex->rough1); CLAMP_PARTICLE_TEXTURE_POS(PAMAP_DENS, ptex->exist); } @@ -3911,7 +3444,7 @@ void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTex /* initialize ptex */ ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp = - ptex->gravity = ptex->field = ptex->length = ptex->clump = ptex->kink = + ptex->gravity = ptex->field = ptex->length = ptex->clump = ptex->kink_freq = ptex->kink_amp = ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f; ptex->time = (float)(pa - sim->psys->particles) / (float)sim->psys->totpart; @@ -3961,7 +3494,7 @@ void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTex break; } - externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL); + externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false); if ((event & mtex->mapto) & PAMAP_TIME) { /* the first time has to set the base value for time regardless of blend mode */ @@ -4065,7 +3598,7 @@ static void get_child_modifier_parameters(ParticleSettings *part, ParticleThread if (ctx->vg_clump) ptex->clump *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_clump); if (ctx->vg_kink) - ptex->kink *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_kink); + ptex->kink_freq *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_kink); if (ctx->vg_rough1) ptex->rough1 *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_rough1); if (ctx->vg_rough2) @@ -4075,55 +3608,6 @@ static void get_child_modifier_parameters(ParticleSettings *part, ParticleThread if (ctx->vg_effector) ptex->effector *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_effector); } -static void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, ParticleKey *par, float *par_rot, ChildParticle *cpa, float *orco, float mat[4][4], ParticleKey *state, float t) -{ - ParticleSettings *part = sim->psys->part; - int i = cpa - sim->psys->child; - int guided = 0; - - float kink_freq = part->kink_freq; - float rough1 = part->rough1; - float rough2 = part->rough2; - float rough_end = part->rough_end; - - if (ptex) { - kink_freq *= ptex->kink; - rough1 *= ptex->rough1; - rough2 *= ptex->rough2; - rough_end *= ptex->roughe; - } - - if (part->flag & PART_CHILD_EFFECT) - /* state is safe to cast, since only co and vel are used */ - guided = do_guides(sim->psys->effectors, (ParticleKey *)state, cpa->parent, t); - - if (guided == 0) { - float clump = do_clump(state, par, t, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f); - - if (kink_freq != 0.f) { - float kink_amp = part->kink_amp * (1.f - part->kink_amp_clump * clump); - - do_kink(state, par, par_rot, t, kink_freq, part->kink_shape, - kink_amp, part->kink_flat, part->kink, part->kink_axis, - sim->ob->obmat, sim->psys->part->childtype == PART_CHILD_FACES); - } - } - - if (rough1 > 0.f) - do_rough(orco, mat, t, rough1, part->rough1_size, 0.0, state); - - if (rough2 > 0.f) { - float vec[3]; - psys_frand_vec(sim->psys, i + 27, vec); - do_rough(vec, mat, t, rough2, part->rough2_size, part->rough2_thres, state); - } - - if (rough_end > 0.f) { - float vec[3]; - psys_frand_vec(sim->psys, i + 27, vec); - do_rough_end(vec, mat, t, rough_end, part->rough_end_shape, state); - } -} /* get's hair (or keyed) particles state at the "path time" specified in state->time */ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *state, const bool vel) { @@ -4190,7 +3674,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey * mul_mat3_m4_v3(hairmat, state->vel); if (sim->psys->effectors && (part->flag & PART_CHILD_GUIDE) == 0) { - do_guides(sim->psys->effectors, state, p, state->time); + do_guides(sim->psys->part, sim->psys->effectors, state, p, state->time); /* TODO: proper velocity handling */ } @@ -4212,6 +3696,8 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey * copy_qt_qt(state->rot, result.rot); } else { + float par_co[3], par_orco[3]; + cpa = psys->child + p - totpart; if (state->time < 0.0f) @@ -4248,6 +3734,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey * pa = psys->particles + cpa->parent; + psys_particle_on_emitter(psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, par_co, 0, 0, 0, par_orco, 0); if (part->type == PART_HAIR) psys_mat_hair_to_global(sim->ob, sim->psmd->dm, psys->part->from, pa, hairmat); else @@ -4267,8 +3754,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey * cpa_num = pa->num; cpa_fuv = pa->fuv; - - + psys_particle_on_emitter(psmd, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, par_co, 0, 0, 0, par_orco, 0); if (part->type == PART_HAIR) { psys_particle_on_emitter(psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa_fuv, pa->foffset, co, 0, 0, 0, orco, 0); psys_mat_hair_to_global(sim->ob, sim->psmd->dm, psys->part->from, pa, hairmat); @@ -4326,7 +3812,7 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey * copy_particle_key(&tstate, state, 1); /* apply different deformations to the child path */ - do_child_modifiers(sim, &ptex, par, par->rot, cpa, orco, hairmat, state, t); + do_child_modifiers(sim, &ptex, par->co, par->vel, par->rot, par_orco, cpa, orco, hairmat, state, t); /* try to estimate correct velocity */ if (vel) { @@ -4421,6 +3907,7 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta float mat[4][4]; ParticleKey *key1; float t = (cfra - pa->time) / pa->lifetime; + float par_orco[3] = {0.0f, 0.0f, 0.0f}; key1 = &pa->state; offset_child(cpa, key1, key1->rot, state, part->childflat, part->childrad); @@ -4428,7 +3915,7 @@ int psys_get_particle_state(ParticleSimulationData *sim, int p, ParticleKey *sta CLAMP(t, 0.0f, 1.0f); unit_m4(mat); - do_child_modifiers(sim, NULL, key1, key1->rot, cpa, cpa->fuv, mat, state, t); + do_child_modifiers(sim, NULL, key1->co, key1->vel, key1->rot, par_orco, cpa, cpa->fuv, mat, state, t); if (psys->lattice_deform_data) calc_latt_deform(psys->lattice_deform_data, state->co, 1.0f); @@ -4556,7 +4043,7 @@ void psys_get_dupli_path_transform(ParticleSimulationData *sim, ParticleData *pa float loc[3], nor[3], vec[3], side[3], len; float xvec[3] = {-1.0, 0.0, 0.0}, nmat[3][3]; - sub_v3_v3v3(vec, (cache + cache->steps)->co, cache->co); + sub_v3_v3v3(vec, (cache + cache->segments)->co, cache->co); len = normalize_v3(vec); if (pa == NULL && psys->part->childflat != PART_CHILD_FACES) @@ -4573,8 +4060,8 @@ void psys_get_dupli_path_transform(ParticleSimulationData *sim, ParticleData *pa normalize_v3(nor); /* make sure that we get a proper side vector */ - if (fabsf(dot_v3v3(nor, vec)) > 0.999999) { - if (fabsf(dot_v3v3(nor, xvec)) > 0.999999) { + if (fabsf(dot_v3v3(nor, vec)) > 0.999999f) { + if (fabsf(dot_v3v3(nor, xvec)) > 0.999999f) { nor[0] = 0.0f; nor[1] = 1.0f; nor[2] = 0.0f; diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c new file mode 100644 index 00000000000..33541c3a7fd --- /dev/null +++ b/source/blender/blenkernel/intern/particle_child.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. + * + * The Original Code is Copyright (C) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/particle_child.c + * \ingroup bke + */ + +#include "BLI_math.h" +#include "BLI_noise.h" + +#include "DNA_material_types.h" + +#include "BKE_colortools.h" +#include "BKE_particle.h" + +struct Material; + +void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat, + short type, short axis, float obmat[4][4], int smooth_start); +float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump, + bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve); +void do_child_modifiers(ParticleSimulationData *sim, + ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3], + ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t); + +static void get_strand_normal(Material *ma, const float surfnor[3], float surfdist, float nor[3]) +{ + float cross[3], nstrand[3], vnor[3], blend; + + if (!((ma->mode & MA_STR_SURFDIFF) || (ma->strand_surfnor > 0.0f))) + return; + + if (ma->mode & MA_STR_SURFDIFF) { + cross_v3_v3v3(cross, surfnor, nor); + cross_v3_v3v3(nstrand, nor, cross); + + blend = dot_v3v3(nstrand, surfnor); + CLAMP(blend, 0.0f, 1.0f); + + interp_v3_v3v3(vnor, nstrand, surfnor, blend); + normalize_v3(vnor); + } + else { + copy_v3_v3(vnor, nor); + } + + if (ma->strand_surfnor > 0.0f) { + if (ma->strand_surfnor > surfdist) { + blend = (ma->strand_surfnor - surfdist) / ma->strand_surfnor; + interp_v3_v3v3(vnor, vnor, surfnor, blend); + normalize_v3(vnor); + } + } + + copy_v3_v3(nor, vnor); +} + +/* ------------------------------------------------------------------------- */ + +typedef struct ParticlePathIterator { + ParticleCacheKey *key; + int index; + float time; + + ParticleCacheKey *parent_key; + float parent_rotation[4]; +} ParticlePathIterator; + +static void psys_path_iter_get(ParticlePathIterator *iter, ParticleCacheKey *keys, int totkeys, + ParticleCacheKey *parent, int index) +{ + BLI_assert(index >= 0 && index < totkeys); + + iter->key = keys + index; + iter->index = index; + iter->time = (float)index / (float)(totkeys - 1); + + if (parent) { + iter->parent_key = parent + index; + if (index > 0) + mul_qt_qtqt(iter->parent_rotation, iter->parent_key->rot, parent->rot); + else + copy_qt_qt(iter->parent_rotation, parent->rot); + } + else { + iter->parent_key = NULL; + unit_qt(iter->parent_rotation); + } +} + +typedef struct ParticlePathModifier { + struct ParticlePathModifier *next, *prev; + + void (*apply)(ParticleCacheKey *keys, int totkeys, ParticleCacheKey *parent_keys); +} ParticlePathModifier; + +/* ------------------------------------------------------------------------- */ + +static void do_kink_spiral_deform(ParticleKey *state, const float dir[3], const float kink[3], + float time, float freq, float shape, float amplitude, + const float spiral_start[3]) +{ + float result[3]; + + CLAMP(time, 0.f, 1.f); + + copy_v3_v3(result, state->co); + + { + /* Creates a logarithmic spiral: + * r(theta) = a * exp(b * theta) + * + * The "density" parameter b is defined by the shape parameter + * and goes up to the Golden Spiral for 1.0 + * http://en.wikipedia.org/wiki/Golden_spiral + */ + const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI * 0.25f; + /* angle of the spiral against the curve (rotated opposite to make a smooth transition) */ + const float start_angle = ((b != 0.0f) ? atanf(1.0f / b) : + (float)-M_PI_2) + (b > 0.0f ? -(float)M_PI_2 : (float)M_PI_2); + + float spiral_axis[3], rot[3][3]; + float vec[3]; + + float theta = freq * time * 2.0f * (float)M_PI; + float radius = amplitude * expf(b * theta); + + /* a bit more intuitive than using negative frequency for this */ + if (amplitude < 0.0f) + theta = -theta; + + cross_v3_v3v3(spiral_axis, dir, kink); + normalize_v3(spiral_axis); + + mul_v3_v3fl(vec, kink, -radius); + + axis_angle_normalized_to_mat3(rot, spiral_axis, theta); + mul_m3_v3(rot, vec); + + madd_v3_v3fl(vec, kink, amplitude); + + axis_angle_normalized_to_mat3(rot, spiral_axis, -start_angle); + mul_m3_v3(rot, vec); + + add_v3_v3v3(result, spiral_start, vec); + } + + copy_v3_v3(state->co, result); +} + +static void do_kink_spiral(ParticleThreadContext *ctx, ParticleTexture *ptex, const float parent_orco[3], + ChildParticle *cpa, const float orco[3], float hairmat[4][4], + ParticleCacheKey *keys, ParticleCacheKey *parent_keys, int *r_totkeys, float *r_max_length) +{ + struct ParticleSettings *part = ctx->sim.psys->part; + const int seed = ctx->sim.psys->child_seed + (int)(cpa - ctx->sim.psys->child); + const int totkeys = ctx->segments + 1; + const int extrakeys = ctx->extra_segments; + + float kink_amp_random = part->kink_amp_random; + float kink_amp = part->kink_amp * (1.0f - kink_amp_random * psys_frand(ctx->sim.psys, 93541 + seed)); + float kink_freq = part->kink_freq; + float kink_shape = part->kink_shape; + float kink_axis_random = part->kink_axis_random; + float rough1 = part->rough1; + float rough2 = part->rough2; + float rough_end = part->rough_end; + + ParticlePathIterator iter; + ParticleCacheKey *key; + int k; + + float dir[3]; + float spiral_start[3] = {0.0f, 0.0f, 0.0f}; + float spiral_start_time = 0.0f; + float spiral_par_co[3] = {0.0f, 0.0f, 0.0f}; + float spiral_par_vel[3] = {0.0f, 0.0f, 0.0f}; + float spiral_par_rot[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + float totlen; + float cut_time; + int start_index = 0, end_index = 0; + float kink_base[3]; + + if (ptex) { + kink_amp *= ptex->kink_amp; + kink_freq *= ptex->kink_freq; + rough1 *= ptex->rough1; + rough2 *= ptex->rough2; + rough_end *= ptex->roughe; + } + + cut_time = (totkeys - 1) * ptex->length; + zero_v3(spiral_start); + + for (k = 0, key = keys; k < totkeys-1; k++, key++) { + if ((float)(k + 1) >= cut_time) { + float fac = cut_time - (float)k; + ParticleCacheKey *par = parent_keys + k; + + start_index = k + 1; + end_index = start_index + extrakeys; + + spiral_start_time = ((float)k + fac) / (float)(totkeys - 1); + interp_v3_v3v3(spiral_start, key->co, (key+1)->co, fac); + + interp_v3_v3v3(spiral_par_co, par->co, (par+1)->co, fac); + interp_v3_v3v3(spiral_par_vel, par->vel, (par+1)->vel, fac); + interp_qt_qtqt(spiral_par_rot, par->rot, (par+1)->rot, fac); + + break; + } + } + + zero_v3(dir); + + zero_v3(kink_base); + kink_base[part->kink_axis] = 1.0f; + mul_mat3_m4_v3(ctx->sim.ob->obmat, kink_base); + + for (k = 0, key = keys; k < end_index; k++, key++) { + float par_time; + float *par_co, *par_vel, *par_rot; + + psys_path_iter_get(&iter, keys, end_index, NULL, k); + if (k < start_index) { + sub_v3_v3v3(dir, (key+1)->co, key->co); + normalize_v3(dir); + + par_time = (float)k / (float)(totkeys - 1); + par_co = parent_keys[k].co; + par_vel = parent_keys[k].vel; + par_rot = parent_keys[k].rot; + } + else { + float spiral_time = (float)(k - start_index) / (float)(extrakeys-1); + float kink[3], tmp[3]; + + /* use same time value for every point on the spiral */ + par_time = spiral_start_time; + par_co = spiral_par_co; + par_vel = spiral_par_vel; + par_rot = spiral_par_rot; + + project_v3_v3v3(tmp, kink_base, dir); + sub_v3_v3v3(kink, kink_base, tmp); + normalize_v3(kink); + + if (kink_axis_random > 0.0f) { + float a = kink_axis_random * (psys_frand(ctx->sim.psys, 7112 + seed) * 2.0f - 1.0f) * (float)M_PI; + float rot[3][3]; + + axis_angle_normalized_to_mat3(rot, dir, a); + mul_m3_v3(rot, kink); + } + + do_kink_spiral_deform((ParticleKey *)key, dir, kink, spiral_time, kink_freq, kink_shape, kink_amp, spiral_start); + } + + /* apply different deformations to the child path */ + do_child_modifiers(&ctx->sim, ptex, par_co, par_vel, par_rot, parent_orco, cpa, orco, hairmat, (ParticleKey *)key, par_time); + } + + totlen = 0.0f; + for (k = 0, key = keys; k < end_index-1; k++, key++) + totlen += len_v3v3((key+1)->co, key->co); + + *r_totkeys = end_index; + *r_max_length = totlen; +} + +/* ------------------------------------------------------------------------- */ + +static bool check_path_length(int k, ParticleCacheKey *keys, ParticleCacheKey *key, float max_length, float step_length, float *cur_length, float dvec[3]) +{ + if (*cur_length + step_length > max_length) { + sub_v3_v3v3(dvec, key->co, (key-1)->co); + mul_v3_fl(dvec, (max_length - *cur_length) / step_length); + add_v3_v3v3(key->co, (key-1)->co, dvec); + keys->segments = k; + /* something over the maximum step value */ + return false; + } + else { + *cur_length += step_length; + return true; + } +} + +void psys_apply_child_modifiers(ParticleThreadContext *ctx, struct ListBase *modifiers, + ChildParticle *cpa, ParticleTexture *ptex, const float orco[3], const float ornor[3], float hairmat[4][4], + ParticleCacheKey *keys, ParticleCacheKey *parent_keys, const float parent_orco[3]) +{ + struct ParticleSettings *part = ctx->sim.psys->part; + struct Material *ma = ctx->ma; + const bool draw_col_ma = (part->draw_col == PART_DRAW_COL_MAT); + const bool use_length_check = !ELEM(part->kink, PART_KINK_SPIRAL); + + ParticlePathModifier *mod; + ParticleCacheKey *key; + int totkeys, k; + float max_length; + +#if 0 /* TODO for the future: use true particle modifiers that work on the whole curve */ + for (mod = modifiers->first; mod; mod = mod->next) { + mod->apply(keys, totkeys, parent_keys); + } +#else + (void)modifiers; + (void)mod; + + if (part->kink == PART_KINK_SPIRAL) { + do_kink_spiral(ctx, ptex, parent_orco, cpa, orco, hairmat, keys, parent_keys, &totkeys, &max_length); + keys->segments = totkeys - 1; + } + else { + ParticlePathIterator iter; + + totkeys = ctx->segments + 1; + max_length = ptex->length; + + for (k = 0, key = keys; k < totkeys; k++, key++) { + ParticleKey *par; + + psys_path_iter_get(&iter, keys, totkeys, parent_keys, k); + par = (ParticleKey *)iter.parent_key; + + /* apply different deformations to the child path */ + do_child_modifiers(&ctx->sim, ptex, par->co, par->vel, iter.parent_rotation, parent_orco, cpa, orco, hairmat, (ParticleKey *)key, iter.time); + } + } + + { + const float step_length = 1.0f / (float)(totkeys - 1); + + float cur_length = 0.0f; + + /* we have to correct velocity because of kink & clump */ + for (k = 0, key = keys; k < totkeys; ++k, ++key) { + if (k >= 2) { + sub_v3_v3v3((key-1)->vel, key->co, (key-2)->co); + mul_v3_fl((key-1)->vel, 0.5); + + if (ma && draw_col_ma) + get_strand_normal(ma, ornor, cur_length, (key-1)->vel); + } + if (k == totkeys-1) { + /* last key */ + sub_v3_v3v3(key->vel, key->co, (key-1)->co); + } + + if (use_length_check && k > 1) { + float dvec[3]; + /* check if path needs to be cut before actual end of data points */ + if (!check_path_length(k, keys, key, max_length, step_length, &cur_length, dvec)) + break; + } + + if (ma && draw_col_ma) { + copy_v3_v3(key->col, &ma->r); + get_strand_normal(ma, ornor, cur_length, key->vel); + } + } + } +#endif +} + +/* ------------------------------------------------------------------------- */ + +void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, + float amplitude, float flat, short type, short axis, float obmat[4][4], int smooth_start) +{ + float kink[3] = {1.f, 0.f, 0.f}, par_vec[3], q1[4] = {1.f, 0.f, 0.f, 0.f}; + float t, dt = 1.f, result[3]; + + if (ELEM(type, PART_KINK_NO, PART_KINK_SPIRAL)) + return; + + CLAMP(time, 0.f, 1.f); + + if (shape != 0.0f && !ELEM(type, PART_KINK_BRAID)) { + if (shape < 0.0f) + time = (float)pow(time, 1.f + shape); + else + time = (float)pow(time, 1.f / (1.f - shape)); + } + + t = time * freq * (float)M_PI; + + if (smooth_start) { + dt = fabsf(t); + /* smooth the beginning of kink */ + CLAMP(dt, 0.f, (float)M_PI); + dt = sinf(dt / 2.f); + } + + if (!ELEM(type, PART_KINK_RADIAL)) { + float temp[3]; + + kink[axis] = 1.f; + + if (obmat) + mul_mat3_m4_v3(obmat, kink); + + mul_qt_v3(par_rot, kink); + + /* make sure kink is normal to strand */ + project_v3_v3v3(temp, kink, par_vel); + sub_v3_v3(kink, temp); + normalize_v3(kink); + } + + copy_v3_v3(result, state->co); + sub_v3_v3v3(par_vec, par_co, state->co); + + switch (type) { + case PART_KINK_CURL: + { + float curl_offset[3]; + + /* rotate kink vector around strand tangent */ + mul_v3_v3fl(curl_offset, kink, amplitude); + axis_angle_to_quat(q1, par_vel, t); + mul_qt_v3(q1, curl_offset); + + interp_v3_v3v3(par_vec, state->co, par_co, flat); + add_v3_v3v3(result, par_vec, curl_offset); + break; + } + case PART_KINK_RADIAL: + { + if (flat > 0.f) { + float proj[3]; + /* flatten along strand */ + project_v3_v3v3(proj, par_vec, par_vel); + madd_v3_v3fl(result, proj, flat); + } + + madd_v3_v3fl(result, par_vec, -amplitude * sinf(t)); + break; + } + case PART_KINK_WAVE: + { + madd_v3_v3fl(result, kink, amplitude * sinf(t)); + + if (flat > 0.f) { + float proj[3]; + /* flatten along wave */ + project_v3_v3v3(proj, par_vec, kink); + madd_v3_v3fl(result, proj, flat); + + /* flatten along strand */ + project_v3_v3v3(proj, par_vec, par_vel); + madd_v3_v3fl(result, proj, flat); + } + break; + } + case PART_KINK_BRAID: + { + float y_vec[3] = {0.f, 1.f, 0.f}; + float z_vec[3] = {0.f, 0.f, 1.f}; + float vec_one[3], state_co[3]; + float inp_y, inp_z, length; + + if (par_rot) { + mul_qt_v3(par_rot, y_vec); + mul_qt_v3(par_rot, z_vec); + } + + negate_v3(par_vec); + normalize_v3_v3(vec_one, par_vec); + + inp_y = dot_v3v3(y_vec, vec_one); + inp_z = dot_v3v3(z_vec, vec_one); + + if (inp_y > 0.5f) { + copy_v3_v3(state_co, y_vec); + + mul_v3_fl(y_vec, amplitude * cosf(t)); + mul_v3_fl(z_vec, amplitude / 2.f * sinf(2.f * t)); + } + else if (inp_z > 0.0f) { + mul_v3_v3fl(state_co, z_vec, sinf((float)M_PI / 3.f)); + madd_v3_v3fl(state_co, y_vec, -0.5f); + + mul_v3_fl(y_vec, -amplitude * cosf(t + (float)M_PI / 3.f)); + mul_v3_fl(z_vec, amplitude / 2.f * cosf(2.f * t + (float)M_PI / 6.f)); + } + else { + mul_v3_v3fl(state_co, z_vec, -sinf((float)M_PI / 3.f)); + madd_v3_v3fl(state_co, y_vec, -0.5f); + + mul_v3_fl(y_vec, amplitude * -sinf(t + (float)M_PI / 6.f)); + mul_v3_fl(z_vec, amplitude / 2.f * -sinf(2.f * t + (float)M_PI / 3.f)); + } + + mul_v3_fl(state_co, amplitude); + add_v3_v3(state_co, par_co); + sub_v3_v3v3(par_vec, state->co, state_co); + + length = normalize_v3(par_vec); + mul_v3_fl(par_vec, MIN2(length, amplitude / 2.f)); + + add_v3_v3v3(state_co, par_co, y_vec); + add_v3_v3(state_co, z_vec); + add_v3_v3(state_co, par_vec); + + shape = 2.f * (float)M_PI * (1.f + shape); + + if (t < shape) { + shape = t / shape; + shape = (float)sqrt((double)shape); + interp_v3_v3v3(result, result, state_co, shape); + } + else { + copy_v3_v3(result, state_co); + } + break; + } + } + + /* blend the start of the kink */ + if (dt < 1.f) + interp_v3_v3v3(state->co, state->co, result, dt); + else + copy_v3_v3(state->co, result); +} + +static float do_clump_level(float result[3], const float co[3], const float par_co[3], float time, + float clumpfac, float clumppow, float pa_clump, CurveMapping *clumpcurve) +{ + float clump = 0.0f; + + if (clumpcurve) { + clump = pa_clump * (1.0f - CLAMPIS(curvemapping_evaluateF(clumpcurve, 0, time), 0.0f, 1.0f)); + + interp_v3_v3v3(result, co, par_co, clump); + } + else if (clumpfac != 0.0f) { + float cpow; + + if (clumppow < 0.0f) + cpow = 1.0f + clumppow; + else + cpow = 1.0f + 9.0f * clumppow; + + if (clumpfac < 0.0f) /* clump roots instead of tips */ + clump = -clumpfac * pa_clump * (float)pow(1.0 - (double)time, (double)cpow); + else + clump = clumpfac * pa_clump * (float)pow((double)time, (double)cpow); + + interp_v3_v3v3(result, co, par_co, clump); + } + + return clump; +} + +float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump, + bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve) +{ + float clump; + + if (use_clump_noise && clump_noise_size != 0.0f) { + float center[3], noisevec[3]; + float da[4], pa[12]; + + mul_v3_v3fl(noisevec, orco_offset, 1.0f / clump_noise_size); + voronoi(noisevec[0], noisevec[1], noisevec[2], da, pa, 1.0f, 0); + mul_v3_fl(&pa[0], clump_noise_size); + add_v3_v3v3(center, par_co, &pa[0]); + + do_clump_level(state->co, state->co, center, time, clumpfac, clumppow, pa_clump, clumpcurve); + } + + clump = do_clump_level(state->co, state->co, par_co, time, clumpfac, clumppow, pa_clump, clumpcurve); + + return clump; +} + +static void do_rough(const float loc[3], float mat[4][4], float t, float fac, float size, float thres, ParticleKey *state) +{ + float rough[3]; + float rco[3]; + + if (thres != 0.0f) { + if (fabsf((float)(-1.5f + loc[0] + loc[1] + loc[2])) < 1.5f * thres) { + return; + } + } + + copy_v3_v3(rco, loc); + mul_v3_fl(rco, t); + rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2, 0, 2); + rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2, 0, 2); + rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2, 0, 2); + + madd_v3_v3fl(state->co, mat[0], fac * rough[0]); + madd_v3_v3fl(state->co, mat[1], fac * rough[1]); + madd_v3_v3fl(state->co, mat[2], fac * rough[2]); +} + +static void do_rough_end(const float loc[3], float mat[4][4], float t, float fac, float shape, ParticleKey *state) +{ + float rough[2]; + float roughfac; + + roughfac = fac * (float)pow((double)t, shape); + copy_v2_v2(rough, loc); + rough[0] = -1.0f + 2.0f * rough[0]; + rough[1] = -1.0f + 2.0f * rough[1]; + mul_v2_fl(rough, roughfac); + + madd_v3_v3fl(state->co, mat[0], rough[0]); + madd_v3_v3fl(state->co, mat[1], rough[1]); +} + +static void do_rough_curve(const float loc[3], float mat[4][4], float time, float fac, float size, CurveMapping *roughcurve, ParticleKey *state) +{ + float rough[3]; + float rco[3]; + + if (!roughcurve) + return; + + fac *= CLAMPIS(curvemapping_evaluateF(roughcurve, 0, time), 0.0f, 1.0f); + + copy_v3_v3(rco, loc); + mul_v3_fl(rco, time); + rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2, 0, 2); + rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2, 0, 2); + rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2, 0, 2); + + madd_v3_v3fl(state->co, mat[0], fac * rough[0]); + madd_v3_v3fl(state->co, mat[1], fac * rough[1]); + madd_v3_v3fl(state->co, mat[2], fac * rough[2]); +} + +void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3], + ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t) +{ + ParticleSettings *part = sim->psys->part; + CurveMapping *clumpcurve = (part->child_flag & PART_CHILD_USE_CLUMP_CURVE) ? part->clumpcurve : NULL; + CurveMapping *roughcurve = (part->child_flag & PART_CHILD_USE_ROUGH_CURVE) ? part->roughcurve : NULL; + int i = cpa - sim->psys->child; + int guided = 0; + + float kink_amp = part->kink_amp; + float kink_amp_clump = part->kink_amp_clump; + float kink_freq = part->kink_freq; + float rough1 = part->rough1; + float rough2 = part->rough2; + float rough_end = part->rough_end; + const bool smooth_start = (sim->psys->part->childtype == PART_CHILD_FACES); + + if (ptex) { + kink_amp *= ptex->kink_amp; + kink_freq *= ptex->kink_freq; + rough1 *= ptex->rough1; + rough2 *= ptex->rough2; + rough_end *= ptex->roughe; + } + + if (part->flag & PART_CHILD_EFFECT) + /* state is safe to cast, since only co and vel are used */ + guided = do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)state, cpa->parent, t); + + if (guided == 0) { + float orco_offset[3]; + float clump; + + sub_v3_v3v3(orco_offset, orco, par_orco); + clump = do_clump(state, par_co, t, orco_offset, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f, + part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve); + + if (kink_freq != 0.f) { + kink_amp *= (1.f - kink_amp_clump * clump); + + do_kink(state, par_co, par_vel, par_rot, t, kink_freq, part->kink_shape, + kink_amp, part->kink_flat, part->kink, part->kink_axis, + sim->ob->obmat, smooth_start); + } + } + + if (roughcurve) { + do_rough_curve(orco, mat, t, rough1, part->rough1_size, roughcurve, state); + } + else { + if (rough1 > 0.f) + do_rough(orco, mat, t, rough1, part->rough1_size, 0.0, state); + + if (rough2 > 0.f) { + float vec[3]; + psys_frand_vec(sim->psys, i + 27, vec); + do_rough(vec, mat, t, rough2, part->rough2_size, part->rough2_thres, state); + } + + if (rough_end > 0.f) { + float vec[3]; + psys_frand_vec(sim->psys, i + 27, vec); + do_rough_end(vec, mat, t, rough_end, part->rough_end_shape, state); + } + } +} diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c new file mode 100644 index 00000000000..6cc42e85a12 --- /dev/null +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -0,0 +1,1419 @@ +/* + * ***** 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 by Janne Karhu. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Raul Fernandez Hernandez (Farsthary), + * Stephen Swhitehorn, + * Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/particle_distribute.c + * \ingroup bke + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_jitter.h" +#include "BLI_kdtree.h" +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_sort.h" +#include "BLI_task.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_particle_types.h" +#include "DNA_scene_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_DerivedMesh.h" +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_particle.h" + +static int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot); + +static void alloc_child_particles(ParticleSystem *psys, int tot) +{ + if (psys->child) { + /* only re-allocate if we have to */ + if (psys->part->childtype && psys->totchild == tot) { + memset(psys->child, 0, tot*sizeof(ChildParticle)); + return; + } + + MEM_freeN(psys->child); + psys->child=NULL; + psys->totchild=0; + } + + if (psys->part->childtype) { + psys->totchild= tot; + if (psys->totchild) + psys->child= MEM_callocN(psys->totchild*sizeof(ChildParticle), "child_particles"); + } +} + +static void distribute_simple_children(Scene *scene, Object *ob, DerivedMesh *finaldm, ParticleSystem *psys) +{ + ChildParticle *cpa = NULL; + int i, p; + int child_nbr= psys_get_child_number(scene, psys); + int totpart= psys_get_tot_child(scene, psys); + + alloc_child_particles(psys, totpart); + + cpa = psys->child; + for (i=0; i<child_nbr; i++) { + for (p=0; p<psys->totpart; p++,cpa++) { + float length=2.0; + cpa->parent=p; + + /* create even spherical distribution inside unit sphere */ + while (length>=1.0f) { + cpa->fuv[0]=2.0f*BLI_frand()-1.0f; + cpa->fuv[1]=2.0f*BLI_frand()-1.0f; + cpa->fuv[2]=2.0f*BLI_frand()-1.0f; + length=len_v3(cpa->fuv); + } + + cpa->num=-1; + } + } + /* dmcache must be updated for parent particles if children from faces is used */ + psys_calc_dmcache(ob, finaldm, psys); +} +static void distribute_grid(DerivedMesh *dm, ParticleSystem *psys) +{ + ParticleData *pa=NULL; + float min[3], max[3], delta[3], d; + MVert *mv, *mvert = dm->getVertDataArray(dm,0); + int totvert=dm->getNumVerts(dm), from=psys->part->from; + int i, j, k, p, res=psys->part->grid_res, size[3], axis; + + /* find bounding box of dm */ + if (totvert > 0) { + mv=mvert; + copy_v3_v3(min, mv->co); + copy_v3_v3(max, mv->co); + mv++; + for (i = 1; i < totvert; i++, mv++) { + minmax_v3v3_v3(min, max, mv->co); + } + } + else { + zero_v3(min); + zero_v3(max); + } + + sub_v3_v3v3(delta, max, min); + + /* determine major axis */ + axis = axis_dominant_v3_single(delta); + + d = delta[axis]/(float)res; + + size[axis] = res; + size[(axis+1)%3] = (int)ceil(delta[(axis+1)%3]/d); + size[(axis+2)%3] = (int)ceil(delta[(axis+2)%3]/d); + + /* float errors grrr.. */ + size[(axis+1)%3] = MIN2(size[(axis+1)%3],res); + size[(axis+2)%3] = MIN2(size[(axis+2)%3],res); + + size[0] = MAX2(size[0], 1); + size[1] = MAX2(size[1], 1); + size[2] = MAX2(size[2], 1); + + /* no full offset for flat/thin objects */ + min[0]+= d < delta[0] ? d/2.f : delta[0]/2.f; + min[1]+= d < delta[1] ? d/2.f : delta[1]/2.f; + min[2]+= d < delta[2] ? d/2.f : delta[2]/2.f; + + for (i=0,p=0,pa=psys->particles; i<res; i++) { + for (j=0; j<res; j++) { + for (k=0; k<res; k++,p++,pa++) { + pa->fuv[0] = min[0] + (float)i*d; + pa->fuv[1] = min[1] + (float)j*d; + pa->fuv[2] = min[2] + (float)k*d; + pa->flag |= PARS_UNEXIST; + pa->hair_index = 0; /* abused in volume calculation */ + } + } + } + + /* enable particles near verts/edges/faces/inside surface */ + if (from==PART_FROM_VERT) { + float vec[3]; + + pa=psys->particles; + + min[0] -= d/2.0f; + min[1] -= d/2.0f; + min[2] -= d/2.0f; + + for (i=0,mv=mvert; i<totvert; i++,mv++) { + sub_v3_v3v3(vec,mv->co,min); + vec[0]/=delta[0]; + vec[1]/=delta[1]; + vec[2]/=delta[2]; + pa[((int)(vec[0] * (size[0] - 1)) * res + + (int)(vec[1] * (size[1] - 1))) * res + + (int)(vec[2] * (size[2] - 1))].flag &= ~PARS_UNEXIST; + } + } + else if (ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) { + float co1[3], co2[3]; + + MFace *mface= NULL, *mface_array; + float v1[3], v2[3], v3[3], v4[4], lambda; + int a, a1, a2, a0mul, a1mul, a2mul, totface; + int amax= from==PART_FROM_FACE ? 3 : 1; + + totface=dm->getNumTessFaces(dm); + mface=mface_array=dm->getTessFaceDataArray(dm,CD_MFACE); + + for (a=0; a<amax; a++) { + if (a==0) { a0mul=res*res; a1mul=res; a2mul=1; } + else if (a==1) { a0mul=res; a1mul=1; a2mul=res*res; } + else { a0mul=1; a1mul=res*res; a2mul=res; } + + for (a1=0; a1<size[(a+1)%3]; a1++) { + for (a2=0; a2<size[(a+2)%3]; a2++) { + mface= mface_array; + + pa = psys->particles + a1*a1mul + a2*a2mul; + copy_v3_v3(co1, pa->fuv); + co1[a] -= d < delta[a] ? d/2.f : delta[a]/2.f; + copy_v3_v3(co2, co1); + co2[a] += delta[a] + 0.001f*d; + co1[a] -= 0.001f*d; + + /* lets intersect the faces */ + for (i=0; i<totface; i++,mface++) { + copy_v3_v3(v1, mvert[mface->v1].co); + copy_v3_v3(v2, mvert[mface->v2].co); + copy_v3_v3(v3, mvert[mface->v3].co); + + if (isect_axial_line_tri_v3(a, co1, co2, v2, v3, v1, &lambda)) { + if (from==PART_FROM_FACE) + (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; + else /* store number of intersections */ + (pa+(int)(lambda*size[a])*a0mul)->hair_index++; + } + else if (mface->v4) { + copy_v3_v3(v4, mvert[mface->v4].co); + + if (isect_axial_line_tri_v3(a, co1, co2, v4, v1, v3, &lambda)) { + if (from==PART_FROM_FACE) + (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; + else + (pa+(int)(lambda*size[a])*a0mul)->hair_index++; + } + } + } + + if (from==PART_FROM_VOLUME) { + int in=pa->hair_index%2; + if (in) pa->hair_index++; + for (i=0; i<size[0]; i++) { + if (in || (pa+i*a0mul)->hair_index%2) + (pa+i*a0mul)->flag &= ~PARS_UNEXIST; + /* odd intersections == in->out / out->in */ + /* even intersections -> in stays same */ + in=(in + (pa+i*a0mul)->hair_index) % 2; + } + } + } + } + } + } + + if (psys->part->flag & PART_GRID_HEXAGONAL) { + for (i=0,p=0,pa=psys->particles; i<res; i++) { + for (j=0; j<res; j++) { + for (k=0; k<res; k++,p++,pa++) { + if (j%2) + pa->fuv[0] += d/2.f; + + if (k%2) { + pa->fuv[0] += d/2.f; + pa->fuv[1] += d/2.f; + } + } + } + } + } + + if (psys->part->flag & PART_GRID_INVERT) { + for (i=0; i<size[0]; i++) { + for (j=0; j<size[1]; j++) { + pa=psys->particles + res*(i*res + j); + for (k=0; k<size[2]; k++, pa++) { + pa->flag ^= PARS_UNEXIST; + } + } + } + } + + if (psys->part->grid_rand > 0.f) { + float rfac = d * psys->part->grid_rand; + for (p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { + if (pa->flag & PARS_UNEXIST) + continue; + + pa->fuv[0] += rfac * (psys_frand(psys, p + 31) - 0.5f); + pa->fuv[1] += rfac * (psys_frand(psys, p + 32) - 0.5f); + pa->fuv[2] += rfac * (psys_frand(psys, p + 33) - 0.5f); + } + } +} + +/* modified copy from rayshade.c */ +static void hammersley_create(float *out, int n, int seed, float amount) +{ + RNG *rng; + double p, t, offs[2]; + int k, kk; + + rng = BLI_rng_new(31415926 + n + seed); + offs[0] = BLI_rng_get_double(rng) + (double)amount; + offs[1] = BLI_rng_get_double(rng) + (double)amount; + BLI_rng_free(rng); + + for (k = 0; k < n; k++) { + t = 0; + for (p = 0.5, kk = k; kk; p *= 0.5, kk >>= 1) + if (kk & 1) /* kk mod 2 = 1 */ + t += p; + + out[2*k + 0] = fmod((double)k/(double)n + offs[0], 1.0); + out[2*k + 1] = fmod(t + offs[1], 1.0); + } +} + +/* almost exact copy of BLI_jitter_init */ +static void init_mv_jit(float *jit, int num, int seed2, float amount) +{ + RNG *rng; + float *jit2, x, rad1, rad2, rad3; + int i, num2; + + if (num==0) return; + + rad1= (float)(1.0f/sqrtf((float)num)); + rad2= (float)(1.0f/((float)num)); + rad3= (float)sqrtf((float)num)/((float)num); + + rng = BLI_rng_new(31415926 + num + seed2); + x= 0; + num2 = 2 * num; + for (i=0; i<num2; i+=2) { + + jit[i] = x + amount*rad1*(0.5f - BLI_rng_get_float(rng)); + jit[i+1] = i/(2.0f*num) + amount*rad1*(0.5f - BLI_rng_get_float(rng)); + + jit[i]-= (float)floor(jit[i]); + jit[i+1]-= (float)floor(jit[i+1]); + + x+= rad3; + x -= (float)floor(x); + } + + jit2= MEM_mallocN(12 + 2*sizeof(float)*num, "initjit"); + + for (i=0 ; i<4 ; i++) { + BLI_jitterate1((float (*)[2])jit, (float (*)[2])jit2, num, rad1); + BLI_jitterate1((float (*)[2])jit, (float (*)[2])jit2, num, rad1); + BLI_jitterate2((float (*)[2])jit, (float (*)[2])jit2, num, rad2); + } + MEM_freeN(jit2); + BLI_rng_free(rng); +} + +static void psys_uv_to_w(float u, float v, int quad, float *w) +{ + float vert[4][3], co[3]; + + if (!quad) { + if (u+v > 1.0f) + v= 1.0f-v; + else + u= 1.0f-u; + } + + vert[0][0] = 0.0f; vert[0][1] = 0.0f; vert[0][2] = 0.0f; + vert[1][0] = 1.0f; vert[1][1] = 0.0f; vert[1][2] = 0.0f; + vert[2][0] = 1.0f; vert[2][1] = 1.0f; vert[2][2] = 0.0f; + + co[0] = u; + co[1] = v; + co[2] = 0.0f; + + if (quad) { + vert[3][0] = 0.0f; vert[3][1] = 1.0f; vert[3][2] = 0.0f; + interp_weights_poly_v3( w,vert, 4, co); + } + else { + interp_weights_poly_v3( w,vert, 3, co); + w[3] = 0.0f; + } +} + +/* 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; + + if (value == 0.f) + return 0; + + while (low <= high) { + mid= (low + high)/2; + + if (sum[mid] < value && value <= sum[mid+1]) + return mid; + + if (sum[mid] >= value) + high= mid - 1; + else if (sum[mid] < value) + low= mid + 1; + else + return mid; + } + + return low; +} + +/* the max number if calls to rng_* funcs within psys_thread_distribute_particle + * be sure to keep up to date if this changes */ +#define PSYS_RND_DIST_SKIP 2 + +/* note: this function must be thread safe, for from == PART_FROM_CHILD */ +#define ONLY_WORKING_WITH_PA_VERTS 0 +static void distribute_from_verts_exec(ParticleTask *thread, ParticleData *pa, int p) +{ + ParticleThreadContext *ctx= thread->ctx; + int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ + + /* TODO_PARTICLE - use original index */ + pa->num= ctx->index[p]; + pa->fuv[0] = 1.0f; + pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0; + +#if ONLY_WORKING_WITH_PA_VERTS + if (ctx->tree) { + KDTreeNearest ptn[3]; + int w, maxw; + + psys_particle_on_dm(ctx->dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0,orco1,0); + BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco1, 1, 1); + maxw = BLI_kdtree_find_nearest_n(ctx->tree,orco1,ptn,3); + + for (w=0; w<maxw; w++) { + pa->verts[w]=ptn->num; + } + } +#endif + + if (rng_skip_tot > 0) /* should never be below zero */ + BLI_rng_skip(thread->rng, rng_skip_tot); +} + +static void distribute_from_faces_exec(ParticleTask *thread, ParticleData *pa, int p) { + ParticleThreadContext *ctx= thread->ctx; + DerivedMesh *dm= ctx->dm; + float randu, randv; + int distr= ctx->distr; + int i; + int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ + + MFace *mface; + + pa->num = i = ctx->index[p]; + mface = dm->getTessFaceData(dm,i,CD_MFACE); + + switch (distr) { + case PART_DISTR_JIT: + if (ctx->jitlevel == 1) { + if (mface->v4) + psys_uv_to_w(0.5f, 0.5f, mface->v4, pa->fuv); + else + psys_uv_to_w(1.0f / 3.0f, 1.0f / 3.0f, mface->v4, pa->fuv); + } + else { + ctx->jitoff[i] = fmod(ctx->jitoff[i],(float)ctx->jitlevel); + if (!isnan(ctx->jitoff[i])) { + psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mface->v4, pa->fuv); + ctx->jitoff[i]++; + } + } + break; + case PART_DISTR_RAND: + randu= BLI_rng_get_float(thread->rng); + randv= BLI_rng_get_float(thread->rng); + rng_skip_tot -= 2; + + psys_uv_to_w(randu, randv, mface->v4, pa->fuv); + break; + } + pa->foffset= 0.0f; + + if (rng_skip_tot > 0) /* should never be below zero */ + BLI_rng_skip(thread->rng, rng_skip_tot); +} + +static void distribute_from_volume_exec(ParticleTask *thread, ParticleData *pa, int p) { + ParticleThreadContext *ctx= thread->ctx; + DerivedMesh *dm= ctx->dm; + float *v1, *v2, *v3, *v4, nor[3], co1[3], co2[3]; + float cur_d, min_d, randu, randv; + int distr= ctx->distr; + int i, intersect, tot; + int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ + + MFace *mface; + MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); + + pa->num = i = ctx->index[p]; + mface = dm->getTessFaceData(dm,i,CD_MFACE); + + switch (distr) { + case PART_DISTR_JIT: + if (ctx->jitlevel == 1) { + if (mface->v4) + psys_uv_to_w(0.5f, 0.5f, mface->v4, pa->fuv); + else + psys_uv_to_w(1.0f / 3.0f, 1.0f / 3.0f, mface->v4, pa->fuv); + } + else { + ctx->jitoff[i] = fmod(ctx->jitoff[i],(float)ctx->jitlevel); + if (!isnan(ctx->jitoff[i])) { + psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mface->v4, pa->fuv); + ctx->jitoff[i]++; + } + } + break; + case PART_DISTR_RAND: + randu= BLI_rng_get_float(thread->rng); + randv= BLI_rng_get_float(thread->rng); + rng_skip_tot -= 2; + + psys_uv_to_w(randu, randv, mface->v4, pa->fuv); + break; + } + pa->foffset= 0.0f; + + /* experimental */ + tot=dm->getNumTessFaces(dm); + + psys_interpolate_face(mvert,mface,0,0,pa->fuv,co1,nor,0,0,0,0); + + normalize_v3(nor); + mul_v3_fl(nor,-100.0); + + add_v3_v3v3(co2,co1,nor); + + min_d=2.0; + intersect=0; + + for (i=0,mface=dm->getTessFaceDataArray(dm,CD_MFACE); i<tot; i++,mface++) { + if (i==pa->num) continue; + + v1=mvert[mface->v1].co; + v2=mvert[mface->v2].co; + v3=mvert[mface->v3].co; + + if (isect_line_tri_v3(co1, co2, v2, v3, v1, &cur_d, 0)) { + if (cur_d<min_d) { + min_d=cur_d; + pa->foffset=cur_d*50.0f; /* to the middle of volume */ + intersect=1; + } + } + if (mface->v4) { + v4=mvert[mface->v4].co; + + if (isect_line_tri_v3(co1, co2, v4, v1, v3, &cur_d, 0)) { + if (cur_d<min_d) { + min_d=cur_d; + pa->foffset=cur_d*50.0f; /* to the middle of volume */ + intersect=1; + } + } + } + } + if (intersect==0) + pa->foffset=0.0; + else { + switch (distr) { + case PART_DISTR_JIT: + pa->foffset *= ctx->jit[p % (2 * ctx->jitlevel)]; + break; + case PART_DISTR_RAND: + pa->foffset *= BLI_frand(); + break; + } + } + + if (rng_skip_tot > 0) /* should never be below zero */ + BLI_rng_skip(thread->rng, rng_skip_tot); +} + +static void distribute_children_exec(ParticleTask *thread, ChildParticle *cpa, int p) { + ParticleThreadContext *ctx= thread->ctx; + Object *ob= ctx->sim.ob; + DerivedMesh *dm= ctx->dm; + float orco1[3], co1[3], nor1[3]; + float randu, randv; + int cfrom= ctx->cfrom; + int i; + int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ + + MFace *mf; + + if (ctx->index[p] < 0) { + cpa->num=0; + cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]=0.0f; + cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; + return; + } + + mf= dm->getTessFaceData(dm, ctx->index[p], CD_MFACE); + + randu= BLI_rng_get_float(thread->rng); + randv= BLI_rng_get_float(thread->rng); + rng_skip_tot -= 2; + + psys_uv_to_w(randu, randv, mf->v4, cpa->fuv); + + cpa->num = ctx->index[p]; + + if (ctx->tree) { + KDTreeNearest ptn[10]; + int w,maxw;//, do_seams; + float maxd /*, mind,dd */, totw= 0.0f; + int parent[10]; + float pweight[10]; + + psys_particle_on_dm(dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,NULL,NULL,orco1,NULL); + BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco1, 1, 1); + maxw = BLI_kdtree_find_nearest_n(ctx->tree,orco1,ptn,3); + + maxd=ptn[maxw-1].dist; + /* mind=ptn[0].dist; */ /* UNUSED */ + + /* the weights here could be done better */ + for (w=0; w<maxw; w++) { + parent[w]=ptn[w].index; + pweight[w]=(float)pow(2.0,(double)(-6.0f*ptn[w].dist/maxd)); + } + for (;w<10; w++) { + parent[w]=-1; + pweight[w]=0.0f; + } + + for (w=0,i=0; w<maxw && i<4; w++) { + if (parent[w]>=0) { + cpa->pa[i]=parent[w]; + cpa->w[i]=pweight[w]; + totw+=pweight[w]; + i++; + } + } + for (;i<4; i++) { + cpa->pa[i]=-1; + cpa->w[i]=0.0f; + } + + if (totw > 0.0f) { + for (w = 0; w < 4; w++) { + cpa->w[w] /= totw; + } + } + + cpa->parent=cpa->pa[0]; + } + + if (rng_skip_tot > 0) /* should never be below zero */ + BLI_rng_skip(thread->rng, rng_skip_tot); +} + +static void exec_distribute_parent(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + ParticleTask *task = taskdata; + ParticleSystem *psys= task->ctx->sim.psys; + ParticleData *pa; + int p; + + pa= psys->particles + task->begin; + switch (psys->part->from) { + case PART_FROM_FACE: + for (p = task->begin; p < task->end; ++p, ++pa) + distribute_from_faces_exec(task, pa, p); + break; + case PART_FROM_VOLUME: + for (p = task->begin; p < task->end; ++p, ++pa) + distribute_from_volume_exec(task, pa, p); + break; + case PART_FROM_VERT: + for (p = task->begin; p < task->end; ++p, ++pa) + distribute_from_verts_exec(task, pa, p); + break; + } +} + +static void exec_distribute_child(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + ParticleTask *task = taskdata; + ParticleSystem *psys = task->ctx->sim.psys; + ChildParticle *cpa; + int p; + + /* RNG skipping at the beginning */ + cpa = psys->child; + for (p = 0; p < task->begin; ++p, ++cpa) { + if (task->ctx->skip) /* simplification skip */ + BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP * task->ctx->skip[p]); + + BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP); + } + + for (; p < task->end; ++p, ++cpa) { + if (task->ctx->skip) /* simplification skip */ + BLI_rng_skip(task->rng, PSYS_RND_DIST_SKIP * task->ctx->skip[p]); + + distribute_children_exec(task, cpa, p); + } +} + +static int distribute_compare_orig_index(const void *p1, const void *p2, void *user_data) +{ + int *orig_index = (int *) user_data; + int index1 = orig_index[*(const int *)p1]; + int index2 = orig_index[*(const int *)p2]; + + if (index1 < index2) + return -1; + else if (index1 == index2) { + /* this pointer comparison appears to make qsort stable for glibc, + * and apparently on solaris too, makes the renders reproducible */ + if (p1 < p2) + return -1; + else if (p1 == p2) + return 0; + else + return 1; + } + else + return 1; +} + +static void distribute_invalid(Scene *scene, ParticleSystem *psys, int from) +{ + if (from == PART_FROM_CHILD) { + ChildParticle *cpa; + int p, totchild = psys_get_tot_child(scene, psys); + + if (psys->child && totchild) { + for (p=0,cpa=psys->child; p<totchild; p++,cpa++) { + cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3] = 0.0; + cpa->foffset= 0.0f; + cpa->parent=0; + cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; + cpa->num= -1; + } + } + } + else { + PARTICLE_P; + LOOP_PARTICLES { + pa->fuv[0] = pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0; + pa->foffset= 0.0f; + pa->num= -1; + } + } +} + +/* Creates a distribution of coordinates on a DerivedMesh */ +/* This is to denote functionality that does not yet work with mesh - only derived mesh */ +static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, ParticleSimulationData *sim, int from) +{ + Scene *scene = sim->scene; + DerivedMesh *finaldm = sim->psmd->dm; + Object *ob = sim->ob; + ParticleSystem *psys= sim->psys; + ParticleData *pa=0, *tpars= 0; + ParticleSettings *part; + ParticleSeam *seams= 0; + KDTree *tree=0; + DerivedMesh *dm= NULL; + float *jit= NULL; + int i, p=0; + 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 cur, maxweight=0.0, tweight, totweight, inv_totweight, co[3], nor[3], orco[3]; + + if (ELEM(NULL, ob, psys, psys->part)) + return 0; + + part=psys->part; + totpart=psys->totpart; + if (totpart==0) + return 0; + + if (!finaldm->deformedOnly && !finaldm->getTessFaceDataArray(finaldm, CD_ORIGINDEX)) { + printf("Can't create particles with the current modifier stack, disable destructive modifiers\n"); +// XXX error("Can't paint with the current modifier stack, disable destructive modifiers"); + return 0; + } + + psys_thread_context_init(ctx, sim); + + /* First handle special cases */ + if (from == PART_FROM_CHILD) { + /* Simple children */ + if (part->childtype != PART_CHILD_FACES) { + BLI_srandom(31415926 + psys->seed + psys->child_seed); + distribute_simple_children(scene, ob, finaldm, psys); + return 0; + } + } + else { + /* Grid distribution */ + if (part->distr==PART_DISTR_GRID && from != PART_FROM_VERT) { + BLI_srandom(31415926 + psys->seed); + dm= CDDM_from_mesh((Mesh*)ob->data); + DM_ensure_tessface(dm); + distribute_grid(dm,psys); + dm->release(dm); + return 0; + } + } + + /* Create trees and original coordinates if needed */ + if (from == PART_FROM_CHILD) { + distr=PART_DISTR_RAND; + BLI_srandom(31415926 + psys->seed + psys->child_seed); + dm= finaldm; + + /* BMESH ONLY */ + DM_ensure_tessface(dm); + + children=1; + + tree=BLI_kdtree_new(totpart); + + for (p=0,pa=psys->particles; p<totpart; p++,pa++) { + psys_particle_on_dm(dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,NULL); + BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco, 1, 1); + BLI_kdtree_insert(tree, p, orco); + } + + BLI_kdtree_balance(tree); + + totpart = psys_get_tot_child(scene, psys); + cfrom = from = PART_FROM_FACE; + } + else { + distr = part->distr; + BLI_srandom(31415926 + psys->seed); + + if (psys->part->use_modifier_stack) + dm = finaldm; + else + dm= CDDM_from_mesh((Mesh*)ob->data); + + /* BMESH ONLY, for verts we don't care about tessfaces */ + if (from != PART_FROM_VERT) { + DM_ensure_tessface(dm); + } + + /* we need orco for consistent distributions */ + if (!CustomData_has_layer(&dm->vertData, CD_ORCO)) + DM_add_vert_layer(dm, CD_ORCO, CD_ASSIGN, BKE_mesh_orco_verts_get(ob)); + + if (from == PART_FROM_VERT) { + MVert *mv= dm->getVertDataArray(dm, CD_MVERT); + float (*orcodata)[3] = dm->getVertDataArray(dm, CD_ORCO); + int totvert = dm->getNumVerts(dm); + + tree=BLI_kdtree_new(totvert); + + for (p=0; p<totvert; p++) { + if (orcodata) { + copy_v3_v3(co,orcodata[p]); + BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co, 1, 1); + } + else + copy_v3_v3(co,mv[p].co); + BLI_kdtree_insert(tree, p, co); + } + + BLI_kdtree_balance(tree); + } + } + + /* Get total number of emission elements and allocate needed arrays */ + totelem = (from == PART_FROM_VERT) ? dm->getNumVerts(dm) : dm->getNumTessFaces(dm); + + if (totelem == 0) { + distribute_invalid(scene, psys, children ? PART_FROM_CHILD : 0); + + if (G.debug & G_DEBUG) + fprintf(stderr,"Particle distribution error: Nothing to emit from!\n"); + + if (dm != finaldm) dm->release(dm); + + BLI_kdtree_free(tree); + + return 0; + } + + element_weight = MEM_callocN(sizeof(float)*totelem, "particle_distribution_weights"); + particle_element= MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes"); + element_sum = MEM_callocN(sizeof(float)*(totelem+1), "particle_distribution_sum"); + jitter_offset = MEM_callocN(sizeof(float)*totelem, "particle_distribution_jitoff"); + + /* Calculate weights from face areas */ + if ((part->flag&PART_EDISTR || children) && from != PART_FROM_VERT) { + MVert *v1, *v2, *v3, *v4; + float totarea=0.f, co1[3], co2[3], co3[3], co4[3]; + float (*orcodata)[3]; + + orcodata= dm->getVertDataArray(dm, CD_ORCO); + + for (i=0; i<totelem; i++) { + MFace *mf=dm->getTessFaceData(dm,i,CD_MFACE); + + if (orcodata) { + copy_v3_v3(co1, orcodata[mf->v1]); + copy_v3_v3(co2, orcodata[mf->v2]); + copy_v3_v3(co3, orcodata[mf->v3]); + BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co1, 1, 1); + BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co2, 1, 1); + BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co3, 1, 1); + if (mf->v4) { + copy_v3_v3(co4, orcodata[mf->v4]); + BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co4, 1, 1); + } + } + else { + v1= (MVert*)dm->getVertData(dm,mf->v1,CD_MVERT); + v2= (MVert*)dm->getVertData(dm,mf->v2,CD_MVERT); + v3= (MVert*)dm->getVertData(dm,mf->v3,CD_MVERT); + copy_v3_v3(co1, v1->co); + copy_v3_v3(co2, v2->co); + copy_v3_v3(co3, v3->co); + if (mf->v4) { + v4= (MVert*)dm->getVertData(dm,mf->v4,CD_MVERT); + copy_v3_v3(co4, v4->co); + } + } + + cur = mf->v4 ? area_quad_v3(co1, co2, co3, co4) : area_tri_v3(co1, co2, co3); + + if (cur > maxweight) + maxweight = cur; + + element_weight[i] = cur; + totarea += cur; + } + + for (i=0; i<totelem; i++) + element_weight[i] /= totarea; + + maxweight /= totarea; + } + else { + float min=1.0f/(float)(MIN2(totelem,totpart)); + for (i=0; i<totelem; i++) + element_weight[i]=min; + maxweight=min; + } + + /* Calculate weights from vgroup */ + vweight = psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY); + + if (vweight) { + if (from==PART_FROM_VERT) { + for (i=0;i<totelem; i++) + element_weight[i]*=vweight[i]; + } + else { /* PART_FROM_FACE / PART_FROM_VOLUME */ + for (i=0;i<totelem; i++) { + MFace *mf=dm->getTessFaceData(dm,i,CD_MFACE); + tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3]; + + if (mf->v4) { + tweight += vweight[mf->v4]; + tweight /= 4.0f; + } + else { + tweight /= 3.0f; + } + + element_weight[i]*=tweight; + } + } + MEM_freeN(vweight); + } + + /* Calculate total weight of all elements */ + totweight= 0.0f; + for (i=0;i<totelem; i++) + totweight += element_weight[i]; + + inv_totweight = (totweight > 0.f ? 1.f/totweight : 0.f); + + /* Calculate cumulative weights */ + element_sum[0] = 0.0f; + for (i=0; i<totelem; i++) + element_sum[i+1] = element_sum[i] + element_weight[i] * inv_totweight; + + /* Finally assign elements to particles */ + 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]; + particle_element[p] = distribute_binary_search(element_sum, totelem, pos); + particle_element[p] = MIN2(totelem-1, particle_element[p]); + jitter_offset[particle_element[p]] = pos; + } + } + else { + double step, pos; + + step= (totpart < 2) ? 0.5 : 1.0/(double)totpart; + pos= 1e-6; /* tiny offset to avoid zero weight face */ + i= 0; + + for (p=0; p<totpart; p++, pos+=step) { + while ((i < totelem) && (pos > (double)element_sum[i + 1])) + i++; + + particle_element[p] = MIN2(totelem-1, i); + + /* avoid zero weight face */ + if (p == totpart-1 && element_weight[particle_element[p]] == 0.0f) + particle_element[p] = particle_element[p-1]; + + jitter_offset[particle_element[p]] = pos; + } + } + + MEM_freeN(element_sum); + + /* For hair, sort by origindex (allows optimization's in rendering), */ + /* however with virtual parents the children need to be in random order. */ + if (part->type == PART_HAIR && !(part->childtype==PART_CHILD_FACES && part->parents!=0.0f)) { + int *orig_index = NULL; + + if (from == PART_FROM_VERT) { + if (dm->numVertData) + orig_index = dm->getVertDataArray(dm, CD_ORIGINDEX); + } + else { + if (dm->numTessFaceData) + orig_index = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + } + + if (orig_index) { + BLI_qsort_r(particle_element, totpart, sizeof(int), distribute_compare_orig_index, orig_index); + } + } + + /* Create jittering if needed */ + if (distr==PART_DISTR_JIT && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) { + jitlevel= part->userjit; + + if (jitlevel == 0) { + jitlevel= totpart/totelem; + if (part->flag & PART_EDISTR) jitlevel*= 2; /* looks better in general, not very scietific */ + if (jitlevel<3) jitlevel= 3; + } + + jit= MEM_callocN((2+ jitlevel*2)*sizeof(float), "jit"); + + /* for small amounts of particles we use regular jitter since it looks + * a bit better, for larger amounts we switch to hammersley sequence + * because it is much faster */ + if (jitlevel < 25) + init_mv_jit(jit, jitlevel, psys->seed, part->jitfac); + else + hammersley_create(jit, jitlevel+1, psys->seed, part->jitfac); + BLI_array_randomize(jit, 2*sizeof(float), jitlevel, psys->seed); /* for custom jit or even distribution */ + } + + /* Setup things for threaded distribution */ + ctx->tree= tree; + ctx->seams= seams; + ctx->totseam= totseam; + ctx->sim.psys= psys; + ctx->index= particle_element; + ctx->jit= jit; + ctx->jitlevel= jitlevel; + ctx->jitoff= jitter_offset; + ctx->weight= element_weight; + ctx->maxweight= maxweight; + ctx->cfrom= cfrom; + ctx->distr= distr; + ctx->dm= dm; + ctx->tpars= tpars; + + if (children) { + totpart= psys_render_simplify_distribution(ctx, totpart); + alloc_child_particles(psys, totpart); + } + + return 1; +} + +static void psys_task_init_distribute(ParticleTask *task, ParticleSimulationData *sim) +{ + /* init random number generator */ + int seed = 31415926 + sim->psys->seed; + + task->rng = BLI_rng_new(seed); +} + +static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) +{ + TaskScheduler *task_scheduler; + TaskPool *task_pool; + ParticleThreadContext ctx; + ParticleTask *tasks; + DerivedMesh *finaldm = sim->psmd->dm; + int i, totpart, numtasks; + + /* create a task pool for distribution tasks */ + if (!psys_thread_context_init_distribute(&ctx, sim, from)) + return; + + task_scheduler = BLI_task_scheduler_get(); + task_pool = BLI_task_pool_create(task_scheduler, &ctx); + + totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart); + psys_tasks_create(&ctx, totpart, &tasks, &numtasks); + for (i = 0; i < numtasks; ++i) { + ParticleTask *task = &tasks[i]; + + psys_task_init_distribute(task, sim); + if (from == PART_FROM_CHILD) + BLI_task_pool_push(task_pool, exec_distribute_child, task, false, TASK_PRIORITY_LOW); + else + BLI_task_pool_push(task_pool, exec_distribute_parent, task, false, TASK_PRIORITY_LOW); + } + BLI_task_pool_work_and_wait(task_pool); + + BLI_task_pool_free(task_pool); + + psys_calc_dmcache(sim->ob, finaldm, sim->psys); + + if (ctx.dm != finaldm) + ctx.dm->release(ctx.dm); + + psys_tasks_free(tasks, numtasks); + + psys_thread_context_free(&ctx); +} + +/* ready for future use, to emit particles without geometry */ +static void distribute_particles_on_shape(ParticleSimulationData *sim, int UNUSED(from)) +{ + distribute_invalid(sim->scene, sim->psys, 0); + + fprintf(stderr,"Shape emission not yet possible!\n"); +} + +void distribute_particles(ParticleSimulationData *sim, int from) +{ + PARTICLE_PSMD; + int distr_error=0; + + if (psmd) { + if (psmd->dm) + distribute_particles_on_dm(sim, from); + else + distr_error=1; + } + else + distribute_particles_on_shape(sim, from); + + if (distr_error) { + distribute_invalid(sim->scene, sim->psys, from); + + fprintf(stderr,"Particle distribution error!\n"); + } +} + +/* ======== Simplify ======== */ + +static float psys_render_viewport_falloff(double rate, float dist, float width) +{ + return pow(rate, dist / width); +} + +static float psys_render_projected_area(ParticleSystem *psys, const float center[3], float area, double vprate, float *viewport) +{ + ParticleRenderData *data = psys->renderdata; + float co[4], view[3], ortho1[3], ortho2[3], w, dx, dy, radius; + + /* transform to view space */ + copy_v3_v3(co, center); + co[3] = 1.0f; + mul_m4_v4(data->viewmat, co); + + /* compute two vectors orthogonal to view vector */ + normalize_v3_v3(view, co); + ortho_basis_v3v3_v3(ortho1, ortho2, view); + + /* compute on screen minification */ + w = co[2] * data->winmat[2][3] + data->winmat[3][3]; + dx = data->winx * ortho2[0] * data->winmat[0][0]; + dy = data->winy * ortho2[1] * data->winmat[1][1]; + w = sqrtf(dx * dx + dy * dy) / w; + + /* w squared because we are working with area */ + area = area * w * w; + + /* viewport of the screen test */ + + /* project point on screen */ + mul_m4_v4(data->winmat, co); + if (co[3] != 0.0f) { + co[0] = 0.5f * data->winx * (1.0f + co[0] / co[3]); + co[1] = 0.5f * data->winy * (1.0f + co[1] / co[3]); + } + + /* screen space radius */ + radius = sqrtf(area / (float)M_PI); + + /* make smaller using fallof once over screen edge */ + *viewport = 1.0f; + + if (co[0] + radius < 0.0f) + *viewport *= psys_render_viewport_falloff(vprate, -(co[0] + radius), data->winx); + else if (co[0] - radius > data->winx) + *viewport *= psys_render_viewport_falloff(vprate, (co[0] - radius) - data->winx, data->winx); + + if (co[1] + radius < 0.0f) + *viewport *= psys_render_viewport_falloff(vprate, -(co[1] + radius), data->winy); + else if (co[1] - radius > data->winy) + *viewport *= psys_render_viewport_falloff(vprate, (co[1] - radius) - data->winy, data->winy); + + return area; +} + +/* BMESH_TODO, for orig face data, we need to use MPoly */ +static int psys_render_simplify_distribution(ParticleThreadContext *ctx, int tot) +{ + DerivedMesh *dm = ctx->dm; + Mesh *me = (Mesh *)(ctx->sim.ob->data); + MFace *mf, *mface; + MVert *mvert; + ParticleRenderData *data; + ParticleRenderElem *elems, *elem; + ParticleSettings *part = ctx->sim.psys->part; + float *facearea, (*facecenter)[3], size[3], fac, powrate, scaleclamp; + float co1[3], co2[3], co3[3], co4[3], lambda, arearatio, t, area, viewport; + double vprate; + int *facetotvert; + int a, b, totorigface, totface, newtot, skipped; + + /* double lookup */ + const int *index_mf_to_mpoly; + const int *index_mp_to_orig; + + if (part->ren_as != PART_DRAW_PATH || !(part->draw & PART_DRAW_REN_STRAND)) + return tot; + if (!ctx->sim.psys->renderdata) + return tot; + + data = ctx->sim.psys->renderdata; + if (data->timeoffset) + return 0; + if (!(part->simplify_flag & PART_SIMPLIFY_ENABLE)) + return tot; + + mvert = dm->getVertArray(dm); + mface = dm->getTessFaceArray(dm); + totface = dm->getNumTessFaces(dm); + totorigface = me->totpoly; + + if (totface == 0 || totorigface == 0) + return tot; + + index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); + if (index_mf_to_mpoly == NULL) { + index_mp_to_orig = NULL; + } + + facearea = MEM_callocN(sizeof(float) * totorigface, "SimplifyFaceArea"); + facecenter = MEM_callocN(sizeof(float[3]) * totorigface, "SimplifyFaceCenter"); + facetotvert = MEM_callocN(sizeof(int) * totorigface, "SimplifyFaceArea"); + elems = MEM_callocN(sizeof(ParticleRenderElem) * totorigface, "SimplifyFaceElem"); + + if (data->elems) + MEM_freeN(data->elems); + + data->do_simplify = true; + data->elems = elems; + data->index_mf_to_mpoly = index_mf_to_mpoly; + data->index_mp_to_orig = index_mp_to_orig; + + /* compute number of children per original face */ + for (a = 0; a < tot; a++) { + b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, ctx->index[a]) : ctx->index[a]; + if (b != ORIGINDEX_NONE) { + elems[b].totchild++; + } + } + + /* compute areas and centers of original faces */ + for (mf = mface, a = 0; a < totface; a++, mf++) { + b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a) : a; + + if (b != ORIGINDEX_NONE) { + copy_v3_v3(co1, mvert[mf->v1].co); + copy_v3_v3(co2, mvert[mf->v2].co); + copy_v3_v3(co3, mvert[mf->v3].co); + + add_v3_v3(facecenter[b], co1); + add_v3_v3(facecenter[b], co2); + add_v3_v3(facecenter[b], co3); + + if (mf->v4) { + copy_v3_v3(co4, mvert[mf->v4].co); + add_v3_v3(facecenter[b], co4); + facearea[b] += area_quad_v3(co1, co2, co3, co4); + facetotvert[b] += 4; + } + else { + facearea[b] += area_tri_v3(co1, co2, co3); + facetotvert[b] += 3; + } + } + } + + for (a = 0; a < totorigface; a++) + if (facetotvert[a] > 0) + mul_v3_fl(facecenter[a], 1.0f / facetotvert[a]); + + /* for conversion from BU area / pixel area to reference screen size */ + BKE_mesh_texspace_get(me, 0, 0, size); + fac = ((size[0] + size[1] + size[2]) / 3.0f) / part->simplify_refsize; + fac = fac * fac; + + powrate = log(0.5f) / log(part->simplify_rate * 0.5f); + if (part->simplify_flag & PART_SIMPLIFY_VIEWPORT) + vprate = pow(1.0f - part->simplify_viewport, 5.0); + else + vprate = 1.0; + + /* set simplification parameters per original face */ + for (a = 0, elem = elems; a < totorigface; a++, elem++) { + area = psys_render_projected_area(ctx->sim.psys, facecenter[a], facearea[a], vprate, &viewport); + arearatio = fac * area / facearea[a]; + + if ((arearatio < 1.0f || viewport < 1.0f) && elem->totchild) { + /* lambda is percentage of elements to keep */ + lambda = (arearatio < 1.0f) ? powf(arearatio, powrate) : 1.0f; + lambda *= viewport; + + lambda = MAX2(lambda, 1.0f / elem->totchild); + + /* compute transition region */ + t = part->simplify_transition; + elem->t = (lambda - t < 0.0f) ? lambda : (lambda + t > 1.0f) ? 1.0f - lambda : t; + elem->reduce = 1; + + /* scale at end and beginning of the transition region */ + elem->scalemax = (lambda + t < 1.0f) ? 1.0f / lambda : 1.0f / (1.0f - elem->t * elem->t / t); + elem->scalemin = (lambda + t < 1.0f) ? 0.0f : elem->scalemax * (1.0f - elem->t / t); + + elem->scalemin = sqrtf(elem->scalemin); + elem->scalemax = sqrtf(elem->scalemax); + + /* clamp scaling */ + scaleclamp = (float)min_ii(elem->totchild, 10); + elem->scalemin = MIN2(scaleclamp, elem->scalemin); + elem->scalemax = MIN2(scaleclamp, elem->scalemax); + + /* extend lambda to include transition */ + lambda = lambda + elem->t; + if (lambda > 1.0f) + lambda = 1.0f; + } + else { + lambda = arearatio; + + elem->scalemax = 1.0f; //sqrt(lambda); + elem->scalemin = 1.0f; //sqrt(lambda); + elem->reduce = 0; + } + + elem->lambda = lambda; + elem->scalemin = sqrtf(elem->scalemin); + elem->scalemax = sqrtf(elem->scalemax); + elem->curchild = 0; + } + + MEM_freeN(facearea); + MEM_freeN(facecenter); + MEM_freeN(facetotvert); + + /* move indices and set random number skipping */ + ctx->skip = MEM_callocN(sizeof(int) * tot, "SimplificationSkip"); + + skipped = 0; + for (a = 0, newtot = 0; a < tot; a++) { + b = (index_mf_to_mpoly) ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, ctx->index[a]) : ctx->index[a]; + + if (b != ORIGINDEX_NONE) { + if (elems[b].curchild++ < ceil(elems[b].lambda * elems[b].totchild)) { + ctx->index[newtot] = ctx->index[a]; + ctx->skip[newtot] = skipped; + skipped = 0; + newtot++; + } + else skipped++; + } + else skipped++; + } + + for (a = 0, elem = elems; a < totorigface; a++, elem++) + elem->curchild = 0; + + return newtot; +} diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 2a71cf2780f..dcae5972c61 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -69,6 +69,7 @@ #include "BLI_kdtree.h" #include "BLI_kdopbvh.h" #include "BLI_sort.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_linklist.h" @@ -281,7 +282,7 @@ static void realloc_particles(ParticleSimulationData *sim, int new_totpart) } } -static int get_psys_child_number(struct Scene *scene, ParticleSystem *psys) +int psys_get_child_number(Scene *scene, ParticleSystem *psys) { int nbr; @@ -296,30 +297,9 @@ static int get_psys_child_number(struct Scene *scene, ParticleSystem *psys) return get_render_child_particle_number(&scene->r, nbr); } -static int get_psys_tot_child(struct Scene *scene, ParticleSystem *psys) +int psys_get_tot_child(Scene *scene, ParticleSystem *psys) { - return psys->totpart*get_psys_child_number(scene, psys); -} - -static void alloc_child_particles(ParticleSystem *psys, int tot) -{ - if (psys->child) { - /* only re-allocate if we have to */ - if (psys->part->childtype && psys->totchild == tot) { - memset(psys->child, 0, tot*sizeof(ChildParticle)); - return; - } - - MEM_freeN(psys->child); - psys->child=NULL; - psys->totchild=0; - } - - if (psys->part->childtype) { - psys->totchild= tot; - if (psys->totchild) - psys->child= MEM_callocN(psys->totchild*sizeof(ChildParticle), "child_particles"); - } + return psys->totpart*psys_get_child_number(scene, psys); } /************************************************/ @@ -449,1053 +429,60 @@ void psys_calc_dmcache(Object *ob, DerivedMesh *dm, ParticleSystem *psys) } } -static void distribute_simple_children(Scene *scene, Object *ob, DerivedMesh *finaldm, ParticleSystem *psys) -{ - ChildParticle *cpa = NULL; - int i, p; - int child_nbr= get_psys_child_number(scene, psys); - int totpart= get_psys_tot_child(scene, psys); - - alloc_child_particles(psys, totpart); - - cpa = psys->child; - for (i=0; i<child_nbr; i++) { - for (p=0; p<psys->totpart; p++,cpa++) { - float length=2.0; - cpa->parent=p; - - /* create even spherical distribution inside unit sphere */ - while (length>=1.0f) { - cpa->fuv[0]=2.0f*BLI_frand()-1.0f; - cpa->fuv[1]=2.0f*BLI_frand()-1.0f; - cpa->fuv[2]=2.0f*BLI_frand()-1.0f; - length=len_v3(cpa->fuv); - } - - cpa->num=-1; - } - } - /* dmcache must be updated for parent particles if children from faces is used */ - psys_calc_dmcache(ob, finaldm, psys); -} -static void distribute_grid(DerivedMesh *dm, ParticleSystem *psys) -{ - ParticleData *pa=NULL; - float min[3], max[3], delta[3], d; - MVert *mv, *mvert = dm->getVertDataArray(dm,0); - int totvert=dm->getNumVerts(dm), from=psys->part->from; - int i, j, k, p, res=psys->part->grid_res, size[3], axis; - - /* find bounding box of dm */ - if (totvert > 0) { - mv=mvert; - copy_v3_v3(min, mv->co); - copy_v3_v3(max, mv->co); - mv++; - for (i = 1; i < totvert; i++, mv++) { - minmax_v3v3_v3(min, max, mv->co); - } - } - else { - zero_v3(min); - zero_v3(max); - } - - sub_v3_v3v3(delta, max, min); - - /* determine major axis */ - axis = axis_dominant_v3_single(delta); - - d = delta[axis]/(float)res; - - size[axis] = res; - size[(axis+1)%3] = (int)ceil(delta[(axis+1)%3]/d); - size[(axis+2)%3] = (int)ceil(delta[(axis+2)%3]/d); - - /* float errors grrr.. */ - size[(axis+1)%3] = MIN2(size[(axis+1)%3],res); - size[(axis+2)%3] = MIN2(size[(axis+2)%3],res); - - size[0] = MAX2(size[0], 1); - size[1] = MAX2(size[1], 1); - size[2] = MAX2(size[2], 1); - - /* no full offset for flat/thin objects */ - min[0]+= d < delta[0] ? d/2.f : delta[0]/2.f; - min[1]+= d < delta[1] ? d/2.f : delta[1]/2.f; - min[2]+= d < delta[2] ? d/2.f : delta[2]/2.f; - - for (i=0,p=0,pa=psys->particles; i<res; i++) { - for (j=0; j<res; j++) { - for (k=0; k<res; k++,p++,pa++) { - pa->fuv[0] = min[0] + (float)i*d; - pa->fuv[1] = min[1] + (float)j*d; - pa->fuv[2] = min[2] + (float)k*d; - pa->flag |= PARS_UNEXIST; - pa->hair_index = 0; /* abused in volume calculation */ - } - } - } - - /* enable particles near verts/edges/faces/inside surface */ - if (from==PART_FROM_VERT) { - float vec[3]; - - pa=psys->particles; - - min[0] -= d/2.0f; - min[1] -= d/2.0f; - min[2] -= d/2.0f; - - for (i=0,mv=mvert; i<totvert; i++,mv++) { - sub_v3_v3v3(vec,mv->co,min); - vec[0]/=delta[0]; - vec[1]/=delta[1]; - vec[2]/=delta[2]; - pa[((int)(vec[0] * (size[0] - 1)) * res + - (int)(vec[1] * (size[1] - 1))) * res + - (int)(vec[2] * (size[2] - 1))].flag &= ~PARS_UNEXIST; - } - } - else if (ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) { - float co1[3], co2[3]; - - MFace *mface= NULL, *mface_array; - float v1[3], v2[3], v3[3], v4[4], lambda; - int a, a1, a2, a0mul, a1mul, a2mul, totface; - int amax= from==PART_FROM_FACE ? 3 : 1; - - totface=dm->getNumTessFaces(dm); - mface=mface_array=dm->getTessFaceDataArray(dm,CD_MFACE); - - for (a=0; a<amax; a++) { - if (a==0) { a0mul=res*res; a1mul=res; a2mul=1; } - else if (a==1) { a0mul=res; a1mul=1; a2mul=res*res; } - else { a0mul=1; a1mul=res*res; a2mul=res; } - - for (a1=0; a1<size[(a+1)%3]; a1++) { - for (a2=0; a2<size[(a+2)%3]; a2++) { - mface= mface_array; - - pa = psys->particles + a1*a1mul + a2*a2mul; - copy_v3_v3(co1, pa->fuv); - co1[a] -= d < delta[a] ? d/2.f : delta[a]/2.f; - copy_v3_v3(co2, co1); - co2[a] += delta[a] + 0.001f*d; - co1[a] -= 0.001f*d; - - /* lets intersect the faces */ - for (i=0; i<totface; i++,mface++) { - copy_v3_v3(v1, mvert[mface->v1].co); - copy_v3_v3(v2, mvert[mface->v2].co); - copy_v3_v3(v3, mvert[mface->v3].co); - - if (isect_axial_line_tri_v3(a, co1, co2, v2, v3, v1, &lambda)) { - if (from==PART_FROM_FACE) - (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; - else /* store number of intersections */ - (pa+(int)(lambda*size[a])*a0mul)->hair_index++; - } - else if (mface->v4) { - copy_v3_v3(v4, mvert[mface->v4].co); - - if (isect_axial_line_tri_v3(a, co1, co2, v4, v1, v3, &lambda)) { - if (from==PART_FROM_FACE) - (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; - else - (pa+(int)(lambda*size[a])*a0mul)->hair_index++; - } - } - } - - if (from==PART_FROM_VOLUME) { - int in=pa->hair_index%2; - if (in) pa->hair_index++; - for (i=0; i<size[0]; i++) { - if (in || (pa+i*a0mul)->hair_index%2) - (pa+i*a0mul)->flag &= ~PARS_UNEXIST; - /* odd intersections == in->out / out->in */ - /* even intersections -> in stays same */ - in=(in + (pa+i*a0mul)->hair_index) % 2; - } - } - } - } - } - } - - if (psys->part->flag & PART_GRID_HEXAGONAL) { - for (i=0,p=0,pa=psys->particles; i<res; i++) { - for (j=0; j<res; j++) { - for (k=0; k<res; k++,p++,pa++) { - if (j%2) - pa->fuv[0] += d/2.f; - - if (k%2) { - pa->fuv[0] += d/2.f; - pa->fuv[1] += d/2.f; - } - } - } - } - } - - if (psys->part->flag & PART_GRID_INVERT) { - for (i=0; i<size[0]; i++) { - for (j=0; j<size[1]; j++) { - pa=psys->particles + res*(i*res + j); - for (k=0; k<size[2]; k++, pa++) { - pa->flag ^= PARS_UNEXIST; - } - } - } - } - - if (psys->part->grid_rand > 0.f) { - float rfac = d * psys->part->grid_rand; - for (p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { - if (pa->flag & PARS_UNEXIST) - continue; - - pa->fuv[0] += rfac * (psys_frand(psys, p + 31) - 0.5f); - pa->fuv[1] += rfac * (psys_frand(psys, p + 32) - 0.5f); - pa->fuv[2] += rfac * (psys_frand(psys, p + 33) - 0.5f); - } - } -} - -/* modified copy from rayshade.c */ -static void hammersley_create(float *out, int n, int seed, float amount) -{ - RNG *rng; - double p, t, offs[2]; - int k, kk; - - rng = BLI_rng_new(31415926 + n + seed); - offs[0] = BLI_rng_get_double(rng) + (double)amount; - offs[1] = BLI_rng_get_double(rng) + (double)amount; - BLI_rng_free(rng); - - for (k = 0; k < n; k++) { - t = 0; - for (p = 0.5, kk = k; kk; p *= 0.5, kk >>= 1) - if (kk & 1) /* kk mod 2 = 1 */ - t += p; - - out[2*k + 0] = fmod((double)k/(double)n + offs[0], 1.0); - out[2*k + 1] = fmod(t + offs[1], 1.0); - } -} - -/* almost exact copy of BLI_jitter_init */ -static void init_mv_jit(float *jit, int num, int seed2, float amount) -{ - RNG *rng; - float *jit2, x, rad1, rad2, rad3; - int i, num2; - - if (num==0) return; - - rad1= (float)(1.0f/sqrtf((float)num)); - rad2= (float)(1.0f/((float)num)); - rad3= (float)sqrtf((float)num)/((float)num); - - rng = BLI_rng_new(31415926 + num + seed2); - x= 0; - num2 = 2 * num; - for (i=0; i<num2; i+=2) { - - jit[i] = x + amount*rad1*(0.5f - BLI_rng_get_float(rng)); - jit[i+1] = i/(2.0f*num) + amount*rad1*(0.5f - BLI_rng_get_float(rng)); - - jit[i]-= (float)floor(jit[i]); - jit[i+1]-= (float)floor(jit[i+1]); - - x+= rad3; - x -= (float)floor(x); - } - - jit2= MEM_mallocN(12 + 2*sizeof(float)*num, "initjit"); - - for (i=0 ; i<4 ; i++) { - BLI_jitterate1((float (*)[2])jit, (float (*)[2])jit2, num, rad1); - BLI_jitterate1((float (*)[2])jit, (float (*)[2])jit2, num, rad1); - BLI_jitterate2((float (*)[2])jit, (float (*)[2])jit2, num, rad2); - } - MEM_freeN(jit2); - BLI_rng_free(rng); -} - -static void psys_uv_to_w(float u, float v, int quad, float *w) -{ - float vert[4][3], co[3]; - - if (!quad) { - if (u+v > 1.0f) - v= 1.0f-v; - else - u= 1.0f-u; - } - - vert[0][0] = 0.0f; vert[0][1] = 0.0f; vert[0][2] = 0.0f; - vert[1][0] = 1.0f; vert[1][1] = 0.0f; vert[1][2] = 0.0f; - vert[2][0] = 1.0f; vert[2][1] = 1.0f; vert[2][2] = 0.0f; - - co[0] = u; - co[1] = v; - co[2] = 0.0f; - - if (quad) { - vert[3][0] = 0.0f; vert[3][1] = 1.0f; vert[3][2] = 0.0f; - interp_weights_poly_v3( w,vert, 4, co); - } - else { - interp_weights_poly_v3( w,vert, 3, co); - w[3] = 0.0f; - } -} - -/* 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; - - if (value == 0.f) - return 0; - - while (low <= high) { - mid= (low + high)/2; - - if (sum[mid] < value && value <= sum[mid+1]) - return mid; - - if (sum[mid] >= value) - high= mid - 1; - else if (sum[mid] < value) - low= mid + 1; - else - return mid; - } - - return low; -} - -/* the max number if calls to rng_* funcs within psys_thread_distribute_particle - * be sure to keep up to date if this changes */ -#define PSYS_RND_DIST_SKIP 2 - -/* note: this function must be thread safe, for from == PART_FROM_CHILD */ -#define ONLY_WORKING_WITH_PA_VERTS 0 -static void distribute_threads_exec(ParticleThread *thread, ParticleData *pa, ChildParticle *cpa, int p) -{ - ParticleThreadContext *ctx= thread->ctx; - Object *ob= ctx->sim.ob; - DerivedMesh *dm= ctx->dm; - float *v1, *v2, *v3, *v4, nor[3], orco1[3], co1[3], co2[3], nor1[3]; - float cur_d, min_d, randu, randv; - int from= ctx->from; - int cfrom= ctx->cfrom; - int distr= ctx->distr; - int i, intersect, tot; - int rng_skip_tot= PSYS_RND_DIST_SKIP; /* count how many rng_* calls wont need skipping */ - - if (from == PART_FROM_VERT) { - /* TODO_PARTICLE - use original index */ - pa->num= ctx->index[p]; - pa->fuv[0] = 1.0f; - pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0; - -#if ONLY_WORKING_WITH_PA_VERTS - if (ctx->tree) { - KDTreeNearest ptn[3]; - int w, maxw; - - psys_particle_on_dm(ctx->dm,from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0,orco1,0); - BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco1, 1, 1); - maxw = BLI_kdtree_find_nearest_n(ctx->tree,orco1,ptn,3); - - for (w=0; w<maxw; w++) { - pa->verts[w]=ptn->num; - } - } -#endif - } - else if (from == PART_FROM_FACE || from == PART_FROM_VOLUME) { - MFace *mface; - - pa->num = i = ctx->index[p]; - mface = dm->getTessFaceData(dm,i,CD_MFACE); - - switch (distr) { - case PART_DISTR_JIT: - if (ctx->jitlevel == 1) { - if (mface->v4) - psys_uv_to_w(0.5f, 0.5f, mface->v4, pa->fuv); - else - psys_uv_to_w(1.0f / 3.0f, 1.0f / 3.0f, mface->v4, pa->fuv); - } - else { - ctx->jitoff[i] = fmod(ctx->jitoff[i],(float)ctx->jitlevel); - if (!isnan(ctx->jitoff[i])) { - psys_uv_to_w(ctx->jit[2*(int)ctx->jitoff[i]], ctx->jit[2*(int)ctx->jitoff[i]+1], mface->v4, pa->fuv); - ctx->jitoff[i]++; - } - } - break; - case PART_DISTR_RAND: - randu= BLI_rng_get_float(thread->rng); - randv= BLI_rng_get_float(thread->rng); - rng_skip_tot -= 2; - - psys_uv_to_w(randu, randv, mface->v4, pa->fuv); - break; - } - pa->foffset= 0.0f; - - /* experimental */ - if (from==PART_FROM_VOLUME) { - MVert *mvert=dm->getVertDataArray(dm,CD_MVERT); - - tot=dm->getNumTessFaces(dm); - - psys_interpolate_face(mvert,mface,0,0,pa->fuv,co1,nor,0,0,0,0); - - normalize_v3(nor); - mul_v3_fl(nor,-100.0); - - add_v3_v3v3(co2,co1,nor); - - min_d=2.0; - intersect=0; - - for (i=0,mface=dm->getTessFaceDataArray(dm,CD_MFACE); i<tot; i++,mface++) { - if (i==pa->num) continue; - - v1=mvert[mface->v1].co; - v2=mvert[mface->v2].co; - v3=mvert[mface->v3].co; - - if (isect_line_tri_v3(co1, co2, v2, v3, v1, &cur_d, 0)) { - if (cur_d<min_d) { - min_d=cur_d; - pa->foffset=cur_d*50.0f; /* to the middle of volume */ - intersect=1; - } - } - if (mface->v4) { - v4=mvert[mface->v4].co; - - if (isect_line_tri_v3(co1, co2, v4, v1, v3, &cur_d, 0)) { - if (cur_d<min_d) { - min_d=cur_d; - pa->foffset=cur_d*50.0f; /* to the middle of volume */ - intersect=1; - } - } - } - } - if (intersect==0) - pa->foffset=0.0; - else { - switch (distr) { - case PART_DISTR_JIT: - pa->foffset *= ctx->jit[p % (2 * ctx->jitlevel)]; - break; - case PART_DISTR_RAND: - pa->foffset *= BLI_frand(); - break; - } - } - } - } - else if (from == PART_FROM_CHILD) { - MFace *mf; - - if (ctx->index[p] < 0) { - cpa->num=0; - cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3]=0.0f; - cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; - return; - } - - mf= dm->getTessFaceData(dm, ctx->index[p], CD_MFACE); - - randu= BLI_rng_get_float(thread->rng); - randv= BLI_rng_get_float(thread->rng); - rng_skip_tot -= 2; - - psys_uv_to_w(randu, randv, mf->v4, cpa->fuv); - - cpa->num = ctx->index[p]; - - if (ctx->tree) { - KDTreeNearest ptn[10]; - int w,maxw;//, do_seams; - float maxd /*, mind,dd */, totw= 0.0f; - int parent[10]; - float pweight[10]; - - psys_particle_on_dm(dm,cfrom,cpa->num,DMCACHE_ISCHILD,cpa->fuv,cpa->foffset,co1,nor1,NULL,NULL,orco1,NULL); - BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco1, 1, 1); - maxw = BLI_kdtree_find_nearest_n(ctx->tree,orco1,ptn,3); - - maxd=ptn[maxw-1].dist; - /* mind=ptn[0].dist; */ /* UNUSED */ - - /* the weights here could be done better */ - for (w=0; w<maxw; w++) { - parent[w]=ptn[w].index; - pweight[w]=(float)pow(2.0,(double)(-6.0f*ptn[w].dist/maxd)); - } - for (;w<10; w++) { - parent[w]=-1; - pweight[w]=0.0f; - } - - for (w=0,i=0; w<maxw && i<4; w++) { - if (parent[w]>=0) { - cpa->pa[i]=parent[w]; - cpa->w[i]=pweight[w]; - totw+=pweight[w]; - i++; - } - } - for (;i<4; i++) { - cpa->pa[i]=-1; - cpa->w[i]=0.0f; - } - - if (totw>0.0f) for (w=0; w<4; w++) - cpa->w[w]/=totw; - - cpa->parent=cpa->pa[0]; - } - } - - if (rng_skip_tot > 0) /* should never be below zero */ - BLI_rng_skip(thread->rng, rng_skip_tot); -} - -static void *distribute_threads_exec_cb(void *data) +/* threaded child particle distribution and path caching */ +void psys_thread_context_init(ParticleThreadContext *ctx, ParticleSimulationData *sim) { - ParticleThread *thread= (ParticleThread*)data; - ParticleSystem *psys= thread->ctx->sim.psys; - ParticleData *pa; - ChildParticle *cpa; - int p, totpart; - - if (thread->ctx->from == PART_FROM_CHILD) { - totpart= psys->totchild; - cpa= psys->child; - - for (p=0; p<totpart; p++, cpa++) { - if (thread->ctx->skip) /* simplification skip */ - BLI_rng_skip(thread->rng, PSYS_RND_DIST_SKIP * thread->ctx->skip[p]); - - if ((p+thread->num) % thread->tot == 0) - distribute_threads_exec(thread, NULL, cpa, p); - else /* thread skip */ - BLI_rng_skip(thread->rng, PSYS_RND_DIST_SKIP); - } - } - else { - totpart= psys->totpart; - pa= psys->particles + thread->num; - for (p=thread->num; p<totpart; p+=thread->tot, pa+=thread->tot) - distribute_threads_exec(thread, pa, NULL, p); - } - - return 0; + memset(ctx, 0, sizeof(ParticleThreadContext)); + ctx->sim = *sim; + ctx->dm = ctx->sim.psmd->dm; + ctx->ma = give_current_material(sim->ob, sim->psys->part->omat); } -static int distribute_compare_orig_index(const void *p1, const void *p2, void *user_data) -{ - int *orig_index = (int *) user_data; - int index1 = orig_index[*(const int *)p1]; - int index2 = orig_index[*(const int *)p2]; - - if (index1 < index2) - return -1; - else if (index1 == index2) { - /* this pointer comparison appears to make qsort stable for glibc, - * and apparently on solaris too, makes the renders reproducible */ - if (p1 < p2) - return -1; - else if (p1 == p2) - return 0; - else - return 1; - } - else - return 1; -} +#define MAX_PARTICLES_PER_TASK 256 /* XXX arbitrary - maybe use at least number of points instead for better balancing? */ -static void distribute_invalid(Scene *scene, ParticleSystem *psys, int from) +BLI_INLINE int ceil_ii(int a, int b) { - if (from == PART_FROM_CHILD) { - ChildParticle *cpa; - int p, totchild = get_psys_tot_child(scene, psys); - - if (psys->child && totchild) { - for (p=0,cpa=psys->child; p<totchild; p++,cpa++) { - cpa->fuv[0]=cpa->fuv[1]=cpa->fuv[2]=cpa->fuv[3] = 0.0; - cpa->foffset= 0.0f; - cpa->parent=0; - cpa->pa[0]=cpa->pa[1]=cpa->pa[2]=cpa->pa[3]=0; - cpa->num= -1; - } - } - } - else { - PARTICLE_P; - LOOP_PARTICLES { - pa->fuv[0] = pa->fuv[1] = pa->fuv[2] = pa->fuv[3] = 0.0; - pa->foffset= 0.0f; - pa->num= -1; - } - } + return (a + b - 1) / b; } -/* Creates a distribution of coordinates on a DerivedMesh */ -/* This is to denote functionality that does not yet work with mesh - only derived mesh */ -static int distribute_threads_init_data(ParticleThread *threads, Scene *scene, DerivedMesh *finaldm, int from) +void psys_tasks_create(ParticleThreadContext *ctx, int totpart, ParticleTask **r_tasks, int *r_numtasks) { - ParticleThreadContext *ctx= threads[0].ctx; - Object *ob= ctx->sim.ob; - ParticleSystem *psys= ctx->sim.psys; - ParticleData *pa=0, *tpars= 0; - ParticleSettings *part; - ParticleSeam *seams= 0; - KDTree *tree=0; - DerivedMesh *dm= NULL; - float *jit= NULL; - int i, seed, p=0, totthread= threads[0].tot; - 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 cur, maxweight=0.0, tweight, totweight, inv_totweight, co[3], nor[3], orco[3]; - - if (ELEM(NULL, ob, psys, psys->part)) - return 0; - - part=psys->part; - totpart=psys->totpart; - if (totpart==0) - return 0; - - if (!finaldm->deformedOnly && !finaldm->getTessFaceDataArray(finaldm, CD_ORIGINDEX)) { - printf("Can't create particles with the current modifier stack, disable destructive modifiers\n"); -// XXX error("Can't paint with the current modifier stack, disable destructive modifiers"); - return 0; - } - - /* First handle special cases */ - if (from == PART_FROM_CHILD) { - /* Simple children */ - if (part->childtype != PART_CHILD_FACES) { - BLI_srandom(31415926 + psys->seed + psys->child_seed); - distribute_simple_children(scene, ob, finaldm, psys); - return 0; - } - } - else { - /* Grid distribution */ - if (part->distr==PART_DISTR_GRID && from != PART_FROM_VERT) { - BLI_srandom(31415926 + psys->seed); - dm= CDDM_from_mesh((Mesh*)ob->data); - DM_ensure_tessface(dm); - distribute_grid(dm,psys); - dm->release(dm); - return 0; - } - } + ParticleTask *tasks; + int numtasks = ceil_ii(totpart, MAX_PARTICLES_PER_TASK); + float particles_per_task = (float)totpart / (float)numtasks, p, pnext; + int i; - /* Create trees and original coordinates if needed */ - if (from == PART_FROM_CHILD) { - distr=PART_DISTR_RAND; - BLI_srandom(31415926 + psys->seed + psys->child_seed); - dm= finaldm; - - /* BMESH ONLY */ - DM_ensure_tessface(dm); - - children=1; - - tree=BLI_kdtree_new(totpart); - - for (p=0,pa=psys->particles; p<totpart; p++,pa++) { - psys_particle_on_dm(dm,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,orco,NULL); - BKE_mesh_orco_verts_transform((Mesh*)ob->data, &orco, 1, 1); - BLI_kdtree_insert(tree, p, orco); - } - - BLI_kdtree_balance(tree); - - totpart = get_psys_tot_child(scene, psys); - cfrom = from = PART_FROM_FACE; - } - else { - distr = part->distr; - BLI_srandom(31415926 + psys->seed); - - if (psys->part->use_modifier_stack) - dm = finaldm; - else - dm= CDDM_from_mesh((Mesh*)ob->data); - - /* BMESH ONLY, for verts we don't care about tessfaces */ - if (from != PART_FROM_VERT) { - DM_ensure_tessface(dm); - } - - /* we need orco for consistent distributions */ - if (!CustomData_has_layer(&dm->vertData, CD_ORCO)) - DM_add_vert_layer(dm, CD_ORCO, CD_ASSIGN, BKE_mesh_orco_verts_get(ob)); - - if (from == PART_FROM_VERT) { - MVert *mv= dm->getVertDataArray(dm, CD_MVERT); - float (*orcodata)[3] = dm->getVertDataArray(dm, CD_ORCO); - int totvert = dm->getNumVerts(dm); - - tree=BLI_kdtree_new(totvert); - - for (p=0; p<totvert; p++) { - if (orcodata) { - copy_v3_v3(co,orcodata[p]); - BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co, 1, 1); - } - else - copy_v3_v3(co,mv[p].co); - BLI_kdtree_insert(tree, p, co); - } - - BLI_kdtree_balance(tree); - } - } - - /* Get total number of emission elements and allocate needed arrays */ - totelem = (from == PART_FROM_VERT) ? dm->getNumVerts(dm) : dm->getNumTessFaces(dm); - - if (totelem == 0) { - distribute_invalid(scene, psys, children ? PART_FROM_CHILD : 0); - - if (G.debug & G_DEBUG) - fprintf(stderr,"Particle distribution error: Nothing to emit from!\n"); - - if (dm != finaldm) dm->release(dm); - - BLI_kdtree_free(tree); - - return 0; - } - - element_weight = MEM_callocN(sizeof(float)*totelem, "particle_distribution_weights"); - particle_element= MEM_callocN(sizeof(int)*totpart, "particle_distribution_indexes"); - element_sum = MEM_callocN(sizeof(float)*(totelem+1), "particle_distribution_sum"); - jitter_offset = MEM_callocN(sizeof(float)*totelem, "particle_distribution_jitoff"); - - /* Calculate weights from face areas */ - if ((part->flag&PART_EDISTR || children) && from != PART_FROM_VERT) { - MVert *v1, *v2, *v3, *v4; - float totarea=0.f, co1[3], co2[3], co3[3], co4[3]; - float (*orcodata)[3]; - - orcodata= dm->getVertDataArray(dm, CD_ORCO); - - for (i=0; i<totelem; i++) { - MFace *mf=dm->getTessFaceData(dm,i,CD_MFACE); - - if (orcodata) { - copy_v3_v3(co1, orcodata[mf->v1]); - copy_v3_v3(co2, orcodata[mf->v2]); - copy_v3_v3(co3, orcodata[mf->v3]); - BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co1, 1, 1); - BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co2, 1, 1); - BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co3, 1, 1); - if (mf->v4) { - copy_v3_v3(co4, orcodata[mf->v4]); - BKE_mesh_orco_verts_transform((Mesh*)ob->data, &co4, 1, 1); - } - } - else { - v1= (MVert*)dm->getVertData(dm,mf->v1,CD_MVERT); - v2= (MVert*)dm->getVertData(dm,mf->v2,CD_MVERT); - v3= (MVert*)dm->getVertData(dm,mf->v3,CD_MVERT); - copy_v3_v3(co1, v1->co); - copy_v3_v3(co2, v2->co); - copy_v3_v3(co3, v3->co); - if (mf->v4) { - v4= (MVert*)dm->getVertData(dm,mf->v4,CD_MVERT); - copy_v3_v3(co4, v4->co); - } - } - - cur = mf->v4 ? area_quad_v3(co1, co2, co3, co4) : area_tri_v3(co1, co2, co3); - - if (cur > maxweight) - maxweight = cur; - - element_weight[i] = cur; - totarea += cur; - } - - for (i=0; i<totelem; i++) - element_weight[i] /= totarea; - - maxweight /= totarea; - } - else { - float min=1.0f/(float)(MIN2(totelem,totpart)); - for (i=0; i<totelem; i++) - element_weight[i]=min; - maxweight=min; - } - - /* Calculate weights from vgroup */ - vweight = psys_cache_vgroup(dm,psys,PSYS_VG_DENSITY); - - if (vweight) { - if (from==PART_FROM_VERT) { - for (i=0;i<totelem; i++) - element_weight[i]*=vweight[i]; - } - else { /* PART_FROM_FACE / PART_FROM_VOLUME */ - for (i=0;i<totelem; i++) { - MFace *mf=dm->getTessFaceData(dm,i,CD_MFACE); - tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3]; - - if (mf->v4) { - tweight += vweight[mf->v4]; - tweight /= 4.0f; - } - else { - tweight /= 3.0f; - } - - element_weight[i]*=tweight; - } - } - MEM_freeN(vweight); - } - - /* Calculate total weight of all elements */ - totweight= 0.0f; - for (i=0;i<totelem; i++) - totweight += element_weight[i]; - - inv_totweight = (totweight > 0.f ? 1.f/totweight : 0.f); - - /* Calculate cumulative weights */ - element_sum[0] = 0.0f; - for (i=0; i<totelem; i++) - element_sum[i+1] = element_sum[i] + element_weight[i] * inv_totweight; + tasks = MEM_callocN(sizeof(ParticleTask) * numtasks, "ParticleThread"); + *r_numtasks = numtasks; + *r_tasks = tasks; - /* Finally assign elements to particles */ - 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]; - particle_element[p] = distribute_binary_search(element_sum, totelem, pos); - particle_element[p] = MIN2(totelem-1, particle_element[p]); - jitter_offset[particle_element[p]] = pos; - } - } - else { - double step, pos; - - step= (totpart < 2) ? 0.5 : 1.0/(double)totpart; - pos= 1e-6; /* tiny offset to avoid zero weight face */ - i= 0; - - for (p=0; p<totpart; p++, pos+=step) { - while ((i < totelem) && (pos > (double)element_sum[i + 1])) - i++; - - particle_element[p] = MIN2(totelem-1, i); - - /* avoid zero weight face */ - if (p == totpart-1 && element_weight[particle_element[p]] == 0.0f) - particle_element[p] = particle_element[p-1]; - - jitter_offset[particle_element[p]] = pos; - } - } - - MEM_freeN(element_sum); - - /* For hair, sort by origindex (allows optimization's in rendering), */ - /* however with virtual parents the children need to be in random order. */ - if (part->type == PART_HAIR && !(part->childtype==PART_CHILD_FACES && part->parents!=0.0f)) { - int *orig_index = NULL; - - if (from == PART_FROM_VERT) { - if (dm->numVertData) - orig_index = dm->getVertDataArray(dm, CD_ORIGINDEX); - } - else { - if (dm->numTessFaceData) - orig_index = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); - } - - if (orig_index) { - BLI_qsort_r(particle_element, totpart, sizeof(int), distribute_compare_orig_index, orig_index); - } - } - - /* Create jittering if needed */ - if (distr==PART_DISTR_JIT && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) { - jitlevel= part->userjit; + p = 0.0f; + for (i = 0; i < numtasks; i++, p = pnext) { + pnext = p + particles_per_task; - if (jitlevel == 0) { - jitlevel= totpart/totelem; - if (part->flag & PART_EDISTR) jitlevel*= 2; /* looks better in general, not very scietific */ - if (jitlevel<3) jitlevel= 3; - } - - jit= MEM_callocN((2+ jitlevel*2)*sizeof(float), "jit"); - - /* for small amounts of particles we use regular jitter since it looks - * a bit better, for larger amounts we switch to hammersley sequence - * because it is much faster */ - if (jitlevel < 25) - init_mv_jit(jit, jitlevel, psys->seed, part->jitfac); - else - hammersley_create(jit, jitlevel+1, psys->seed, part->jitfac); - BLI_array_randomize(jit, 2*sizeof(float), jitlevel, psys->seed); /* for custom jit or even distribution */ - } - - /* Setup things for threaded distribution */ - ctx->tree= tree; - ctx->seams= seams; - ctx->totseam= totseam; - ctx->sim.psys= psys; - ctx->index= particle_element; - ctx->jit= jit; - ctx->jitlevel= jitlevel; - ctx->jitoff= jitter_offset; - ctx->weight= element_weight; - ctx->maxweight= maxweight; - ctx->from= (children) ? PART_FROM_CHILD : from; - ctx->cfrom= cfrom; - ctx->distr= distr; - ctx->dm= dm; - ctx->tpars= tpars; - - if (children) { - totpart= psys_render_simplify_distribution(ctx, totpart); - alloc_child_particles(psys, totpart); - } - - if (!children || psys->totchild < 10000) - totthread= 1; - - seed= 31415926 + ctx->sim.psys->seed; - for (i=0; i<totthread; i++) { - threads[i].rng= BLI_rng_new(seed); - threads[i].tot= totthread; + tasks[i].ctx = ctx; + tasks[i].begin = (int)p; + tasks[i].end = min_ii((int)pnext, totpart); } - - return 1; } -static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) +void psys_tasks_free(ParticleTask *tasks, int numtasks) { - DerivedMesh *finaldm = sim->psmd->dm; - ListBase threads; - ParticleThread *pthreads; - ParticleThreadContext *ctx; - int i, totthread; - - pthreads= psys_threads_create(sim); - - if (!distribute_threads_init_data(pthreads, sim->scene, finaldm, from)) { - psys_threads_free(pthreads); - return; - } - - totthread= pthreads[0].tot; - if (totthread > 1) { - BLI_init_threads(&threads, distribute_threads_exec_cb, totthread); - - for (i=0; i<totthread; i++) - BLI_insert_thread(&threads, &pthreads[i]); - - BLI_end_threads(&threads); - } - else - distribute_threads_exec_cb(&pthreads[0]); - - psys_calc_dmcache(sim->ob, finaldm, sim->psys); - - ctx= pthreads[0].ctx; - if (ctx->dm != finaldm) - ctx->dm->release(ctx->dm); - - psys_threads_free(pthreads); -} - -/* ready for future use, to emit particles without geometry */ -static void distribute_particles_on_shape(ParticleSimulationData *sim, int UNUSED(from)) -{ - distribute_invalid(sim->scene, sim->psys, 0); - - fprintf(stderr,"Shape emission not yet possible!\n"); -} - -static void distribute_particles(ParticleSimulationData *sim, int from) -{ - PARTICLE_PSMD; - int distr_error=0; - - if (psmd) { - if (psmd->dm) - distribute_particles_on_dm(sim, from); - else - distr_error=1; - } - else - distribute_particles_on_shape(sim, from); - - if (distr_error) { - distribute_invalid(sim->scene, sim->psys, from); - - fprintf(stderr,"Particle distribution error!\n"); - } -} - -/* threaded child particle distribution and path caching */ -ParticleThread *psys_threads_create(ParticleSimulationData *sim) -{ - ParticleThread *threads; - ParticleThreadContext *ctx; - int i, totthread = BKE_scene_num_threads(sim->scene); + int i; - threads= MEM_callocN(sizeof(ParticleThread)*totthread, "ParticleThread"); - ctx= MEM_callocN(sizeof(ParticleThreadContext), "ParticleThreadContext"); - - ctx->sim = *sim; - ctx->dm= ctx->sim.psmd->dm; - ctx->ma= give_current_material(sim->ob, sim->psys->part->omat); - - memset(threads, 0, sizeof(ParticleThread)*totthread); - - for (i=0; i<totthread; i++) { - threads[i].ctx= ctx; - threads[i].num= i; - threads[i].tot= totthread; + /* threads */ + for (i = 0; i < numtasks; ++i) { + if (tasks[i].rng) + BLI_rng_free(tasks[i].rng); + if (tasks[i].rng_path) + BLI_rng_free(tasks[i].rng_path); } - return threads; + MEM_freeN(tasks); } -void psys_threads_free(ParticleThread *threads) +void psys_thread_context_free(ParticleThreadContext *ctx) { - ParticleThreadContext *ctx= threads[0].ctx; - int i, totthread= threads[0].tot; - /* path caching */ if (ctx->vg_length) MEM_freeN(ctx->vg_length); @@ -1524,17 +511,6 @@ void psys_threads_free(ParticleThread *threads) if (ctx->seams) MEM_freeN(ctx->seams); //if (ctx->vertpart) MEM_freeN(ctx->vertpart); BLI_kdtree_free(ctx->tree); - - /* threads */ - for (i=0; i<totthread; i++) { - if (threads[i].rng) - BLI_rng_free(threads[i].rng); - if (threads[i].rng_path) - BLI_rng_free(threads[i].rng_path); - } - - MEM_freeN(ctx); - MEM_freeN(threads); } static void initialize_particle_texture(ParticleSimulationData *sim, ParticleData *pa, int p) @@ -3098,7 +2074,7 @@ static void basic_integrate(ParticleSimulationData *sim, int p, float dfra, floa tkey.time=pa->state.time; if (part->type != PART_HAIR) { - if (do_guides(sim->psys->effectors, &tkey, p, time)) { + if (do_guides(sim->psys->part, sim->psys->effectors, &tkey, p, time)) { copy_v3_v3(pa->state.co,tkey.co); /* guides don't produce valid velocity */ sub_v3_v3v3(pa->state.vel, tkey.co, pa->prev_state.co); @@ -3893,7 +2869,7 @@ static void psys_update_path_cache(ParticleSimulationData *sim, float cfra) Base *base; int distr=0, alloc=0, skip=0; - if ((psys->part->childtype && psys->totchild != get_psys_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET) + if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET) alloc=1; if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT))) @@ -3903,7 +2879,7 @@ static void psys_update_path_cache(ParticleSimulationData *sim, float cfra) if (alloc) realloc_particles(sim, sim->psys->totpart); - if (get_psys_tot_child(sim->scene, psys)) { + if (psys_get_tot_child(sim->scene, psys)) { /* don't generate children while computing the hair keys */ if (!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) { distribute_particles(sim, PART_FROM_CHILD); @@ -3964,125 +2940,235 @@ static void psys_update_path_cache(ParticleSimulationData *sim, float cfra) psys_free_path_cache(psys, NULL); } -static void do_hair_dynamics(ParticleSimulationData *sim) +static bool psys_hair_use_simulation(ParticleData *pa, float max_length) { - ParticleSystem *psys = sim->psys; - DerivedMesh *dm = psys->hair_in_dm; - MVert *mvert = NULL; - MEdge *medge = NULL; - MDeformVert *dvert = NULL; + /* Minimum segment length relative to average length. + * Hairs with segments below this length will be excluded from the simulation, + * because otherwise the solver will become unstable. + * The hair system should always make sure the hair segments have reasonable length ratios, + * but this can happen in old files when e.g. cutting hair. + */ + const float min_length = 0.1f * max_length; + HairKey *key; - PARTICLE_P; - int totpoint = 0; - int totedge; int k; - float hairmat[4][4]; - float (*deformedVerts)[3]; - - if (!psys->clmd) { - psys->clmd = (ClothModifierData*)modifier_new(eModifierType_Cloth); - psys->clmd->sim_parms->goalspring = 0.0f; - psys->clmd->sim_parms->vel_damping = 1.0f; - psys->clmd->sim_parms->flags |= CLOTH_SIMSETTINGS_FLAG_GOAL|CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS; - psys->clmd->coll_parms->flags &= ~CLOTH_COLLSETTINGS_FLAG_SELF; + + if (pa->totkey < 2) + return false; + + for (k=1, key=pa->hair+1; k<pa->totkey; k++,key++) { + float length = len_v3v3(key->co, (key-1)->co); + if (length < min_length) + return false; } + + return true; +} - /* create a dm from hair vertices */ - LOOP_PARTICLES - totpoint += pa->totkey; - - totedge = totpoint; - totpoint += psys->totpart; - - if (dm && (totpoint != dm->getNumVerts(dm) || totedge != dm->getNumEdges(dm))) { - dm->release(dm); - dm = psys->hair_in_dm = NULL; +static MDeformVert *hair_set_pinning(MDeformVert *dvert, float weight) +{ + if (dvert) { + if (!dvert->totweight) { + dvert->dw = MEM_callocN(sizeof(MDeformWeight), "deformWeight"); + dvert->totweight = 1; + } + + dvert->dw->weight = weight; + dvert++; } + return dvert; +} +static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int totedge, DerivedMesh **r_dm, ClothHairData **r_hairdata) +{ + ParticleSystem *psys = sim->psys; + ParticleSettings *part = psys->part; + DerivedMesh *dm; + ClothHairData *hairdata; + MVert *mvert; + MEdge *medge; + MDeformVert *dvert; + HairKey *key; + PARTICLE_P; + int k, hair_index; + float hairmat[4][4]; + float max_length; + float hair_radius; + + dm = *r_dm; if (!dm) { - dm = psys->hair_in_dm = CDDM_new(totpoint, totedge, 0, 0, 0); + *r_dm = dm = CDDM_new(totpoint, totedge, 0, 0, 0); DM_add_vert_layer(dm, CD_MDEFORMVERT, CD_CALLOC, NULL); } - mvert = CDDM_get_verts(dm); medge = CDDM_get_edges(dm); dvert = DM_get_vert_data_layer(dm, CD_MDEFORMVERT); - + + hairdata = *r_hairdata; + if (!hairdata) { + *r_hairdata = hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data"); + } + + /* calculate maximum segment length */ + max_length = 0.0f; + LOOP_PARTICLES { + for (k=1, key=pa->hair+1; k<pa->totkey; k++,key++) { + float length = len_v3v3(key->co, (key-1)->co); + if (max_length < length) + max_length = length; + } + } + psys->clmd->sim_parms->vgroup_mass = 1; - + + /* XXX placeholder for more flexible future hair settings */ + hair_radius = part->size; + /* make vgroup for pin roots etc.. */ - psys->particles->hair_index = 1; + hair_index = 1; LOOP_PARTICLES { - if (p) - pa->hair_index = (pa-1)->hair_index + (pa-1)->totkey + 1; - + float root_mat[4][4]; + float bending_stiffness; + bool use_hair; + + pa->hair_index = hair_index; + use_hair = psys_hair_use_simulation(pa, max_length); + psys_mat_hair_to_object(sim->ob, sim->psmd->dm, psys->part->from, pa, hairmat); - + mul_m4_m4m4(root_mat, sim->ob->obmat, hairmat); + normalize_m4(root_mat); + + bending_stiffness = CLAMPIS(1.0f - part->bending_random * psys_frand(psys, p + 666), 0.0f, 1.0f); + for (k=0, key=pa->hair; k<pa->totkey; k++,key++) { + ClothHairData *hair; + float *co, *co_next; + + co = key->co; + co_next = (key+1)->co; /* create fake root before actual root to resist bending */ if (k==0) { - float temp[3]; - sub_v3_v3v3(temp, key->co, (key+1)->co); - copy_v3_v3(mvert->co, key->co); - add_v3_v3v3(mvert->co, mvert->co, temp); + hair = &psys->clmd->hairdata[pa->hair_index - 1]; + copy_v3_v3(hair->loc, root_mat[3]); + copy_m3_m4(hair->rot, root_mat); + + hair->radius = hair_radius; + hair->bending_stiffness = bending_stiffness; + + add_v3_v3v3(mvert->co, co, co); + sub_v3_v3(mvert->co, co_next); mul_m4_v3(hairmat, mvert->co); - mvert++; - + medge->v1 = pa->hair_index - 1; medge->v2 = pa->hair_index; + + dvert = hair_set_pinning(dvert, 1.0f); + + mvert++; medge++; - - if (dvert) { - if (!dvert->totweight) { - dvert->dw = MEM_callocN(sizeof(MDeformWeight), "deformWeight"); - dvert->totweight = 1; - } - - dvert->dw->weight = 1.0f; - dvert++; - } } - - copy_v3_v3(mvert->co, key->co); + + /* store root transform in cloth data */ + hair = &psys->clmd->hairdata[pa->hair_index + k]; + copy_v3_v3(hair->loc, root_mat[3]); + copy_m3_m4(hair->rot, root_mat); + + hair->radius = hair_radius; + hair->bending_stiffness = bending_stiffness; + + copy_v3_v3(mvert->co, co); mul_m4_v3(hairmat, mvert->co); - mvert++; if (k) { medge->v1 = pa->hair_index + k - 1; medge->v2 = pa->hair_index + k; - medge++; - } - - if (dvert) { - if (!dvert->totweight) { - dvert->dw = MEM_callocN(sizeof(MDeformWeight), "deformWeight"); - dvert->totweight = 1; - } - /* roots should be 1.0, the rest can be anything from 0.0 to 1.0 */ - dvert->dw->weight = key->weight; - dvert++; } + + /* roots and disabled hairs should be 1.0, the rest can be anything from 0.0 to 1.0 */ + if (use_hair) + dvert = hair_set_pinning(dvert, key->weight); + else + dvert = hair_set_pinning(dvert, 1.0f); + + mvert++; + if (k) + medge++; } + + hair_index += pa->totkey + 1; } +} +static void do_hair_dynamics(ParticleSimulationData *sim) +{ + ParticleSystem *psys = sim->psys; + PARTICLE_P; + EffectorWeights *clmd_effweights; + int totpoint; + int totedge; + float (*deformedVerts)[3]; + bool realloc_roots; + + if (!psys->clmd) { + psys->clmd = (ClothModifierData*)modifier_new(eModifierType_Cloth); + psys->clmd->sim_parms->goalspring = 0.0f; + psys->clmd->sim_parms->vel_damping = 1.0f; + psys->clmd->sim_parms->flags |= CLOTH_SIMSETTINGS_FLAG_GOAL|CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS; + psys->clmd->coll_parms->flags &= ~CLOTH_COLLSETTINGS_FLAG_SELF; + psys->clmd->coll_parms->flags |= CLOTH_COLLSETTINGS_FLAG_POINTS; + } + + /* count simulated points */ + totpoint = 0; + totedge = 0; + LOOP_PARTICLES { + /* "out" dm contains all hairs */ + totedge += pa->totkey; + totpoint += pa->totkey + 1; /* +1 for virtual root point */ + } + + realloc_roots = false; /* whether hair root info array has to be reallocated */ + if (psys->hair_in_dm) { + DerivedMesh *dm = psys->hair_in_dm; + if (totpoint != dm->getNumVerts(dm) || totedge != dm->getNumEdges(dm)) { + dm->release(dm); + psys->hair_in_dm = NULL; + realloc_roots = true; + } + } + + if (!psys->hair_in_dm || !psys->clmd->hairdata || realloc_roots) { + if (psys->clmd->hairdata) { + MEM_freeN(psys->clmd->hairdata); + psys->clmd->hairdata = NULL; + } + } + + hair_create_input_dm(sim, totpoint, totedge, &psys->hair_in_dm, &psys->clmd->hairdata); + if (psys->hair_out_dm) psys->hair_out_dm->release(psys->hair_out_dm); - + psys->clmd->point_cache = psys->pointcache; + /* for hair sim we replace the internal cloth effector weights temporarily + * to use the particle settings + */ + clmd_effweights = psys->clmd->sim_parms->effector_weights; psys->clmd->sim_parms->effector_weights = psys->part->effector_weights; - - deformedVerts = MEM_mallocN(sizeof(*deformedVerts) * dm->getNumVerts(dm), "do_hair_dynamics vertexCos"); - psys->hair_out_dm = CDDM_copy(dm); + + deformedVerts = MEM_mallocN(sizeof(*deformedVerts) * psys->hair_in_dm->getNumVerts(psys->hair_in_dm), "do_hair_dynamics vertexCos"); + psys->hair_out_dm = CDDM_copy(psys->hair_in_dm); psys->hair_out_dm->getVertCos(psys->hair_out_dm, deformedVerts); - - clothModifier_do(psys->clmd, sim->scene, sim->ob, dm, deformedVerts); - + + clothModifier_do(psys->clmd, sim->scene, sim->ob, psys->hair_in_dm, deformedVerts); + CDDM_apply_vert_coords(psys->hair_out_dm, deformedVerts); - + MEM_freeN(deformedVerts); - - psys->clmd->sim_parms->effector_weights = NULL; + + /* restore cloth effector weights */ + psys->clmd->sim_parms->effector_weights = clmd_effweights; } static void hair_step(ParticleSimulationData *sim, float cfra) { @@ -4473,7 +3559,7 @@ static void update_children(ParticleSimulationData *sim) /* don't generate children while growing hair - waste of time */ psys_free_children(sim->psys); else if (sim->psys->part->childtype) { - if (sim->psys->totchild != get_psys_tot_child(sim->scene, sim->psys)) + if (sim->psys->totchild != psys_get_tot_child(sim->scene, sim->psys)) distribute_particles(sim, PART_FROM_CHILD); else { /* Children are up to date, nothing to do. */ diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index bd953890443..5e07437d426 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -281,7 +281,7 @@ static int map_insert_vert(PBVH *bvh, GHash *map, /* Find vertices used by the faces in this node and update the draw buffers */ static void build_mesh_leaf_node(PBVH *bvh, PBVHNode *node) { - GHashIterator *iter; + GHashIterator gh_iter; GHash *map; int i, j, totface; bool has_visible = false; @@ -314,22 +314,17 @@ static void build_mesh_leaf_node(PBVH *bvh, PBVHNode *node) "bvh node vert indices"); /* Build the vertex list, unique verts first */ - for (iter = BLI_ghashIterator_new(map), i = 0; - BLI_ghashIterator_done(iter) == false; - BLI_ghashIterator_step(iter), ++i) - { - void *value = BLI_ghashIterator_getValue(iter); + GHASH_ITER (gh_iter, map) { + void *value = BLI_ghashIterator_getValue(&gh_iter); int ndx = GET_INT_FROM_POINTER(value); if (ndx < 0) ndx = -ndx + node->uniq_verts - 1; node->vert_indices[ndx] = - GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(iter)); + GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(&gh_iter)); } - BLI_ghashIterator_free(iter); - for (i = 0; i < totface; ++i) { MFace *f = bvh->faces + node->prim_indices[i]; int sides = f->v4 ? 4 : 3; diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 75301471fca..a9780ef50d3 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -774,7 +774,7 @@ static int ptcache_smoke_read(PTCacheFile *pf, void *smoke_v) /* version header */ ptcache_file_read(pf, version, 4, sizeof(char)); - if (strncmp(version, SMOKE_CACHE_VERSION, 4)) + if (!STREQLEN(version, SMOKE_CACHE_VERSION, 4)) { /* reset file pointer */ fseek(pf->fp, -4, SEEK_CUR); @@ -956,7 +956,7 @@ static int ptcache_dynamicpaint_read(PTCacheFile *pf, void *dp_v) /* version header */ ptcache_file_read(pf, version, 1, sizeof(char) * 4); - if (strncmp(version, DPAINT_CACHE_VERSION, 4)) { + if (!STREQLEN(version, DPAINT_CACHE_VERSION, 4)) { printf("Dynamic Paint: Invalid cache version: '%c%c%c%c'!\n", UNPACK4(version)); return 0; } @@ -1710,7 +1710,7 @@ static int ptcache_file_header_begin_read(PTCacheFile *pf) if (fread(bphysics, sizeof(char), 8, pf->fp) != 8) error = 1; - if (!error && strncmp(bphysics, "BPHYSICS", 8)) + if (!error && !STREQLEN(bphysics, "BPHYSICS", 8)) error = 1; if (!error && !fread(&typeflag, sizeof(unsigned int), 1, pf->fp)) @@ -2386,7 +2386,7 @@ static int ptcache_write_needed(PTCacheID *pid, int cfra, int *overwrite) PointCache *cache = pid->cache; int ofra = 0, efra = cache->endframe; - /* allways start from scratch on the first frame */ + /* always start from scratch on the first frame */ if (cfra && cfra == cache->startframe) { BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, cfra); cache->state.flag &= ~PTC_STATE_REDO_NEEDED; @@ -2512,7 +2512,7 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) while ((de = readdir(dir)) != NULL) { if (strstr(de->d_name, ext)) { /* do we have the right extension?*/ - if (strncmp(filename, de->d_name, len ) == 0) { /* do we have the right prefix */ + if (STREQLEN(filename, de->d_name, len)) { /* do we have the right prefix */ if (mode == PTCACHE_CLEAR_ALL) { pid->cache->state.last_exact = MIN2(pid->cache->startframe, 0); BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name); @@ -2656,7 +2656,7 @@ void BKE_ptcache_id_time(PTCacheID *pid, Scene *scene, float cfra, int *startfra while ((de = readdir(dir)) != NULL) { if (strstr(de->d_name, ext)) { /* do we have the right extension?*/ - if (strncmp(filename, de->d_name, len ) == 0) { /* do we have the right prefix */ + if (STREQLEN(filename, de->d_name, len)) { /* do we have the right prefix */ /* read the number of the file */ unsigned int frame, len2 = (int)strlen(de->d_name); char num[7]; @@ -2827,7 +2827,7 @@ void BKE_ptcache_remove(void) return; while ((de = readdir(dir)) != NULL) { - if ( strcmp(de->d_name, ".")==0 || strcmp(de->d_name, "..")==0) { + if (FILENAME_IS_CURRPAR(de->d_name)) { /* do nothing */ } else if (strstr(de->d_name, PTCACHE_EXT)) { /* do we have the right extension?*/ @@ -2976,7 +2976,7 @@ void BKE_ptcache_disk_cache_rename(PTCacheID *pid, const char *name_src, const c while ((de = readdir(dir)) != NULL) { if (strstr(de->d_name, ext)) { /* do we have the right extension?*/ - if (strncmp(old_filename, de->d_name, len ) == 0) { /* do we have the right prefix */ + if (STREQLEN(old_filename, de->d_name, len)) { /* do we have the right prefix */ /* read the number of the file */ int frame, len2 = (int)strlen(de->d_name); char num[7]; @@ -3031,7 +3031,7 @@ void BKE_ptcache_load_external(PTCacheID *pid) while ((de = readdir(dir)) != NULL) { if (strstr(de->d_name, ext)) { /* do we have the right extension?*/ - if (strncmp(filename, de->d_name, len ) == 0) { /* do we have the right prefix */ + if (STREQLEN(filename, de->d_name, len)) { /* do we have the right prefix */ /* read the number of the file */ int frame, len2 = (int)strlen(de->d_name); char num[7]; diff --git a/source/blender/blenkernel/intern/property.c b/source/blender/blenkernel/intern/property.c index bb04d548a44..100df5fd121 100644 --- a/source/blender/blenkernel/intern/property.c +++ b/source/blender/blenkernel/intern/property.c @@ -131,60 +131,6 @@ bProperty *BKE_bproperty_new(int type) return prop; } -/* used by BKE_bproperty_unique() only */ -static bProperty *bproperty_get(bProperty *first, bProperty *self, const char *name) -{ - bProperty *p; - for (p = first; p; p = p->next) { - if (p != self && (strcmp(p->name, name) == 0)) - return p; - } - return NULL; -} -void BKE_bproperty_unique(bProperty *first, bProperty *prop, int force) -{ - bProperty *p; - - /* set the first if its not set */ - if (first == NULL) { - first = prop; - while (first->prev) { - first = first->prev; - } - } - - if (force) { - /* change other names to make them unique */ - while ((p = bproperty_get(first, prop, prop->name))) { - BKE_bproperty_unique(first, p, 0); - } - } - else { - /* change our own name until its unique */ - if (bproperty_get(first, prop, prop->name)) { - /* there is a collision */ - char new_name[sizeof(prop->name)]; - char base_name[sizeof(prop->name)]; - char num[sizeof(prop->name)]; - int i = 0; - - /* strip numbers */ - BLI_strncpy(base_name, prop->name, sizeof(base_name)); - for (i = strlen(base_name) - 1; (i >= 0 && isdigit(base_name[i])); i--) { - base_name[i] = '\0'; - } - i = 0; - - do { /* ensure we have enough chars for the new number in the name */ - const size_t num_len = BLI_snprintf(num, sizeof(num), "%d", i++); - BLI_snprintf(new_name, sizeof(prop->name), - "%.*s%s", (int)(sizeof(prop->name) - num_len), base_name, num); - } while (bproperty_get(first, prop, new_name)); - - BLI_strncpy(prop->name, new_name, sizeof(prop->name)); - } - } -} bProperty *BKE_bproperty_object_get(Object *ob, const char *name) { diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 4bd3b6bd8dd..92db7165c6d 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -438,6 +438,10 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) new_shape = rigidbody_get_shape_trimesh_from_mesh(ob); break; } + /* use box shape if we can't fall back to old shape */ + if (new_shape == NULL && rbo->physics_shape == NULL) { + new_shape = RB_shape_new_box(size[0], size[1], size[2]); + } /* assign new collision shape if creation was successful */ if (new_shape) { if (rbo->physics_shape) @@ -445,11 +449,6 @@ static void rigidbody_validate_sim_shape(Object *ob, bool rebuild) rbo->physics_shape = new_shape; RB_shape_set_margin(rbo->physics_shape, RBO_GET_MARGIN(rbo)); } - /* use box shape if we can't fall back to old shape */ - else if (rbo->physics_shape == NULL) { - rbo->shape = RB_SHAPE_BOX; - rigidbody_validate_sim_shape(ob, true); - } } /* --------------------- */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 13e9b7fb0c4..f94f7cd8f0b 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -689,6 +689,12 @@ Scene *BKE_scene_add(Main *bmain, const char *name) BLI_strncpy(sce->sequencer_colorspace_settings.name, colorspace_name, sizeof(sce->sequencer_colorspace_settings.name)); + /* Safe Areas */ + copy_v2_fl2(sce->safe_areas.title, 3.5f / 100.0f, 3.5f / 100.0f); + copy_v2_fl2(sce->safe_areas.action, 10.0f / 100.0f, 5.0f / 100.0f); + copy_v2_fl2(sce->safe_areas.title_center, 17.5f / 100.0f, 5.0f / 100.0f); + copy_v2_fl2(sce->safe_areas.action_center, 15.0f / 100.0f, 5.0f / 100.0f); + return sce; } @@ -1636,9 +1642,9 @@ static void prepare_mesh_for_viewport_render(Main *bmain, Scene *scene) Object *obedit = scene->obedit; if (obedit) { Mesh *mesh = obedit->data; - /* TODO(sergey): Check object recalc flags as well? */ if ((obedit->type == OB_MESH) && - (mesh->id.flag & (LIB_ID_RECALC | LIB_ID_RECALC_DATA))) + ((obedit->id.flag & LIB_ID_RECALC_ALL) || + (mesh->id.flag & LIB_ID_RECALC_ALL))) { if (check_rendered_viewport_visible(bmain)) { BMesh *bm = mesh->edit_btmesh->bm; @@ -1997,7 +2003,7 @@ void BKE_scene_disable_color_management(Scene *scene) bool BKE_scene_check_color_management_enabled(const Scene *scene) { - return strcmp(scene->display_settings.display_device, "None") != 0; + return !STREQ(scene->display_settings.display_device, "None"); } bool BKE_scene_check_rigidbody_active(const Scene *scene) diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index ad4ed5a0b99..c9dba38b713 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -46,6 +46,7 @@ #include "BLI_listbase.h" #include "BLI_utildefines.h" +#include "BLI_rect.h" #include "BKE_idprop.h" #include "BKE_screen.h" @@ -137,7 +138,7 @@ void BKE_spacetype_register(SpaceType *st) BLI_addtail(&spacetypes, st); } -int BKE_spacetype_exists(int spaceid) +bool BKE_spacetype_exists(int spaceid) { return BKE_spacetype_from_id(spaceid) != NULL; } @@ -419,16 +420,17 @@ ScrArea *BKE_screen_find_area_from_space(struct bScreen *sc, SpaceLink *sl) return sa; } -/* note, using this function is generally a last resort, you really want to be +/** + * \note Using this function is generally a last resort, you really want to be * using the context when you can - campbell - * -1 for any type */ + */ ScrArea *BKE_screen_find_big_area(bScreen *sc, const int spacetype, const short min) { ScrArea *sa, *big = NULL; int size, maxsize = 0; for (sa = sc->areabase.first; sa; sa = sa->next) { - if ((spacetype == -1) || sa->spacetype == spacetype) { + if ((spacetype == SPACE_TYPE_ANY) || (sa->spacetype == spacetype)) { if (min <= sa->winx && min <= sa->winy) { size = sa->winx * sa->winy; if (size > maxsize) { @@ -442,6 +444,22 @@ ScrArea *BKE_screen_find_big_area(bScreen *sc, const int spacetype, const short return big; } +ScrArea *BKE_screen_find_area_xy(bScreen *sc, const int spacetype, int x, int y) +{ + ScrArea *sa, *sa_found = NULL; + + for (sa = sc->areabase.first; sa; sa = sa->next) { + if (BLI_rcti_isect_pt(&sa->totrct, x, y)) { + if ((spacetype == SPACE_TYPE_ANY) || (sa->spacetype == spacetype)) { + sa_found = sa; + } + break; + } + } + return sa_found; +} + + /** * Utility function to get the active layer to use when adding new objects. */ diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index b43e481d97d..83287fe7725 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -1290,6 +1290,7 @@ typedef struct SeqIndexBuildContext { int tc_flags; int size_flags; int quality; + bool overwrite; Main *bmain; Scene *scene; @@ -1329,7 +1330,7 @@ static double seq_rendersize_to_scale_factor(int size) return 0.25; } -static void seq_open_anim_file(Sequence *seq) +static void seq_open_anim_file(Sequence *seq, bool openfile) { char name[FILE_MAX]; StripProxy *proxy; @@ -1342,8 +1343,14 @@ static void seq_open_anim_file(Sequence *seq) seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(name, G.main->name); - seq->anim = openanim(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); + if (openfile) { + seq->anim = openanim(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + seq->anim = openanim_noload(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } if (seq->anim == NULL) { return; @@ -1457,7 +1464,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c return NULL; } - seq_open_anim_file(seq); + seq_open_anim_file(seq, true); frameno = IMB_anim_index_get_frame_index(seq->anim, seq->strip->proxy->tc, frameno); @@ -1481,7 +1488,8 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c } } -static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, int cfra, int proxy_render_size) +static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, int cfra, + int proxy_render_size, const bool overwrite) { char name[PROXY_MAXFILE]; int quality; @@ -1493,6 +1501,10 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i return; } + if (!overwrite && BLI_exists(name)) { + return; + } + ibuf_tmp = seq_render_strip(context, seq, cfra); rectx = (proxy_render_size * ibuf_tmp->x) / 100; @@ -1526,7 +1538,7 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i IMB_freeImBuf(ibuf); } -SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq) +SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list) { SeqIndexBuildContext *context; Sequence *nseq; @@ -1546,6 +1558,7 @@ SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *sc context->tc_flags = nseq->strip->proxy->build_tc_flags; context->size_flags = nseq->strip->proxy->build_size_flags; context->quality = nseq->strip->proxy->quality; + context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0; context->bmain = bmain; context->scene = scene; @@ -1553,11 +1566,12 @@ SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *sc context->seq = nseq; if (nseq->type == SEQ_TYPE_MOVIE) { - seq_open_anim_file(nseq); + seq_open_anim_file(nseq, true); if (nseq->anim) { context->index_context = IMB_anim_index_rebuild_context(nseq->anim, - context->tc_flags, context->size_flags, context->quality); + context->tc_flags, context->size_flags, context->quality, + context->overwrite, file_list); } } @@ -1566,6 +1580,7 @@ SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *sc void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, short *do_update, float *progress) { + const bool overwrite = context->overwrite; SeqRenderData render_context; Sequence *seq = context->seq; Scene *scene = context->scene; @@ -1602,16 +1617,16 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho for (cfra = seq->startdisp + seq->startstill; cfra < seq->enddisp - seq->endstill; cfra++) { if (context->size_flags & IMB_PROXY_25) { - seq_proxy_build_frame(&render_context, seq, cfra, 25); + seq_proxy_build_frame(&render_context, seq, cfra, 25, overwrite); } if (context->size_flags & IMB_PROXY_50) { - seq_proxy_build_frame(&render_context, seq, cfra, 50); + seq_proxy_build_frame(&render_context, seq, cfra, 50, overwrite); } if (context->size_flags & IMB_PROXY_75) { - seq_proxy_build_frame(&render_context, seq, cfra, 75); + seq_proxy_build_frame(&render_context, seq, cfra, 75, overwrite); } if (context->size_flags & IMB_PROXY_100) { - seq_proxy_build_frame(&render_context, seq, cfra, 100); + seq_proxy_build_frame(&render_context, seq, cfra, 100, overwrite); } *progress = (float) (cfra - seq->startdisp - seq->startstill) / (seq->enddisp - seq->endstill - seq->startdisp - seq->startstill); @@ -1635,6 +1650,22 @@ void BKE_sequencer_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop MEM_freeN(context); } +void BKE_sequencer_proxy_set(struct Sequence *seq, bool value) +{ + if (value) { + seq->flag |= SEQ_USE_PROXY; + if (seq->strip->proxy == NULL) { + seq->strip->proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy"); + seq->strip->proxy->quality = 90; + seq->strip->proxy->build_tc_flags = SEQ_PROXY_TC_ALL; + seq->strip->proxy->build_size_flags = SEQ_PROXY_IMAGE_SIZE_25; + } + } + else { + seq->flag ^= SEQ_USE_PROXY; + } +} + /*********************** color balance *************************/ static StripColorBalance calc_cb(StripColorBalance *cb_) @@ -2609,7 +2640,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq * When rendering from command line renderer is called from main thread, in this * case it's always safe to render scene here */ - if (!is_thread_main || is_rendering == false || is_background) { + if (!is_thread_main || is_rendering == false || is_background || context->eval_ctx->mode == DAG_EVAL_RENDER) { if (re == NULL) re = RE_NewRender(scene->id.name); @@ -2755,15 +2786,22 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s case SEQ_TYPE_MOVIE: { - seq_open_anim_file(seq); + seq_open_anim_file(seq, false); if (seq->anim) { + IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); IMB_anim_set_preseek(seq->anim, seq->anim_preseek); ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - seq_rendersize_to_proxysize(context->preview_render_size)); + proxy_size); + /* fetching for requested proxy size failed, try fetching the original instead */ + if (!ibuf && proxy_size != IMB_PROXY_NONE) { + ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); + } if (ibuf) { BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index d91818615f3..2ff81cd5394 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -89,7 +89,7 @@ static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) 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) +#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]; @@ -394,7 +394,7 @@ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) /* 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) +#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]; diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index a996da5915e..b6453fe8fb1 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -2131,6 +2131,9 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd if (sfs->source == MOD_SMOKE_FLOW_SOURCE_PARTICLES) { /* emit_from_particles() updates timestep internally */ emit_from_particles(collob, sds, sfs, &em_temp, scene, sdt); + if (!(sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE)) { + hires_multiplier = 1; + } } else { /* MOD_SMOKE_FLOW_SOURCE_MESH */ /* update flow object frame */ diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 88720d5fcb7..25ac3c91b7f 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -118,9 +118,10 @@ void BKE_sound_free(bSound *sound) sound_free_waveform(sound); - if (sound->mutex) { - BLI_mutex_free(sound->mutex); - sound->mutex = NULL; + if (sound->spinlock) { + BLI_spin_end(sound->spinlock); + MEM_freeN(sound->spinlock); + sound->spinlock = NULL; } #endif /* WITH_AUDASPACE */ @@ -687,8 +688,7 @@ void sound_free_waveform(bSound *sound) void sound_read_waveform(bSound *sound, short *stop) { AUD_SoundInfo info = AUD_getInfo(sound->playback_handle); - SoundWaveform *waveform = MEM_mallocN(sizeof(SoundWaveform), - "SoundWaveform"); + SoundWaveform *waveform = MEM_mallocN(sizeof(SoundWaveform), "SoundWaveform"); if (info.length > 0) { int length = info.length * SOUND_WAVE_SAMPLES_PER_SECOND; @@ -710,18 +710,18 @@ void sound_read_waveform(bSound *sound, short *stop) MEM_freeN(waveform->data); } MEM_freeN(waveform); - BLI_mutex_lock(sound->mutex); + BLI_spin_lock(sound->spinlock); sound->flags &= ~SOUND_FLAGS_WAVEFORM_LOADING; - BLI_mutex_unlock(sound->mutex); + BLI_spin_unlock(sound->spinlock); return; } sound_free_waveform(sound); - BLI_mutex_lock(sound->mutex); + BLI_spin_lock(sound->spinlock); sound->waveform = waveform; sound->flags &= ~SOUND_FLAGS_WAVEFORM_LOADING; - BLI_mutex_unlock(sound->mutex); + BLI_spin_unlock(sound->spinlock); } void sound_update_scene(Main *bmain, struct Scene *scene) diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 961a66f853b..9519c7b25a1 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -1871,64 +1871,6 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes) } } -static void ccgdm_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert) -{ - const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - int b; - - /* orco texture coordinates */ - if (attribs->totorco) { - /*const*/ float (*array)[3] = attribs->orco.array; - const float *orco = (array) ? array[index] : zero; - - if (attribs->orco.gl_texco) - glTexCoord3fv(orco); - else - glVertexAttrib3fvARB(attribs->orco.gl_index, orco); - } - - /* uv texture coordinates */ - for (b = 0; b < attribs->tottface; b++) { - const float *uv; - - if (attribs->tface[b].array) { - MTFace *tf = &attribs->tface[b].array[a]; - uv = tf->uv[vert]; - } - else { - uv = zero; - } - - if (attribs->tface[b].gl_texco) - glTexCoord2fv(uv); - else - glVertexAttrib2fvARB(attribs->tface[b].gl_index, uv); - } - - /* vertex colors */ - for (b = 0; b < attribs->totmcol; b++) { - GLubyte col[4]; - - if (attribs->mcol[b].array) { - MCol *cp = &attribs->mcol[b].array[a * 4 + vert]; - col[0] = cp->b; col[1] = cp->g; col[2] = cp->r; col[3] = cp->a; - } - else { - col[0] = 0; col[1] = 0; col[2] = 0; col[3] = 0; - } - - glVertexAttrib4ubvARB(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[a * 4 + vert] : zero; - - glVertexAttrib4fvARB(attribs->tang.gl_index, tang); - } -} - /* Only used by non-editmesh types */ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, DMSetMaterial setMaterial, @@ -1959,7 +1901,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \ else \ index = 0; \ - ccgdm_draw_attrib_vertex(&attribs, a, index, vert); \ + DM_draw_attrib_vertex(&attribs, a, index, vert); \ } (void)0 totface = ccgSubSurf_getNumFaces(ss); @@ -2130,7 +2072,7 @@ static void ccgDM_drawMappedFacesMat(DerivedMesh *dm, index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \ else \ index = 0; \ - ccgdm_draw_attrib_vertex(&attribs, a, index, vert); \ + DM_draw_attrib_vertex(&attribs, a, index, vert); \ } (void)0 totface = ccgSubSurf_getNumFaces(ss); @@ -3523,6 +3465,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss, ccgdm->dm.calcNormals = ccgDM_calcNormals; ccgdm->dm.calcLoopNormals = CDDM_calc_loop_normals; + ccgdm->dm.calcLoopNormalsSpaceArray = CDDM_calc_loop_normals_spacearr; ccgdm->dm.recalcTessellation = ccgDM_recalcTessellation; ccgdm->dm.getVertCos = ccgdm_getVertCos; diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 3a311bfb23b..c5a8cbe68b2 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -508,10 +508,14 @@ void BKE_text_unlink(Main *bmain, Text *text) bNodeTree *ntree; bNode *node; Material *mat; + Lamp *la; + Tex *te; + World *wo; + FreestyleLineStyle *linestyle; Scene *sce; SceneRenderLayer *srl; FreestyleModuleConfig *module; - short update; + bool update; for (ob = bmain->object.first; ob; ob = ob->id.next) { /* game controllers */ @@ -563,23 +567,97 @@ void BKE_text_unlink(Main *bmain, Text *text) } /* nodes */ + for (la = bmain->lamp.first; la; la = la->id.next) { + ntree = la->nodetree; + if (!ntree) + continue; + for (node = ntree->nodes.first; node; node = node->next) { + if (node->type == NODE_FRAME) { + if ((Text *)node->id == text) { + node->id = NULL; + } + } + } + } + + for (linestyle = bmain->linestyle.first; linestyle; linestyle = linestyle->id.next) { + ntree = linestyle->nodetree; + if (!ntree) + continue; + for (node = ntree->nodes.first; node; node = node->next) { + if (node->type == NODE_FRAME) { + if ((Text *)node->id == text) { + node->id = NULL; + } + } + } + } + for (mat = bmain->mat.first; mat; mat = mat->id.next) { ntree = mat->nodetree; if (!ntree) continue; for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == SH_NODE_SCRIPT) { + if (ELEM(node->type, SH_NODE_SCRIPT, NODE_FRAME)) { + if ((Text *)node->id == text) { + node->id = NULL; + } + } + } + } + + for (te = bmain->tex.first; te; te = te->id.next) { + ntree = te->nodetree; + if (!ntree) + continue; + for (node = ntree->nodes.first; node; node = node->next) { + if (node->type == NODE_FRAME) { + if ((Text *)node->id == text) { + node->id = NULL; + } + } + } + } + + for (wo = bmain->world.first; wo; wo = wo->id.next) { + ntree = wo->nodetree; + if (!ntree) + continue; + for (node = ntree->nodes.first; node; node = node->next) { + if (node->type == NODE_FRAME) { + if ((Text *)node->id == text) { + node->id = NULL; + } + } + } + } + + for (sce = bmain->scene.first; sce; sce = sce->id.next) { + ntree = sce->nodetree; + if (!ntree) + continue; + for (node = ntree->nodes.first; node; node = node->next) { + if (node->type == NODE_FRAME) { Text *ntext = (Text *)node->id; if (ntext == text) node->id = NULL; } } + + /* Freestyle (while looping oer the scene) */ + for (srl = sce->r.layers.first; srl; srl = srl->next) { + for (module = srl->freestyleConfig.modules.first; module; module = module->next) { + if (module->script == text) + module->script = NULL; + } + } } - + for (ntree = bmain->nodetree.first; ntree; ntree = ntree->id.next) { for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == SH_NODE_SCRIPT) { - Text *ntext = (Text *)node->id; - if (ntext == text) node->id = NULL; + if (ELEM(node->type, SH_NODE_SCRIPT, NODE_FRAME)) { + if ((Text *)node->id == text) { + node->id = NULL; + } } } } @@ -600,16 +678,6 @@ void BKE_text_unlink(Main *bmain, Text *text) } } - /* Freestyle */ - for (sce = bmain->scene.first; sce; sce = sce->id.next) { - for (srl = sce->r.layers.first; srl; srl = srl->next) { - for (module = srl->freestyleConfig.modules.first; module; module = module->next) { - if (module->script == text) - module->script = NULL; - } - } - } - text->id.us = 0; } @@ -617,7 +685,7 @@ void BKE_text_clear(Text *text) /* called directly from rna */ { int oldstate; - oldstate = txt_get_undostate( ); + oldstate = txt_get_undostate(); txt_set_undostate(1); txt_sel_all(text); txt_delete_sel(text); @@ -2740,7 +2808,7 @@ void txt_unindent(Text *text) while (true) { bool changed = false; - if (strncmp(text->curl->line, remove, indentlen) == 0) { + if (STREQLEN(text->curl->line, remove, indentlen)) { if (num == 0) unindented_first = true; text->curl->len -= indentlen; diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index dce6584bdfe..6c4cbcf9316 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -349,7 +349,11 @@ bool do_colorband(const ColorBand *coba, float in, float out[4]) CBData left, right; /* we're looking for first pos > in */ - for (a = 0; a < coba->tot; a++, cbd1++) if (cbd1->pos > in) break; + for (a = 0; a < coba->tot; a++, cbd1++) { + if (cbd1->pos > in) { + break; + } + } if (a == coba->tot) { cbd2 = cbd1 - 1; @@ -745,6 +749,7 @@ void default_mtex(MTex *mtex) mtex->lengthfac = 1.0f; mtex->clumpfac = 1.0f; mtex->kinkfac = 1.0f; + mtex->kinkampfac = 1.0f; mtex->roughfac = 1.0f; mtex->padensfac = 1.0f; mtex->lifefac = 1.0f; @@ -755,7 +760,7 @@ void default_mtex(MTex *mtex) mtex->fieldfac = 1.0f; mtex->normapspace = MTEX_NSPACE_TANGENT; mtex->brush_map_mode = MTEX_MAP_MODE_TILED; - mtex->random_angle = 2.0f * M_PI; + mtex->random_angle = 2.0f * (float)M_PI; mtex->brush_angle_mode = 0; } @@ -1629,7 +1634,7 @@ void BKE_texture_get_value(Scene *scene, Tex *texture, float *tex_co, TexResult } /* no node textures for now */ - result_type = multitex_ext_safe(texture, tex_co, texres, NULL, do_color_manage); + result_type = multitex_ext_safe(texture, tex_co, texres, NULL, do_color_manage, false); /* if the texture gave an RGB value, we assume it didn't give a valid * intensity, since this is in the context of modifiers don't use perceptual color conversion. diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index d580c184a8b..0037002f6d8 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -721,7 +721,7 @@ MovieTrackingTrack *BKE_tracking_track_get_named(MovieTracking *tracking, MovieT MovieTrackingTrack *track = tracksbase->first; while (track) { - if (!strcmp(track->name, name)) + if (STREQ(track->name, name)) return track; track = track->next; @@ -1267,7 +1267,7 @@ MovieTrackingPlaneTrack *BKE_tracking_plane_track_get_named(MovieTracking *track plane_track; plane_track = plane_track->next) { - if (!strcmp(plane_track->name, name)) { + if (STREQ(plane_track->name, name)) { return plane_track; } } @@ -1563,7 +1563,7 @@ MovieTrackingObject *BKE_tracking_object_get_named(MovieTracking *tracking, cons MovieTrackingObject *object = tracking->objects.first; while (object) { - if (!strcmp(object->name, name)) + if (STREQ(object->name, name)) return object; object = object->next; diff --git a/source/blender/blenkernel/intern/tracking_auto.c b/source/blender/blenkernel/intern/tracking_auto.c index 22a380ea835..4cb3f2ca493 100644 --- a/source/blender/blenkernel/intern/tracking_auto.c +++ b/source/blender/blenkernel/intern/tracking_auto.c @@ -373,7 +373,7 @@ bool BKE_autotrack_context_step(AutoTrackContext *context) bool ok = false; int track; -#pragma omp parallel for if(context->num_tracks > 1) +#pragma omp parallel for if (context->num_tracks > 1) for (track = 0; track < context->num_tracks; ++track) { AutoTrackOptions *options = &context->options[track]; libmv_Marker libmv_current_marker, diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index 8a6a0438b84..85eac1f21ed 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -253,7 +253,7 @@ static void end_avi(void) } #endif /* WITH_AVI */ -/* similar to BKE_makepicstring() */ +/* similar to BKE_image_path_from_imformat() */ void BKE_movie_filepath_get(char *string, RenderData *rd) { bMovieHandle *mh = BKE_movie_handle_get(rd->im_format.imtype); diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 9737ef429cb..128a5da9b68 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -469,8 +469,8 @@ static int ffmpeg_proprty_valid(AVCodecContext *c, const char *prop_name, IDProp { int valid = 1; - if (strcmp(prop_name, "video") == 0) { - if (strcmp(curr->name, "bf") == 0) { + if (STREQ(prop_name, "video")) { + if (STREQ(curr->name, "bf")) { /* flash codec doesn't support b frames */ valid &= c->codec_id != AV_CODEC_ID_FLV1; } @@ -624,9 +624,9 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex if ((of->oformat->flags & AVFMT_GLOBALHEADER) #if 0 - || !strcmp(of->oformat->name, "mp4") - || !strcmp(of->oformat->name, "mov") - || !strcmp(of->oformat->name, "3gp") + || STREQ(of->oformat->name, "mp4") + || STREQ(of->oformat->name, "mov") + || STREQ(of->oformat->name, "3gp") #endif ) { diff --git a/source/blender/blenkernel/intern/writeframeserver.c b/source/blender/blenkernel/intern/writeframeserver.c index 746b99ffb52..ae6b19fb019 100644 --- a/source/blender/blenkernel/intern/writeframeserver.c +++ b/source/blender/blenkernel/intern/writeframeserver.c @@ -214,7 +214,7 @@ static int handle_request(RenderData *rd, char *req) *p = 0; - if (strcmp(path, "/index.html") == 0 || strcmp(path, "/") == 0) { + if (STREQ(path, "/index.html") || STREQ(path, "/")) { safe_puts(index_page); return -1; } @@ -226,7 +226,7 @@ static int handle_request(RenderData *rd, char *req) write_ppm = 1; return atoi(path + 12); } - if (strcmp(path, "/info.txt") == 0) { + if (STREQ(path, "/info.txt")) { char buf[4096]; sprintf(buf, @@ -250,7 +250,7 @@ static int handle_request(RenderData *rd, char *req) safe_puts(buf); return -1; } - if (strcmp(path, "/close.txt") == 0) { + if (STREQ(path, "/close.txt")) { safe_puts(good_bye); G.is_break = true; /* Abort render */ return -1; diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h index 49d072ddfdc..4981b163cdf 100644 --- a/source/blender/blenlib/BLI_kdopbvh.h +++ b/source/blender/blenlib/BLI_kdopbvh.h @@ -105,6 +105,10 @@ int BLI_bvhtree_find_nearest(BVHTree *tree, const float co[3], BVHTreeNearest *n int BLI_bvhtree_ray_cast(BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata); +/* Calls the callback for every ray intersection */ +int BLI_bvhtree_ray_cast_all(BVHTree *tree, const float co[3], const float dir[3], float radius, + 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]); /* range query */ diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 2bc23c8a72d..daa7db8e3cf 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -42,10 +42,13 @@ #endif #ifndef M_PI -#define M_PI 3.14159265358979323846 +#define M_PI 3.14159265358979323846 /* pi */ #endif #ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923 +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif +#ifndef M_PI_4 +#define M_PI_4 0.78539816339744830962 /* pi/4 */ #endif #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ @@ -60,22 +63,22 @@ #define M_SQRT1_3 0.57735026918962576450 /* 1/sqrt(3) */ #endif #ifndef M_1_PI -#define M_1_PI 0.318309886183790671538 +#define M_1_PI 0.318309886183790671538 /* 1/pi */ #endif #ifndef M_E -#define M_E 2.7182818284590452354 +#define M_E 2.7182818284590452354 /* e */ #endif #ifndef M_LOG2E -#define M_LOG2E 1.4426950408889634074 +#define M_LOG2E 1.4426950408889634074 /* log_2 e */ #endif #ifndef M_LOG10E -#define M_LOG10E 0.43429448190325182765 +#define M_LOG10E 0.43429448190325182765 /* log_10 e */ #endif #ifndef M_LN2 -#define M_LN2 0.69314718055994530942 +#define M_LN2 0.69314718055994530942 /* log_e 2 */ #endif #ifndef M_LN10 -#define M_LN10 2.30258509299404568402 +#define M_LN10 2.30258509299404568402 /* log_e 10 */ #endif #if defined(__GNUC__) diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index 5c14ac55492..560ebe3d390 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -141,6 +141,10 @@ void xyz_to_lab(float x, float y, float z, float *l, float *a, float *b); MINLINE int compare_rgb_uchar(const unsigned char a[3], const unsigned char b[3], const int limit); +MINLINE float dither_random_value(float s, float t); +MINLINE void float_to_byte_dither_v3(unsigned char b[3], const float f[3], float dither, float s, float t); + + #define rgba_char_args_set_fl(col, r, g, b, a) \ rgba_char_args_set(col, (r) * 255, (g) * 255, (b) * 255, (a) * 255) diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 905889a33d7..fbd026f7617 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -119,6 +119,11 @@ void mat4_to_axis_angle(float axis[3], float *angle, float M[4][4]); void axis_angle_to_mat3_single(float R[3][3], const char axis, const float angle); void angle_to_mat2(float R[2][2], const float angle); +/****************************** Exponential Map ******************************/ +void quat_to_expmap(float expmap[3], const float q[4]); +void quat_normalized_to_expmap(float expmap[3], const float q[4]); +void expmap_to_quat(float r[4], const float expmap[3]); + /******************************** XYZ Eulers *********************************/ void eul_to_quat(float quat[4], const float eul[3]); diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 4455e72cf45..0f437b7fc38 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -290,6 +290,8 @@ MINLINE void normal_float_to_short_v3(short r[3], const float n[3]); void minmax_v3v3_v3(float min[3], float max[3], const float vec[3]); void minmax_v2v2_v2(float min[2], float max[2], const float vec[2]); +void minmax_v3v3_v3_array(float r_min[3], float r_max[3], float (*vec_arr)[3], int nbr); + void dist_ensure_v3_v3fl(float v1[3], const float v2[3], const float dist); void dist_ensure_v2_v2fl(float v1[2], const float v2[2], const float dist); diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index bade390d056..7103f8a7597 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -108,6 +108,8 @@ void BLI_cleanup_dir(const char *relabase, char *dir) ATTR_NONNULL(2); /* doesn't touch trailing slash */ void BLI_cleanup_path(const char *relabase, char *path) ATTR_NONNULL(2); +void BLI_filename_make_safe(char *fname) ATTR_NONNULL(1); + /* go back one directory */ bool BLI_parent_dir(char *path) ATTR_NONNULL(); @@ -163,6 +165,15 @@ void BLI_string_to_utf8(char *original, char *utf_8, const char *code); # define FILE_MAX 1024 #endif +/* Parent and current dir helpers. */ +#define FILENAME_PARENT ".." +#define FILENAME_CURRENT "." + +/* Avoid calling strcmp on one or two chars! */ +#define FILENAME_IS_PARENT(_n) (((_n)[0] == '.') && ((_n)[1] == '.') && ((_n)[2] == '\0')) +#define FILENAME_IS_CURRENT(_n) (((_n)[0] == '.') && ((_n)[1] == '\0')) +#define FILENAME_IS_CURRPAR(_n) (((_n)[0] == '.') && (((_n)[1] == '\0') || (((_n)[1] == '.') && ((_n)[2] == '\0')))) + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/PIL_time.h b/source/blender/blenlib/PIL_time.h index 158559fa3d9..6e347339980 100644 --- a/source/blender/blenlib/PIL_time.h +++ b/source/blender/blenlib/PIL_time.h @@ -37,7 +37,7 @@ extern "C" { #endif -extern +extern /** Return an indication of time, expressed as * seconds since some fixed point. Successive calls * are guaranteed to generate values greater than or @@ -45,6 +45,12 @@ extern */ double PIL_check_seconds_timer(void); +extern +/** + * int version of #PIL_check_seconds_timer + */ +long int PIL_check_seconds_timer_i(void); + /** * Platform-independent sleep function. * \param ms Number of milliseconds to sleep diff --git a/source/blender/blenlib/intern/BLI_args.c b/source/blender/blenlib/intern/BLI_args.c index 49a3c466727..9faf6c93447 100644 --- a/source/blender/blenlib/intern/BLI_args.c +++ b/source/blender/blenlib/intern/BLI_args.c @@ -100,7 +100,7 @@ static bool keycmp(const void *a, const void *b) return (BLI_strcasecmp(ka->arg, kb->arg) != 0); } else { - return (strcmp(ka->arg, kb->arg) != 0); + return (!STREQ(ka->arg, kb->arg)); } } else { diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index c87b60f08db..5360ea744a1 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -771,7 +771,7 @@ unsigned int BLI_ghashutil_strhash_p(const void *ptr) } bool BLI_ghashutil_strcmp(const void *a, const void *b) { - return (strcmp(a, b) != 0); + return (!STREQ(a, b)); } GHashPair *BLI_ghashutil_pairalloc(const void *first, const void *second) diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index b76b925e6cc..e4504bcaab1 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -1468,6 +1468,42 @@ static void dfs_raycast(BVHRayCastData *data, BVHNode *node) } } +static void dfs_raycast_all(BVHRayCastData *data, BVHNode *node) +{ + int i; + + /* ray-bv is really fast.. and simple tests revealed its worth to test it + * 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 (node->totnode == 0) { + if (data->callback) { + data->hit.index = -1; + data->hit.dist = FLT_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); + } + } + else { + /* pick loop direction to dive into the tree (based on ray direction and split axis) */ + if (data->ray_dot_axis[node->main_axis] > 0.0f) { + for (i = 0; i != node->totnode; i++) { + dfs_raycast_all(data, node->children[i]); + } + } + else { + for (i = node->totnode - 1; i >= 0; i--) { + dfs_raycast_all(data, node->children[i]); + } + } + } +} + #if 0 static void iterative_raycast(BVHRayCastData *data, BVHNode *node) { @@ -1573,6 +1609,48 @@ float BLI_bvhtree_bb_raycast(const float bv[6], const float light_start[3], cons } +int BLI_bvhtree_ray_cast_all(BVHTree *tree, const float co[3], const float dir[3], float radius, + BVHTree_RayCastCallback callback, void *userdata) +{ + int i; + BVHRayCastData data; + BVHNode *root = tree->nodes[tree->totleaf]; + + data.tree = tree; + + data.callback = callback; + data.userdata = userdata; + + copy_v3_v3(data.ray.origin, co); + copy_v3_v3(data.ray.direction, dir); + data.ray.radius = radius; + + normalize_v3(data.ray.direction); + + for (i = 0; i < 3; i++) { + data.ray_dot_axis[i] = dot_v3v3(data.ray.direction, KDOP_AXES[i]); + data.idot_axis[i] = 1.0f / data.ray_dot_axis[i]; + + if (fabsf(data.ray_dot_axis[i]) < FLT_EPSILON) { + data.ray_dot_axis[i] = 0.0; + } + data.index[2 * i] = data.idot_axis[i] < 0.0f ? 1 : 0; + data.index[2 * i + 1] = 1 - data.index[2 * i]; + data.index[2 * i] += 2 * i; + data.index[2 * i + 1] += 2 * i; + } + + + data.hit.index = -1; + data.hit.dist = FLT_MAX; + + if (root) { + dfs_raycast_all(&data, root); + } + + return data.hit.index; +} + /** * Range Query - as request by broken :P * diff --git a/source/blender/blenlib/intern/boxpack2d.c b/source/blender/blenlib/intern/boxpack2d.c index bae56444f37..2db52cbda60 100644 --- a/source/blender/blenlib/intern/boxpack2d.c +++ b/source/blender/blenlib/intern/boxpack2d.c @@ -32,6 +32,9 @@ #include "BLI_utildefines.h" #include "BLI_boxpack2d.h" /* own include */ +#include "BLI_sort.h" /* qsort_r */ +#define qsort_r BLI_qsort_r + #include "BLI_strict_flags.h" #ifdef __GNUC__ @@ -226,18 +229,20 @@ static int box_areasort(const void *p1, const void *p2) * sorts from lower left to top right It uses the current box's width and height * as offsets when sorting, this has the result of not placing boxes outside * the bounds of the existing backed area where possible - * */ -static float box_width; -static float box_height; -static BoxVert *vertarray; + */ +struct VertSortContext { + BoxVert *vertarray; + float box_width, box_height; +}; -static int vertex_sort(const void *p1, const void *p2) +static int vertex_sort(const void *p1, const void *p2, void *vs_ctx_p) { - BoxVert *v1, *v2; + const struct VertSortContext *vs_ctx = vs_ctx_p; + const BoxVert *v1, *v2; float a1, a2; - v1 = vertarray + ((const int *)p1)[0]; - v2 = vertarray + ((const int *)p2)[0]; + v1 = &vs_ctx->vertarray[*((const unsigned int *)p1)]; + v2 = &vs_ctx->vertarray[*((const unsigned int *)p2)]; #ifdef USE_FREE_STRIP /* push free verts to the end so we can strip */ @@ -246,8 +251,8 @@ static int vertex_sort(const void *p1, const void *p2) else if (UNLIKELY(v2->free == 0)) return -1; #endif - a1 = max_ff(v1->x + box_width, v1->y + box_height); - a2 = max_ff(v2->x + box_width, v2->y + box_height); + a1 = max_ff(v1->x + vs_ctx->box_width, v1->y + vs_ctx->box_height); + a2 = max_ff(v2->x + vs_ctx->box_width, v2->y + vs_ctx->box_height); #ifdef USE_PACK_BIAS a1 += v1->bias; @@ -285,6 +290,8 @@ void BLI_box_pack_2d(BoxPack *boxarray, const unsigned int len, float *r_tot_x, BoxPack *box, *box_test; /*current box and another for intersection tests*/ BoxVert *vert; /* the current vert */ + struct VertSortContext vs_ctx; + if (!len) { *r_tot_x = tot_x; *r_tot_y = tot_y; @@ -295,9 +302,11 @@ void BLI_box_pack_2d(BoxPack *boxarray, const unsigned int len, float *r_tot_x, qsort(boxarray, (size_t)len, sizeof(BoxPack), box_areasort); /* add verts to the boxes, these are only used internally */ - vert = vertarray = MEM_mallocN((size_t)len * 4 * sizeof(BoxVert), "BoxPack Verts"); + vert = MEM_mallocN((size_t)len * 4 * sizeof(BoxVert), "BoxPack Verts"); vertex_pack_indices = MEM_mallocN((size_t)len * 3 * sizeof(int), "BoxPack Indices"); + vs_ctx.vertarray = vert; + for (box = boxarray, box_index = 0, i = 0; box_index < len; box_index++, box++) { vert->blb = vert->brb = vert->tlb = @@ -371,16 +380,16 @@ void BLI_box_pack_2d(BoxPack *boxarray, const unsigned int len, float *r_tot_x, /* Main boxpacking loop */ for (box_index = 1; box_index < len; box_index++, box++) { - /* These static floatds are used for sorting */ - box_width = box->w; - box_height = box->h; + /* These floats are used for sorting re-sorting */ + vs_ctx.box_width = box->w; + vs_ctx.box_height = box->h; - qsort(vertex_pack_indices, (size_t)verts_pack_len, sizeof(int), vertex_sort); + qsort_r(vertex_pack_indices, (size_t)verts_pack_len, sizeof(int), vertex_sort, &vs_ctx); #ifdef USE_FREE_STRIP /* strip free vertices */ i = verts_pack_len - 1; - while ((i != 0) && vertarray[vertex_pack_indices[i]].free == 0) { + while ((i != 0) && vs_ctx.vertarray[vertex_pack_indices[i]].free == 0) { i--; } verts_pack_len = i + 1; @@ -391,7 +400,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const unsigned int len, float *r_tot_x, isect = true; for (i = 0; i < verts_pack_len && isect; i++) { - vert = vertarray + vertex_pack_indices[i]; + vert = &vs_ctx.vertarray[vertex_pack_indices[i]]; /* printf("\ttesting vert %i %i %i %f %f\n", i, * vert->free, verts_pack_len, vert->x, vert->y); */ @@ -661,5 +670,5 @@ void BLI_box_pack_2d(BoxPack *boxarray, const unsigned int len, float *r_tot_x, box->v[0] = box->v[1] = box->v[2] = box->v[3] = NULL; } MEM_freeN(vertex_pack_indices); - MEM_freeN(vertarray); + MEM_freeN(vs_ctx.vertarray); } diff --git a/source/blender/blenlib/intern/dynlib.c b/source/blender/blenlib/intern/dynlib.c index b66eda4f141..e916b01e859 100644 --- a/source/blender/blenlib/intern/dynlib.c +++ b/source/blender/blenlib/intern/dynlib.c @@ -43,6 +43,8 @@ struct DynamicLibrary { #ifdef WIN32 +#define _WIN32_WINNT 0x501 /* Windows XP or newer */ +#define WIN32_LEAN_AND_MEAN #include <windows.h> #include "utf_winfunc.h" #include "utfconv.h" diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 62e1b6e4cbf..d6fe5e52b95 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -316,7 +316,7 @@ static bool delete_recursive(const char *dir) while (i--) { char file[8]; BLI_split_file_part(fl->path, file, sizeof(file)); - if (STREQ(file, ".") || STREQ(file, "..")) { + if (FILENAME_IS_CURRPAR(file)) { /* Skip! */ } else if (S_ISDIR(fl->type)) { @@ -584,7 +584,7 @@ static int recursive_operation(const char *startfrom, const char *startto, for (i = 0; i < n; i++) { const struct dirent * const dirent = dirlist[i]; - if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, "..")) + if (FILENAME_IS_CURRPAR(dirent->d_name)) continue; join_dirfile_alloc(&from_path, &from_alloc_len, from, dirent->d_name); diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c index bd3e1e0bbb0..d52c09790f9 100644 --- a/source/blender/blenlib/intern/listbase.c +++ b/source/blender/blenlib/intern/listbase.c @@ -474,7 +474,7 @@ void *BLI_findstring(const ListBase *listbase, const char *id, const int offset) for (link = listbase->first; link; link = link->next) { id_iter = ((const char *)link) + offset; - if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) { + if (id[0] == id_iter[0] && STREQ(id, id_iter)) { return link; } } @@ -494,7 +494,7 @@ void *BLI_rfindstring(const ListBase *listbase, const char *id, const int offset for (link = listbase->last; link; link = link->prev) { id_iter = ((const char *)link) + offset; - if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) { + if (id[0] == id_iter[0] && STREQ(id, id_iter)) { return link; } } @@ -515,7 +515,7 @@ void *BLI_findstring_ptr(const ListBase *listbase, const char *id, const int off /* exact copy of BLI_findstring(), except for this line */ id_iter = *((const char **)(((const char *)link) + offset)); - if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) { + if (id[0] == id_iter[0] && STREQ(id, id_iter)) { return link; } } @@ -536,7 +536,7 @@ void *BLI_rfindstring_ptr(const ListBase *listbase, const char *id, const int of /* exact copy of BLI_rfindstring(), except for this line */ id_iter = *((const char **)(((const char *)link) + offset)); - if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) { + if (id[0] == id_iter[0] && STREQ(id, id_iter)) { return link; } } @@ -600,7 +600,7 @@ int BLI_findstringindex(const ListBase *listbase, const char *id, const int offs while (link) { id_iter = ((const char *)link) + offset; - if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) + if (id[0] == id_iter[0] && STREQ(id, id_iter)) return i; i++; link = link->next; diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index 9233749d5df..dc62d04ad55 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -31,6 +31,8 @@ #include "BLI_math_color.h" #include "BLI_utildefines.h" +#include "math.h" + #ifndef __MATH_COLOR_INLINE_C__ #define __MATH_COLOR_INLINE_C__ @@ -269,6 +271,24 @@ MINLINE int compare_rgb_uchar(const unsigned char col_a[3], const unsigned char return 0; } +MINLINE float dither_random_value(float s, float t) +{ + static float vec[2] = {12.9898f, 78.233f}; + float value; + + value = sinf(s * vec[0] + t * vec[1]) * 43758.5453f; + return value - floorf(value); +} + +MINLINE void float_to_byte_dither_v3(unsigned char b[3], const float f[3], float dither, float s, float t) +{ + float dither_value = dither_random_value(s, t) * 0.005f * dither; + + b[0] = FTOCHAR(dither_value + f[0]); + b[1] = FTOCHAR(dither_value + f[1]); + b[2] = FTOCHAR(dither_value + f[2]); +} + /**************** Alpha Transformations *****************/ MINLINE void premul_to_straight_v4_v4(float straight[4], const float premul[4]) diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index 3ac031d7b90..3d5d47bc2e0 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -1016,6 +1016,40 @@ void angle_to_mat2(float mat[2][2], const float angle) mat[1][1] = angle_cos; } +/****************************** Exponential Map ******************************/ + +void quat_normalized_to_expmap(float expmap[3], const float q[4]) +{ + float angle; + BLI_ASSERT_UNIT_QUAT(q); + + /* Obtain axis/angle representation. */ + quat_to_axis_angle(expmap, &angle, q); + + /* Convert to exponential map. */ + mul_v3_fl(expmap, angle); +} + +void quat_to_expmap(float expmap[3], const float q[4]) +{ + float q_no[4]; + normalize_qt_qt(q_no, q); + quat_normalized_to_expmap(expmap, q_no); +} + +void expmap_to_quat(float r[4], const float expmap[3]) +{ + float axis[3]; + float angle; + + /* Obtain axis/angle representation. */ + angle = normalize_v3_v3(axis, expmap); + angle = angle_wrap_rad(angle); + + /* Convert to quaternion. */ + axis_angle_to_quat(r, axis, angle); +} + /******************************** XYZ Eulers *********************************/ /* XYZ order */ diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 887ec7d4d2c..d065fa7e5a7 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -757,6 +757,13 @@ void minmax_v2v2_v2(float min[2], float max[2], const float vec[2]) if (max[1] < vec[1]) max[1] = vec[1]; } +void minmax_v3v3_v3_array(float r_min[3], float r_max[3], float (*vec_arr)[3], int nbr) +{ + while (nbr--) { + minmax_v3v3_v3(r_min, r_max, *vec_arr++); + } +} + /** ensure \a v1 is \a dist from \a v2 */ void dist_ensure_v3_v3fl(float v1[3], const float v2[3], const float dist) { diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 3fff22218e2..fb4c31d5cdf 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -346,7 +346,7 @@ void BLI_cleanup_path(const char *relabase, char *path) /* Note * memmove(start, eind, strlen(eind) + 1); * is the same as - * strcpy( start, eind ); + * strcpy(start, eind); * except strcpy should not be used because there is overlap, * so use memmove's slightly more obscure syntax - Campbell */ @@ -428,6 +428,23 @@ void BLI_cleanup_file(const char *relabase, char *path) BLI_del_slash(path); } + +/** + * Make given name safe to be used in paths. + * + * For now, simply replaces reserved chars (as listed in + * http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words ) + * by underscores ('_'). + */ +void BLI_filename_make_safe(char *fname) +{ + const char *invalid = "/\\?%*:|\"<>. "; + + for (; *fname && (fname = strpbrk(fname, invalid)); fname++) { + *fname = '_'; + } +} + /** * Does path begin with the special "//" prefix that Blender uses to indicate * a path relative to the .blend file. @@ -1099,9 +1116,9 @@ void BLI_make_exist(char *dir) a = strlen(dir); - for (BLI_join_dirfile(par_path, sizeof(par_path), dir, ".."); + for (BLI_join_dirfile(par_path, sizeof(par_path), dir, FILENAME_PARENT); !(BLI_is_dir(dir) && BLI_exists(par_path)); - BLI_join_dirfile(par_path, sizeof(par_path), dir, "..")) + BLI_join_dirfile(par_path, sizeof(par_path), dir, FILENAME_PARENT)) { a--; while (dir[a] != SEP) { @@ -1352,9 +1369,7 @@ bool BLI_ensure_extension(char *path, size_t maxlen, const char *ext) ssize_t a; /* first check the extension is already there */ - if ( (ext_len <= path_len) && - (strcmp(path + (path_len - ext_len), ext) == 0)) - { + if ((ext_len <= path_len) && (STREQ(path + (path_len - ext_len), ext))) { return true; } diff --git a/source/blender/blenlib/intern/polyfill2d_beautify.c b/source/blender/blenlib/intern/polyfill2d_beautify.c index 7914b7cb39b..ba71f52b530 100644 --- a/source/blender/blenlib/intern/polyfill2d_beautify.c +++ b/source/blender/blenlib/intern/polyfill2d_beautify.c @@ -237,7 +237,10 @@ static void polyedge_beauty_cost_update_single( { /* recalculate edge */ const float cost = polyedge_rotate_beauty_calc(coords, tris, e); - if (cost < 0.0f) { + /* We can get cases where both choices generate very small negative costs, which leads to infinite loop. + * Anyway, costs above that are not worth recomputing, maybe we could even optimize it to a smaller limit? + * See T43578. */ + if (cost < -FLT_EPSILON) { eheap_table[i] = BLI_heap_insert(eheap, cost, e); } else { diff --git a/source/blender/blenlib/intern/rand.c b/source/blender/blenlib/intern/rand.c index 7657cec8cfd..a03b236b5c6 100644 --- a/source/blender/blenlib/intern/rand.c +++ b/source/blender/blenlib/intern/rand.c @@ -33,6 +33,7 @@ #include <stdlib.h> #include <string.h> #include <math.h> +#include <time.h> #include "MEM_guardedalloc.h" diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c index 4c5268562ad..38a15ab0a6d 100644 --- a/source/blender/blenlib/intern/storage.c +++ b/source/blender/blenlib/intern/storage.c @@ -135,10 +135,10 @@ static int bli_compare(struct direntry *entry1, struct direntry *entry2) /* OK, now we know their S_IFMT fields are the same, go on to a name comparison */ /* make sure "." and ".." are always first */ - if (strcmp(entry1->relname, ".") == 0) return (-1); - if (strcmp(entry2->relname, ".") == 0) return (1); - if (strcmp(entry1->relname, "..") == 0) return (-1); - if (strcmp(entry2->relname, "..") == 0) return (1); + if (FILENAME_IS_CURRENT(entry1->relname)) return (-1); + if (FILENAME_IS_CURRENT(entry2->relname)) return (1); + if (FILENAME_IS_PARENT(entry1->relname)) return (-1); + if (FILENAME_IS_PARENT(entry2->relname)) return (1); return (BLI_natstrcmp(entry1->relname, entry2->relname)); } diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 51b8efbb79f..d83077e0e78 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -115,7 +115,7 @@ void BLI_system_backtrace(FILE *fp) symbolinfo->SizeOfStruct = sizeof(SYMBOL_INFO); for (i = 0; i < nframes; i++) { - SymFromAddr(process, ( DWORD64 )( stack[ i ] ), 0, symbolinfo); + SymFromAddr(process, (DWORD64)(stack[i]), 0, symbolinfo); fprintf(fp, "%u: %s - 0x%0X\n", nframes - i - 1, symbolinfo->Name, symbolinfo->Address); } diff --git a/source/blender/blenlib/intern/time.c b/source/blender/blenlib/intern/time.c index 078fc2c295b..a0fb78cd193 100644 --- a/source/blender/blenlib/intern/time.c +++ b/source/blender/blenlib/intern/time.c @@ -33,6 +33,9 @@ #include "PIL_time.h" #ifdef WIN32 + +#define _WIN32_WINNT 0x501 /* Windows XP or newer */ +#define WIN32_LEAN_AND_MEAN #include <windows.h> double PIL_check_seconds_timer(void) @@ -70,6 +73,11 @@ double PIL_check_seconds_timer(void) } } +long int PIL_check_seconds_timer_i(void) +{ + return (long int)PIL_check_seconds_timer(); +} + void PIL_sleep_ms(int ms) { Sleep(ms); @@ -90,6 +98,16 @@ double PIL_check_seconds_timer(void) return ((double) tv.tv_sec + tv.tv_usec / 1000000.0); } +long int PIL_check_seconds_timer_i(void) +{ + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + + return tv.tv_sec; +} + void PIL_sleep_ms(int ms) { if (ms >= 1000) { diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 4b7b9cecb17..7a6107a974a 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -61,9 +61,7 @@ typedef struct BlendFileData { struct Main *main; struct UserDef *user; - int winpos; int fileflags; - int displaymode; int globalf; char filename[1024]; /* 1024 = FILE_MAX */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index d31167976ed..6f26ec73f90 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -115,6 +115,7 @@ #include "BKE_armature.h" #include "BKE_brush.h" +#include "BKE_cloth.h" #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_curve.h" @@ -778,7 +779,7 @@ static void decode_blender_header(FileData *fd) readsize = fd->read(fd, header, sizeof(header)); if (readsize == sizeof(header)) { - if (strncmp(header, "BLENDER", 7) == 0) { + if (STREQLEN(header, "BLENDER", 7)) { int remove_this_endian_test = 1; fd->flags |= FD_FLAGS_FILE_OK; @@ -1183,7 +1184,7 @@ bool BLO_is_a_library(const char *path, char *dir, char *group) /* now we know that we are in a blend file and it is safe to * assume that gp actually points to a group */ - if (strcmp("Screen", gp) != 0) + if (!STREQ("Screen", gp)) BLI_strncpy(group, gp, BLO_GROUP_MAX); } return 1; @@ -3751,6 +3752,13 @@ static void direct_link_particlesettings(FileData *fd, ParticleSettings *part) direct_link_partdeflect(part->pd); direct_link_partdeflect(part->pd2); + part->clumpcurve = newdataadr(fd, part->clumpcurve); + if (part->clumpcurve) + direct_link_curvemapping(fd, part->clumpcurve); + part->roughcurve = newdataadr(fd, part->roughcurve); + if (part->roughcurve) + direct_link_curvemapping(fd, part->roughcurve); + part->effector_weights = newdataadr(fd, part->effector_weights); if (!part->effector_weights) part->effector_weights = BKE_add_effector_weights(part->eff_group); @@ -3867,6 +3875,7 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles) if (psys->clmd) { psys->clmd = newdataadr(fd, psys->clmd); psys->clmd->clothObject = NULL; + psys->clmd->hairdata = NULL; psys->clmd->sim_parms= newdataadr(fd, psys->clmd->sim_parms); psys->clmd->coll_parms= newdataadr(fd, psys->clmd->coll_parms); @@ -3876,8 +3885,12 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles) if (psys->clmd->sim_parms->presets > 10) psys->clmd->sim_parms->presets = 0; } + if (psys->clmd->coll_parms) { + psys->clmd->coll_parms->flags |= CLOTH_COLLSETTINGS_FLAG_POINTS; + } psys->hair_in_dm = psys->hair_out_dm = NULL; + psys->clmd->solver_result = NULL; psys->clmd->point_cache = psys->pointcache; } @@ -4472,7 +4485,7 @@ static void lib_link_object(FileData *fd, Main *main) steeringa->target = newlibadr(fd, ob->id.lib, steeringa->target); steeringa->navmesh = newlibadr(fd, ob->id.lib, steeringa->navmesh); } - else if(act->type == ACT_MOUSE) { + else if (act->type == ACT_MOUSE) { /* bMouseActuator *moa= act->data; */ } } @@ -4592,6 +4605,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) ClothModifierData *clmd = (ClothModifierData *)md; clmd->clothObject = NULL; + clmd->hairdata = NULL; clmd->sim_parms= newdataadr(fd, clmd->sim_parms); clmd->coll_parms= newdataadr(fd, clmd->coll_parms); @@ -4611,6 +4625,8 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) clmd->sim_parms->effector_weights = BKE_add_effector_weights(NULL); } } + + clmd->solver_result = NULL; } else if (md->type == eModifierType_Fluidsim) { FluidsimModifierData *fluidmd = (FluidsimModifierData *)md; @@ -4765,6 +4781,11 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { BLI_endian_switch_int32_array(hmd->indexar, hmd->totindex); } + + hmd->curfalloff = newdataadr(fd, hmd->curfalloff); + if (hmd->curfalloff) { + direct_link_curvemapping(fd, hmd->curfalloff); + } } else if (md->type == eModifierType_ParticleSystem) { ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; @@ -5197,6 +5218,8 @@ static void lib_link_scene(FileData *fd, Main *main) sce->toolsettings->skgen_template = newlibadr(fd, sce->id.lib, sce->toolsettings->skgen_template); + sce->toolsettings->particle.shape_object = newlibadr(fd, sce->id.lib, sce->toolsettings->particle.shape_object); + for (base = sce->base.first; base; base = next) { next = base->next; @@ -5943,7 +5966,7 @@ typedef enum ePointerUserMode { static bool restore_pointer(ID *id, ID *newid, ePointerUserMode user) { - if (strcmp(newid->name + 2, id->name + 2) == 0) { + if (STREQ(newid->name + 2, id->name + 2)) { if (newid->lib == id->lib) { if (user == USER_ONE) { if (newid->us == 0) { @@ -6804,9 +6827,10 @@ static void direct_link_sound(FileData *fd, bSound *sound) sound->waveform = NULL; } - if (sound->mutex) - sound->mutex = BLI_mutex_alloc(); - + if (sound->spinlock) { + sound->spinlock = MEM_mallocN(sizeof(SpinLock), "sound_spinlock"); + BLI_spin_init(sound->spinlock); + } /* clear waveform loading flag */ sound->flags &= ~SOUND_FLAGS_WAVEFORM_LOADING; @@ -7521,9 +7545,7 @@ static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead) bfd->main->build_commit_timestamp = fg->build_commit_timestamp; BLI_strncpy(bfd->main->build_hash, fg->build_hash, sizeof(bfd->main->build_hash)); - bfd->winpos = fg->winpos; bfd->fileflags = fg->fileflags; - bfd->displaymode = fg->displaymode; bfd->globalf = fg->globalf; BLI_strncpy(bfd->filename, fg->filename, sizeof(bfd->filename)); @@ -9033,7 +9055,7 @@ static ID *append_named_part(Main *mainl, FileData *fd, const char *idname, cons if (bhead->code == idcode) { const char *idname_test= bhead_id_name(fd, bhead); - if (strcmp(idname_test + 2, idname) == 0) { + if (STREQ(idname_test + 2, idname)) { found = 1; id = is_yet_read(fd, mainl, bhead); if (id == NULL) { @@ -9154,7 +9176,7 @@ static void append_id_part(FileData *fd, Main *mainvar, ID *id, ID **r_id) for (bhead = blo_firstbhead(fd); bhead; bhead = blo_nextbhead(fd, bhead)) { if (bhead->code == GS(id->name)) { - if (strcmp(id->name, bhead_id_name(fd, bhead))==0) { + if (STREQ(id->name, bhead_id_name(fd, bhead))) { id->flag &= ~LIB_READ; id->flag |= LIB_NEED_EXPAND; // printf("read lib block %s\n", id->name); diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 8eb54c30cb1..8e485f17ef8 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -325,7 +325,7 @@ static void area_add_window_regions(ScrArea *sa, SpaceLink *sl, ListBase *lb) SpaceNla *snla = (SpaceNla *)sl; memcpy(&ar->v2d, &snla->v2d, sizeof(View2D)); - ar->v2d.tot.ymin = (float)(-sa->winy)/3.0f; + ar->v2d.tot.ymin = (float)(-sa->winy) / 3.0f; ar->v2d.tot.ymax = 0.0f; ar->v2d.scroll |= (V2D_SCROLL_BOTTOM|V2D_SCROLL_SCALE_HORIZONTAL); @@ -340,8 +340,8 @@ static void area_add_window_regions(ScrArea *sa, SpaceLink *sl, ListBase *lb) /* we totally reinit the view for the Action Editor, as some old instances had some weird cruft set */ ar->v2d.tot.xmin = -20.0f; - ar->v2d.tot.ymin = (float)(-sa->winy)/3.0f; - ar->v2d.tot.xmax = (float)((sa->winx > 120)? (sa->winx) : 120); + ar->v2d.tot.ymin = (float)(-sa->winy) / 3.0f; + ar->v2d.tot.xmax = (float)((sa->winx > 120) ? (sa->winx) : 120); ar->v2d.tot.ymax = 0.0f; ar->v2d.cur = ar->v2d.tot; diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 7e5127aa407..10526a191d2 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1883,7 +1883,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main) SEQ_END if (scene->r.bake_samples == 0) - scene->r.bake_samples = 256; + scene->r.bake_samples = 256; if (scene->world) { World *world = blo_do_versions_newlibadr(fd, scene->id.lib, scene->world); diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 0ff953e7420..468f56c9c0b 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -526,11 +526,114 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) br->mtex.brush_angle_mode |= MTEX_ANGLE_RANDOM; br->mask_mtex.brush_angle_mode |= MTEX_ANGLE_RANDOM; } - br->mtex.random_angle = 2.0f * M_PI; - br->mask_mtex.random_angle = 2.0f * M_PI; + br->mtex.random_angle = 2.0 * M_PI; + br->mask_mtex.random_angle = 2.0 * M_PI; } #undef BRUSH_RAKE #undef BRUSH_RANDOM_ROTATION } + + /* Customizable Safe Areas */ + if (!MAIN_VERSION_ATLEAST(main, 273, 2)) { + if (!DNA_struct_elem_find(fd->filesdna, "Scene", "DisplaySafeAreas", "safe_areas")) { + Scene *scene; + + for (scene = main->scene.first; scene; scene = scene->id.next) { + copy_v2_fl2(scene->safe_areas.title, 3.5f / 100.0f, 3.5f / 100.0f); + copy_v2_fl2(scene->safe_areas.action, 10.0f / 100.0f, 5.0f / 100.0f); + copy_v2_fl2(scene->safe_areas.title_center, 17.5f / 100.0f, 5.0f / 100.0f); + copy_v2_fl2(scene->safe_areas.action_center, 15.0f / 100.0f, 5.0f / 100.0f); + } + } + } + + if (!MAIN_VERSION_ATLEAST(main, 273, 3)) { + ParticleSettings *part; + for (part = main->particle.first; part; part = part->id.next) { + if (part->clumpcurve) + part->child_flag |= PART_CHILD_USE_CLUMP_CURVE; + if (part->roughcurve) + part->child_flag |= PART_CHILD_USE_ROUGH_CURVE; + } + } + + if (!MAIN_VERSION_ATLEAST(main, 273, 6)) { + if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "bending_damping")) { + Object *ob; + ModifierData *md; + for (ob = main->object.first; ob; ob = ob->id.next) { + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Cloth) { + ClothModifierData *clmd = (ClothModifierData *)md; + clmd->sim_parms->bending_damping = 0.5f; + } + else if (md->type == eModifierType_ParticleSystem) { + ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md; + if (pmd->psys->clmd) { + pmd->psys->clmd->sim_parms->bending_damping = 0.5f; + } + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "float", "clump_noise_size")) { + ParticleSettings *part; + for (part = main->particle.first; part; part = part->id.next) { + part->clump_noise_size = 1.0f; + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "int", "kink_extra_steps")) { + ParticleSettings *part; + for (part = main->particle.first; part; part = part->id.next) { + part->kink_extra_steps = 4; + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "MTex", "float", "kinkampfac")) { + ParticleSettings *part; + for (part = main->particle.first; part; part = part->id.next) { + int a; + for (a = 0; a < MAX_MTEX; a++) { + MTex *mtex = part->mtex[a]; + if (mtex) { + mtex->kinkampfac = 1.0f; + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "HookModifierData", "char", "flag")) { + Object *ob; + + for (ob = main->object.first; ob; ob = ob->id.next) { + ModifierData *md; + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Hook) { + HookModifierData *hmd = (HookModifierData *)md; + hmd->falloff_type = eHook_Falloff_InvSquare; + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "NodePlaneTrackDeformData", "char", "flag")) { + FOREACH_NODETREE(main, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + bNode *node; + for (node = ntree->nodes.first; node; node = node->next) { + if (ELEM(node->type, CMP_NODE_PLANETRACKDEFORM)) { + NodePlaneTrackDeformData *data = node->storage; + data->flag = 0; + data->motion_blur_samples = 16; + data->motion_blur_shutter = 0.5f; + } + } + } + } + FOREACH_NODETREE_END + } + } } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 27e49522be4..7d276ed5ed7 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1301,7 +1301,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main) Object *ob; for (vf = main->vfont.first; vf; vf = vf->id.next) { - if (strcmp(vf->name + strlen(vf->name)-6, ".Bfont") == 0) { + if (STREQ(vf->name + strlen(vf->name)-6, ".Bfont")) { strcpy(vf->name, FO_BUILTIN_NAME); } } @@ -2185,8 +2185,8 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main) cam->flag |= CAM_SHOWPASSEPARTOUT; /* make sure old cameras have title safe on */ - if (!(cam->flag & CAM_SHOWTITLESAFE)) - cam->flag |= CAM_SHOWTITLESAFE; + if (!(cam->flag & CAM_SHOW_SAFE_MARGINS)) + cam->flag |= CAM_SHOW_SAFE_MARGINS; /* set an appropriate camera passepartout alpha */ if (!(cam->passepartalpha)) @@ -2316,7 +2316,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main) if (main->versionfile == 241) { Image *ima; for (ima = main->image.first; ima; ima = ima->id.next) - if (strcmp(ima->name, "Compositor") == 0) { + if (STREQ(ima->name, "Compositor")) { strcpy(ima->id.name + 2, "Viewer Node"); strcpy(ima->name, "Viewer Node"); } @@ -2504,11 +2504,11 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main) ima->gen_x = 256; ima->gen_y = 256; ima->gen_type = 1; - if (0 == strncmp(ima->id.name + 2, "Viewer Node", sizeof(ima->id.name) - 2)) { + if (STREQLEN(ima->id.name + 2, "Viewer Node", sizeof(ima->id.name) - 2)) { ima->source = IMA_SRC_VIEWER; ima->type = IMA_TYPE_COMPOSITE; } - if (0 == strncmp(ima->id.name + 2, "Render Result", sizeof(ima->id.name) - 2)) { + if (STREQLEN(ima->id.name + 2, "Render Result", sizeof(ima->id.name) - 2)) { ima->source = IMA_SRC_VIEWER; ima->type = IMA_TYPE_R_RESULT; } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index f4c08149e05..9d20d154b7c 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -909,16 +909,39 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree) write_node_socket_interface(wd, ntree, sock); } -static void current_screen_compat(Main *mainvar, bScreen **screen) +/** + * Take care using 'use_active_win', since we wont want the currently active window + * to change which scene renders (currently only used for undo). + */ +static void current_screen_compat(Main *mainvar, bScreen **r_screen, bool use_active_win) { wmWindowManager *wm; - wmWindow *window; + wmWindow *window = NULL; /* find a global current screen in the first open window, to have * a reasonable default for reading in older versions */ wm = mainvar->wm.first; - window = (wm) ? wm->windows.first : NULL; - *screen = (window) ? window->screen : NULL; + + if (wm) { + if (use_active_win) { + /* write the active window into the file, needed for multi-window undo T43424 */ + for (window = wm->windows.first; window; window = window->next) { + if (window->active) { + break; + } + } + + /* fallback */ + if (window == NULL) { + window = wm->windows.first; + } + } + else { + window = wm->windows.first; + } + } + + *r_screen = (window) ? window->screen : NULL; } typedef struct RenderInfo { @@ -937,7 +960,7 @@ static void write_renderinfo(WriteData *wd, Main *mainvar) RenderInfo data; /* XXX in future, handle multiple windows with multiple screens? */ - current_screen_compat(mainvar, &curscreen); + current_screen_compat(mainvar, &curscreen, false); if (curscreen) curscene = curscreen->scene; for (sce= mainvar->scene.first; sce; sce= sce->id.next) { @@ -1062,6 +1085,11 @@ static void write_particlesettings(WriteData *wd, ListBase *idbase) writestruct(wd, DATA, "PartDeflect", 1, part->pd2); writestruct(wd, DATA, "EffectorWeights", 1, part->effector_weights); + if (part->clumpcurve) + write_curvemapping(wd, part->clumpcurve); + if (part->roughcurve) + write_curvemapping(wd, part->roughcurve); + dw = part->dupliweights.first; for (; dw; dw=dw->next) { /* update indices */ @@ -1434,6 +1462,10 @@ static void write_modifiers(WriteData *wd, ListBase *modbase) if (md->type==eModifierType_Hook) { HookModifierData *hmd = (HookModifierData*) md; + if (hmd->curfalloff) { + write_curvemapping(wd, hmd->curfalloff); + } + writedata(wd, DATA, sizeof(int)*hmd->totindex, hmd->indexar); } else if (md->type==eModifierType_Cloth) { @@ -3387,22 +3419,21 @@ static void write_linestyles(WriteData *wd, ListBase *idbase) * - for undofile, curscene needs to be saved */ static void write_global(WriteData *wd, int fileflags, Main *mainvar) { + const bool is_undo = (wd->current != NULL); FileGlobal fg; bScreen *screen; char subvstr[8]; /* prevent mem checkers from complaining */ - fg.pads= 0; + memset(fg.pad, 0, sizeof(fg.pad)); memset(fg.filename, 0, sizeof(fg.filename)); memset(fg.build_hash, 0, sizeof(fg.build_hash)); - current_screen_compat(mainvar, &screen); + current_screen_compat(mainvar, &screen, is_undo); /* XXX still remap G */ fg.curscreen= screen; fg.curscene= screen ? screen->scene : NULL; - fg.displaymode= G.displaymode; - fg.winpos= G.winpos; /* prevent to save this, is not good convention, and feature with concerns... */ fg.fileflags= (fileflags & ~G_FILE_FLAGS_RUNTIME); diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 23737866bf2..80adb595ac9 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC operators/bmo_bisect_plane.c operators/bmo_bridge.c operators/bmo_connect.c + operators/bmo_connect_concave.c operators/bmo_connect_nonplanar.c operators/bmo_connect_pair.c operators/bmo_create.c diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index ee35d8cd1d2..8aa64906019 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -41,6 +41,7 @@ #include "BLI_listbase.h" #include "bmesh.h" +#include "bmesh_structure.h" static void recount_totsels(BMesh *bm) { @@ -69,6 +70,69 @@ static void recount_totsels(BMesh *bm) } } +/** \name BMesh helper functions for selection flushing. + * \{ */ + +static bool bm_vert_is_edge_select_any_other(BMVert *v, BMEdge *e_first) +{ + BMEdge *e_iter = e_first; + + /* start by stepping over the current edge */ + while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first) { + if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) { + return true; + } + } + return false; +} + +#if 0 +static bool bm_vert_is_edge_select_any(BMVert *v) +{ + if (v->e) { + BMEdge *e_iter, *e_first; + e_iter = e_first = v->e; + do { + if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) { + return true; + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); + } + return false; +} +#endif + +static bool bm_edge_is_face_select_any_other(BMLoop *l_first) +{ + BMLoop *l_iter = l_first; + + /* start by stepping over the current face */ + while ((l_iter = l_iter->radial_next) != l_first) { + if (BM_elem_flag_test(l_iter->f, BM_ELEM_SELECT)) { + return true; + } + } + return false; +} + +#if 0 +static bool bm_edge_is_face_select_any(BMEdge *e) +{ + if (e->l) { + BMLoop *l_iter, *l_first; + l_iter = l_first = e->l; + do { + if (BM_elem_flag_test(l_iter->f, BM_ELEM_SELECT)) { + return true; + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + return false; +} +#endif + +/** \} */ + /** * \brief Select Mode Clean * @@ -341,8 +405,8 @@ void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select) if (select) { if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) { - bm->totvertsel += 1; BM_elem_flag_enable(v, BM_ELEM_SELECT); + bm->totvertsel += 1; } } else { @@ -367,39 +431,27 @@ void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select) } if (select) { - if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel += 1; - - BM_elem_flag_enable(e, BM_ELEM_SELECT); + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + bm->totedgesel += 1; + } BM_vert_select_set(bm, e->v1, true); BM_vert_select_set(bm, e->v2, true); } else { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) bm->totedgesel -= 1; - BM_elem_flag_disable(e, BM_ELEM_SELECT); + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + bm->totedgesel -= 1; + } if ((bm->selectmode & SCE_SELECT_VERTEX) == 0) { - BMIter iter; - BMVert *verts[2] = {e->v1, e->v2}; - BMEdge *e2; int i; /* check if the vert is used by a selected edge */ for (i = 0; i < 2; i++) { - bool deselect = true; - - for (e2 = BM_iter_new(&iter, bm, BM_EDGES_OF_VERT, verts[i]); e2; e2 = BM_iter_step(&iter)) { - if (e2 == e) { - continue; - } - - if (BM_elem_flag_test(e2, BM_ELEM_SELECT)) { - deselect = false; - break; - } - } - - if (deselect) { - BM_vert_select_set(bm, verts[i], false); + BMVert *v = *((&e->v1) + i); + if (bm_vert_is_edge_select_any_other(v, e) == false) { + BM_vert_select_set(bm, v, false); } } } @@ -430,10 +482,10 @@ void BM_face_select_set(BMesh *bm, BMFace *f, const bool select) if (select) { if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { - bm->totfacesel++; + BM_elem_flag_enable(f, BM_ELEM_SELECT); + bm->totfacesel += 1; } - BM_elem_flag_enable(f, BM_ELEM_SELECT); l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { BM_vert_select_set(bm, l_iter->v, true); @@ -441,42 +493,80 @@ void BM_face_select_set(BMesh *bm, BMFace *f, const bool select) } while ((l_iter = l_iter->next) != l_first); } else { - BMIter liter; - BMLoop *l; - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) bm->totfacesel -= 1; - BM_elem_flag_disable(f, BM_ELEM_SELECT); + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + bm->totfacesel -= 1; + } /* flush down to edges */ - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMIter fiter; - BMFace *f2; - BM_ITER_ELEM (f2, &fiter, l->e, BM_FACES_OF_EDGE) { - if (BM_elem_flag_test(f2, BM_ELEM_SELECT)) - break; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + /* vertex flushing is handled below */ + if (bm_edge_is_face_select_any_other(l_iter) == false) { + BM_edge_select_set_noflush(bm, l_iter->e, false); } + } while ((l_iter = l_iter->next) != l_first); - if (!f2) { - BM_edge_select_set(bm, l->e, false); + /* flush down to verts */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (bm_vert_is_edge_select_any_other(l_iter->v, l_iter->e) == false) { + BM_vert_select_set(bm, l_iter->v, false); } + } while ((l_iter = l_iter->next) != l_first); + } +} + +/** \name Non flushing versions element selection. + * \{ */ + +void BM_edge_select_set_noflush(BMesh *bm, BMEdge *e, const bool select) +{ + BLI_assert(e->head.htype == BM_EDGE); + + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_enable(e, BM_ELEM_SELECT); + bm->totedgesel += 1; } + } + else { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_disable(e, BM_ELEM_SELECT); + bm->totedgesel -= 1; + } + } +} - /* flush down to verts */ - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMIter eiter; - BMEdge *e; - BM_ITER_ELEM (e, &eiter, l->v, BM_EDGES_OF_VERT) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) - break; - } +void BM_face_select_set_noflush(BMesh *bm, BMFace *f, const bool select) +{ + BLI_assert(f->head.htype == BM_FACE); - if (!e) { - BM_vert_select_set(bm, l->v, false); - } + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + return; + } + + if (select) { + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_elem_flag_enable(f, BM_ELEM_SELECT); + bm->totfacesel += 1; + } + } + else { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_elem_flag_disable(f, BM_ELEM_SELECT); + bm->totfacesel -= 1; } } } +/** \} */ + /** * Select Mode Set * diff --git a/source/blender/bmesh/intern/bmesh_marking.h b/source/blender/bmesh/intern/bmesh_marking.h index 9e0c0923164..15f972c6435 100644 --- a/source/blender/bmesh/intern/bmesh_marking.h +++ b/source/blender/bmesh/intern/bmesh_marking.h @@ -59,6 +59,10 @@ void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select); void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select); void BM_face_select_set(BMesh *bm, BMFace *f, const bool select); +/* lower level functions which don't do flushing */ +void BM_edge_select_set_noflush(BMesh *bm, BMEdge *e, const bool select); +void BM_face_select_set_noflush(BMesh *bm, BMFace *e, const bool select); + void BM_mesh_select_mode_clean_ex(BMesh *bm, const short selectmode); void BM_mesh_select_mode_clean(BMesh *bm); diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index b16ea42304a..9a2869b64ef 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -34,10 +34,12 @@ #include "BLI_linklist_stack.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_stack.h" #include "BLI_utildefines.h" #include "BKE_cdderivedmesh.h" #include "BKE_editmesh.h" +#include "BKE_mesh.h" #include "BKE_multires.h" #include "intern/bmesh_private.h" @@ -438,7 +440,8 @@ void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (* static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float split_angle, float (*r_lnos)[3]) { - BMIter eiter; + BMIter eiter, viter; + BMVert *v; BMEdge *e; int i; @@ -450,15 +453,18 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo { char htype = BM_LOOP; - if (vnos) { - htype |= BM_VERT; - } if (fnos) { htype |= BM_FACE; } BM_mesh_elem_index_ensure(bm, htype); } + /* Clear all vertices' tags (means they are all smooth for now). */ + BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { + BM_elem_index_set(v, i); /* set_inline */ + BM_elem_flag_disable(v, BM_ELEM_TAG); + } + /* This first loop checks which edges are actually smooth, and pre-populate lnos with vnos (as if they were * all smooth). */ @@ -481,13 +487,13 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo * If the angle between both its polys' normals is below split_angle value, * and it is tagged as such, * and both its faces are smooth, - * and both its faces have compatible (non-flipped) normals, i.e. both loops on the same edge do not share - * the same vertex. + * and both its faces have compatible (non-flipped) normals, + * i.e. both loops on the same edge do not share the same vertex. */ if (is_angle_smooth && - BM_elem_flag_test_bool(e, BM_ELEM_SMOOTH) && - BM_elem_flag_test_bool(l_a->f, BM_ELEM_SMOOTH) && - BM_elem_flag_test_bool(l_b->f, BM_ELEM_SMOOTH) && + BM_elem_flag_test(e, BM_ELEM_SMOOTH) && + BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) && + BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) { const float *no; @@ -499,20 +505,40 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const float (*vnos)[3], const flo no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no; copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no); } + else { + /* Sharp edge, tag its verts as such. */ + BM_elem_flag_enable(e->v1, BM_ELEM_TAG); + BM_elem_flag_enable(e->v2, BM_ELEM_TAG); + } + } + else { + /* Sharp edge, tag its verts as such. */ + BM_elem_flag_enable(e->v1, BM_ELEM_TAG); + BM_elem_flag_enable(e->v2, BM_ELEM_TAG); } } - bm->elem_index_dirty &= ~BM_EDGE; + bm->elem_index_dirty &= ~(BM_EDGE | BM_VERT); } -/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c */ -static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3]) +/* BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c + * Will use first clnors_data array, and fallback to cd_loop_clnors_offset (use NULL and -1 to not use clnors). */ +static void bm_mesh_loops_calc_normals( + BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset) { BMIter fiter; BMFace *f_curr; + const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); + + MLoopNorSpaceArray _lnors_spacearr = {NULL}; /* Temp normal stack. */ BLI_SMALLSTACK_DECLARE(normal, float *); + /* Temp clnors stack. */ + BLI_SMALLSTACK_DECLARE(clnors, short *); + /* Temp edge vectors stack, only used when computing lnor spacearr. */ + BLI_Stack *edge_vectors = NULL; { char htype = BM_LOOP; @@ -525,6 +551,15 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const BM_mesh_elem_index_ensure(bm, htype); } + if (!r_lnors_spacearr && has_clnors) { + /* We need to compute lnor spacearr if some custom lnor data are given to us! */ + r_lnors_spacearr = &_lnors_spacearr; + } + if (r_lnors_spacearr) { + BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop); + edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); + } + /* We now know edges that can be smoothed (they are tagged), and edges that will be hard (they aren't). * Now, time to generate the normals. */ @@ -533,8 +568,10 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr); do { - if (BM_elem_flag_test_bool(l_curr->e, BM_ELEM_TAG)) { - /* A smooth edge. + if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && + (!r_lnors_spacearr || BM_elem_flag_test(l_curr->v, BM_ELEM_TAG))) + { + /* A smooth edge, and we are not generating lnors_spacearr, or the related vertex is sharp. * We skip it because it is either: * - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit * one of its ends, i.e. one of its two sharp edges), or... @@ -542,12 +579,45 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const * are just fine! */ } - else if (!BM_elem_flag_test_bool(l_curr->prev->e, BM_ELEM_TAG)) { + else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && + !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) + { /* Simple case (both edges around that vertex are sharp in related polygon), * this vertex just takes its poly normal. */ + const int l_curr_index = BM_elem_index_get(l_curr); const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no; - copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no); + copy_v3_v3(r_lnos[l_curr_index], no); + + /* If needed, generate this (simple!) lnor space. */ + if (r_lnors_spacearr) { + float vec_curr[3], vec_prev[3]; + MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr); + + { + const BMVert *v_pivot = l_curr->v; + const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; + const BMVert *v_1 = BM_edge_other_vert(l_curr->e, v_pivot); + const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co; + const BMVert *v_2 = BM_edge_other_vert(l_curr->prev->e, v_pivot); + const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + + sub_v3_v3v3(vec_curr, co_1, co_pivot); + normalize_v3(vec_curr); + sub_v3_v3v3(vec_prev, co_2, co_pivot); + normalize_v3(vec_prev); + } + + BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL); + /* We know there is only one loop in this space, no need to create a linklist in this case... */ + BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, false); + + if (has_clnors) { + short (*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] : + BM_ELEM_CD_GET_VOID_P(l_curr, cd_loop_clnors_offset); + BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]); + } + } } /* We *do not need* to check/tag loops as already computed! * Due to the fact a loop only links to one of its two edges, a same fan *will never be walked more than @@ -567,13 +637,26 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const */ BMVert *v_pivot = l_curr->v; BMEdge *e_next; + const BMEdge *e_org = l_curr->e; BMLoop *lfan_pivot, *lfan_pivot_next; + int lfan_pivot_index; float lnor[3] = {0.0f, 0.0f, 0.0f}; - float vec_curr[3], vec_next[3]; + float vec_curr[3], vec_next[3], vec_org[3]; + + /* We validate clnors data on the fly - cheapest way to do! */ + int clnors_avg[2] = {0, 0}; + short (*clnor_ref)[2] = NULL; + int clnors_nbr = 0; + bool clnors_invalid = false; const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; + MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) : NULL; + + BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors)); + lfan_pivot = l_curr; + lfan_pivot_index = BM_elem_index_get(lfan_pivot); e_next = lfan_pivot->e; /* Current edge here, actually! */ /* Only need to compute previous edge's vector once, then we can just reuse old current one! */ @@ -581,8 +664,13 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot); const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; - sub_v3_v3v3(vec_curr, co_2, co_pivot); - normalize_v3(vec_curr); + sub_v3_v3v3(vec_org, co_2, co_pivot); + normalize_v3(vec_org); + copy_v3_v3(vec_curr, vec_org); + + if (r_lnors_spacearr) { + BLI_stack_push(edge_vectors, vec_org); + } } while (true) { @@ -617,12 +705,38 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no; /* Accumulate */ madd_v3_v3fl(lnor, no, fac); + + if (has_clnors) { + /* Accumulate all clnors, if they are not all equal we have to fix that! */ + short (*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] : + BM_ELEM_CD_GET_VOID_P(lfan_pivot, cd_loop_clnors_offset); + if (clnors_nbr) { + clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]); + } + else { + clnor_ref = clnor; + } + clnors_avg[0] += (*clnor)[0]; + clnors_avg[1] += (*clnor)[1]; + clnors_nbr++; + /* We store here a pointer to all custom lnors processed. */ + BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); + } } /* We store here a pointer to all loop-normals processed. */ - BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[BM_elem_index_get(lfan_pivot)]); + BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]); + + if (r_lnors_spacearr) { + /* Assign current lnor space to current 'vertex' loop. */ + BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, lfan_pivot_index, true); + if (e_next != e_org) { + /* We store here all edges-normalized vectors processed. */ + BLI_stack_push(edge_vectors, vec_next); + } + } - if (!BM_elem_flag_test_bool(e_next, BM_ELEM_TAG)) { + if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { /* Next edge is sharp, we have finished with this fan of faces around this vert! */ break; } @@ -631,23 +745,105 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const copy_v3_v3(vec_curr, vec_next); /* Next pivot loop to current one. */ lfan_pivot = lfan_pivot_next; + lfan_pivot_index = BM_elem_index_get(lfan_pivot); } - /* In case we get a zero normal here, just use vertex normal already set! */ - if (LIKELY(normalize_v3(lnor) != 0.0f)) { - /* Copy back the final computed normal into all related loop-normals. */ - float *nor; - while ((nor = BLI_SMALLSTACK_POP(normal))) { - copy_v3_v3(nor, lnor); + { + float lnor_len = normalize_v3(lnor); + + /* If we are generating lnor spacearr, we can now define the one for this fan. */ + if (r_lnors_spacearr) { + if (UNLIKELY(lnor_len == 0.0f)) { + /* Use vertex normal as fallback! */ + copy_v3_v3(lnor, r_lnos[lfan_pivot_index]); + lnor_len = 1.0f; + } + + BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors); + + if (has_clnors) { + if (clnors_invalid) { + short *clnor; + + clnors_avg[0] /= clnors_nbr; + clnors_avg[1] /= clnors_nbr; + /* Fix/update all clnors of this fan with computed average value. */ + printf("Invalid clnors in this fan!\n"); + while ((clnor = BLI_SMALLSTACK_POP(clnors))) { + //print_v2("org clnor", clnor); + clnor[0] = (short)clnors_avg[0]; + clnor[1] = (short)clnors_avg[1]; + } + //print_v2("new clnors", clnors_avg); + } + else { + /* We still have to consume the stack! */ + while (BLI_SMALLSTACK_POP(clnors)); + } + BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); + } + } + + /* In case we get a zero normal here, just use vertex normal already set! */ + if (LIKELY(lnor_len != 0.0f)) { + /* Copy back the final computed normal into all related loop-normals. */ + float *nor; + + while ((nor = BLI_SMALLSTACK_POP(normal))) { + copy_v3_v3(nor, lnor); + } + } + else { + /* We still have to consume the stack! */ + while (BLI_SMALLSTACK_POP(normal)); } } - else { - /* We still have to clear the stack! */ - while (BLI_SMALLSTACK_POP(normal)); + + /* Tag related vertex as sharp, to avoid fanning around it again (in case it was a smooth one). */ + if (r_lnors_spacearr) { + BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG); } } } while ((l_curr = l_curr->next) != l_first); } + + if (r_lnors_spacearr) { + BLI_stack_free(edge_vectors); + if (r_lnors_spacearr == &_lnors_spacearr) { + BKE_lnor_spacearr_free(r_lnors_spacearr); + } + } +} + +static void bm_mesh_loops_calc_normals_no_autosmooth( + BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], float (*r_lnos)[3]) +{ + BMIter fiter; + BMFace *f_curr; + + { + char htype = BM_LOOP; + if (vnos) { + htype |= BM_VERT; + } + if (fnos) { + htype |= BM_FACE; + } + BM_mesh_elem_index_ensure(bm, htype); + } + + BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) { + BMLoop *l_curr, *l_first; + const bool is_face_flat = !BM_elem_flag_test(f_curr, BM_ELEM_SMOOTH); + + l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr); + do { + const float *no = is_face_flat ? (fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no) : + (vnos ? vnos[BM_elem_index_get(l_curr->v)] : l_curr->v->no); + copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no); + + } while ((l_curr = l_curr->next) != l_first); + } } #if 0 /* Unused currently */ @@ -657,13 +853,24 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, const float (*vcos)[3], const * Updates the loop normals of a mesh. Assumes vertex and face normals are valid (else call BM_mesh_normals_update() * first)! */ -void BM_mesh_loop_normals_update(BMesh *bm, const float split_angle, float (*r_lnos)[3]) +void BM_mesh_loop_normals_update( + BMesh *bm, const bool use_split_normals, const float split_angle, float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset) { - /* Tag smooth edges and set lnos from vnos when they might be completely smooth... */ - bm_mesh_edges_sharp_tag(bm, NULL, NULL, split_angle, r_lnos); + const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); + + if (use_split_normals) { + /* Tag smooth edges and set lnos from vnos when they might be completely smooth... + * When using custom loop normals, disable the angle feature! */ + bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos); - /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ - bm_mesh_loops_calc_normals(bm, NULL, NULL, r_lnos); + /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ + bm_mesh_loops_calc_normals(bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset); + } + else { + BLI_assert(!r_lnors_spacearr); + bm_mesh_loops_calc_normals_no_autosmooth(bm, NULL, NULL, r_lnos); + } } #endif @@ -673,14 +880,25 @@ void BM_mesh_loop_normals_update(BMesh *bm, const float split_angle, float (*r_l * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals'). * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry (splitting edges). */ -void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], - const float split_angle, float (*r_lnos)[3]) +void BM_loops_calc_normal_vcos( + BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3], + const bool use_split_normals, const float split_angle, float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset) { - /* Tag smooth edges and set lnos from vnos when they might be completely smooth... */ - bm_mesh_edges_sharp_tag(bm, vnos, fnos, split_angle, r_lnos); + const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); + + if (use_split_normals) { + /* Tag smooth edges and set lnos from vnos when they might be completely smooth... + * When using custom loop normals, disable the angle feature! */ + bm_mesh_edges_sharp_tag(bm, vnos, fnos, has_clnors ? (float)M_PI : split_angle, r_lnos); - /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ - bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos); + /* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */ + bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset); + } + else { + BLI_assert(!r_lnors_spacearr); + bm_mesh_loops_calc_normals_no_autosmooth(bm, vnos, fnos, r_lnos); + } } static void UNUSED_FUNCTION(bm_mdisps_space_set)(Object *ob, BMesh *bm, int from, int to) diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 22e50502aee..bac5da8347e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -28,6 +28,7 @@ */ struct BMAllocTemplate; +struct MLoopNorSpaceArray; void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); @@ -39,8 +40,10 @@ void BM_mesh_clear(BMesh *bm); void BM_mesh_normals_update(BMesh *bm); void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], float (*vnos)[3]); -void BM_loops_calc_normal_vcos(BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3], - const float split_angle, float (*r_lnos)[3]); +void BM_loops_calc_normal_vcos( + BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3], + const bool use_split_normals, const float split_angle, float (*r_lnos)[3], + struct MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset); void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag); void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag); diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 114358884b7..d0679b9919a 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -933,6 +933,28 @@ static BMOpDefine bmo_connect_verts_def = { }; /* + * Connect Verts to form Convex Faces. + * + * Ensures all faces are convex **faces**. + */ +static BMOpDefine bmo_connect_verts_concave_def = { + "connect_verts_concave", + /* slots_in */ + {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, + {{'\0'}}, + }, + /* slots_out */ + {{"edges.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, + {"faces.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, + {{'\0'}}, + }, + bmo_connect_verts_concave_exec, + (BMO_OPTYPE_FLAG_UNTAN_MULTIRES | + BMO_OPTYPE_FLAG_NORMALS_CALC | + BMO_OPTYPE_FLAG_SELECT_FLUSH), +}; + +/* * Connect Verts Across non Planer Faces. * * Split faces by connecting edges along non planer **faces**. @@ -1950,6 +1972,7 @@ const BMOpDefine *bmo_opdefines[] = { &bmo_collapse_def, &bmo_collapse_uvs_def, &bmo_connect_verts_def, + &bmo_connect_verts_concave_def, &bmo_connect_verts_nonplanar_def, &bmo_connect_vert_pair_def, &bmo_contextual_create_def, diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h index 9c1b7085835..979f7d2640a 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -41,6 +41,7 @@ void bmo_bridge_loops_exec(BMesh *bm, BMOperator *op); void bmo_collapse_exec(BMesh *bm, BMOperator *op); void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op); void bmo_connect_verts_exec(BMesh *bm, BMOperator *op); +void bmo_connect_verts_concave_exec(BMesh *bm, BMOperator *op); void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op); void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op); void bmo_contextual_create_exec(BMesh *bm, BMOperator *op); diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index e4aa4bb0c0e..bc06ba2c9b1 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -743,7 +743,9 @@ bool BM_face_point_inside_test(BMFace *f, const float co[3]) void BM_face_triangulate( BMesh *bm, BMFace *f, BMFace **r_faces_new, - int *r_faces_new_tot, + int *r_faces_new_tot, + BMEdge **r_edges_new, + int *r_edges_new_tot, const int quad_method, const int ngon_method, const bool use_tag, @@ -755,6 +757,7 @@ void BM_face_triangulate( BMLoop *l_iter, *l_first, *l_new; BMFace *f_new; int nf_i = 0; + int ne_i = 0; bool use_beauty = (ngon_method == MOD_TRIANGULATE_NGON_BEAUTY); BLI_assert(BM_face_is_normal_valid(f)); @@ -835,6 +838,9 @@ void BM_face_triangulate( if (r_faces_new) { r_faces_new[nf_i++] = f_new; } + if (r_edges_new) { + r_edges_new[ne_i++] = l_new->e; + } } else if (f->len > 4) { @@ -895,8 +901,7 @@ void BM_face_triangulate( } } - /* we know any edge that we create and _isnt_ */ - if (use_tag) { + if (use_tag || r_edges_new) { /* new faces loops */ l_iter = l_first = l_new; do { @@ -906,7 +911,12 @@ void BM_face_triangulate( bool is_new_edge = (l_iter == l_iter->radial_next); if (is_new_edge) { - BM_elem_flag_enable(e, BM_ELEM_TAG); + if (use_tag) { + BM_elem_flag_enable(e, BM_ELEM_TAG); + } + if (r_edges_new) { + r_edges_new[ne_i++] = e; + } } /* note, never disable tag's */ } while ((l_iter = l_iter->next) != l_first); @@ -925,6 +935,10 @@ void BM_face_triangulate( if (r_faces_new_tot) { *r_faces_new_tot = nf_i; } + + if (r_edges_new_tot) { + *r_edges_new_tot = ne_i; + } } /** diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index b25a7dbaa55..9980b59a298 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -63,6 +63,8 @@ void BM_face_triangulate( BMesh *bm, BMFace *f, BMFace **r_faces_new, int *r_faces_new_tot, + BMEdge **r_edges_new, + int *r_edges_new_tot, const int quad_method, const int ngon_method, const bool use_tag, struct MemArena *pf_arena, diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index f56cf76c41d..4d2361afa3a 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -663,7 +663,7 @@ BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e_first) /** * Returns edge length */ -float BM_edge_calc_length(BMEdge *e) +float BM_edge_calc_length(const BMEdge *e) { return len_v3v3(e->v1->co, e->v2->co); } @@ -671,7 +671,7 @@ float BM_edge_calc_length(BMEdge *e) /** * Returns edge length squared (for comparisons) */ -float BM_edge_calc_length_squared(BMEdge *e) +float BM_edge_calc_length_squared(const BMEdge *e) { return len_squared_v3v3(e->v1->co, e->v2->co); } @@ -731,9 +731,9 @@ bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb) /** * Fast alternative to ``(BM_vert_edge_count(v) == 2)`` */ -bool BM_vert_is_edge_pair(BMVert *v) +bool BM_vert_is_edge_pair(const BMVert *v) { - BMEdge *e = v->e; + const BMEdge *e = v->e; if (e) { const BMDiskLink *dl = bmesh_disk_edge_link_from_vert(e, v); return (dl->next == dl->prev); diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h index 5e6d4b5154b..abff55719f5 100644 --- a/source/blender/bmesh/intern/bmesh_queries.h +++ b/source/blender/bmesh/intern/bmesh_queries.h @@ -37,8 +37,8 @@ BLI_INLINE bool BM_edge_in_loop(const BMEdge *e, const BMLoop *l) ATTR_WARN_U BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_verts_in_edge(const BMVert *v1, const BMVert *v2, const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BM_edge_calc_length(BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -float BM_edge_calc_length_squared(BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_edge_calc_length(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +float BM_edge_calc_length_squared(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb) ATTR_NONNULL(); bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb) ATTR_NONNULL(); BLI_INLINE BMVert *BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -70,7 +70,7 @@ int BM_edge_face_count(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL int BM_vert_face_count(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMEdge *BM_vert_other_disk_edge(BMVert *v, BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -bool BM_vert_is_edge_pair(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_vert_is_edge_pair(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_vert_is_wire(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/bmesh/operators/bmo_connect_concave.c b/source/blender/bmesh/operators/bmo_connect_concave.c new file mode 100644 index 00000000000..a00f65bd10f --- /dev/null +++ b/source/blender/bmesh/operators/bmo_connect_concave.c @@ -0,0 +1,219 @@ +/* + * ***** 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/bmesh/operators/bmo_connect_concave.c + * \ingroup bmesh + * + * Connect vertices so all resulting faces are convex. + * + * Implementation: + * + * - triangulate all concave face (tagging convex verts), + * - rotate edges (beautify) so edges will connect nearby verts. + * - sort long edges (longest first), + * put any edges between 2 convex verts last since they often split convex regions. + * - merge the sorted edges as long as they don't create convex ngons. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_alloca.h" +#include "BLI_memarena.h" +#include "BLI_heap.h" +#include "BLI_polyfill2d.h" +#include "BLI_polyfill2d_beautify.h" +#include "BLI_edgehash.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +#define EDGE_OUT (1 << 0) +#define FACE_OUT (1 << 1) + +static int bm_edge_length_cmp(const void *a_, const void *b_) +{ + const BMEdge *e_a = *(const void **)a_; + const BMEdge *e_b = *(const void **)b_; + + int e_a_concave = ((BM_elem_flag_test(e_a->v1, BM_ELEM_TAG)) && (BM_elem_flag_test(e_a->v2, BM_ELEM_TAG))); + int e_b_concave = ((BM_elem_flag_test(e_b->v1, BM_ELEM_TAG)) && (BM_elem_flag_test(e_b->v2, BM_ELEM_TAG))); + + /* merge edges between concave edges last since these + * are most likely to remain and be the main dividers */ + if (e_a_concave < e_b_concave) return -1; + else if (e_a_concave > e_b_concave) return 1; + else { + /* otherwise shortest edges last */ + const float e_a_len = BM_edge_calc_length_squared(e_a); + const float e_b_len = BM_edge_calc_length_squared(e_b); + if (e_a_len < e_b_len) return 1; + else if (e_a_len > e_b_len) return -1; + else return 0; + } +} + +static bool bm_face_split_by_concave( + BMesh *bm, BMFace *f_base, const float eps, + + MemArena *pf_arena, + struct Heap *pf_heap, struct EdgeHash *pf_ehash) +{ + const int f_base_len = f_base->len; + int faces_array_tot = f_base->len - 3; + int edges_array_tot = f_base->len - 3; + BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot); + BMEdge **edges_array = BLI_array_alloca(edges_array, edges_array_tot); + const int quad_method = 0, ngon_method = 0; /* beauty */ + + float normal[3]; + BLI_assert(f_base->len > 3); + + copy_v3_v3(normal, f_base->no); + + BM_face_triangulate( + bm, f_base, + faces_array, &faces_array_tot, + edges_array, &edges_array_tot, + quad_method, ngon_method, false, + pf_arena, + pf_heap, pf_ehash); + + BLI_assert(edges_array_tot <= f_base_len - 3); + + if (faces_array_tot) { + int i; + for (i = 0; i < faces_array_tot; i++) { + BMFace *f = faces_array[i]; + BMO_elem_flag_enable(bm, f, FACE_OUT); + } + } + BMO_elem_flag_enable(bm, f_base, FACE_OUT); + + if (edges_array_tot) { + int i; + + qsort(edges_array, edges_array_tot, sizeof(*edges_array), bm_edge_length_cmp); + + for (i = 0; i < edges_array_tot; i++) { + BMLoop *l_pair[2]; + BMEdge *e = edges_array[i]; + BMO_elem_flag_enable(bm, e, EDGE_OUT); + + if (BM_edge_is_contiguous(e) && + BM_edge_loop_pair(e, &l_pair[0], &l_pair[1])) + { + bool ok = true; + int j; + for (j = 0; j < 2; j++) { + BMLoop *l = l_pair[j]; + + /* check that merging the edge (on this side) + * wouldn't result in a convex face-loop. + * + * This is the (l->next, l->prev) we would have once joined. + */ + float cross[3]; + cross_tri_v3( + cross, + l->v->co, + l->radial_next->next->next->v->co, + l->prev->v->co + ); + + if (dot_v3v3(cross, normal) <= eps) { + ok = false; + break; + } + } + + if (ok) { + BMFace *f_new, *f_pair[2] = {l_pair[0]->f, l_pair[1]->f}; + f_new = BM_faces_join(bm, f_pair, 2, true); + if (f_new) { + BMO_elem_flag_enable(bm, f_new, FACE_OUT); + } + } + } + } + } + + BLI_heap_clear(pf_heap, NULL); + BLI_edgehash_clear_ex(pf_ehash, NULL, BLI_POLYFILL_ALLOC_NGON_RESERVE); + + return true; +} + +static bool bm_face_convex_tag_verts(BMFace *f) +{ + bool is_concave = false; + if (f->len > 3) { + const BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_loop_is_convex(l_iter) == false) { + is_concave = true; + BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); + } + else { + BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG); + } + } while ((l_iter = l_iter->next) != l_first); + } + return is_concave; +} + +void bmo_connect_verts_concave_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMFace *f; + bool changed = false; + + MemArena *pf_arena; + Heap *pf_heap; + EdgeHash *pf_ehash; + + pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__); + pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE); + pf_ehash = BLI_edgehash_new_ex(__func__, BLI_POLYFILL_ALLOC_NGON_RESERVE); + + BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { + if (f->len > 3 && bm_face_convex_tag_verts(f)) { + if (bm_face_split_by_concave( + bm, f, FLT_EPSILON, + pf_arena, pf_heap, pf_ehash)) + { + changed = true; + } + } + } + + if (changed) { + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT); + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT); + } + + BLI_memarena_free(pf_arena); + BLI_heap_free(pf_heap, NULL); + BLI_edgehash_free(pf_ehash, NULL); +} diff --git a/source/blender/bmesh/operators/bmo_connect_nonplanar.c b/source/blender/bmesh/operators/bmo_connect_nonplanar.c index 6859ce2060c..c9ce2c5f6b8 100644 --- a/source/blender/bmesh/operators/bmo_connect_nonplanar.c +++ b/source/blender/bmesh/operators/bmo_connect_nonplanar.c @@ -153,23 +153,11 @@ void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op) { BMOIter siter; BMFace *f; - int totface = 0, totloop = 0; + bool changed = false; BLI_LINKSTACK_DECLARE(fstack, BMFace *); const float angle_limit = BMO_slot_float_get(op->slots_in, "angle_limit"); - - BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { - if (f->len > 3) { - totface += 1; - totloop += f->len; - } - } - - if (totface == 0) { - return; - } - BLI_LINKSTACK_INIT(fstack); BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { @@ -188,11 +176,14 @@ void bmo_connect_verts_nonplanar_exec(BMesh *bm, BMOperator *op) BLI_LINKSTACK_PUSH(fstack, f_pair[j]); } } + changed = true; } } BLI_LINKSTACK_FREE(fstack); - BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT); - BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT); + if (changed) { + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT); + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT); + } } diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c index 8cd9ee14bcb..ecb41363761 100644 --- a/source/blender/bmesh/operators/bmo_dissolve.c +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -475,7 +475,7 @@ void bmo_dissolve_limit_exec(BMesh *bm, BMOperator *op) { BMOpSlot *einput = BMO_slot_get(op->slots_in, "edges"); BMOpSlot *vinput = BMO_slot_get(op->slots_in, "verts"); - const float angle_max = (float)M_PI / 2.0f; + const float angle_max = M_PI_2; const float angle_limit = min_ff(angle_max, BMO_slot_float_get(op->slots_in, "angle_limit")); const bool do_dissolve_boundaries = BMO_slot_bool_get(op->slots_in, "use_dissolve_boundaries"); const BMO_Delimit delimit = BMO_slot_int_get(op->slots_in, "delimit"); diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c index 88b53b63abb..aa92c3054cd 100644 --- a/source/blender/bmesh/operators/bmo_extrude.c +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -608,10 +608,10 @@ static void calc_solidify_normals(BMesh *bm) } else { /* only one face attached to that edge */ - /* an edge without another attached- the weight on this is - * undefined, M_PI / 2 is 90d in radians and that seems good enough */ + /* an edge without another attached- the weight on this is undefined, + * M_PI_2 is 90d in radians and that seems good enough */ copy_v3_v3(edge_normal, f1->no); - mul_v3_fl(edge_normal, M_PI / 2); + mul_v3_fl(edge_normal, M_PI_2); } add_v3_v3(e->v1->no, edge_normal); diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index 9c8b9ca160d..871bee64c19 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -529,7 +529,7 @@ static void bmesh_find_doubles_common(BMesh *bm, BMOperator *op, const float dist = BMO_slot_float_get(op->slots_in, "dist"); const float dist_sq = dist * dist; - const float dist3 = (M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */ + const float dist3 = ((float)M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */ /* Test whether keep_verts arg exists and is non-empty */ if (BMO_slot_exists(op->slots_in, "keep_verts")) { diff --git a/source/blender/bmesh/operators/bmo_similar.c b/source/blender/bmesh/operators/bmo_similar.c index 02462e380b4..5f8438919df 100644 --- a/source/blender/bmesh/operators/bmo_similar.c +++ b/source/blender/bmesh/operators/bmo_similar.c @@ -412,10 +412,10 @@ void bmo_similar_edges_exec(BMesh *bm, BMOperator *op) /* compute the angle between the two edges */ angle = angle_normalized_v3v3(e_ext[i].dir, e_ext[indices[idx]].dir); - if (angle > (float)(M_PI / 2.0)) /* use the smallest angle between the edges */ + if (angle > (float)M_PI_2) /* use the smallest angle between the edges */ angle = fabsf(angle - (float)M_PI); - if (angle / (float)(M_PI / 2.0) <= thresh) { + if (angle / (float)M_PI_2 <= thresh) { BMO_elem_flag_enable(bm, e, EDGE_MARK); cont = false; } diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c index 8a1569630fa..26b53a21709 100644 --- a/source/blender/bmesh/tools/bmesh_intersect.c +++ b/source/blender/bmesh/tools/bmesh_intersect.c @@ -541,7 +541,7 @@ static void bm_isect_tri_tri( if (((1 << i_b_e0) | (1 << i_b_e1)) & b_mask) continue; fac = line_point_factor_v3(fv_a[i_a]->co, fv_b[i_b_e0]->co, fv_b[i_b_e1]->co); - if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0 + s->epsilon.eps)) { + if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0f + s->epsilon.eps)) { float ix[3]; interp_v3_v3v3(ix, fv_b[i_b_e0]->co, fv_b[i_b_e1]->co, fac); if (len_squared_v3v3(ix, fv_a[i_a]->co) <= s->epsilon.eps2x_sq) { @@ -579,7 +579,7 @@ static void bm_isect_tri_tri( if (((1 << i_a_e0) | (1 << i_a_e1)) & a_mask) continue; fac = line_point_factor_v3(fv_b[i_b]->co, fv_a[i_a_e0]->co, fv_a[i_a_e1]->co); - if ((fac > 0.0 - s->epsilon.eps) && (fac < 1.0 + s->epsilon.eps)) { + if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0f + s->epsilon.eps)) { float ix[3]; interp_v3_v3v3(ix, fv_a[i_a_e0]->co, fv_a[i_a_e1]->co, fac); if (len_squared_v3v3(ix, fv_b[i_b]->co) <= s->epsilon.eps2x_sq) { diff --git a/source/blender/bmesh/tools/bmesh_triangulate.c b/source/blender/bmesh/tools/bmesh_triangulate.c index b76054f270f..404776a0769 100644 --- a/source/blender/bmesh/tools/bmesh_triangulate.c +++ b/source/blender/bmesh/tools/bmesh_triangulate.c @@ -64,7 +64,9 @@ static void bm_face_triangulate_mapping( BLI_assert(face->len > 3); BM_face_triangulate( - bm, face, faces_array, &faces_array_tot, + bm, face, + faces_array, &faces_array_tot, + NULL, NULL, quad_method, ngon_method, use_tag, pf_arena, pf_heap, pf_ehash); @@ -121,7 +123,9 @@ void BM_mesh_triangulate( if (face->len > 3) { if (tag_only == false || BM_elem_flag_test(face, BM_ELEM_TAG)) { BM_face_triangulate( - bm, face, NULL, NULL, + bm, face, + NULL, NULL, + NULL, NULL, quad_method, ngon_method, tag_only, pf_arena, pf_heap, pf_ehash); diff --git a/source/blender/collada/AnimationExporter.cpp b/source/blender/collada/AnimationExporter.cpp index f2c057d32d8..9077db524db 100644 --- a/source/blender/collada/AnimationExporter.cpp +++ b/source/blender/collada/AnimationExporter.cpp @@ -76,9 +76,9 @@ void AnimationExporter::operator()(Object *ob) else transformName = extract_transform_name(fcu->rna_path); - if ((!strcmp(transformName, "location") || !strcmp(transformName, "scale")) || - (!strcmp(transformName, "rotation_euler") && ob->rotmode == ROT_MODE_EUL) || - (!strcmp(transformName, "rotation_quaternion"))) + if ((STREQ(transformName, "location") || STREQ(transformName, "scale")) || + (STREQ(transformName, "rotation_euler") && ob->rotmode == ROT_MODE_EUL) || + (STREQ(transformName, "rotation_quaternion"))) { dae_animation(ob, fcu, transformName, false); } @@ -98,8 +98,8 @@ void AnimationExporter::operator()(Object *ob) while (fcu) { transformName = extract_transform_name(fcu->rna_path); - if ((!strcmp(transformName, "color")) || (!strcmp(transformName, "spot_size")) || - (!strcmp(transformName, "spot_blend")) || (!strcmp(transformName, "distance"))) + if ((STREQ(transformName, "color")) || (STREQ(transformName, "spot_size")) || + (STREQ(transformName, "spot_blend")) || (STREQ(transformName, "distance"))) { dae_animation(ob, fcu, transformName, true); } @@ -113,10 +113,10 @@ void AnimationExporter::operator()(Object *ob) while (fcu) { transformName = extract_transform_name(fcu->rna_path); - if ((!strcmp(transformName, "lens")) || - (!strcmp(transformName, "ortho_scale")) || - (!strcmp(transformName, "clip_end")) || - (!strcmp(transformName, "clip_start"))) + if ((STREQ(transformName, "lens")) || + (STREQ(transformName, "ortho_scale")) || + (STREQ(transformName, "clip_end")) || + (STREQ(transformName, "clip_start"))) { dae_animation(ob, fcu, transformName, true); } @@ -134,9 +134,9 @@ void AnimationExporter::operator()(Object *ob) while (fcu) { transformName = extract_transform_name(fcu->rna_path); - if ((!strcmp(transformName, "specular_hardness")) || (!strcmp(transformName, "specular_color")) || - (!strcmp(transformName, "diffuse_color")) || (!strcmp(transformName, "alpha")) || - (!strcmp(transformName, "ior"))) + if ((STREQ(transformName, "specular_hardness")) || (STREQ(transformName, "specular_color")) || + (STREQ(transformName, "diffuse_color")) || (STREQ(transformName, "alpha")) || + (STREQ(transformName, "ior"))) { dae_animation(ob, fcu, transformName, true, ma); } @@ -225,7 +225,7 @@ float *AnimationExporter::get_eul_source_for_quat(Object *ob) while (fcu) { char *transformName = extract_transform_name(fcu->rna_path); - if (!strcmp(transformName, "rotation_quaternion") ) { + if (STREQ(transformName, "rotation_quaternion") ) { for (int i = 0; i < fcu->totvert; i++) { *(quat + (i * 4) + fcu->array_index) = fcu->bezt[i].vec[1][1]; } @@ -278,17 +278,17 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa bool has_tangents = false; bool quatRotation = false; - if (!strcmp(transformName, "rotation_quaternion") ) { + if (STREQ(transformName, "rotation_quaternion") ) { fprintf(stderr, "quaternion rotation curves are not supported. rotation curve will not be exported\n"); quatRotation = true; return; } //axis names for colors - else if (!strcmp(transformName, "color") || - !strcmp(transformName, "specular_color") || - !strcmp(transformName, "diffuse_color") || - !strcmp(transformName, "alpha")) + else if (STREQ(transformName, "color") || + STREQ(transformName, "specular_color") || + STREQ(transformName, "diffuse_color") || + STREQ(transformName, "alpha")) { const char *axis_names[] = {"R", "G", "B"}; if (fcu->array_index < 3) @@ -296,10 +296,10 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa } //axis names for transforms - else if (!strcmp(transformName, "location") || - !strcmp(transformName, "scale") || - !strcmp(transformName, "rotation_euler") || - !strcmp(transformName, "rotation_quaternion")) + else if (STREQ(transformName, "location") || + STREQ(transformName, "scale") || + STREQ(transformName, "rotation_euler") || + STREQ(transformName, "rotation_quaternion")) { const char *axis_names[] = {"X", "Y", "Z"}; if (fcu->array_index < 3) @@ -357,7 +357,7 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa MEM_freeN(eul); MEM_freeN(eul_axis); } - else if (!strcmp(transformName, "lens") && (ob->type == OB_CAMERA)) { + else if (STREQ(transformName, "lens") && (ob->type == OB_CAMERA)) { output_id = create_lens_source_from_fcurve((Camera *) ob->data, COLLADASW::InputSemantic::OUTPUT, fcu, anim_id); } else { @@ -763,7 +763,7 @@ std::string AnimationExporter::create_source_from_fcurve(COLLADASW::InputSemanti { std::string source_id = anim_id + get_semantic_suffix(semantic); - //bool is_angle = !strcmp(fcu->rna_path, "rotation"); + //bool is_angle = STREQ(fcu->rna_path, "rotation"); bool is_angle = false; if (strstr(fcu->rna_path, "rotation") || strstr(fcu->rna_path,"spot_size")) is_angle = true; @@ -1103,13 +1103,13 @@ std::string AnimationExporter::get_light_param_sid(char *rna_path, int tm_type, if (rna_path) { char *name = extract_transform_name(rna_path); - if (!strcmp(name, "color")) + if (STREQ(name, "color")) tm_type = 1; - else if (!strcmp(name, "spot_size")) + else if (STREQ(name, "spot_size")) tm_type = 2; - else if (!strcmp(name, "spot_blend")) + else if (STREQ(name, "spot_blend")) tm_type = 3; - else if (!strcmp(name, "distance")) + else if (STREQ(name, "distance")) tm_type = 4; else tm_type = -1; @@ -1151,13 +1151,13 @@ std::string AnimationExporter::get_camera_param_sid(char *rna_path, int tm_type, if (rna_path) { char *name = extract_transform_name(rna_path); - if (!strcmp(name, "lens")) + if (STREQ(name, "lens")) tm_type = 0; - else if (!strcmp(name, "ortho_scale")) + else if (STREQ(name, "ortho_scale")) tm_type = 1; - else if (!strcmp(name, "clip_end")) + else if (STREQ(name, "clip_end")) tm_type = 2; - else if (!strcmp(name, "clip_start")) + else if (STREQ(name, "clip_start")) tm_type = 3; else @@ -1203,23 +1203,23 @@ std::string AnimationExporter::get_transform_sid(char *rna_path, int tm_type, co if (rna_path) { char *name = extract_transform_name(rna_path); - if (!strcmp(name, "rotation_euler")) + if (STREQ(name, "rotation_euler")) tm_type = 0; - else if (!strcmp(name, "rotation_quaternion")) + else if (STREQ(name, "rotation_quaternion")) tm_type = 1; - else if (!strcmp(name, "scale")) + else if (STREQ(name, "scale")) tm_type = 2; - else if (!strcmp(name, "location")) + else if (STREQ(name, "location")) tm_type = 3; - else if (!strcmp(name, "specular_hardness")) + else if (STREQ(name, "specular_hardness")) tm_type = 4; - else if (!strcmp(name, "specular_color")) + else if (STREQ(name, "specular_color")) tm_type = 5; - else if (!strcmp(name, "diffuse_color")) + else if (STREQ(name, "diffuse_color")) tm_type = 6; - else if (!strcmp(name, "alpha")) + else if (STREQ(name, "alpha")) tm_type = 7; - else if (!strcmp(name, "ior")) + else if (STREQ(name, "ior")) tm_type = 8; else @@ -1311,7 +1311,7 @@ void AnimationExporter::enable_fcurves(bAction *act, char *bone_name) for (fcu = (FCurve *)act->curves.first; fcu; fcu = fcu->next) { if (bone_name) { - if (!strncmp(fcu->rna_path, prefix, strlen(prefix))) + if (STREQLEN(fcu->rna_path, prefix, strlen(prefix))) fcu->flag &= ~FCURVE_DISABLED; else fcu->flag |= FCURVE_DISABLED; @@ -1378,11 +1378,11 @@ void AnimationExporter::find_frames(Object *ob, std::vector<float> &fra, const c FCurve *fcu = (FCurve *)ob->adt->action->curves.first; for (; fcu; fcu = fcu->next) { - if (prefix && strncmp(prefix, fcu->rna_path, strlen(prefix))) + if (prefix && !STREQLEN(prefix, fcu->rna_path, strlen(prefix))) continue; char *name = extract_transform_name(fcu->rna_path); - if (!strcmp(name, tm_name)) { + if (STREQ(name, tm_name)) { for (unsigned int i = 0; i < fcu->totvert; i++) { float f = fcu->bezt[i].vec[1][0]; if (std::find(fra.begin(), fra.end(), f) == fra.end()) diff --git a/source/blender/collada/AnimationImporter.cpp b/source/blender/collada/AnimationImporter.cpp index 6e2d337a32e..7e937e42787 100644 --- a/source/blender/collada/AnimationImporter.cpp +++ b/source/blender/collada/AnimationImporter.cpp @@ -649,7 +649,7 @@ void AnimationImporter:: Assign_float_animations(const COLLADAFW::UniqueId& list for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { FCurve *fcu = *iter; /* All anim_types whose values are to be converted from Degree to Radians can be ORed here */ - if (strcmp("spot_size", anim_type)==0) { + if (STREQ("spot_size", anim_type)) { /* NOTE: Do NOT convert if imported file was made by blender <= 2.69.10 * Reason: old blender versions stored spot_size in radians (was a bug) */ diff --git a/source/blender/collada/ArmatureExporter.cpp b/source/blender/collada/ArmatureExporter.cpp index 5ce62873377..36ab85b9b5b 100644 --- a/source/blender/collada/ArmatureExporter.cpp +++ b/source/blender/collada/ArmatureExporter.cpp @@ -181,7 +181,7 @@ void ArmatureExporter::add_bone_node(Bone *bone, Object *ob_arm, Scene *sce, std::list<Object *>::iterator i = child_objects.begin(); while (i != child_objects.end()) { - if ((*i)->partype == PARBONE && (0 == strcmp((*i)->parsubstr, bone->name))) { + if ((*i)->partype == PARBONE && STREQ((*i)->parsubstr, bone->name)) { float backup_parinv[4][4]; copy_m4_m4(backup_parinv, (*i)->parentinv); diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp index 7ffd300c6de..c2ee6170470 100644 --- a/source/blender/collada/ArmatureImporter.cpp +++ b/source/blender/collada/ArmatureImporter.cpp @@ -54,7 +54,7 @@ static EditBone *get_edit_bone(bArmature * armature, char *name) { EditBone *eBone; for (eBone = (EditBone *)armature->edbo->first; eBone; eBone = eBone->next) { - if (!strcmp(name, eBone->name)) + if (STREQ(name, eBone->name)) return eBone; } diff --git a/source/blender/collada/ControllerExporter.cpp b/source/blender/collada/ControllerExporter.cpp index 25fd9c81535..16c3f68bca4 100644 --- a/source/blender/collada/ControllerExporter.cpp +++ b/source/blender/collada/ControllerExporter.cpp @@ -473,7 +473,7 @@ static float get_property(Bone *bone, const char *key, float def) if (bone->prop) { IDProperty *property = IDP_GetPropertyFromGroup(bone->prop, key); if (property) { - switch(property->type) { + switch (property->type) { case IDP_INT: result = (float)(IDP_Int(property)); break; diff --git a/source/blender/collada/EffectExporter.cpp b/source/blender/collada/EffectExporter.cpp index 3c35618a4cd..13dc1eda580 100644 --- a/source/blender/collada/EffectExporter.cpp +++ b/source/blender/collada/EffectExporter.cpp @@ -133,7 +133,7 @@ void EffectsExporter::writeTextures(COLLADASW::EffectProfile &ep, if (!ima) return; // color - if (t->mapto & (MAP_COL | MAP_COLSPEC)) { + if (t->mapto & MAP_COL) { ep.setDiffuse(createTexture(ima, uvname, sampler), false, "diffuse"); } // ambient @@ -141,7 +141,7 @@ void EffectsExporter::writeTextures(COLLADASW::EffectProfile &ep, ep.setAmbient(createTexture(ima, uvname, sampler), false, "ambient"); } // specular - if (t->mapto & MAP_SPEC) { + if (t->mapto & (MAP_SPEC | MAP_COLSPEC)) { ep.setSpecular(createTexture(ima, uvname, sampler), false, "specular"); } // emission @@ -263,7 +263,7 @@ void EffectsExporter::operator()(Material *ma, Object *ob) COLLADASW::Sampler samplers[MAX_MTEX]; //COLLADASW::Surface surfaces[MAX_MTEX]; //void *samp_surf[MAX_MTEX][2]; - void *samp_surf[MAX_MTEX][1]; + void *samp_surf[MAX_MTEX]; // image to index to samp_surf map // samp_surf[index] stores 2 pointers, sampler and surface @@ -302,7 +302,7 @@ void EffectsExporter::operator()(Material *ma, Object *ob) //surfaces[a] = surface; // store pointers so they can be used later when we create <texture>s - samp_surf[b][0] = &samplers[a]; + samp_surf[b] = &samplers[a]; //samp_surf[b][1] = &surfaces[a]; im_samp_map[key] = b; @@ -349,7 +349,7 @@ void EffectsExporter::operator()(Material *ma, Object *ob) key + COLLADASW::Sampler::SURFACE_SID_SUFFIX); sampler.setImageId(key); samplers[a] = sampler; - samp_surf[b][0] = &samplers[a]; + samp_surf[b] = &samplers[a]; im_samp_map[key] = b; b++; a++; @@ -380,19 +380,19 @@ void EffectsExporter::operator()(Material *ma, Object *ob) key = translate_id(key); int i = im_samp_map[key]; std::string uvname = strlen(t->uvname) ? t->uvname : active_uv; - COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i][0]; + COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i]; writeTextures(ep, key, sampler, t, ima, uvname); } std::set<Image *>::iterator uv_t_iter; int idx; for (idx = 0, uv_t_iter = uv_textures.begin(); uv_t_iter != uv_textures.end(); uv_t_iter++, idx++ ) { - if(active_uv_layer>-1 && idx==active_uv_layer) { + if (active_uv_layer>-1 && idx==active_uv_layer) { Image *ima = *uv_t_iter; std::string key(id_name(ima)); key = translate_id(key); int i = im_samp_map[key]; - COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i][0]; + COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i]; ep.setDiffuse(createTexture(ima, active_uv, sampler), false, "diffuse"); } } diff --git a/source/blender/collada/ErrorHandler.cpp b/source/blender/collada/ErrorHandler.cpp index 854e8abd76d..b271604f839 100644 --- a/source/blender/collada/ErrorHandler.cpp +++ b/source/blender/collada/ErrorHandler.cpp @@ -34,6 +34,8 @@ #include <string.h> +#include "BLI_utildefines.h" + //-------------------------------------------------------------------- ErrorHandler::ErrorHandler() : mError(false) { @@ -55,13 +57,13 @@ bool ErrorHandler::handleError(const COLLADASaxFWL::IError *error) // Workaround to avoid wrong error if (parserError.getErrorType() == GeneratedSaxParser::ParserError::ERROR_VALIDATION_MIN_OCCURS_UNMATCHED) { - if (strcmp(parserError.getElement(), "effect") == 0) { + if (STREQ(parserError.getElement(), "effect")) { isError = false; } } if (parserError.getErrorType() == GeneratedSaxParser::ParserError::ERROR_VALIDATION_SEQUENCE_PREVIOUS_SIBLING_NOT_PRESENT) { - if (!((strcmp(parserError.getElement(), "extra") == 0) && - (strcmp(parserError.getAdditionalText().c_str(), "sibling: fx_profile_abstract") == 0))) + if (!(STREQ(parserError.getElement(), "extra") && + STREQ(parserError.getAdditionalText().c_str(), "sibling: fx_profile_abstract"))) { isError = false; } diff --git a/source/blender/collada/ImageExporter.cpp b/source/blender/collada/ImageExporter.cpp index 5fd5fab3492..a5c1493208b 100644 --- a/source/blender/collada/ImageExporter.cpp +++ b/source/blender/collada/ImageExporter.cpp @@ -89,7 +89,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies) // make absolute destination path BLI_strncpy(export_file, name.c_str(), sizeof(export_file)); - BKE_add_image_extension(export_file, &imageFormat); + BKE_image_path_ensure_ext_from_imformat(export_file, &imageFormat); BLI_join_dirfile(export_path, sizeof(export_path), export_dir, export_file); diff --git a/source/blender/collada/ImportSettings.cpp b/source/blender/collada/ImportSettings.cpp index 74607787f25..9483aa1ac76 100644 --- a/source/blender/collada/ImportSettings.cpp +++ b/source/blender/collada/ImportSettings.cpp @@ -20,7 +20,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/collada/ExportSettings.cpp +/** \file blender/collada/ImportSettings.cpp * \ingroup collada */ diff --git a/source/blender/collada/ImportSettings.h b/source/blender/collada/ImportSettings.h index 51a13da7724..783f58e6bff 100644 --- a/source/blender/collada/ImportSettings.h +++ b/source/blender/collada/ImportSettings.h @@ -20,7 +20,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file ExportSettings.h +/** \file ImportSettings.h * \ingroup collada */ diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp index a4bf1d28366..02e78b9bc45 100644 --- a/source/blender/collada/MeshImporter.cpp +++ b/source/blender/collada/MeshImporter.cpp @@ -227,8 +227,7 @@ void MeshImporter::set_vcol(MLoopCol *mlc, VCOLDataWrapper &vob, int loop_index, { COLLADAFW::UIntValuesArray& indices =index_list.getIndices(); int index; - for(index = 0; index < count; index++,mlc++) - { + for (index = 0; index < count; index++, mlc++) { int v_index = indices[index+loop_index]; vob.get_vcol(v_index,mlc); } @@ -708,8 +707,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) mpoly->flag |= ME_SMOOTH; } - for(unsigned int vcolor_index = 0 ; vcolor_index < index_list_array_vcolor.getCount();vcolor_index++) - { + for (unsigned int vcolor_index = 0 ; vcolor_index < index_list_array_vcolor.getCount();vcolor_index++) { COLLADAFW::IndexList& index_list = *index_list_array_vcolor[vcolor_index]; COLLADAFW::String colname = extract_vcolname(index_list.getName()); MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_named(&me->ldata, CD_MLOOPCOL, colname.c_str()); @@ -820,7 +818,7 @@ void MeshImporter::bmeshConversion() Mesh *me = (*m).second; BKE_mesh_tessface_clear(me); BKE_mesh_calc_normals(me); - //BKE_mesh_validate(me, 1); + /* BKE_mesh_validate(me, true, true); */ } } } @@ -1063,7 +1061,7 @@ MTFace *MeshImporter::assign_material_to_geom(COLLADAFW::MaterialBinding cmateri // set texture face if (color_texture && strlen((color_texture)->uvname) && - strcmp(layername, color_texture->uvname) != 0) { + !STREQ(layername, color_texture->uvname)) { texture_face = (MTFace *)CustomData_get_layer_named(&me->fdata, CD_MTFACE, color_texture->uvname); strcpy(layername, color_texture->uvname); diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 4bcdd4d9e34..048c974423f 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -90,8 +90,6 @@ set(SRC intern/COM_OpenCLDevice.h intern/COM_CompositorContext.cpp intern/COM_CompositorContext.h - intern/COM_ChannelInfo.cpp - intern/COM_ChannelInfo.h intern/COM_SingleThreadedOperation.cpp intern/COM_SingleThreadedOperation.h intern/COM_Debug.cpp diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index b60fffc6a22..9936914d3d8 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -105,7 +105,9 @@ typedef enum OrderOfChunks { #define COM_RULE_OF_THIRDS_DIVIDER 100.0f -#define COM_NUMBER_OF_CHANNELS 4 +#define COM_NUM_CHANNELS_VALUE 1 +#define COM_NUM_CHANNELS_VECTOR 3 +#define COM_NUM_CHANNELS_COLOR 4 #define COM_BLUR_BOKEH_PIXELS 512 diff --git a/source/blender/compositor/intern/COM_ChannelInfo.h b/source/blender/compositor/intern/COM_ChannelInfo.h deleted file mode 100644 index ec78e7e1cb1..00000000000 --- a/source/blender/compositor/intern/COM_ChannelInfo.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2011, Blender Foundation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Contributor: - * Jeroen Bakker - * Monique Dewanchand - */ - -#ifndef _COM_ChannelInfo_h -#define _COM_ChannelInfo_h - -#include <vector> -#include "BKE_text.h" -#include <string> -#include "DNA_node_types.h" -#include "BLI_rect.h" - -using namespace std; - -/** - * @brief List of possible channel types - * @ingroup Model - */ -typedef enum ChannelType { - COM_CT_ColorComponent /** @brief this channel is contains color information. Specific used is determined by channelnumber, and in the future color space */, - COM_CT_Alpha /** @brief this channel is contains transparency value */, - COM_CT_Value /** @brief this channel is contains a value */, - COM_CT_X /** @brief this channel is contains a X value */, - COM_CT_Y /** @brief this channel is contains a Y value */, - COM_CT_Z /** @brief this channel is contains a Z value */, - COM_CT_W /** @brief this channel is contains a W value */, - COM_CT_UNUSED /** @brief this channel is unused */ -} ChannelType; - -/** - * @brief ChannelInfo holds information about a channel. - * - * Channels are transported from node to node via a NodeLink. - * ChannelInfo holds specific setting of these channels in order that the to-node of the link - * Can handle specific logic per channel setting. - * - * @note currently this is not used, but a future place to implement color spacing and other things. - * @ingroup Model - */ -class ChannelInfo { -private: - /** - * @brief the channel number, in the link. [0-3] - */ - int m_number; - - /** - * @brief type of channel - */ - ChannelType m_type; - - /** - * @brieg Is this value in this channel premultiplied with its alpha - * @note only valid if type = ColorComponent; - */ - bool m_premultiplied; - -// /** -// * Color space of this value. -// * only valid when type = ColorComponent; -// */ -// string colorspacename; - -public: - /** - * @brief creates a new ChannelInfo and set default values - */ - ChannelInfo(); - - /** - * @brief set the index of this channel in the NodeLink - */ - void setNumber(const int number) { this->m_number = number; } - - /** - * @brief get the index of this channel in the NodeLink - */ - const int getNumber() const { return this->m_number; } - - /** - * @brief set the type of channel - */ - void setType(const ChannelType type) { this->m_type = type; } - - /** - * @brief get the type of channel - */ - const ChannelType getType() const { return this->m_type; } - - /** - * @brief set the premultiplicatioin of this channel - */ - void setPremultiplied(const bool premultiplied) { this->m_premultiplied = premultiplied; } - - /** - * @brief is this channel premultiplied - */ - const bool isPremultiplied() const { return this->m_premultiplied; } -}; - - -#endif diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cpp b/source/blender/compositor/intern/COM_ExecutionSystem.cpp index 7c08188db90..0667271f4b1 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cpp +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cpp @@ -137,11 +137,15 @@ void ExecutionSystem::execute() } unsigned int index; + // First allocale all write buffer for (index = 0; index < this->m_operations.size(); index++) { NodeOperation *operation = this->m_operations[index]; - operation->setbNodeTree(this->m_context.getbNodeTree()); - operation->initExecution(); + if (operation->isWriteBufferOperation()) { + operation->setbNodeTree(this->m_context.getbNodeTree()); + operation->initExecution(); + } } + // Connect read buffers to their write buffers for (index = 0; index < this->m_operations.size(); index++) { NodeOperation *operation = this->m_operations[index]; if (operation->isReadBufferOperation()) { @@ -149,6 +153,14 @@ void ExecutionSystem::execute() readOperation->updateMemoryBuffer(); } } + // initialize other operations + for (index = 0; index < this->m_operations.size(); index++) { + NodeOperation *operation = this->m_operations[index]; + if (!operation->isWriteBufferOperation()) { + operation->setbNodeTree(this->m_context.getbNodeTree()); + operation->initExecution(); + } + } for (index = 0; index < this->m_groups.size(); index++) { ExecutionGroup *executionGroup = this->m_groups[index]; executionGroup->setChunksize(this->m_context.getChunksize()); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cpp b/source/blender/compositor/intern/COM_MemoryBuffer.cpp index c59ecced93c..37035c50d2c 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cpp +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cpp @@ -27,6 +27,19 @@ using std::min; using std::max; +static unsigned int determine_num_channels(DataType datatype) +{ + switch (datatype) { + case COM_DT_VALUE: + return COM_NUM_CHANNELS_VALUE; + case COM_DT_VECTOR: + return COM_NUM_CHANNELS_VECTOR; + case COM_DT_COLOR: + default: + return COM_NUM_CHANNELS_COLOR; + } +} + unsigned int MemoryBuffer::determineBufferSize() { return getWidth() * getHeight(); @@ -34,61 +47,62 @@ unsigned int MemoryBuffer::determineBufferSize() int MemoryBuffer::getWidth() const { - return this->m_rect.xmax - this->m_rect.xmin; + return this->m_width; } int MemoryBuffer::getHeight() const { - return this->m_rect.ymax - this->m_rect.ymin; + return this->m_height; } MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, unsigned int chunkNumber, rcti *rect) { BLI_rcti_init(&this->m_rect, rect->xmin, rect->xmax, rect->ymin, rect->ymax); + this->m_width = BLI_rcti_size_x(&this->m_rect); + this->m_height = BLI_rcti_size_y(&this->m_rect); this->m_memoryProxy = memoryProxy; this->m_chunkNumber = chunkNumber; - this->m_buffer = (float *)MEM_mallocN_aligned(sizeof(float) * determineBufferSize() * COM_NUMBER_OF_CHANNELS, 16, "COM_MemoryBuffer"); + this->m_num_channels = determine_num_channels(memoryProxy->getDataType()); + this->m_buffer = (float *)MEM_mallocN_aligned(sizeof(float) * determineBufferSize() * this->m_num_channels, 16, "COM_MemoryBuffer"); this->m_state = COM_MB_ALLOCATED; - this->m_datatype = COM_DT_COLOR; - this->m_chunkWidth = this->m_rect.xmax - this->m_rect.xmin; + this->m_datatype = memoryProxy->getDataType();; } MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, rcti *rect) { BLI_rcti_init(&this->m_rect, rect->xmin, rect->xmax, rect->ymin, rect->ymax); + this->m_width = BLI_rcti_size_x(&this->m_rect); + this->m_height = BLI_rcti_size_y(&this->m_rect); this->m_memoryProxy = memoryProxy; this->m_chunkNumber = -1; - this->m_buffer = (float *)MEM_mallocN_aligned(sizeof(float) * determineBufferSize() * COM_NUMBER_OF_CHANNELS, 16, "COM_MemoryBuffer"); + this->m_num_channels = determine_num_channels(memoryProxy->getDataType()); + this->m_buffer = (float *)MEM_mallocN_aligned(sizeof(float) * determineBufferSize() * this->m_num_channels, 16, "COM_MemoryBuffer"); this->m_state = COM_MB_TEMPORARILY; - this->m_datatype = COM_DT_COLOR; - this->m_chunkWidth = this->m_rect.xmax - this->m_rect.xmin; + this->m_datatype = memoryProxy->getDataType(); +} +MemoryBuffer::MemoryBuffer(DataType dataType, rcti *rect) +{ + BLI_rcti_init(&this->m_rect, rect->xmin, rect->xmax, rect->ymin, rect->ymax); + this->m_width = BLI_rcti_size_x(&this->m_rect); + this->m_height = BLI_rcti_size_y(&this->m_rect); + this->m_height = this->m_rect.ymax - this->m_rect.ymin; + this->m_memoryProxy = NULL; + this->m_chunkNumber = -1; + this->m_num_channels = determine_num_channels(dataType); + this->m_buffer = (float *)MEM_mallocN_aligned(sizeof(float) * determineBufferSize() * this->m_num_channels, 16, "COM_MemoryBuffer"); + this->m_state = COM_MB_TEMPORARILY; + this->m_datatype = dataType; } MemoryBuffer *MemoryBuffer::duplicate() { MemoryBuffer *result = new MemoryBuffer(this->m_memoryProxy, &this->m_rect); - memcpy(result->m_buffer, this->m_buffer, this->determineBufferSize() * COM_NUMBER_OF_CHANNELS * sizeof(float)); + memcpy(result->m_buffer, this->m_buffer, this->determineBufferSize() * this->m_num_channels * sizeof(float)); return result; } void MemoryBuffer::clear() { - memset(this->m_buffer, 0, this->determineBufferSize() * COM_NUMBER_OF_CHANNELS * sizeof(float)); + memset(this->m_buffer, 0, this->determineBufferSize() * this->m_num_channels * sizeof(float)); } -float *MemoryBuffer::convertToValueBuffer() -{ - const unsigned int size = this->determineBufferSize(); - unsigned int i; - - float *result = (float *)MEM_mallocN(sizeof(float) * size, __func__); - - const float *fp_src = this->m_buffer; - float *fp_dst = result; - - for (i = 0; i < size; i++, fp_dst++, fp_src += COM_NUMBER_OF_CHANNELS) { - *fp_dst = *fp_src; - } - - return result; -} float MemoryBuffer::getMaximumValue() { @@ -98,7 +112,7 @@ float MemoryBuffer::getMaximumValue() const float *fp_src = this->m_buffer; - for (i = 0; i < size; i++, fp_src += COM_NUMBER_OF_CHANNELS) { + for (i = 0; i < size; i++, fp_src += this->m_num_channels) { float value = *fp_src; if (value > result) { result = value; @@ -116,7 +130,7 @@ float MemoryBuffer::getMaximumValue(rcti *rect) BLI_rcti_isect(rect, &this->m_rect, &rect_clamp); if (!BLI_rcti_is_empty(&rect_clamp)) { - MemoryBuffer *temp = new MemoryBuffer(NULL, &rect_clamp); + MemoryBuffer *temp = new MemoryBuffer(this->m_datatype, &rect_clamp); temp->copyContentFrom(this); float result = temp->getMaximumValue(); delete temp; @@ -152,9 +166,9 @@ void MemoryBuffer::copyContentFrom(MemoryBuffer *otherBuffer) for (otherY = minY; otherY < maxY; otherY++) { - otherOffset = ((otherY - otherBuffer->m_rect.ymin) * otherBuffer->m_chunkWidth + minX - otherBuffer->m_rect.xmin) * COM_NUMBER_OF_CHANNELS; - offset = ((otherY - this->m_rect.ymin) * this->m_chunkWidth + minX - this->m_rect.xmin) * COM_NUMBER_OF_CHANNELS; - memcpy(&this->m_buffer[offset], &otherBuffer->m_buffer[otherOffset], (maxX - minX) * COM_NUMBER_OF_CHANNELS * sizeof(float)); + otherOffset = ((otherY - otherBuffer->m_rect.ymin) * otherBuffer->m_width + minX - otherBuffer->m_rect.xmin) * this->m_num_channels; + offset = ((otherY - this->m_rect.ymin) * this->m_width + minX - this->m_rect.xmin) * this->m_num_channels; + memcpy(&this->m_buffer[offset], &otherBuffer->m_buffer[otherOffset], (maxX - minX) * this->m_num_channels * sizeof(float)); } } @@ -163,9 +177,8 @@ void MemoryBuffer::writePixel(int x, int y, const float color[4]) if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin && y < this->m_rect.ymax) { - const int offset = (this->m_chunkWidth * (y - this->m_rect.ymin) + x - this->m_rect.xmin) * COM_NUMBER_OF_CHANNELS; - copy_v4_v4(&this->m_buffer[offset], color); - } + const int offset = (this->m_width * (y - this->m_rect.ymin) + x - this->m_rect.xmin) * this->m_num_channels; + memcpy(&this->m_buffer[offset], color, sizeof(float)*this->m_num_channels); } } void MemoryBuffer::addPixel(int x, int y, const float color[4]) @@ -173,8 +186,12 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4]) if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin && y < this->m_rect.ymax) { - const int offset = (this->m_chunkWidth * (y - this->m_rect.ymin) + x - this->m_rect.xmin) * COM_NUMBER_OF_CHANNELS; - add_v4_v4(&this->m_buffer[offset], color); + const int offset = (this->m_width * (y - this->m_rect.ymin) + x - this->m_rect.xmin) * this->m_num_channels; + float *dst = &this->m_buffer[offset]; + const float *src = color; + for (int i = 0; i < this->m_num_channels ; i++, dst++, src++) { + *dst += *src; + } } } @@ -208,8 +225,9 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4] } } -void MemoryBuffer::readEWA(float result[4], const float uv[2], const float derivatives[2][2], PixelSampler sampler) +void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2], PixelSampler sampler) { + BLI_assert(this->m_datatype == COM_DT_COLOR); ReadEWAData data; data.buffer = this; data.sampler = sampler; diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index d6ef9cd673e..e0c542108a1 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -83,11 +83,6 @@ private: unsigned int m_chunkNumber; /** - * @brief width of the chunk - */ - unsigned int m_chunkWidth; - - /** * @brief state of the buffer */ MemoryBufferState m_state; @@ -97,6 +92,15 @@ private: */ float *m_buffer; + /** + * @brief the number of channels of a single value in the buffer. + * For value buffers this is 1, vector 3 and color 4 + */ + unsigned int m_num_channels; + + int m_width; + int m_height; + public: /** * @brief construct new MemoryBuffer for a chunk @@ -107,7 +111,12 @@ public: * @brief construct new temporarily MemoryBuffer for an area */ MemoryBuffer(MemoryProxy *memoryProxy, rcti *rect); - + + /** + * @brief construct new temporarily MemoryBuffer for an area + */ + MemoryBuffer(DataType datatype, rcti *rect); + /** * @brief destructor */ @@ -117,7 +126,9 @@ public: * @brief read the ChunkNumber of this MemoryBuffer */ unsigned int getChunkNumber() { return this->m_chunkNumber; } - + + unsigned int get_num_channels() { return this->m_num_channels; } + /** * @brief get the data of this MemoryBuffer * @note buffer should already be available in memory @@ -134,8 +145,8 @@ public: inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) { - int w = m_rect.xmax - m_rect.xmin; - int h = m_rect.ymax - m_rect.ymin; + int w = this->m_width; + int h = this->m_height; x = x - m_rect.xmin; y = y - m_rect.ymin; @@ -164,7 +175,39 @@ public: } } - inline void read(float result[4], int x, int y, + inline void wrap_pixel(float &x, float &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) + { + float w = (float)this->m_width; + float h = (float)this->m_height; + x = x - m_rect.xmin; + y = y - m_rect.ymin; + + switch (extend_x) { + case COM_MB_CLIP: + break; + case COM_MB_EXTEND: + if (x < 0) x = 0.0f; + if (x >= w) x = w; + break; + case COM_MB_REPEAT: + x = fmodf(x, w); + break; + } + + switch (extend_y) { + case COM_MB_CLIP: + break; + case COM_MB_EXTEND: + if (y < 0) y = 0.0f; + if (y >= h) y = h; + break; + case COM_MB_REPEAT: + y = fmodf(y, h); + break; + } + } + + inline void read(float *result, int x, int y, MemoryBufferExtend extend_x = COM_MB_CLIP, MemoryBufferExtend extend_y = COM_MB_CLIP) { @@ -172,81 +215,54 @@ public: bool clip_y = (extend_y == COM_MB_CLIP && (y < m_rect.ymin || y >= m_rect.ymax)); if (clip_x || clip_y) { /* clip result outside rect is zero */ - zero_v4(result); + memset(result, 0, this->m_num_channels * sizeof(float)); } else { - wrap_pixel(x, y, extend_x, extend_y); - const int offset = (this->m_chunkWidth * y + x) * COM_NUMBER_OF_CHANNELS; - copy_v4_v4(result, &this->m_buffer[offset]); + int u = x; + int v = y; + this->wrap_pixel(u, v, extend_x, extend_y); + const int offset = (this->m_width * y + x) * this->m_num_channels; + float* buffer = &this->m_buffer[offset]; + memcpy(result, buffer, sizeof(float) * this->m_num_channels); } } - inline void readNoCheck(float result[4], int x, int y, + inline void readNoCheck(float *result, int x, int y, MemoryBufferExtend extend_x = COM_MB_CLIP, MemoryBufferExtend extend_y = COM_MB_CLIP) { - wrap_pixel(x, y, extend_x, extend_y); - const int offset = (this->m_chunkWidth * y + x) * COM_NUMBER_OF_CHANNELS; + int u = x; + int v = y; - BLI_assert(offset >= 0); - BLI_assert(offset < this->determineBufferSize() * COM_NUMBER_OF_CHANNELS); - BLI_assert(!(extend_x == COM_MB_CLIP && (x < m_rect.xmin || x >= m_rect.xmax)) && - !(extend_y == COM_MB_CLIP && (y < m_rect.ymin || y >= m_rect.ymax))); + this->wrap_pixel(u, v, extend_x, extend_y); + const int offset = (this->m_width * v + u) * this->m_num_channels; + BLI_assert(offset >= 0); + BLI_assert(offset < this->determineBufferSize() * this->m_num_channels); + BLI_assert(!(extend_x == COM_MB_CLIP && (u < m_rect.xmin || u >= m_rect.xmax)) && + !(extend_y == COM_MB_CLIP && (v < m_rect.ymin || v >= m_rect.ymax))); #if 0 /* always true */ BLI_assert((int)(MEM_allocN_len(this->m_buffer) / sizeof(*this->m_buffer)) == (int)(this->determineBufferSize() * COM_NUMBER_OF_CHANNELS)); #endif - - copy_v4_v4(result, &this->m_buffer[offset]); + float *buffer = &this->m_buffer[offset]; + memcpy(result, buffer, sizeof(float) * this->m_num_channels); } void writePixel(int x, int y, const float color[4]); void addPixel(int x, int y, const float color[4]); - inline void readBilinear(float result[4], float x, float y, + inline void readBilinear(float *result, float x, float y, MemoryBufferExtend extend_x = COM_MB_CLIP, MemoryBufferExtend extend_y = COM_MB_CLIP) { - int x1 = floor(x); - int y1 = floor(y); - int x2 = x1 + 1; - int y2 = y1 + 1; - wrap_pixel(x1, y1, extend_x, extend_y); - wrap_pixel(x2, y2, extend_x, extend_y); - - float valuex = x - x1; - float valuey = y - y1; - float mvaluex = 1.0f - valuex; - float mvaluey = 1.0f - valuey; - - float color1[4]; - float color2[4]; - float color3[4]; - float color4[4]; - - read(color1, x1, y1); - read(color2, x1, y2); - read(color3, x2, y1); - read(color4, x2, y2); - - color1[0] = color1[0] * mvaluey + color2[0] * valuey; - color1[1] = color1[1] * mvaluey + color2[1] * valuey; - color1[2] = color1[2] * mvaluey + color2[2] * valuey; - color1[3] = color1[3] * mvaluey + color2[3] * valuey; - - color3[0] = color3[0] * mvaluey + color4[0] * valuey; - color3[1] = color3[1] * mvaluey + color4[1] * valuey; - color3[2] = color3[2] * mvaluey + color4[2] * valuey; - color3[3] = color3[3] * mvaluey + color4[3] * valuey; - - result[0] = color1[0] * mvaluex + color3[0] * valuex; - result[1] = color1[1] * mvaluex + color3[1] * valuex; - result[2] = color1[2] * mvaluex + color3[2] * valuex; - result[3] = color1[3] * mvaluex + color3[3] * valuex; + float u = x; + float v = y; + this->wrap_pixel(u, v, extend_x, extend_y); + BLI_bilinear_interpolation_fl(this->m_buffer, result, this->m_width, this->m_height, this->m_num_channels, u - 0.5f, v - 0.5f); } - void readEWA(float result[4], const float uv[2], const float derivatives[2][2], PixelSampler sampler); + void readEWA(float *result, const float uv[2], const float derivatives[2][2], PixelSampler sampler); /** * @brief is this MemoryBuffer a temporarily buffer (based on an area, not on a chunk) @@ -284,7 +300,6 @@ public: MemoryBuffer *duplicate(); - float *convertToValueBuffer(); float getMaximumValue(); float getMaximumValue(rcti *rect); private: diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cpp b/source/blender/compositor/intern/COM_MemoryProxy.cpp index 90ca0baea06..1df3e59db62 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.cpp +++ b/source/blender/compositor/intern/COM_MemoryProxy.cpp @@ -23,10 +23,11 @@ #include "COM_MemoryProxy.h" -MemoryProxy::MemoryProxy() +MemoryProxy::MemoryProxy(DataType datatype) { this->m_writeBufferOperation = NULL; this->m_executor = NULL; + this->m_datatype = datatype; } void MemoryProxy::allocate(unsigned int width, unsigned int height) diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h index 233b035a2d7..b332852088b 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.h +++ b/source/blender/compositor/intern/COM_MemoryProxy.h @@ -63,8 +63,13 @@ private: */ MemoryBuffer *m_buffer; + /** + * @brief datatype of this MemoryProxy + */ + DataType m_datatype; + public: - MemoryProxy(); + MemoryProxy(DataType type); /** * @brief set the ExecutionGroup that can be scheduled to calculate a certain chunk. @@ -104,6 +109,8 @@ public: */ inline MemoryBuffer *getBuffer() { return this->m_buffer; } + inline DataType getDataType() { return this->m_datatype; } + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryProxy") #endif diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp b/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp index fb5bc8fcd9b..74c05c3e62e 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cpp @@ -476,7 +476,7 @@ void NodeOperationBuilder::add_input_buffers(NodeOperation *operation, NodeOpera /* check of other end already has write operation, otherwise add a new one */ WriteBufferOperation *writeoperation = find_attached_write_buffer_operation(output); if (!writeoperation) { - writeoperation = new WriteBufferOperation(); + writeoperation = new WriteBufferOperation(output->getDataType()); writeoperation->setbNodeTree(m_context->getbNodeTree()); addOperation(writeoperation); @@ -486,7 +486,7 @@ void NodeOperationBuilder::add_input_buffers(NodeOperation *operation, NodeOpera } /* add readbuffer op for the input */ - ReadBufferOperation *readoperation = new ReadBufferOperation(); + ReadBufferOperation *readoperation = new ReadBufferOperation(output->getDataType()); readoperation->setMemoryProxy(writeoperation->getMemoryProxy()); this->addOperation(readoperation); @@ -519,7 +519,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, NodeOper /* if no write buffer operation exists yet, create a new one */ if (!writeOperation) { - writeOperation = new WriteBufferOperation(); + writeOperation = new WriteBufferOperation(operation->getOutputSocket()->getDataType()); writeOperation->setbNodeTree(m_context->getbNodeTree()); addOperation(writeOperation); @@ -534,7 +534,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, NodeOper if (&target->getOperation() == writeOperation) continue; /* skip existing write op links */ - ReadBufferOperation *readoperation = new ReadBufferOperation(); + ReadBufferOperation *readoperation = new ReadBufferOperation(operation->getOutputSocket()->getDataType()); readoperation->setMemoryProxy(writeOperation->getMemoryProxy()); addOperation(readoperation); diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cpp b/source/blender/compositor/intern/COM_OpenCLDevice.cpp index c5b663d2aef..5960082c2fd 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cpp +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cpp @@ -24,6 +24,18 @@ #include "COM_WorkScheduler.h" typedef enum COM_VendorID {NVIDIA = 0x10DE, AMD = 0x1002} COM_VendorID; +const cl_image_format IMAGE_FORMAT_COLOR = { + CL_RGBA, + CL_FLOAT +}; +const cl_image_format IMAGE_FORMAT_VECTOR = { + CL_RGB, + CL_FLOAT +}; +const cl_image_format IMAGE_FORMAT_VALUE = { + CL_R, + CL_FLOAT +}; OpenCLDevice::OpenCLDevice(cl_context context, cl_device_id device, cl_program program, cl_int vendorId) { @@ -72,6 +84,23 @@ cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, return COM_clAttachMemoryBufferToKernelParameter(kernel, parameterIndex, offsetIndex, cleanup, inputMemoryBuffers, (ReadBufferOperation *)reader); } +const cl_image_format* OpenCLDevice::determineImageFormat(MemoryBuffer *memoryBuffer) +{ + const cl_image_format *imageFormat; + int num_channels = memoryBuffer->get_num_channels(); + if (num_channels == 1) { + imageFormat = &IMAGE_FORMAT_VALUE; + } + else if (num_channels == 3) { + imageFormat = &IMAGE_FORMAT_VECTOR; + } + else { + imageFormat = &IMAGE_FORMAT_COLOR; + } + + return imageFormat; +} + cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, int parameterIndex, int offsetIndex, list<cl_mem> *cleanup, MemoryBuffer **inputMemoryBuffers, ReadBufferOperation *reader) @@ -80,12 +109,9 @@ cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, MemoryBuffer *result = reader->getInputMemoryBuffer(inputMemoryBuffers); - const cl_image_format imageFormat = { - CL_RGBA, - CL_FLOAT - }; + const cl_image_format *imageFormat = determineImageFormat(result); - cl_mem clBuffer = clCreateImage2D(this->m_context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &imageFormat, result->getWidth(), + cl_mem clBuffer = clCreateImage2D(this->m_context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, imageFormat, result->getWidth(), result->getHeight(), 0, result->getBuffer(), &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h index 94df2f2b44c..a513954ee0d 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.h +++ b/source/blender/compositor/intern/COM_OpenCLDevice.h @@ -94,6 +94,12 @@ public: */ void execute(WorkPackage *work); + /** + * @brief determine an image format + * @param memorybuffer + */ + static const cl_image_format *determineImageFormat(MemoryBuffer *memoryBuffer); + cl_context getContext() { return this->m_context; } cl_command_queue getQueue() { return this->m_queue; } diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cpp b/source/blender/compositor/intern/COM_WorkScheduler.cpp index e1016731c7f..8445dee5ae4 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cpp +++ b/source/blender/compositor/intern/COM_WorkScheduler.cpp @@ -196,7 +196,7 @@ void WorkScheduler::schedule(ExecutionGroup *group, int chunkNumber) BLI_thread_queue_push(g_cpuqueue, package); } #else - BLI_thread_queue_push(cpuqueue, package); + BLI_thread_queue_push(g_cpuqueue, package); #endif #endif } diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cpp b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cpp index 9b69bc5a46e..379b9f193e8 100644 --- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cpp +++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cpp @@ -54,6 +54,10 @@ void PlaneTrackDeformNode::convertToOperations(NodeConverter &converter, const C warp_image_operation->setTrackingObject(data->tracking_object); warp_image_operation->setPlaneTrackName(data->plane_track_name); warp_image_operation->setFramenumber(frame_number); + if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { + warp_image_operation->setMotionBlurSamples(data->motion_blur_samples); + warp_image_operation->setMotionBlurShutter(data->motion_blur_shutter); + } converter.addOperation(warp_image_operation); converter.mapInputSocket(input_image, warp_image_operation->getInputSocket(0)); @@ -64,6 +68,10 @@ void PlaneTrackDeformNode::convertToOperations(NodeConverter &converter, const C plane_mask_operation->setTrackingObject(data->tracking_object); plane_mask_operation->setPlaneTrackName(data->plane_track_name); plane_mask_operation->setFramenumber(frame_number); + if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { + plane_mask_operation->setMotionBlurSamples(data->motion_blur_samples); + plane_mask_operation->setMotionBlurShutter(data->motion_blur_shutter); + } converter.addOperation(plane_mask_operation); converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket()); diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.cpp b/source/blender/compositor/nodes/COM_SocketProxyNode.cpp index 48c8acfc6a1..17b00af16d8 100644 --- a/source/blender/compositor/nodes/COM_SocketProxyNode.cpp +++ b/source/blender/compositor/nodes/COM_SocketProxyNode.cpp @@ -73,8 +73,9 @@ void SocketBufferNode::convertToOperations(NodeConverter &converter, const Compo NodeOutput *output = this->getOutputSocket(0); NodeInput *input = this->getInputSocket(0); - WriteBufferOperation *writeOperation = new WriteBufferOperation(); - ReadBufferOperation *readOperation = new ReadBufferOperation(); + DataType datatype = output->getDataType(); + WriteBufferOperation *writeOperation = new WriteBufferOperation(datatype); + ReadBufferOperation *readOperation = new ReadBufferOperation(datatype); readOperation->setMemoryProxy(writeOperation->getMemoryProxy()); converter.addOperation(writeOperation); converter.addOperation(readOperation); diff --git a/source/blender/compositor/nodes/COM_TextureNode.cpp b/source/blender/compositor/nodes/COM_TextureNode.cpp index 2ac027ca326..b80ca2fcdbd 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.cpp +++ b/source/blender/compositor/nodes/COM_TextureNode.cpp @@ -35,7 +35,7 @@ void TextureNode::convertToOperations(NodeConverter &converter, const Compositor Tex *texture = (Tex *)editorNode->id; TextureOperation *operation = new TextureOperation(); const ColorManagedDisplaySettings *displaySettings = context.getDisplaySettings(); - bool sceneColorManage = strcmp(displaySettings->display_device, "None") != 0; + bool sceneColorManage = !STREQ(displaySettings->display_device, "None"); operation->setTexture(texture); operation->setRenderData(context.getRenderData()); operation->setSceneColorManage(sceneColorManage); diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cpp b/source/blender/compositor/nodes/COM_TranslateNode.cpp index 990cbe19be2..04dc1d435d3 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cpp +++ b/source/blender/compositor/nodes/COM_TranslateNode.cpp @@ -57,8 +57,8 @@ void TranslateNode::convertToOperations(NodeConverter &converter, const Composit converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); if (data->wrap_axis) { - WriteBufferOperation *writeOperation = new WriteBufferOperation(); - WrapOperation *wrapOperation = new WrapOperation(); + WriteBufferOperation *writeOperation = new WriteBufferOperation(COM_DT_COLOR); + WrapOperation *wrapOperation = new WrapOperation(COM_DT_COLOR); wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy()); wrapOperation->setWrapping(data->wrap_axis); diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cpp b/source/blender/compositor/operations/COM_AntiAliasOperation.cpp index 995c61589cc..b25e2281467 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.cpp +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cpp @@ -95,7 +95,7 @@ void *AntiAliasOperation::initializeTileData(rcti *rect) float *input = tile->getBuffer(); char *valuebuffer = (char *)MEM_mallocN(sizeof(char) * size, __func__); for (int i = 0; i < size; i++) { - float in = input[i * COM_NUMBER_OF_CHANNELS]; + float in = input[i]; valuebuffer[i] = FTOCHAR(in); } antialias_tagbuf(tile->getWidth(), tile->getHeight(), valuebuffer); diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cpp b/source/blender/compositor/operations/COM_BokehBlurOperation.cpp index f5bca5371e6..22f6fc33a0d 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cpp @@ -110,11 +110,11 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) int step = getStep(); - int offsetadd = getOffsetAdd(); + int offsetadd = getOffsetAdd() * COM_NUM_CHANNELS_COLOR; float m = this->m_bokehDimension / pixelSize; for (int ny = miny; ny < maxy; ny += step) { - int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth); + int bufferindex = ((minx - bufferstartx) * COM_NUM_CHANNELS_COLOR) + ((ny - bufferstarty) * COM_NUM_CHANNELS_COLOR * bufferwidth); for (int nx = minx; nx < maxx; nx += step) { float u = this->m_bokehMidX - (nx - x) * m; float v = this->m_bokehMidY - (ny - y) * m; diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cpp b/source/blender/compositor/operations/COM_CompositorOperation.cpp index ef331a50dfd..e3438bcbd15 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cpp +++ b/source/blender/compositor/operations/COM_CompositorOperation.cpp @@ -138,7 +138,7 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int tileNumber) int y2 = rect->ymax; int offset = (y1 * this->getWidth() + x1); int add = (this->getWidth() - (x2 - x1)); - int offset4 = offset * COM_NUMBER_OF_CHANNELS; + int offset4 = offset * COM_NUM_CHANNELS_COLOR; int x; int y; bool breaked = false; @@ -196,14 +196,14 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int tileNumber) this->m_depthInput->readSampled(color, input_x, input_y, COM_PS_NEAREST); zbuffer[offset] = color[0]; - offset4 += COM_NUMBER_OF_CHANNELS; + offset4 += COM_NUM_CHANNELS_COLOR; offset++; if (isBreaked()) { breaked = true; } } offset += add; - offset4 += add * COM_NUMBER_OF_CHANNELS; + offset4 += add * COM_NUM_CHANNELS_COLOR; } } diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cpp b/source/blender/compositor/operations/COM_ConvertOperation.cpp index 1445b78552f..977586acb60 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cpp +++ b/source/blender/compositor/operations/COM_ConvertOperation.cpp @@ -49,9 +49,9 @@ ConvertValueToColorOperation::ConvertValueToColorOperation() : ConvertBaseOperat void ConvertValueToColorOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - float inputValue[4]; - this->m_inputOperation->readSampled(inputValue, x, y, sampler); - output[0] = output[1] = output[2] = inputValue[0]; + float value; + this->m_inputOperation->readSampled(&value, x, y, sampler); + output[0] = output[1] = output[2] = value; output[3] = 1.0f; } @@ -98,8 +98,9 @@ ConvertColorToVectorOperation::ConvertColorToVectorOperation() : ConvertBaseOper void ConvertColorToVectorOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - this->m_inputOperation->readSampled(output, x, y, sampler); -} + float color[4]; + this->m_inputOperation->readSampled(color, x, y, sampler); + copy_v3_v3(output, color);} /* ******** Value to Vector ******** */ @@ -112,12 +113,9 @@ ConvertValueToVectorOperation::ConvertValueToVectorOperation() : ConvertBaseOper void ConvertValueToVectorOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - float input[4]; - this->m_inputOperation->readSampled(input, x, y, sampler); - output[0] = input[0]; - output[1] = input[0]; - output[2] = input[0]; - output[3] = 0.0f; + float value; + this->m_inputOperation->readSampled(&value, x, y, sampler); + output[0] = output[1] = output[2] = value; } diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cpp b/source/blender/compositor/operations/COM_DilateErodeOperation.cpp index cbf4ce693d9..6d15ef3395b 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cpp +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cpp @@ -82,18 +82,18 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, const int bufferWidth = BLI_rcti_size_x(rect); int offset; - this->m_inputProgram->read(inputValue, x, y, NULL); + inputBuffer->read(inputValue, x, y); if (inputValue[0] > sw) { for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4; + offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)); for (int xi = minx; xi < maxx; xi++) { if (buffer[offset] < sw) { const float dx = xi - x; const float dis = dx * dx + dy * dy; mindist = min(mindist, dis); } - offset += 4; + offset ++; } } pixelvalue = -sqrtf(mindist); @@ -101,15 +101,14 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, else { for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4; + offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)); for (int xi = minx; xi < maxx; xi++) { if (buffer[offset] > sw) { const float dx = xi - x; const float dis = dx * dx + dy * dy; mindist = min(mindist, dis); } - offset += 4; - + offset ++; } } pixelvalue = sqrtf(mindist); @@ -206,14 +205,14 @@ void DilateDistanceOperation::executePixel(float output[4], int x, int y, void * for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4; + offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)); for (int xi = minx; xi < maxx; xi++) { const float dx = xi - x; const float dis = dx * dx + dy * dy; if (dis <= mindist) { value = max(buffer[offset], value); } - offset += 4; + offset ++; } } output[0] = value; @@ -280,14 +279,14 @@ void ErodeDistanceOperation::executePixel(float output[4], int x, int y, void *d for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4; + offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)); for (int xi = minx; xi < maxx; xi++) { const float dx = xi - x; const float dis = dx * dx + dy * dy; if (dis <= mindist) { value = min(buffer[offset], value); } - offset += 4; + offset ++; } } output[0] = value; @@ -383,7 +382,7 @@ void *DilateStepOperation::initializeTileData(rcti *rect) buf[x] = -FLT_MAX; } for (x = xmin; x < xmax; ++x) { - buf[x - rect->xmin + window - 1] = buffer[4 * (y * width + x)]; + buf[x - rect->xmin + window - 1] = buffer[(y * width + x)]; } for (i = 0; i < (bwidth + 3 * half_window) / window; i++) { @@ -510,7 +509,7 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) buf[x] = FLT_MAX; } for (x = xmin; x < xmax; ++x) { - buf[x - rect->xmin + window - 1] = buffer[4 * (y * width + x)]; + buf[x - rect->xmin + window - 1] = buffer[(y * width + x)]; } for (i = 0; i < (bwidth + 3 * half_window) / window; i++) { diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp index 5006720f091..234a61a4c41 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp @@ -1270,11 +1270,9 @@ void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect) MemoryBuffer *innerMask = (MemoryBuffer *)this->m_inputInnerMask->initializeTileData(rect); MemoryBuffer *outerMask = (MemoryBuffer *)this->m_inputOuterMask->initializeTileData(rect); float *data = (float *)MEM_mallocN(sizeof(float) * this->getWidth() * this->getHeight(), __func__); - float *imask = innerMask->convertToValueBuffer(); - float *omask = outerMask->convertToValueBuffer(); + float *imask = innerMask->getBuffer(); + float *omask = outerMask->getBuffer(); doDoubleEdgeMask(imask, omask, data); - MEM_freeN(imask); - MEM_freeN(omask); this->m_cachedInstance = data; } unlockMutex(); diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cpp b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cpp index 705a8c07381..08f520e4271 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cpp @@ -92,16 +92,16 @@ void *FastGaussianBlurOperation::initializeTileData(rcti *rect) this->m_sy = this->m_data.sizey * this->m_size / 2.0f; if ((this->m_sx == this->m_sy) && (this->m_sx > 0.f)) { - for (c = 0; c < COM_NUMBER_OF_CHANNELS; ++c) + for (c = 0; c < COM_NUM_CHANNELS_COLOR; ++c) IIR_gauss(copy, this->m_sx, c, 3); } else { if (this->m_sx > 0.0f) { - for (c = 0; c < COM_NUMBER_OF_CHANNELS; ++c) + for (c = 0; c < COM_NUM_CHANNELS_COLOR; ++c) IIR_gauss(copy, this->m_sx, c, 1); } if (this->m_sy > 0.0f) { - for (c = 0; c < COM_NUMBER_OF_CHANNELS; ++c) + for (c = 0; c < COM_NUM_CHANNELS_COLOR; ++c) IIR_gauss(copy, this->m_sy, c, 2); } } @@ -120,6 +120,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, float sigma, unsign unsigned int x, y, sz; unsigned int i; float *buffer = src->getBuffer(); + const unsigned int num_channels = src->get_num_channels(); // <0.5 not valid, though can have a possibly useful sort of sharpening effect if (sigma < 0.5f) return; @@ -198,31 +199,31 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, float sigma, unsign int offset; for (y = 0; y < src_height; ++y) { const int yx = y * src_width; - offset = yx * COM_NUMBER_OF_CHANNELS + chan; + offset = yx * num_channels + chan; for (x = 0; x < src_width; ++x) { X[x] = buffer[offset]; - offset += COM_NUMBER_OF_CHANNELS; + offset += num_channels; } YVV(src_width); - offset = yx * COM_NUMBER_OF_CHANNELS + chan; + offset = yx * num_channels + chan; for (x = 0; x < src_width; ++x) { buffer[offset] = Y[x]; - offset += COM_NUMBER_OF_CHANNELS; + offset += num_channels; } } } if (xy & 2) { // V int offset; - const int add = src_width * COM_NUMBER_OF_CHANNELS; + const int add = src_width * num_channels; for (x = 0; x < src_width; ++x) { - offset = x * COM_NUMBER_OF_CHANNELS + chan; + offset = x * num_channels + chan; for (y = 0; y < src_height; ++y) { X[y] = buffer[offset]; offset += add; } YVV(src_height); - offset = x * COM_NUMBER_OF_CHANNELS + chan; + offset = x * num_channels + chan; for (y = 0; y < src_height; ++y) { buffer[offset] = Y[y]; offset += add; @@ -298,7 +299,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) { float *src = newBuf->getBuffer(); float *dst = copy->getBuffer(); - for (int i = copy->getWidth() * copy->getHeight(); i != 0; i--, src += COM_NUMBER_OF_CHANNELS, dst += COM_NUMBER_OF_CHANNELS) { + for (int i = copy->getWidth() * copy->getHeight(); i != 0; i--, src += COM_NUM_CHANNELS_VALUE, dst += COM_NUM_CHANNELS_VALUE) { if (*src < *dst) { *dst = *src; } @@ -307,7 +308,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) { float *src = newBuf->getBuffer(); float *dst = copy->getBuffer(); - for (int i = copy->getWidth() * copy->getHeight(); i != 0; i--, src += COM_NUMBER_OF_CHANNELS, dst += COM_NUMBER_OF_CHANNELS) { + for (int i = copy->getWidth() * copy->getHeight(); i != 0; i--, src += COM_NUM_CHANNELS_VALUE, dst += COM_NUM_CHANNELS_VALUE) { if (*src > *dst) { *dst = *src; } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cpp b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cpp index c78347e7b1c..dde57ab640f 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cpp @@ -102,15 +102,14 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo /* *** this is the main part which is different to 'GaussianXBlurOperation' *** */ int step = getStep(); - int offsetadd = getOffsetAdd(); - int bufferindex = ((xmin - bufferstartx) * 4) + ((ymin - bufferstarty) * 4 * bufferwidth); + int bufferindex = ((xmin - bufferstartx)) + ((ymin - bufferstarty) * bufferwidth); /* gauss */ float alpha_accum = 0.0f; float multiplier_accum = 0.0f; /* dilate */ - float value_max = finv_test(buffer[(x * 4) + (y * 4 * bufferwidth)], do_invert); /* init with the current color to avoid unneeded lookups */ + float value_max = finv_test(buffer[(x) + (y * bufferwidth)], do_invert); /* init with the current color to avoid unneeded lookups */ float distfacinv_max = 1.0f; /* 0 to 1 */ for (int nx = xmin; nx < xmax; nx += step) { @@ -134,7 +133,7 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo distfacinv_max = multiplier; } } - bufferindex += offsetadd; + bufferindex += step; } /* blend between the max value and gauss blue - gives nice feather */ diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cpp b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cpp index ab97c8b0d13..bb5b3c044af 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cpp @@ -108,11 +108,11 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo float multiplier_accum = 0.0f; /* dilate */ - float value_max = finv_test(buffer[(x * 4) + (y * 4 * bufferwidth)], do_invert); /* init with the current color to avoid unneeded lookups */ + float value_max = finv_test(buffer[(x) + (y * bufferwidth)], do_invert); /* init with the current color to avoid unneeded lookups */ float distfacinv_max = 1.0f; /* 0 to 1 */ for (int ny = ymin; ny < ymax; ny += step) { - int bufferindex = ((xmin - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth); + int bufferindex = ((xmin - bufferstartx)) + ((ny - bufferstarty) * bufferwidth); const int index = (ny - y) + this->m_filtersize; float value = finv_test(buffer[bufferindex], do_invert); diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cpp b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cpp index 441b623b589..dbad51c4329 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cpp @@ -296,7 +296,7 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, int minyr = y - refrady < 0 ? -y : -refrady; int maxyr = y + refrady > imgy ? imgy - y : refrady; - float *srcd = buffer + COM_NUMBER_OF_CHANNELS * ( (y + minyr) * imgx + x + minxr); + float *srcd = buffer + COM_NUM_CHANNELS_COLOR * ( (y + minyr) * imgx + x + minxr); gausstabx = m_maintabs[refradx - 1]; gausstabcentx = gausstabx + refradx; @@ -304,9 +304,9 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, gausstabcenty = gausstaby + refrady; sum = gval = rval = bval = aval = 0.0f; - for (i = minyr; i < maxyr; i++, srcd += COM_NUMBER_OF_CHANNELS * imgx) { + for (i = minyr; i < maxyr; i++, srcd += COM_NUM_CHANNELS_COLOR * imgx) { src = srcd; - for (j = minxr; j < maxxr; j++, src += COM_NUMBER_OF_CHANNELS) { + for (j = minxr; j < maxxr; j++, src += COM_NUM_CHANNELS_COLOR) { val = gausstabcenty[i] * gausstabcentx[j]; sum += val; diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cpp b/source/blender/compositor/operations/COM_GlareBaseOperation.cpp index 99c745d7fb0..98919f1b161 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.cpp +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cpp @@ -49,7 +49,7 @@ MemoryBuffer *GlareBaseOperation::createMemoryBuffer(rcti *rect2) rect.ymin = 0; rect.xmax = getWidth(); rect.ymax = getHeight(); - MemoryBuffer *result = new MemoryBuffer(NULL, &rect); + MemoryBuffer *result = new MemoryBuffer(COM_DT_COLOR, &rect); float *data = result->getBuffer(); this->generateGlare(data, tile, this->m_settings); return result; diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp index 99a7c5b64c4..04993b08ddf 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp @@ -259,8 +259,8 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) float *kernelBuffer = in2->getBuffer(); float *imageBuffer = in1->getBuffer(); - MemoryBuffer *rdst = new MemoryBuffer(NULL, in1->getRect()); - memset(rdst->getBuffer(), 0, rdst->getWidth() * rdst->getHeight() * COM_NUMBER_OF_CHANNELS * sizeof(float)); + MemoryBuffer *rdst = new MemoryBuffer(COM_DT_COLOR, in1->getRect()); + memset(rdst->getBuffer(), 0, rdst->getWidth() * rdst->getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float)); // convolution result width & height w2 = 2 * kernelWidth - 1; @@ -276,7 +276,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) // normalize convolutor wt[0] = wt[1] = wt[2] = 0.f; for (y = 0; y < kernelHeight; y++) { - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUMBER_OF_CHANNELS]; + colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR]; for (x = 0; x < kernelWidth; x++) add_v3_v3(wt, colp[x]); } @@ -284,7 +284,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) if (wt[1] != 0.f) wt[1] = 1.f / wt[1]; if (wt[2] != 0.f) wt[2] = 1.f / wt[2]; for (y = 0; y < kernelHeight; y++) { - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUMBER_OF_CHANNELS]; + colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR]; for (x = 0; x < kernelWidth; x++) mul_v3_v3(colp[x], wt); } @@ -313,7 +313,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) // in2, channel ch -> data1 for (y = 0; y < kernelHeight; y++) { fp = &data1ch[y * w2]; - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUMBER_OF_CHANNELS]; + colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR]; for (x = 0; x < kernelWidth; x++) fp[x] = colp[x][ch]; } @@ -325,7 +325,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) int yy = ybl * ybsz + y; if (yy >= imageHeight) continue; fp = &data2[y * w2]; - colp = (fRGB *)&imageBuffer[yy * imageWidth * COM_NUMBER_OF_CHANNELS]; + colp = (fRGB *)&imageBuffer[yy * imageWidth * COM_NUM_CHANNELS_COLOR]; for (x = 0; x < xbsz; x++) { int xx = xbl * xbsz + x; if (xx >= imageWidth) continue; @@ -349,7 +349,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) const int yy = ybl * ybsz + y - hh; if ((yy < 0) || (yy >= imageHeight)) continue; fp = &data2[y * w2]; - colp = (fRGB *)&rdst->getBuffer()[yy * imageWidth * COM_NUMBER_OF_CHANNELS]; + colp = (fRGB *)&rdst->getBuffer()[yy * imageWidth * COM_NUM_CHANNELS_COLOR]; for (x = 0; x < (int)w2; x++) { const int xx = xbl * xbsz + x - hw; if ((xx < 0) || (xx >= imageWidth)) continue; @@ -364,7 +364,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) MEM_freeN(data2); MEM_freeN(data1); - memcpy(dst, rdst->getBuffer(), sizeof(float) * imageWidth * imageHeight * COM_NUMBER_OF_CHANNELS); + memcpy(dst, rdst->getBuffer(), sizeof(float) * imageWidth * imageHeight * COM_NUM_CHANNELS_COLOR); delete(rdst); } @@ -381,7 +381,7 @@ void GlareFogGlowOperation::generateGlare(float *data, MemoryBuffer *inputTile, // make the convolution kernel rcti kernelRect; BLI_rcti_init(&kernelRect, 0, sz, 0, sz); - ckrn = new MemoryBuffer(NULL, &kernelRect); + ckrn = new MemoryBuffer(COM_DT_COLOR, &kernelRect); scale = 0.25f * sqrtf((float)(sz * sz)); diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.cpp b/source/blender/compositor/operations/COM_GlareGhostOperation.cpp index 8854be52ded..69b5bd7d6bf 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.cpp +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cpp @@ -97,7 +97,7 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No } - memset(tbuf1->getBuffer(), 0, tbuf1->getWidth() * tbuf1->getHeight() * COM_NUMBER_OF_CHANNELS * sizeof(float)); + memset(tbuf1->getBuffer(), 0, tbuf1->getWidth() * tbuf1->getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float)); for (n = 1; n < settings->iter && (!breaked); n++) { for (y = 0; y < gbuf->getHeight() && (!breaked); y++) { v = ((float)y + 0.5f) / (float)gbuf->getHeight(); @@ -117,9 +117,9 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No } if (isBreaked()) breaked = true; } - memcpy(gbuf->getBuffer(), tbuf1->getBuffer(), tbuf1->getWidth() * tbuf1->getHeight() * COM_NUMBER_OF_CHANNELS * sizeof(float)); + memcpy(gbuf->getBuffer(), tbuf1->getBuffer(), tbuf1->getWidth() * tbuf1->getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float)); } - memcpy(data, gbuf->getBuffer(), gbuf->getWidth() * gbuf->getHeight() * COM_NUMBER_OF_CHANNELS * sizeof(float)); + memcpy(data, gbuf->getBuffer(), gbuf->getWidth() * gbuf->getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float)); delete gbuf; delete tbuf1; diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cpp b/source/blender/compositor/operations/COM_GlareStreaksOperation.cpp index 5644ff30ef3..deeb5094bd0 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cpp +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cpp @@ -36,7 +36,7 @@ void GlareStreaksOperation::generateGlare(float *data, MemoryBuffer *inputTile, bool breaked = false; MemoryBuffer *tsrc = inputTile->duplicate(); - MemoryBuffer *tdst = new MemoryBuffer(NULL, inputTile->getRect()); + MemoryBuffer *tdst = new MemoryBuffer(COM_DT_COLOR, inputTile->getRect()); tdst->clear(); memset(data, 0, size4 * sizeof(float)); diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp index 2733c483146..ff0ffe27b42 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cpp +++ b/source/blender/compositor/operations/COM_ImageOperation.cpp @@ -119,10 +119,10 @@ static void sampleImageAtLocation(ImBuf *ibuf, float x, float y, PixelSampler sa nearest_interpolation_color(ibuf, NULL, color, x, y); break; case COM_PS_BILINEAR: - bilinear_interpolation_color(ibuf, NULL, color, x, y); + bilinear_interpolation_color(ibuf, NULL, color, x - 0.5f, y - 0.5f); break; case COM_PS_BICUBIC: - bicubic_interpolation_color(ibuf, NULL, color, x, y); + bicubic_interpolation_color(ibuf, NULL, color, x - 0.5f, y - 0.5f); break; } } @@ -133,10 +133,10 @@ static void sampleImageAtLocation(ImBuf *ibuf, float x, float y, PixelSampler sa nearest_interpolation_color(ibuf, byte_color, NULL, x, y); break; case COM_PS_BILINEAR: - bilinear_interpolation_color(ibuf, byte_color, NULL, x, y); + bilinear_interpolation_color(ibuf, byte_color, NULL, x - 0.5f, y - 0.5f); break; case COM_PS_BICUBIC: - bicubic_interpolation_color(ibuf, byte_color, NULL, x, y); + bicubic_interpolation_color(ibuf, byte_color, NULL, x - 0.5f, y - 0.5f); break; } rgba_uchar_to_float(color, byte_color); diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cpp b/source/blender/compositor/operations/COM_InpaintOperation.cpp index b64c98be0c7..18611c051d3 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cpp +++ b/source/blender/compositor/operations/COM_InpaintOperation.cpp @@ -83,8 +83,8 @@ float *InpaintSimpleOperation::get_pixel(int x, int y) ASSERT_XY_RANGE(x, y); return &this->m_cached_buffer[ - y * width * COM_NUMBER_OF_CHANNELS + - x * COM_NUMBER_OF_CHANNELS]; + y * width * COM_NUM_CHANNELS_COLOR + + x * COM_NUM_CHANNELS_COLOR]; } int InpaintSimpleOperation::mdist(int x, int y) diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cpp b/source/blender/compositor/operations/COM_KeyingBlurOperation.cpp index ddc09ecb483..72c7512cb23 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cpp @@ -58,7 +58,7 @@ void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data const int start = max(0, x - this->m_size + 1), end = min(bufferWidth, x + this->m_size); for (int cx = start; cx < end; ++cx) { - int bufferIndex = (y * bufferWidth + cx) * 4; + int bufferIndex = (y * bufferWidth + cx); average += buffer[bufferIndex]; count++; } @@ -67,7 +67,7 @@ void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data const int start = max(0, y - this->m_size + 1), end = min(inputBuffer->getHeight(), y + this->m_size); for (int cy = start; cy < end; ++cy) { - int bufferIndex = (cy * bufferWidth + x) * 4; + int bufferIndex = (cy * bufferWidth + x); average += buffer[bufferIndex]; count++; } diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cpp b/source/blender/compositor/operations/COM_KeyingClipOperation.cpp index d9eb7b588a8..d4ba94f240f 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.cpp +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cpp @@ -62,7 +62,7 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data int bufferWidth = inputBuffer->getWidth(); int bufferHeight = inputBuffer->getHeight(); - float value = buffer[(y * bufferWidth + x) * 4]; + float value = buffer[(y * bufferWidth + x)]; bool ok = false; int start_x = max_ff(0, x - delta + 1), @@ -83,7 +83,7 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data continue; } - int bufferIndex = (cy * bufferWidth + cx) * 4; + int bufferIndex = (cy * bufferWidth + cx); float currentValue = buffer[bufferIndex]; if (fabsf(currentValue - value) < tolerance) { diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cpp b/source/blender/compositor/operations/COM_MapUVOperation.cpp index 6bf730253e7..bb8fe825c68 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cpp +++ b/source/blender/compositor/operations/COM_MapUVOperation.cpp @@ -83,11 +83,11 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_ return false; } else { - float col[4]; - m_inputUVProgram->readSampled(col, x, y, COM_PS_BILINEAR); - r_u = col[0] * m_inputColorProgram->getWidth(); - r_v = col[1] * m_inputColorProgram->getHeight(); - r_alpha = col[2]; + float vector[3]; + m_inputUVProgram->readSampled(vector, x, y, COM_PS_BILINEAR); + r_u = vector[0] * m_inputColorProgram->getWidth(); + r_v = vector[1] * m_inputColorProgram->getHeight(); + r_alpha = vector[2]; return true; } } diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cpp b/source/blender/compositor/operations/COM_MovieClipOperation.cpp index 9a184ae1216..2ed498d1303 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cpp +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cpp @@ -103,10 +103,10 @@ void MovieClipBaseOperation::executePixelSampled(float output[4], float x, float nearest_interpolation_color(ibuf, NULL, output, x, y); break; case COM_PS_BILINEAR: - bilinear_interpolation_color(ibuf, NULL, output, x, y); + bilinear_interpolation_color(ibuf, NULL, output, x - 0.5f, y - 0.5f); break; case COM_PS_BICUBIC: - bicubic_interpolation_color(ibuf, NULL, output, x, y); + bicubic_interpolation_color(ibuf, NULL, output, x - 0.5f, y - 0.5f); break; } } diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp index 23fba5a7999..35ab20fb122 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp @@ -54,10 +54,10 @@ void MultilayerColorOperation::executePixelSampled(float output[4], float x, flo nearest_interpolation_color(this->m_buffer, NULL, output, x, y); break; case COM_PS_BILINEAR: - bilinear_interpolation_color(this->m_buffer, NULL, output, x, y); + bilinear_interpolation_color(this->m_buffer, NULL, output, x - 0.5f, y - 0.5f); break; case COM_PS_BICUBIC: - bicubic_interpolation_color(this->m_buffer, NULL, output, x, y); + bicubic_interpolation_color(this->m_buffer, NULL, output, x - 0.5f, y - 0.5f); break; } } diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cpp b/source/blender/compositor/operations/COM_NormalizeOperation.cpp index f81b50e6836..e0b5893ffbb 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cpp +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cpp @@ -104,7 +104,7 @@ void *NormalizeOperation::initializeTileData(rcti *rect) if ((value < minv) && (value >= -BLENDER_ZMAX)) { minv = value; } - bc += 4; + bc ++; } minmult->x = minv; diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cpp b/source/blender/compositor/operations/COM_OutputFileOperation.cpp index 92e8f309ea1..2f92351c00d 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cpp @@ -140,8 +140,9 @@ void OutputSingleLayerOperation::deinitExecution() IMB_colormanagement_imbuf_for_write(ibuf, true, false, m_viewSettings, m_displaySettings, this->m_format); - BKE_makepicstring(filename, this->m_path, bmain->name, this->m_rd->cfra, - this->m_format, (this->m_rd->scemode & R_EXTENSION) != 0, true); + BKE_image_path_from_imformat( + filename, this->m_path, bmain->name, this->m_rd->cfra, + this->m_format, (this->m_rd->scemode & R_EXTENSION) != 0, true); if (0 == BKE_imbuf_write(ibuf, filename, this->m_format)) printf("Cannot save Node File Output to %s\n", filename); @@ -211,8 +212,9 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() char filename[FILE_MAX]; void *exrhandle = IMB_exr_get_handle(); - BKE_makepicstring_from_type(filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER, - (this->m_rd->scemode & R_EXTENSION) != 0, true); + BKE_image_path_from_imtype( + filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER, + (this->m_rd->scemode & R_EXTENSION) != 0, true); BLI_make_existing_file(filename); for (unsigned int i = 0; i < this->m_layers.size(); ++i) { diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cpp b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cpp index fe272000b6e..fb8730c9fa0 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cpp +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cpp @@ -135,7 +135,7 @@ void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect) getInputSocketReader(3) }; float corners[4][2]; readCornersFromSockets(rect, readers, corners); - calculateCorners(corners, true); + calculateCorners(corners, true, 0); m_corners_ready = true; } @@ -194,8 +194,7 @@ void *PlaneCornerPinWarpImageOperation::initializeTileData(rcti *rect) getInputSocketReader(4) }; float corners[4][2]; readCornersFromSockets(rect, readers, corners); - calculateCorners(corners, true); - calculatePerspectiveMatrix(); + calculateCorners(corners, true, 0); m_corners_ready = true; } diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cpp b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cpp index c507b4cfa98..8133f392ac6 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cpp +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cpp @@ -56,36 +56,38 @@ PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : this->addInputSocket(COM_DT_COLOR, COM_SC_NO_RESIZE); this->addOutputSocket(COM_DT_COLOR); this->m_pixelReader = NULL; + this->m_motion_blur_samples = 1; + this->m_motion_blur_shutter = 0.5f; this->setComplex(true); } -void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], bool normalized) +void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], + bool normalized, + int sample) { + BLI_assert(sample < this->m_motion_blur_samples); + const int width = this->m_pixelReader->getWidth(); + const int height = this->m_pixelReader->getHeight(); + float frame_corners[4][2] = {{0.0f, 0.0f}, + {(float) width, 0.0f}, + {(float) width, (float) height}, + {0.0f, (float) height}}; + MotionSample *sample_data = &this->m_samples[sample]; if (normalized) { for (int i = 0; i < 4; i++) { - this->m_frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); - this->m_frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); + sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); + sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); } } else { for (int i = 0; i < 4; i++) { - this->m_frameSpaceCorners[i][0] = corners[i][0]; - this->m_frameSpaceCorners[i][1] = corners[i][1]; + sample_data->frameSpaceCorners[i][0] = corners[i][0]; + sample_data->frameSpaceCorners[i][1] = corners[i][1]; } } -} - -void PlaneDistortWarpImageOperation::calculatePerspectiveMatrix() -{ - const int width = this->m_pixelReader->getWidth(); - const int height = this->m_pixelReader->getHeight(); - float frame_corners[4][2] = {{0.0f, 0.0f}, - {(float) width, 0.0f}, - {(float) width, (float) height}, - {0.0f, (float) height}}; - BKE_tracking_homography_between_two_quads(this->m_frameSpaceCorners, + BKE_tracking_homography_between_two_quads(sample_data->frameSpaceCorners, frame_corners, - this->m_perspectiveMatrix); + sample_data->perspectiveMatrix); } void PlaneDistortWarpImageOperation::initExecution() @@ -100,35 +102,47 @@ void PlaneDistortWarpImageOperation::deinitExecution() void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - float xy[2] = {x, y}; float uv[2]; float deriv[2][2]; - - pixelTransform(xy, uv, deriv); - - m_pixelReader->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1], COM_PS_BILINEAR); -} - -void PlaneDistortWarpImageOperation::pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2]) -{ - warpCoord(xy[0], xy[1], m_perspectiveMatrix, r_uv, r_deriv); + if (this->m_motion_blur_samples == 1) { + warpCoord(x, y, this->m_samples[0].perspectiveMatrix, uv, deriv); + m_pixelReader->readFiltered(output, + uv[0], uv[1], + deriv[0], deriv[1], + COM_PS_BILINEAR); + } + else { + zero_v4(output); + for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) { + float color[4]; + warpCoord(x, y, this->m_samples[sample].perspectiveMatrix, uv, deriv); + m_pixelReader->readFiltered(color, + uv[0], uv[1], + deriv[0], deriv[1], + COM_PS_BILINEAR); + add_v4_v4(output, color); + } + mul_v4_fl(output, 1.0f / (float)this->m_motion_blur_samples); + } } bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) { - float UVs[4][2]; - float deriv[2][2]; - - /* TODO(sergey): figure out proper way to do this. */ - warpCoord(input->xmin - 2, input->ymin - 2, this->m_perspectiveMatrix, UVs[0], deriv); - warpCoord(input->xmax + 2, input->ymin - 2, this->m_perspectiveMatrix, UVs[1], deriv); - warpCoord(input->xmax + 2, input->ymax + 2, this->m_perspectiveMatrix, UVs[2], deriv); - warpCoord(input->xmin - 2, input->ymax + 2, this->m_perspectiveMatrix, UVs[3], deriv); - float min[2], max[2]; INIT_MINMAX2(min, max); - for (int i = 0; i < 4; i++) { - minmax_v2v2_v2(min, max, UVs[i]); + + for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) { + float UVs[4][2]; + float deriv[2][2]; + MotionSample *sample_data = &this->m_samples[sample]; + /* TODO(sergey): figure out proper way to do this. */ + warpCoord(input->xmin - 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); + warpCoord(input->xmax + 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); + warpCoord(input->xmax + 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv); + warpCoord(input->xmin - 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv); + for (int i = 0; i < 4; i++) { + minmax_v2v2_v2(min, max, UVs[i]); + } } rcti newInput; @@ -151,20 +165,26 @@ PlaneDistortMaskOperation::PlaneDistortMaskOperation() : /* Currently hardcoded to 8 samples. */ m_osa = 8; + this->m_motion_blur_samples = 1; + this->m_motion_blur_shutter = 0.5f; } -void PlaneDistortMaskOperation::calculateCorners(const float corners[4][2], bool normalized) +void PlaneDistortMaskOperation::calculateCorners(const float corners[4][2], + bool normalized, + int sample) { + BLI_assert(sample < this->m_motion_blur_samples); + MotionSample *sample_data = &this->m_samples[sample]; if (normalized) { for (int i = 0; i < 4; i++) { - this->m_frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); - this->m_frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); + sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); + sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); } } else { for (int i = 0; i < 4; i++) { - this->m_frameSpaceCorners[i][0] = corners[i][0]; - this->m_frameSpaceCorners[i][1] = corners[i][1]; + sample_data->frameSpaceCorners[i][0] = corners[i][0]; + sample_data->frameSpaceCorners[i][1] = corners[i][1]; } } } @@ -177,18 +197,44 @@ void PlaneDistortMaskOperation::initExecution() void PlaneDistortMaskOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { float point[2]; - int inside_counter = 0; - for (int sample = 0; sample < this->m_osa; sample++) { - point[0] = x + this->m_jitter[sample][0]; - point[1] = y + this->m_jitter[sample][1]; - - if (isect_point_tri_v2(point, this->m_frameSpaceCorners[0], this->m_frameSpaceCorners[1], this->m_frameSpaceCorners[2]) || - isect_point_tri_v2(point, this->m_frameSpaceCorners[0], this->m_frameSpaceCorners[2], this->m_frameSpaceCorners[3])) + if (this->m_motion_blur_samples == 1) { + MotionSample *sample_data = &this->m_samples[0]; + for (int sample = 0; sample < this->m_osa; sample++) { + point[0] = x + this->m_jitter[sample][0]; + point[1] = y + this->m_jitter[sample][1]; + if (isect_point_tri_v2(point, sample_data->frameSpaceCorners[0], + sample_data->frameSpaceCorners[1], + sample_data->frameSpaceCorners[2]) || + isect_point_tri_v2(point, sample_data->frameSpaceCorners[0], + sample_data->frameSpaceCorners[2], + sample_data->frameSpaceCorners[3])) + { + inside_counter++; + } + } + output[0] = (float)inside_counter / this->m_osa; + } + else { + for (int motion_sample = 0; + motion_sample < this->m_motion_blur_samples; + ++motion_sample) { - inside_counter++; + MotionSample *sample_data = &this->m_samples[motion_sample]; + for (int osa_sample = 0; osa_sample < this->m_osa; ++osa_sample) { + point[0] = x + this->m_jitter[osa_sample][0]; + point[1] = y + this->m_jitter[osa_sample][1]; + if (isect_point_tri_v2(point, sample_data->frameSpaceCorners[0], + sample_data->frameSpaceCorners[1], + sample_data->frameSpaceCorners[2]) || + isect_point_tri_v2(point, sample_data->frameSpaceCorners[0], + sample_data->frameSpaceCorners[2], + sample_data->frameSpaceCorners[3])) + { + inside_counter++; + } + } } + output[0] = (float)inside_counter / (this->m_osa * this->m_motion_blur_samples); } - - output[0] = (float) inside_counter / this->m_osa; } diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index ee2874c6b46..fc5dd1ff7d8 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -33,43 +33,72 @@ #include "BLI_listbase.h" #include "BLI_string.h" +#define PLANE_DISTORT_MAX_SAMPLES 64 class PlaneDistortWarpImageOperation : public NodeOperation { protected: + struct MotionSample { + float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ + float perspectiveMatrix[3][3]; + }; SocketReader *m_pixelReader; - float m_frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ - float m_perspectiveMatrix[3][3]; + MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES]; + int m_motion_blur_samples; + float m_motion_blur_shutter; public: PlaneDistortWarpImageOperation(); - void calculateCorners(const float corners[4][2], bool normalized); - void calculatePerspectiveMatrix(); + void calculateCorners(const float corners[4][2], + bool normalized, + int sample); void initExecution(); void deinitExecution(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); - void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2]); bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output); + + void setMotionBlurSamples(int samples) { + BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); + this->m_motion_blur_samples = samples; + } + void setMotionBlurShutter(float shutter) { + this->m_motion_blur_shutter = shutter; + } }; class PlaneDistortMaskOperation : public NodeOperation { protected: + struct MotionSample { + float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ + }; int m_osa; + MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES]; float m_jitter[32][2]; - float m_frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ + int m_motion_blur_samples; + float m_motion_blur_shutter; public: PlaneDistortMaskOperation(); - - void calculateCorners(const float corners[4][2], bool normalized); - + + void calculateCorners(const float corners[4][2], + bool normalized, + int sample); + void initExecution(); - + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); + + void setMotionBlurSamples(int samples) { + BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); + this->m_motion_blur_samples = samples; + } + void setMotionBlurShutter(float shutter) { + this->m_motion_blur_shutter = shutter; + } }; #endif diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cpp b/source/blender/compositor/operations/COM_PlaneTrackOperation.cpp index fec39cbfde0..20a8fa95200 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cpp +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cpp @@ -46,31 +46,29 @@ PlaneTrackCommon::PlaneTrackCommon() this->m_planeTrackName[0] = '\0'; } -void PlaneTrackCommon::readCornersFromTrack(float corners[4][2]) +void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) { MovieTracking *tracking; MovieTrackingObject *object; if (!this->m_movieClip) return; - + tracking = &this->m_movieClip->tracking; - + object = BKE_tracking_object_get_named(tracking, this->m_trackingObjectName); if (object) { MovieTrackingPlaneTrack *plane_track; - plane_track = BKE_tracking_plane_track_get_named(tracking, object, this->m_planeTrackName); - if (plane_track) { MovieTrackingPlaneMarker *plane_marker; - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, this->m_framenumber); - + float clip_framenr = + BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, + frame); plane_marker = BKE_tracking_plane_marker_get(plane_track, clip_framenr); - copy_v2_v2(corners[0], plane_marker->corners[0]); - copy_v2_v2(corners[1], plane_marker->corners[1]); - copy_v2_v2(corners[2], plane_marker->corners[2]); - copy_v2_v2(corners[3], plane_marker->corners[3]); + BKE_tracking_plane_marker_get_subframe_corners(plane_track, + clip_framenr, + corners); } } } @@ -79,14 +77,12 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2], unsigned { resolution[0] = 0; resolution[1] = 0; - + if (this->m_movieClip) { int width, height; MovieClipUser user = {0}; - BKE_movieclip_user_set_frame(&user, this->m_framenumber); BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height); - resolution[0] = width; resolution[1] = height; } @@ -98,10 +94,21 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2], unsigned void PlaneTrackMaskOperation::initExecution() { PlaneDistortMaskOperation::initExecution(); - float corners[4][2]; - readCornersFromTrack(corners); - calculateCorners(corners, true); + if (this->m_motion_blur_samples == 1) { + readCornersFromTrack(corners, this->m_framenumber); + calculateCorners(corners, true, 0); + } + else { + const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter; + const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples; + float frame_iter = frame; + for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) { + readCornersFromTrack(corners, frame_iter); + calculateCorners(corners, true, sample); + frame_iter += frame_step; + } + } } /* ******** PlaneTrackWarpImageOperation ******** */ @@ -109,9 +116,20 @@ void PlaneTrackMaskOperation::initExecution() void PlaneTrackWarpImageOperation::initExecution() { PlaneDistortWarpImageOperation::initExecution(); - + /* TODO(sergey): De-duplicate with mask operation. */ float corners[4][2]; - readCornersFromTrack(corners); - calculateCorners(corners, true); - calculatePerspectiveMatrix(); + if (this->m_motion_blur_samples == 1) { + readCornersFromTrack(corners, this->m_framenumber); + calculateCorners(corners, true, 0); + } + else { + const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter; + const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples; + float frame_iter = frame; + for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) { + readCornersFromTrack(corners, frame_iter); + calculateCorners(corners, true, sample); + frame_iter += frame_step; + } + } } diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index 3c5dd783542..41761493e12 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -43,7 +43,7 @@ protected: /* note: this class is not an operation itself (to prevent virtual inheritance issues) * implementation classes must make wrappers to use these methods, see below. */ - void readCornersFromTrack(float corners[4][2]); + void readCornersFromTrack(float corners[4][2], float frame); void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); public: @@ -68,7 +68,7 @@ public: void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { PlaneTrackCommon::determineResolution(resolution, preferredResolution); - + unsigned int temp[2]; NodeOperation::determineResolution(temp, resolution); } @@ -81,13 +81,12 @@ public: PlaneDistortWarpImageOperation(), PlaneTrackCommon() {} - + void initExecution(); - + void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { PlaneTrackCommon::determineResolution(resolution, preferredResolution); - unsigned int temp[2]; NodeOperation::determineResolution(temp, resolution); } diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.cpp b/source/blender/compositor/operations/COM_QualityStepHelper.cpp index d99d6f28eff..4d9c15f9747 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.cpp +++ b/source/blender/compositor/operations/COM_QualityStepHelper.cpp @@ -37,15 +37,15 @@ void QualityStepHelper::initExecution(QualityHelper helper) case COM_QUALITY_HIGH: default: this->m_step = 1; - this->m_offsetadd = 4; + this->m_offsetadd = 1; break; case COM_QUALITY_MEDIUM: this->m_step = 2; - this->m_offsetadd = 8; + this->m_offsetadd = 2; break; case COM_QUALITY_LOW: this->m_step = 3; - this->m_offsetadd = 12; + this->m_offsetadd = 3; break; } break; diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cpp b/source/blender/compositor/operations/COM_ReadBufferOperation.cpp index 47d5fc6bcca..ad4084a7092 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.cpp +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cpp @@ -24,9 +24,9 @@ #include "COM_WriteBufferOperation.h" #include "COM_defines.h" -ReadBufferOperation::ReadBufferOperation() : NodeOperation() +ReadBufferOperation::ReadBufferOperation(DataType datatype) : NodeOperation() { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(datatype); this->m_single_value = false; this->m_offset = 0; this->m_buffer = NULL; @@ -58,11 +58,19 @@ void ReadBufferOperation::executePixelSampled(float output[4], float x, float y, /* write buffer has a single value stored at (0,0) */ m_buffer->read(output, 0, 0); } - else if (sampler == COM_PS_NEAREST) { - m_buffer->read(output, x, y); - } else { - m_buffer->readBilinear(output, x, y); + switch (sampler) { + case COM_PS_NEAREST: + m_buffer->read(output, x, y); + break; + case COM_PS_BILINEAR: + default: + m_buffer->readBilinear(output, x, y); + break; + case COM_PS_BICUBIC: + m_buffer->readBilinear(output, x, y); + break; + } } } diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h index 569920d51ef..7e5bc55a8ca 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.h +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h @@ -25,6 +25,7 @@ #include "COM_NodeOperation.h" #include "COM_MemoryProxy.h" +#include "COM_MemoryBuffer.h" class ReadBufferOperation : public NodeOperation { private: @@ -33,7 +34,7 @@ private: unsigned int m_offset; MemoryBuffer *m_buffer; public: - ReadBufferOperation(); + ReadBufferOperation(DataType datetype); void setMemoryProxy(MemoryProxy *memoryProxy) { this->m_memoryProxy = memoryProxy; } MemoryProxy *getMemoryProxy() { return this->m_memoryProxy; } void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cpp b/source/blender/compositor/operations/COM_RenderLayersProg.cpp index 06f4f6d77cf..4c6d5c24781 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cpp +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cpp @@ -104,22 +104,13 @@ void RenderLayersBaseProg::doInterpolation(float output[4], float x, float y, Pi } case COM_PS_BILINEAR: - BLI_bilinear_interpolation_fl(this->m_inputBuffer, output, width, height, this->m_elementsize, x, y); + BLI_bilinear_interpolation_fl(this->m_inputBuffer, output, width, height, this->m_elementsize, x - 0.5f, y - 0.5f); break; case COM_PS_BICUBIC: - BLI_bicubic_interpolation_fl(this->m_inputBuffer, output, width, height, this->m_elementsize, x, y); + BLI_bicubic_interpolation_fl(this->m_inputBuffer, output, width, height, this->m_elementsize, x - 0.5f, y - 0.5f); break; } - - if (this->m_elementsize == 1) { - output[1] = 0.0f; - output[2] = 0.0f; - output[3] = 0.0f; - } - else if (this->m_elementsize == 3) { - output[3] = 1.0f; - } } void RenderLayersBaseProg::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -145,7 +136,17 @@ void RenderLayersBaseProg::executePixelSampled(float output[4], float x, float y #endif if (this->m_inputBuffer == NULL) { - zero_v4(output); + int elemsize = this->m_elementsize; + if (elemsize == 1) { + output[0] = 0.0f; + } + else if (elemsize == 3) { + zero_v3(output); + } + else { + BLI_assert(elemsize == 4); + zero_v4(output); + } } else { doInterpolation(output, x, y, sampler); @@ -204,15 +205,12 @@ void RenderLayersAlphaProg::executePixelSampled(float output[4], float x, float float *inputBuffer = this->getInputBuffer(); if (inputBuffer == NULL) { - zero_v4(output); + output[0] = 0.0f; } else { float temp[4]; doInterpolation(temp, x, y, sampler); output[0] = temp[3]; - output[1] = 0.0f; - output[2] = 0.0f; - output[3] = 0.0f; } } @@ -227,7 +225,7 @@ RenderLayersColorOperation::RenderLayersColorOperation() : RenderLayersBaseProg( RenderLayersCyclesOperation::RenderLayersCyclesOperation(int pass) : RenderLayersBaseProg(pass, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Depth Operation ******** */ @@ -245,16 +243,10 @@ void RenderLayersDepthProg::executePixelSampled(float output[4], float x, float if (inputBuffer == NULL || ix < 0 || iy < 0 || ix >= (int)this->getWidth() || iy >= (int)this->getHeight() ) { output[0] = 0.0f; - output[1] = 0.0f; - output[2] = 0.0f; - output[3] = 0.0f; } else { unsigned int offset = (iy * this->getWidth() + ix); output[0] = inputBuffer[offset]; - output[1] = 0.0f; - output[2] = 0.0f; - output[3] = 0.0f; } } @@ -262,21 +254,21 @@ void RenderLayersDepthProg::executePixelSampled(float output[4], float x, float RenderLayersDiffuseOperation::RenderLayersDiffuseOperation() : RenderLayersBaseProg(SCE_PASS_DIFFUSE, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Emit Operation ******** */ RenderLayersEmitOperation::RenderLayersEmitOperation() : RenderLayersBaseProg(SCE_PASS_EMIT, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Environment Operation ******** */ RenderLayersEnvironmentOperation::RenderLayersEnvironmentOperation() : RenderLayersBaseProg(SCE_PASS_ENVIRONMENT, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Image Operation ******** */ @@ -290,7 +282,7 @@ RenderLayersColorProg::RenderLayersColorProg() : RenderLayersBaseProg(SCE_PASS_C RenderLayersIndirectOperation::RenderLayersIndirectOperation() : RenderLayersBaseProg(SCE_PASS_INDIRECT, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Material Index Operation ******** */ @@ -325,28 +317,28 @@ RenderLayersObjectIndexOperation::RenderLayersObjectIndexOperation() : RenderLay RenderLayersReflectionOperation::RenderLayersReflectionOperation() : RenderLayersBaseProg(SCE_PASS_REFLECT, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Refraction Operation ******** */ RenderLayersRefractionOperation::RenderLayersRefractionOperation() : RenderLayersBaseProg(SCE_PASS_REFRACT, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Shadow Operation ******** */ RenderLayersShadowOperation::RenderLayersShadowOperation() : RenderLayersBaseProg(SCE_PASS_SHADOW, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Specular Operation ******** */ RenderLayersSpecularOperation::RenderLayersSpecularOperation() : RenderLayersBaseProg(SCE_PASS_SPEC, 3) { - this->addOutputSocket(COM_DT_COLOR); + this->addOutputSocket(COM_DT_VECTOR); } /* ******** Render Layers Speed Operation ******** */ diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cpp b/source/blender/compositor/operations/COM_SetVectorOperation.cpp index 17212d78e15..769eaf1d2ed 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.cpp +++ b/source/blender/compositor/operations/COM_SetVectorOperation.cpp @@ -33,7 +33,6 @@ void SetVectorOperation::executePixelSampled(float output[4], float x, float y, output[0] = this->m_x; output[1] = this->m_y; output[2] = this->m_z; - output[3] = this->m_w; } void SetVectorOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cpp b/source/blender/compositor/operations/COM_SunBeamsOperation.cpp index 12b04c386f2..0ca687ea162 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cpp +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cpp @@ -135,7 +135,7 @@ struct BufferLineAccumulator { falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; - float *iter = input->getBuffer() + COM_NUMBER_OF_CHANNELS * (x + input->getWidth() * y); + float *iter = input->getBuffer() + COM_NUM_CHANNELS_COLOR * (x + input->getWidth() * y); return iter; } @@ -159,7 +159,7 @@ struct BufferLineAccumulator { zero_v4(output); if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) { - copy_v4_v4(output, input->getBuffer() + COM_NUMBER_OF_CHANNELS * ((int)source[0] + input->getWidth() * (int)source[1])); + copy_v4_v4(output, input->getBuffer() + COM_NUM_CHANNELS_COLOR * ((int)source[0] + input->getWidth() * (int)source[1])); return; } @@ -198,7 +198,7 @@ struct BufferLineAccumulator { /* decrement u */ x -= fxu; y -= fyu; - buffer -= (fxu + fyu * buffer_width) * COM_NUMBER_OF_CHANNELS; + buffer -= (fxu + fyu * buffer_width) * COM_NUM_CHANNELS_COLOR; /* decrement v (in steps of dv < 1) */ v_local -= dv; @@ -207,7 +207,7 @@ struct BufferLineAccumulator { x -= fxv; y -= fyv; - buffer -= (fxv + fyv * buffer_width) * COM_NUMBER_OF_CHANNELS; + buffer -= (fxv + fyv * buffer_width) * COM_NUM_CHANNELS_COLOR; } } diff --git a/source/blender/compositor/operations/COM_TextureOperation.cpp b/source/blender/compositor/operations/COM_TextureOperation.cpp index ede767cbff7..1b0485afb8d 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cpp +++ b/source/blender/compositor/operations/COM_TextureOperation.cpp @@ -77,12 +77,9 @@ void TextureBaseOperation::determineResolution(unsigned int resolution[2], unsig void TextureAlphaOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { - TextureBaseOperation::executePixelSampled(output, x, y, sampler); - output[0] = output[3]; - output[1] = 0.0f; - output[2] = 0.0f; - output[3] = 0.0f; -} + float color[4]; + TextureBaseOperation::executePixelSampled(color, x, y, sampler); + output[0] = color[3];} void TextureBaseOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { @@ -103,7 +100,7 @@ void TextureBaseOperation::executePixelSampled(float output[4], float x, float y vec[1] = textureSize[1] * (v + textureOffset[1]); vec[2] = textureSize[2] * textureOffset[2]; - retval = multitex_ext(this->m_texture, vec, NULL, NULL, 0, &texres, m_pool, m_sceneColorManage); + retval = multitex_ext(this->m_texture, vec, NULL, NULL, 0, &texres, m_pool, m_sceneColorManage, false); if (texres.talpha) output[3] = texres.ta; @@ -124,18 +121,23 @@ MemoryBuffer *TextureBaseOperation::createMemoryBuffer(rcti *rect2) { int height = getHeight(); int width = getWidth(); + DataType datatype = this->getOutputSocket()->getDataType(); + int add = 4; + if (datatype == COM_DT_VALUE) { + add = 1; + } rcti rect; rect.xmin = 0; rect.ymin = 0; rect.xmax = width; rect.ymax = height; - MemoryBuffer *result = new MemoryBuffer(NULL, &rect); + MemoryBuffer *result = new MemoryBuffer(datatype, &rect); float *data = result->getBuffer(); for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++, data += 4) { + for (int x = 0; x < width; x++, data += add) { this->executePixelSampled(data, x, y, COM_PS_NEAREST); } } diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cpp b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cpp index 4b81001fdab..4809efd7436 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cpp @@ -137,30 +137,33 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, copy_v4_fl(multiplier_accum, 1.0f); float size_center = tempSize[0] * scalar; - const int addXStep = QualityStepHelper::getStep() * COM_NUMBER_OF_CHANNELS; - + const int addXStepValue = QualityStepHelper::getStep(); + const int addYStepValue = addXStepValue; + const int addXStepColor = addXStepValue * COM_NUM_CHANNELS_COLOR; + if (size_center > this->m_threshold) { - for (int ny = miny; ny < maxy; ny += QualityStepHelper::getStep()) { + for (int ny = miny; ny < maxy; ny += addYStepValue) { float dy = ny - y; - int offsetNy = ny * inputSizeBuffer->getWidth() * COM_NUMBER_OF_CHANNELS; - int offsetNxNy = offsetNy + (minx * COM_NUMBER_OF_CHANNELS); - for (int nx = minx; nx < maxx; nx += QualityStepHelper::getStep()) { + int offsetValueNy = ny * inputSizeBuffer->getWidth(); + int offsetValueNxNy = offsetValueNy + (minx); + int offsetColorNxNy = offsetValueNxNy * COM_NUM_CHANNELS_COLOR; + for (int nx = minx; nx < maxx; nx += addXStepValue) { if (nx != x || ny != y) { - float size = min(inputSizeFloatBuffer[offsetNxNy] * scalar, size_center); + float size = min(inputSizeFloatBuffer[offsetValueNxNy] * scalar, size_center); if (size > this->m_threshold) { float dx = nx - x; if (size > fabsf(dx) && size > fabsf(dy)) { float uv[2] = { - (float)(COM_BLUR_BOKEH_PIXELS / 2) + (dx / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1), - (float)(COM_BLUR_BOKEH_PIXELS / 2) + (dy / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1)}; - inputBokehBuffer->readNoCheck(bokeh, uv[0], uv[1]); - madd_v4_v4v4(color_accum, bokeh, &inputProgramFloatBuffer[offsetNxNy]); + (float)(COM_BLUR_BOKEH_PIXELS / 2) + (dx / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1), + (float)(COM_BLUR_BOKEH_PIXELS / 2) + (dy / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1)}; + inputBokehBuffer->read(bokeh, uv[0], uv[1]); + madd_v4_v4v4(color_accum, bokeh, &inputProgramFloatBuffer[offsetColorNxNy]); add_v4_v4(multiplier_accum, bokeh); } } } - offsetNxNy += addXStep; - } + offsetColorNxNy += addXStepColor; + offsetValueNxNy += addXStepValue; } } } diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cpp b/source/blender/compositor/operations/COM_VectorBlurOperation.cpp index f5890157440..8dc06ef07d7 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cpp @@ -57,7 +57,7 @@ void VectorBlurOperation::initExecution() void VectorBlurOperation::executePixel(float output[4], int x, int y, void *data) { float *buffer = (float *) data; - int index = (y * this->getWidth() + x) * COM_NUMBER_OF_CHANNELS; + int index = (y * this->getWidth() + x) * COM_NUM_CHANNELS_COLOR; copy_v4_v4(output, &buffer[index]); } @@ -108,14 +108,12 @@ bool VectorBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBuff void VectorBlurOperation::generateVectorBlur(float *data, MemoryBuffer *inputImage, MemoryBuffer *inputSpeed, MemoryBuffer *inputZ) { - float *zbuf = inputZ->convertToValueBuffer(); NodeBlurData blurdata; blurdata.samples = this->m_settings->samples / QualityStepHelper::getStep(); blurdata.maxspeed = this->m_settings->maxspeed; blurdata.minspeed = this->m_settings->minspeed; blurdata.curved = this->m_settings->curved; blurdata.fac = this->m_settings->fac; - RE_zbuf_accumulate_vecblur(&blurdata, this->getWidth(), this->getHeight(), data, inputImage->getBuffer(), inputSpeed->getBuffer(), zbuf); - MEM_freeN((void *)zbuf); + RE_zbuf_accumulate_vecblur(&blurdata, this->getWidth(), this->getHeight(), data, inputImage->getBuffer(), inputSpeed->getBuffer(), inputZ->getBuffer()); return; } diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cpp b/source/blender/compositor/operations/COM_VectorCurveOperation.cpp index fedc8c6db7d..dfdc54012a8 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.cpp +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cpp @@ -51,7 +51,6 @@ void VectorCurveOperation::executePixelSampled(float output[4], float x, float y this->m_inputProgram->readSampled(input, x, y, sampler); curvemapping_evaluate_premulRGBF(this->m_curveMapping, output, input); - output[3] = input[3]; } void VectorCurveOperation::deinitExecution() diff --git a/source/blender/compositor/operations/COM_WrapOperation.cpp b/source/blender/compositor/operations/COM_WrapOperation.cpp index c30361d1df8..7fbef453a13 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cpp +++ b/source/blender/compositor/operations/COM_WrapOperation.cpp @@ -23,7 +23,7 @@ #include "COM_WrapOperation.h" -WrapOperation::WrapOperation() : ReadBufferOperation() +WrapOperation::WrapOperation(DataType datatype) : ReadBufferOperation(datatype) { this->m_wrappingType = CMP_NODE_WRAP_NONE; } diff --git a/source/blender/compositor/operations/COM_WrapOperation.h b/source/blender/compositor/operations/COM_WrapOperation.h index 33ea1280564..92c93565691 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.h +++ b/source/blender/compositor/operations/COM_WrapOperation.h @@ -29,7 +29,7 @@ class WrapOperation : public ReadBufferOperation { private: int m_wrappingType; public: - WrapOperation(); + WrapOperation(DataType datetype); bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cpp b/source/blender/compositor/operations/COM_WriteBufferOperation.cpp index 832864b399d..fccff2a33bf 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cpp +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cpp @@ -25,10 +25,10 @@ #include <stdio.h> #include "COM_OpenCLDevice.h" -WriteBufferOperation::WriteBufferOperation() : NodeOperation() +WriteBufferOperation::WriteBufferOperation(DataType datatype) : NodeOperation() { - this->addInputSocket(COM_DT_COLOR); - this->m_memoryProxy = new MemoryProxy(); + this->addInputSocket(datatype); + this->m_memoryProxy = new MemoryProxy(datatype); this->m_memoryProxy->setWriteBufferOperation(this); this->m_memoryProxy->setExecutor(NULL); } @@ -61,6 +61,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int tileNumber) { MemoryBuffer *memoryBuffer = this->m_memoryProxy->getBuffer(); float *buffer = memoryBuffer->getBuffer(); + const int num_channels = memoryBuffer->get_num_channels(); if (this->m_input->isComplex()) { void *data = this->m_input->initializeTileData(rect); int x1 = rect->xmin; @@ -71,10 +72,10 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int tileNumber) int y; bool breaked = false; for (y = y1; y < y2 && (!breaked); y++) { - int offset4 = (y * memoryBuffer->getWidth() + x1) * COM_NUMBER_OF_CHANNELS; + int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; for (x = x1; x < x2; x++) { this->m_input->read(&(buffer[offset4]), x, y, data); - offset4 += COM_NUMBER_OF_CHANNELS; + offset4 += num_channels; } if (isBreaked()) { breaked = true; @@ -96,10 +97,10 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int tileNumber) int y; bool breaked = false; for (y = y1; y < y2 && (!breaked); y++) { - int offset4 = (y * memoryBuffer->getWidth() + x1) * COM_NUMBER_OF_CHANNELS; + int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; for (x = x1; x < x2; x++) { this->m_input->readSampled(&(buffer[offset4]), x, y, COM_PS_NEAREST); - offset4 += COM_NUMBER_OF_CHANNELS; + offset4 += num_channels; } if (isBreaked()) { breaked = true; @@ -126,12 +127,9 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, rcti *rect, const unsigned int outputBufferWidth = outputBuffer->getWidth(); const unsigned int outputBufferHeight = outputBuffer->getHeight(); - const cl_image_format imageFormat = { - CL_RGBA, - CL_FLOAT - }; + const cl_image_format *imageFormat = device->determineImageFormat(outputBuffer); - cl_mem clOutputBuffer = clCreateImage2D(device->getContext(), CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, &imageFormat, outputBufferWidth, outputBufferHeight, 0, outputFloatBuffer, &error); + cl_mem clOutputBuffer = clCreateImage2D(device->getContext(), CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, imageFormat, outputBufferWidth, outputBufferHeight, 0, outputFloatBuffer, &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } // STEP 2 diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h index 96466df979c..9220cb179c6 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.h +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h @@ -35,7 +35,7 @@ class WriteBufferOperation : public NodeOperation { bool m_single_value; /* single value stored in buffer */ NodeOperation *m_input; public: - WriteBufferOperation(); + WriteBufferOperation(DataType datatype); ~WriteBufferOperation(); MemoryProxy *getMemoryProxy() { return this->m_memoryProxy; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); diff --git a/source/blender/datatoc/datatoc_icon_split.py b/source/blender/datatoc/datatoc_icon_split.py index 55f0a5c5f6a..aae907ec340 100755 --- a/source/blender/datatoc/datatoc_icon_split.py +++ b/source/blender/datatoc/datatoc_icon_split.py @@ -63,7 +63,7 @@ def image_from_file(filepath): try: import bpy - except: + except ImportError: bpy = None if bpy is not None: diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index d76eba93640..37c40052275 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -61,6 +61,7 @@ #include "RNA_access.h" +#include "BKE_animsys.h" #include "BKE_curve.h" #include "BKE_key.h" #include "BKE_nla.h" @@ -3519,6 +3520,7 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float /* step 5) draw name ............................................... */ /* TODO: when renaming, we might not want to draw this, especially if name happens to be longer than channel */ if (acf->name) { + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; char name[ANIM_CHAN_NAME_SIZE]; /* hopefully this will be enough! */ /* set text color */ @@ -3532,7 +3534,7 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float acf->name(ale, name); offset += 3; - UI_draw_string(offset, ytext, name); + UI_fontstyle_draw_simple(fstyle, offset, ytext, name); /* draw red underline if channel is disabled */ if ((ale->type == ANIMTYPE_FCURVE) && (ale->flag & FCURVE_DISABLED)) { @@ -3688,6 +3690,7 @@ static void achannel_nlatrack_solo_widget_cb(bContext *C, void *adt_poin, void * static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poin) { ID *id = (ID *)id_poin; + AnimData *adt = BKE_animdata_from_id(id); FCurve *fcu = (FCurve *)fcu_poin; ReportList *reports = CTX_wm_reports(C); @@ -3698,9 +3701,8 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi bool done = false; float cfra; - /* get current frame */ - // NOTE: this will do for now... - cfra = (float)CFRA; + /* get current frame and apply NLA-mapping to it (if applicable) */ + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); /* get flags for keyframing */ flag = ANIM_get_keyframing_flags(scene, 1); @@ -3737,9 +3739,8 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi bool done = false; float cfra; - /* get current frame */ - // NOTE: this will do for now... - cfra = (float)CFRA; + /* get current frame and apply NLA-mapping to it (if applicable) */ + cfra = BKE_nla_tweakedit_remap(key->adt, (float)CFRA, NLATIME_CONVERT_UNMAP); /* get flags for keyframing */ flag = ANIM_get_keyframing_flags(scene, 1); diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 2d7656642d3..0e052279796 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -59,6 +59,7 @@ /* Draw current frame number in a little green box beside the current frame indicator */ static void draw_cfra_number(Scene *scene, View2D *v2d, const float cfra, const bool time) { + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; float xscale, yscale, x, y; char numstr[32] = " t"; /* t is the character to start replacing from */ int slen; @@ -78,7 +79,8 @@ static void draw_cfra_number(Scene *scene, View2D *v2d, const float cfra, const else { BLI_timecode_string_from_time_simple(&numstr[4], sizeof(numstr) - 4, 1, cfra); } - slen = UI_fontstyle_string_width(numstr) - 1; + + slen = UI_fontstyle_string_width(fstyle, numstr) - 1; /* get starting coordinates for drawing */ x = cfra * xscale; @@ -90,7 +92,7 @@ static void draw_cfra_number(Scene *scene, View2D *v2d, const float cfra, const /* draw current frame number - black text */ UI_ThemeColor(TH_TEXT); - UI_draw_string(x - 0.25f * U.widget_unit, y + 0.15f * U.widget_unit, numstr); + UI_fontstyle_draw_simple(fstyle, x - 0.25f * U.widget_unit, y + 0.15f * U.widget_unit, numstr); /* restore view transform */ glScalef(xscale, 1.0, 1.0); diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index b6c7a4aa5a0..140b7e0b117 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -317,7 +317,7 @@ void debug_markers_print_list(ListBase *markers) /* function to draw markers */ static void draw_marker( - View2D *v2d, TimeMarker *marker, int cfra, int flag, + View2D *v2d, const uiFontStyle *fstyle, TimeMarker *marker, int cfra, int flag, /* avoid re-calculating each time */ const float ypixels, const float xscale, const float yscale) { @@ -399,13 +399,14 @@ static void draw_marker( } #endif - UI_draw_string(x, y, marker->name); + UI_fontstyle_draw_simple(fstyle, x, y, marker->name); } } /* Draw Scene-Markers in time window */ void ED_markers_draw(const bContext *C, int flag) { + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; ListBase *markers = ED_context_get_markers(C); View2D *v2d; TimeMarker *marker; @@ -455,7 +456,7 @@ void ED_markers_draw(const bContext *C, int flag) if ((marker->frame >= v2d_clip_range_x[0]) && (marker->frame <= v2d_clip_range_x[1])) { - draw_marker(v2d, marker, scene->r.cfra, flag, + draw_marker(v2d, fstyle, marker, scene->r.cfra, flag, ypixels, xscale, yscale); } } @@ -1056,7 +1057,7 @@ static void select_timeline_marker_frame(ListBase *markers, int frame, bool exte } BLI_LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_first) { - /* this way a not-extend select will allways give 1 selected marker */ + /* this way a not-extend select will always give 1 selected marker */ if (marker->frame == frame) { marker->flag ^= SELECT; break; diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 8ede1a0ad76..bcdad1c93ad 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -130,6 +130,7 @@ static void draw_modifier__generator(uiLayout *layout, ID *id, FModifier *fcm, s switch (data->mode) { case FCM_GENERATOR_POLYNOMIAL: /* polynomial expression */ { + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; float *cp = NULL; char xval[32]; unsigned int i; @@ -147,11 +148,12 @@ static void draw_modifier__generator(uiLayout *layout, ID *id, FModifier *fcm, s /* calculate maximum width of label for "x^n" labels */ if (data->arraysize > 2) { BLI_snprintf(xval, sizeof(xval), "x^%u", data->arraysize); - maxXWidth = UI_fontstyle_string_width(xval) + 0.5 * UI_UNIT_X; /* XXX: UI_fontstyle_string_width is not accurate */ + /* XXX: UI_fontstyle_string_width is not accurate */ + maxXWidth = UI_fontstyle_string_width(fstyle, xval) + 0.5 * UI_UNIT_X; } else { /* basic size (just "x") */ - maxXWidth = UI_fontstyle_string_width("x") + 0.5 * UI_UNIT_X; + maxXWidth = UI_fontstyle_string_width(fstyle, "x") + 0.5 * UI_UNIT_X; } /* draw controls for each coefficient and a + sign at end of row */ diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index 439b3b94974..ee1bfdff0bd 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -785,7 +785,8 @@ static short mirror_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt) static short mirror_bezier_yaxis(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { if (bezt->f2 & SELECT) { - mirror_bezier_yaxis_ex(bezt, 0.0f); + /* Yes, names are inverted, we are mirroring accross y axis, hence along x axis... */ + mirror_bezier_xaxis_ex(bezt, 0.0f); } return 0; @@ -794,7 +795,8 @@ static short mirror_bezier_yaxis(KeyframeEditData *UNUSED(ked), BezTriple *bezt) static short mirror_bezier_xaxis(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { if (bezt->f2 & SELECT) { - mirror_bezier_xaxis_ex(bezt, 0.0f); + /* Yes, names are inverted, we are mirroring accross x axis, hence along y axis... */ + mirror_bezier_yaxis_ex(bezt, 0.0f); } return 0; @@ -814,7 +816,7 @@ static short mirror_bezier_value(KeyframeEditData *ked, BezTriple *bezt) { /* value to mirror over is stored in the custom data -> first float value slot */ if (bezt->f2 & SELECT) { - mirror_bezier_xaxis_ex(bezt, ked->f1); + mirror_bezier_yaxis_ex(bezt, ked->f1); } return 0; diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 7ac11c1cd06..932a00d1687 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -641,7 +641,7 @@ static void flip_names(tAnimCopybufItem *aci, char **name) str_iter = *name = MEM_mallocN(sizeof(char) * (prefix_l + postfix_l + length + 1), "flipped_path"); BLI_strncpy(str_iter, aci->rna_path, prefix_l + 1); - str_iter += prefix_l ; + str_iter += prefix_l; BLI_strncpy(str_iter, bname_new, length + 1); str_iter += length; BLI_strncpy(str_iter, str_end, postfix_l + 1); @@ -663,14 +663,14 @@ static tAnimCopybufItem *pastebuf_match_path_full(FCurve *fcu, const short from_ if ((from_single) || (aci->array_index == fcu->array_index)) { char *name = NULL; flip_names(aci, &name); - if (strcmp(name, fcu->rna_path) == 0) { + if (STREQ(name, fcu->rna_path)) { MEM_freeN(name); break; } MEM_freeN(name); } } - else if (to_simple || (strcmp(aci->rna_path, fcu->rna_path) == 0)) { + else if (to_simple || STREQ(aci->rna_path, fcu->rna_path)) { if ((from_single) || (aci->array_index == fcu->array_index)) { break; } @@ -711,7 +711,7 @@ static tAnimCopybufItem *pastebuf_match_path_property(FCurve *fcu, const short f int len_path = strlen(fcu->rna_path); if (len_id <= len_path) { /* note, paths which end with "] will fail with this test - Animated ID Props */ - if (strcmp(identifier, fcu->rna_path + (len_path - len_id)) == 0) { + if (STREQ(identifier, fcu->rna_path + (len_path - len_id))) { if ((from_single) || (aci->array_index == fcu->array_index)) break; } diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index a5c0eee8d3b..4b9a629183e 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -556,7 +556,7 @@ KeyingSet *ANIM_builtin_keyingset_get_named(KeyingSet *prevKS, const char name[] /* loop over KeyingSets checking names */ for (ks = first; ks; ks = ks->next) { - if (strcmp(name, ks->idname) == 0) + if (STREQ(name, ks->idname)) return ks; } @@ -603,7 +603,7 @@ void ANIM_keyingset_info_unregister(Main *bmain, KeyingSetInfo *ksi) ksn = ks->next; /* remove if matching typeinfo name */ - if (strcmp(ks->typeinfo, ksi->idname) == 0) { + if (STREQ(ks->typeinfo, ksi->idname)) { Scene *scene; BKE_keyingset_free(ks); BLI_remlink(&builtin_keyingsets, ks); @@ -914,6 +914,37 @@ short ANIM_validate_keyingset(bContext *C, ListBase *dsources, KeyingSet *ks) return 0; } +/* Determine which keying flags apply based on the override flags */ +static short keyingset_apply_keying_flags(const short base_flags, const short overrides, const short own_flags) +{ + short result = 0; + + /* The logic for whether a keying flag applies is as follows: + * - If the flag in question is set in "overrides", that means that the + * status of that flag in "own_flags" is used + * - If however the flag isn't set, then its value in "base_flags" is used + * instead (i.e. no override) + */ +#define APPLY_KEYINGFLAG_OVERRIDE(kflag) \ + if (overrides & kflag) { \ + result |= (own_flags & kflag); \ + } \ + else { \ + result |= (base_flags & kflag); \ + } + + /* Apply the flags one by one... + * (See rna_def_common_keying_flags() for the supported flags) + */ + APPLY_KEYINGFLAG_OVERRIDE(INSERTKEY_NEEDED) + APPLY_KEYINGFLAG_OVERRIDE(INSERTKEY_MATRIX) + APPLY_KEYINGFLAG_OVERRIDE(INSERTKEY_XYZ2RGB) + +#undef APPLY_KEYINGFLAG_OVERRIDE + + return result; +} + /* Given a KeyingSet and context info (if required), modify keyframes for the channels specified * by the KeyingSet. This takes into account many of the different combinations of using KeyingSets. * Returns the number of channels that keyframes were added to @@ -923,7 +954,8 @@ int ANIM_apply_keyingset(bContext *C, ListBase *dsources, bAction *act, KeyingSe Scene *scene = CTX_data_scene(C); ReportList *reports = CTX_wm_reports(C); KS_Path *ksp; - int kflag = 0, success = 0; + const short base_kflags = ANIM_get_keyframing_flags(scene, 1); + short kflag = 0, success = 0; const char *groupname = NULL; /* sanity checks */ @@ -932,11 +964,8 @@ int ANIM_apply_keyingset(bContext *C, ListBase *dsources, bAction *act, KeyingSe /* get flags to use */ if (mode == MODIFYKEY_MODE_INSERT) { - /* use KeyingSet's flags as base */ - kflag = ks->keyingflag; - - /* supplement with info from the context */ - kflag |= ANIM_get_keyframing_flags(scene, 1); + /* use context settings as base */ + kflag = keyingset_apply_keying_flags(base_kflags, ks->keyingoverride, ks->keyingflag); } else if (mode == MODIFYKEY_MODE_DELETE) kflag = 0; @@ -962,8 +991,8 @@ int ANIM_apply_keyingset(bContext *C, ListBase *dsources, bAction *act, KeyingSe continue; } - /* since keying settings can be defined on the paths too, extend the path before using it */ - kflag2 = (kflag | ksp->keyingflag); + /* since keying settings can be defined on the paths too, apply the settings for this path first */ + kflag2 = keyingset_apply_keying_flags(kflag, ksp->keyingoverride, ksp->keyingflag); /* get pointer to name of group to add channels to */ if (ksp->groupmode == KSP_GROUP_NONE) diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index eba1bc4d78d..22aaeccc4a8 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -270,7 +270,7 @@ static EditBone *get_named_editbone(ListBase *edbo, const char *name) if (name) { for (eBone = edbo->first; eBone; eBone = eBone->next) { - if (!strcmp(name, eBone->name)) + if (STREQ(name, eBone->name)) return eBone; } } diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 7f7843a6d1d..88c52989c07 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -38,6 +38,8 @@ #include "MEM_guardedalloc.h" +#include "BLF_translation.h" + #include "BLI_blenlib.h" #include "BLI_math.h" @@ -236,28 +238,49 @@ float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const bool return roll; } - +/* note, ranges arithmatic is used below */ typedef enum eCalcRollTypes { - CALC_ROLL_X = 0, - CALC_ROLL_Y = 1, - CALC_ROLL_Z = 2, - - CALC_ROLL_TAN_X = 3, - CALC_ROLL_TAN_Z = 4, - - CALC_ROLL_ACTIVE = 5, - CALC_ROLL_VIEW = 6, - CALC_ROLL_CURSOR = 7, + /* pos */ + CALC_ROLL_POS_X = 0, + CALC_ROLL_POS_Y, + CALC_ROLL_POS_Z, + + CALC_ROLL_TAN_POS_X, + CALC_ROLL_TAN_POS_Z, + + /* neg */ + CALC_ROLL_NEG_X, + CALC_ROLL_NEG_Y, + CALC_ROLL_NEG_Z, + + CALC_ROLL_TAN_NEG_X, + CALC_ROLL_TAN_NEG_Z, + + /* no sign */ + CALC_ROLL_ACTIVE, + CALC_ROLL_VIEW, + CALC_ROLL_CURSOR, } eCalcRollTypes; static EnumPropertyItem prop_calc_roll_types[] = { - {CALC_ROLL_TAN_X, "X", 0, "Local X Tangent", ""}, - {CALC_ROLL_TAN_Z, "Z", 0, "Local Z Tangent", ""}, + {0, "", 0, N_("Positive"), ""}, + {CALC_ROLL_TAN_POS_X, "POS_X", 0, "Local +X Tangent", ""}, + {CALC_ROLL_TAN_POS_Z, "POS_Z", 0, "Local +Z Tangent", ""}, + + {CALC_ROLL_POS_X, "GLOBAL_POS_X", 0, "Global +X Axis", ""}, + {CALC_ROLL_POS_Y, "GLOBAL_POS_Y", 0, "Global +Y Axis", ""}, + {CALC_ROLL_POS_Z, "GLOBAL_POS_Z", 0, "Global +Z Axis", ""}, - {CALC_ROLL_X, "GLOBAL_X", 0, "Global X Axis", ""}, - {CALC_ROLL_Y, "GLOBAL_Y", 0, "Global Y Axis", ""}, - {CALC_ROLL_Z, "GLOBAL_Z", 0, "Global Z Axis", ""}, + {0, "", 0, N_("Negative"), ""}, + {CALC_ROLL_TAN_NEG_X, "NEG_X", 0, "Local -X Tangent", ""}, + {CALC_ROLL_TAN_NEG_Z, "NEG_Z", 0, "Local -Z Tangent", ""}, + + {CALC_ROLL_NEG_X, "GLOBAL_NEG_X", 0, "Global -X Axis", ""}, + {CALC_ROLL_NEG_Y, "GLOBAL_NEG_Y", 0, "Global -Y Axis", ""}, + {CALC_ROLL_NEG_Z, "GLOBAL_NEG_Z", 0, "Global -Z Axis", ""}, + + {0, "", 0, N_("Other"), ""}, {CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""}, {CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""}, {CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""}, @@ -268,15 +291,22 @@ static EnumPropertyItem prop_calc_roll_types[] = { static int armature_calc_roll_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_edit_object(C); - const short type = RNA_enum_get(op->ptr, "type"); + eCalcRollTypes type = RNA_enum_get(op->ptr, "type"); const bool axis_only = RNA_boolean_get(op->ptr, "axis_only"); - const bool axis_flip = RNA_boolean_get(op->ptr, "axis_flip"); + /* axis_flip when matching the active bone never makes sense */ + bool axis_flip = ((type >= CALC_ROLL_ACTIVE) ? RNA_boolean_get(op->ptr, "axis_flip") : + (type >= CALC_ROLL_TAN_NEG_X) ? true : false); float imat[3][3]; bArmature *arm = ob->data; EditBone *ebone; + if ((type >= CALC_ROLL_NEG_X) && (type <= CALC_ROLL_TAN_NEG_Z)) { + type -= (CALC_ROLL_ACTIVE - CALC_ROLL_NEG_X); + axis_flip = true; + } + copy_m3_m4(imat, ob->obmat); invert_m3(imat); @@ -302,7 +332,7 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op) } } } - else if (ELEM(type, CALC_ROLL_TAN_X, CALC_ROLL_TAN_Z)) { + else if (ELEM(type, CALC_ROLL_TAN_POS_X, CALC_ROLL_TAN_POS_Z)) { for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { if (ebone->parent) { bool is_edit = (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)); @@ -323,7 +353,7 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op) sub_v3_v3v3(dir_b, ebone_other->head, ebone_other->tail); normalize_v3(dir_b); - if (type == CALC_ROLL_TAN_Z) { + if (type == CALC_ROLL_TAN_POS_Z) { cross_v3_v3v3(vec, dir_a, dir_b); } else { @@ -374,7 +404,7 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op) copy_v3_v3(vec, mat[2]); } else { /* Axis */ - assert(type >= 0 && type <= 5); + assert(type <= 5); if (type < 3) vec[type] = 1.0f; else vec[type - 2] = -1.0f; mul_m3_v3(imat, vec); @@ -423,7 +453,7 @@ void ARMATURE_OT_calculate_roll(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, CALC_ROLL_TAN_X, "Type", ""); + ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, CALC_ROLL_TAN_POS_X, "Type", ""); RNA_def_boolean(ot->srna, "axis_flip", 0, "Flip Axis", "Negate the alignment axis"); RNA_def_boolean(ot->srna, "axis_only", 0, "Shortest Rotation", "Ignore the axis direction, use the shortest rotation to align"); } diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 9afc45955bd..a8b5f888597 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -141,7 +141,7 @@ void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *n char oldname[MAXBONENAME]; /* names better differ! */ - if (strncmp(oldnamep, newnamep, MAXBONENAME)) { + if (!STREQLEN(oldnamep, newnamep, MAXBONENAME)) { /* we alter newname string... so make copy */ BLI_strncpy(newname, newnamep, MAXBONENAME); @@ -219,7 +219,7 @@ void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *n if (ob->parent && (ob->parent->data == arm)) { if (ob->partype == PARBONE) { /* bone name in object */ - if (!strcmp(ob->parsubstr, oldname)) + if (STREQ(ob->parsubstr, oldname)) BLI_strncpy(ob->parsubstr, newname, MAXBONENAME); } } @@ -267,6 +267,7 @@ void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *n /* Fix all animdata that may refer to this bone - we can't just do the ones attached to objects, since * other ID-blocks may have drivers referring to this bone [#29822] */ + // XXX: the ID here is for armatures, but most bone drivers are actually on the object instead... { BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname); @@ -284,7 +285,7 @@ void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *n if (sl->spacetype == SPACE_VIEW3D) { View3D *v3d = (View3D *)sl; if (v3d->ob_centre && v3d->ob_centre->data == arm) { - if (!strcmp(v3d->ob_centre_bone, oldname)) { + if (STREQ(v3d->ob_centre_bone, oldname)) { BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME); } } diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index 3e226c39c8c..53989dd783c 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -29,20 +29,26 @@ * \ingroup edarmature */ +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_constraint_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" #include "BLI_math.h" #include "BLF_translation.h" #include "BKE_action.h" +#include "BKE_animsys.h" #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_depsgraph.h" +#include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_report.h" @@ -98,33 +104,122 @@ static void joined_armature_fix_links_constraints( /* action constraint? (pose constraints only) */ if (con->type == CONSTRAINT_TYPE_ACTION) { - bActionConstraint *data = con->data; // XXX old animation system - bAction *act; - bActionChannel *achan; + bActionConstraint *data = con->data; if (data->act) { - act = data->act; + BKE_action_fix_paths_rename(&tarArm->id, data->act, "pose.bones[", + pchan->name, curbone->name, 0, 0, false); + } + } + + } +} + +/* userdata for joined_armature_fix_animdata_cb() */ +typedef struct tJoinArmature_AdtFixData { + Object *srcArm; + Object *tarArm; + + GHash *names_map; +} tJoinArmature_AdtFixData; - for (achan = act->chanbase.first; achan; achan = achan->next) { - if (STREQ(achan->name, pchan->name)) { - BLI_strncpy(achan->name, curbone->name, sizeof(achan->name)); +/* Callback to pass to void BKE_animdata_main_cb() for fixing driver ID's to point to the new ID */ +/* FIXME: For now, we only care about drivers here. When editing rigs, it's very rare to have animation + * on the rigs being edited already, so it should be safe to skip these. + */ +static void joined_armature_fix_animdata_cb(ID *id, AnimData *adt, void *user_data) +{ + tJoinArmature_AdtFixData *afd = (tJoinArmature_AdtFixData *)user_data; + ID *src_id = &afd->srcArm->id; + ID *dst_id = &afd->tarArm->id; + + GHashIterator gh_iter; + FCurve *fcu; + + /* Fix paths - If this is the target object, it will have some "dirty" paths */ + if (id == src_id) { + /* Fix drivers */ + for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { + /* skip driver if it doesn't affect the bones */ + if (strstr(fcu->rna_path, "pose.bones[") == NULL) { + continue; + } + + // FIXME: this is too crude... it just does everything! + GHASH_ITER(gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed; this still means there will be some waste if there aren't many drivers/keys */ + if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { + fcu->rna_path = BKE_animsys_fix_rna_path_rename(id, fcu->rna_path, "pose.bones", + old_name, new_name, 0, 0, false); + + /* we don't want to apply a second remapping on this driver now, + * so stop trying names, but keep fixing drivers + */ + break; + } + } + } + } + + + /* Driver targets */ + for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { + ChannelDriver *driver = fcu->driver; + DriverVar *dvar; + + /* Fix driver references to invalid ID's */ + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + /* only change the used targets, since the others will need fixing manually anyway */ + DRIVER_TARGETS_USED_LOOPER(dvar) + { + /* change the ID's used... */ + if (dtar->id == src_id) { + dtar->id = dst_id; + + /* also check on the subtarget... + * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own + * little twists so that we know that it isn't going to clobber the wrong data + */ + if ((dtar->rna_path && strstr(dtar->rna_path, "pose.bones[")) || (dtar->pchan_name[0])) { + GHASH_ITER(gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed */ + if (!STREQ(old_name, new_name)) { + if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { + /* Fix up path */ + dtar->rna_path = BKE_animsys_fix_rna_path_rename(id, dtar->rna_path, "pose.bones", + old_name, new_name, 0, 0, false); + break; /* no need to try any more names for bone path */ + } + else if (STREQ(dtar->pchan_name, old_name)) { + /* Change target bone name */ + BLI_strncpy(dtar->pchan_name, new_name, sizeof(dtar->pchan_name)); + break; /* no need to try any more names for bone subtarget */ + } + } + } } } } + DRIVER_TARGETS_LOOPER_END } - } } /* Helper function for armature joining - link fixing */ -static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone) +static void joined_armature_fix_links(Main *bmain, Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone) { Object *ob; bPose *pose; bPoseChannel *pchant; /* let's go through all objects in database */ - for (ob = G.main->object.first; ob; ob = ob->id.next) { + for (ob = bmain->object.first; ob; ob = ob->id.next) { /* do some object-type specific things */ if (ob->type == OB_ARMATURE) { pose = ob->pose; @@ -198,8 +293,17 @@ int join_armature_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) { if ((base->object->type == OB_ARMATURE) && (base->object != ob)) { + tJoinArmature_AdtFixData afd = {NULL}; bArmature *curarm = base->object->data; + /* we assume that each armature datablock is only used in a single place */ + BLI_assert(ob->data != base->object->data); + + /* init callback data for fixing up AnimData links later */ + afd.srcArm = base->object; + afd.tarArm = ob; + afd.names_map = BLI_ghash_str_new("join_armature_adt_fix"); + /* Make a list of editbones in current armature */ ED_armature_to_edit(base->object->data); @@ -219,6 +323,7 @@ int join_armature_exec(bContext *C, wmOperator *op) /* Get new name */ unique_editbone_name(arm->edbo, curbone->name, NULL); + BLI_ghash_insert(afd.names_map, BLI_strdup(pchan->name), curbone->name); /* Transform the bone */ { @@ -249,7 +354,7 @@ int join_armature_exec(bContext *C, wmOperator *op) } /* Fix Constraints and Other Links to this Bone and Armature */ - joined_armature_fix_links(ob, base->object, pchan, curbone); + joined_armature_fix_links(bmain, ob, base->object, pchan, curbone); /* Rename pchan */ BLI_strncpy(pchan->name, curbone->name, sizeof(pchan->name)); @@ -264,6 +369,37 @@ int join_armature_exec(bContext *C, wmOperator *op) BKE_pose_channels_hash_free(pose); } + /* Fix all the drivers (and animation data) */ + BKE_animdata_main_cb(bmain, joined_armature_fix_animdata_cb, &afd); + BLI_ghash_free(afd.names_map, MEM_freeN, NULL); + + /* Only copy over animdata now, after all the remapping has been done, + * so that we don't have to worry about ambiguities re which armature + * a bone came from! + */ + if (base->object->adt) { + if (ob->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + ob->adt = BKE_copy_animdata(base->object->adt, false); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(&ob->id, &base->object->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + if (curarm->adt) { + if (arm->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + arm->adt = BKE_copy_animdata(curarm->adt, false); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(&arm->id, &curarm->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + /* Free the old object data */ ED_base_object_free_and_unlink(bmain, scene, base); } } diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index c6ef76c570c..307e6c076f2 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -254,7 +254,7 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv static int armature_select_linked_poll(bContext *C) { - return (ED_operator_view3d_active(C) && ED_operator_editarmature(C) ); + return (ED_operator_view3d_active(C) && ED_operator_editarmature(C)); } void ARMATURE_OT_select_linked(wmOperatorType *ot) diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c index aace8c5434c..5376fc8c79b 100644 --- a/source/blender/editors/armature/editarmature_retarget.c +++ b/source/blender/editors/armature/editarmature_retarget.c @@ -718,7 +718,7 @@ static void RIG_reconnectControlBones(RigGraph *rg) cti->get_constraint_targets(con, &targets); for (target_index = 0, ct = targets.first; ct; target_index++, ct = ct->next) { - if ((ct->tar == rg->ob) && strcmp(ct->subtarget, ctrl->bone->name) == 0) { + if ((ct->tar == rg->ob) && STREQ(ct->subtarget, ctrl->bone->name)) { /* SET bone link to bone corresponding to pchan */ EditBone *link = BLI_ghash_lookup(rg->bones_map, pchan->name); @@ -841,7 +841,7 @@ static void RIG_reconnectControlBones(RigGraph *rg) cti->get_constraint_targets(con, &targets); for (ct = targets.first; ct; ct = ct->next) { - if ((ct->tar == rg->ob) && strcmp(ct->subtarget, ctrl->bone->name) == 0) { + if ((ct->tar == rg->ob) && STREQ(ct->subtarget, ctrl->bone->name)) { /* SET bone link to ctrl corresponding to pchan */ RigControl *link = BLI_ghash_lookup(rg->controls_map, pchan->name); @@ -1160,7 +1160,7 @@ static void RIG_arcFromBoneChain(RigGraph *rg, ListBase *list, EditBone *root_bo last_bone = bone; - if (strcmp(bone->name, "head") == 0) { + if (STREQ(bone->name, "head")) { contain_head = 1; } } diff --git a/source/blender/editors/armature/editarmature_sketch.c b/source/blender/editors/armature/editarmature_sketch.c index 3dbf7b4b65a..6c2d4256a0f 100644 --- a/source/blender/editors/armature/editarmature_sketch.c +++ b/source/blender/editors/armature/editarmature_sketch.c @@ -333,11 +333,11 @@ static void sk_autoname(bContext *C, ReebArc *arc) if (side[0] == '\0') { valid = 1; } - else if (strcmp(side, "R") == 0 || strcmp(side, "L") == 0) { + else if (STREQ(side, "R") || STREQ(side, "L")) { valid = 1; caps = 1; } - else if (strcmp(side, "r") == 0 || strcmp(side, "l") == 0) { + else if (STREQ(side, "r") || STREQ(side, "l")) { valid = 1; caps = 0; } diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 2e6eace88aa..d571fb374d9 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -1050,7 +1050,7 @@ static void poselib_preview_get_next(tPoseLib_PreviewData *pld, int step) LinkData *ld, *ldn, *ldc; /* free and rebuild if needed (i.e. if search-str changed) */ - if (strcmp(pld->searchstr, pld->searchold)) { + if (!STREQ(pld->searchstr, pld->searchold)) { /* free list of temporary search matches */ BLI_freelistN(&pld->searchp); diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index 1297755b7d0..2ba1eedd33b 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -259,7 +259,7 @@ LinkData *poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, con FCurve *fcu = (FCurve *)ld->data; /* check if paths match */ - if (strcmp(path, fcu->rna_path) == 0) + if (STREQ(path, fcu->rna_path)) return ld; } diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 99c64be5797..4aeeaa87269 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -643,25 +643,20 @@ static void keyData_switchDirectionNurb(Curve *cu, Nurb *nu) static GHash *dupli_keyIndexHash(GHash *keyindex) { GHash *gh; - GHashIterator *hashIter; + GHashIterator gh_iter; gh = BLI_ghash_ptr_new_ex("dupli_keyIndex gh", BLI_ghash_size(keyindex)); - for (hashIter = BLI_ghashIterator_new(keyindex); - BLI_ghashIterator_done(hashIter) == false; - BLI_ghashIterator_step(hashIter)) - { - void *cv = BLI_ghashIterator_getKey(hashIter); - CVKeyIndex *index = BLI_ghashIterator_getValue(hashIter); - CVKeyIndex *newIndex = MEM_callocN(sizeof(CVKeyIndex), "dupli_keyIndexHash index"); + GHASH_ITER (gh_iter, keyindex) { + void *cv = BLI_ghashIterator_getKey(&gh_iter); + CVKeyIndex *index = BLI_ghashIterator_getValue(&gh_iter); + CVKeyIndex *newIndex = MEM_mallocN(sizeof(CVKeyIndex), "dupli_keyIndexHash index"); memcpy(newIndex, index, sizeof(CVKeyIndex)); BLI_ghash_insert(gh, cv, newIndex); } - BLI_ghashIterator_free(hashIter); - return gh; } @@ -999,7 +994,7 @@ static void fcurve_path_rename(AnimData *adt, const char *orig_rna_path, char *r for (fcu = orig_curves->first; fcu; fcu = nextfcu) { nextfcu = fcu->next; - if (!strncmp(fcu->rna_path, orig_rna_path, len)) { + if (STREQLEN(fcu->rna_path, orig_rna_path, len)) { char *spath, *suffix = fcu->rna_path + len; nfcu = copy_fcurve(fcu); spath = nfcu->rna_path; @@ -1102,10 +1097,10 @@ static void curve_rename_fcurves(Curve *cu, ListBase *orig_curves) for (fcu = orig_curves->first; fcu; fcu = next) { next = fcu->next; - if (!strncmp(fcu->rna_path, "splines", 7)) { + if (STREQLEN(fcu->rna_path, "splines", 7)) { const char *ch = strchr(fcu->rna_path, '.'); - if (ch && (!strncmp(ch, ".bezier_points", 14) || !strncmp(ch, ".points", 7))) + if (ch && (STREQLEN(ch, ".bezier_points", 14) || STREQLEN(ch, ".points", 7))) fcurve_remove(adt, orig_curves, fcu); } } @@ -1129,7 +1124,7 @@ static void curve_rename_fcurves(Curve *cu, ListBase *orig_curves) for (fcu = orig_curves->first; fcu; fcu = next) { next = fcu->next; - if (!strncmp(fcu->rna_path, "splines", 7)) fcurve_remove(adt, orig_curves, fcu); + if (STREQLEN(fcu->rna_path, "splines", 7)) fcurve_remove(adt, orig_curves, fcu); else BLI_addtail(&curves, fcu); } @@ -2007,7 +2002,7 @@ static void ed_curve_delete_selected(Object *obedit) } /* only for OB_SURF */ -bool ed_editnurb_extrude_flag(EditNurb *editnurb, short flag) +bool ed_editnurb_extrude_flag(EditNurb *editnurb, const short flag) { Nurb *nu; BPoint *bp, *bpn, *newbp; @@ -2368,7 +2363,6 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb, } else { /* knots done after duplicate as pntsu may change */ - nu->knotsu = nu->knotsv = NULL; BKE_nurb_order_clamp_u(nu); BKE_nurb_knot_calc_u(nu); @@ -4889,285 +4883,405 @@ void CURVE_OT_spin(wmOperatorType *ot) RNA_def_float_vector(ot->srna, "axis", 3, NULL, -FLT_MAX, FLT_MAX, "Axis", "Axis in global view space", -1.0f, 1.0f); } -/***************** add vertex operator **********************/ +/***************** extrude vertex operator **********************/ -static int addvert_Nurb(bContext *C, short mode, float location[3]) +static bool ed_editcurve_extrude(Curve *cu, EditNurb *editnurb) { - Object *obedit = CTX_data_edit_object(C); - Curve *cu = (Curve *)obedit->data; - EditNurb *editnurb = cu->editnurb; - Nurb *nu, *newnu = NULL; - BezTriple *bezt, *newbezt = NULL; - BPoint *bp, *newbp = NULL; - float imat[4][4], temp[3]; - bool ok = false; - BezTriple *bezt_recalc[3] = {NULL}; + Nurb *nu = NULL; + Nurb *nu_last = NULL; - invert_m4_m4(imat, obedit->obmat); + bool changed = false; - findselectedNurbvert(&editnurb->nurbs, &nu, &bezt, &bp); - - if ((nu == NULL) || (nu->type == CU_BEZIER && bezt == NULL) || (nu->type != CU_BEZIER && bp == NULL)) { - if (mode != 'e') { - if (cu->actnu != CU_ACT_NONE) - nu = BLI_findlink(&editnurb->nurbs, cu->actnu); - - if (!nu || nu->type == CU_BEZIER) { - newbezt = (BezTriple *)MEM_callocN(sizeof(BezTriple), "addvert_Nurb"); - newbezt->radius = 1; - newbezt->alfa = 0; - BEZ_SEL(newbezt); - newbezt->h2 = newbezt->h1 = HD_AUTO; - - newnu = (Nurb *)MEM_callocN(sizeof(Nurb), "addvert_Nurb newnu"); - if (!nu) { - /* no selected segment -- create new one which is BEZIER type - * type couldn't be determined from Curve bt could be changed - * in the future, so shouldn't make much headache */ - newnu->type = CU_BEZIER; - newnu->resolu = cu->resolu; - newnu->flag |= CU_SMOOTH; - } - else { - memcpy(newnu, nu, sizeof(Nurb)); - } + Nurb *cu_actnu; + union { + BezTriple *bezt; + BPoint *bp; + void *p; + } cu_actvert; - BLI_addtail(&editnurb->nurbs, newnu); - newnu->bezt = newbezt; - newnu->pntsu = 1; + BKE_curve_nurb_vert_active_get(cu, &cu_actnu, &cu_actvert.p); + BKE_curve_nurb_vert_active_set(cu, NULL, NULL); - temp[0] = 1; - temp[1] = 0; - temp[2] = 0; + /* first pass (endpoints) */ + for (nu = editnurb->nurbs.first; nu; nu = nu->next) { - copy_v3_v3(newbezt->vec[1], location); - sub_v3_v3v3(newbezt->vec[0], newbezt->vec[1], temp); - add_v3_v3v3(newbezt->vec[2], newbezt->vec[1], temp); + if ((nu->flagu & CU_NURB_CYCLIC) && (nu->pntsu > 1)) { + continue; + } - mul_m4_v3(imat, newbezt->vec[0]); - mul_m4_v3(imat, newbezt->vec[1]); - mul_m4_v3(imat, newbezt->vec[2]); + if (nu->type == CU_BEZIER) { - ok = 1; - nu = newnu; - } - else if (nu->pntsv == 1) { - newbp = (BPoint *)MEM_callocN(sizeof(BPoint), "addvert_Nurb5"); - newbp->radius = 1; - newbp->alfa = 0; - newbp->f1 |= SELECT; + /* Check to see if the first bezier point is selected */ + if (nu->pntsu > 0 && nu->bezt != NULL) { + BezTriple *nu_bezt_old = nu->bezt; + BezTriple *bezt = nu->bezt; + + if (BEZSELECTED_HIDDENHANDLES(cu, bezt)) { + BezTriple *bezt_new; + BEZ_DESEL(bezt); - newnu = (Nurb *)MEM_mallocN(sizeof(Nurb), "addvert_Nurb newnu"); - memcpy(newnu, nu, sizeof(Nurb)); - BLI_addtail(&editnurb->nurbs, newnu); - newnu->bp = newbp; - newnu->orderu = 2; - newnu->pntsu = 1; + bezt_new = MEM_mallocN((nu->pntsu + 1) * sizeof(BezTriple), __func__); + ED_curve_beztcpy(editnurb, bezt_new + 1, bezt, nu->pntsu); + *bezt_new = *bezt; - mul_v3_m4v3(newbp->vec, imat, location); - newbp->vec[3] = 1.0; - newnu->knotsu = newnu->knotsv = NULL; - BKE_nurb_knot_calc_u(newnu); + MEM_freeN(nu->bezt); + nu->bezt = bezt_new; - ok = 1; - nu = newnu; + nu->pntsu += 1; + + if (ARRAY_HAS_ITEM(cu_actvert.bezt, nu_bezt_old, nu->pntsu - 1)) { + cu_actvert.bezt = (cu_actvert.bezt == bezt) ? + bezt_new : &nu->bezt[(cu_actvert.bezt - nu_bezt_old) + 1]; + BKE_curve_nurb_vert_active_set(cu, nu, cu_actvert.bezt); + } + + BEZ_SEL(bezt_new); + changed = true; + } } + /* Check to see if the last bezier point is selected */ + if (nu->pntsu > 1) { + BezTriple *nu_bezt_old = nu->bezt; + BezTriple *bezt = &nu->bezt[nu->pntsu - 1]; + + if (BEZSELECTED_HIDDENHANDLES(cu, bezt)) { + BezTriple *bezt_new; + BEZ_DESEL(bezt); + + bezt_new = MEM_mallocN((nu->pntsu + 1) * sizeof(BezTriple), __func__); + ED_curve_beztcpy(editnurb, bezt_new, nu->bezt, nu->pntsu); + bezt_new[nu->pntsu] = *bezt; + + MEM_freeN(nu->bezt); + nu->bezt = bezt_new; + + bezt_new += nu->pntsu; + nu->pntsu += 1; + + if (ARRAY_HAS_ITEM(cu_actvert.bezt, nu_bezt_old, nu->pntsu - 1)) { + cu_actvert.bezt = (cu_actvert.bezt == bezt) ? + bezt_new : &nu->bezt[cu_actvert.bezt - nu_bezt_old]; + BKE_curve_nurb_vert_active_set(cu, nu, cu_actvert.bezt); + } + + BEZ_SEL(bezt_new); + changed = true; + } + } } + else { - if (!ok) - return OPERATOR_CANCELLED; - } + /* Check to see if the first bpoint is selected */ + if (nu->pntsu > 0 && nu->bp != NULL) { + BPoint *nu_bp_old = nu->bp; + BPoint *bp = nu->bp; - if (!ok && nu->type == CU_BEZIER) { - /* which bezpoint? */ - if (bezt == &nu->bezt[nu->pntsu - 1]) { /* last */ - BEZ_DESEL(bezt); - newbezt = (BezTriple *)MEM_callocN((nu->pntsu + 1) * sizeof(BezTriple), "addvert_Nurb"); - ED_curve_beztcpy(editnurb, newbezt, nu->bezt, nu->pntsu); - newbezt[nu->pntsu] = *bezt; - copy_v3_v3(temp, bezt->vec[1]); - MEM_freeN(nu->bezt); - nu->bezt = newbezt; - newbezt += nu->pntsu; - BEZ_SEL(newbezt); - newbezt->h1 = newbezt->h2; - bezt = &nu->bezt[nu->pntsu - 1]; - ok = 1; + if (bp->f1 & SELECT) { + BPoint *bp_new; + bp->f1 &= ~SELECT; - if (nu->pntsu > 1) { - bezt_recalc[1] = newbezt; - bezt_recalc[0] = newbezt - 1; - } - } - else if (bezt == nu->bezt) { /* first */ - BEZ_DESEL(bezt); - newbezt = (BezTriple *)MEM_callocN((nu->pntsu + 1) * sizeof(BezTriple), "addvert_Nurb"); - ED_curve_beztcpy(editnurb, newbezt + 1, bezt, nu->pntsu); - *newbezt = *bezt; - BEZ_SEL(newbezt); - newbezt->h2 = newbezt->h1; - copy_v3_v3(temp, bezt->vec[1]); - MEM_freeN(nu->bezt); - nu->bezt = newbezt; - bezt = newbezt + 1; - ok = 1; + bp_new = MEM_mallocN((nu->pntsu + 1) * sizeof(BPoint), __func__); + ED_curve_bpcpy(editnurb, bp_new + 1, bp, nu->pntsu); + *bp_new = *bp; + + MEM_freeN(nu->bp); + nu->bp = bp_new; + nu->pntsu += 1; + BKE_nurb_knot_calc_u(nu); + + if (ARRAY_HAS_ITEM(cu_actvert.bp, nu_bp_old, nu->pntsu - 1)) { + cu_actvert.bp = (cu_actvert.bp == bp) ? + bp_new : &nu->bp[(cu_actvert.bp - nu_bp_old) + 1]; + BKE_curve_nurb_vert_active_set(cu, nu, cu_actvert.bp); + } + + bp_new->f1 |= SELECT; + changed = true; + } + } + + /* Check to see if the last bpoint is selected */ if (nu->pntsu > 1) { - bezt_recalc[1] = newbezt; - bezt_recalc[2] = newbezt + 1; + BPoint *nu_bp_old = nu->bp; + BPoint *bp = &nu->bp[nu->pntsu - 1]; + + if (bp->f1 & SELECT) { + BPoint *bp_new; + bp->f1 &= ~SELECT; + + bp_new = MEM_mallocN((nu->pntsu + 1) * sizeof(BPoint), __func__); + ED_curve_bpcpy(editnurb, bp_new, nu->bp, nu->pntsu); + bp_new[nu->pntsu] = *bp; + + MEM_freeN(nu->bp); + nu->bp = bp_new; + + bp_new += nu->pntsu; + nu->pntsu += 1; + + if (ARRAY_HAS_ITEM(cu_actvert.bp, nu_bp_old, nu->pntsu - 1)) { + cu_actvert.bp = (cu_actvert.bp == bp) ? + bp_new : &nu->bp[cu_actvert.bp - nu_bp_old]; + BKE_curve_nurb_vert_active_set(cu, nu, cu_actvert.bp); + } + + BKE_nurb_knot_calc_u(nu); + + bp_new->f1 |= SELECT; + changed = true; + } } } - else if (mode != 'e') { - BEZ_DESEL(bezt); - newbezt = (BezTriple *)MEM_callocN(sizeof(BezTriple), "addvert_Nurb"); - *newbezt = *bezt; - BEZ_SEL(newbezt); - newbezt->h2 = newbezt->h1; - copy_v3_v3(temp, bezt->vec[1]); + } - newnu = (Nurb *)MEM_mallocN(sizeof(Nurb), "addvert_Nurb newnu"); - memcpy(newnu, nu, sizeof(Nurb)); - BLI_addtail(&editnurb->nurbs, newnu); - newnu->bezt = newbezt; - newnu->pntsu = 1; + /* second pass (interior points) */ + nu_last = editnurb->nurbs.last; + for (nu = editnurb->nurbs.first; (nu != nu_last->next); nu = nu->next) { + int i, i_end; - nu = newnu; - bezt = newbezt; - ok = 1; + if ((nu->flagu & CU_NURB_CYCLIC) && (nu->pntsu > 1)) { + /* all points are interior */ + i = 0; + i_end = nu->pntsu; } else { - bezt = NULL; + /* skip endpoints */ + i = 1; + i_end = nu->pntsu - 1; } - if (bezt) { - if (!newnu) nu->pntsu++; + if (nu->type == CU_BEZIER) { + BezTriple *bezt; - if (mode == 'e') { - copy_v3_v3(newbezt->vec[0], bezt->vec[0]); - copy_v3_v3(newbezt->vec[1], bezt->vec[1]); - copy_v3_v3(newbezt->vec[2], bezt->vec[2]); - } - else { - mul_v3_m4v3(newbezt->vec[1], imat, location); - sub_v3_v3v3(temp, newbezt->vec[1], temp); + for (bezt = &nu->bezt[i]; i < i_end; i++, bezt++) { + if (BEZSELECTED_HIDDENHANDLES(cu, bezt)) { + Nurb *nurb_new; + BezTriple *bezt_new; + + BEZ_DESEL(bezt); + nurb_new = BKE_nurb_copy(nu, 1, 1); + nurb_new->flagu &= ~CU_NURB_CYCLIC; + BLI_addtail(&editnurb->nurbs, nurb_new); + bezt_new = nurb_new->bezt; + ED_curve_beztcpy(editnurb, bezt_new, bezt, 1); + BEZ_SEL(bezt_new); + + if (cu_actvert.bezt == bezt || cu_actnu == NULL) { + BKE_curve_nurb_vert_active_set(cu, nurb_new, bezt_new); + } - if (bezt_recalc[1]) { - const char h1 = bezt_recalc[1]->h1, h2 = bezt_recalc[1]->h2; - bezt_recalc[1]->h1 = bezt_recalc[1]->h2 = HD_AUTO; - BKE_nurb_handle_calc(bezt_recalc[1], bezt_recalc[0], bezt_recalc[2], 0); - bezt_recalc[1]->h1 = h1; - bezt_recalc[1]->h2 = h2; - } - else { - add_v3_v3v3(newbezt->vec[0], bezt->vec[0], temp); - add_v3_v3v3(newbezt->vec[2], bezt->vec[2], temp); + changed = true; } - + } + } + else { + BPoint *bp; + + for (bp = &nu->bp[i]; i < i_end; i++, bp++) { + if (bp->f1 & SELECT) { + Nurb *nurb_new; + BPoint *bp_new; + + bp->f1 &= ~SELECT; + nurb_new = BKE_nurb_copy(nu, 1, 1); + nurb_new->flagu &= ~CU_NURB_CYCLIC; + BLI_addtail(&editnurb->nurbs, nurb_new); + bp_new = nurb_new->bp; + ED_curve_bpcpy(editnurb, bp_new, bp, 1); + bp_new->f1 |= SELECT; + + if (cu_actvert.bp == bp || cu_actnu == NULL) { + BKE_curve_nurb_vert_active_set(cu, nurb_new, bp_new); + } - if (newnu) BKE_nurb_handles_calc(newnu); - else BKE_nurb_handles_calc(nu); + changed = true; + } } } } - else if (!ok && nu->pntsv == 1) { - /* which b-point? */ - if (bp == &nu->bp[nu->pntsu - 1]) { /* last */ - bp->f1 = 0; - newbp = (BPoint *)MEM_callocN((nu->pntsu + 1) * sizeof(BPoint), "addvert_Nurb4"); - ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu); - newbp[nu->pntsu] = *bp; - MEM_freeN(nu->bp); - nu->bp = newbp; - newbp += nu->pntsu; - newbp->f1 |= SELECT; - bp = newbp - 1; - ok = 1; - } - else if (bp == nu->bp) { /* first */ - bp->f1 = 0; - newbp = (BPoint *)MEM_callocN((nu->pntsu + 1) * sizeof(BPoint), "addvert_Nurb3"); - ED_curve_bpcpy(editnurb, newbp + 1, bp, nu->pntsu); - *newbp = *bp; - newbp->f1 |= SELECT; - MEM_freeN(nu->bp); - nu->bp = newbp; - bp = newbp + 1; - ok = 1; - } - else if (mode != 'e') { - bp->f1 = 0; - newbp = (BPoint *)MEM_callocN(sizeof(BPoint), "addvert_Nurb5"); - *newbp = *bp; - newbp->f1 |= SELECT; - newnu = (Nurb *)MEM_mallocN(sizeof(Nurb), "addvert_Nurb newnu"); - memcpy(newnu, nu, sizeof(Nurb)); - BLI_addtail(&editnurb->nurbs, newnu); - newnu->bp = newbp; - newnu->orderu = 2; - newnu->pntsu = 1; - newnu->knotsu = newnu->knotsv = NULL; + if (changed == false) { + BKE_curve_nurb_vert_active_set(cu, cu_actnu, cu_actvert.p); + } - nu = newnu; - bp = newbp; - ok = 1; + return changed; +} + +/***************** add vertex operator **********************/ + +static int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, const float location[3]) +{ + Nurb *nu; + + float minmax[2][3]; + float temp[3]; + bool nu_has_select = false; + + bool changed = false; + + INIT_MINMAX(minmax[0], minmax[1]); + + for (nu = editnurb->nurbs.first; nu; nu = nu->next) { + int i; + if (nu->type == CU_BEZIER) { + BezTriple *bezt; + + for (i = 0, bezt = nu->bezt; i < nu->pntsu; i++, bezt++) { + if (BEZSELECTED_HIDDENHANDLES(cu, bezt)) { + minmax_v3v3_v3(UNPACK2(minmax), bezt->vec[1]); + nu_has_select = true; + } + } } else { - bp = NULL; + BPoint *bp; + + for (i = 0, bp = nu->bp; i < nu->pntsu; i++, bp++) { + if (bp->f1 & SELECT) { + minmax_v3v3_v3(UNPACK2(minmax), bp->vec); + nu_has_select = true; + } + } } + } + + if (nu_has_select && ed_editcurve_extrude(cu, editnurb)) { + float ofs[3], center[3]; + int i; + + mid_v3_v3v3(center, minmax[0], minmax[1]); + sub_v3_v3v3(ofs, location, center); - if (bp) { - if (mode == 'e') { - copy_v3_v3(newbp->vec, bp->vec); + if ((cu->flag & CU_3D) == 0) { + ofs[2] = 0.0f; + } + + for (nu = editnurb->nurbs.first; nu; nu = nu->next) { + if (nu->type == CU_BEZIER) { + BezTriple *bezt; + for (i = 0, bezt = nu->bezt; i < nu->pntsu; i++, bezt++) { + if (BEZSELECTED_HIDDENHANDLES(cu, bezt)) { + add_v3_v3(bezt->vec[0], ofs); + add_v3_v3(bezt->vec[1], ofs); + add_v3_v3(bezt->vec[2], ofs); + + if (((nu->flagu & CU_NURB_CYCLIC) == 0) && + (i == 0 || i == nu->pntsu - 1)) + { + BKE_nurb_handle_calc_simple_auto(nu, bezt); + } + } + } } else { - mul_v3_m4v3(newbp->vec, imat, location); - newbp->vec[3] = 1.0; + BPoint *bp; - if (!newnu && nu->orderu < 4 && nu->orderu <= nu->pntsu) - nu->orderu++; + for (i = 0, bp = nu->bp; i < nu->pntsu; i++, bp++) { + if (bp->f1 & SELECT) { + add_v3_v3(bp->vec, ofs); + } + } } + } + changed = true; + } + else { + /* nothing selected: create a new curve */ + nu = BKE_curve_nurb_active_get(cu); - if (!newnu) { - nu->pntsu++; - BKE_nurb_knot_calc_u(nu); + if (!nu || nu->type == CU_BEZIER) { + Nurb *nurb_new; + BezTriple *bezt_new; + + if (nu) { + nurb_new = BKE_nurb_copy(nu, 1, 1); } else { - BKE_nurb_knot_calc_u(newnu); + nurb_new = MEM_callocN(sizeof(Nurb), "BLI_editcurve_addvert new_bezt_nurb 2"); + nurb_new->type = CU_BEZIER; + nurb_new->resolu = cu->resolu; + nurb_new->orderu = 4; + nurb_new->flag |= CU_SMOOTH; + BKE_nurb_bezierPoints_add(nurb_new, 1); } - } - } + BLI_addtail(&editnurb->nurbs, nurb_new); - if (ok) { - if (nu->bezt) { - BKE_curve_nurb_vert_active_set(cu, nu, newbezt); + bezt_new = nurb_new->bezt; + + BEZ_SEL(bezt_new); + + bezt_new->h1 = HD_AUTO; + bezt_new->h2 = HD_AUTO; + + temp[0] = 1.0f; + temp[1] = 0.0f; + temp[2] = 0.0f; + + copy_v3_v3(bezt_new->vec[1], location); + sub_v3_v3v3(bezt_new->vec[0], bezt_new->vec[1], temp); + add_v3_v3v3(bezt_new->vec[2], bezt_new->vec[1], temp); + + changed = true; } else { - BKE_curve_nurb_vert_active_set(cu, nu, newbp); - } + Nurb *nurb_new; + BPoint *bp_new; - BKE_nurb_test2D(nu); + { + nurb_new = MEM_callocN(sizeof(Nurb), __func__); + nurb_new->type = CU_POLY; + nurb_new->resolu = cu->resolu; + nurb_new->flag |= CU_SMOOTH; + nurb_new->orderu = 4; + BKE_nurb_points_add(nurb_new, 1); + } + BLI_addtail(&editnurb->nurbs, nurb_new); - if (ED_curve_updateAnimPaths(obedit->data)) - WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit); + bp_new = nurb_new->bp; - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); - DAG_id_tag_update(obedit->data, 0); + bp_new->f1 |= SELECT; - return OPERATOR_FINISHED; + copy_v3_v3(bp_new->vec, location); + bp_new->vec[3] = 1.0f; + + BKE_nurb_knot_calc_u(nurb_new); + + changed = true; + } } - return OPERATOR_CANCELLED; + return changed; } static int add_vertex_exec(bContext *C, wmOperator *op) { + Object *obedit = CTX_data_edit_object(C); + Curve *cu = obedit->data; + EditNurb *editnurb = cu->editnurb; float location[3]; + float imat[4][4]; RNA_float_get_array(op->ptr, "location", location); - return addvert_Nurb(C, 0, location); + + invert_m4_m4(imat, obedit->obmat); + mul_m4_v3(imat, location); + + if (ed_editcurve_addvert(cu, editnurb, location)) { + 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; + } + else { + return OPERATOR_CANCELLED; + } } static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -5236,29 +5350,39 @@ void CURVE_OT_vertex_add(wmOperatorType *ot) /***************** extrude operator **********************/ -static int extrude_exec(bContext *C, wmOperator *UNUSED(op)) +static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit = CTX_data_edit_object(C); Curve *cu = obedit->data; EditNurb *editnurb = cu->editnurb; - Nurb *nu; + bool changed = false; + bool as_curve = false; /* first test: curve? */ - for (nu = editnurb->nurbs.first; nu; nu = nu->next) - if (nu->pntsv == 1 && isNurbsel_count(cu, nu) == 1) - break; + if (obedit->type != OB_CURVE) { + Nurb *nu; + for (nu = editnurb->nurbs.first; nu; nu = nu->next) { + if (nu->pntsv == 1 && isNurbsel_count(cu, nu) == 1) { + as_curve = true; + break; + } + } + } - if (obedit->type == OB_CURVE || nu) { - addvert_Nurb(C, 'e', NULL); + if (obedit->type == OB_CURVE || as_curve) { + changed = ed_editcurve_extrude(cu, editnurb); } else { - if (ed_editnurb_extrude_flag(editnurb, SELECT)) { - if (ED_curve_updateAnimPaths(obedit->data)) - WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, obedit); + changed = ed_editnurb_extrude_flag(editnurb, SELECT); + } - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); - DAG_id_tag_update(obedit->data, 0); + if (changed) { + 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; @@ -5272,7 +5396,7 @@ void CURVE_OT_extrude(wmOperatorType *ot) ot->idname = "CURVE_OT_extrude"; /* api callbacks */ - ot->exec = extrude_exec; + ot->exec = curve_extrude_exec; ot->poll = ED_operator_editsurfcurve; /* flags */ @@ -6543,7 +6667,6 @@ static int curve_delete_segments(Object *obedit, const bool split) } } - nu->knotsu = nu->knotsv = NULL; BKE_nurb_order_clamp_u(nu); BKE_nurb_knot_calc_u(nu); @@ -6640,7 +6763,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) Object *obedit = CTX_data_edit_object(C); ListBase *editnurb = object_editcurve_get(obedit); Nurb *nu; - int clear = (strcmp(op->idname, "CURVE_OT_shade_flat") == 0); + int clear = (STREQ(op->idname, "CURVE_OT_shade_flat")); if (obedit->type != OB_CURVE) return OPERATOR_CANCELLED; diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index c9a961d1a4d..7c53896b969 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -529,11 +529,9 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) if (newob && enter_editmode) ED_undo_push(C, "Enter Editmode"); - ED_object_new_primitive_matrix(C, obedit, loc, rot, mat, false); + ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); dia = RNA_float_get(op->ptr, "radius"); - mat[0][0] *= dia; - mat[1][1] *= dia; - mat[2][2] *= dia; + mul_mat3_m4_fl(mat, dia); nu = add_nurbs_primitive(C, obedit, mat, type, newob); editnurb = object_editcurve_get(obedit); diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 97adaea41a8..a2ba6216f9c 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -390,7 +390,7 @@ void paste_gpdata(Scene *scene) /* find suitable layer from buffer to use to paste from */ for (gpls = gpcopybuf.first; gpls; gpls = gpls->next) { /* check if layer name matches */ - if ((no_name) || (strcmp(gpls->info, gpld->info) == 0)) + if ((no_name) || STREQ(gpls->info, gpld->info)) break; } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 3dae25263e8..a830253f634 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -100,7 +100,8 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr switch (sa->spacetype) { case SPACE_VIEW3D: /* 3D-View */ - case SPACE_TIME: /* Timeline - XXX: this is a hack to get it to show GP keyframes for 3D view */ + case SPACE_TIME: /* Timeline - XXX: this is a hack to get it to show GP keyframes for 3D view */ + case SPACE_ACTION: /* DepeSheet - XXX: this is a hack to get the keyframe jump operator to take GP Keyframes into account */ { BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src, GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT)); @@ -620,6 +621,10 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make copies of selected strokes, and deselect these once we're done */ for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + if (gps->flag & GP_STROKE_SELECT) { if (gps->totpoints == 1) { /* Special Case: If there's just a single point in this stroke... */ @@ -728,6 +733,10 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) /* make copies of selected strokes, and deselect these once we're done */ for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + if (gps->flag & GP_STROKE_SELECT) { if (gps->totpoints == 1) { /* Special Case: If there's just a single point in this stroke... */ @@ -936,6 +945,11 @@ static int gp_delete_selected_strokes(bContext *C) for (gps = gpf->strokes.first; gps; gps = gpsn) { gpsn = gps->next; + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* free stroke if selected */ if (gps->flag & GP_STROKE_SELECT) { /* free stroke memory arrays, then stroke itself */ if (gps->points) MEM_freeN(gps->points); @@ -975,6 +989,10 @@ static int gp_dissolve_selected_points(bContext *C) for (gps = gpf->strokes.first; gps; gps = gpsn) { gpsn = gps->next; + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + if (gps->flag & GP_STROKE_SELECT) { bGPDspoint *pt; int i; @@ -1050,6 +1068,11 @@ static int gp_delete_selected_points(bContext *C) for (gps = gpf->strokes.first; gps; gps = gpsn) { gpsn = gps->next; + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + if (gps->flag & GP_STROKE_SELECT) { bGPDspoint *pt; int i; @@ -2480,50 +2503,50 @@ static bool gp_convert_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) const bool valid_timing = RNA_boolean_get(ptr, "use_timing_data"); /* Always show those props */ - if (strcmp(prop_id, "type") == 0 || - strcmp(prop_id, "use_normalize_weights") == 0 || - strcmp(prop_id, "radius_multiplier") == 0 || - strcmp(prop_id, "use_link_strokes") == 0) + if (STREQ(prop_id, "type") || + STREQ(prop_id, "use_normalize_weights") || + STREQ(prop_id, "radius_multiplier") || + STREQ(prop_id, "use_link_strokes")) { return true; } /* Never show this prop */ - if (strcmp(prop_id, "use_timing_data") == 0) + if (STREQ(prop_id, "use_timing_data")) return false; if (link_strokes) { /* Only show when link_stroke is true */ - if (strcmp(prop_id, "timing_mode") == 0) + if (STREQ(prop_id, "timing_mode")) return true; if (timing_mode != GP_STROKECONVERT_TIMING_NONE) { /* Only show when link_stroke is true and stroke timing is enabled */ - if (strcmp(prop_id, "frame_range") == 0 || - strcmp(prop_id, "start_frame") == 0) + if (STREQ(prop_id, "frame_range") || + STREQ(prop_id, "start_frame")) { return true; } /* Only show if we have valid timing data! */ - if (valid_timing && strcmp(prop_id, "use_realtime") == 0) + if (valid_timing && STREQ(prop_id, "use_realtime")) return true; /* Only show if realtime or valid_timing is false! */ - if ((!realtime || !valid_timing) && strcmp(prop_id, "end_frame") == 0) + if ((!realtime || !valid_timing) && STREQ(prop_id, "end_frame")) return true; if (valid_timing && timing_mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { /* Only show for custom gaps! */ - if (strcmp(prop_id, "gap_duration") == 0) + if (STREQ(prop_id, "gap_duration")) return true; /* Only show randomness for non-null custom gaps! */ - if (strcmp(prop_id, "gap_randomness") == 0 && (gap_duration > 0.0f)) + if (STREQ(prop_id, "gap_randomness") && (gap_duration > 0.0f)) return true; /* Only show seed for randomize action! */ - if (strcmp(prop_id, "seed") == 0 && (gap_duration > 0.0f) && (gap_randomness > 0.0f)) + if (STREQ(prop_id, "seed") && (gap_duration > 0.0f) && (gap_randomness > 0.0f)) return true; } } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index e6f6644fd24..c03766a95e1 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -999,7 +999,7 @@ static void gp_session_validatebuffer(tGPsdata *p) } /* (re)init new painting data */ -static int gp_session_initdata(bContext *C, tGPsdata *p) +static bool gp_session_initdata(bContext *C, tGPsdata *p) { bGPdata **gpd_ptr = NULL; ScrArea *curarea = CTX_wm_area(C); @@ -1082,7 +1082,13 @@ static int gp_session_initdata(bContext *C, tGPsdata *p) case SPACE_CLIP: { SpaceClip *sc = curarea->spacedata.first; + MovieClip *clip = ED_space_clip_get_clip(sc); + if (clip == NULL) { + p->status = GP_STATUS_ERROR; + return false; + } + /* set the current area */ p->sa = curarea; p->ar = ar; @@ -1097,13 +1103,18 @@ static int gp_session_initdata(bContext *C, tGPsdata *p) p->custom_color[3] = 0.9f; if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { - MovieClip *clip = ED_space_clip_get_clip(sc); int framenr = ED_space_clip_get_clip_frame_number(sc); MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); - MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); - - p->imat[3][0] -= marker->pos[0]; - p->imat[3][1] -= marker->pos[1]; + MovieTrackingMarker *marker = track ? BKE_tracking_marker_get(track, framenr) : NULL; + + if (marker) { + p->imat[3][0] -= marker->pos[0]; + p->imat[3][1] -= marker->pos[1]; + } + else { + p->status = GP_STATUS_ERROR; + return false; + } } invert_m4_m4(p->mat, p->imat); diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 7967d6a4d95..9ba77a4244e 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -136,11 +136,14 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op) bGPDspoint *pt; int i; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - pt->flag &= ~GP_SPOINT_SELECT; + /* only edit strokes that are valid in this view... */ + if (ED_gpencil_stroke_can_use(C, gps)) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + + gps->flag &= ~GP_STROKE_SELECT; } - - gps->flag &= ~GP_STROKE_SELECT; } } } diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index 5f0647fb43d..34e640a4b7b 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -76,7 +76,7 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) if (step == 1) { /* undo */ //printf("\t\tGP - undo step\n"); if (cur_node->prev) { - if (!name || strcmp(cur_node->name, name) == 0) { + if (!name || STREQ(cur_node->name, name)) { cur_node = cur_node->prev; new_gpd = cur_node->gpd; } @@ -85,7 +85,7 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) else if (step == -1) { //printf("\t\tGP - redo step\n"); if (cur_node->next) { - if (!name || strcmp(cur_node->name, name) == 0) { + if (!name || STREQ(cur_node->name, name)) { cur_node = cur_node->next; new_gpd = cur_node->gpd; } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 4a913c3d2e5..289cbc568d2 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -90,6 +90,42 @@ bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]), /* ******************************************************** */ +/* Check whether given stroke can be edited given the supplied context */ +// XXX: do we need additional flags for screenspace vs dataspace? +bool ED_gpencil_stroke_can_use_direct(const ScrArea *sa, const bGPDstroke *gps) +{ + /* sanity check */ + if (ELEM(NULL, sa, gps)) + return false; + + /* filter stroke types by flags + spacetype */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* 3D strokes - only in 3D view */ + return (sa->spacetype == SPACE_VIEW3D); + } + else if (gps->flag & GP_STROKE_2DIMAGE) { + /* Special "image" strokes - only in Image Editor */ + return (sa->spacetype == SPACE_IMAGE); + } + else if (gps->flag & GP_STROKE_2DSPACE) { + /* 2D strokes (dataspace) - for any 2D view (i.e. everything other than 3D view) */ + return (sa->spacetype != SPACE_VIEW3D); + } + else { + /* view aligned - anything goes */ + return true; + } +} + +/* Check whether given stroke can be edited in the current context */ +bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) +{ + ScrArea *sa = CTX_wm_area(C); + return ED_gpencil_stroke_can_use_direct(sa, gps); +} + +/* ******************************************************** */ + /* Init handling for space-conversion function (from passed-in parameters) */ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) { @@ -127,7 +163,9 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) } -/* Convert Grease Pencil points to screen-space values */ +/* Convert Grease Pencil points to screen-space values + * WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn + */ void gp_point_to_xy(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, int *r_x, int *r_y) { @@ -135,7 +173,12 @@ void gp_point_to_xy(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, View2D *v2d = gsc->v2d; rctf *subrect = gsc->subrect; int xyval[2]; - + + /* sanity checks */ + BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); + BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); + + if (gps->flag & GP_STROKE_3DSPACE) { if (ED_view3d_project_int_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { *r_x = xyval[0]; @@ -152,11 +195,13 @@ void gp_point_to_xy(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y); } else { - if (subrect == NULL) { /* normal 3D view */ + if (subrect == NULL) { + /* normal 3D view (or view space) */ *r_x = (int)(pt->x / 100 * ar->winx); *r_y = (int)(pt->y / 100 * ar->winy); } - else { /* camera view, use subrect */ + else { + /* camera view, use subrect */ *r_x = (int)((pt->x / 100) * BLI_rctf_size_x(subrect)) + subrect->xmin; *r_y = (int)((pt->y / 100) * BLI_rctf_size_y(subrect)) + subrect->ymin; } diff --git a/source/blender/editors/include/BIF_gl.h b/source/blender/editors/include/BIF_gl.h index b06af01bab6..3b7d7bac44f 100644 --- a/source/blender/editors/include/BIF_gl.h +++ b/source/blender/editors/include/BIF_gl.h @@ -35,10 +35,6 @@ #include "GPU_glew.h" -/* hacking pointsize and linewidth */ -#define glPointSize(f) glPointSize(U.pixelsize * (f)) -#define glLineWidth(f) glLineWidth(U.pixelsize * (f)) - /* * these should be phased out. cpack should be replaced in * code with calls to glColor3ub. - zr @@ -51,7 +47,6 @@ * */ void cpack(unsigned int x); - #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) # define glMultMatrixf(x) \ glMultMatrixf(_Generic((x), \ @@ -71,9 +66,14 @@ void cpack(unsigned int x); float (*)[4]: (float *)(x), \ float [4][4]: (float *)(x)) \ ) +/* hacking pointsize and linewidth */ +#define glPointSize(f) glPointSize(U.pixelsize * _Generic((f), double: (float)(f), default: (f))) +#define glLineWidth(f) glLineWidth(U.pixelsize * _Generic((f), double: (float)(f), default: (f))) #else # define glMultMatrixf(x) glMultMatrixf((float *)(x)) # define glLoadMatrixf(x) glLoadMatrixf((float *)(x)) +#define glPointSize(f) glPointSize(U.pixelsize * (f)) +#define glLineWidth(f) glLineWidth(U.pixelsize * (f)) #endif #define GLA_PIXEL_OFS 0.375f diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index b0d1be1bf5d..c4f08ca4775 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -43,6 +43,7 @@ struct Object; struct bGPdata; struct bGPDlayer; struct bGPDframe; +struct bGPDstroke; struct PointerRNA; struct ImBuf; struct wmKeyConfig; @@ -80,6 +81,11 @@ struct bGPdata *ED_gpencil_data_get_active_direct(struct ID *screen_id, struct S /* 3D View */ struct bGPdata *ED_gpencil_data_get_active_v3d(struct Scene *scene, struct View3D *v3d); +/* ----------- Stroke Editing Utilities ---------------- */ + +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); + /* ----------- Grease Pencil Operators ----------------- */ void ED_keymap_gpencil(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 1188ecd0aa5..704876e1261 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -35,6 +35,7 @@ struct Main; struct bContext; struct Image; struct ImageUser; +struct ImBuf; struct ToolSettings; struct uiBlock; struct wmWindowManager; @@ -58,6 +59,8 @@ void ED_space_image_get_aspect(struct SpaceImage *sima, float *aspx, float *aspy void ED_space_image_get_zoom(struct SpaceImage *sima, struct ARegion *ar, float *zoomx, float *zoomy); void ED_space_image_get_uv_aspect(struct SpaceImage *sima, float *aspx, float *aspy); +void ED_space_image_scopes_update(const struct bContext *C, struct SpaceImage *sima, struct ImBuf *ibuf, bool use_view_settings); + void ED_space_image_paint_update(struct wmWindowManager *wm, struct ToolSettings *settings); void ED_space_image_uv_sculpt_update(struct wmWindowManager *wm, struct ToolSettings *settings); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 900da3ee07c..ccdde39f263 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -141,9 +141,9 @@ bool ED_object_editmode_load(struct Object *obedit); void ED_object_location_from_view(struct bContext *C, float loc[3]); void ED_object_rotation_from_view(struct bContext *C, float rot[3], const char align_axis); void ED_object_base_init_transform(struct bContext *C, struct Base *base, const float loc[3], const float rot[3]); -float ED_object_new_primitive_matrix(struct bContext *C, struct Object *editob, - const float loc[3], const float rot[3], float primmat[4][4], - bool apply_diameter); +float ED_object_new_primitive_matrix( + struct bContext *C, struct Object *editob, + const float loc[3], const float rot[3], float primmat[4][4]); void ED_object_add_unit_props(struct wmOperatorType *ot); void ED_object_add_generic_props(struct wmOperatorType *ot, bool do_editmode); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b790c656b61..8c33395cc49 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -107,7 +107,7 @@ void ED_screen_set_subwinactive(struct bContext *C, struct wmEvent *event); void ED_screen_exit(struct bContext *C, struct wmWindow *window, struct bScreen *screen); void ED_screen_animation_timer(struct bContext *C, int redraws, int refresh, int sync, int enable); void ED_screen_animation_timer_update(struct bScreen *screen, int redraws, int refresh); -void ED_screen_retore_temp_type(struct bContext *C, ScrArea *sa, bool is_screen_change); +void ED_screen_restore_temp_type(struct bContext *C, ScrArea *sa); ScrArea *ED_screen_full_newspace(struct bContext *C, ScrArea *sa, int type); void ED_screen_full_prevspace(struct bContext *C, ScrArea *sa); void ED_screen_full_restore(struct bContext *C, ScrArea *sa); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 76ad4ba7bdb..b62d9960117 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -310,7 +310,6 @@ struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D bool draw_background, int alpha_mode, char err_out[256]); struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256]); -void ED_view3d_offscreen_sky_color_get(struct Scene *scene, float sky_color[3]); struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, bool do_clip); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index fa349c4f006..bfb7a3420c9 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -54,7 +54,7 @@ DEF_ICON(DOT) DEF_ICON(COLLAPSEMENU) DEF_ICON(X) #ifndef DEF_ICON_BLANK_SKIP - DEF_ICON(BLANK005) + DEF_ICON(BLANK005) /* XXX 'DOWNARROW' icon! */ #endif DEF_ICON(GO_LEFT) DEF_ICON(PLUG) @@ -461,9 +461,9 @@ DEF_ICON(FORCE_SMOKEFLOW) DEF_ICON(BLANK685) /* EMPTY */ - DEF_ICON(BLANK690) - DEF_ICON(BLANK691) - DEF_ICON(BLANK692) + DEF_ICON(BLANK690) /* XXX 'Temperature' icon! */ + DEF_ICON(BLANK691) /* XXX 'Temperature' icon! */ + DEF_ICON(BLANK692) /* XXX 'Gear' icon! */ DEF_ICON(BLANK693) DEF_ICON(BLANK694) DEF_ICON(BLANK695) @@ -590,8 +590,8 @@ DEF_ICON(MOD_SKIN) DEF_ICON(MOD_TRIANGULATE) DEF_ICON(MOD_WIREFRAME) DEF_ICON(MOD_DATA_TRANSFER) +DEF_ICON(MOD_NORMALEDIT) #ifndef DEF_ICON_BLANK_SKIP - DEF_ICON(BLANK168) DEF_ICON(BLANK169) DEF_ICON(BLANK170) DEF_ICON(BLANK171) @@ -689,7 +689,7 @@ DEF_ICON(RNDCURVE) DEF_ICON(PROP_OFF) DEF_ICON(PROP_ON) DEF_ICON(PROP_CON) -DEF_ICON(SCULPT_DYNTOPO) +DEF_ICON(SCULPT_DYNTOPO) /* XXX Empty icon! */ DEF_ICON(PARTICLE_POINT) DEF_ICON(PARTICLE_TIP) DEF_ICON(PARTICLE_PATH) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 8789e837f17..590ab1d694d 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -316,6 +316,10 @@ void UI_draw_roundbox_shade_x(int mode, float minx, float miny, float maxx, floa void UI_draw_roundbox_shade_y(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadeLeft, float shadeRight); void UI_draw_text_underline(int pos_x, int pos_y, int len, int height); +void UI_draw_safe_areas( + float x1, float x2, float y1, float y2, + const float title_aspect[2], const float action_aspect[2]); + /* state for scrolldrawing */ #define UI_SCROLL_PRESSED (1 << 0) #define UI_SCROLL_ARROWS (1 << 1) @@ -888,6 +892,7 @@ void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char * void uiTemplateImage(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, struct PointerRNA *userptr, int compact); void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr, int color_management); void uiTemplateImageLayers(uiLayout *layout, struct bContext *C, struct Image *ima, struct ImageUser *iuser); +void uiTemplateImageInfo(uiLayout *layout, struct bContext *C, Image *ima, ImageUser *iuser); void uiTemplateRunningJobs(uiLayout *layout, struct bContext *C); void UI_but_func_operator_search(uiBut *but); void uiTemplateOperatorSearch(uiLayout *layout); @@ -970,15 +975,20 @@ void UI_context_active_but_prop_get_filebrowser(const struct bContext *C, struct void UI_context_active_but_prop_get_templateID(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA **prop); /* Styled text draw */ -void UI_fontstyle_set(struct uiFontStyle *fs); +void UI_fontstyle_set(const struct uiFontStyle *fs); void UI_fontstyle_draw_ex( - struct uiFontStyle *fs, const struct rcti *rect, const char *str, + const struct uiFontStyle *fs, const struct rcti *rect, const char *str, size_t len, float *r_xofs, float *r_yofs); -void UI_fontstyle_draw(struct uiFontStyle *fs, const struct rcti *rect, const char *str); -void UI_fontstyle_draw_rotated(struct uiFontStyle *fs, const struct rcti *rect, const char *str); +void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str); +void UI_fontstyle_draw_rotated(const struct uiFontStyle *fs, const struct rcti *rect, const char *str); +void UI_fontstyle_draw_simple(const struct uiFontStyle *fs, float x, float y, const char *str); +void UI_fontstyle_draw_simple_backdrop( + const uiFontStyle *fs, float x, float y, const char *str, + const unsigned char fg[4], const unsigned char bg[4]); + +int UI_fontstyle_string_width(const struct uiFontStyle *fs, const char *str); +int UI_fontstyle_height_max(const struct uiFontStyle *fs); -int UI_fontstyle_string_width(const char *str); // XXX temp -void UI_draw_string(float x, float y, const char *str); // XXX temp void UI_draw_icon_tri(float x, float y, char dir); uiStyle *UI_style_get(void); /* use for fonts etc */ @@ -1009,6 +1019,9 @@ void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p); /* Float precision helpers */ #define UI_PRECISION_FLOAT_MAX 7 +/* Typical UI text */ +#define UI_FSTYLE_WIDGET (const uiFontStyle *)&(UI_style_get()->widget) + int UI_calc_float_precision(int prec, double value); #endif /* __UI_INTERFACE_H__ */ diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index d289e90c257..4a6d9911d3b 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -293,6 +293,8 @@ enum { TH_INFO_DEBUG, TH_INFO_DEBUG_TEXT, TH_VIEW_OVERLAY, + + TH_V3D_CLIPPING_BORDER }; /* XXX WARNING: previous is saved in file, so do not change order! */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 0b1d1c8c30c..8fa604d57cb 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -297,7 +297,7 @@ void ui_block_bounds_calc(uiBlock *block) /* hardcoded exception... but that one is annoying with larger safety */ bt = block->buttons.first; - if (bt && strncmp(bt->str, "ERROR", 5) == 0) xof = 10; + if (bt && STREQLEN(bt->str, "ERROR", 5)) xof = 10; else xof = 40; block->safety.xmin = block->rect.xmin - xof; diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index bcd9b9a5547..e03ea5efba4 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -450,6 +450,51 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(w #endif } +/** + * Draw title and text safe areas. + * + * The first 4 parameters are the offsets for the view, not the zones. + */ +void UI_draw_safe_areas( + float x1, float x2, float y1, float y2, + const float title_aspect[2], const float action_aspect[2]) +{ + const float size_x_half = (x2 - x1) * 0.5f; + const float size_y_half = (y2 - y1) * 0.5f; + + const float *safe_areas[] = {title_aspect, action_aspect}; + int i, safe_len = ARRAY_SIZE(safe_areas); + bool is_first = true; + + for (i = 0; i < safe_len; i++) { + if (safe_areas[i][0] || safe_areas[i][1]) { + float margin_x, margin_y; + float minx, miny, maxx, maxy; + + if (is_first) { + UI_ThemeColorBlendShade(TH_VIEW_OVERLAY, TH_BACK, 0.25f, 0); + is_first = false; + } + + margin_x = safe_areas[i][0] * size_x_half; + margin_y = safe_areas[i][1] * size_y_half; + + minx = x1 + margin_x; + miny = y1 + margin_y; + maxx = x2 - margin_x; + maxy = y2 - margin_y; + + glBegin(GL_LINE_LOOP); + glVertex2f(maxx, miny); + glVertex2f(maxx, maxy); + glVertex2f(minx, maxy); + glVertex2f(minx, miny); + glEnd(); + } + } +} + + static void draw_scope_end(const rctf *rect, GLint *scissor) { /* restore scissortest */ @@ -494,7 +539,7 @@ static void histogram_draw_one(float r, float g, float b, float alpha, glColor4f(r, g, b, alpha); glShadeModel(GL_FLAT); - glBegin(GL_QUAD_STRIP); + glBegin(GL_TRIANGLE_STRIP); glVertex2f(x, y); glVertex2f(x, y + (data[0] * h)); for (i = 1; i < res; i++) { @@ -777,8 +822,8 @@ static void vectorscope_draw_target(float centerx, float centery, float diam, co if (u > 0 && v >= 0) tangle = atanf(v / u); else if (u > 0 && v < 0) tangle = atanf(v / u) + 2.0f * (float)M_PI; else if (u < 0) tangle = atanf(v / u) + (float)M_PI; - else if (u == 0 && v > 0.0f) tangle = (float)M_PI / 2.0f; - else if (u == 0 && v < 0.0f) tangle = -(float)M_PI / 2.0f; + else if (u == 0 && v > 0.0f) tangle = M_PI_2; + else if (u == 0 && v < 0.0f) tangle = -M_PI_2; tampli = sqrtf(u * u + v * v); /* small target vary by 2.5 degree and 2.5 IRE unit */ @@ -1102,7 +1147,7 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti v1[1] = y1 + sizey_solid; v2[1] = rect->ymax; - glBegin(GL_QUAD_STRIP); + glBegin(GL_TRIANGLE_STRIP); for (a = 0; a <= sizex; a++) { pos = ((float)a) / sizex; do_colorband(coba, pos, colf); @@ -1121,7 +1166,7 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti v1[1] = y1; v2[1] = y1 + sizey_solid; - glBegin(GL_QUAD_STRIP); + glBegin(GL_TRIANGLE_STRIP); for (a = 0; a <= sizex; a++) { pos = ((float)a) / sizex; do_colorband(coba, pos, colf); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index d7a4720d595..f5b24f49f98 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -73,10 +73,13 @@ static void eyedropper_draw_cursor_text(const struct bContext *C, ARegion *ar, const char *name) { - int width; + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; wmWindow *win = CTX_wm_window(C); int x = win->eventstate->x; int y = win->eventstate->y; + const unsigned char fg[4] = {255, 255, 255, 255}; + const unsigned char bg[4] = {0, 0, 0, 50}; + if ((name[0] == '\0') || (BLI_rcti_isect_pt(&ar->winrct, x, y) == false)) @@ -84,19 +87,12 @@ static void eyedropper_draw_cursor_text(const struct bContext *C, ARegion *ar, c return; } - width = UI_fontstyle_string_width(name); x = x - ar->winrct.xmin; y = y - ar->winrct.ymin; - y += 20; - - glColor4ub(0, 0, 0, 50); - - UI_draw_roundbox_corner_set(UI_CNR_ALL | UI_RB_ALPHA); - UI_draw_roundbox(x, y, x + width + 8, y + 15, 4); + y += U.widget_unit; - glColor4ub(255, 255, 255, 255); - UI_draw_string(x + 4, y + 4, name); + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg); } /** \} */ @@ -176,43 +172,42 @@ static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int /* we could use some clever */ wmWindow *win = CTX_wm_window(C); - ScrArea *sa; - for (sa = win->screen->areabase.first; sa; sa = sa->next) { - if (BLI_rcti_isect_pt(&sa->totrct, mx, my)) { - if (sa->spacetype == SPACE_IMAGE) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { - SpaceImage *sima = sa->spacedata.first; - int mval[2] = {mx - ar->winrct.xmin, - my - ar->winrct.ymin}; - - if (ED_space_image_color_sample(CTX_data_scene(C), sima, ar, mval, r_col)) { - return; - } + ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my); + + if (sa) { + if (sa->spacetype == SPACE_IMAGE) { + ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + SpaceImage *sima = sa->spacedata.first; + int mval[2] = {mx - ar->winrct.xmin, + my - ar->winrct.ymin}; + + if (ED_space_image_color_sample(CTX_data_scene(C), sima, ar, mval, r_col)) { + return; } } - else if (sa->spacetype == SPACE_NODE) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { - SpaceNode *snode = sa->spacedata.first; - int mval[2] = {mx - ar->winrct.xmin, - my - ar->winrct.ymin}; - - if (ED_space_node_color_sample(CTX_data_scene(C), snode, ar, mval, r_col)) { - return; - } + } + else if (sa->spacetype == SPACE_NODE) { + ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + SpaceNode *snode = sa->spacedata.first; + int mval[2] = {mx - ar->winrct.xmin, + my - ar->winrct.ymin}; + + if (ED_space_node_color_sample(CTX_data_scene(C), snode, ar, mval, r_col)) { + return; } } - else if (sa->spacetype == SPACE_CLIP) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { - SpaceClip *sc = sa->spacedata.first; - int mval[2] = {mx - ar->winrct.xmin, - my - ar->winrct.ymin}; - - if (ED_space_clip_color_sample(CTX_data_scene(C), sc, ar, mval, r_col)) { - return; - } + } + else if (sa->spacetype == SPACE_CLIP) { + ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + SpaceClip *sc = sa->spacedata.first; + int mval[2] = {mx - ar->winrct.xmin, + my - ar->winrct.ymin}; + + if (ED_space_clip_color_sample(CTX_data_scene(C), sc, ar, mval, r_col)) { + return; } } } @@ -477,53 +472,49 @@ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int /* we could use some clever */ wmWindow *win = CTX_wm_window(C); - ScrArea *sa; + ScrArea *sa = BKE_screen_find_area_xy(win->screen, -1, mx, my); ScrArea *area_prev = CTX_wm_area(C); ARegion *ar_prev = CTX_wm_region(C); ddr->name[0] = '\0'; - for (sa = win->screen->areabase.first; sa; sa = sa->next) { - if (BLI_rcti_isect_pt(&sa->totrct, mx, my)) { - if (sa->spacetype == SPACE_VIEW3D) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { - const int mval[2] = { - mx - ar->winrct.xmin, - my - ar->winrct.ymin}; - Base *base; - - CTX_wm_area_set(C, sa); - CTX_wm_region_set(C, ar); - - /* grr, always draw else we leave stale text */ - ED_region_tag_redraw(ar); - - base = ED_view3d_give_base_under_cursor(C, mval); - if (base) { - Object *ob = base->object; - ID *id = NULL; - if (ddr->idcode == ID_OB) { - id = (ID *)ob; - } - else if (ob->data) { - if (GS(((ID *)ob->data)->name) == ddr->idcode) { - id = (ID *)ob->data; - } - else { - BLI_snprintf(ddr->name, sizeof(ddr->name), "Incompatible, expected a %s", - ddr->idcode_name); - } + if (sa) { + if (sa->spacetype == SPACE_VIEW3D) { + ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + const int mval[2] = { + mx - ar->winrct.xmin, + my - ar->winrct.ymin}; + Base *base; + + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, ar); + + /* grr, always draw else we leave stale text */ + ED_region_tag_redraw(ar); + + base = ED_view3d_give_base_under_cursor(C, mval); + if (base) { + Object *ob = base->object; + ID *id = NULL; + if (ddr->idcode == ID_OB) { + id = (ID *)ob; + } + else if (ob->data) { + if (GS(((ID *)ob->data)->name) == ddr->idcode) { + id = (ID *)ob->data; } - - if (id) { - BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s", - ddr->idcode_name, id->name + 2); - *r_id = id; + else { + BLI_snprintf(ddr->name, sizeof(ddr->name), "Incompatible, expected a %s", + ddr->idcode_name); } + } - break; + if (id) { + BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s", + ddr->idcode_name, id->name + 2); + *r_id = id; } } } @@ -760,7 +751,7 @@ static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, /* we could use some clever */ wmWindow *win = CTX_wm_window(C); - ScrArea *sa; + ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my); Scene *scene = win->screen->scene; UnitSettings *unit = &scene->unit; const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0; @@ -770,47 +761,44 @@ static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, ddr->name[0] = '\0'; - for (sa = win->screen->areabase.first; sa; sa = sa->next) { - if (BLI_rcti_isect_pt(&sa->totrct, mx, my)) { - if (sa->spacetype == SPACE_VIEW3D) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { - View3D *v3d = sa->spacedata.first; - RegionView3D *rv3d = ar->regiondata; - /* weak, we could pass in some reference point */ - const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3]; - const int mval[2] = { - mx - ar->winrct.xmin, - my - ar->winrct.ymin}; - float co[3]; - - CTX_wm_area_set(C, sa); - CTX_wm_region_set(C, ar); - - /* grr, always draw else we leave stale text */ - ED_region_tag_redraw(ar); - - view3d_operator_needs_opengl(C); - - if (ED_view3d_autodist(scene, ar, v3d, mval, co, true, NULL)) { - const float mval_center_fl[2] = { - (float)ar->winx / 2, - (float)ar->winy / 2}; - float co_align[3]; - - /* quick way to get view-center aligned point */ - ED_view3d_win_to_3d(ar, co, mval_center_fl, co_align); - - *r_depth = len_v3v3(view_co, co_align); - - bUnit_AsString(ddr->name, sizeof(ddr->name), - (double)*r_depth, - 4, unit->system, B_UNIT_LENGTH, do_split, false); - } - else { - BLI_strncpy(ddr->name, "Nothing under cursor", sizeof(ddr->name)); - } - break; + if (sa) { + if (sa->spacetype == SPACE_VIEW3D) { + ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); + if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + /* weak, we could pass in some reference point */ + const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3]; + const int mval[2] = { + mx - ar->winrct.xmin, + my - ar->winrct.ymin}; + float co[3]; + + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, ar); + + /* grr, always draw else we leave stale text */ + ED_region_tag_redraw(ar); + + view3d_operator_needs_opengl(C); + + if (ED_view3d_autodist(scene, ar, v3d, mval, co, true, NULL)) { + const float mval_center_fl[2] = { + (float)ar->winx / 2, + (float)ar->winy / 2}; + float co_align[3]; + + /* quick way to get view-center aligned point */ + ED_view3d_win_to_3d(ar, co, mval_center_fl, co_align); + + *r_depth = len_v3v3(view_co, co_align); + + bUnit_AsString(ddr->name, sizeof(ddr->name), + (double)*r_depth, + 4, unit->system, B_UNIT_LENGTH, do_split, false); + } + else { + BLI_strncpy(ddr->name, "Nothing under cursor", sizeof(ddr->name)); } } } @@ -830,8 +818,10 @@ static void depthdropper_depth_set(bContext *C, DepthDropper *ddr, const float d /* set sample from accumulated values */ static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr) { - float depth; - depth = ddr->accum_depth * 1.0f / (float)ddr->accum_tot; + float depth = ddr->accum_depth; + if (ddr->accum_tot) { + depth /= (float)ddr->accum_tot; + } depthdropper_depth_set(C, ddr, depth); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 98b065dabcf..96973194576 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -114,6 +114,7 @@ static void ui_but_smart_controller_add(bContext *C, uiBut *from, uiBut *to); static void ui_but_link_add(bContext *C, uiBut *from, uiBut *to); static int ui_do_but_EXIT(bContext *C, uiBut *but, struct uiHandleButtonData *data, const wmEvent *event); +static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b); #ifdef USE_KEYNAV_LIMIT static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event); @@ -355,6 +356,12 @@ static enum eSnapType ui_event_to_snap(const wmEvent *event) return (event->ctrl) ? (event->shift) ? SNAP_ON_SMALL : SNAP_ON : SNAP_OFF; } +static bool ui_event_is_snap(const wmEvent *event) +{ + return (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY) || + ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)); +} + static void ui_color_snap_hue(const enum eSnapType snap, float *r_hue) { const float snap_increment = (snap == SNAP_ON_SMALL) ? 24 : 12; @@ -2749,7 +2756,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle case VKEY: case XKEY: case CKEY: - if (event->ctrl || event->oskey) { + if (IS_EVENT_MOD(event, ctrl, oskey)) { if (event->type == VKEY) changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_PASTE); else if (event->type == CKEY) @@ -2822,10 +2829,10 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle /* Ctrl + A: Select all */ #if defined(__APPLE__) /* OSX uses cmd-a systemwide, so add it */ - if ((event->oskey && !(event->alt || event->shift || event->ctrl)) || - (event->ctrl && !(event->alt || event->shift || event->oskey))) + if ((event->oskey && !IS_EVENT_MOD(event, shift, alt, ctrl)) || + (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey))) #else - if (event->ctrl && !(event->alt || event->shift || event->oskey)) + if (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)) #endif { ui_textedit_move(but, data, STRCUR_DIR_PREV, @@ -2848,7 +2855,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle update = true; /* do live update for tab key */ } /* the hotkey here is not well defined, was G.qual so we check all */ - else if (event->shift || event->ctrl || event->alt || event->oskey) { + else if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { ui_textedit_prev_but(block, but, data); button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -3342,6 +3349,38 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } + else if (ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->alt) { + /* Support alt+wheel on expanded enum rows */ + if (but->type == UI_BTYPE_ROW) { + const int direction = (event->type == WHEELDOWNMOUSE) ? -1 : 1; + uiBut *but_select = ui_but_find_select_in_enum(but, direction); + if (but_select) { + uiBut *but_other = (direction == -1) ? but_select->next : but_select->prev; + if (but_other && ui_but_find_select_in_enum__cmp(but, but_other)) { + ARegion *ar = data->region; + + data->cancel = true; + button_activate_exit(C, but, data, false, false); + + /* Activate the text button. */ + button_activate_init(C, ar, but_other, BUTTON_ACTIVATE_OVER); + data = but_other->active; + if (data) { + ui_apply_but(C, but->block, but_other, but_other->active, true); + button_activate_exit(C, but_other, data, false, false); + + /* restore active button */ + button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER); + } + else { + /* shouldn't happen */ + BLI_assert(0); + } + } + } + return WM_UI_HANDLER_BREAK; + } + } } return WM_UI_HANDLER_CONTINUE; } @@ -3706,7 +3745,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton click = 1; } } - else if (event->type == MOUSEMOVE) { + else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) { const enum eSnapType snap = ui_event_to_snap(event); float fac; @@ -4003,7 +4042,7 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton click = 1; } } - else if (event->type == MOUSEMOVE) { + else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) { #ifdef USE_DRAG_MULTINUM data->multi_data.drag_dir[0] += abs(data->draglastx - mx); data->multi_data.drag_dir[1] += abs(data->draglasty - my); @@ -4542,8 +4581,8 @@ static int ui_do_but_UNITVEC(bContext *C, uiBlock *block, uiBut *but, uiHandleBu } } else if (data->state == BUTTON_STATE_NUM_EDITING) { - if (event->type == MOUSEMOVE) { - if (mx != data->draglastx || my != data->draglasty) { + if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) { + if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) { const enum eSnapType snap = ui_event_to_snap(event); if (ui_numedit_but_UNITVEC(but, data, mx, my, snap)) ui_numedit_apply(C, block, but, data); @@ -4862,8 +4901,8 @@ static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleBu button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == MOUSEMOVE) { - if (mx != data->draglastx || my != data->draglasty) { + else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) { + if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) { const enum eSnapType snap = ui_event_to_snap(event); if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->shift != 0)) @@ -5134,8 +5173,8 @@ static int ui_do_but_HSVCIRCLE(bContext *C, uiBlock *block, uiBut *but, uiHandle ui_but_hsv_set(but); /* converts to rgb */ ui_numedit_apply(C, block, but, data); } - else if (event->type == MOUSEMOVE) { - if (mx != data->draglastx || my != data->draglasty) { + else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) { + if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) { const enum eSnapType snap = ui_event_to_snap(event); if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->shift != 0)) { @@ -6254,7 +6293,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * if ((data->state == BUTTON_STATE_HIGHLIGHT) || (event->type == EVT_DROP)) { /* handle copy-paste */ - if (ELEM(event->type, CKEY, VKEY) && event->val == KM_PRESS && (event->ctrl || event->oskey)) { + if (ELEM(event->type, CKEY, VKEY) && event->val == KM_PRESS && + IS_EVENT_MOD(event, ctrl, oskey)) + { /* Specific handling for listrows, we try to find their overlapping tex button. */ if (but->type == UI_BTYPE_LISTROW) { uiBut *labelbut = ui_but_list_row_text_activate(C, but, data, event, BUTTON_ACTIVATE_OVER); @@ -6273,7 +6314,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } /* handle eyedropper */ else if ((event->type == EKEY) && (event->val == KM_PRESS)) { - if (event->alt || event->shift || event->ctrl || event->oskey) { + if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { /* pass */ } else { @@ -6305,7 +6346,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } /* handle keyframing */ else if ((event->type == IKEY) && - !ELEM(KM_MOD_FIRST, event->ctrl, event->oskey) && + !IS_EVENT_MOD(event, ctrl, oskey) && (event->val == KM_PRESS)) { if (event->alt) { @@ -6326,7 +6367,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } /* handle drivers */ else if ((event->type == DKEY) && - !ELEM(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && + !IS_EVENT_MOD(event, shift, ctrl, oskey) && (event->val == KM_PRESS)) { if (event->alt) @@ -6340,7 +6381,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } /* handle keyingsets */ else if ((event->type == KKEY) && - !ELEM(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && + !IS_EVENT_MOD(event, shift, ctrl, oskey) && (event->val == KM_PRESS)) { if (event->alt) @@ -6353,7 +6394,10 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * return WM_UI_HANDLER_BREAK; } /* handle menu */ - else if (event->type == RIGHTMOUSE && event->val == KM_PRESS) { + else if ((event->type == RIGHTMOUSE) && + !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && + (event->val == KM_PRESS)) + { /* RMB has two options now */ if (ui_but_menu(C, but)) { return WM_UI_HANDLER_BREAK; @@ -6591,6 +6635,45 @@ static bool ui_but_isect_pie_seg(uiBlock *block, uiBut *but) return false; } +static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but_b) +{ + return ((but_a->type == but_b->type) && + (but_a->alignnr == but_b->alignnr) && + (but_a->poin == but_b->poin) && + (but_a->rnapoin.type == but_b->rnapoin.type) && + (but_a->rnaprop == but_b->rnaprop)); +} + +/** + * Finds the pressed button in an aligned row (typically an expanded enum). + * + * \param direction Use when there may be multiple buttons pressed. + */ +uiBut *ui_but_find_select_in_enum(uiBut *but, int direction) +{ + uiBut *but_iter = but; + uiBut *but_found = NULL; + BLI_assert(ELEM(direction, -1, 1)); + + while ((but_iter->prev) && + ui_but_find_select_in_enum__cmp(but_iter->prev, but)) + { + but_iter = but_iter->prev; + } + + while (but_iter && ui_but_find_select_in_enum__cmp(but_iter, but)) { + if (but_iter->flag & UI_SELECT) { + but_found = but_iter; + if (direction == 1) { + break; + } + } + but_iter = but_iter->next; + } + + return but_found; +} + uiBut *ui_but_find_active_in_region(ARegion *ar) { uiBlock *block; @@ -8301,7 +8384,7 @@ static int ui_handle_menu_event( case WHEELDOWNMOUSE: case MOUSEPAN: /* arrowkeys: only handle for block_loop blocks */ - if (event->alt || event->shift || event->ctrl || event->oskey) { + if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { /* pass */ } else if (inside || (block->flag & UI_BLOCK_LOOP)) { @@ -8446,9 +8529,7 @@ static int ui_handle_menu_event( case ZKEY: { if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) && - (event->shift == 0) && - (event->ctrl == 0) && - (event->oskey == 0)) + !IS_EVENT_MOD(event, shift, ctrl, oskey)) { if (ui_menu_pass_event_to_parent_if_nonactive(menu, but, level, retval)) break; @@ -8825,7 +8906,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle block->pie_data.duration_gesture = duration; } - if (len_sq < 1.0) { + if (len_sq < 1.0f) { uiBut *but = ui_but_find_active_in_region(menu->region); if (but) { @@ -8940,9 +9021,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case ZKEY: { if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) && - (event->shift == 0) && - (event->ctrl == 0) && - (event->oskey == 0)) + !IS_EVENT_MOD(event, shift, ctrl, oskey)) { for (but = block->buttons.first; but; but = but->next) { if (but->menu_key == event->type) { @@ -9123,13 +9202,13 @@ static void ui_region_handler_remove(bContext *C, void *UNUSED(userdata)) ui_apply_but_funcs_after(C); } +/* handle buttons at the window level, modal, for example while + * number sliding, text editing, or when a menu block is open */ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSED(userdata)) { ARegion *ar; uiBut *but; - /* here we handle buttons at the window level, modal, for example - * while number sliding, text editing, or when a menu block is open */ ar = CTX_wm_menu(C); if (!ar) ar = CTX_wm_region(C); @@ -9137,13 +9216,32 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE but = ui_but_find_active_in_region(ar); if (but) { + bScreen *screen = CTX_wm_screen(C); + ARegion *ar_temp; uiBut *but_other; uiHandleButtonData *data; + bool is_inside_menu = false; + + /* look for a popup menu containing the mouse */ + for (ar_temp = screen->regionbase.first; ar_temp; ar_temp = ar_temp->next) { + rcti rect = ar_temp->winrct; + + /* resize region rect to ignore shadow */ + BLI_rcti_resize(&rect, (BLI_rcti_size_x(&ar_temp->winrct) - UI_ThemeMenuShadowWidth() * 2), + (BLI_rcti_size_y(&ar_temp->winrct) - UI_ThemeMenuShadowWidth() * 2)); + if (BLI_rcti_isect_pt_v(&rect, &event->x)) { + BLI_assert(ar_temp->type->regionid == RGN_TYPE_TEMPORARY); + + is_inside_menu = true; + break; + } + } /* handle activated button events */ data = but->active; if ((data->state == BUTTON_STATE_MENU_OPEN) && + (is_inside_menu == false) && /* make sure mouse isn't inside another menu (see T43247) */ (but->type == UI_BTYPE_PULLDOWN) && (but_other = ui_but_find_mouse_over(ar, event)) && (but != but_other) && diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 54ba3d784d1..458e268170f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -616,6 +616,7 @@ extern void ui_but_active_free(const struct bContext *C, uiBut *but); extern bool ui_but_is_active(struct ARegion *ar) ATTR_WARN_UNUSED_RESULT; extern int ui_but_menu_direction(uiBut *but); extern void ui_but_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore); +extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction); extern uiBut *ui_but_find_active_in_region(struct ARegion *ar); bool ui_but_is_editable(const uiBut *but); bool ui_but_is_editable_as_text(const uiBut *but); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index f02c6a234a9..fb73b8ec34c 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -235,9 +235,10 @@ static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool variable = (ui_layout_vary_direction(layout) == UI_ITEM_VARY_X); if (variable) { + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; /* it may seem odd that the icon only adds (UI_UNIT_X / 4) * but taking margins into account its fine */ - return (UI_fontstyle_string_width(name) + + return (UI_fontstyle_string_width(fstyle, name) + (UI_UNIT_X * ((compact ? 1.25f : 1.50f) + (icon ? 0.25f : 0.0f)))); } @@ -639,7 +640,7 @@ static uiBut *ui_item_with_label(uiLayout *layout, uiBlock *block, const char *n if (name[0]) { /* XXX UI_fontstyle_string_width is not accurate */ #if 0 - labelw = UI_fontstyle_string_width(name); + labelw = UI_fontstyle_string_width(fstyle, name); CLAMP(labelw, w / 4, 3 * w / 4); #endif labelw = w / 3; @@ -1003,7 +1004,7 @@ void uiItemEnumO_value(uiLayout *layout, const char *name, int icon, const char PointerRNA ptr; PropertyRNA *prop; - UI_OPERATOR_ERROR_RET(ot, opname, return ); + UI_OPERATOR_ERROR_RET(ot, opname, return); WM_operator_properties_create_ptr(&ptr, ot); @@ -1035,7 +1036,7 @@ void uiItemEnumO_string(uiLayout *layout, const char *name, int icon, const char int value; bool free; - UI_OPERATOR_ERROR_RET(ot, opname, return ); + UI_OPERATOR_ERROR_RET(ot, opname, return); WM_operator_properties_create_ptr(&ptr, ot); @@ -1074,7 +1075,7 @@ void uiItemBooleanO(uiLayout *layout, const char *name, int icon, const char *op wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */ PointerRNA ptr; - UI_OPERATOR_ERROR_RET(ot, opname, return ); + UI_OPERATOR_ERROR_RET(ot, opname, return); WM_operator_properties_create_ptr(&ptr, ot); RNA_boolean_set(&ptr, propname, value); @@ -1087,7 +1088,7 @@ void uiItemIntO(uiLayout *layout, const char *name, int icon, const char *opname wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */ PointerRNA ptr; - UI_OPERATOR_ERROR_RET(ot, opname, return ); + UI_OPERATOR_ERROR_RET(ot, opname, return); WM_operator_properties_create_ptr(&ptr, ot); RNA_int_set(&ptr, propname, value); @@ -1100,7 +1101,7 @@ void uiItemFloatO(uiLayout *layout, const char *name, int icon, const char *opna wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */ PointerRNA ptr; - UI_OPERATOR_ERROR_RET(ot, opname, return ); + UI_OPERATOR_ERROR_RET(ot, opname, return); WM_operator_properties_create_ptr(&ptr, ot); RNA_float_set(&ptr, propname, value); @@ -1113,7 +1114,7 @@ void uiItemStringO(uiLayout *layout, const char *name, int icon, const char *opn wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */ PointerRNA ptr; - UI_OPERATOR_ERROR_RET(ot, opname, return ); + UI_OPERATOR_ERROR_RET(ot, opname, return); WM_operator_properties_create_ptr(&ptr, ot); RNA_string_set(&ptr, propname, value); @@ -1877,7 +1878,7 @@ void uiItemMenuEnumO(uiLayout *layout, bContext *C, const char *opname, const ch MenuItemLevel *lvl; uiBut *but; - UI_OPERATOR_ERROR_RET(ot, opname, return ); + UI_OPERATOR_ERROR_RET(ot, opname, return); if (!ot->srna) { ui_item_disabled(layout, opname); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 074faaa86bc..19e389154e4 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -515,7 +515,7 @@ static bool ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b) (but_a->rnaprop == but_b->rnaprop) && (but_a->optype == but_b->optype) && (but_a->unit_type == but_b->unit_type) && - (strncmp(but_a->drawstr, but_b->drawstr, UI_MAX_DRAW_STR) == 0)) + STREQLEN(but_a->drawstr, but_b->drawstr, UI_MAX_DRAW_STR)) { return true; } @@ -858,7 +858,7 @@ int UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(e void UI_drop_color_copy(wmDrag *drag, wmDropBox *drop) { - uiDragColorHandle *drag_info = (uiDragColorHandle *)drag->poin; + uiDragColorHandle *drag_info = drag->poin; RNA_float_set_array(drop->ptr, "color", drag_info->color); RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index e68c86353a3..d165e2719c5 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1490,7 +1490,7 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) } BLF_enable(fontid, BLF_ROTATION); - BLF_rotation(fontid, M_PI / 2); + BLF_rotation(fontid, M_PI_2); //UI_fontstyle_set(&style->widget); ui_fontscale(&fstyle_points, aspect / (U.pixelsize * 1.1f)); BLF_size(fontid, fstyle_points, U.dpi); @@ -1757,7 +1757,7 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar) /* XXX hardcoded key warning */ if ((inside || inside_header) && event->val == KM_PRESS) { - if (event->type == AKEY && !ELEM(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift, event->alt)) { + if (event->type == AKEY && ((event->ctrl + event->oskey + event->shift + event->alt) == 0)) { if (pa->flag & PNL_CLOSEDY) { if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 0ec59e4e4cd..b6a93c8306d 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -1919,7 +1919,7 @@ static void ui_update_color_picker_buts_rgb(uiBlock *block, ColorPicker *cpicker ui_but_v3_set(bt, rgb); } - else if (strcmp(bt->str, "Hex: ") == 0) { + else if (STREQ(bt->str, "Hex: ")) { float rgb_gamma[3]; unsigned char rgb_gamma_uchar[3]; double intpart; @@ -2726,7 +2726,8 @@ static uiBlock *ui_block_func_PIE(bContext *UNUSED(C), uiPopupBlockHandle *handl static float ui_pie_menu_title_width(const char *name, int icon) { - return (UI_fontstyle_string_width(name) + + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + return (UI_fontstyle_string_width(fstyle, name) + (UI_UNIT_X * (1.50f + (icon ? 0.25f : 0.0f)))); } diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 8b2ce90dcf5..2f46c0906ae 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -45,6 +45,7 @@ #include "BKE_global.h" +#include "BIF_gl.h" #include "BLF_api.h" #ifdef WITH_INTERNATIONAL @@ -147,8 +148,9 @@ static uiFont *uifont_to_blfont(int id) /* *************** draw ************************ */ -void UI_fontstyle_draw_ex(uiFontStyle *fs, const rcti *rect, const char *str, - size_t len, float *r_xofs, float *r_yofs) +void UI_fontstyle_draw_ex( + const uiFontStyle *fs, const rcti *rect, const char *str, + size_t len, float *r_xofs, float *r_yofs) { float height; int xofs = 0, yofs; @@ -194,15 +196,17 @@ void UI_fontstyle_draw_ex(uiFontStyle *fs, const rcti *rect, const char *str, *r_yofs = yofs; } -void UI_fontstyle_draw(uiFontStyle *fs, const rcti *rect, const char *str) +void UI_fontstyle_draw(const uiFontStyle *fs, const rcti *rect, const char *str) { float xofs, yofs; - UI_fontstyle_draw_ex(fs, rect, str, - BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs); + + UI_fontstyle_draw_ex( + fs, rect, str, + BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs); } /* drawn same as above, but at 90 degree angle */ -void UI_fontstyle_draw_rotated(uiFontStyle *fs, const rcti *rect, const char *str) +void UI_fontstyle_draw_rotated(const uiFontStyle *fs, const rcti *rect, const char *str) { float height; int xofs, yofs; @@ -220,7 +224,7 @@ void UI_fontstyle_draw_rotated(uiFontStyle *fs, const rcti *rect, const char *st /* rotate counter-clockwise for now (assumes left-to-right language)*/ xofs += height; yofs = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX) + 5; - angle = (float)M_PI / 2.0f; + angle = M_PI_2; /* translate rect to vertical */ txtrect.xmin = rect->xmin - BLI_rcti_size_y(rect); @@ -255,6 +259,66 @@ void UI_fontstyle_draw_rotated(uiFontStyle *fs, const rcti *rect, const char *st BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); } +/** + * Similar to #UI_fontstyle_draw + * but ignore alignment, shadow & no clipping rect. + * + * For drawing on-screen labels. + */ +void UI_fontstyle_draw_simple(const uiFontStyle *fs, float x, float y, const char *str) +{ + if (fs->kerning == 1) + BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); + + UI_fontstyle_set(fs); + BLF_position(fs->uifont_id, x, y, 0.0f); + BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); + + if (fs->kerning == 1) + BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); +} + +/** + * Same as #UI_fontstyle_draw but draw a colored backdrop. + */ +void UI_fontstyle_draw_simple_backdrop( + const uiFontStyle *fs, float x, float y, const char *str, + const unsigned char fg[4], const unsigned char bg[4]) +{ + if (fs->kerning == 1) + BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); + + UI_fontstyle_set(fs); + + { + const float width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); + const float height = BLF_height_max(fs->uifont_id); + const float decent = BLF_descender(fs->uifont_id); + const float margin = height / 4.0f; + + /* backdrop */ + glColor4ubv(bg); + + UI_draw_roundbox_corner_set(UI_CNR_ALL | UI_RB_ALPHA); + UI_draw_roundbox( + x - margin, + (y + decent) - margin, + x + width + margin, + (y + decent) + height + margin, + margin); + + glColor4ubv(fg); + } + + + BLF_position(fs->uifont_id, x, y, 0.0f); + BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); + + if (fs->kerning == 1) + BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); +} + + /* ************** helpers ************************ */ /* XXX: read a style configure */ uiStyle *UI_style_get(void) @@ -291,41 +355,29 @@ uiStyle *UI_style_get_dpi(void) return &_style; } -/* temporarily, does widget font */ -int UI_fontstyle_string_width(const char *str) +int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str) { - uiStyle *style = UI_style_get(); - uiFontStyle *fstyle = &style->widget; int width; - if (fstyle->kerning == 1) /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + if (fs->kerning == 1) /* for BLF_width */ + BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - UI_fontstyle_set(fstyle); - width = BLF_width(fstyle->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); + UI_fontstyle_set(fs); + width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - if (fstyle->kerning == 1) - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + if (fs->kerning == 1) + BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); return width; } -/* temporarily, does widget font */ -void UI_draw_string(float x, float y, const char *str) +int UI_fontstyle_height_max(const uiFontStyle *fs) { - uiStyle *style = UI_style_get(); - - if (style->widget.kerning == 1) - BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT); - - UI_fontstyle_set(&style->widget); - BLF_position(style->widget.uifont_id, x, y, 0.0f); - BLF_draw(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (style->widget.kerning == 1) - BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT); + UI_fontstyle_set(fs); + return BLF_height_max(fs->uifont_id); } + /* ************** init exit ************************ */ /* called on each startup.blend read */ @@ -398,11 +450,10 @@ void uiStyleInit(void) if (font->blf_id == -1) { font->blf_id = BLF_load_mem("default", (unsigned char *)datatoc_bfont_ttf, datatoc_bfont_ttf_size); } - else { - BLF_default_set(font->blf_id); - } } + BLF_default_set(font->blf_id); + if (font->blf_id == -1) { if (G.debug & G_DEBUG) printf("%s: error, no fonts available\n", __func__); @@ -453,7 +504,7 @@ void uiStyleInit(void) BLF_size(blf_mono_font_render, 12 * U.pixelsize, 72); } -void UI_fontstyle_set(uiFontStyle *fs) +void UI_fontstyle_set(const uiFontStyle *fs) { uiFont *font = uifont_to_blfont(fs->uifont_id); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 407843d663c..5ac991cbd94 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2862,7 +2862,7 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co /* validate arguments */ /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ - if (!strcmp(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { + if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { RNA_warning("template_list using default '%s' UIList class must provide a custom list_id", UI_UL_DEFAULT_CLASS_NAME); return; @@ -3570,8 +3570,7 @@ void uiTemplateColorspaceSettings(uiLayout *layout, PointerRNA *ptr, const char colorspace_settings_ptr = RNA_property_pointer_get(ptr, prop); - uiItemL(layout, IFACE_("Input Color Space:"), ICON_NONE); - uiItemR(layout, &colorspace_settings_ptr, "name", 0, "", ICON_NONE); + uiItemR(layout, &colorspace_settings_ptr, "name", 0, IFACE_("Color Space"), ICON_NONE); } void uiTemplateColormanagedViewSettings(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr, const char *propname) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 04a886ba2a8..9b22f8a1e7f 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -610,36 +610,36 @@ static void round_box_shade_col4_r(unsigned char r_col[4], const char col1[4], c r_col[3] = (faci * col1[3] + facm * col2[3]) >> 8; } -static void widget_verts_to_quad_strip(uiWidgetBase *wtb, const int totvert, float quad_strip[WIDGET_SIZE_MAX * 2 + 2][2]) +static void widget_verts_to_triangle_strip(uiWidgetBase *wtb, const int totvert, float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]) { int a; for (a = 0; a < totvert; a++) { - copy_v2_v2(quad_strip[a * 2], wtb->outer_v[a]); - copy_v2_v2(quad_strip[a * 2 + 1], wtb->inner_v[a]); + copy_v2_v2(triangle_strip[a * 2], wtb->outer_v[a]); + copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[a]); } - copy_v2_v2(quad_strip[a * 2], wtb->outer_v[0]); - copy_v2_v2(quad_strip[a * 2 + 1], wtb->inner_v[0]); + copy_v2_v2(triangle_strip[a * 2], wtb->outer_v[0]); + copy_v2_v2(triangle_strip[a * 2 + 1], wtb->inner_v[0]); } -static void widget_verts_to_quad_strip_open(uiWidgetBase *wtb, const int totvert, float quad_strip[WIDGET_SIZE_MAX * 2][2]) +static void widget_verts_to_triangle_strip_open(uiWidgetBase *wtb, const int totvert, float triangle_strip[WIDGET_SIZE_MAX * 2][2]) { int a; for (a = 0; a < totvert; a++) { - quad_strip[a * 2][0] = wtb->outer_v[a][0]; - quad_strip[a * 2][1] = wtb->outer_v[a][1]; - quad_strip[a * 2 + 1][0] = wtb->outer_v[a][0]; - quad_strip[a * 2 + 1][1] = wtb->outer_v[a][1] - 1.0f; + triangle_strip[a * 2][0] = wtb->outer_v[a][0]; + triangle_strip[a * 2][1] = wtb->outer_v[a][1]; + triangle_strip[a * 2 + 1][0] = wtb->outer_v[a][0]; + triangle_strip[a * 2 + 1][1] = wtb->outer_v[a][1] - 1.0f; } } static void widgetbase_outline(uiWidgetBase *wtb) { - float quad_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ - widget_verts_to_quad_strip(wtb, wtb->totvert, quad_strip); + float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ + widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip); glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, quad_strip); - glDrawArrays(GL_QUAD_STRIP, 0, wtb->totvert * 2 + 2); + glVertexPointer(2, GL_FLOAT, 0, triangle_strip); + glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2); glDisableClientState(GL_VERTEX_ARRAY); } @@ -733,18 +733,18 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) /* for each AA step */ if (wtb->outline) { - float quad_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ - float quad_strip_emboss[WIDGET_SIZE_MAX * 2][2]; /* only for emboss */ + float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ + float triangle_strip_emboss[WIDGET_SIZE_MAX * 2][2]; /* only for emboss */ const unsigned char tcol[4] = {wcol->outline[0], wcol->outline[1], wcol->outline[2], wcol->outline[3] / WIDGET_AA_JITTER}; - widget_verts_to_quad_strip(wtb, wtb->totvert, quad_strip); + widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip); if (wtb->emboss) { - widget_verts_to_quad_strip_open(wtb, wtb->halfwayvert, quad_strip_emboss); + widget_verts_to_triangle_strip_open(wtb, wtb->halfwayvert, triangle_strip_emboss); } glEnableClientState(GL_VERTEX_ARRAY); @@ -757,8 +757,8 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) /* outline */ glColor4ubv(tcol); - glVertexPointer(2, GL_FLOAT, 0, quad_strip); - glDrawArrays(GL_QUAD_STRIP, 0, wtb->totvert * 2 + 2); + glVertexPointer(2, GL_FLOAT, 0, triangle_strip); + glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2); /* emboss bottom shadow */ if (wtb->emboss) { @@ -766,8 +766,8 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) if (emboss[3]) { glColor4ubv(emboss); - glVertexPointer(2, GL_FLOAT, 0, quad_strip_emboss); - glDrawArrays(GL_QUAD_STRIP, 0, wtb->halfwayvert * 2); + glVertexPointer(2, GL_FLOAT, 0, triangle_strip_emboss); + glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->halfwayvert * 2); } } @@ -2098,7 +2098,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r rcti rect1 = *rect; float alphastep; int step, totvert; - float quad_strip[WIDGET_SIZE_MAX * 2 + 2][2]; + float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; const float radout = UI_ThemeMenuShadowWidth(); /* disabled shadow */ @@ -2126,10 +2126,10 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r glColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac)); - widget_verts_to_quad_strip(&wtb, totvert, quad_strip); + widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip); - glVertexPointer(2, GL_FLOAT, 0, quad_strip); - glDrawArrays(GL_QUAD_STRIP, 0, totvert * 2); /* add + 2 for getting a complete soft rect. Now it skips top edge to allow transparent menus */ + glVertexPointer(2, GL_FLOAT, 0, triangle_strip); + glDrawArrays(GL_TRIANGLE_STRIP, 0, totvert * 2); /* add + 2 for getting a complete soft rect. Now it skips top edge to allow transparent menus */ } glDisableClientState(GL_VERTEX_ARRAY); @@ -2210,7 +2210,7 @@ void ui_hsvcircle_pos_from_vals(uiBut *but, const rcti *rect, float *hsv, float float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f; float ang, radius_t; - ang = 2.0f * (float)M_PI * hsv[0] + 0.5f * (float)M_PI; + ang = 2.0f * (float)M_PI * hsv[0] + (float)M_PI_2; if ((but->flag & UI_BUT_COLOR_CUBIC) && (U.color_picker_type == USER_CP_CIRCLE_HSV)) radius_t = (1.0f - pow3f(1.0f - hsv[1])); @@ -3007,11 +3007,8 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat float height = rect->ymax - rect->ymin; /* find color luminance and change it slightly */ float bw = rgb_to_bw(col); - - if (bw > 0.5) - bw -= 0.5; - else - bw += 0.5; + + bw += (bw < 0.5f) ? 0.5f : -0.5f; glColor4f(bw, bw, bw, 1.0); glBegin(GL_TRIANGLES); @@ -3947,7 +3944,7 @@ void ui_draw_pie_center(uiBlock *block) int subd = 40; float angle = atan2f(pie_dir[1], pie_dir[0]); - float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? ((float)M_PI / 2.0f) : ((float)M_PI / 4.0f); + float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4; glPushMatrix(); glTranslatef(cx, cy, 0.0f); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index aa5b2570952..dab4b6a941c 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -682,6 +682,9 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo case TH_INFO_DEBUG_TEXT: cp = ts->info_debug_text; break; + case TH_V3D_CLIPPING_BORDER: + cp = ts->clipping_border_3d; + break; } } } @@ -951,6 +954,7 @@ void ui_theme_init_default(void) rgba_char_args_set(btheme->tv3d.gradients.high_gradient, 58, 58, 58, 255); btheme->tv3d.gradients.show_grad = false; + rgba_char_args_set(btheme->tv3d.clipping_border_3d, 50, 50, 50, 255); /* space buttons */ /* to have something initialized */ btheme->tbuts = btheme->tv3d; @@ -1574,7 +1578,7 @@ void init_userdef_do_versions(void) U.tb_rightmouse = 5; } if (U.mixbufsize == 0) U.mixbufsize = 2048; - if (strcmp(U.tempdir, "/") == 0) { + if (STREQ(U.tempdir, "/")) { BKE_tempdir_system_init(U.tempdir); } if (U.autokey_mode == 0) { @@ -1907,39 +1911,39 @@ void init_userdef_do_versions(void) wmKeyMap *km; for (km = U.user_keymaps.first; km; km = km->next) { - if (strcmp(km->idname, "Armature_Sketch") == 0) + if (STREQ(km->idname, "Armature_Sketch")) strcpy(km->idname, "Armature Sketch"); - else if (strcmp(km->idname, "View3D") == 0) + else if (STREQ(km->idname, "View3D")) strcpy(km->idname, "3D View"); - else if (strcmp(km->idname, "View3D Generic") == 0) + else if (STREQ(km->idname, "View3D Generic")) strcpy(km->idname, "3D View Generic"); - else if (strcmp(km->idname, "EditMesh") == 0) + else if (STREQ(km->idname, "EditMesh")) strcpy(km->idname, "Mesh"); - else if (strcmp(km->idname, "TimeLine") == 0) + else if (STREQ(km->idname, "TimeLine")) strcpy(km->idname, "Timeline"); - else if (strcmp(km->idname, "UVEdit") == 0) + else if (STREQ(km->idname, "UVEdit")) strcpy(km->idname, "UV Editor"); - else if (strcmp(km->idname, "Animation_Channels") == 0) + else if (STREQ(km->idname, "Animation_Channels")) strcpy(km->idname, "Animation Channels"); - else if (strcmp(km->idname, "GraphEdit Keys") == 0) + else if (STREQ(km->idname, "GraphEdit Keys")) strcpy(km->idname, "Graph Editor"); - else if (strcmp(km->idname, "GraphEdit Generic") == 0) + else if (STREQ(km->idname, "GraphEdit Generic")) strcpy(km->idname, "Graph Editor Generic"); - else if (strcmp(km->idname, "Action_Keys") == 0) + else if (STREQ(km->idname, "Action_Keys")) strcpy(km->idname, "Dopesheet"); - else if (strcmp(km->idname, "NLA Data") == 0) + else if (STREQ(km->idname, "NLA Data")) strcpy(km->idname, "NLA Editor"); - else if (strcmp(km->idname, "Node Generic") == 0) + else if (STREQ(km->idname, "Node Generic")) strcpy(km->idname, "Node Editor"); - else if (strcmp(km->idname, "Logic Generic") == 0) + else if (STREQ(km->idname, "Logic Generic")) strcpy(km->idname, "Logic Editor"); - else if (strcmp(km->idname, "File") == 0) + else if (STREQ(km->idname, "File")) strcpy(km->idname, "File Browser"); - else if (strcmp(km->idname, "FileMain") == 0) + else if (STREQ(km->idname, "FileMain")) strcpy(km->idname, "File Browser Main"); - else if (strcmp(km->idname, "FileButtons") == 0) + else if (STREQ(km->idname, "FileButtons")) strcpy(km->idname, "File Browser Buttons"); - else if (strcmp(km->idname, "Buttons Generic") == 0) + else if (STREQ(km->idname, "Buttons Generic")) strcpy(km->idname, "Property Editor"); } } @@ -2572,6 +2576,25 @@ void init_userdef_do_versions(void) } } + if (U.versionfile < 273 || (U.versionfile == 273 && U.subversionfile < 5)) { + bTheme *btheme; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + unsigned char *cp = (unsigned char *)btheme->tv3d.clipping_border_3d; + int c; + copy_v4_v4_char((char *)cp, btheme->tv3d.back); + c = cp[0] - 8; + CLAMP(c, 0, 255); + cp[0] = c; + c = cp[1] - 8; + CLAMP(c, 0, 255); + cp[1] = c; + c = cp[2] - 8; + CLAMP(c, 0, 255); + cp[2] = c; + cp[3] = 255; + } + } + if (U.pixelsize == 0.0f) U.pixelsize = 1.0f; diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index de46d47e5a3..b4a120b2dea 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -371,7 +371,8 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, int resize, int mask_ float winx, winy; rctf *cur, *tot; - /* use mask as size of region that View2D resides in, as it takes into account scrollbars already */ + /* use mask as size of region that View2D resides in, as it takes into account + * scrollbars already - keep in sync with zoomx/zoomy in view_zoomstep_apply_ex! */ winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1); winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1); @@ -393,6 +394,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, int resize, int mask_ */ totwidth = BLI_rctf_size_x(tot); totheight = BLI_rctf_size_y(tot); + /* keep in sync with zoomx/zoomy in view_zoomstep_apply_ex! */ curwidth = width = BLI_rctf_size_x(cur); curheight = height = BLI_rctf_size_y(cur); @@ -1851,7 +1853,7 @@ void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *v /* draw vertical steps */ if (dfac > 0.0f) { - BLF_rotation_default(M_PI / 2); + BLF_rotation_default(M_PI_2); BLF_enable_default(BLF_ROTATION); for (; fac < vert.ymax - 10; fac += dfac, val += grid->dy) { diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 297d8d0f258..88140d897ae 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -625,6 +625,7 @@ static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const bool { ARegion *ar = CTX_wm_region(C); View2D *v2d = &ar->v2d; + const rctf cur_old = v2d->cur; float dx, dy; /* calculate amount to move view by, ensuring symmetry so the @@ -651,17 +652,23 @@ static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const bool v2d->cur.xmax -= 2 * dx; } else { + + v2d->cur.xmin += dx; + v2d->cur.xmax -= dx; + if (use_mousepos && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) { - float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dx) - (mval_faci * dx); - - v2d->cur.xmin += ofs + dx; - v2d->cur.xmax += ofs - dx; - } - else { - v2d->cur.xmin += dx; - v2d->cur.xmax -= dx; + /* get zoom fac the same way as in ui_view2d_curRect_validate_resize - better keep in sync! */ + const float zoomx = (float)(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur); + + /* only move view to mouse if zoom fac is inside minzoom/maxzoom */ + if (IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom)) { + float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old); + float mval_faci = 1.0f - mval_fac; + float ofs = (mval_fac * dx) - (mval_faci * dx); + + v2d->cur.xmin += ofs; + v2d->cur.xmax += ofs; + } } } } @@ -676,17 +683,23 @@ static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const bool v2d->cur.ymax -= 2 * dy; } else { + + v2d->cur.ymin += dy; + v2d->cur.ymax -= dy; + if (use_mousepos && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) { - float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dy) - (mval_faci * dy); - - v2d->cur.ymin += ofs + dy; - v2d->cur.ymax += ofs - dy; - } - else { - v2d->cur.ymin += dy; - v2d->cur.ymax -= dy; + /* get zoom fac the same way as in ui_view2d_curRect_validate_resize - better keep in sync! */ + const float zoomy = (float)(BLI_rcti_size_y(&v2d->mask) + 1) / BLI_rctf_size_y(&v2d->cur); + + /* only move view to mouse if zoom fac is inside minzoom/maxzoom */ + if (IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom)) { + float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old); + float mval_faci = 1.0f - mval_fac; + float ofs = (mval_fac * dy) - (mval_faci * dy); + + v2d->cur.ymin += ofs; + v2d->cur.ymax += ofs; + } } } } diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index b288a02a3d1..6ce5e8a304b 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -32,6 +32,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLI_math.h" #include "BLF_translation.h" @@ -71,7 +72,7 @@ static Object *make_prim_init(bContext *C, const char *idname, *was_editmode = true; } - *dia = ED_object_new_primitive_matrix(C, obedit, loc, rot, mat, false); + *dia = ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); return obedit; } @@ -421,7 +422,9 @@ static int add_primitive_monkey_exec(bContext *C, wmOperator *op) { Object *obedit; BMEditMesh *em; - float loc[3], rot[3], mat[4][4], dia; + float mat[4][4]; + float loc[3], rot[3]; + float dia; bool enter_editmode; unsigned int layer; bool was_editmode; @@ -431,9 +434,7 @@ static int add_primitive_monkey_exec(bContext *C, wmOperator *op) obedit = make_prim_init(C, CTX_DATA_(BLF_I18NCONTEXT_ID_MESH, "Suzanne"), &dia, mat, &was_editmode, loc, rot, layer); dia = RNA_float_get(op->ptr, "radius"); - mat[0][0] *= dia; - mat[1][1] *= dia; - mat[2][2] *= dia; + mul_mat3_m4_fl(mat, dia); em = BKE_editmesh_from_object(obedit); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 0a78f299159..e8a5202029b 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1241,7 +1241,9 @@ static BMElem *bm_elem_from_knife_vert(KnifeVert *kfv, KnifeEdge **r_kfe) for (ref = kfv->edges.first; ref; ref = ref->next) { kfe = ref->ref; if (kfe->e) { - *r_kfe = kfe; + if (r_kfe) { + *r_kfe = kfe; + } break; } } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index a480b2051cc..16b74c3bddd 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -964,6 +964,40 @@ void MESH_OT_vert_connect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (!EDBM_op_call_and_selectf( + em, op, + "faces.out", true, + "connect_verts_concave faces=%hf", + BM_ELEM_SELECT)) + { + return OPERATOR_CANCELLED; + } + + + EDBM_update_generic(em, true, true); + return OPERATOR_FINISHED; +} + +void MESH_OT_vert_connect_concave(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Split Concave Faces"; + ot->idname = "MESH_OT_vert_connect_concave"; + ot->description = "Make all faces convex"; + + /* api callbacks */ + ot->exec = edbm_vert_connect_concave_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + static int edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op) { @@ -4541,7 +4575,7 @@ static int edbm_noise_exec(bContext *C, wmOperator *op) BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { float tin, dum; - externtex(ma->mtex[0], eve->co, &tin, &dum, &dum, &dum, &dum, 0, NULL); + externtex(ma->mtex[0], eve->co, &tin, &dum, &dum, &dum, &dum, 0, NULL, false); eve->co[2] += fac * tin; } } diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 152d055d239..30e9a85d083 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -57,6 +57,7 @@ #include "ED_mesh.h" #include "ED_object.h" +#include "ED_screen.h" #include "ED_uvedit.h" #include "ED_view3d.h" @@ -838,6 +839,71 @@ void MESH_OT_customdata_clear_skin(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* Clear custom loop normals */ +static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + Mesh *me = ob->data; + + if (!BKE_mesh_has_custom_loop_normals(me)) { + CustomData *data = GET_CD_DATA(me, ldata); + + if (me->edit_btmesh) { + BM_data_layer_add(me->edit_btmesh->bm, data, CD_CUSTOMLOOPNORMAL); + } + else { + CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, me->totloop); + } + + DAG_id_tag_update(&me->id, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); + + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void MESH_OT_customdata_custom_splitnormals_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Custom Split Normals Data"; + ot->idname = "MESH_OT_customdata_custom_splitnormals_add"; + ot->description = "Add a custom split normals layer, if none exists yet"; + + /* api callbacks */ + ot->exec = mesh_customdata_custom_splitnormals_add_exec; + ot->poll = ED_operator_object_active_editable_mesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + Mesh *me = ob->data; + + if (BKE_mesh_has_custom_loop_normals(me)) { + return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL); + } + return OPERATOR_CANCELLED; +} + +void MESH_OT_customdata_custom_splitnormals_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Custom Split Normals Data"; + ot->idname = "MESH_OT_customdata_custom_splitnormals_clear"; + ot->description = "Remove the custom split normals layer, if it exists"; + + /* api callbacks */ + ot->exec = mesh_customdata_custom_splitnormals_clear_exec; + ot->poll = ED_operator_object_active_editable_mesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /************************** Add Geometry Layers *************************/ void ED_mesh_update(Mesh *mesh, bContext *C, int calc_edges, int calc_tessface) diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 6ba91097ec4..8f5ecaed524 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -175,6 +175,7 @@ void MESH_OT_normals_make_consistent(struct wmOperatorType *ot); void MESH_OT_vertices_smooth(struct wmOperatorType *ot); void MESH_OT_vertices_smooth_laplacian(struct wmOperatorType *ot); void MESH_OT_vert_connect(struct wmOperatorType *ot); +void MESH_OT_vert_connect_concave(struct wmOperatorType *ot); void MESH_OT_vert_connect_nonplanar(struct wmOperatorType *ot); void MESH_OT_edge_split(struct wmOperatorType *ot); void MESH_OT_bridge_edge_loops(struct wmOperatorType *ot); @@ -237,6 +238,8 @@ void MESH_OT_vertex_color_remove(struct wmOperatorType *ot); /* no create_mask yet */ void MESH_OT_customdata_clear_mask(struct wmOperatorType *ot); void MESH_OT_customdata_clear_skin(struct wmOperatorType *ot); +void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); +void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); void MESH_OT_drop_named_image(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index e7dc5a69d53..59d0de87de6 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -153,6 +153,8 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_vertex_color_remove); WM_operatortype_append(MESH_OT_customdata_clear_mask); WM_operatortype_append(MESH_OT_customdata_clear_skin); + WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_add); + WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_clear); WM_operatortype_append(MESH_OT_drop_named_image); WM_operatortype_append(MESH_OT_edgering_select); @@ -161,6 +163,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_solidify); WM_operatortype_append(MESH_OT_select_nth); WM_operatortype_append(MESH_OT_vert_connect); + WM_operatortype_append(MESH_OT_vert_connect_concave); WM_operatortype_append(MESH_OT_vert_connect_nonplanar); WM_operatortype_append(MESH_OT_knife_tool); WM_operatortype_append(MESH_OT_knife_project); diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 19040062fda..dee216d9c73 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -329,7 +329,7 @@ int join_mesh_exec(bContext *C, wmOperator *op) if (odg) { /* Search for a match in the new object, and set new index */ for (dg = ob->defbase.first, index = 0; dg; dg = dg->next, index++) { - if (!strcmp(dg->name, odg->name)) { + if (STREQ(dg->name, odg->name)) { dvert[i].dw[j].def_nr = index; break; } diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index c3edd63b2d4..1180c082783 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -162,14 +162,13 @@ void ED_object_rotation_from_view(bContext *C, float rot[3], const char align_ax BLI_assert(align_axis >= 'X' && align_axis <= 'Z'); if (rv3d) { - const float pi_2 = (float)M_PI / 2.0f; float quat[4]; switch (align_axis) { case 'X': { float quat_y[4]; - axis_angle_to_quat(quat_y, rv3d->viewinv[1], -pi_2); + axis_angle_to_quat(quat_y, rv3d->viewinv[1], -M_PI_2); mul_qt_qtqt(quat, rv3d->viewquat, quat_y); quat[0] = -quat[0]; @@ -182,7 +181,7 @@ void ED_object_rotation_from_view(bContext *C, float rot[3], const char align_ax quat[0] = -quat[0]; quat_to_eul(rot, quat); - rot[0] -= pi_2; + rot[0] -= (float)M_PI_2; break; } case 'Z': @@ -219,9 +218,9 @@ void ED_object_base_init_transform(bContext *C, Base *base, const float loc[3], /* Uses context to figure out transform for primitive. * Returns standard diameter. */ -float ED_object_new_primitive_matrix(bContext *C, Object *obedit, - const float loc[3], const float rot[3], float primmat[4][4], - bool apply_diameter) +float ED_object_new_primitive_matrix( + bContext *C, Object *obedit, + const float loc[3], const float rot[3], float primmat[4][4]) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); @@ -246,13 +245,6 @@ float ED_object_new_primitive_matrix(bContext *C, Object *obedit, { const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL); - - if (apply_diameter) { - primmat[0][0] *= dia; - primmat[1][1] *= dia; - primmat[2][2] *= dia; - } - return dia; } @@ -516,7 +508,7 @@ static int effector_add_exec(bContext *C, wmOperator *op) cu = ob->data; cu->flag |= CU_PATH | CU_3D; ED_object_editmode_enter(C, 0); - ED_object_new_primitive_matrix(C, ob, loc, rot, mat, false); + ED_object_new_primitive_matrix(C, ob, loc, rot, mat); BLI_addtail(&cu->editnurb->nurbs, add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, dia)); if (!enter_editmode) ED_object_editmode_exit(C, EM_FREEDATA); @@ -639,7 +631,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) DAG_id_tag_update(&obedit->id, OB_RECALC_DATA); } - ED_object_new_primitive_matrix(C, obedit, loc, rot, mat, false); + ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); dia = RNA_float_get(op->ptr, "radius"); add_metaball_primitive(C, obedit, mat, dia, RNA_enum_get(op->ptr, "type")); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 83eeedfef52..fca527f8931 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -916,7 +916,7 @@ cage_cleanup: BakeData *bake = &scene->r.bake; char name[FILE_MAX]; - BKE_makepicstring_from_type(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false); + BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false); if (is_automatic_name) { BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_"); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 2596ea0a064..8f793f7f7f9 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -471,7 +471,7 @@ static void test_constraints(Object *owner, bPoseChannel *pchan) /* TODO: clear subtarget? */ curcon->flag |= CONSTRAINT_DISABLE; } - else if (strcmp(pchan->name, ct->subtarget) == 0) { + else if (STREQ(pchan->name, ct->subtarget)) { /* cannot target self */ ct->subtarget[0] = '\0'; curcon->flag |= CONSTRAINT_DISABLE; diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index f6cf0312e2d..c57cae41bf4 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -42,6 +42,7 @@ #include "BKE_context.h" #include "BKE_data_transfer.h" +#include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_remap.h" @@ -82,6 +83,7 @@ static EnumPropertyItem DT_layer_items[] = { {DT_TYPE_BWEIGHT_EDGE, "BEVEL_WEIGHT_EDGE", 0, "Bevel Weight", "Transfer bevel weights"}, {DT_TYPE_FREESTYLE_EDGE, "FREESTYLE_EDGE", 0, "Freestyle Mark", "Transfer Freestyle edge mark"}, {0, "", 0, "Face Corner Data", ""}, + {DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"}, {DT_TYPE_VCOL, "VCOL", 0, "VCol", "Vertex (face corners) colors"}, {DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"}, {0, "", 0, "Face Data", ""}, @@ -393,6 +395,8 @@ static int data_transfer_exec(bContext *C, wmOperator *op) } } + DAG_id_tag_update(&ob_dst->id, OB_RECALC_DATA); + if (reverse_transfer) { SWAP(Object *, ob_src, ob_dst); } @@ -400,6 +404,8 @@ static int data_transfer_exec(bContext *C, wmOperator *op) BLI_freelistN(&ctx_objects); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); + #if 0 /* TODO */ /* Note: issue with that is that if canceled, operator cannot be redone... Nasty in our case. */ return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED; @@ -592,6 +598,8 @@ static int datalayout_transfer_exec(bContext *C, wmOperator *op) BKE_object_data_transfer_layout(scene, ob_src, ob_dst, dtmd->data_types, use_delete, dtmd->layers_select_src, dtmd->layers_select_dst); + + DAG_id_tag_update(&ob_dst->id, OB_RECALC_DATA); } else { Object *ob_src = ob_act; @@ -621,11 +629,15 @@ static int datalayout_transfer_exec(bContext *C, wmOperator *op) BKE_object_data_transfer_layout(scene, ob_src, ob_dst, data_type, use_delete, layers_select_src, layers_select_dst); } + + DAG_id_tag_update(&ob_dst->id, OB_RECALC_DATA); } BLI_freelistN(&ctx_objects); } + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 0a053d771df..64004de1087 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -42,6 +42,8 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" +#include "BLF_translation.h" + #include "DNA_armature_types.h" #include "DNA_curve_types.h" #include "DNA_group_types.h" @@ -1347,7 +1349,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) ID *data; Curve *cu; Nurb *nu; - int clear = (strcmp(op->idname, "OBJECT_OT_shade_flat") == 0); + int clear = (STREQ(op->idname, "OBJECT_OT_shade_flat")); bool done = false, linked_data = false; CTX_DATA_BEGIN(C, Object *, ob, selected_editable_objects) @@ -1701,7 +1703,7 @@ static int game_property_new_exec(bContext *C, wmOperator *op) BLI_strncpy(prop->name, name, sizeof(prop->name)); } - BKE_bproperty_unique(NULL, prop, 0); // make_unique_prop_names(prop->name); + BLI_uniquename(&ob->prop, prop, DATA_("Property"), '.', offsetof(bProperty, name), sizeof(prop->name)); WM_event_add_notifier(C, NC_LOGIC, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/object/object_group.c b/source/blender/editors/object/object_group.c index 20e2e22cdf8..3c43f2729bd 100644 --- a/source/blender/editors/object/object_group.c +++ b/source/blender/editors/object/object_group.c @@ -61,47 +61,6 @@ /********************* 3d view operators ***********************/ -static bool group_link_early_exit_check(Group *group, Object *object) -{ - GroupObject *group_object; - - for (group_object = group->gobject.first; group_object; group_object = group_object->next) { - if (group_object->ob == object) { - return true; - } - } - - return false; -} - -static bool check_object_instances_group_recursive(Object *object, Group *group) -{ - if (object->dup_group) { - Group *dup_group = object->dup_group; - if ((dup_group->id.flag & LIB_DOIT) == 0) { - /* Cycle already exists in groups, let's prevent further crappyness */ - return true; - } - /* flag the object to identify cyclic dependencies in further dupli groups */ - dup_group->id.flag &= ~LIB_DOIT; - - if (dup_group == group) - return true; - else { - GroupObject *gob; - for (gob = dup_group->gobject.first; gob; gob = gob->next) { - if (check_object_instances_group_recursive(gob->ob, group)) - return true; - } - } - - /* un-flag the object, it's allowed to have the same group multiple times in parallel */ - dup_group->id.flag |= LIB_DOIT; - } - - return false; -} - /* can be called with C == NULL */ static EnumPropertyItem *group_object_active_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { @@ -185,15 +144,12 @@ static int objects_add_active_exec(bContext *C, wmOperator *op) if (!BKE_group_object_exists(group, ob)) continue; - /* for recursive check */ - BKE_main_id_tag_listbase(&bmain->group, true); - CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) { - if (group_link_early_exit_check(group, base->object)) + if (BKE_group_object_exists(group, base->object)) continue; - if (!check_object_instances_group_recursive(base->object, group)) { + if (!BKE_group_object_cyclic_check(bmain, base->object, group)) { BKE_group_object_add(group, base->object, scene, base); updated = true; } @@ -486,7 +442,7 @@ static int group_link_exec(bContext *C, wmOperator *op) * we could sckip all the dependency check and just consider * operator is finished. */ - if (group_link_early_exit_check(group, ob)) { + if (BKE_group_object_exists(group, ob)) { return OPERATOR_FINISHED; } @@ -495,8 +451,7 @@ static int group_link_exec(bContext *C, wmOperator *op) * It is also bad idea to add object to group which is in group which * contains our current object. */ - BKE_main_id_tag_listbase(&bmain->group, true); - if (check_object_instances_group_recursive(ob, group)) { + if (BKE_group_object_cyclic_check(bmain, ob, group)) { BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index 9f9a647c9f1..1d764ced524 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -503,6 +503,15 @@ static int add_hook_object(Main *bmain, Scene *scene, Object *obedit, Object *ob unit_m4(pose_mat); + invert_m4_m4(obedit->imat, obedit->obmat); + if (mode == OBJECT_ADDHOOK_NEWOB) { + /* pass */ + } + else { + /* may overwrite with pose-bone location, below */ + mul_v3_m4v3(cent, obedit->imat, ob->obmat[3]); + } + if (mode == OBJECT_ADDHOOK_SELOB_BONE) { bArmature *arm = ob->data; BLI_assert(ob->type == OB_ARMATURE); @@ -514,6 +523,8 @@ static int add_hook_object(Main *bmain, Scene *scene, Object *obedit, Object *ob pchan_act = BKE_pose_channel_active(ob); if (LIKELY(pchan_act)) { invert_m4_m4(pose_mat, pchan_act->pose_mat); + mul_v3_m4v3(cent, ob->obmat, pchan_act->pose_mat[3]); + mul_v3_m4v3(cent, obedit->imat, cent); } } else { @@ -521,6 +532,9 @@ static int add_hook_object(Main *bmain, Scene *scene, Object *obedit, Object *ob } } + copy_v3_v3(hmd->cent, cent); + + /* matrix calculus */ /* vert x (obmat x hook->imat) x hook->obmat x ob->imat */ /* (parentinv ) */ diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 5a479af83b5..bed85444101 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -440,9 +440,9 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene * for (a = 0; a < totpart; a++) { key = cache[a]; - if (key->steps > 0) { - totvert += key->steps + 1; - totedge += key->steps; + if (key->segments > 0) { + totvert += key->segments + 1; + totedge += key->segments; } } @@ -450,9 +450,9 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene * for (a = 0; a < totchild; a++) { key = cache[a]; - if (key->steps > 0) { - totvert += key->steps + 1; - totedge += key->steps; + if (key->segments > 0) { + totvert += key->segments + 1; + totedge += key->segments; } } @@ -476,7 +476,7 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene * cache = psys->pathcache; for (a = 0; a < totpart; a++) { key = cache[a]; - kmax = key->steps; + kmax = key->segments; for (k = 0; k <= kmax; k++, key++, cvert++, mvert++) { copy_v3_v3(mvert->co, key->co); if (k) { @@ -495,7 +495,7 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene * cache = psys->childcache; for (a = 0; a < totchild; a++) { key = cache[a]; - kmax = key->steps; + kmax = key->segments; for (k = 0; k <= kmax; k++, key++, cvert++, mvert++) { copy_v3_v3(mvert->co, key->co); if (k) { diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 67b5c8061cd..430994aed60 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -973,7 +973,7 @@ void OBJECT_OT_parent_set(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); + ot->prop = RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); RNA_def_boolean(ot->srna, "xmirror", false, "X Mirror", "Apply weights symmetrically along X axis, for Envelope/Automatic vertex groups creation"); RNA_def_boolean(ot->srna, "keep_transform", false, "Keep Transform", @@ -2344,10 +2344,8 @@ void OBJECT_OT_make_local(wmOperatorType *ot) } enum { - /* Be careful with those values, they are used as bitflags in some cases, in others as bool... - * See single_object_users, single_obdata_users, single_object_action_users, etc.< */ - MAKE_SINGLE_USER_ALL = 0, - MAKE_SINGLE_USER_SELECTED = SELECT, + MAKE_SINGLE_USER_ALL = 1, + MAKE_SINGLE_USER_SELECTED = 2, }; static int make_single_user_exec(bContext *C, wmOperator *op) @@ -2355,7 +2353,7 @@ static int make_single_user_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); /* ok if this is NULL */ - const int flag = RNA_enum_get(op->ptr, "type"); + const int flag = (RNA_enum_get(op->ptr, "type") == MAKE_SINGLE_USER_SELECTED) ? SELECT : 0; const bool copy_groups = false; bool update_deps = false; @@ -2421,7 +2419,7 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", type_items, SELECT, "Type", ""); + ot->prop = RNA_def_enum(ot->srna, "type", type_items, MAKE_SINGLE_USER_SELECTED, "Type", ""); RNA_def_boolean(ot->srna, "object", 0, "Object", "Make single user objects"); RNA_def_boolean(ot->srna, "obdata", 0, "Object Data", "Make single user object data"); diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index e70281c502b..f885cbbb24f 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -1646,7 +1646,7 @@ static void vgroup_blend_subset(Object *ob, const bool *vgroup_validmap, const i BMesh *bm = em ? em->bm : NULL; Mesh *me = em ? NULL : ob->data; - MeshElemMap *emap ; + MeshElemMap *emap; int *emap_mem; BLI_SMALLSTACK_DECLARE(dv_stack, MDeformVert *); diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 5e30ca4ed2f..fd32aad852d 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -61,7 +61,7 @@ #include "BKE_modifier.h" #include "BKE_particle.h" #include "BKE_report.h" - +#include "BKE_bvhutils.h" #include "BKE_pointcache.h" #include "BIF_gl.h" @@ -85,8 +85,10 @@ static void PE_create_particle_edit_from_psys(Scene *scene, Object *ob, ParticleSystem *psys); static void PE_create_particle_edit_from_cache(Scene *scene, Object *ob, PointCache *cache, ListBase *mem_cache); -static void PTCacheUndo_clear(PTCacheEdit *edit); -static void recalc_emitter_field(Object *ob, ParticleSystem *psys); +void PTCacheUndo_clear(PTCacheEdit *edit); +void recalc_lengths(PTCacheEdit *edit); +void recalc_emitter_field(Object *ob, ParticleSystem *psys); +void update_world_cos(Object *ob, PTCacheEdit *edit); #define KEY_K PTCacheEditKey *key; int k #define POINT_P PTCacheEditPoint *point; int p @@ -362,6 +364,7 @@ typedef struct PEData { Object *ob; DerivedMesh *dm; PTCacheEdit *edit; + BVHTreeFromMesh shape_bvh; const int *mval; rcti *rect; @@ -418,6 +421,25 @@ static void PE_set_view3d_data(bContext *C, PEData *data) } } +static void PE_create_shape_tree(PEData *data, Object *shapeob) +{ + DerivedMesh *dm = shapeob->derivedFinal; + + memset(&data->shape_bvh, 0, sizeof(data->shape_bvh)); + + if (!dm) { + return; + } + + DM_ensure_tessface(dm); + bvhtree_from_mesh_faces(&data->shape_bvh, dm, 0.0f, 4, 8); +} + +static void PE_free_shape_tree(PEData *data) +{ + free_bvhtree_from_mesh(&data->shape_bvh); +} + /*************************** selection utilities *******************************/ static bool key_test_depth(PEData *data, const float co[3], const int screen_co[2]) @@ -1062,7 +1084,7 @@ static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit) } } /* set current distances to be kept between neighbouting keys */ -static void recalc_lengths(PTCacheEdit *edit) +void recalc_lengths(PTCacheEdit *edit) { POINT_P; KEY_K; @@ -1078,7 +1100,7 @@ static void recalc_lengths(PTCacheEdit *edit) } /* calculate a tree for finding nearest emitter's vertice */ -static void recalc_emitter_field(Object *ob, ParticleSystem *psys) +void recalc_emitter_field(Object *ob, ParticleSystem *psys) { DerivedMesh *dm=psys_get_modifier(ob, psys)->dm; PTCacheEdit *edit= psys->edit; @@ -1166,7 +1188,7 @@ static void PE_update_selection(Scene *scene, Object *ob, int useflag) point->flag &= ~PEP_EDIT_RECALC; } -static void update_world_cos(Object *ob, PTCacheEdit *edit) +void update_world_cos(Object *ob, PTCacheEdit *edit) { ParticleSystem *psys = edit->psys; ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys); @@ -3236,7 +3258,7 @@ static void brush_puff(PEData *data, int point_index) static void BKE_brush_weight_get(PEData *data, float UNUSED(mat[4][4]), float UNUSED(imat[4][4]), int point_index, int key_index, PTCacheEditKey *UNUSED(key)) { - /* roots have full weight allways */ + /* roots have full weight always */ if (key_index) { PTCacheEdit *edit = data->edit; ParticleSystem *psys = edit->psys; @@ -4039,6 +4061,180 @@ void PARTICLE_OT_brush_edit(wmOperatorType *ot) RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); } +/*********************** cut shape ***************************/ + +static int shape_cut_poll(bContext *C) +{ + if (PE_hair_poll(C)) { + Scene *scene= CTX_data_scene(C); + ParticleEditSettings *pset= PE_settings(scene); + + if (pset->shape_object) + return true; + } + + return false; +} + +typedef struct PointInsideBVH { + BVHTreeFromMesh bvhdata; + int num_hits; +} PointInsideBVH; + +static void point_inside_bvh_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + PointInsideBVH *data = userdata; + + data->bvhdata.raycast_callback(&data->bvhdata, index, ray, hit); + + if (hit->index != -1) + ++data->num_hits; +} + +/* true if the point is inside the shape mesh */ +static bool shape_cut_test_point(PEData *data, ParticleCacheKey *key) +{ + BVHTreeFromMesh *shape_bvh = &data->shape_bvh; + const float dir[3] = {1.0f, 0.0f, 0.0f}; + PointInsideBVH userdata; + + 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); + + /* for any point inside a watertight mesh the number of hits is uneven */ + return (userdata.num_hits % 2) == 1; +} + +static void shape_cut(PEData *data, int pa_index) +{ + PTCacheEdit *edit = data->edit; + Object *ob = data->ob; + ParticleEditSettings *pset = PE_settings(data->scene); + ParticleCacheKey *key; + + bool cut; + float cut_time = 1.0; + int k, totkeys = 1 << pset->draw_step; + + /* don't cut hidden */ + if (edit->points[pa_index].flag & PEP_HIDE) + return; + + cut = false; + + /* check if root is inside the cut shape */ + key = edit->pathcache[pa_index]; + if (!shape_cut_test_point(data, key)) { + cut_time = -1.0f; + cut = true; + } + else { + for (k = 0; k < totkeys; k++, key++) { + BVHTreeRayHit hit; + float dir[3]; + float len; + + sub_v3_v3v3(dir, (key+1)->co, key->co); + len = normalize_v3(dir); + + memset(&hit, 0, sizeof(hit)); + hit.index = -1; + hit.dist = len; + BLI_bvhtree_ray_cast(data->shape_bvh.tree, key->co, dir, 0.0f, &hit, data->shape_bvh.raycast_callback, &data->shape_bvh); + if (hit.index >= 0) { + if (hit.dist < len) { + cut_time = (hit.dist / len + (float)k) / (float)totkeys; + cut = true; + break; + } + } + } + } + + if (cut) { + if (cut_time < 0.0f) { + edit->points[pa_index].flag |= PEP_TAG; + } + else { + rekey_particle_to_time(data->scene, ob, pa_index, cut_time); + edit->points[pa_index].flag |= PEP_EDIT_RECALC; + } + } +} + +static int shape_cut_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + ParticleEditSettings *pset = PE_settings(scene); + PTCacheEdit *edit = PE_get_current(scene, ob); + Object *shapeob = pset->shape_object; + int selected = count_selected_keys(scene, edit); + int lock_root = pset->flag & PE_LOCK_FIRST; + + if (!PE_start_edit(edit)) + return OPERATOR_CANCELLED; + + /* disable locking temporatily for disconnected hair */ + if (edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR) + pset->flag &= ~PE_LOCK_FIRST; + + if (edit->psys && edit->pathcache) { + PEData data; + int removed; + + PE_set_data(C, &data); + PE_create_shape_tree(&data, shapeob); + + if (selected) + foreach_selected_point(&data, shape_cut); + else + foreach_point(&data, shape_cut); + + removed = remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob)); + recalc_lengths(edit); + + if (removed) { + update_world_cos(ob, edit); + psys_free_path_cache(NULL, edit); + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + else + 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); + } + + PE_free_shape_tree(&data); + } + + pset->flag |= lock_root; + + return OPERATOR_FINISHED; +} + +void PARTICLE_OT_shape_cut(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Shape Cut"; + ot->idname = "PARTICLE_OT_shape_cut"; + ot->description = "Cut hair to conform to the set shape object"; + + /* api callbacks */ + ot->exec = shape_cut_exec; + ot->poll = shape_cut_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; +} + /*********************** undo ***************************/ static void free_PTCacheUndo(PTCacheUndo *undo) @@ -4266,7 +4462,7 @@ int PE_undo_valid(Scene *scene) return 0; } -static void PTCacheUndo_clear(PTCacheEdit *edit) +void PTCacheUndo_clear(PTCacheEdit *edit) { PTCacheUndo *undo; diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 5a61f77e5a8..dba6d6f5085 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -40,18 +40,21 @@ #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" +#include "BLI_string.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" #include "BKE_cdderivedmesh.h" #include "BKE_global.h" +#include "BKE_library.h" #include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_report.h" - #include "RNA_access.h" #include "RNA_define.h" @@ -62,8 +65,37 @@ #include "ED_screen.h" #include "ED_object.h" +#include "UI_resources.h" + #include "physics_intern.h" +extern void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys); +extern void PTCacheUndo_clear(PTCacheEdit *edit); +extern void recalc_lengths(PTCacheEdit *edit); +extern void recalc_emitter_field(Object *ob, ParticleSystem *psys); +extern void update_world_cos(Object *ob, PTCacheEdit *edit); + +#define KEY_K PTCacheEditKey *key; int k +#define POINT_P PTCacheEditPoint *point; int p +#define LOOP_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) +#if 0 +#define LOOP_VISIBLE_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!(point->flag & PEP_HIDE)) +#define LOOP_SELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point_is_selected(point)) +#define LOOP_UNSELECTED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (!point_is_selected(point)) +#define LOOP_EDITED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_EDIT_RECALC) +#define LOOP_TAGGED_POINTS for (p=0, point=edit->points; p<edit->totpoint; p++, point++) if (point->flag & PEP_TAG) +#endif +#define LOOP_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) +#if 0 +#define LOOP_VISIBLE_KEYS for (k=0, key=point->keys; k<point->totkey; k++, key++) if (!(key->flag & PEK_HIDE)) +#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) +#endif + +static float I[4][4] = {{1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}}; + /********************** particle system slot operators *********************/ static int particle_system_add_exec(bContext *C, wmOperator *UNUSED(op)) @@ -623,38 +655,50 @@ void PARTICLE_OT_disconnect_hair(wmOperatorType *ot) RNA_def_boolean(ot->srna, "all", 0, "All hair", "Disconnect all hair systems from the emitter mesh"); } -static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) +/* from/to_world_space : whether from/to particles are in world or hair space + * from/to_mat : additional transform for from/to particles (e.g. for using object space copying) + */ +static bool remap_hair_emitter(Scene *scene, Object *ob, ParticleSystem *psys, + Object *target_ob, ParticleSystem *target_psys, PTCacheEdit *target_edit, + float from_mat[4][4], float to_mat[4][4], bool from_global, bool to_global) { - ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys); - ParticleData *pa; - PTCacheEdit *edit; - PTCacheEditPoint *point; - PTCacheEditKey *ekey = NULL; - HairKey *key; + ParticleSystemModifierData *target_psmd = psys_get_modifier(target_ob, target_psys); + ParticleData *pa, *tpa; + PTCacheEditPoint *edit_point; + PTCacheEditKey *ekey; BVHTreeFromMesh bvhtree= {NULL}; - BVHTreeNearest nearest; MFace *mface = NULL, *mf; MEdge *medge = NULL, *me; MVert *mvert; - DerivedMesh *dm = NULL; + DerivedMesh *dm, *target_dm; int numverts; int i, k; - float hairmat[4][4], imat[4][4]; - float v[4][3], vec[3]; + float from_ob_imat[4][4], to_ob_imat[4][4]; + float from_imat[4][4], to_imat[4][4]; - if (!psys || !psys->part || psys->part->type != PART_HAIR || !psmd->dm) + if (!target_psmd->dm) + return false; + if (!psys->part || psys->part->type != PART_HAIR) return false; + if (!target_psys->part || target_psys->part->type != PART_HAIR) + return false; + + edit_point = target_edit ? target_edit->points : NULL; - edit= psys->edit; - point= edit ? edit->points : NULL; + invert_m4_m4(from_ob_imat, ob->obmat); + invert_m4_m4(to_ob_imat, target_ob->obmat); + invert_m4_m4(from_imat, from_mat); + invert_m4_m4(to_imat, to_mat); - if (psmd->dm->deformedOnly) { - /* we don't want to mess up psmd->dm when converting to global coordinates below */ - dm = psmd->dm; + if (target_psmd->dm->deformedOnly) { + /* we don't want to mess up target_psmd->dm when converting to global coordinates below */ + dm = target_psmd->dm; } else { - dm = mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH); + /* warning: this rebuilds target_psmd->dm! */ + dm = mesh_get_derived_deform(scene, target_ob, CD_MASK_BAREMESH); } + target_dm = target_psmd->dm; /* don't modify the original vertices */ dm = CDDM_copy(dm); @@ -662,12 +706,11 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) DM_ensure_tessface(dm); numverts = dm->getNumVerts(dm); - mvert = dm->getVertArray(dm); /* convert to global coordinates */ for (i=0; i<numverts; i++) - mul_m4_v3(ob->obmat, mvert[i].co); + mul_m4_v3(to_mat, mvert[i].co); if (dm->getNumTessFaces(dm) != 0) { mface = dm->getTessFaceArray(dm); @@ -682,13 +725,23 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) return false; } - for (i=0, pa= psys->particles; i<psys->totpart; i++, pa++) { - key = pa->hair; + for (i = 0, tpa = target_psys->particles, pa = psys->particles; + i < target_psys->totpart; + i++, tpa++, pa++) { + + float from_co[3]; + BVHTreeNearest nearest; + + if (from_global) + mul_v3_m4v3(from_co, from_ob_imat, pa->hair[0].co); + else + mul_v3_m4v3(from_co, from_ob_imat, pa->hair[0].world_co); + mul_m4_v3(from_mat, from_co); nearest.index = -1; nearest.dist_sq = FLT_MAX; - BLI_bvhtree_find_nearest(bvhtree.tree, key->co, &nearest, bvhtree.nearest_callback, &bvhtree); + BLI_bvhtree_find_nearest(bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree); if (nearest.index == -1) { if (G.debug & G_DEBUG) @@ -697,6 +750,8 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) } if (mface) { + float v[4][3]; + mf = &mface[nearest.index]; copy_v3_v3(v[0], mvert[mf->v1].co); @@ -704,44 +759,80 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) copy_v3_v3(v[2], mvert[mf->v3].co); if (mf->v4) { copy_v3_v3(v[3], mvert[mf->v4].co); - interp_weights_poly_v3(pa->fuv, v, 4, nearest.co); + interp_weights_poly_v3(tpa->fuv, v, 4, nearest.co); } else - interp_weights_poly_v3(pa->fuv, v, 3, nearest.co); + interp_weights_poly_v3(tpa->fuv, v, 3, nearest.co); + tpa->foffset = 0.0f; - pa->num = nearest.index; - pa->num_dmcache = psys_particle_dm_face_lookup(ob, psmd->dm, pa->num, pa->fuv, NULL); + tpa->num = nearest.index; + tpa->num_dmcache = psys_particle_dm_face_lookup(target_ob, target_dm, tpa->num, tpa->fuv, NULL); } else { me = &medge[nearest.index]; - pa->fuv[1] = line_point_factor_v3(nearest.co, - mvert[me->v2].co, - mvert[me->v2].co); - pa->fuv[0] = 1.0f - pa->fuv[1]; - pa->fuv[2] = pa->fuv[3] = 0.0f; + tpa->fuv[1] = line_point_factor_v3(nearest.co, + mvert[me->v1].co, + mvert[me->v2].co); + tpa->fuv[0] = 1.0f - tpa->fuv[1]; + tpa->fuv[2] = tpa->fuv[3] = 0.0f; + tpa->foffset = 0.0f; - pa->num = nearest.index; - pa->num_dmcache = -1; + tpa->num = nearest.index; + tpa->num_dmcache = -1; } - psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); - invert_m4_m4(imat, hairmat); - - sub_v3_v3v3(vec, nearest.co, key->co); - - if (point) { - ekey = point->keys; - point++; - } - - for (k=0, key=pa->hair; k<pa->totkey; k++, key++) { - add_v3_v3(key->co, vec); - mul_m4_v3(imat, key->co); - - if (ekey) { - ekey->flag |= PEK_USE_WCO; - ekey++; + /* translate hair keys */ + { + HairKey *key, *tkey; + float hairmat[4][4], imat[4][4]; + float offset[3]; + + if (to_global) + copy_m4_m4(imat, target_ob->obmat); + else { + /* note: using target_dm here, which is in target_ob object space and has full modifiers */ + psys_mat_hair_to_object(target_ob, target_dm, target_psys->part->from, tpa, hairmat); + invert_m4_m4(imat, hairmat); + } + mul_m4_m4m4(imat, imat, to_imat); + + /* offset in world space */ + sub_v3_v3v3(offset, nearest.co, from_co); + + if (edit_point) { + for (k=0, key=pa->hair, tkey=tpa->hair, ekey = edit_point->keys; k<tpa->totkey; k++, key++, tkey++, ekey++) { + float co_orig[3]; + + if (from_global) + mul_v3_m4v3(co_orig, from_ob_imat, key->co); + else + mul_v3_m4v3(co_orig, from_ob_imat, key->world_co); + mul_m4_v3(from_mat, co_orig); + + add_v3_v3v3(tkey->co, co_orig, offset); + + mul_m4_v3(imat, tkey->co); + + ekey->flag |= PEK_USE_WCO; + } + + edit_point++; + } + else { + for (k=0, key=pa->hair, tkey=tpa->hair; k<tpa->totkey; k++, key++, tkey++) { + float co_orig[3]; + + if (from_global) + mul_v3_m4v3(co_orig, from_ob_imat, key->co); + else + mul_v3_m4v3(co_orig, from_ob_imat, key->world_co); + mul_m4_v3(from_mat, co_orig); + + add_v3_v3v3(tkey->co, co_orig, offset); + + mul_m4_v3(imat, tkey->co); + } } } } @@ -749,15 +840,26 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) free_bvhtree_from_mesh(&bvhtree); dm->release(dm); - psys_free_path_cache(psys, psys->edit); + psys_free_path_cache(target_psys, target_edit); - psys->flag &= ~PSYS_GLOBAL_HAIR; - - PE_update_object(scene, ob, 0); + PE_update_object(scene, target_ob, 0); return true; } +static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) +{ + bool ok; + + if (!psys) + return false; + + ok = remap_hair_emitter(scene, ob, psys, ob, psys, psys->edit, ob->obmat, ob->obmat, psys->flag & PSYS_GLOBAL_HAIR, false); + psys->flag &= ~PSYS_GLOBAL_HAIR; + + return ok; +} + static int connect_hair_exec(bContext *C, wmOperator *op) { Scene *scene= CTX_data_scene(C); @@ -805,3 +907,284 @@ void PARTICLE_OT_connect_hair(wmOperatorType *ot) RNA_def_boolean(ot->srna, "all", 0, "All hair", "Connect all hair systems to the emitter mesh"); } +/************************ particle system copy operator *********************/ + +typedef enum eCopyParticlesSpace { + PAR_COPY_SPACE_OBJECT = 0, + PAR_COPY_SPACE_WORLD = 1, +} eCopyParticlesSpace; + +static void copy_particle_edit(Scene *scene, Object *ob, ParticleSystem *psys, ParticleSystem *psys_from) +{ + PTCacheEdit *edit_from = psys_from->edit, *edit; + ParticleData *pa; + KEY_K; + POINT_P; + + if (!edit_from) + return; + + edit = MEM_dupallocN(edit_from); + edit->psys = psys; + psys->edit = edit; + + edit->pathcache = NULL; + BLI_listbase_clear(&edit->pathcachebufs); + + edit->emitter_field = NULL; + edit->emitter_cosnos = NULL; + + BLI_listbase_clear(&edit->undo); + edit->curundo = NULL; + + edit->points = MEM_dupallocN(edit_from->points); + pa = psys->particles; + LOOP_POINTS { + HairKey *hkey = pa->hair; + + point->keys= MEM_dupallocN(point->keys); + LOOP_KEYS { + key->co = hkey->co; + key->time = &hkey->time; + key->flag = hkey->editflag; + if (!(psys->flag & PSYS_GLOBAL_HAIR)) { + key->flag |= PEK_USE_WCO; + hkey->editflag |= PEK_USE_WCO; + } + + hkey++; + } + + pa++; + } + update_world_cos(ob, edit); + + UI_GetThemeColor3ubv(TH_EDGE_SELECT, edit->sel_col); + UI_GetThemeColor3ubv(TH_WIRE, edit->nosel_col); + + recalc_lengths(edit); + recalc_emitter_field(ob, psys); + PE_update_object(scene, ob, true); + + PTCacheUndo_clear(edit); + PE_undo_push(scene, "Original"); +} + +static void remove_particle_systems_from_object(Object *ob_to) +{ + ModifierData *md, *md_next; + + if (ob_to->type != OB_MESH) + return; + if (!ob_to->data || ((ID *)ob_to->data)->lib) + return; + + for (md = ob_to->modifiers.first; md; md = md_next) { + md_next = md->next; + + /* remove all particle system modifiers as well, + * these need to sync to the particle system list + */ + if (ELEM(md->type, eModifierType_ParticleSystem, eModifierType_DynamicPaint, eModifierType_Smoke)) { + BLI_remlink(&ob_to->modifiers, md); + modifier_free(md); + } + } + + BKE_object_free_particlesystems(ob_to); +} + +/* single_psys_from is optional, if NULL all psys of ob_from are copied */ +static bool copy_particle_systems_to_object(Scene *scene, Object *ob_from, ParticleSystem *single_psys_from, Object *ob_to, int space) +{ + ModifierData *md; + ParticleSystem *psys_start, *psys, *psys_from; + ParticleSystem **tmp_psys; + DerivedMesh *final_dm; + CustomDataMask cdmask; + int i, totpsys; + + if (ob_to->type != OB_MESH) + return false; + if (!ob_to->data || ((ID *)ob_to->data)->lib) + return false; + + /* For remapping we need a valid DM. + * Because the modifiers are appended at the end it's safe to use + * the final DM of the object without particles. + * However, when evaluating the DM all the particle modifiers must be valid, + * i.e. have the psys assigned already. + * To break this hen/egg problem we create all psys separately first (to collect required customdata masks), + * then create the DM, then add them to the object and make the psys modifiers ... + */ + #define PSYS_FROM_FIRST (single_psys_from ? single_psys_from : ob_from->particlesystem.first) + #define PSYS_FROM_NEXT(cur) (single_psys_from ? NULL : (cur)->next) + totpsys = single_psys_from ? 1 : BLI_listbase_count(&ob_from->particlesystem); + + tmp_psys = MEM_mallocN(sizeof(ParticleSystem*) * totpsys, "temporary particle system array"); + + cdmask = 0; + for (psys_from = PSYS_FROM_FIRST, i = 0; + psys_from; + psys_from = PSYS_FROM_NEXT(psys_from), ++i) { + + psys = BKE_object_copy_particlesystem(psys_from); + tmp_psys[i] = psys; + + if (psys_start == NULL) + psys_start = psys; + + cdmask |= psys_emitter_customdata_mask(psys); + } + /* to iterate source and target psys in sync, + * we need to know where the newly added psys start + */ + psys_start = totpsys > 0 ? tmp_psys[0] : NULL; + + /* get the DM (psys and their modifiers have not been appended yet) */ + final_dm = mesh_get_derived_final(scene, ob_to, cdmask); + + /* now append psys to the object and make modifiers */ + for (i = 0, psys_from = PSYS_FROM_FIRST; + i < totpsys; + ++i, psys_from = PSYS_FROM_NEXT(psys_from)) { + + ParticleSystemModifierData *psmd; + + psys = tmp_psys[i]; + + /* append to the object */ + BLI_addtail(&ob_to->particlesystem, psys); + + /* add a particle system modifier for each system */ + md = modifier_new(eModifierType_ParticleSystem); + psmd = (ParticleSystemModifierData *)md; + /* push on top of the stack, no use trying to reproduce old stack order */ + BLI_addtail(&ob_to->modifiers, md); + + BLI_snprintf(md->name, sizeof(md->name), "ParticleSystem %i", i); + modifier_unique_name(&ob_to->modifiers, (ModifierData *)psmd); + + psmd->psys = psys; + psmd->dm = CDDM_copy(final_dm); + CDDM_calc_normals(psmd->dm); + DM_ensure_tessface(psmd->dm); + + if (psys_from->edit) + copy_particle_edit(scene, ob_to, psys, psys_from); + } + MEM_freeN(tmp_psys); + + /* note: do this after creating DM copies for all the particle system modifiers, + * the remapping otherwise makes final_dm invalid! + */ + for (psys = psys_start, psys_from = PSYS_FROM_FIRST, i = 0; + psys; + psys = psys->next, psys_from = PSYS_FROM_NEXT(psys_from), ++i) { + + float (*from_mat)[4], (*to_mat)[4]; + + switch (space) { + case PAR_COPY_SPACE_OBJECT: + from_mat = I; + to_mat = I; + break; + case PAR_COPY_SPACE_WORLD: + from_mat = ob_from->obmat; + to_mat = ob_to->obmat; + break; + default: + /* should not happen */ + BLI_assert(false); + break; + } + + remap_hair_emitter(scene, ob_from, psys_from, ob_to, psys, psys->edit, from_mat, to_mat, psys_from->flag & PSYS_GLOBAL_HAIR, psys->flag & PSYS_GLOBAL_HAIR); + + /* tag for recalc */ +// psys->recalc |= PSYS_RECALC_RESET; + } + + #undef PSYS_FROM_FIRST + #undef PSYS_FROM_NEXT + + DAG_id_tag_update(&ob_to->id, OB_RECALC_DATA); + WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob_to); + return true; +} + +static int copy_particle_systems_poll(bContext *C) +{ + Object *ob; + if (!ED_operator_object_active_editable(C)) + return false; + + ob = ED_object_active_context(C); + if (BLI_listbase_is_empty(&ob->particlesystem)) + return false; + + return true; +} + +static int copy_particle_systems_exec(bContext *C, wmOperator *op) +{ + const int space = RNA_enum_get(op->ptr, "space"); + const bool remove_target_particles = RNA_boolean_get(op->ptr, "remove_target_particles"); + const bool use_active = RNA_boolean_get(op->ptr, "use_active"); + Scene *scene = CTX_data_scene(C); + Object *ob_from = ED_object_active_context(C); + ParticleSystem *psys_from = use_active ? CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data : NULL; + + int changed_tot = 0; + int fail = 0; + + CTX_DATA_BEGIN (C, Object *, ob_to, selected_editable_objects) + { + if (ob_from != ob_to) { + bool changed = false; + if (remove_target_particles) { + remove_particle_systems_from_object(ob_to); + changed = true; + } + if (copy_particle_systems_to_object(scene, ob_from, psys_from, ob_to, space)) + changed = true; + else + fail++; + + if (changed) + changed_tot++; + } + } + CTX_DATA_END; + + if ((changed_tot == 0 && fail == 0) || fail) { + BKE_reportf(op->reports, RPT_ERROR, + "Copy particle systems to selected: %d done, %d failed", + changed_tot, fail); + } + + return OPERATOR_FINISHED; +} + +void PARTICLE_OT_copy_particle_systems(wmOperatorType *ot) +{ + static EnumPropertyItem space_items[] = { + {PAR_COPY_SPACE_OBJECT, "OBJECT", 0, "Object", "Copy inside each object's local space"}, + {PAR_COPY_SPACE_WORLD, "WORLD", 0, "World", "Copy in world space"}, + {0, NULL, 0, NULL, NULL} + }; + + ot->name = "Copy Particle Systems"; + ot->description = "Copy particle systems from the active object to selected objects"; + ot->idname = "PARTICLE_OT_copy_particle_systems"; + + ot->poll = copy_particle_systems_poll; + ot->exec = copy_particle_systems_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "space", space_items, PAR_COPY_SPACE_OBJECT, "Space", "Space transform for copying from one object to another"); + RNA_def_boolean(ot->srna, "remove_target_particles", true, "Remove Target Particles", "Remove particle systems on the target objects"); + RNA_def_boolean(ot->srna, "use_active", false, "Use Active", "Use the active particle system from the context"); +} diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h index 8c49bf21191..88542515553 100644 --- a/source/blender/editors/physics/physics_intern.h +++ b/source/blender/editors/physics/physics_intern.h @@ -56,6 +56,8 @@ void PARTICLE_OT_mirror(struct wmOperatorType *ot); void PARTICLE_OT_brush_edit(struct wmOperatorType *ot); +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); @@ -70,6 +72,7 @@ void PARTICLE_OT_target_move_up(struct wmOperatorType *ot); void PARTICLE_OT_target_move_down(struct wmOperatorType *ot); void PARTICLE_OT_connect_hair(struct wmOperatorType *ot); void PARTICLE_OT_disconnect_hair(struct wmOperatorType *ot); +void PARTICLE_OT_copy_particle_systems(struct wmOperatorType *ot); void PARTICLE_OT_dupliob_copy(struct wmOperatorType *ot); void PARTICLE_OT_dupliob_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c index 37cf95e5c2d..8f32b0dffe9 100644 --- a/source/blender/editors/physics/physics_ops.c +++ b/source/blender/editors/physics/physics_ops.c @@ -64,6 +64,8 @@ static void operatortypes_particle(void) WM_operatortype_append(PARTICLE_OT_brush_edit); + WM_operatortype_append(PARTICLE_OT_shape_cut); + WM_operatortype_append(PARTICLE_OT_particle_edit_toggle); WM_operatortype_append(PARTICLE_OT_edited_clear); @@ -78,6 +80,7 @@ static void operatortypes_particle(void) WM_operatortype_append(PARTICLE_OT_target_move_down); WM_operatortype_append(PARTICLE_OT_connect_hair); WM_operatortype_append(PARTICLE_OT_disconnect_hair); + WM_operatortype_append(PARTICLE_OT_copy_particle_systems); WM_operatortype_append(PARTICLE_OT_dupliob_copy); WM_operatortype_append(PARTICLE_OT_dupliob_remove); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 8024add41cf..fc6ff4eb0c0 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -315,8 +315,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender) /* rr->rectf is now filled with image data */ - if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) - BKE_stamp_buf(scene, camera, rect, rr->rectf, rr->rectx, rr->recty, 4); + if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) { + BKE_image_stamp_buf(scene, camera, rect, rr->rectf, rr->rectx, rr->recty, 4); + } RE_ReleaseResult(oglrender->re); @@ -335,8 +336,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender) IMB_color_to_bw(ibuf); } - BKE_makepicstring(name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); + BKE_image_path_from_imformat( + name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); ok = BKE_imbuf_write_as(ibuf, name, &scene->r.im_format, true); /* no need to stamp here */ if (ok) printf("OpenGL Render written to '%s'\n", name); else printf("OpenGL Render failed to write '%s'\n", name); @@ -564,8 +566,9 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype); if (!is_movie) { - BKE_makepicstring(name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); + BKE_image_path_from_imformat( + name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) { BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index ea80a07fdd4..99edaff759e 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -359,7 +359,7 @@ static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPre for (base = sce->base.first; base; base = base->next) { if (base->object->type == OB_LAMP) { /* if doesn't match 'Lamp.002' --> main key light */ - if (strcmp(base->object->id.name + 2, "Lamp.002") != 0) { + if (!STREQ(base->object->id.name + 2, "Lamp.002")) { if (mat->material_type == MA_TYPE_VOLUME) base->object->restrictflag |= OB_RESTRICT_RENDER; else @@ -1100,7 +1100,7 @@ static void icon_preview_free(void *customdata) void ED_preview_icon_render(Scene *scene, ID *id, unsigned int *rect, int sizex, int sizey) { - IconPreview ip = {0}; + IconPreview ip = {NULL}; short stop = false, update = false; float progress = 0.0f; diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 91c689373be..94a3defa95a 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -1412,7 +1412,7 @@ static int envmap_save_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "filepath", path); if (scene->r.scemode & R_EXTENSION) { - BKE_add_image_extension(path, &scene->r.im_format); + BKE_image_path_ensure_ext_from_imformat(path, &scene->r.im_format); } WM_cursor_wait(1); diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index ab28f5fa675..fe357a7a0e2 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -161,11 +161,19 @@ ScrArea *render_view_open(bContext *C, int mx, int my) } else if (scene->r.displaymode == R_OUTPUT_SCREEN) { sa = CTX_wm_area(C); - if (sa && sa->spacetype == SPACE_IMAGE) - area_was_image = true; - /* this function returns with changed context */ - sa = ED_screen_full_newspace(C, sa, SPACE_IMAGE); + /* if the active screen is already in fullscreen mode, skip this and + * unset the area, so that the fullscreen area is just changed later */ + if (sa && sa->full) { + sa = NULL; + } + else { + if (sa && sa->spacetype == SPACE_IMAGE) + area_was_image = true; + + /* this function returns with changed context */ + sa = ED_screen_full_newspace(C, sa, SPACE_IMAGE); + } } if (!sa) { @@ -186,10 +194,15 @@ ScrArea *render_view_open(bContext *C, int mx, int my) /* makes ESC go back to prev space */ sima->flag |= SI_PREVSPACE; + + /* we already had a fullscreen here -> mark new space as a stacked fullscreen */ + if (sa->full) { + sa->flag |= AREA_FLAG_STACKED_FULLSCREEN; + } } else { /* use any area of decent size */ - sa = BKE_screen_find_big_area(CTX_wm_screen(C), -1, 0); + sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TYPE_ANY, 0); if (sa->spacetype != SPACE_IMAGE) { // XXX newspace(sa, SPACE_IMAGE); sima = sa->spacedata.first; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 71714bdcda9..83b22bb1a8a 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1645,6 +1645,8 @@ void ED_area_prevspace(bContext *C, ScrArea *sa) /* no change */ return; } + sa->flag &= ~AREA_FLAG_STACKED_FULLSCREEN; + ED_area_tag_redraw(sa); /* send space change notifier */ @@ -2161,4 +2163,3 @@ void ED_region_cache_draw_cached_segments(const ARegion *ar, const int num_segme } } } - diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 134feb59d55..4c1698bd1d4 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -1136,9 +1136,9 @@ void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int void cpack(unsigned int x) { - glColor3ub( ( (x) & 0xFF), - (((x) >> 8) & 0xFF), - (((x) >> 16) & 0xFF) ); + glColor3ub(( (x) & 0xFF), + (((x) >> 8) & 0xFF), + (((x) >> 16) & 0xFF)); } void glaDrawBorderCorners(const rcti *border, float zoomx, float zoomy) diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index ce9c608247b..3431ce9f50a 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -497,7 +497,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPDstroke *gps; for (gps = gpf->strokes.first; gps; gps = gps->next) { - CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); + if (ED_gpencil_stroke_can_use_direct(sa, gps)) { + CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); + } } } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 3972d00293c..f338fa160f5 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1762,15 +1762,16 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) void ED_screen_full_prevspace(bContext *C, ScrArea *sa) { - wmWindow *win = CTX_wm_window(C); - - ED_area_prevspace(C, sa); - - if (sa->full) - ED_screen_state_toggle(C, win, sa, SCREENMAXIMIZED); + if (sa->flag & AREA_FLAG_STACKED_FULLSCREEN) { + /* stacked fullscreen -> only go back to previous screen and don't toggle out of fullscreen */ + ED_area_prevspace(C, sa); + } + else { + ED_screen_restore_temp_type(C, sa); + } } -void ED_screen_retore_temp_type(bContext *C, ScrArea *sa, bool is_screen_change) +void ED_screen_restore_temp_type(bContext *C, ScrArea *sa) { /* incase nether functions below run */ ED_area_tag_redraw(sa); @@ -1780,7 +1781,7 @@ void ED_screen_retore_temp_type(bContext *C, ScrArea *sa, bool is_screen_change) sa->flag &= ~AREA_FLAG_TEMP_TYPE; } - if (is_screen_change && sa->full) { + if (sa->full) { ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED); } } @@ -1793,25 +1794,11 @@ void ED_screen_full_restore(bContext *C, ScrArea *sa) bScreen *screen = CTX_wm_screen(C); short state = (screen ? screen->state : SCREENMAXIMIZED); - /* if fullscreen area has a secondary space (such as a file browser or fullscreen render - * overlaid on top of a existing setup) then return to the previous space */ + /* if fullscreen area has a temporary space (such as a file browser or fullscreen render + * overlaid on top of an existing setup) then return to the previous space */ if (sl->next) { - /* specific checks for space types */ - - /* Special check added for non-render image window (back from fullscreen through "Back to Previous" button) */ - if (sl->spacetype == SPACE_IMAGE) { - SpaceImage *sima = sa->spacedata.first; - - if (sima->flag & (SI_PREVSPACE | SI_FULLWINDOW)) { - sima->flag &= ~SI_PREVSPACE; - sima->flag &= ~SI_FULLWINDOW; - ED_screen_full_prevspace(C, sa); - } - else - ED_screen_state_toggle(C, win, sa, state); - } - else if (sa->flag & AREA_FLAG_TEMP_TYPE) { + if (sa->flag & AREA_FLAG_TEMP_TYPE) { ED_screen_full_prevspace(C, sa); } else { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 39321ec0770..ad3d2d1a21a 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -598,19 +598,6 @@ typedef struct sActionzoneData { int x, y, gesture_dir, modifier; } sActionzoneData; -/* used by other operators too */ -static ScrArea *screen_areahascursor(bScreen *scr, int x, int y) -{ - ScrArea *sa = NULL; - sa = scr->areabase.first; - while (sa) { - if (BLI_rcti_isect_pt(&sa->totrct, x, y)) break; - sa = sa->next; - } - - return sa; -} - /* quick poll to save operators to be created and handled */ static int actionzone_area_poll(bContext *C) { @@ -808,7 +795,7 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* gesture is large enough? */ if (is_gesture) { /* second area, for join when (sa1 != sa2) */ - sad->sa2 = screen_areahascursor(sc, event->x, event->y); + sad->sa2 = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y); /* apply sends event */ actionzone_apply(C, op, sad->az->type); actionzone_exit(op); @@ -929,7 +916,7 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: /* second area, for join */ - sad->sa2 = screen_areahascursor(CTX_wm_screen(C), event->x, event->y); + sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y); break; case LEFTMOUSE: /* release LMB */ if (event->val == KM_RELEASE) { @@ -1679,7 +1666,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V); ED_area_tag_redraw(sd->sarea); } - sd->sarea = screen_areahascursor(CTX_wm_screen(C), event->x, event->y); /* area context not set */ + /* area context not set */ + sd->sarea = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y); if (sd->sarea) { ED_area_tag_redraw(sd->sarea); @@ -2482,8 +2470,8 @@ static int area_join_init(bContext *C, wmOperator *op) x2 = RNA_int_get(op->ptr, "max_x"); y2 = RNA_int_get(op->ptr, "max_y"); - sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1); - sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2); + sa1 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, x1, y1); + sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, x2, y2); if (sa1 == NULL || sa2 == NULL || sa1 == sa2) return 0; @@ -2616,7 +2604,7 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) case MOUSEMOVE: { - ScrArea *sa = screen_areahascursor(sc, event->x, event->y); + ScrArea *sa = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y); int dir; if (sa) { @@ -3399,7 +3387,12 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv (sad->flag & ANIMPLAY_FLAG_REVERSE) == false && finite(time = sound_sync_scene(scene))) { - scene->r.cfra = (double)time * FPS + 0.5; + double newfra = (double)time * FPS; + /* give some space here to avoid jumps */ + if (newfra + 0.5 > scene->r.cfra && newfra - 0.5 < scene->r.cfra) + scene->r.cfra++; + else + scene->r.cfra = newfra + 0.5; } else { if (sync) { @@ -3476,11 +3469,29 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv for (sa = window->screen->areabase.first; sa; sa = sa->next) { ARegion *ar; for (ar = sa->regionbase.first; ar; ar = ar->next) { + bool redraw = false; if (ar == sad->ar) { - ED_region_tag_redraw(ar); + redraw = true; } else if (match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws)) { + redraw = true; + } + + if (redraw) { ED_region_tag_redraw(ar); + /* do follow here if editor type supports it */ + if ((sad->redraws & TIME_FOLLOW)) { + if ((ar->regiontype == RGN_TYPE_WINDOW && + ELEM (sa->spacetype, SPACE_SEQ, SPACE_TIME, SPACE_IPO, SPACE_ACTION, SPACE_NLA)) || + (sa->spacetype == SPACE_CLIP && ar->regiontype == RGN_TYPE_PREVIEW)) + { + float w = BLI_rctf_size_x(&ar->v2d.cur); + if (scene->r.cfra < ar->v2d.cur.xmin || scene->r.cfra > (ar->v2d.cur.xmax)) { + ar->v2d.cur.xmin = scene->r.cfra; + ar->v2d.cur.xmax = ar->v2d.cur.xmin + w; + } + } + } } } @@ -3546,6 +3557,8 @@ int ED_screen_animation_play(bContext *C, int sync, int mode) /* stop playback now */ ED_screen_animation_timer(C, 0, 0, 0, 0); sound_stop_scene(scene); + + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } else { int refresh = SPACE_TIME; /* these settings are currently only available from a menu in the TimeLine */ @@ -3707,9 +3720,9 @@ static int fullscreen_back_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "No fullscreen areas were found"); return OPERATOR_CANCELLED; } - - ED_screen_full_restore(C, sa); - + + ED_screen_full_prevspace(C, sa); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index e7f256e414a..9c05f1d4780 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -377,8 +377,9 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float char name[FILE_MAX]; int ok; - BKE_makepicstring(name, rd.pic, sj->bmain->name, rd.cfra, - &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true); + BKE_image_path_from_imformat( + name, rd.pic, sj->bmain->name, rd.cfra, + &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true); ibuf->rect = sj->dumprect; ok = BKE_imbuf_write(ibuf, name, &rd.im_format); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 430ea1cc11d..2d425b7e5a0 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -652,16 +652,12 @@ static void paint_draw_tex_overlay(UnifiedPaintSettings *ups, Brush *brush, } /* set quad color. Colored overlay does not get blending */ - if (col) - glColor4f(1.0, - 1.0, - 1.0, - overlay_alpha / 100.0f); - else - glColor4f(U.sculpt_paint_overlay_col[0], - U.sculpt_paint_overlay_col[1], - U.sculpt_paint_overlay_col[2], - overlay_alpha / 100.0f); + if (col) { + glColor4f(1.0, 1.0, 1.0, overlay_alpha / 100.0f); + } + else { + glColor4f(UNPACK3(U.sculpt_paint_overlay_col), overlay_alpha / 100.0f); + } /* draw textured quad */ glBegin(GL_QUADS); diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index 1f5ee708ad0..439c2a639bd 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -108,7 +108,7 @@ static void paintcurve_undo_restore(bContext *C, ListBase *lb) uc = (UndoCurve *)lb->first; - if (strncmp(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname))) == 0) { + if (STREQLEN(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname)))) { SWAP(PaintCurvePoint *, pc->points, uc->points); SWAP(int, pc->tot_points, uc->tot_points); SWAP(int, pc->add_index, uc->active_point); diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index d52b17372f7..5cfbd164153 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -158,9 +158,10 @@ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, Cop } } else { - if (mode == RESTORE_COPY) + if (mode == RESTORE_COPY) { IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + } /* swap to the tmpbuf for easy copying */ if (ibuf->rect_float) { SWAP(float *, tmpibuf->rect_float, tile->rect.fp); @@ -192,7 +193,7 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi for (tile = lb->first; tile; tile = tile->next) { if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) { if (tile->use_float == use_float) { - if (strcmp(tile->idname, ima->id.name) == 0 && strcmp(tile->ibufname, ibuf->name) == 0) { + if (STREQ(tile->idname, ima->id.name) && STREQ(tile->ibufname, ibuf->name)) { if (mask) { /* allocate mask if requested */ if (!tile->mask) { @@ -327,7 +328,7 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) short use_float; /* find image based on name, pointer becomes invalid with global undo */ - if (ima && strcmp(tile->idname, ima->id.name) == 0) { + if (ima && STREQ(tile->idname, ima->id.name)) { /* ima is valid */ } else { @@ -336,7 +337,7 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - if (ima && ibuf && strcmp(tile->ibufname, ibuf->name) != 0) { + if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) { /* current ImBuf filename was changed, probably current frame * was changed when painting on image sequence, rather than storing * full image user (which isn't so obvious, btw) try to find ImBuf with diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 3e675012d05..098477ed2a1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -203,6 +203,7 @@ typedef struct ProjPaintState { /* the paint color. It can change depending of inverted mode or not */ float paint_color[3]; float paint_color_linear[3]; + float dither; Brush *brush; short tool, blend, mode; @@ -422,17 +423,19 @@ static int project_bucket_offset_safe(const ProjPaintState *ps, const float proj } } -static float VecZDepthOrtho(const float pt[2], - const float v1[3], const float v2[3], const float v3[3], - float w[3]) +static float VecZDepthOrtho( + const float pt[2], + const float v1[3], const float v2[3], const float v3[3], + float w[3]) { barycentric_weights_v2(v1, v2, v3, pt, w); return (v1[2] * w[0]) + (v2[2] * w[1]) + (v3[2] * w[2]); } -static float VecZDepthPersp(const float pt[2], - const float v1[4], const float v2[4], const float v3[4], - float w[3]) +static float VecZDepthPersp( + const float pt[2], + const float v1[4], const float v2[4], const float v3[4], + float w[3]) { float wtot_inv, wtot; float w_tmp[3]; @@ -542,8 +545,9 @@ static void uvco_to_wrapped_pxco(const float uv[2], int ibuf_x, int ibuf_y, floa } /* Set the top-most face color that the screen space coord 'pt' touches (or return 0 if none touch) */ -static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], - float *rgba_fp, unsigned char *rgba, const bool interp) +static bool project_paint_PickColor( + const ProjPaintState *ps, const float pt[2], + float *rgba_fp, unsigned char *rgba, const bool interp) { float w[3], uv[2]; int side; @@ -637,9 +641,10 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], * 1 : occluded * 2 : occluded with w[3] weights set (need to know in some cases) */ -static int project_paint_occlude_ptv(const float pt[3], - const float v1[4], const float v2[4], const float v3[4], - float w[3], const bool is_ortho) +static int project_paint_occlude_ptv( + const float pt[3], + const float v1[4], const float v2[4], const float v3[4], + float w[3], const bool is_ortho) { /* if all are behind us, return false */ if (v1[2] > pt[2] && v2[2] > pt[2] && v3[2] > pt[2]) @@ -669,9 +674,10 @@ static int project_paint_occlude_ptv(const float pt[3], } -static int project_paint_occlude_ptv_clip(const ProjPaintState *ps, const MFace *mf, - const float pt[3], const float v1[4], const float v2[4], const float v3[4], - const int side) +static int project_paint_occlude_ptv_clip( + const ProjPaintState *ps, const MFace *mf, + const float pt[3], const float v1[4], const float v2[4], const float v3[4], + const int side) { float w[3], wco[3]; int ret = project_paint_occlude_ptv(pt, v1, v2, v3, w, ps->is_ortho); @@ -699,8 +705,9 @@ static int project_paint_occlude_ptv_clip(const ProjPaintState *ps, const MFace /* Check if a screenspace location is occluded by any other faces * check, pixelScreenCo must be in screenspace, its Z-Depth only needs to be used for comparison * and doesn't need to be correct in relation to X and Y coords (this is the case in perspective view) */ -static bool project_bucket_point_occluded(const ProjPaintState *ps, LinkNode *bucketFace, - const int orig_face, const float pixelScreenCo[4]) +static bool project_bucket_point_occluded( + const ProjPaintState *ps, LinkNode *bucketFace, + const int orig_face, const float pixelScreenCo[4]) { MFace *mf; int face_index; @@ -912,9 +919,10 @@ static void project_face_winding_init(const ProjPaintState *ps, const int face_i /* This function returns 1 if this face has a seam along the 2 face-vert indices * 'orig_i1_fidx' and 'orig_i2_fidx' */ -static bool check_seam(const ProjPaintState *ps, - const int orig_face, const int orig_i1_fidx, const int orig_i2_fidx, - int *other_face, int *orig_fidx) +static bool check_seam( + const ProjPaintState *ps, + const int orig_face, const int orig_i1_fidx, const int orig_i2_fidx, + int *other_face, int *orig_fidx) { LinkNode *node; int face_index; @@ -1003,8 +1011,9 @@ BLI_INLINE float shell_v2v2_normal_dir_to_dist(float n[2], float d[2]) /* Calculate outset UV's, this is not the same as simply scaling the UVs, * since the outset coords are a margin that keep an even distance from the original UV's, * note that the image aspect is taken into account */ -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 is_quad, const bool cw) +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 is_quad, const bool cw) { float a1, a2, a3, a4 = 0.0f; float puv[4][2]; /* pixelspace uv's */ @@ -1212,8 +1221,9 @@ static void screen_px_from_persp( } -static void project_face_pixel(const MTFace *tf_other, ImBuf *ibuf_other, const float w[3], - int side, unsigned char rgba_ub[4], float rgba_f[4]) +static void project_face_pixel( + const MTFace *tf_other, ImBuf *ibuf_other, const float w[3], + int side, unsigned char rgba_ub[4], float rgba_f[4]) { const float *uvCo1, *uvCo2, *uvCo3; float uv_other[2], x, y; @@ -1454,7 +1464,7 @@ static ProjPixel *project_paint_uvpixel_init( y_px = mod_i(y_px, ibuf->y); BLI_assert(ps->pixel_sizeof == project_paint_pixel_sizeof(ps->tool)); - projPixel = (ProjPixel *)BLI_memarena_alloc(arena, ps->pixel_sizeof); + projPixel = BLI_memarena_alloc(arena, ps->pixel_sizeof); /* calculate the undo tile offset of the pixel, used to store the original * pixel color and accumulated mask if any */ @@ -1585,7 +1595,7 @@ static ProjPixel *project_paint_uvpixel_init( } static bool line_clip_rect2f( - rctf *rect, + const rctf *rect, const float l1[2], const float l2[2], float l1_clip[2], float l2_clip[2]) { @@ -1800,7 +1810,7 @@ static float len_squared_v2v2_alt(const float v1[2], const float v2_1, const flo /* note, use a squared value so we can use len_squared_v2v2 * be sure that you have done a bounds check first or this may fail */ /* only give bucket_bounds as an arg because we need it elsewhere */ -static bool project_bucket_isect_circle(const float cent[2], const float radius_squared, rctf *bucket_bounds) +static bool project_bucket_isect_circle(const float cent[2], const float radius_squared, const rctf *bucket_bounds) { /* Would normally to a simple intersection test, however we know the bounds of these 2 already intersect @@ -1855,7 +1865,7 @@ static bool project_bucket_isect_circle(const float cent[2], const float radius_ * however switching back to this for ortho is always an option */ static void rect_to_uvspace_ortho( - rctf *bucket_bounds, + const rctf *bucket_bounds, const float *v1coSS, const float *v2coSS, const float *v3coSS, const float *uv1co, const float *uv2co, const float *uv3co, float bucket_bounds_uv[4][2], @@ -1888,7 +1898,7 @@ static void rect_to_uvspace_ortho( /* same as above but use barycentric_weights_v2_persp */ static void rect_to_uvspace_persp( - rctf *bucket_bounds, + const rctf *bucket_bounds, const float *v1coSS, const float *v2coSS, const float *v3coSS, const float *uv1co, const float *uv2co, const float *uv3co, float bucket_bounds_uv[4][2], @@ -1964,7 +1974,7 @@ static int float_z_sort(const void *p1, const void *p2) /* assumes one point is within the rectangle */ static void line_rect_clip( - rctf *rect, + const rctf *rect, const float l1[4], const float l2[4], const float uv1[2], const float uv2[2], float uv[2], bool is_ortho) @@ -1989,14 +1999,14 @@ static void line_rect_clip( tmp = (is_ortho) ? 1.0f : (l1[3] + min * (l2[3] - l1[3])); - uv[0] = (uv1[0] + min * (uv2[0] - uv1[0])) / tmp; - uv[1] = (uv1[1] + min * (uv2[1] - uv1[1])) / tmp; + uv[0] = (uv1[0] + min / tmp * (uv2[0] - uv1[0])); + uv[1] = (uv1[1] + min / tmp * (uv2[1] - uv1[1])); } static void project_bucket_clip_face( const bool is_ortho, - rctf *bucket_bounds, + const rctf *bucket_bounds, float *v1coSS, float *v2coSS, float *v3coSS, const float *uv1co, const float *uv2co, const float *uv3co, float bucket_bounds_uv[8][2], @@ -2004,7 +2014,8 @@ static void project_bucket_clip_face( { int inside_bucket_flag = 0; int inside_face_flag = 0; - const int flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); + const int flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != + (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); bool colinear = false; float bucket_bounds_ss[4][2]; @@ -2020,7 +2031,8 @@ static void project_bucket_clip_face( inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v3coSS) << 2; if (inside_bucket_flag == ISECT_ALL3) { - /* all screenspace points are inside the bucket bounding box, this means we don't need to clip and can simply return the UVs */ + /* all screenspace points are inside the bucket bounding box, + * this means we don't need to clip and can simply return the UVs */ if (flip) { /* facing the back? */ copy_v2_v2(bucket_bounds_uv[0], uv3co); copy_v2_v2(bucket_bounds_uv[1], uv2co); @@ -2076,8 +2088,8 @@ static void project_bucket_clip_face( /* at this point we have all uv points needed in a row. all that's needed is to invert them if necessary */ if (flip) { /* flip only to the middle of the array */ - int i, max = *tot / 2; - for (i = 0; i < max; i++) { + int i, max = *tot - 1, mid = *tot / 2; + for (i = 0; i < mid; i++) { SWAP(float, bucket_bounds_uv[i][0], bucket_bounds_uv[max - i][0]); SWAP(float, bucket_bounds_uv[i][1], bucket_bounds_uv[max - i][1]); } @@ -2205,7 +2217,8 @@ static void project_bucket_clip_face( for (i = 0; i < (*tot); i++) { v2_clipSS[0] = isectVCosSS[i][0] - cent[0]; v2_clipSS[1] = isectVCosSS[i][1] - cent[1]; - isectVCosSS[i][2] = atan2f(v1_clipSS[0] * v2_clipSS[1] - v1_clipSS[1] * v2_clipSS[0], v1_clipSS[0] * v2_clipSS[0] + v1_clipSS[1] * v2_clipSS[1]); + isectVCosSS[i][2] = atan2f(v1_clipSS[0] * v2_clipSS[1] - v1_clipSS[1] * v2_clipSS[0], + v1_clipSS[0] * v2_clipSS[0] + v1_clipSS[1] * v2_clipSS[1]); } if (flip) qsort(isectVCosSS, *tot, sizeof(float) * 3, float_z_sort_flip); @@ -2260,7 +2273,9 @@ static void project_bucket_clip_face( int i; if (is_ortho) rect_to_uvspace_ortho(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip); else rect_to_uvspace_persp(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip); - printf("( [(%f,%f), (%f,%f), (%f,%f), (%f,%f)], ", test_uv[0][0], test_uv[0][1], test_uv[1][0], test_uv[1][1], test_uv[2][0], test_uv[2][1], test_uv[3][0], test_uv[3][1]); + printf("( [(%f,%f), (%f,%f), (%f,%f), (%f,%f)], ", + test_uv[0][0], test_uv[0][1], test_uv[1][0], test_uv[1][1], + test_uv[2][0], test_uv[2][1], test_uv[3][0], test_uv[3][1]); printf(" [(%f,%f), (%f,%f), (%f,%f)], ", uv1co[0], uv1co[1], uv2co[0], uv2co[1], uv3co[0], uv3co[1]); @@ -2358,9 +2373,15 @@ static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot return 1; } -/* One of the most important function for projection painting, since it selects the pixels to be added into each bucket. - * initialize pixels from this face where it intersects with the bucket_index, optionally initialize pixels for removing seams */ -static void project_paint_face_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const int face_index, const int image_index, rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf, const short clamp_u, const short clamp_v) +/* One of the most important function for projection painting, + * since it selects the pixels to be added into each bucket. + * + * initialize pixels from this face where it intersects with the bucket_index, + * optionally initialize pixels for removing seams */ +static void project_paint_face_init( + const ProjPaintState *ps, + const int thread_index, const int bucket_index, const int face_index, const int image_index, + const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf, const short clamp_u, const short clamp_v) { /* Projection vars, to get the 3D locations into screen space */ MemArena *arena = ps->arena_mt[thread_index]; @@ -2824,7 +2845,7 @@ static void project_bucket_bounds(const ProjPaintState *ps, const int bucket_x, /* Fill this bucket with pixels from the faces that intersect it. * * have bucket_bounds as an argument so we don't need to give bucket_x/y the rect function needs */ -static void project_bucket_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, rctf *bucket_bounds) +static void project_bucket_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const rctf *bucket_bounds) { LinkNode *node; int face_index, image_index = 0; @@ -3000,145 +3021,9 @@ static void project_paint_delayed_face_init(ProjPaintState *ps, const MFace *mf, #endif } -/* run once per stroke before projection painting */ -static void project_paint_begin(ProjPaintState *ps) +/* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ +static void proj_paint_state_non_cddm_init(ProjPaintState *ps) { - /* Viewport vars */ - float mat[3][3]; - - float no[3]; - - float *projScreenCo; /* Note, we could have 4D vectors are only needed for */ - float projMargin; - - /* Image Vars - keep track of images we have used */ - LinkNode *image_LinkList = NULL; - LinkNode *node; - - ProjPaintImage *projIma; - Image *tpage_last = NULL, *tpage; - TexPaintSlot *slot_last = NULL, *slot = NULL; - TexPaintSlot *slot_last_clone = NULL, *slot_clone; - - /* Face vars */ - MPoly *mpoly_orig; - MFace *mf; - MTFace **tf; - MTFace *tf_base; - - MTFace **tf_clone; - MTFace *tf_clone_base = NULL; - - int a, i; /* generic looping vars */ - int image_index = -1, face_index; - - /* double lookup */ - const int *index_mf_to_mpoly = NULL; - const int *index_mp_to_orig = NULL; - - MVert *mv; - - MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ - - const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); - - bool reset_threads = false; - - /* ---- end defines ---- */ - - if (ps->source == PROJ_SRC_VIEW) - ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); /* faster clipping lookups */ - - ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); - - /* paint onto the derived mesh */ - - /* Workaround for subsurf selection, try the display mesh first */ - if (ps->source == PROJ_SRC_IMAGE_CAM) { - /* using render mesh, assume only camera was rendered from */ - ps->dm = mesh_create_derived_render(ps->scene, ps->ob, ps->scene->customdata_mask | CD_MASK_MTFACE); - ps->dm_release = true; - } - else if (ps->ob->derivedFinal && - CustomData_has_layer(&ps->ob->derivedFinal->faceData, CD_MTFACE) && - (ps->do_face_sel == false || CustomData_has_layer(&ps->ob->derivedFinal->polyData, CD_ORIGINDEX))) - { - ps->dm = ps->ob->derivedFinal; - ps->dm_release = false; - } - else { - ps->dm = mesh_get_derived_final( - ps->scene, ps->ob, - ps->scene->customdata_mask | CD_MASK_MTFACE | (ps->do_face_sel ? CD_ORIGINDEX : 0)); - ps->dm_release = true; - } - - if (!CustomData_has_layer(&ps->dm->faceData, CD_MTFACE)) { - - if (ps->dm_release) - ps->dm->release(ps->dm); - - ps->dm = NULL; - return; - } - - DM_update_materials(ps->dm, ps->ob); - - ps->dm_totvert = ps->dm->getNumVerts(ps->dm); - ps->dm_totface = ps->dm->getNumTessFaces(ps->dm); - - ps->dm_mvert = ps->dm->getVertArray(ps->dm); - ps->dm_mface = ps->dm->getTessFaceArray(ps->dm); - ps->dm_mtface = MEM_mallocN(ps->dm_totface * sizeof(MTFace *), "proj_paint_mtfaces"); - - if (ps->do_face_sel) { - index_mf_to_mpoly = ps->dm->getTessFaceDataArray(ps->dm, CD_ORIGINDEX); - index_mp_to_orig = ps->dm->getPolyDataArray(ps->dm, CD_ORIGINDEX); - if (index_mf_to_mpoly == NULL) { - index_mp_to_orig = NULL; - } - else { - mpoly_orig = ((Mesh *)ps->ob->data)->mpoly; - } - } - else { - mpoly_orig = NULL; - } - - /* use clone mtface? */ - if (ps->do_layer_clone) { - ps->dm_mtface_clone = MEM_mallocN(ps->dm_totface * sizeof(MTFace *), "proj_paint_mtfaces"); - } - - if (ps->do_layer_stencil || ps->do_stencil_brush) { - //int layer_num = CustomData_get_stencil_layer(&ps->dm->faceData, CD_MTFACE); - int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); - if (layer_num != -1) - ps->dm_mtface_stencil = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); - - if (ps->dm_mtface_stencil == NULL) { - /* get active instead */ - ps->dm_mtface_stencil = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); - } - - if (ps->do_stencil_brush) - tf_base = ps->dm_mtface_stencil; - } - - if (ps->do_layer_clone) { - int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); - - if (layer_num != -1) - tf_clone_base = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); - - if (tf_clone_base == NULL) { - /* get active instead */ - tf_clone_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); - } - - } - - /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ if (ps->dm->type != DM_TYPE_CDDM) { ps->dm_mvert = MEM_dupallocN(ps->dm_mvert); ps->dm_mface = MEM_dupallocN(ps->dm_mface); @@ -3149,97 +3034,108 @@ static void project_paint_begin(ProjPaintState *ps) ps->dm_mtface_stencil = MEM_dupallocN(ps->dm_mtface_stencil); #endif } +} + +static void proj_paint_state_viewport_init(ProjPaintState *ps) +{ + float mat[3][3]; + float viewmat[4][4]; + float viewinv[4][4]; ps->viewDir[0] = 0.0f; ps->viewDir[1] = 0.0f; ps->viewDir[2] = 1.0f; - { - float viewmat[4][4]; - float viewinv[4][4]; + invert_m4_m4(ps->ob->imat, ps->ob->obmat); - invert_m4_m4(ps->ob->imat, ps->ob->obmat); + if (ELEM(ps->source, PROJ_SRC_VIEW, PROJ_SRC_VIEW_FILL)) { + /* normal drawing */ + ps->winx = ps->ar->winx; + ps->winy = ps->ar->winy; - if (ELEM(ps->source, PROJ_SRC_VIEW, PROJ_SRC_VIEW_FILL)) { - /* normal drawing */ - ps->winx = ps->ar->winx; - ps->winy = ps->ar->winy; + copy_m4_m4(viewmat, ps->rv3d->viewmat); + copy_m4_m4(viewinv, ps->rv3d->viewinv); - copy_m4_m4(viewmat, ps->rv3d->viewmat); - copy_m4_m4(viewinv, ps->rv3d->viewinv); + ED_view3d_ob_project_mat_get(ps->rv3d, ps->ob, ps->projectMat); - ED_view3d_ob_project_mat_get(ps->rv3d, ps->ob, ps->projectMat); + ps->is_ortho = ED_view3d_clip_range_get(ps->v3d, ps->rv3d, &ps->clipsta, &ps->clipend, true); + } + else { + /* re-projection */ + float winmat[4][4]; + float vmat[4][4]; - ps->is_ortho = ED_view3d_clip_range_get(ps->v3d, ps->rv3d, &ps->clipsta, &ps->clipend, true); - } - else { - /* re-projection */ - float winmat[4][4]; - float vmat[4][4]; + ps->winx = ps->reproject_ibuf->x; + ps->winy = ps->reproject_ibuf->y; - ps->winx = ps->reproject_ibuf->x; - ps->winy = ps->reproject_ibuf->y; + if (ps->source == PROJ_SRC_IMAGE_VIEW) { + /* image stores camera data, tricky */ + IDProperty *idgroup = IDP_GetProperties(&ps->reproject_image->id, 0); + IDProperty *view_data = IDP_GetPropertyFromGroup(idgroup, PROJ_VIEW_DATA_ID); - if (ps->source == PROJ_SRC_IMAGE_VIEW) { - /* image stores camera data, tricky */ - IDProperty *idgroup = IDP_GetProperties(&ps->reproject_image->id, 0); - IDProperty *view_data = IDP_GetPropertyFromGroup(idgroup, PROJ_VIEW_DATA_ID); + const float *array = (float *)IDP_Array(view_data); - const float *array = (float *)IDP_Array(view_data); + /* use image array, written when creating image */ + memcpy(winmat, array, sizeof(winmat)); array += sizeof(winmat) / sizeof(float); + memcpy(viewmat, array, sizeof(viewmat)); array += sizeof(viewmat) / sizeof(float); + ps->clipsta = array[0]; + ps->clipend = array[1]; + ps->is_ortho = array[2] ? 1 : 0; - /* use image array, written when creating image */ - memcpy(winmat, array, sizeof(winmat)); array += sizeof(winmat) / sizeof(float); - memcpy(viewmat, array, sizeof(viewmat)); array += sizeof(viewmat) / sizeof(float); - ps->clipsta = array[0]; - ps->clipend = array[1]; - ps->is_ortho = array[2] ? 1 : 0; + invert_m4_m4(viewinv, viewmat); + } + else if (ps->source == PROJ_SRC_IMAGE_CAM) { + Object *cam_ob = ps->scene->camera; + CameraParams params; - invert_m4_m4(viewinv, viewmat); - } - else if (ps->source == PROJ_SRC_IMAGE_CAM) { - Object *cam_ob = ps->scene->camera; - CameraParams params; - - /* viewmat & viewinv */ - copy_m4_m4(viewinv, cam_ob->obmat); - normalize_m4(viewinv); - invert_m4_m4(viewmat, viewinv); - - /* window matrix, clipping and ortho */ - BKE_camera_params_init(¶ms); - BKE_camera_params_from_object(¶ms, cam_ob); - BKE_camera_params_compute_viewplane(¶ms, ps->winx, ps->winy, 1.0f, 1.0f); - BKE_camera_params_compute_matrix(¶ms); - - copy_m4_m4(winmat, params.winmat); - ps->clipsta = params.clipsta; - ps->clipend = params.clipend; - ps->is_ortho = params.is_ortho; - } + /* viewmat & viewinv */ + copy_m4_m4(viewinv, cam_ob->obmat); + normalize_m4(viewinv); + invert_m4_m4(viewmat, viewinv); + + /* window matrix, clipping and ortho */ + BKE_camera_params_init(¶ms); + BKE_camera_params_from_object(¶ms, cam_ob); + BKE_camera_params_compute_viewplane(¶ms, ps->winx, ps->winy, 1.0f, 1.0f); + BKE_camera_params_compute_matrix(¶ms); - /* same as #ED_view3d_ob_project_mat_get */ - mul_m4_m4m4(vmat, viewmat, ps->ob->obmat); - mul_m4_m4m4(ps->projectMat, winmat, vmat); + copy_m4_m4(winmat, params.winmat); + ps->clipsta = params.clipsta; + ps->clipend = params.clipend; + ps->is_ortho = params.is_ortho; + } + else { + BLI_assert(0); } + /* same as #ED_view3d_ob_project_mat_get */ + mul_m4_m4m4(vmat, viewmat, ps->ob->obmat); + mul_m4_m4m4(ps->projectMat, winmat, vmat); + } - /* viewDir - object relative */ - invert_m4_m4(ps->ob->imat, ps->ob->obmat); - copy_m3_m4(mat, viewinv); - mul_m3_v3(mat, ps->viewDir); - copy_m3_m4(mat, ps->ob->imat); - mul_m3_v3(mat, ps->viewDir); - normalize_v3(ps->viewDir); - /* viewPos - object relative */ - copy_v3_v3(ps->viewPos, viewinv[3]); - copy_m3_m4(mat, ps->ob->imat); - mul_m3_v3(mat, ps->viewPos); - add_v3_v3(ps->viewPos, ps->ob->imat[3]); - } + /* viewDir - object relative */ + invert_m4_m4(ps->ob->imat, ps->ob->obmat); + copy_m3_m4(mat, viewinv); + mul_m3_v3(mat, ps->viewDir); + copy_m3_m4(mat, ps->ob->imat); + mul_m3_v3(mat, ps->viewDir); + normalize_v3(ps->viewDir); + + /* viewPos - object relative */ + copy_v3_v3(ps->viewPos, viewinv[3]); + copy_m3_m4(mat, ps->ob->imat); + mul_m3_v3(mat, ps->viewPos); + add_v3_v3(ps->viewPos, ps->ob->imat[3]); +} + +static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter) +{ + MVert *mv; + float *projScreenCo; + float projMargin; + int a; - /* calculate vert screen coords - * run this early so we can calculate the x/y resolution of our bucket rect */ INIT_MINMAX2(ps->screenMin, ps->screenMax); ps->screenCoords = MEM_mallocN(sizeof(float) * ps->dm_totvert * 4, "ProjectPaint ScreenVerts"); @@ -3305,38 +3201,24 @@ static void project_paint_begin(ProjPaintState *ps) ps->screenMin[1] = 0; ps->screenMax[1] = (float)(ps->winy); } +} - /* only for convenience */ - ps->screen_width = ps->screenMax[0] - ps->screenMin[0]; - ps->screen_height = ps->screenMax[1] - ps->screenMin[1]; - - ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); - ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); - - /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ - - if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { - reset_threads = true; - } - - /* really high values could cause problems since it has to allocate a few - * (ps->buckets_x*ps->buckets_y) sized arrays */ - CLAMP(ps->buckets_x, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); - CLAMP(ps->buckets_y, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); - - ps->bucketRect = (LinkNode **)MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketRect"); - ps->bucketFaces = (LinkNode **)MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); - - ps->bucketFlags = (unsigned char *)MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); #ifndef PROJ_DEBUG_NOSEAMBLEED +static void proj_paint_state_seam_bleed_init(ProjPaintState *ps) +{ if (ps->seam_bleed_px > 0.0f) { - ps->vertFaces = (LinkNode **)MEM_callocN(sizeof(LinkNode *) * ps->dm_totvert, "paint-vertFaces"); - ps->faceSeamFlags = (char *)MEM_callocN(sizeof(char) * ps->dm_totface, "paint-faceSeamFlags"); - ps->faceWindingFlags = (char *)MEM_callocN(sizeof(char) * ps->dm_totface, "paint-faceWindindFlags"); + ps->vertFaces = MEM_callocN(sizeof(LinkNode *) * ps->dm_totvert, "paint-vertFaces"); + ps->faceSeamFlags = MEM_callocN(sizeof(char) * ps->dm_totface, "paint-faceSeamFlags"); + ps->faceWindingFlags = MEM_callocN(sizeof(char) * ps->dm_totface, "paint-faceWindindFlags"); ps->faceSeamUVs = MEM_mallocN(sizeof(float) * ps->dm_totface * 8, "paint-faceSeamUVs"); } +} #endif +static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_threads) +{ + int a; + /* Thread stuff * * very small brushes run a lot slower multithreaded since the advantage with @@ -3360,11 +3242,15 @@ static void project_paint_begin(ProjPaintState *ps) for (a = 0; a < ps->thread_tot; a++) { ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena"); } +} - arena = ps->arena_mt[0]; - +static void proj_paint_state_vert_flags_init(ProjPaintState *ps) +{ if (ps->do_backfacecull && ps->do_mask_normal) { float viewDirPersp[3]; + MVert *mv; + float no[3]; + int a; ps->vertFlags = MEM_callocN(sizeof(char) * ps->dm_totvert, "paint-vertFlags"); @@ -3385,38 +3271,339 @@ static void project_paint_begin(ProjPaintState *ps) } } } - - for (face_index = 0, tf = ps->dm_mtface, mf = ps->dm_mface; face_index < ps->dm_totface; mf++, tf++, face_index++) { - bool is_face_sel; + else { + ps->vertFlags = NULL; + } +} #ifndef PROJ_DEBUG_NOSEAMBLEED - /* add face user if we have bleed enabled, set the UV seam flags later */ - /* annoying but we need to add all faces even ones we never use elsewhere */ - if (ps->seam_bleed_px > 0.0f) { - BLI_linklist_prepend_arena(&ps->vertFaces[mf->v1], SET_INT_IN_POINTER(face_index), arena); - BLI_linklist_prepend_arena(&ps->vertFaces[mf->v2], SET_INT_IN_POINTER(face_index), arena); - BLI_linklist_prepend_arena(&ps->vertFaces[mf->v3], SET_INT_IN_POINTER(face_index), arena); - if (mf->v4) { - BLI_linklist_prepend_arena(&ps->vertFaces[mf->v4], SET_INT_IN_POINTER(face_index), arena); - } +static void project_paint_bleed_add_face_user( + const ProjPaintState *ps, MemArena *arena, + const MFace *mf, const int face_index) +{ + /* add face user if we have bleed enabled, set the UV seam flags later */ + /* annoying but we need to add all faces even ones we never use elsewhere */ + if (ps->seam_bleed_px > 0.0f) { + BLI_linklist_prepend_arena(&ps->vertFaces[mf->v1], SET_INT_IN_POINTER(face_index), arena); + BLI_linklist_prepend_arena(&ps->vertFaces[mf->v2], SET_INT_IN_POINTER(face_index), arena); + BLI_linklist_prepend_arena(&ps->vertFaces[mf->v3], SET_INT_IN_POINTER(face_index), arena); + if (mf->v4) { + BLI_linklist_prepend_arena(&ps->vertFaces[mf->v4], SET_INT_IN_POINTER(face_index), arena); } + } +} #endif - if (ps->do_face_sel) { - int orig_index; - if (index_mp_to_orig && ((orig_index = DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, - face_index))) != ORIGINDEX_NONE) - { - MPoly *mp = &mpoly_orig[orig_index]; - is_face_sel = ((mp->flag & ME_FACE_SEL) != 0); +/* Return true if DM can be painted on, false otherwise */ +static bool proj_paint_state_dm_init(ProjPaintState *ps) +{ + /* Workaround for subsurf selection, try the display mesh first */ + if (ps->source == PROJ_SRC_IMAGE_CAM) { + /* using render mesh, assume only camera was rendered from */ + ps->dm = mesh_create_derived_render(ps->scene, ps->ob, ps->scene->customdata_mask | CD_MASK_MTFACE); + ps->dm_release = true; + } + else if (ps->ob->derivedFinal && + CustomData_has_layer(&ps->ob->derivedFinal->faceData, CD_MTFACE) && + (ps->do_face_sel == false || CustomData_has_layer(&ps->ob->derivedFinal->polyData, CD_ORIGINDEX))) + { + ps->dm = ps->ob->derivedFinal; + ps->dm_release = false; + } + else { + ps->dm = mesh_get_derived_final( + ps->scene, ps->ob, + ps->scene->customdata_mask | CD_MASK_MTFACE | (ps->do_face_sel ? CD_ORIGINDEX : 0)); + ps->dm_release = true; + } + + if (!CustomData_has_layer(&ps->dm->faceData, CD_MTFACE)) { + + if (ps->dm_release) + ps->dm->release(ps->dm); + + ps->dm = NULL; + return false; + } + + DM_update_materials(ps->dm, ps->ob); + + ps->dm_totvert = ps->dm->getNumVerts(ps->dm); + ps->dm_totface = ps->dm->getNumTessFaces(ps->dm); + + ps->dm_mvert = ps->dm->getVertArray(ps->dm); + ps->dm_mface = ps->dm->getTessFaceArray(ps->dm); + ps->dm_mtface = MEM_mallocN(ps->dm_totface * sizeof(MTFace *), "proj_paint_mtfaces"); + + return true; +} + +typedef struct { + MTFace *tf_clone_base; + MTFace **tf_clone; + TexPaintSlot *slot_last_clone; + TexPaintSlot *slot_clone; +} ProjPaintLayerClone; + +static void proj_paint_layer_clone_init( + ProjPaintState *ps, + ProjPaintLayerClone *layer_clone) +{ + MTFace *tf_clone_base = NULL; + + /* use clone mtface? */ + if (ps->do_layer_clone) { + const int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); + + ps->dm_mtface_clone = MEM_mallocN(ps->dm_totface * sizeof(MTFace *), "proj_paint_mtfaces"); + + if (layer_num != -1) + tf_clone_base = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); + + if (tf_clone_base == NULL) { + /* get active instead */ + tf_clone_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + } + + } + + memset(layer_clone, 0, sizeof(*layer_clone)); + layer_clone->tf_clone_base = tf_clone_base; +} + +/* Return true if face should be skipped, false otherwise */ +static bool project_paint_clone_face_skip( + ProjPaintState *ps, + ProjPaintLayerClone *lc, + const TexPaintSlot *slot, + const int face_index) +{ + if (ps->do_layer_clone) { + if (ps->do_material_slots) { + lc->slot_clone = project_paint_face_clone_slot(ps, face_index); + /* all faces should have a valid slot, reassert here */ + if (ELEM(lc->slot_clone, NULL, slot)) + return true; + } + else if (ps->clone_ima == ps->canvas_ima) + return true; + + lc->tf_clone = ps->dm_mtface_clone + face_index; + + if (ps->do_material_slots) { + if (lc->slot_clone != lc->slot_last_clone) { + if (!slot->uvname || + !(lc->tf_clone_base = CustomData_get_layer_named( + &ps->dm->faceData, CD_MTFACE, + lc->slot_clone->uvname))) + { + lc->tf_clone_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + } + lc->slot_last_clone = lc->slot_clone; } - else { - is_face_sel = ((mf->flag & ME_FACE_SEL) != 0); + } + + *lc->tf_clone = lc->tf_clone_base + face_index; + } + return false; +} + +typedef struct { + MPoly *mpoly_orig; + + /* double lookup */ + const int *index_mf_to_mpoly; + const int *index_mp_to_orig; +} ProjPaintFaceLookup; + +static void proj_paint_face_lookup_init( + const ProjPaintState *ps, + ProjPaintFaceLookup *face_lookup) +{ + memset(face_lookup, 0, sizeof(*face_lookup)); + if (ps->do_face_sel) { + face_lookup->index_mf_to_mpoly = ps->dm->getTessFaceDataArray(ps->dm, CD_ORIGINDEX); + face_lookup->index_mp_to_orig = ps->dm->getPolyDataArray(ps->dm, CD_ORIGINDEX); + if (face_lookup->index_mf_to_mpoly == NULL) { + face_lookup->index_mp_to_orig = NULL; + } + else { + face_lookup->mpoly_orig = ((Mesh *)ps->ob->data)->mpoly; + } + } +} + +/* Return true if face should be considered selected, false otherwise */ +static bool project_paint_check_face_sel( + const ProjPaintState *ps, + const ProjPaintFaceLookup *face_lookup, + const MFace *mf, const int face_index) +{ + if (ps->do_face_sel) { + int orig_index; + if (face_lookup->index_mp_to_orig && + ((orig_index = DM_origindex_mface_mpoly( + face_lookup->index_mf_to_mpoly, + face_lookup->index_mp_to_orig, + face_index))) != ORIGINDEX_NONE) + { + MPoly *mp = &face_lookup->mpoly_orig[orig_index]; + return ((mp->flag & ME_FACE_SEL) != 0); + } + else { + return ((mf->flag & ME_FACE_SEL) != 0); + } + } + else { + return true; + } +} + +typedef struct { + const float *v1; + const float *v2; + const float *v3; + const float *v4; +} ProjPaintFaceCoSS; + +static void proj_paint_face_coSS_init( + const ProjPaintState *ps, const MFace *mf, + ProjPaintFaceCoSS *coSS) +{ + coSS->v1 = ps->screenCoords[mf->v1]; + coSS->v2 = ps->screenCoords[mf->v2]; + coSS->v3 = ps->screenCoords[mf->v3]; + coSS->v4 = mf->v4 ? ps->screenCoords[mf->v4] : NULL; +} + +/* Return true if face should be culled, false otherwise */ +static bool project_paint_flt_max_cull( + const ProjPaintState *ps, + const ProjPaintFaceCoSS *coSS) +{ + if (!ps->is_ortho) { + if (coSS->v1[0] == FLT_MAX || + coSS->v2[0] == FLT_MAX || + coSS->v3[0] == FLT_MAX || + (coSS->v4 && coSS->v4[0] == FLT_MAX)) + { + return true; + } + } + return false; +} + +#ifdef PROJ_DEBUG_WINCLIP +/* Return true if face should be culled, false otherwise */ +static bool project_paint_winclip( + const ProjPaintState *ps, const MFace *mf, + const ProjPaintFaceCoSS *coSS) +{ + /* ignore faces outside the view */ + return ((ps->source != PROJ_SRC_VIEW_FILL) && + ((coSS->v1[0] < ps->screenMin[0] && + coSS->v2[0] < ps->screenMin[0] && + coSS->v3[0] < ps->screenMin[0] && + (mf->v4 && coSS->v4[0] < ps->screenMin[0])) || + + (coSS->v1[0] > ps->screenMax[0] && + coSS->v2[0] > ps->screenMax[0] && + coSS->v3[0] > ps->screenMax[0] && + (mf->v4 && coSS->v4[0] > ps->screenMax[0])) || + + (coSS->v1[1] < ps->screenMin[1] && + coSS->v2[1] < ps->screenMin[1] && + coSS->v3[1] < ps->screenMin[1] && + (mf->v4 && coSS->v4[1] < ps->screenMin[1])) || + + (coSS->v1[1] > ps->screenMax[1] && + coSS->v2[1] > ps->screenMax[1] && + coSS->v3[1] > ps->screenMax[1] && + (mf->v4 && coSS->v4[1] > ps->screenMax[1])))); +} +#endif //PROJ_DEBUG_WINCLIP + +/* Return true if face should be culled, false otherwise */ +static bool project_paint_backface_cull( + const ProjPaintState *ps, const MFace *mf, + const ProjPaintFaceCoSS *coSS) +{ + if (ps->do_backfacecull) { + if (ps->do_mask_normal) { + /* Since we are interpolating the normals of faces, we want to make + * sure all the verts are pointing away from the view, + * not just the face */ + if ((ps->vertFlags[mf->v1] & PROJ_VERT_CULL) && + (ps->vertFlags[mf->v2] & PROJ_VERT_CULL) && + (ps->vertFlags[mf->v3] & PROJ_VERT_CULL) && + (mf->v4 == 0 || ps->vertFlags[mf->v4] & PROJ_VERT_CULL)) + { + return true; } } else { - is_face_sel = true; + if (line_point_side_v2(coSS->v1, coSS->v2, coSS->v3) < 0.0f) { + return true; + } + } + } + + return false; +} + +static void project_paint_build_proj_ima( + ProjPaintState *ps, MemArena *arena, + LinkNode *image_LinkList) +{ + ProjPaintImage *projIma; + LinkNode *node; + int i; + + /* build an array of images we use */ + projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); + + for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { + int size; + projIma->ima = node->link; + projIma->touch = 0; + 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); + projIma->undoRect = (volatile void **) BLI_memarena_alloc(arena, size); + memset(projIma->undoRect, 0, size); + projIma->maskRect = BLI_memarena_alloc(arena, size); + memset(projIma->maskRect, 0, size); + projIma->valid = BLI_memarena_alloc(arena, size); + memset(projIma->valid, 0, size); + } +} + +static void project_paint_prepare_all_faces( + ProjPaintState *ps, MemArena *arena, + const ProjPaintFaceLookup *face_lookup, + ProjPaintLayerClone *layer_clone, + MTFace *tf_base) +{ + /* Image Vars - keep track of images we have used */ + LinkNode *image_LinkList = NULL; + + Image *tpage_last = NULL, *tpage; + TexPaintSlot *slot_last = NULL; + TexPaintSlot *slot = NULL; + MTFace **tf; + MFace *mf; + int image_index = -1, face_index; + + for (face_index = 0, tf = ps->dm_mtface, mf = ps->dm_mface; face_index < ps->dm_totface; mf++, tf++, face_index++) { + bool is_face_sel; + +#ifndef PROJ_DEBUG_NOSEAMBLEED + project_paint_bleed_add_face_user(ps, arena, mf, face_index); +#endif + + is_face_sel = project_paint_check_face_sel(ps, face_lookup, mf, face_index); if (!ps->do_stencil_brush) { slot = project_paint_face_paint_slot(ps, face_index); @@ -3445,103 +3632,31 @@ static void project_paint_begin(ProjPaintState *ps) *tf = tf_base + face_index; - if (ps->do_layer_clone) { - if (ps->do_material_slots) { - slot_clone = project_paint_face_clone_slot(ps, face_index); - /* all faces should have a valid slot, reassert here */ - if (ELEM(slot_clone, NULL, slot)) - continue; - } - else if (ps->clone_ima == ps->canvas_ima) - continue; - - tf_clone = ps->dm_mtface_clone + face_index; - - if (ps->do_material_slots) { - if (slot_clone != slot_last_clone) { - if (!slot->uvname || !(tf_clone_base = CustomData_get_layer_named(&ps->dm->faceData, CD_MTFACE, slot_clone->uvname))) - tf_clone_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); - slot_last_clone = slot_clone; - } - } - - *tf_clone = tf_clone_base + face_index; + if (project_paint_clone_face_skip(ps, layer_clone, slot, face_index)) { + continue; } /* tfbase here should be non-null! */ BLI_assert (tf_base != NULL); if (is_face_sel && tpage) { - const float *v1coSS, *v2coSS, *v3coSS, *v4coSS = NULL; + ProjPaintFaceCoSS coSS; + proj_paint_face_coSS_init(ps, mf, &coSS); - v1coSS = ps->screenCoords[mf->v1]; - v2coSS = ps->screenCoords[mf->v2]; - v3coSS = ps->screenCoords[mf->v3]; - if (mf->v4) { - v4coSS = ps->screenCoords[mf->v4]; - } - - - if (!ps->is_ortho) { - if (v1coSS[0] == FLT_MAX || - v2coSS[0] == FLT_MAX || - v3coSS[0] == FLT_MAX || - (mf->v4 && v4coSS[0] == FLT_MAX)) - { - continue; - } + if (project_paint_flt_max_cull(ps, &coSS)) { + continue; } #ifdef PROJ_DEBUG_WINCLIP - /* ignore faces outside the view */ - if ((ps->source != PROJ_SRC_VIEW_FILL) && - ((v1coSS[0] < ps->screenMin[0] && - v2coSS[0] < ps->screenMin[0] && - v3coSS[0] < ps->screenMin[0] && - (mf->v4 && v4coSS[0] < ps->screenMin[0])) || - - (v1coSS[0] > ps->screenMax[0] && - v2coSS[0] > ps->screenMax[0] && - v3coSS[0] > ps->screenMax[0] && - (mf->v4 && v4coSS[0] > ps->screenMax[0])) || - - (v1coSS[1] < ps->screenMin[1] && - v2coSS[1] < ps->screenMin[1] && - v3coSS[1] < ps->screenMin[1] && - (mf->v4 && v4coSS[1] < ps->screenMin[1])) || - - (v1coSS[1] > ps->screenMax[1] && - v2coSS[1] > ps->screenMax[1] && - v3coSS[1] > ps->screenMax[1] && - (mf->v4 && v4coSS[1] > ps->screenMax[1]))) - ) - { + if (project_paint_winclip(ps, mf, &coSS)) { continue; } #endif //PROJ_DEBUG_WINCLIP - if (ps->do_backfacecull) { - if (ps->do_mask_normal) { - /* Since we are interpolating the normals of faces, we want to make - * sure all the verts are pointing away from the view, - * not just the face */ - if ((ps->vertFlags[mf->v1] & PROJ_VERT_CULL) && - (ps->vertFlags[mf->v2] & PROJ_VERT_CULL) && - (ps->vertFlags[mf->v3] & PROJ_VERT_CULL) && - (mf->v4 == 0 || ps->vertFlags[mf->v4] & PROJ_VERT_CULL) - ) - { - continue; - } - } - else { - if (line_point_side_v2(v1coSS, v2coSS, v3coSS) < 0.0f) { - continue; - } - - } + if (project_paint_backface_cull(ps, mf, &coSS)) { + continue; } if (tpage_last != tpage) { @@ -3566,28 +3681,98 @@ static void project_paint_begin(ProjPaintState *ps) } /* build an array of images we use*/ - projIma = ps->projImages = (ProjPaintImage *)BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); - - for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { - int size; - projIma->ima = node->link; - projIma->touch = 0; - 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); - projIma->undoRect = (volatile void **) BLI_memarena_alloc(arena, size); - memset(projIma->undoRect, 0, size); - projIma->maskRect = (unsigned short **) BLI_memarena_alloc(arena, size); - memset(projIma->maskRect, 0, size); - projIma->valid = (bool **) BLI_memarena_alloc(arena, size); - memset(projIma->valid, 0, size); - } + project_paint_build_proj_ima(ps, arena, image_LinkList); /* we have built the array, discard the linked list */ BLI_linklist_free(image_LinkList, NULL); } +/* run once per stroke before projection painting */ +static void project_paint_begin(ProjPaintState *ps) +{ + ProjPaintLayerClone layer_clone; + ProjPaintFaceLookup face_lookup; + MTFace *tf_base; + + MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ + + const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); + + bool reset_threads = false; + + /* ---- end defines ---- */ + + if (ps->source == PROJ_SRC_VIEW) + ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); /* faster clipping lookups */ + + ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); + + /* paint onto the derived mesh */ + if (!proj_paint_state_dm_init(ps)) { + return; + } + + proj_paint_face_lookup_init(ps, &face_lookup); + proj_paint_layer_clone_init(ps, &layer_clone); + + if (ps->do_layer_stencil || ps->do_stencil_brush) { + //int layer_num = CustomData_get_stencil_layer(&ps->dm->faceData, CD_MTFACE); + int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); + if (layer_num != -1) + ps->dm_mtface_stencil = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); + + if (ps->dm_mtface_stencil == NULL) { + /* get active instead */ + ps->dm_mtface_stencil = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + } + + if (ps->do_stencil_brush) + tf_base = ps->dm_mtface_stencil; + } + + /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ + proj_paint_state_non_cddm_init(ps); + + proj_paint_state_viewport_init(ps); + + /* calculate vert screen coords + * run this early so we can calculate the x/y resolution of our bucket rect */ + proj_paint_state_screen_coords_init(ps, diameter); + + /* only for convenience */ + ps->screen_width = ps->screenMax[0] - ps->screenMin[0]; + ps->screen_height = ps->screenMax[1] - ps->screenMin[1]; + + ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); + ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); + + /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ + + if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { + reset_threads = true; + } + + /* really high values could cause problems since it has to allocate a few + * (ps->buckets_x*ps->buckets_y) sized arrays */ + CLAMP(ps->buckets_x, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); + CLAMP(ps->buckets_y, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); + + ps->bucketRect = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketRect"); + ps->bucketFaces = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); + + ps->bucketFlags = MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); +#ifndef PROJ_DEBUG_NOSEAMBLEED + proj_paint_state_seam_bleed_init(ps); +#endif + + proj_paint_state_thread_init(ps, reset_threads); + arena = ps->arena_mt[0]; + + proj_paint_state_vert_flags_init(ps); + + project_paint_prepare_all_faces(ps, arena, &face_lookup, &layer_clone, tf_base); +} + static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) { /* setup clone offset */ @@ -3773,7 +3958,9 @@ static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) } -static bool project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf *bucket_bounds, const float mval[2]) +static bool project_bucket_iter_next( + ProjPaintState *ps, int *bucket_index, + rctf *bucket_bounds, const float mval[2]) { const int diameter = 2 * ps->brush_size; @@ -4011,20 +4198,21 @@ static void do_projectpaint_soften(ProjPaintState *ps, ProjPixel *projPixel, flo } } -static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, const float texrgb[3], float mask) +static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, const float texrgb[3], float mask, float dither, float u, float v) { float rgb[3]; unsigned char rgba_ub[4]; - copy_v3_v3(rgb, ps->paint_color); - if (ps->is_texbrush) { - mul_v3_v3(rgb, texrgb); + mul_v3_v3v3(rgb, texrgb, ps->paint_color_linear); /* TODO(sergey): Support texture paint color space. */ linearrgb_to_srgb_v3_v3(rgb, rgb); } + else { + copy_v3_v3(rgb, ps->paint_color); + } - rgb_float_to_uchar(rgba_ub, rgb); + float_to_byte_dither_v3(rgba_ub, rgb, dither, u, v); rgba_ub[3] = f_to_char(mask); if (ps->do_masking) { @@ -4205,7 +4393,8 @@ static void *do_projectpaint_thread(void *ph_v) } else { linearrgb_to_srgb_v3_v3(color_f, color_f); - rgba_float_to_uchar(projPixel->newColor.ch, color_f); + float_to_byte_dither_v3(projPixel->newColor.ch, color_f, ps->dither, projPixel->x_px, projPixel->y_px); + projPixel->newColor.ch[3] = FTOCHAR(color_f[3]); IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, projPixel->newColor.ch, ps->blend); } @@ -4401,7 +4590,7 @@ static void *do_projectpaint_thread(void *ph_v) break; default: if (is_floatbuf) do_projectpaint_draw_f(ps, projPixel, texrgb, mask); - else do_projectpaint_draw(ps, projPixel, texrgb, mask); + else do_projectpaint_draw(ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); break; } } @@ -4485,13 +4674,13 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po /* thread specific */ handles[a].thread_index = a; - handles[a].projImages = (ProjPaintImage *)BLI_memarena_alloc(ps->arena_mt[a], ps->image_tot * sizeof(ProjPaintImage)); + handles[a].projImages = BLI_memarena_alloc(ps->arena_mt[a], ps->image_tot * sizeof(ProjPaintImage)); memcpy(handles[a].projImages, ps->projImages, ps->image_tot * sizeof(ProjPaintImage)); /* image bounds */ for (i = 0; i < ps->image_tot; i++) { - handles[a].projImages[i].partRedrawRect = (ImagePaintPartialRedraw *)BLI_memarena_alloc(ps->arena_mt[a], sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + handles[a].projImages[i].partRedrawRect = BLI_memarena_alloc(ps->arena_mt[a], sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); memcpy(handles[a].projImages[i].partRedrawRect, ps->projImages[i].partRedrawRect, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); } @@ -4705,6 +4894,8 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int if (ps->normal_angle_range <= 0.0f) ps->do_mask_normal = false; /* no need to do blending */ + ps->dither = settings->imapaint.dither; + return; } @@ -4732,7 +4923,7 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m /* Don't allow brush size below 2 */ if (BKE_brush_size_get(ps->scene, ps->brush) < 2) - BKE_brush_size_set(ps->scene, ps->brush, 2); + BKE_brush_size_set(ps->scene, ps->brush, 2 * U.pixelsize); /* allocate and initialize spatial data structures */ project_paint_begin(ps); @@ -4851,7 +5042,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) ps.is_maskbrush = false; ps.do_masking = false; orig_brush_size = BKE_brush_size_get(scene, ps.brush); - BKE_brush_size_set(scene, ps.brush, 32); /* cover the whole image */ + BKE_brush_size_set(scene, ps.brush, 32 * U.pixelsize); /* cover the whole image */ ps.tool = PAINT_TOOL_DRAW; /* so pixels are initialized with minimal info */ @@ -4945,6 +5136,9 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) image = BKE_image_add_from_imbuf(ibuf); + /* Drop reference to ibuf so that the image owns it */ + IMB_freeImBuf(ibuf); + if (image) { /* now for the trickyness. store the view projection here! * re-projection will reuse this */ @@ -4965,7 +5159,9 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) memcpy(array, rv3d->winmat, sizeof(rv3d->winmat)); array += sizeof(rv3d->winmat) / sizeof(float); memcpy(array, rv3d->viewmat, sizeof(rv3d->viewmat)); array += sizeof(rv3d->viewmat) / sizeof(float); is_ortho = ED_view3d_clip_range_get(v3d, rv3d, &array[0], &array[1], true); - array[2] = is_ortho ? 1.0f : 0.0f; /* using float for a bool is dodgy but since its an extra member in the array... easier then adding a single bool prop */ + /* using float for a bool is dodgy but since its an extra member in the array... + * easier then adding a single bool prop */ + array[2] = is_ortho ? 1.0f : 0.0f; IDP_AddToGroup(idgroup, view_data); @@ -5041,17 +5237,17 @@ bool BKE_paint_proj_mesh_data_check(Scene *scene, Object *ob, bool *uvs, bool *m hasmat = true; if (!ma->texpaintslot) { /* refresh here just in case */ - BKE_texpaint_slot_refresh_cache(scene, ma); + BKE_texpaint_slot_refresh_cache(scene, ma); /* if still no slots, we have to add */ - if (ma->texpaintslot) { + if (ma->texpaintslot) { hastex = true; - break; + break; } } else { hastex = true; - break; + break; } } } @@ -5060,7 +5256,7 @@ bool BKE_paint_proj_mesh_data_check(Scene *scene, Object *ob, bool *uvs, bool *m else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { if (imapaint->canvas == NULL) { hastex = false; - } + } } me = BKE_mesh_from_object(ob); @@ -5141,7 +5337,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain) RNA_string_get(op->ptr, "name", imagename); } ima = BKE_image_add_generated(bmain, width, height, imagename, alpha ? 32 : 24, use_float, - gen_type, color); + gen_type, color); return ima; } @@ -5221,7 +5417,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) DAG_id_tag_update(&ma->id, 0); ED_area_tag_redraw(CTX_wm_area(C)); - BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); return true; } @@ -5251,7 +5447,7 @@ static int texture_paint_add_texture_paint_slot_invoke(bContext *C, wmOperator * if (!ma) { ma = BKE_material_add(CTX_data_main(C), "Material"); /* no material found, just assign to first slot */ - assign_material(ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); + assign_material(ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); } type = RNA_enum_from_value(layer_type_items, type); @@ -5323,8 +5519,13 @@ static int texture_paint_delete_texture_paint_slot_exec(bContext *C, wmOperator slot = ma->texpaintslot + ma->paint_active_slot; - if (ma->mtex[slot->index]->tex) + if (ma->mtex[slot->index]->tex) { id_us_min(&ma->mtex[slot->index]->tex->id); + + if (ma->mtex[slot->index]->tex->ima) { + id_us_min(&ma->mtex[slot->index]->tex->ima->id); + } + } MEM_freeN(ma->mtex[slot->index]); ma->mtex[slot->index] = NULL; diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index ea5f77acc5b..fac1a05862f 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -106,15 +106,14 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op) const int old_size = BKE_brush_size_get(scene, brush); int size = (int)(scalar * old_size); - if (old_size == size) { + if (abs(old_size - size) < U.pixelsize) { if (scalar > 1) { - size++; + size += U.pixelsize; } else if (scalar < 1) { - size--; + size -= U.pixelsize; } } - CLAMP(size, 1, 2000); // XXX magic number BKE_brush_size_set(scene, brush, size); } @@ -128,6 +127,8 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op) BKE_brush_unprojected_radius_set(scene, brush, unprojected_radius); } + + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); } return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index bb875d2ef00..d9d0d8f5ef6 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -122,6 +122,10 @@ typedef struct PaintStroke { float zoom_2d; int pen_flip; + + /* line constraint */ + bool constrain_line; + float constrained_pos[2]; StrokeGetLocation get_location; StrokeTestStart test_start; @@ -161,13 +165,25 @@ static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata) glColor4ub(0, 0, 0, paint->paint_cursor_col[3]); glLineWidth(3.0); - sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], - x, y); + if (stroke->constrain_line) { + sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], + stroke->constrained_pos[0], stroke->constrained_pos[1]); + } + else { + sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], + x, y); + } glColor4ub(255, 255, 255, paint->paint_cursor_col[3]); glLineWidth(1.0); - sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], - x, y); + if (stroke->constrain_line) { + sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], + stroke->constrained_pos[0], stroke->constrained_pos[1]); + } + else { + sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], + x, y); + } glDisable(GL_LINE_STIPPLE); @@ -288,7 +304,7 @@ static bool paint_brush_update(bContext *C, ups->anchored_size = ups->pixel_radius = sqrtf(dx * dx + dy * dy); - ups->brush_rotation = ups->brush_rotation_sec = atan2f(dx, dy) + M_PI; + ups->brush_rotation = ups->brush_rotation_sec = atan2f(dx, dy) + (float)M_PI; if (brush->flag & BRUSH_EDGE_TO_EDGE) { halfway[0] = dx * 0.5f + stroke->initial_mouse[0]; @@ -654,10 +670,11 @@ PaintStroke *paint_stroke_new(bContext *C, get_imapaint_zoom(C, &zoomx, &zoomy); stroke->zoom_2d = max_ff(zoomx, zoomy); - if ((br->flag & BRUSH_CURVE) && - RNA_struct_property_is_set(op->ptr, "mode")) + if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { - RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); + if (br->flag & (BRUSH_CURVE | BRUSH_LINE)) { + RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); + } } /* initialize here */ ups->overlap_factor = 1.0; @@ -996,6 +1013,34 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str return false; } +static void paint_stroke_line_constrain (PaintStroke *stroke, float mouse[2]) +{ + if (stroke->constrain_line) { + float line[2]; + float angle, len, res; + + sub_v2_v2v2(line, mouse, stroke->last_mouse_position); + angle = atan2(line[1], line[0]); + len = len_v2(line); + + /* divide angle by PI/4 */ + angle = 4.0f * angle / (float)M_PI; + + /* now take residue */ + res = angle - floorf(angle); + + /* residue decides how close we are at a certain angle */ + if (res <= 0.5f) { + angle = floorf(angle) * (float)M_PI_4; + } + else { + angle = (floorf(angle) + 1.0f) * (float)M_PI_4; + } + + mouse[0] = stroke->constrained_pos[0] = len * cosf(angle) + stroke->last_mouse_position[0]; + mouse[1] = stroke->constrained_pos[1] = len * sinf(angle) + stroke->last_mouse_position[1]; + } +} int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) { @@ -1069,7 +1114,9 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == stroke->event_type && !first_modal) { if (event->val == KM_RELEASE) { - paint_stroke_line_end (C, op, stroke, sample_average.mouse); + copy_v2_fl2(mouse, event->mval[0], event->mval[1]); + paint_stroke_line_constrain(stroke, mouse); + paint_stroke_line_end (C, op, stroke, mouse); stroke_done(C, op); return OPERATOR_FINISHED; } @@ -1079,13 +1126,22 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) stroke_done(C, op); return OPERATOR_FINISHED; } - else if ((br->flag & BRUSH_LINE) && stroke->stroke_started && - (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) - { - if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || (br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { - copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); + else if (br->flag & BRUSH_LINE) { + if (event->ctrl) + stroke->constrain_line = true; + else + stroke->constrain_line = false; + + copy_v2_fl2(mouse, event->mval[0], event->mval[1]); + paint_stroke_line_constrain(stroke, mouse); + + if (stroke->stroke_started && (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) + { + if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || (br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { + copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); + } + paint_calculate_rake_rotation(stroke->ups, br, mouse); } - paint_calculate_rake_rotation(stroke->ups, br, sample_average.mouse); } else if (first_modal || /* regular dabs */ diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c index 20e3155c01d..0293a0bfc00 100644 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -210,7 +210,7 @@ static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char * /* pass */ } else { - if (!name || strcmp(stack->current->name, name) == 0) { + if (!name || STREQ(stack->current->name, name)) { if (G.debug & G_DEBUG_WM) { printf("%s: undo '%s'\n", __func__, stack->current->name); } @@ -225,7 +225,7 @@ static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char * /* pass */ } else { - if (!name || strcmp(stack->current->name, name) == 0) { + if (!name || STREQ(stack->current->name, name)) { undo = (stack->current && stack->current->next) ? stack->current->next : stack->elems.first; undo_restore(C, stack, undo); stack->current = undo; @@ -394,7 +394,7 @@ int ED_undo_paint_valid(int type, const char *name) /* pass */ } else { - if (name && strcmp(stack->current->name, name) == 0) + if (name && STREQ(stack->current->name, name)) return 1; else return stack->elems.first != stack->elems.last; diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 9c2c13f9a2d..c0ed5005397 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -177,7 +177,7 @@ float paint_get_tex_pixel(MTex *mtex, float u, float v, struct ImagePool *pool, float co[3] = {u, v, 0.0f}; externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool); + rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false); return intensity; } @@ -189,7 +189,7 @@ void paint_get_tex_pixel_col(MTex *mtex, float u, float v, float rgba[4], struct float intensity; hasrgb = externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool); + rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false); if (!hasrgb) { rgba[0] = intensity; rgba[1] = intensity; @@ -373,15 +373,15 @@ static void imapaint_pick_uv(Scene *scene, Object *ob, unsigned int faceindex, c } /* returns 0 if not found, otherwise 1 */ -static int imapaint_pick_face(ViewContext *vc, const int mval[2], unsigned int *r_index, unsigned int totface) +static int imapaint_pick_face(ViewContext *vc, const int mval[2], unsigned int *r_index, unsigned int totpoly) { - if (totface == 0) + if (totpoly == 0) return 0; /* sample only on the exact position */ *r_index = view3d_sample_backbuf(vc, mval[0], mval[1]); - if ((*r_index) == 0 || (*r_index) > (unsigned int)totface) { + if ((*r_index) == 0 || (*r_index) > (unsigned int)totpoly) { return 0; } @@ -456,7 +456,7 @@ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_pr ViewContext vc; const int mval[2] = {x, y}; unsigned int faceindex; - unsigned int totface = me->totface; + unsigned int totpoly = me->totpoly; MTFace *dm_mtface = dm->getTessFaceDataArray(dm, CD_MTFACE); if (dm_mtface) { @@ -464,7 +464,7 @@ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_pr view3d_operator_needs_opengl(C); - if (imapaint_pick_face(&vc, mval, &faceindex, totface)) { + if (imapaint_pick_face(&vc, mval, &faceindex, totpoly)) { Image *image; if (use_material) diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index dbb29997102..69428c0f0d7 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -4248,14 +4248,14 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st sculpt_restore_mesh(sd, ob); if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, - sd->constant_detail / 100.0f); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, sd->constant_detail / 100.0f); } else { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, - (ss->cache->radius / - (float)ups->pixel_radius) * - (float)sd->detail_size / 0.4f); + BKE_pbvh_bmesh_detail_size_set( + ss->pbvh, + (ss->cache->radius / + (float)ups->pixel_radius) * + (float)(sd->detail_size * U.pixelsize) / 0.4f); } if (sculpt_stroke_dynamic_topology(ss, brush)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 4e9d23d3d97..a4adbc6bca8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -104,7 +104,7 @@ static int sculpt_undo_restore_coords(bContext *C, DerivedMesh *dm, SculptUndoNo if (unode->maxvert) { /* regular mesh restore */ - if (ss->kb && strcmp(ss->kb->name, unode->shapeName)) { + if (ss->kb && !STREQ(ss->kb->name, unode->shapeName)) { /* shape key has been changed before calling undo operator */ Key *key = BKE_key_from_object(ob); @@ -404,7 +404,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) bool need_mask = false; for (unode = lb->first; unode; unode = unode->next) { - if (strcmp(unode->idname, ob->id.name) == 0) { + if (STREQ(unode->idname, ob->id.name)) { if (unode->type == SCULPT_UNDO_MASK) { /* is possible that we can't do the mask undo (below) * because of the vertex count */ @@ -423,7 +423,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) return; for (unode = lb->first; unode; unode = unode->next) { - if (!(strcmp(unode->idname, ob->id.name) == 0)) + if (!STREQ(unode->idname, ob->id.name)) continue; /* check if undo data matches current data well enough to @@ -550,7 +550,7 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) unode = lb->first; - if (unode && strcmp(unode->idname, ob->id.name) != 0) { + if (unode && !STREQ(unode->idname, ob->id.name)) { if (unode->bm_entry) BM_log_cleanup_entry(unode->bm_entry); diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 23bc4a483d3..a9feb9f48de 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -598,7 +598,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm UvEdge *edges; GHash *edgeHash; - GHashIterator *ghi; + GHashIterator gh_iter; bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS); int island_index = 0; @@ -754,21 +754,15 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm uv_sculpt_stroke_exit(C, op); return NULL; } - ghi = BLI_ghashIterator_new(edgeHash); - if (!ghi) { - BLI_ghash_free(edgeHash, NULL, NULL); - MEM_freeN(edges); - uv_sculpt_stroke_exit(C, op); - return NULL; - } + /* fill the edges with data */ - for (i = 0; !BLI_ghashIterator_done(ghi); BLI_ghashIterator_step(ghi)) { - data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(ghi)); + i = 0; + GHASH_ITER (gh_iter, edgeHash) { + data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); } data->totalUvEdges = BLI_ghash_size(edgeHash); /* cleanup temporary stuff */ - BLI_ghashIterator_free(ghi); BLI_ghash_free(edgeHash, NULL, NULL); MEM_freeN(edges); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index d9f4b97fe09..5ce3517696e 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -399,7 +399,7 @@ static bool sound_mixdown_check(bContext *UNUSED(C), wmOperator *op) if (item->value == container) { const char **ext = snd_ext_sound; while (*ext != NULL) { - if (!strcmp(*ext + 1, item->name)) { + if (STREQ(*ext + 1, item->name)) { extension = *ext; break; } @@ -449,9 +449,9 @@ static int sound_mixdown_invoke(bContext *C, wmOperator *op, const wmEvent *even static bool sound_mixdown_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) { const char *prop_id = RNA_property_identifier(prop); - return !(strcmp(prop_id, "filepath") == 0 || - strcmp(prop_id, "directory") == 0 || - strcmp(prop_id, "filename") == 0); + return !(STREQ(prop_id, "filepath") || + STREQ(prop_id, "directory") || + STREQ(prop_id, "filename")); } static void sound_mixdown_draw(bContext *C, wmOperator *op) diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index a67af289f59..a263f22e072 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -559,7 +559,7 @@ static void template_texture_user_menu(bContext *C, uiLayout *layout, void *UNUS char name[UI_MAX_NAME_STR]; /* add label per category */ - if (!last_category || strcmp(last_category, user->category) != 0) { + if (!last_category || !STREQ(last_category, user->category)) { uiItemL(layout, user->category, ICON_NONE); but = block->buttons.last; but->drawflag = UI_BUT_TEXT_LEFT; diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index f8299a8d335..c32d06cf9b1 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -540,7 +540,7 @@ void uiTemplateMovieclipInformation(uiLayout *layout, PointerRNA *ptr, const cha uiItemL(col, str, ICON_NONE); /* Display current frame number. */ - framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, user->framenr) ; + framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, user->framenr); if (framenr <= clip->len) BLI_snprintf(str, sizeof(str), IFACE_("Frame: %d / %d"), framenr, clip->len); else diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index e3d45a297a7..b99b23c60d3 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1342,7 +1342,8 @@ static int clip_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) if (clip->anim) { pj->index_context = IMB_anim_index_rebuild_context(clip->anim, clip->proxy.build_tc_flag, - clip->proxy.build_size_flag, clip->proxy.quality); + clip->proxy.build_size_flag, clip->proxy.quality, + true, NULL); } WM_jobs_customdata_set(wm_job, pj, proxy_freejob); diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index 2d5e5c6e0f2..8263268898f 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -107,7 +107,7 @@ static ConsoleLine *console_history_find(SpaceConsole *sc, const char *str, Cons if (cl == cl_ignore) continue; - if (strcmp(str, cl->line) == 0) + if (STREQ(str, cl->line)) return cl; } @@ -722,7 +722,7 @@ static int console_history_cycle_exec(bContext *C, wmOperator *op) if (ci->prev) { ConsoleLine *ci_prev = (ConsoleLine *)ci->prev; - if (strcmp(ci->line, ci_prev->line) == 0) + if (STREQ(ci->line, ci_prev->line)) console_history_free(sc, ci_prev); } @@ -791,7 +791,7 @@ static int console_history_append_exec(bContext *C, wmOperator *op) while ((cl = console_history_find(sc, ci->line, ci))) console_history_free(sc, cl); - if (strcmp(str, ci->line) == 0) { + if (STREQ(str, ci->line)) { MEM_freeN(str); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 3f3c14d3a7d..57611930e99 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -133,7 +133,8 @@ void file_draw_buttons(const bContext *C, ARegion *ar) loadbutton = 0; } else { - loadbutton = UI_fontstyle_string_width(params->title) + btn_margin; + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + loadbutton = UI_fontstyle_string_width(fstyle, params->title) + btn_margin; CLAMP_MIN(loadbutton, btn_minw); if (available_w <= loadbutton + separator + input_minw) { loadbutton = 0; @@ -238,7 +239,7 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh static int get_file_icon(struct direntry *file) { if (file->type & S_IFDIR) { - if (strcmp(file->relname, "..") == 0) { + if (FILENAME_IS_PARENT(file->relname)) { return ICON_FILE_PARENT; } if (file->flags & FILE_TYPE_APPLICATIONBUNDLE) { @@ -402,7 +403,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname) BLI_strncpy(filename, sfile->params->renameedit, sizeof(filename)); BLI_make_file_string(G.main->name, newname, sfile->params->dir, filename); - if (strcmp(orgname, newname) != 0) { + if (!STREQ(orgname, newname)) { if (!BLI_exists(newname)) { BLI_rename(orgname, newname); /* to make sure we show what is on disk */ @@ -528,7 +529,7 @@ void file_draw_list(const bContext *C, ARegion *ar) int shade = (params->active_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) ? 20 : 0; /* readonly files (".." and ".") must not be drawn as selected - set color back to normal */ - if (STREQ(file->relname, "..") || STREQ(file->relname, ".")) { + if (FILENAME_IS_CURRPAR(file->relname)) { colorid = TH_BACK; } draw_tile(sx, sy - 1, layout->tile_w + 4, sfile->layout->tile_h + layout->tile_border_y, colorid, shade); @@ -537,7 +538,7 @@ void file_draw_list(const bContext *C, ARegion *ar) UI_draw_roundbox_corner_set(UI_CNR_NONE); /* don't drag parent or refresh items */ - do_drag = !(STREQ(file->relname, "..") || STREQ(file->relname, ".")); + do_drag = !(FILENAME_IS_CURRPAR(file->relname)); if (FILE_IMGDISPLAY == params->display) { is_icon = 0; @@ -590,7 +591,7 @@ void file_draw_list(const bContext *C, ARegion *ar) if (params->display == FILE_SHORTDISPLAY) { sx += (int)layout->column_widths[COLUMN_NAME] + column_space; - if (!(file->type & S_IFDIR)) { + if ((BLI_is_dir(file->path) == false) && file->size[0]) { file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align); sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; } @@ -619,7 +620,7 @@ void file_draw_list(const bContext *C, ARegion *ar) file_draw_string(sx, sy, file->time, layout->column_widths[COLUMN_TIME], layout->tile_h, align); sx += (int)layout->column_widths[COLUMN_TIME] + column_space; - if (!(file->type & S_IFDIR)) { + if ((BLI_is_dir(file->path) == false) && file->size[0]) { file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align); sx += (int)layout->column_widths[COLUMN_SIZE] + column_space; } diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 3a579820106..ba6f91e8301 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -183,11 +183,11 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen) retval = FILE_SELECT_DIR; } /* the path is too long and we are not going up! */ - else if (strcmp(file->relname, "..") && strlen(params->dir) + strlen(file->relname) >= FILE_MAX) { + else if (!FILENAME_IS_PARENT(file->relname) && strlen(params->dir) + strlen(file->relname) >= FILE_MAX) { // XXX error("Path too long, cannot enter this directory"); } else { - if (strcmp(file->relname, "..") == 0) { + if (FILENAME_IS_PARENT(file->relname)) { /* avoids /../../ */ BLI_parent_dir(params->dir); } @@ -269,7 +269,7 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent * for (idx = sel.last; idx >= 0; idx--) { struct direntry *file = filelist_file(sfile->files, idx); - if (STREQ(file->relname, "..") || STREQ(file->relname, ".")) { + if (FILENAME_IS_CURRPAR(file->relname)) { file->selflag &= ~FILE_SEL_HIGHLIGHTED; } @@ -362,7 +362,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (idx >= 0) { struct direntry *file = filelist_file(sfile->files, idx); - if (STREQ(file->relname, "..") || STREQ(file->relname, ".")) { + if (FILENAME_IS_CURRPAR(file->relname)) { /* skip - If a readonly file (".." or ".") is selected, skip deselect all! */ } else { @@ -1531,7 +1531,7 @@ static int file_rename_poll(bContext *C) if (idx >= 0) { struct direntry *file = filelist_file(sfile->files, idx); - if (STREQ(file->relname, "..") || STREQ(file->relname, ".")) { + if (FILENAME_IS_CURRPAR(file->relname)) { poll = 0; } } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index bcef0817ffe..f0180c3c841 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -237,9 +237,6 @@ typedef struct FileList { bool (*filterf)(struct direntry *, const char *, FileListFilter *); } FileList; -#define FILENAME_IS_BREADCRUMBS(_n) \ - ((_n)[0] == '.' && ((_n)[1] == '\0' || ((_n)[1] == '.' && (_n)[2] == '\0'))) - #define SPECIAL_IMG_SIZE 48 #define SPECIAL_IMG_ROWS 4 #define SPECIAL_IMG_COLS 4 @@ -304,10 +301,10 @@ static int compare_direntry_generic(const struct direntry *entry1, const struct if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1); /* make sure "." and ".." are always first */ - if (strcmp(entry1->relname, ".") == 0) return (-1); - if (strcmp(entry2->relname, ".") == 0) return (1); - if (strcmp(entry1->relname, "..") == 0) return (-1); - if (strcmp(entry2->relname, "..") == 0) return (1); + if (FILENAME_IS_CURRENT(entry1->relname)) return (-1); + if (FILENAME_IS_CURRENT(entry2->relname)) return (1); + if (FILENAME_IS_PARENT(entry1->relname)) return (-1); + if (FILENAME_IS_PARENT(entry2->relname)) return (1); return 0; } @@ -447,7 +444,7 @@ static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), Fi { bool is_filtered = !is_hidden_file(file->relname, filter); - if (is_filtered && filter->filter && !FILENAME_IS_BREADCRUMBS(file->relname)) { + if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relname)) { if ((file->type & S_IFDIR) && !(filter->filter & FILE_TYPE_FOLDER)) { is_filtered = false; } @@ -471,7 +468,7 @@ static bool is_filtered_lib(struct direntry *file, const char *root, FileListFil if (BLO_is_a_library(root, dir, group)) { is_filtered = !is_hidden_file(file->relname, filter); - if (is_filtered && filter->filter && !FILENAME_IS_BREADCRUMBS(file->relname)) { + if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relname)) { if (is_filtered && (filter->filter_search[0] != '\0')) { if (fnmatch(filter->filter_search, file->relname, FNM_CASEFOLD) != 0) { is_filtered = false; @@ -634,10 +631,10 @@ ImBuf *filelist_geticon(struct FileList *filelist, const int index) fidx = filelist->fidx[index]; file = &filelist->filelist[fidx]; if (file->type & S_IFDIR) { - if (strcmp(filelist->filelist[fidx].relname, "..") == 0) { + if (FILENAME_IS_PARENT(filelist->filelist[fidx].relname)) { ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT]; } - else if (strcmp(filelist->filelist[fidx].relname, ".") == 0) { + else if (FILENAME_IS_CURRENT(filelist->filelist[fidx].relname)) { ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH]; } else { @@ -764,28 +761,20 @@ struct direntry *filelist_file(struct FileList *filelist, int index) int filelist_find(struct FileList *filelist, const char *filename) { - int index = -1; - int i; int fidx = -1; if (!filelist->fidx) return fidx; - - for (i = 0; i < filelist->numfiles; ++i) { - if (strcmp(filelist->filelist[i].relname, filename) == 0) { /* not dealing with user input so don't need BLI_path_cmp */ - index = i; - break; - } - } + for (fidx = 0; fidx < filelist->numfiltered; fidx++) { + int index = filelist->fidx[fidx]; - for (i = 0; i < filelist->numfiltered; ++i) { - if (filelist->fidx[i] == index) { - fidx = i; - break; + if (STREQ(filelist->filelist[index].relname, filename)) { + return fidx; } } - return fidx; + + return -1; } /* would recognize .blend as well */ @@ -1116,7 +1105,7 @@ static void filelist_from_library(struct FileList *filelist) filelist->filelist = malloc(filelist->numfiles * sizeof(*filelist->filelist)); memset(filelist->filelist, 0, filelist->numfiles * sizeof(*filelist->filelist)); - filelist->filelist[0].relname = BLI_strdup(".."); + filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT); filelist->filelist[0].type |= S_IFDIR; for (i = 0, l = names; i < nnames; i++, l = l->next) { @@ -1191,7 +1180,7 @@ static void filelist_from_main(struct FileList *filelist) filelist->filelist[a].type |= S_IFDIR; } - filelist->filelist[0].relname = BLI_strdup(".."); + filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT); filelist->filelist[1].relname = BLI_strdup("Scene"); filelist->filelist[2].relname = BLI_strdup("Object"); filelist->filelist[3].relname = BLI_strdup("Mesh"); @@ -1244,7 +1233,7 @@ static void filelist_from_main(struct FileList *filelist) if (!filelist->filter_data.hide_parent) { memset(&(filelist->filelist[0]), 0, sizeof(struct direntry)); - filelist->filelist[0].relname = BLI_strdup(".."); + filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT); filelist->filelist[0].type |= S_IFDIR; files++; diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 3e663275dcd..c452f2e1ff4 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -669,7 +669,7 @@ int autocomplete_directory(struct bContext *C, char *str, void *UNUSED(arg_v)) AutoComplete *autocpl = UI_autocomplete_begin(str, FILE_MAX); while ((de = readdir(dir)) != NULL) { - if (strcmp(".", de->d_name) == 0 || strcmp("..", de->d_name) == 0) { + if (FILENAME_IS_CURRPAR(de->d_name)) { /* pass */ } else { diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 4ab9bc6a849..05dfdf66ab6 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -283,10 +283,10 @@ void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename) if (!fp) return; while (fgets(line, sizeof(line), fp) != NULL) { /* read a line */ - if (strncmp(line, "[Bookmarks]", 11) == 0) { + if (STREQLEN(line, "[Bookmarks]", 11)) { category = FS_CATEGORY_BOOKMARKS; } - else if (strncmp(line, "[Recent]", 8) == 0) { + else if (STREQLEN(line, "[Recent]", 8)) { category = FS_CATEGORY_RECENT; } else { @@ -359,7 +359,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) continue; FSRefMakePath(&dir, path, FILE_MAX); - if (strcmp((char *)path, "/home") && strcmp((char *)path, "/net")) { + if (!STREQ((char *)path, "/home") && !STREQ((char *)path, "/net")) { /* /net and /home are meaningless on OSX, home folders are stored in /Users */ fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM, (char *)path, FS_INSERT_SORTED); } @@ -488,7 +488,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) else { while ((mnt = getmntent(fp))) { /* not sure if this is right, but seems to give the relevant mnts */ - if (strncmp(mnt->mnt_fsname, "/dev", 4)) + if (!STREQLEN(mnt->mnt_fsname, "/dev", 4)) continue; len = strlen(mnt->mnt_dir); diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index 9093734f25f..2d8a0a3da29 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -848,11 +848,13 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) * NOTE: we need to scale the y-values to be valid for the units */ glBegin(GL_LINES); + { t = v2d->cur.xmin; glVertex2f(t, t * unitfac); t = v2d->cur.xmax; glVertex2f(t, t * unitfac); + } glEnd(); /* cleanup line drawing */ @@ -875,6 +877,7 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) setlinestyle(5); glBegin(GL_LINES); + { /* x-axis lookup */ co[0] = x; @@ -894,6 +897,7 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) co[0] = x; glVertex2fv(co); + } glEnd(); setlinestyle(0); diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index b87f80c4e62..2944901663b 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1721,7 +1721,7 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op) * so if the paths or the ID's don't match up, then a curve needs to be added * to a new group */ - if ((euf) && (euf->id == ale->id) && (strcmp(euf->rna_path, fcu->rna_path) == 0)) { + if ((euf) && (euf->id == ale->id) && (STREQ(euf->rna_path, fcu->rna_path))) { /* this should be fine to add to the existing group then */ euf->fcurves[fcu->array_index] = fcu; } diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 78dbae7618b..46a39806ad7 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -66,6 +66,15 @@ /* ************************************************************************** */ /* KEYFRAMES STUFF */ +static void graphkeys_auto_view(bContext *C) +{ + const SpaceIpo *sipo = CTX_wm_space_graph(C); + + if (sipo && sipo->flag & SIPO_AUTO_VIEW_SELECTED) { + WM_operator_name_call(C, "GRAPH_OT_view_selected", WM_OP_INVOKE_DEFAULT, NULL); + } +} + /* ******************** Deselect All Operator ***************************** */ /* This operator works in one of three ways: * 1) (de)select all (AKEY) - test if select all or deselect all @@ -83,7 +92,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) +static short deselect_graph_keys(bAnimContext *ac, short test, short sel, short do_channels) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; @@ -142,6 +151,8 @@ static void deselect_graph_keys(bAnimContext *ac, short test, short sel, short d /* Cleanup */ ANIM_animdata_freelist(&anim_data); + + return sel; } /* ------------------- */ @@ -150,6 +161,7 @@ static int graphkeys_deselectall_exec(bContext *C, wmOperator *op) { bAnimContext ac; bAnimListElem *ale_active = NULL; + short sel; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -163,9 +175,9 @@ static int graphkeys_deselectall_exec(bContext *C, wmOperator *op) /* 'standard' behavior - check if selected, then apply relevant selection */ if (RNA_boolean_get(op->ptr, "invert")) - deselect_graph_keys(&ac, 0, SELECT_INVERT, true); + sel = deselect_graph_keys(&ac, 0, SELECT_INVERT, true); else - deselect_graph_keys(&ac, 1, SELECT_ADD, true); + sel = deselect_graph_keys(&ac, 1, SELECT_ADD, true); /* restore active F-Curve... */ if (ale_active) { @@ -180,6 +192,9 @@ static int graphkeys_deselectall_exec(bContext *C, wmOperator *op) ale_active = NULL; } + if (sel != SELECT_SUBTRACT) + graphkeys_auto_view(C); + /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -374,6 +389,8 @@ static int graphkeys_borderselect_exec(bContext *C, wmOperator *op) /* apply borderselect action */ borderselect_graphkeys(&ac, &rect_fl, mode, selectmode, incl_handles, NULL); + graphkeys_auto_view(C); + /* send notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -461,6 +478,8 @@ static int graphkeys_lassoselect_exec(bContext *C, wmOperator *op) MEM_freeN((void *)data_lasso.mcords); + graphkeys_auto_view(C); + /* send notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -491,6 +510,14 @@ void GRAPH_OT_select_lasso(wmOperatorType *ot) RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection instead of deselecting everything first"); } +static int graph_circle_select_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (event->type == EVT_MODAL_MAP && event->val == GESTURE_MODAL_NOP) + graphkeys_auto_view(C); + + return WM_gesture_circle_modal(C, op, event); +} + static int graph_circle_select_exec(bContext *C, wmOperator *op) { bAnimContext ac; @@ -552,7 +579,7 @@ void GRAPH_OT_select_circle(wmOperatorType *ot) ot->idname = "GRAPH_OT_select_circle"; ot->invoke = WM_gesture_circle_invoke; - ot->modal = WM_gesture_circle_modal; + ot->modal = graph_circle_select_modal; ot->exec = graph_circle_select_exec; ot->poll = graphop_visible_keyframes_poll; ot->cancel = WM_gesture_circle_cancel; @@ -725,6 +752,8 @@ static int graphkeys_columnselect_exec(bContext *C, wmOperator *op) else columnselect_graph_keys(&ac, mode); + graphkeys_auto_view(C); + /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -783,6 +812,8 @@ static int graphkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) /* Cleanup */ ANIM_animdata_freelist(&anim_data); + graphkeys_auto_view(C); + /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -807,12 +838,13 @@ void GRAPH_OT_select_linked(wmOperatorType *ot) /* ******************** Select More/Less Operators *********************** */ /* Common code to perform selection */ -static void select_moreless_graph_keys(bAnimContext *ac, short mode) +static void select_moreless_graph_keys(bContext *C, bAnimContext *ac, short mode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; + const SpaceIpo *sipo = (SpaceIpo *)ac->sl; KeyframeEditData ked; KeyframeEditFunc build_cb; @@ -842,6 +874,24 @@ static void select_moreless_graph_keys(bAnimContext *ac, short mode) /* free the selmap used here */ MEM_freeN(ked.data); ked.data = NULL; + + /* only do auto view if a bezier point is selected */ + if (sipo->flag & SIPO_AUTO_VIEW_SELECTED) { + const FCurve *fcu = (FCurve *)ale->key_data; + const BezTriple *bezt; + unsigned int i; + + /* only continue if F-Curve has keyframes */ + if (fcu->bezt == NULL) + continue; + + for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) { + if (BEZSELECTED(bezt)) { + graphkeys_auto_view(C); + break; + } + } + } } /* Cleanup */ @@ -859,7 +909,7 @@ static int graphkeys_select_more_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; /* perform select changes */ - select_moreless_graph_keys(&ac, SELMAP_MORE); + select_moreless_graph_keys(C, &ac, SELMAP_MORE); /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -893,7 +943,7 @@ static int graphkeys_select_less_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; /* perform select changes */ - select_moreless_graph_keys(&ac, SELMAP_LESS); + select_moreless_graph_keys(C, &ac, SELMAP_LESS); /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -1008,6 +1058,8 @@ static int graphkeys_select_leftright_exec(bContext *C, wmOperator *op) /* do the selecting now */ graphkeys_select_leftright(&ac, leftright, selectmode); + graphkeys_auto_view(C); + /* set notifier that keyframe selection (and channels too) have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | ND_ANIMCHAN | NA_SELECTED, NULL); diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 05868283b2e..fb3c140fddf 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -64,6 +64,7 @@ #include "image_intern.h" #define B_NOP -1 +#define MAX_IMAGE_INFO_LEN 128 /* proto */ @@ -640,8 +641,6 @@ static void rna_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg)) void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, PointerRNA *userptr, int compact) { -#define MAX_INFO_LEN 128 - PropertyRNA *prop; PointerRNA imaptr; RNAUpdateCb *cb; @@ -650,7 +649,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char Scene *scene = CTX_data_scene(C); uiLayout *row, *split, *col; uiBlock *block; - char str[MAX_INFO_LEN]; + char str[MAX_IMAGE_INFO_LEN]; void *lock; @@ -687,14 +686,14 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char uiLayoutSetContextPointer(layout, "edit_image_user", userptr); if (!compact) - uiTemplateID(layout, C, ptr, propname, "IMAGE_OT_new", "IMAGE_OT_open", NULL); + uiTemplateID(layout, C, ptr, propname, ima ? NULL : "IMAGE_OT_new", "IMAGE_OT_open", NULL); if (ima) { UI_block_funcN_set(block, rna_update_cb, MEM_dupallocN(cb), NULL); if (ima->source == IMA_SRC_VIEWER) { ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); - image_info(scene, iuser, ima, ibuf, str, MAX_INFO_LEN); + image_info(scene, iuser, ima, ibuf, str, MAX_IMAGE_INFO_LEN); BKE_image_release_ibuf(ima, ibuf, lock); uiItemL(layout, ima->id.name + 2, ICON_NONE); @@ -763,10 +762,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char } else if (ima->source != IMA_SRC_GENERATED) { if (compact == 0) { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); - image_info(scene, iuser, ima, ibuf, str, MAX_INFO_LEN); - BKE_image_release_ibuf(ima, ibuf, lock); - uiItemL(layout, str, ICON_NONE); + uiTemplateImageInfo(layout, C, ima, iuser); } } @@ -780,7 +776,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char bool has_alpha = true; if (ibuf) { - int imtype = BKE_ftype_to_imtype(ibuf->ftype); + int imtype = BKE_image_ftype_to_imtype(ibuf->ftype); char valid_channels = BKE_imtype_valid_channels(imtype, false); has_alpha = (valid_channels & IMA_CHAN_FLAG_ALPHA) != 0; @@ -791,7 +787,14 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char if (has_alpha) { col = uiLayoutColumn(layout, false); uiItemR(col, &imaptr, "use_alpha", 0, NULL, ICON_NONE); - uiItemR(col, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE); + row = uiLayoutRow(col, false); + uiLayoutSetActive(row, RNA_boolean_get(&imaptr, "use_alpha")); + uiItemR(row, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE); + } + + if (ima->source == IMA_SRC_MOVIE) { + col = uiLayoutColumn(layout, false); + uiItemR(col, &imaptr, "use_deinterlace", 0, IFACE_("Deinterlace"), ICON_NONE); } uiItemS(layout); @@ -856,8 +859,6 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char } MEM_freeN(cb); - -#undef MAX_INFO_LEN } void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, int color_management) @@ -977,6 +978,24 @@ void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser } } +void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *iuser) +{ + ImBuf *ibuf; + char str[MAX_IMAGE_INFO_LEN]; + void *lock; + + if (!ima || !iuser) + return; + + ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); + + image_info(CTX_data_scene(C), iuser, ima, ibuf, str, MAX_IMAGE_INFO_LEN); + BKE_image_release_ibuf(ima, ibuf, lock); + uiItemL(layout, str, ICON_NONE); +} + +#undef MAX_IMAGE_INFO_LEN + void image_buttons_register(ARegionType *UNUSED(art)) { diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 757059ebc29..8e2c6b97a5b 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -35,6 +35,7 @@ #include "BLI_rect.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" @@ -290,6 +291,20 @@ void ED_image_point_pos__reverse(SpaceImage *sima, ARegion *ar, const float co[2 r_co[1] = (co[1] * height * zoomy) + (float)sy; } +void ED_space_image_scopes_update(const struct bContext *C, struct SpaceImage *sima, struct ImBuf *ibuf, bool use_view_settings) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + + /* scope update can be expensive, don't update during paint modes */ + if (sima->mode == SI_MODE_PAINT) + return; + if (ob && ((ob->mode & OB_MODE_TEXTURE_PAINT) != 0)) + return; + + scopes_update(&sima->scopes, ibuf, use_view_settings ? &scene->view_settings : NULL, &scene->display_settings); +} + bool ED_space_image_show_render(SpaceImage *sima) { return (sima->image && ELEM(sima->image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 6cf53533618..af1502509f5 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -899,9 +899,9 @@ static void image_tools_area_draw(const bContext *C, ARegion *ar) BKE_histogram_update_sample_line(&sima->sample_line_hist, ibuf, &scene->view_settings, &scene->display_settings); } if (sima->image->flag & IMA_VIEW_AS_RENDER) - scopes_update(&sima->scopes, ibuf, &scene->view_settings, &scene->display_settings); + ED_space_image_scopes_update(C, sima, ibuf, true); else - scopes_update(&sima->scopes, ibuf, NULL, &scene->display_settings); + ED_space_image_scopes_update(C, sima, ibuf, false); } } ED_space_image_release_buffer(sima, ibuf, lock); diff --git a/source/blender/editors/space_logic/logic_ops.c b/source/blender/editors/space_logic/logic_ops.c index 62703ba517e..2c6280f5670 100644 --- a/source/blender/editors/space_logic/logic_ops.c +++ b/source/blender/editors/space_logic/logic_ops.c @@ -39,6 +39,8 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "BKE_context.h" #include "BKE_main.h" #include "BKE_sca.h" @@ -298,7 +300,7 @@ static int sensor_add_exec(bContext *C, wmOperator *op) BLI_strncpy(sens->name, sens_name, sizeof(sens->name)); } - make_unique_prop_names(C, sens->name); + BLI_uniquename(&ob->sensors, sens, DATA_("Sensor"), '.', offsetof(bSensor, name), sizeof(sens->name)); ob->scaflag |= OB_SHOWSENS; WM_event_add_notifier(C, NC_LOGIC, NULL); @@ -405,7 +407,8 @@ static int controller_add_exec(bContext *C, wmOperator *op) BLI_strncpy(cont->name, cont_name, sizeof(cont->name)); } - make_unique_prop_names(C, cont->name); + BLI_uniquename(&ob->controllers, cont, DATA_("Controller"), '.', offsetof(bController, name), sizeof(cont->name)); + /* set the controller state mask from the current object state. * A controller is always in a single state, so select the lowest bit set * from the object state */ @@ -523,7 +526,7 @@ static int actuator_add_exec(bContext *C, wmOperator *op) BLI_strncpy(act->name, act_name, sizeof(act->name)); } - make_unique_prop_names(C, act->name); + BLI_uniquename(&ob->actuators, act, DATA_("Actuator"), '.', offsetof(bActuator, name), sizeof(act->name)); ob->scaflag |= OB_SHOWACT; WM_event_add_notifier(C, NC_LOGIC, NULL); diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c index 21a9246d7e6..37c634672df 100644 --- a/source/blender/editors/space_logic/logic_window.c +++ b/source/blender/editors/space_logic/logic_window.c @@ -48,6 +48,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_path_util.h" #include "BKE_action.h" #include "BKE_context.h" @@ -94,87 +95,6 @@ /* proto */ static ID **get_selected_and_linked_obs(bContext *C, short *count, short scavisflag); -static int vergname(const void *v1, const void *v2) -{ - const char * const *x1 = v1, * const *x2 = v2; - return BLI_natstrcmp(*x1, *x2); -} - -void make_unique_prop_names(bContext *C, char *str) -{ - Object *ob; - bProperty *prop; - bSensor *sens; - bController *cont; - bActuator *act; - ID **idar; - short a, obcount, propcount=0, nr; - const char **names; - - /* this function is called by a Button, and gives the current - * stringpointer as an argument, this is the one that can change - */ - - idar= get_selected_and_linked_obs(C, &obcount, BUTS_SENS_SEL|BUTS_SENS_ACT|BUTS_ACT_SEL|BUTS_ACT_ACT|BUTS_CONT_SEL|BUTS_CONT_ACT); - - /* for each object, make properties and sca names unique */ - - /* count total names */ - for (a=0; a<obcount; a++) { - ob= (Object *)idar[a]; - propcount+= BLI_listbase_count(&ob->prop); - propcount+= BLI_listbase_count(&ob->sensors); - propcount+= BLI_listbase_count(&ob->controllers); - propcount+= BLI_listbase_count(&ob->actuators); - } - if (propcount==0) { - if (idar) MEM_freeN(idar); - return; - } - - /* make names array for sorting */ - names= MEM_callocN(propcount*sizeof(void *), "names"); - - /* count total names */ - nr= 0; - for (a=0; a<obcount; a++) { - ob= (Object *)idar[a]; - prop= ob->prop.first; - while (prop) { - names[nr++] = prop->name; - prop= prop->next; - } - sens= ob->sensors.first; - while (sens) { - names[nr++] = sens->name; - sens= sens->next; - } - cont= ob->controllers.first; - while (cont) { - names[nr++] = cont->name; - cont= cont->next; - } - act= ob->actuators.first; - while (act) { - names[nr++] = act->name; - act= act->next; - } - } - - qsort(names, propcount, sizeof(void *), vergname); - - /* now we check for double names, and change them */ - - for (nr=0; nr<propcount; nr++) { - if (names[nr]!=str && strcmp( names[nr], str )==0 ) { - BLI_newname(str, +1); - } - } - - MEM_freeN(idar); - MEM_freeN(names); -} - static void do_logic_buts(bContext *C, void *UNUSED(arg), int event) { Main *bmain= CTX_data_main(C); @@ -206,7 +126,7 @@ static void do_logic_buts(bContext *C, void *UNUSED(arg), int event) ob->scaflag &= ~OB_ADDSENS; sens= new_sensor(SENS_ALWAYS); BLI_addtail(&(ob->sensors), sens); - make_unique_prop_names(C, sens->name); + BLI_uniquename(&ob->sensors, sens, DATA_("Sensor"), '.', offsetof(bSensor, name), sizeof(sens->name)); ob->scaflag |= OB_SHOWSENS; } } @@ -248,7 +168,7 @@ static void do_logic_buts(bContext *C, void *UNUSED(arg), int event) if (ob->scaflag & OB_ADDCONT) { ob->scaflag &= ~OB_ADDCONT; cont= new_controller(CONT_LOGIC_AND); - make_unique_prop_names(C, cont->name); + BLI_uniquename(&ob->controllers, cont, DATA_("Controller"), '.', offsetof(bController, name), sizeof(cont->name)); ob->scaflag |= OB_SHOWCONT; BLI_addtail(&(ob->controllers), cont); /* set the controller state mask from the current object state. @@ -324,7 +244,7 @@ static void do_logic_buts(bContext *C, void *UNUSED(arg), int event) if (ob->scaflag & OB_ADDACT) { ob->scaflag &= ~OB_ADDACT; act= new_actuator(ACT_OBJECT); - make_unique_prop_names(C, act->name); + BLI_uniquename(&ob->actuators, act, DATA_("Actuator"), '.', offsetof(bActuator, name), sizeof(act->name)); BLI_addtail(&(ob->actuators), act); ob->scaflag |= OB_SHOWACT; } diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 0abe5e42c3e..5476d1c2683 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -650,7 +650,7 @@ static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; /* get a list of the AnimData blocks being shown in the NLA */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* delete tracks */ diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index c45626e4284..2089dced7e3 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -38,6 +38,7 @@ #include "DNA_space_types.h" #include "DNA_screen_types.h" #include "DNA_userdef_types.h" +#include "DNA_text_types.h" #include "BKE_context.h" #include "BKE_curve.h" @@ -389,6 +390,7 @@ static void node_draw_frame_label(bNodeTree *ntree, bNode *node, const float asp float width, ascender; float x, y; const int font_size = data->label_size / aspect; + const float margin = (float)(NODE_DY / 4); nodeLabel(ntree, node, label, sizeof(label)); @@ -404,11 +406,42 @@ static void node_draw_frame_label(bNodeTree *ntree, bNode *node, const float asp /* 'x' doesn't need aspect correction */ x = BLI_rctf_cent_x(rct) - (0.5f * width); - y = rct->ymax - (((NODE_DY / 4) / aspect) + (ascender * aspect)); + y = rct->ymax - ((margin / aspect) + (ascender * aspect)); BLF_position(fontid, x, y, 0); BLF_draw(fontid, label, BLF_DRAW_STR_DUMMY_MAX); + /* draw text body */ + if (node->id) { + Text *text = (Text *)node->id; + TextLine *line; + const float line_spacing = (BLF_height_max(fontid) * aspect) * 0.7f; + + /* 'x' doesn't need aspect correction */ + x = rct->xmin + margin; + y = rct->ymax - ((margin / aspect) + (ascender * aspect)); + y -= line_spacing; + + BLF_enable(fontid, BLF_CLIPPING); + BLF_clipping( + fontid, + rct->xmin, + rct->ymin, + rct->xmin + ((rct->xmax - rct->xmin) / aspect) - margin, + rct->ymax); + + for (line = text->lines.first; line; line = line->next) { + BLF_position(fontid, x, y, 0); + BLF_draw(fontid, line->line, line->len); + y -= line_spacing; + if (y < rct->ymin) { + break; + } + } + + BLF_disable(fontid, BLF_CLIPPING); + } + BLF_disable(fontid, BLF_ASPECT); } @@ -498,6 +531,7 @@ static void node_buts_frame_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA { uiItemR(layout, ptr, "label_size", 0, IFACE_("Label Size"), ICON_NONE); uiItemR(layout, ptr, "shrink", 0, IFACE_("Shrink"), ICON_NONE); + uiItemR(layout, ptr, "text", 0, NULL, ICON_NONE); } @@ -669,10 +703,10 @@ static void node_buts_image_user(uiLayout *layout, bContext *C, PointerRNA *ptr, uiItemR(col, ptr, "use_auto_refresh", 0, NULL, ICON_NONE); } - col = uiLayoutColumn(layout, false); - - if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER) + if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER) { + col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "layer", 0, NULL, ICON_NONE); + } } static void node_shader_buts_material(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -805,10 +839,51 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateID(layout, C, ptr, "image", NULL, "IMAGE_OT_open", NULL); + + node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr); + uiItemR(layout, ptr, "color_space", 0, "", ICON_NONE); uiItemR(layout, ptr, "projection", 0, "", ICON_NONE); +} - node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr); +static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + PointerRNA imaptr = RNA_pointer_get(ptr, "image"); + PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); + Image *ima = imaptr.data; + + uiLayoutSetContextPointer(layout, "image_user", &iuserptr); + uiTemplateID(layout, C, ptr, "image", ima ? NULL : "IMAGE_OT_new", "IMAGE_OT_open", NULL); + + if (!ima) + return; + + uiItemR(layout, &imaptr, "source", 0, IFACE_("Source"), ICON_NONE); + + if (!(ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER))) { + uiLayout *row = uiLayoutRow(layout, true); + + if (ima->packedfile) + uiItemO(row, "", ICON_PACKAGE, "image.unpack"); + else + uiItemO(row, "", ICON_UGLYPACKAGE, "image.pack"); + + row = uiLayoutRow(row, true); + uiLayoutSetEnabled(row, ima->packedfile == NULL); + uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE); + uiItemO(row, "", ICON_FILE_REFRESH, "image.reload"); + } + + /* multilayer? */ + if (ima->type == IMA_TYPE_MULTILAYER && ima->rr) { + uiTemplateImageLayers(layout, C, ima, iuserptr.data); + } + else if (ima->source != IMA_SRC_GENERATED) { + uiTemplateImageInfo(layout, C, ima, iuserptr.data); + } + + uiItemR(layout, ptr, "color_space", 0, IFACE_("Color Space"), ICON_NONE); + uiItemR(layout, ptr, "projection", 0, IFACE_("Projection"), ICON_NONE); } static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -861,6 +936,7 @@ static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C), static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "object", 0, NULL, 0); uiItemR(layout, ptr, "from_dupli", 0, NULL, 0); } @@ -1071,6 +1147,7 @@ static void node_shader_set_butfunc(bNodeType *ntype) break; case SH_NODE_TEX_ENVIRONMENT: ntype->draw_buttons = node_shader_buts_tex_environment; + ntype->draw_buttons_ex = node_shader_buts_tex_environment_ex; break; case SH_NODE_TEX_GRADIENT: ntype->draw_buttons = node_shader_buts_tex_gradient; @@ -2279,6 +2356,7 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr) { bNode *node = ptr->data; + NodePlaneTrackDeformData *data = node->storage; uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL); @@ -2307,6 +2385,12 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P uiItemR(layout, ptr, "plane_track_name", 0, "", ICON_ANIM_DATA); } } + + uiItemR(layout, ptr, "use_motion_blur", 0, NULL, ICON_NONE); + if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { + uiItemR(layout, ptr, "motion_blur_samples", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", 0, NULL, ICON_NONE); + } } static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout), bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) @@ -3053,9 +3137,7 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b /* somehow the offset has to be calculated inverse */ glaDefine2DArea(&ar->winrct); - /* ortho at pixel level curarea */ - /* almost #wmOrtho2_region_pixelspace, but no +1 px */ - wmOrtho2_pixelspace(ar->winx, ar->winy); + wmOrtho2_region_pixelspace(ar); x = (ar->winx - snode->zoom * ibuf->x) / 2 + snode->xof; y = (ar->winy - snode->zoom * ibuf->y) / 2 + snode->yof; diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 16ad63bcafa..87a64e95e63 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1304,6 +1304,11 @@ void drawnodespace(const bContext *C, ARegion *ar) path = snode->treepath.last; + /* update tree path name (drawn in the bottom left) */ + if (snode->id && UNLIKELY(!STREQ(path->node_name, snode->id->name + 2))) { + BLI_strncpy(path->node_name, snode->id->name + 2, sizeof(path->node_name)); + } + /* current View2D center, will be set temporarily for parent node trees */ UI_view2d_center_get(v2d, ¢er[0], ¢er[1]); diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 1e258368b8b..f79d6e26f22 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -566,7 +566,7 @@ void snode_set_context(const bContext *C) return; } - if (snode->nodetree && strcmp(snode->nodetree->idname, snode->tree_idname) != 0) { + if (snode->nodetree && !STREQ(snode->nodetree->idname, snode->tree_idname)) { /* current tree does not match selected type, clear tree path */ ntree = NULL; id = NULL; diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index e3baddef158..62cb0bc73cd 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -668,7 +668,7 @@ static void node_main_area_draw(const bContext *C, ARegion *ar) static int node_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_IM) return 1; } @@ -682,7 +682,7 @@ static int node_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent * static int node_mask_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_MSK) return 1; } @@ -691,14 +691,14 @@ static int node_mask_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent static void node_id_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; RNA_string_set(drop->ptr, "name", id->name + 2); } static void node_id_path_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (id) { RNA_string_set(drop->ptr, "name", id->name + 2); diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 9bc043b384e..47bf87b360b 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -31,6 +31,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_object_types.h" @@ -346,6 +347,11 @@ static void restrictbutton_ebone_visibility_cb(bContext *C, void *UNUSED(poin), WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); } +static void restrictbutton_gp_layer_flag_cb(bContext *C, void *UNUSED(poin), void *UNUSED(poin2)) +{ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); +} + static int group_restrict_flag(Group *gr, int flag) { GroupObject *gob; @@ -447,7 +453,7 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) TreeStoreElem *tselem = tsep; if (ts && tselem) { - TreeElement *te = outliner_find_tse(soops, tselem); + TreeElement *te = outliner_find_tree_element(&soops->tree, tselem); if (tselem->type == 0) { test_idbutton(tselem->id->name); // library.c, unique name and alpha sort @@ -549,6 +555,17 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); break; } + case TSE_GP_LAYER: + { + bGPdata *gpd = (bGPdata *)tselem->id; // id = GP Datablock + bGPDlayer *gpl = te->directdata; + + // XXX: name needs translation stuff + BLI_uniquename(&gpd->layers, gpl, "GP Layer", '.', + offsetof(bGPDlayer, info), sizeof(gpl->info)); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, gpd); + break; + } case TSE_R_LAYER: break; } @@ -742,6 +759,29 @@ static void outliner_draw_restrictbuts(uiBlock *block, Scene *scene, ARegion *ar UI_block_emboss_set(block, UI_EMBOSS); } + else if (tselem->type == TSE_GP_LAYER) { + bGPDlayer *gpl = (bGPDlayer *)te->directdata; + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + + bt = uiDefIconButBitS(block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_HIDE, 0, ICON_RESTRICT_VIEW_OFF, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0, + TIP_("Restrict/Allow visibility in the 3D View")); + UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, NULL, gpl); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + + bt = uiDefIconButBitS(block, UI_BTYPE_ICON_TOGGLE, GP_LAYER_LOCKED, 0, ICON_UNLOCKED, + (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, + UI_UNIT_Y, &gpl->flag, 0, 0, 0, 0, + TIP_("Restrict/Allow editing of strokes and keyframes in this layer")); + UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, NULL, gpl); + UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); + + /* TODO: visibility in renders */ + + UI_block_emboss_set(block, UI_EMBOSS); + } } if (TSELEM_OPEN(tselem, soops)) outliner_draw_restrictbuts(block, scene, ar, soops, &te->subtree); @@ -877,6 +917,36 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon) } +static void tselem_draw_gp_icon_uibut(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl) +{ + /* restrict column clip - skip it for now... */ + if (arg->x >= arg->xmax) { + /* pass */ + } + else { + PointerRNA ptr; + float w = 0.85f * U.widget_unit; + float h = 0.85f * UI_UNIT_Y; + + RNA_pointer_create(id, &RNA_GPencilLayer, gpl, &ptr); + + UI_block_align_begin(arg->block); + + UI_block_emboss_set(arg->block, RNA_boolean_get(&ptr, "is_stroke_visible") ? UI_EMBOSS : UI_EMBOSS_NONE); + uiDefButR(arg->block, UI_BTYPE_COLOR, 1, "", arg->xb, arg->yb, w, h, + &ptr, "color", -1, + 0, 0, 0, 0, NULL); + + UI_block_emboss_set(arg->block, RNA_boolean_get(&ptr, "is_fill_visible") ? UI_EMBOSS : UI_EMBOSS_NONE); + uiDefButR(arg->block, UI_BTYPE_COLOR, 1, "", arg->xb, arg->yb, w, h, + &ptr, "fill_color", -1, + 0, 0, 0, 0, NULL); + + UI_block_emboss_set(arg->block, UI_EMBOSS_NONE); + UI_block_align_end(arg->block); + } +} + static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeStoreElem *tselem, TreeElement *te, float alpha) { @@ -1020,6 +1090,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ case eModifierType_DataTransfer: UI_icon_draw(x, y, ICON_MOD_DATA_TRANSFER); break; + case eModifierType_NormalEdit: + UI_icon_draw(x, y, ICON_MOD_NORMALEDIT); break; case eModifierType_PointCache: UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ /* Default */ @@ -1074,6 +1146,9 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto else UI_icon_draw(x, y, RNA_struct_ui_icon(te->rnaptr.type)); break; + case TSE_GP_LAYER: + tselem_draw_gp_icon_uibut(&arg, tselem->id, te->directdata); + break; default: UI_icon_draw(x, y, ICON_DOT); break; } @@ -1167,6 +1242,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_DIRECT); break; case ID_LS: tselem_draw_icon_uibut(&arg, ICON_LINE_DATA); break; + case ID_GD: + tselem_draw_icon_uibut(&arg, ICON_GREASEPENCIL); break; } } } @@ -1250,8 +1327,9 @@ static void outliner_set_coord_tree_element(SpaceOops *soops, TreeElement *te, i } -static void outliner_draw_tree_element(bContext *C, uiBlock *block, Scene *scene, ARegion *ar, SpaceOops *soops, - TreeElement *te, int startx, int *starty, TreeElement **te_edit) +static void outliner_draw_tree_element( + bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ARegion *ar, SpaceOops *soops, + TreeElement *te, int startx, int *starty, TreeElement **te_edit) { TreeElement *ten; TreeStoreElem *tselem; @@ -1410,9 +1488,9 @@ static void outliner_draw_tree_element(bContext *C, uiBlock *block, Scene *scene else if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) UI_ThemeColorBlend(TH_BACK, TH_TEXT, 0.75f); else UI_ThemeColor(TH_TEXT); - UI_draw_string(startx + offsx, *starty + 5 * ufac, te->name); + UI_fontstyle_draw_simple(fstyle, startx + offsx, *starty + 5 * ufac, te->name); - offsx += (int)(UI_UNIT_X + UI_fontstyle_string_width(te->name)); + offsx += (int)(UI_UNIT_X + UI_fontstyle_string_width(fstyle, te->name)); /* closed item, we draw the icons, not when it's a scene, or master-server list though */ if (!TSELEM_OPEN(tselem, soops)) { @@ -1450,13 +1528,15 @@ static void outliner_draw_tree_element(bContext *C, uiBlock *block, Scene *scene if (TSELEM_OPEN(tselem, soops)) { *starty -= UI_UNIT_Y; - - for (ten = te->subtree.first; ten; ten = ten->next) - outliner_draw_tree_element(C, block, scene, ar, soops, ten, startx + UI_UNIT_X, starty, te_edit); + + for (ten = te->subtree.first; ten; ten = ten->next) { + outliner_draw_tree_element(C, block, fstyle, scene, ar, soops, ten, startx + UI_UNIT_X, starty, te_edit); + } } else { - for (ten = te->subtree.first; ten; ten = ten->next) + for (ten = te->subtree.first; ten; ten = ten->next) { outliner_set_coord_tree_element(soops, ten, startx, *starty); + } *starty -= UI_UNIT_Y; } @@ -1539,6 +1619,7 @@ static void outliner_draw_selection(ARegion *ar, SpaceOops *soops, ListBase *lb, static void outliner_draw_tree(bContext *C, uiBlock *block, Scene *scene, ARegion *ar, SpaceOops *soops, TreeElement **te_edit) { + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; TreeElement *te; int starty, startx; float col[3]; @@ -1569,7 +1650,7 @@ static void outliner_draw_tree(bContext *C, uiBlock *block, Scene *scene, ARegio starty = (int)ar->v2d.tot.ymax - UI_UNIT_Y - OL_Y_OFFSET; startx = 0; for (te = soops->tree.first; te; te = te->next) { - outliner_draw_tree_element(C, block, scene, ar, soops, te, startx, &starty, te_edit); + outliner_draw_tree_element(C, block, fstyle, scene, ar, soops, te, startx, &starty, te_edit); } } diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index ab7b1583275..d17ab33d0fa 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -50,6 +50,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_material.h" +#include "BKE_group.h" #include "ED_object.h" #include "ED_screen.h" @@ -73,22 +74,6 @@ /* This is not used anywhere at the moment */ #if 0 -/* return 1 when levels were opened */ -static int outliner_open_back(SpaceOops *soops, TreeElement *te) -{ - TreeStoreElem *tselem; - int retval = 0; - - for (te = te->parent; te; te = te->parent) { - tselem = TREESTORE(te); - if (tselem->flag & TSE_CLOSED) { - tselem->flag &= ~TSE_CLOSED; - retval = 1; - } - } - return retval; -} - static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found) { TreeElement *te; @@ -113,7 +98,9 @@ static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *te } #endif -static TreeElement *outliner_dropzone_element(const SpaceOops *soops, TreeElement *te, const float fmval[2], const int children) +static TreeElement *outliner_dropzone_element( + const SpaceOops *soops, TreeElement *te, + const float fmval[2], const bool children) { if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) { /* name and first icon */ @@ -132,7 +119,7 @@ static TreeElement *outliner_dropzone_element(const SpaceOops *soops, TreeElemen } /* Used for drag and drop parenting */ -TreeElement *outliner_dropzone_find(const SpaceOops *soops, const float fmval[2], const int children) +TreeElement *outliner_dropzone_find(const SpaceOops *soops, const float fmval[2], const bool children) { TreeElement *te; @@ -601,6 +588,50 @@ void OUTLINER_OT_selected_toggle(wmOperatorType *ot) /* Show Active --------------------------------------------------- */ +static void outliner_set_coordinates_element_recursive(SpaceOops *soops, TreeElement *te, int startx, int *starty) +{ + TreeStoreElem *tselem = TREESTORE(te); + + /* store coord and continue, we need coordinates for elements outside view too */ + te->xs = (float)startx; + te->ys = (float)(*starty); + *starty -= UI_UNIT_Y; + + if (TSELEM_OPEN(tselem, soops)) { + TreeElement *ten; + for (ten = te->subtree.first; ten; ten = ten->next) { + outliner_set_coordinates_element_recursive(soops, ten, startx + UI_UNIT_X, starty); + } + } +} + +/* to retrieve coordinates with redrawing the entire tree */ +static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops) +{ + TreeElement *te; + int starty = (int)(ar->v2d.tot.ymax) - UI_UNIT_Y; + + for (te = soops->tree.first; te; te = te->next) { + outliner_set_coordinates_element_recursive(soops, te, 0, &starty); + } +} + +/* return 1 when levels were opened */ +static int outliner_open_back(TreeElement *te) +{ + TreeStoreElem *tselem; + int retval = 0; + + for (te = te->parent; te; te = te->parent) { + tselem = TREESTORE(te); + if (tselem->flag & TSE_CLOSED) { + tselem->flag &= ~TSE_CLOSED; + retval = 1; + } + } + return retval; +} + static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceOops *so = CTX_wm_space_outliner(C); @@ -617,6 +648,11 @@ static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op)) te = outliner_find_id(so, &so->tree, (ID *)OBACT); if (te) { + /* open up tree to active object */ + if (outliner_open_back(te)) { + outliner_set_coordinates(ar, so); + } + /* make te->ys center of view */ ytop = te->ys + BLI_rcti_size_y(&v2d->mask) / 2; if (ytop > 0) ytop = 0; @@ -642,7 +678,7 @@ void OUTLINER_OT_show_active(wmOperatorType *ot) /* identifiers */ ot->name = "Show Active"; ot->idname = "OUTLINER_OT_show_active"; - ot->description = "Adjust the view so that the active Object is shown centered"; + ot->description = "Open up the tree and adjust the view so that the active Object is shown centered"; /* callbacks */ ot->exec = outliner_show_active_exec; @@ -690,37 +726,6 @@ void OUTLINER_OT_scroll_page(wmOperatorType *ot) #if 0 -/* recursive helper for function below */ -static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty) -{ - TreeStoreElem *tselem = TREESTORE(te); - - /* store coord and continue, we need coordinates for elements outside view too */ - te->xs = (float)startx; - te->ys = (float)(*starty); - *starty -= UI_UNIT_Y; - - if (TSELEM_OPEN(tselem, soops)) { - TreeElement *ten; - for (ten = te->subtree.first; ten; ten = ten->next) { - outliner_set_coordinates_element(soops, ten, startx + UI_UNIT_X, starty); - } - } - -} - -/* to retrieve coordinates with redrawing the entire tree */ -static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops) -{ - TreeElement *te; - int starty = (int)(ar->v2d.tot.ymax) - UI_UNIT_Y; - int startx = 0; - - for (te = soops->tree.first; te; te = te->next) { - outliner_set_coordinates_element(soops, te, startx, &starty); - } -} - /* find next element that has this name */ static TreeElement *outliner_find_name(SpaceOops *soops, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound) @@ -1500,7 +1505,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); /* Find object hovered over */ - te = outliner_dropzone_find(soops, fmval, 1); + te = outliner_dropzone_find(soops, fmval, true); if (te) { RNA_string_set(op->ptr, "parent", te->name); @@ -1715,7 +1720,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); /* Find object hovered over */ - te = outliner_dropzone_find(soops, fmval, 0); + te = outliner_dropzone_find(soops, fmval, false); if (te) { Base *base; @@ -1785,7 +1790,7 @@ static int material_drop_invoke(bContext *C, wmOperator *op, const wmEvent *even UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); /* Find object hovered over */ - te = outliner_dropzone_find(soops, fmval, 1); + te = outliner_dropzone_find(soops, fmval, true); if (te) { RNA_string_set(op->ptr, "object", te->name); @@ -1829,3 +1834,65 @@ void OUTLINER_OT_material_drop(wmOperatorType *ot) RNA_def_string(ot->srna, "material", "Material", MAX_ID_NAME, "Material", "Target Material"); } +static int group_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + Group *group = NULL; + Object *ob = NULL; + Scene *scene = CTX_data_scene(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + TreeElement *te = NULL; + char ob_name[MAX_ID_NAME - 2]; + float fmval[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + /* Find object hovered over */ + te = outliner_dropzone_find(soops, fmval, true); + + if (te) { + group = (Group *)BKE_libblock_find_name(ID_GR, te->name); + + RNA_string_get(op->ptr, "object", ob_name); + ob = (Object *)BKE_libblock_find_name(ID_OB, ob_name); + + if (ELEM(NULL, group, ob)) { + return OPERATOR_CANCELLED; + } + if (BKE_group_object_exists(group, ob)) { + return OPERATOR_FINISHED; + } + + if (BKE_group_object_cyclic_check(bmain, ob, group)) { + BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected"); + return OPERATOR_CANCELLED; + } + + BKE_group_object_add(group, ob, scene, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void OUTLINER_OT_group_link(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Link Object to Group"; + ot->description = "Link Object to Group in Outliner"; + ot->idname = "OUTLINER_OT_group_link"; + + /* api callbacks */ + ot->invoke = group_link_invoke; + + ot->poll = ED_operator_outliner_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); +} diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 317d33dd3e2..f0920466b85 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -104,6 +104,7 @@ typedef struct TreeElement { #define TSE_KEYMAP 34 #define TSE_KEYMAP_ITEM 35 #define TSE_ID_BASE 36 +#define TSE_GP_LAYER 37 /* button events */ #define OL_NAMEBUTTON 1 @@ -166,6 +167,7 @@ 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); struct ID *outliner_search_back(SpaceOops *soops, TreeElement *te, short idcode); @@ -205,7 +207,7 @@ void group_toggle_renderability_cb(struct bContext *C, struct Scene *scene, Tree void item_rename_cb(struct bContext *C, struct Scene *scene, TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem); -TreeElement *outliner_dropzone_find(const struct SpaceOops *soops, const float fmval[2], const int children); +TreeElement *outliner_dropzone_find(const struct SpaceOops *soops, const float fmval[2], const bool children); /* ...................................................... */ void OUTLINER_OT_item_activate(struct wmOperatorType *ot); @@ -237,6 +239,7 @@ void OUTLINER_OT_parent_drop(struct wmOperatorType *ot); void OUTLINER_OT_parent_clear(struct wmOperatorType *ot); void OUTLINER_OT_scene_drop(struct wmOperatorType *ot); void OUTLINER_OT_material_drop(struct wmOperatorType *ot); +void OUTLINER_OT_group_link(struct wmOperatorType *ot); /* outliner_tools.c ---------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 4f13454ef34..d54ae3f22a7 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -77,6 +77,7 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_parent_clear); WM_operatortype_append(OUTLINER_OT_scene_drop); WM_operatortype_append(OUTLINER_OT_material_drop); + WM_operatortype_append(OUTLINER_OT_group_link); } void outliner_keymap(wmKeyConfig *keyconf) diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 23586a6a509..730ee02f448 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -775,7 +775,7 @@ static eOLDrawState tree_element_active_sequence_dup( continue; } -// if (!strcmp(p->strip->stripdata->name, seq->strip->stripdata->name)) +// if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) // XXX select_single_seq(p, 0); p = p->next; } @@ -873,6 +873,9 @@ eOLDrawState tree_element_type_active( return tree_element_active_sequence_dup(scene, te, tselem, set); case TSE_KEYMAP_ITEM: return tree_element_active_keymap_item(C, te, tselem, set); + case TSE_GP_LAYER: + //return tree_element_active_gplayer(C, scene, te, tselem, set); + break; } return OL_DRAWSEL_NONE; @@ -915,7 +918,7 @@ static bool do_outliner_item_activate(bContext *C, Scene *scene, ARegion *ar, Sp if (tselem->type != TSE_SEQUENCE && tselem->type != TSE_SEQ_STRIP && tselem->type != TSE_SEQUENCE_DUP) tree_element_set_active_object(C, scene, soops, te, (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, - recursive && tselem->type == 0 ); + recursive && tselem->type == 0); if (tselem->type == 0) { // the lib blocks /* editmode? */ diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index d09ed1a100e..bd884039b30 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -499,19 +499,28 @@ static void refreshdrivers_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te /* --------------------------------- */ +typedef enum eOutliner_PropDataOps { + OL_DOP_INVALID = 0, + OL_DOP_SELECT, + OL_DOP_DESELECT, + OL_DOP_HIDE, + OL_DOP_UNHIDE, + OL_DOP_SELECT_LINKED, +} eOutliner_PropDataOps; + static void pchan_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg)) { bPoseChannel *pchan = (bPoseChannel *)te->directdata; - if (event == 1) + if (event == OL_DOP_SELECT) pchan->bone->flag |= BONE_SELECTED; - else if (event == 2) + else if (event == OL_DOP_DESELECT) pchan->bone->flag &= ~BONE_SELECTED; - else if (event == 3) { + else if (event == OL_DOP_HIDE) { pchan->bone->flag |= BONE_HIDDEN_P; pchan->bone->flag &= ~BONE_SELECTED; } - else if (event == 4) + else if (event == OL_DOP_UNHIDE) pchan->bone->flag &= ~BONE_HIDDEN_P; } @@ -519,15 +528,15 @@ static void bone_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), v { Bone *bone = (Bone *)te->directdata; - if (event == 1) + if (event == OL_DOP_SELECT) bone->flag |= BONE_SELECTED; - else if (event == 2) + else if (event == OL_DOP_DESELECT) bone->flag &= ~BONE_SELECTED; - else if (event == 3) { + else if (event == OL_DOP_HIDE) { bone->flag |= BONE_HIDDEN_P; bone->flag &= ~BONE_SELECTED; } - else if (event == 4) + else if (event == OL_DOP_UNHIDE) bone->flag &= ~BONE_HIDDEN_P; } @@ -535,22 +544,22 @@ static void ebone_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), { EditBone *ebone = (EditBone *)te->directdata; - if (event == 1) + if (event == OL_DOP_SELECT) ebone->flag |= BONE_SELECTED; - else if (event == 2) + else if (event == OL_DOP_DESELECT) ebone->flag &= ~BONE_SELECTED; - else if (event == 3) { + else if (event == OL_DOP_HIDE) { ebone->flag |= BONE_HIDDEN_A; ebone->flag &= ~BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; } - else if (event == 4) + else if (event == OL_DOP_UNHIDE) ebone->flag &= ~BONE_HIDDEN_A; } static void sequence_cb(int event, TreeElement *te, TreeStoreElem *tselem, void *scene_ptr) { Sequence *seq = (Sequence *)te->directdata; - if (event == 1) { + if (event == OL_DOP_SELECT) { Scene *scene = (Scene *)scene_ptr; Editing *ed = BKE_sequencer_editing_get(scene, false); if (BLI_findindex(ed->seqbasep, seq) != -1) { @@ -563,7 +572,7 @@ static void sequence_cb(int event, TreeElement *te, TreeStoreElem *tselem, void static void data_select_linked_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v) { - if (event == 5) { + if (event == OL_DOP_SELECT_LINKED) { if (RNA_struct_is_ID(te->rnaptr.type)) { bContext *C = (bContext *) C_v; ID *id = te->rnaptr.data; @@ -593,6 +602,49 @@ static void outliner_do_data_operation(SpaceOops *soops, int type, int event, Li } } +static void outline_delete_hierarchy(bContext *C, Scene *scene, Base *base) +{ + Base *child_base; + Object *parent; + + if (!base) { + return; + } + + for (child_base = scene->base.first; child_base; child_base = child_base->next) { + for (parent = child_base->object->parent; parent && (parent != base->object); parent = parent->parent); + if (parent) { + outline_delete_hierarchy(C, scene, child_base); + } + } + + ED_base_object_free_and_unlink(CTX_data_main(C), scene, base); +} + +static void object_delete_hierarchy_cb( + bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem) +{ + Base *base = (Base *)te->directdata; + Object *obedit = scene->obedit; + + if (!base) { + base = BKE_scene_base_find(scene, (Object *)tselem->id); + } + if (base) { + /* Check also library later. */ + for (; obedit && (obedit != base->object); obedit = obedit->parent); + if (obedit == base->object) { + ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO); + } + + outline_delete_hierarchy(C, scene, base); + te->directdata = NULL; + tselem->id = NULL; + } + + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); +} + /* **************************************** */ enum { @@ -601,6 +653,7 @@ enum { OL_OP_DESELECT, OL_OP_SELECT_HIERARCHY, OL_OP_DELETE, + OL_OP_DELETE_HIERARCHY, OL_OP_LOCALIZED, /* disabled, see below */ OL_OP_TOGVIS, OL_OP_TOGSEL, @@ -613,6 +666,7 @@ static EnumPropertyItem prop_object_op_types[] = { {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""}, {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""}, {OL_OP_DELETE, "DELETE", 0, "Delete", ""}, + {OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""}, {OL_OP_TOGVIS, "TOGVIS", 0, "Toggle Visible", ""}, {OL_OP_TOGSEL, "TOGSEL", 0, "Toggle Selectable", ""}, {OL_OP_TOGREN, "TOGREN", 0, "Toggle Renderable", ""}, @@ -672,6 +726,16 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) str = "Delete Objects"; WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); } + else if (event == OL_OP_DELETE_HIERARCHY) { + outliner_do_object_operation(C, scene, soops, &soops->tree, object_delete_hierarchy_cb); + + /* XXX: See OL_OP_DELETE comment above. */ + outliner_cleanup_tree(soops); + + DAG_relations_tag_update(bmain); + str = "Delete Object Hierarchy"; + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + } else if (event == OL_OP_LOCALIZED) { /* disabled, see above enum (ton) */ outliner_do_object_operation(C, scene, soops, &soops->tree, id_local_cb); str = "Localized Objects"; @@ -1196,11 +1260,11 @@ void OUTLINER_OT_animdata_operation(wmOperatorType *ot) /* **************************************** */ static EnumPropertyItem prop_data_op_types[] = { - {1, "SELECT", 0, "Select", ""}, - {2, "DESELECT", 0, "Deselect", ""}, - {3, "HIDE", 0, "Hide", ""}, - {4, "UNHIDE", 0, "Unhide", ""}, - {5, "SELECT_LINKED", 0, "Select Linked", ""}, + {OL_DOP_SELECT, "SELECT", 0, "Select", ""}, + {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""}, + {OL_DOP_HIDE, "HIDE", 0, "Hide", ""}, + {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""}, + {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, {0, NULL, 0, NULL, NULL} }; @@ -1208,7 +1272,7 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) { SpaceOops *soops = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - int event; + eOutliner_PropDataOps event; /* check for invalid states */ if (soops == NULL) @@ -1217,7 +1281,7 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) event = RNA_enum_get(op->ptr, "type"); set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel); - if (event <= 0) + if (event <= OL_DOP_INVALID) return OPERATOR_CANCELLED; switch (datalevel) { @@ -1253,7 +1317,7 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) break; case TSE_RNA_STRUCT: - if (event == 5) { + if (event == OL_DOP_SELECT_LINKED) { outliner_do_data_operation(soops, datalevel, event, &soops->tree, data_select_linked_cb, C); } break; diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 4aa36da594b..abc82775c8d 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -38,6 +38,7 @@ #include "DNA_armature_types.h" #include "DNA_constraint_types.h" #include "DNA_camera_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" @@ -196,7 +197,7 @@ void outliner_cleanup_tree(SpaceOops *soops) } /* Find specific item from the treestore */ -static TreeElement *outliner_find_tree_element(ListBase *lb, TreeStoreElem *store_elem) +TreeElement *outliner_find_tree_element(ListBase *lb, TreeStoreElem *store_elem) { TreeElement *te, *tes; for (te = lb->first; te; te = te->next) { @@ -430,6 +431,8 @@ static void outliner_add_scene_contents(SpaceOops *soops, ListBase *lb, Scene *s // TODO: move this to the front? if (outliner_animdata_test(sce->adt)) outliner_add_element(soops, lb, sce, te, TSE_ANIM_DATA, 0); + + outliner_add_element(soops, lb, sce->gpd, te, 0, 0); outliner_add_element(soops, lb, sce->world, te, 0, 0); @@ -451,6 +454,8 @@ static void outliner_add_object_contents(SpaceOops *soops, TreeElement *te, Tree if (ob->proxy && ob->id.lib == NULL) outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0); + + outliner_add_element(soops, &te->subtree, ob->gpd, te, 0, 0); outliner_add_element(soops, &te->subtree, ob->data, te, 0, 0); @@ -809,6 +814,21 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor } break; } + case ID_GD: + { + bGPdata *gpd = (bGPdata *)id; + bGPDlayer *gpl; + int a = 0; + + if (outliner_animdata_test(gpd->adt)) + outliner_add_element(soops, &te->subtree, gpd, te, TSE_ANIM_DATA, 0); + + // TODO: base element for layers? + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + outliner_add_element(soops, &te->subtree, gpl, te, TSE_GP_LAYER, a); + a++; + } + } } } @@ -856,6 +876,9 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i else if (type == TSE_ANIM_DATA) { /* pass */ } + else if (type == TSE_GP_LAYER) { + /* pass */ + } else if (type == TSE_ID_BASE) { /* pass */ } @@ -938,6 +961,12 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i } } } + else if (type == TSE_GP_LAYER) { + bGPDlayer *gpl = (bGPDlayer *)idv; + + te->name = gpl->info; + te->directdata = gpl; + } else if (type == TSE_SEQUENCE) { Sequence *seq = (Sequence *) idv; Sequence *p; @@ -1160,7 +1189,7 @@ static int need_add_seq_dup(Sequence *seq) continue; } - if (!strcmp(p->strip->stripdata->name, seq->strip->stripdata->name)) + if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) return(2); p = p->prev; } @@ -1172,7 +1201,7 @@ static int need_add_seq_dup(Sequence *seq) continue; } - if (!strcmp(p->strip->stripdata->name, seq->strip->stripdata->name)) + if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) return(0); p = p->next; } @@ -1191,7 +1220,7 @@ static void outliner_add_seq_dup(SpaceOops *soops, Sequence *seq, TreeElement *t continue; } - if (!strcmp(p->strip->stripdata->name, seq->strip->stripdata->name)) + if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) /* ch = */ /* UNUSED */ outliner_add_element(soops, &te->subtree, (void *)p, te, TSE_SEQUENCE, index); p = p->next; } @@ -1311,7 +1340,10 @@ static void outliner_sort(SpaceOops *soops, ListBase *lb) TreeElement *te; TreeStoreElem *tselem; int totelem = 0; - + + if (soops->flag & SO_SKIP_SORT_ALPHA) + return; + te = lb->last; if (te == NULL) return; tselem = TREESTORE(te); diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 504d9628d98..30de9c16500 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -98,10 +98,10 @@ static int outliner_parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *e UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_OB) { /* Ensure item under cursor is valid drop target */ - TreeElement *te = outliner_dropzone_find(soops, fmval, 1); + TreeElement *te = outliner_dropzone_find(soops, fmval, true); if (te && te->idcode == ID_OB && TREESTORE(te)->type == 0) { Scene *scene; @@ -129,7 +129,7 @@ static int outliner_parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *e static void outliner_parent_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; RNA_string_set(drop->ptr, "child", id->name + 2); } @@ -148,10 +148,10 @@ static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent * } if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_OB) { if (((Object *)id)->parent) { - if ((te = outliner_dropzone_find(soops, fmval, 1))) { + if ((te = outliner_dropzone_find(soops, fmval, true))) { TreeStoreElem *tselem = TREESTORE(te); switch (te->idcode) { @@ -171,7 +171,7 @@ static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent * static void outliner_parent_clear_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; RNA_string_set(drop->ptr, "dragged_obj", id->name + 2); /* Set to simple parent clear type. Avoid menus for drag and drop if possible. @@ -188,10 +188,10 @@ static int outliner_scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *ev UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_OB) { /* Ensure item under cursor is valid drop target */ - TreeElement *te = outliner_dropzone_find(soops, fmval, 0); + TreeElement *te = outliner_dropzone_find(soops, fmval, false); return (te && te->idcode == ID_SCE && TREESTORE(te)->type == 0); } } @@ -200,7 +200,7 @@ static int outliner_scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *ev static void outliner_scene_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; RNA_string_set(drop->ptr, "object", id->name + 2); } @@ -213,10 +213,10 @@ static int outliner_material_drop_poll(bContext *C, wmDrag *drag, const wmEvent UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_MA) { /* Ensure item under cursor is valid drop target */ - TreeElement *te = outliner_dropzone_find(soops, fmval, 1); + TreeElement *te = outliner_dropzone_find(soops, fmval, true); return (te && te->idcode == ID_OB && TREESTORE(te)->type == 0); } } @@ -225,11 +225,35 @@ static int outliner_material_drop_poll(bContext *C, wmDrag *drag, const wmEvent static void outliner_material_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; RNA_string_set(drop->ptr, "material", id->name + 2); } +static int outliner_group_link_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + float fmval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + if (drag->type == WM_DRAG_ID) { + ID *id = drag->poin; + if (GS(id->name) == ID_OB) { + /* Ensure item under cursor is valid drop target */ + TreeElement *te = outliner_dropzone_find(soops, fmval, true); + return (te && te->idcode == ID_GR && TREESTORE(te)->type == 0); + } + } + return 0; +} + +static void outliner_group_link_copy(wmDrag *drag, wmDropBox *drop) +{ + ID *id = drag->poin; + RNA_string_set(drop->ptr, "object", id->name + 2); +} + /* region dropbox definition */ static void outliner_dropboxes(void) { @@ -239,6 +263,7 @@ static void outliner_dropboxes(void) WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", outliner_parent_clear_poll, outliner_parent_clear_copy); WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", outliner_scene_drop_poll, outliner_scene_drop_copy); WM_dropbox_add(lb, "OUTLINER_OT_material_drop", outliner_material_drop_poll, outliner_material_drop_copy); + WM_dropbox_add(lb, "OUTLINER_OT_group_link", outliner_group_link_poll, outliner_group_link_copy); } static void outliner_main_area_draw(const bContext *C, ARegion *ar) @@ -362,6 +387,13 @@ static void outliner_main_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa) break; } break; + case NC_GPENCIL: + switch (wmn->data) { + case ND_DATA: + ED_region_tag_redraw(ar); + break; + } + break; } } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index afb650f8cfd..3be6cd79504 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -70,6 +70,7 @@ #include "WM_api.h" +#include "MEM_guardedalloc.h" /* own include */ #include "sequencer_intern.h" @@ -200,23 +201,25 @@ static void drawseqwave(const bContext *C, SpaceSeq *sseq, Scene *scene, Sequenc SoundWaveform *waveform; - if (!sound->mutex) - sound->mutex = BLI_mutex_alloc(); + if (!sound->spinlock) { + sound->spinlock = MEM_mallocN(sizeof(SpinLock), "sound_spinlock"); + BLI_spin_init(sound->spinlock); + } - BLI_mutex_lock(sound->mutex); + BLI_spin_lock(sound->spinlock); if (!seq->sound->waveform) { if (!(sound->flags & SOUND_FLAGS_WAVEFORM_LOADING)) { /* prevent sounds from reloading */ seq->sound->flags |= SOUND_FLAGS_WAVEFORM_LOADING; - BLI_mutex_unlock(sound->mutex); + BLI_spin_unlock(sound->spinlock); sequencer_preview_add_sound(C, seq); } else { - BLI_mutex_unlock(sound->mutex); + BLI_spin_unlock(sound->spinlock); } return; /* nothing to draw */ } - BLI_mutex_unlock(sound->mutex); + BLI_spin_unlock(sound->spinlock); waveform = seq->sound->waveform; @@ -467,7 +470,7 @@ static void draw_seq_text(View2D *v2d, Sequence *seq, float x1, float x2, float } } else if (seq->type == SEQ_TYPE_MOVIECLIP) { - if (seq->clip && strcmp(name, seq->clip->id.name + 2) != 0) { + if (seq->clip && !STREQ(name, seq->clip->id.name + 2)) { str_len = BLI_snprintf(str, sizeof(str), "%s: %s | %d", name, seq->clip->id.name + 2, seq->len); } @@ -477,7 +480,7 @@ static void draw_seq_text(View2D *v2d, Sequence *seq, float x1, float x2, float } } else if (seq->type == SEQ_TYPE_MASK) { - if (seq->mask && strcmp(name, seq->mask->id.name + 2) != 0) { + if (seq->mask && !STREQ(name, seq->mask->id.name + 2)) { str_len = BLI_snprintf(str, sizeof(str), "%s: %s | %d", name, seq->mask->id.name + 2, seq->len); } @@ -1292,24 +1295,18 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq glEnd(); /* safety border */ - if ((sseq->flag & SEQ_DRAW_SAFE_MARGINS) != 0) { - float fac = 0.1; - - float a = fac * (x2 - x1); - x1 += a; - x2 -= a; - - a = fac * (y2 - y1); - y1 += a; - y2 -= a; - - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_gl_mode(GL_LINE_LOOP, x1, y1, x2, y2, 12.0); - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - + if (sseq->flag & SEQ_SHOW_SAFE_MARGINS) { + UI_draw_safe_areas( + x1, x2, y1, y2, + scene->safe_areas.title, + scene->safe_areas.action); + + if (sseq->flag & SEQ_SHOW_SAFE_CENTER) { + UI_draw_safe_areas( + x1, x2, y1, y2, + scene->safe_areas.title_center, + scene->safe_areas.action_center); + } } setlinestyle(0); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 81af6dccc70..8d4a82fc02c 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -37,6 +37,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_ghash.h" #include "BLF_translation.h" @@ -58,12 +59,14 @@ /* for menu/popup icons etc etc*/ +#include "ED_numinput.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_sequencer.h" #include "ED_space_api.h" #include "UI_view2d.h" +#include "UI_interface.h" /* own include */ @@ -146,11 +149,12 @@ static void proxy_startjob(void *pjv, short *stop, short *do_update, float *prog struct SeqIndexBuildContext *context = link->data; BKE_sequencer_proxy_rebuild(context, stop, do_update, progress); - } - - if (*stop) { - pj->stop = 1; - fprintf(stderr, "Canceling proxy rebuild on users request...\n"); + + if (*stop) { + pj->stop = 1; + fprintf(stderr, "Canceling proxy rebuild on users request...\n"); + break; + } } } @@ -179,7 +183,8 @@ static void seq_proxy_build_job(const bContext *C) struct SeqIndexBuildContext *context; LinkData *link; Sequence *seq; - + GSet *file_list; + if (ed == NULL) { return; } @@ -200,16 +205,19 @@ static void seq_proxy_build_job(const bContext *C) WM_jobs_callbacks(wm_job, proxy_startjob, NULL, NULL, proxy_endjob); } + file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); SEQP_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { - context = BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq); + context = BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list); link = BLI_genericNodeN(context); BLI_addtail(&pj->queue, link); } } SEQ_END + BLI_gset_free(file_list, MEM_freeN); + if (!WM_jobs_is_running(wm_job)) { G.is_break = false; WM_jobs_start(CTX_wm_manager(C), wm_job); @@ -987,13 +995,13 @@ static void UNUSED_FUNCTION(seq_remap_paths) (Scene *scene) // XXX if (0 == sbutton(to, 0, sizeof(to)-1, "To: ")) // return; - if (strcmp(to, from) == 0) + if (STREQ(to, from)) return; SEQP_BEGIN (ed, seq) { if (seq->flag & SELECT) { - if (strncmp(seq->strip->dir, from, strlen(from)) == 0) { + if (STREQLEN(seq->strip->dir, from, strlen(from))) { printf("found %s\n", seq->strip->dir); /* strip off the beginning */ @@ -1262,6 +1270,7 @@ typedef struct SlipData { bool slow; int slow_offset; /* offset at the point where offset was turned on */ void *draw_handle; + NumInput num_input; } SlipData; static void transseq_backup(TransSeq *ts, Sequence *seq) @@ -1375,6 +1384,13 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve data->trim = MEM_mallocN(num_seq * sizeof(bool), "trimdata_trim"); data->num_seq = num_seq; + initNumInput(&data->num_input); + data->num_input.idx_max = 0; + data->num_input.val_flag[0] |= NUM_NO_FRACTION; + data->num_input.unit_sys = USER_UNIT_NONE; + data->num_input.unit_type[0] = 0; + + slip_add_sequences_rec(ed->seqbasep, data->seq_array, data->trim, 0, true); for (i = 0; i < num_seq; i++) { @@ -1500,47 +1516,83 @@ 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]; + + 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); + } + else { + BLI_snprintf(msg, HEADER_LENGTH, "Trim offset: %d", offset); + } + } + + ED_area_headerprint(sa, msg); + +#undef HEADER_LENGTH +} + static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *event) { Scene *scene = CTX_data_scene(C); SlipData *data = (SlipData *)op->customdata; ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); + const bool has_numInput = hasNumInput(&data->num_input); + bool handled = true; + + /* Modal numinput active, try to handle numeric inputs first... */ + if (event->val == KM_PRESS && has_numInput && handleNumInput(C, &data->num_input, event)) { + float offset; + applyNumInput(&data->num_input, &offset); + + sequencer_slip_update_header(scene, sa, data, (int)offset); + + RNA_int_set(op->ptr, "offset", offset); + + if (sequencer_slip_recursively(scene, data, offset)) { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + } + + return OPERATOR_RUNNING_MODAL; + } switch (event->type) { case MOUSEMOVE: { - float mouseloc[2]; - int offset; - int mouse_x; - View2D *v2d = UI_view2d_fromcontext(C); - - if (data->slow) { - mouse_x = event->mval[0] - data->slow_offset; - mouse_x *= 0.1f; - mouse_x += data->slow_offset; - } - else { - mouse_x = event->mval[0]; - } + if (!has_numInput) { + float mouseloc[2]; + int offset; + int mouse_x; + View2D *v2d = UI_view2d_fromcontext(C); + + if (data->slow) { + mouse_x = event->mval[0] - data->slow_offset; + mouse_x *= 0.1f; + mouse_x += data->slow_offset; + } + else { + mouse_x = event->mval[0]; + } - /* choose the side based on which side of the playhead the mouse is on */ - UI_view2d_region_to_view(v2d, mouse_x, 0, &mouseloc[0], &mouseloc[1]); - offset = mouseloc[0] - data->init_mouseloc[0]; + /* choose the side based on which side of the playhead the mouse is on */ + UI_view2d_region_to_view(v2d, mouse_x, 0, &mouseloc[0], &mouseloc[1]); + offset = mouseloc[0] - data->init_mouseloc[0]; - RNA_int_set(op->ptr, "offset", offset); + sequencer_slip_update_header(scene, sa, data, offset); - if (sa) { -#define HEADER_LENGTH 40 - char msg[HEADER_LENGTH]; - BLI_snprintf(msg, HEADER_LENGTH, "Trim offset: %d", offset); -#undef HEADER_LENGTH - ED_area_headerprint(sa, msg); - } + RNA_int_set(op->ptr, "offset", offset); - if (sequencer_slip_recursively(scene, data, offset)) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + if (sequencer_slip_recursively(scene, data, offset)) { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + } } break; } @@ -1597,19 +1649,36 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even case RIGHTSHIFTKEY: case LEFTSHIFTKEY: - if (event->val == KM_PRESS) { - data->slow = true; - data->slow_offset = event->mval[0]; - } - else if (event->val == KM_RELEASE) { - data->slow = false; + if (!has_numInput) { + if (event->val == KM_PRESS) { + data->slow = true; + data->slow_offset = event->mval[0]; + } + else if (event->val == KM_RELEASE) { + data->slow = false; + } } break; default: + handled = false; break; } + /* Modal numinput inactive, try to handle numeric inputs last... */ + if (!handled && event->val == KM_PRESS && handleNumInput(C, &data->num_input, event)) { + float offset; + applyNumInput(&data->num_input, &offset); + + sequencer_slip_update_header(scene, sa, data, (int)offset); + + RNA_int_set(op->ptr, "offset", offset); + + if (sequencer_slip_recursively(scene, data, offset)) { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + } + } + return OPERATOR_RUNNING_MODAL; } @@ -3360,18 +3429,21 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); Sequence *seq; - + GSet *file_list; + if (ed == NULL) { return OPERATOR_CANCELLED; } + file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); + SEQP_BEGIN(ed, seq) { if ((seq->flag & SELECT)) { struct SeqIndexBuildContext *context; short stop = 0, do_update; float progress; - context = BKE_sequencer_proxy_rebuild_context(bmain, scene, seq); + context = BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list); BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress); BKE_sequencer_proxy_rebuild_finish(context, 0); BKE_sequencer_free_imbuf(scene, &ed->seqbase, false); @@ -3379,6 +3451,8 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) } SEQ_END + BLI_gset_free(file_list, MEM_freeN); + return OPERATOR_FINISHED; } @@ -3397,6 +3471,88 @@ void SEQUENCER_OT_rebuild_proxy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER; } +static int sequencer_enable_proxies_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + return WM_operator_props_dialog_popup(C, op, 10 * UI_UNIT_X, 5 * UI_UNIT_Y); +} + +static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = BKE_sequencer_editing_get(scene, false); + Sequence *seq; + bool proxy_25 = RNA_boolean_get(op->ptr, "proxy_25"); + bool proxy_50 = RNA_boolean_get(op->ptr, "proxy_50"); + bool proxy_75 = RNA_boolean_get(op->ptr, "proxy_75"); + bool proxy_100 = RNA_boolean_get(op->ptr, "proxy_100"); + bool override = RNA_boolean_get(op->ptr, "override"); + bool turnon = true; + + if (ed == NULL || !(proxy_25 || proxy_50 || proxy_75 || proxy_100)) { + turnon = false; + } + + SEQP_BEGIN(ed, seq) + { + if ((seq->flag & SELECT)) { + if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE, SEQ_TYPE_META, SEQ_TYPE_SCENE, SEQ_TYPE_MULTICAM)) { + BKE_sequencer_proxy_set(seq, turnon); + + if (proxy_25) + seq->strip->proxy->build_size_flags |= SEQ_PROXY_IMAGE_SIZE_25; + else + seq->strip->proxy->build_size_flags &= ~SEQ_PROXY_IMAGE_SIZE_25; + + if (proxy_50) + seq->strip->proxy->build_size_flags |= SEQ_PROXY_IMAGE_SIZE_50; + else + seq->strip->proxy->build_size_flags &= ~SEQ_PROXY_IMAGE_SIZE_50; + + if (proxy_75) + seq->strip->proxy->build_size_flags |= SEQ_PROXY_IMAGE_SIZE_75; + else + seq->strip->proxy->build_size_flags &= ~SEQ_PROXY_IMAGE_SIZE_75; + + if (proxy_100) + seq->strip->proxy->build_size_flags |= SEQ_PROXY_IMAGE_SIZE_100; + else + seq->strip->proxy->build_size_flags &= ~SEQ_PROXY_IMAGE_SIZE_100; + + if (!override) + seq->strip->proxy->build_flags |= SEQ_PROXY_SKIP_EXISTING; + else + seq->strip->proxy->build_flags &= ~SEQ_PROXY_SKIP_EXISTING; + } + } + } + SEQ_END + + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_enable_proxies(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Selected Strip Proxies"; + ot->idname = "SEQUENCER_OT_enable_proxies"; + ot->description = "Enable selected proxies on all selected Movie strips"; + + /* api callbacks */ + ot->invoke = sequencer_enable_proxies_invoke; + ot->exec = sequencer_enable_proxies_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER; + + RNA_def_boolean(ot->srna, "proxy_25", false, "25%", ""); + RNA_def_boolean(ot->srna, "proxy_50", false, "50%", ""); + RNA_def_boolean(ot->srna, "proxy_75", false, "75%", ""); + RNA_def_boolean(ot->srna, "proxy_100", false, "100%", ""); + RNA_def_boolean(ot->srna, "override", false, "Override", ""); +} + /* change ops */ static EnumPropertyItem prop_change_effect_input_types[] = { diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 5f1c9317fd9..461c72961c2 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -131,6 +131,7 @@ void SEQUENCER_OT_copy(struct wmOperatorType *ot); void SEQUENCER_OT_paste(struct wmOperatorType *ot); void SEQUENCER_OT_rebuild_proxy(struct wmOperatorType *ot); +void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot); /* preview specific operators */ void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 9b5ef18f7cd..33a8a1c5b41 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -88,6 +88,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_view_ghost_border); WM_operatortype_append(SEQUENCER_OT_rebuild_proxy); + WM_operatortype_append(SEQUENCER_OT_enable_proxies); WM_operatortype_append(SEQUENCER_OT_change_effect_input); WM_operatortype_append(SEQUENCER_OT_change_effect_type); WM_operatortype_append(SEQUENCER_OT_change_path); diff --git a/source/blender/editors/space_sequencer/sequencer_preview.c b/source/blender/editors/space_sequencer/sequencer_preview.c index da00b0ff6e1..c8834d394f5 100644 --- a/source/blender/editors/space_sequencer/sequencer_preview.c +++ b/source/blender/editors/space_sequencer/sequencer_preview.c @@ -95,9 +95,9 @@ static void preview_startjob(void *data, short *stop, short *do_update, float *p sound = previewjb->sound; /* make sure we cleanup the loading flag! */ - BLI_mutex_lock(sound->mutex); + BLI_spin_lock(sound->spinlock); sound->flags &= ~SOUND_FLAGS_WAVEFORM_LOADING; - BLI_mutex_unlock(sound->mutex); + BLI_spin_unlock(sound->spinlock); BLI_mutex_lock(pj->mutex); previewjb = previewjb->next; @@ -117,7 +117,7 @@ static void preview_startjob(void *data, short *stop, short *do_update, float *p BLI_freelinkN(&pj->previews, previewjb); previewjb = preview_next; pj->processed++; - *progress = (pj->total > 0) ? (float)pj->processed / (float)pj->total : 1.0; + *progress = (pj->total > 0) ? (float)pj->processed / (float)pj->total : 1.0f; *do_update = true; BLI_mutex_unlock(pj->mutex); } diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 6792bbe0577..4fbe0c6a241 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -996,7 +996,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq) if (SEQ_HAS_PATH(actseq) && dir) { SEQP_BEGIN (ed, seq) { - if (SEQ_HAS_PATH(seq) && seq->strip && strcmp(seq->strip->dir, dir) == 0) { + if (SEQ_HAS_PATH(seq) && seq->strip && STREQ(seq->strip->dir, dir)) { seq->flag |= SELECT; changed = true; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 6231f02907a..7be356dd2ec 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -504,6 +504,13 @@ static void sequencer_main_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa break; } break; + case NC_ANIMATION: + switch (wmn->data) { + case ND_KEYFRAME: + ED_region_tag_redraw(ar); + break; + } + break; case NC_SPACE: if (wmn->data == ND_SPACE_SEQUENCER) ED_region_tag_redraw(ar); @@ -599,6 +606,16 @@ static void sequencer_preview_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED case ND_MARKERS: case ND_SEQUENCER: case ND_RENDER_OPTIONS: + case ND_DRAW_RENDER_VIEWPORT: + ED_region_tag_redraw(ar); + break; + } + break; + case NC_ANIMATION: + switch (wmn->data) { + case ND_KEYFRAME: + /* Otherwise, often prevents seing immediately effects of keyframe editing... */ + BKE_sequencer_cache_cleanup(); ED_region_tag_redraw(ar); break; } diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c index 99e1606c9bd..1637ae14892 100644 --- a/source/blender/editors/space_text/text_autocomplete.c +++ b/source/blender/editors/space_text/text_autocomplete.c @@ -183,7 +183,7 @@ static GHash *text_autocomplete_build(Text *text) const int choice_len = i_end - i_start; if ((choice_len > seek_len) && - (seek_len == 0 || strncmp(seek, str_sub, seek_len) == 0) && + (seek_len == 0 || STREQLEN(seek, str_sub, seek_len)) && (seek != str_sub)) { // printf("Adding: %s\n", s); @@ -207,18 +207,16 @@ static GHash *text_autocomplete_build(Text *text) } { - GHashIterator *iter = BLI_ghashIterator_new(gh); + GHashIterator gh_iter; /* get the formatter for highlighting */ TextFormatType *tft; tft = ED_text_format_get(text); - for (; !BLI_ghashIterator_done(iter); BLI_ghashIterator_step(iter)) { - const char *s = BLI_ghashIterator_getValue(iter); + GHASH_ITER (gh_iter, gh) { + const char *s = BLI_ghashIterator_getValue(&gh_iter); texttool_suggest_add(s, tft->format_identifier(s)); } - BLI_ghashIterator_free(iter); - } } diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 31662c02966..462b619f497 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -549,7 +549,7 @@ static void text_update_drawcache(SpaceText *st, ARegion *ar) full_update |= drawcache->tabnumber != st->tabnumber; /* word-wrapping option was toggled */ full_update |= drawcache->lheight != st->lheight_dpi; /* word-wrapping option was toggled */ full_update |= drawcache->cwidth != st->cwidth; /* word-wrapping option was toggled */ - full_update |= strncmp(drawcache->text_id, txt->id.name, MAX_ID_NAME); /* text datablock was changed */ + full_update |= !STREQLEN(drawcache->text_id, txt->id.name, MAX_ID_NAME); /* text datablock was changed */ if (st->wordwrap) { /* update line heights */ @@ -1447,7 +1447,6 @@ void draw_text_main(SpaceText *st, ARegion *ar) /* draw other stuff */ draw_brackets(st, ar); - glTranslatef(GLA_PIXEL_OFS, GLA_PIXEL_OFS, 0.0f); /* XXX scroll requires exact pixel space */ draw_textscroll(st, &scroll, &back); draw_documentation(st, ar); draw_suggestion_list(st, ar); diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 1cf0316aa06..ad8050a50e8 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -2537,10 +2537,14 @@ static void text_cursor_set_to_pos(SpaceText *st, ARegion *ar, int x, int y, con y -= txt_get_span(text->lines.first, *linep) - st->top; if (y > 0) { - while (y-- != 0) if ((*linep)->next) *linep = (*linep)->next; + while (y-- != 0) { + if ((*linep)->next) *linep = (*linep)->next; + } } else if (y < 0) { - while (y++ != 0) if ((*linep)->prev) *linep = (*linep)->prev; + while (y++ != 0) { + if ((*linep)->prev) *linep = (*linep)->prev; + } } @@ -2919,7 +2923,7 @@ static int text_find_and_replace(bContext *C, wmOperator *op, short mode) if (mode != TEXT_FIND && txt_has_sel(text)) { tmp = txt_sel_to_buf(text); - if (flags & ST_MATCH_CASE) found = strcmp(st->findstr, tmp) == 0; + if (flags & ST_MATCH_CASE) found = STREQ(st->findstr, tmp); else found = BLI_strcasecmp(st->findstr, tmp) == 0; if (found) { diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index 820d1d68f8d..e2c195fd544 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -58,6 +58,7 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "UI_interface.h" #include "ED_space_api.h" #include "ED_markers.h" @@ -90,13 +91,12 @@ static void time_draw_sfra_efra(Scene *scene, View2D *v2d) fdrawline((float)PEFRA, v2d->cur.ymin, (float)PEFRA, v2d->cur.ymax); } -#define CACHE_DRAW_HEIGHT 3.0f - static void time_draw_cache(SpaceTime *stime, Object *ob, Scene *scene) { PTCacheID *pid; ListBase pidlist; SpaceTimeCache *stc = stime->caches.first; + const float cache_draw_height = (4.0f * UI_DPI_FAC * U.pixelsize); float yoffs = 0.f; if (!(stime->cache_display & TIME_CACHE_DISPLAY) || (!ob)) @@ -172,7 +172,7 @@ static void time_draw_cache(SpaceTime *stime, Object *ob, Scene *scene) glPushMatrix(); glTranslatef(0.0, (float)V2D_SCROLL_HEIGHT + yoffs, 0.0); - glScalef(1.0, CACHE_DRAW_HEIGHT, 0.0); + glScalef(1.0, cache_draw_height, 0.0); switch (pid->type) { case PTCACHE_TYPE_SOFTBODY: @@ -227,7 +227,7 @@ static void time_draw_cache(SpaceTime *stime, Object *ob, Scene *scene) glPopMatrix(); - yoffs += CACHE_DRAW_HEIGHT; + yoffs += cache_draw_height; stc = stc->next; } @@ -292,6 +292,8 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel) bDopeSheet ads = {NULL}; DLRBT_Tree keys; ActKeyColumn *ak; + float ymin = v2d->tot.ymin; + float ymax = v2d->tot.ymax * 0.6f + ymin * 0.4f; /* init binarytree-list for getting keyframes */ BLI_dlrbTree_init(&keys); @@ -299,7 +301,7 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel) /* init dopesheet settings */ if (onlysel) ads.filterflag |= ADS_FILTER_ONLYSEL; - + /* populate tree with keyframe nodes */ switch (GS(id->name)) { case ID_SCE: @@ -326,8 +328,8 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel) (ak) && (ak->cfra <= v2d->cur.xmax); ak = ak->next) { - glVertex2f(ak->cfra, v2d->tot.ymin); - glVertex2f(ak->cfra, v2d->tot.ymax); + glVertex2f(ak->cfra, ymin); + glVertex2f(ak->cfra, ymax); } glEnd(); // GL_LINES diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 320267a4a7c..ab69e67361d 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -44,6 +44,7 @@ set(SRC drawarmature.c drawmesh.c drawobject.c + drawsimdebug.c drawvolume.c space_view3d.c view3d_buttons.c diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index 691da78f26b..f33f074e6c5 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -1619,7 +1619,7 @@ static void draw_pose_dofs(Object *ob) for (a = -16; a <= 16; a++) { /* *0.5f here comes from M_PI/360.0f when rotations were still in degrees */ float fac = ((float)a) / 16.0f * 0.5f; - phi = (float)(0.5 * M_PI) + fac * (pchan->limitmax[0] - pchan->limitmin[0]); + phi = (float)M_PI_2 + fac * (pchan->limitmax[0] - pchan->limitmin[0]); i = (a == -16) ? 2 : 3; corner[i][0] = 0.0f; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 1cbc8e5567c..add9b41a91f 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -733,7 +733,7 @@ void drawcircball(int mode, const float cent[3], float rad, float tmat[4][4]) } /* circle for object centers, special_color is for library or ob users */ -static void drawcentercircle(View3D *v3d, RegionView3D *rv3d, const float co[3], int selstate, int special_color) +static void drawcentercircle(View3D *v3d, RegionView3D *rv3d, const float co[3], int selstate, bool special_color) { const float size = ED_view3d_pixel_size(rv3d, co) * (float)U.obcenter_dia * 0.5f; float verts[CIRCLE_RESOL][3]; @@ -741,6 +741,8 @@ static void drawcentercircle(View3D *v3d, RegionView3D *rv3d, const float co[3], /* using gldepthfunc guarantees that it does write z values, * but not checks for it, so centers remain visible independent order of drawing */ if (v3d->zbuf) glDepthFunc(GL_ALWAYS); + /* write to near buffer always */ + glDepthRange(0.0, 0.0); glEnable(GL_BLEND); if (special_color) { @@ -770,6 +772,7 @@ static void drawcentercircle(View3D *v3d, RegionView3D *rv3d, const float co[3], /* finish up */ glDisableClientState(GL_VERTEX_ARRAY); + glDepthRange(0.0, 1.0); glDisable(GL_BLEND); if (v3d->zbuf) glDepthFunc(GL_LEQUAL); @@ -4329,8 +4332,8 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3 ListBase *lb = NULL; DispList *dl; Curve *cu; - const short render_only = (v3d->flag2 & V3D_RENDER_OVERRIDE); - const short solid = (dt > OB_WIRE); + const bool render_only = (v3d->flag2 & V3D_RENDER_OVERRIDE) != 0; + const bool solid = (dt > OB_WIRE); if (drawCurveDerivedMesh(scene, v3d, rv3d, base, dt) == false) { return false; @@ -5145,7 +5148,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv cache = psys->pathcache; for (a = 0, pa = psys->particles; a < totpart; a++, pa++) { path = cache[a]; - if (path->steps > 0) { + if (path->segments > 0) { glVertexPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->co); if (1) { //ob_dt > OB_WIRE) { @@ -5157,7 +5160,136 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv } } - glDrawArrays(GL_LINE_STRIP, 0, path->steps + 1); + glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1); + } + } + + if (part->type == PART_HAIR) { + if (part->draw & PART_DRAW_GUIDE_HAIRS) { + DerivedMesh *hair_dm = psys->hair_out_dm; + + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + for (a = 0, pa = psys->particles; a < totpart; a++, pa++) { + if (pa->totkey > 1) { + HairKey *hkey = pa->hair; + + glVertexPointer(3, GL_FLOAT, sizeof(HairKey), hkey->world_co); + + // XXX use proper theme color here +// UI_ThemeColor(TH_NORMAL); + glColor3f(0.58f, 0.67f, 1.0f); + + glDrawArrays(GL_LINE_STRIP, 0, pa->totkey); + } + } + + if (hair_dm) { + MVert *mvert = hair_dm->getVertArray(hair_dm); + int i; + + glColor3f(0.9f, 0.4f, 0.4f); + + glBegin(GL_LINES); + for (a = 0, pa = psys->particles; a < totpart; a++, pa++) { + for (i = 1; i < pa->totkey; ++i) { + float v1[3], v2[3]; + + copy_v3_v3(v1, mvert[pa->hair_index + i - 1].co); + copy_v3_v3(v2, mvert[pa->hair_index + i].co); + + mul_m4_v3(ob->obmat, v1); + mul_m4_v3(ob->obmat, v2); + + glVertex3fv(v1); + glVertex3fv(v2); + } + } + glEnd(); + } + + glEnable(GL_LIGHTING); + glEnable(GL_COLOR_MATERIAL); + glEnableClientState(GL_NORMAL_ARRAY); + if ((dflag & DRAW_CONSTCOLOR) == 0) + if (part->draw_col == PART_DRAW_COL_MAT) + glEnableClientState(GL_COLOR_ARRAY); + } + + if (part->draw & PART_DRAW_HAIR_GRID) { + ClothModifierData *clmd = psys->clmd; + if (clmd) { + float *a = clmd->hair_grid_min; + float *b = clmd->hair_grid_max; + int *res = clmd->hair_grid_res; + int i; + + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + if (select) + UI_ThemeColor(TH_ACTIVE); + else + UI_ThemeColor(TH_WIRE); + glBegin(GL_LINES); + glVertex3f(a[0], a[1], a[2]); glVertex3f(b[0], a[1], a[2]); + glVertex3f(b[0], a[1], a[2]); glVertex3f(b[0], b[1], a[2]); + glVertex3f(b[0], b[1], a[2]); glVertex3f(a[0], b[1], a[2]); + glVertex3f(a[0], b[1], a[2]); glVertex3f(a[0], a[1], a[2]); + + glVertex3f(a[0], a[1], b[2]); glVertex3f(b[0], a[1], b[2]); + glVertex3f(b[0], a[1], b[2]); glVertex3f(b[0], b[1], b[2]); + glVertex3f(b[0], b[1], b[2]); glVertex3f(a[0], b[1], b[2]); + glVertex3f(a[0], b[1], b[2]); glVertex3f(a[0], a[1], b[2]); + + glVertex3f(a[0], a[1], a[2]); glVertex3f(a[0], a[1], b[2]); + glVertex3f(b[0], a[1], a[2]); glVertex3f(b[0], a[1], b[2]); + glVertex3f(a[0], b[1], a[2]); glVertex3f(a[0], b[1], b[2]); + glVertex3f(b[0], b[1], a[2]); glVertex3f(b[0], b[1], b[2]); + glEnd(); + + if (select) + UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -100); + else + UI_ThemeColorShadeAlpha(TH_WIRE, 0, -100); + glEnable(GL_BLEND); + glBegin(GL_LINES); + for (i = 1; i < res[0]-1; ++i) { + float f = interpf(b[0], a[0], (float)i / (float)(res[0]-1)); + glVertex3f(f, a[1], a[2]); glVertex3f(f, b[1], a[2]); + glVertex3f(f, b[1], a[2]); glVertex3f(f, b[1], b[2]); + glVertex3f(f, b[1], b[2]); glVertex3f(f, a[1], b[2]); + glVertex3f(f, a[1], b[2]); glVertex3f(f, a[1], a[2]); + } + for (i = 1; i < res[1]-1; ++i) { + float f = interpf(b[1], a[1], (float)i / (float)(res[1]-1)); + glVertex3f(a[0], f, a[2]); glVertex3f(b[0], f, a[2]); + glVertex3f(b[0], f, a[2]); glVertex3f(b[0], f, b[2]); + glVertex3f(b[0], f, b[2]); glVertex3f(a[0], f, b[2]); + glVertex3f(a[0], f, b[2]); glVertex3f(a[0], f, a[2]); + } + for (i = 1; i < res[2]-1; ++i) { + float f = interpf(b[2], a[2], (float)i / (float)(res[2]-1)); + glVertex3f(a[0], a[1], f); glVertex3f(b[0], a[1], f); + glVertex3f(b[0], a[1], f); glVertex3f(b[0], b[1], f); + glVertex3f(b[0], b[1], f); glVertex3f(a[0], b[1], f); + glVertex3f(a[0], b[1], f); glVertex3f(a[0], a[1], f); + } + glEnd(); + glDisable(GL_BLEND); + + glEnable(GL_LIGHTING); + glEnable(GL_COLOR_MATERIAL); + glEnableClientState(GL_NORMAL_ARRAY); + if ((dflag & DRAW_CONSTCOLOR) == 0) + if (part->draw_col == PART_DRAW_COL_MAT) + glEnableClientState(GL_COLOR_ARRAY); + } } } @@ -5176,7 +5308,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv } } - glDrawArrays(GL_LINE_STRIP, 0, path->steps + 1); + glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1); } @@ -5328,7 +5460,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit) PTCacheEditKey *key; ParticleEditSettings *pset = PE_settings(scene); int i, k, totpoint = edit->totpoint, timed = pset->flag & PE_FADE_TIME ? pset->fade_frames : 0; - int steps = 1; + int totkeys = 1; float sel_col[3]; float nosel_col[3]; float *pathcol = NULL, *pcol; @@ -5347,10 +5479,10 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit) UI_GetThemeColor3fv(TH_VERTEX, nosel_col); /* draw paths */ - steps = (*edit->pathcache)->steps + 1; + totkeys = (*edit->pathcache)->segments + 1; glEnable(GL_BLEND); - pathcol = MEM_callocN(steps * 4 * sizeof(float), "particle path color data"); + pathcol = MEM_callocN(totkeys * 4 * sizeof(float), "particle path color data"); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); @@ -5370,7 +5502,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit) glVertexPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->co); if (point->flag & PEP_HIDE) { - for (k = 0, pcol = pathcol; k < steps; k++, pcol += 4) { + for (k = 0, pcol = pathcol; k < totkeys; k++, pcol += 4) { copy_v3_v3(pcol, path->col); pcol[3] = 0.25f; } @@ -5378,7 +5510,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit) glColorPointer(4, GL_FLOAT, 4 * sizeof(float), pathcol); } else if (timed) { - for (k = 0, pcol = pathcol, pkey = path; k < steps; k++, pkey++, pcol += 4) { + for (k = 0, pcol = pathcol, pkey = path; k < totkeys; k++, pkey++, pcol += 4) { copy_v3_v3(pcol, pkey->col); pcol[3] = 1.0f - fabsf((float)(CFRA) -pkey->time) / (float)pset->fade_frames; } @@ -5388,7 +5520,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit) else glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); - glDrawArrays(GL_LINE_STRIP, 0, path->steps + 1); + glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1); } if (pathcol) { MEM_freeN(pathcol); pathcol = pcol = NULL; } @@ -7727,7 +7859,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short if (render_override) { /* don't draw */ } - else if ((scene->basact) == base) + else if (is_obact) do_draw_center = ACTIVE; else if (base->flag & SELECT) do_draw_center = SELECT; diff --git a/source/blender/editors/space_view3d/drawsimdebug.c b/source/blender/editors/space_view3d/drawsimdebug.c new file mode 100644 index 00000000000..6113bfd4143 --- /dev/null +++ b/source/blender/editors/space_view3d/drawsimdebug.c @@ -0,0 +1,173 @@ +/* + * ***** 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 by the Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_view3d/drawsimdebug.c + * \ingroup spview3d + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" +#include "DNA_object_types.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_ghash.h" + +#include "BKE_effect.h" +#include "BKE_global.h" +#include "BKE_modifier.h" + +#include "view3d_intern.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_resources.h" + +static void draw_sim_debug_elements(SimDebugData *debug_data, float imat[4][4]) +{ + GHashIterator iter; + + /**** dots ****/ + + glPointSize(3.0f); + glBegin(GL_POINTS); + for (BLI_ghashIterator_init(&iter, debug_data->gh); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { + SimDebugElement *elem = BLI_ghashIterator_getValue(&iter); + if (elem->type != SIM_DEBUG_ELEM_DOT) + continue; + + glColor3f(elem->color[0], elem->color[1], elem->color[2]); + glVertex3f(elem->v1[0], elem->v1[1], elem->v1[2]); + } + glEnd(); + glPointSize(1.0f); + + /**** circles ****/ + + { + float circle[16][2] = { + {0.000000, 1.000000}, {0.382683, 0.923880}, {0.707107, 0.707107}, {0.923880, 0.382683}, + {1.000000, -0.000000}, {0.923880, -0.382683}, {0.707107, -0.707107}, {0.382683, -0.923880}, + {-0.000000, -1.000000}, {-0.382683, -0.923880}, {-0.707107, -0.707107}, {-0.923879, -0.382684}, + {-1.000000, 0.000000}, {-0.923879, 0.382684}, {-0.707107, 0.707107}, {-0.382683, 0.923880} }; + for (BLI_ghashIterator_init(&iter, debug_data->gh); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { + SimDebugElement *elem = BLI_ghashIterator_getValue(&iter); + float radius = elem->v2[0]; + float co[3]; + int i; + + if (elem->type != SIM_DEBUG_ELEM_CIRCLE) + continue; + + glColor3f(elem->color[0], elem->color[1], elem->color[2]); + glBegin(GL_LINE_LOOP); + for (i = 0; i < 16; ++i) { + co[0] = radius * circle[i][0]; + co[1] = radius * circle[i][1]; + co[2] = 0.0f; + mul_mat3_m4_v3(imat, co); + add_v3_v3(co, elem->v1); + + glVertex3f(co[0], co[1], co[2]); + } + glEnd(); + } + } + + /**** lines ****/ + + glBegin(GL_LINES); + for (BLI_ghashIterator_init(&iter, debug_data->gh); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { + SimDebugElement *elem = BLI_ghashIterator_getValue(&iter); + if (elem->type != SIM_DEBUG_ELEM_LINE) + continue; + + glColor3f(elem->color[0], elem->color[1], elem->color[2]); + glVertex3f(elem->v1[0], elem->v1[1], elem->v1[2]); + glVertex3f(elem->v2[0], elem->v2[1], elem->v2[2]); + } + glEnd(); + + /**** vectors ****/ + + glPointSize(2.0f); + glBegin(GL_POINTS); + for (BLI_ghashIterator_init(&iter, debug_data->gh); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { + SimDebugElement *elem = BLI_ghashIterator_getValue(&iter); + if (elem->type != SIM_DEBUG_ELEM_VECTOR) + continue; + + glColor3f(elem->color[0], elem->color[1], elem->color[2]); + glVertex3f(elem->v1[0], elem->v1[1], elem->v1[2]); + } + glEnd(); + glPointSize(1.0f); + + glBegin(GL_LINES); + for (BLI_ghashIterator_init(&iter, debug_data->gh); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { + SimDebugElement *elem = BLI_ghashIterator_getValue(&iter); + float t[3]; + if (elem->type != SIM_DEBUG_ELEM_VECTOR) + continue; + + glColor3f(elem->color[0], elem->color[1], elem->color[2]); + glVertex3f(elem->v1[0], elem->v1[1], elem->v1[2]); + add_v3_v3v3(t, elem->v1, elem->v2); + glVertex3f(t[0], t[1], t[2]); + } + glEnd(); +} + +void draw_sim_debug_data(Scene *UNUSED(scene), View3D *UNUSED(v3d), ARegion *ar) +{ + RegionView3D *rv3d = ar->regiondata; + /*Object *ob = base->object;*/ + float imat[4][4]; + + if (!_sim_debug_data) + return; + + invert_m4_m4(imat, rv3d->viewmatob); + +// glDepthMask(GL_FALSE); +// glEnable(GL_BLEND); + + glPushMatrix(); + + glLoadMatrixf(rv3d->viewmat); + draw_sim_debug_elements(_sim_debug_data, imat); + + glPopMatrix(); + +// glDepthMask(GL_TRUE); +// glDisable(GL_BLEND); +} diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 55d5273b198..25085368dac 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -564,7 +564,7 @@ static void view3d_main_area_exit(wmWindowManager *wm, ARegion *ar) static int view3d_ob_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_OB) return 1; } @@ -574,7 +574,7 @@ static int view3d_ob_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent static int view3d_group_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_GR) return 1; } @@ -584,7 +584,7 @@ static int view3d_group_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEve static int view3d_mat_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_MA) return 1; } @@ -594,7 +594,7 @@ static int view3d_mat_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent static int view3d_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_ID) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (GS(id->name) == ID_IM) return 1; } @@ -641,14 +641,14 @@ static int view3d_ima_mesh_drop_poll(bContext *C, wmDrag *drag, const wmEvent *e static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; RNA_string_set(drop->ptr, "name", id->name + 2); } static void view3d_group_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; drop->opcontext = WM_OP_EXEC_DEFAULT; RNA_string_set(drop->ptr, "name", id->name + 2); @@ -656,14 +656,14 @@ static void view3d_group_drop_copy(wmDrag *drag, wmDropBox *drop) static void view3d_id_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; RNA_string_set(drop->ptr, "name", id->name + 2); } static void view3d_id_path_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; if (id) { RNA_string_set(drop->ptr, "name", id->name + 2); @@ -817,6 +817,16 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN case ND_WORLD: /* handled by space_view3d_listener() for v3d access */ break; + case ND_DRAW_RENDER_VIEWPORT: + { + if (v3d->camera && (scene == wmn->reference)) { + RegionView3D *rv3d = ar->regiondata; + if (rv3d->persp == RV3D_CAMOB) { + ED_region_tag_redraw(ar); + } + } + break; + } } if (wmn->action == NA_EDITED) ED_region_tag_redraw(ar); @@ -856,6 +866,20 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN break; } break; + case NC_CAMERA: + switch (wmn->data) { + case ND_DRAW_RENDER_VIEWPORT: + { + if (v3d->camera && (v3d->camera->data == wmn->reference)) { + RegionView3D *rv3d = ar->regiondata; + if (rv3d->persp == RV3D_CAMOB) { + ED_region_tag_redraw(ar); + } + } + break; + } + } + break; case NC_GROUP: /* all group ops for now */ ED_region_tag_redraw(ar); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 65721d52dff..929929c7dd0 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -162,15 +162,15 @@ static void view3d_draw_clipping(RegionView3D *rv3d) /* fill in zero alpha for rendering & re-projection [#31530] */ unsigned char col[4]; - UI_GetThemeColorShade3ubv(TH_BACK, -8, col); - col[3] = 0; + UI_GetThemeColor4ubv(TH_V3D_CLIPPING_BORDER, col); glColor4ubv(col); + glEnable(GL_BLEND); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, bb->vec); glDrawElements(GL_QUADS, sizeof(clipping_index) / sizeof(unsigned int), GL_UNSIGNED_INT, clipping_index); glDisableClientState(GL_VERTEX_ARRAY); - + glDisable(GL_BLEND); } } @@ -586,20 +586,26 @@ static void draw_view_axis(RegionView3D *rv3d, rcti *rect) float ydisp = 0.0; /* vertical displacement to allow obj info text */ int bright = - 20 * (10 - U.rvibright); /* axis alpha offset (rvibright has range 0-10) */ float vec[3]; - char axis_text[2] = "x"; float dx, dy; - int i; - + + int axis_order[3] = {0, 1, 2}; + int axis_i; + startx += rect->xmin; starty += rect->ymin; - + + axis_sort_v3(rv3d->viewinv[2], axis_order); + /* thickness of lines is proportional to k */ glLineWidth(2); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - for (i = 0; i < 3; i++) { + for (axis_i = 0; axis_i < 3; axis_i++) { + int i = axis_order[axis_i]; + const char axis_text[2] = {'x' + i, '\0'}; + zero_v3(vec); vec[i] = 1.0f; mul_qt_v3(rv3d->viewquat, vec); @@ -616,8 +622,6 @@ static void draw_view_axis(RegionView3D *rv3d, rcti *rect) BLF_draw_default_ascii(startx + dx + 2, starty + dy + ydisp + 2, 0.0f, axis_text, 1); } - axis_text[0]++; - /* BLF_draw_default disables blending */ glEnable(GL_BLEND); } @@ -1066,14 +1070,13 @@ static void drawviewborder_triangle(float x1, float x2, float y1, float y2, cons static void drawviewborder(Scene *scene, ARegion *ar, View3D *v3d) { - float hmargin, vmargin; float x1, x2, y1, y2; float x1i, x2i, y1i, y2i; rctf viewborder; Camera *ca = NULL; RegionView3D *rv3d = ar->regiondata; - + if (v3d->camera == NULL) return; if (v3d->camera->type == OB_CAMERA) @@ -1225,17 +1228,20 @@ static void drawviewborder(Scene *scene, ARegion *ar, View3D *v3d) drawviewborder_triangle(x1, x2, y1, y2, 1, 'B'); } - if (ca->flag & CAM_SHOWTITLESAFE) { - UI_ThemeColorBlendShade(TH_VIEW_OVERLAY, TH_BACK, 0.25, 0); - - hmargin = 0.1f * (x2 - x1); - vmargin = 0.05f * (y2 - y1); - UI_draw_roundbox_gl_mode(GL_LINE_LOOP, x1 + hmargin, y1 + vmargin, x2 - hmargin, y2 - vmargin, 2.0f); + if (ca->flag & CAM_SHOW_SAFE_MARGINS) { + UI_draw_safe_areas( + x1, x2, y1, y2, + scene->safe_areas.title, + scene->safe_areas.action); - hmargin = 0.035f * (x2 - x1); - vmargin = 0.035f * (y2 - y1); - UI_draw_roundbox_gl_mode(GL_LINE_LOOP, x1 + hmargin, y1 + vmargin, x2 - hmargin, y2 - vmargin, 2.0f); + if (ca->flag & CAM_SHOW_SAFE_CENTER) { + UI_draw_safe_areas( + x1, x2, y1, y2, + scene->safe_areas.title_center, + scene->safe_areas.action_center); + } } + if (ca->flag & CAM_SHOWSENSOR) { /* determine sensor fit, and get sensor x/y, for auto fit we * assume and square sensor and only use sensor_x */ @@ -1279,8 +1285,9 @@ static void drawviewborder(Scene *scene, ARegion *ar, View3D *v3d) /* camera name - draw in highlighted text color */ if (ca && (ca->flag & CAM_SHOWNAME)) { UI_ThemeColor(TH_TEXT_HI); - BLF_draw_default(x1i, y1i - 15, 0.0f, v3d->camera->id.name + 2, sizeof(v3d->camera->id.name) - 2); - UI_ThemeColor(TH_WIRE); + BLF_draw_default( + x1i, y1i - (0.7f * U.widget_unit), 0.0f, + v3d->camera->id.name + 2, sizeof(v3d->camera->id.name) - 2); } } @@ -1608,7 +1615,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, { float image_aspect[2]; float fac, asp, zoomx, zoomy; - float x1, y1, x2, y2; + float x1, y1, x2, y2, centx, centy; ImBuf *ibuf = NULL, *freeibuf, *releaseibuf; void *lock; @@ -1714,6 +1721,9 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, y2 += yof_scale; } + centx = (x1 + x2) / 2.0f; + centy = (y1 + y2) / 2.0f; + /* aspect correction */ if (bgpic->flag & V3D_BGPIC_CAMERA_ASPECT) { /* apply aspect from clip */ @@ -1731,16 +1741,14 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, if ((asp_src > asp_dst) == ((bgpic->flag & V3D_BGPIC_CAMERA_CROP) != 0)) { /* fit X */ const float div = asp_src / asp_dst; - const float cent = (x1 + x2) / 2.0f; - x1 = ((x1 - cent) * div) + cent; - x2 = ((x2 - cent) * div) + cent; + x1 = ((x1 - centx) * div) + centx; + x2 = ((x2 - centx) * div) + centx; } else { /* fit Y */ const float div = asp_dst / asp_src; - const float cent = (y1 + y2) / 2.0f; - y1 = ((y1 - cent) * div) + cent; - y2 = ((y2 - cent) * div) + cent; + y1 = ((y1 - centy) * div) + centy; + y2 = ((y2 - centy) * div) + centy; } } } @@ -1767,6 +1775,9 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, y1 = sco[1] + asp * fac * (bgpic->yof - bgpic->size); x2 = sco[0] + fac * (bgpic->xof + bgpic->size); y2 = sco[1] + asp * fac * (bgpic->yof + bgpic->size); + + centx = (x1 + x2) / 2.0f; + centy = (y1 + y2) / 2.0f; } /* complete clip? */ @@ -1817,6 +1828,19 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, glPushMatrix(); ED_region_pixelspace(ar); + glTranslatef(centx, centy, 0.0); + if (rv3d->persp != RV3D_CAMOB) { + glRotatef(RAD2DEGF(-bgpic->rotation), 0.0f, 0.0f, 1.0f); + } + + if (bgpic->flag & V3D_BGPIC_FLIP_X) { + zoomx *= -1.0f; + x1 = x2; + } + if (bgpic->flag & V3D_BGPIC_FLIP_Y) { + zoomy *= -1.0f; + y1 = y2; + } glPixelZoom(zoomx, zoomy); glColor4f(1.0f, 1.0f, 1.0f, 1.0f - bgpic->blend); @@ -1824,7 +1848,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, * glaDrawPixelsSafe in some cases, which will end up in missing * alpha transparency for the background image (sergey) */ - glaDrawPixelsTex(x1, y1, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR, ibuf->rect); + glaDrawPixelsTex(x1 - centx, y1 - centy, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR, ibuf->rect); glPixelZoom(1.0, 1.0); glPixelTransferf(GL_ALPHA_SCALE, 1.0f); @@ -2566,6 +2590,7 @@ CustomDataMask ED_view3d_screen_datamask(bScreen *screen) void ED_view3d_update_viewmat(Scene *scene, View3D *v3d, ARegion *ar, float viewmat[4][4], float winmat[4][4]) { RegionView3D *rv3d = ar->regiondata; + rctf cameraborder; /* setup window matrices */ if (winmat) @@ -2583,7 +2608,23 @@ void ED_view3d_update_viewmat(Scene *scene, View3D *v3d, ARegion *ar, float view mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); invert_m4_m4(rv3d->persinv, rv3d->persmat); invert_m4_m4(rv3d->viewinv, rv3d->viewmat); + + /* calculate GLSL view dependent values */ + /* store window coordinates scaling/offset */ + if (rv3d->persp == RV3D_CAMOB && v3d->camera) { + ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &cameraborder, false); + rv3d->viewcamtexcofac[0] = (float)ar->winx / BLI_rctf_size_x(&cameraborder); + rv3d->viewcamtexcofac[1] = (float)ar->winy / BLI_rctf_size_y(&cameraborder); + + rv3d->viewcamtexcofac[2] = -rv3d->viewcamtexcofac[0] * cameraborder.xmin / (float)ar->winx; + rv3d->viewcamtexcofac[3] = -rv3d->viewcamtexcofac[1] * cameraborder.ymin / (float)ar->winy; + } + else { + rv3d->viewcamtexcofac[0] = rv3d->viewcamtexcofac[1] = 1.0f; + rv3d->viewcamtexcofac[2] = rv3d->viewcamtexcofac[3] = 0.0f; + } + /* calculate pixelsize factor once, is used for lamps and obcenters */ { /* note: '1.0f / len_v3(v1)' replaced 'len_v3(rv3d->viewmat[0])' @@ -2815,15 +2856,15 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar, bool { /* clear background */ if (scene->world && ((v3d->flag3 & V3D_SHOW_WORLD) || force)) { - float alpha = (force) ? 1.0f : 0.0; + float alpha = (force) ? 1.0f : 0.0f; bool glsl = GPU_glsl_support() && BKE_scene_use_new_shading_nodes(scene) && scene->world->nodetree && scene->world->use_nodes; if (glsl) { RegionView3D *rv3d = ar->regiondata; GPUMaterial *gpumat = GPU_material_world(scene, scene->world); - + /* calculate full shader for background */ - GPU_material_bind(gpumat, 1, 1, 1.0, false, rv3d->viewmat, rv3d->viewinv, (v3d->scenelock != 0)); + GPU_material_bind(gpumat, 1, 1, 1.0, false, rv3d->viewmat, rv3d->viewinv, rv3d->viewcamtexcofac, (v3d->scenelock != 0)); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); @@ -3082,15 +3123,6 @@ void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, int winx, G.f &= ~G_RENDER_OGL; } -/* get a color used for offscreen sky, returns color in sRGB space */ -void ED_view3d_offscreen_sky_color_get(Scene *scene, float sky_color[3]) -{ - if (scene->world) - linearrgb_to_srgb_v3_v3(sky_color, &scene->world->horr); - else - UI_GetThemeColor3fv(TH_BACK, sky_color); -} - /* utility func for ED_view3d_draw_offscreen */ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, int sizex, int sizey, unsigned int flag, bool draw_background, int alpha_mode, char err_out[256]) @@ -3573,9 +3605,13 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) /* draw viewport using opengl */ if (v3d->drawtype != OB_RENDER || !view3d_main_area_do_render_draw(scene) || clip_border) { view3d_main_area_draw_objects(C, scene, v3d, ar, &grid_unit); + #ifdef DEBUG_DRAW bl_debug_draw(); #endif + if (G.debug & G_DEBUG_SIMDATA) + draw_sim_debug_data(scene, v3d, ar); + ED_region_pixelspace(ar); } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index aab307babe8..e6f61fb4aca 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -1034,7 +1034,7 @@ static void viewrotate_apply(ViewOpsData *vod, int x, int y) * - of rotation is linearly proportional * - to the distance that the mouse is * - dragged. */ - phi = si * (float)(M_PI / 2.0); + phi = si * (float)M_PI_2; q1[0] = cosf(phi); mul_v3_fl(q1 + 1, sinf(phi)); @@ -3373,7 +3373,8 @@ void VIEW3D_OT_render_border(wmOperatorType *ot) /* rna */ WM_operator_properties_border(ot); - prop = RNA_def_boolean(ot->srna, "camera_only", 0, "Camera Only", "Set render border for camera view and final render only"); + prop = RNA_def_boolean(ot->srna, "camera_only", false, "Camera Only", + "Set render border for camera view and final render only"); RNA_def_property_flag(prop, PROP_HIDDEN); } @@ -5003,8 +5004,9 @@ BGpic *ED_view3D_background_image_new(View3D *v3d) { BGpic *bgpic = MEM_callocN(sizeof(BGpic), "Background Image"); - bgpic->size = 5.0; - bgpic->blend = 0.5; + bgpic->rotation = 0.0f; + bgpic->size = 5.0f; + bgpic->blend = 0.5f; bgpic->iuser.fie_ima = 2; bgpic->iuser.ok = 1; bgpic->view = 0; /* 0 for all */ diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index d3a9f5ca967..25dbc8830fe 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -48,9 +48,11 @@ struct bMotionPath; struct bPoseChannel; struct bScreen; struct Mesh; +struct SimDebugData; struct wmNDOFMotionData; struct wmOperatorType; struct wmWindowManager; +struct wmKeyConfig; /* drawing flags: */ enum { @@ -177,6 +179,9 @@ void draw_mesh_paint_weight_edges(RegionView3D *rv3d, struct DerivedMesh *dm, void draw_mesh_paint(View3D *v3d, RegionView3D *rv3d, struct Object *ob, struct DerivedMesh *dm, const int draw_flags); +/* drawsimdebug.c */ +void draw_sim_debug_data(Scene *scene, View3D *v3d, ARegion *ar); + /* view3d_draw.c */ void view3d_main_area_draw(const struct bContext *C, struct ARegion *ar); void ED_view3d_draw_depth(Scene *scene, struct ARegion *ar, View3D *v3d, bool alphaoverride); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 5df348408df..0457f5f2d52 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -344,8 +344,8 @@ void view3d_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "VIEW3D_OT_ndof_all", NDOF_MOTION, 0, KM_CTRL | KM_SHIFT, 0); kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_view_selected", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "use_all_regions", false); - RNA_float_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", NDOF_BUTTON_ROLL_CCW, KM_PRESS, 0, 0)->ptr, "angle", M_PI / -2); - RNA_float_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", NDOF_BUTTON_ROLL_CW, KM_PRESS, 0, 0)->ptr, "angle", M_PI / 2); + RNA_float_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", NDOF_BUTTON_ROLL_CCW, KM_PRESS, 0, 0)->ptr, "angle", -M_PI_2); + RNA_float_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", NDOF_BUTTON_ROLL_CW, KM_PRESS, 0, 0)->ptr, "angle", M_PI_2); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_FRONT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_FRONT); diff --git a/source/blender/editors/space_view3d/view3d_toolbar.c b/source/blender/editors/space_view3d/view3d_toolbar.c index f127f375a9f..af24a99a6f2 100644 --- a/source/blender/editors/space_view3d/view3d_toolbar.c +++ b/source/blender/editors/space_view3d/view3d_toolbar.c @@ -216,7 +216,7 @@ static void view3d_panel_tool_shelf(const bContext *C, Panel *pa) CustomTool *ct; for (ct = st->toolshelf.first; ct; ct = ct->next) { - if (0 == strncmp(context, ct->context, OP_MAX_TYPENAME)) { + if (STREQLEN(context, ct->context, OP_MAX_TYPENAME)) { col = uiLayoutColumn(pa->layout, true); uiItemFullO(col, ct->opname, NULL, ICON_NONE, NULL, WM_OP_INVOKE_REGION_WIN, 0); } diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index b727b96c8cc..14bbdebd138 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -93,8 +93,8 @@ #define MAX_INFO_LEN 256 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg); -static int doEdgeSlide(TransInfo *t, float perc); -static int doVertSlide(TransInfo *t, float perc); +static void doEdgeSlide(TransInfo *t, float perc); +static void doVertSlide(TransInfo *t, float perc); static void drawEdgeSlide(const struct bContext *C, TransInfo *t); static void drawVertSlide(const struct bContext *C, TransInfo *t); @@ -671,8 +671,11 @@ static void viewRedrawPost(bContext *C, TransInfo *t) WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); /* redraw UV editor */ - if (t->mode == TFM_EDGE_SLIDE && (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) + if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) && + (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) + { WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); + } /* XXX temp, first hack to get auto-render in compositor work (ton) */ WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C)); @@ -969,6 +972,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) { char cmode = constraintModeToChar(t); bool handled = false; + const int modifiers_prev = t->modifiers; t->redraw |= handleMouseInput(t, &t->mouse, event); @@ -1495,6 +1499,13 @@ int transformEvent(TransInfo *t, const wmEvent *event) } } + /* if we change snap options, get the unsnapped values back */ + if ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) != + (modifiers_prev & (MOD_SNAP | MOD_SNAP_INVERT))) + { + applyMouseInput(t, &t->mouse, t->mval, t->values); + } + /* Per transform event, if present */ if (t->handleEvent && (!handled || @@ -3129,7 +3140,7 @@ static void initResize(TransInfo *t) t->num.unit_type[2] = B_UNIT_NONE; } -static void headerResize(TransInfo *t, float vec[3], char str[MAX_INFO_LEN]) +static void headerResize(TransInfo *t, const float vec[3], char str[MAX_INFO_LEN]) { char tvec[NUM_STR_REP_LEN * 3]; size_t ofs = 0; @@ -4102,7 +4113,7 @@ static void initTranslation(TransInfo *t) } } -static void headerTranslation(TransInfo *t, float vec[3], char str[MAX_INFO_LEN]) +static void headerTranslation(TransInfo *t, const float vec[3], char str[MAX_INFO_LEN]) { size_t ofs = 0; char tvec[NUM_STR_REP_LEN * 3]; @@ -4195,7 +4206,7 @@ static void headerTranslation(TransInfo *t, float vec[3], char str[MAX_INFO_LEN] } } -static void applyTranslationValue(TransInfo *t, float vec[3]) +static void applyTranslationValue(TransInfo *t, const float vec[3]) { TransData *td = t->data; float tvec[3]; @@ -4932,7 +4943,7 @@ static void initBoneSize(TransInfo *t) t->num.unit_type[2] = B_UNIT_NONE; } -static void headerBoneSize(TransInfo *t, float vec[3], char str[MAX_INFO_LEN]) +static void headerBoneSize(TransInfo *t, const float vec[3], char str[MAX_INFO_LEN]) { char tvec[NUM_STR_REP_LEN * 3]; if (hasNumInput(&t->num)) { @@ -5102,6 +5113,85 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) } /** \} */ +/* -------------------------------------------------------------------- */ +/* Original Data Store */ + +/** \name Orig-Data Store Utility Functions + * \{ */ + +static void slide_origdata_init_flag( + TransInfo *t, SlideOrigData *sod) +{ + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + + if ((t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) && + /* don't do this at all for non-basis shape keys, too easy to + * accidentally break uv maps or vertex colors then */ + (bm->shapenr <= 1)) + { + sod->use_origfaces = true; + } + else { + sod->use_origfaces = false; + } +} + +static void slide_origdata_init_data( + TransInfo *t, SlideOrigData *sod) +{ + if (sod->use_origfaces) { + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + + sod->origfaces = BLI_ghash_ptr_new(__func__); + sod->bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default); + /* we need to have matching customdata */ + BM_mesh_copy_init_customdata(sod->bm_origfaces, bm, NULL); + } +} + +static void slide_origdata_create_date( + TransInfo *t, SlideOrigData *sod, + BMVert **v_pt, unsigned int v_stride, unsigned int v_num) +{ + if (sod->use_origfaces) { + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + + unsigned int i; + for (i = 0; i < v_num; i++, v_pt = (void *)(((char *)v_pt) + v_stride)) { + BMIter fiter; + BMFace *f; + BMVert *v = *v_pt; + + BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) { + if (!BLI_ghash_haskey(sod->origfaces, f)) { + BMFace *f_copy = BM_face_copy(sod->bm_origfaces, bm, f, true, true); + BLI_ghash_insert(sod->origfaces, f, f_copy); + } + } + } + } +} + +static void slide_origdata_free_date( + SlideOrigData *sod) +{ + if (sod->use_origfaces) { + if (sod->bm_origfaces) { + BM_mesh_free(sod->bm_origfaces); + sod->bm_origfaces = NULL; + } + + if (sod->origfaces) { + BLI_ghash_free(sod->origfaces, NULL, NULL); + sod->origfaces = NULL; + } + } +} + +/** \} */ /* -------------------------------------------------------------------- */ /* Transform (Edge Slide) */ @@ -5360,16 +5450,7 @@ static bool createEdgeSlideVerts(TransInfo *t) rv3d = t->ar ? t->ar->regiondata : NULL; } - if ((t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) && - /* don't do this at all for non-basis shape keys, too easy to - * accidentally break uv maps or vertex colors then */ - (bm->shapenr <= 1)) - { - sld->use_origfaces = true; - } - else { - sld->use_origfaces = false; - } + slide_origdata_init_flag(t, &sld->orig_data); sld->is_proportional = true; sld->curr_sv_index = 0; @@ -5761,30 +5842,12 @@ static bool createEdgeSlideVerts(TransInfo *t) } bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - - if (sld->use_origfaces) { - sld->origfaces = BLI_ghash_ptr_new(__func__); - sld->bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default); - /* we need to have matching customdata */ - BM_mesh_copy_init_customdata(sld->bm_origfaces, bm, NULL); - } + slide_origdata_init_data(t, &sld->orig_data); + slide_origdata_create_date(t, &sld->orig_data, &sld->sv->v, sizeof(*sld->sv), sld->totsv); /*create copies of faces for customdata projection*/ sv_array = sld->sv; for (i = 0; i < sld->totsv; i++, sv_array++) { - BMIter fiter; - BMFace *f; - - - if (sld->use_origfaces) { - BM_ITER_ELEM (f, &fiter, sv_array->v, BM_FACES_OF_VERT) { - if (!BLI_ghash_haskey(sld->origfaces, f)) { - BMFace *f_copy = BM_face_copy(sld->bm_origfaces, bm, f, true, true); - BLI_ghash_insert(sld->origfaces, f, f_copy); - } - } - } - /* switch a/b if loop direction is different from global direction */ l_nr = sv_array->loop_nr; if (dot_v3v3(loop_dir[l_nr], mval_dir) < 0.0f) { @@ -5828,11 +5891,12 @@ static bool createEdgeSlideVerts(TransInfo *t) void projectEdgeSlideData(TransInfo *t, bool is_final) { EdgeSlideData *sld = t->customData; + SlideOrigData *sod = &sld->orig_data; TransDataEdgeSlideVert *sv; BMEditMesh *em = sld->em; int i; - if (sld->use_origfaces == false) { + if (sod->use_origfaces == false) { return; } @@ -5844,7 +5908,7 @@ void projectEdgeSlideData(TransInfo *t, bool is_final) BMFace *f_copy; /* the copy of 'f' */ BMFace *f_copy_flip; /* the copy of 'f' or detect if we need to flip to the shorter side. */ - f_copy = BLI_ghash_lookup(sld->origfaces, l->f); + f_copy = BLI_ghash_lookup(sod->origfaces, l->f); /* project onto copied projection face */ f_copy_flip = f_copy; @@ -5858,12 +5922,12 @@ void projectEdgeSlideData(TransInfo *t, bool is_final) if (sld->perc < 0.0f) { if (BM_vert_in_face(sv->v_b, l_ed_sel->radial_next->f)) { - f_copy_flip = BLI_ghash_lookup(sld->origfaces, l_ed_sel->radial_next->f); + f_copy_flip = BLI_ghash_lookup(sod->origfaces, l_ed_sel->radial_next->f); } } else if (sld->perc > 0.0f) { if (BM_vert_in_face(sv->v_a, l_ed_sel->radial_next->f)) { - f_copy_flip = BLI_ghash_lookup(sld->origfaces, l_ed_sel->radial_next->f); + f_copy_flip = BLI_ghash_lookup(sod->origfaces, l_ed_sel->radial_next->f); } } @@ -5949,7 +6013,7 @@ void projectEdgeSlideData(TransInfo *t, bool is_final) l_adj = l; } - f_copy_flip = BLI_ghash_lookup(sld->origfaces, l_adj->f); + f_copy_flip = BLI_ghash_lookup(sod->origfaces, l_adj->f); } } } @@ -5966,34 +6030,23 @@ void projectEdgeSlideData(TransInfo *t, bool is_final) } /* make sure face-attributes are correct (e.g. MTexPoly) */ - BM_elem_attrs_copy(sld->bm_origfaces, em->bm, f_copy, l->f); + BM_elem_attrs_copy(sod->bm_origfaces, em->bm, f_copy, l->f); } } } void freeEdgeSlideTempFaces(EdgeSlideData *sld) { - if (sld->use_origfaces) { - if (sld->bm_origfaces) { - BM_mesh_free(sld->bm_origfaces); - sld->bm_origfaces = NULL; - } - - if (sld->origfaces) { - BLI_ghash_free(sld->origfaces, NULL, NULL); - sld->origfaces = NULL; - } - } + slide_origdata_free_date(&sld->orig_data); } - void freeEdgeSlideVerts(TransInfo *t) { EdgeSlideData *sld = t->customData; if (!sld) return; - + freeEdgeSlideTempFaces(sld); bmesh_edit_end(sld->em->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); @@ -6167,7 +6220,7 @@ static void drawEdgeSlide(const struct bContext *C, TransInfo *t) } } -static int doEdgeSlide(TransInfo *t, float perc) +static void doEdgeSlide(TransInfo *t, float perc) { EdgeSlideData *sld = t->customData; TransDataEdgeSlideVert *svlist = sld->sv, *sv; @@ -6224,8 +6277,6 @@ static int doEdgeSlide(TransInfo *t, float perc) } projectEdgeSlideData(t, 0); - - return 1; } static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) @@ -6390,6 +6441,8 @@ static bool createVertSlideVerts(TransInfo *t) rv3d = ar ? ar->regiondata : NULL; } + slide_origdata_init_flag(t, &sld->orig_data); + sld->is_proportional = true; sld->curr_sv_index = 0; sld->flipped_vtx = false; @@ -6485,6 +6538,10 @@ static bool createVertSlideVerts(TransInfo *t) sld->sv = sv_array; sld->totsv = j; + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + slide_origdata_init_data(t, &sld->orig_data); + slide_origdata_create_date(t, &sld->orig_data, &sld->sv->v, sizeof(*sld->sv), sld->totsv); + sld->em = em; sld->perc = 0.0f; @@ -6499,6 +6556,53 @@ static bool createVertSlideVerts(TransInfo *t) return true; } +void projectVertSlideData(TransInfo *t, bool is_final) +{ + VertSlideData *sld = t->customData; + SlideOrigData *sod = &sld->orig_data; + TransDataVertSlideVert *sv; + BMEditMesh *em = sld->em; + int i; + + if (sod->use_origfaces == false) { + return; + } + + for (i = 0, sv = sld->sv; i < sld->totsv; sv++, i++) { + BMIter fiter; + BMLoop *l; + + BM_ITER_ELEM (l, &fiter, sv->v, BM_LOOPS_OF_VERT) { + BMFace *f_copy; /* the copy of 'f' */ + BMFace *f_copy_flip; /* the copy of 'f' or detect if we need to flip to the shorter side. */ + + f_copy = BLI_ghash_lookup(sod->origfaces, l->f); + + /* project onto copied projection face */ + f_copy_flip = f_copy; + + /* only loop data, no vertex data since that contains shape keys, + * and we do not want to mess up other shape keys */ + BM_loop_interp_from_face(em->bm, l, f_copy_flip, false, false); + + if (is_final) { + BM_loop_interp_multires(em->bm, l, f_copy_flip); + if (f_copy != f_copy_flip) { + BM_loop_interp_multires(em->bm, l, f_copy); + } + } + + /* make sure face-attributes are correct (e.g. MTexPoly) */ + BM_elem_attrs_copy(sod->bm_origfaces, em->bm, f_copy, l->f); + } + } +} + +void freeVertSlideTempFaces(VertSlideData *sld) +{ + slide_origdata_free_date(&sld->orig_data); +} + void freeVertSlideVerts(TransInfo *t) { VertSlideData *sld = t->customData; @@ -6506,6 +6610,9 @@ void freeVertSlideVerts(TransInfo *t) if (!sld) return; + freeVertSlideTempFaces(sld); + + bmesh_edit_end(sld->em->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); if (sld->totsv > 0) { TransDataVertSlideVert *sv = sld->sv; @@ -6702,7 +6809,7 @@ static void drawVertSlide(const struct bContext *C, TransInfo *t) } } -static int doVertSlide(TransInfo *t, float perc) +static void doVertSlide(TransInfo *t, float perc) { VertSlideData *sld = t->customData; TransDataVertSlideVert *svlist = sld->sv, *sv; @@ -6742,7 +6849,7 @@ static int doVertSlide(TransInfo *t, float perc) } } - return 1; + projectVertSlideData(t, false); } static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) @@ -7117,7 +7224,7 @@ static void initSeqSlide(TransInfo *t) t->num.unit_type[1] = B_UNIT_NONE; } -static void headerSeqSlide(TransInfo *t, float val[2], char str[MAX_INFO_LEN]) +static void headerSeqSlide(TransInfo *t, const float val[2], char str[MAX_INFO_LEN]) { char tvec[NUM_STR_REP_LEN * 3]; size_t ofs = 0; @@ -7516,7 +7623,7 @@ static void initTimeSlide(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; } -static void headerTimeSlide(TransInfo *t, float sval, char str[MAX_INFO_LEN]) +static void headerTimeSlide(TransInfo *t, const float sval, char str[MAX_INFO_LEN]) { char tvec[NUM_STR_REP_LEN * 3]; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 0d824be862e..2330ec6e79e 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -213,18 +213,23 @@ typedef struct TransDataEdgeSlideVert { int loop_nr; } TransDataEdgeSlideVert; + +/* store original data so we can correct UV's and similar when sliding */ +typedef struct SlideOrigData { + /* flag that is set when origfaces is initialized */ + bool use_origfaces; + struct GHash *origfaces; + struct BMesh *bm_origfaces; +} SlideOrigData; + typedef struct EdgeSlideData { TransDataEdgeSlideVert *sv; int totsv; - - struct GHash *origfaces; int mval_start[2], mval_end[2]; struct BMEditMesh *em; - /* flag that is set when origfaces is initialized */ - bool use_origfaces; - struct BMesh *bm_origfaces; + SlideOrigData orig_data; float perc; @@ -251,6 +256,8 @@ typedef struct VertSlideData { struct BMEditMesh *em; + SlideOrigData orig_data; + float perc; bool is_proportional; @@ -699,7 +706,9 @@ void freeEdgeSlideTempFaces(EdgeSlideData *sld); void freeEdgeSlideVerts(TransInfo *t); void projectEdgeSlideData(TransInfo *t, bool is_final); +void freeVertSlideTempFaces(VertSlideData *sld); void freeVertSlideVerts(TransInfo *t); +void projectVertSlideData(TransInfo *t, bool is_final); /* TODO. transform_queries.c */ diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index d8f17315c01..ccb81c7342b 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -208,7 +208,7 @@ static void axisProjection(TransInfo *t, const float axis[3], const float in[3], viewAxisCorrectCenter(t, t_con_center); angle = fabsf(angle_v3v3(axis, t->viewinv[2])); - if (angle > (float)M_PI / 2.0f) { + if (angle > (float)M_PI_2) { angle = (float)M_PI - angle; } angle = RAD2DEGF(angle); diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index c1350b1f076..42f7087d4a7 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -5354,7 +5354,7 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o /* only if bone name matches too... * NOTE: this will do constraints too, but those are ok to do here too? */ - if (pchanName && strcmp(pchanName, pchan->name) == 0) + if (pchanName && STREQ(pchanName, pchan->name)) insert_keyframe(reports, id, act, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, flag); if (pchanName) MEM_freeN(pchanName); @@ -5597,6 +5597,12 @@ void special_aftertrans_update(bContext *C, TransInfo *t) * during cleanup - psy-fi */ freeEdgeSlideTempFaces(sld); } + else if (t->mode == TFM_VERT_SLIDE) { + /* as above */ + VertSlideData *sld = t->customData; + projectVertSlideData(t, true); + freeVertSlideTempFaces(sld); + } if (t->obedit->type == OB_MESH) { special_aftertrans_update__mesh(C, t); @@ -7321,6 +7327,11 @@ static void createTransGPencil(bContext *C, TransInfo *t) bGPDstroke *gps; for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + if (propedit) { /* Proportional Editing... */ if (propedit_connected) { @@ -7424,6 +7435,11 @@ static void createTransGPencil(bContext *C, TransInfo *t) TransData *tail = td; bool stroke_ok; + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* What we need to include depends on proportional editing settings... */ if (propedit) { if (propedit_connected) { diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index fcf789546e8..3000b3e00bc 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -1874,9 +1874,12 @@ void calculatePropRatio(TransInfo *t) TransData *td = t->data; int i; float dist; - short connected = t->flag & T_PROP_CONNECTED; + const bool connected = (t->flag & T_PROP_CONNECTED) != 0; + + t->proptext[0] = '\0'; if (t->flag & T_PROP_EDIT) { + const char *pet_id = NULL; for (i = 0; i < t->total; i++, td++) { if (td->flag & TD_SELECTED) { td->factor = 1.0f; @@ -1937,6 +1940,9 @@ void calculatePropRatio(TransInfo *t) case PROP_RANDOM: td->factor = BLI_frand() * dist; break; + case PROP_INVSQUARE: + td->factor = dist * (2.0f - dist); + break; default: td->factor = 1; break; @@ -1945,35 +1951,40 @@ void calculatePropRatio(TransInfo *t) } switch (t->prop_mode) { case PROP_SHARP: - strcpy(t->proptext, IFACE_("(Sharp)")); + pet_id = N_("(Sharp)"); break; case PROP_SMOOTH: - strcpy(t->proptext, IFACE_("(Smooth)")); + pet_id = N_("(Smooth)"); break; case PROP_ROOT: - strcpy(t->proptext, IFACE_("(Root)")); + pet_id = N_("(Root)"); break; case PROP_LIN: - strcpy(t->proptext, IFACE_("(Linear)")); + pet_id = N_("(Linear)"); break; case PROP_CONST: - strcpy(t->proptext, IFACE_("(Constant)")); + pet_id = N_("(Constant)"); break; case PROP_SPHERE: - strcpy(t->proptext, IFACE_("(Sphere)")); + pet_id = N_("(Sphere)"); break; case PROP_RANDOM: - strcpy(t->proptext, IFACE_("(Random)")); + pet_id = N_("(Random)"); + break; + case PROP_INVSQUARE: + pet_id = N_("(InvSquare)"); break; default: - t->proptext[0] = '\0'; break; } + + if (pet_id) { + BLI_strncpy(t->proptext, IFACE_(pet_id), sizeof(t->proptext)); + } } else { for (i = 0; i < t->total; i++, td++) { td->factor = 1.0; } - t->proptext[0] = '\0'; } } diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index f2869843dd5..38190d407ab 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -177,7 +177,7 @@ static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], con mul_qt_v3(quat, gmat[0]); /* Y-axis */ - axis_angle_to_quat(quat, axis, M_PI / 2.0); + axis_angle_to_quat(quat, axis, M_PI_2); copy_v3_v3(gmat[1], gmat[0]); mul_qt_v3(quat, gmat[1]); diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 25dee50a192..1498e2894d4 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -868,7 +868,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot) RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); - Transform_Properties(ot, P_MIRROR | P_SNAP); + Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV); } static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot) diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 32d4f2e5a15..438eb1f3864 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -231,7 +231,7 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha BLI_split_file_part(abs_name, fi, sizeof(fi)); BLI_snprintf(local_name, sizeof(local_name), "//%s/%s", folder, fi); - if (strcmp(abs_name, local_name) != 0) { + if (!STREQ(abs_name, local_name)) { switch (checkPackedFile(local_name, pf)) { case PF_NOFILE: BLI_snprintf(line, sizeof(line), IFACE_("Create %s"), local_name); diff --git a/source/blender/editors/util/editmode_undo.c b/source/blender/editors/util/editmode_undo.c index 2428ee21367..7f5edb5ea9e 100644 --- a/source/blender/editors/util/editmode_undo.c +++ b/source/blender/editors/util/editmode_undo.c @@ -206,7 +206,7 @@ static void undo_clean_stack(bContext *C) /* for when objects are converted, renamed, or global undo changes pointers... */ if (uel->type == obedit->type) { - if (strcmp(uel->id.name, obedit->id.name) == 0) { + if (STREQ(uel->id.name, obedit->id.name)) { if (uel->validate_undo == NULL) is_valid = true; else if (uel->validate_undo(uel->undodata, editdata)) @@ -305,7 +305,7 @@ void undo_editmode_name(bContext *C, const char *undoname) UndoElem *uel; for (uel = undobase.last; uel; uel = uel->prev) { - if (strcmp(undoname, uel->name) == 0) + if (STREQ(undoname, uel->name)) break; } if (uel && uel->prev) { @@ -321,7 +321,7 @@ int undo_editmode_valid(const char *undoname) UndoElem *uel; for (uel = undobase.last; uel; uel = uel->prev) { - if (strcmp(undoname, uel->name) == 0) + if (STREQ(undoname, uel->name)) break; } return uel != NULL; diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index fac57490b44..ab882a388ad 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -580,7 +580,7 @@ static int undo_history_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } else if (undosys == UNDOSYSTEM_IMAPAINT) { - ED_undo_paint_step_num(C, UNDO_PAINT_IMAGE, item ); + ED_undo_paint_step_num(C, UNDO_PAINT_IMAGE, item); } else { ED_viewport_render_kill_jobs(CTX_wm_manager(C), CTX_data_main(C), true); diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index 569fe1c326d..00615f9bef7 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -2672,8 +2672,8 @@ static PBool p_abf_matrix_invert(PAbfSystem *sys, PChart *chart) } for (i = 0; i < ninterior; i++) { - sys->lambdaPlanar[i] += nlGetVariable(0, i); - sys->lambdaLength[i] += nlGetVariable(0, ninterior + i); + sys->lambdaPlanar[i] += (float)nlGetVariable(0, i); + sys->lambdaLength[i] += (float)nlGetVariable(0, ninterior + i); } } @@ -4561,7 +4561,7 @@ void param_pack(ParamHandle *handle, float margin, bool do_rotate) box->index = i; /* warning this index skips PCHART_NOPACK boxes */ if (margin > 0.0f) - area += sqrtf(box->w * box->h); + area += (double)sqrtf(box->w * box->h); } if (margin > 0.0f) { diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index c5931cf2943..4cb1f8943f9 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1601,7 +1601,7 @@ static int stitch_init(bContext *C, wmOperator *op) BMFace *efa; BMLoop *l; BMIter iter, liter; - GHashIterator *ghi; + GHashIterator gh_iter; UvEdge *all_edges; StitchState *state; Scene *scene = CTX_data_scene(C); @@ -1748,14 +1748,11 @@ static int stitch_init(bContext *C, wmOperator *op) } } - - ghi = BLI_ghashIterator_new(edge_hash); total_edges = BLI_ghash_size(edge_hash); state->edges = edges = MEM_mallocN(sizeof(*edges) * total_edges, "stitch_edges"); /* I assume any system will be able to at least allocate an iterator :p */ if (!edges) { - BLI_ghashIterator_free(ghi); state_delete(state); return 0; } @@ -1763,12 +1760,12 @@ static int stitch_init(bContext *C, wmOperator *op) state->total_separate_edges = total_edges; /* fill the edges with data */ - for (i = 0, BLI_ghashIterator_init(ghi, edge_hash); !BLI_ghashIterator_done(ghi); BLI_ghashIterator_step(ghi)) { - edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(ghi)); + i = 0; + GHASH_ITER (gh_iter, edge_hash) { + edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); } /* cleanup temporary stuff */ - BLI_ghashIterator_free(ghi); MEM_freeN(all_edges); BLI_ghash_free(edge_hash, NULL, NULL); diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index b5e27ab0cf6..793f84b05ec 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -1130,7 +1130,7 @@ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel) ParamHandle *handle; const bool fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0; - const bool correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) != 0; + const bool correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0; bool use_subsurf; modifier_unwrap_state(obedit, scene, &use_subsurf); diff --git a/source/blender/freestyle/intern/application/Controller.cpp b/source/blender/freestyle/intern/application/Controller.cpp index f8931d32f56..8d9b1a4fb52 100644 --- a/source/blender/freestyle/intern/application/Controller.cpp +++ b/source/blender/freestyle/intern/application/Controller.cpp @@ -295,7 +295,7 @@ int Controller::LoadMesh(Render *re, SceneRenderLayer *srl) } cam->setProjectionMatrix(proj); _RootNode->AddChild(cam); - _RootNode->AddChild(new NodeSceneRenderLayer(*srl)); + _RootNode->AddChild(new NodeSceneRenderLayer(*re->scene, *srl)); sceneHashFunc.reset(); //blenderScene->accept(sceneHashFunc); @@ -869,7 +869,7 @@ void Controller::DrawStrokes() real d = _Chrono.stop(); if (G.debug & G_DEBUG_FREESTYLE) { cout << "Strokes generation : " << d << endl; - cout << "Stroke count : " << _Canvas->stroke_count << endl; + cout << "Stroke count : " << _Canvas->getStrokeCount() << endl; } resetModified(); DeleteViewMap(); diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 29f3bfe0d94..70de246a8de 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -654,6 +654,12 @@ int BlenderStrokeRenderer::GenerateScene() { GenerateStrokeMesh(*it, true); } + return get_stroke_count(); +} + +// Return the number of strokes +int BlenderStrokeRenderer::get_stroke_count() const +{ return strokeGroups.size() + texturedStrokeGroups.size(); } @@ -912,7 +918,7 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex) BLI_assert(mesh->totedge == edge_index); BLI_assert(mesh->totloop == loop_index); BLI_assert(mesh->totcol == material_index); - BKE_mesh_validate(mesh, true); + BKE_mesh_validate(mesh, true, true); #endif } @@ -956,7 +962,8 @@ Render *BlenderStrokeRenderer::RenderScene(Render *re, bool render) Render *freestyle_render = RE_NewRender(freestyle_scene->id.name); - RE_RenderFreestyleStrokes(freestyle_render, freestyle_bmain, freestyle_scene, render); + RE_RenderFreestyleStrokes(freestyle_render, freestyle_bmain, freestyle_scene, + render && get_stroke_count() > 0); return freestyle_render; } diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.h b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.h index a381932e9e7..ec53efa14cd 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.h +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.h @@ -85,6 +85,7 @@ protected: static const char *uvNames[]; + int get_stroke_count() const; float get_stroke_vertex_z(void) const; unsigned int get_stroke_mesh_id(void) const; bool test_triangle_visibility(StrokeVertexRep *svRep[3]) const; @@ -96,7 +97,6 @@ protected: #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BlenderStrokeRenderer") #endif - }; } /* namespace Freestyle */ diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index cfadf80dcf6..32b4c5455a7 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -496,7 +496,7 @@ void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_r rl = render_get_active_layer( freestyle_render, freestyle_render->result ); if (!rl || rl->rectf == NULL) { if (G.debug & G_DEBUG_FREESTYLE) { - cout << "Cannot find Freestyle result image" << endl; + cout << "No Freestyle result image to composite" << endl; } return; } diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index f4ead300d5e..487a473b7bd 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -87,24 +87,24 @@ static PyObject *Freestyle_getCurrentScene(PyObject *self) static int ramp_blend_type(const char *type) { - if (!strcmp(type, "MIX")) return MA_RAMP_BLEND; - if (!strcmp(type, "ADD")) return MA_RAMP_ADD; - if (!strcmp(type, "MULTIPLY")) return MA_RAMP_MULT; - if (!strcmp(type, "SUBTRACT")) return MA_RAMP_SUB; - if (!strcmp(type, "SCREEN")) return MA_RAMP_SCREEN; - if (!strcmp(type, "DIVIDE")) return MA_RAMP_DIV; - if (!strcmp(type, "DIFFERENCE")) return MA_RAMP_DIFF; - if (!strcmp(type, "DARKEN")) return MA_RAMP_DARK; - if (!strcmp(type, "LIGHTEN")) return MA_RAMP_LIGHT; - if (!strcmp(type, "OVERLAY")) return MA_RAMP_OVERLAY; - if (!strcmp(type, "DODGE")) return MA_RAMP_DODGE; - if (!strcmp(type, "BURN")) return MA_RAMP_BURN; - if (!strcmp(type, "HUE")) return MA_RAMP_HUE; - if (!strcmp(type, "SATURATION")) return MA_RAMP_SAT; - if (!strcmp(type, "VALUE")) return MA_RAMP_VAL; - if (!strcmp(type, "COLOR")) return MA_RAMP_COLOR; - if (!strcmp(type, "SOFT_LIGHT")) return MA_RAMP_SOFT; - if (!strcmp(type, "LINEAR_LIGHT")) return MA_RAMP_LINEAR; + if (STREQ(type, "MIX")) return MA_RAMP_BLEND; + if (STREQ(type, "ADD")) return MA_RAMP_ADD; + if (STREQ(type, "MULTIPLY")) return MA_RAMP_MULT; + if (STREQ(type, "SUBTRACT")) return MA_RAMP_SUB; + if (STREQ(type, "SCREEN")) return MA_RAMP_SCREEN; + if (STREQ(type, "DIVIDE")) return MA_RAMP_DIV; + if (STREQ(type, "DIFFERENCE")) return MA_RAMP_DIFF; + if (STREQ(type, "DARKEN")) return MA_RAMP_DARK; + if (STREQ(type, "LIGHTEN")) return MA_RAMP_LIGHT; + if (STREQ(type, "OVERLAY")) return MA_RAMP_OVERLAY; + if (STREQ(type, "DODGE")) return MA_RAMP_DODGE; + if (STREQ(type, "BURN")) return MA_RAMP_BURN; + if (STREQ(type, "HUE")) return MA_RAMP_HUE; + if (STREQ(type, "SATURATION")) return MA_RAMP_SAT; + if (STREQ(type, "VALUE")) return MA_RAMP_VAL; + if (STREQ(type, "COLOR")) return MA_RAMP_COLOR; + if (STREQ(type, "SOFT_LIGHT")) return MA_RAMP_SOFT; + if (STREQ(type, "LINEAR_LIGHT")) return MA_RAMP_LINEAR; return -1; } diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp index 6c6e821a1e4..d933d9f6ed7 100644 --- a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp +++ b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp @@ -220,7 +220,6 @@ static PyObject *ViewShape_vertices_get(BPy_ViewShape *self, void *UNUSED(closur static int ViewShape_vertices_set(BPy_ViewShape *self, PyObject *value, void *UNUSED(closure)) { - PyObject *list = 0; PyObject *item; vector< ViewVertex *> v; @@ -229,9 +228,9 @@ static int ViewShape_vertices_set(BPy_ViewShape *self, PyObject *value, void *UN return -1; } - v.reserve(PyList_Size(list)); - for (unsigned int i = 0; i < PyList_Size(list); i++) { - item = PyList_GET_ITEM(list, i); + v.reserve(PyList_GET_SIZE(value)); + for (unsigned int i = 0; i < PyList_GET_SIZE(value); i++) { + item = PyList_GET_ITEM(value, i); if (BPy_ViewVertex_Check(item)) { v.push_back(((BPy_ViewVertex *)item)->vv); } @@ -264,7 +263,6 @@ static PyObject *ViewShape_edges_get(BPy_ViewShape *self, void *UNUSED(closure)) static int ViewShape_edges_set(BPy_ViewShape *self, PyObject *value, void *UNUSED(closure)) { - PyObject *list = 0; PyObject *item; vector<ViewEdge *> v; @@ -273,9 +271,9 @@ static int ViewShape_edges_set(BPy_ViewShape *self, PyObject *value, void *UNUSE return -1; } - v.reserve(PyList_Size(list)); - for (int i = 0; i < PyList_Size(list); i++) { - item = PyList_GET_ITEM(list, i); + v.reserve(PyList_GET_SIZE(value)); + for (int i = 0; i < PyList_GET_SIZE(value); i++) { + item = PyList_GET_ITEM(value, i); if (BPy_ViewEdge_Check(item)) { v.push_back(((BPy_ViewEdge *)item)->ve); } diff --git a/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp b/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp index 9329bd40c76..edc49eb8004 100644 --- a/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp +++ b/source/blender/freestyle/intern/python/Iterator/BPy_AdjacencyIterator.cpp @@ -117,7 +117,7 @@ static PyObject *AdjacencyIterator_iternext(BPy_AdjacencyIterator *self) self->at_start = false; else { self->a_it->increment(); - if (self->a_it->isEnd()){ + if (self->a_it->isEnd()) { PyErr_SetNone(PyExc_StopIteration); return NULL; } diff --git a/source/blender/freestyle/intern/scene_graph/NodeSceneRenderLayer.h b/source/blender/freestyle/intern/scene_graph/NodeSceneRenderLayer.h index 2fc08bb1175..4b079df5632 100644 --- a/source/blender/freestyle/intern/scene_graph/NodeSceneRenderLayer.h +++ b/source/blender/freestyle/intern/scene_graph/NodeSceneRenderLayer.h @@ -29,7 +29,7 @@ #include "Node.h" extern "C" { -#include "DNA_scene_types.h" /* for SceneRenderLayer */ +#include "DNA_scene_types.h" /* for Scene and SceneRenderLayer */ } using namespace std; @@ -39,14 +39,24 @@ namespace Freestyle { class NodeSceneRenderLayer : public Node { public: - inline NodeSceneRenderLayer(SceneRenderLayer& srl) : Node(), _SceneRenderLayer(srl) {} + inline NodeSceneRenderLayer(Scene& scene, SceneRenderLayer& srl) : Node(), _Scene(scene), _SceneRenderLayer(srl) {} virtual ~NodeSceneRenderLayer() {} + inline struct Scene& scene() const + { + return _Scene; + } + inline struct SceneRenderLayer& sceneRenderLayer() const { return _SceneRenderLayer; } + inline void setSceneRenderLayer(Scene& scene) + { + _Scene = scene; + } + inline void setSceneRenderLayer(SceneRenderLayer& srl) { _SceneRenderLayer = srl; @@ -56,6 +66,8 @@ public: virtual void accept(SceneVisitor& v); protected: + + Scene& _Scene; SceneRenderLayer& _SceneRenderLayer; }; diff --git a/source/blender/freestyle/intern/scene_graph/SceneHash.cpp b/source/blender/freestyle/intern/scene_graph/SceneHash.cpp index ee1d0c53b87..22538736fbf 100644 --- a/source/blender/freestyle/intern/scene_graph/SceneHash.cpp +++ b/source/blender/freestyle/intern/scene_graph/SceneHash.cpp @@ -35,13 +35,18 @@ string SceneHash::toString() return ss.str(); } -void SceneHash::visitNodeSceneRenderLayer(NodeSceneRenderLayer& srl) +void SceneHash::visitNodeSceneRenderLayer(NodeSceneRenderLayer& node) { - struct FreestyleConfig *config = &srl.sceneRenderLayer().freestyleConfig; - adler32((unsigned char *)&config->flags, sizeof(int)); - adler32((unsigned char *)&config->crease_angle, sizeof(float)); - adler32((unsigned char *)&config->sphere_radius, sizeof(float)); - adler32((unsigned char *)&config->dkr_epsilon, sizeof(float)); + struct RenderData *r = &node.scene().r; + adler32((unsigned char *)&r->xsch, sizeof(r->xsch)); // resolution_x + adler32((unsigned char *)&r->ysch, sizeof(r->ysch)); // resolution_y + adler32((unsigned char *)&r->size, sizeof(r->size)); // resolution_percentage + + struct FreestyleConfig *config = &node.sceneRenderLayer().freestyleConfig; + adler32((unsigned char *)&config->flags, sizeof(config->flags)); + adler32((unsigned char *)&config->crease_angle, sizeof(config->crease_angle)); + adler32((unsigned char *)&config->sphere_radius, sizeof(config->sphere_radius)); + adler32((unsigned char *)&config->dkr_epsilon, sizeof(config->dkr_epsilon)); } void SceneHash::visitNodeCamera(NodeCamera& cam) diff --git a/source/blender/freestyle/intern/stroke/Canvas.h b/source/blender/freestyle/intern/stroke/Canvas.h index b56b5f92c14..5919344b6e0 100644 --- a/source/blender/freestyle/intern/stroke/Canvas.h +++ b/source/blender/freestyle/intern/stroke/Canvas.h @@ -95,6 +95,7 @@ protected: static const char *_MapsPath; SteerableViewMap *_steerableViewMap; bool _basic; + int stroke_count; public: /* Builds the Canvas */ @@ -213,7 +214,10 @@ public: return false; } - int stroke_count; + inline int getStrokeCount() const + { + return stroke_count; + } /*! modifiers */ inline void setSelectedFEdge(FEdge *iFEdge) diff --git a/source/blender/freestyle/intern/stroke/StrokeRep.cpp b/source/blender/freestyle/intern/stroke/StrokeRep.cpp index f7857107006..ab06e207331 100644 --- a/source/blender/freestyle/intern/stroke/StrokeRep.cpp +++ b/source/blender/freestyle/intern/stroke/StrokeRep.cpp @@ -425,7 +425,7 @@ void Strip::cleanUpSingularities (const vector<StrokeVertex*>& iStrokeVertices) Vec2r avP(0.0, 0.0); for (j = i - timeSinceSingu1; j <= i; j++) avP = Vec2r(avP + _vertices[2 * j]->point2d()); - avP = Vec2r( 1.0 / float(timeSinceSingu1 + 1) * avP); + avP = Vec2r(1.0 / float(timeSinceSingu1 + 1) * avP); for (j = i - timeSinceSingu1; j <= i; j++) _vertices[2 * j]->setPoint2d(avP); //_vertex[2 * j] = _vertex[2 * i]; diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 09a5653b1ec..64ce936e64a 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -32,6 +32,7 @@ #ifndef __GPU_MATERIAL_H__ #define __GPU_MATERIAL_H__ +#include "DNA_customdata_types.h" /* for CustomDataType */ #include "DNA_listBase.h" #include "BLI_sys_types.h" /* for bool */ @@ -68,6 +69,7 @@ typedef struct GPULamp GPULamp; /* Functions to create GPU Materials nodes */ typedef enum GPUType { + /* The value indicates the number of elements in each type */ GPU_NONE = 0, GPU_FLOAT = 1, GPU_VEC2 = 2, @@ -75,20 +77,22 @@ typedef enum GPUType { GPU_VEC4 = 4, GPU_MAT3 = 9, GPU_MAT4 = 16, + GPU_TEX2D = 1002, GPU_SHADOW2D = 1003, GPU_ATTRIB = 3001 } GPUType; typedef enum GPUBuiltin { - GPU_VIEW_MATRIX = 1, - GPU_OBJECT_MATRIX = 2, - GPU_INVERSE_VIEW_MATRIX = 4, - GPU_INVERSE_OBJECT_MATRIX = 8, - GPU_VIEW_POSITION = 16, - GPU_VIEW_NORMAL = 32, - GPU_OBCOLOR = 64, - GPU_AUTO_BUMPSCALE = 128, + GPU_VIEW_MATRIX = (1 << 0), + GPU_OBJECT_MATRIX = (1 << 1), + GPU_INVERSE_VIEW_MATRIX = (1 << 2), + GPU_INVERSE_OBJECT_MATRIX = (1 << 3), + GPU_VIEW_POSITION = (1 << 4), + GPU_VIEW_NORMAL = (1 << 5), + GPU_OBCOLOR = (1 << 6), + GPU_AUTO_BUMPSCALE = (1 << 7), + GPU_CAMERA_TEXCO_FACTORS = (1 << 8), } GPUBuiltin; typedef enum GPUOpenGLBuiltin { @@ -98,7 +102,7 @@ typedef enum GPUOpenGLBuiltin { typedef enum GPUMatType { GPU_MATERIAL_TYPE_MESH = 1, - GPU_MATERIAL_TYPE_WORLD = 2, + GPU_MATERIAL_TYPE_WORLD = 2, } GPUMatType; @@ -120,13 +124,40 @@ typedef struct GPUNodeStack { short sockettype; } GPUNodeStack; -GPUNodeLink *GPU_attribute(int type, const char *name); +typedef enum GPUDynamicType { + GPU_DYNAMIC_NONE = 0, + GPU_DYNAMIC_OBJECT_VIEWMAT = 1, + GPU_DYNAMIC_OBJECT_MAT = 2, + GPU_DYNAMIC_OBJECT_VIEWIMAT = 3, + GPU_DYNAMIC_OBJECT_IMAT = 4, + GPU_DYNAMIC_OBJECT_COLOR = 5, + GPU_DYNAMIC_OBJECT_AUTOBUMPSCALE = 15, + + GPU_DYNAMIC_LAMP_FIRST = 6, + GPU_DYNAMIC_LAMP_DYNVEC = 6, + GPU_DYNAMIC_LAMP_DYNCO = 7, + GPU_DYNAMIC_LAMP_DYNIMAT = 8, + GPU_DYNAMIC_LAMP_DYNPERSMAT = 9, + GPU_DYNAMIC_LAMP_DYNENERGY = 10, + GPU_DYNAMIC_LAMP_DYNCOL = 11, + GPU_DYNAMIC_LAMP_LAST = 11, + GPU_DYNAMIC_SAMPLER_2DBUFFER = 12, + GPU_DYNAMIC_SAMPLER_2DIMAGE = 13, + GPU_DYNAMIC_SAMPLER_2DSHADOW = 14, + GPU_DYNAMIC_LAMP_DISTANCE = 16, + GPU_DYNAMIC_LAMP_ATT1 = 17, + GPU_DYNAMIC_LAMP_ATT2 = 18, + GPU_DYNAMIC_LAMP_SPOTSIZE = 19, + GPU_DYNAMIC_LAMP_SPOTBLEND = 20, +} GPUDynamicType; + +GPUNodeLink *GPU_attribute(CustomDataType type, const char *name); GPUNodeLink *GPU_uniform(float *num); -GPUNodeLink *GPU_dynamic_uniform(float *num, int dynamictype, void *data); +GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *data); GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, bool is_data); GPUNodeLink *GPU_image_preview(struct PreviewImage *prv); GPUNodeLink *GPU_texture(int size, float *pixels); -GPUNodeLink *GPU_dynamic_texture(struct GPUTexture *tex, int dynamictype, void *data); +GPUNodeLink *GPU_dynamic_texture(struct GPUTexture *tex, GPUDynamicType dynamictype, void *data); GPUNodeLink *GPU_builtin(GPUBuiltin builtin); GPUNodeLink *GPU_opengl_builtin(GPUOpenGLBuiltin builtin); @@ -147,7 +178,7 @@ void GPU_material_free(struct ListBase *gpumaterial); void GPU_materials_free(void); bool GPU_lamp_override_visible(GPULamp *lamp, struct SceneRenderLayer *srl, struct Material *ma); -void GPU_material_bind(GPUMaterial *material, int oblay, int viewlay, double time, int mipmap, float viewmat[4][4], float viewinv[4][4], bool scenelock); +void GPU_material_bind(GPUMaterial *material, int oblay, int viewlay, double time, int mipmap, float viewmat[4][4], float viewinv[4][4], float cameraborder[4], bool scenelock); void GPU_material_bind_uniforms(GPUMaterial *material, float obmat[4][4], float obcol[4], float autobumpscale); void GPU_material_unbind(GPUMaterial *material); int GPU_material_bound(GPUMaterial *material); @@ -180,33 +211,6 @@ void GPU_shaderesult_set(GPUShadeInput *shi, GPUShadeResult *shr); /* Export GLSL shader */ -typedef enum GPUDynamicType { - GPU_DYNAMIC_NONE = 0, - GPU_DYNAMIC_OBJECT_VIEWMAT = 1, - GPU_DYNAMIC_OBJECT_MAT = 2, - GPU_DYNAMIC_OBJECT_VIEWIMAT = 3, - GPU_DYNAMIC_OBJECT_IMAT = 4, - GPU_DYNAMIC_OBJECT_COLOR = 5, - GPU_DYNAMIC_OBJECT_AUTOBUMPSCALE = 15, - - GPU_DYNAMIC_LAMP_FIRST = 6, - GPU_DYNAMIC_LAMP_DYNVEC = 6, - GPU_DYNAMIC_LAMP_DYNCO = 7, - GPU_DYNAMIC_LAMP_DYNIMAT = 8, - GPU_DYNAMIC_LAMP_DYNPERSMAT = 9, - GPU_DYNAMIC_LAMP_DYNENERGY = 10, - GPU_DYNAMIC_LAMP_DYNCOL = 11, - GPU_DYNAMIC_LAMP_LAST = 11, - GPU_DYNAMIC_SAMPLER_2DBUFFER = 12, - GPU_DYNAMIC_SAMPLER_2DIMAGE = 13, - GPU_DYNAMIC_SAMPLER_2DSHADOW = 14, - GPU_DYNAMIC_LAMP_DISTANCE = 16, - GPU_DYNAMIC_LAMP_ATT1 = 17, - GPU_DYNAMIC_LAMP_ATT2 = 18, - GPU_DYNAMIC_LAMP_SPOTSIZE = 19, - GPU_DYNAMIC_LAMP_SPOTBLEND = 20, -} GPUDynamicType; - typedef enum GPUDataType { GPU_DATA_NONE = 0, GPU_DATA_1I = 1, // 1 integer @@ -267,7 +271,7 @@ void GPU_lamp_update_colors(GPULamp *lamp, float r, float g, float b, float ener void GPU_lamp_update_distance(GPULamp *lamp, float distance, float att1, float att2); void GPU_lamp_update_spot(GPULamp *lamp, float spotsize, float spotblend); int GPU_lamp_shadow_layer(GPULamp *lamp); -GPUNodeLink *GPU_lamp_get_data(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **col, GPUNodeLink **lv, GPUNodeLink **dist, GPUNodeLink **shadow); +GPUNodeLink *GPU_lamp_get_data(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **col, GPUNodeLink **lv, GPUNodeLink **dist, GPUNodeLink **shadow, GPUNodeLink **energy); #ifdef __cplusplus } diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index f0ef55ae673..d1102b03e9a 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -187,8 +187,10 @@ static void gpu_buffer_pool_free_unused(GPUBufferPool *pool) while (pool->totbuf) gpu_buffer_pool_delete_last(pool); - glDeleteBuffersARB(pool->totpbvhbufids, pool->pbvhbufids); - pool->totpbvhbufids = 0; + if (pool->totpbvhbufids > 0) { + glDeleteBuffersARB(pool->totpbvhbufids, pool->pbvhbufids); + pool->totpbvhbufids = 0; + } BLI_mutex_unlock(&buffer_mutex); } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 1f9efdcbefd..97064f43f5d 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -62,14 +62,32 @@ extern char datatoc_gpu_shader_vertex_world_glsl[]; static char *glsl_material_library = NULL; -/* structs and defines */ - +/* type definitions and constants */ + +enum { + MAX_FUNCTION_NAME = 64 +}; +enum { + MAX_PARAMETER = 32 +}; + +typedef enum { + FUNCTION_QUAL_IN, + FUNCTION_QUAL_OUT, + FUNCTION_QUAL_INOUT +} GPUFunctionQual; + +typedef struct GPUFunction { + char name[MAX_FUNCTION_NAME]; + GPUType paramtype[MAX_PARAMETER]; + GPUFunctionQual paramqual[MAX_PARAMETER]; + int totparam; +} GPUFunction; + +/* Indices match the GPUType enum */ static const char *GPU_DATATYPE_STR[17] = {"", "float", "vec2", "vec3", "vec4", NULL, NULL, NULL, NULL, "mat3", NULL, NULL, NULL, NULL, NULL, NULL, "mat4"}; -#define LINK_IMAGE_BLENDER 1 -#define LINK_IMAGE_PREVIEW 2 - /* GLSL code parsing for finding function definitions. * These are stored in a hash for lookup when creating a material. */ @@ -126,7 +144,9 @@ static char *gpu_str_skip_token(char *str, char *token, int max) static void gpu_parse_functions_string(GHash *hash, char *code) { GPUFunction *function; - int i, type, qual; + GPUType type; + GPUFunctionQual qual; + int i; while ((code = strstr(code, "void "))) { function = MEM_callocN(sizeof(GPUFunction), "GPUFunction"); @@ -146,7 +166,7 @@ static void gpu_parse_functions_string(GHash *hash, char *code) code = gpu_str_skip_token(code, NULL, 0); /* test for type */ - type= 0; + type= GPU_NONE; for (i=1; i<=16; i++) { if (GPU_DATATYPE_STR[i] && gpu_str_prefix(code, GPU_DATATYPE_STR[i])) { type= i; @@ -160,7 +180,7 @@ static void gpu_parse_functions_string(GHash *hash, char *code) type= GPU_TEX2D; if (type) { - /* add paramater */ + /* add parameter */ code = gpu_str_skip_token(code, NULL, 0); code = gpu_str_skip_token(code, NULL, 0); function->paramqual[function->totparam]= qual; @@ -231,7 +251,7 @@ static char *gpu_generate_function_prototyps(GHash *hash) } #endif -GPUFunction *GPU_lookup_function(const char *name) +static GPUFunction *gpu_lookup_function(const char *name) { if (!FUNCTION_HASH) { FUNCTION_HASH = BLI_ghash_str_new("GPU_lookup_function gh"); @@ -320,7 +340,7 @@ static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *t } } -static void codegen_print_datatype(DynStr *ds, int type, float *data) +static void codegen_print_datatype(DynStr *ds, const GPUType type, float *data) { int i; @@ -363,10 +383,28 @@ const char *GPU_builtin_name(GPUBuiltin builtin) return "unfobcolor"; else if (builtin == GPU_AUTO_BUMPSCALE) return "unfobautobumpscale"; + else if (builtin == GPU_CAMERA_TEXCO_FACTORS) + return "unfcameratexfactors"; else return ""; } +/* assign only one texid per buffer to avoid sampling the same texture twice */ +static void codegen_set_texid(GHash *bindhash, GPUInput *input, int *texid, void *key) +{ + if (BLI_ghash_haskey(bindhash, key)) { + /* Reuse existing texid */ + input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, key)); + } + else { + /* Allocate new texid */ + input->texid = *texid; + (*texid)++; + input->bindtex = true; + BLI_ghash_insert(bindhash, key, SET_INT_IN_POINTER(input->texid)); + } +} + static void codegen_set_unique_ids(ListBase *nodes) { GHash *bindhash, *definehash; @@ -382,68 +420,43 @@ static void codegen_set_unique_ids(ListBase *nodes) for (input=node->inputs.first; input; input=input->next) { /* set id for unique names of uniform variables */ input->id = id++; - input->bindtex = 0; - input->definetex = 0; + input->bindtex = false; + input->definetex = false; /* set texid used for settings texture slot with multitexture */ if (codegen_input_has_texture(input) && ((input->source == GPU_SOURCE_TEX) || (input->source == GPU_SOURCE_TEX_PIXEL))) { + /* assign only one texid per buffer to avoid sampling + * the same texture twice */ if (input->link) { - /* input is texture from buffer, assign only one texid per - * buffer to avoid sampling the same texture twice */ - if (!BLI_ghash_haskey(bindhash, input->link)) { - input->texid = texid++; - input->bindtex = 1; - BLI_ghash_insert(bindhash, input->link, SET_INT_IN_POINTER(input->texid)); - } - else - input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, input->link)); + /* input is texture from buffer */ + codegen_set_texid(bindhash, input, &texid, input->link); } else if (input->ima) { - /* input is texture from image, assign only one texid per - * buffer to avoid sampling the same texture twice */ - if (!BLI_ghash_haskey(bindhash, input->ima)) { - input->texid = texid++; - input->bindtex = 1; - BLI_ghash_insert(bindhash, input->ima, SET_INT_IN_POINTER(input->texid)); - } - else - input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, input->ima)); + /* input is texture from image */ + codegen_set_texid(bindhash, input, &texid, input->ima); } else if (input->prv) { - /* input is texture from preview render, assign only one texid per - * buffer to avoid sampling the same texture twice */ - if (!BLI_ghash_haskey(bindhash, input->prv)) { - input->texid = texid++; - input->bindtex = 1; - BLI_ghash_insert(bindhash, input->prv, SET_INT_IN_POINTER(input->texid)); - } - else - input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, input->prv)); + /* input is texture from preview render */ + codegen_set_texid(bindhash, input, &texid, input->prv); } - else { - if (!BLI_ghash_haskey(bindhash, input->tex)) { - /* input is user created texture, check tex pointer */ - input->texid = texid++; - input->bindtex = 1; - BLI_ghash_insert(bindhash, input->tex, SET_INT_IN_POINTER(input->texid)); - } - else - input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, input->tex)); + else if (input->tex) { + /* input is user created texture, check tex pointer */ + codegen_set_texid(bindhash, input, &texid, input->tex); } /* make sure this pixel is defined exactly once */ if (input->source == GPU_SOURCE_TEX_PIXEL) { if (input->ima) { if (!BLI_ghash_haskey(definehash, input->ima)) { - input->definetex = 1; + input->definetex = true; BLI_ghash_insert(definehash, input->ima, SET_INT_IN_POINTER(input->texid)); } } else { if (!BLI_ghash_haskey(definehash, input->link)) { - input->definetex = 1; + input->definetex = true; BLI_ghash_insert(definehash, input->link, SET_INT_IN_POINTER(input->texid)); } } @@ -604,7 +617,7 @@ static void codegen_call_functions(DynStr *ds, ListBase *nodes, GPUOutput *final BLI_dynstr_append(ds, ";\n"); } -static char *code_generate_fragment(ListBase *nodes, GPUOutput *output, const char *UNUSED(name)) +static char *code_generate_fragment(ListBase *nodes, GPUOutput *output) { DynStr *ds = BLI_dynstr_new(); char *code; @@ -638,7 +651,7 @@ static char *code_generate_fragment(ListBase *nodes, GPUOutput *output, const ch return code; } -static char *code_generate_vertex(ListBase *nodes, int type) +static char *code_generate_vertex(ListBase *nodes, const GPUMatType type) { DynStr *ds = BLI_dynstr_new(); GPUNode *node; @@ -738,7 +751,7 @@ GPUShader *GPU_pass_shader(GPUPass *pass) return pass->shader; } -static void GPU_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) +static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) { GPUShader *shader = pass->shader; GPUNode *node; @@ -860,16 +873,16 @@ void GPU_pass_unbind(GPUPass *pass) /* Node Link Functions */ -static GPUNodeLink *GPU_node_link_create(int type) +static GPUNodeLink *GPU_node_link_create(void) { GPUNodeLink *link = MEM_callocN(sizeof(GPUNodeLink), "GPUNodeLink"); - link->type = type; + link->type = GPU_NONE; link->users++; return link; } -static void GPU_node_link_free(GPUNodeLink *link) +static void gpu_node_link_free(GPUNodeLink *link) { link->users--; @@ -894,12 +907,7 @@ static GPUNode *GPU_node_begin(const char *name) return node; } -static void GPU_node_end(GPUNode *UNUSED(node)) -{ - /* empty */ -} - -static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, int type) +static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType type) { GPUInput *input; GPUNode *outnode; @@ -909,7 +917,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, int type) outnode = link->output->node; name = outnode->name; - if (strcmp(name, "set_value")==0 || strcmp(name, "set_rgb")==0) { + if (STREQ(name, "set_value") || STREQ(name, "set_rgb")) { input = MEM_dupallocN(outnode->inputs.first); input->type = type; if (input->link) @@ -953,7 +961,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, int type) input->tex = link->dynamictex; input->textarget = GL_TEXTURE_2D; input->textype = type; - input->dynamictex = 1; + input->dynamictex = true; input->dynamicdata = link->ptr2; MEM_freeN(link); } @@ -975,7 +983,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, int type) input->type = GPU_VEC4; input->source = GPU_SOURCE_TEX; - if (link->image == LINK_IMAGE_PREVIEW) + if (link->image == GPU_NODE_LINK_IMAGE_PREVIEW) input->prv = link->ptr1; else { input->ima = link->ptr1; @@ -1020,13 +1028,13 @@ static void gpu_node_input_socket(GPUNode *node, GPUNodeStack *sock) gpu_node_input_link(node, sock->link, sock->type); } else { - link = GPU_node_link_create(0); + link = GPU_node_link_create(); link->ptr1 = sock->vec; gpu_node_input_link(node, link, sock->type); } } -static void GPU_node_output(GPUNode *node, int type, const char *UNUSED(name), GPUNodeLink **link) +static void gpu_node_output(GPUNode *node, const GPUType type, GPUNodeLink **link) { GPUOutput *output = MEM_callocN(sizeof(GPUOutput), "GPUOutput"); @@ -1034,7 +1042,8 @@ static void GPU_node_output(GPUNode *node, int type, const char *UNUSED(name), G output->node = node; if (link) { - *link = output->link = GPU_node_link_create(type); + *link = output->link = GPU_node_link_create(); + output->link->type = type; output->link->output = output; /* note: the caller owns the reference to the linkfer, GPUOutput @@ -1045,13 +1054,13 @@ static void GPU_node_output(GPUNode *node, int type, const char *UNUSED(name), G BLI_addtail(&node->outputs, output); } -static void GPU_inputs_free(ListBase *inputs) +static void gpu_inputs_free(ListBase *inputs) { GPUInput *input; for (input=inputs->first; input; input=input->next) { if (input->link) - GPU_node_link_free(input->link); + gpu_node_link_free(input->link); else if (input->tex && !input->dynamictex) GPU_texture_free(input->tex); } @@ -1059,28 +1068,28 @@ static void GPU_inputs_free(ListBase *inputs) BLI_freelistN(inputs); } -static void GPU_node_free(GPUNode *node) +static void gpu_node_free(GPUNode *node) { GPUOutput *output; - GPU_inputs_free(&node->inputs); + gpu_inputs_free(&node->inputs); for (output=node->outputs.first; output; output=output->next) if (output->link) { output->link->output = NULL; - GPU_node_link_free(output->link); + gpu_node_link_free(output->link); } BLI_freelistN(&node->outputs); MEM_freeN(node); } -static void GPU_nodes_free(ListBase *nodes) +static void gpu_nodes_free(ListBase *nodes) { GPUNode *node; while ((node = BLI_pophead(nodes))) { - GPU_node_free(node); + gpu_node_free(node); } } @@ -1102,7 +1111,7 @@ static void gpu_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *a if (input->source == GPU_SOURCE_ATTRIB) { for (a=0; a<attribs->totlayer; a++) { if (attribs->layer[a].type == input->attribtype && - strcmp(attribs->layer[a].name, input->attribname) == 0) + STREQ(attribs->layer[a].name, input->attribname)) { break; } @@ -1142,9 +1151,9 @@ static void gpu_nodes_get_builtin_flag(ListBase *nodes, int *builtin) /* varargs linking */ -GPUNodeLink *GPU_attribute(int type, const char *name) +GPUNodeLink *GPU_attribute(const CustomDataType type, const char *name) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); link->attribtype= type; link->attribname= name; @@ -1154,7 +1163,7 @@ GPUNodeLink *GPU_attribute(int type, const char *name) GPUNodeLink *GPU_uniform(float *num) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); link->ptr1= num; link->ptr2= NULL; @@ -1162,13 +1171,13 @@ GPUNodeLink *GPU_uniform(float *num) return link; } -GPUNodeLink *GPU_dynamic_uniform(float *num, int dynamictype, void *data) +GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *data) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); link->ptr1= num; link->ptr2= data; - link->dynamic= 1; + link->dynamic= true; link->dynamictype = dynamictype; @@ -1177,9 +1186,9 @@ GPUNodeLink *GPU_dynamic_uniform(float *num, int dynamictype, void *data) GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); - link->image = LINK_IMAGE_BLENDER; + link->image = GPU_NODE_LINK_IMAGE_BLENDER; link->ptr1 = ima; link->ptr2 = iuser; link->image_isdata = is_data; @@ -1189,9 +1198,9 @@ GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data) GPUNodeLink *GPU_image_preview(PreviewImage *prv) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); - link->image= LINK_IMAGE_PREVIEW; + link->image= GPU_NODE_LINK_IMAGE_PREVIEW; link->ptr1= prv; return link; @@ -1200,20 +1209,20 @@ GPUNodeLink *GPU_image_preview(PreviewImage *prv) GPUNodeLink *GPU_texture(int size, float *pixels) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); - link->texture = 1; + link->texture = true; link->texturesize = size; link->ptr1= pixels; return link; } -GPUNodeLink *GPU_dynamic_texture(GPUTexture *tex, int dynamictype, void *data) +GPUNodeLink *GPU_dynamic_texture(GPUTexture *tex, GPUDynamicType dynamictype, void *data) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); - link->dynamic = 1; + link->dynamic = true; link->dynamictex = tex; link->dynamictype = dynamictype; link->ptr2 = data; @@ -1223,7 +1232,7 @@ GPUNodeLink *GPU_dynamic_texture(GPUTexture *tex, int dynamictype, void *data) GPUNodeLink *GPU_builtin(GPUBuiltin builtin) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); link->builtin= builtin; @@ -1232,7 +1241,7 @@ GPUNodeLink *GPU_builtin(GPUBuiltin builtin) GPUNodeLink *GPU_opengl_builtin(GPUOpenGLBuiltin builtin) { - GPUNodeLink *link = GPU_node_link_create(0); + GPUNodeLink *link = GPU_node_link_create(); link->oglbuiltin = builtin; @@ -1247,7 +1256,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) va_list params; int i; - function = GPU_lookup_function(name); + function = gpu_lookup_function(name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); return 0; @@ -1259,7 +1268,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) for (i=0; i<function->totparam; i++) { if (function->paramqual[i] != FUNCTION_QUAL_IN) { linkptr= va_arg(params, GPUNodeLink**); - GPU_node_output(node, function->paramtype[i], "", linkptr); + gpu_node_output(node, function->paramtype[i], linkptr); } else { link= va_arg(params, GPUNodeLink*); @@ -1268,8 +1277,6 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) } va_end(params); - GPU_node_end(node); - gpu_material_add_node(mat, node); return 1; @@ -1283,7 +1290,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod va_list params; int i, totin, totout; - function = GPU_lookup_function(name); + function = gpu_lookup_function(name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); return 0; @@ -1302,7 +1309,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod if (out) { for (i = 0; out[i].type != GPU_NONE; i++) { - GPU_node_output(node, out[i].type, out[i].name, &out[i].link); + gpu_node_output(node, out[i].type, &out[i].link); totout++; } } @@ -1312,7 +1319,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod if (function->paramqual[i] != FUNCTION_QUAL_IN) { if (totout == 0) { linkptr= va_arg(params, GPUNodeLink**); - GPU_node_output(node, function->paramtype[i], "", linkptr); + gpu_node_output(node, function->paramtype[i], linkptr); } else totout--; @@ -1331,8 +1338,6 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod } va_end(params); - GPU_node_end(node); - gpu_material_add_node(mat, node); return 1; @@ -1348,7 +1353,7 @@ int GPU_link_changed(GPUNodeLink *link) node = link->output->node; name = node->name; - if (strcmp(name, "set_value")==0 || strcmp(name, "set_rgb")==0) { + if (STREQ(name, "set_value") || STREQ(name, "set_rgb")) { input = node->inputs.first; return (input->link != NULL); } @@ -1373,7 +1378,7 @@ static void gpu_nodes_tag(GPUNodeLink *link) if (node->tag) return; - node->tag= 1; + node->tag = true; for (input=node->inputs.first; input; input=input->next) if (input->link) gpu_nodes_tag(input->link); @@ -1384,7 +1389,7 @@ static void gpu_nodes_prune(ListBase *nodes, GPUNodeLink *outlink) GPUNode *node, *next; for (node=nodes->first; node; node=node->next) - node->tag= 0; + node->tag = false; gpu_nodes_tag(outlink); @@ -1393,12 +1398,14 @@ static void gpu_nodes_prune(ListBase *nodes, GPUNodeLink *outlink) if (!node->tag) { BLI_remlink(nodes, node); - GPU_node_free(node); + gpu_node_free(node); } } } -GPUPass *GPU_generate_pass(ListBase *nodes, GPUNodeLink *outlink, GPUVertexAttribs *attribs, int *builtins, int type, const char *name) +GPUPass *GPU_generate_pass(ListBase *nodes, GPUNodeLink *outlink, + GPUVertexAttribs *attribs, int *builtins, + const GPUMatType type, const char *UNUSED(name)) { GPUShader *shader; GPUPass *pass; @@ -1416,7 +1423,7 @@ GPUPass *GPU_generate_pass(ListBase *nodes, GPUNodeLink *outlink, GPUVertexAttri gpu_nodes_get_builtin_flag(nodes, builtins); /* generate code and compile with opengl */ - fragmentcode = code_generate_fragment(nodes, outlink->output, name); + fragmentcode = code_generate_fragment(nodes, outlink->output); vertexcode = code_generate_vertex(nodes, type); shader = GPU_shader_create(vertexcode, fragmentcode, glsl_material_library, NULL); @@ -1428,7 +1435,7 @@ GPUPass *GPU_generate_pass(ListBase *nodes, GPUNodeLink *outlink, GPUVertexAttri MEM_freeN(vertexcode); memset(attribs, 0, sizeof(*attribs)); memset(builtins, 0, sizeof(*builtins)); - GPU_nodes_free(nodes); + gpu_nodes_free(nodes); return NULL; } @@ -1442,8 +1449,8 @@ GPUPass *GPU_generate_pass(ListBase *nodes, GPUNodeLink *outlink, GPUVertexAttri pass->libcode = glsl_material_library; /* extract dynamic inputs and throw away nodes */ - GPU_nodes_extract_dynamic_inputs(pass, nodes); - GPU_nodes_free(nodes); + gpu_nodes_extract_dynamic_inputs(pass, nodes); + gpu_nodes_free(nodes); return pass; } @@ -1451,7 +1458,7 @@ GPUPass *GPU_generate_pass(ListBase *nodes, GPUNodeLink *outlink, GPUVertexAttri void GPU_pass_free(GPUPass *pass) { GPU_shader_free(pass->shader); - GPU_inputs_free(&pass->inputs); + gpu_inputs_free(&pass->inputs); if (pass->fragmentcode) MEM_freeN(pass->fragmentcode); if (pass->vertexcode) diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index a0698235db6..a6da5e018fd 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -33,6 +33,7 @@ #ifndef __GPU_CODEGEN_H__ #define __GPU_CODEGEN_H__ +#include "DNA_customdata_types.h" #include "DNA_listBase.h" #include "GPU_material.h" #include "GPU_glew.h" @@ -45,22 +46,6 @@ struct GPUVertexAttribs; struct GPUFrameBuffer; struct PreviewImage; -#define MAX_FUNCTION_NAME 64 -#define MAX_PARAMETER 32 - -#define FUNCTION_QUAL_IN 0 -#define FUNCTION_QUAL_OUT 1 -#define FUNCTION_QUAL_INOUT 2 - -typedef struct GPUFunction { - char name[MAX_FUNCTION_NAME]; - int paramtype[MAX_PARAMETER]; - int paramqual[MAX_PARAMETER]; - int totparam; -} GPUFunction; - -GPUFunction *GPU_lookup_function(const char *name); - /* Pass Generation * - Takes a list of nodes and a desired output, and makes a pass. This * will take ownership of the nodes and free them early if unused or @@ -76,11 +61,19 @@ typedef enum GPUDataSource { GPU_SOURCE_ATTRIB } GPUDataSource; +typedef enum { + GPU_NODE_LINK_IMAGE_NONE = 0, + GPU_NODE_LINK_IMAGE_BLENDER = 1, + GPU_NODE_LINK_IMAGE_PREVIEW = 2 +} GPUNodeLinkImage; + struct GPUNode { struct GPUNode *next, *prev; const char *name; - int tag; + + /* Internal flag to mark nodes during pruning */ + bool tag; ListBase inputs; ListBase outputs; @@ -89,21 +82,23 @@ struct GPUNode { struct GPUNodeLink { GPUNodeStack *socket; - int attribtype; + CustomDataType attribtype; const char *attribname; - int image; - int image_isdata; + GPUNodeLinkImage image; + bool image_isdata; - int texture; + bool texture; int texturesize; void *ptr1, *ptr2; - int dynamic; - int dynamictype; + bool dynamic; + GPUDynamicType dynamictype; + + GPUType type; - int type; + /* Refcount */ int users; struct GPUTexture *dynamictex; @@ -118,7 +113,7 @@ typedef struct GPUOutput { struct GPUOutput *next, *prev; GPUNode *node; - int type; /* data type = length of vector/matrix */ + GPUType type; /* data type = length of vector/matrix */ GPUNodeLink *link; /* output link */ int id; /* unique id as created by code generator */ } GPUOutput; @@ -128,23 +123,23 @@ typedef struct GPUInput { GPUNode *node; - int type; /* datatype */ - int source; /* data source */ + GPUType type; /* datatype */ + GPUDataSource source; /* data source */ int id; /* unique id as created by code generator */ - int texid; /* number for multitexture */ + int texid; /* number for multitexture, starting from zero */ int attribid; /* id for vertex attributes */ - int bindtex; /* input is responsible for binding the texture? */ - int definetex; /* input is responsible for defining the pixel? */ - int textarget; /* GL_TEXTURE_* */ - int textype; /* datatype */ + bool bindtex; /* input is responsible for binding the texture? */ + bool definetex; /* input is responsible for defining the pixel? */ + int textarget; /* GL texture target, e.g. GL_TEXTURE_2D */ + GPUType textype; /* datatype */ struct Image *ima; /* image */ struct ImageUser *iuser;/* image user */ struct PreviewImage *prv; /* preview images & icons */ - int image_isdata; /* image does not contain color data */ + bool image_isdata; /* image does not contain color data */ float *dynamicvec; /* vector data in case it is dynamic */ - int dynamictype; /* origin of the dynamic uniform (GPUDynamicType) */ + GPUDynamicType dynamictype; /* origin of the dynamic uniform */ void *dynamicdata; /* data source of the dynamic uniform */ struct GPUTexture *tex; /* input texture, only set at runtime */ int shaderloc; /* id from opengl */ @@ -152,9 +147,9 @@ typedef struct GPUInput { float vec[16]; /* vector data */ GPUNodeLink *link; - int dynamictex; /* dynamic? */ - int attribtype; /* attribute type */ - char attribname[32]; /* attribute name */ + bool dynamictex; /* dynamic? */ + CustomDataType attribtype; /* attribute type */ + char attribname[MAX_CUSTOMDATA_LAYER_NAME]; /* attribute name */ int attribfirst; /* this is the first one that is bound */ GPUBuiltin builtin; /* builtin uniform */ GPUOpenGLBuiltin oglbuiltin; /* opengl built in varying */ @@ -175,7 +170,8 @@ struct GPUPass { typedef struct GPUPass GPUPass; GPUPass *GPU_generate_pass(ListBase *nodes, struct GPUNodeLink *outlink, - struct GPUVertexAttribs *attribs, int *builtin, int type, const char *name); + struct GPUVertexAttribs *attribs, int *builtin, + const GPUMatType type, const char *name); struct GPUShader *GPU_pass_shader(GPUPass *pass); diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 5f58e3fe65b..4a93fbac48e 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -1324,7 +1324,7 @@ void GPU_free_images_old(void) { Image *ima; static int lasttime = 0; - int ctime = (int)PIL_check_seconds_timer(); + int ctime = PIL_check_seconds_timer_i(); /* * Run garbage collector once for every collecting period of time @@ -1384,6 +1384,7 @@ static struct GPUMaterialState { bool gscenelock; float (*gviewmat)[4]; float (*gviewinv)[4]; + float (*gviewcamtexcofac); bool backface_culling; @@ -1492,6 +1493,7 @@ void GPU_begin_object_materials(View3D *v3d, RegionView3D *rv3d, Scene *scene, O GMS.gscenelock = (v3d->scenelock != 0); GMS.gviewmat= rv3d->viewmat; GMS.gviewinv= rv3d->viewinv; + GMS.gviewcamtexcofac = rv3d->viewcamtexcofac; /* alpha pass setup. there's various cases to handle here: * - object transparency on: only solid materials draw in the first pass, @@ -1650,7 +1652,7 @@ int GPU_enable_material(int nr, void *attribs) gpumat = GPU_material_from_blender(GMS.gscene, mat); GPU_material_vertex_attributes(gpumat, gattribs); - GPU_material_bind(gpumat, GMS.gob->lay, GMS.glay, 1.0, !(GMS.gob->mode & OB_MODE_TEXTURE_PAINT), GMS.gviewmat, GMS.gviewinv, GMS.gscenelock); + GPU_material_bind(gpumat, GMS.gob->lay, GMS.glay, 1.0, !(GMS.gob->mode & OB_MODE_TEXTURE_PAINT), GMS.gviewmat, GMS.gviewinv, GMS.gviewcamtexcofac, GMS.gscenelock); auto_bump_scale = GMS.gob->derivedFinal != NULL ? GMS.gob->derivedFinal->auto_bump_scale : 1.0f; GPU_material_bind_uniforms(gpumat, GMS.gob->obmat, GMS.gob->col, auto_bump_scale); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 75a9572f54f..41049e8430a 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -101,6 +101,7 @@ struct GPUMaterial { int viewmatloc, invviewmatloc; int obmatloc, invobmatloc; int obcolloc, obautobumpscaleloc; + int cameratexcofacloc; ListBase lamps; }; @@ -228,6 +229,8 @@ static int GPU_material_construct_end(GPUMaterial *material, const char *passnam material->obcolloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBCOLOR)); if (material->builtins & GPU_AUTO_BUMPSCALE) material->obautobumpscaleloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_AUTO_BUMPSCALE)); + if (material->builtins & GPU_CAMERA_TEXCO_FACTORS) + material->cameratexcofacloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_CAMERA_TEXCO_FACTORS)); return 1; } @@ -277,7 +280,7 @@ bool GPU_lamp_override_visible(GPULamp *lamp, SceneRenderLayer *srl, Material *m return true; } -void GPU_material_bind(GPUMaterial *material, int oblay, int viewlay, double time, int mipmap, float viewmat[4][4], float viewinv[4][4], bool scenelock) +void GPU_material_bind(GPUMaterial *material, int oblay, int viewlay, double time, int mipmap, float viewmat[4][4], float viewinv[4][4], float camerafactors[4], bool scenelock) { if (material->pass) { LinkData *nlink; @@ -337,6 +340,16 @@ void GPU_material_bind(GPUMaterial *material, int oblay, int viewlay, double tim if (material->builtins & GPU_INVERSE_VIEW_MATRIX) { GPU_shader_uniform_vector(shader, material->invviewmatloc, 16, 1, (float*)viewinv); } + if (material->builtins & GPU_CAMERA_TEXCO_FACTORS) { + if (camerafactors) { + GPU_shader_uniform_vector(shader, material->cameratexcofacloc, 4, 1, (float*)camerafactors); + } + else { + /* use default, no scaling no offset */ + float borders[4] = {1.0f, 1.0f, 0.0f, 0.0f}; + GPU_shader_uniform_vector(shader, material->cameratexcofacloc, 4, 1, (float*)borders); + } + } GPU_pass_update_uniforms(material->pass); @@ -1755,10 +1768,10 @@ static void gpu_lamp_calc_winmat(GPULamp *lamp) orthographic_m4(lamp->winmat, -wsize, wsize, -wsize, wsize, lamp->d, lamp->clipend); } else { - angle= saacos(lamp->spotsi); - temp= 0.5f*lamp->size*cosf(angle)/sinf(angle); - pixsize= (lamp->d)/temp; - wsize= pixsize*0.5f*lamp->size; + angle = saacos(lamp->spotsi); + temp = 0.5f * lamp->size * cosf(angle) / sinf(angle); + pixsize = lamp->d / temp; + wsize = pixsize * 0.5f * lamp->size; perspective_m4(lamp->winmat, -wsize, wsize, -wsize, wsize, lamp->d, lamp->clipend); } } @@ -2096,13 +2109,14 @@ int GPU_lamp_shadow_layer(GPULamp *lamp) return -1; } -GPUNodeLink *GPU_lamp_get_data(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **col, GPUNodeLink **lv, GPUNodeLink **dist, GPUNodeLink **shadow) +GPUNodeLink *GPU_lamp_get_data(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **col, GPUNodeLink **lv, GPUNodeLink **dist, GPUNodeLink **shadow, GPUNodeLink **energy) { GPUNodeLink *visifac; *col = GPU_dynamic_uniform(lamp->dyncol, GPU_DYNAMIC_LAMP_DYNCOL, lamp->ob); + *energy = GPU_dynamic_uniform(&lamp->dynenergy, GPU_DYNAMIC_LAMP_DYNENERGY, lamp->ob); visifac = lamp_get_visibility(mat, lamp, lv, dist); - /* looks like it's not used? psy-fi */ + shade_light_textures(mat, lamp, col); if (GPU_lamp_has_shadow_buffer(lamp)) { @@ -2208,30 +2222,46 @@ GPUShaderExport *GPU_shader_export(struct Scene *scene, struct Material *ma) glBindTexture(GL_TEXTURE_2D, lastbindcode); } break; + + case GPU_NONE: + case GPU_FLOAT: + case GPU_VEC2: + case GPU_VEC3: + case GPU_VEC4: + case GPU_MAT3: + case GPU_MAT4: + case GPU_ATTRIB: + break; } } else { uniform->type = input->dynamictype; BLI_strncpy(uniform->varname, input->shadername, sizeof(uniform->varname)); switch (input->type) { - case 1: + case GPU_FLOAT: uniform->datatype = GPU_DATA_1F; break; - case 2: + case GPU_VEC2: uniform->datatype = GPU_DATA_2F; break; - case 3: + case GPU_VEC3: uniform->datatype = GPU_DATA_3F; break; - case 4: + case GPU_VEC4: uniform->datatype = GPU_DATA_4F; break; - case 9: + case GPU_MAT3: uniform->datatype = GPU_DATA_9F; break; - case 16: + case GPU_MAT4: uniform->datatype = GPU_DATA_16F; break; + + case GPU_NONE: + case GPU_TEX2D: + case GPU_SHADOW2D: + case GPU_ATTRIB: + break; } if (uniform->type >= GPU_DYNAMIC_LAMP_FIRST && uniform->type <= GPU_DYNAMIC_LAMP_LAST) diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index d8e1fab9f80..35a39b2d7a1 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -82,7 +82,7 @@ void GPU_select_begin(unsigned int *buffer, unsigned int bufsize, rctf *input, c g_query_state.oldhits = oldhits; if (!g_query_state.use_gpu_select) { - glSelectBuffer( bufsize, (GLuint *)buffer); + glSelectBuffer(bufsize, (GLuint *)buffer); glRenderMode(GL_SELECT); glInitNames(); glPushName(-1); @@ -92,8 +92,8 @@ void GPU_select_begin(unsigned int *buffer, unsigned int bufsize, rctf *input, c g_query_state.num_of_queries = ALLOC_QUERIES; - g_query_state.queries = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.queries) , "gpu selection queries"); - g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id) , "gpu selection ids"); + g_query_state.queries = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.queries), "gpu selection queries"); + g_query_state.id = MEM_mallocN(g_query_state.num_of_queries * sizeof(*g_query_state.id), "gpu selection ids"); glGenQueriesARB(g_query_state.num_of_queries, g_query_state.queries); glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_VIEWPORT_BIT); @@ -135,7 +135,7 @@ void GPU_select_begin(unsigned int *buffer, unsigned int bufsize, rctf *input, c bool GPU_select_load_id(unsigned int id) { /* if no selection mode active, ignore */ - if(!g_query_state.select_is_active) + if (!g_query_state.select_is_active) return true; if (!g_query_state.use_gpu_select) { @@ -158,7 +158,7 @@ bool GPU_select_load_id(unsigned int id) g_query_state.active_query++; g_query_state.query_issued = true; - if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS) { + if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS && g_query_state.index < g_query_state.oldhits) { if (g_query_state.buffer[g_query_state.index * 4 + 3] == id) { g_query_state.index++; return true; @@ -191,7 +191,7 @@ unsigned int GPU_select_end(void) glGetQueryObjectuivARB(g_query_state.queries[i], GL_QUERY_RESULT_ARB, &result); if (result > 0) { if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) { - if(hits < g_query_state.bufsize) { + if (hits < g_query_state.bufsize) { g_query_state.buffer[hits * 4] = 1; g_query_state.buffer[hits * 4 + 1] = 0xFFFF; g_query_state.buffer[hits * 4 + 2] = 0xFFFF; diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 1e0927d90b3..a1a4a14e857 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -169,9 +169,9 @@ void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist) outview = normalize(co); } -void lamp(vec4 col, 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; + outcol = col * energy; outlv = lv; outdist = dist; outshadow = vec4(shadow, 1.0); @@ -297,6 +297,10 @@ void math_modulo(float val1, float val2, out float outval) outval = 0.0; else outval = mod(val1, val2); + + /* change sign to match C convention, mod in GLSL will take absolute for negative numbers, + * see https://www.opengl.org/sdk/docs/man/html/mod.xhtml */ + outval = (val1 > 0.0) ? outval : -outval; } void math_abs(float val1, out float outval) @@ -338,6 +342,7 @@ void vec_math_cross(vec3 v1, vec3 v2, out vec3 outvec, out float outval) { outvec = cross(v1, v2); outval = length(outvec); + outvec /= outval; } void vec_math_normalize(vec3 v, out vec3 outvec, out float outval) @@ -2367,7 +2372,7 @@ void node_geometry(vec3 I, vec3 N, mat4 toworld, backfacing = (gl_FrontFacing)? 0.0: 1.0; } -void node_tex_coord(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, +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) @@ -2378,7 +2383,7 @@ void node_tex_coord(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, object = (obinvmat*(viewinvmat*vec4(I, 1.0))).xyz; camera = vec3(I.xy, -I.z); vec4 projvec = gl_ProjectionMatrix * vec4(I, 1.0); - window = vec3(mtex_2d_mapping(projvec.xyz/projvec.w).xy, 0.0); + window = vec3(mtex_2d_mapping(projvec.xyz/projvec.w).xy * camerafac.xy + camerafac.zw, 0.0); vec3 shade_I; shade_view(I, shade_I); @@ -2386,7 +2391,7 @@ void node_tex_coord(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, reflection = (viewinvmat*vec4(view_reflection, 0.0)).xyz; } -void node_tex_coord_background(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, +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) @@ -2405,7 +2410,9 @@ void node_tex_coord_background(vec3 I, vec3 N, mat4 viewinvmat, mat4 obinvmat, object = coords; camera = vec3(co.xy, -co.z); - window = (gl_ProjectionMatrix[3][3] == 0.0) ? vec3(mtex_2d_mapping(I).xy, 0.0) : vec3(0.5, 0.5, 0.0); + window = (gl_ProjectionMatrix[3][3] == 0.0) ? + vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0) : + vec3(vec2(0.5) * camerafac.xy + camerafac.zw, 0.0); reflection = -coords; } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index ce6a7eb1c47..17bb873cd98 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -86,6 +86,7 @@ struct anim; struct ColorManagedDisplay; +struct GSet; /** * * \attention Defined in allocimbuf.c @@ -236,11 +237,14 @@ void IMB_anim_set_index_dir(struct anim *anim, const char *dir); int IMB_anim_index_get_frame_index(struct anim *anim, IMB_Timecode_Type tc, int position); +IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim); + struct IndexBuildContext; /* prepare context for proxies/imecodes builder */ struct IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, - IMB_Proxy_Size proxy_sizes_in_use, int quality); + IMB_Proxy_Size proxy_sizes_in_use, int quality, + const bool overwite, struct GSet *file_list); /* will rebuild all used indices and proxies at once */ void IMB_anim_index_rebuild(struct IndexBuildContext *context, diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 44cb7f1211e..867c4a826fe 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -47,8 +47,6 @@ * contains an Amiga-format file). */ -struct ImMetaData; - #define IB_MIPMAP_LEVELS 20 #define IB_FILENAME_SIZE 1024 @@ -111,7 +109,7 @@ typedef struct ImBuf { /* externally used data */ int index; /* reference index for ImBuf lists */ int userflags; /* used to set imbuf to dirty and other stuff */ - struct ImMetaData *metadata; /* image metadata */ + struct IDProperty *metadata; /* image metadata */ void *userdata; /* temporary storage, only used by baking at the moment */ /* file information */ diff --git a/source/blender/imbuf/intern/IMB_metadata.h b/source/blender/imbuf/intern/IMB_metadata.h index a717764b44f..5d4a0028ee1 100644 --- a/source/blender/imbuf/intern/IMB_metadata.h +++ b/source/blender/imbuf/intern/IMB_metadata.h @@ -35,13 +35,6 @@ struct ImBuf; -typedef struct ImMetaData { - struct ImMetaData *next, *prev; - char *key; - char *value; - int len; -} ImMetaData; - /** The metadata is a list of key/value pairs (both char *) that can me * saved in the header of several image formats. * Apart from some common keys like diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index b8f6e66adfe..ffdecb793aa 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -1314,25 +1314,27 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, int position, filter_y = (anim->ib_flags & IB_animdeinterlace); - if (anim->curtype == 0) { - ibuf = anim_getnew(anim); - if (ibuf == NULL) { - return(NULL); + if (preview_size == IMB_PROXY_NONE) { + if (anim->curtype == 0) { + ibuf = anim_getnew(anim); + if (ibuf == NULL) { + return(NULL); + } + + IMB_freeImBuf(ibuf); /* ???? */ + ibuf = NULL; } - IMB_freeImBuf(ibuf); /* ???? */ - ibuf = NULL; + if (position < 0) return(NULL); + if (position >= anim->duration) return(NULL); } - - if (position < 0) return(NULL); - if (position >= anim->duration) return(NULL); - - if (preview_size != IMB_PROXY_NONE) { + else { struct anim *proxy = IMB_anim_open_proxy(anim, preview_size); if (proxy) { position = IMB_anim_index_get_frame_index( anim, tc, position); + return IMB_anim_absolute( proxy, position, IMB_TC_NONE, IMB_PROXY_NONE); diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c index 8853fe449ba..f8cf1164e4f 100644 --- a/source/blender/imbuf/intern/bmp.c +++ b/source/blender/imbuf/intern/bmp.c @@ -72,22 +72,24 @@ typedef struct BMPHEADER { #define BMP_FILEHEADER_SIZE 14 +#define CHECK_HEADER_FIELD(_mem, _field) ((_mem[0] == _field[0]) && (_mem[1] == _field[1])) +#define CHECK_HEADER_FIELD_BMP(_mem) \ + (CHECK_HEADER_FIELD(_mem, "BM") || \ + CHECK_HEADER_FIELD(_mem, "BA") || \ + CHECK_HEADER_FIELD(_mem, "CI") || \ + CHECK_HEADER_FIELD(_mem, "CP") || \ + CHECK_HEADER_FIELD(_mem, "IC") || \ + CHECK_HEADER_FIELD(_mem, "PT")) + static int checkbmp(unsigned char *mem) { -#define CHECK_HEADER_FIELD(mem, field) ((mem[0] == field[0]) && (mem[1] == field[1])) int ret_val = 0; BMPINFOHEADER bmi; unsigned int u; if (mem) { - if (CHECK_HEADER_FIELD(mem, "BM") || - CHECK_HEADER_FIELD(mem, "BA") || - CHECK_HEADER_FIELD(mem, "CI") || - CHECK_HEADER_FIELD(mem, "CP") || - CHECK_HEADER_FIELD(mem, "IC") || - CHECK_HEADER_FIELD(mem, "PT")) - { + if (CHECK_HEADER_FIELD_BMP(mem)) { /* skip fileheader */ mem += BMP_FILEHEADER_SIZE; } @@ -111,8 +113,6 @@ static int checkbmp(unsigned char *mem) } return(ret_val); - -#undef CHECK_HEADER_FIELD } int imb_is_a_bmp(unsigned char *buf) @@ -124,10 +124,11 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char co { struct ImBuf *ibuf = NULL; BMPINFOHEADER bmi; - int x, y, depth, ibuf_depth, skip, i; + int x, y, depth, ibuf_depth, skip, i, j; unsigned char *bmp, *rect; unsigned short col; double xppm, yppm; + bool top_to_bottom = false; (void)size; /* unused */ @@ -135,7 +136,7 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char co colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); - if ((mem[0] == 'B') && (mem[1] == 'M')) { + if (CHECK_HEADER_FIELD_BMP(mem)) { /* skip fileheader */ mem += BMP_FILEHEADER_SIZE; } @@ -157,11 +158,14 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char co ibuf_depth = depth; } + if (y < 0) { + /* Negative height means bitmap is stored top-to-bottom... */ + y = -y; + top_to_bottom = true; + } + #if 0 - printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, - depth, bmi.biBitCount); - printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, - depth, bmi.biBitCount); + printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, depth, bmi.biBitCount); #endif if (flags & IB_test) { @@ -177,7 +181,9 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char co const char (*palette)[4] = (void *)bmp; bmp += bmi.biClrUsed * 4; for (i = y; i > 0; i--) { - int j; + if (top_to_bottom) { + rect = (unsigned char *) &ibuf->rect[(i - 1) * x]; + } for (j = x; j > 0; j--) { const char *pcol = palette[bmp[0]]; rect[0] = pcol[0]; @@ -192,21 +198,27 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char co } } else if (depth == 16) { - for (i = x * y; i > 0; i--) { - col = bmp[0] + (bmp[1] << 8); - rect[0] = ((col >> 10) & 0x1f) << 3; - rect[1] = ((col >> 5) & 0x1f) << 3; - rect[2] = ((col >> 0) & 0x1f) << 3; - - rect[3] = 255; - rect += 4; bmp += 2; - } + for (i = y; i > 0; i--) { + if (top_to_bottom) { + rect = (unsigned char *) &ibuf->rect[(i - 1) * x]; + } + for (j = x; j > 0; j--) { + col = bmp[0] + (bmp[1] << 8); + rect[0] = ((col >> 10) & 0x1f) << 3; + rect[1] = ((col >> 5) & 0x1f) << 3; + rect[2] = ((col >> 0) & 0x1f) << 3; + rect[3] = 255; + rect += 4; bmp += 2; + } + } } else if (depth == 24) { const int x_pad = x % 4; for (i = y; i > 0; i--) { - int j; + if (top_to_bottom) { + rect = (unsigned char *) &ibuf->rect[(i - 1) * x]; + } for (j = x; j > 0; j--) { rect[0] = bmp[2]; rect[1] = bmp[1]; @@ -220,12 +232,17 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char co } } else if (depth == 32) { - for (i = x * y; i > 0; i--) { - rect[0] = bmp[2]; - rect[1] = bmp[1]; - rect[2] = bmp[0]; - rect[3] = bmp[3]; - rect += 4; bmp += 4; + for (i = y; i > 0; i--) { + if (top_to_bottom) { + rect = (unsigned char *) &ibuf->rect[(i - 1) * x]; + } + for (j = x; j > 0; j--) { + rect[0] = bmp[2]; + rect[1] = bmp[1]; + rect[2] = bmp[0]; + rect[3] = bmp[3]; + rect += 4; bmp += 4; + } } } } @@ -239,6 +256,9 @@ struct ImBuf *imb_bmp_decode(unsigned char *mem, size_t size, int flags, char co return(ibuf); } +#undef CHECK_HEADER_FIELD_BMP +#undef CHECK_HEADER_FIELD + /* Couple of helper functions for writing our data */ static int putIntLSB(unsigned int ui, FILE *ofile) { @@ -298,7 +318,9 @@ int imb_savebmp(struct ImBuf *ibuf, const char *name, int flags) if (putc(data[ptr], ofile) == EOF) return 0; } /* add padding here */ - for (t = 0; t < extrabytes; t++) if (putc(0, ofile) == EOF) return 0; + for (t = 0; t < extrabytes; t++) { + if (putc(0, ofile) == EOF) return 0; + } } if (ofile) { fflush(ofile); diff --git a/source/blender/imbuf/intern/cineon/dpxlib.c b/source/blender/imbuf/intern/cineon/dpxlib.c index f7362d76913..23e5517bb09 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.c +++ b/source/blender/imbuf/intern/cineon/dpxlib.c @@ -183,8 +183,10 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf if (verbose) printf("DPX: File is LSB.\n"); } else { - if (verbose) printf("DPX: Bad magic number %lu in \"%s\".\n", - (uintptr_t)header.fileHeader.magic_num, byteStuff); + if (verbose) { + printf("DPX: Bad magic number %lu in \"%s\".\n", + (uintptr_t)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 049d8bdcff1..95e166b2f8b 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -1141,7 +1141,7 @@ void IMB_colormanagement_validate_settings(ColorManagedDisplaySettings *display_ for (view_link = display->views.first; view_link; view_link = view_link->next) { ColorManagedView *view = view_link->data; - if (!strcmp(view->name, view_settings->view_transform)) + if (STREQ(view->name, view_settings->view_transform)) break; } @@ -1496,7 +1496,7 @@ static bool is_ibuf_rect_in_display_space(ImBuf *ibuf, const ColorManagedViewSet const char *from_colorspace = ibuf->rect_colorspace->name; const char *to_colorspace = IMB_colormanagement_get_display_colorspace_name(view_settings, display_settings); - if (to_colorspace && !strcmp(from_colorspace, to_colorspace)) + if (to_colorspace && STREQ(from_colorspace, to_colorspace)) return true; } @@ -1625,7 +1625,7 @@ static void colormanagement_transform_ex(float *buffer, int width, int height, i return; } - if (!strcmp(from_colorspace, to_colorspace)) { + if (STREQ(from_colorspace, to_colorspace)) { /* if source and destination color spaces are identical, skip * threading overhead and simply do nothing */ @@ -1666,7 +1666,7 @@ void IMB_colormanagement_transform_v4(float pixel[4], const char *from_colorspac return; } - if (!strcmp(from_colorspace, to_colorspace)) { + if (STREQ(from_colorspace, to_colorspace)) { /* if source and destination color spaces are identical, skip * threading overhead and simply do nothing */ @@ -1918,7 +1918,7 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, boo * should be pretty safe since this image buffer is supposed to be used for * saving only and ftype would be overwritten a bit later by BKE_imbuf_write */ - colormanaged_ibuf->ftype = BKE_imtype_to_ftype(image_format_data->imtype); + colormanaged_ibuf->ftype = BKE_image_imtype_to_ftype(image_format_data->imtype); /* if file format isn't able to handle float buffer itself, * we need to allocate byte buffer and store color managed @@ -2152,7 +2152,7 @@ ColorManagedDisplay *colormanage_display_get_named(const char *name) ColorManagedDisplay *display; for (display = global_displays.first; display; display = display->next) { - if (!strcmp(display->name, name)) + if (STREQ(display->name, name)) return display; } @@ -2257,7 +2257,7 @@ ColorManagedView *colormanage_view_get_named(const char *name) ColorManagedView *view; for (view = global_views.first; view; view = view->next) { - if (!strcmp(view->name, name)) + if (STREQ(view->name, name)) return view; } @@ -2373,7 +2373,7 @@ ColorSpace *colormanage_colorspace_get_named(const char *name) ColorSpace *colorspace; for (colorspace = global_colorspaces.first; colorspace; colorspace = colorspace->next) { - if (!strcmp(colorspace->name, name)) + if (STREQ(colorspace->name, name)) return colorspace; } @@ -2459,7 +2459,7 @@ ColorManagedLook *colormanage_look_get_named(const char *name) ColorManagedLook *look; for (look = global_looks.first; look; look = look->next) { - if (!strcmp(look->name, name)) { + if (STREQ(look->name, name)) { return look; } } diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 81aef4ac6e4..59d08128e5f 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -122,16 +122,6 @@ static void clear_dither_context(DitherContext *di) MEM_freeN(di); } -MINLINE float dither_random_value(float s, float t) -{ - static float vec[2] = {12.9898f, 78.233f}; - float st[2]; - float value; - copy_v2_fl2(st, s, t); - - value = sinf(dot_v2v2(st, vec)) * 43758.5453f; - return value - floorf(value); -} /************************* Generic Buffer Conversion *************************/ diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index e66be77ecaf..d0281744830 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -35,6 +35,7 @@ #include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_fileops.h" +#include "BLI_ghash.h" #include "IMB_indexer.h" #include "IMB_anim.h" @@ -1148,19 +1149,64 @@ static void index_rebuild_fallback(FallbackIndexBuilderContext *context, * ---------------------------------------------------------------------- */ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, - IMB_Proxy_Size proxy_sizes_in_use, int quality) + IMB_Proxy_Size proxy_sizes_in_use, int quality, + const bool overwrite, GSet *file_list) { IndexBuildContext *context = NULL; + IMB_Proxy_Size proxy_sizes_to_build = proxy_sizes_in_use; + int i; + + /* Don't generate the same file twice! */ + if (file_list) { + for (i = 0; i < IMB_PROXY_MAX_SLOT; ++i) { + IMB_Proxy_Size proxy_size = proxy_sizes[i]; + if (proxy_size & proxy_sizes_to_build) { + char filename[FILE_MAX]; + get_proxy_filename(anim, proxy_size, filename, false); + + if (BLI_gset_haskey(file_list, filename)) { + proxy_sizes_to_build &= ~proxy_size; + printf("Proxy: %s already registered for generation, skipping\n", filename); + } + else { + BLI_gset_insert(file_list, BLI_strdup(filename)); + } + } + } + } + + if (!overwrite) { + IMB_Proxy_Size built_proxies = IMB_anim_proxy_get_existing(anim); + if (built_proxies != 0) { + + for (i = 0; i < IMB_PROXY_MAX_SLOT; ++i) { + IMB_Proxy_Size proxy_size = proxy_sizes[i]; + if (proxy_size & built_proxies) { + char filename[FILE_MAX]; + get_proxy_filename(anim, proxy_size, filename, false); + printf("Skipping proxy: %s\n", filename); + } + } + } + proxy_sizes_to_build &= ~built_proxies; + } + + fflush(stdout); + + if (proxy_sizes_to_build == 0) { + return NULL; + } + switch (anim->curtype) { #ifdef WITH_FFMPEG case ANIM_FFMPEG: - context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_in_use, quality); + context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); break; #endif #ifdef WITH_AVI default: - context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_in_use, quality); + context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); break; #endif } @@ -1237,7 +1283,7 @@ void IMB_free_indices(struct anim *anim) void IMB_anim_set_index_dir(struct anim *anim, const char *dir) { - if (strcmp(anim->index_dir, dir) == 0) { + if (STREQ(anim->index_dir, dir)) { return; } BLI_strncpy(anim->index_dir, dir, sizeof(anim->index_dir)); @@ -1304,3 +1350,18 @@ int IMB_anim_index_get_frame_index(struct anim *anim, IMB_Timecode_Type tc, return IMB_indexer_get_frame_index(idx, position); } +IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim) +{ + const int num_proxy_sizes = IMB_PROXY_MAX_SLOT; + IMB_Proxy_Size existing = 0; + int i; + for (i = 0; i < num_proxy_sizes; ++i) { + IMB_Proxy_Size proxy_size = proxy_sizes[i]; + char filename[FILE_MAX]; + get_proxy_filename(anim, proxy_size, filename, false); + if (BLI_exists(filename)) { + existing |= proxy_size; + } + } + return existing; +} diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index f4b5f987869..310e517e38d 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -37,9 +37,12 @@ #include "MEM_guardedalloc.h" +#include "BLI_utildefines.h" #include "BLI_string.h" #include "BLI_fileops.h" +#include "BKE_idprop.h" + #include "imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -267,7 +270,7 @@ handle_app1(j_decompress_ptr cinfo) if (length < 16) { for (i = 0; i < length; i++) INPUT_BYTE(cinfo, neogeo[i], return false); length = 0; - if (strncmp(neogeo, "NeoGeo", 6) == 0) memcpy(&ibuf_ftype, neogeo + 6, 4); + if (STREQLEN(neogeo, "NeoGeo", 6)) memcpy(&ibuf_ftype, neogeo + 6, 4); ibuf_ftype = BIG_LONG(ibuf_ftype); } INPUT_SYNC(cinfo); /* do before skip_input_data */ @@ -385,7 +388,7 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int fla * That is why we need split it to the * common key/value here. */ - if (strncmp(str, "Blender", 7)) { + if (!STREQLEN(str, "Blender", 7)) { /* * Maybe the file have text that * we don't know "what it's", in that @@ -478,7 +481,6 @@ static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf) uchar *rect; int x, y; char neogeo[128]; - ImMetaData *iptr; char *text; jpeg_start_compress(cinfo, true); @@ -490,28 +492,28 @@ static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf) jpeg_write_marker(cinfo, 0xe1, (JOCTET *) neogeo, 10); if (ibuf->metadata) { + IDProperty *prop; /* key + max value + "Blender" */ text = MEM_mallocN(530, "stamp info read"); - iptr = ibuf->metadata; - while (iptr) { - if (!strcmp(iptr->key, "None")) { - jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *) iptr->value, strlen(iptr->value) + 1); - goto next_stamp_info; - } + for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) { + if (prop->type == IDP_STRING) { + int text_len; + if (!strcmp(prop->name, "None")) { + jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *) IDP_String(prop), prop->len + 1); + } - /* - * The JPEG format don't support a pair "key/value" - * like PNG, so we "encode" the stamp in a - * single string: - * "Blender:key:value" - * - * The first "Blender" is a simple identify to help - * in the read process. - */ - sprintf(text, "Blender:%s:%s", iptr->key, iptr->value); - jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *) text, strlen(text) + 1); -next_stamp_info: - iptr = iptr->next; + /* + * The JPEG format don't support a pair "key/value" + * like PNG, so we "encode" the stamp in a + * single string: + * "Blender:key:value" + * + * The first "Blender" is a simple identify to help + * in the read process. + */ + text_len = sprintf(text, "Blender:%s:%s", prop->name, IDP_String(prop)); + jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *) text, text_len + 1); + } } MEM_freeN(text); } diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c index 797d34d118b..8cb5070dd62 100644 --- a/source/blender/imbuf/intern/metadata.c +++ b/source/blender/imbuf/intern/metadata.c @@ -36,6 +36,8 @@ #include "BLI_utildefines.h" #include "BLI_string.h" +#include "BKE_idprop.h" + #include "MEM_guardedalloc.h" #include "IMB_imbuf_types.h" @@ -47,119 +49,84 @@ void IMB_metadata_free(struct ImBuf *img) { - ImMetaData *info; - if (!img) return; if (!img->metadata) { return; } - info = img->metadata; - while (info) { - ImMetaData *next = info->next; - MEM_freeN(info->key); - MEM_freeN(info->value); - MEM_freeN(info); - info = next; - } + + IDP_FreeProperty(img->metadata); + MEM_freeN(img->metadata); } bool IMB_metadata_get_field(struct ImBuf *img, const char *key, char *field, const size_t len) { - ImMetaData *info; + IDProperty *prop; + bool retval = false; if (!img) return false; - if (!img->metadata) { + if (!img->metadata) return false; - } - info = img->metadata; - while (info) { - if (strcmp(key, info->key) == 0) { - BLI_strncpy(field, info->value, len); - retval = true; - break; - } - info = info->next; + + prop = IDP_GetPropertyFromGroup(img->metadata, key); + + if (prop && prop->type == IDP_STRING) { + BLI_strncpy(field, IDP_String(prop), len); + retval = true; } return retval; } bool IMB_metadata_add_field(struct ImBuf *img, const char *key, const char *value) { - ImMetaData *info; - ImMetaData *last; + IDProperty *prop; if (!img) return false; if (!img->metadata) { - img->metadata = MEM_callocN(sizeof(ImMetaData), "ImMetaData"); - info = img->metadata; - } - else { - info = img->metadata; - last = info; - while (info) { - last = info; - info = info->next; - } - info = MEM_callocN(sizeof(ImMetaData), "ImMetaData"); - last->next = info; + IDPropertyTemplate val; + img->metadata = IDP_New(IDP_GROUP, &val, "metadata"); } - info->key = BLI_strdup(key); - info->value = BLI_strdup(value); - return true; + + prop = IDP_NewString(value, key, 512); + return IDP_AddToGroup(img->metadata, prop); } bool IMB_metadata_del_field(struct ImBuf *img, const char *key) { - ImMetaData *p, *p1; + IDProperty *prop; if ((!img) || (!img->metadata)) return false; - p = img->metadata; - p1 = NULL; - while (p) { - if (!strcmp(key, p->key)) { - if (p1) - p1->next = p->next; - else - img->metadata = p->next; - - MEM_freeN(p->key); - MEM_freeN(p->value); - MEM_freeN(p); - return true; - } - p1 = p; - p = p->next; + prop = IDP_GetPropertyFromGroup(img->metadata, key); + + if (prop) { + IDP_FreeFromGroup(img->metadata, prop); } return false; } bool IMB_metadata_change_field(struct ImBuf *img, const char *key, const char *field) { - ImMetaData *p; + IDProperty *prop; if (!img) return false; - if (!img->metadata) - return (IMB_metadata_add_field(img, key, field)); + prop = (img->metadata) ? IDP_GetPropertyFromGroup(img->metadata, key) : NULL; - p = img->metadata; - while (p) { - if (!strcmp(key, p->key)) { - MEM_freeN(p->value); - p->value = BLI_strdup(field); - return true; - } - p = p->next; + if (!prop) { + return (IMB_metadata_add_field(img, key, field)); + } + else if (prop->type == IDP_STRING) { + IDP_AssignString(prop, field, 1024); + return true; + } + else { + return false; } - - return (IMB_metadata_add_field(img, key, field)); } - diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index cb789cc8491..29bb35986e8 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -142,15 +142,16 @@ static void moviecache_valfree(void *val) static void check_unused_keys(MovieCache *cache) { - GHashIterator *iter; + GHashIterator gh_iter; - iter = BLI_ghashIterator_new(cache->hash); - while (!BLI_ghashIterator_done(iter)) { - MovieCacheKey *key = BLI_ghashIterator_getKey(iter); - MovieCacheItem *item = BLI_ghashIterator_getValue(iter); - int remove = 0; + BLI_ghashIterator_init(&gh_iter, cache->hash); + + while (!BLI_ghashIterator_done(&gh_iter)) { + MovieCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + MovieCacheItem *item = BLI_ghashIterator_getValue(&gh_iter); + bool remove; - BLI_ghashIterator_step(iter); + BLI_ghashIterator_step(&gh_iter); remove = !item->ibuf; @@ -161,8 +162,6 @@ static void check_unused_keys(MovieCache *cache) if (remove) BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree); } - - BLI_ghashIterator_free(iter); } static int compare_int(const void *av, const void *bv) @@ -473,16 +472,17 @@ void IMB_moviecache_free(MovieCache *cache) void IMB_moviecache_cleanup(MovieCache *cache, bool (cleanup_check_cb) (ImBuf *ibuf, void *userkey, void *userdata), void *userdata) { - GHashIterator *iter; + GHashIterator gh_iter; check_unused_keys(cache); - iter = BLI_ghashIterator_new(cache->hash); - while (!BLI_ghashIterator_done(iter)) { - MovieCacheKey *key = BLI_ghashIterator_getKey(iter); - MovieCacheItem *item = BLI_ghashIterator_getValue(iter); + BLI_ghashIterator_init(&gh_iter, cache->hash); + + while (!BLI_ghashIterator_done(&gh_iter)) { + MovieCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + MovieCacheItem *item = BLI_ghashIterator_getValue(&gh_iter); - BLI_ghashIterator_step(iter); + BLI_ghashIterator_step(&gh_iter); if (cleanup_check_cb(item->ibuf, key->userkey, userdata)) { PRINT("%s: cache '%s' remove item %p\n", __func__, cache->name, item); @@ -490,8 +490,6 @@ void IMB_moviecache_cleanup(MovieCache *cache, bool (cleanup_check_cb) (ImBuf *i BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree); } } - - BLI_ghashIterator_free(iter); } /* get segments of cached frames. useful for debugging cache policies */ @@ -518,13 +516,12 @@ void IMB_moviecache_get_cache_segments(MovieCache *cache, int proxy, int render_ int totframe = BLI_ghash_size(cache->hash); int *frames = MEM_callocN(totframe * sizeof(int), "movieclip cache frames"); int a, totseg = 0; - GHashIterator *iter; + GHashIterator gh_iter; - iter = BLI_ghashIterator_new(cache->hash); a = 0; - while (!BLI_ghashIterator_done(iter)) { - MovieCacheKey *key = BLI_ghashIterator_getKey(iter); - MovieCacheItem *item = BLI_ghashIterator_getValue(iter); + GHASH_ITER(gh_iter, cache->hash) { + MovieCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + MovieCacheItem *item = BLI_ghashIterator_getValue(&gh_iter); int framenr, curproxy, curflags; if (item->ibuf) { @@ -533,12 +530,8 @@ void IMB_moviecache_get_cache_segments(MovieCache *cache, int proxy, int render_ if (curproxy == proxy && curflags == render_flags) frames[a++] = framenr; } - - BLI_ghashIterator_step(iter); } - BLI_ghashIterator_free(iter); - qsort(frames, totframe, sizeof(int), compare_int); /* count */ diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index ba1bda640a6..5de243845a4 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -60,6 +60,8 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #include "BLI_math_color.h" #include "BLI_threads.h" +#include "BKE_idprop.h" + #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" #include "IMB_allocimbuf.h" @@ -305,10 +307,15 @@ static void openexr_header_compression(Header *header, int compression) static void openexr_header_metadata(Header *header, struct ImBuf *ibuf) { - ImMetaData *info; + if (ibuf->metadata) { + IDProperty *prop; - for (info = ibuf->metadata; info; info = info->next) - header->insert(info->key, StringAttribute(info->value)); + for (prop = (IDProperty *)ibuf->metadata->data.group.first; prop; prop = prop->next) { + if (prop->type == IDP_STRING) { + header->insert(prop->name, StringAttribute(IDP_String(prop))); + } + } + } if (ibuf->ppm[0] > 0.0) addXDensity(*header, ibuf->ppm[0] / 39.3700787); /* 1 meter = 39.3700787 inches */ @@ -784,7 +791,7 @@ void IMB_exr_read_channels(void *handle) /* check if exr was saved with previous versions of blender which flipped images */ const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel"); - short flip = (ta && strncmp(ta->value().c_str(), "Blender V2.43", 13) == 0); /* 'previous multilayer attribute, flipped */ + short flip = (ta && STREQLEN(ta->value().c_str(), "Blender V2.43", 13)); /* 'previous multilayer attribute, flipped */ for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { @@ -982,7 +989,7 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname) if (pass == NULL) { pass = (ExrPass *)MEM_callocN(sizeof(ExrPass), "exr pass"); - if (strcmp(passname, "Combined") == 0) + if (STREQ(passname, "Combined")) BLI_addhead(lb, pass); else BLI_addtail(lb, pass); diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c index 3266dc97c78..683bdabcd6c 100644 --- a/source/blender/imbuf/intern/png.c +++ b/source/blender/imbuf/intern/png.c @@ -38,6 +38,7 @@ #include "BLI_math.h" #include "BKE_global.h" +#include "BKE_idprop.h" #include "MEM_guardedalloc.h" @@ -397,24 +398,25 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags) /* image text info */ if (ibuf->metadata) { png_text *metadata; - ImMetaData *iptr; + IDProperty *prop; + int num_text = 0; - iptr = ibuf->metadata; - while (iptr) { - num_text++; - iptr = iptr->next; + + for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) { + if (prop->type == IDP_STRING) { + num_text++; + } } metadata = MEM_callocN(num_text * sizeof(png_text), "png_metadata"); - iptr = ibuf->metadata; num_text = 0; - while (iptr) { - - metadata[num_text].compression = PNG_TEXT_COMPRESSION_NONE; - metadata[num_text].key = iptr->key; - metadata[num_text].text = iptr->value; - num_text++; - iptr = iptr->next; + for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) { + if (prop->type == IDP_STRING) { + metadata[num_text].compression = PNG_TEXT_COMPRESSION_NONE; + metadata[num_text].key = prop->name; + metadata[num_text].text = IDP_String(prop); + num_text++; + } } png_set_text(png_ptr, info_ptr, metadata, num_text); @@ -490,7 +492,7 @@ static void imb_png_warning(png_structp UNUSED(png_ptr), png_const_charp message * and with new libpng it became too much picky, giving a warning on * the splash screen even. */ - if ((G.debug & G_DEBUG) == 0 && !strncmp(message, "iCCP", 4)) { + if ((G.debug & G_DEBUG) == 0 && STREQLEN(message, "iCCP", 4)) { return; } fprintf(stderr, "libpng warning: %s\n", message); diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c index af353461f1f..aee465c49cc 100644 --- a/source/blender/imbuf/intern/thumbs_blend.c +++ b/source/blender/imbuf/intern/thumbs_blend.c @@ -55,7 +55,7 @@ static ImBuf *loadblend_thumb(gzFile gzfile) /* read the blend file header */ if (gzread(gzfile, buf, 12) != 12) return NULL; - if (strncmp(buf, "BLENDER", 7)) + if (!STREQLEN(buf, "BLENDER", 7)) return NULL; if (buf[7] == '-') diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index e3b8e271387..4d58642e9c4 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -309,8 +309,8 @@ int imb_is_a_tiff(unsigned char *mem) char big_endian[IMB_TIFF_NCB] = { 0x4d, 0x4d, 0x00, 0x2a }; char lil_endian[IMB_TIFF_NCB] = { 0x49, 0x49, 0x2a, 0x00 }; - return ( (memcmp(big_endian, mem, IMB_TIFF_NCB) == 0) || - (memcmp(lil_endian, mem, IMB_TIFF_NCB) == 0) ); + return ((memcmp(big_endian, mem, IMB_TIFF_NCB) == 0) || + (memcmp(lil_endian, mem, IMB_TIFF_NCB) == 0)); } static void scanline_contig_16bit(float *rectf, const unsigned short *sbuf, int scanline_w, int spp) @@ -594,7 +594,7 @@ ImBuf *imb_loadtiff(unsigned char *mem, size_t size, int flags, char colorspace[ format = NULL; TIFFGetField(image, TIFFTAG_PIXAR_TEXTUREFORMAT, &format); - if (format && strcmp(format, "Plain Texture") == 0 && TIFFIsTiled(image)) { + if (format && STREQ(format, "Plain Texture") && TIFFIsTiled(image)) { int numlevel = TIFFNumberOfDirectories(image); /* create empty mipmap levels in advance */ diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index c00435faa9d..b51b53cc801 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -270,6 +270,8 @@ enum { LIB_ID_RECALC = 1 << 12, LIB_ID_RECALC_DATA = 1 << 13, LIB_ANIM_NO_RECALC = 1 << 14, + + LIB_ID_RECALC_ALL = (LIB_ID_RECALC|LIB_ID_RECALC_DATA), }; #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 243e747c006..7b331181a58 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -491,7 +491,7 @@ typedef struct bAction { ID id; /* ID-serialisation for relinking */ ListBase curves; /* function-curves (FCurve) */ - ListBase chanbase; /* legacy data - Action Channels (bActionChannel) in pre-2.5 animation system */ + ListBase chanbase DNA_DEPRECATED; /* legacy data - Action Channels (bActionChannel) in pre-2.5 animation system */ ListBase groups; /* groups of function-curves (bActionGroup) */ ListBase markers; /* markers local to the Action (used to provide Pose-Libraries) */ diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 3d0d6b820d7..9e6b71fcbac 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -719,13 +719,13 @@ typedef struct KS_Path { int idtype; /* ID-type that path can be used on */ short groupmode; /* group naming (eKSP_Grouping) */ - short pad; + short flag; /* various settings, etc. */ char *rna_path; /* dynamically (or statically in the case of predefined sets) path */ int array_index; /* index that path affects */ - short flag; /* various settings, etc. */ - short keyingflag; /* settings to supply insertkey() with */ + short keyingflag; /* (eInsertKeyFlags) settings to supply insertkey() with */ + short keyingoverride; /* (eInsertKeyFlags) for each flag set, the relevant keyingflag bit overrides the default */ } KS_Path; /* KS_Path->flag */ @@ -770,10 +770,14 @@ typedef struct KeyingSet { char description[240]; /* (RNA_DYN_DESCR_MAX) short help text. */ char typeinfo[64]; /* name of the typeinfo data used for the relative paths */ + int active_path; /* index of the active path */ + short flag; /* settings for KeyingSet */ - short keyingflag; /* settings to supply insertkey() with */ - int active_path; /* index of the active path */ + short keyingflag; /* (eInsertKeyFlags) settings to supply insertkey() with */ + short keyingoverride; /* (eInsertKeyFlags) for each flag set, the relevant keyingflag bit overrides the default */ + + char pad[6]; } KeyingSet; /* KeyingSet settings */ diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index c99494ce00e..da7d98d2b22 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -56,7 +56,7 @@ typedef struct Camera { float lens, ortho_scale, drawsize; float sensor_x, sensor_y; float shiftx, shifty; - + /* yafray: dof params */ /* qdn: yafray var 'YF_dofdist' now enabled for defocus composite node as well. * The name was not changed so that no other files need to be modified */ @@ -96,12 +96,13 @@ enum { CAM_SHOWLIMITS = (1 << 0), CAM_SHOWMIST = (1 << 1), CAM_SHOWPASSEPARTOUT = (1 << 2), - CAM_SHOWTITLESAFE = (1 << 3), + CAM_SHOW_SAFE_MARGINS = (1 << 3), CAM_SHOWNAME = (1 << 4), CAM_ANGLETOGGLE = (1 << 5), CAM_DS_EXPAND = (1 << 6), CAM_PANORAMA = (1 << 7), /* deprecated */ CAM_SHOWSENSOR = (1 << 8), + CAM_SHOW_SAFE_CENTER = (1 << 9), }; #if (DNA_DEPRECATED_GCC_POISON == 1) diff --git a/source/blender/makesdna/DNA_cloth_types.h b/source/blender/makesdna/DNA_cloth_types.h index 6c7d500e4e2..3144dadb680 100644 --- a/source/blender/makesdna/DNA_cloth_types.h +++ b/source/blender/makesdna/DNA_cloth_types.h @@ -69,10 +69,19 @@ typedef struct ClothSimSettings { float goalspring; float goalfrict; float velocity_smooth; /* smoothing of velocities for hair */ + float density_target; /* minimum density for hair */ + float density_strength; /* influence of hair density */ float collider_friction; /* friction with colliders */ float vel_damping; /* damp the velocity to speed up getting to the resting position */ float shrink_min; /* min amount to shrink cloth by 0.0f (no shrink) - 1.0f (shrink to nothing) */ float shrink_max; /* max amount to shrink cloth by 0.0f (no shrink) - 1.0f (shrink to nothing) */ + + /* XXX various hair stuff + * should really be separate, this struct is a horrible mess already + */ + float bending_damping; /* damping of bending springs */ + float voxel_cell_size; /* size of voxel grid cells for continuum dynamics */ + int pad; int stepsPerFrame; /* Number of time steps per frame. */ int flags; /* flags, see CSIMSETT_FLAGS enum above. */ @@ -86,7 +95,6 @@ typedef struct ClothSimSettings { short shapekey_rest; /* vertex group for scaling structural stiffness */ short presets; /* used for presets on GUI */ short reset; - char pad[4]; struct EffectorWeights *effector_weights; } ClothSimSettings; @@ -97,15 +105,16 @@ typedef struct ClothCollSettings { float epsilon; /* min distance for collisions. */ float self_friction; /* Fiction/damping with self contact. */ float friction; /* Friction/damping applied on contact with other object.*/ + float damping; /* Collision restitution on contact with other object.*/ float selfepsilon; /* for selfcollision */ float repel_force, distance_repel; int flags; /* collision flags defined in BKE_cloth.h */ short self_loop_count; /* How many iterations for the selfcollision loop */ short loop_count; /* How many iterations for the collision loop. */ + int pad; struct Group *group; /* Only use colliders from this group of objects */ short vgroup_selfcol; /* vgroup to paint which vertices are used for self collisions */ - short pad; - int pad2; + short pad2[3]; } ClothCollSettings; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 70dc43676ac..74f5967db13 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -63,9 +63,10 @@ typedef struct CustomDataExternal { * layers, each with a data type (e.g. MTFace, MDeformVert, etc.). */ typedef struct CustomData { CustomDataLayer *layers; /* CustomDataLayers, ordered by type */ - int typemap[41]; /* runtime only! - maps types to indices of first layer of that type, + int typemap[42]; /* runtime only! - maps types to indices of first layer of that type, * MUST be >= CD_NUMTYPES, but we cant use a define here. * Correct size is ensured in CustomData_update_typemap assert() */ + int pad_i1; int totlayer, maxlayer; /* number of layers, size of layers array */ int totsize; /* in editmode, total size of all data layers */ struct BLI_mempool *pool; /* (BMesh Only): Memory pool for allocation of blocks */ @@ -73,7 +74,7 @@ typedef struct CustomData { } CustomData; /* CustomData.type */ -enum { +typedef enum CustomDataType { CD_MVERT = 0, CD_MSTICKY = 1, /* DEPRECATED */ CD_MDEFORMVERT = 2, @@ -119,9 +120,10 @@ enum { CD_FREESTYLE_FACE = 38, CD_MLOOPTANGENT = 39, CD_TESSLOOPNORMAL = 40, + CD_CUSTOMLOOPNORMAL = 41, - CD_NUMTYPES = 41 -}; + CD_NUMTYPES = 42 +} CustomDataType; /* Bits for CustomDataMask */ #define CD_MASK_MVERT (1 << CD_MVERT) @@ -167,6 +169,7 @@ enum { #define CD_MASK_FREESTYLE_FACE (1LL << CD_FREESTYLE_FACE) #define CD_MASK_MLOOPTANGENT (1LL << CD_MLOOPTANGENT) #define CD_MASK_TESSLOOPNORMAL (1LL << CD_TESSLOOPNORMAL) +#define CD_MASK_CUSTOMLOOPNORMAL (1LL << CD_CUSTOMLOOPNORMAL) /* CustomData.flag */ enum { diff --git a/source/blender/makesdna/DNA_fileglobal_types.h b/source/blender/makesdna/DNA_fileglobal_types.h index 040b55d9eb6..fc7959c0043 100644 --- a/source/blender/makesdna/DNA_fileglobal_types.h +++ b/source/blender/makesdna/DNA_fileglobal_types.h @@ -41,9 +41,9 @@ struct Scene; */ typedef struct FileGlobal { char subvstr[4]; /* needs to be here, for human fileformat recognition */ - short subversion, pads; + short subversion; short minversion, minsubversion; - short displaymode, winpos; + char pad[6]; struct bScreen *curscreen; struct Scene *curscene; int fileflags; diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index fcb894c9ebf..dca1c8330b0 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -143,6 +143,7 @@ enum { IMA_USER_FRAME_IN_RANGE = (1 << 10), /* for image user, but these flags are mixed */ IMA_VIEW_AS_RENDER = (1 << 11), IMA_IGNORE_ALPHA = (1 << 12), + IMA_DEINTERLACE = (1 << 13), }; #if (DNA_DEPRECATED_GCC_POISON == 1) diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 3f94a9cfebb..c3270430a3a 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -438,6 +438,7 @@ typedef struct Material { #define MAP_PA_CLUMP 128 #define MAP_PA_KINK 256 #define MAP_PA_ROUGH 512 +#define MAP_PA_FREQ 1024 /* pr_type */ #define MA_FLAT 0 diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 7b1a9bba3a2..8f394965664 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -83,7 +83,8 @@ typedef enum ModifierType { eModifierType_LaplacianDeform = 47, eModifierType_Wireframe = 48, eModifierType_DataTransfer = 49, - eModifierType_PointCache = 50, + eModifierType_NormalEdit = 50, + eModifierType_PointCache = 51, NUM_MODIFIER_TYPES } ModifierType; @@ -536,16 +537,39 @@ typedef struct ArmatureModifierData { char defgrp_name[64]; /* MAX_VGROUP_NAME */ } ArmatureModifierData; +enum { + MOD_HOOK_UNIFORM_SPACE = (1 << 0), +}; + +/* same as WarpModifierFalloff */ +typedef enum { + eHook_Falloff_None = 0, + eHook_Falloff_Curve = 1, + eHook_Falloff_Sharp = 2, /* PROP_SHARP */ + eHook_Falloff_Smooth = 3, /* PROP_SMOOTH */ + eHook_Falloff_Root = 4, /* PROP_ROOT */ + eHook_Falloff_Linear = 5, /* PROP_LIN */ + eHook_Falloff_Const = 6, /* PROP_CONST */ + eHook_Falloff_Sphere = 7, /* PROP_SPHERE */ + eHook_Falloff_InvSquare = 8, /* PROP_INVSQUARE */ + /* PROP_RANDOM not used */ +} HookModifierFalloff; + typedef struct HookModifierData { ModifierData modifier; struct Object *object; char subtarget[64]; /* optional name of bone target, MAX_ID_NAME-2 */ + char flag; + char falloff_type; /* use enums from WarpModifier (exact same functionality) */ + char pad[6]; float parentinv[4][4]; /* matrix making current transform unmodified */ float cent[3]; /* visualization of hook */ float falloff; /* if not zero, falloff is distance where influence zero */ + struct CurveMapping *curfalloff; + int *indexar; /* if NULL, it's using vertexgroup */ int totindex; float force; @@ -565,6 +589,15 @@ typedef struct ClothModifierData { struct ClothCollSettings *coll_parms; /* definition is in DNA_cloth_types.h */ struct PointCache *point_cache; /* definition is in DNA_object_force.h */ struct ListBase ptcaches DNA_DEPRECATED; + /* XXX nasty hack, remove once hair can be separated from cloth modifier data */ + struct ClothHairData *hairdata; + /* grid geometry values of hair continuum */ + float hair_grid_min[3]; + float hair_grid_max[3]; + int hair_grid_res[3]; + float hair_grid_cellsize; + + struct ClothSolverResult *solver_result; } ClothModifierData; typedef struct CollisionModifierData { @@ -970,6 +1003,7 @@ typedef enum { eWarp_Falloff_Linear = 5, /* PROP_LIN */ eWarp_Falloff_Const = 6, /* PROP_CONST */ eWarp_Falloff_Sphere = 7, /* PROP_SPHERE */ + eWarp_Falloff_InvSquare = 8, /* PROP_INVSQUARE */ /* PROP_RANDOM not used */ } WarpModifierFalloff; @@ -1425,5 +1459,37 @@ typedef struct PointCacheModifierData { struct DerivedMesh *output_dm; } PointCacheModifierData; +/* Set Split Normals modifier */ +typedef struct NormalEditModifierData { + ModifierData modifier; + char defgrp_name[64]; /* MAX_VGROUP_NAME */ + struct Object *target; /* Source of normals, or center of ellipsoid. */ + short mode; + short flag; + short mix_mode; + char pad[2]; + float mix_factor; + float offset[3]; +} NormalEditModifierData; + +/* NormalEditModifierData.mode */ +enum { + MOD_NORMALEDIT_MODE_RADIAL = 0, + MOD_NORMALEDIT_MODE_DIRECTIONAL = 1, +}; + +/* NormalEditModifierData.flags */ +enum { + MOD_NORMALEDIT_INVERT_VGROUP = (1 << 0), + MOD_NORMALEDIT_USE_DIRECTION_PARALLEL = (1 << 1), +}; + +/* NormalEditModifierData.mix_mode */ +enum { + MOD_NORMALEDIT_MIX_COPY = 0, + MOD_NORMALEDIT_MIX_ADD = 1, + MOD_NORMALEDIT_MIX_SUB = 2, + MOD_NORMALEDIT_MIX_MUL = 3, +}; #endif /* __DNA_MODIFIER_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 02c451e8549..f08b3ea9590 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -825,6 +825,10 @@ typedef struct NodeTranslateData { typedef struct NodePlaneTrackDeformData { char tracking_object[64]; char plane_track_name[64]; + char flag; + char motion_blur_samples; + char pad[2]; + float motion_blur_shutter; } NodePlaneTrackDeformData; typedef struct NodeShaderScript { @@ -962,6 +966,8 @@ typedef struct NodeSunBeams { /* image texture */ #define SHD_PROJ_FLAT 0 #define SHD_PROJ_BOX 1 +#define SHD_PROJ_SPHERE 2 +#define SHD_PROJ_TUBE 3 /* image texture interpolation */ #define SHD_INTERP_LINEAR 0 @@ -1078,4 +1084,11 @@ enum { /* viewer and cmposite output */ #define CMP_NODE_OUTPUT_IGNORE_ALPHA 1 +/* Plane track deform node */ +enum { + CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR = 1, +}; + +#define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64 + #endif diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index af8f0b87454..ea58bf74e7d 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -44,6 +44,7 @@ typedef struct HairKey { float weight; /* softbody weight */ short editflag; /* saved particled edit mode flags */ short pad; + float world_co[3]; } HairKey; typedef struct ParticleKey { /* when changed update size of struct to copy_particleKey()!! */ @@ -207,6 +208,8 @@ typedef struct ParticleSettings { /* length */ float randlength; /* children */ + int child_flag; + int pad3; int child_nbr, ren_child_nbr; float parents, childsize, childrandsize; float childrad, childflat; @@ -215,6 +218,8 @@ typedef struct ParticleSettings { /* kink */ float kink_amp, kink_freq, kink_shape, kink_flat; float kink_amp_clump; + int kink_extra_steps, pad4; + float kink_axis_random, kink_amp_random; /* rough */ float rough1, rough1_size; float rough2, rough2_size, rough2_thres; @@ -232,6 +237,12 @@ typedef struct ParticleSettings { int trail_count; /* keyed particles */ int keyed_loops; + struct CurveMapping *clumpcurve; + struct CurveMapping *roughcurve; + float clump_noise_size; + + /* hair dynamics */ + float bending_random; struct MTex *mtex[18]; /* MAX_MTEX */ @@ -246,7 +257,7 @@ typedef struct ParticleSettings { /* modified dm support */ short use_modifier_stack; - short pad[3]; + short pad5[3]; } ParticleSettings; @@ -314,6 +325,29 @@ typedef struct ParticleSystem { float _pad; /* spare capacity */ } ParticleSystem; +typedef enum eParticleDrawFlag { + PART_DRAW_VEL = (1 << 0), + PART_DRAW_GLOBAL_OB = (1 << 1), + PART_DRAW_SIZE = (1 << 2), + PART_DRAW_EMITTER = (1 << 3), /* render emitter also */ + PART_DRAW_HEALTH = (1 << 4), + PART_ABS_PATH_TIME = (1 << 5), + PART_DRAW_COUNT_GR = (1 << 6), + PART_DRAW_BB_LOCK = (1 << 7), /* used with billboards */ + PART_DRAW_ROTATE_OB = (1 << 7), /* used with dupliobjects/groups */ + PART_DRAW_PARENT = (1 << 8), + PART_DRAW_NUM = (1 << 9), + PART_DRAW_RAND_GR = (1 << 10), + PART_DRAW_REN_ADAPT = (1 << 11), + PART_DRAW_VEL_LENGTH = (1 << 12), + PART_DRAW_MAT_COL = (1 << 13), /* deprecated, but used in do_versions */ + PART_DRAW_WHOLE_GR = (1 << 14), + PART_DRAW_REN_STRAND = (1 << 15), + PART_DRAW_NO_SCALE_OB = (1 << 16), /* used with dupliobjects/groups */ + PART_DRAW_GUIDE_HAIRS = (1 << 17), + PART_DRAW_HAIR_GRID = (1 << 18), +} eParticleDrawFlag; + /* part->type */ /* hair is allways baked static in object/geometry space */ /* other types (normal particles) are in global space and not static baked */ @@ -386,31 +420,21 @@ typedef struct ParticleSystem { #define PART_PHYS_FLUID 4 /* part->kink */ -#define PART_KINK_NO 0 -#define PART_KINK_CURL 1 -#define PART_KINK_RADIAL 2 -#define PART_KINK_WAVE 3 -#define PART_KINK_BRAID 4 - -/* part->draw */ -#define PART_DRAW_VEL 1 -#define PART_DRAW_GLOBAL_OB 2 -#define PART_DRAW_SIZE 4 -#define PART_DRAW_EMITTER 8 /* render emitter also */ -#define PART_DRAW_HEALTH 16 -#define PART_ABS_PATH_TIME 32 -#define PART_DRAW_COUNT_GR 64 -#define PART_DRAW_BB_LOCK 128 /* used with billboards */ -#define PART_DRAW_ROTATE_OB 128 /* used with dupliobjects/groups */ -#define PART_DRAW_PARENT 256 -#define PART_DRAW_NUM 512 -#define PART_DRAW_RAND_GR 1024 -#define PART_DRAW_REN_ADAPT 2048 -#define PART_DRAW_VEL_LENGTH (1<<12) -#define PART_DRAW_MAT_COL (1<<13) /* deprecated, but used in do_versions */ -#define PART_DRAW_WHOLE_GR (1<<14) -#define PART_DRAW_REN_STRAND (1<<15) -#define PART_DRAW_NO_SCALE_OB (1<<16) /* used with dupliobjects/groups */ +typedef enum eParticleKink { + PART_KINK_NO = 0, + PART_KINK_CURL = 1, + PART_KINK_RADIAL = 2, + PART_KINK_WAVE = 3, + PART_KINK_BRAID = 4, + PART_KINK_SPIRAL = 5, +} eParticleKink; + +/* part->child_flag */ +typedef enum eParticleChildFlag { + PART_CHILD_USE_CLUMP_NOISE = (1<<0), + PART_CHILD_USE_CLUMP_CURVE = (1<<1), + PART_CHILD_USE_ROUGH_CURVE = (1<<2), +} eParticleChildFlag; /* part->draw_col */ #define PART_DRAW_COL_NONE 0 @@ -562,24 +586,27 @@ typedef struct ParticleSystem { #define PTARGET_MODE_ENEMY 2 /* mapto */ -/* init */ -#define PAMAP_INIT (PAMAP_TIME | PAMAP_LIFE | PAMAP_DENS | PAMAP_SIZE) -#define PAMAP_TIME (1<<0) /* emission time */ -#define PAMAP_LIFE (1<<1) /* life time */ -#define PAMAP_DENS (1<<2) /* density */ -#define PAMAP_SIZE (1<<3) /* physical size */ -/* reset */ -#define PAMAP_IVEL (1<<5) /* initial velocity */ -/* physics */ -#define PAMAP_PHYSICS (PAMAP_FIELD | PAMAP_GRAVITY | PAMAP_DAMP) -#define PAMAP_FIELD (1<<6) /* force fields */ -#define PAMAP_GRAVITY (1<<10) -#define PAMAP_DAMP (1<<11) -/* children */ -#define PAMAP_CHILD (PAMAP_CLUMP | PAMAP_KINK | PAMAP_ROUGH | PAMAP_LENGTH) -#define PAMAP_CLUMP (1<<7) -#define PAMAP_KINK (1<<8) -#define PAMAP_ROUGH (1<<9) -#define PAMAP_LENGTH (1<<4) +typedef enum eParticleTextureInfluence { + /* init */ + PAMAP_TIME = (1<<0), /* emission time */ + PAMAP_LIFE = (1<<1), /* life time */ + PAMAP_DENS = (1<<2), /* density */ + PAMAP_SIZE = (1<<3), /* physical size */ + PAMAP_INIT = (PAMAP_TIME | PAMAP_LIFE | PAMAP_DENS | PAMAP_SIZE), + /* reset */ + PAMAP_IVEL = (1<<5), /* initial velocity */ + /* physics */ + PAMAP_FIELD = (1<<6), /* force fields */ + PAMAP_GRAVITY = (1<<10), + PAMAP_DAMP = (1<<11), + PAMAP_PHYSICS = (PAMAP_FIELD | PAMAP_GRAVITY | PAMAP_DAMP), + /* children */ + PAMAP_CLUMP = (1<<7), + PAMAP_KINK_FREQ = (1<<8), + PAMAP_KINK_AMP = (1<<12), + PAMAP_ROUGH = (1<<9), + PAMAP_LENGTH = (1<<4), + PAMAP_CHILD = (PAMAP_CLUMP | PAMAP_KINK_FREQ | PAMAP_KINK_AMP | PAMAP_ROUGH | PAMAP_LENGTH), +} eParticleTextureInfluence; #endif diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 49f8b3cd4d0..9ac506696ad 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -849,7 +849,7 @@ typedef struct ImagePaintSettings { struct Image *clone; /* clone layer for image mode for projective texture painting */ struct Image *canvas; /* canvas when the explicit system is used for painting */ float stencil_col[3]; - float pad1; + float dither; /* dither amount used when painting on byte images */ } ImagePaintSettings; /* ------------------------------------------- */ @@ -882,6 +882,7 @@ typedef struct ParticleEditSettings { struct Scene *scene; struct Object *object; + struct Object *shape_object; } ParticleEditSettings; /* ------------------------------------------- */ @@ -1216,6 +1217,21 @@ typedef struct PhysicsSettings { int flag, quick_cache_step, rt; } PhysicsSettings; +/* ------------------------------------------- */ +/* Safe Area options used in Camera View & VSE + */ +typedef struct DisplaySafeAreas { + /* each value represents the (x,y) margins as a multiplier. + * 'center' in this context is just the name for a different kind of safe-area */ + + float title[2]; /* Title Safe */ + float action[2]; /* Image/Graphics Safe */ + + /* use for alternate aspect ratio */ + float title_center[2]; + float action_center[2]; +} DisplaySafeAreas; + /* *************************************************************** */ /* Scene ID-Block */ @@ -1251,6 +1267,7 @@ typedef struct Scene { struct ToolSettings *toolsettings; /* default allocated now */ struct SceneStats *stats; /* default allocated now */ + struct DisplaySafeAreas safe_areas; /* migrate or replace? depends on some internal things... */ /* no, is on the right place (ton) */ @@ -1608,7 +1625,8 @@ extern const char *RE_engine_id_CYCLES; #define PROP_LIN 4 #define PROP_CONST 5 #define PROP_RANDOM 6 -#define PROP_MODE_MAX 7 +#define PROP_INVSQUARE 7 +#define PROP_MODE_MAX 8 /* toolsettings->proportional */ #define PROP_EDIT_OFF 0 diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 7372f55c3de..3e02071c3dd 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -272,14 +272,16 @@ typedef struct ARegion { /* area->flag */ enum { - HEADER_NO_PULLDOWN = (1 << 0), - AREA_FLAG_DRAWJOINTO = (1 << 1), - AREA_FLAG_DRAWJOINFROM = (1 << 2), - AREA_TEMP_INFO = (1 << 3), - AREA_FLAG_DRAWSPLIT_H = (1 << 4), - AREA_FLAG_DRAWSPLIT_V = (1 << 5), + HEADER_NO_PULLDOWN = (1 << 0), + AREA_FLAG_DRAWJOINTO = (1 << 1), + AREA_FLAG_DRAWJOINFROM = (1 << 2), + AREA_TEMP_INFO = (1 << 3), + AREA_FLAG_DRAWSPLIT_H = (1 << 4), + AREA_FLAG_DRAWSPLIT_V = (1 << 5), /* used to check if we should switch back to prevspace (of a different type) */ - AREA_FLAG_TEMP_TYPE = (1 << 6), + AREA_FLAG_TEMP_TYPE = (1 << 6), + /* for temporary fullscreens (file browser, image editor render) that are opened above user set fullscreens */ + AREA_FLAG_STACKED_FULLSCREEN = (1 << 7), }; #define EDGEWIDTH 1 diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 20678bdae49..c6748cca8e1 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -94,6 +94,8 @@ typedef struct StripProxy { // to build short build_tc_flags; // time code flags (see below) of all tc indices // to build + short build_flags; + char pad[6]; } StripProxy; typedef struct Strip { @@ -398,6 +400,11 @@ enum { #define SEQ_PROXY_TC_RECORD_RUN_NO_GAPS 8 #define SEQ_PROXY_TC_ALL 15 +/* SeqProxy->build_flags */ +enum { + SEQ_PROXY_SKIP_EXISTING = 1, +}; + /* seq->alpha_mode */ enum { SEQ_ALPHA_STRAIGHT = 0, diff --git a/source/blender/makesdna/DNA_sound_types.h b/source/blender/makesdna/DNA_sound_types.h index 4ab22e4f7b7..cb132c1b550 100644 --- a/source/blender/makesdna/DNA_sound_types.h +++ b/source/blender/makesdna/DNA_sound_types.h @@ -95,8 +95,8 @@ typedef struct bSound { */ void *playback_handle; - /* mutex for asynchronous loading of sounds */ - void *mutex; + /* spinlock for asynchronous loading of sounds */ + void *spinlock; /* XXX unused currently (SOUND_TYPE_LIMITER) */ /* float start, end; */ } bSound; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index f65cef1a0d1..165f6113b6d 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -273,6 +273,7 @@ typedef enum eSpaceOutliner_Flag { SO_NEWSELECTED = (1 << 1), SO_HIDE_RESTRICTCOLS = (1 << 2), SO_HIDE_KEYINGSETINFO = (1 << 3), + SO_SKIP_SORT_ALPHA = (1 << 4), } eSpaceOutliner_Flag; /* SpaceOops->outlinevis */ @@ -369,6 +370,8 @@ typedef enum eGraphEdit_Flag { /* normalize curves on display */ SIPO_NORMALIZE = (1 << 14), SIPO_NORMALIZE_FREEZE = (1 << 15), + /* automatically set view on selection */ + SIPO_AUTO_VIEW_SELECTED = (1 << 16), } eGraphEdit_Flag; /* SpaceIpo->mode (Graph Editor Mode) */ @@ -461,6 +464,8 @@ typedef enum eScreen_Redraws_Flag { // TIME_CONTINUE_PHYSICS = (1 << 7), /* UNUSED */ TIME_NODES = (1 << 8), TIME_CLIPS = (1 << 9), + + TIME_FOLLOW = (1 << 15), } eScreen_Redraws_Flag; /* time->cache */ @@ -527,12 +532,13 @@ typedef enum eSpaceSeq_Flag { SEQ_DRAWFRAMES = (1 << 0), SEQ_MARKER_TRANS = (1 << 1), SEQ_DRAW_COLOR_SEPARATED = (1 << 2), - SEQ_DRAW_SAFE_MARGINS = (1 << 3), + SEQ_SHOW_SAFE_MARGINS = (1 << 3), SEQ_SHOW_GPENCIL = (1 << 4), SEQ_NO_DRAW_CFRANUM = (1 << 5), SEQ_USE_ALPHA = (1 << 6), /* use RGBA display mode for preview */ SEQ_ALL_WAVEFORMS = (1 << 7), /* draw all waveforms */ SEQ_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */ + SEQ_SHOW_SAFE_CENTER = (1 << 9), } eSpaceSeq_Flag; /* sseq->view */ @@ -932,7 +938,7 @@ typedef struct bNodeTreePath { bNodeInstanceKey parent_key; /* base key for nodes in this tree instance */ int pad; float view_center[2]; /* v2d center point, so node trees can have different offsets in editors */ - /* XXX this is not automatically updated when node names are changed! */ + char node_name[64]; /* MAX_NAME */ } bNodeTreePath; @@ -1200,6 +1206,9 @@ typedef enum eSpace_Type { SPACEICONMAX = SPACE_CLIP } eSpace_Type; +/* use for function args */ +#define SPACE_TYPE_ANY -1 + // TODO: SPACE_SCRIPT #if (DNA_DEPRECATED_GCC_POISON == 1) #pragma GCC poison SPACE_IMASEL SPACE_SOUND diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index 042645eeb76..af6adbecd83 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -84,8 +84,9 @@ typedef struct MTex { /* particles */ float timefac, lengthfac, clumpfac, dampfac; - float kinkfac, roughfac, padensfac, gravityfac; + float kinkfac, kinkampfac, roughfac, padensfac, gravityfac; float lifefac, sizefac, ivelfac, fieldfac; + int pad2; /* lamp */ float shadowfac; @@ -175,8 +176,8 @@ typedef struct VoxelData { short flag; short extend; short smoked_type; + short hair_type; short data_type; - short pad; int _pad; struct Object *object; /* for rendering smoke sims */ @@ -617,6 +618,7 @@ enum { #define TEX_VD_RAW_16BIT 2 #define TEX_VD_IMAGE_SEQUENCE 3 #define TEX_VD_SMOKE 4 +#define TEX_VD_HAIR 5 /* for voxels which use VoxelData->source_path */ #define TEX_VD_IS_SOURCE_PATH(_format) (ELEM(_format, TEX_VD_BLENDERVOXEL, TEX_VD_RAW_8BIT, TEX_VD_RAW_16BIT)) @@ -626,6 +628,11 @@ enum { #define TEX_VD_SMOKEVEL 2 #define TEX_VD_SMOKEFLAME 3 +#define TEX_VD_HAIRDENSITY 0 +#define TEX_VD_HAIRVELOCITY 1 +#define TEX_VD_HAIRENERGY 2 +#define TEX_VD_HAIRRESTDENSITY 3 + /* data_type */ #define TEX_VD_INTENSITY 0 #define TEX_VD_RGBA_PREMUL 1 diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 11807ba5338..3ac923a72ef 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -288,9 +288,10 @@ typedef struct ThemeSpace { char handle_vertex[4]; char handle_vertex_select[4]; - char pad2[4]; char handle_vertex_size; + + char clipping_border_3d[4]; char marker_outline[4], marker[4], act_marker[4], sel_marker[4], dis_marker[4], lock_marker[4]; char bundle_solid[4]; @@ -461,7 +462,7 @@ typedef struct UserDef { int scrollback; /* console scrollback limit */ int dpi; /* range 48-128? */ - short encoding; + char pad2[2]; short transopts; short menuthreshold1, menuthreshold2; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 0eee28e73d9..543a95cc120 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -74,10 +74,11 @@ typedef struct BGpic { struct ImageUser iuser; struct MovieClip *clip; struct MovieClipUser cuser; - float xof, yof, size, blend; + float xof, yof, size, blend, rotation; short view; short flag; - short source, pad; + short source; + char pad[6]; } BGpic; /* ********************************* */ @@ -89,12 +90,12 @@ typedef struct RegionView3D { float viewinv[4][4]; /* inverse of viewmat */ float persmat[4][4]; /* viewmat*winmat */ float persinv[4][4]; /* inverse of persmat */ + float viewcamtexcofac[4]; /* offset/scale for camera glsl texcoords */ /* viewmat/persmat multiplied with object matrix, while drawing and selection */ float viewmatob[4][4]; float persmatob[4][4]; - /* user defined clipping planes */ float clip[6][4]; float clip_local[6][4]; /* clip in object space, means we can test for clipping in editmode without first going into worldspace */ @@ -340,7 +341,11 @@ enum { /* Camera framing options */ V3D_BGPIC_CAMERA_ASPECT = (1 << 5), /* don't stretch to fit the camera view */ - V3D_BGPIC_CAMERA_CROP = (1 << 6) /* crop out the image */ + V3D_BGPIC_CAMERA_CROP = (1 << 6), /* crop out the image */ + + /* Axis flip options */ + V3D_BGPIC_FLIP_X = (1 << 7), + V3D_BGPIC_FLIP_Y = (1 << 8), }; #define V3D_BGPIC_EXPANDED (V3D_BGPIC_EXPANDED | V3D_BGPIC_CAMERACLIP) diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 3e1a029567d..964196c1f7b 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -203,6 +203,7 @@ extern StructRNA RNA_DataTransferModifier; extern StructRNA RNA_DecimateModifier; extern StructRNA RNA_DelaySensor; extern StructRNA RNA_DisplaceModifier; +extern StructRNA RNA_DisplaySafeAreas; extern StructRNA RNA_DistortedNoiseTexture; extern StructRNA RNA_DomainFluidSettings; extern StructRNA RNA_DopeSheet; @@ -494,6 +495,7 @@ extern StructRNA RNA_SequenceEditor; extern StructRNA RNA_SequenceElement; extern StructRNA RNA_SequenceProxy; extern StructRNA RNA_SequenceTransform; +extern StructRNA RNA_NormalEditModifier; extern StructRNA RNA_ShaderNode; extern StructRNA RNA_ShaderNodeCameraData; extern StructRNA RNA_ShaderNodeCombineRGB; diff --git a/source/blender/makesrna/SConscript b/source/blender/makesrna/SConscript index 2600f55bbc6..5c244f5e8f6 100644 --- a/source/blender/makesrna/SConscript +++ b/source/blender/makesrna/SConscript @@ -53,6 +53,7 @@ incs = [ '../imbuf', '../makesdna', '../nodes', + '../physics', '../pointcache', '../render/extern/include', '../windowmanager', diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index c6c5b54f989..0515a6fc054 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -28,8 +28,6 @@ if(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=implicit-function-declaration") endif() -# message(STATUS "Configuring makesrna") - # files rna_access.c rna_define.c makesrna.c intentionally excluded. set(DEFSRC rna_ID.c @@ -118,15 +116,32 @@ set(APISRC rna_scene_api.c rna_sensor_api.c rna_sequencer_api.c + rna_sound_api.c rna_space_api.c rna_text_api.c rna_ui_api.c + rna_vfont_api.c rna_wm_api.c ) string(REGEX REPLACE "rna_([a-zA-Z0-9_-]*).c" "${CMAKE_CURRENT_BINARY_DIR}/rna_\\1_gen.c" GENSRC "${DEFSRC}") set_source_files_properties(${GENSRC} PROPERTIES GENERATED TRUE) +# -------------------------- +# CFLAGS for Generated Files +# +# less strict flags for generated source +set(GENSRC_CFLAGS) +if(CMAKE_COMPILER_IS_GNUCC OR (CMAKE_C_COMPILER_ID MATCHES "Clang")) + set(GENSRC_CFLAGS "-Wno-missing-prototypes") +endif() + +if(GENSRC_CFLAGS) + set_source_files_properties(${GENSRC} PROPERTIES COMPILE_FLAGS "${GENSRC_CFLAGS}") +endif() +unset(GENSRC_CFLAGS) + + set(SRC_RNA_INC ../RNA_access.h ../RNA_define.h @@ -298,6 +313,7 @@ blender_include_dirs( ../../ikplugin ../../makesdna ../../nodes/ + ../../physics ../../pointcache/ ../../windowmanager ../../editors/include @@ -319,9 +335,6 @@ add_executable(makesrna ${SRC} ${SRC_RNA_INC} ${SRC_DNA_INC}) target_link_libraries(makesrna bf_dna) target_link_libraries(makesrna bf_dna_blenlib) -# too many warnings with clang -remove_cc_flag("-Wmissing-prototypes") - # Output rna_*_gen.c # note (linux only): with crashes try add this after COMMAND: valgrind --leak-check=full --track-origins=yes add_custom_command( diff --git a/source/blender/makesrna/intern/SConscript b/source/blender/makesrna/intern/SConscript index 7ff671a0e9b..9614b6ac1ca 100644 --- a/source/blender/makesrna/intern/SConscript +++ b/source/blender/makesrna/intern/SConscript @@ -72,6 +72,7 @@ incs = [ '../../imbuf', '../../makesdna', '../../makesrna', + '../../physics', '../../pointcache', '../../render/extern/include', '../../windowmanager', diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 0c1701f6089..9058f715d87 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -221,11 +221,11 @@ static int replace_if_different(const char *tmpfile, const char *dep_files[]) static const char *rna_safe_id(const char *id) { - if (strcmp(id, "default") == 0) + if (STREQ(id, "default")) return "default_value"; - else if (strcmp(id, "operator") == 0) + else if (STREQ(id, "operator")) return "operator_value"; - else if (strcmp(id, "new") == 0) + else if (STREQ(id, "new")) return "create"; return id; @@ -246,11 +246,11 @@ static int cmp_property(const void *a, const void *b) const PropertyRNA *propa = *(const PropertyRNA **)a; const PropertyRNA *propb = *(const PropertyRNA **)b; - if (strcmp(propa->identifier, "rna_type") == 0) return -1; - else if (strcmp(propb->identifier, "rna_type") == 0) return 1; + if (STREQ(propa->identifier, "rna_type")) return -1; + else if (STREQ(propb->identifier, "rna_type")) return 1; - if (strcmp(propa->identifier, "name") == 0) return -1; - else if (strcmp(propb->identifier, "name") == 0) return 1; + if (STREQ(propa->identifier, "name")) return -1; + else if (STREQ(propb->identifier, "name")) return 1; return strcmp(propa->name, propb->name); } @@ -372,7 +372,7 @@ static StructRNA *rna_find_struct(const char *identifier) StructDefRNA *ds; for (ds = DefRNA.structs.first; ds; ds = ds->cont.next) - if (strcmp(ds->srna->identifier, identifier) == 0) + if (STREQ(ds->srna->identifier, identifier)) return ds->srna; return NULL; @@ -383,7 +383,7 @@ static const char *rna_find_type(const char *type) StructDefRNA *ds; for (ds = DefRNA.structs.first; ds; ds = ds->cont.next) - if (ds->dnaname && strcmp(ds->dnaname, type) == 0) + if (ds->dnaname && STREQ(ds->dnaname, type)) return ds->srna->identifier; return NULL; @@ -394,7 +394,7 @@ static const char *rna_find_dna_type(const char *type) StructDefRNA *ds; for (ds = DefRNA.structs.first; ds; ds = ds->cont.next) - if (strcmp(ds->srna->identifier, type) == 0) + if (STREQ(ds->srna->identifier, type)) return ds->dnaname; return NULL; @@ -621,9 +621,9 @@ static char *rna_def_property_get_func(FILE *f, StructRNA *srna, PropertyRNA *pr fprintf(f, "static PointerRNA %s(CollectionPropertyIterator *iter)\n", func); fprintf(f, "{\n"); if (manualfunc) { - if (strcmp(manualfunc, "rna_iterator_listbase_get") == 0 || - strcmp(manualfunc, "rna_iterator_array_get") == 0 || - strcmp(manualfunc, "rna_iterator_array_dereference_get") == 0) + if (STREQ(manualfunc, "rna_iterator_listbase_get") || + STREQ(manualfunc, "rna_iterator_array_get") || + STREQ(manualfunc, "rna_iterator_array_dereference_get")) { fprintf(f, " return rna_pointer_inherit_refine(&iter->parent, &RNA_%s, %s(iter));\n", (cprop->item_type) ? (const char *)cprop->item_type : "UnknownType", manualfunc); @@ -1168,8 +1168,8 @@ static char *rna_def_property_lookup_int_func(FILE *f, StructRNA *srna, Property return NULL; /* only supported in case of standard next functions */ - if (strcmp(nextfunc, "rna_iterator_array_next") == 0) {} - else if (strcmp(nextfunc, "rna_iterator_listbase_next") == 0) {} + if (STREQ(nextfunc, "rna_iterator_array_next")) {} + else if (STREQ(nextfunc, "rna_iterator_listbase_next")) {} else return NULL; } @@ -1190,7 +1190,7 @@ static char *rna_def_property_lookup_int_func(FILE *f, StructRNA *srna, Property fprintf(f, " %s_%s_begin(&iter, ptr);\n\n", srna->identifier, rna_safe_id(prop->identifier)); fprintf(f, " if (iter.valid) {\n"); - if (strcmp(nextfunc, "rna_iterator_array_next") == 0) { + if (STREQ(nextfunc, "rna_iterator_array_next")) { fprintf(f, " ArrayIterator *internal = &iter.internal.array;\n"); fprintf(f, " if (index < 0 || index >= internal->length) {\n"); fprintf(f, "#ifdef __GNUC__\n"); @@ -1210,7 +1210,7 @@ static char *rna_def_property_lookup_int_func(FILE *f, StructRNA *srna, Property fprintf(f, " found = 1;\n"); fprintf(f, " }\n"); } - else if (strcmp(nextfunc, "rna_iterator_listbase_next") == 0) { + else if (STREQ(nextfunc, "rna_iterator_listbase_next")) { fprintf(f, " ListBaseIterator *internal = &iter.internal.listbase;\n"); fprintf(f, " if (internal->skip) {\n"); fprintf(f, " while (index-- > 0 && iter.valid) {\n"); @@ -1396,23 +1396,23 @@ static void rna_set_raw_property(PropertyDefRNA *dp, PropertyRNA *prop) if (!dp->dnatype || !dp->dnaname || !dp->dnastructname) return; - if (strcmp(dp->dnatype, "char") == 0) { + if (STREQ(dp->dnatype, "char")) { prop->rawtype = PROP_RAW_CHAR; prop->flag |= PROP_RAW_ACCESS; } - else if (strcmp(dp->dnatype, "short") == 0) { + else if (STREQ(dp->dnatype, "short")) { prop->rawtype = PROP_RAW_SHORT; prop->flag |= PROP_RAW_ACCESS; } - else if (strcmp(dp->dnatype, "int") == 0) { + else if (STREQ(dp->dnatype, "int")) { prop->rawtype = PROP_RAW_INT; prop->flag |= PROP_RAW_ACCESS; } - else if (strcmp(dp->dnatype, "float") == 0) { + else if (STREQ(dp->dnatype, "float")) { prop->rawtype = PROP_RAW_FLOAT; prop->flag |= PROP_RAW_ACCESS; } - else if (strcmp(dp->dnatype, "double") == 0) { + else if (STREQ(dp->dnatype, "double")) { prop->rawtype = PROP_RAW_DOUBLE; prop->flag |= PROP_RAW_ACCESS; } @@ -1525,7 +1525,7 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp) const char *nextfunc = (const char *)cprop->next; const char *item_type = (const char *)cprop->item_type; - if (dp->dnatype && strcmp(dp->dnatype, "ListBase") == 0) { + if (dp->dnatype && STREQ(dp->dnatype, "ListBase")) { /* pass */ } else if (dp->dnalengthname || dp->dnalengthfixed) { @@ -1535,8 +1535,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp) /* test if we can allow raw array access, if it is using our standard * array get/next function, we can be sure it is an actual array */ if (cprop->next && cprop->get) - if (strcmp((const char *)cprop->next, "rna_iterator_array_next") == 0 && - strcmp((const char *)cprop->get, "rna_iterator_array_get") == 0) + if (STREQ((const char *)cprop->next, "rna_iterator_array_next") && + STREQ((const char *)cprop->get, "rna_iterator_array_get")) { prop->flag |= PROP_RAW_ARRAY; } @@ -2419,11 +2419,11 @@ static void rna_auto_types(void) for (ds = DefRNA.structs.first; ds; ds = ds->cont.next) { /* DNA name for Screen is patched in 2.5, we do the reverse here .. */ - if (ds->dnaname && strcmp(ds->dnaname, "Screen") == 0) + if (ds->dnaname && STREQ(ds->dnaname, "Screen")) ds->dnaname = "bScreen"; for (dp = ds->cont.properties.first; dp; dp = dp->next) { - if (dp->dnastructname && strcmp(dp->dnastructname, "Screen") == 0) + if (dp->dnastructname && STREQ(dp->dnastructname, "Screen")) dp->dnastructname = "bScreen"; if (dp->dnatype) { @@ -2443,7 +2443,7 @@ static void rna_auto_types(void) else if (dp->prop->type == PROP_COLLECTION) { CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)dp->prop; - if (!cprop->item_type && !cprop->get && strcmp(dp->dnatype, "ListBase") == 0) + if (!cprop->item_type && !cprop->get && STREQ(dp->dnatype, "ListBase")) cprop->item_type = (StructRNA *)rna_find_type(dp->dnatype); } } @@ -2786,7 +2786,7 @@ static void rna_generate_struct_prototypes(FILE *f) const char *struct_name = rna_parameter_type_name(dp->prop); for (a = 0; a < all_structures; a++) { - if (strcmp(struct_name, structures[a]) == 0) { + if (STREQ(struct_name, structures[a])) { found = 1; break; } @@ -3326,10 +3326,10 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_test.c", NULL, RNA_def_test}, {"rna_text.c", "rna_text_api.c", RNA_def_text}, {"rna_timeline.c", NULL, RNA_def_timeline_marker}, - {"rna_sound.c", NULL, RNA_def_sound}, + {"rna_sound.c", "rna_sound_api.c", RNA_def_sound}, {"rna_ui.c", "rna_ui_api.c", RNA_def_ui}, {"rna_userdef.c", NULL, RNA_def_userdef}, - {"rna_vfont.c", NULL, RNA_def_vfont}, + {"rna_vfont.c", "rna_vfont_api.c", RNA_def_vfont}, {"rna_wm.c", "rna_wm_api.c", RNA_def_wm}, {"rna_world.c", NULL, RNA_def_world}, {"rna_movieclip.c", NULL, RNA_def_movieclip}, @@ -3418,7 +3418,7 @@ static void rna_generate(BlenderRNA *brna, FILE *f, const char *filename, const if (!filename || ds->filename == filename) rna_generate_struct(brna, ds->srna, f); - if (strcmp(filename, "rna_ID.c") == 0) { + if (STREQ(filename, "rna_ID.c")) { /* this is ugly, but we cannot have c files compiled for both * makesrna and blender with some build systems at the moment */ fprintf(f, "#include \"rna_define.c\"\n\n"); @@ -3780,7 +3780,7 @@ static int rna_is_collection_functions_struct(const char **collection_structs, c int a = 0, found = 0; while (collection_structs[a]) { - if (!strcmp(collection_structs[a], struct_name)) { + if (STREQ(collection_structs[a], struct_name)) { found = 1; break; } @@ -3881,7 +3881,7 @@ static void rna_generate_header_cpp(BlenderRNA *UNUSED(brna), FILE *f) for (ds = DefRNA.structs.first; ds; ds = ds->cont.next) { srna = ds->srna; - if (!strcmp(srna->identifier, first_collection_func_struct)) { + if (STREQ(srna->identifier, first_collection_func_struct)) { StructDefRNA *ds2; StructRNA *srna2; diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 09b970fe7fd..7096060e73b 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -241,13 +241,13 @@ static IDProperty *rna_idproperty_ui(PropertyRNA *prop) IDProperty *idprop; for (idprop = ((IDProperty *)prop)->prev; idprop; idprop = idprop->prev) { - if (strcmp(RNA_IDP_UI, idprop->name) == 0) + if (STREQ(RNA_IDP_UI, idprop->name)) break; } if (idprop == NULL) { for (idprop = ((IDProperty *)prop)->next; idprop; idprop = idprop->next) { - if (strcmp(RNA_IDP_UI, idprop->name) == 0) + if (STREQ(RNA_IDP_UI, idprop->name)) break; } } @@ -511,7 +511,7 @@ StructRNA *RNA_struct_find(const char *identifier) StructRNA *type; if (identifier) { for (type = BLENDER_RNA.structs.first; type; type = type->cont.next) - if (strcmp(type->identifier, identifier) == 0) + if (STREQ(type->identifier, identifier)) return type; } return NULL; @@ -719,7 +719,7 @@ FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier) RNA_PROP_BEGIN (&tptr, funcptr, iterprop) { - if (strcmp(identifier, RNA_function_identifier(funcptr.data)) == 0) { + if (STREQ(identifier, RNA_function_identifier(funcptr.data))) { func = funcptr.data; break; } @@ -3229,7 +3229,7 @@ int RNA_property_collection_lookup_string(PointerRNA *ptr, PropertyRNA *prop, co nameptr = RNA_property_string_get_alloc(&iter.ptr, nameprop, name, sizeof(name), &namelen); - if ((keylen == namelen) && (strcmp(nameptr, key) == 0)) { + if ((keylen == namelen) && STREQ(nameptr, key)) { *r_ptr = iter.ptr; found = 1; } @@ -5264,7 +5264,7 @@ char *RNA_pointer_as_string_id(bContext *C, PointerRNA *ptr) { propname = RNA_property_identifier(prop); - if (strcmp(propname, "rna_type") == 0) + if (STREQ(propname, "rna_type")) continue; if (first_time == 0) @@ -5335,7 +5335,7 @@ char *RNA_pointer_as_string_keywords_ex(bContext *C, PointerRNA *ptr, arg_name = RNA_property_identifier(prop); - if (strcmp(arg_name, "rna_type") == 0) { + if (STREQ(arg_name, "rna_type")) { continue; } @@ -5632,7 +5632,7 @@ PropertyRNA *RNA_function_find_parameter(PointerRNA *UNUSED(ptr), FunctionRNA *f parm = func->cont.properties.first; for (; parm; parm = parm->next) - if (strcmp(RNA_property_identifier(parm), identifier) == 0) + if (STREQ(RNA_property_identifier(parm), identifier)) break; return parm; @@ -5830,7 +5830,7 @@ void RNA_parameter_get_lookup(ParameterList *parms, const char *identifier, void parm = parms->func->cont.properties.first; for (; parm; parm = parm->next) - if (strcmp(RNA_property_identifier(parm), identifier) == 0) + if (STREQ(RNA_property_identifier(parm), identifier)) break; if (parm) @@ -5886,7 +5886,7 @@ void RNA_parameter_set_lookup(ParameterList *parms, const char *identifier, cons parm = parms->func->cont.properties.first; for (; parm; parm = parm->next) - if (strcmp(RNA_property_identifier(parm), identifier) == 0) + if (STREQ(RNA_property_identifier(parm), identifier)) break; if (parm) @@ -6713,7 +6713,7 @@ bool RNA_property_equals(PointerRNA *a, PointerRNA *b, PropertyRNA *prop, eRNAEq int len_a, len_b; char *value_a = RNA_property_string_get_alloc(a, prop, fixed_a, sizeof(fixed_a), &len_a); char *value_b = RNA_property_string_get_alloc(b, prop, fixed_b, sizeof(fixed_b), &len_b); - bool equals = strcmp(value_a, value_b) == 0; + bool equals = STREQ(value_a, value_b); if (value_a != fixed_a) MEM_freeN(value_a); if (value_b != fixed_b) MEM_freeN(value_b); diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index e256d145016..3fed505b9d6 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -533,7 +533,7 @@ static void rna_def_action_groups(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_struct_ui_text(srna, "Action Groups", "Collection of action groups"); func = RNA_def_function(srna, "new", "rna_Action_groups_new"); - RNA_def_function_ui_description(func, "Add a keyframe to the curve"); + RNA_def_function_ui_description(func, "Create a new action group and add it to the action"); parm = RNA_def_string(func, "name", "Group", 0, "", "New name for the action group"); RNA_def_property_flag(parm, PROP_REQUIRED); diff --git a/source/blender/makesrna/intern/rna_actuator.c b/source/blender/makesrna/intern/rna_actuator.c index 6792dd7e5f8..691a7432275 100644 --- a/source/blender/makesrna/intern/rna_actuator.c +++ b/source/blender/makesrna/intern/rna_actuator.c @@ -120,14 +120,10 @@ static StructRNA *rna_Actuator_refine(struct PointerRNA *ptr) static void rna_Actuator_name_set(PointerRNA *ptr, const char *value) { - bActuator *act = (bActuator *)ptr->data; - + Object *ob = ptr->id.data; + bActuator *act = ptr->data; BLI_strncpy_utf8(act->name, value, sizeof(act->name)); - - if (ptr->id.data) { - Object *ob = (Object *)ptr->id.data; - BLI_uniquename(&ob->actuators, act, DATA_("Actuator"), '.', offsetof(bActuator, name), sizeof(act->name)); - } + BLI_uniquename(&ob->actuators, act, DATA_("Actuator"), '.', offsetof(bActuator, name), sizeof(act->name)); } static void rna_Actuator_type_set(struct PointerRNA *ptr, int value) @@ -494,11 +490,11 @@ static void rna_Actuator_Armature_update(Main *UNUSED(bmain), Scene *UNUSED(scen bPoseChannel *pchan; bPose *pose = ob->pose; for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - if (!strcmp(pchan->name, posechannel)) { + if (STREQ(pchan->name, posechannel)) { /* found it, now look for constraint channel */ bConstraint *con; for (con = pchan->constraints.first; con; con = con->next) { - if (!strcmp(con->name, constraint)) { + if (STREQ(con->name, constraint)) { /* found it, all ok */ return; } diff --git a/source/blender/makesrna/intern/rna_animation.c b/source/blender/makesrna/intern/rna_animation.c index 12fa754e636..e4696d87992 100644 --- a/source/blender/makesrna/intern/rna_animation.c +++ b/source/blender/makesrna/intern/rna_animation.c @@ -343,7 +343,7 @@ static void rna_KeyingSet_name_set(PointerRNA *ptr, const char *value) KeyingSet *ks = (KeyingSet *)ptr->data; /* update names of corresponding groups if name changes */ - if (strcmp(ks->name, value)) { + if (!STREQ(ks->name, value)) { KS_Path *ksp; for (ksp = ks->paths.first; ksp; ksp = ksp->next) { @@ -359,7 +359,7 @@ static void rna_KeyingSet_name_set(PointerRNA *ptr, const char *value) * conflicts */ for (agrp = adt->action->groups.first; agrp; agrp = agrp->next) { - if (strcmp(ks->name, agrp->name) == 0) { + if (STREQ(ks->name, agrp->name)) { /* there should only be one of these in the action, so can stop... */ BLI_strncpy(agrp->name, value, sizeof(agrp->name)); break; @@ -554,16 +554,46 @@ static FCurve *rna_Driver_from_existing(AnimData *adt, bContext *C, FCurve *src_ #else /* helper function for Keying Set -> keying settings */ -/* TODO: use reg option! */ -static void rna_def_common_keying_flags(StructRNA *srna, short UNUSED(reg)) +static void rna_def_common_keying_flags(StructRNA *srna, short reg) { PropertyRNA *prop; - - prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "keyingflag"); - RNA_def_property_enum_items(prop, keying_flag_items); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); - RNA_def_property_ui_text(prop, "Options", "Keying set options"); + + /* override scene/userpref defaults? */ + prop = RNA_def_property(srna, "use_insertkey_override_needed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "keyingoverride", INSERTKEY_NEEDED); + RNA_def_property_ui_text(prop, "Override Insert Keyframes Default- Only Needed", + "Override default setting to only insert keyframes where they're needed in the relevant F-Curves"); + if (reg) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + + prop = RNA_def_property(srna, "use_insertkey_override_visual", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "keyingoverride", INSERTKEY_MATRIX); + RNA_def_property_ui_text(prop, "Override Insert Keyframes Default - Visual", + "Override default setting to insert keyframes based on 'visual transforms'"); + if (reg) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + + prop = RNA_def_property(srna, "use_insertkey_override_xyz_to_rgb", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "keyingoverride", INSERTKEY_XYZ2RGB); + RNA_def_property_ui_text(prop, "Override F-Curve Colors - XYZ to RGB", + "Override default setting to set color for newly added transformation F-Curves (Location, Rotation, Scale) " + "to be based on the transform axis"); + if (reg) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + + + /* value to override defaults with */ + prop = RNA_def_property(srna, "use_insertkey_needed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "keyingflag", INSERTKEY_NEEDED); + RNA_def_property_ui_text(prop, "Insert Keyframes - Only Needed", "Only insert keyframes where they're needed in the relevant F-Curves"); + if (reg) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + + prop = RNA_def_property(srna, "use_insertkey_visual", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "keyingflag", INSERTKEY_MATRIX); + RNA_def_property_ui_text(prop, "Insert Keyframes - Visual", "Insert keyframes based on 'visual transforms'"); + if (reg) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + + prop = RNA_def_property(srna, "use_insertkey_xyz_to_rgb", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "keyingflag", INSERTKEY_XYZ2RGB); + RNA_def_property_ui_text(prop, "F-Curve Colors - XYZ to RGB", "Color for newly added transformation F-Curves (Location, Rotation, Scale) is based on the transform axis"); + if (reg) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); } /* --- */ @@ -610,7 +640,20 @@ static void rna_def_keyingset_info(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_ui_text(prop, "Description", "A short description of the keying set"); - rna_def_common_keying_flags(srna, 1); /* '1' arg here is to indicate that we need these to be set on registering */ + /* Regarding why we don't use rna_def_common_keying_flags() here: + * - Using it would keep this case in sync with the other places + * where these options are exposed (which are optimised for being + * used in the UI). + * - Unlike all the other places, this case is used for defining + * new "built in" Keying Sets via the Python API. In that case, + * it makes more sense to expose these in a way more similar to + * other places featuring bl_idname/label/description (i.e. operators) + */ + prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "keyingflag"); + RNA_def_property_enum_items(prop, keying_flag_items); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); + RNA_def_property_ui_text(prop, "Options", "Keying Set options to use when inserting keyframes"); RNA_define_verify_sdna(1); diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index be973ab33f8..5704004b4b6 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -137,7 +137,7 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_enum_items(prop, prop_draw_type_extra_items); RNA_def_property_flag(prop, PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Composition Guides", "Draw overlay"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); prop = RNA_def_property(srna, "sensor_fit", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "sensor_fit"); @@ -150,7 +150,7 @@ void RNA_def_camera(BlenderRNA *brna) prop = RNA_def_property(srna, "passepartout_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "passepartalpha"); RNA_def_property_ui_text(prop, "Passepartout Alpha", "Opacity (alpha) of the darkened overlay in Camera view"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); prop = RNA_def_property(srna, "angle_x", PROP_FLOAT, PROP_ANGLE); RNA_def_property_range(prop, DEG2RAD(0.367), DEG2RAD(172.847)); @@ -256,22 +256,27 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOWPASSEPARTOUT); RNA_def_property_ui_text(prop, "Show Passepartout", "Show a darkened overlay outside the image area in Camera view"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); - prop = RNA_def_property(srna, "show_title_safe", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOWTITLESAFE); - RNA_def_property_ui_text(prop, "Show Safe Areas", "Show TV title safe and action safe zones in Camera view"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + prop = RNA_def_property(srna, "show_safe_areas", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOW_SAFE_MARGINS); + RNA_def_property_ui_text(prop, "Show Safe Areas", "Show TV title safe and action safe areas in Camera view"); + RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); + + prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOW_SAFE_CENTER); + RNA_def_property_ui_text(prop, "Show Center-cut safe areas", "Show safe areas to fit content in a different aspect ratio"); + RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); prop = RNA_def_property(srna, "show_name", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOWNAME); RNA_def_property_ui_text(prop, "Show Name", "Show the active Camera's name in Camera view"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); prop = RNA_def_property(srna, "show_sensor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOWSENSOR); RNA_def_property_ui_text(prop, "Show Sensor Size", "Show sensor size (film gate) in Camera view"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); prop = RNA_def_property(srna, "lens_unit", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c index cecc39c8e15..991020a1574 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -38,6 +38,8 @@ #include "BKE_cloth.h" #include "BKE_modifier.h" +#include "BPH_mass_spring.h" + #include "WM_api.h" #include "WM_types.h" @@ -268,6 +270,64 @@ static char *rna_ClothCollisionSettings_path(PointerRNA *ptr) #else +static void rna_def_cloth_solver_result(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem status_items[] = { + {BPH_SOLVER_SUCCESS, "SUCCESS", 0, "Success", "Computation was successful"}, + {BPH_SOLVER_NUMERICAL_ISSUE, "NUMERICAL_ISSUE", 0, "Numerical Issue", "The provided data did not satisfy the prerequisites"}, + {BPH_SOLVER_NO_CONVERGENCE, "NO_CONVERGENCE", 0, "No Convergence", "Iterative procedure did not converge"}, + {BPH_SOLVER_INVALID_INPUT, "INVALID_INPUT", 0, "Invalid Input", "The inputs are invalid, or the algorithm has been improperly called"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "ClothSolverResult", NULL); + RNA_def_struct_ui_text(srna, "Solver Result", "Result of cloth solver iteration"); + + RNA_define_verify_sdna(0); + + prop = RNA_def_property(srna, "status", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, status_items); + RNA_def_property_enum_sdna(prop, NULL, "status"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Status", "Status of the solver iteration"); + + prop = RNA_def_property(srna, "max_error", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "max_error"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Maximum Error", "Maximum error during substeps"); + + prop = RNA_def_property(srna, "min_error", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "min_error"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Minimum Error", "Minimum error during substeps"); + + prop = RNA_def_property(srna, "avg_error", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "avg_error"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Average Error", "Average error during substeps"); + + prop = RNA_def_property(srna, "max_iterations", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "max_iterations"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Maximum Iterations", "Maximum iterations during substeps"); + + prop = RNA_def_property(srna, "min_iterations", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "min_iterations"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Minimum Iterations", "Minimum iterations during substeps"); + + prop = RNA_def_property(srna, "avg_iterations", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "avg_iterations"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Average Iterations", "Average iterations during substeps"); + + RNA_define_verify_sdna(1); +} + static void rna_def_cloth_sim_settings(BlenderRNA *brna) { StructRNA *srna; @@ -325,6 +385,18 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Collider Friction", ""); RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "density_target", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "density_target"); + RNA_def_property_range(prop, 0.0f, 10000.0f); + RNA_def_property_ui_text(prop, "Target Density", "Maximum density of hair"); + RNA_def_property_update(prop, 0, "rna_cloth_update"); + + prop = RNA_def_property(srna, "density_strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "density_strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Target Density Strength", "Influence of target density on the simulation"); + RNA_def_property_update(prop, 0, "rna_cloth_update"); + /* mass */ prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_NONE); @@ -375,7 +447,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "quality", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "stepsPerFrame"); - RNA_def_property_range(prop, 4, 80); + RNA_def_property_range(prop, 1, 80); RNA_def_property_ui_text(prop, "Quality", "Quality of the simulation in steps per frame (higher is better quality but slower)"); RNA_def_property_update(prop, 0, "rna_cloth_update"); @@ -398,6 +470,13 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shrink Factor Max", "Max amount to shrink cloth by"); RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "voxel_cell_size", PROP_FLOAT, PROP_UNSIGNED); + RNA_def_property_float_sdna(prop, NULL, "voxel_cell_size"); + RNA_def_property_range(prop, 0.0001f, 10000.0f); + RNA_def_property_float_default(prop, 0.1f); + RNA_def_property_ui_text(prop, "Voxel Grid Cell Size", "Size of the voxel grid cells for interaction effects"); + RNA_def_property_update(prop, 0, "rna_cloth_update"); + /* springs */ prop = RNA_def_property(srna, "use_stiffness_scale", PROP_BOOLEAN, PROP_NONE); @@ -456,6 +535,13 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Bending Stiffness Maximum", "Maximum bending stiffness value"); RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "bending_damping", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "bending_damping"); + RNA_def_property_range(prop, 0.0f, 1000.0f); + RNA_def_property_ui_text(prop, "Bending Spring Damping", + "Damping of bending motion"); + RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "use_sewing_springs", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", CLOTH_SIMSETTINGS_FLAG_SEW); RNA_def_property_ui_text(prop, "Sew Cloth", "Pulls loose edges together"); @@ -577,6 +663,13 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Friction", "Friction force if a collision happened (higher = less movement)"); RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "damping", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "damping"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Restitution", "Amount of velocity lost on collision"); + RNA_def_property_update(prop, 0, "rna_cloth_update"); + prop = RNA_def_property(srna, "collision_quality", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "loop_count"); RNA_def_property_range(prop, 1, 20); @@ -625,6 +718,7 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna) void RNA_def_cloth(BlenderRNA *brna) { + rna_def_cloth_solver_result(brna); rna_def_cloth_sim_settings(brna); rna_def_cloth_collision_settings(brna); } diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 37201eca5f6..5e114a51ceb 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -46,6 +46,8 @@ #include "DNA_material_types.h" #include "DNA_movieclip_types.h" #include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "DNA_sequence_types.h" #include "MEM_guardedalloc.h" @@ -345,6 +347,13 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * WM_main_add_notifier(NC_LINESTYLE, linestyle); break; } + case ID_PA: + { + ParticleSettings *part = ptr->id.data; + + DAG_id_tag_update(&part->id, OB_RECALC_DATA | PSYS_RECALC_REDO); + WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, part); + } default: break; } diff --git a/source/blender/makesrna/intern/rna_controller.c b/source/blender/makesrna/intern/rna_controller.c index 8b5074eaf0d..ba0214d36ec 100644 --- a/source/blender/makesrna/intern/rna_controller.c +++ b/source/blender/makesrna/intern/rna_controller.c @@ -85,15 +85,10 @@ static StructRNA *rna_Controller_refine(struct PointerRNA *ptr) static void rna_Constroller_name_set(PointerRNA *ptr, const char *value) { - bController *cont = (bController *)ptr->data; - + Object *ob = ptr->id.data; + bController *cont = ptr->data; BLI_strncpy_utf8(cont->name, value, sizeof(cont->name)); - - if (ptr->id.data) { - Object *ob = (Object *)ptr->id.data; - BLI_uniquename(&ob->controllers, cont, DATA_("Controller"), '.', offsetof(bController, name), - sizeof(cont->name)); - } + BLI_uniquename(&ob->controllers, cont, DATA_("Controller"), '.', offsetof(bController, name), sizeof(cont->name)); } static void rna_Controller_type_set(struct PointerRNA *ptr, int value) diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 54c08009792..6db1b46ff21 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -65,15 +65,15 @@ BlenderDefRNA DefRNA = {NULL, {NULL, NULL}, {NULL, NULL}, NULL, 0, 0, 0, 1, 1}; /* Duplicated code since we can't link in blenkernel or blenlib */ -/* pedantic check for '.', do this since its a hassle for translators */ +/* pedantic check for final '.', note '...' are allowed though. */ #ifndef NDEBUG -# define DESCR_CHECK(description, id1, id2) \ - if (description && (description)[0]) { \ - int i = strlen(description); \ - if ((description)[i - 1] == '.') { \ - fprintf(stderr, "%s: '%s' '%s' description ends with a '.' !\n", \ - __func__, id1 ? id1 : "", id2 ? id2 : ""); \ - } \ +# define DESCR_CHECK(description, id1, id2) \ + if (description && (description)[0]) { \ + int i = strlen(description); \ + if (i > 3 && (description)[i - 1] == '.' && (description)[i - 3] != '.') { \ + fprintf(stderr, "%s: '%s' '%s' description ends with a '.' !\n", \ + __func__, id1 ? id1 : "", id2 ? id2 : ""); \ + } \ } (void)0 #else @@ -109,7 +109,7 @@ PropertyDefRNA *rna_findlink(ListBase *listbase, const char *identifier) for (link = listbase->first; link; link = link->next) { PropertyRNA *prop = ((PropertyDefRNA *)link)->prop; - if (prop && (strcmp(prop->identifier, identifier) == 0)) { + if (prop && (STREQ(prop->identifier, identifier))) { return (PropertyDefRNA *)link; } } @@ -428,7 +428,7 @@ static int rna_validate_identifier(const char *identifier, char *error, bool pro } for (a = 0; kwlist[a]; a++) { - if (strcmp(identifier, kwlist[a]) == 0) { + if (STREQ(identifier, kwlist[a])) { strcpy(error, "this keyword is reserved by python"); return 0; } @@ -442,7 +442,7 @@ static int rna_validate_identifier(const char *identifier, char *error, bool pro }; for (a = 0; kwlist_prop[a]; a++) { - if (strcmp(identifier, kwlist_prop[a]) == 0) { + if (STREQ(identifier, kwlist_prop[a])) { strcpy(error, "this keyword is reserved by python"); return 0; } @@ -496,7 +496,7 @@ void RNA_identifier_sanitize(char *identifier, int property) } for (a = 0; kwlist[a]; a++) { - if (strcmp(identifier, kwlist[a]) == 0) { + if (STREQ(identifier, kwlist[a])) { /* this keyword is reserved by python. * just replace the last character by '_' to keep it readable. */ @@ -513,7 +513,7 @@ void RNA_identifier_sanitize(char *identifier, int property) }; for (a = 0; kwlist_prop[a]; a++) { - if (strcmp(identifier, kwlist_prop[a]) == 0) { + if (STREQ(identifier, kwlist_prop[a])) { /* this keyword is reserved by python. * just replace the last character by '_' to keep it readable. */ @@ -814,7 +814,7 @@ StructRNA *RNA_def_struct(BlenderRNA *brna, const char *identifier, const char * if (from) { /* find struct to derive from */ for (srnafrom = brna->structs.first; srnafrom; srnafrom = srnafrom->cont.next) - if (strcmp(srnafrom->identifier, from) == 0) + if (STREQ(srnafrom->identifier, from)) break; if (!srnafrom) { @@ -896,7 +896,7 @@ void RNA_def_struct_nested(BlenderRNA *brna, StructRNA *srna, const char *struct /* find struct to derive from */ for (srnafrom = brna->structs.first; srnafrom; srnafrom = srnafrom->cont.next) - if (strcmp(srnafrom->identifier, structname) == 0) + if (STREQ(srnafrom->identifier, structname)) break; if (!srnafrom) { @@ -1871,15 +1871,15 @@ void RNA_def_property_int_sdna(PropertyRNA *prop, const char *structname, const } /* SDNA doesn't pass us unsigned unfortunately .. */ - if (dp->dnatype && strcmp(dp->dnatype, "char") == 0) { + if (dp->dnatype && STREQ(dp->dnatype, "char")) { iprop->hardmin = iprop->softmin = CHAR_MIN; iprop->hardmax = iprop->softmax = CHAR_MAX; } - else if (dp->dnatype && strcmp(dp->dnatype, "short") == 0) { + else if (dp->dnatype && STREQ(dp->dnatype, "short")) { iprop->hardmin = iprop->softmin = SHRT_MIN; iprop->hardmax = iprop->softmax = SHRT_MAX; } - else if (dp->dnatype && strcmp(dp->dnatype, "int") == 0) { + else if (dp->dnatype && STREQ(dp->dnatype, "int")) { iprop->hardmin = INT_MIN; iprop->hardmax = INT_MAX; @@ -1923,7 +1923,7 @@ void RNA_def_property_float_sdna(PropertyRNA *prop, const char *structname, cons } } - if (dp->dnatype && strcmp(dp->dnatype, "char") == 0) { + if (dp->dnatype && STREQ(dp->dnatype, "char")) { fprop->hardmin = fprop->softmin = 0.0f; fprop->hardmax = fprop->softmax = 1.0f; } @@ -2060,7 +2060,7 @@ void RNA_def_property_collection_sdna(PropertyRNA *prop, const char *structname, } } - if (dp->dnatype && strcmp(dp->dnatype, "ListBase") == 0) { + if (dp->dnatype && STREQ(dp->dnatype, "ListBase")) { cprop->next = (PropCollectionNextFunc)"rna_iterator_listbase_next"; cprop->get = (PropCollectionGetFunc)"rna_iterator_listbase_get"; cprop->end = (PropCollectionEndFunc)"rna_iterator_listbase_end"; @@ -3427,7 +3427,7 @@ int RNA_def_property_free_identifier(StructOrFunctionRNA *cont_, const char *ide PropertyRNA *prop; for (prop = cont->properties.first; prop; prop = prop->next) { - if (strcmp(prop->identifier, identifier) == 0) { + if (STREQ(prop->identifier, identifier)) { if (prop->flag & PROP_RUNTIME) { rna_def_property_free(cont_, prop); return 1; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 3b9078153f3..6c893255601 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1948,6 +1948,10 @@ static void rna_def_fcurve(BlenderRNA *brna) parm = RNA_def_pointer(func, "data", "AnyType", "Data", "Data containing the property controlled by given FCurve"); RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL); + + + /* Functions */ + RNA_api_fcurves(srna); } /* *********************** */ diff --git a/source/blender/makesrna/intern/rna_fcurve_api.c b/source/blender/makesrna/intern/rna_fcurve_api.c index ab96f6f384d..8551ca609f4 100644 --- a/source/blender/makesrna/intern/rna_fcurve_api.c +++ b/source/blender/makesrna/intern/rna_fcurve_api.c @@ -39,6 +39,7 @@ #include "RNA_define.h" #include "DNA_anim_types.h" +#include "DNA_scene_types.h" #include "rna_internal.h" /* own include */ @@ -49,8 +50,115 @@ #include "BKE_animsys.h" #include "BKE_fcurve.h" +#include "BLI_math.h" + +static void rna_FCurve_convert_to_samples(FCurve *fcu, ReportList *reports, int start, int end) +{ + /* XXX fcurve_store_samples uses end frame included, which is not consistent with usual behavior in Blender, + * nor python slices, etc. Let have public py API be consistent here at least. */ + end--; + if (start > end) { + BKE_reportf(reports, RPT_ERROR, "Invalid frame range (%d - %d)", start, end + 1); + } + else if (fcu->fpt) { + BKE_report(reports, RPT_WARNING, "FCurve has already sample points"); + } + else if (!fcu->bezt) { + BKE_report(reports, RPT_WARNING, "FCurve has no keyframes"); + } + else { + fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve); + WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + } +} + +static void rna_FCurve_convert_to_keyframes(FCurve *fcu, ReportList *reports, int start, int end) +{ + if (start >= end) { + BKE_reportf(reports, RPT_ERROR, "Invalid frame range (%d - %d)", start, end); + } + else if (fcu->bezt) { + BKE_report(reports, RPT_WARNING, "FCurve has already keyframes"); + } + else if (!fcu->fpt) { + BKE_report(reports, RPT_WARNING, "FCurve has no sample points"); + } + else { + BezTriple *bezt; + FPoint *fpt = fcu->fpt; + int tot_kf = end - start; + int tot_sp = fcu->totvert; + + bezt = fcu->bezt = MEM_callocN(sizeof(*fcu->bezt) * (size_t)tot_kf, __func__); + fcu->totvert = tot_kf; + + /* Get first sample point to 'copy' as keyframe. */ + for (; tot_sp && (fpt->vec[0] < (float)start); fpt++, tot_sp--); + + /* Add heading dummy flat points if needed. */ + for (; tot_kf && (fpt->vec[0] > (float)start); start++, bezt++, tot_kf--) { + /* Linear interpolation, of course. */ + bezt->f1 = bezt->f2 = bezt->f3 = SELECT; + bezt->ipo = BEZT_IPO_LIN; + bezt->h1 = bezt->h2 = HD_AUTO_ANIM; + bezt->vec[1][0] = (float)start; + bezt->vec[1][1] = fpt->vec[1]; + } + + /* Copy actual sample points. */ + for (; tot_kf && tot_sp; start++, bezt++, tot_kf--, fpt++, tot_sp--) { + /* Linear interpolation, of course. */ + bezt->f1 = bezt->f2 = bezt->f3 = SELECT; + bezt->ipo = BEZT_IPO_LIN; + bezt->h1 = bezt->h2 = HD_AUTO_ANIM; + copy_v2_v2(bezt->vec[1], fpt->vec); + } + + /* Add leading dummy flat points if needed. */ + for (fpt--; tot_kf; start++, bezt++, tot_kf--) { + /* Linear interpolation, of course. */ + bezt->f1 = bezt->f2 = bezt->f3 = SELECT; + bezt->ipo = BEZT_IPO_LIN; + bezt->h1 = bezt->h2 = HD_AUTO_ANIM; + bezt->vec[1][0] = (float)start; + bezt->vec[1][1] = fpt->vec[1]; + } + + MEM_SAFE_FREE(fcu->fpt); + + /* Not strictly needed since we use linear interpolation, but better be consistent here. */ + calchandles_fcurve(fcu); + WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + } +} + #else +void RNA_api_fcurves(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + + func = RNA_def_function(srna, "convert_to_samples", "rna_FCurve_convert_to_samples"); + RNA_def_function_ui_description(func, + "Convert current FCurve from keyframes to sample points, if necessary"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_int(func, "start", 0, MINAFRAME, MAXFRAME, "Start Frame", "", MINAFRAME, MAXFRAME); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_int(func, "end", 0, MINAFRAME, MAXFRAME, "End Frame", "", MINAFRAME, MAXFRAME); + RNA_def_property_flag(parm, PROP_REQUIRED); + + func = RNA_def_function(srna, "convert_to_keyframes", "rna_FCurve_convert_to_keyframes"); + RNA_def_function_ui_description(func, + "Convert current FCurve from sample points to keyframes (linear interpolation), " + "if necessary"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_int(func, "start", 0, MINAFRAME, MAXFRAME, "Start Frame", "", MINAFRAME, MAXFRAME); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_int(func, "end", 0, MINAFRAME, MAXFRAME, "End Frame", "", MINAFRAME, MAXFRAME); + RNA_def_property_flag(parm, PROP_REQUIRED); +} + void RNA_api_drivers(StructRNA *UNUSED(srna)) { /* FunctionRNA *func; */ diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index b1935b34251..5ab6d1a231b 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -193,7 +193,7 @@ static int rna_Image_file_format_get(PointerRNA *ptr) { Image *image = (Image *)ptr->data; ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); - int imtype = BKE_ftype_to_imtype(ibuf ? ibuf->ftype : 0); + int imtype = BKE_image_ftype_to_imtype(ibuf ? ibuf->ftype : 0); BKE_image_release_ibuf(image, ibuf, NULL); @@ -204,7 +204,7 @@ static void rna_Image_file_format_set(PointerRNA *ptr, int value) { Image *image = (Image *)ptr->data; if (BKE_imtype_is_movie(value) == 0) { /* should be able to throw an error here */ - int ftype = BKE_imtype_to_ftype(value); + int ftype = BKE_image_imtype_to_ftype(value); BKE_image_file_format_set(image, ftype); } } @@ -640,6 +640,11 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Alpha", "Use the alpha channel information from the image or make image fully opaque"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_colormanage_update"); + prop = RNA_def_property(srna, "use_deinterlace", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_DEINTERLACE); + RNA_def_property_ui_text(prop, "Deinterlace", "Deinterlace movie file on load"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_reload_update"); + prop = RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_Image_dirty_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 90f90ea8632..5fc65ea385c 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -52,8 +52,6 @@ #include "BKE_packedFile.h" #include "BKE_main.h" -#include "BKE_global.h" /* grr: G.main->name */ - #include "IMB_imbuf.h" #include "IMB_colormanagement.h" @@ -109,13 +107,13 @@ static void rna_Image_save_render(Image *image, bContext *C, ReportList *reports } } -static void rna_Image_save(Image *image, bContext *C, ReportList *reports) +static void rna_Image_save(Image *image, Main *bmain, bContext *C, ReportList *reports) { ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); if (ibuf) { char filename[FILE_MAX]; BLI_strncpy(filename, image->name, sizeof(filename)); - BLI_path_abs(filename, ID_BLEND_PATH(G.main, &image->id)); + BLI_path_abs(filename, ID_BLEND_PATH(bmain, &image->id)); if (image->packedfile) { if (writePackedFile(reports, image->name, image->packedfile, 0) != RET_OK) { @@ -144,7 +142,9 @@ static void rna_Image_save(Image *image, bContext *C, ReportList *reports) WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, image); } -static void rna_Image_pack(Image *image, bContext *C, ReportList *reports, int as_png) +static void rna_Image_pack( + Image *image, Main *bmain, bContext *C, ReportList *reports, + int as_png, const char *data, int data_len) { ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); @@ -152,11 +152,20 @@ static void rna_Image_pack(Image *image, bContext *C, ReportList *reports, int a BKE_report(reports, RPT_ERROR, "Cannot pack edited image from disk, only as internal PNG"); } else { + if (image->packedfile) { + freePackedFile(image->packedfile); + image->packedfile = NULL; + } if (as_png) { BKE_image_memorypack(image); } + else if (data) { + char *data_dup = MEM_mallocN(sizeof(*data_dup) * (size_t)data_len, __func__); + memcpy(data_dup, data, (size_t)data_len); + image->packedfile = newPackedFileMemory(data_dup, data_len); + } else { - image->packedfile = newPackedFile(reports, image->name, ID_BLEND_PATH(G.main, &image->id)); + image->packedfile = newPackedFile(reports, image->name, ID_BLEND_PATH(bmain, &image->id)); } } @@ -301,12 +310,16 @@ void RNA_api_image(StructRNA *srna) func = RNA_def_function(srna, "save", "rna_Image_save"); RNA_def_function_ui_description(func, "Save image to its source path"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_CONTEXT | FUNC_USE_REPORTS); func = RNA_def_function(srna, "pack", "rna_Image_pack"); RNA_def_function_ui_description(func, "Pack an image as embedded data into the .blend file"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_CONTEXT | FUNC_USE_REPORTS); RNA_def_boolean(func, "as_png", 0, "as_png", "Pack the image as PNG (needed for generated/dirty images)"); + parm = RNA_def_property(func, "data", PROP_STRING, PROP_BYTESTRING); + RNA_def_property_ui_text(parm, "data", "Raw data (bytes, exact content of the embedded file)"); + RNA_def_int(func, "data_len", 0, 0, INT_MAX, + "data_len", "length of given data (mandatory if data is provided)", 0, INT_MAX); func = RNA_def_function(srna, "unpack", "rna_Image_unpack"); RNA_def_function_ui_description(func, "Save an image packed in the .blend file to disk"); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 2c00b70d1a9..e4aaa705fe0 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -260,6 +260,7 @@ void RNA_api_armature_edit_bone(StructRNA *srna); void RNA_api_bone(StructRNA *srna); void RNA_api_camera(StructRNA *srna); void RNA_api_curve(StructRNA *srna); +void RNA_api_fcurves(StructRNA *srna); void RNA_api_drivers(StructRNA *srna); void RNA_api_image(struct StructRNA *srna); void RNA_api_lattice(struct StructRNA *srna); @@ -297,6 +298,8 @@ void RNA_api_texture(struct StructRNA *srna); void RNA_api_environment_map(struct StructRNA *srna); void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop); void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop); +void RNA_api_sound(struct StructRNA *srna); +void RNA_api_vfont(struct StructRNA *srna); /* main collection functions */ void RNA_def_main_cameras(BlenderRNA *brna, PropertyRNA *cprop); diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 2e4f24fc0ce..7a06b647ae3 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -181,7 +181,7 @@ static void rna_Material_active_paint_texture_index_update(Main *bmain, Scene *s bScreen *sc; Material *ma = ptr->id.data; - if (ma->use_nodes && ma->nodetree && BKE_scene_use_new_shading_nodes(scene)) { + if (ma->use_nodes && ma->nodetree) { struct bNode *node; int index = 0; for (node = ma->nodetree->nodes.first; node; node = node->next) { diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 3d6eab2bc88..44bae770186 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -207,6 +207,11 @@ static void rna_MeshAnyLayer_name_set(PointerRNA *ptr, const char *value) rna_cd_layer_name_set(cd, (CustomDataLayer *)ptr->data, value); } +static int rna_Mesh_has_custom_normals_get(PointerRNA *ptr) +{ + Mesh *me = ptr->data; + return (int)BKE_mesh_has_custom_loop_normals(me); +} /* -------------------------------------------------------------------- */ /* Update Callbacks */ @@ -338,6 +343,17 @@ static void rna_MeshLoop_normal_get(PointerRNA *ptr, float *values) } } +static void rna_MeshLoop_normal_set(PointerRNA *ptr, const float *values) +{ + Mesh *me = rna_mesh(ptr); + MLoop *ml = (MLoop *)ptr->data; + float (*vec)[3] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_NORMAL); + + if (vec) { + normalize_v3_v3(*vec, values); + } +} + static void rna_MeshLoop_tangent_get(PointerRNA *ptr, float *values) { Mesh *me = rna_mesh(ptr); @@ -1974,8 +1990,7 @@ static void rna_def_mloop(BlenderRNA *brna) prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_DIRECTION); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, -1.0f, 1.0f); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_float_funcs(prop, "rna_MeshLoop_normal_get", NULL, NULL); + RNA_def_property_float_funcs(prop, "rna_MeshLoop_normal_get", "rna_MeshLoop_normal_set", NULL); RNA_def_property_ui_text(prop, "Normal", "Local space unit length split normal vector of this vertex for this polygon " "(must be computed beforehand using calc_normals_split or calc_tangents)"); @@ -3186,8 +3201,8 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_AUTOSMOOTH); RNA_def_property_ui_text(prop, "Auto Smooth", - "Treat all set-smoothed faces with angles less than the specified angle " - "as 'smooth', unless they are linked by a sharp edge"); + "Auto smooth (based on smooth/sharp faces/edges and angle between faces), " + "or use custom split normals data if available"); RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); prop = RNA_def_property(srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); @@ -3196,9 +3211,19 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f)); RNA_def_property_ui_range(prop, DEG2RADF(0.0f), DEG2RADF(180.0f), 1.0, 1); RNA_def_property_ui_text(prop, "Auto Smooth Angle", - "Maximum angle between face normals that 'Auto Smooth' will operate on"); + "Maximum angle between face normals that will be considered as smooth " + "(unused if custom split normals data are available)"); RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); + RNA_define_verify_sdna(false); + prop = RNA_def_property(srna, "has_custom_normals", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "", 0); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Has Custom Normals", + "True if there are custom split normals data in this mesh"); + RNA_def_property_boolean_funcs(prop, "rna_Mesh_has_custom_normals_get", NULL); + RNA_define_verify_sdna(true); + prop = RNA_def_property(srna, "show_double_sided", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_TWOSIDED); RNA_def_property_ui_text(prop, "Double Sided", "Render/display the mesh with double or single sided lighting"); diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index cc1f57d8a14..53704e24bff 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -61,10 +61,19 @@ static const char *rna_Mesh_unit_test_compare(struct Mesh *mesh, struct Mesh *me return ret; } -static void rna_Mesh_calc_normals_split(Mesh *mesh, float min_angle) +static void rna_Mesh_create_normals_split(Mesh *mesh) +{ + if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { + CustomData_add_layer(&mesh->ldata, CD_NORMAL, CD_CALLOC, NULL, mesh->totloop); + CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); + } +} + +static void rna_Mesh_calc_normals_split(Mesh *mesh) { float (*r_loopnors)[3]; float (*polynors)[3]; + short (*clnors)[2] = NULL; bool free_polynors = false; if (CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { @@ -76,6 +85,9 @@ static void rna_Mesh_calc_normals_split(Mesh *mesh, float min_angle) CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); } + /* may be NULL */ + clnors = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); + if (CustomData_has_layer(&mesh->pdata, CD_NORMAL)) { /* This assume that layer is always up to date, not sure this is the case (esp. in Edit mode?)... */ polynors = CustomData_get_layer(&mesh->pdata, CD_NORMAL); @@ -88,9 +100,10 @@ static void rna_Mesh_calc_normals_split(Mesh *mesh, float min_angle) free_polynors = true; } - BKE_mesh_normals_loop_split(mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge, - mesh->mloop, r_loopnors, mesh->totloop, mesh->mpoly, polynors, mesh->totpoly, - min_angle); + BKE_mesh_normals_loop_split( + mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge, + mesh->mloop, r_loopnors, mesh->totloop, mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly, + (mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, NULL, clnors, NULL); if (free_polynors) { MEM_freeN(polynors); @@ -117,7 +130,7 @@ static void rna_Mesh_calc_tangents(Mesh *mesh, ReportList *reports, const char * /* Compute loop normals if needed. */ if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { - rna_Mesh_calc_normals_split(mesh, (float)M_PI); + rna_Mesh_calc_normals_split(mesh); } BKE_mesh_loop_tangents(mesh, uvmap, r_looptangents, reports); @@ -139,6 +152,78 @@ static void rna_Mesh_calc_smooth_groups(Mesh *mesh, int use_bitflags, int *r_pol r_group_total, use_bitflags); } +static void rna_Mesh_normals_split_custom_do(Mesh *mesh, float (*custom_loopnors)[3], const bool use_vertices) +{ + float (*polynors)[3]; + short (*clnors)[2]; + const int numloops = mesh->totloop; + bool free_polynors = false; + + clnors = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); + if (clnors) { + memset(clnors, 0, sizeof(*clnors) * numloops); + } + else { + clnors = CustomData_add_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, numloops); + } + + if (CustomData_has_layer(&mesh->pdata, CD_NORMAL)) { + polynors = CustomData_get_layer(&mesh->pdata, CD_NORMAL); + } + else { + polynors = MEM_mallocN(sizeof(float[3]) * mesh->totpoly, __func__); + BKE_mesh_calc_normals_poly(mesh->mvert, mesh->totvert, mesh->mloop, mesh->mpoly, mesh->totloop, mesh->totpoly, + polynors, false); + free_polynors = true; + } + + if (use_vertices) { + BKE_mesh_normals_loop_custom_from_vertices_set( + mesh->mvert, custom_loopnors, mesh->totvert, mesh->medge, mesh->totedge, mesh->mloop, mesh->totloop, + mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly, clnors); + } + else { + BKE_mesh_normals_loop_custom_set( + mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge, mesh->mloop, custom_loopnors, mesh->totloop, + mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly, clnors); + } + + if (free_polynors) { + MEM_freeN(polynors); + } +} + +static void rna_Mesh_normals_split_custom_set(Mesh *mesh, ReportList *reports, int normals_len, float *normals) +{ + float (*loopnors)[3] = (float (*)[3])normals; + const int numloops = mesh->totloop; + + if (normals_len != numloops * 3) { + BKE_reportf(reports, RPT_ERROR, + "Number of custom normals is not number of loops (%f / %d)", + (float)normals_len / 3.0f, numloops); + return; + } + + rna_Mesh_normals_split_custom_do(mesh, loopnors, false); +} + +static void rna_Mesh_normals_split_custom_set_from_vertices( + Mesh *mesh, ReportList *reports, int normals_len, float *normals) +{ + float (*vertnors)[3] = (float (*)[3])normals; + const int numverts = mesh->totvert; + + if (normals_len != numverts * 3) { + BKE_reportf(reports, RPT_ERROR, + "Number of custom normals is not number of vertices (%f / %d)", + (float)normals_len / 3.0f, numverts); + return; + } + + rna_Mesh_normals_split_custom_do(mesh, vertnors, true); +} + static void rna_Mesh_transform(Mesh *mesh, float *mat, int shape_keys) { BKE_mesh_transform(mesh, (float (*)[4])mat, shape_keys); @@ -152,6 +237,7 @@ void RNA_api_mesh(StructRNA *srna) { FunctionRNA *func; PropertyRNA *parm; + const int normals_array_dim[] = {1, 3}; func = RNA_def_function(srna, "transform", "rna_Mesh_transform"); RNA_def_function_ui_description(func, "Transform mesh vertices by a matrix"); @@ -162,12 +248,11 @@ void RNA_api_mesh(StructRNA *srna) func = RNA_def_function(srna, "calc_normals", "BKE_mesh_calc_normals"); RNA_def_function_ui_description(func, "Calculate vertex normals"); + func = RNA_def_function(srna, "create_normals_split", "rna_Mesh_create_normals_split"); + RNA_def_function_ui_description(func, "Empty split vertex normals"); + func = RNA_def_function(srna, "calc_normals_split", "rna_Mesh_calc_normals_split"); RNA_def_function_ui_description(func, "Calculate split vertex normals, which preserve sharp edges"); - parm = RNA_def_float(func, "split_angle", M_PI, 0.0f, M_PI, "", - "Angle between polys' normals above which an edge is always sharp (180° to disable)", - 0.0f, M_PI); - RNA_def_property_subtype(parm, (PropertySubType)PROP_UNIT_ROTATION); func = RNA_def_function(srna, "free_normals_split", "rna_Mesh_free_normals_split"); RNA_def_function_ui_description(func, "Free split vertex normals"); @@ -196,6 +281,26 @@ void RNA_api_mesh(StructRNA *srna) parm = RNA_def_int(func, "groups", 0, 0, INT_MAX, "groups", "Total number of groups", 0, INT_MAX); RNA_def_property_flag(parm, PROP_OUTPUT); + func = RNA_def_function(srna, "normals_split_custom_set", "rna_Mesh_normals_split_custom_set"); + RNA_def_function_ui_description(func, + "Define custom split normals of this mesh " + "(use zero-vectors to keep auto ones)"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + /* TODO, see how array size of 0 works, this shouldnt be used */ + parm = RNA_def_float_array(func, "normals", 1, NULL, -1.0f, 1.0f, "", "Normals", 0.0f, 0.0f); + RNA_def_property_multi_array(parm, 2, normals_array_dim); + RNA_def_property_flag(parm, PROP_DYNAMIC | PROP_REQUIRED); + + func = RNA_def_function(srna, "normals_split_custom_set_from_vertices", + "rna_Mesh_normals_split_custom_set_from_vertices"); + RNA_def_function_ui_description(func, + "Define custom split normals of this mesh, from vertices' normals " + "(use zero-vectors to keep auto ones)"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + /* TODO, see how array size of 0 works, this shouldnt be used */ + parm = RNA_def_float_array(func, "normals", 1, NULL, -1.0f, 1.0f, "", "Normals", 0.0f, 0.0f); + RNA_def_property_multi_array(parm, 2, normals_array_dim); + RNA_def_property_flag(parm, PROP_DYNAMIC | PROP_REQUIRED); func = RNA_def_function(srna, "update", "ED_mesh_update"); RNA_def_boolean(func, "calc_edges", 0, "Calculate Edges", "Force recalculation of edges"); @@ -211,7 +316,9 @@ void RNA_api_mesh(StructRNA *srna) func = RNA_def_function(srna, "validate", "BKE_mesh_validate"); RNA_def_function_ui_description(func, "Validate geometry, return True when the mesh has had " "invalid geometry corrected/removed"); - RNA_def_boolean(func, "verbose", 0, "Verbose", "Output information about the errors found"); + RNA_def_boolean(func, "verbose", false, "Verbose", "Output information about the errors found"); + RNA_def_boolean(func, "cleanup_cddata", true, "Cleanup CDData", + "Remove temp/cached cdlayers, like e.g. normals..."); parm = RNA_def_boolean(func, "result", 0, "Result", ""); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_mesh_utils.h b/source/blender/makesrna/intern/rna_mesh_utils.h index c0ea1a153ff..f68a48c075b 100644 --- a/source/blender/makesrna/intern/rna_mesh_utils.h +++ b/source/blender/makesrna/intern/rna_mesh_utils.h @@ -20,7 +20,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/source/blender/makesrna/intern/rna_mesh_utils.h +/** \file blender/makesrna/intern/rna_mesh_utils.h * \ingroup RNA */ diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index d8d27a06bb5..b9aad6f5b42 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -46,6 +46,7 @@ #include "BKE_data_transfer.h" #include "BKE_DerivedMesh.h" #include "BKE_dynamicpaint.h" +#include "BKE_effect.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_remap.h" #include "BKE_multires.h" @@ -62,9 +63,10 @@ EnumPropertyItem modifier_type_items[] = { {0, "", 0, N_("Modify"), ""}, - {eModifierType_DataTransfer, "DATA_TRANSFER", ICON_MOD_DATA_TRANSFER, "Data Transfer", ""}, + {eModifierType_DataTransfer, "DATA_TRANSFER", ICON_MOD_DATA_TRANSFER, "Data Transfer", ""}, {eModifierType_MeshCache, "MESH_CACHE", ICON_MOD_MESHDEFORM, "Mesh Cache", ""}, {eModifierType_PointCache, "POINT_CACHE", ICON_MOD_MESHDEFORM, "Point Cache", ""}, + {eModifierType_NormalEdit, "NORMAL_EDIT", ICON_MOD_NORMALEDIT, "Normal Edit", ""}, {eModifierType_UVProject, "UV_PROJECT", ICON_MOD_UVPROJECT, "UV Project", ""}, {eModifierType_UVWarp, "UV_WARP", ICON_MOD_UVPROJECT, "UV Warp", ""}, {eModifierType_WeightVGEdit, "VERTEX_WEIGHT_EDIT", ICON_MOD_VERTEX_WEIGHT, "Vertex Weight Edit", ""}, @@ -134,6 +136,22 @@ EnumPropertyItem modifier_triangulate_ngon_method_items[] = { {0, NULL, 0, NULL, NULL} }; +#ifndef RNA_RUNTIME +/* use eWarp_Falloff_*** & eHook_Falloff_***, they're in sync */ +static EnumPropertyItem modifier_warp_falloff_items[] = { + {eWarp_Falloff_None, "NONE", 0, "No Falloff", ""}, + {eWarp_Falloff_Curve, "CURVE", 0, "Curve", ""}, + {eWarp_Falloff_Smooth, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""}, + {eWarp_Falloff_Sphere, "SPHERE", ICON_SPHERECURVE, "Sphere", ""}, + {eWarp_Falloff_Root, "ROOT", ICON_ROOTCURVE, "Root", ""}, + {eWarp_Falloff_InvSquare, "INVERSE_SQUARE", ICON_ROOTCURVE, "Inverse Square", ""}, + {eWarp_Falloff_Sharp, "SHARP", ICON_SHARPCURVE, "Sharp", ""}, + {eWarp_Falloff_Linear, "LINEAR", ICON_LINCURVE, "Linear", ""}, + {eWarp_Falloff_Const, "CONSTANT", ICON_NOCURVE, "Constant", ""}, + {0, NULL, 0, NULL, NULL} +}; +#endif + /* ***** Data Transfer ***** */ EnumPropertyItem DT_method_vertex_items[] = { @@ -358,6 +376,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr) return &RNA_WireframeModifier; case eModifierType_DataTransfer: return &RNA_DataTransferModifier; + case eModifierType_NormalEdit: + return &RNA_NormalEditModifier; case eModifierType_PointCache: return &RNA_PointCacheModifier; /* Default */ @@ -434,6 +454,7 @@ RNA_MOD_VGROUP_NAME_SET(LaplacianSmooth, defgrp_name); RNA_MOD_VGROUP_NAME_SET(Lattice, name); RNA_MOD_VGROUP_NAME_SET(Mask, vgroup); RNA_MOD_VGROUP_NAME_SET(MeshDeform, defgrp_name); +RNA_MOD_VGROUP_NAME_SET(NormalEdit, defgrp_name); RNA_MOD_VGROUP_NAME_SET(Shrinkwrap, vgroup_name); RNA_MOD_VGROUP_NAME_SET(SimpleDeform, vgroup_name); RNA_MOD_VGROUP_NAME_SET(Smooth, defgrp_name); @@ -520,6 +541,7 @@ RNA_MOD_OBJECT_SET(DataTransfer, ob_source, OB_MESH); RNA_MOD_OBJECT_SET(Lattice, object, OB_LATTICE); RNA_MOD_OBJECT_SET(Mask, ob_arm, OB_ARMATURE); RNA_MOD_OBJECT_SET(MeshDeform, object, OB_MESH); +RNA_MOD_OBJECT_SET(NormalEdit, target, OB_EMPTY); RNA_MOD_OBJECT_SET(Shrinkwrap, target, OB_MESH); RNA_MOD_OBJECT_SET(Shrinkwrap, auxTarget, OB_MESH); @@ -613,7 +635,7 @@ static void rna_MultiresModifier_filepath_set(PointerRNA *ptr, const char *value Object *ob = (Object *)ptr->id.data; CustomDataExternal *external = ((Mesh *)ob->data)->ldata.external; - if (external && strcmp(external->filename, value)) { + if (external && !STREQ(external->filename, value)) { BLI_strncpy(external->filename, value, sizeof(external->filename)); multires_force_external_reload(ob); } @@ -1113,18 +1135,6 @@ static void rna_def_modifier_warp(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static EnumPropertyItem prop_falloff_items[] = { - {eWarp_Falloff_None, "NONE", 0, "No Falloff", ""}, - {eWarp_Falloff_Curve, "CURVE", 0, "Curve", ""}, - {eWarp_Falloff_Smooth, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""}, - {eWarp_Falloff_Sphere, "SPHERE", ICON_SPHERECURVE, "Sphere", ""}, - {eWarp_Falloff_Root, "ROOT", ICON_ROOTCURVE, "Root", ""}, - {eWarp_Falloff_Sharp, "SHARP", ICON_SHARPCURVE, "Sharp", ""}, - {eWarp_Falloff_Linear, "LINEAR", ICON_LINCURVE, "Linear", ""}, - {eWarp_Falloff_Const, "CONSTANT", ICON_NOCURVE, "Constant", ""}, - {0, NULL, 0, NULL, NULL} - }; - srna = RNA_def_struct(brna, "WarpModifier", "Modifier"); RNA_def_struct_ui_text(srna, "Warp Modifier", "Warp modifier"); RNA_def_struct_sdna(srna, "WarpModifierData"); @@ -1147,7 +1157,7 @@ static void rna_def_modifier_warp(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, prop_falloff_items); + RNA_def_property_enum_items(prop, modifier_warp_falloff_items); RNA_def_property_ui_text(prop, "Falloff Type", ""); RNA_def_property_translation_context(prop, BLF_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); @@ -1679,15 +1689,28 @@ static void rna_def_modifier_hook(BlenderRNA *brna) RNA_def_struct_sdna(srna, "HookModifierData"); RNA_def_struct_ui_icon(srna, ICON_HOOK); - prop = RNA_def_property(srna, "falloff", PROP_FLOAT, PROP_DISTANCE); + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "force"); + RNA_def_property_range(prop, 0, 1); + RNA_def_property_ui_text(prop, "Strength", "Relative force of the hook"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, modifier_warp_falloff_items); /* share the enum */ + RNA_def_property_ui_text(prop, "Falloff Type", ""); + RNA_def_property_translation_context(prop, BLF_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "falloff"); RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_range(prop, 0, 100, 100, 2); - RNA_def_property_ui_text(prop, "Falloff", "If not zero, the distance from the hook where influence ends"); + RNA_def_property_ui_text(prop, "Radius", "If not zero, the distance from the hook where influence ends"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); - prop = RNA_def_property(srna, "force", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 0, 1); - RNA_def_property_ui_text(prop, "Force", "Relative force of the hook"); + prop = RNA_def_property(srna, "falloff_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curfalloff"); + RNA_def_property_ui_text(prop, "Falloff Curve", "Custom Lamp Falloff Curve"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "center", PROP_FLOAT, PROP_NONE); @@ -1707,6 +1730,11 @@ static void rna_def_modifier_hook(BlenderRNA *brna) "Name of Parent Bone for hook (if applicable), also recalculates and clears offset"); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + prop = RNA_def_property(srna, "use_falloff_uniform", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_HOOK_UNIFORM_SPACE); + RNA_def_property_ui_text(prop, "Uniform Falloff", "Compensate for non-uniform object scale"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "name"); RNA_def_property_ui_text(prop, "Vertex Group", @@ -2468,9 +2496,29 @@ static void rna_def_modifier_cloth(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "coll_parms"); RNA_def_property_ui_text(prop, "Cloth Collision Settings", ""); + prop = RNA_def_property(srna, "solver_result", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ClothSolverResult"); + RNA_def_property_pointer_sdna(prop, NULL, "solver_result"); + RNA_def_property_ui_text(prop, "Solver Result", ""); + prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_ui_text(prop, "Point Cache", ""); + + prop = RNA_def_property(srna, "hair_grid_min", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "hair_grid_min"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Hair Grid Minimum", ""); + + prop = RNA_def_property(srna, "hair_grid_max", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "hair_grid_max"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Hair Grid Maximum", ""); + + prop = RNA_def_property(srna, "hair_grid_resolution", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "hair_grid_res"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Hair Grid Resolution", ""); } static void rna_def_modifier_smoke(BlenderRNA *brna) @@ -3158,24 +3206,24 @@ static void rna_def_modifier_uvwarp(BlenderRNA *brna) prop = RNA_def_property(srna, "object_from", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "object_src"); - RNA_def_property_ui_text(prop, "Target", "Object defining offset"); + RNA_def_property_ui_text(prop, "Object From", "Object defining offset"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); prop = RNA_def_property(srna, "bone_from", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "bone_src"); - RNA_def_property_ui_text(prop, "Sub-Target", "Bone defining offset"); + RNA_def_property_ui_text(prop, "Bone From", "Bone defining offset"); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); prop = RNA_def_property(srna, "object_to", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "object_dst"); - RNA_def_property_ui_text(prop, "Target", "Object defining offset"); + RNA_def_property_ui_text(prop, "Object To", "Object defining offset"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); prop = RNA_def_property(srna, "bone_to", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "bone_dst"); - RNA_def_property_ui_text(prop, "Sub-Target", "Bone defining offset"); + RNA_def_property_ui_text(prop, "Bone To", "Bone defining offset"); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); @@ -4108,6 +4156,7 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna) }; static EnumPropertyItem DT_layer_loop_items[] = { + {DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"}, {0, NULL, 0, NULL, NULL} }; static EnumPropertyItem DT_layer_loop_vcol_items[] = { @@ -4320,6 +4369,77 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); } +static void rna_def_modifier_normaledit(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem prop_mode_items[] = { + {MOD_NORMALEDIT_MODE_RADIAL, "RADIAL", 0, "Radial", + "From an ellipsoid (shape defined by the boundbox's dimensions, target is optional)"}, + {MOD_NORMALEDIT_MODE_DIRECTIONAL, "DIRECTIONAL", 0, "Directional", + "Normals 'track' (point to) the target object"}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem prop_mix_mode_items[] = { + {MOD_NORMALEDIT_MIX_COPY, "COPY", 0, "Copy", "Copy new normals (overwrite existing)"}, + {MOD_NORMALEDIT_MIX_ADD, "ADD", 0, "Add", "Copy sum of new and old normals"}, + {MOD_NORMALEDIT_MIX_SUB, "SUB", 0, "Subtract", "Copy new normals minus old normals"}, + {MOD_NORMALEDIT_MIX_MUL, "MUL", 0, "Multiply", "Copy product of old and new normals (*not* cross product)"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "NormalEditModifier", "Modifier"); + RNA_def_struct_ui_text(srna, "Normal Edit Modifier", "Modifier affecting/generating custom normals"); + RNA_def_struct_sdna(srna, "NormalEditModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_NORMALEDIT); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to affect (generate) normals"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_float_array(srna, "offset", 3, NULL, -FLT_MAX, FLT_MAX, "Offset", + "Offset from object's center", -100.0f, 100.0f); + RNA_def_property_subtype(prop, PROP_COORDS); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "mix_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_mix_mode_items); + RNA_def_property_ui_text(prop, "Mix Mode", "How to mix generated normals with existing ones"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_float(srna, "mix_factor", 1.0f, 0.0f, 1.0f, "Mix Factor", + "How much of generated normals to mix with exiting ones", 0.0f, 1.0f); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "defgrp_name"); + RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for selecting/weighting the affected areas"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_NormalEditModifier_defgrp_name_set"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_invert_vertex_group", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_NORMALEDIT_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Target", "Target object used to affect normals"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_NormalEditModifier_target_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "use_direction_parallel", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_NORMALEDIT_USE_DIRECTION_PARALLEL); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Parallel Normals", + "Use same direction for all normals, from origin to target's center " + "(Directional mode only)"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + static void rna_def_modifier_pointcache(BlenderRNA *brna) { StructRNA *srna; @@ -4451,6 +4571,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_laplaciandeform(brna); rna_def_modifier_wireframe(brna); rna_def_modifier_datatransfer(brna); + rna_def_modifier_normaledit(brna); rna_def_modifier_pointcache(brna); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index fa8d2669161..8d2a44136e8 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -3038,6 +3038,13 @@ static void def_frame(StructRNA *srna) { PropertyRNA *prop; + prop = RNA_def_property(srna, "text", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "id"); + RNA_def_property_struct_type(prop, "Text"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Text", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_struct_sdna_from(srna, "NodeFrame", "storage"); prop = RNA_def_property(srna, "shrink", PROP_BOOLEAN, PROP_NONE); @@ -3431,10 +3438,14 @@ static void def_sh_tex_image(StructRNA *srna) }; static const EnumPropertyItem prop_projection_items[] = { - {SHD_PROJ_FLAT, "FLAT", 0, "Flat", - "Image is projected flat using the X and Y coordinates of the texture vector"}, - {SHD_PROJ_BOX, "BOX", 0, "Box", - "Image is projected using different components for each side of the object space bounding box"}, + {SHD_PROJ_FLAT, "FLAT", 0, "Flat", + "Image is projected flat using the X and Y coordinates of the texture vector"}, + {SHD_PROJ_BOX, "BOX", 0, "Box", + "Image is projected using different components for each side of the object space bounding box"}, + {SHD_PROJ_SPHERE, "SPHERE", 0, "Sphere", + "Image is projected spherically using the Z axis as central"}, + {SHD_PROJ_TUBE, "TUBE", 0, "Tube", + "Image is projected from the tube using the Z axis as central"}, {0, NULL, 0, NULL, NULL} }; @@ -3444,7 +3455,7 @@ static void def_sh_tex_image(StructRNA *srna) {SHD_INTERP_CLOSEST, "Closest", 0, "Closest", "No interpolation (sample closest texel)"}, {SHD_INTERP_CUBIC, "Cubic", 0, "Cubic", - "Cubic interpolation (OSL only)"}, + "Cubic interpolation (CPU only)"}, {SHD_INTERP_SMART, "Smart", 0, "Smart", "Bicubic when magnifying, else bilinear (OSL only)"}, {0, NULL, 0, NULL, NULL} @@ -3643,7 +3654,14 @@ static void def_sh_tex_wave(StructRNA *srna) static void def_sh_tex_coord(StructRNA *srna) { PropertyRNA *prop; - + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "id"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Object", "Use coordinates from this object (for object texture coordinates output)"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + prop = RNA_def_property(srna, "from_dupli", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "custom1", 1); RNA_def_property_ui_text(prop, "From Dupli", "Use the parent of the dupli object if possible"); @@ -6213,6 +6231,21 @@ static void def_cmp_planetrackdeform(StructRNA *srna) RNA_def_property_string_sdna(prop, NULL, "plane_track_name"); RNA_def_property_ui_text(prop, "Plane Track", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "use_motion_blur", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR); + RNA_def_property_ui_text(prop, "Motion Blur", "Use multi-sampled motion blur of the mask"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "motion_blur_samples", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX); + RNA_def_property_ui_text(prop, "Samples", "Number of motion blur samples"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "motion_blur_shutter", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0f); + RNA_def_property_ui_text(prop, "Shutter", "Exposure for motion blur as a factor of FPS"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } static void def_cmp_sunbeams(StructRNA *srna) @@ -7105,7 +7138,13 @@ static void rna_def_node(BlenderRNA *brna) static EnumPropertyItem dummy_static_type_items[] = { {NODE_CUSTOM, "CUSTOM", 0, "Custom", "Custom Node"}, {0, NULL, 0, NULL, NULL}}; - + + static EnumPropertyItem node_shading_compatibilities[] = { + {NODE_OLD_SHADING, "OLD_SHADING", 0, "Old Shading", "Old shading system compatibility"}, + {NODE_NEW_SHADING, "NEW_SHADING", 0, "New Shading", "New shading system compatibility"}, + {0, NULL, 0, NULL, NULL} + }; + srna = RNA_def_struct(brna, "Node", NULL); RNA_def_struct_ui_text(srna, "Node", "Node in a node tree"); RNA_def_struct_sdna(srna, "bNode"); @@ -7245,6 +7284,12 @@ static void rna_def_node(BlenderRNA *brna) parm = RNA_def_boolean(func, "result", false, "Result", ""); RNA_def_function_return(func, parm); + prop = RNA_def_property(srna, "shading_compatibility", PROP_ENUM, PROP_NONE); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_enum_sdna(prop, NULL, "typeinfo->compatibility"); + RNA_def_property_enum_items(prop, node_shading_compatibilities); + /* registration */ prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "typeinfo->idname"); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index f9a76005df8..60cf7535135 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -640,7 +640,7 @@ void rna_object_uvlayer_name_set(PointerRNA *ptr, const char *value, char *resul for (a = 0; a < me->pdata.totlayer; a++) { layer = &me->pdata.layers[a]; - if (layer->type == CD_MTEXPOLY && strcmp(layer->name, value) == 0) { + if (layer->type == CD_MTEXPOLY && STREQ(layer->name, value)) { BLI_strncpy(result, value, maxlen); return; } @@ -663,7 +663,7 @@ void rna_object_vcollayer_name_set(PointerRNA *ptr, const char *value, char *res for (a = 0; a < me->fdata.totlayer; a++) { layer = &me->fdata.layers[a]; - if (layer->type == CD_MCOL && strcmp(layer->name, value) == 0) { + if (layer->type == CD_MCOL && STREQ(layer->name, value)) { BLI_strncpy(result, value, maxlen); return; } diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 5c5f0c1ec36..d2417f0a93e 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -131,6 +131,7 @@ static EnumPropertyItem part_hair_ren_as_items[] = { #include "BKE_context.h" #include "BKE_cloth.h" +#include "BKE_colortools.h" #include "BKE_deform.h" #include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" @@ -356,13 +357,15 @@ static void rna_ParticleSystem_co_hair(ParticleSystem *particlesystem, Object *o return; if (part->ren_as == PART_DRAW_PATH && particlesystem->pathcache) - path_nbr = (int)pow(2.0, step_nbr); + path_nbr = 1 << step_nbr; + if (part->kink == PART_KINK_SPIRAL) + path_nbr += part->kink_extra_steps; if (particle_no < totpart) { if (path_nbr) { cache = particlesystem->pathcache[particle_no]; - max_k = (int)cache->steps; + max_k = (int)cache->segments; } } @@ -371,10 +374,10 @@ static void rna_ParticleSystem_co_hair(ParticleSystem *particlesystem, Object *o if (path_nbr) { cache = particlesystem->childcache[particle_no - totpart]; - if (cache->steps < 0) + if (cache->segments < 0) max_k = 0; else - max_k = (int)cache->steps; + max_k = (int)cache->segments; } } @@ -645,6 +648,15 @@ static void rna_Particle_redo_child(Main *bmain, Scene *scene, PointerRNA *ptr) particle_recalc(bmain, scene, ptr, PSYS_RECALC_CHILD); } +static void rna_Particle_cloth_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + Object *ob = (Object *)ptr->id.data; + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); +} + + static ParticleSystem *rna_particle_system_for_target(Object *ob, ParticleTarget *target) { ParticleSystem *psys; @@ -716,6 +728,7 @@ static void rna_Particle_hair_dynamics(Main *bmain, Scene *scene, PointerRNA *pt psys->clmd->sim_parms->goalspring = 0.0f; psys->clmd->sim_parms->flags |= CLOTH_SIMSETTINGS_FLAG_GOAL | CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS; psys->clmd->coll_parms->flags &= ~CLOTH_COLLSETTINGS_FLAG_SELF; + psys->clmd->coll_parms->flags |= CLOTH_COLLSETTINGS_FLAG_POINTS; rna_Particle_redo(bmain, scene, ptr); } else @@ -864,6 +877,32 @@ static int rna_PartSettings_is_fluid_get(PointerRNA *ptr) return part->type == PART_FLUID; } +static void rna_ParticleSettings_use_clump_curve_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + ParticleSettings *part = ptr->data; + + if (part->child_flag & PART_CHILD_USE_CLUMP_CURVE) { + if (!part->clumpcurve) { + BKE_particlesettings_clump_curve_init(part); + } + } + + rna_Particle_redo_child(bmain, scene, ptr); +} + +static void rna_ParticleSettings_use_roughness_curve_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + ParticleSettings *part = ptr->data; + + if (part->child_flag & PART_CHILD_USE_ROUGH_CURVE) { + if (!part->roughcurve) { + BKE_particlesettings_rough_curve_init(part); + } + } + + rna_Particle_redo_child(bmain, scene, ptr); +} + static void rna_ParticleSystem_name_set(PointerRNA *ptr, const char *value) { Object *ob = ptr->id.data; @@ -1770,9 +1809,14 @@ static void rna_def_particle_settings_mtex(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Clump", "Affect the child clumping"); RNA_def_property_update(prop, 0, "rna_Particle_reset"); - prop = RNA_def_property(srna, "use_map_kink", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_KINK); - RNA_def_property_ui_text(prop, "Kink", "Affect the child kink"); + prop = RNA_def_property(srna, "use_map_kink_amp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_KINK_AMP); + RNA_def_property_ui_text(prop, "Kink Amplitude", "Affect the child kink amplitude"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop = RNA_def_property(srna, "use_map_kink_freq", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_KINK_FREQ); + RNA_def_property_ui_text(prop, "Kink Frequency", "Affect the child kink frequency"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); prop = RNA_def_property(srna, "use_map_rough", PROP_BOOLEAN, PROP_NONE); @@ -1849,10 +1893,16 @@ static void rna_def_particle_settings_mtex(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Clump Factor", "Amount texture affects child clump"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); - prop = RNA_def_property(srna, "kink_factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "kink_amp_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "kinkampfac"); + RNA_def_property_ui_range(prop, 0, 1, 10, 3); + RNA_def_property_ui_text(prop, "Kink Amplitude Factor", "Amount texture affects child kink amplitude"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop = RNA_def_property(srna, "kink_freq_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "kinkfac"); RNA_def_property_ui_range(prop, 0, 1, 10, 3); - RNA_def_property_ui_text(prop, "Kink Factor", "Amount texture affects child kink"); + RNA_def_property_ui_text(prop, "Kink Frequency Factor", "Amount texture affects child kink frequency"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); prop = RNA_def_property(srna, "rough_factor", PROP_FLOAT, PROP_NONE); @@ -1938,6 +1988,7 @@ static void rna_def_particle_settings(BlenderRNA *brna) {PART_KINK_RADIAL, "RADIAL", 0, "Radial", ""}, {PART_KINK_WAVE, "WAVE", 0, "Wave", ""}, {PART_KINK_BRAID, "BRAID", 0, "Braid", ""}, + {PART_KINK_SPIRAL, "SPIRAL", 0, "Spiral", ""}, {0, NULL, 0, NULL, NULL} }; @@ -2170,6 +2221,16 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Particle_reset"); /*draw flag*/ + prop = RNA_def_property(srna, "show_guide_hairs", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_GUIDE_HAIRS); + RNA_def_property_ui_text(prop, "Guide Hairs", "Show guide hairs"); + RNA_def_property_update(prop, 0, "rna_Particle_redo"); + + prop = RNA_def_property(srna, "show_hair_grid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_HAIR_GRID); + RNA_def_property_ui_text(prop, "Guide Hairs", "Show guide hairs"); + RNA_def_property_update(prop, 0, "rna_Particle_redo"); + prop = RNA_def_property(srna, "show_velocity", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_VEL); RNA_def_property_ui_text(prop, "Velocity", "Show particle velocity"); @@ -2301,6 +2362,11 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Segments", "Number of hair segments"); RNA_def_property_update(prop, 0, "rna_Particle_reset"); + prop = RNA_def_property(srna, "bending_random", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "bending_random"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Random Bending Stiffness", "Random stiffness of hairs"); + RNA_def_property_update(prop, 0, "rna_Particle_cloth_update"); /*TODO: not found in UI, readonly? */ prop = RNA_def_property(srna, "keys_step", PROP_INT, PROP_NONE); @@ -2766,6 +2832,29 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shape", "Shape of clumping"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "use_clump_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "child_flag", PART_CHILD_USE_CLUMP_CURVE); + RNA_def_property_ui_text(prop, "Use Clump Curve", "Use a curve to define clump tapering"); + RNA_def_property_update(prop, 0, "rna_ParticleSettings_use_clump_curve_update"); + + prop = RNA_def_property(srna, "clump_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "clumpcurve"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Clump Curve", "Curve defining clump tapering"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop = RNA_def_property(srna, "use_clump_noise", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "child_flag", PART_CHILD_USE_CLUMP_NOISE); + RNA_def_property_ui_text(prop, "Use Clump Noise", "Create random clumps around the parent"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop = RNA_def_property(srna, "clump_noise_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "clump_noise_size"); + RNA_def_property_range(prop, 0.00001f, 100000.0f); + RNA_def_property_ui_range(prop, 0.01f, 10.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "Clump Noise Size", "Size of clump noise"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); /* kink */ prop = RNA_def_property(srna, "kink_amplitude", PROP_FLOAT, PROP_NONE); @@ -2781,6 +2870,12 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Amplitude Clump", "How much clump affects kink amplitude"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "kink_amplitude_random", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "kink_amp_random"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Amplitude Random", "Random variation of the amplitude"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "kink_frequency", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "kink_freq"); RNA_def_property_range(prop, -100000.0f, 100000.0f); @@ -2798,6 +2893,17 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Flatness", "How flat the hairs are"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "kink_extra_steps", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, INT_MAX); + RNA_def_property_ui_range(prop, 1, 100, 1, -1); + RNA_def_property_ui_text(prop, "Extra Steps", "Extra steps for resolution of special kink features"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + + prop = RNA_def_property(srna, "kink_axis_random", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Axis Random", "Random variation of the orientation"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + /* rough */ prop = RNA_def_property(srna, "roughness_1", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "rough1"); @@ -2846,6 +2952,18 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shape", "Shape of end point rough"); RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "use_roughness_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "child_flag", PART_CHILD_USE_ROUGH_CURVE); + RNA_def_property_ui_text(prop, "Use Roughness Curve", "Use a curve to define roughness"); + RNA_def_property_update(prop, 0, "rna_ParticleSettings_use_roughness_curve_update"); + + prop = RNA_def_property(srna, "roughness_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "roughcurve"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Roughness Curve", "Curve defining roughness"); + RNA_def_property_update(prop, 0, "rna_Particle_redo_child"); + prop = RNA_def_property(srna, "child_length", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "clength"); RNA_def_property_range(prop, 0.0f, 1.0f); diff --git a/source/blender/makesrna/intern/rna_property.c b/source/blender/makesrna/intern/rna_property.c index c6b8e89c282..a2c19e55056 100644 --- a/source/blender/makesrna/intern/rna_property.c +++ b/source/blender/makesrna/intern/rna_property.c @@ -28,6 +28,11 @@ #include <stdlib.h> #include "DNA_property_types.h" +#include "DNA_object_types.h" + +#include "BLI_path_util.h" + +#include "BLF_translation.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -97,9 +102,11 @@ static void rna_GameProperty_type_set(PointerRNA *ptr, int value) static void rna_GameProperty_name_set(PointerRNA *ptr, const char *value) { - bProperty *prop = (bProperty *)(ptr->data); + Object *ob = ptr->id.data; + bProperty *prop = ptr->data; BLI_strncpy_utf8(prop->name, value, sizeof(prop->name)); - BKE_bproperty_unique(NULL, prop, 1); + + BLI_uniquename(&ob->prop, prop, DATA_("Property"), '.', offsetof(bProperty, name), sizeof(prop->name)); } diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 6db9c8e9cd9..124a83a5f30 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -267,7 +267,7 @@ static StructRNA *rna_RenderEngine_register(Main *bmain, ReportList *reports, vo /* check if we have registered this engine type before, and remove it */ for (et = R_engines.first; et; et = et->next) { - if (strcmp(et->idname, dummyet.idname) == 0) { + if (STREQ(et->idname, dummyet.idname)) { if (et->ext.srna) rna_RenderEngine_unregister(bmain, et->ext.srna); break; diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 4f51ec5468c..b2c135cd1f9 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -179,7 +179,7 @@ static int rna_idproperty_known(CollectionPropertyIterator *iter, void *data) * for the second loop where we go over unknown id properties */ do { for (prop = ptype->cont.properties.first; prop; prop = prop->next) - if ((prop->flag & PROP_BUILTIN) == 0 && strcmp(prop->identifier, idprop->name) == 0) + if ((prop->flag & PROP_BUILTIN) == 0 && STREQ(prop->identifier, idprop->name)) return 1; } while ((ptype = ptype->base)); @@ -386,7 +386,7 @@ int rna_builtin_properties_lookup_string(PointerRNA *ptr, const char *key, Point } else { for (prop = srna->cont.properties.first; prop; prop = prop->next) { - if (!(prop->flag & PROP_BUILTIN) && strcmp(prop->identifier, key) == 0) { + if (!(prop->flag & PROP_BUILTIN) && STREQ(prop->identifier, key)) { propptr.type = &RNA_Property; propptr.data = prop; @@ -408,7 +408,7 @@ int rna_builtin_properties_lookup_string(PointerRNA *ptr, const char *key, Point if (group) { for (idp = group->data.group.first; idp; idp = idp->next) { - if (strcmp(idp->name, key) == 0) { + if (STREQ(idp->name, key)) { propptr.type = &RNA_Property; propptr.data = idp; @@ -1002,7 +1002,7 @@ static int rna_BlenderRNA_structs_lookup_string(PointerRNA *ptr, const char *key { StructRNA *srna = ((BlenderRNA *)ptr->data)->structs.first; for (; srna; srna = srna->cont.next) { - if (key[0] == srna->identifier[0] && strcmp(key, srna->identifier) == 0) { + if (key[0] == srna->identifier[0] && STREQ(key, srna->identifier)) { RNA_pointer_create(NULL, &RNA_Struct, srna, r_ptr); return true; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index d9587b8d983..1c133db37b7 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -112,6 +112,7 @@ EnumPropertyItem proportional_falloff_items[] = { {PROP_SMOOTH, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", "Smooth falloff"}, {PROP_SPHERE, "SPHERE", ICON_SPHERECURVE, "Sphere", "Spherical falloff"}, {PROP_ROOT, "ROOT", ICON_ROOTCURVE, "Root", "Root falloff"}, + {PROP_INVSQUARE, "INVERSE_SQUARE", ICON_ROOTCURVE, "Inverse Square", "Inverse Square falloff"}, {PROP_SHARP, "SHARP", ICON_SHARPCURVE, "Sharp", "Sharp falloff"}, {PROP_LIN, "LINEAR", ICON_LINCURVE, "Linear", "Linear falloff"}, {PROP_CONST, "CONSTANT", ICON_NOCURVE, "Constant", "Constant falloff"}, @@ -385,7 +386,7 @@ static int rna_Scene_object_bases_lookup_string(PointerRNA *ptr, const char *key Base *base; for (base = scene->base.first; base; base = base->next) { - if (strncmp(base->object->id.name + 2, key, sizeof(base->object->id.name) - 2) == 0) { + if (STREQLEN(base->object->id.name + 2, key, sizeof(base->object->id.name) - 2)) { *r_ptr = rna_pointer_inherit_refine(ptr, &RNA_ObjectBase, base); return true; } @@ -942,7 +943,7 @@ static int rna_SceneRender_file_ext_length(PointerRNA *ptr) RenderData *rd = (RenderData *)ptr->data; char ext[8]; ext[0] = '\0'; - BKE_add_image_extension(ext, &rd->im_format); + BKE_image_path_ensure_ext_from_imformat(ext, &rd->im_format); return strlen(ext); } @@ -950,7 +951,7 @@ static void rna_SceneRender_file_ext_get(PointerRNA *ptr, char *str) { RenderData *rd = (RenderData *)ptr->data; str[0] = '\0'; - BKE_add_image_extension(str, &rd->im_format); + BKE_image_path_ensure_ext_from_imformat(str, &rd->im_format); } #ifdef WITH_QUICKTIME @@ -1158,7 +1159,7 @@ static int rna_RenderSettings_engine_get(PointerRNA *ptr) int a = 0; for (type = R_engines.first; type; type = type->next, a++) - if (strcmp(type->idname, rd->engine) == 0) + if (STREQ(type->idname, rd->engine)) return a; return 0; @@ -1243,7 +1244,7 @@ static int rna_RenderSettings_use_game_engine_get(PointerRNA *ptr) RenderEngineType *type; for (type = R_engines.first; type; type = type->next) - if (strcmp(type->idname, rd->engine) == 0) + if (STREQ(type->idname, rd->engine)) return (type->flag & RE_GAME); return 0; @@ -3207,7 +3208,7 @@ static void rna_def_scene_game_recast_data(BlenderRNA *brna) prop = RNA_def_property(srna, "slope_max", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "agentmaxslope"); - RNA_def_property_range(prop, 0, M_PI / 2); + RNA_def_property_range(prop, 0, M_PI_2); RNA_def_property_ui_text(prop, "Max Slope", "Maximum walkable slope angle"); RNA_def_property_update(prop, NC_SCENE, NULL); @@ -3622,8 +3623,8 @@ static void rna_def_scene_game_data(BlenderRNA *brna) prop = RNA_def_property(srna, "logic_step_max", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "maxlogicstep"); - RNA_def_property_ui_range(prop, 1, 5, 1, 1); - RNA_def_property_range(prop, 1, 5); + RNA_def_property_range(prop, 1, 10000); + RNA_def_property_ui_range(prop, 1, 50, 1, 1); RNA_def_property_ui_text(prop, "Max Logic Steps", "Maximum number of logic frame per game frame if graphics slows down the game, " "higher value allows better synchronization with physics"); @@ -3631,8 +3632,8 @@ static void rna_def_scene_game_data(BlenderRNA *brna) prop = RNA_def_property(srna, "physics_step_max", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "maxphystep"); - RNA_def_property_ui_range(prop, 1, 5, 1, 1); - RNA_def_property_range(prop, 1, 5); + RNA_def_property_range(prop, 1, 10000); + RNA_def_property_ui_range(prop, 1, 50, 1, 1); RNA_def_property_ui_text(prop, "Max Physics Steps", "Maximum number of physics step per game frame if graphics slows down the game, " "higher value allows physics to keep up with realtime"); @@ -4681,6 +4682,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update"); prop = RNA_def_property(srna, "use_freestyle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_boolean_sdna(prop, NULL, "mode", R_EDGE_FRS); RNA_def_property_ui_text(prop, "Edge", "Draw stylized strokes using Freestyle"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); @@ -5390,6 +5392,55 @@ static void rna_def_selected_uv_element(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Face Index", ""); } +static void rna_def_display_safe_areas(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static float default_title[2] = {0.035f, 0.035f}; + static float default_action[2] = {0.1f, 0.05f}; + + static float default_title_center[2] = {0.175f, 0.05f}; + static float default_action_center[2] = {0.15f, 0.05f}; + + srna = RNA_def_struct(brna, "DisplaySafeAreas", NULL); + RNA_def_struct_ui_text(srna, "Safe Areas", "Safe Areas used in 3D view and the VSE"); + RNA_def_struct_sdna(srna, "DisplaySafeAreas"); + + /* SAFE AREAS */ + prop = RNA_def_property(srna, "title", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "title"); + RNA_def_property_array(prop, 2); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_array_default(prop, default_title); + RNA_def_property_ui_text(prop, "Title Safe margins", "Safe area for text and graphics"); + RNA_def_property_update(prop, NC_SCENE | ND_DRAW_RENDER_VIEWPORT, NULL); + + prop = RNA_def_property(srna, "action", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "action"); + RNA_def_property_array(prop, 2); + RNA_def_property_float_array_default(prop, default_action); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Action Safe Margins", "Safe area for general elements"); + RNA_def_property_update(prop, NC_SCENE | ND_DRAW_RENDER_VIEWPORT, NULL); + + + prop = RNA_def_property(srna, "title_center", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "title_center"); + RNA_def_property_array(prop, 2); + RNA_def_property_float_array_default(prop, default_title_center); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Center Title Safe Margins", "Safe area for text and graphics in a different aspect ratio"); + RNA_def_property_update(prop, NC_SCENE | ND_DRAW_RENDER_VIEWPORT, NULL); + + prop = RNA_def_property(srna, "action_center", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "action_center"); + RNA_def_property_array(prop, 2); + RNA_def_property_float_array_default(prop, default_action_center); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Center Action Safe Margins", "Safe area for general elements in a different aspect ratio"); + RNA_def_property_update(prop, NC_SCENE | ND_DRAW_RENDER_VIEWPORT, NULL); +} void RNA_def_scene(BlenderRNA *brna) @@ -5685,6 +5736,13 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_struct_type(prop, "RenderSettings"); RNA_def_property_ui_text(prop, "Render Data", ""); + /* Safe Areas */ + prop = RNA_def_property(srna, "safe_areas", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "safe_areas"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "DisplaySafeAreas"); + RNA_def_property_ui_text(prop, "Safe Areas", ""); + /* Markers */ prop = RNA_def_property(srna, "timeline_markers", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "markers", NULL); @@ -5799,6 +5857,7 @@ void RNA_def_scene(BlenderRNA *brna) rna_def_scene_game_data(brna); rna_def_transform_orientation(brna); rna_def_selected_uv_element(brna); + rna_def_display_safe_areas(brna); RNA_define_animate_sdna(true); /* *** Animated *** */ rna_def_scene_render_data(brna); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index be618c5d1a3..36657c8a898 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -121,11 +121,14 @@ static void rna_Scene_update_tagged(Scene *scene) static void rna_SceneRender_get_frame_path(RenderData *rd, int frame, char *name) { - if (BKE_imtype_is_movie(rd->im_format.imtype)) + if (BKE_imtype_is_movie(rd->im_format.imtype)) { BKE_movie_filepath_get(name, rd); - else - BKE_makepicstring(name, rd->pic, G.main->name, (frame == INT_MIN) ? rd->cfra : frame, - &rd->im_format, (rd->scemode & R_EXTENSION) != 0, true); + } + else { + BKE_image_path_from_imformat( + name, rd->pic, G.main->name, (frame == INT_MIN) ? rd->cfra : frame, + &rd->im_format, (rd->scemode & R_EXTENSION) != 0, true); + } } static void rna_Scene_ray_cast(Scene *scene, float ray_start[3], float ray_end[3], diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index 7b01acff6a5..86d20e6e239 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -413,6 +413,11 @@ static void rna_def_screen(BlenderRNA *brna) RNA_def_property_ui_text(prop, "All 3D View Editors", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TIME, "rna_Screen_redraw_update"); + prop = RNA_def_property(srna, "use_follow", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "redraws_flag", TIME_FOLLOW); + RNA_def_property_ui_text(prop, "Follow", "Follow current frame in editors that update"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TIME, "rna_Screen_redraw_update"); + prop = RNA_def_property(srna, "use_play_animation_editors", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "redraws_flag", TIME_ALL_ANIM_WIN); RNA_def_property_ui_text(prop, "Animation Editors", ""); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 8e83543812d..0a282eeb7ac 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -709,6 +709,11 @@ static void rna_def_image_paint(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "stencil_col"); RNA_def_property_ui_text(prop, "Stencil Color", "Stencil color in the viewport"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_viewport_update"); + + prop = RNA_def_property(srna, "dither", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_text(prop, "Dither", "Amount of dithering when painting on byte images"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "use_clone_layer", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", IMAGEPAINT_PROJECT_LAYER_CLONE); @@ -888,6 +893,10 @@ static void rna_def_particle_edit(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Object", "The edited object"); + prop = RNA_def_property(srna, "shape_object", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Shape Object", "Outer shape to use for tools"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_ParticleEdit_redo"); /* brush */ diff --git a/source/blender/makesrna/intern/rna_sensor.c b/source/blender/makesrna/intern/rna_sensor.c index 3944b59dff7..5d7bb6d2d94 100644 --- a/source/blender/makesrna/intern/rna_sensor.c +++ b/source/blender/makesrna/intern/rna_sensor.c @@ -107,14 +107,10 @@ static StructRNA *rna_Sensor_refine(struct PointerRNA *ptr) static void rna_Sensor_name_set(PointerRNA *ptr, const char *value) { - bSensor *sens = (bSensor *)ptr->data; - + Object *ob = ptr->id.data; + bSensor *sens = ptr->data; BLI_strncpy_utf8(sens->name, value, sizeof(sens->name)); - - if (ptr->id.data) { - Object *ob = (Object *)ptr->id.data; - BLI_uniquename(&ob->sensors, sens, DATA_("Sensor"), '.', offsetof(bSensor, name), sizeof(sens->name)); - } + BLI_uniquename(&ob->sensors, sens, DATA_("Sensor"), '.', offsetof(bSensor, name), sizeof(sens->name)); } static void rna_Sensor_type_set(struct PointerRNA *ptr, int value) @@ -246,11 +242,11 @@ static void rna_Sensor_Armature_update(Main *UNUSED(bmain), Scene *UNUSED(scene) bPoseChannel *pchan; bPose *pose = ob->pose; for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - if (!strcmp(pchan->name, posechannel)) { + if (STREQ(pchan->name, posechannel)) { /* found it, now look for constraint channel */ bConstraint *con; for (con = pchan->constraints.first; con; con = con->next) { - if (!strcmp(con->name, constraint)) { + if (STREQ(con->name, constraint)) { /* found it, all ok */ return; } diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index dda0e8493d9..f628112a19a 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -310,18 +310,7 @@ static void rna_Sequence_frame_offset_range(PointerRNA *ptr, int *min, int *max, static void rna_Sequence_use_proxy_set(PointerRNA *ptr, int value) { Sequence *seq = (Sequence *)ptr->data; - if (value) { - seq->flag |= SEQ_USE_PROXY; - if (seq->strip->proxy == NULL) { - seq->strip->proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy"); - seq->strip->proxy->quality = 90; - seq->strip->proxy->build_tc_flags = SEQ_PROXY_TC_ALL; - seq->strip->proxy->build_size_flags = SEQ_PROXY_IMAGE_SIZE_25; - } - } - else { - seq->flag ^= SEQ_USE_PROXY; - } + BKE_sequencer_proxy_set(seq, value != 0); } static void rna_Sequence_use_translation_set(PointerRNA *ptr, int value) @@ -1187,6 +1176,10 @@ static void rna_def_strip_proxy(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceProxy_update"); + prop = RNA_def_property(srna, "use_overwrite", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "build_flags", SEQ_PROXY_SKIP_EXISTING); + RNA_def_property_ui_text(prop, "Overwrite", "Overwrite existing proxy files when building"); + prop = RNA_def_property(srna, "build_25", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "build_size_flags", SEQ_PROXY_IMAGE_SIZE_25); RNA_def_property_ui_text(prop, "25%", "Build 25% proxy resolution"); diff --git a/source/blender/makesrna/intern/rna_sound.c b/source/blender/makesrna/intern/rna_sound.c index aa39e81d390..4b745c60b5b 100644 --- a/source/blender/makesrna/intern/rna_sound.c +++ b/source/blender/makesrna/intern/rna_sound.c @@ -98,6 +98,8 @@ static void rna_def_sound(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mono", "If the file contains multiple audio channels they are rendered to a single one"); RNA_def_property_update(prop, 0, "rna_Sound_update"); + + RNA_api_sound(srna); } void RNA_def_sound(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_sound_api.c b/source/blender/makesrna/intern/rna_sound_api.c new file mode 100644 index 00000000000..0164daa98d0 --- /dev/null +++ b/source/blender/makesrna/intern/rna_sound_api.c @@ -0,0 +1,73 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 by the Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/makesrna/intern/rna_sound_api.c + * \ingroup RNA + */ + +#include "DNA_packedFile_types.h" + +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#ifdef RNA_RUNTIME + +#include "BKE_packedFile.h" + +static void rna_Sound_pack(bSound *sound, Main *bmain, ReportList *reports) +{ + sound->packedfile = newPackedFile(reports, sound->name, ID_BLEND_PATH(bmain, &sound->id)); +} + +static void rna_Sound_unpack(bSound *sound, Main *bmain, ReportList *reports, int method) +{ + if (!sound->packedfile) { + BKE_report(reports, RPT_ERROR, "Sound not packed"); + } + else { + /* reports its own error on failure */ + unpackSound(bmain, reports, sound, method); + } +} + +#else + +void RNA_api_sound(StructRNA *srna) +{ + FunctionRNA *func; + + func = RNA_def_function(srna, "pack", "rna_Sound_pack"); + RNA_def_function_ui_description(func, "Pack the sound into the current blend file"); + RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_MAIN); + + func = RNA_def_function(srna, "unpack", "rna_Sound_unpack"); + RNA_def_function_ui_description(func, "Unpack the sound to the samples filename"); + RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_MAIN); + RNA_def_enum(func, "method", unpack_method_items, PF_USE_LOCAL, "method", "How to unpack"); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index af0e69cdc3e..382ed358e12 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -791,7 +791,7 @@ static void rna_SpaceImageEditor_cursor_location_set(PointerRNA *ptr, const floa } } -static void rna_SpaceImageEditor_scopes_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +static void rna_SpaceImageEditor_scopes_update(struct bContext *C, struct PointerRNA *ptr) { SpaceImage *sima = (SpaceImage *)ptr->data; ImBuf *ibuf; @@ -799,7 +799,7 @@ static void rna_SpaceImageEditor_scopes_update(Main *UNUSED(bmain), Scene *scene ibuf = ED_space_image_acquire_buffer(sima, &lock); if (ibuf) { - scopes_update(&sima->scopes, ibuf, &scene->view_settings, &scene->display_settings); + ED_space_image_scopes_update(C, sima, ibuf, true); WM_main_add_notifier(NC_IMAGE, sima->image); } ED_space_image_release_buffer(sima, ibuf, lock); @@ -1249,7 +1249,7 @@ static int rna_SpaceNodeEditor_node_tree_poll(PointerRNA *ptr, const PointerRNA bNodeTree *ntree = (bNodeTree *)value.data; /* node tree type must match the selected type in node editor */ - return (strcmp(snode->tree_idname, ntree->idname) == 0); + return (STREQ(snode->tree_idname, ntree->idname)); } static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA *UNUSED(ptr)) @@ -1593,7 +1593,12 @@ static void rna_def_space_outliner(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "search_flags", SO_FIND_COMPLETE); RNA_def_property_ui_text(prop, "Complete Matches Only", "Only use complete matches of search string"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL); - + + prop = RNA_def_property(srna, "use_sort_alpha", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SO_SKIP_SORT_ALPHA); + RNA_def_property_ui_text(prop, "Sort Alphabetically", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL); + prop = RNA_def_property(srna, "show_restrict_columns", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SO_HIDE_RESTRICTCOLS); RNA_def_property_ui_text(prop, "Show Restriction Columns", "Show column"); @@ -1695,7 +1700,22 @@ static void rna_def_background_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Size", "Scaling factor for the background image"); RNA_def_property_range(prop, 0.0, FLT_MAX); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - + + prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, NULL, "rotation"); + RNA_def_property_ui_text(prop, "Rotation", "Rotation for the background image"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "use_flip_x", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_BGPIC_FLIP_X); + RNA_def_property_ui_text(prop, "Flip Horizontally", "Flip the background image horizontally"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "use_flip_y", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_BGPIC_FLIP_Y); + RNA_def_property_ui_text(prop, "Flip Vertically", "Flip the background image vertically"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "blend"); RNA_def_property_float_funcs(prop, "rna_BackgroundImage_opacity_get", "rna_BackgroundImage_opacity_set", NULL); @@ -2361,6 +2381,7 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "scopes"); RNA_def_property_struct_type(prop, "Scopes"); RNA_def_property_ui_text(prop, "Scopes", "Scopes to visualize image statistics"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_SpaceImageEditor_scopes_update"); prop = RNA_def_property(srna, "use_image_pin", PROP_BOOLEAN, PROP_NONE); @@ -2565,11 +2586,16 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Separate Colors", "Separate color channels in preview"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_safe_margin", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_DRAW_SAFE_MARGINS); - RNA_def_property_ui_text(prop, "Safe Margin", "Draw title safe margins in preview"); + prop = RNA_def_property(srna, "show_safe_areas", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_SAFE_MARGINS); + RNA_def_property_ui_text(prop, "Safe Areas", "Show TV title safe and action safe areas in preview"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - + + prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_SAFE_CENTER); + RNA_def_property_ui_text(prop, "Center-Cut Safe Areas", "Show safe areas to fit content in a different aspect ratio"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + prop = RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_DRAWFRAMES); RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames"); @@ -2814,7 +2840,8 @@ static void rna_def_space_dopesheet(BlenderRNA *brna) prop = RNA_def_property(srna, "show_group_colors", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SACTION_NODRAWGCOLORS); RNA_def_property_ui_text(prop, "Show Group Colors", - "Draw groups and channels with colors matching their corresponding groups"); + "Draw groups and channels with colors matching their corresponding groups " + "(pose bones only currently)"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_DOPESHEET, NULL); /* editing */ @@ -2976,6 +3003,12 @@ static void rna_def_space_graph(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Has Ghost Curves", "Graph Editor instance has some ghost curves stored"); + /* auto view */ + prop = RNA_def_property(srna, "use_auto_view_selected", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_AUTO_VIEW_SELECTED); + RNA_def_property_ui_text(prop, "Auto View Selected", "Automatically adjust view based on selection"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL); + /* nromalize curves */ prop = RNA_def_property(srna, "use_normalization", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_NORMALIZE); diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 188a30d23ce..c1882e7767d 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -440,15 +440,13 @@ static void rna_ImageTexture_mipmap_set(PointerRNA *ptr, int value) else tex->imaflag &= ~TEX_MIPMAP; } -static void rna_Envmap_source_update(Main *bmain, Scene *scene, PointerRNA *ptr) +static void rna_Envmap_update_generic(Main *bmain, Scene *scene, PointerRNA *ptr) { Tex *tex = ptr->id.data; - if (tex->env) { ED_preview_kill_jobs(bmain->wm.first, bmain); BKE_free_envmapdata(tex->env); } - rna_Texture_update(bmain, scene, ptr); } @@ -806,7 +804,7 @@ static void rna_def_environment_map(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "stype"); RNA_def_property_enum_items(prop, prop_source_items); RNA_def_property_ui_text(prop, "Source", ""); - RNA_def_property_update(prop, 0, "rna_Envmap_source_update"); + RNA_def_property_update(prop, 0, "rna_Envmap_update_generic"); prop = RNA_def_property(srna, "viewpoint_object", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "object"); @@ -1394,7 +1392,7 @@ static void rna_def_texture_environment_map(BlenderRNA *brna) RNA_def_property_struct_type(prop, "Image"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Image", "Source image file to read the environment map from"); - RNA_def_property_update(prop, 0, "rna_Texture_update"); + RNA_def_property_update(prop, 0, "rna_Envmap_update_generic"); prop = RNA_def_property(srna, "image_user", PROP_POINTER, PROP_NEVER_NULL); RNA_def_property_pointer_sdna(prop, NULL, "iuser"); @@ -1851,6 +1849,7 @@ static void rna_def_texture_voxeldata(BlenderRNA *brna) {TEX_VD_IMAGE_SEQUENCE, "IMAGE_SEQUENCE", 0, "Image Sequence", "Generate voxels from a sequence of image slices"}, {TEX_VD_SMOKE, "SMOKE", 0, "Smoke", "Render voxels from a Blender smoke simulation"}, + {TEX_VD_HAIR, "HAIR", 0, "Hair", "Render voxels from a Blender hair simulation"}, {0, NULL, 0, NULL, NULL} }; @@ -1869,6 +1868,14 @@ static void rna_def_texture_voxeldata(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem hair_type_items[] = { + {TEX_VD_HAIRDENSITY, "HAIRDENSITY", 0, "Density", "Use hair density as texture data"}, + {TEX_VD_HAIRRESTDENSITY, "HAIRRESTDENSITY", 0, "Rest Density", "Use hair rest density as texture data"}, + {TEX_VD_HAIRVELOCITY, "HAIRVELOCITY", 0, "Velocity", "Use hair velocity as texture data"}, + {TEX_VD_HAIRENERGY, "HAIRENERGY", 0, "Energy", "Use potential hair energy as texture data"}, + {0, NULL, 0, NULL, NULL} + }; + srna = RNA_def_struct(brna, "VoxelData", NULL); RNA_def_struct_sdna(srna, "VoxelData"); RNA_def_struct_ui_text(srna, "VoxelData", "Voxel data settings"); @@ -1886,6 +1893,12 @@ static void rna_def_texture_voxeldata(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Source", "Simulation value to be used as a texture"); RNA_def_property_update(prop, 0, "rna_Texture_voxeldata_update"); + prop = RNA_def_property(srna, "hair_data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "hair_type"); + RNA_def_property_enum_items(prop, hair_type_items); + RNA_def_property_ui_text(prop, "Source", "Simulation value to be used as a texture"); + RNA_def_property_update(prop, 0, "rna_Texture_voxeldata_update"); + prop = RNA_def_property(srna, "extension", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "extend"); RNA_def_property_enum_items(prop, voxeldata_extension); diff --git a/source/blender/makesrna/intern/rna_texture_api.c b/source/blender/makesrna/intern/rna_texture_api.c index 695a3e0548e..acf8333d6dc 100644 --- a/source/blender/makesrna/intern/rna_texture_api.c +++ b/source/blender/makesrna/intern/rna_texture_api.c @@ -74,7 +74,7 @@ static void texture_evaluate(struct Tex *tex, float value[3], float r_color[4]) TexResult texres = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL}; /* TODO(sergey): always use color management now. */ - multitex_ext(tex, value, NULL, NULL, 1, &texres, NULL, true); + multitex_ext(tex, value, NULL, NULL, 1, &texres, NULL, true, false); r_color[0] = texres.tr; r_color[1] = texres.tg; diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index a61846fa028..fde43abe9fb 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -221,7 +221,7 @@ static StructRNA *rna_Panel_register(Main *bmain, ReportList *reports, void *dat /* check if we have registered this panel type before, and remove it */ for (pt = art->paneltypes.first; pt; pt = pt->next) { - if (strcmp(pt->idname, dummypt.idname) == 0) { + if (STREQ(pt->idname, dummypt.idname)) { if (pt->ext.srna) rna_Panel_unregister(bmain, pt->ext.srna); else @@ -587,7 +587,7 @@ static StructRNA *rna_Header_register(Main *bmain, ReportList *reports, void *da /* check if we have registered this header type before, and remove it */ for (ht = art->headertypes.first; ht; ht = ht->next) { - if (strcmp(ht->idname, dummyht.idname) == 0) { + if (STREQ(ht->idname, dummyht.idname)) { if (ht->ext.srna) rna_Header_unregister(bmain, ht->ext.srna); break; diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 4f889c91665..b5eeebae664 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -295,7 +295,7 @@ static const char *rna_ui_get_enum_name(bContext *C, PointerRNA *ptr, const char if (items) { for (item = items; item->identifier; item++) { - if (item->identifier[0] && strcmp(item->identifier, identifier) == 0) { + if (item->identifier[0] && STREQ(item->identifier, identifier)) { name = item->name; break; } @@ -326,7 +326,7 @@ static const char *rna_ui_get_enum_description(bContext *C, PointerRNA *ptr, con if (items) { for (item = items; item->identifier; item++) { - if (item->identifier[0] && strcmp(item->identifier, identifier) == 0) { + if (item->identifier[0] && STREQ(item->identifier, identifier)) { desc = item->description; break; } @@ -356,7 +356,7 @@ static int rna_ui_get_enum_icon(bContext *C, PointerRNA *ptr, const char *propna if (items) { for (item = items; item->identifier; item++) { - if (item->identifier[0] && strcmp(item->identifier, identifier) == 0) { + if (item->identifier[0] && STREQ(item->identifier, identifier)) { icon = item->icon; break; } diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index fa0e1db10e2..15ecf70a938 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1682,6 +1682,11 @@ static void rna_def_userdef_theme_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Skin Root", ""); RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "clipping_border_3d", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Clipping Border", ""); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + rna_def_userdef_theme_spaces_paint_curves(srna); } @@ -3530,7 +3535,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna) /* Undo */ prop = RNA_def_property(srna, "undo_steps", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "undosteps"); - RNA_def_property_range(prop, 0, 64); + RNA_def_property_range(prop, 0, 256); RNA_def_property_int_funcs(prop, NULL, "rna_userdef_undo_steps_set", NULL); RNA_def_property_ui_text(prop, "Undo Steps", "Number of undo steps available (smaller values conserve memory)"); @@ -3863,6 +3868,11 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Virtual Pixel Mode", "Modify the pixel size for hi-res devices"); RNA_def_property_update(prop, 0, "rna_userdef_virtual_pixel_update"); + prop = RNA_def_property(srna, "pixel_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_sdna(prop, NULL, "pixelsize"); + RNA_def_property_ui_text(prop, "Pixel Size", ""); + prop = RNA_def_property(srna, "font_path_ui", PROP_STRING, PROP_FILEPATH); RNA_def_property_string_sdna(prop, NULL, "font_path_ui"); RNA_def_property_ui_text(prop, "Interface Font", "Path to interface font"); diff --git a/source/blender/makesrna/intern/rna_vfont.c b/source/blender/makesrna/intern/rna_vfont.c index f24f94282b6..0879f4d355d 100644 --- a/source/blender/makesrna/intern/rna_vfont.c +++ b/source/blender/makesrna/intern/rna_vfont.c @@ -84,6 +84,8 @@ void RNA_def_vfont(BlenderRNA *brna) prop = RNA_def_property(srna, "packed_file", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "packedfile"); RNA_def_property_ui_text(prop, "Packed File", ""); + + RNA_api_vfont(srna); } #endif diff --git a/source/blender/makesrna/intern/rna_vfont_api.c b/source/blender/makesrna/intern/rna_vfont_api.c new file mode 100644 index 00000000000..d92e75daf0a --- /dev/null +++ b/source/blender/makesrna/intern/rna_vfont_api.c @@ -0,0 +1,73 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 by the Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/makesrna/intern/rna_vfont_api.c + * \ingroup RNA + */ + +#include "DNA_packedFile_types.h" + +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#ifdef RNA_RUNTIME + +#include "BKE_packedFile.h" + +static void rna_VectorFont_pack(VFont *vfont, Main *bmain, ReportList *reports) +{ + vfont->packedfile = newPackedFile(reports, vfont->name, ID_BLEND_PATH(bmain, &vfont->id)); +} + +static void rna_VectorFont_unpack(VFont *vfont, ReportList *reports, int method) +{ + if (!vfont->packedfile) { + BKE_report(reports, RPT_ERROR, "Font not packed"); + } + else { + /* reports its own error on failure */ + unpackVFont(reports, vfont, method); + } +} + +#else + +void RNA_api_vfont(StructRNA *srna) +{ + FunctionRNA *func; + + func = RNA_def_function(srna, "pack", "rna_VectorFont_pack"); + RNA_def_function_ui_description(func, "Pack the font into the current blend file"); + RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_MAIN); + + func = RNA_def_function(srna, "unpack", "rna_VectorFont_unpack"); + RNA_def_function_ui_description(func, "Unpack the font to the samples filename"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_enum(func, "method", unpack_method_items, PF_USE_LOCAL, "method", "How to unpack"); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 3b97508dd67..347b669812e 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -835,7 +835,7 @@ static void rna_wmKeyMapItem_idname_set(PointerRNA *ptr, const char *value) WM_operator_bl_idname(idname, value); - if (strcmp(idname, kmi->idname) != 0) { + if (!STREQ(idname, kmi->idname)) { BLI_strncpy(kmi->idname, idname, sizeof(kmi->idname)); WM_keymap_properties_reset(kmi, NULL); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 876a95d8d8c..2c32627a89e 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -74,6 +74,7 @@ set(SRC intern/MOD_mirror.c intern/MOD_multires.c intern/MOD_none.c + intern/MOD_normal_edit.c intern/MOD_ocean.c intern/MOD_particleinstance.c intern/MOD_particlesystem.c diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h index 8edcb7e0380..06e0906c758 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -82,6 +82,7 @@ extern ModifierTypeInfo modifierType_MeshCache; extern ModifierTypeInfo modifierType_LaplacianDeform; extern ModifierTypeInfo modifierType_Wireframe; extern ModifierTypeInfo modifierType_DataTransfer; +extern ModifierTypeInfo modifierType_NormalEdit; extern ModifierTypeInfo modifierType_PointCache; /* MOD_util.c */ diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index b6d75afaf23..7430a4300a5 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -201,7 +201,7 @@ static void dm_mvert_map_doubles( const float dist, const bool with_follow) { - const float dist3 = (M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */ + const float dist3 = ((float)M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */ int i_source, i_target, i_target_low_bound, target_end, source_end; SortVertsElem *sorted_verts_target, *sorted_verts_source; SortVertsElem *sve_source, *sve_target, *sve_target_low_bound; diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index f30529bc40f..1dc1a1f8d64 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -86,7 +86,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *UNUSED(ob), float frac; MPoly *mpoly_dst; MLoop *ml_dst, *ml_src /*, *mloop_dst */; - GHashIterator *hashIter; + GHashIterator gh_iter; /* maps vert indices in old mesh to indices in new mesh */ GHash *vertHash = BLI_ghash_int_new("build ve apply gh"); /* maps edge indices in new mesh to indices in old mesh */ @@ -230,15 +230,11 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *UNUSED(ob), BLI_ghash_size(edgeHash), 0, numLoops_dst, numFaces_dst); /* copy the vertices across */ - for (hashIter = BLI_ghashIterator_new(vertHash); - BLI_ghashIterator_done(hashIter) == false; - BLI_ghashIterator_step(hashIter) - ) - { + GHASH_ITER (gh_iter, vertHash) { MVert source; MVert *dest; - int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); - int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); + int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(&gh_iter)); + int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); source = mvert_src[oldIndex]; dest = CDDM_get_vert(result, newIndex); @@ -246,7 +242,6 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *UNUSED(ob), DM_copy_vert_data(dm, result, oldIndex, newIndex, 1); *dest = source; } - BLI_ghashIterator_free(hashIter); /* copy the edges across, remapping indices */ for (i = 0; i < BLI_ghash_size(edgeHash); i++) { diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 11455a6461f..394b2ff412f 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -48,6 +48,7 @@ #include "BKE_cloth.h" #include "BKE_cdderivedmesh.h" +#include "BKE_effect.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_modifier.h" @@ -177,6 +178,8 @@ static void copyData(ModifierData *md, ModifierData *target) tclmd->point_cache = BKE_ptcache_new(); tclmd->point_cache->step = 1; tclmd->clothObject = NULL; + tclmd->hairdata = NULL; + tclmd->solver_result = NULL; } static bool dependsOnTime(ModifierData *UNUSED(md)) @@ -204,6 +207,12 @@ static void freeData(ModifierData *md) BKE_ptcache_free(clmd->point_cache); clmd->point_cache = NULL; + + if (clmd->hairdata) + MEM_freeN(clmd->hairdata); + + if (clmd->solver_result) + MEM_freeN(clmd->solver_result); } } diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index f6d7c03df32..2b512a6ba25 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -43,6 +43,7 @@ #include "BKE_cdderivedmesh.h" #include "BKE_modifier.h" #include "BKE_deform.h" +#include "BKE_colortools.h" #include "depsgraph_private.h" @@ -55,6 +56,9 @@ static void initData(ModifierData *md) HookModifierData *hmd = (HookModifierData *) md; hmd->force = 1.0; + hmd->curfalloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + hmd->falloff_type = eHook_Falloff_Smooth; + hmd->flag = 0; } static void copyData(ModifierData *md, ModifierData *target) @@ -64,6 +68,8 @@ static void copyData(ModifierData *md, ModifierData *target) modifier_copyData_generic(md, target); + thmd->curfalloff = curvemapping_copy(hmd->curfalloff); + thmd->indexar = MEM_dupallocN(hmd->indexar); } @@ -83,6 +89,8 @@ static void freeData(ModifierData *md) { HookModifierData *hmd = (HookModifierData *) md; + curvemapping_free(hmd->curfalloff); + if (hmd->indexar) MEM_freeN(hmd->indexar); } @@ -120,32 +128,172 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, } } -static float hook_falloff(const float co_1[3], const float co_2[3], const float falloff_squared, float fac) +struct HookData_cb { + float (*vertexCos)[3]; + + MDeformVert *dvert; + int defgrp_index; + + struct CurveMapping *curfalloff; + + char falloff_type; + float falloff; + float falloff_sq; + float fac_orig; + + unsigned int use_falloff : 1; + unsigned int use_uniform : 1; + + float cent[3]; + + float mat_uniform[3][3]; + float mat[4][4]; +}; + +static float hook_falloff( + const struct HookData_cb *hd, + const float len_sq) +{ + BLI_assert(hd->falloff_sq); + if (len_sq > hd->falloff_sq) { + return 0.0f; + } + else if (len_sq > 0.0f) { + float fac; + + if (hd->falloff_type == eHook_Falloff_Const) { + fac = 1.0f; + goto finally; + } + else if (hd->falloff_type == eHook_Falloff_InvSquare) { + /* avoid sqrt below */ + fac = 1.0f - (len_sq / hd->falloff_sq); + goto finally; + } + + fac = 1.0f - (sqrtf(len_sq) / hd->falloff); + + /* closely match PROP_SMOOTH and similar */ + switch (hd->falloff_type) { +#if 0 + case eHook_Falloff_None: + fac = 1.0f; + break; +#endif + case eHook_Falloff_Curve: + fac = curvemapping_evaluateF(hd->curfalloff, 0, fac); + break; + case eHook_Falloff_Sharp: + fac = fac * fac; + break; + case eHook_Falloff_Smooth: + fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; + break; + case eHook_Falloff_Root: + fac = sqrtf(fac); + break; + case eHook_Falloff_Linear: + /* pass */ + break; +#if 0 + case eHook_Falloff_Const: + fac = 1.0f; + break; +#endif + case eHook_Falloff_Sphere: + fac = sqrtf(2 * fac - fac * fac); + break; +#if 0 + case eHook_Falloff_InvSquare: + fac = fac * (2.0f - fac); + break; +#endif + } + +finally: + return fac * hd->fac_orig; + } + else { + return hd->fac_orig; + } +} + +static void hook_co_apply(struct HookData_cb *hd, const int j) { - if (falloff_squared) { - float len_squared = len_squared_v3v3(co_1, co_2); - if (len_squared > falloff_squared) { - return 0.0f; + float *co = hd->vertexCos[j]; + float fac; + + if (hd->use_falloff) { + float len_sq; + + if (hd->use_uniform) { + float co_uniform[3]; + mul_v3_m3v3(co_uniform, hd->mat_uniform, co); + len_sq = len_squared_v3v3(hd->cent, co_uniform); } - else if (len_squared > 0.0f) { - return fac * (1.0f - (len_squared / falloff_squared)); + else { + len_sq = len_squared_v3v3(hd->cent, co); } + + fac = hook_falloff(hd, len_sq); } + else { + fac = hd->fac_orig; + } + + if (fac) { + if (hd->dvert) { + fac *= defvert_find_weight(&hd->dvert[j], hd->defgrp_index); + } - return fac; + if (fac) { + float co_tmp[3]; + mul_v3_m4v3(co_tmp, hd->mat, co); + interp_v3_v3v3(co, co, co_tmp, fac); + } + } } static void deformVerts_do(HookModifierData *hmd, Object *ob, DerivedMesh *dm, float (*vertexCos)[3], int numVerts) { bPoseChannel *pchan = BKE_pose_channel_find_name(hmd->object->pose, hmd->subtarget); - float vec[3], mat[4][4], dmat[4][4]; + float dmat[4][4]; int i, *index_pt; - const float falloff_squared = hmd->falloff * hmd->falloff; /* for faster comparisons */ - - MDeformVert *dvert; - int defgrp_index, max_dvert; + struct HookData_cb hd; + if (hmd->curfalloff == NULL) { + /* should never happen, but bad lib linking could cause it */ + hmd->curfalloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + } + + if (hmd->curfalloff) { + curvemapping_initialize(hmd->curfalloff); + } + + /* Generic data needed for applying per-vertex calculations (initialize all members) */ + hd.vertexCos = vertexCos; + modifier_get_vgroup(ob, dm, hmd->name, &hd.dvert, &hd.defgrp_index); + + hd.curfalloff = hmd->curfalloff; + + hd.falloff_type = hmd->falloff_type; + hd.falloff = (hmd->falloff_type == eHook_Falloff_None) ? 0.0f : hmd->falloff; + hd.falloff_sq = SQUARE(hd.falloff); + hd.fac_orig = hmd->force; + + hd.use_falloff = (hd.falloff_sq != 0.0f); + hd.use_uniform = (hmd->flag & MOD_HOOK_UNIFORM_SPACE) != 0; + + if (hd.use_uniform) { + copy_m3_m4(hd.mat_uniform, hmd->parentinv); + mul_v3_m3v3(hd.cent, hd.mat_uniform, hmd->cent); + } + else { + unit_m3(hd.mat_uniform); /* unused */ + copy_v3_v3(hd.cent, hmd->cent); + } + /* get world-space matrix of target, corrected for the space the verts are in */ if (hmd->subtarget[0] && pchan) { /* bone target if there's a matching pose-channel */ @@ -156,10 +304,9 @@ static void deformVerts_do(HookModifierData *hmd, Object *ob, DerivedMesh *dm, copy_m4_m4(dmat, hmd->object->obmat); } invert_m4_m4(ob->imat, ob->obmat); - mul_m4_series(mat, ob->imat, dmat, hmd->parentinv); + mul_m4_series(hd.mat, ob->imat, dmat, hmd->parentinv); + /* --- done with 'hd' init --- */ - modifier_get_vgroup(ob, dm, hmd->name, &dvert, &defgrp_index); - max_dvert = (dvert) ? numVerts : 0; /* Regarding index range checking below. * @@ -168,13 +315,11 @@ static void deformVerts_do(HookModifierData *hmd, Object *ob, DerivedMesh *dm, * indices that are out of range because old blender did * not correct them on exit editmode. - zr */ - + if (hmd->force == 0.0f) { /* do nothing, avoid annoying checks in the loop */ } else if (hmd->indexar) { /* vertex indices? */ - const float fac_orig = hmd->force; - float fac; const int *origindex_ar; /* if DerivedMesh is present and has original index data, use it */ @@ -185,16 +330,7 @@ static void deformVerts_do(HookModifierData *hmd, Object *ob, DerivedMesh *dm, for (j = 0; j < numVerts; j++) { if (origindex_ar[j] == *index_pt) { - float *co = vertexCos[j]; - if ((fac = hook_falloff(hmd->cent, co, falloff_squared, fac_orig))) { - if (dvert) - fac *= defvert_find_weight(dvert + j, defgrp_index); - - if (fac) { - mul_v3_m4v3(vec, mat, co); - interp_v3_v3v3(co, co, vec, fac); - } - } + hook_co_apply(&hd, j); } } } @@ -203,34 +339,14 @@ static void deformVerts_do(HookModifierData *hmd, Object *ob, DerivedMesh *dm, else { /* missing dm or ORIGINDEX */ for (i = 0, index_pt = hmd->indexar; i < hmd->totindex; i++, index_pt++) { if (*index_pt < numVerts) { - float *co = vertexCos[*index_pt]; - if ((fac = hook_falloff(hmd->cent, co, falloff_squared, fac_orig))) { - if (dvert) - fac *= defvert_find_weight(dvert + (*index_pt), defgrp_index); - - if (fac) { - mul_v3_m4v3(vec, mat, co); - interp_v3_v3v3(co, co, vec, fac); - } - } + hook_co_apply(&hd, *index_pt); } } } } - else if (dvert) { /* vertex group hook */ - const float fac_orig = hmd->force; - - for (i = 0; i < max_dvert; i++, dvert++) { - float fac; - float *co = vertexCos[i]; - - if ((fac = hook_falloff(hmd->cent, co, falloff_squared, fac_orig))) { - fac *= defvert_find_weight(dvert, defgrp_index); - if (fac) { - mul_v3_m4v3(vec, mat, co); - interp_v3_v3v3(co, co, vec, fac); - } - } + else if (hd.dvert) { /* vertex group hook */ + for (i = 0; i < numVerts; i++) { + hook_co_apply(&hd, i); } } } diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index 6f33bfa73fc..93cd92196b2 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -466,13 +466,13 @@ static void validate_solution(LaplacianSystem *sys, short flag, float lambda, fl if (sys->zerola[i] == 0) { lam = sys->numNeEd[i] == sys->numNeFa[i] ? (lambda >= 0.0f ? 1.0f : -1.0f) : (lambda_border >= 0.0f ? 1.0f : -1.0f); if (flag & MOD_LAPLACIANSMOOTH_X) { - sys->vertexCos[i][0] += lam * (nlGetVariable(0, i) - sys->vertexCos[i][0]); + sys->vertexCos[i][0] += lam * ((float)nlGetVariable(0, i) - sys->vertexCos[i][0]); } if (flag & MOD_LAPLACIANSMOOTH_Y) { - sys->vertexCos[i][1] += lam * (nlGetVariable(1, i) - sys->vertexCos[i][1]); + sys->vertexCos[i][1] += lam * ((float)nlGetVariable(1, i) - sys->vertexCos[i][1]); } if (flag & MOD_LAPLACIANSMOOTH_Z) { - sys->vertexCos[i][2] += lam * (nlGetVariable(2, i) - sys->vertexCos[i][2]); + sys->vertexCos[i][2] += lam * ((float)nlGetVariable(2, i) - sys->vertexCos[i][2]); } } } diff --git a/source/blender/modifiers/intern/MOD_mask.c b/source/blender/modifiers/intern/MOD_mask.c index 254ca0bda08..174f3df73d4 100644 --- a/source/blender/modifiers/intern/MOD_mask.c +++ b/source/blender/modifiers/intern/MOD_mask.c @@ -99,7 +99,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, const bool found_test = (mmd->flag & MOD_MASK_INV) == 0; DerivedMesh *result = NULL; GHash *vertHash = NULL, *edgeHash, *polyHash; - GHashIterator *hashIter; + GHashIterator gh_iter; MDeformVert *dvert, *dv; int numPolys = 0, numLoops = 0, numEdges = 0, numVerts = 0; int maxVerts, maxEdges, maxPolys; @@ -291,14 +291,11 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, /* using ghash-iterators, map data into new mesh */ /* vertices */ - for (hashIter = BLI_ghashIterator_new(vertHash); - BLI_ghashIterator_done(hashIter) == false; - BLI_ghashIterator_step(hashIter)) - { + GHASH_ITER (gh_iter, vertHash) { MVert source; MVert *dest; - int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); - int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); + int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(&gh_iter)); + int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); dm->getVert(dm, oldIndex, &source); dest = &mvert_new[newIndex]; @@ -306,17 +303,13 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DM_copy_vert_data(dm, result, oldIndex, newIndex, 1); *dest = source; } - BLI_ghashIterator_free(hashIter); /* edges */ - for (hashIter = BLI_ghashIterator_new(edgeHash); - BLI_ghashIterator_done(hashIter) == false; - BLI_ghashIterator_step(hashIter)) - { + GHASH_ITER (gh_iter, edgeHash) { MEdge source; MEdge *dest; - int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); - int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); + int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(&gh_iter)); + int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); dm->getEdge(dm, oldIndex, &source); dest = &medge_new[newIndex]; @@ -327,15 +320,11 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DM_copy_edge_data(dm, result, oldIndex, newIndex, 1); *dest = source; } - BLI_ghashIterator_free(hashIter); /* faces */ - for (hashIter = BLI_ghashIterator_new(polyHash); - BLI_ghashIterator_done(hashIter) == false; - BLI_ghashIterator_step(hashIter)) - { - int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(hashIter)); - int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(hashIter)); + GHASH_ITER (gh_iter, polyHash) { + int oldIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(&gh_iter)); + int newIndex = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); MPoly *source = &mpoly[oldIndex]; MPoly *dest = &mpoly_new[newIndex]; int oldLoopIndex = source->loopstart; @@ -354,8 +343,6 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, } } - BLI_ghashIterator_free(hashIter); - MEM_freeN(loop_mapping); /* why is this needed? - campbell */ diff --git a/source/blender/modifiers/intern/MOD_meshcache_pc2.c b/source/blender/modifiers/intern/MOD_meshcache_pc2.c index 679bdcd1d47..219eae4ecca 100644 --- a/source/blender/modifiers/intern/MOD_meshcache_pc2.c +++ b/source/blender/modifiers/intern/MOD_meshcache_pc2.c @@ -57,7 +57,7 @@ static bool meshcache_read_pc2_head(FILE *fp, const int verts_tot, return false; } - if (strcmp(pc2_head->header, "POINTCACHE2") != 0) { + if (!STREQ(pc2_head->header, "POINTCACHE2")) { *err_str = "Invalid header"; return false; } diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c new file mode 100644 index 00000000000..58ef850bb4f --- /dev/null +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -0,0 +1,501 @@ +/* + * ***** 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. + * + * Contributor(s): Bastien Montagne + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_normal_edit.c + * \ingroup modifiers + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_bitmap.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_mesh.h" +#include "BKE_deform.h" + +#include "depsgraph_private.h" + +#include "MOD_util.h" + + +static void generate_vert_coordinates( + DerivedMesh *dm, Object *ob, Object *ob_center, const float offset[3], + const int num_verts, float (*r_cos)[3], float r_size[3]) +{ + float min_co[3], max_co[3]; + float diff[3]; + bool do_diff = false; + + INIT_MINMAX(min_co, max_co); + + dm->getVertCos(dm, r_cos); + + /* Get size (i.e. deformation of the spheroid generating normals), either from target object, or own geometry. */ + if (ob_center) { + copy_v3_v3(r_size, ob_center->size); + } + else { + minmax_v3v3_v3_array(min_co, max_co, r_cos, num_verts); + /* Set size. */ + sub_v3_v3v3(r_size, max_co, min_co); + } + + /* Error checks - we do not want one or more of our sizes to be null! */ + if (is_zero_v3(r_size)) { + r_size[0] = r_size[1] = r_size[2] = 1.0f; + } + else { + CLAMP_MIN(r_size[0], FLT_EPSILON); + CLAMP_MIN(r_size[1], FLT_EPSILON); + CLAMP_MIN(r_size[2], FLT_EPSILON); + } + + if (ob_center) { + /* Translate our coordinates so that center of ob_center is at (0, 0, 0). */ + float mat[4][4]; + + /* Get ob_center coordinates in ob local coordinates. */ + invert_m4_m4(mat, ob_center->obmat); + mul_m4_m4m4(mat, mat, ob->obmat); + copy_v3_v3(diff, mat[3]); + + do_diff = true; + } + else if (!is_zero_v3(offset)) { + negate_v3_v3(diff, offset); + + do_diff = true; + } + /* Else, no need to change coordinates! */ + + if (do_diff) { + int i = num_verts; + while (i--) { + add_v3_v3(r_cos[i], diff); + } + } +} + +/* Note this modifies nos_new in-place. */ +static void mix_normals( + const float mix_factor, MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, + const short mix_mode, + const int num_verts, MLoop *mloop, float (*nos_old)[3], float (*nos_new)[3], const int num_loops) +{ + /* Mix with org normals... */ + float *facs = NULL, *wfac; + float (*no_new)[3], (*no_old)[3]; + int i; + + if (dvert) { + facs = MEM_mallocN(sizeof(*facs) * (size_t)num_loops, __func__); + BKE_defvert_extract_vgroup_to_loopweights( + dvert, defgrp_index, num_verts, mloop, num_loops, facs, use_invert_vgroup); + } + + for (i = num_loops, no_new = nos_new, no_old = nos_old, wfac = facs; i--; no_new++, no_old++, wfac++) { + const float fac = facs ? *wfac * mix_factor : mix_factor; + + switch (mix_mode) { + case MOD_NORMALEDIT_MIX_ADD: + add_v3_v3(*no_new, *no_old); + normalize_v3(*no_new); + break; + case MOD_NORMALEDIT_MIX_SUB: + sub_v3_v3(*no_new, *no_old); + normalize_v3(*no_new); + break; + case MOD_NORMALEDIT_MIX_MUL: + mul_v3_v3(*no_new, *no_old); + normalize_v3(*no_new); + break; + case MOD_NORMALEDIT_MIX_COPY: + break; + } + interp_v3_v3v3_slerp_safe(*no_new, *no_old, *no_new, fac); + } + + MEM_SAFE_FREE(facs); +} + +static void normalEditModifier_do_radial( + NormalEditModifierData *smd, Object *ob, DerivedMesh *dm, + short (*clnors)[2], float (*loopnors)[3], float (*polynors)[3], + const short mix_mode, const float mix_factor, + MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, + MVert *mvert, const int num_verts, MEdge *medge, const int num_edges, + MLoop *mloop, const int num_loops, MPoly *mpoly, const int num_polys) +{ + int i; + + float (*cos)[3] = MEM_mallocN(sizeof(*cos) * num_verts, __func__); + float (*nos)[3] = MEM_mallocN(sizeof(*nos) * num_loops, __func__); + float size[3]; + + BLI_bitmap *done_verts = BLI_BITMAP_NEW((size_t)num_verts, __func__); + + generate_vert_coordinates(dm, ob, smd->target, smd->offset, num_verts, cos, size); + + /** + * size gives us our spheroid coefficients ``(A, B, C)``. + * Then, we want to find out for each vert its (a, b, c) triple (proportional to (A, B, C) one). + * + * Ellipsoid basic equation: ``(x^2/a^2) + (y^2/b^2) + (z^2/c^2) = 1.`` + * Since we want to find (a, b, c) matching this equation and proportional to (A, B, C), we can do: + * <pre> + * m = B / A + * n = C / A + * </pre> + * + * hence: + * <pre> + * (x^2/a^2) + (y^2/b^2) + (z^2/c^2) = 1 + * -> b^2*c^2*x^2 + a^2*c^2*y^2 + a^2*b^2*z^2 = a^2*b^2*c^2 + * b = ma + * c = na + * -> m^2*a^2*n^2*a^2*x^2 + a^2*n^2*a^2*y^2 + a^2*m^2*a^2*z^2 = a^2*m^2*a^2*n^2*a^2 + * -> m^2*n^2*a^4*x^2 + n^2*a^4*y^2 + m^2*a^4*z^2 = m^2*n^2*a^6 + * -> a^2 = (m^2*n^2*x^2 + n^2y^2 + m^2z^2) / (m^2*n^2) = x^2 + (y^2 / m^2) + (z^2 / n^2) + * -> b^2 = (m^2*n^2*x^2 + n^2y^2 + m^2z^2) / (n^2) = (m^2 * x^2) + y^2 + (m^2 * z^2 / n^2) + * -> c^2 = (m^2*n^2*x^2 + n^2y^2 + m^2z^2) / (m^2) = (n^2 * x^2) + (n^2 * y^2 / m^2) + z^2 + * </pre> + * + * All we have to do now is compute normal of the spheroid at that point: + * <pre> + * n = (x / a^2, y / b^2, z / c^2) + * </pre> + * And we are done! + */ + { + const float a = size[0], b = size[1], c = size[2]; + const float m2 = (b * b) / (a * a); + const float n2 = (c * c) / (a * a); + + MLoop *ml; + float (*no)[3]; + + /* We reuse cos to now store the ellipsoid-normal of the verts! */ + for (i = num_loops, ml = mloop, no = nos; i-- ; ml++, no++) { + const int vidx = ml->v; + float *co = cos[vidx]; + + if (!BLI_BITMAP_TEST(done_verts, vidx)) { + const float x2 = co[0] * co[0]; + const float y2 = co[1] * co[1]; + const float z2 = co[2] * co[2]; + const float a2 = x2 + (y2 / m2) + (z2 / n2); + const float b2 = (m2 * x2) + y2 + (m2 * z2 / n2); + const float c2 = (n2 * x2) + (n2 * y2 / m2) + z2; + + co[0] /= a2; + co[1] /= b2; + co[2] /= c2; + normalize_v3(co); + + BLI_BITMAP_ENABLE(done_verts, vidx); + } + copy_v3_v3(*no, co); + } + } + + if (loopnors) { + mix_normals(mix_factor, dvert, defgrp_index, use_invert_vgroup, + mix_mode, num_verts, mloop, loopnors, nos, num_loops); + } + + BKE_mesh_normals_loop_custom_set(mvert, num_verts, medge, num_edges, mloop, nos, num_loops, + mpoly, (const float(*)[3])polynors, num_polys, clnors); + + MEM_freeN(cos); + MEM_freeN(nos); + MEM_freeN(done_verts); +} + +static void normalEditModifier_do_directional( + NormalEditModifierData *smd, Object *ob, DerivedMesh *dm, + short (*clnors)[2], float (*loopnors)[3], float (*polynors)[3], + const short mix_mode, const float mix_factor, + MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, + MVert *mvert, const int num_verts, MEdge *medge, const int num_edges, + MLoop *mloop, const int num_loops, MPoly *mpoly, const int num_polys) +{ + const bool use_parallel_normals = (smd->flag & MOD_NORMALEDIT_USE_DIRECTION_PARALLEL) != 0; + + float (*cos)[3] = MEM_mallocN(sizeof(*cos) * num_verts, __func__); + float (*nos)[3] = MEM_mallocN(sizeof(*nos) * num_loops, __func__); + + float target_co[3]; + int i; + + dm->getVertCos(dm, cos); + + /* Get target's center coordinates in ob local coordinates. */ + { + float mat[4][4]; + + invert_m4_m4(mat, ob->obmat); + mul_m4_m4m4(mat, mat, smd->target->obmat); + copy_v3_v3(target_co, mat[3]); + } + + if (use_parallel_normals) { + float no[3]; + + sub_v3_v3v3(no, target_co, smd->offset); + normalize_v3(no); + + for (i = num_loops; i--; ) { + copy_v3_v3(nos[i], no); + } + } + else { + BLI_bitmap *done_verts = BLI_BITMAP_NEW((size_t)num_verts, __func__); + MLoop *ml; + float (*no)[3]; + + /* We reuse cos to now store the 'to target' normal of the verts! */ + for (i = num_loops, no = nos, ml = mloop; i--; no++, ml++) { + const int vidx = ml->v; + float *co = cos[vidx]; + + if (!BLI_BITMAP_TEST(done_verts, vidx)) { + sub_v3_v3v3(co, target_co, co); + normalize_v3(co); + + BLI_BITMAP_ENABLE(done_verts, vidx); + } + + copy_v3_v3(*no, co); + } + + MEM_freeN(done_verts); + } + + if (loopnors) { + mix_normals(mix_factor, dvert, defgrp_index, use_invert_vgroup, + mix_mode, num_verts, mloop, loopnors, nos, num_loops); + } + + BKE_mesh_normals_loop_custom_set(mvert, num_verts, medge, num_edges, mloop, nos, num_loops, + mpoly, (const float(*)[3])polynors, num_polys, clnors); + + MEM_freeN(cos); + MEM_freeN(nos); +} + +static bool is_valid_target(NormalEditModifierData *smd) +{ + if (smd->mode == MOD_NORMALEDIT_MODE_RADIAL) { + return true; + } + else if ((smd->mode == MOD_NORMALEDIT_MODE_DIRECTIONAL) && smd->target) { + return true; + } + modifier_setError((ModifierData *)smd, "Invalid target settings"); + return false; +} + +static void normalEditModifier_do(NormalEditModifierData *smd, Object *ob, DerivedMesh *dm) +{ + Mesh *me = ob->data; + + const int num_verts = dm->getNumVerts(dm); + const int num_edges = dm->getNumEdges(dm); + const int num_loops = dm->getNumLoops(dm); + const int num_polys = dm->getNumPolys(dm); + MVert *mvert = dm->getVertArray(dm); + MEdge *medge = dm->getEdgeArray(dm); + MLoop *mloop = dm->getLoopArray(dm); + MPoly *mpoly = dm->getPolyArray(dm); + + const bool use_invert_vgroup = ((smd->flag & MOD_NORMALEDIT_INVERT_VGROUP) != 0); + const bool use_current_clnors = !((smd->mix_mode == MOD_NORMALEDIT_MIX_COPY) && + (smd->mix_factor == 1.0f) && + (smd->defgrp_name[0] == '\0')); + + int defgrp_index; + MDeformVert *dvert; + + float (*loopnors)[3] = NULL; + short (*clnors)[2]; + + float (*polynors)[3]; + bool free_polynors = false; + + /* Do not run that modifier at all if autosmooth is disabled! */ + if (!is_valid_target(smd) || !num_loops) { + return; + } + + if (!(me->flag & ME_AUTOSMOOTH)) { + modifier_setError((ModifierData *)smd, "Enable 'Auto Smooth' option in mesh settings"); + return; + } + + + if (use_current_clnors) { + dm->calcLoopNormals(dm, true, me->smoothresh); + loopnors = dm->getLoopDataArray(dm, CD_NORMAL); + } + + clnors = CustomData_duplicate_referenced_layer(&dm->loopData, CD_CUSTOMLOOPNORMAL, num_loops); + if (!clnors) { + DM_add_loop_layer(dm, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL); + clnors = dm->getLoopDataArray(dm, CD_CUSTOMLOOPNORMAL); + } + + polynors = dm->getPolyDataArray(dm, CD_NORMAL); + if (!polynors) { + polynors = MEM_mallocN(sizeof(*polynors) * num_polys, __func__); + BKE_mesh_calc_normals_poly(mvert, num_verts, mloop, mpoly, num_loops, num_polys, polynors, false); + free_polynors = true; + } + + modifier_get_vgroup(ob, dm, smd->defgrp_name, &dvert, &defgrp_index); + + if (smd->mode == MOD_NORMALEDIT_MODE_RADIAL) { + normalEditModifier_do_radial( + smd, ob, dm, clnors, loopnors, polynors, + smd->mix_mode, smd->mix_factor, dvert, defgrp_index, use_invert_vgroup, + mvert, num_verts, medge, num_edges, mloop, num_loops, mpoly, num_polys); + } + else if (smd->mode == MOD_NORMALEDIT_MODE_DIRECTIONAL) { + normalEditModifier_do_directional( + smd, ob, dm, clnors, loopnors, polynors, + smd->mix_mode, smd->mix_factor, dvert, defgrp_index, use_invert_vgroup, + mvert, num_verts, medge, num_edges, mloop, num_loops, mpoly, num_polys); + } + + if (free_polynors) { + MEM_freeN(polynors); + } +} + +static void initData(ModifierData *md) +{ + NormalEditModifierData *smd = (NormalEditModifierData *)md; + + smd->mode = MOD_NORMALEDIT_MODE_RADIAL; + + smd->mix_mode = MOD_NORMALEDIT_MIX_COPY; + smd->mix_factor = 1.0f; +} + +static void copyData(ModifierData *md, ModifierData *target) +{ + modifier_copyData_generic(md, target); +} + +static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md) +{ + NormalEditModifierData *smd = (NormalEditModifierData *)md; + CustomDataMask dataMask = CD_CUSTOMLOOPNORMAL; + + /* Ask for vertexgroups if we need them. */ + if (smd->defgrp_name[0]) { + dataMask |= (CD_MASK_MDEFORMVERT); + } + + return dataMask; +} + +static bool dependsOnNormals(ModifierData *UNUSED(md)) +{ + return true; +} + +static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData) +{ + NormalEditModifierData *smd = (NormalEditModifierData *) md; + + walk(userData, ob, &smd->target); +} + +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + NormalEditModifierData *smd = (NormalEditModifierData *) md; + + walk(userData, ob, (ID **)&smd->target); +} + +static bool isDisabled(ModifierData *md, int UNUSED(useRenderParams)) +{ + NormalEditModifierData *smd = (NormalEditModifierData *)md; + + return !is_valid_target(smd); +} + +static void updateDepgraph(ModifierData *md, DagForest *forest, struct Scene *UNUSED(scene), + Object *UNUSED(ob), DagNode *obNode) +{ + NormalEditModifierData *smd = (NormalEditModifierData *) md; + + if (smd->target) { + DagNode *Node = dag_get_node(forest, smd->target); + + dag_add_relation(forest, Node, obNode, DAG_RL_OB_DATA, "NormalEdit Modifier"); + } +} + +static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *dm, ModifierApplyFlag UNUSED(flag)) +{ + normalEditModifier_do((NormalEditModifierData *)md, ob, dm); + return dm; +} + +ModifierTypeInfo modifierType_NormalEdit = { + /* name */ "Set Split Normals", + /* structName */ "NormalEditModifierData", + /* structSize */ sizeof(NormalEditModifierData), + /* type */ eModifierTypeType_Constructive, + /* flags */ eModifierTypeFlag_AcceptsMesh | + eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_SupportsMapping | + eModifierTypeFlag_SupportsEditmode | + eModifierTypeFlag_EnableInEditmode, + /* copyData */ copyData, + /* deformVerts */ NULL, + /* deformMatrices */ NULL, + /* deformVertsEM */ NULL, + /* deformMatricesEM */ NULL, + /* applyModifier */ applyModifier, + /* applyModifierEM */ NULL, + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepgraph */ updateDepgraph, + /* dependsOnTime */ NULL, + /* dependsOnNormals */ dependsOnNormals, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 56197bfcca8..557d136965b 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -43,6 +43,8 @@ #include "BLI_utildefines.h" #include "BKE_cdderivedmesh.h" +#include "BKE_effect.h" +#include "BKE_global.h" #include "BKE_lattice.h" #include "BKE_modifier.h" #include "BKE_particle.h" @@ -183,7 +185,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, int maxvert, maxpoly, maxloop, totpart = 0, first_particle = 0; int k, p, p_skip; short track = ob->trackflag % 3, trackneg, axis = pimd->axis; - float max_co = 0.0, min_co = 0.0, temp_co[3], cross[3]; + float max_co = 0.0, min_co = 0.0, temp_co[3]; float *size = NULL; trackneg = ((ob->trackflag > 2) ? 1 : 0); @@ -276,6 +278,9 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, orig_mloop = dm->getLoopArray(dm); for (p = 0, p_skip = 0; p < totpart; p++) { + float prev_dir[3]; + float frame[4]; /* frame orientation quaternion */ + /* skip particle? */ if (particle_skip(pimd, psys, p)) continue; @@ -321,19 +326,55 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, normalize_v3(state.vel); - /* TODO: incremental rotations somehow */ + /* Incrementally Rotating Frame (Bishop Frame) */ + if (k == 0) { + float hairmat[4][4]; + float mat[3][3]; + + if (first_particle + p < psys->totpart) + pa = psys->particles + first_particle + p; + else { + ChildParticle *cpa = psys->child + (p - psys->totpart); + pa = psys->particles + cpa->parent; + } + psys_mat_hair_to_global(sim.ob, sim.psmd->dm, sim.psys->part->from, pa, hairmat); + copy_m3_m4(mat, hairmat); + /* to quaternion */ + mat3_to_quat(frame, mat); + + /* note: direction is same as normal vector currently, + * but best to keep this separate so the frame can be + * rotated later if necessary + */ + copy_v3_v3(prev_dir, state.vel); + } + else { + float rot[4]; + + /* incrementally rotate along bend direction */ + rotation_between_vecs_to_quat(rot, prev_dir, state.vel); + mul_qt_qtqt(frame, rot, frame); + + copy_v3_v3(prev_dir, state.vel); + } + + copy_qt_qt(state.rot, frame); +#if 0 + /* Absolute Frame (Frenet Frame) */ if (state.vel[axis] < -0.9999f || state.vel[axis] > 0.9999f) { unit_qt(state.rot); } else { + float cross[3]; float temp[3] = {0.0f, 0.0f, 0.0f}; temp[axis] = 1.0f; - + cross_v3_v3v3(cross, temp, state.vel); - + /* state.vel[axis] is the only component surviving from a dot product with the axis */ axis_angle_to_quat(state.rot, cross, saacos(state.vel[axis])); } +#endif } else { state.time = -1.0; diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index a41fbb03462..9860daeda4c 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -85,37 +85,7 @@ static void copyData(ModifierData *md, ModifierData *target) static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md) { ParticleSystemModifierData *psmd = (ParticleSystemModifierData *) md; - CustomDataMask dataMask = 0; - MTex *mtex; - int i; - - if (!psmd->psys->part) - return 0; - - for (i = 0; i < MAX_MTEX; i++) { - mtex = psmd->psys->part->mtex[i]; - if (mtex && mtex->mapto && (mtex->texco & TEXCO_UV)) - dataMask |= CD_MASK_MTFACE; - } - - if (psmd->psys->part->tanfac != 0.0f) - dataMask |= CD_MASK_MTFACE; - - /* ask for vertexgroups if we need them */ - for (i = 0; i < PSYS_TOT_VG; i++) { - if (psmd->psys->vgroup[i]) { - dataMask |= CD_MASK_MDEFORMVERT; - break; - } - } - - /* particles only need this if they are after a non deform modifier, and - * the modifier stack will only create them in that case. */ - dataMask |= CD_MASK_ORIGSPACE_MLOOP | CD_MASK_ORIGINDEX; - - dataMask |= CD_MASK_ORCO; - - return dataMask; + return psys_emitter_customdata_mask(psmd->psys); } /* saves the current emitter state for a particle system and calculates particles */ diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 228c42e526c..2f6297371b2 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -159,8 +159,8 @@ DerivedMesh *get_cddm(Object *ob, struct BMEditMesh *em, DerivedMesh *dm, float if (dm) { if (dm->type != DM_TYPE_CDDM) { dm = CDDM_copy(dm); - CDDM_apply_vert_coords(dm, vertexCos); } + CDDM_apply_vert_coords(dm, vertexCos); if (use_normals) { DM_ensure_normals(dm); @@ -305,6 +305,7 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(LaplacianDeform); INIT_TYPE(Wireframe); INIT_TYPE(DataTransfer); + INIT_TYPE(NormalEdit); INIT_TYPE(PointCache); #undef INIT_TYPE } diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index f6714e79401..3eb34e56700 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -167,6 +167,7 @@ static void warpModifier_do(WarpModifierData *wmd, Object *ob, float tmat[4][4]; + const float falloff_radius_sq = SQUARE(wmd->falloff_radius); float strength = wmd->strength; float fac = 1.0f, weight; int i; @@ -179,6 +180,9 @@ static void warpModifier_do(WarpModifierData *wmd, Object *ob, return; modifier_get_vgroup(ob, dm, wmd->defgrp_name, &dvert, &defgrp_index); + if (dvert == NULL) { + defgrp_index = -1; + } if (wmd->curfalloff == NULL) /* should never happen, but bad lib linking could cause it */ wmd->curfalloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); @@ -222,17 +226,15 @@ static void warpModifier_do(WarpModifierData *wmd, Object *ob, float *co = vertexCos[i]; if (wmd->falloff_type == eWarp_Falloff_None || - ((fac = len_v3v3(co, mat_from[3])) < wmd->falloff_radius && - (fac = (wmd->falloff_radius - fac) / wmd->falloff_radius))) + ((fac = len_squared_v3v3(co, mat_from[3])) < falloff_radius_sq && + (fac = (wmd->falloff_radius - sqrtf(fac)) / wmd->falloff_radius))) { /* skip if no vert group found */ - if (dvert && defgrp_index != -1) { + if (defgrp_index != -1) { dv = &dvert[i]; - - if (dv) { - weight = defvert_find_weight(dv, defgrp_index) * strength; - if (weight <= 0.0f) /* Should never occure... */ - continue; + weight = defvert_find_weight(dv, defgrp_index) * strength; + if (weight <= 0.0f) { + continue; } } @@ -263,6 +265,9 @@ static void warpModifier_do(WarpModifierData *wmd, Object *ob, case eWarp_Falloff_Sphere: fac = sqrtf(2 * fac - fac * fac); break; + case eWarp_Falloff_InvSquare: + fac = fac * (2.0f - fac); + break; } fac *= weight; @@ -274,27 +279,29 @@ static void warpModifier_do(WarpModifierData *wmd, Object *ob, fac *= texres.tin; } - /* into the 'from' objects space */ - mul_m4_v3(mat_from_inv, co); + if (fac != 0.0f) { + /* into the 'from' objects space */ + mul_m4_v3(mat_from_inv, co); - if (fac >= 1.0f) { - mul_m4_v3(mat_final, co); - } - else if (fac > 0.0f) { - if (wmd->flag & MOD_WARP_VOLUME_PRESERVE) { - /* interpolate the matrix for nicer locations */ - blend_m4_m4m4(tmat, mat_unit, mat_final, fac); - mul_m4_v3(tmat, co); + if (fac == 1.0f) { + mul_m4_v3(mat_final, co); } else { - float tvec[3]; - mul_v3_m4v3(tvec, mat_final, co); - interp_v3_v3v3(co, co, tvec, fac); + if (wmd->flag & MOD_WARP_VOLUME_PRESERVE) { + /* interpolate the matrix for nicer locations */ + blend_m4_m4m4(tmat, mat_unit, mat_final, fac); + mul_m4_v3(tmat, co); + } + else { + float tvec[3]; + mul_v3_m4v3(tvec, mat_final, co); + interp_v3_v3v3(co, co, tvec, fac); + } } - } - /* out of the 'from' objects space */ - mul_m4_v3(mat_from, co); + /* out of the 'from' objects space */ + mul_m4_v3(mat_from, co); + } } } diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index 8b611f9e6da..aa5e6116516 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -74,7 +74,7 @@ static bool dependsOnNormals(ModifierData *UNUSED(md)) return true; } -static DerivedMesh *WireframeModifier_do( WireframeModifierData *wmd, Object *ob, DerivedMesh *dm) +static DerivedMesh *WireframeModifier_do(WireframeModifierData *wmd, Object *ob, DerivedMesh *dm) { DerivedMesh *result; BMesh *bm; diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c index ecc02e732ce..e8845b543b8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.c @@ -439,7 +439,7 @@ static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr) static int node_composit_poll_rlayers(bNodeType *UNUSED(ntype), bNodeTree *ntree) { - if (strcmp(ntree->idname, "CompositorNodeTree") == 0) { + if (STREQ(ntree->idname, "CompositorNodeTree")) { Scene *scene; /* XXX ugly: check if ntree is a local scene node tree. diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c index 7a15d6364dc..415427c360f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c +++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c @@ -47,7 +47,8 @@ static bNodeSocketTemplate cmp_node_planetrackdeform_out[] = { static void init(bNodeTree *UNUSED(ntree), bNode *node) { NodePlaneTrackDeformData *data = MEM_callocN(sizeof(NodePlaneTrackDeformData), "node plane track deform data"); - + data->motion_blur_samples = 16; + data->motion_blur_shutter = 0.5f; node->storage = data; } diff --git a/source/blender/nodes/intern/node_socket.c b/source/blender/nodes/intern/node_socket.c index 282567b797f..01c642b063d 100644 --- a/source/blender/nodes/intern/node_socket.c +++ b/source/blender/nodes/intern/node_socket.c @@ -108,7 +108,7 @@ static bNodeSocket *verify_socket_template(bNodeTree *ntree, bNode *node, int in bNodeSocket *sock; for (sock = socklist->first; sock; sock = sock->next) { - if (strncmp(sock->name, stemp->name, NODE_MAXSTR) == 0) + if (STREQLEN(sock->name, stemp->name, NODE_MAXSTR)) break; } if (sock) { diff --git a/source/blender/nodes/shader/nodes/node_shader_gamma.c b/source/blender/nodes/shader/nodes/node_shader_gamma.c index 503fe034754..0264abe451f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_gamma.c +++ b/source/blender/nodes/shader/nodes/node_shader_gamma.c @@ -48,9 +48,9 @@ static void node_shader_exec_gamma(void *UNUSED(data), int UNUSED(thread), bNode nodestack_get_vec(col, SOCK_VECTOR, in[0]); nodestack_get_vec(&gamma, SOCK_FLOAT, in[1]); - out[0]->vec[0] = col[0] > 0.0 ? pow(col[0], gamma) : col[0]; - out[0]->vec[1] = col[1] > 0.0 ? pow(col[1], gamma) : col[1]; - out[0]->vec[2] = col[2] > 0.0 ? pow(col[2], gamma) : col[2]; + out[0]->vec[0] = col[0] > 0.0f ? powf(col[0], gamma) : col[0]; + out[0]->vec[1] = col[1] > 0.0f ? powf(col[1], gamma) : col[1]; + out[0]->vec[2] = col[2] > 0.0f ? powf(col[2], gamma) : col[2]; } static int node_shader_gpu_gamma(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) diff --git a/source/blender/nodes/shader/nodes/node_shader_geom.c b/source/blender/nodes/shader/nodes/node_shader_geom.c index 7c598f92bed..2fb03536bbe 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geom.c +++ b/source/blender/nodes/shader/nodes/node_shader_geom.c @@ -63,7 +63,7 @@ static void node_shader_exec_geom(void *data, int UNUSED(thread), bNode *node, b if (ngeo->uvname[0]) { /* find uv map by name */ for (i = 0; i < shi->totuv; i++) { - if (strcmp(shi->uv[i].name, ngeo->uvname) == 0) { + if (STREQ(shi->uv[i].name, ngeo->uvname)) { suv = &shi->uv[i]; break; } @@ -84,7 +84,7 @@ static void node_shader_exec_geom(void *data, int UNUSED(thread), bNode *node, b if (ngeo->colname[0]) { for (i = 0; i < shi->totcol; i++) { - if (strcmp(shi->col[i].name, ngeo->colname) == 0) { + if (STREQ(shi->col[i].name, ngeo->colname)) { scol = &shi->col[i]; break; } diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.c b/source/blender/nodes/shader/nodes/node_shader_geometry.c index 01851b51c27..553ea65154f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geometry.c +++ b/source/blender/nodes/shader/nodes/node_shader_geometry.c @@ -37,6 +37,7 @@ static bNodeSocketTemplate sh_node_geometry_out[] = { { SOCK_VECTOR, 0, N_("Incoming"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_VECTOR, 0, N_("Parametric"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Backfacing"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 0, N_("Pointiness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { -1, 0, "" } }; diff --git a/source/blender/nodes/shader/nodes/node_shader_lamp.c b/source/blender/nodes/shader/nodes/node_shader_lamp.c index adf53ba9b52..3b000d49822 100644 --- a/source/blender/nodes/shader/nodes/node_shader_lamp.c +++ b/source/blender/nodes/shader/nodes/node_shader_lamp.c @@ -62,11 +62,11 @@ static int gpu_shader_lamp(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED( { if (node->id) { GPULamp *lamp = GPU_lamp_from_blender(GPU_material_scene(mat), (Object *)node->id, NULL); - GPUNodeLink *col, *lv, *dist, *visifac, *shadow; + GPUNodeLink *col, *lv, *dist, *visifac, *shadow, *energy; - visifac = GPU_lamp_get_data(mat, lamp, &col, &lv, &dist, &shadow); + visifac = GPU_lamp_get_data(mat, lamp, &col, &lv, &dist, &shadow, &energy); - return GPU_stack_link(mat, "lamp", in, out, col, lv, dist, shadow, visifac); + return GPU_stack_link(mat, "lamp", in, out, col, energy, lv, dist, shadow, visifac); } return 0; diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c index fd864f1c7c6..9fe56d4f01d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c @@ -51,7 +51,7 @@ static void node_shader_init_subsurface_scattering(bNodeTree *UNUSED(ntree), bNo node->custom1 = SHD_SUBSURFACE_CUBIC; /*for (sock = node->inputs.first; sock; sock = sock->next) { - if (strcmp(sock->name, "Sharpness") == 0) { + if (STREQ(sock->name, "Sharpness")) { bNodeSocketValueFloat *dval = sock->default_value; dval->value = 0.0f; } @@ -72,7 +72,7 @@ static void node_shader_update_subsurface_scattering(bNodeTree *UNUSED(ntree), b int falloff = node->custom1; for (sock = node->inputs.first; sock; sock = sock->next) { - if (strcmp(sock->name, "Sharpness") == 0) { + if (STREQ(sock->name, "Sharpness")) { if (falloff == SHD_SUBSURFACE_CUBIC) sock->flag &= ~SOCK_UNAVAIL; else diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.c b/source/blender/nodes/shader/nodes/node_shader_tex_coord.c index 85eca6ae990..be393582a42 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.c @@ -51,12 +51,14 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, bNode *UNUSED(node), bNod if (type == GPU_MATERIAL_TYPE_MESH) { return GPU_stack_link(mat, "node_tex_coord", in, out, GPU_builtin(GPU_VIEW_POSITION), GPU_builtin(GPU_VIEW_NORMAL), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), orco, mtface); + GPU_builtin(GPU_INVERSE_VIEW_MATRIX), GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), + GPU_builtin(GPU_CAMERA_TEXCO_FACTORS), orco, mtface); } else { return GPU_stack_link(mat, "node_tex_coord_background", in, out, GPU_builtin(GPU_VIEW_POSITION), GPU_builtin(GPU_VIEW_NORMAL), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), orco, mtface); + GPU_builtin(GPU_INVERSE_VIEW_MATRIX), GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), + GPU_builtin(GPU_CAMERA_TEXCO_FACTORS), orco, mtface); } } diff --git a/source/blender/nodes/texture/nodes/node_texture_math.c b/source/blender/nodes/texture/nodes/node_texture_math.c index 1984ee506e0..19bc16fb82d 100644 --- a/source/blender/nodes/texture/nodes/node_texture_math.c +++ b/source/blender/nodes/texture/nodes/node_texture_math.c @@ -195,6 +195,10 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor break; } } + + if (node->custom2 & SHD_MATH_CLAMP) { + CLAMP(*out, 0.0f, 1.0f); + } } static void exec(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out) diff --git a/source/blender/physics/BPH_mass_spring.h b/source/blender/physics/BPH_mass_spring.h new file mode 100644 index 00000000000..c189dea28a4 --- /dev/null +++ b/source/blender/physics/BPH_mass_spring.h @@ -0,0 +1,62 @@ +/* + * ***** 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) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BPH_MASS_SPRING_H__ +#define __BPH_MASS_SPRING_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct Implicit_Data; +struct Object; +struct ClothModifierData; +struct ListBase; +struct VoxelData; + +typedef enum eMassSpringSolverStatus { + BPH_SOLVER_SUCCESS = (1 << 0), + BPH_SOLVER_NUMERICAL_ISSUE = (1 << 1), + BPH_SOLVER_NO_CONVERGENCE = (1 << 2), + BPH_SOLVER_INVALID_INPUT = (1 << 3), +} eMassSpringSolverStatus; + +struct Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings); +void BPH_mass_spring_solver_free(struct Implicit_Data *id); + +int BPH_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd); +void BPH_cloth_solver_free(struct ClothModifierData *clmd); +int BPH_cloth_solve(struct Object *ob, float frame, struct ClothModifierData *clmd, struct ListBase *effectors); +void BKE_cloth_solver_set_positions(struct ClothModifierData *clmd); + +bool BPH_cloth_solver_get_texture_data(struct Object *ob, struct ClothModifierData *clmd, struct VoxelData *vd); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/blender/physics/CMakeLists.txt b/source/blender/physics/CMakeLists.txt new file mode 100644 index 00000000000..08ff1faca49 --- /dev/null +++ b/source/blender/physics/CMakeLists.txt @@ -0,0 +1,53 @@ +# ***** 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. +# +# The Original Code is: all of this file. +# +# Contributor(s): Lukas Toenne +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + intern + ../blenlib + ../blenkernel + ../imbuf + ../makesdna + ../../../intern/guardedalloc + ../../../extern/Eigen3 +) + +set(INC_SYS + +) + +set(SRC + intern/BPH_mass_spring.cpp + intern/ConstrainedConjugateGradient.h + intern/hair_volume.cpp + intern/implicit.h + intern/implicit_blender.c + intern/implicit_eigen.cpp + intern/eigen_utils.h + + BPH_mass_spring.h +) + +blender_add_lib(bf_physics "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/physics/SConscript b/source/blender/physics/SConscript new file mode 100644 index 00000000000..c8165886cab --- /dev/null +++ b/source/blender/physics/SConscript @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# +# ***** 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) 2011, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Jeroen Bakker, Monique Dewanchand, Blender Developers Fund. +# +# ***** END GPL LICENSE BLOCK ***** + +Import ('env') +defs = [] + +sources = env.Glob('intern/*.cpp') + env.Glob('intern/*.c') + +incs = [ + '.', + 'intern', + '../blenlib', + '../blenkernel', + '../imbuf', + '../makesdna', + '../../../intern/guardedalloc', + '../../../extern/Eigen3', + ] + +env.BlenderLib('bf_physics', sources, incs, defines=defs, libtype=['core', 'player'], priority=[180, 190]) diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp new file mode 100644 index 00000000000..6cddbc6aa46 --- /dev/null +++ b/source/blender/physics/intern/BPH_mass_spring.cpp @@ -0,0 +1,1017 @@ +/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/physics/intern/BPH_mass_spring.cpp
+ * \ingroup bph
+ */
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_cloth_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BLI_math.h"
+#include "BLI_linklist.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_cloth.h"
+#include "BKE_collision.h"
+#include "BKE_effect.h"
+}
+
+#include "BPH_mass_spring.h"
+#include "implicit.h"
+
+static float I3[3][3] = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};
+
+/* Number of off-diagonal non-zero matrix blocks.
+ * Basically there is one of these for each vertex-vertex interaction.
+ */
+static int cloth_count_nondiag_blocks(Cloth *cloth)
+{
+ LinkNode *link;
+ int nondiag = 0;
+
+ for (link = cloth->springs; link; link = link->next) {
+ ClothSpring *spring = (ClothSpring *)link->link;
+ switch (spring->type) {
+ case CLOTH_SPRING_TYPE_BENDING_ANG:
+ /* angular bending combines 3 vertices */
+ nondiag += 3;
+ break;
+
+ default:
+ /* all other springs depend on 2 vertices only */
+ nondiag += 1;
+ break;
+ }
+ }
+
+ return nondiag;
+}
+
+int BPH_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
+{
+ Cloth *cloth = clmd->clothObject;
+ ClothVertex *verts = cloth->verts;
+ const float ZERO[3] = {0.0f, 0.0f, 0.0f};
+ Implicit_Data *id;
+ unsigned int i, nondiag;
+
+ nondiag = cloth_count_nondiag_blocks(cloth);
+ cloth->implicit = id = BPH_mass_spring_solver_create(cloth->numverts, nondiag);
+
+ for (i = 0; i < cloth->numverts; i++) {
+ BPH_mass_spring_set_vertex_mass(id, i, verts[i].mass);
+ }
+
+ for (i = 0; i < cloth->numverts; i++) {
+ BPH_mass_spring_set_motion_state(id, i, verts[i].x, ZERO);
+ }
+
+ return 1;
+}
+
+void BPH_cloth_solver_free(ClothModifierData *clmd)
+{
+ Cloth *cloth = clmd->clothObject;
+
+ if (cloth->implicit) {
+ BPH_mass_spring_solver_free(cloth->implicit);
+ cloth->implicit = NULL;
+ }
+}
+
+void BKE_cloth_solver_set_positions(ClothModifierData *clmd)
+{
+ Cloth *cloth = clmd->clothObject;
+ ClothVertex *verts = cloth->verts;
+ unsigned int numverts = cloth->numverts, i;
+ ClothHairData *cloth_hairdata = clmd->hairdata;
+ Implicit_Data *id = cloth->implicit;
+
+ for (i = 0; i < numverts; i++) {
+ if (cloth_hairdata) {
+ ClothHairData *root = &cloth_hairdata[i];
+ BPH_mass_spring_set_rest_transform(id, i, root->rot);
+ }
+ else
+ BPH_mass_spring_set_rest_transform(id, i, I3);
+
+ BPH_mass_spring_set_motion_state(id, i, verts[i].x, verts[i].v);
+ }
+}
+
+static bool collision_response(ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, float dt, float restitution, float r_impulse[3])
+{
+ Cloth *cloth = clmd->clothObject;
+ int index = collpair->ap1;
+ 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 margin_distance = collpair->distance - epsilon2;
+ float mag_v_rel;
+
+ zero_v3(r_impulse);
+
+ if (margin_distance > 0.0f)
+ return false; /* XXX tested before already? */
+
+ /* only handle static collisions here */
+ if ( collpair->flag & COLLISION_IN_FUTURE )
+ return false;
+
+ /* velocity */
+ copy_v3_v3(v1, cloth->verts[index].v);
+ collision_get_collider_velocity(v2_old, v2_new, collmd, collpair);
+ /* relative velocity = velocity of the cloth point relative to the collider */
+ sub_v3_v3v3(v_rel_old, v1, v2_old);
+ sub_v3_v3v3(v_rel_new, v1, v2_new);
+ /* normal component of the relative velocity */
+ mag_v_rel = dot_v3v3(v_rel_old, collpair->normal);
+
+ /* only valid when moving toward the collider */
+ if (mag_v_rel < -ALMOST_ZERO) {
+ float v_nor_old, v_nor_new;
+ float v_tan_old[3], v_tan_new[3];
+ float bounce, repulse;
+
+ /* Collision response based on
+ * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005)
+ * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf
+ */
+
+ v_nor_old = mag_v_rel;
+ v_nor_new = dot_v3v3(v_rel_new, collpair->normal);
+
+ madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old);
+ madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new);
+
+ bounce = -v_nor_old * restitution;
+
+ repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */
+ /* XXX this clamping factor is quite arbitrary ...
+ * not sure if there is a more scientific approach, but seems to give good results
+ */
+ CLAMP(repulse, 0.0f, 4.0f * bounce);
+
+ if (margin_distance < -epsilon2) {
+ mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new);
+ }
+ else {
+ bounce = 0.0f;
+ mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new);
+ }
+
+ result = true;
+ }
+
+ return result;
+}
+
+/* Init constraint matrix
+ * This is part of the modified CG method suggested by Baraff/Witkin in
+ * "Large Steps in Cloth Simulation" (Siggraph 1998)
+ */
+static void cloth_setup_constraints(ClothModifierData *clmd, ColliderContacts *contacts, int totcolliders, float dt)
+{
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ ClothVertex *verts = cloth->verts;
+ int numverts = cloth->numverts;
+ int i, j, v;
+
+ const float ZERO[3] = {0.0f, 0.0f, 0.0f};
+
+ for (v = 0; v < numverts; v++) {
+ if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) {
+ /* pinned vertex constraints */
+ BPH_mass_spring_add_constraint_ndof0(data, v, ZERO); /* velocity is defined externally */
+ }
+
+ verts[v].impulse_count = 0;
+ }
+
+ for (i = 0; i < totcolliders; ++i) {
+ ColliderContacts *ct = &contacts[i];
+ for (j = 0; j < ct->totcollisions; ++j) {
+ CollPair *collpair = &ct->collisions[j];
+// float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp);
+ float restitution = 0.0f;
+ int v = collpair->face1;
+ float impulse[3];
+
+ /* pinned verts handled separately */
+ if (verts[v].flags & CLOTH_VERT_FLAG_PINNED)
+ continue;
+
+ /* XXX cheap way of avoiding instability from multiple collisions in the same step
+ * this should eventually be supported ...
+ */
+ if (verts[v].impulse_count > 0)
+ continue;
+
+ /* calculate collision response */
+ if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse))
+ continue;
+
+ BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse);
+ ++verts[v].impulse_count;
+ }
+ }
+}
+
+/* computes where the cloth would be if it were subject to perfectly stiff edges
+ * (edge distance constraints) in a lagrangian solver. then add forces to help
+ * guide the implicit solver to that state. this function is called after
+ * collisions*/
+static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothModifierData *clmd, float (*initial_cos)[3], float UNUSED(step), float dt)
+{
+ Cloth *cloth= clmd->clothObject;
+ float (*cos)[3] = (float (*)[3])MEM_callocN(sizeof(float)*3*cloth->numverts, "cos cloth_calc_helper_forces");
+ float *masses = (float *)MEM_callocN(sizeof(float)*cloth->numverts, "cos cloth_calc_helper_forces");
+ LinkNode *node;
+ ClothSpring *spring;
+ ClothVertex *cv;
+ int i, steps;
+
+ cv = cloth->verts;
+ for (i=0; i<cloth->numverts; i++, cv++) {
+ copy_v3_v3(cos[i], cv->tx);
+
+ if (cv->goal == 1.0f || len_squared_v3v3(initial_cos[i], cv->tx) != 0.0f) {
+ masses[i] = 1e+10;
+ }
+ else {
+ masses[i] = cv->mass;
+ }
+ }
+
+ steps = 55;
+ for (i=0; i<steps; i++) {
+ for (node=cloth->springs; node; node=node->next) {
+ /* ClothVertex *cv1, *cv2; */ /* UNUSED */
+ int v1, v2;
+ float len, c, l, vec[3];
+
+ spring = (ClothSpring *)node->link;
+ if (spring->type != CLOTH_SPRING_TYPE_STRUCTURAL && spring->type != CLOTH_SPRING_TYPE_SHEAR)
+ continue;
+
+ v1 = spring->ij; v2 = spring->kl;
+ /* cv1 = cloth->verts + v1; */ /* UNUSED */
+ /* cv2 = cloth->verts + v2; */ /* UNUSED */
+ len = len_v3v3(cos[v1], cos[v2]);
+
+ sub_v3_v3v3(vec, cos[v1], cos[v2]);
+ normalize_v3(vec);
+
+ c = (len - spring->restlen);
+ if (c == 0.0f)
+ continue;
+
+ l = c / ((1.0f / masses[v1]) + (1.0f / masses[v2]));
+
+ mul_v3_fl(vec, -(1.0f / masses[v1]) * l);
+ add_v3_v3(cos[v1], vec);
+
+ sub_v3_v3v3(vec, cos[v2], cos[v1]);
+ normalize_v3(vec);
+
+ mul_v3_fl(vec, -(1.0f / masses[v2]) * l);
+ add_v3_v3(cos[v2], vec);
+ }
+ }
+
+ cv = cloth->verts;
+ for (i=0; i<cloth->numverts; i++, cv++) {
+ float vec[3];
+
+ /*compute forces*/
+ sub_v3_v3v3(vec, cos[i], cv->tx);
+ mul_v3_fl(vec, cv->mass*dt*20.0f);
+ add_v3_v3(cv->tv, vec);
+ //copy_v3_v3(cv->tx, cos[i]);
+ }
+
+ MEM_freeN(cos);
+ MEM_freeN(masses);
+
+ return 1;
+}
+
+BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, float time)
+{
+ Cloth *cloth = clmd->clothObject;
+ ClothSimSettings *parms = clmd->sim_parms;
+ Implicit_Data *data = cloth->implicit;
+ ClothVertex *verts = cloth->verts;
+
+ bool no_compress = parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS;
+
+ zero_v3(s->f);
+ zero_m3(s->dfdx);
+ zero_m3(s->dfdv);
+
+ s->flags &= ~CLOTH_SPRING_FLAG_NEEDED;
+
+ // calculate force of structural + shear springs
+ if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR) || (s->type & CLOTH_SPRING_TYPE_SEWING) ) {
+#ifdef CLOTH_FORCE_SPRING_STRUCTURAL
+ float k, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ scaling = parms->structural + s->stiffness * fabsf(parms->max_struct - parms->structural);
+ k = scaling / (parms->avg_spring_len + FLT_EPSILON);
+
+ if (s->type & CLOTH_SPRING_TYPE_SEWING) {
+ // TODO: verify, half verified (couldn't see error)
+ // sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects
+ BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, parms->max_sewing, s->f, s->dfdx, s->dfdv);
+ }
+ else {
+ BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, 0.0f, s->f, s->dfdx, s->dfdv);
+ }
+#endif
+ }
+ else if (s->type & CLOTH_SPRING_TYPE_GOAL) {
+#ifdef CLOTH_FORCE_SPRING_GOAL
+ float goal_x[3], goal_v[3];
+ float k, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ // current_position = xold + t * (newposition - xold)
+ interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time);
+ sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1
+
+ scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring);
+ k = verts[s->ij].goal * scaling / (parms->avg_spring_len + FLT_EPSILON);
+
+ BPH_mass_spring_force_spring_goal(data, s->ij, goal_x, goal_v, k, parms->goalfrict * 0.01f, s->f, s->dfdx, s->dfdv);
+#endif
+ }
+ else if (s->type & CLOTH_SPRING_TYPE_BENDING) { /* calculate force of bending springs */
+#ifdef CLOTH_FORCE_SPRING_BEND
+ float kb, cb, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ scaling = parms->bending + s->stiffness * fabsf(parms->max_bend - parms->bending);
+ kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
+
+ scaling = parms->bending_damping;
+ cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
+
+ BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb, s->f, s->dfdx, s->dfdv);
+#endif
+ }
+ else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) {
+#ifdef CLOTH_FORCE_SPRING_BEND
+ float kb, cb, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ /* XXX WARNING: angular bending springs for hair apply stiffness factor as an overall factor, unlike cloth springs!
+ * this is crap, but needed due to cloth/hair mixing ...
+ * max_bend factor is not even used for hair, so ...
+ */
+ scaling = s->stiffness * parms->bending;
+ kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
+
+ scaling = parms->bending_damping;
+ cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
+
+ /* XXX assuming same restlen for ij and jk segments here, this can be done correctly for hair later */
+ BPH_mass_spring_force_spring_bending_angular(data, s->ij, s->kl, s->mn, s->target, kb, cb);
+
+#if 0
+ {
+ float x_kl[3], x_mn[3], v[3], d[3];
+
+ BPH_mass_spring_get_motion_state(data, s->kl, x_kl, v);
+ BPH_mass_spring_get_motion_state(data, s->mn, x_mn, v);
+
+ BKE_sim_debug_data_add_dot(clmd->debug_data, x_kl, 0.9, 0.9, 0.9, "target", 7980, s->kl);
+ BKE_sim_debug_data_add_line(clmd->debug_data, x_kl, x_mn, 0.8, 0.8, 0.8, "target", 7981, s->kl);
+
+ copy_v3_v3(d, s->target);
+ BKE_sim_debug_data_add_vector(clmd->debug_data, x_kl, d, 0.8, 0.8, 0.2, "target", 7982, s->kl);
+
+// copy_v3_v3(d, s->target_ij);
+// BKE_sim_debug_data_add_vector(clmd->debug_data, x, d, 1, 0.4, 0.4, "target", 7983, s->kl);
+ }
+#endif
+#endif
+ }
+}
+
+static void hair_get_boundbox(ClothModifierData *clmd, float gmin[3], float gmax[3])
+{
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ unsigned int numverts = cloth->numverts;
+ int i;
+
+ INIT_MINMAX(gmin, gmax);
+ for (i = 0; i < numverts; i++) {
+ float x[3];
+ BPH_mass_spring_get_motion_state(data, i, x, NULL);
+ DO_MINMAX(x, gmin, gmax);
+ }
+}
+
+static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListBase *effectors, float time)
+{
+ /* Collect forces and derivatives: F, dFdX, dFdV */
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ unsigned int i = 0;
+ float drag = clmd->sim_parms->Cvi * 0.01f; /* viscosity of air scaled in percent */
+ float gravity[3] = {0.0f, 0.0f, 0.0f};
+ MFace *mfaces = cloth->mfaces;
+ unsigned int numverts = cloth->numverts;
+ ClothVertex *vert;
+
+#ifdef CLOTH_FORCE_GRAVITY
+ /* global acceleration (gravitation) */
+ if (clmd->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
+ /* scale gravity force */
+ mul_v3_v3fl(gravity, clmd->scene->physics_settings.gravity, 0.001f * clmd->sim_parms->effector_weights->global_gravity);
+ }
+ vert = cloth->verts;
+ for (i = 0; i < cloth->numverts; i++, vert++) {
+ BPH_mass_spring_force_gravity(data, i, vert->mass, gravity);
+ }
+#endif
+
+ /* cloth_calc_volume_force(clmd); */
+
+#ifdef CLOTH_FORCE_DRAG
+ BPH_mass_spring_force_drag(data, drag);
+#endif
+
+ /* handle external forces like wind */
+ if (effectors) {
+ /* cache per-vertex forces to avoid redundant calculation */
+ float (*winvec)[3] = (float (*)[3])MEM_callocN(sizeof(float) * 3 * numverts, "effector forces");
+ for (i = 0; i < cloth->numverts; i++) {
+ float x[3], v[3];
+ EffectedPoint epoint;
+
+ BPH_mass_spring_get_motion_state(data, i, x, v);
+ pd_point_from_loc(clmd->scene, x, v, i, &epoint);
+ pdDoEffectors(effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL);
+ }
+
+ for (i = 0; i < cloth->numfaces; i++) {
+ MFace *mf = &mfaces[i];
+ BPH_mass_spring_force_face_wind(data, mf->v1, mf->v2, mf->v3, mf->v4, winvec);
+ }
+
+ /* Hair has only edges */
+ if (cloth->numfaces == 0) {
+#if 0
+ ClothHairData *hairdata = clmd->hairdata;
+ ClothHairData *hair_ij, *hair_kl;
+
+ for (LinkNode *link = cloth->springs; link; link = link->next) {
+ ClothSpring *spring = (ClothSpring *)link->link;
+ if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) {
+ if (hairdata) {
+ hair_ij = &hairdata[spring->ij];
+ hair_kl = &hairdata[spring->kl];
+ BPH_mass_spring_force_edge_wind(data, spring->ij, spring->kl, hair_ij->radius, hair_kl->radius, winvec);
+ }
+ else
+ BPH_mass_spring_force_edge_wind(data, spring->ij, spring->kl, 1.0f, 1.0f, winvec);
+ }
+ }
+#else
+ ClothHairData *hairdata = clmd->hairdata;
+
+ vert = cloth->verts;
+ for (i = 0; i < cloth->numverts; i++, vert++) {
+ if (hairdata) {
+ ClothHairData *hair = &hairdata[i];
+ BPH_mass_spring_force_vertex_wind(data, i, hair->radius, winvec);
+ }
+ else
+ BPH_mass_spring_force_vertex_wind(data, i, 1.0f, winvec);
+ }
+ }
+#endif
+
+ MEM_freeN(winvec);
+ }
+
+ // calculate spring forces
+ for (LinkNode *link = cloth->springs; link; link = link->next) {
+ ClothSpring *spring = (ClothSpring *)link->link;
+ // only handle active springs
+ if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE))
+ cloth_calc_spring_force(clmd, spring, time);
+ }
+}
+
+/* returns vertexes' motion state */
+BLI_INLINE void cloth_get_grid_location(Implicit_Data *data, float cell_scale, const float cell_offset[3],
+ int index, float x[3], float v[3])
+{
+ BPH_mass_spring_get_position(data, index, x);
+ BPH_mass_spring_get_new_velocity(data, index, v);
+
+ mul_v3_fl(x, cell_scale);
+ add_v3_v3(x, cell_offset);
+}
+
+/* returns next spring forming a continous hair sequence */
+BLI_INLINE LinkNode *hair_spring_next(LinkNode *spring_link)
+{
+ ClothSpring *spring = (ClothSpring *)spring_link->link;
+ LinkNode *next = spring_link->next;
+ if (next) {
+ ClothSpring *next_spring = (ClothSpring *)next->link;
+ if (next_spring->type == CLOTH_SPRING_TYPE_STRUCTURAL && next_spring->kl == spring->ij)
+ return next;
+ }
+ return NULL;
+}
+
+/* XXX this is nasty: cloth meshes do not explicitly store
+ * the order of hair segments!
+ * We have to rely on the spring build function for now,
+ * which adds structural springs in reverse order:
+ * (3,4), (2,3), (1,2)
+ * This is currently the only way to figure out hair geometry inside this code ...
+ */
+static LinkNode *cloth_continuum_add_hair_segments(HairGrid *grid, const float cell_scale, const float cell_offset[3], Cloth *cloth, LinkNode *spring_link)
+{
+ Implicit_Data *data = cloth->implicit;
+ LinkNode *next_spring_link = NULL; /* return value */
+ ClothSpring *spring1, *spring2, *spring3;
+ // ClothVertex *verts = cloth->verts;
+ // ClothVertex *vert3, *vert4;
+ float x1[3], v1[3], x2[3], v2[3], x3[3], v3[3], x4[3], v4[3];
+ float dir1[3], dir2[3], dir3[3];
+
+ spring1 = NULL;
+ spring2 = NULL;
+ spring3 = (ClothSpring *)spring_link->link;
+
+ zero_v3(x1); zero_v3(v1);
+ zero_v3(dir1);
+ zero_v3(x2); zero_v3(v2);
+ zero_v3(dir2);
+
+ // vert3 = &verts[spring3->kl];
+ cloth_get_grid_location(data, cell_scale, cell_offset, spring3->kl, x3, v3);
+ // vert4 = &verts[spring3->ij];
+ cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
+ sub_v3_v3v3(dir3, x4, x3);
+ normalize_v3(dir3);
+
+ while (spring_link) {
+ /* move on */
+ spring1 = spring2;
+ spring2 = spring3;
+
+ // vert3 = vert4;
+
+ copy_v3_v3(x1, x2); copy_v3_v3(v1, v2);
+ copy_v3_v3(x2, x3); copy_v3_v3(v2, v3);
+ copy_v3_v3(x3, x4); copy_v3_v3(v3, v4);
+
+ copy_v3_v3(dir1, dir2);
+ copy_v3_v3(dir2, dir3);
+
+ /* read next segment */
+ next_spring_link = spring_link->next;
+ spring_link = hair_spring_next(spring_link);
+
+ if (spring_link) {
+ spring3 = (ClothSpring *)spring_link->link;
+ // vert4 = &verts[spring3->ij];
+ cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
+ sub_v3_v3v3(dir3, x4, x3);
+ normalize_v3(dir3);
+ }
+ else {
+ spring3 = NULL;
+ // vert4 = NULL;
+ zero_v3(x4); zero_v3(v4);
+ zero_v3(dir3);
+ }
+
+ BPH_hair_volume_add_segment(grid, x1, v1, x2, v2, x3, v3, x4, v4,
+ spring1 ? dir1 : NULL,
+ dir2,
+ spring3 ? dir3 : NULL);
+ }
+
+ return next_spring_link;
+}
+
+static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
+{
+#if 0
+ Implicit_Data *data = cloth->implicit;
+ int numverts = cloth->numverts;
+ ClothVertex *vert;
+ int i;
+
+ for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) {
+ float x[3], v[3];
+
+ cloth_get_vertex_motion_state(data, vert, x, v);
+ BPH_hair_volume_add_vertex(grid, x, v);
+ }
+#else
+ LinkNode *link;
+ float cellsize, gmin[3], cell_scale, cell_offset[3];
+
+ /* scale and offset for transforming vertex locations into grid space
+ * (cell size is 0..1, gmin becomes origin)
+ */
+ BPH_hair_volume_grid_geometry(grid, &cellsize, NULL, gmin, NULL);
+ cell_scale = cellsize > 0.0f ? 1.0f / cellsize : 0.0f;
+ mul_v3_v3fl(cell_offset, gmin, cell_scale);
+ negate_v3(cell_offset);
+
+ link = cloth->springs;
+ while (link) {
+ ClothSpring *spring = (ClothSpring *)link->link;
+ if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL)
+ link = cloth_continuum_add_hair_segments(grid, cell_scale, cell_offset, cloth, link);
+ else
+ link = link->next;
+ }
+#endif
+ BPH_hair_volume_normalize_vertex_grid(grid);
+}
+
+static void cloth_continuum_step(ClothModifierData *clmd, float dt)
+{
+ ClothSimSettings *parms = clmd->sim_parms;
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ int numverts = cloth->numverts;
+ ClothVertex *vert;
+
+ const float fluid_factor = 0.95f; /* blend between PIC and FLIP methods */
+ float smoothfac = parms->velocity_smooth;
+ /* XXX FIXME arbitrary factor!!! this should be based on some intuitive value instead,
+ * like number of hairs per cell and time decay instead of "strength"
+ */
+ float density_target = parms->density_target;
+ float density_strength = parms->density_strength;
+ float gmin[3], gmax[3];
+ int i;
+
+ /* clear grid info */
+ zero_v3_int(clmd->hair_grid_res);
+ zero_v3(clmd->hair_grid_min);
+ zero_v3(clmd->hair_grid_max);
+ clmd->hair_grid_cellsize = 0.0f;
+
+ hair_get_boundbox(clmd, gmin, gmax);
+
+ /* gather velocities & density */
+ if (smoothfac > 0.0f || density_strength > 0.0f) {
+ HairGrid *grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
+
+ cloth_continuum_fill_grid(grid, cloth);
+
+ /* main hair continuum solver */
+ BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength);
+
+ for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) {
+ float x[3], v[3], nv[3];
+
+ /* calculate volumetric velocity influence */
+ BPH_mass_spring_get_position(data, i, x);
+ BPH_mass_spring_get_new_velocity(data, i, v);
+
+ BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv);
+
+ interp_v3_v3v3(nv, v, nv, smoothfac);
+
+ /* apply on hair data */
+ BPH_mass_spring_set_new_velocity(data, i, nv);
+ }
+
+ /* store basic grid info in the modifier data */
+ BPH_hair_volume_grid_geometry(grid, &clmd->hair_grid_cellsize, clmd->hair_grid_res, clmd->hair_grid_min, clmd->hair_grid_max);
+
+#if 0 /* DEBUG hair velocity vector field */
+ {
+ const int size = 64;
+ int i, j;
+ float offset[3], a[3], b[3];
+ const int axis = 0;
+ const float shift = 0.0f;
+
+ copy_v3_v3(offset, clmd->hair_grid_min);
+ zero_v3(a);
+ zero_v3(b);
+
+ offset[axis] = shift * clmd->hair_grid_cellsize;
+ a[(axis+1) % 3] = clmd->hair_grid_max[(axis+1) % 3] - clmd->hair_grid_min[(axis+1) % 3];
+ b[(axis+2) % 3] = clmd->hair_grid_max[(axis+2) % 3] - clmd->hair_grid_min[(axis+2) % 3];
+
+ BKE_sim_debug_data_clear_category(clmd->debug_data, "grid velocity");
+ for (j = 0; j < size; ++j) {
+ for (i = 0; i < size; ++i) {
+ float x[3], v[3], gvel[3], gvel_smooth[3], gdensity;
+
+ madd_v3_v3v3fl(x, offset, a, (float)i / (float)(size-1));
+ madd_v3_v3fl(x, b, (float)j / (float)(size-1));
+ zero_v3(v);
+
+ BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
+
+// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity, 0.7, 0.3, 1, "grid density", i, j, 3111);
+ if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) {
+ float dvel[3];
+ sub_v3_v3v3(dvel, gvel_smooth, gvel);
+// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel, 0.4, 0, 1, "grid velocity", i, j, 3112);
+// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, "grid velocity", i, j, 3113);
+ BKE_sim_debug_data_add_vector(clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114);
+#if 0
+ if (gdensity > 0.0f) {
+ float col0[3] = {0.0, 0.0, 0.0};
+ float col1[3] = {0.0, 1.0, 0.0};
+ float col[3];
+
+ interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0));
+// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, "grid velocity", i, j, 3115);
+// BKE_sim_debug_data_add_dot(clmd->debug_data, x, col[0], col[1], col[2], "grid velocity", i, j, 3115);
+ BKE_sim_debug_data_add_circle(clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115);
+ }
+#endif
+ }
+ }
+ }
+ }
+#endif
+
+ BPH_hair_volume_free_vertex_grid(grid);
+ }
+}
+
+#if 0
+static void cloth_calc_volume_force(ClothModifierData *clmd)
+{
+ ClothSimSettings *parms = clmd->sim_parms;
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ int numverts = cloth->numverts;
+ ClothVertex *vert;
+
+ /* 2.0f is an experimental value that seems to give good results */
+ float smoothfac = 2.0f * parms->velocity_smooth;
+ float collfac = 2.0f * parms->collider_friction;
+ float pressfac = parms->pressure;
+ float minpress = parms->pressure_threshold;
+ float gmin[3], gmax[3];
+ int i;
+
+ hair_get_boundbox(clmd, gmin, gmax);
+
+ /* gather velocities & density */
+ if (smoothfac > 0.0f || pressfac > 0.0f) {
+ HairVertexGrid *vertex_grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_res, gmin, gmax);
+
+ vert = cloth->verts;
+ for (i = 0; i < numverts; i++, vert++) {
+ float x[3], v[3];
+
+ if (vert->solver_index < 0) {
+ copy_v3_v3(x, vert->x);
+ copy_v3_v3(v, vert->v);
+ }
+ else {
+ BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
+ }
+ BPH_hair_volume_add_vertex(vertex_grid, x, v);
+ }
+ BPH_hair_volume_normalize_vertex_grid(vertex_grid);
+
+ vert = cloth->verts;
+ for (i = 0; i < numverts; i++, vert++) {
+ float x[3], v[3], f[3], dfdx[3][3], dfdv[3][3];
+
+ if (vert->solver_index < 0)
+ continue;
+
+ /* calculate volumetric forces */
+ BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
+ BPH_hair_volume_vertex_grid_forces(vertex_grid, x, v, smoothfac, pressfac, minpress, f, dfdx, dfdv);
+ /* apply on hair data */
+ BPH_mass_spring_force_extern(data, vert->solver_index, f, dfdx, dfdv);
+ }
+
+ BPH_hair_volume_free_vertex_grid(vertex_grid);
+ }
+}
+#endif
+
+static void cloth_clear_result(ClothModifierData *clmd)
+{
+ ClothSolverResult *sres = clmd->solver_result;
+
+ sres->status = 0;
+ sres->max_error = sres->min_error = sres->avg_error = 0.0f;
+ sres->max_iterations = sres->min_iterations = 0;
+ sres->avg_iterations = 0.0f;
+}
+
+static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, int steps)
+{
+ ClothSolverResult *sres = clmd->solver_result;
+
+ if (sres->status) { /* already initialized ? */
+ /* error only makes sense for successful iterations */
+ if (result->status == BPH_SOLVER_SUCCESS) {
+ sres->min_error = min_ff(sres->min_error, result->error);
+ sres->max_error = max_ff(sres->max_error, result->error);
+ sres->avg_error += result->error / (float)steps;
+ }
+
+ sres->min_iterations = min_ii(sres->min_iterations, result->iterations);
+ sres->max_iterations = max_ii(sres->max_iterations, result->iterations);
+ sres->avg_iterations += (float)result->iterations / (float)steps;
+ }
+ else {
+ /* error only makes sense for successful iterations */
+ if (result->status == BPH_SOLVER_SUCCESS) {
+ sres->min_error = sres->max_error = result->error;
+ sres->avg_error += result->error / (float)steps;
+ }
+
+ sres->min_iterations = sres->max_iterations = result->iterations;
+ sres->avg_iterations += (float)result->iterations / (float)steps;
+ }
+
+ sres->status |= result->status;
+}
+
+int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors)
+{
+ unsigned int i=0;
+ float step=0.0f, tf=clmd->sim_parms->timescale;
+ Cloth *cloth = clmd->clothObject;
+ ClothVertex *verts = cloth->verts/*, *cv*/;
+ unsigned int numverts = cloth->numverts;
+ float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame;
+ Implicit_Data *id = cloth->implicit;
+ ColliderContacts *contacts = NULL;
+ int totcolliders = 0;
+
+ BKE_sim_debug_data_clear_category("collision");
+
+ if (!clmd->solver_result)
+ clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result");
+ cloth_clear_result(clmd);
+
+ if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */
+ for (i = 0; i < numverts; i++) {
+ // update velocities with constrained velocities from pinned verts
+ if (verts [i].flags & CLOTH_VERT_FLAG_PINNED) {
+ float v[3];
+ sub_v3_v3v3(v, verts[i].xconst, verts[i].xold);
+ // mul_v3_fl(v, clmd->sim_parms->stepsPerFrame);
+ BPH_mass_spring_set_velocity(id, i, v);
+ }
+ }
+ }
+
+ while (step < tf) {
+ ImplicitSolverResult result;
+
+ /* initialize forces to zero */
+ BPH_mass_spring_clear_forces(id);
+ BPH_mass_spring_clear_constraints(id);
+
+ /* copy velocities for collision */
+ for (i = 0; i < numverts; i++) {
+ BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv);
+ copy_v3_v3(verts[i].v, verts[i].tv);
+ }
+
+ /* determine contact points */
+ if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
+ if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_POINTS) {
+ cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders);
+ }
+ }
+
+ /* setup vertex constraints for pinned vertices and contacts */
+ cloth_setup_constraints(clmd, contacts, totcolliders, dt);
+
+ // damping velocity for artistic reasons
+ // this is a bad way to do it, should be removed imo - lukas_t
+ if (clmd->sim_parms->vel_damping != 1.0f) {
+ for (i = 0; i < numverts; i++) {
+ float v[3];
+ BPH_mass_spring_get_motion_state(id, i, NULL, v);
+ mul_v3_fl(v, clmd->sim_parms->vel_damping);
+ BPH_mass_spring_set_velocity(id, i, v);
+ }
+ }
+
+ // calculate forces
+ cloth_calc_force(clmd, frame, effectors, step);
+
+ // calculate new velocity and position
+ BPH_mass_spring_solve_velocities(id, dt, &result);
+ cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
+
+ cloth_continuum_step(clmd, dt);
+
+ BPH_mass_spring_solve_positions(id, dt);
+
+ BPH_mass_spring_apply_result(id);
+
+ /* move pinned verts to correct position */
+ for (i = 0; i < numverts; i++) {
+ if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) {
+ if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) {
+ float x[3];
+ interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, step + dt);
+ BPH_mass_spring_set_position(id, i, x);
+ }
+ }
+
+ BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
+ }
+
+ /* free contact points */
+ if (contacts) {
+ cloth_free_contacts(contacts, totcolliders);
+ }
+
+ step += dt;
+ }
+
+ /* copy results back to cloth data */
+ for (i = 0; i < numverts; i++) {
+ BPH_mass_spring_get_motion_state(id, i, verts[i].x, verts[i].v);
+ copy_v3_v3(verts[i].txold, verts[i].x);
+ }
+
+ return 1;
+}
+
+bool BPH_cloth_solver_get_texture_data(Object *UNUSED(ob), ClothModifierData *clmd, VoxelData *vd)
+{
+ Cloth *cloth = clmd->clothObject;
+ HairGrid *grid;
+ float gmin[3], gmax[3];
+
+ if (!clmd->clothObject || !clmd->clothObject->implicit)
+ return false;
+
+ hair_get_boundbox(clmd, gmin, gmax);
+
+ grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
+ cloth_continuum_fill_grid(grid, cloth);
+
+ BPH_hair_volume_get_texture_data(grid, vd);
+
+ BPH_hair_volume_free_vertex_grid(grid);
+
+ return true;
+}
diff --git a/source/blender/physics/intern/ConstrainedConjugateGradient.h b/source/blender/physics/intern/ConstrainedConjugateGradient.h new file mode 100644 index 00000000000..2d4389f6766 --- /dev/null +++ b/source/blender/physics/intern/ConstrainedConjugateGradient.h @@ -0,0 +1,294 @@ + +#ifndef EIGEN_CONSTRAINEDCG_H +#define EIGEN_CONSTRAINEDCG_H + +#include <Eigen/Core> + +namespace Eigen { + +namespace internal { + +/** \internal Low-level conjugate gradient algorithm + * \param mat The matrix A + * \param rhs The right hand side vector b + * \param x On input and initial solution, on output the computed solution. + * \param precond A preconditioner being able to efficiently solve for an + * approximation of Ax=b (regardless of b) + * \param iters On input the max number of iteration, on output the number of performed iterations. + * \param tol_error On input the tolerance error, on output an estimation of the relative error. + */ +template<typename MatrixType, typename Rhs, typename Dest, typename FilterMatrixType, typename Preconditioner> +EIGEN_DONT_INLINE +void constrained_conjugate_gradient(const MatrixType& mat, const Rhs& rhs, Dest& x, + const FilterMatrixType &filter, + const Preconditioner& precond, int& iters, + typename Dest::RealScalar& tol_error) +{ + using std::sqrt; + using std::abs; + typedef typename Dest::RealScalar RealScalar; + typedef typename Dest::Scalar Scalar; + typedef Matrix<Scalar,Dynamic,1> VectorType; + + RealScalar tol = tol_error; + int maxIters = iters; + + int n = mat.cols(); + + VectorType residual = filter * (rhs - mat * x); //initial residual + + RealScalar rhsNorm2 = (filter * rhs).squaredNorm(); + if(rhsNorm2 == 0) + { + /* XXX TODO set constrained result here */ + x.setZero(); + iters = 0; + tol_error = 0; + return; + } + RealScalar threshold = tol*tol*rhsNorm2; + RealScalar residualNorm2 = residual.squaredNorm(); + if (residualNorm2 < threshold) + { + iters = 0; + tol_error = sqrt(residualNorm2 / rhsNorm2); + return; + } + + VectorType p(n); + p = filter * precond.solve(residual); //initial search direction + + VectorType z(n), tmp(n); + RealScalar absNew = numext::real(residual.dot(p)); // the square of the absolute value of r scaled by invM + int i = 0; + while(i < maxIters) + { + tmp.noalias() = filter * (mat * p); // the bottleneck of the algorithm + + Scalar alpha = absNew / p.dot(tmp); // the amount we travel on dir + x += alpha * p; // update solution + residual -= alpha * tmp; // update residue + + residualNorm2 = residual.squaredNorm(); + if(residualNorm2 < threshold) + break; + + z = precond.solve(residual); // approximately solve for "A z = residual" + + RealScalar absOld = absNew; + absNew = numext::real(residual.dot(z)); // update the absolute value of r + RealScalar beta = absNew / absOld; // calculate the Gram-Schmidt value used to create the new search direction + p = filter * (z + beta * p); // update search direction + i++; + } + tol_error = sqrt(residualNorm2 / rhsNorm2); + iters = i; +} + +} + +#if 0 /* unused */ +template<typename MatrixType> +struct MatrixFilter +{ + MatrixFilter() : + m_cmat(NULL) + { + } + + MatrixFilter(const MatrixType &cmat) : + m_cmat(&cmat) + { + } + + void setMatrix(const MatrixType &cmat) { m_cmat = &cmat; } + + template <typename VectorType> + void apply(VectorType v) const + { + v = (*m_cmat) * v; + } + +protected: + const MatrixType *m_cmat; +}; +#endif + +template< typename _MatrixType, int _UpLo=Lower, + typename _FilterMatrixType = _MatrixType, + typename _Preconditioner = DiagonalPreconditioner<typename _MatrixType::Scalar> > +class ConstrainedConjugateGradient; + +namespace internal { + +template< typename _MatrixType, int _UpLo, typename _FilterMatrixType, typename _Preconditioner> +struct traits<ConstrainedConjugateGradient<_MatrixType,_UpLo,_FilterMatrixType,_Preconditioner> > +{ + typedef _MatrixType MatrixType; + typedef _FilterMatrixType FilterMatrixType; + typedef _Preconditioner Preconditioner; +}; + +} + +/** \ingroup IterativeLinearSolvers_Module + * \brief A conjugate gradient solver for sparse self-adjoint problems with additional constraints + * + * This class allows to solve for A.x = b sparse linear problems using a conjugate gradient algorithm. + * The sparse matrix A must be selfadjoint. The vectors x and b can be either dense or sparse. + * + * \tparam _MatrixType the type of the sparse matrix A, can be a dense or a sparse matrix. + * \tparam _UpLo the triangular part that will be used for the computations. It can be Lower + * or Upper. Default is Lower. + * \tparam _Preconditioner the type of the preconditioner. Default is DiagonalPreconditioner + * + * The maximal number of iterations and tolerance value can be controlled via the setMaxIterations() + * and setTolerance() methods. The defaults are the size of the problem for the maximal number of iterations + * and NumTraits<Scalar>::epsilon() for the tolerance. + * + * This class can be used as the direct solver classes. Here is a typical usage example: + * \code + * int n = 10000; + * VectorXd x(n), b(n); + * SparseMatrix<double> A(n,n); + * // fill A and b + * ConjugateGradient<SparseMatrix<double> > cg; + * cg.compute(A); + * x = cg.solve(b); + * std::cout << "#iterations: " << cg.iterations() << std::endl; + * std::cout << "estimated error: " << cg.error() << std::endl; + * // update b, and solve again + * x = cg.solve(b); + * \endcode + * + * By default the iterations start with x=0 as an initial guess of the solution. + * One can control the start using the solveWithGuess() method. Here is a step by + * step execution example starting with a random guess and printing the evolution + * of the estimated error: + * * \code + * x = VectorXd::Random(n); + * cg.setMaxIterations(1); + * int i = 0; + * do { + * x = cg.solveWithGuess(b,x); + * std::cout << i << " : " << cg.error() << std::endl; + * ++i; + * } while (cg.info()!=Success && i<100); + * \endcode + * Note that such a step by step excution is slightly slower. + * + * \sa class SimplicialCholesky, DiagonalPreconditioner, IdentityPreconditioner + */ +template< typename _MatrixType, int _UpLo, typename _FilterMatrixType, typename _Preconditioner> +class ConstrainedConjugateGradient : public IterativeSolverBase<ConstrainedConjugateGradient<_MatrixType,_UpLo,_FilterMatrixType,_Preconditioner> > +{ + typedef IterativeSolverBase<ConstrainedConjugateGradient> Base; + using Base::mp_matrix; + using Base::m_error; + using Base::m_iterations; + using Base::m_info; + using Base::m_isInitialized; +public: + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::Index Index; + typedef typename MatrixType::RealScalar RealScalar; + typedef _FilterMatrixType FilterMatrixType; + typedef _Preconditioner Preconditioner; + + enum { + UpLo = _UpLo + }; + +public: + + /** Default constructor. */ + ConstrainedConjugateGradient() : Base() {} + + /** Initialize the solver with matrix \a A for further \c Ax=b solving. + * + * This constructor is a shortcut for the default constructor followed + * by a call to compute(). + * + * \warning this class stores a reference to the matrix A as well as some + * precomputed values that depend on it. Therefore, if \a A is changed + * this class becomes invalid. Call compute() to update it with the new + * matrix A, or modify a copy of A. + */ + ConstrainedConjugateGradient(const MatrixType& A) : Base(A) {} + + ~ConstrainedConjugateGradient() {} + + FilterMatrixType &filter() { return m_filter; } + const FilterMatrixType &filter() const { return m_filter; } + + /** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A + * \a x0 as an initial solution. + * + * \sa compute() + */ + template<typename Rhs,typename Guess> + inline const internal::solve_retval_with_guess<ConstrainedConjugateGradient, Rhs, Guess> + solveWithGuess(const MatrixBase<Rhs>& b, const Guess& x0) const + { + eigen_assert(m_isInitialized && "ConjugateGradient is not initialized."); + eigen_assert(Base::rows()==b.rows() + && "ConjugateGradient::solve(): invalid number of rows of the right hand side matrix b"); + return internal::solve_retval_with_guess + <ConstrainedConjugateGradient, Rhs, Guess>(*this, b.derived(), x0); + } + + /** \internal */ + template<typename Rhs,typename Dest> + void _solveWithGuess(const Rhs& b, Dest& x) const + { + m_iterations = Base::maxIterations(); + m_error = Base::m_tolerance; + + for(int j=0; j<b.cols(); ++j) + { + m_iterations = Base::maxIterations(); + m_error = Base::m_tolerance; + + typename Dest::ColXpr xj(x,j); + internal::constrained_conjugate_gradient(mp_matrix->template selfadjointView<UpLo>(), b.col(j), xj, m_filter, + Base::m_preconditioner, m_iterations, m_error); + } + + m_isInitialized = true; + m_info = m_error <= Base::m_tolerance ? Success : NoConvergence; + } + + /** \internal */ + template<typename Rhs,typename Dest> + void _solve(const Rhs& b, Dest& x) const + { + x.setOnes(); + _solveWithGuess(b,x); + } + +protected: + FilterMatrixType m_filter; +}; + + +namespace internal { + +template<typename _MatrixType, int _UpLo, typename _Filter, typename _Preconditioner, typename Rhs> +struct solve_retval<ConstrainedConjugateGradient<_MatrixType,_UpLo,_Filter,_Preconditioner>, Rhs> + : solve_retval_base<ConstrainedConjugateGradient<_MatrixType,_UpLo,_Filter,_Preconditioner>, Rhs> +{ + typedef ConstrainedConjugateGradient<_MatrixType,_UpLo,_Filter,_Preconditioner> Dec; + EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs) + + template<typename Dest> void evalTo(Dest& dst) const + { + dec()._solve(rhs(),dst); + } +}; + +} // end namespace internal + +} // end namespace Eigen + +#endif // EIGEN_CONSTRAINEDCG_H diff --git a/source/blender/physics/intern/eigen_utils.h b/source/blender/physics/intern/eigen_utils.h new file mode 100644 index 00000000000..cd3bd5ee45d --- /dev/null +++ b/source/blender/physics/intern/eigen_utils.h @@ -0,0 +1,230 @@ +/* + * ***** 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) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __EIGEN_UTILS_H__ +#define __EIGEN_UTILS_H__ + +/** \file eigen_utils.h + * \ingroup bph + */ + +#ifdef __GNUC__ +# pragma GCC diagnostic push +/* XXX suppress verbose warnings in eigen */ +# pragma GCC diagnostic ignored "-Wlogical-op" +#endif + +#include <Eigen/Sparse> +#include <Eigen/src/Core/util/DisableStupidWarnings.h> + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#include "BLI_utildefines.h" + + +typedef float Scalar; + +/* slightly extended Eigen vector class + * with conversion to/from plain C float array + */ +class Vector3 : public Eigen::Vector3f { +public: + typedef float *ctype; + + Vector3() + { + } + + Vector3(const ctype &v) + { + for (int k = 0; k < 3; ++k) + coeffRef(k) = v[k]; + } + + Vector3& operator = (const ctype &v) + { + for (int k = 0; k < 3; ++k) + coeffRef(k) = v[k]; + return *this; + } + + operator ctype() + { + return data(); + } +}; + +/* slightly extended Eigen matrix class + * with conversion to/from plain C float array + */ +class Matrix3 : public Eigen::Matrix3f { +public: + typedef float (*ctype)[3]; + + Matrix3() + { + } + + Matrix3(const ctype &v) + { + for (int k = 0; k < 3; ++k) + for (int l = 0; l < 3; ++l) + coeffRef(l, k) = v[k][l]; + } + + Matrix3& operator = (const ctype &v) + { + for (int k = 0; k < 3; ++k) + for (int l = 0; l < 3; ++l) + coeffRef(l, k) = v[k][l]; + return *this; + } + + operator ctype() + { + return (ctype)data(); + } +}; + +typedef Eigen::VectorXf lVector; + +/* Extension of dense Eigen vectors, + * providing 3-float block access for blenlib math functions + */ +class lVector3f : public Eigen::VectorXf { +public: + typedef Eigen::VectorXf base_t; + + lVector3f() + { + } + + template <typename T> + lVector3f& operator = (T rhs) + { + base_t::operator=(rhs); + return *this; + } + + float* v3(int vertex) + { + return &coeffRef(3 * vertex); + } + + const float* v3(int vertex) const + { + return &coeffRef(3 * vertex); + } +}; + +typedef Eigen::Triplet<Scalar> Triplet; +typedef std::vector<Triplet> TripletList; + +typedef Eigen::SparseMatrix<Scalar> lMatrix; + +/* Constructor type that provides more convenient handling of Eigen triplets + * for efficient construction of sparse 3x3 block matrices. + * This should be used for building lMatrix instead of writing to such lMatrix directly (which is very inefficient). + * After all elements have been defined using the set() method, the actual matrix can be filled using construct(). + */ +struct lMatrix3fCtor { + lMatrix3fCtor() + { + } + + void reset() + { + m_trips.clear(); + } + + void reserve(int numverts) + { + /* reserve for diagonal entries */ + m_trips.reserve(numverts * 9); + } + + void add(int i, int j, const Matrix3 &m) + { + i *= 3; + j *= 3; + for (int k = 0; k < 3; ++k) + for (int l = 0; l < 3; ++l) + m_trips.push_back(Triplet(i + k, j + l, m.coeff(l, k))); + } + + void sub(int i, int j, const Matrix3 &m) + { + i *= 3; + j *= 3; + for (int k = 0; k < 3; ++k) + for (int l = 0; l < 3; ++l) + m_trips.push_back(Triplet(i + k, j + l, -m.coeff(l, k))); + } + + inline void construct(lMatrix &m) + { + m.setFromTriplets(m_trips.begin(), m_trips.end()); + m_trips.clear(); + } + +private: + TripletList m_trips; +}; + +typedef Eigen::ConjugateGradient<lMatrix, Eigen::Lower, Eigen::DiagonalPreconditioner<Scalar> > ConjugateGradient; + +using Eigen::ComputationInfo; + +BLI_INLINE void print_lvector(const lVector3f &v) +{ + for (int i = 0; i < v.rows(); ++i) { + if (i > 0 && i % 3 == 0) + printf("\n"); + + printf("%f,\n", v[i]); + } +} + +BLI_INLINE void print_lmatrix(const lMatrix &m) +{ + for (int j = 0; j < m.rows(); ++j) { + if (j > 0 && j % 3 == 0) + printf("\n"); + + for (int i = 0; i < m.cols(); ++i) { + if (i > 0 && i % 3 == 0) + printf(" "); + + implicit_print_matrix_elem(m.coeff(j, i)); + } + printf("\n"); + } +} + +#endif diff --git a/source/blender/physics/intern/hair_volume.cpp b/source/blender/physics/intern/hair_volume.cpp new file mode 100644 index 00000000000..b07af1d201c --- /dev/null +++ b/source/blender/physics/intern/hair_volume.cpp @@ -0,0 +1,1154 @@ +/* + * ***** 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) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Janne Karhu, Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/physics/intern/hair_volume.cpp + * \ingroup bph + */ + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_texture_types.h" + +#include "BKE_effect.h" +} + +#include "implicit.h" +#include "eigen_utils.h" + +/* ================ Volumetric Hair Interaction ================ + * adapted from + * + * Volumetric Methods for Simulation and Rendering of Hair + * (Petrovic, Henne, Anderson, Pixar Technical Memo #06-08, Pixar Animation Studios) + * + * as well as + * + * "Detail Preserving Continuum Simulation of Straight Hair" + * (McAdams, Selle 2009) + */ + +/* Note about array indexing: + * Generally the arrays here are one-dimensional. + * The relation between 3D indices and the array offset is + * offset = x + res_x * y + res_x * res_y * z + */ + +static float I[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; + +BLI_INLINE int floor_int(float value) +{ + return value > 0.0f ? (int)value : ((int)value) - 1; +} + +BLI_INLINE float floor_mod(float value) +{ + return value - floorf(value); +} + +BLI_INLINE int hair_grid_size(const int res[3]) +{ + return res[0] * res[1] * res[2]; +} + +typedef struct HairGridVert { + int samples; + float velocity[3]; + float density; + + float velocity_smooth[3]; +} HairGridVert; + +typedef struct HairGrid { + HairGridVert *verts; + int res[3]; + float gmin[3], gmax[3]; + float cellsize, inv_cellsize; +} HairGrid; + +#define HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, axis) ( min_ii( max_ii( (int)((vec[axis] - gmin[axis]) * scale), 0), res[axis]-2 ) ) + +BLI_INLINE int hair_grid_offset(const float vec[3], const int res[3], const float gmin[3], float scale) +{ + int i, j, k; + i = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 0); + j = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 1); + k = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 2); + return i + (j + k*res[1])*res[0]; +} + +BLI_INLINE int hair_grid_interp_weights(const int res[3], const float gmin[3], float scale, const float vec[3], float uvw[3]) +{ + int i, j, k, offset; + + i = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 0); + j = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 1); + k = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 2); + offset = i + (j + k*res[1])*res[0]; + + uvw[0] = (vec[0] - gmin[0]) * scale - (float)i; + uvw[1] = (vec[1] - gmin[1]) * scale - (float)j; + uvw[2] = (vec[2] - gmin[2]) * scale - (float)k; + +// BLI_assert(0.0f <= uvw[0] && uvw[0] <= 1.0001f); +// BLI_assert(0.0f <= uvw[1] && uvw[1] <= 1.0001f); +// BLI_assert(0.0f <= uvw[2] && uvw[2] <= 1.0001f); + + return offset; +} + +BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid, const int res[3], const float gmin[3], float scale, const float vec[3], + float *density, float velocity[3], float vel_smooth[3], float density_gradient[3], float velocity_gradient[3][3]) +{ + HairGridVert data[8]; + float uvw[3], muvw[3]; + int res2 = res[1] * res[0]; + int offset; + + offset = hair_grid_interp_weights(res, gmin, scale, vec, uvw); + muvw[0] = 1.0f - uvw[0]; + muvw[1] = 1.0f - uvw[1]; + muvw[2] = 1.0f - uvw[2]; + + data[0] = grid[offset ]; + data[1] = grid[offset +1]; + data[2] = grid[offset +res[0] ]; + data[3] = grid[offset +res[0]+1]; + data[4] = grid[offset+res2 ]; + data[5] = grid[offset+res2 +1]; + data[6] = grid[offset+res2+res[0] ]; + data[7] = grid[offset+res2+res[0]+1]; + + if (density) { + *density = muvw[2]*( muvw[1]*( muvw[0]*data[0].density + uvw[0]*data[1].density ) + + uvw[1]*( muvw[0]*data[2].density + uvw[0]*data[3].density ) ) + + uvw[2]*( muvw[1]*( muvw[0]*data[4].density + uvw[0]*data[5].density ) + + uvw[1]*( muvw[0]*data[6].density + uvw[0]*data[7].density ) ); + } + + if (velocity) { + int k; + for (k = 0; k < 3; ++k) { + velocity[k] = muvw[2]*( muvw[1]*( muvw[0]*data[0].velocity[k] + uvw[0]*data[1].velocity[k] ) + + uvw[1]*( muvw[0]*data[2].velocity[k] + uvw[0]*data[3].velocity[k] ) ) + + uvw[2]*( muvw[1]*( muvw[0]*data[4].velocity[k] + uvw[0]*data[5].velocity[k] ) + + uvw[1]*( muvw[0]*data[6].velocity[k] + uvw[0]*data[7].velocity[k] ) ); + } + } + + if (vel_smooth) { + int k; + for (k = 0; k < 3; ++k) { + vel_smooth[k] = muvw[2]*( muvw[1]*( muvw[0]*data[0].velocity_smooth[k] + uvw[0]*data[1].velocity_smooth[k] ) + + uvw[1]*( muvw[0]*data[2].velocity_smooth[k] + uvw[0]*data[3].velocity_smooth[k] ) ) + + uvw[2]*( muvw[1]*( muvw[0]*data[4].velocity_smooth[k] + uvw[0]*data[5].velocity_smooth[k] ) + + uvw[1]*( muvw[0]*data[6].velocity_smooth[k] + uvw[0]*data[7].velocity_smooth[k] ) ); + } + } + + if (density_gradient) { + density_gradient[0] = muvw[1] * muvw[2] * ( data[0].density - data[1].density ) + + uvw[1] * muvw[2] * ( data[2].density - data[3].density ) + + muvw[1] * uvw[2] * ( data[4].density - data[5].density ) + + uvw[1] * uvw[2] * ( data[6].density - data[7].density ); + + density_gradient[1] = muvw[2] * muvw[0] * ( data[0].density - data[2].density ) + + uvw[2] * muvw[0] * ( data[4].density - data[6].density ) + + muvw[2] * uvw[0] * ( data[1].density - data[3].density ) + + uvw[2] * uvw[0] * ( data[5].density - data[7].density ); + + density_gradient[2] = muvw[2] * muvw[0] * ( data[0].density - data[4].density ) + + uvw[2] * muvw[0] * ( data[1].density - data[5].density ) + + muvw[2] * uvw[0] * ( data[2].density - data[6].density ) + + uvw[2] * uvw[0] * ( data[3].density - data[7].density ); + } + + if (velocity_gradient) { + /* XXX TODO */ + zero_m3(velocity_gradient); + } +} + +void BPH_hair_volume_vertex_grid_forces(HairGrid *grid, const float x[3], const float v[3], + float smoothfac, float pressurefac, float minpressure, + float f[3], float dfdx[3][3], float dfdv[3][3]) +{ + float gdensity, gvelocity[3], ggrad[3], gvelgrad[3][3], gradlen; + + hair_grid_interpolate(grid->verts, grid->res, grid->gmin, grid->inv_cellsize, x, &gdensity, gvelocity, NULL, ggrad, gvelgrad); + + zero_v3(f); + sub_v3_v3(gvelocity, v); + mul_v3_v3fl(f, gvelocity, smoothfac); + + gradlen = normalize_v3(ggrad) - minpressure; + if (gradlen > 0.0f) { + mul_v3_fl(ggrad, gradlen); + madd_v3_v3fl(f, ggrad, pressurefac); + } + + zero_m3(dfdx); + + sub_m3_m3m3(dfdv, gvelgrad, I); + mul_m3_fl(dfdv, smoothfac); +} + +void BPH_hair_volume_grid_interpolate(HairGrid *grid, const float x[3], + float *density, float velocity[3], float velocity_smooth[3], float density_gradient[3], float velocity_gradient[3][3]) +{ + hair_grid_interpolate(grid->verts, grid->res, grid->gmin, grid->inv_cellsize, x, density, velocity, velocity_smooth, density_gradient, velocity_gradient); +} + +void BPH_hair_volume_grid_velocity(HairGrid *grid, const float x[3], const float v[3], + float fluid_factor, + float r_v[3]) +{ + float gdensity, gvelocity[3], gvel_smooth[3], ggrad[3], gvelgrad[3][3]; + float v_pic[3], v_flip[3]; + + hair_grid_interpolate(grid->verts, grid->res, grid->gmin, grid->inv_cellsize, x, &gdensity, gvelocity, gvel_smooth, ggrad, gvelgrad); + + /* velocity according to PIC method (Particle-in-Cell) */ + copy_v3_v3(v_pic, gvel_smooth); + + /* velocity according to FLIP method (Fluid-Implicit-Particle) */ + sub_v3_v3v3(v_flip, gvel_smooth, gvelocity); + add_v3_v3(v_flip, v); + + interp_v3_v3v3(r_v, v_pic, v_flip, fluid_factor); +} + +void BPH_hair_volume_grid_clear(HairGrid *grid) +{ + const int size = hair_grid_size(grid->res); + int i; + for (i = 0; i < size; ++i) { + zero_v3(grid->verts[i].velocity); + zero_v3(grid->verts[i].velocity_smooth); + grid->verts[i].density = 0.0f; + grid->verts[i].samples = 0; + } +} + +BLI_INLINE bool hair_grid_point_valid(const float vec[3], float gmin[3], float gmax[3]) +{ + return !(vec[0] < gmin[0] || vec[1] < gmin[1] || vec[2] < gmin[2] || + vec[0] > gmax[0] || vec[1] > gmax[1] || vec[2] > gmax[2]); +} + +BLI_INLINE float dist_tent_v3f3(const float a[3], float x, float y, float z) +{ + float w = (1.0f - fabsf(a[0] - x)) * (1.0f - fabsf(a[1] - y)) * (1.0f - fabsf(a[2] - z)); + return w; +} + +BLI_INLINE float weights_sum(const float weights[8]) +{ + float totweight = 0.0f; + int i; + for (i = 0; i < 8; ++i) + totweight += weights[i]; + return totweight; +} + +/* returns the grid array offset as well to avoid redundant calculation */ +BLI_INLINE int hair_grid_weights(const int res[3], const float gmin[3], float scale, const float vec[3], float weights[8]) +{ + int i, j, k, offset; + float uvw[3]; + + i = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 0); + j = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 1); + k = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 2); + offset = i + (j + k*res[1])*res[0]; + + uvw[0] = (vec[0] - gmin[0]) * scale; + uvw[1] = (vec[1] - gmin[1]) * scale; + uvw[2] = (vec[2] - gmin[2]) * scale; + + weights[0] = dist_tent_v3f3(uvw, (float)i , (float)j , (float)k ); + weights[1] = dist_tent_v3f3(uvw, (float)(i+1), (float)j , (float)k ); + weights[2] = dist_tent_v3f3(uvw, (float)i , (float)(j+1), (float)k ); + weights[3] = dist_tent_v3f3(uvw, (float)(i+1), (float)(j+1), (float)k ); + weights[4] = dist_tent_v3f3(uvw, (float)i , (float)j , (float)(k+1)); + weights[5] = dist_tent_v3f3(uvw, (float)(i+1), (float)j , (float)(k+1)); + weights[6] = dist_tent_v3f3(uvw, (float)i , (float)(j+1), (float)(k+1)); + weights[7] = dist_tent_v3f3(uvw, (float)(i+1), (float)(j+1), (float)(k+1)); + +// BLI_assert(fabsf(weights_sum(weights) - 1.0f) < 0.0001f); + + return offset; +} + +BLI_INLINE void grid_to_world(HairGrid *grid, float vecw[3], const float vec[3]) +{ + copy_v3_v3(vecw, vec); + mul_v3_fl(vecw, grid->cellsize); + add_v3_v3(vecw, grid->gmin); +} + +void BPH_hair_volume_add_vertex(HairGrid *grid, const float x[3], const float v[3]) +{ + const int res[3] = { grid->res[0], grid->res[1], grid->res[2] }; + float weights[8]; + int di, dj, dk; + int offset; + + if (!hair_grid_point_valid(x, grid->gmin, grid->gmax)) + return; + + offset = hair_grid_weights(res, grid->gmin, grid->inv_cellsize, x, weights); + + for (di = 0; di < 2; ++di) { + for (dj = 0; dj < 2; ++dj) { + for (dk = 0; dk < 2; ++dk) { + int voffset = offset + di + (dj + dk*res[1])*res[0]; + int iw = di + dj*2 + dk*4; + + grid->verts[voffset].density += weights[iw]; + madd_v3_v3fl(grid->verts[voffset].velocity, v, weights[iw]); + } + } + } +} + +#if 0 +BLI_INLINE void hair_volume_eval_grid_vertex(HairGridVert *vert, const float loc[3], float radius, float dist_scale, + const float x2[3], const float v2[3], const float x3[3], const float v3[3]) +{ + float closest[3], lambda, dist, weight; + + lambda = closest_to_line_v3(closest, loc, x2, x3); + dist = len_v3v3(closest, loc); + + weight = (radius - dist) * dist_scale; + + if (weight > 0.0f) { + float vel[3]; + + interp_v3_v3v3(vel, v2, v3, lambda); + madd_v3_v3fl(vert->velocity, vel, weight); + vert->density += weight; + vert->samples += 1; + } +} + +BLI_INLINE int major_axis_v3(const float v[3]) +{ + const float a = fabsf(v[0]); + const float b = fabsf(v[1]); + const float c = fabsf(v[2]); + return a > b ? (a > c ? 0 : 2) : (b > c ? 1 : 2); +} + +BLI_INLINE void hair_volume_add_segment_2D(HairGrid *grid, + const float UNUSED(x1[3]), const float UNUSED(v1[3]), const float x2[3], const float v2[3], + const float x3[3], const float v3[3], const float UNUSED(x4[3]), const float UNUSED(v4[3]), + const float UNUSED(dir1[3]), const float dir2[3], const float UNUSED(dir3[3]), + int resj, int resk, int jmin, int jmax, int kmin, int kmax, + HairGridVert *vert, int stride_j, int stride_k, const float loc[3], int axis_j, int axis_k, + int debug_i) +{ + const float radius = 1.5f; + const float dist_scale = grid->inv_cellsize; + + int j, k; + + /* boundary checks to be safe */ + CLAMP_MIN(jmin, 0); + CLAMP_MAX(jmax, resj-1); + CLAMP_MIN(kmin, 0); + CLAMP_MAX(kmax, resk-1); + + HairGridVert *vert_j = vert + jmin * stride_j; + float loc_j[3] = { loc[0], loc[1], loc[2] }; + loc_j[axis_j] += (float)jmin; + for (j = jmin; j <= jmax; ++j, vert_j += stride_j, loc_j[axis_j] += 1.0f) { + + HairGridVert *vert_k = vert_j + kmin * stride_k; + float loc_k[3] = { loc_j[0], loc_j[1], loc_j[2] }; + loc_k[axis_k] += (float)kmin; + for (k = kmin; k <= kmax; ++k, vert_k += stride_k, loc_k[axis_k] += 1.0f) { + + hair_volume_eval_grid_vertex(vert_k, loc_k, radius, dist_scale, x2, v2, x3, v3); + +#if 0 + { + float wloc[3], x2w[3], x3w[3]; + grid_to_world(grid, wloc, loc_k); + grid_to_world(grid, x2w, x2); + grid_to_world(grid, x3w, x3); + + if (vert_k->samples > 0) + BKE_sim_debug_data_add_circle(wloc, 0.01f, 1.0, 1.0, 0.3, "grid", 2525, debug_i, j, k); + + if (grid->debug_value) { + BKE_sim_debug_data_add_dot(wloc, 1, 0, 0, "grid", 93, debug_i, j, k); + BKE_sim_debug_data_add_dot(x2w, 0.1, 0.1, 0.7, "grid", 649, debug_i, j, k); + BKE_sim_debug_data_add_line(wloc, x2w, 0.3, 0.8, 0.3, "grid", 253, debug_i, j, k); + BKE_sim_debug_data_add_line(wloc, x3w, 0.8, 0.3, 0.3, "grid", 254, debug_i, j, k); +// BKE_sim_debug_data_add_circle(x2w, len_v3v3(wloc, x2w), 0.2, 0.7, 0.2, "grid", 255, i, j, k); + } + } +#endif + } + } +} + +/* Uses a variation of Bresenham's algorithm for rasterizing a 3D grid with a line segment. + * + * The radius of influence around a segment is assumed to be at most 2*cellsize, + * i.e. only cells containing the segment and their direct neighbors are examined. + * + * + */ +void BPH_hair_volume_add_segment(HairGrid *grid, + const float x1[3], const float v1[3], const float x2[3], const float v2[3], + const float x3[3], const float v3[3], const float x4[3], const float v4[3], + const float dir1[3], const float dir2[3], const float dir3[3]) +{ + const int res[3] = { grid->res[0], grid->res[1], grid->res[2] }; + + /* find the primary direction from the major axis of the direction vector */ + const int axis0 = major_axis_v3(dir2); + const int axis1 = (axis0 + 1) % 3; + const int axis2 = (axis0 + 2) % 3; + + /* vertex buffer offset factors along cardinal axes */ + const int strides[3] = { 1, res[0], res[0] * res[1] }; + const int stride0 = strides[axis0]; + const int stride1 = strides[axis1]; + const int stride2 = strides[axis2]; + + /* increment of secondary directions per step in the primary direction + * note: we always go in the positive direction along axis0, so the sign can be inverted + */ + const float inc1 = dir2[axis1] / dir2[axis0]; + const float inc2 = dir2[axis2] / dir2[axis0]; + + /* start/end points, so increment along axis0 is always positive */ + const float *start = x2[axis0] < x3[axis0] ? x2 : x3; + const float *end = x2[axis0] < x3[axis0] ? x3 : x2; + const float start0 = start[axis0], start1 = start[axis1], start2 = start[axis2]; + const float end0 = end[axis0]; + + /* range along primary direction */ + const int imin = max_ii(floor_int(start[axis0]) - 1, 0); + const int imax = min_ii(floor_int(end[axis0]) + 2, res[axis0]-1); + + float h = 0.0f; + HairGridVert *vert0; + float loc0[3]; + int j0, k0, j0_prev, k0_prev; + int i; + + for (i = imin; i <= imax; ++i) { + float shift1, shift2; /* fraction of a full cell shift [0.0, 1.0) */ + int jmin, jmax, kmin, kmax; + + h = CLAMPIS((float)i, start0, end0); + + shift1 = start1 + (h - start0) * inc1; + shift2 = start2 + (h - start0) * inc2; + + j0_prev = j0; + j0 = floor_int(shift1); + + k0_prev = k0; + k0 = floor_int(shift2); + + if (i > imin) { + jmin = min_ii(j0, j0_prev); + jmax = max_ii(j0, j0_prev); + kmin = min_ii(k0, k0_prev); + kmax = max_ii(k0, k0_prev); + } + else { + jmin = jmax = j0; + kmin = kmax = k0; + } + + vert0 = grid->verts + i * stride0; + loc0[axis0] = (float)i; + loc0[axis1] = 0.0f; + loc0[axis2] = 0.0f; + + hair_volume_add_segment_2D(grid, x1, v1, x2, v2, x3, v3, x4, v4, dir1, dir2, dir3, + res[axis1], res[axis2], jmin-1, jmax+2, kmin-1, kmax+2, + vert0, stride1, stride2, loc0, axis1, axis2, + i); + } +} +#else +BLI_INLINE void hair_volume_eval_grid_vertex_sample(HairGridVert *vert, const float loc[3], float radius, float dist_scale, + const float x[3], const float v[3]) +{ + float dist, weight; + + dist = len_v3v3(x, loc); + + weight = (radius - dist) * dist_scale; + + if (weight > 0.0f) { + madd_v3_v3fl(vert->velocity, v, weight); + vert->density += weight; + vert->samples += 1; + } +} + +/* XXX simplified test implementation using a series of discrete sample along the segment, + * instead of finding the closest point for all affected grid vertices. + */ +void BPH_hair_volume_add_segment(HairGrid *grid, + const float UNUSED(x1[3]), const float UNUSED(v1[3]), const float x2[3], const float v2[3], + const float x3[3], const float v3[3], const float UNUSED(x4[3]), const float UNUSED(v4[3]), + const float UNUSED(dir1[3]), const float UNUSED(dir2[3]), const float UNUSED(dir3[3])) +{ + const float radius = 1.5f; + const float dist_scale = grid->inv_cellsize; + + const int res[3] = { grid->res[0], grid->res[1], grid->res[2] }; + const int stride[3] = { 1, res[0], res[0] * res[1] }; + const int num_samples = 10; + + int s; + + for (s = 0; s < num_samples; ++s) { + float x[3], v[3]; + int i, j, k; + + float f = (float)s / (float)(num_samples-1); + interp_v3_v3v3(x, x2, x3, f); + interp_v3_v3v3(v, v2, v3, f); + + int imin = max_ii(floor_int(x[0]) - 2, 0); + int imax = min_ii(floor_int(x[0]) + 2, res[0]-1); + int jmin = max_ii(floor_int(x[1]) - 2, 0); + int jmax = min_ii(floor_int(x[1]) + 2, res[1]-1); + int kmin = max_ii(floor_int(x[2]) - 2, 0); + int kmax = min_ii(floor_int(x[2]) + 2, res[2]-1); + + for (k = kmin; k <= kmax; ++k) { + for (j = jmin; j <= jmax; ++j) { + for (i = imin; i <= imax; ++i) { + float loc[3] = { (float)i, (float)j, (float)k }; + HairGridVert *vert = grid->verts + i * stride[0] + j * stride[1] + k * stride[2]; + + hair_volume_eval_grid_vertex_sample(vert, loc, radius, dist_scale, x, v); + } + } + } + } +} +#endif + +void BPH_hair_volume_normalize_vertex_grid(HairGrid *grid) +{ + int i, size = hair_grid_size(grid->res); + /* divide velocity with density */ + for (i = 0; i < size; i++) { + float density = grid->verts[i].density; + if (density > 0.0f) + mul_v3_fl(grid->verts[i].velocity, 1.0f/density); + } +} + +static const float density_threshold = 0.001f; /* cells with density below this are considered empty */ + +/* Contribution of target density pressure to the laplacian in the pressure poisson equation. + * This is based on the model found in + * "Two-way Coupled SPH and Particle Level Set Fluid Simulation" (Losasso et al., 2008) + */ +BLI_INLINE float hair_volume_density_divergence(float density, float target_density, float strength) +{ + if (density > density_threshold && density > target_density) + return strength * logf(target_density / density); + else + return 0.0f; +} + +bool BPH_hair_volume_solve_divergence(HairGrid *grid, float dt, float target_density, float target_strength) +{ + const float flowfac = grid->cellsize; + const float inv_flowfac = 1.0f / grid->cellsize; + + /*const int num_cells = hair_grid_size(grid->res);*/ + const int res[3] = { grid->res[0], grid->res[1], grid->res[2] }; + const int resA[3] = { grid->res[0] + 2, grid->res[1] + 2, grid->res[2] + 2 }; + + const int stride0 = 1; + const int stride1 = grid->res[0]; + const int stride2 = grid->res[1] * grid->res[0]; + const int strideA0 = 1; + const int strideA1 = grid->res[0] + 2; + const int strideA2 = (grid->res[1] + 2) * (grid->res[0] + 2); + + const int num_cells = res[0] * res[1] * res[2]; + const int num_cellsA = (res[0] + 2) * (res[1] + 2) * (res[2] + 2); + + HairGridVert *vert_start = grid->verts - (stride0 + stride1 + stride2); + HairGridVert *vert; + int i, j, k; + +#define MARGIN_i0 (i < 1) +#define MARGIN_j0 (j < 1) +#define MARGIN_k0 (k < 1) +#define MARGIN_i1 (i >= resA[0]-1) +#define MARGIN_j1 (j >= resA[1]-1) +#define MARGIN_k1 (k >= resA[2]-1) + +#define NEIGHBOR_MARGIN_i0 (i < 2) +#define NEIGHBOR_MARGIN_j0 (j < 2) +#define NEIGHBOR_MARGIN_k0 (k < 2) +#define NEIGHBOR_MARGIN_i1 (i >= resA[0]-2) +#define NEIGHBOR_MARGIN_j1 (j >= resA[1]-2) +#define NEIGHBOR_MARGIN_k1 (k >= resA[2]-2) + + BLI_assert(num_cells >= 1); + + /* Calculate divergence */ + lVector B(num_cellsA); + for (k = 0; k < resA[2]; ++k) { + for (j = 0; j < resA[1]; ++j) { + for (i = 0; i < resA[0]; ++i) { + int u = i * strideA0 + j * strideA1 + k * strideA2; + bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 || MARGIN_k1; + + if (is_margin) { + B[u] = 0.0f; + continue; + } + + vert = vert_start + i * stride0 + j * stride1 + k * stride2; + + const float *v0 = vert->velocity; + float dx = 0.0f, dy = 0.0f, dz = 0.0f; + if (!NEIGHBOR_MARGIN_i0) + dx += v0[0] - (vert - stride0)->velocity[0]; + if (!NEIGHBOR_MARGIN_i1) + dx += (vert + stride0)->velocity[0] - v0[0]; + if (!NEIGHBOR_MARGIN_j0) + dy += v0[1] - (vert - stride1)->velocity[1]; + if (!NEIGHBOR_MARGIN_j1) + dy += (vert + stride1)->velocity[1] - v0[1]; + if (!NEIGHBOR_MARGIN_k0) + dz += v0[2] - (vert - stride2)->velocity[2]; + if (!NEIGHBOR_MARGIN_k1) + dz += (vert + stride2)->velocity[2] - v0[2]; + + float divergence = -0.5f * flowfac * (dx + dy + dz); + + /* adjustment term for target density */ + float target = hair_volume_density_divergence(vert->density, target_density, target_strength); + + /* B vector contains the finite difference approximation of the velocity divergence. + * Note: according to the discretized Navier-Stokes equation the rhs vector + * and resulting pressure gradient should be multiplied by the (inverse) density; + * however, this is already included in the weighting of hair velocities on the grid! + */ + B[u] = divergence - target; + +#if 0 + { + float wloc[3], loc[3]; + float col0[3] = {0.0, 0.0, 0.0}; + float colp[3] = {0.0, 1.0, 1.0}; + float coln[3] = {1.0, 0.0, 1.0}; + float col[3]; + float fac; + + loc[0] = (float)(i - 1); + loc[1] = (float)(j - 1); + loc[2] = (float)(k - 1); + grid_to_world(grid, wloc, loc); + + if (divergence > 0.0f) { + fac = CLAMPIS(divergence * target_strength, 0.0, 1.0); + interp_v3_v3v3(col, col0, colp, fac); + } + else { + fac = CLAMPIS(-divergence * target_strength, 0.0, 1.0); + interp_v3_v3v3(col, col0, coln, fac); + } + if (fac > 0.05f) + BKE_sim_debug_data_add_circle(grid->debug_data, wloc, 0.01f, col[0], col[1], col[2], "grid", 5522, i, j, k); + } +#endif + } + } + } + + /* Main Poisson equation system: + * This is derived from the discretezation of the Poisson equation + * div(grad(p)) = div(v) + * + * The finite difference approximation yields the linear equation system described here: + * http://en.wikipedia.org/wiki/Discrete_Poisson_equation + */ + lMatrix A(num_cellsA, num_cellsA); + /* Reserve space for the base equation system (without boundary conditions). + * Each column contains a factor 6 on the diagonal + * and up to 6 factors -1 on other places. + */ + A.reserve(Eigen::VectorXi::Constant(num_cellsA, 7)); + + for (k = 0; k < resA[2]; ++k) { + for (j = 0; j < resA[1]; ++j) { + for (i = 0; i < resA[0]; ++i) { + int u = i * strideA0 + j * strideA1 + k * strideA2; + bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 || MARGIN_k1; + + vert = vert_start + i * stride0 + j * stride1 + k * stride2; + if (!is_margin && vert->density > density_threshold) { + int neighbors_lo = 0; + int neighbors_hi = 0; + int non_solid_neighbors = 0; + int neighbor_lo_index[3]; + int neighbor_hi_index[3]; + int n; + + /* check for upper bounds in advance + * to get the correct number of neighbors, + * needed for the diagonal element + */ + if (!NEIGHBOR_MARGIN_k0 && (vert - stride2)->density > density_threshold) + neighbor_lo_index[neighbors_lo++] = u - strideA2; + if (!NEIGHBOR_MARGIN_j0 && (vert - stride1)->density > density_threshold) + neighbor_lo_index[neighbors_lo++] = u - strideA1; + if (!NEIGHBOR_MARGIN_i0 && (vert - stride0)->density > density_threshold) + neighbor_lo_index[neighbors_lo++] = u - strideA0; + if (!NEIGHBOR_MARGIN_i1 && (vert + stride0)->density > density_threshold) + neighbor_hi_index[neighbors_hi++] = u + strideA0; + if (!NEIGHBOR_MARGIN_j1 && (vert + stride1)->density > density_threshold) + neighbor_hi_index[neighbors_hi++] = u + strideA1; + if (!NEIGHBOR_MARGIN_k1 && (vert + stride2)->density > density_threshold) + neighbor_hi_index[neighbors_hi++] = u + strideA2; + + /*int liquid_neighbors = neighbors_lo + neighbors_hi;*/ + non_solid_neighbors = 6; + + for (n = 0; n < neighbors_lo; ++n) + A.insert(neighbor_lo_index[n], u) = -1.0f; + A.insert(u, u) = (float)non_solid_neighbors; + for (n = 0; n < neighbors_hi; ++n) + A.insert(neighbor_hi_index[n], u) = -1.0f; + } + else { + A.insert(u, u) = 1.0f; + } + } + } + } + + ConjugateGradient cg; + cg.setMaxIterations(100); + cg.setTolerance(0.01f); + + cg.compute(A); + + lVector p = cg.solve(B); + + if (cg.info() == Eigen::Success) { + /* Calculate velocity = grad(p) */ + for (k = 0; k < resA[2]; ++k) { + for (j = 0; j < resA[1]; ++j) { + for (i = 0; i < resA[0]; ++i) { + int u = i * strideA0 + j * strideA1 + k * strideA2; + bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 || MARGIN_k1; + if (is_margin) + continue; + + vert = vert_start + i * stride0 + j * stride1 + k * stride2; + if (vert->density > density_threshold) { + float p_left = p[u - strideA0]; + float p_right = p[u + strideA0]; + float p_down = p[u - strideA1]; + float p_up = p[u + strideA1]; + float p_bottom = p[u - strideA2]; + float p_top = p[u + strideA2]; + + /* finite difference estimate of pressure gradient */ + float dvel[3]; + dvel[0] = p_right - p_left; + dvel[1] = p_up - p_down; + dvel[2] = p_top - p_bottom; + mul_v3_fl(dvel, -0.5f * inv_flowfac); + + /* pressure gradient describes velocity delta */ + add_v3_v3v3(vert->velocity_smooth, vert->velocity, dvel); + } + else { + zero_v3(vert->velocity_smooth); + } + } + } + } + +#if 0 + { + int axis = 0; + float offset = 0.0f; + + int slice = (offset - grid->gmin[axis]) / grid->cellsize; + + for (k = 0; k < resA[2]; ++k) { + for (j = 0; j < resA[1]; ++j) { + for (i = 0; i < resA[0]; ++i) { + int u = i * strideA0 + j * strideA1 + k * strideA2; + bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 || MARGIN_k1; + if (i != slice) + continue; + + vert = vert_start + i * stride0 + j * stride1 + k * stride2; + + float wloc[3], loc[3]; + float col0[3] = {0.0, 0.0, 0.0}; + float colp[3] = {0.0, 1.0, 1.0}; + float coln[3] = {1.0, 0.0, 1.0}; + float col[3]; + float fac; + + loc[0] = (float)(i - 1); + loc[1] = (float)(j - 1); + loc[2] = (float)(k - 1); + grid_to_world(grid, wloc, loc); + + float pressure = p[u]; + if (pressure > 0.0f) { + fac = CLAMPIS(pressure * grid->debug1, 0.0, 1.0); + interp_v3_v3v3(col, col0, colp, fac); + } + else { + fac = CLAMPIS(-pressure * grid->debug1, 0.0, 1.0); + interp_v3_v3v3(col, col0, coln, fac); + } + if (fac > 0.05f) + BKE_sim_debug_data_add_circle(grid->debug_data, wloc, 0.01f, col[0], col[1], col[2], "grid", 5533, i, j, k); + + if (!is_margin) { + float dvel[3]; + sub_v3_v3v3(dvel, vert->velocity_smooth, vert->velocity); +// BKE_sim_debug_data_add_vector(grid->debug_data, wloc, dvel, 1, 1, 1, "grid", 5566, i, j, k); + } + + if (!is_margin) { + float d = CLAMPIS(vert->density * grid->debug2, 0.0f, 1.0f); + float col0[3] = {0.3, 0.3, 0.3}; + float colp[3] = {0.0, 0.0, 1.0}; + float col[3]; + + interp_v3_v3v3(col, col0, colp, d); +// if (d > 0.05f) +// BKE_sim_debug_data_add_dot(grid->debug_data, wloc, col[0], col[1], col[2], "grid", 5544, i, j, k); + } + } + } + } + } +#endif + + return true; + } + else { + /* Clear result in case of error */ + for (i = 0, vert = grid->verts; i < num_cells; ++i, ++vert) { + zero_v3(vert->velocity_smooth); + } + + return false; + } +} + +#if 0 /* XXX weighting is incorrect, disabled for now */ +/* Velocity filter kernel + * See http://en.wikipedia.org/wiki/Filter_%28large_eddy_simulation%29 + */ + +BLI_INLINE void hair_volume_filter_box_convolute(HairVertexGrid *grid, float invD, const int kernel_size[3], int i, int j, int k) +{ + int res = grid->res; + int p, q, r; + int minp = max_ii(i - kernel_size[0], 0), maxp = min_ii(i + kernel_size[0], res-1); + int minq = max_ii(j - kernel_size[1], 0), maxq = min_ii(j + kernel_size[1], res-1); + int minr = max_ii(k - kernel_size[2], 0), maxr = min_ii(k + kernel_size[2], res-1); + int offset, kernel_offset, kernel_dq, kernel_dr; + HairGridVert *verts; + float *vel_smooth; + + offset = i + (j + k*res)*res; + verts = grid->verts; + vel_smooth = verts[offset].velocity_smooth; + + kernel_offset = minp + (minq + minr*res)*res; + kernel_dq = res; + kernel_dr = res * res; + for (r = minr; r <= maxr; ++r) { + for (q = minq; q <= maxq; ++q) { + for (p = minp; p <= maxp; ++p) { + + madd_v3_v3fl(vel_smooth, verts[kernel_offset].velocity, invD); + + kernel_offset += 1; + } + kernel_offset += kernel_dq; + } + kernel_offset += kernel_dr; + } +} + +void BPH_hair_volume_vertex_grid_filter_box(HairVertexGrid *grid, int kernel_size) +{ + int size = hair_grid_size(grid->res); + int kernel_sizev[3] = {kernel_size, kernel_size, kernel_size}; + int tot; + float invD; + int i, j, k; + + if (kernel_size <= 0) + return; + + tot = kernel_size * 2 + 1; + invD = 1.0f / (float)(tot*tot*tot); + + /* clear values for convolution */ + for (i = 0; i < size; ++i) { + zero_v3(grid->verts[i].velocity_smooth); + } + + for (i = 0; i < grid->res; ++i) { + for (j = 0; j < grid->res; ++j) { + for (k = 0; k < grid->res; ++k) { + hair_volume_filter_box_convolute(grid, invD, kernel_sizev, i, j, k); + } + } + } + + /* apply as new velocity */ + for (i = 0; i < size; ++i) { + copy_v3_v3(grid->verts[i].velocity, grid->verts[i].velocity_smooth); + } +} +#endif + +HairGrid *BPH_hair_volume_create_vertex_grid(float cellsize, const float gmin[3], const float gmax[3]) +{ + float scale; + float extent[3]; + int resmin[3], resmax[3], res[3]; + float gmin_margin[3], gmax_margin[3]; + int size; + HairGrid *grid; + int i; + + /* sanity check */ + if (cellsize <= 0.0f) + cellsize = 1.0f; + scale = 1.0f / cellsize; + + sub_v3_v3v3(extent, gmax, gmin); + for (i = 0; i < 3; ++i) { + resmin[i] = floor_int(gmin[i] * scale); + resmax[i] = floor_int(gmax[i] * scale) + 1; + + /* add margin of 1 cell */ + resmin[i] -= 1; + resmax[i] += 1; + + res[i] = resmax[i] - resmin[i] + 1; + /* sanity check: avoid null-sized grid */ + if (res[i] < 4) { + res[i] = 4; + resmax[i] = resmin[i] + 4; + } + /* sanity check: avoid too large grid size */ + if (res[i] > MAX_HAIR_GRID_RES) { + res[i] = MAX_HAIR_GRID_RES; + resmax[i] = resmin[i] + MAX_HAIR_GRID_RES; + } + + gmin_margin[i] = (float)resmin[i] * cellsize; + gmax_margin[i] = (float)resmax[i] * cellsize; + } + size = hair_grid_size(res); + + grid = (HairGrid *)MEM_callocN(sizeof(HairGrid), "hair grid"); + grid->res[0] = res[0]; + grid->res[1] = res[1]; + grid->res[2] = res[2]; + copy_v3_v3(grid->gmin, gmin_margin); + copy_v3_v3(grid->gmax, gmax_margin); + grid->cellsize = cellsize; + grid->inv_cellsize = scale; + grid->verts = (HairGridVert *)MEM_callocN(sizeof(HairGridVert) * size, "hair voxel data"); + + return grid; +} + +void BPH_hair_volume_free_vertex_grid(HairGrid *grid) +{ + if (grid) { + if (grid->verts) + MEM_freeN(grid->verts); + MEM_freeN(grid); + } +} + +void BPH_hair_volume_grid_geometry(HairGrid *grid, float *cellsize, int res[3], float gmin[3], float gmax[3]) +{ + if (cellsize) *cellsize = grid->cellsize; + if (res) copy_v3_v3_int(res, grid->res); + if (gmin) copy_v3_v3(gmin, grid->gmin); + if (gmax) copy_v3_v3(gmax, grid->gmax); +} + +#if 0 +static HairGridVert *hair_volume_create_collision_grid(ClothModifierData *clmd, lfVector *lX, unsigned int numverts) +{ + int res = hair_grid_res; + int size = hair_grid_size(res); + HairGridVert *collgrid; + ListBase *colliders; + ColliderCache *col = NULL; + float gmin[3], gmax[3], scale[3]; + /* 2.0f is an experimental value that seems to give good results */ + float collfac = 2.0f * clmd->sim_parms->collider_friction; + unsigned int v = 0; + int i = 0; + + hair_volume_get_boundbox(lX, numverts, gmin, gmax); + hair_grid_get_scale(res, gmin, gmax, scale); + + collgrid = MEM_mallocN(sizeof(HairGridVert) * size, "hair collider voxel data"); + + /* initialize grid */ + for (i = 0; i < size; ++i) { + zero_v3(collgrid[i].velocity); + collgrid[i].density = 0.0f; + } + + /* gather colliders */ + colliders = get_collider_cache(clmd->scene, NULL, NULL); + if (colliders && collfac > 0.0f) { + for (col = colliders->first; col; col = col->next) { + MVert *loc0 = col->collmd->x; + MVert *loc1 = col->collmd->xnew; + float vel[3]; + float weights[8]; + int di, dj, dk; + + for (v=0; v < col->collmd->numverts; v++, loc0++, loc1++) { + int offset; + + if (!hair_grid_point_valid(loc1->co, gmin, gmax)) + continue; + + offset = hair_grid_weights(res, gmin, scale, lX[v], weights); + + sub_v3_v3v3(vel, loc1->co, loc0->co); + + for (di = 0; di < 2; ++di) { + for (dj = 0; dj < 2; ++dj) { + for (dk = 0; dk < 2; ++dk) { + int voffset = offset + di + (dj + dk*res)*res; + int iw = di + dj*2 + dk*4; + + collgrid[voffset].density += weights[iw]; + madd_v3_v3fl(collgrid[voffset].velocity, vel, weights[iw]); + } + } + } + } + } + } + free_collider_cache(&colliders); + + /* divide velocity with density */ + for (i = 0; i < size; i++) { + float density = collgrid[i].density; + if (density > 0.0f) + mul_v3_fl(collgrid[i].velocity, 1.0f/density); + } + + return collgrid; +} +#endif + +bool BPH_hair_volume_get_texture_data(HairGrid *grid, VoxelData *vd) +{ + int totres, i; + int depth; + + vd->resol[0] = grid->res[0]; + vd->resol[1] = grid->res[1]; + vd->resol[2] = grid->res[2]; + + totres = hair_grid_size(grid->res); + + if (vd->hair_type == TEX_VD_HAIRVELOCITY) { + depth = 4; + vd->data_type = TEX_VD_RGBA_PREMUL; + } + else { + depth = 1; + vd->data_type = TEX_VD_INTENSITY; + } + + if (totres > 0) { + vd->dataset = (float *)MEM_mapallocN(sizeof(float) * depth * (totres), "hair volume texture data"); + + for (i = 0; i < totres; ++i) { + switch (vd->hair_type) { + case TEX_VD_HAIRDENSITY: + vd->dataset[i] = grid->verts[i].density; + break; + + case TEX_VD_HAIRRESTDENSITY: + vd->dataset[i] = 0.0f; // TODO + break; + + case TEX_VD_HAIRVELOCITY: { + vd->dataset[i + 0*totres] = grid->verts[i].velocity[0]; + vd->dataset[i + 1*totres] = grid->verts[i].velocity[1]; + vd->dataset[i + 2*totres] = grid->verts[i].velocity[2]; + vd->dataset[i + 3*totres] = len_v3(grid->verts[i].velocity); + break; + } + case TEX_VD_HAIRENERGY: + vd->dataset[i] = 0.0f; // TODO + break; + } + } + } + else { + vd->dataset = NULL; + } + + return true; +} diff --git a/source/blender/physics/intern/implicit.h b/source/blender/physics/intern/implicit.h new file mode 100644 index 00000000000..7ded479f578 --- /dev/null +++ b/source/blender/physics/intern/implicit.h @@ -0,0 +1,184 @@ +/* + * ***** 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) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __IMPLICIT_H__ +#define __IMPLICIT_H__ + +/** \file implicit.h + * \ingroup bph + */ + +#include "stdio.h" + +#include "BLI_utildefines.h" + +#include "BKE_collision.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//#define IMPLICIT_SOLVER_EIGEN +#define IMPLICIT_SOLVER_BLENDER + +#define CLOTH_ROOT_FRAME /* enable use of root frame coordinate transform */ + +#define CLOTH_FORCE_GRAVITY +#define CLOTH_FORCE_DRAG +#define CLOTH_FORCE_SPRING_STRUCTURAL +#define CLOTH_FORCE_SPRING_BEND +#define CLOTH_FORCE_SPRING_GOAL +#define CLOTH_FORCE_EFFECTORS + +//#define IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT + +//#define IMPLICIT_ENABLE_EIGEN_DEBUG + +struct Implicit_Data; +struct ImplicitSolverInput; + +typedef struct ImplicitSolverResult { + int status; + + int iterations; + float error; +} ImplicitSolverResult; + +BLI_INLINE void implicit_print_matrix_elem(float v) +{ + printf("%-8.3f", v); +} + +void BPH_mass_spring_set_vertex_mass(struct Implicit_Data *data, int index, float mass); +void BPH_mass_spring_set_rest_transform(struct Implicit_Data *data, int index, float rot[3][3]); + +void BPH_mass_spring_set_motion_state(struct Implicit_Data *data, int index, const float x[3], const float v[3]); +void BPH_mass_spring_set_position(struct Implicit_Data *data, int index, const float x[3]); +void BPH_mass_spring_set_velocity(struct Implicit_Data *data, int index, const float v[3]); +void BPH_mass_spring_get_motion_state(struct Implicit_Data *data, int index, float x[3], float v[3]); +void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3]); + +/* modified velocity during solver step */ +void BPH_mass_spring_get_new_velocity(struct Implicit_Data *data, int index, float v[3]); +void BPH_mass_spring_set_new_velocity(struct Implicit_Data *data, int index, const float v[3]); + +void BPH_mass_spring_clear_constraints(struct Implicit_Data *data); +void BPH_mass_spring_add_constraint_ndof0(struct Implicit_Data *data, int index, const float dV[3]); +void BPH_mass_spring_add_constraint_ndof1(struct Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3]); +void BPH_mass_spring_add_constraint_ndof2(struct Implicit_Data *data, int index, const float c1[3], const float dV[3]); + +bool BPH_mass_spring_solve_velocities(struct Implicit_Data *data, float dt, struct ImplicitSolverResult *result); +bool BPH_mass_spring_solve_positions(struct Implicit_Data *data, float dt); +void BPH_mass_spring_apply_result(struct Implicit_Data *data); + +/* Clear the force vector at the beginning of the time step */ +void BPH_mass_spring_clear_forces(struct Implicit_Data *data); +/* Fictitious forces introduced by moving coordinate systems */ +void BPH_mass_spring_force_reference_frame(struct Implicit_Data *data, int index, const float acceleration[3], const float omega[3], const float domega_dt[3], float mass); +/* Simple uniform gravity force */ +void BPH_mass_spring_force_gravity(struct Implicit_Data *data, int index, float mass, const float g[3]); +/* Global drag force (velocity damping) */ +void BPH_mass_spring_force_drag(struct Implicit_Data *data, float drag); +/* Custom external force */ +void BPH_mass_spring_force_extern(struct Implicit_Data *data, int i, const float f[3], float dfdx[3][3], float dfdv[3][3]); +/* Wind force, acting on a face */ +void BPH_mass_spring_force_face_wind(struct Implicit_Data *data, int v1, int v2, int v3, int v4, const float (*winvec)[3]); +/* Wind force, acting on an edge */ +void BPH_mass_spring_force_edge_wind(struct Implicit_Data *data, int v1, int v2, float radius1, float radius2, const float (*winvec)[3]); +/* Wind force, acting on a vertex */ +void BPH_mass_spring_force_vertex_wind(struct Implicit_Data *data, int v, float radius, const float (*winvec)[3]); +/* Linear spring force between two points */ +bool BPH_mass_spring_force_spring_linear(struct Implicit_Data *data, int i, int j, float restlen, + float stiffness, float damping, bool no_compress, float clamp_force, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]); +/* Bending force, forming a triangle at the base of two structural springs */ +bool BPH_mass_spring_force_spring_bending(struct Implicit_Data *data, int i, int j, float restlen, + float kb, float cb, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]); +/* Angular bending force based on local target vectors */ +bool BPH_mass_spring_force_spring_bending_angular(struct Implicit_Data *data, int i, int j, int k, + const float target[3], float stiffness, float damping); +/* Global goal spring */ +bool BPH_mass_spring_force_spring_goal(struct Implicit_Data *data, int i, const float goal_x[3], const float goal_v[3], + float stiffness, float damping, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]); + +/* ======== Hair Volumetric Forces ======== */ + +struct HairGrid; + +struct Object; +struct VoxelData; + +#define MAX_HAIR_GRID_RES 256 + +struct HairGrid *BPH_hair_volume_create_vertex_grid(float cellsize, const float gmin[3], const float gmax[3]); +void BPH_hair_volume_free_vertex_grid(struct HairGrid *grid); +void BPH_hair_volume_grid_geometry(struct HairGrid *grid, float *cellsize, int res[3], float gmin[3], float gmax[3]); + +void BPH_hair_volume_grid_clear(struct HairGrid *grid); +void BPH_hair_volume_add_vertex(struct HairGrid *grid, const float x[3], const float v[3]); +void BPH_hair_volume_add_segment(struct HairGrid *grid, + const float x1[3], const float v1[3], const float x2[3], const float v2[3], + const float x3[3], const float v3[3], const float x4[3], const float v4[3], + const float dir1[3], const float dir2[3], const float dir3[3]); + +void BPH_hair_volume_normalize_vertex_grid(struct HairGrid *grid); + +bool BPH_hair_volume_solve_divergence(struct HairGrid *grid, float dt, float target_density, float target_strength); +#if 0 /* XXX weighting is incorrect, disabled for now */ +void BPH_hair_volume_vertex_grid_filter_box(struct HairVertexGrid *grid, int kernel_size); +#endif + +void BPH_hair_volume_grid_interpolate(struct HairGrid *grid, const float x[3], + float *density, float velocity[3], float velocity_smooth[3], + float density_gradient[3], float velocity_gradient[3][3]); + +/* Effect of fluid simulation grid on velocities. + * fluid_factor controls blending between PIC (Particle-in-Cell) + * and FLIP (Fluid-Implicit-Particle) methods (0 = only PIC, 1 = only FLIP) + */ +void BPH_hair_volume_grid_velocity(struct HairGrid *grid, const float x[3], const float v[3], + float fluid_factor, + float r_v[3]); +/* XXX Warning: expressing grid effects on velocity as a force is not very stable, + * due to discontinuities in interpolated values! + * Better use hybrid approaches such as described in + * "Detail Preserving Continuum Simulation of Straight Hair" + * (McAdams, Selle 2009) + */ +void BPH_hair_volume_vertex_grid_forces(struct HairGrid *grid, const float x[3], const float v[3], + float smoothfac, float pressurefac, float minpressure, + float f[3], float dfdx[3][3], float dfdv[3][3]); + +bool BPH_hair_volume_get_texture_data(struct HairGrid *grid, struct VoxelData *vd); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c new file mode 100644 index 00000000000..e6ba55ab605 --- /dev/null +++ b/source/blender/physics/intern/implicit_blender.c @@ -0,0 +1,2031 @@ +/* + * ***** 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) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/physics/intern/implicit_blender.c + * \ingroup bph + */ + +#include "implicit.h" + +#ifdef IMPLICIT_SOLVER_BLENDER + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force.h" +#include "DNA_meshdata_types.h" +#include "DNA_texture_types.h" + +#include "BLI_math.h" +#include "BLI_linklist.h" +#include "BLI_utildefines.h" + +#include "BKE_cloth.h" +#include "BKE_collision.h" +#include "BKE_effect.h" +#include "BKE_global.h" + +#include "BPH_mass_spring.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#ifdef _OPENMP +# define CLOTH_OPENMP_LIMIT 512 +#endif + +#if 0 /* debug timing */ +#ifdef _WIN32 +#include <windows.h> +static LARGE_INTEGER _itstart, _itend; +static LARGE_INTEGER ifreq; +static void itstart(void) +{ + static int first = 1; + if (first) { + QueryPerformanceFrequency(&ifreq); + first = 0; + } + QueryPerformanceCounter(&_itstart); +} +static void itend(void) +{ + QueryPerformanceCounter(&_itend); +} +double itval(void) +{ + return ((double)_itend.QuadPart - + (double)_itstart.QuadPart)/((double)ifreq.QuadPart); +} +#else +#include <sys/time.h> +// intrinsics need better compile flag checking +// #include <xmmintrin.h> +// #include <pmmintrin.h> +// #include <pthread.h> + +static struct timeval _itstart, _itend; +static struct timezone itz; +static void itstart(void) +{ + gettimeofday(&_itstart, &itz); +} +static void itend(void) +{ + gettimeofday(&_itend, &itz); +} +static double itval(void) +{ + double t1, t2; + t1 = (double)_itstart.tv_sec + (double)_itstart.tv_usec/(1000*1000); + t2 = (double)_itend.tv_sec + (double)_itend.tv_usec/(1000*1000); + return t2-t1; +} +#endif +#endif /* debug timing */ + +static float I[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; +static float ZERO[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + +/* +#define C99 +#ifdef C99 +#defineDO_INLINE inline +#else +#defineDO_INLINE static +#endif +*/ +struct Cloth; + +////////////////////////////////////////// +/* fast vector / matrix library, enhancements are welcome :) -dg */ +///////////////////////////////////////// + +/* DEFINITIONS */ +typedef float lfVector[3]; +typedef struct fmatrix3x3 { + float m[3][3]; /* 3x3 matrix */ + unsigned int c, r; /* column and row number */ + /* int pinned; // is this vertex allowed to move? */ + float n1, n2, n3; /* three normal vectors for collision constrains */ + unsigned int vcount; /* vertex count */ + unsigned int scount; /* spring count */ +} fmatrix3x3; + +/////////////////////////// +// float[3] vector +/////////////////////////// +/* simple vector code */ +/* STATUS: verified */ +DO_INLINE void mul_fvector_S(float to[3], float from[3], float scalar) +{ + to[0] = from[0] * scalar; + to[1] = from[1] * scalar; + to[2] = from[2] * scalar; +} +/* simple v^T * v product ("outer product") */ +/* STATUS: HAS TO BE verified (*should* work) */ +DO_INLINE void mul_fvectorT_fvector(float to[3][3], float vectorA[3], float vectorB[3]) +{ + mul_fvector_S(to[0], vectorB, vectorA[0]); + mul_fvector_S(to[1], vectorB, vectorA[1]); + mul_fvector_S(to[2], vectorB, vectorA[2]); +} +/* simple v^T * v product with scalar ("outer product") */ +/* STATUS: HAS TO BE verified (*should* work) */ +DO_INLINE void mul_fvectorT_fvectorS(float to[3][3], float vectorA[3], float vectorB[3], float aS) +{ + mul_fvectorT_fvector(to, vectorA, vectorB); + + mul_fvector_S(to[0], to[0], aS); + mul_fvector_S(to[1], to[1], aS); + mul_fvector_S(to[2], to[2], aS); +} + +#if 0 +/* printf vector[3] on console: for debug output */ +static void print_fvector(float m3[3]) +{ + printf("%f\n%f\n%f\n\n", m3[0], m3[1], m3[2]); +} + +/////////////////////////// +// long float vector float (*)[3] +/////////////////////////// +/* print long vector on console: for debug output */ +DO_INLINE void print_lfvector(float (*fLongVector)[3], unsigned int verts) +{ + unsigned int i = 0; + for (i = 0; i < verts; i++) { + print_fvector(fLongVector[i]); + } +} +#endif + +/* create long vector */ +DO_INLINE lfVector *create_lfvector(unsigned int verts) +{ + /* TODO: check if memory allocation was successful */ + return (lfVector *)MEM_callocN(verts * sizeof(lfVector), "cloth_implicit_alloc_vector"); + // return (lfVector *)cloth_aligned_malloc(&MEMORY_BASE, verts * sizeof(lfVector)); +} +/* delete long vector */ +DO_INLINE void del_lfvector(float (*fLongVector)[3]) +{ + if (fLongVector != NULL) { + MEM_freeN(fLongVector); + // cloth_aligned_free(&MEMORY_BASE, fLongVector); + } +} +/* copy long vector */ +DO_INLINE void cp_lfvector(float (*to)[3], float (*from)[3], unsigned int verts) +{ + memcpy(to, from, verts * sizeof(lfVector)); +} +/* init long vector with float[3] */ +DO_INLINE void init_lfvector(float (*fLongVector)[3], float vector[3], unsigned int verts) +{ + unsigned int i = 0; + for (i = 0; i < verts; i++) { + copy_v3_v3(fLongVector[i], vector); + } +} +/* zero long vector with float[3] */ +DO_INLINE void zero_lfvector(float (*to)[3], unsigned int verts) +{ + memset(to, 0.0f, verts * sizeof(lfVector)); +} +/* multiply long vector with scalar*/ +DO_INLINE void mul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, unsigned int verts) +{ + unsigned int i = 0; + + for (i = 0; i < verts; i++) { + mul_fvector_S(to[i], fLongVector[i], scalar); + } +} +/* multiply long vector with scalar*/ +/* A -= B * float */ +DO_INLINE void submul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, unsigned int verts) +{ + unsigned int i = 0; + for (i = 0; i < verts; i++) { + VECSUBMUL(to[i], fLongVector[i], scalar); + } +} +/* dot product for big vector */ +DO_INLINE float dot_lfvector(float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) +{ + long i = 0; + float temp = 0.0; +// XXX brecht, disabled this for now (first schedule line was already disabled), +// due to non-commutative nature of floating point ops this makes the sim give +// different results each time you run it! +// schedule(guided, 2) +//#pragma omp parallel for reduction(+: temp) if (verts > CLOTH_OPENMP_LIMIT) + for (i = 0; i < (long)verts; i++) { + temp += dot_v3v3(fLongVectorA[i], fLongVectorB[i]); + } + return temp; +} +/* A = B + C --> for big vector */ +DO_INLINE void add_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) +{ + unsigned int i = 0; + + for (i = 0; i < verts; i++) { + VECADD(to[i], fLongVectorA[i], fLongVectorB[i]); + } + +} +/* A = B + C * float --> for big vector */ +DO_INLINE void add_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], float bS, unsigned int verts) +{ + unsigned int i = 0; + + for (i = 0; i < verts; i++) { + VECADDS(to[i], fLongVectorA[i], fLongVectorB[i], bS); + + } +} +/* A = B * float + C * float --> for big vector */ +DO_INLINE void add_lfvectorS_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float aS, float (*fLongVectorB)[3], float bS, unsigned int verts) +{ + unsigned int i = 0; + + for (i = 0; i < verts; i++) { + VECADDSS(to[i], fLongVectorA[i], aS, fLongVectorB[i], bS); + } +} +/* A = B - C * float --> for big vector */ +DO_INLINE void sub_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], float bS, unsigned int verts) +{ + unsigned int i = 0; + for (i = 0; i < verts; i++) { + VECSUBS(to[i], fLongVectorA[i], fLongVectorB[i], bS); + } + +} +/* A = B - C --> for big vector */ +DO_INLINE void sub_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], unsigned int verts) +{ + unsigned int i = 0; + + for (i = 0; i < verts; i++) { + sub_v3_v3v3(to[i], fLongVectorA[i], fLongVectorB[i]); + } + +} +/////////////////////////// +// 3x3 matrix +/////////////////////////// +#if 0 +/* printf 3x3 matrix on console: for debug output */ +static void print_fmatrix(float m3[3][3]) +{ + printf("%f\t%f\t%f\n", m3[0][0], m3[0][1], m3[0][2]); + printf("%f\t%f\t%f\n", m3[1][0], m3[1][1], m3[1][2]); + printf("%f\t%f\t%f\n\n", m3[2][0], m3[2][1], m3[2][2]); +} + +static void print_sparse_matrix(fmatrix3x3 *m) +{ + if (m) { + unsigned int i; + for (i = 0; i < m[0].vcount + m[0].scount; i++) { + printf("%d:\n", i); + print_fmatrix(m[i].m); + } + } +} +#endif + +#if 0 +static void print_lvector(lfVector *v, int numverts) +{ + int i; + for (i = 0; i < numverts; ++i) { + if (i > 0) + printf("\n"); + + printf("%f,\n", v[i][0]); + printf("%f,\n", v[i][1]); + printf("%f,\n", v[i][2]); + } +} +#endif + +#if 0 +static void print_bfmatrix(fmatrix3x3 *m) +{ + int tot = m[0].vcount + m[0].scount; + int size = m[0].vcount * 3; + float *t = MEM_callocN(sizeof(float) * size*size, "bfmatrix"); + int q, i, j; + + for (q = 0; q < tot; ++q) { + int k = 3 * m[q].r; + int l = 3 * m[q].c; + + for (j = 0; j < 3; ++j) { + for (i = 0; i < 3; ++i) { +// if (t[k + i + (l + j) * size] != 0.0f) { +// printf("warning: overwriting value at %d, %d\n", m[q].r, m[q].c); +// } + if (k == l) { + t[k + i + (k + j) * size] += m[q].m[i][j]; + } + else { + t[k + i + (l + j) * size] += m[q].m[i][j]; + t[l + j + (k + i) * size] += m[q].m[j][i]; + } + } + } + } + + for (j = 0; j < size; ++j) { + if (j > 0 && j % 3 == 0) + printf("\n"); + + for (i = 0; i < size; ++i) { + if (i > 0 && i % 3 == 0) + printf(" "); + + implicit_print_matrix_elem(t[i + j * size]); + } + printf("\n"); + } + + MEM_freeN(t); +} +#endif + +/* copy 3x3 matrix */ +DO_INLINE void cp_fmatrix(float to[3][3], float from[3][3]) +{ + // memcpy(to, from, sizeof (float) * 9); + copy_v3_v3(to[0], from[0]); + copy_v3_v3(to[1], from[1]); + copy_v3_v3(to[2], from[2]); +} + +/* copy 3x3 matrix */ +DO_INLINE void initdiag_fmatrixS(float to[3][3], float aS) +{ + cp_fmatrix(to, ZERO); + + to[0][0] = aS; + to[1][1] = aS; + to[2][2] = aS; +} + +#if 0 +/* calculate determinant of 3x3 matrix */ +DO_INLINE float det_fmatrix(float m[3][3]) +{ + return m[0][0]*m[1][1]*m[2][2] + m[1][0]*m[2][1]*m[0][2] + m[0][1]*m[1][2]*m[2][0] - + m[0][0]*m[1][2]*m[2][1] - m[0][1]*m[1][0]*m[2][2] - m[2][0]*m[1][1]*m[0][2]; +} + +DO_INLINE void inverse_fmatrix(float to[3][3], float from[3][3]) +{ + unsigned int i, j; + float d; + + if ((d=det_fmatrix(from)) == 0) { + printf("can't build inverse"); + exit(0); + } + for (i=0;i<3;i++) { + for (j=0;j<3;j++) { + int i1=(i+1)%3; + int i2=(i+2)%3; + int j1=(j+1)%3; + int j2=(j+2)%3; + // reverse indexs i&j to take transpose + to[j][i] = (from[i1][j1]*from[i2][j2]-from[i1][j2]*from[i2][j1])/d; + /* + if (i==j) + to[i][j] = 1.0f / from[i][j]; + else + to[i][j] = 0; + */ + } + } + +} +#endif + +/* 3x3 matrix multiplied by a scalar */ +/* STATUS: verified */ +DO_INLINE void mul_fmatrix_S(float matrix[3][3], float scalar) +{ + mul_fvector_S(matrix[0], matrix[0], scalar); + mul_fvector_S(matrix[1], matrix[1], scalar); + mul_fvector_S(matrix[2], matrix[2], scalar); +} + +/* a vector multiplied by a 3x3 matrix */ +/* STATUS: verified */ +DO_INLINE void mul_fvector_fmatrix(float *to, float *from, float matrix[3][3]) +{ + to[0] = matrix[0][0]*from[0] + matrix[1][0]*from[1] + matrix[2][0]*from[2]; + to[1] = matrix[0][1]*from[0] + matrix[1][1]*from[1] + matrix[2][1]*from[2]; + to[2] = matrix[0][2]*from[0] + matrix[1][2]*from[1] + matrix[2][2]*from[2]; +} + +/* 3x3 matrix multiplied by a vector */ +/* STATUS: verified */ +DO_INLINE void mul_fmatrix_fvector(float *to, float matrix[3][3], float from[3]) +{ + to[0] = dot_v3v3(matrix[0], from); + to[1] = dot_v3v3(matrix[1], from); + to[2] = dot_v3v3(matrix[2], from); +} +/* 3x3 matrix addition with 3x3 matrix */ +DO_INLINE void add_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + VECADD(to[0], matrixA[0], matrixB[0]); + VECADD(to[1], matrixA[1], matrixB[1]); + VECADD(to[2], matrixA[2], matrixB[2]); +} +/* A -= B*x + C*y (3x3 matrix sub-addition with 3x3 matrix) */ +DO_INLINE void subadd_fmatrixS_fmatrixS(float to[3][3], float matrixA[3][3], float aS, float matrixB[3][3], float bS) +{ + VECSUBADDSS(to[0], matrixA[0], aS, matrixB[0], bS); + VECSUBADDSS(to[1], matrixA[1], aS, matrixB[1], bS); + VECSUBADDSS(to[2], matrixA[2], aS, matrixB[2], bS); +} +/* A = B - C (3x3 matrix subtraction with 3x3 matrix) */ +DO_INLINE void sub_fmatrix_fmatrix(float to[3][3], float matrixA[3][3], float matrixB[3][3]) +{ + sub_v3_v3v3(to[0], matrixA[0], matrixB[0]); + sub_v3_v3v3(to[1], matrixA[1], matrixB[1]); + sub_v3_v3v3(to[2], matrixA[2], matrixB[2]); +} +///////////////////////////////////////////////////////////////// +// special functions +///////////////////////////////////////////////////////////////// +/* 3x3 matrix multiplied+added by a vector */ +/* STATUS: verified */ +DO_INLINE void muladd_fmatrix_fvector(float to[3], float matrix[3][3], float from[3]) +{ + to[0] += dot_v3v3(matrix[0], from); + to[1] += dot_v3v3(matrix[1], from); + to[2] += dot_v3v3(matrix[2], from); +} + +BLI_INLINE void outerproduct(float r[3][3], const float a[3], const float b[3]) +{ + mul_v3_v3fl(r[0], a, b[0]); + mul_v3_v3fl(r[1], a, b[1]); + mul_v3_v3fl(r[2], a, b[2]); +} + +BLI_INLINE void cross_m3_v3m3(float r[3][3], const float v[3], float m[3][3]) +{ + cross_v3_v3v3(r[0], v, m[0]); + cross_v3_v3v3(r[1], v, m[1]); + cross_v3_v3v3(r[2], v, m[2]); +} + +BLI_INLINE void cross_v3_identity(float r[3][3], const float v[3]) +{ + r[0][0] = 0.0f; r[1][0] = v[2]; r[2][0] = -v[1]; + r[0][1] = -v[2]; r[1][1] = 0.0f; r[2][1] = v[0]; + r[0][2] = v[1]; r[1][2] = -v[0]; r[2][2] = 0.0f; +} + +BLI_INLINE void madd_m3_m3fl(float r[3][3], float m[3][3], float f) +{ + r[0][0] += m[0][0] * f; + r[0][1] += m[0][1] * f; + r[0][2] += m[0][2] * f; + r[1][0] += m[1][0] * f; + r[1][1] += m[1][1] * f; + r[1][2] += m[1][2] * f; + r[2][0] += m[2][0] * f; + r[2][1] += m[2][1] * f; + r[2][2] += m[2][2] * f; +} + +BLI_INLINE void madd_m3_m3m3fl(float r[3][3], float a[3][3], float b[3][3], float f) +{ + r[0][0] = a[0][0] + b[0][0] * f; + r[0][1] = a[0][1] + b[0][1] * f; + r[0][2] = a[0][2] + b[0][2] * f; + r[1][0] = a[1][0] + b[1][0] * f; + r[1][1] = a[1][1] + b[1][1] * f; + r[1][2] = a[1][2] + b[1][2] * f; + r[2][0] = a[2][0] + b[2][0] * f; + r[2][1] = a[2][1] + b[2][1] * f; + r[2][2] = a[2][2] + b[2][2] * f; +} +///////////////////////////////////////////////////////////////// + +/////////////////////////// +// SPARSE SYMMETRIC big matrix with 3x3 matrix entries +/////////////////////////// +/* printf a big matrix on console: for debug output */ +#if 0 +static void print_bfmatrix(fmatrix3x3 *m3) +{ + unsigned int i = 0; + + for (i = 0; i < m3[0].vcount + m3[0].scount; i++) + { + print_fmatrix(m3[i].m); + } +} +#endif + +BLI_INLINE void init_fmatrix(fmatrix3x3 *matrix, int r, int c) +{ + matrix->r = r; + matrix->c = c; +} + +/* create big matrix */ +DO_INLINE fmatrix3x3 *create_bfmatrix(unsigned int verts, unsigned int springs) +{ + // TODO: check if memory allocation was successful */ + fmatrix3x3 *temp = (fmatrix3x3 *)MEM_callocN(sizeof(fmatrix3x3) * (verts + springs), "cloth_implicit_alloc_matrix"); + int i; + + temp[0].vcount = verts; + temp[0].scount = springs; + + /* vertex part of the matrix is diagonal blocks */ + for (i = 0; i < verts; ++i) { + init_fmatrix(temp + i, i, i); + } + + return temp; +} +/* delete big matrix */ +DO_INLINE void del_bfmatrix(fmatrix3x3 *matrix) +{ + if (matrix != NULL) { + MEM_freeN(matrix); + } +} + +/* copy big matrix */ +DO_INLINE void cp_bfmatrix(fmatrix3x3 *to, fmatrix3x3 *from) +{ + // TODO bounds checking + memcpy(to, from, sizeof(fmatrix3x3) * (from[0].vcount+from[0].scount)); +} + +/* init big matrix */ +// slow in parallel +DO_INLINE void init_bfmatrix(fmatrix3x3 *matrix, float m3[3][3]) +{ + unsigned int i; + + for (i = 0; i < matrix[0].vcount+matrix[0].scount; i++) { + cp_fmatrix(matrix[i].m, m3); + } +} + +/* init the diagonal of big matrix */ +// slow in parallel +DO_INLINE void initdiag_bfmatrix(fmatrix3x3 *matrix, float m3[3][3]) +{ + unsigned int i, j; + float tmatrix[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + + for (i = 0; i < matrix[0].vcount; i++) { + cp_fmatrix(matrix[i].m, m3); + } + for (j = matrix[0].vcount; j < matrix[0].vcount+matrix[0].scount; j++) { + cp_fmatrix(matrix[j].m, tmatrix); + } +} + +/* SPARSE SYMMETRIC multiply big matrix with long vector*/ +/* STATUS: verified */ +DO_INLINE void mul_bfmatrix_lfvector( float (*to)[3], fmatrix3x3 *from, lfVector *fLongVector) +{ + unsigned int i = 0; + unsigned int vcount = from[0].vcount; + lfVector *temp = create_lfvector(vcount); + + zero_lfvector(to, vcount); + +#pragma omp parallel sections private(i) if (vcount > CLOTH_OPENMP_LIMIT) + { +#pragma omp section + { + for (i = from[0].vcount; i < from[0].vcount+from[0].scount; i++) { + muladd_fmatrix_fvector(to[from[i].c], from[i].m, fLongVector[from[i].r]); + } + } +#pragma omp section + { + for (i = 0; i < from[0].vcount+from[0].scount; i++) { + muladd_fmatrix_fvector(temp[from[i].r], from[i].m, fLongVector[from[i].c]); + } + } + } + add_lfvector_lfvector(to, to, temp, from[0].vcount); + + del_lfvector(temp); + + +} + +/* SPARSE SYMMETRIC sub big matrix with big matrix*/ +/* A -= B * float + C * float --> for big matrix */ +/* VERIFIED */ +DO_INLINE void subadd_bfmatrixS_bfmatrixS( fmatrix3x3 *to, fmatrix3x3 *from, float aS, fmatrix3x3 *matrix, float bS) +{ + unsigned int i = 0; + + /* process diagonal elements */ + for (i = 0; i < matrix[0].vcount+matrix[0].scount; i++) { + subadd_fmatrixS_fmatrixS(to[i].m, from[i].m, aS, matrix[i].m, bS); + } + +} + +/////////////////////////////////////////////////////////////////// +// simulator start +/////////////////////////////////////////////////////////////////// + +typedef struct Implicit_Data { + /* inputs */ + fmatrix3x3 *bigI; /* identity (constant) */ + fmatrix3x3 *tfm; /* local coordinate transform */ + fmatrix3x3 *M; /* masses */ + lfVector *F; /* forces */ + fmatrix3x3 *dFdV, *dFdX; /* force jacobians */ + int num_blocks; /* number of off-diagonal blocks (springs) */ + + /* motion state data */ + lfVector *X, *Xnew; /* positions */ + lfVector *V, *Vnew; /* velocities */ + + /* internal solver data */ + lfVector *B; /* B for A*dV = B */ + fmatrix3x3 *A; /* A for A*dV = B */ + + lfVector *dV; /* velocity change (solution of A*dV = B) */ + lfVector *z; /* target velocity in constrained directions */ + fmatrix3x3 *S; /* filtering matrix for constraints */ + fmatrix3x3 *P, *Pinv; /* pre-conditioning matrix */ +} Implicit_Data; + +Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings) +{ + Implicit_Data *id = (Implicit_Data *)MEM_callocN(sizeof(Implicit_Data), "implicit vecmat"); + + /* process diagonal elements */ + id->tfm = create_bfmatrix(numverts, 0); + id->A = create_bfmatrix(numverts, numsprings); + id->dFdV = create_bfmatrix(numverts, numsprings); + id->dFdX = create_bfmatrix(numverts, numsprings); + id->S = create_bfmatrix(numverts, 0); + id->Pinv = create_bfmatrix(numverts, numsprings); + id->P = create_bfmatrix(numverts, numsprings); + id->bigI = create_bfmatrix(numverts, numsprings); // TODO 0 springs + id->M = create_bfmatrix(numverts, numsprings); + id->X = create_lfvector(numverts); + id->Xnew = create_lfvector(numverts); + id->V = create_lfvector(numverts); + id->Vnew = create_lfvector(numverts); + id->F = create_lfvector(numverts); + id->B = create_lfvector(numverts); + id->dV = create_lfvector(numverts); + id->z = create_lfvector(numverts); + + initdiag_bfmatrix(id->bigI, I); + + return id; +} + +void BPH_mass_spring_solver_free(Implicit_Data *id) +{ + del_bfmatrix(id->tfm); + del_bfmatrix(id->A); + del_bfmatrix(id->dFdV); + del_bfmatrix(id->dFdX); + del_bfmatrix(id->S); + del_bfmatrix(id->P); + del_bfmatrix(id->Pinv); + del_bfmatrix(id->bigI); + del_bfmatrix(id->M); + + del_lfvector(id->X); + del_lfvector(id->Xnew); + del_lfvector(id->V); + del_lfvector(id->Vnew); + del_lfvector(id->F); + del_lfvector(id->B); + del_lfvector(id->dV); + del_lfvector(id->z); + + MEM_freeN(id); +} + +/* ==== Transformation from/to root reference frames ==== */ + +BLI_INLINE void world_to_root_v3(Implicit_Data *data, int index, float r[3], const float v[3]) +{ + copy_v3_v3(r, v); + mul_transposed_m3_v3(data->tfm[index].m, r); +} + +BLI_INLINE void root_to_world_v3(Implicit_Data *data, int index, float r[3], const float v[3]) +{ + mul_v3_m3v3(r, data->tfm[index].m, v); +} + +BLI_INLINE void world_to_root_m3(Implicit_Data *data, int index, float r[3][3], float m[3][3]) +{ + float trot[3][3]; + copy_m3_m3(trot, data->tfm[index].m); + transpose_m3(trot); + mul_m3_m3m3(r, trot, m); +} + +BLI_INLINE void root_to_world_m3(Implicit_Data *data, int index, float r[3][3], float m[3][3]) +{ + mul_m3_m3m3(r, data->tfm[index].m, m); +} + +/* ================================ */ + +DO_INLINE void filter(lfVector *V, fmatrix3x3 *S) +{ + unsigned int i=0; + + for (i = 0; i < S[0].vcount; i++) { + mul_m3_v3(S[i].m, V[S[i].r]); + } +} + +#if 0 /* this version of the CG algorithm does not work very well with partial constraints (where S has non-zero elements) */ +static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S) +{ + // Solves for unknown X in equation AX=B + unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100; + float conjgrad_epsilon=0.0001f /* , conjgrad_lasterror=0 */ /* UNUSED */; + lfVector *q, *d, *tmp, *r; + float s, starget, a, s_prev; + unsigned int numverts = lA[0].vcount; + q = create_lfvector(numverts); + d = create_lfvector(numverts); + tmp = create_lfvector(numverts); + r = create_lfvector(numverts); + + // zero_lfvector(ldV, CLOTHPARTICLES); + filter(ldV, S); + + add_lfvector_lfvector(ldV, ldV, z, numverts); + + // r = B - Mul(tmp, A, X); // just use B if X known to be zero + cp_lfvector(r, lB, numverts); + mul_bfmatrix_lfvector(tmp, lA, ldV); + sub_lfvector_lfvector(r, r, tmp, numverts); + + filter(r, S); + + cp_lfvector(d, r, numverts); + + s = dot_lfvector(r, r, numverts); + starget = s * sqrtf(conjgrad_epsilon); + + while (s>starget && conjgrad_loopcount < conjgrad_looplimit) { + // Mul(q, A, d); // q = A*d; + mul_bfmatrix_lfvector(q, lA, d); + + filter(q, S); + + a = s/dot_lfvector(d, q, numverts); + + // X = X + d*a; + add_lfvector_lfvectorS(ldV, ldV, d, a, numverts); + + // r = r - q*a; + sub_lfvector_lfvectorS(r, r, q, a, numverts); + + s_prev = s; + s = dot_lfvector(r, r, numverts); + + //d = r+d*(s/s_prev); + add_lfvector_lfvectorS(d, r, d, (s/s_prev), numverts); + + filter(d, S); + + conjgrad_loopcount++; + } + /* conjgrad_lasterror = s; */ /* UNUSED */ + + del_lfvector(q); + del_lfvector(d); + del_lfvector(tmp); + del_lfvector(r); + // printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount); + + return conjgrad_loopcount<conjgrad_looplimit; // true means we reached desired accuracy in given time - ie stable +} +#endif + +static int cg_filtered(lfVector *ldV, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, ImplicitSolverResult *result) +{ + // Solves for unknown X in equation AX=B + unsigned int conjgrad_loopcount=0, conjgrad_looplimit=100; + float conjgrad_epsilon=0.01f; + + unsigned int numverts = lA[0].vcount; + lfVector *fB = create_lfvector(numverts); + lfVector *AdV = create_lfvector(numverts); + lfVector *r = create_lfvector(numverts); + lfVector *c = create_lfvector(numverts); + lfVector *q = create_lfvector(numverts); + lfVector *s = create_lfvector(numverts); + float bnorm2, delta_new, delta_old, delta_target, alpha; + + cp_lfvector(ldV, z, numverts); + + /* d0 = filter(B)^T * P * filter(B) */ + cp_lfvector(fB, lB, numverts); + filter(fB, S); + bnorm2 = dot_lfvector(fB, fB, numverts); + delta_target = conjgrad_epsilon*conjgrad_epsilon * bnorm2; + + /* r = filter(B - A * dV) */ + mul_bfmatrix_lfvector(AdV, lA, ldV); + sub_lfvector_lfvector(r, lB, AdV, numverts); + filter(r, S); + + /* c = filter(P^-1 * r) */ + cp_lfvector(c, r, numverts); + filter(c, S); + + /* delta = r^T * c */ + delta_new = dot_lfvector(r, c, numverts); + +#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT + printf("==== A ====\n"); + print_bfmatrix(lA); + printf("==== z ====\n"); + print_lvector(z, numverts); + printf("==== B ====\n"); + print_lvector(lB, numverts); + printf("==== S ====\n"); + print_bfmatrix(S); +#endif + + while (delta_new > delta_target && conjgrad_loopcount < conjgrad_looplimit) { + mul_bfmatrix_lfvector(q, lA, c); + filter(q, S); + + alpha = delta_new / dot_lfvector(c, q, numverts); + + add_lfvector_lfvectorS(ldV, ldV, c, alpha, numverts); + + add_lfvector_lfvectorS(r, r, q, -alpha, numverts); + + /* s = P^-1 * r */ + cp_lfvector(s, r, numverts); + delta_old = delta_new; + delta_new = dot_lfvector(r, s, numverts); + + add_lfvector_lfvectorS(c, s, c, delta_new / delta_old, numverts); + filter(c, S); + + conjgrad_loopcount++; + } + +#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT + printf("==== dV ====\n"); + print_lvector(ldV, numverts); + printf("========\n"); +#endif + + del_lfvector(fB); + del_lfvector(AdV); + del_lfvector(r); + del_lfvector(c); + del_lfvector(q); + del_lfvector(s); + // printf("W/O conjgrad_loopcount: %d\n", conjgrad_loopcount); + + result->status = conjgrad_loopcount < conjgrad_looplimit ? BPH_SOLVER_SUCCESS : BPH_SOLVER_NO_CONVERGENCE; + result->iterations = conjgrad_loopcount; + result->error = bnorm2 > 0.0f ? sqrtf(delta_new / bnorm2) : 0.0f; + + return conjgrad_loopcount < conjgrad_looplimit; // true means we reached desired accuracy in given time - ie stable +} + +#if 0 +// block diagonalizer +DO_INLINE void BuildPPinv(fmatrix3x3 *lA, fmatrix3x3 *P, fmatrix3x3 *Pinv) +{ + unsigned int i = 0; + + // Take only the diagonal blocks of A +// #pragma omp parallel for private(i) if (lA[0].vcount > CLOTH_OPENMP_LIMIT) + for (i = 0; i<lA[0].vcount; i++) { + // block diagonalizer + cp_fmatrix(P[i].m, lA[i].m); + inverse_fmatrix(Pinv[i].m, P[i].m); + + } +} +/* +// version 1.3 +static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, fmatrix3x3 *P, fmatrix3x3 *Pinv) +{ + unsigned int numverts = lA[0].vcount, iterations = 0, conjgrad_looplimit=100; + float delta0 = 0, deltaNew = 0, deltaOld = 0, alpha = 0; + float conjgrad_epsilon=0.0001; // 0.2 is dt for steps=5 + lfVector *r = create_lfvector(numverts); + lfVector *p = create_lfvector(numverts); + lfVector *s = create_lfvector(numverts); + lfVector *h = create_lfvector(numverts); + + BuildPPinv(lA, P, Pinv); + + filter(dv, S); + add_lfvector_lfvector(dv, dv, z, numverts); + + mul_bfmatrix_lfvector(r, lA, dv); + sub_lfvector_lfvector(r, lB, r, numverts); + filter(r, S); + + mul_prevfmatrix_lfvector(p, Pinv, r); + filter(p, S); + + deltaNew = dot_lfvector(r, p, numverts); + + delta0 = deltaNew * sqrt(conjgrad_epsilon); + + // itstart(); + + while ((deltaNew > delta0) && (iterations < conjgrad_looplimit)) + { + iterations++; + + mul_bfmatrix_lfvector(s, lA, p); + filter(s, S); + + alpha = deltaNew / dot_lfvector(p, s, numverts); + + add_lfvector_lfvectorS(dv, dv, p, alpha, numverts); + + add_lfvector_lfvectorS(r, r, s, -alpha, numverts); + + mul_prevfmatrix_lfvector(h, Pinv, r); + filter(h, S); + + deltaOld = deltaNew; + + deltaNew = dot_lfvector(r, h, numverts); + + add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts); + + filter(p, S); + + } + + // itend(); + // printf("cg_filtered_pre time: %f\n", (float)itval()); + + del_lfvector(h); + del_lfvector(s); + del_lfvector(p); + del_lfvector(r); + + printf("iterations: %d\n", iterations); + + return iterations<conjgrad_looplimit; +} +*/ +// version 1.4 +static int cg_filtered_pre(lfVector *dv, fmatrix3x3 *lA, lfVector *lB, lfVector *z, fmatrix3x3 *S, fmatrix3x3 *P, fmatrix3x3 *Pinv, fmatrix3x3 *bigI) +{ + unsigned int numverts = lA[0].vcount, iterations = 0, conjgrad_looplimit=100; + float delta0 = 0, deltaNew = 0, deltaOld = 0, alpha = 0, tol = 0; + lfVector *r = create_lfvector(numverts); + lfVector *p = create_lfvector(numverts); + lfVector *s = create_lfvector(numverts); + lfVector *h = create_lfvector(numverts); + lfVector *bhat = create_lfvector(numverts); + lfVector *btemp = create_lfvector(numverts); + + BuildPPinv(lA, P, Pinv); + + initdiag_bfmatrix(bigI, I); + sub_bfmatrix_Smatrix(bigI, bigI, S); + + // x = Sx_0+(I-S)z + filter(dv, S); + add_lfvector_lfvector(dv, dv, z, numverts); + + // b_hat = S(b-A(I-S)z) + mul_bfmatrix_lfvector(r, lA, z); + mul_bfmatrix_lfvector(bhat, bigI, r); + sub_lfvector_lfvector(bhat, lB, bhat, numverts); + + // r = S(b-Ax) + mul_bfmatrix_lfvector(r, lA, dv); + sub_lfvector_lfvector(r, lB, r, numverts); + filter(r, S); + + // p = SP^-1r + mul_prevfmatrix_lfvector(p, Pinv, r); + filter(p, S); + + // delta0 = bhat^TP^-1bhat + mul_prevfmatrix_lfvector(btemp, Pinv, bhat); + delta0 = dot_lfvector(bhat, btemp, numverts); + + // deltaNew = r^TP + deltaNew = dot_lfvector(r, p, numverts); + + /* + filter(dv, S); + add_lfvector_lfvector(dv, dv, z, numverts); + + mul_bfmatrix_lfvector(r, lA, dv); + sub_lfvector_lfvector(r, lB, r, numverts); + filter(r, S); + + mul_prevfmatrix_lfvector(p, Pinv, r); + filter(p, S); + + deltaNew = dot_lfvector(r, p, numverts); + + delta0 = deltaNew * sqrt(conjgrad_epsilon); + */ + + // itstart(); + + tol = (0.01*0.2); + + while ((deltaNew > delta0*tol*tol) && (iterations < conjgrad_looplimit)) + { + iterations++; + + mul_bfmatrix_lfvector(s, lA, p); + filter(s, S); + + alpha = deltaNew / dot_lfvector(p, s, numverts); + + add_lfvector_lfvectorS(dv, dv, p, alpha, numverts); + + add_lfvector_lfvectorS(r, r, s, -alpha, numverts); + + mul_prevfmatrix_lfvector(h, Pinv, r); + filter(h, S); + + deltaOld = deltaNew; + + deltaNew = dot_lfvector(r, h, numverts); + + add_lfvector_lfvectorS(p, h, p, deltaNew / deltaOld, numverts); + + filter(p, S); + + } + + // itend(); + // printf("cg_filtered_pre time: %f\n", (float)itval()); + + del_lfvector(btemp); + del_lfvector(bhat); + del_lfvector(h); + del_lfvector(s); + del_lfvector(p); + del_lfvector(r); + + // printf("iterations: %d\n", iterations); + + return iterations<conjgrad_looplimit; +} +#endif + +bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSolverResult *result) +{ + unsigned int numverts = data->dFdV[0].vcount; + + lfVector *dFdXmV = create_lfvector(numverts); + zero_lfvector(data->dV, numverts); + + cp_bfmatrix(data->A, data->M); + + subadd_bfmatrixS_bfmatrixS(data->A, data->dFdV, dt, data->dFdX, (dt*dt)); + + mul_bfmatrix_lfvector(dFdXmV, data->dFdX, data->V); + + add_lfvectorS_lfvectorS(data->B, data->F, dt, dFdXmV, (dt*dt), numverts); + + // itstart(); + + cg_filtered(data->dV, data->A, data->B, data->z, data->S, result); /* conjugate gradient algorithm to solve Ax=b */ + // cg_filtered_pre(id->dV, id->A, id->B, id->z, id->S, id->P, id->Pinv, id->bigI); + + // itend(); + // printf("cg_filtered calc time: %f\n", (float)itval()); + + // advance velocities + add_lfvector_lfvector(data->Vnew, data->V, data->dV, numverts); + + del_lfvector(dFdXmV); + + return result->status == BPH_SOLVER_SUCCESS; +} + +bool BPH_mass_spring_solve_positions(Implicit_Data *data, float dt) +{ + int numverts = data->M[0].vcount; + + // advance positions + add_lfvector_lfvectorS(data->Xnew, data->X, data->Vnew, dt, numverts); + + return true; +} + +void BPH_mass_spring_apply_result(Implicit_Data *data) +{ + int numverts = data->M[0].vcount; + cp_lfvector(data->X, data->Xnew, numverts); + cp_lfvector(data->V, data->Vnew, numverts); +} + +void BPH_mass_spring_set_vertex_mass(Implicit_Data *data, int index, float mass) +{ + unit_m3(data->M[index].m); + mul_m3_fl(data->M[index].m, mass); +} + +void BPH_mass_spring_set_rest_transform(Implicit_Data *data, int index, float tfm[3][3]) +{ +#ifdef CLOTH_ROOT_FRAME + copy_m3_m3(data->tfm[index].m, tfm); +#else + unit_m3(data->tfm[index].m); + (void)tfm; +#endif +} + +void BPH_mass_spring_set_motion_state(Implicit_Data *data, int index, const float x[3], const float v[3]) +{ + world_to_root_v3(data, index, data->X[index], x); + world_to_root_v3(data, index, data->V[index], v); +} + +void BPH_mass_spring_set_position(Implicit_Data *data, int index, const float x[3]) +{ + world_to_root_v3(data, index, data->X[index], x); +} + +void BPH_mass_spring_set_velocity(Implicit_Data *data, int index, const float v[3]) +{ + world_to_root_v3(data, index, data->V[index], v); +} + +void BPH_mass_spring_get_motion_state(struct Implicit_Data *data, int index, float x[3], float v[3]) +{ + if (x) root_to_world_v3(data, index, x, data->X[index]); + if (v) root_to_world_v3(data, index, v, data->V[index]); +} + +void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3]) +{ + root_to_world_v3(data, index, x, data->X[index]); +} + +void BPH_mass_spring_get_new_velocity(struct Implicit_Data *data, int index, float v[3]) +{ + root_to_world_v3(data, index, v, data->Vnew[index]); +} + +void BPH_mass_spring_set_new_velocity(struct Implicit_Data *data, int index, const float v[3]) +{ + world_to_root_v3(data, index, data->Vnew[index], v); +} + +/* -------------------------------- */ + +static int BPH_mass_spring_add_block(Implicit_Data *data, int v1, int v2) +{ + int s = data->M[0].vcount + data->num_blocks; /* index from array start */ + BLI_assert(s < data->M[0].vcount + data->M[0].scount); + ++data->num_blocks; + + /* tfm and S don't have spring entries (diagonal blocks only) */ + init_fmatrix(data->bigI + s, v1, v2); + init_fmatrix(data->M + s, v1, v2); + init_fmatrix(data->dFdX + s, v1, v2); + init_fmatrix(data->dFdV + s, v1, v2); + init_fmatrix(data->A + s, v1, v2); + init_fmatrix(data->P + s, v1, v2); + init_fmatrix(data->Pinv + s, v1, v2); + + return s; +} + +void BPH_mass_spring_clear_constraints(Implicit_Data *data) +{ + int i, numverts = data->S[0].vcount; + for (i = 0; i < numverts; ++i) { + unit_m3(data->S[i].m); + zero_v3(data->z[i]); + } +} + +void BPH_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3]) +{ + zero_m3(data->S[index].m); + + world_to_root_v3(data, index, data->z[index], dV); +} + +void BPH_mass_spring_add_constraint_ndof1(Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3]) +{ + float m[3][3], p[3], q[3], u[3], cmat[3][3]; + + world_to_root_v3(data, index, p, c1); + mul_fvectorT_fvector(cmat, p, p); + sub_m3_m3m3(m, I, cmat); + + world_to_root_v3(data, index, q, c2); + mul_fvectorT_fvector(cmat, q, q); + sub_m3_m3m3(m, m, cmat); + + /* XXX not sure but multiplication should work here */ + copy_m3_m3(data->S[index].m, m); +// mul_m3_m3m3(data->S[index].m, data->S[index].m, m); + + world_to_root_v3(data, index, u, dV); + add_v3_v3(data->z[index], u); +} + +void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data, int index, const float c1[3], const float dV[3]) +{ + float m[3][3], p[3], u[3], cmat[3][3]; + + world_to_root_v3(data, index, p, c1); + mul_fvectorT_fvector(cmat, p, p); + sub_m3_m3m3(m, I, cmat); + + copy_m3_m3(data->S[index].m, m); +// mul_m3_m3m3(data->S[index].m, data->S[index].m, m); + + world_to_root_v3(data, index, u, dV); + add_v3_v3(data->z[index], u); +} + +void BPH_mass_spring_clear_forces(Implicit_Data *data) +{ + int numverts = data->M[0].vcount; + zero_lfvector(data->F, numverts); + init_bfmatrix(data->dFdX, ZERO); + init_bfmatrix(data->dFdV, ZERO); + + data->num_blocks = 0; +} + +void BPH_mass_spring_force_reference_frame(Implicit_Data *data, int index, const float acceleration[3], const float omega[3], const float domega_dt[3], float mass) +{ +#ifdef CLOTH_ROOT_FRAME + float acc[3], w[3], dwdt[3]; + float f[3], dfdx[3][3], dfdv[3][3]; + float euler[3], coriolis[3], centrifugal[3], rotvel[3]; + float deuler[3][3], dcoriolis[3][3], dcentrifugal[3][3], drotvel[3][3]; + + world_to_root_v3(data, index, acc, acceleration); + world_to_root_v3(data, index, w, omega); + world_to_root_v3(data, index, dwdt, domega_dt); + + cross_v3_v3v3(euler, dwdt, data->X[index]); + cross_v3_v3v3(coriolis, w, data->V[index]); + mul_v3_fl(coriolis, 2.0f); + cross_v3_v3v3(rotvel, w, data->X[index]); + cross_v3_v3v3(centrifugal, w, rotvel); + + sub_v3_v3v3(f, acc, euler); + sub_v3_v3(f, coriolis); + sub_v3_v3(f, centrifugal); + + mul_v3_fl(f, mass); /* F = m * a */ + + cross_v3_identity(deuler, dwdt); + cross_v3_identity(dcoriolis, w); + mul_m3_fl(dcoriolis, 2.0f); + cross_v3_identity(drotvel, w); + cross_m3_v3m3(dcentrifugal, w, drotvel); + + add_m3_m3m3(dfdx, deuler, dcentrifugal); + negate_m3(dfdx); + mul_m3_fl(dfdx, mass); + + copy_m3_m3(dfdv, dcoriolis); + negate_m3(dfdv); + mul_m3_fl(dfdv, mass); + + add_v3_v3(data->F[index], f); + add_m3_m3m3(data->dFdX[index].m, data->dFdX[index].m, dfdx); + add_m3_m3m3(data->dFdV[index].m, data->dFdV[index].m, dfdv); +#else + (void)data; + (void)index; + (void)acceleration; + (void)omega; + (void)domega_dt; +#endif +} + +void BPH_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, const float g[3]) +{ + /* force = mass * acceleration (in this case: gravity) */ + float f[3]; + world_to_root_v3(data, index, f, g); + mul_v3_fl(f, mass); + + add_v3_v3(data->F[index], f); +} + +void BPH_mass_spring_force_drag(Implicit_Data *data, float drag) +{ + int i, numverts = data->M[0].vcount; + for (i = 0; i < numverts; i++) { + float tmp[3][3]; + + /* NB: uses root space velocity, no need to transform */ + madd_v3_v3fl(data->F[i], data->V[i], -drag); + + copy_m3_m3(tmp, I); + mul_m3_fl(tmp, -drag); + add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, tmp); + } +} + +void BPH_mass_spring_force_extern(struct Implicit_Data *data, int i, const float f[3], float dfdx[3][3], float dfdv[3][3]) +{ + float tf[3], tdfdx[3][3], tdfdv[3][3]; + world_to_root_v3(data, i, tf, f); + world_to_root_m3(data, i, tdfdx, dfdx); + world_to_root_m3(data, i, tdfdv, dfdv); + + add_v3_v3(data->F[i], tf); + add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, tdfdx); + add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, tdfdv); +} + +static float calc_nor_area_tri(float nor[3], const float v1[3], const float v2[3], const float v3[3]) +{ + float n1[3], n2[3]; + + sub_v3_v3v3(n1, v1, v2); + sub_v3_v3v3(n2, v2, v3); + + cross_v3_v3v3(nor, n1, n2); + return normalize_v3(nor); +} + +static float calc_nor_area_quad(float nor[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3]) +{ + float n1[3], n2[3]; + + sub_v3_v3v3(n1, v1, v3); + sub_v3_v3v3(n2, v2, v4); + + cross_v3_v3v3(nor, n1, n2); + return normalize_v3(nor); +} + +/* XXX does not support force jacobians yet, since the effector system does not provide them either */ +void BPH_mass_spring_force_face_wind(Implicit_Data *data, int v1, int v2, int v3, int v4, const float (*winvec)[3]) +{ + const float effector_scale = 0.02f; + float win[3], nor[3], area; + float factor; + + // calculate face normal and area + if (v4) { + area = calc_nor_area_quad(nor, data->X[v1], data->X[v2], data->X[v3], data->X[v4]); + factor = effector_scale * area * 0.25f; + } + else { + area = calc_nor_area_tri(nor, data->X[v1], data->X[v2], data->X[v3]); + factor = effector_scale * area / 3.0f; + } + + world_to_root_v3(data, v1, win, winvec[v1]); + madd_v3_v3fl(data->F[v1], nor, factor * dot_v3v3(win, nor)); + + world_to_root_v3(data, v2, win, winvec[v2]); + madd_v3_v3fl(data->F[v2], nor, factor * dot_v3v3(win, nor)); + + world_to_root_v3(data, v3, win, winvec[v3]); + madd_v3_v3fl(data->F[v3], nor, factor * dot_v3v3(win, nor)); + + if (v4) { + world_to_root_v3(data, v4, win, winvec[v4]); + madd_v3_v3fl(data->F[v4], nor, factor * dot_v3v3(win, nor)); + } +} + +static void edge_wind_vertex(const float dir[3], float length, float radius, const float wind[3], float f[3], float UNUSED(dfdx[3][3]), float UNUSED(dfdv[3][3])) +{ + const float density = 0.01f; /* XXX arbitrary value, corresponds to effect of air density */ + float cos_alpha, sin_alpha, cross_section; + float windlen = len_v3(wind); + + if (windlen == 0.0f) { + zero_v3(f); + return; + } + + /* angle of wind direction to edge */ + cos_alpha = dot_v3v3(wind, dir) / windlen; + sin_alpha = sqrtf(1.0f - cos_alpha * cos_alpha); + cross_section = radius * ((float)M_PI * radius * sin_alpha + length * cos_alpha); + + mul_v3_v3fl(f, wind, density * cross_section); +} + +void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, float radius1, float radius2, const float (*winvec)[3]) +{ + float win[3], dir[3], length; + float f[3], dfdx[3][3], dfdv[3][3]; + + sub_v3_v3v3(dir, data->X[v1], data->X[v2]); + length = normalize_v3(dir); + + world_to_root_v3(data, v1, win, winvec[v1]); + edge_wind_vertex(dir, length, radius1, win, f, dfdx, dfdv); + add_v3_v3(data->F[v1], f); + + world_to_root_v3(data, v2, win, winvec[v2]); + edge_wind_vertex(dir, length, radius2, win, f, dfdx, dfdv); + add_v3_v3(data->F[v2], f); +} + +void BPH_mass_spring_force_vertex_wind(Implicit_Data *data, int v, float UNUSED(radius), const float (*winvec)[3]) +{ + const float density = 0.01f; /* XXX arbitrary value, corresponds to effect of air density */ + + float wind[3]; + float f[3]; + + world_to_root_v3(data, v, wind, winvec[v]); + mul_v3_v3fl(f, wind, density); + add_v3_v3(data->F[v], f); +} + +BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, float L, float k) +{ + // dir is unit length direction, rest is spring's restlength, k is spring constant. + //return ( (I-outerprod(dir, dir))*Min(1.0f, rest/length) - I) * -k; + outerproduct(to, dir, dir); + sub_m3_m3m3(to, I, to); + + mul_m3_fl(to, (L/length)); + sub_m3_m3m3(to, to, I); + mul_m3_fl(to, k); +} + +/* unused */ +#if 0 +BLI_INLINE void dfdx_damp(float to[3][3], const float dir[3], float length, const float vel[3], float rest, float damping) +{ + // inner spring damping vel is the relative velocity of the endpoints. + // return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest))); + mul_fvectorT_fvector(to, dir, dir); + sub_fmatrix_fmatrix(to, I, to); + mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel)/MAX2(length, rest)))); +} +#endif + +BLI_INLINE void dfdv_damp(float to[3][3], const float dir[3], float damping) +{ + // derivative of force wrt velocity + outerproduct(to, dir, dir); + mul_m3_fl(to, -damping); +} + +BLI_INLINE float fb(float length, float L) +{ + float x = length / L; + float xx = x * x; + float xxx = xx * x; + float xxxx = xxx * x; + return (-11.541f * xxxx + 34.193f * xxx - 39.083f * xx + 23.116f * x - 9.713f); +} + +BLI_INLINE float fbderiv(float length, float L) +{ + float x = length/L; + float xx = x * x; + float xxx = xx * x; + return (-46.164f * xxx + 102.579f * xx - 78.166f * x + 23.116f); +} + +BLI_INLINE float fbstar(float length, float L, float kb, float cb) +{ + float tempfb_fl = kb * fb(length, L); + float fbstar_fl = cb * (length - L); + + if (tempfb_fl < fbstar_fl) + return fbstar_fl; + else + return tempfb_fl; +} + +// function to calculae bending spring force (taken from Choi & Co) +BLI_INLINE float fbstar_jacobi(float length, float L, float kb, float cb) +{ + float tempfb_fl = kb * fb(length, L); + float fbstar_fl = cb * (length - L); + + if (tempfb_fl < fbstar_fl) { + return -cb; + } + else { + return -kb * fbderiv(length, L); + } +} + +/* calculate elonglation */ +BLI_INLINE bool spring_length(Implicit_Data *data, int i, int j, float r_extent[3], float r_dir[3], float *r_length, float r_vel[3]) +{ + sub_v3_v3v3(r_extent, data->X[j], data->X[i]); + sub_v3_v3v3(r_vel, data->V[j], data->V[i]); + *r_length = len_v3(r_extent); + + if (*r_length > ALMOST_ZERO) { + /* + if (length>L) { + if ((clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED) && + ( ((length-L)*100.0f/L) > clmd->sim_parms->maxspringlen )) { + // cut spring! + s->flags |= CSPRING_FLAG_DEACTIVATE; + return false; + } + } + */ + mul_v3_v3fl(r_dir, r_extent, 1.0f/(*r_length)); + } + else { + zero_v3(r_dir); + } + + return true; +} + +BLI_INLINE void apply_spring(Implicit_Data *data, int i, int j, const float f[3], float dfdx[3][3], float dfdv[3][3]) +{ + int block_ij = BPH_mass_spring_add_block(data, i, j); + + add_v3_v3(data->F[i], f); + sub_v3_v3(data->F[j], f); + + add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfdx); + add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfdx); + sub_m3_m3m3(data->dFdX[block_ij].m, data->dFdX[block_ij].m, dfdx); + + add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, dfdv); + add_m3_m3m3(data->dFdV[j].m, data->dFdV[j].m, dfdv); + sub_m3_m3m3(data->dFdV[block_ij].m, data->dFdV[block_ij].m, dfdv); +} + +bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, float restlen, + float stiffness, float damping, bool no_compress, float clamp_force, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) +{ + float extent[3], length, dir[3], vel[3]; + + // calculate elonglation + spring_length(data, i, j, extent, dir, &length, vel); + + if (length > restlen || no_compress) { + float stretch_force, f[3], dfdx[3][3], dfdv[3][3]; + + stretch_force = stiffness * (length - restlen); + if (clamp_force > 0.0f && stretch_force > clamp_force) { + stretch_force = clamp_force; + } + mul_v3_v3fl(f, dir, stretch_force); + + // Ascher & Boxman, p.21: Damping only during elonglation + // something wrong with it... + madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir)); + + dfdx_spring(dfdx, dir, length, restlen, stiffness); + dfdv_damp(dfdv, dir, damping); + + apply_spring(data, i, j, f, dfdx, dfdv); + + if (r_f) copy_v3_v3(r_f, f); + if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); + if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); + + return true; + } + else { + if (r_f) zero_v3(r_f); + if (r_dfdx) zero_m3(r_dfdx); + if (r_dfdv) zero_m3(r_dfdv); + + return false; + } +} + +/* See "Stable but Responsive Cloth" (Choi, Ko 2005) */ +bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, float restlen, + float kb, float cb, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) +{ + float extent[3], length, dir[3], vel[3]; + + // calculate elonglation + spring_length(data, i, j, extent, dir, &length, vel); + + if (length < restlen) { + float f[3], dfdx[3][3], dfdv[3][3]; + + mul_v3_v3fl(f, dir, fbstar(length, restlen, kb, cb)); + + outerproduct(dfdx, dir, dir); + mul_m3_fl(dfdx, fbstar_jacobi(length, restlen, kb, cb)); + + /* XXX damping not supported */ + zero_m3(dfdv); + + apply_spring(data, i, j, f, dfdx, dfdv); + + if (r_f) copy_v3_v3(r_f, f); + if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); + if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); + + return true; + } + else { + if (r_f) zero_v3(r_f); + if (r_dfdx) zero_m3(r_dfdx); + if (r_dfdv) zero_m3(r_dfdv); + + return false; + } +} + +/* Jacobian of a direction vector. + * Basically the part of the differential orthogonal to the direction, + * inversely proportional to the length of the edge. + * + * dD_ij/dx_i = -dD_ij/dx_j = (D_ij * D_ij^T - I) / len_ij + */ +BLI_INLINE void spring_grad_dir(Implicit_Data *data, int i, int j, float edge[3], float dir[3], float grad_dir[3][3]) +{ + float length; + + sub_v3_v3v3(edge, data->X[j], data->X[i]); + length = normalize_v3_v3(dir, edge); + + if (length > ALMOST_ZERO) { + outerproduct(grad_dir, dir, dir); + sub_m3_m3m3(grad_dir, I, grad_dir); + mul_m3_fl(grad_dir, 1.0f / length); + } + else { + zero_m3(grad_dir); + } +} + +BLI_INLINE void spring_angbend_forces(Implicit_Data *data, int i, int j, int k, + const float goal[3], + float stiffness, float damping, + int q, const float dx[3], const float dv[3], + float r_f[3]) +{ + float edge_ij[3], dir_ij[3]; + float edge_jk[3], dir_jk[3]; + float vel_ij[3], vel_jk[3], vel_ortho[3]; + float f_bend[3], f_damp[3]; + float fk[3]; + float dist[3]; + + zero_v3(fk); + + sub_v3_v3v3(edge_ij, data->X[j], data->X[i]); + if (q == i) sub_v3_v3(edge_ij, dx); + if (q == j) add_v3_v3(edge_ij, dx); + normalize_v3_v3(dir_ij, edge_ij); + + sub_v3_v3v3(edge_jk, data->X[k], data->X[j]); + if (q == j) sub_v3_v3(edge_jk, dx); + if (q == k) add_v3_v3(edge_jk, dx); + normalize_v3_v3(dir_jk, edge_jk); + + sub_v3_v3v3(vel_ij, data->V[j], data->V[i]); + if (q == i) sub_v3_v3(vel_ij, dv); + if (q == j) add_v3_v3(vel_ij, dv); + + sub_v3_v3v3(vel_jk, data->V[k], data->V[j]); + if (q == j) sub_v3_v3(vel_jk, dv); + if (q == k) add_v3_v3(vel_jk, dv); + + /* bending force */ + sub_v3_v3v3(dist, goal, edge_jk); + mul_v3_v3fl(f_bend, dist, stiffness); + + add_v3_v3(fk, f_bend); + + /* damping force */ + madd_v3_v3v3fl(vel_ortho, vel_jk, dir_jk, -dot_v3v3(vel_jk, dir_jk)); + mul_v3_v3fl(f_damp, vel_ortho, damping); + + sub_v3_v3(fk, f_damp); + + copy_v3_v3(r_f, fk); +} + +/* Finite Differences method for estimating the jacobian of the force */ +BLI_INLINE void spring_angbend_estimate_dfdx(Implicit_Data *data, int i, int j, int k, + const float goal[3], + float stiffness, float damping, + int q, float dfdx[3][3]) +{ + const float delta = 0.00001f; // TODO find a good heuristic for this + float dvec_null[3][3], dvec_pos[3][3], dvec_neg[3][3]; + float f[3]; + int a, b; + + zero_m3(dvec_null); + unit_m3(dvec_pos); + mul_m3_fl(dvec_pos, delta * 0.5f); + copy_m3_m3(dvec_neg, dvec_pos); + negate_m3(dvec_neg); + + /* XXX TODO offset targets to account for position dependency */ + + for (a = 0; a < 3; ++a) { + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, + q, dvec_pos[a], dvec_null[a], f); + copy_v3_v3(dfdx[a], f); + + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, + q, dvec_neg[a], dvec_null[a], f); + sub_v3_v3(dfdx[a], f); + + for (b = 0; b < 3; ++b) { + dfdx[a][b] /= delta; + } + } +} + +/* Finite Differences method for estimating the jacobian of the force */ +BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j, int k, + const float goal[3], + float stiffness, float damping, + int q, float dfdv[3][3]) +{ + const float delta = 0.00001f; // TODO find a good heuristic for this + float dvec_null[3][3], dvec_pos[3][3], dvec_neg[3][3]; + float f[3]; + int a, b; + + zero_m3(dvec_null); + unit_m3(dvec_pos); + mul_m3_fl(dvec_pos, delta * 0.5f); + copy_m3_m3(dvec_neg, dvec_pos); + negate_m3(dvec_neg); + + /* XXX TODO offset targets to account for position dependency */ + + for (a = 0; a < 3; ++a) { + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, + q, dvec_null[a], dvec_pos[a], f); + copy_v3_v3(dfdv[a], f); + + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, + q, dvec_null[a], dvec_neg[a], f); + sub_v3_v3(dfdv[a], f); + + for (b = 0; b < 3; ++b) { + dfdv[a][b] /= delta; + } + } +} + +/* Angular spring that pulls the vertex toward the local target + * See "Artistic Simulation of Curly Hair" (Pixar technical memo #12-03a) + */ +bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, int j, int k, + const float target[3], float stiffness, float damping) +{ + float goal[3]; + float fj[3], fk[3]; + float dfj_dxi[3][3], dfj_dxj[3][3], dfk_dxi[3][3], dfk_dxj[3][3], dfk_dxk[3][3]; + float dfj_dvi[3][3], dfj_dvj[3][3], dfk_dvi[3][3], dfk_dvj[3][3], dfk_dvk[3][3]; + + const float vecnull[3] = {0.0f, 0.0f, 0.0f}; + + int block_ij = BPH_mass_spring_add_block(data, i, j); + int block_jk = BPH_mass_spring_add_block(data, j, k); + int block_ik = BPH_mass_spring_add_block(data, i, k); + + world_to_root_v3(data, j, goal, target); + + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, k, vecnull, vecnull, fk); + negate_v3_v3(fj, fk); /* counterforce */ + + spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, i, dfk_dxi); + spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, j, dfk_dxj); + spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, k, dfk_dxk); + copy_m3_m3(dfj_dxi, dfk_dxi); negate_m3(dfj_dxi); + copy_m3_m3(dfj_dxj, dfk_dxj); negate_m3(dfj_dxj); + + spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, i, dfk_dvi); + spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, j, dfk_dvj); + spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, k, dfk_dvk); + copy_m3_m3(dfj_dvi, dfk_dvi); negate_m3(dfj_dvi); + copy_m3_m3(dfj_dvj, dfk_dvj); negate_m3(dfj_dvj); + + /* add forces and jacobians to the solver data */ + + add_v3_v3(data->F[j], fj); + add_v3_v3(data->F[k], fk); + + add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfj_dxj); + add_m3_m3m3(data->dFdX[k].m, data->dFdX[k].m, dfk_dxk); + + add_m3_m3m3(data->dFdX[block_ij].m, data->dFdX[block_ij].m, dfj_dxi); + add_m3_m3m3(data->dFdX[block_jk].m, data->dFdX[block_jk].m, dfk_dxj); + add_m3_m3m3(data->dFdX[block_ik].m, data->dFdX[block_ik].m, dfk_dxi); + + add_m3_m3m3(data->dFdV[j].m, data->dFdV[j].m, dfj_dvj); + add_m3_m3m3(data->dFdV[k].m, data->dFdV[k].m, dfk_dvk); + + add_m3_m3m3(data->dFdV[block_ij].m, data->dFdV[block_ij].m, dfj_dvi); + add_m3_m3m3(data->dFdV[block_jk].m, data->dFdV[block_jk].m, dfk_dvj); + add_m3_m3m3(data->dFdV[block_ik].m, data->dFdV[block_ik].m, dfk_dvi); + + + /* XXX analytical calculation of derivatives below is incorrect. + * This proved to be difficult, but for now just using the finite difference method for + * estimating the jacobians should be sufficient. + */ +#if 0 + float edge_ij[3], dir_ij[3], grad_dir_ij[3][3]; + float edge_jk[3], dir_jk[3], grad_dir_jk[3][3]; + float dist[3], vel_jk[3], vel_jk_ortho[3], projvel[3]; + float target[3]; + float tmp[3][3]; + float fi[3], fj[3], fk[3]; + float dfi_dxi[3][3], dfj_dxi[3][3], dfj_dxj[3][3], dfk_dxi[3][3], dfk_dxj[3][3], dfk_dxk[3][3]; + float dfdvi[3][3]; + + // TESTING + damping = 0.0f; + + zero_v3(fi); + zero_v3(fj); + zero_v3(fk); + zero_m3(dfi_dxi); + zero_m3(dfj_dxi); + zero_m3(dfk_dxi); + zero_m3(dfk_dxj); + zero_m3(dfk_dxk); + + /* jacobian of direction vectors */ + spring_grad_dir(data, i, j, edge_ij, dir_ij, grad_dir_ij); + spring_grad_dir(data, j, k, edge_jk, dir_jk, grad_dir_jk); + + sub_v3_v3v3(vel_jk, data->V[k], data->V[j]); + + /* bending force */ + mul_v3_v3fl(target, dir_ij, restlen); + sub_v3_v3v3(dist, target, edge_jk); + mul_v3_v3fl(fk, dist, stiffness); + + /* damping force */ + madd_v3_v3v3fl(vel_jk_ortho, vel_jk, dir_jk, -dot_v3v3(vel_jk, dir_jk)); + madd_v3_v3fl(fk, vel_jk_ortho, damping); + + /* XXX this only holds true as long as we assume straight rest shape! + * eventually will become a bit more involved since the opposite segment + * gets its own target, under condition of having equal torque on both sides. + */ + copy_v3_v3(fi, fk); + + /* counterforce on the middle point */ + sub_v3_v3(fj, fi); + sub_v3_v3(fj, fk); + + /* === derivatives === */ + + madd_m3_m3fl(dfk_dxi, grad_dir_ij, stiffness * restlen); + + madd_m3_m3fl(dfk_dxj, grad_dir_ij, -stiffness * restlen); + madd_m3_m3fl(dfk_dxj, I, stiffness); + + madd_m3_m3fl(dfk_dxk, I, -stiffness); + + copy_m3_m3(dfi_dxi, dfk_dxk); + negate_m3(dfi_dxi); + + /* dfj_dfi == dfi_dfj due to symmetry, + * dfi_dfj == dfk_dfj due to fi == fk + * XXX see comment above on future bent rest shapes + */ + copy_m3_m3(dfj_dxi, dfk_dxj); + + /* dfj_dxj == -(dfi_dxj + dfk_dxj) due to fj == -(fi + fk) */ + sub_m3_m3m3(dfj_dxj, dfj_dxj, dfj_dxi); + sub_m3_m3m3(dfj_dxj, dfj_dxj, dfk_dxj); + + /* add forces and jacobians to the solver data */ + add_v3_v3(data->F[i], fi); + add_v3_v3(data->F[j], fj); + add_v3_v3(data->F[k], fk); + + add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfi_dxi); + add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfj_dxj); + add_m3_m3m3(data->dFdX[k].m, data->dFdX[k].m, dfk_dxk); + + add_m3_m3m3(data->dFdX[block_ij].m, data->dFdX[block_ij].m, dfj_dxi); + add_m3_m3m3(data->dFdX[block_jk].m, data->dFdX[block_jk].m, dfk_dxj); + add_m3_m3m3(data->dFdX[block_ik].m, data->dFdX[block_ik].m, dfk_dxi); +#endif + + return true; +} + +bool BPH_mass_spring_force_spring_goal(Implicit_Data *data, int i, const float goal_x[3], const float goal_v[3], + float stiffness, float damping, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) +{ + float root_goal_x[3], root_goal_v[3], extent[3], length, dir[3], vel[3]; + float f[3], dfdx[3][3], dfdv[3][3]; + + /* goal is in world space */ + world_to_root_v3(data, i, root_goal_x, goal_x); + world_to_root_v3(data, i, root_goal_v, goal_v); + + sub_v3_v3v3(extent, root_goal_x, data->X[i]); + sub_v3_v3v3(vel, root_goal_v, data->V[i]); + length = normalize_v3_v3(dir, extent); + + if (length > ALMOST_ZERO) { + mul_v3_v3fl(f, dir, stiffness * length); + + // Ascher & Boxman, p.21: Damping only during elonglation + // something wrong with it... + madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir)); + + dfdx_spring(dfdx, dir, length, 0.0f, stiffness); + dfdv_damp(dfdv, dir, damping); + + add_v3_v3(data->F[i], f); + add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfdx); + add_m3_m3m3(data->dFdV[i].m, data->dFdV[i].m, dfdv); + + if (r_f) copy_v3_v3(r_f, f); + if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); + if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); + + return true; + } + else { + if (r_f) zero_v3(r_f); + if (r_dfdx) zero_m3(r_dfdx); + if (r_dfdv) zero_m3(r_dfdv); + + return false; + } +} + +#endif /* IMPLICIT_SOLVER_BLENDER */ diff --git a/source/blender/physics/intern/implicit_eigen.cpp b/source/blender/physics/intern/implicit_eigen.cpp new file mode 100644 index 00000000000..0ea8ccb0a49 --- /dev/null +++ b/source/blender/physics/intern/implicit_eigen.cpp @@ -0,0 +1,1369 @@ +/* + * ***** 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) Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/physics/intern/implicit_eigen.cpp + * \ingroup bph + */ + +#include "implicit.h" + +#ifdef IMPLICIT_SOLVER_EIGEN + +//#define USE_EIGEN_CORE +#define USE_EIGEN_CONSTRAINED_CG + +#ifdef __GNUC__ +# pragma GCC diagnostic push +/* XXX suppress verbose warnings in eigen */ +# pragma GCC diagnostic ignored "-Wlogical-op" +#endif + +#ifndef IMPLICIT_ENABLE_EIGEN_DEBUG +#ifdef NDEBUG +#define IMPLICIT_NDEBUG +#endif +#define NDEBUG +#endif + +#include <Eigen/Sparse> +#include <Eigen/src/Core/util/DisableStupidWarnings.h> + +#ifdef USE_EIGEN_CONSTRAINED_CG +#include <intern/ConstrainedConjugateGradient.h> +#endif + +#ifndef IMPLICIT_ENABLE_EIGEN_DEBUG +#ifndef IMPLICIT_NDEBUG +#undef NDEBUG +#else +#undef IMPLICIT_NDEBUG +#endif +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force.h" +#include "DNA_meshdata_types.h" +#include "DNA_texture_types.h" + +#include "BLI_math.h" +#include "BLI_linklist.h" +#include "BLI_utildefines.h" + +#include "BKE_cloth.h" +#include "BKE_collision.h" +#include "BKE_effect.h" +#include "BKE_global.h" + +#include "BPH_mass_spring.h" +} + +typedef float Scalar; + +static float I[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; +static float ZERO[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + +/* slightly extended Eigen vector class + * with conversion to/from plain C float array + */ +class fVector : public Eigen::Vector3f { +public: + typedef float *ctype; + + fVector() + { + } + + fVector(const ctype &v) + { + for (int k = 0; k < 3; ++k) + coeffRef(k) = v[k]; + } + + fVector& operator = (const ctype &v) + { + for (int k = 0; k < 3; ++k) + coeffRef(k) = v[k]; + return *this; + } + + operator ctype() + { + return data(); + } +}; + +/* slightly extended Eigen matrix class + * with conversion to/from plain C float array + */ +class fMatrix : public Eigen::Matrix3f { +public: + typedef float (*ctype)[3]; + + fMatrix() + { + } + + fMatrix(const ctype &v) + { + for (int k = 0; k < 3; ++k) + for (int l = 0; l < 3; ++l) + coeffRef(l, k) = v[k][l]; + } + + fMatrix& operator = (const ctype &v) + { + for (int k = 0; k < 3; ++k) + for (int l = 0; l < 3; ++l) + coeffRef(l, k) = v[k][l]; + return *this; + } + + operator ctype() + { + return (ctype)data(); + } +}; + +/* Extension of dense Eigen vectors, + * providing 3-float block access for blenlib math functions + */ +class lVector : public Eigen::VectorXf { +public: + typedef Eigen::VectorXf base_t; + + lVector() + { + } + + template <typename T> + lVector& operator = (T rhs) + { + base_t::operator=(rhs); + return *this; + } + + float* v3(int vertex) + { + return &coeffRef(3 * vertex); + } + + const float* v3(int vertex) const + { + return &coeffRef(3 * vertex); + } +}; + +typedef Eigen::Triplet<Scalar> Triplet; +typedef std::vector<Triplet> TripletList; + +typedef Eigen::SparseMatrix<Scalar> lMatrix; + +/* Constructor type that provides more convenient handling of Eigen triplets + * for efficient construction of sparse 3x3 block matrices. + * This should be used for building lMatrix instead of writing to such lMatrix directly (which is very inefficient). + * After all elements have been defined using the set() method, the actual matrix can be filled using construct(). + */ +struct lMatrixCtor { + lMatrixCtor() + { + } + + void reset() + { + m_trips.clear(); + } + + void reserve(int numverts) + { + /* reserve for diagonal entries */ + m_trips.reserve(numverts * 9); + } + + void add(int i, int j, const fMatrix &m) + { + i *= 3; + j *= 3; + for (int k = 0; k < 3; ++k) + for (int l = 0; l < 3; ++l) + m_trips.push_back(Triplet(i + k, j + l, m.coeff(l, k))); + } + + void sub(int i, int j, const fMatrix &m) + { + i *= 3; + j *= 3; + for (int k = 0; k < 3; ++k) + for (int l = 0; l < 3; ++l) + m_trips.push_back(Triplet(i + k, j + l, -m.coeff(l, k))); + } + + inline void construct(lMatrix &m) + { + m.setFromTriplets(m_trips.begin(), m_trips.end()); + m_trips.clear(); + } + +private: + TripletList m_trips; +}; + +#ifdef USE_EIGEN_CORE +typedef Eigen::ConjugateGradient<lMatrix, Eigen::Lower, Eigen::DiagonalPreconditioner<Scalar> > ConjugateGradient; +#endif +#ifdef USE_EIGEN_CONSTRAINED_CG +typedef Eigen::ConstrainedConjugateGradient<lMatrix, Eigen::Lower, lMatrix, + Eigen::DiagonalPreconditioner<Scalar> > + ConstraintConjGrad; +#endif +using Eigen::ComputationInfo; + +static void print_lvector(const lVector &v) +{ + for (int i = 0; i < v.rows(); ++i) { + if (i > 0 && i % 3 == 0) + printf("\n"); + + printf("%f,\n", v[i]); + } +} + +static void print_lmatrix(const lMatrix &m) +{ + for (int j = 0; j < m.rows(); ++j) { + if (j > 0 && j % 3 == 0) + printf("\n"); + + for (int i = 0; i < m.cols(); ++i) { + if (i > 0 && i % 3 == 0) + printf(" "); + + implicit_print_matrix_elem(m.coeff(j, i)); + } + printf("\n"); + } +} + +BLI_INLINE void lMatrix_reserve_elems(lMatrix &m, int num) +{ + m.reserve(Eigen::VectorXi::Constant(m.cols(), num)); +} + +BLI_INLINE float *lVector_v3(lVector &v, int vertex) +{ + return v.data() + 3 * vertex; +} + +BLI_INLINE const float *lVector_v3(const lVector &v, int vertex) +{ + return v.data() + 3 * vertex; +} + +#if 0 +BLI_INLINE void triplets_m3(TripletList &tlist, float m[3][3], int i, int j) +{ + i *= 3; + j *= 3; + for (int l = 0; l < 3; ++l) { + for (int k = 0; k < 3; ++k) { + tlist.push_back(Triplet(i + k, j + l, m[k][l])); + } + } +} + +BLI_INLINE void triplets_m3fl(TripletList &tlist, float m[3][3], int i, int j, float factor) +{ + i *= 3; + j *= 3; + for (int l = 0; l < 3; ++l) { + for (int k = 0; k < 3; ++k) { + tlist.push_back(Triplet(i + k, j + l, m[k][l] * factor)); + } + } +} + +BLI_INLINE void lMatrix_add_triplets(lMatrix &r, const TripletList &tlist) +{ + lMatrix t(r.rows(), r.cols()); + t.setFromTriplets(tlist.begin(), tlist.end()); + r += t; +} + +BLI_INLINE void lMatrix_madd_triplets(lMatrix &r, const TripletList &tlist, float f) +{ + lMatrix t(r.rows(), r.cols()); + t.setFromTriplets(tlist.begin(), tlist.end()); + r += f * t; +} + +BLI_INLINE void lMatrix_sub_triplets(lMatrix &r, const TripletList &tlist) +{ + lMatrix t(r.rows(), r.cols()); + t.setFromTriplets(tlist.begin(), tlist.end()); + r -= t; +} +#endif + +BLI_INLINE void outerproduct(float r[3][3], const float a[3], const float b[3]) +{ + mul_v3_v3fl(r[0], a, b[0]); + mul_v3_v3fl(r[1], a, b[1]); + mul_v3_v3fl(r[2], a, b[2]); +} + +BLI_INLINE void cross_m3_v3m3(float r[3][3], const float v[3], float m[3][3]) +{ + cross_v3_v3v3(r[0], v, m[0]); + cross_v3_v3v3(r[1], v, m[1]); + cross_v3_v3v3(r[2], v, m[2]); +} + +BLI_INLINE void cross_v3_identity(float r[3][3], const float v[3]) +{ + r[0][0] = 0.0f; r[1][0] = v[2]; r[2][0] = -v[1]; + r[0][1] = -v[2]; r[1][1] = 0.0f; r[2][1] = v[0]; + r[0][2] = v[1]; r[1][2] = -v[0]; r[2][2] = 0.0f; +} + +BLI_INLINE void madd_m3_m3fl(float r[3][3], float m[3][3], float f) +{ + r[0][0] += m[0][0] * f; + r[0][1] += m[0][1] * f; + r[0][2] += m[0][2] * f; + r[1][0] += m[1][0] * f; + r[1][1] += m[1][1] * f; + r[1][2] += m[1][2] * f; + r[2][0] += m[2][0] * f; + r[2][1] += m[2][1] * f; + r[2][2] += m[2][2] * f; +} + +BLI_INLINE void madd_m3_m3m3fl(float r[3][3], float a[3][3], float b[3][3], float f) +{ + r[0][0] = a[0][0] + b[0][0] * f; + r[0][1] = a[0][1] + b[0][1] * f; + r[0][2] = a[0][2] + b[0][2] * f; + r[1][0] = a[1][0] + b[1][0] * f; + r[1][1] = a[1][1] + b[1][1] * f; + r[1][2] = a[1][2] + b[1][2] * f; + r[2][0] = a[2][0] + b[2][0] * f; + r[2][1] = a[2][1] + b[2][1] * f; + r[2][2] = a[2][2] + b[2][2] * f; +} + +struct Implicit_Data { + typedef std::vector<fMatrix> fMatrixVector; + + Implicit_Data(int numverts) + { + resize(numverts); + } + + void resize(int numverts) + { + this->numverts = numverts; + int tot = 3 * numverts; + + M.resize(tot, tot); + F.resize(tot); + dFdX.resize(tot, tot); + dFdV.resize(tot, tot); + + tfm.resize(numverts, I); + + X.resize(tot); + Xnew.resize(tot); + V.resize(tot); + Vnew.resize(tot); + + A.resize(tot, tot); + B.resize(tot); + + dV.resize(tot); + z.resize(tot); + S.resize(tot, tot); + + iM.reserve(numverts); + idFdX.reserve(numverts); + idFdV.reserve(numverts); + iS.reserve(numverts); + } + + int numverts; + + /* inputs */ + lMatrix M; /* masses */ + lVector F; /* forces */ + lMatrix dFdX, dFdV; /* force jacobians */ + + fMatrixVector tfm; /* local coordinate transform */ + + /* motion state data */ + lVector X, Xnew; /* positions */ + lVector V, Vnew; /* velocities */ + + /* internal solver data */ + lVector B; /* B for A*dV = B */ + lMatrix A; /* A for A*dV = B */ + + lVector dV; /* velocity change (solution of A*dV = B) */ + lVector z; /* target velocity in constrained directions */ + lMatrix S; /* filtering matrix for constraints */ + + /* temporary constructors */ + lMatrixCtor iM; /* masses */ + lMatrixCtor idFdX, idFdV; /* force jacobians */ + lMatrixCtor iS; /* filtering matrix for constraints */ +}; + +Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings) +{ + Implicit_Data *id = new Implicit_Data(numverts); + return id; +} + +void BPH_mass_spring_solver_free(Implicit_Data *id) +{ + if (id) + delete id; +} + +int BPH_mass_spring_solver_numvert(Implicit_Data *id) +{ + if (id) + return id->numverts; + else + return 0; +} + +/* ==== Transformation from/to root reference frames ==== */ + +BLI_INLINE void world_to_root_v3(Implicit_Data *data, int index, float r[3], const float v[3]) +{ + copy_v3_v3(r, v); + mul_transposed_m3_v3(data->tfm[index], r); +} + +BLI_INLINE void root_to_world_v3(Implicit_Data *data, int index, float r[3], const float v[3]) +{ + mul_v3_m3v3(r, data->tfm[index], v); +} + +BLI_INLINE void world_to_root_m3(Implicit_Data *data, int index, float r[3][3], float m[3][3]) +{ + float trot[3][3]; + copy_m3_m3(trot, data->tfm[index]); + transpose_m3(trot); + mul_m3_m3m3(r, trot, m); +} + +BLI_INLINE void root_to_world_m3(Implicit_Data *data, int index, float r[3][3], float m[3][3]) +{ + mul_m3_m3m3(r, data->tfm[index], m); +} + +/* ================================ */ + +bool BPH_mass_spring_solve_velocities(Implicit_Data *data, float dt, ImplicitSolverResult *result) +{ +#ifdef USE_EIGEN_CORE + typedef ConjugateGradient solver_t; +#endif +#ifdef USE_EIGEN_CONSTRAINED_CG + typedef ConstraintConjGrad solver_t; +#endif + + data->iM.construct(data->M); + data->idFdX.construct(data->dFdX); + data->idFdV.construct(data->dFdV); + data->iS.construct(data->S); + + solver_t cg; + cg.setMaxIterations(100); + cg.setTolerance(0.01f); + +#ifdef USE_EIGEN_CONSTRAINED_CG + cg.filter() = data->S; +#endif + + data->A = data->M - dt * data->dFdV - dt*dt * data->dFdX; + cg.compute(data->A); + + data->B = dt * data->F + dt*dt * data->dFdX * data->V; + +#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT + printf("==== A ====\n"); + print_lmatrix(id->A); + printf("==== z ====\n"); + print_lvector(id->z); + printf("==== B ====\n"); + print_lvector(id->B); + printf("==== S ====\n"); + print_lmatrix(id->S); +#endif + +#ifdef USE_EIGEN_CORE + data->dV = cg.solve(data->B); +#endif +#ifdef USE_EIGEN_CONSTRAINED_CG + data->dV = cg.solveWithGuess(data->B, data->z); +#endif + +#ifdef IMPLICIT_PRINT_SOLVER_INPUT_OUTPUT + printf("==== dV ====\n"); + print_lvector(id->dV); + printf("========\n"); +#endif + + data->Vnew = data->V + data->dV; + + switch (cg.info()) { + case Eigen::Success: result->status = BPH_SOLVER_SUCCESS; break; + case Eigen::NoConvergence: result->status = BPH_SOLVER_NO_CONVERGENCE; break; + case Eigen::InvalidInput: result->status = BPH_SOLVER_INVALID_INPUT; break; + case Eigen::NumericalIssue: result->status = BPH_SOLVER_NUMERICAL_ISSUE; break; + } + + result->iterations = cg.iterations(); + result->error = cg.error(); + + return cg.info() == Eigen::Success; +} + +bool BPH_mass_spring_solve_positions(Implicit_Data *data, float dt) +{ + data->Xnew = data->X + data->Vnew * dt; + return true; +} + +/* ================================ */ + +void BPH_mass_spring_apply_result(Implicit_Data *data) +{ + data->X = data->Xnew; + data->V = data->Vnew; +} + +void BPH_mass_spring_set_vertex_mass(Implicit_Data *data, int index, float mass) +{ + float m[3][3]; + copy_m3_m3(m, I); + mul_m3_fl(m, mass); + data->iM.add(index, index, m); +} + +void BPH_mass_spring_set_rest_transform(Implicit_Data *data, int index, float tfm[3][3]) +{ +#ifdef CLOTH_ROOT_FRAME + copy_m3_m3(data->tfm[index], tfm); +#else + unit_m3(data->tfm[index]); + (void)tfm; +#endif +} + +void BPH_mass_spring_set_motion_state(Implicit_Data *data, int index, const float x[3], const float v[3]) +{ + world_to_root_v3(data, index, data->X.v3(index), x); + world_to_root_v3(data, index, data->V.v3(index), v); +} + +void BPH_mass_spring_set_position(Implicit_Data *data, int index, const float x[3]) +{ + world_to_root_v3(data, index, data->X.v3(index), x); +} + +void BPH_mass_spring_set_velocity(Implicit_Data *data, int index, const float v[3]) +{ + world_to_root_v3(data, index, data->V.v3(index), v); +} + +void BPH_mass_spring_get_motion_state(struct Implicit_Data *data, int index, float x[3], float v[3]) +{ + if (x) root_to_world_v3(data, index, x, data->X.v3(index)); + if (v) root_to_world_v3(data, index, v, data->V.v3(index)); +} + +void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3]) +{ + root_to_world_v3(data, index, x, data->X.v3(index)); +} + +void BPH_mass_spring_get_new_velocity(Implicit_Data *data, int index, float v[3]) +{ + root_to_world_v3(data, index, v, data->V.v3(index)); +} + +void BPH_mass_spring_set_new_velocity(Implicit_Data *data, int index, const float v[3]) +{ + world_to_root_v3(data, index, data->V.v3(index), v); +} + +void BPH_mass_spring_clear_constraints(Implicit_Data *data) +{ + int numverts = data->numverts; + for (int i = 0; i < numverts; ++i) { + data->iS.add(i, i, I); + zero_v3(data->z.v3(i)); + } +} + +void BPH_mass_spring_add_constraint_ndof0(Implicit_Data *data, int index, const float dV[3]) +{ + data->iS.sub(index, index, I); + + world_to_root_v3(data, index, data->z.v3(index), dV); +} + +void BPH_mass_spring_add_constraint_ndof1(Implicit_Data *data, int index, const float c1[3], const float c2[3], const float dV[3]) +{ + float m[3][3], p[3], q[3], u[3], cmat[3][3]; + + world_to_root_v3(data, index, p, c1); + outerproduct(cmat, p, p); + copy_m3_m3(m, cmat); + + world_to_root_v3(data, index, q, c2); + outerproduct(cmat, q, q); + add_m3_m3m3(m, m, cmat); + + /* XXX not sure but multiplication should work here */ + data->iS.sub(index, index, m); +// mul_m3_m3m3(data->S[index].m, data->S[index].m, m); + + world_to_root_v3(data, index, u, dV); + add_v3_v3(data->z.v3(index), u); +} + +void BPH_mass_spring_add_constraint_ndof2(Implicit_Data *data, int index, const float c1[3], const float dV[3]) +{ + float m[3][3], p[3], u[3], cmat[3][3]; + + world_to_root_v3(data, index, p, c1); + outerproduct(cmat, p, p); + copy_m3_m3(m, cmat); + + data->iS.sub(index, index, m); +// mul_m3_m3m3(data->S[index].m, data->S[index].m, m); + + world_to_root_v3(data, index, u, dV); + add_v3_v3(data->z.v3(index), u); +} + +void BPH_mass_spring_clear_forces(Implicit_Data *data) +{ + data->F.setZero(); + data->dFdX.setZero(); + data->dFdV.setZero(); +} + +void BPH_mass_spring_force_reference_frame(Implicit_Data *data, int index, const float acceleration[3], const float omega[3], const float domega_dt[3], float mass) +{ +#ifdef CLOTH_ROOT_FRAME + float acc[3], w[3], dwdt[3]; + float f[3], dfdx[3][3], dfdv[3][3]; + float euler[3], coriolis[3], centrifugal[3], rotvel[3]; + float deuler[3][3], dcoriolis[3][3], dcentrifugal[3][3], drotvel[3][3]; + + world_to_root_v3(data, index, acc, acceleration); + world_to_root_v3(data, index, w, omega); + world_to_root_v3(data, index, dwdt, domega_dt); + + cross_v3_v3v3(euler, dwdt, data->X.v3(index)); + cross_v3_v3v3(coriolis, w, data->V.v3(index)); + mul_v3_fl(coriolis, 2.0f); + cross_v3_v3v3(rotvel, w, data->X.v3(index)); + cross_v3_v3v3(centrifugal, w, rotvel); + + sub_v3_v3v3(f, acc, euler); + sub_v3_v3(f, coriolis); + sub_v3_v3(f, centrifugal); + + mul_v3_fl(f, mass); /* F = m * a */ + + cross_v3_identity(deuler, dwdt); + cross_v3_identity(dcoriolis, w); + mul_m3_fl(dcoriolis, 2.0f); + cross_v3_identity(drotvel, w); + cross_m3_v3m3(dcentrifugal, w, drotvel); + + add_m3_m3m3(dfdx, deuler, dcentrifugal); + negate_m3(dfdx); + mul_m3_fl(dfdx, mass); + + copy_m3_m3(dfdv, dcoriolis); + negate_m3(dfdv); + mul_m3_fl(dfdv, mass); + + add_v3_v3(data->F.v3(index), f); + data->idFdX.add(index, index, dfdx); + data->idFdV.add(index, index, dfdv); +#else + (void)data; + (void)index; + (void)acceleration; + (void)omega; + (void)domega_dt; +#endif +} + +void BPH_mass_spring_force_gravity(Implicit_Data *data, int index, float mass, const float g[3]) +{ + /* force = mass * acceleration (in this case: gravity) */ + float f[3]; + world_to_root_v3(data, index, f, g); + mul_v3_fl(f, mass); + + add_v3_v3(data->F.v3(index), f); +} + +void BPH_mass_spring_force_drag(Implicit_Data *data, float drag) +{ + int numverts = data->numverts; + for (int i = 0; i < numverts; i++) { + float tmp[3][3]; + + /* NB: uses root space velocity, no need to transform */ + madd_v3_v3fl(data->F.v3(i), data->V.v3(i), -drag); + + copy_m3_m3(tmp, I); + mul_m3_fl(tmp, -drag); + data->idFdV.add(i, i, tmp); + } +} + +void BPH_mass_spring_force_extern(struct Implicit_Data *data, int i, const float f[3], float dfdx[3][3], float dfdv[3][3]) +{ + float tf[3], tdfdx[3][3], tdfdv[3][3]; + world_to_root_v3(data, i, tf, f); + world_to_root_m3(data, i, tdfdx, dfdx); + world_to_root_m3(data, i, tdfdv, dfdv); + + add_v3_v3(data->F.v3(i), tf); + data->idFdX.add(i, i, tdfdx); + data->idFdV.add(i, i, tdfdv); +} + +static float calc_nor_area_tri(float nor[3], const float v1[3], const float v2[3], const float v3[3]) +{ + float n1[3], n2[3]; + + sub_v3_v3v3(n1, v1, v2); + sub_v3_v3v3(n2, v2, v3); + + cross_v3_v3v3(nor, n1, n2); + return normalize_v3(nor); +} + +static float calc_nor_area_quad(float nor[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3]) +{ + float n1[3], n2[3]; + + sub_v3_v3v3(n1, v1, v3); + sub_v3_v3v3(n2, v2, v4); + + cross_v3_v3v3(nor, n1, n2); + return normalize_v3(nor); +} + +/* XXX does not support force jacobians yet, since the effector system does not provide them either */ +void BPH_mass_spring_force_face_wind(Implicit_Data *data, int v1, int v2, int v3, int v4, const float (*winvec)[3]) +{ + const float effector_scale = 0.02f; + float win[3], nor[3], area; + float factor; + + // calculate face normal and area + if (v4) { + area = calc_nor_area_quad(nor, data->X.v3(v1), data->X.v3(v2), data->X.v3(v3), data->X.v3(v4)); + factor = effector_scale * area * 0.25f; + } + else { + area = calc_nor_area_tri(nor, data->X.v3(v1), data->X.v3(v2), data->X.v3(v3)); + factor = effector_scale * area / 3.0f; + } + + world_to_root_v3(data, v1, win, winvec[v1]); + madd_v3_v3fl(data->F.v3(v1), nor, factor * dot_v3v3(win, nor)); + + world_to_root_v3(data, v2, win, winvec[v2]); + madd_v3_v3fl(data->F.v3(v2), nor, factor * dot_v3v3(win, nor)); + + world_to_root_v3(data, v3, win, winvec[v3]); + madd_v3_v3fl(data->F.v3(v3), nor, factor * dot_v3v3(win, nor)); + + if (v4) { + world_to_root_v3(data, v4, win, winvec[v4]); + madd_v3_v3fl(data->F.v3(v4), nor, factor * dot_v3v3(win, nor)); + } +} + +void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, const float (*winvec)[3]) +{ + const float effector_scale = 0.01; + float win[3], dir[3], nor[3], length; + + sub_v3_v3v3(dir, data->X.v3(v1), data->X.v3(v2)); + length = normalize_v3(dir); + + world_to_root_v3(data, v1, win, winvec[v1]); + madd_v3_v3v3fl(nor, win, dir, -dot_v3v3(win, dir)); + madd_v3_v3fl(data->F.v3(v1), nor, effector_scale * length); + + world_to_root_v3(data, v2, win, winvec[v2]); + madd_v3_v3v3fl(nor, win, dir, -dot_v3v3(win, dir)); + madd_v3_v3fl(data->F.v3(v2), nor, effector_scale * length); +} + +BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, float L, float k) +{ + // dir is unit length direction, rest is spring's restlength, k is spring constant. + //return ( (I-outerprod(dir, dir))*Min(1.0f, rest/length) - I) * -k; + outerproduct(to, dir, dir); + sub_m3_m3m3(to, I, to); + + mul_m3_fl(to, (L/length)); + sub_m3_m3m3(to, to, I); + mul_m3_fl(to, k); +} + +/* unused */ +#if 0 +BLI_INLINE void dfdx_damp(float to[3][3], const float dir[3], float length, const float vel[3], float rest, float damping) +{ + // inner spring damping vel is the relative velocity of the endpoints. + // return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest))); + mul_fvectorT_fvector(to, dir, dir); + sub_fmatrix_fmatrix(to, I, to); + mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel)/MAX2(length, rest)))); +} +#endif + +BLI_INLINE void dfdv_damp(float to[3][3], const float dir[3], float damping) +{ + // derivative of force wrt velocity + outerproduct(to, dir, dir); + mul_m3_fl(to, -damping); +} + +BLI_INLINE float fb(float length, float L) +{ + float x = length / L; + return (-11.541f * powf(x, 4) + 34.193f * powf(x, 3) - 39.083f * powf(x, 2) + 23.116f * x - 9.713f); +} + +BLI_INLINE float fbderiv(float length, float L) +{ + float x = length/L; + + return (-46.164f * powf(x, 3) + 102.579f * powf(x, 2) - 78.166f * x + 23.116f); +} + +BLI_INLINE float fbstar(float length, float L, float kb, float cb) +{ + float tempfb_fl = kb * fb(length, L); + float fbstar_fl = cb * (length - L); + + if (tempfb_fl < fbstar_fl) + return fbstar_fl; + else + return tempfb_fl; +} + +// function to calculae bending spring force (taken from Choi & Co) +BLI_INLINE float fbstar_jacobi(float length, float L, float kb, float cb) +{ + float tempfb_fl = kb * fb(length, L); + float fbstar_fl = cb * (length - L); + + if (tempfb_fl < fbstar_fl) { + return -cb; + } + else { + return -kb * fbderiv(length, L); + } +} + +/* calculate elonglation */ +BLI_INLINE bool spring_length(Implicit_Data *data, int i, int j, float r_extent[3], float r_dir[3], float *r_length, float r_vel[3]) +{ + sub_v3_v3v3(r_extent, data->X.v3(j), data->X.v3(i)); + sub_v3_v3v3(r_vel, data->V.v3(j), data->V.v3(i)); + *r_length = len_v3(r_extent); + + if (*r_length > ALMOST_ZERO) { + /* + if (length>L) { + if ((clmd->sim_parms->flags & CSIMSETT_FLAG_TEARING_ENABLED) && + ( ((length-L)*100.0f/L) > clmd->sim_parms->maxspringlen )) { + // cut spring! + s->flags |= CSPRING_FLAG_DEACTIVATE; + return false; + } + } + */ + mul_v3_v3fl(r_dir, r_extent, 1.0f/(*r_length)); + } + else { + zero_v3(r_dir); + } + + return true; +} + +BLI_INLINE void apply_spring(Implicit_Data *data, int i, int j, const float f[3], float dfdx[3][3], float dfdv[3][3]) +{ + add_v3_v3(data->F.v3(i), f); + sub_v3_v3(data->F.v3(j), f); + + data->idFdX.add(i, i, dfdx); + data->idFdX.add(j, j, dfdx); + data->idFdX.sub(i, j, dfdx); + data->idFdX.sub(j, i, dfdx); + + data->idFdV.add(i, i, dfdv); + data->idFdV.add(j, j, dfdv); + data->idFdV.sub(i, j, dfdv); + data->idFdV.sub(j, i, dfdv); +} + +bool BPH_mass_spring_force_spring_linear(Implicit_Data *data, int i, int j, float restlen, + float stiffness, float damping, bool no_compress, float clamp_force, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) +{ + float extent[3], length, dir[3], vel[3]; + + // calculate elonglation + spring_length(data, i, j, extent, dir, &length, vel); + + if (length > restlen || no_compress) { + float stretch_force, f[3], dfdx[3][3], dfdv[3][3]; + + stretch_force = stiffness * (length - restlen); + if (clamp_force > 0.0f && stretch_force > clamp_force) { + stretch_force = clamp_force; + } + mul_v3_v3fl(f, dir, stretch_force); + + // Ascher & Boxman, p.21: Damping only during elonglation + // something wrong with it... + madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir)); + + dfdx_spring(dfdx, dir, length, restlen, stiffness); + dfdv_damp(dfdv, dir, damping); + + apply_spring(data, i, j, f, dfdx, dfdv); + + if (r_f) copy_v3_v3(r_f, f); + if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); + if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); + + return true; + } + else { + if (r_f) zero_v3(r_f); + if (r_dfdx) zero_m3(r_dfdx); + if (r_dfdv) zero_m3(r_dfdv); + + return false; + } +} + +/* See "Stable but Responsive Cloth" (Choi, Ko 2005) */ +bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, float restlen, + float kb, float cb, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) +{ + float extent[3], length, dir[3], vel[3]; + + // calculate elonglation + spring_length(data, i, j, extent, dir, &length, vel); + + if (length < restlen) { + float f[3], dfdx[3][3], dfdv[3][3]; + + mul_v3_v3fl(f, dir, fbstar(length, restlen, kb, cb)); + + outerproduct(dfdx, dir, dir); + mul_m3_fl(dfdx, fbstar_jacobi(length, restlen, kb, cb)); + + /* XXX damping not supported */ + zero_m3(dfdv); + + apply_spring(data, i, j, f, dfdx, dfdv); + + if (r_f) copy_v3_v3(r_f, f); + if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); + if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); + + return true; + } + else { + if (r_f) zero_v3(r_f); + if (r_dfdx) zero_m3(r_dfdx); + if (r_dfdv) zero_m3(r_dfdv); + + return false; + } +} + +/* Jacobian of a direction vector. + * Basically the part of the differential orthogonal to the direction, + * inversely proportional to the length of the edge. + * + * dD_ij/dx_i = -dD_ij/dx_j = (D_ij * D_ij^T - I) / len_ij + */ +BLI_INLINE void spring_grad_dir(Implicit_Data *data, int i, int j, float edge[3], float dir[3], float grad_dir[3][3]) +{ + float length; + + sub_v3_v3v3(edge, data->X.v3(j), data->X.v3(i)); + length = normalize_v3_v3(dir, edge); + + if (length > ALMOST_ZERO) { + outerproduct(grad_dir, dir, dir); + sub_m3_m3m3(grad_dir, I, grad_dir); + mul_m3_fl(grad_dir, 1.0f / length); + } + else { + zero_m3(grad_dir); + } +} + +BLI_INLINE void spring_angbend_forces(Implicit_Data *data, int i, int j, int k, + const float goal[3], + float stiffness, float damping, + int q, const float dx[3], const float dv[3], + float r_f[3]) +{ + float edge_ij[3], dir_ij[3]; + float edge_jk[3], dir_jk[3]; + float vel_ij[3], vel_jk[3], vel_ortho[3]; + float f_bend[3], f_damp[3]; + float fk[3]; + float dist[3]; + + zero_v3(fk); + + sub_v3_v3v3(edge_ij, data->X.v3(j), data->X.v3(i)); + if (q == i) sub_v3_v3(edge_ij, dx); + if (q == j) add_v3_v3(edge_ij, dx); + normalize_v3_v3(dir_ij, edge_ij); + + sub_v3_v3v3(edge_jk, data->X.v3(k), data->X.v3(j)); + if (q == j) sub_v3_v3(edge_jk, dx); + if (q == k) add_v3_v3(edge_jk, dx); + normalize_v3_v3(dir_jk, edge_jk); + + sub_v3_v3v3(vel_ij, data->V.v3(j), data->V.v3(i)); + if (q == i) sub_v3_v3(vel_ij, dv); + if (q == j) add_v3_v3(vel_ij, dv); + + sub_v3_v3v3(vel_jk, data->V.v3(k), data->V.v3(j)); + if (q == j) sub_v3_v3(vel_jk, dv); + if (q == k) add_v3_v3(vel_jk, dv); + + /* bending force */ + sub_v3_v3v3(dist, goal, edge_jk); + mul_v3_v3fl(f_bend, dist, stiffness); + + add_v3_v3(fk, f_bend); + + /* damping force */ + madd_v3_v3v3fl(vel_ortho, vel_jk, dir_jk, -dot_v3v3(vel_jk, dir_jk)); + mul_v3_v3fl(f_damp, vel_ortho, damping); + + sub_v3_v3(fk, f_damp); + + copy_v3_v3(r_f, fk); +} + +/* Finite Differences method for estimating the jacobian of the force */ +BLI_INLINE void spring_angbend_estimate_dfdx(Implicit_Data *data, int i, int j, int k, + const float goal[3], + float stiffness, float damping, + int q, float dfdx[3][3]) +{ + const float delta = 0.00001f; // TODO find a good heuristic for this + float dvec_null[3][3], dvec_pos[3][3], dvec_neg[3][3]; + float f[3]; + int a, b; + + zero_m3(dvec_null); + unit_m3(dvec_pos); + mul_m3_fl(dvec_pos, delta * 0.5f); + copy_m3_m3(dvec_neg, dvec_pos); + negate_m3(dvec_neg); + + /* XXX TODO offset targets to account for position dependency */ + + for (a = 0; a < 3; ++a) { + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, + q, dvec_pos[a], dvec_null[a], f); + copy_v3_v3(dfdx[a], f); + + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, + q, dvec_neg[a], dvec_null[a], f); + sub_v3_v3(dfdx[a], f); + + for (b = 0; b < 3; ++b) { + dfdx[a][b] /= delta; + } + } +} + +/* Finite Differences method for estimating the jacobian of the force */ +BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j, int k, + const float goal[3], + float stiffness, float damping, + int q, float dfdv[3][3]) +{ + const float delta = 0.00001f; // TODO find a good heuristic for this + float dvec_null[3][3], dvec_pos[3][3], dvec_neg[3][3]; + float f[3]; + int a, b; + + zero_m3(dvec_null); + unit_m3(dvec_pos); + mul_m3_fl(dvec_pos, delta * 0.5f); + copy_m3_m3(dvec_neg, dvec_pos); + negate_m3(dvec_neg); + + /* XXX TODO offset targets to account for position dependency */ + + for (a = 0; a < 3; ++a) { + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, + q, dvec_null[a], dvec_pos[a], f); + copy_v3_v3(dfdv[a], f); + + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, + q, dvec_null[a], dvec_neg[a], f); + sub_v3_v3(dfdv[a], f); + + for (b = 0; b < 3; ++b) { + dfdv[a][b] /= delta; + } + } +} + +/* Angular spring that pulls the vertex toward the local target + * See "Artistic Simulation of Curly Hair" (Pixar technical memo #12-03a) + */ +bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, int j, int k, + const float target[3], float stiffness, float damping) +{ + float goal[3]; + float fj[3], fk[3]; + float dfj_dxi[3][3], dfj_dxj[3][3], dfk_dxi[3][3], dfk_dxj[3][3], dfk_dxk[3][3]; + float dfj_dvi[3][3], dfj_dvj[3][3], dfk_dvi[3][3], dfk_dvj[3][3], dfk_dvk[3][3]; + + const float vecnull[3] = {0.0f, 0.0f, 0.0f}; + + world_to_root_v3(data, j, goal, target); + + spring_angbend_forces(data, i, j, k, goal, stiffness, damping, k, vecnull, vecnull, fk); + negate_v3_v3(fj, fk); /* counterforce */ + + spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, i, dfk_dxi); + spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, j, dfk_dxj); + spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, k, dfk_dxk); + copy_m3_m3(dfj_dxi, dfk_dxi); negate_m3(dfj_dxi); + copy_m3_m3(dfj_dxj, dfk_dxj); negate_m3(dfj_dxj); + + spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, i, dfk_dvi); + spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, j, dfk_dvj); + spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, k, dfk_dvk); + copy_m3_m3(dfj_dvi, dfk_dvi); negate_m3(dfj_dvi); + copy_m3_m3(dfj_dvj, dfk_dvj); negate_m3(dfj_dvj); + + /* add forces and jacobians to the solver data */ + + add_v3_v3(data->F.v3(j), fj); + add_v3_v3(data->F.v3(k), fk); + + data->idFdX.add(j, j, dfj_dxj); + data->idFdX.add(k, k, dfk_dxk); + + data->idFdX.add(i, j, dfj_dxi); + data->idFdX.add(j, i, dfj_dxi); + data->idFdX.add(j, k, dfk_dxj); + data->idFdX.add(k, j, dfk_dxj); + data->idFdX.add(i, k, dfk_dxi); + data->idFdX.add(k, i, dfk_dxi); + + data->idFdV.add(j, j, dfj_dvj); + data->idFdV.add(k, k, dfk_dvk); + + data->idFdV.add(i, j, dfj_dvi); + data->idFdV.add(j, i, dfj_dvi); + data->idFdV.add(j, k, dfk_dvj); + data->idFdV.add(k, j, dfk_dvj); + data->idFdV.add(i, k, dfk_dvi); + data->idFdV.add(k, i, dfk_dvi); + + /* XXX analytical calculation of derivatives below is incorrect. + * This proved to be difficult, but for now just using the finite difference method for + * estimating the jacobians should be sufficient. + */ +#if 0 + float edge_ij[3], dir_ij[3], grad_dir_ij[3][3]; + float edge_jk[3], dir_jk[3], grad_dir_jk[3][3]; + float dist[3], vel_jk[3], vel_jk_ortho[3], projvel[3]; + float target[3]; + float tmp[3][3]; + float fi[3], fj[3], fk[3]; + float dfi_dxi[3][3], dfj_dxi[3][3], dfj_dxj[3][3], dfk_dxi[3][3], dfk_dxj[3][3], dfk_dxk[3][3]; + float dfdvi[3][3]; + + // TESTING + damping = 0.0f; + + zero_v3(fi); + zero_v3(fj); + zero_v3(fk); + zero_m3(dfi_dxi); + zero_m3(dfj_dxi); + zero_m3(dfk_dxi); + zero_m3(dfk_dxj); + zero_m3(dfk_dxk); + + /* jacobian of direction vectors */ + spring_grad_dir(data, i, j, edge_ij, dir_ij, grad_dir_ij); + spring_grad_dir(data, j, k, edge_jk, dir_jk, grad_dir_jk); + + sub_v3_v3v3(vel_jk, data->V[k], data->V[j]); + + /* bending force */ + mul_v3_v3fl(target, dir_ij, restlen); + sub_v3_v3v3(dist, target, edge_jk); + mul_v3_v3fl(fk, dist, stiffness); + + /* damping force */ + madd_v3_v3v3fl(vel_jk_ortho, vel_jk, dir_jk, -dot_v3v3(vel_jk, dir_jk)); + madd_v3_v3fl(fk, vel_jk_ortho, damping); + + /* XXX this only holds true as long as we assume straight rest shape! + * eventually will become a bit more involved since the opposite segment + * gets its own target, under condition of having equal torque on both sides. + */ + copy_v3_v3(fi, fk); + + /* counterforce on the middle point */ + sub_v3_v3(fj, fi); + sub_v3_v3(fj, fk); + + /* === derivatives === */ + + madd_m3_m3fl(dfk_dxi, grad_dir_ij, stiffness * restlen); + + madd_m3_m3fl(dfk_dxj, grad_dir_ij, -stiffness * restlen); + madd_m3_m3fl(dfk_dxj, I, stiffness); + + madd_m3_m3fl(dfk_dxk, I, -stiffness); + + copy_m3_m3(dfi_dxi, dfk_dxk); + negate_m3(dfi_dxi); + + /* dfj_dfi == dfi_dfj due to symmetry, + * dfi_dfj == dfk_dfj due to fi == fk + * XXX see comment above on future bent rest shapes + */ + copy_m3_m3(dfj_dxi, dfk_dxj); + + /* dfj_dxj == -(dfi_dxj + dfk_dxj) due to fj == -(fi + fk) */ + sub_m3_m3m3(dfj_dxj, dfj_dxj, dfj_dxi); + sub_m3_m3m3(dfj_dxj, dfj_dxj, dfk_dxj); + + /* add forces and jacobians to the solver data */ + add_v3_v3(data->F[i], fi); + add_v3_v3(data->F[j], fj); + add_v3_v3(data->F[k], fk); + + add_m3_m3m3(data->dFdX[i].m, data->dFdX[i].m, dfi_dxi); + add_m3_m3m3(data->dFdX[j].m, data->dFdX[j].m, dfj_dxj); + add_m3_m3m3(data->dFdX[k].m, data->dFdX[k].m, dfk_dxk); + + add_m3_m3m3(data->dFdX[block_ij].m, data->dFdX[block_ij].m, dfj_dxi); + add_m3_m3m3(data->dFdX[block_jk].m, data->dFdX[block_jk].m, dfk_dxj); + add_m3_m3m3(data->dFdX[block_ik].m, data->dFdX[block_ik].m, dfk_dxi); +#endif + + return true; +} + +bool BPH_mass_spring_force_spring_goal(Implicit_Data *data, int i, const float goal_x[3], const float goal_v[3], + float stiffness, float damping, + float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]) +{ + float root_goal_x[3], root_goal_v[3], extent[3], length, dir[3], vel[3]; + float f[3], dfdx[3][3], dfdv[3][3]; + + /* goal is in world space */ + world_to_root_v3(data, i, root_goal_x, goal_x); + world_to_root_v3(data, i, root_goal_v, goal_v); + + sub_v3_v3v3(extent, root_goal_x, data->X.v3(i)); + sub_v3_v3v3(vel, root_goal_v, data->V.v3(i)); + length = normalize_v3_v3(dir, extent); + + if (length > ALMOST_ZERO) { + mul_v3_v3fl(f, dir, stiffness * length); + + // Ascher & Boxman, p.21: Damping only during elonglation + // something wrong with it... + madd_v3_v3fl(f, dir, damping * dot_v3v3(vel, dir)); + + dfdx_spring(dfdx, dir, length, 0.0f, stiffness); + dfdv_damp(dfdv, dir, damping); + + add_v3_v3(data->F.v3(i), f); + data->idFdX.add(i, i, dfdx); + data->idFdV.add(i, i, dfdv); + + if (r_f) copy_v3_v3(r_f, f); + if (r_dfdx) copy_m3_m3(r_dfdx, dfdx); + if (r_dfdv) copy_m3_m3(r_dfdv, dfdv); + + return true; + } + else { + if (r_f) zero_v3(r_f); + if (r_dfdx) zero_m3(r_dfdx); + if (r_dfdv) zero_m3(r_dfdv); + + return false; + } +} + +#endif /* IMPLICIT_SOLVER_EIGEN */ diff --git a/source/blender/python/generic/bpy_internal_import.c b/source/blender/python/generic/bpy_internal_import.c index b0539607bdc..ed2752d8372 100644 --- a/source/blender/python/generic/bpy_internal_import.c +++ b/source/blender/python/generic/bpy_internal_import.c @@ -81,7 +81,7 @@ void bpy_import_init(PyObject *builtins) /* move reload here * XXX, use import hooks */ - mod = PyImport_ImportModuleLevel("imp", NULL, NULL, NULL, 0); + mod = PyImport_ImportModuleLevel("importlib", NULL, NULL, NULL, 0); if (mod) { PyObject *mod_dict = PyModule_GetDict(mod); @@ -93,7 +93,7 @@ void bpy_import_init(PyObject *builtins) Py_DECREF(mod); } else { - BLI_assert(!"unable to load 'imp' module."); + BLI_assert(!"unable to load 'importlib' module."); } } diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 678fd62ae89..cca419101d3 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -283,6 +283,7 @@ static PyGetSetDef bpy_app_getsets[] = { {(char *)"debug_handlers", bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG_HANDLERS}, {(char *)"debug_wm", bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG_WM}, {(char *)"debug_depsgraph", bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG_DEPSGRAPH}, + {(char *)"debug_simdata", bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG_SIMDATA}, {(char *)"debug_value", bpy_app_debug_value_get, bpy_app_debug_value_set, (char *)bpy_app_debug_value_doc, NULL}, {(char *)"tempdir", bpy_app_tempdir_get, NULL, (char *)bpy_app_tempdir_doc, NULL}, diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index 1a56c0f7c59..17a2038b7dc 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -260,7 +260,7 @@ const char *BPY_app_translations_py_pgettext(const char *msgctxt, const char *ms return msgid; tmp = BLF_lang_get(); - if (strcmp(tmp, locale) || !_translations_cache) { + if (!STREQ(tmp, locale) || !_translations_cache) { PyGILState_STATE _py_state; BLI_strncpy(locale, tmp, STATIC_LOCALE_SIZE); diff --git a/source/blender/python/mathutils/CMakeLists.txt b/source/blender/python/mathutils/CMakeLists.txt index 133b8d3895c..ef6b090140b 100644 --- a/source/blender/python/mathutils/CMakeLists.txt +++ b/source/blender/python/mathutils/CMakeLists.txt @@ -38,6 +38,7 @@ set(SRC mathutils_Quaternion.c mathutils_Vector.c mathutils_geometry.c + mathutils_interpolate.c mathutils_kdtree.c mathutils_noise.c @@ -48,6 +49,7 @@ set(SRC mathutils_Quaternion.h mathutils_Vector.h mathutils_geometry.h + mathutils_interpolate.h mathutils_kdtree.h mathutils_noise.h ) diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 6fe8dc3866b..ca20f837b4a 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -38,7 +38,18 @@ #endif PyDoc_STRVAR(M_Mathutils_doc, -"This module provides access to matrices, eulers, quaternions and vectors." +"This module provides access to the math classes:\n" +"\n" +"- :class:`Color`,\n" +"- :class:`Euler`,\n" +"- :class:`Matrix`,\n" +"- :class:`Quaternion`,\n" +"- :class:`Vector`,\n" +"\n" +".. note::\n" +"\n" +" Classes, methods and attributes that accept vectors also accept other numeric sequences,\n" +" such as tuples, lists." ); static int mathutils_array_parse_fast(float *array, int size, @@ -502,6 +513,7 @@ static struct PyModuleDef M_Mathutils_module_def = { /* submodules only */ #include "mathutils_geometry.h" +#include "mathutils_interpolate.h" #ifndef MATH_STANDALONE # include "mathutils_kdtree.h" # include "mathutils_noise.h" @@ -543,6 +555,13 @@ PyMODINIT_FUNC PyInit_mathutils(void) PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); Py_INCREF(submodule); + PyModule_AddObject(mod, "interpolate", (submodule = PyInit_mathutils_interpolate())); + /* XXX, python doesnt do imports with this usefully yet + * 'from mathutils.geometry import PolyFill' + * ...fails without this. */ + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + #ifndef MATH_STANDALONE /* Noise submodule */ PyModule_AddObject(mod, "noise", (submodule = PyInit_mathutils_noise())); diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 8426038f2d4..ce590999bc1 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -811,7 +811,12 @@ static struct PyMethodDef Color_methods[] = { /* ------------------PY_OBECT DEFINITION-------------------------- */ PyDoc_STRVAR(color_doc, -"This object gives access to Colors in Blender." +".. class:: Color(rgb)\n" +"\n" +" This object gives access to Colors in Blender.\n" +"\n" +" :param rgb: (r, g, b) color values\n" +" :type rgb: 3d vector\n" ); PyTypeObject color_Type = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c index 9c0ced39403..edb4af0aa1d 100644 --- a/source/blender/python/mathutils/mathutils_Euler.c +++ b/source/blender/python/mathutils/mathutils_Euler.c @@ -657,7 +657,14 @@ static struct PyMethodDef Euler_methods[] = { /* ------------------PY_OBECT DEFINITION-------------------------- */ PyDoc_STRVAR(euler_doc, -"This object gives access to Eulers in Blender." +".. class:: Euler(angles, order='XYZ')\n" +"\n" +" This object gives access to Eulers in Blender.\n" +"\n" +" :param angles: Three angles, in radians.\n" +" :type angles: 3d vector\n" +" :param order: Optional order of the angles, a permutation of ``XYZ``.\n" +" :type order: str\n" ); PyTypeObject euler_Type = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 4706c09176e..95b53256eac 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -2716,7 +2716,14 @@ static struct PyMethodDef Matrix_methods[] = { /*------------------PY_OBECT DEFINITION--------------------------*/ PyDoc_STRVAR(matrix_doc, -"This object gives access to Matrices in Blender." +".. class:: Matrix([rows])\n" +"\n" +" This object gives access to Matrices in Blender, supporting square and rectangular\n" +" matrices from 2x2 up to 4x4.\n" +"\n" +" :param rows: Sequence of rows.\n" +" When ommitted, a 4x4 identity matrix is constructed.\n" +" :type rows: 2d number sequence\n" ); PyTypeObject matrix_Type = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index d422634a496..42be316bc9b 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -177,6 +177,30 @@ static PyObject *Quaternion_to_axis_angle(QuaternionObject *self) return ret; } +PyDoc_STRVAR(Quaternion_to_exponential_map_doc, +".. method:: to_exponential_map()\n" +"\n" +" Return the exponential map representation of the quaternion.\n" +"\n" +" This representation consist of the rotation axis multiplied by the rotation angle." +" Such a representation is useful for interpolation between multiple orientations.\n" +"\n" +" :return: exponential map.\n" +" :rtype: :class:`Vector` of size 3\n" +"\n" +" To convert back to a quaternion, pass it to the :class:`Quaternion` constructor.\n" +); +static PyObject *Quaternion_to_exponential_map(QuaternionObject *self) +{ + float expmap[3]; + + if (BaseMath_ReadCallback(self) == -1) + return NULL; + + quat_to_expmap(expmap, self->quat); + return Vector_CreatePyObject(expmap, 3, NULL); +} + PyDoc_STRVAR(Quaternion_cross_doc, ".. method:: cross(other)\n" "\n" @@ -334,7 +358,8 @@ static PyObject *Quaternion_rotate(QuaternionObject *self, PyObject *value) } /* ----------------------------Quaternion.normalize()---------------- */ -/* normalize the axis of rotation of [theta, vector] */ +/* Normalize the quaternion. This may change the angle as well as the + * rotation axis, as all of (w, x, y, z) are scaled. */ PyDoc_STRVAR(Quaternion_normalize_doc, ".. function:: normalize()\n" "\n" @@ -1076,9 +1101,24 @@ static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kw case 0: break; case 1: - if (mathutils_array_parse(quat, QUAT_SIZE, QUAT_SIZE, seq, "mathutils.Quaternion()") == -1) + { + int size; + + if ((size = mathutils_array_parse(quat, 3, QUAT_SIZE, seq, "mathutils.Quaternion()")) == -1) { return NULL; + } + + if (size == 4) { + /* 4d: Quaternion (common case) */ + } + else { + /* 3d: Interpret as exponential map */ + BLI_assert(size == 3); + expmap_to_quat(quat, quat); + } + break; + } case 2: { float axis[3]; @@ -1155,6 +1195,7 @@ static struct PyMethodDef Quaternion_methods[] = { {"to_euler", (PyCFunction) Quaternion_to_euler, METH_VARARGS, Quaternion_to_euler_doc}, {"to_matrix", (PyCFunction) Quaternion_to_matrix, METH_NOARGS, Quaternion_to_matrix_doc}, {"to_axis_angle", (PyCFunction) Quaternion_to_axis_angle, METH_NOARGS, Quaternion_to_axis_angle_doc}, + {"to_exponential_map", (PyCFunction) Quaternion_to_exponential_map, METH_NOARGS, Quaternion_to_exponential_map_doc}, /* operation between 2 or more types */ {"cross", (PyCFunction) Quaternion_cross, METH_O, Quaternion_cross_doc}, @@ -1187,7 +1228,29 @@ static PyGetSetDef Quaternion_getseters[] = { /* ------------------PY_OBECT DEFINITION-------------------------- */ PyDoc_STRVAR(quaternion_doc, -"This object gives access to Quaternions in Blender." +".. class:: Quaternion([seq, [angle]])\n" +"\n" +" This object gives access to Quaternions in Blender.\n" +"\n" +" :param seq: size 3 or 4\n" +" :type seq: :class:`Vector`\n" +" :param angle: rotation angle, in radians\n" +" :type angle: float\n" +"\n" +" The constructor takes arguments in various forms:\n" +"\n" +" (), *no args*\n" +" Create an identity quaternion\n" +" (*wxyz*)\n" +" Create a quaternion from a ``(w, x, y, z)`` vector.\n" +" (*exponential_map*)\n" +" Create a quaternion from a 3d exponential map vector.\n" +"\n" +" .. seealso:: :meth:`to_exponential_map`\n" +" (*axis, angle*)\n" +" Create a quaternion representing a rotation of *angle* radians over *axis*.\n" +"\n" +" .. seealso:: :meth:`to_axis_angle`\n" ); PyTypeObject quaternion_Type = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 167fb5bcf3d..091412b0f53 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -2904,7 +2904,12 @@ static struct PyMethodDef Vector_methods[] = { */ PyDoc_STRVAR(vector_doc, -"This object gives access to Vectors in Blender." +".. class:: Vector(seq)\n" +"\n" +" This object gives access to Vectors in Blender.\n" +"\n" +" :param seq: Components of the vector, must be a sequence of at least two\n" +" :type seq: sequence of numbers\n" ); PyTypeObject vector_Type = { PyVarObject_HEAD_INIT(NULL, 0) diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index b496ee1fbf6..bb4080decef 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -87,14 +87,14 @@ static PyObject *M_Geometry_intersect_ray_tri(PyObject *UNUSED(self), PyObject * return NULL; } - if (((mathutils_array_parse(dir, 3, 3, py_ray, error_prefix) != -1) && - (mathutils_array_parse(orig, 3, 3, py_ray_off, error_prefix) != -1)) == 0) + if (((mathutils_array_parse(dir, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_ray, error_prefix) != -1) && + (mathutils_array_parse(orig, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_ray_off, error_prefix) != -1)) == 0) { return NULL; } for (i = 0; i < ARRAY_SIZE(tri); i++) { - if (mathutils_array_parse(tri[i], 2, 2 | MU_ARRAY_SPILL, py_tri[i], error_prefix) == -1) { + if (mathutils_array_parse(tri[i], 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_tri[i], error_prefix) == -1) { return NULL; } } @@ -183,18 +183,14 @@ static PyObject *M_Geometry_intersect_line_line(PyObject *UNUSED(self), PyObject return NULL; } - if ((((len = mathutils_array_parse(lines[0], 2, 3, py_lines[0], error_prefix)) != -1) && - (mathutils_array_parse(lines[1], len, len, py_lines[1], error_prefix) != -1) && - (mathutils_array_parse(lines[2], len, len, py_lines[2], error_prefix) != -1) && - (mathutils_array_parse(lines[3], len, len, py_lines[3], error_prefix) != -1)) == 0) + if ((((len = mathutils_array_parse(lines[0], 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_lines[0], error_prefix)) != -1) && + (mathutils_array_parse(lines[1], len, len | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_lines[1], error_prefix) != -1) && + (mathutils_array_parse(lines[2], len, len | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_lines[2], error_prefix) != -1) && + (mathutils_array_parse(lines[3], len, len | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_lines[3], error_prefix) != -1)) == 0) { return NULL; } - if (len == 2) { - lines[0][2] = lines[1][2] = lines[2][2] = lines[3][2] = 0.0f; - } - result = isect_line_line_v3(UNPACK4(lines), i1, i2); /* The return-code isnt exposed, * this way we can check know how close the lines are. */ @@ -745,7 +741,7 @@ static PyObject *M_Geometry_intersect_point_line(PyObject *UNUSED(self), PyObjec /* accept 2d verts */ if ((((size = mathutils_array_parse(pt, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_pt, error_prefix)) != -1) && (mathutils_array_parse(line_a, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_line_a, error_prefix) != -1) && - (mathutils_array_parse(line_b, 3, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_line_b, error_prefix) != -1)) == 0) + (mathutils_array_parse(line_b, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_line_b, error_prefix) != -1)) == 0) { return NULL; } @@ -791,11 +787,11 @@ static PyObject *M_Geometry_intersect_point_tri(PyObject *UNUSED(self), PyObject return NULL; } - if (mathutils_array_parse(pt, 3, 3 | MU_ARRAY_SPILL, py_pt, error_prefix) == -1) { + if (mathutils_array_parse(pt, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_pt, error_prefix) == -1) { return NULL; } for (i = 0; i < ARRAY_SIZE(tri); i++) { - if (mathutils_array_parse(tri[i], 3, 3 | MU_ARRAY_SPILL, py_tri[i], error_prefix) == -1) { + if (mathutils_array_parse(tri[i], 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_tri[i], error_prefix) == -1) { return NULL; } } @@ -971,7 +967,7 @@ static PyObject *M_Geometry_barycentric_transform(PyObject *UNUSED(self), PyObje return NULL; } - if (mathutils_array_parse(pt_src, 2, 2 | MU_ARRAY_SPILL, py_pt_src, error_prefix) == -1) { + if (mathutils_array_parse(pt_src, 3, 3 | MU_ARRAY_SPILL, py_pt_src, error_prefix) == -1) { return NULL; } for (i = 0; i < ARRAY_SIZE(tri_src); i++) { @@ -1132,7 +1128,7 @@ static PyObject *M_Geometry_interpolate_bezier(PyObject *UNUSED(self), PyObject for (i = 0; i < 4; i++) { int dims_tmp; - if ((((dims_tmp = mathutils_array_parse(data[i], 2, 2 | MU_ARRAY_SPILL, py_data[i], error_prefix)) == -1))) { + if ((dims_tmp = mathutils_array_parse(data[i], 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_data[i], error_prefix)) == -1) { return NULL; } dims = max_ii(dims, dims_tmp); diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c new file mode 100644 index 00000000000..4d7841b906a --- /dev/null +++ b/source/blender/python/mathutils/mathutils_interpolate.c @@ -0,0 +1,137 @@ +/* + * ***** 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. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/mathutils/mathutils_interpolate.c + * \ingroup pymathutils + */ + + +#include <Python.h> + +#include "mathutils.h" +#include "mathutils_interpolate.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#ifndef MATH_STANDALONE /* define when building outside blender */ +# include "MEM_guardedalloc.h" +#endif + +/*-------------------------DOC STRINGS ---------------------------*/ +PyDoc_STRVAR(M_Interpolate_doc, +"The Blender interpolate module" +); + +/* ---------------------------------WEIGHT CALCULATION ----------------------- */ + +#ifndef MATH_STANDALONE + +PyDoc_STRVAR(M_Interpolate_poly_3d_calc_doc, +".. function:: poly_3d_calc(veclist, pt)\n" +"\n" +" Calculate barycentric weights for a point on a polygon.\n" +"\n" +" :arg veclist: list of vectors\n" +" :arg pt: point" +" :rtype: list of per-vector weights\n" +); +static PyObject *M_Interpolate_poly_3d_calc(PyObject *UNUSED(self), PyObject *args) +{ + float fp[3]; + float (*vecs)[3]; + Py_ssize_t len; + + PyObject *point, *veclist, *ret; + int i; + + if (!PyArg_ParseTuple(args, "OO!:interpolation_weights", + &veclist, + &vector_Type, &point)) + { + return NULL; + } + + if (BaseMath_ReadCallback((VectorObject *)point) == -1) + return NULL; + + fp[0] = ((VectorObject *)point)->vec[0]; + fp[1] = ((VectorObject *)point)->vec[1]; + if (((VectorObject *)point)->size > 2) + fp[2] = ((VectorObject *)point)->vec[2]; + else + fp[2] = 0.0f; /* if its a 2d vector then set the z to be zero */ + + len = mathutils_array_parse_alloc_v(((float **)&vecs), 3, veclist, "interpolation_weights"); + if (len == -1) { + return NULL; + } + + if (len) { + float *weights = MEM_mallocN(sizeof(float) * len, "interpolation weights"); + + interp_weights_poly_v3(weights, vecs, len, fp); + + ret = PyList_New(len); + for (i = 0; i < len; i++) { + PyList_SET_ITEM(ret, i, PyFloat_FromDouble(weights[i])); + } + + MEM_freeN(weights); + + PyMem_Free(vecs); + } + else { + ret = PyList_New(0); + } + + return ret; +} + +#endif /* MATH_STANDALONE */ + + +static PyMethodDef M_Interpolate_methods[] = { +#ifndef MATH_STANDALONE + {"poly_3d_calc", (PyCFunction) M_Interpolate_poly_3d_calc, METH_VARARGS, M_Interpolate_poly_3d_calc_doc}, +#endif + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef M_Interpolate_module_def = { + PyModuleDef_HEAD_INIT, + "mathutils.interpolate", /* m_name */ + M_Interpolate_doc, /* m_doc */ + 0, /* m_size */ + M_Interpolate_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; + +/*----------------------------MODULE INIT-------------------------*/ +PyMODINIT_FUNC PyInit_mathutils_interpolate(void) +{ + PyObject *submodule = PyModule_Create(&M_Interpolate_module_def); + return submodule; +} diff --git a/source/blender/compositor/intern/COM_ChannelInfo.cpp b/source/blender/python/mathutils/mathutils_interpolate.h index 557075cdc80..1bbff6f3286 100644 --- a/source/blender/compositor/intern/COM_ChannelInfo.cpp +++ b/source/blender/python/mathutils/mathutils_interpolate.h @@ -1,5 +1,5 @@ /* - * Copyright 2011, Blender Foundation. + * ***** 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 @@ -15,21 +15,18 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * Contributor: - * Jeroen Bakker - * Monique Dewanchand + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** */ -#include "COM_ChannelInfo.h" -#include "COM_defines.h" -#include <stdio.h> +#ifndef __MATHUTILS_INTERPOLATE_H__ +#define __MATHUTILS_INTERPOLATE_H__ -/** - * @brief create new ChannelInfo instance and sets the defaults. +/** \file blender/python/mathutils/mathutils_interpolate.h + * \ingroup pymathutils */ -ChannelInfo::ChannelInfo() -{ - this->m_number = 0; - this->m_premultiplied = true; - this->m_type = COM_CT_UNUSED; -} + +PyMODINIT_FUNC PyInit_mathutils_interpolate(void); + +#endif /* __MATHUTILS_INTERPOLATE_H__ */ diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index e516c954737..36b9f8ae362 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -33,6 +33,7 @@ set(INC ../imbuf ../makesdna ../makesrna + ../physics ../../../intern/guardedalloc ../../../intern/mikktspace ../../../intern/smoke/extern diff --git a/source/blender/render/SConscript b/source/blender/render/SConscript index ebd131082e4..0cbf188525b 100644 --- a/source/blender/render/SConscript +++ b/source/blender/render/SConscript @@ -40,6 +40,7 @@ incs = [ '../imbuf', '../makesdna', '../makesrna', + '../physics', '../../../intern/mikktspace', '../../../intern/smoke/extern', ] diff --git a/source/blender/render/extern/include/RE_render_ext.h b/source/blender/render/extern/include/RE_render_ext.h index 6d083ec785d..d76d16a641e 100644 --- a/source/blender/render/extern/include/RE_render_ext.h +++ b/source/blender/render/extern/include/RE_render_ext.h @@ -45,7 +45,7 @@ struct Scene; struct View3D; /* particle.c, effect.c, editmesh_modes.c and brush.c, returns 1 if rgb, 0 otherwise */ -int externtex(struct MTex *mtex, const float vec[3], float *tin, float *tr, float *tg, float *tb, float *ta, const int thread, struct ImagePool *pool); +int externtex(struct MTex *mtex, const float vec[3], float *tin, float *tr, float *tg, float *tb, float *ta, const int thread, struct ImagePool *pool, const bool skip_load_image); /* particle.c */ void texture_rgb_blend(float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype); diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index da847d235f2..7c76f0362f4 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -198,9 +198,9 @@ struct ImagePool; struct Object; /* this one uses nodes */ -int multitex_ext(struct Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, struct ImagePool *pool, bool scene_color_manage); +int multitex_ext(struct Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, struct ImagePool *pool, bool scene_color_manage, const bool skip_load_image); /* nodes disabled */ -int multitex_ext_safe(struct Tex *tex, float texvec[3], struct TexResult *texres, struct ImagePool *pool, bool scene_color_manage); +int multitex_ext_safe(struct Tex *tex, float texvec[3], struct TexResult *texres, struct ImagePool *pool, bool scene_color_manage, const bool skip_load_image); /* only for internal node usage */ int multitex_nodes(struct Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, const short thread, short which_output, struct ShadeInput *shi, struct MTex *mtex, diff --git a/source/blender/render/intern/include/envmap.h b/source/blender/render/intern/include/envmap.h index c813e88c656..627e6c0e1e6 100644 --- a/source/blender/render/intern/include/envmap.h +++ b/source/blender/render/intern/include/envmap.h @@ -47,7 +47,7 @@ struct TexResult; struct ImagePool; void make_envmaps(struct Render *re); -int envmaptex(struct Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, struct ImagePool *pool); +int envmaptex(struct Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, struct ImagePool *pool, const bool skip_image_load); void env_rotate_scene(struct Render *re, float mat[4][4], int do_rotate); #endif /* __ENVMAP_H__ */ diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index b87b1e6f367..8d92fb9eec9 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -424,6 +424,7 @@ typedef struct HaloRen { unsigned int lay; struct Material *mat; struct ImagePool *pool; + bool skip_load_image; } HaloRen; /* ------------------------------------------------------------------------- */ diff --git a/source/blender/render/intern/include/texture.h b/source/blender/render/intern/include/texture.h index ff5004fd7f0..ebc83984306 100644 --- a/source/blender/render/intern/include/texture.h +++ b/source/blender/render/intern/include/texture.h @@ -81,8 +81,8 @@ void render_realtime_texture(struct ShadeInput *shi, struct Image *ima); /* imagetexture.h */ -int imagewraposa(struct Tex *tex, struct Image *ima, struct ImBuf *ibuf, const float texvec[3], const float dxt[2], const float dyt[2], struct TexResult *texres, struct ImagePool *pool); -int imagewrap(struct Tex *tex, struct Image *ima, struct ImBuf *ibuf, const float texvec[3], struct TexResult *texres, struct ImagePool *pool); +int imagewraposa(struct Tex *tex, struct Image *ima, struct ImBuf *ibuf, const float texvec[3], const float dxt[2], const float dyt[2], struct TexResult *texres, struct ImagePool *pool, const bool skip_load_image); +int imagewrap(struct Tex *tex, struct Image *ima, struct ImBuf *ibuf, const float texvec[3], struct TexResult *texres, struct ImagePool *pool, const bool skip_load_image); void image_sample(struct Image *ima, float fx, float fy, float dx, float dy, float result[4], struct ImagePool *pool); #endif /* __TEXTURE_H__ */ diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c index 90deac2de32..48ef29150d3 100644 --- a/source/blender/render/intern/source/bake_api.c +++ b/source/blender/render/intern/source/bake_api.c @@ -444,7 +444,7 @@ bool RE_bake_pixels_populate_from_objects( size_t i; int primitive_id; float u, v; - float imat_low [4][4]; + float imat_low[4][4]; bool is_cage = me_cage != NULL; bool result = true; diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index 35878f664ea..ba778ec30e9 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -1344,11 +1344,13 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem if (re->r.scemode & R_VIEWPORT_PREVIEW) { /* preview render */ totchild = (int)((float)totchild * (float)part->disp / 100.0f); - step_nbr = part->draw_step; + step_nbr = 1 << part->draw_step; } else { - step_nbr = part->ren_step; + step_nbr = 1 << part->ren_step; } + if (ELEM(part->kink, PART_KINK_SPIRAL)) + step_nbr += part->kink_extra_steps; psys->flag |= PSYS_DRAWING; @@ -1432,7 +1434,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem /* 2.6 setup strand rendering */ if (part->ren_as == PART_DRAW_PATH && psys->pathcache) { - path_nbr=(int)pow(2.0, (double) step_nbr); + path_nbr = step_nbr; if (path_nbr) { if (!ELEM(ma->material_type, MA_TYPE_HALO, MA_TYPE_WIRE)) { @@ -1551,7 +1553,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem if (path_nbr) { cache = psys->pathcache[a]; - max_k = (int)cache->steps; + max_k = (int)cache->segments; } if (totchild && (part->draw&PART_DRAW_PARENT)==0) continue; @@ -1562,10 +1564,10 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem if (path_nbr) { cache = psys->childcache[a-totpart]; - if (cache->steps < 0) + if (cache->segments < 0) continue; - max_k = (int)cache->steps; + max_k = (int)cache->segments; } pa_time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime); @@ -3314,7 +3316,7 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) v2= mface->v2; v3= reverse_verts==0 ? mface->v3 : mface->v1; v4= mface->v4; - flag= mface->flag & ME_SMOOTH; + flag = do_autosmooth ? ME_SMOOTH : mface->flag & ME_SMOOTH; vlr= RE_findOrAddVlak(obr, obr->totvlak++); vlr->v1= RE_findOrAddVert(obr, vertofs+v1); diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c index c3c70067836..f0268836104 100644 --- a/source/blender/render/intern/source/envmap.c +++ b/source/blender/render/intern/source/envmap.c @@ -694,7 +694,7 @@ static void set_dxtdyt(float r_dxt[3], float r_dyt[3], const float dxt[3], const /* ------------------------------------------------------------------------- */ -int envmaptex(Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, struct ImagePool *pool) +int envmaptex(Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, struct ImagePool *pool, const bool skip_load_image) { extern Render R; /* only in this call */ /* texvec should be the already reflected normal */ @@ -750,7 +750,7 @@ int envmaptex(Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int o mul_mat3_m4_v3(R.viewinv, dyt); } set_dxtdyt(dxts, dyts, dxt, dyt, face); - imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, texres, pool); + imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, texres, pool, skip_load_image); /* edges? */ @@ -767,7 +767,7 @@ int envmaptex(Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int o if (face != face1) { ibuf = env->cube[face1]; set_dxtdyt(dxts, dyts, dxt, dyt, face1); - imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, &texr1, pool); + imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, &texr1, pool, skip_load_image); } else texr1.tr = texr1.tg = texr1.tb = texr1.ta = 0.0; @@ -780,7 +780,7 @@ int envmaptex(Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int o if (face != face1) { ibuf = env->cube[face1]; set_dxtdyt(dxts, dyts, dxt, dyt, face1); - imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, &texr2, pool); + imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, &texr2, pool, skip_load_image); } else texr2.tr = texr2.tg = texr2.tb = texr2.ta = 0.0; @@ -796,7 +796,7 @@ int envmaptex(Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int o } } else { - imagewrap(tex, NULL, ibuf, sco, texres, pool); + imagewrap(tex, NULL, ibuf, sco, texres, pool, skip_load_image); } return 1; diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c index 833d1a75559..687e25a9410 100644 --- a/source/blender/render/intern/source/imagetexture.c +++ b/source/blender/render/intern/source/imagetexture.c @@ -103,7 +103,7 @@ static void ibuf_get_color(float col[4], struct ImBuf *ibuf, int x, int y) } } -int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResult *texres, struct ImagePool *pool) +int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResult *texres, struct ImagePool *pool, const bool skip_load_image) { float fx, fy, val1, val2, val3; int x, y, retval; @@ -120,7 +120,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul if (ima) { /* hack for icon render */ - if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima)) + if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) return retval; ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); @@ -912,7 +912,7 @@ static void image_mipmap_test(Tex *tex, ImBuf *ibuf) } -static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], float dxt[2], float dyt[2], TexResult *texres, struct ImagePool *pool) +static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], float dxt[2], float dyt[2], TexResult *texres, struct ImagePool *pool, const bool skip_load_image) { TexResult texr; float fx, fy, minx, maxx, miny, maxy; @@ -942,7 +942,7 @@ static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float tex if (ibuf==NULL && ima==NULL) return retval; if (ima) { /* hack for icon render */ - if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima)) { + if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) { return retval; } ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); @@ -1338,7 +1338,7 @@ static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float tex } -int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const float DXT[2], const float DYT[2], TexResult *texres, struct ImagePool *pool) +int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const float DXT[2], const float DYT[2], TexResult *texres, struct ImagePool *pool, const bool skip_load_image) { TexResult texr; float fx, fy, minx, maxx, miny, maxy, dx, dy, dxt[2], dyt[2]; @@ -1352,7 +1352,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const /* anisotropic filtering */ if (tex->texfilter != TXF_BOX) - return imagewraposa_aniso(tex, ima, ibuf, texvec, dxt, dyt, texres, pool); + return imagewraposa_aniso(tex, ima, ibuf, texvec, dxt, dyt, texres, pool, skip_load_image); texres->tin= texres->ta= texres->tr= texres->tg= texres->tb= 0.0f; @@ -1365,7 +1365,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const if (ima) { /* hack for icon render */ - if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima)) + if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) return retval; ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c index b5b75f47193..a0fcc7cdcd2 100644 --- a/source/blender/render/intern/source/initrender.c +++ b/source/blender/render/intern/source/initrender.c @@ -157,7 +157,7 @@ float RE_filter_value(int type, float x) { const float two_gaussfac2 = 2.0f * gaussfac * gaussfac; x *= 3.0f * gaussfac; - return 1.0f / sqrtf(M_PI * two_gaussfac2) * expf(-x*x / two_gaussfac2); + return 1.0f / sqrtf((float)M_PI * two_gaussfac2) * expf(-x*x / two_gaussfac2); } case R_FILTER_MITCH: diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 0c17d2dd15d..7d76a916d66 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -256,7 +256,7 @@ Render *RE_GetRender(const char *name) /* search for existing renders */ for (re = RenderGlobal.renderlist.first; re; re = re->next) - if (strncmp(re->name, name, RE_MAXNAME) == 0) + if (STREQLEN(re->name, name, RE_MAXNAME)) break; return re; @@ -2350,7 +2350,9 @@ static void renderresult_stampinfo(Render *re) /* this is the basic trick to get the displayed float or char rect from render result */ RE_AcquireResultImage(re, &rres); - BKE_stamp_buf(re->scene, RE_GetCamera(re), (unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4); + BKE_image_stamp_buf( + re->scene, RE_GetCamera(re), + (unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4); RE_ReleaseResultImage(re); } @@ -2804,8 +2806,9 @@ void RE_BlenderFrame(Render *re, Main *bmain, Scene *scene, SceneRenderLayer *sr } else { char name[FILE_MAX]; - BKE_makepicstring(name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); + BKE_image_path_from_imformat( + name, scene->r.pic, bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); /* reports only used for Movie */ do_write_image_or_movie(re, bmain, scene, NULL, name); @@ -2890,8 +2893,9 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie if (name_override) BLI_strncpy(name, name_override, sizeof(name)); else - BKE_makepicstring(name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); + BKE_image_path_from_imformat( + name, scene->r.pic, bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); if (re->r.im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { if (re->result) { @@ -2919,7 +2923,7 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie if (BLI_testextensie(name, ".exr")) name[strlen(name) - 4] = 0; - BKE_add_image_extension(name, &imf); + BKE_image_path_ensure_ext_from_imformat(name, &imf); ibuf->planes = 24; IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, @@ -3052,8 +3056,9 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri /* Touch/NoOverwrite options are only valid for image's */ if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) { if (scene->r.mode & (R_NO_OVERWRITE | R_TOUCH)) - BKE_makepicstring(name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); + BKE_image_path_from_imformat( + name, scene->r.pic, bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); if (scene->r.mode & R_NO_OVERWRITE && BLI_exists(name)) { printf("skipping existing frame \"%s\"\n", name); diff --git a/source/blender/render/intern/source/pixelshading.c b/source/blender/render/intern/source/pixelshading.c index ac4a7dbdccd..104cde0a0b5 100644 --- a/source/blender/render/intern/source/pixelshading.c +++ b/source/blender/render/intern/source/pixelshading.c @@ -155,7 +155,7 @@ static void render_lighting_halo(HaloRen *har, float col_r[3]) x = max_ff(fabsf(lvrot[0]/lvrot[2]), fabsf(lvrot[1]/lvrot[2])); /* 1.0/(sqrt(1+x*x)) is equivalent to cos(atan(x)) */ - inpr = 1.0 / (sqrtf(1.0f + x * x)); + inpr = 1.0f / (sqrtf(1.0f + x * x)); } else inpr= 0.0; } @@ -201,7 +201,7 @@ static void render_lighting_halo(HaloRen *har, float col_r[3]) /* dot product and reflectivity*/ - inp = 1.0 - fabsf(dot_v3v3(vn, lv)); + inp = 1.0f - fabsf(dot_v3v3(vn, lv)); /* inp= cos(0.5*M_PI-acos(inp)); */ @@ -361,7 +361,7 @@ int shadeHaloFloat(HaloRen *har, float col[4], int zz, else dist= dist/har->radsq; if (har->type & HA_FLARECIRC) { - dist = 0.5 + fabsf(dist - 0.5f); + dist = 0.5f + fabsf(dist - 0.5f); } if (har->hard>=30) { diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c index ff23e3788ab..1f4726809de 100644 --- a/source/blender/render/intern/source/pointdensity.c +++ b/source/blender/render/intern/source/pointdensity.c @@ -160,7 +160,7 @@ static void pointdensity_cache_psys(Render *re, PointDensity *pd, Object *ob, Pa else continue; - cache += cache->steps; /* use endpoint */ + cache += cache->segments; /* use endpoint */ copy_v3_v3(state.co, cache->co); zero_v3(state.vel); diff --git a/source/blender/render/intern/source/rayshade.c b/source/blender/render/intern/source/rayshade.c index 3575b78f95f..065cdeab61c 100644 --- a/source/blender/render/intern/source/rayshade.c +++ b/source/blender/render/intern/source/rayshade.c @@ -1273,7 +1273,7 @@ static float get_avg_speed(ShadeInput *shi) post_x = (shi->winspeed[2] == PASS_VECTOR_MAX)?0.0f:shi->winspeed[2]; post_y = (shi->winspeed[3] == PASS_VECTOR_MAX)?0.0f:shi->winspeed[3]; - speedavg = (sqrtf(pre_x * pre_x + pre_y * pre_y) + sqrtf(post_x * post_x + post_y * post_y)) / 2.0; + speedavg = (sqrtf(pre_x * pre_x + pre_y * pre_y) + sqrtf(post_x * post_x + post_y * post_y)) / 2.0f; return speedavg; } diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index de87fb200ae..6486844bacf 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -309,97 +309,97 @@ static const char *get_pass_name(int passtype, int channel) static int passtype_from_name(const char *str) { - if (strcmp(str, "Combined") == 0) + if (STREQ(str, "Combined")) return SCE_PASS_COMBINED; - if (strcmp(str, "Depth") == 0) + if (STREQ(str, "Depth")) return SCE_PASS_Z; - if (strcmp(str, "Vector") == 0) + if (STREQ(str, "Vector")) return SCE_PASS_VECTOR; - if (strcmp(str, "Normal") == 0) + if (STREQ(str, "Normal")) return SCE_PASS_NORMAL; - if (strcmp(str, "UV") == 0) + if (STREQ(str, "UV")) return SCE_PASS_UV; - if (strcmp(str, "Color") == 0) + if (STREQ(str, "Color")) return SCE_PASS_RGBA; - if (strcmp(str, "Emit") == 0) + if (STREQ(str, "Emit")) return SCE_PASS_EMIT; - if (strcmp(str, "Diffuse") == 0) + if (STREQ(str, "Diffuse")) return SCE_PASS_DIFFUSE; - if (strcmp(str, "Spec") == 0) + if (STREQ(str, "Spec")) return SCE_PASS_SPEC; - if (strcmp(str, "Shadow") == 0) + if (STREQ(str, "Shadow")) return SCE_PASS_SHADOW; - if (strcmp(str, "AO") == 0) + if (STREQ(str, "AO")) return SCE_PASS_AO; - if (strcmp(str, "Env") == 0) + if (STREQ(str, "Env")) return SCE_PASS_ENVIRONMENT; - if (strcmp(str, "Indirect") == 0) + if (STREQ(str, "Indirect")) return SCE_PASS_INDIRECT; - if (strcmp(str, "Reflect") == 0) + if (STREQ(str, "Reflect")) return SCE_PASS_REFLECT; - if (strcmp(str, "Refract") == 0) + if (STREQ(str, "Refract")) return SCE_PASS_REFRACT; - if (strcmp(str, "IndexOB") == 0) + if (STREQ(str, "IndexOB")) return SCE_PASS_INDEXOB; - if (strcmp(str, "IndexMA") == 0) + if (STREQ(str, "IndexMA")) return SCE_PASS_INDEXMA; - if (strcmp(str, "Mist") == 0) + if (STREQ(str, "Mist")) return SCE_PASS_MIST; - if (strcmp(str, "RayHits") == 0) + if (STREQ(str, "RayHits")) return SCE_PASS_RAYHITS; - if (strcmp(str, "DiffDir") == 0) + if (STREQ(str, "DiffDir")) return SCE_PASS_DIFFUSE_DIRECT; - if (strcmp(str, "DiffInd") == 0) + if (STREQ(str, "DiffInd")) return SCE_PASS_DIFFUSE_INDIRECT; - if (strcmp(str, "DiffCol") == 0) + if (STREQ(str, "DiffCol")) return SCE_PASS_DIFFUSE_COLOR; - if (strcmp(str, "GlossDir") == 0) + if (STREQ(str, "GlossDir")) return SCE_PASS_GLOSSY_DIRECT; - if (strcmp(str, "GlossInd") == 0) + if (STREQ(str, "GlossInd")) return SCE_PASS_GLOSSY_INDIRECT; - if (strcmp(str, "GlossCol") == 0) + if (STREQ(str, "GlossCol")) return SCE_PASS_GLOSSY_COLOR; - if (strcmp(str, "TransDir") == 0) + if (STREQ(str, "TransDir")) return SCE_PASS_TRANSM_DIRECT; - if (strcmp(str, "TransInd") == 0) + if (STREQ(str, "TransInd")) return SCE_PASS_TRANSM_INDIRECT; - if (strcmp(str, "TransCol") == 0) + if (STREQ(str, "TransCol")) return SCE_PASS_TRANSM_COLOR; - if (strcmp(str, "SubsurfaceDir") == 0) + if (STREQ(str, "SubsurfaceDir")) return SCE_PASS_SUBSURFACE_DIRECT; - if (strcmp(str, "SubsurfaceInd") == 0) + if (STREQ(str, "SubsurfaceInd")) return SCE_PASS_SUBSURFACE_INDIRECT; - if (strcmp(str, "SubsurfaceCol") == 0) + if (STREQ(str, "SubsurfaceCol")) return SCE_PASS_SUBSURFACE_COLOR; return 0; @@ -509,7 +509,7 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf for (nr = 0, srl = re->r.layers.first; srl; srl = srl->next, nr++) { if (layername && layername[0]) - if (strcmp(srl->name, layername) != 0) + if (!STREQ(srl->name, layername)) continue; if (re->r.scemode & R_SINGLE_LAYER) { @@ -611,7 +611,7 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR); #ifdef WITH_CYCLES_DEBUG - if(BKE_scene_use_new_shading_nodes(re->scene)) { + if (BKE_scene_use_new_shading_nodes(re->scene)) { render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG, RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS); } @@ -1074,6 +1074,9 @@ void render_result_exr_file_path(Scene *scene, const char *layname, int sample, BLI_snprintf(name, sizeof(name), "%s_%s_%s%d.exr", fi, scene->id.name + 2, layname, sample); } + /* Make name safe for paths, see T43275. */ + BLI_filename_make_safe(name); + BLI_make_file_string("/", filepath, BKE_tempdir_session(), name); } diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index b1e00c4a451..17039bca63a 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -220,10 +220,10 @@ static int blend(Tex *tex, const float texvec[3], TexResult *texres) texres->tin= (2.0f+x+y)/4.0f; } else if (tex->stype==TEX_RAD) { /* radial */ - texres->tin = (atan2f(y, x) / (2 * M_PI) + 0.5f); + texres->tin = (atan2f(y, x) / (float)(2 * M_PI) + 0.5f); } else { /* sphere TEX_SPHERE */ - texres->tin = 1.0 - sqrtf(x * x + y * y + texvec[2] * texvec[2]); + texres->tin = 1.0f - sqrtf(x * x + y * y + texvec[2] * texvec[2]); if (texres->tin<0.0f) texres->tin= 0.0f; if (tex->stype==TEX_HALO) texres->tin*= texres->tin; /* halo */ } @@ -274,7 +274,7 @@ static int clouds(Tex *tex, const float texvec[3], TexResult *texres) /* creates a sine wave */ static float tex_sin(float a) { - a = 0.5 + 0.5 * sinf(a); + a = 0.5f + 0.5f * sinf(a); return a; } @@ -1103,7 +1103,7 @@ static void do_2d_mapping(MTex *mtex, float texvec[3], VlakRen *vlr, const float /* ************************************** */ -static int multitex(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, const short thread, short which_output, struct ImagePool *pool) +static int multitex(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, const short thread, short which_output, struct ImagePool *pool, const bool skip_load_image) { float tmpvec[3]; int retval = 0; /* return value, int:0, col:1, nor:2, everything:3 */ @@ -1141,14 +1141,14 @@ static int multitex(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int o retval = texnoise(tex, texres, thread); break; case TEX_IMAGE: - if (osatex) retval = imagewraposa(tex, tex->ima, NULL, texvec, dxt, dyt, texres, pool); - else retval = imagewrap(tex, tex->ima, NULL, texvec, texres, pool); + if (osatex) retval = imagewraposa(tex, tex->ima, NULL, texvec, dxt, dyt, texres, pool, skip_load_image); + else retval = imagewrap(tex, tex->ima, NULL, texvec, texres, pool, skip_load_image); if (tex->ima) { BKE_image_tag_time(tex->ima); } break; case TEX_ENVMAP: - retval = envmaptex(tex, texvec, dxt, dyt, osatex, texres, pool); + retval = envmaptex(tex, texvec, dxt, dyt, osatex, texres, pool, skip_load_image); break; case TEX_MUSGRAVE: /* newnoise: musgrave types */ @@ -1220,7 +1220,7 @@ static int multitex(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int o static int multitex_nodes_intern(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, const short thread, short which_output, ShadeInput *shi, MTex *mtex, struct ImagePool *pool, - bool scene_color_manage) + bool scene_color_manage, const bool skip_load_image) { if (tex==NULL) { memset(texres, 0, sizeof(TexResult)); @@ -1236,7 +1236,7 @@ static int multitex_nodes_intern(Tex *tex, float texvec[3], float dxt[3], float if (mtex) { /* we have mtex, use it for 2d mapping images only */ do_2d_mapping(mtex, texvec, shi->vlr, shi->facenor, dxt, dyt); - rgbnor = multitex(tex, texvec, dxt, dyt, osatex, texres, thread, which_output, pool); + rgbnor = multitex(tex, texvec, dxt, dyt, osatex, texres, thread, which_output, pool, skip_load_image); if (mtex->mapto & (MAP_COL+MAP_COLSPEC+MAP_COLMIR)) { ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); @@ -1269,7 +1269,7 @@ static int multitex_nodes_intern(Tex *tex, float texvec[3], float dxt[3], float } do_2d_mapping(&localmtex, texvec_l, NULL, NULL, dxt_l, dyt_l); - rgbnor = multitex(tex, texvec_l, dxt_l, dyt_l, osatex, texres, thread, which_output, pool); + rgbnor = multitex(tex, texvec_l, dxt_l, dyt_l, osatex, texres, thread, which_output, pool, skip_load_image); { ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); @@ -1285,7 +1285,7 @@ static int multitex_nodes_intern(Tex *tex, float texvec[3], float dxt[3], float return rgbnor; } else { - return multitex(tex, texvec, dxt, dyt, osatex, texres, thread, which_output, pool); + return multitex(tex, texvec, dxt, dyt, osatex, texres, thread, which_output, pool, skip_load_image); } } @@ -1296,11 +1296,12 @@ int multitex_nodes(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int os const short thread, short which_output, ShadeInput *shi, MTex *mtex, struct ImagePool *pool) { return multitex_nodes_intern(tex, texvec, dxt, dyt, osatex, texres, - thread, which_output, shi, mtex, pool, R.scene_color_manage); + thread, which_output, shi, mtex, pool, R.scene_color_manage, + (R.r.scemode & R_NO_IMAGE_LOAD) != 0); } /* this is called for surface shading */ -static int multitex_mtex(ShadeInput *shi, MTex *mtex, float texvec[3], float dxt[3], float dyt[3], TexResult *texres, struct ImagePool *pool) +static int multitex_mtex(ShadeInput *shi, MTex *mtex, float texvec[3], float dxt[3], float dyt[3], TexResult *texres, struct ImagePool *pool, const bool skip_load_image) { Tex *tex = mtex->tex; @@ -1311,7 +1312,7 @@ static int multitex_mtex(ShadeInput *shi, MTex *mtex, float texvec[3], float dxt tex, mtex->which_output, R.r.cfra, (R.r.scemode & R_TEXNODE_PREVIEW) != 0, shi, mtex); } else { - return multitex(mtex->tex, texvec, dxt, dyt, shi->osatex, texres, shi->thread, mtex->which_output, pool); + return multitex(mtex->tex, texvec, dxt, dyt, shi->osatex, texres, shi->thread, mtex->which_output, pool, skip_load_image); } } @@ -1320,21 +1321,21 @@ static int multitex_mtex(ShadeInput *shi, MTex *mtex, float texvec[3], float dxt * * Use it for stuff which is out of render pipeline. */ -int multitex_ext(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, struct ImagePool *pool, bool scene_color_manage) +int multitex_ext(Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, struct ImagePool *pool, bool scene_color_manage, const bool skip_load_image) { - return multitex_nodes_intern(tex, texvec, dxt, dyt, osatex, texres, 0, 0, NULL, NULL, pool, scene_color_manage); + return multitex_nodes_intern(tex, texvec, dxt, dyt, osatex, texres, 0, 0, NULL, NULL, pool, scene_color_manage, skip_load_image); } /* extern-tex doesn't support nodes (ntreeBeginExec() can't be called when rendering is going on)\ * * Use it for stuff which is out of render pipeline. */ -int multitex_ext_safe(Tex *tex, float texvec[3], TexResult *texres, struct ImagePool *pool, bool scene_color_manage) +int multitex_ext_safe(Tex *tex, float texvec[3], TexResult *texres, struct ImagePool *pool, bool scene_color_manage, const bool skip_load_image) { int use_nodes= tex->use_nodes, retval; tex->use_nodes = false; - retval= multitex_nodes_intern(tex, texvec, NULL, NULL, 0, texres, 0, 0, NULL, NULL, pool, scene_color_manage); + retval= multitex_nodes_intern(tex, texvec, NULL, NULL, 0, texres, 0, 0, NULL, NULL, pool, scene_color_manage, skip_load_image); tex->use_nodes= use_nodes; return retval; @@ -1718,7 +1719,7 @@ static void compatible_bump_uv_derivs(CompatibleBump *compat_bump, ShadeInput *s static int compatible_bump_compute(CompatibleBump *compat_bump, ShadeInput *shi, MTex *mtex, Tex *tex, TexResult *texres, float Tnor, const float co[3], const float dx[3], const float dy[3], float texvec[3], float dxt[3], float dyt[3], - struct ImagePool *pool) + struct ImagePool *pool, const bool skip_load_image) { TexResult ttexr = {0, 0, 0, 0, 0, texres->talpha, NULL}; /* temp TexResult */ float tco[3], texv[3], cd, ud, vd, du, dv, idu, idv; @@ -1771,7 +1772,7 @@ static int compatible_bump_compute(CompatibleBump *compat_bump, ShadeInput *shi, /* center, main return value */ texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt); - rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, texres, pool); + rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, texres, pool, skip_load_image); cd = fromrgb ? (texres->tr + texres->tg + texres->tb) / 3.0f : texres->tin; if (mtex->texco == TEXCO_UV) { @@ -1785,7 +1786,7 @@ static int compatible_bump_compute(CompatibleBump *compat_bump, ShadeInput *shi, tco[1] = co[1] + compat_bump->dvdnu*du; tco[2] = 0.f; texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt); - multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr, pool, skip_load_image); ud = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb) / 3.0f : ttexr.tin)); /* +v val */ @@ -1793,7 +1794,7 @@ static int compatible_bump_compute(CompatibleBump *compat_bump, ShadeInput *shi, tco[1] = co[1] + compat_bump->dvdnv*du; tco[2] = 0.f; texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt); - multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr, pool, skip_load_image); vd = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb) / 3.0f : ttexr.tin)); } else { @@ -1827,7 +1828,7 @@ static int compatible_bump_compute(CompatibleBump *compat_bump, ShadeInput *shi, tco[1] = co[1] + tu[1]*du; tco[2] = co[2] + tu[2]*du; texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt); - multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr, pool, skip_load_image); ud = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb) / 3.0f : ttexr.tin)); /* +v val */ @@ -1835,7 +1836,7 @@ static int compatible_bump_compute(CompatibleBump *compat_bump, ShadeInput *shi, tco[1] = co[1] + tv[1]*dv; tco[2] = co[2] + tv[2]*dv; texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt); - multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr, pool, skip_load_image); vd = idv*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb) / 3.0f : ttexr.tin)); } @@ -1877,7 +1878,8 @@ static void ntap_bump_init(NTapBump *ntap_bump) static int ntap_bump_compute(NTapBump *ntap_bump, ShadeInput *shi, MTex *mtex, Tex *tex, TexResult *texres, float Tnor, const float co[3], const float dx[3], const float dy[3], - float texvec[3], float dxt[3], float dyt[3], struct ImagePool *pool) + float texvec[3], float dxt[3], float dyt[3], struct ImagePool *pool, + const bool skip_load_image) { TexResult ttexr = {0, 0, 0, 0, 0, texres->talpha, NULL}; /* temp TexResult */ @@ -1937,7 +1939,7 @@ static int ntap_bump_compute(NTapBump *ntap_bump, ShadeInput *shi, MTex *mtex, T float dBdu, dBdv, auto_bump = 1.0f; float s = 1; /* negate this if flipped texture coordinate */ texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt); - rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, texres, pool); + rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, texres, pool, skip_load_image); if (shi->obr->ob->derivedFinal) { auto_bump = shi->obr->ob->derivedFinal->auto_bump_scale; @@ -1979,14 +1981,14 @@ static int ntap_bump_compute(NTapBump *ntap_bump, ShadeInput *shi, MTex *mtex, T } /* use texres for the center sample, set rgbnor */ - rgbnor = multitex_mtex(shi, mtex, STll, dxt, dyt, texres, pool); + rgbnor = multitex_mtex(shi, mtex, STll, dxt, dyt, texres, pool, skip_load_image); Hll = (fromrgb) ? rgb_to_grayscale(&texres->tr) : texres->tin; /* use ttexr for the other 2 taps */ - multitex_mtex(shi, mtex, STlr, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, STlr, dxt, dyt, &ttexr, pool, skip_load_image); Hlr = (fromrgb) ? rgb_to_grayscale(&ttexr.tr) : ttexr.tin; - multitex_mtex(shi, mtex, STul, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, STul, dxt, dyt, &ttexr, pool, skip_load_image); Hul = (fromrgb) ? rgb_to_grayscale(&ttexr.tr) : ttexr.tin; dHdx = Hscale*(Hlr - Hll); @@ -2017,17 +2019,17 @@ static int ntap_bump_compute(NTapBump *ntap_bump, ShadeInput *shi, MTex *mtex, T } /* use texres for the center sample, set rgbnor */ - rgbnor = multitex_mtex(shi, mtex, STc, dxt, dyt, texres, pool); + rgbnor = multitex_mtex(shi, mtex, STc, dxt, dyt, texres, pool, skip_load_image); /* Hc = (fromrgb) ? rgb_to_grayscale(&texres->tr) : texres->tin; */ /* UNUSED */ /* use ttexr for the other taps */ - multitex_mtex(shi, mtex, STl, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, STl, dxt, dyt, &ttexr, pool, skip_load_image); Hl = (fromrgb) ? rgb_to_grayscale(&ttexr.tr) : ttexr.tin; - multitex_mtex(shi, mtex, STr, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, STr, dxt, dyt, &ttexr, pool, skip_load_image); Hr = (fromrgb) ? rgb_to_grayscale(&ttexr.tr) : ttexr.tin; - multitex_mtex(shi, mtex, STd, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, STd, dxt, dyt, &ttexr, pool, skip_load_image); Hd = (fromrgb) ? rgb_to_grayscale(&ttexr.tr) : ttexr.tin; - multitex_mtex(shi, mtex, STu, dxt, dyt, &ttexr, pool); + multitex_mtex(shi, mtex, STu, dxt, dyt, &ttexr, pool, skip_load_image); Hu = (fromrgb) ? rgb_to_grayscale(&ttexr.tr) : ttexr.tin; dHdx = Hscale*(Hr - Hl); @@ -2137,6 +2139,7 @@ static int ntap_bump_compute(NTapBump *ntap_bump, ShadeInput *shi, MTex *mtex, T void do_material_tex(ShadeInput *shi, Render *re) { + const bool skip_load_image = (R.r.scemode & R_NO_IMAGE_LOAD) != 0; CompatibleBump compat_bump; NTapBump ntap_bump; MTex *mtex; @@ -2249,7 +2252,7 @@ void do_material_tex(ShadeInput *shi, Render *re) if (mtex->uvname[0] != 0) { for (i = 0; i < shi->totuv; i++) { - if (strcmp(shi->uv[i].name, mtex->uvname)==0) { + if (STREQ(shi->uv[i].name, mtex->uvname)) { suv= &shi->uv[i]; break; } @@ -2305,21 +2308,21 @@ void do_material_tex(ShadeInput *shi, Render *re) if (use_compat_bump) { rgbnor = compatible_bump_compute(&compat_bump, shi, mtex, tex, &texres, Tnor*stencilTin, co, dx, dy, texvec, dxt, dyt, - re->pool); + re->pool, skip_load_image); } else if (use_ntap_bump) { rgbnor = ntap_bump_compute(&ntap_bump, shi, mtex, tex, &texres, Tnor*stencilTin, co, dx, dy, texvec, dxt, dyt, - re->pool); + re->pool, skip_load_image); } else { texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt); - rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, &texres, re->pool); + rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, &texres, re->pool, skip_load_image); } } else { texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt); - rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, &texres, re->pool); + rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, &texres, re->pool, skip_load_image); } /* texture output */ @@ -2671,6 +2674,7 @@ void do_material_tex(ShadeInput *shi, Render *re) void do_volume_tex(ShadeInput *shi, const float *xyz, int mapto_flag, float col_r[3], float *val, Render *re) { + const bool skip_load_image = (re->r.scemode & R_NO_IMAGE_LOAD) != 0; MTex *mtex; Tex *tex; TexResult texres= {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL}; @@ -2758,7 +2762,7 @@ void do_volume_tex(ShadeInput *shi, const float *xyz, int mapto_flag, float col_ else texvec[2]= mtex->size[2]*(mtex->ofs[2]); } - rgbnor = multitex(tex, texvec, NULL, NULL, 0, &texres, shi->thread, mtex->which_output, re->pool); /* NULL = dxt/dyt, 0 = shi->osatex - not supported */ + rgbnor = multitex(tex, texvec, NULL, NULL, 0, &texres, shi->thread, mtex->which_output, re->pool, skip_load_image); /* NULL = dxt/dyt, 0 = shi->osatex - not supported */ /* texture output */ @@ -2869,6 +2873,7 @@ void do_volume_tex(ShadeInput *shi, const float *xyz, int mapto_flag, float col_ void do_halo_tex(HaloRen *har, float xn, float yn, float col_r[4]) { + const bool skip_load_image = har->skip_load_image; MTex *mtex; TexResult texres= {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL}; float texvec[3], dxt[3], dyt[3], fact, facm, dx; @@ -2925,7 +2930,7 @@ void do_halo_tex(HaloRen *har, float xn, float yn, float col_r[4]) if (mtex->tex->type==TEX_IMAGE) do_2d_mapping(mtex, texvec, NULL, NULL, dxt, dyt); - rgb = multitex(mtex->tex, texvec, dxt, dyt, osatex, &texres, 0, mtex->which_output, har->pool); + rgb = multitex(mtex->tex, texvec, dxt, dyt, osatex, &texres, 0, mtex->which_output, har->pool, skip_load_image); /* texture output */ if (rgb && (mtex->texflag & MTEX_RGBTOINT)) { @@ -3014,6 +3019,7 @@ void do_halo_tex(HaloRen *har, float xn, float yn, float col_r[4]) /* hor and zen are RGB vectors, blend is 1 float, should all be initialized */ void do_sky_tex(const float rco[3], float lo[3], const float dxyview[2], float hor[3], float zen[3], float *blend, int skyflag, short thread) { + const bool skip_load_image = (R.r.scemode & R_NO_IMAGE_LOAD) != 0; MTex *mtex; Tex *tex; TexResult texres= {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL}; @@ -3130,7 +3136,7 @@ void do_sky_tex(const float rco[3], float lo[3], const float dxyview[2], float h /* texture */ if (tex->type==TEX_IMAGE) do_2d_mapping(mtex, texvec, NULL, NULL, dxt, dyt); - rgb = multitex(mtex->tex, texvec, dxt, dyt, R.osa, &texres, thread, mtex->which_output, R.pool); + rgb = multitex(mtex->tex, texvec, dxt, dyt, R.osa, &texres, thread, mtex->which_output, R.pool, skip_load_image); /* texture output */ if (rgb && (mtex->texflag & MTEX_RGBTOINT)) { @@ -3222,6 +3228,7 @@ void do_sky_tex(const float rco[3], float lo[3], const float dxyview[2], float h void do_lamp_tex(LampRen *la, const float lavec[3], ShadeInput *shi, float col_r[3], int effect) { + const bool skip_load_image = (R.r.scemode & R_NO_IMAGE_LOAD) != 0; Object *ob; MTex *mtex; Tex *tex; @@ -3345,7 +3352,7 @@ void do_lamp_tex(LampRen *la, const float lavec[3], ShadeInput *shi, float col_r do_2d_mapping(mtex, texvec, NULL, NULL, dxt, dyt); } - rgb = multitex(tex, texvec, dxt, dyt, shi->osatex, &texres, shi->thread, mtex->which_output, R.pool); + rgb = multitex(tex, texvec, dxt, dyt, shi->osatex, &texres, shi->thread, mtex->which_output, R.pool, skip_load_image); /* texture output */ if (rgb && (mtex->texflag & MTEX_RGBTOINT)) { @@ -3419,7 +3426,7 @@ void do_lamp_tex(LampRen *la, const float lavec[3], ShadeInput *shi, float col_r /* ------------------------------------------------------------------------- */ -int externtex(MTex *mtex, const float vec[3], float *tin, float *tr, float *tg, float *tb, float *ta, const int thread, struct ImagePool *pool) +int externtex(MTex *mtex, const float vec[3], float *tin, float *tr, float *tg, float *tb, float *ta, const int thread, struct ImagePool *pool, const bool skip_load_image) { Tex *tex; TexResult texr; @@ -3445,7 +3452,7 @@ int externtex(MTex *mtex, const float vec[3], float *tin, float *tr, float *tg, do_2d_mapping(mtex, texvec, NULL, NULL, dxt, dyt); } - rgb = multitex(tex, texvec, dxt, dyt, 0, &texr, thread, mtex->which_output, pool); + rgb = multitex(tex, texvec, dxt, dyt, 0, &texr, thread, mtex->which_output, pool, skip_load_image); if (rgb) { texr.tin = rgb_to_bw(&texr.tr); @@ -3470,6 +3477,7 @@ int externtex(MTex *mtex, const float vec[3], float *tin, float *tr, float *tg, void render_realtime_texture(ShadeInput *shi, Image *ima) { + const bool skip_load_image = (R.r.scemode & R_NO_IMAGE_LOAD) != 0; TexResult texr; static Tex imatex[BLENDER_MAX_THREADS]; /* threadsafe */ static int firsttime= 1; @@ -3510,8 +3518,8 @@ void render_realtime_texture(ShadeInput *shi, Image *ima) texr.nor= NULL; - if (shi->osatex) imagewraposa(tex, ima, NULL, texvec, dx, dy, &texr, R.pool); - else imagewrap(tex, ima, NULL, texvec, &texr, R.pool); + if (shi->osatex) imagewraposa(tex, ima, NULL, texvec, dx, dy, &texr, R.pool, skip_load_image); + else imagewrap(tex, ima, NULL, texvec, &texr, R.pool, skip_load_image); shi->vcol[0]*= texr.tr; shi->vcol[1]*= texr.tg; diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index cafdbe2b488..42c654b2c43 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -954,6 +954,7 @@ HaloRen *RE_inithalo(Render *re, ObjectRen *obr, Material *ma, const float vec[3], const float vec1[3], const float *orco, float hasize, float vectsize, int seed) { + const bool skip_load_image = (re->r.scemode & R_NO_IMAGE_LOAD) != 0; HaloRen *har; MTex *mtex; float tin, tr, tg, tb, ta; @@ -1045,7 +1046,7 @@ HaloRen *RE_inithalo(Render *re, ObjectRen *obr, Material *ma, } } - externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta, 0, re->pool); + externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta, 0, re->pool, skip_load_image); yn= tin*mtex->colfac; //zn= tin*mtex->alphafac; @@ -1065,6 +1066,7 @@ HaloRen *RE_inithalo(Render *re, ObjectRen *obr, Material *ma, } har->pool = re->pool; + har->skip_load_image = (re->r.scemode & R_NO_IMAGE_LOAD) != 0; return har; } @@ -1073,6 +1075,7 @@ HaloRen *RE_inithalo_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Mater const float vec[3], const float vec1[3], const float *orco, const float *uvco, float hasize, float vectsize, int seed, const float pa_co[3]) { + const bool skip_load_image = (re->r.scemode & R_NO_IMAGE_LOAD) != 0; HaloRen *har; MTex *mtex; float tin, tr, tg, tb, ta; @@ -1176,7 +1179,7 @@ HaloRen *RE_inithalo_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Mater copy_v3_v3(texvec, orco); } - hasrgb = externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta, 0, re->pool); + hasrgb = externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta, 0, re->pool, skip_load_image); //yn= tin*mtex->colfac; //zn= tin*mtex->alphafac; @@ -1220,6 +1223,7 @@ HaloRen *RE_inithalo_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Mater } har->pool = re->pool; + har->skip_load_image = (re->r.scemode & R_NO_IMAGE_LOAD) != 0; return har; } diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c index 93e8c6a275a..63a97a04b39 100644 --- a/source/blender/render/intern/source/shadeoutput.c +++ b/source/blender/render/intern/source/shadeoutput.c @@ -2064,7 +2064,7 @@ float RE_lamp_get_data(ShadeInput *shi, Object *lamp_obj, float col[4], float lv if (R.r.scemode & R_BUTS_PREVIEW) { for (go = R.lights.first; go; go = go->next) { /* "Lamp.002" is main key light of material preview */ - if (strcmp(go->ob->id.name + 2, "Lamp.002") == 0) + if (STREQ(go->ob->id.name + 2, "Lamp.002")) return lamp_get_data_internal(shi, go, col, lv, dist, shadow); } return 0.0f; diff --git a/source/blender/render/intern/source/sss.c b/source/blender/render/intern/source/sss.c index 8ea3a753283..890b0445ecc 100644 --- a/source/blender/render/intern/source/sss.c +++ b/source/blender/render/intern/source/sss.c @@ -1031,14 +1031,12 @@ void make_sss_tree(Render *re) void free_sss(Render *re) { if (re->sss_hash) { - GHashIterator *it= BLI_ghashIterator_new(re->sss_hash); + GHashIterator gh_iter; - while (!BLI_ghashIterator_done(it)) { - sss_free_tree(BLI_ghashIterator_getValue(it)); - BLI_ghashIterator_step(it); + GHASH_ITER (gh_iter, re->sss_hash) { + sss_free_tree(BLI_ghashIterator_getValue(&gh_iter)); } - BLI_ghashIterator_free(it); BLI_ghash_free(re->sss_hash, NULL, NULL); re->sss_hash= NULL; } diff --git a/source/blender/render/intern/source/sunsky.c b/source/blender/render/intern/source/sunsky.c index 6078989f9f6..d4e53eb7305 100644 --- a/source/blender/render/intern/source/sunsky.c +++ b/source/blender/render/intern/source/sunsky.c @@ -254,14 +254,14 @@ void GetSkyXYZRadiance(struct SunSky *sunsky, float theta, float phi, float colo float hfade = 1, nfade = 1; - if (theta > (0.5f * (float)M_PI)) { + if (theta > (float)M_PI_2) { hfade = 1.0f - (theta * (float)M_1_PI - 0.5f) * 2.0f; hfade = hfade * hfade * (3.0f - 2.0f * hfade); - theta = 0.5 * M_PI; + theta = M_PI_2; } - if (sunsky->theta > (0.5f * (float)M_PI)) { - if (theta <= 0.5f * (float)M_PI) { + if (sunsky->theta > (float)M_PI_2) { + if (theta <= (float)M_PI_2) { nfade = 1.0f - (0.5f - theta * (float)M_1_PI) * 2.0f; nfade *= 1.0f - (sunsky->theta * (float)M_1_PI - 0.5f) * 2.0f; nfade = nfade * nfade * (3.0f - 2.0f * nfade); diff --git a/source/blender/render/intern/source/voxeldata.c b/source/blender/render/intern/source/voxeldata.c index 69a1317cf7d..40a26af5849 100644 --- a/source/blender/render/intern/source/voxeldata.c +++ b/source/blender/render/intern/source/voxeldata.c @@ -51,16 +51,19 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "BKE_cloth.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "smoke_API.h" +#include "BPH_mass_spring.h" #include "DNA_texture_types.h" #include "DNA_object_force.h" #include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "DNA_pointcache_types.h" #include "DNA_modifier_types.h" #include "DNA_smoke_types.h" @@ -365,6 +368,27 @@ static void init_frame_smoke(VoxelData *vd, int cfra) #endif } +static void init_frame_hair(VoxelData *vd, int UNUSED(cfra)) +{ + Object *ob; + ModifierData *md; + bool found = false; + + vd->dataset = NULL; + if (vd->object == NULL) return; + ob = vd->object; + + if ((md = (ModifierData *)modifiers_findByType(ob, eModifierType_ParticleSystem))) { + ParticleSystemModifierData *pmd = (ParticleSystemModifierData *)md; + + if (pmd->psys && pmd->psys->clmd) { + found |= BPH_cloth_solver_get_texture_data(ob, pmd->psys->clmd, vd); + } + } + + vd->ok = found; +} + void cache_voxeldata(Tex *tex, int scene_frame) { VoxelData *vd = tex->vd; @@ -398,6 +422,9 @@ void cache_voxeldata(Tex *tex, int scene_frame) case TEX_VD_SMOKE: init_frame_smoke(vd, scene_frame); return; + case TEX_VD_HAIR: + init_frame_hair(vd, scene_frame); + return; case TEX_VD_BLENDERVOXEL: BLI_path_abs(path, G.main->name); if (!BLI_exists(path)) return; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index e64b1152252..fc1bc487ca9 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -239,6 +239,7 @@ typedef struct wmNotifier { #define NC_MASK (21<<24) #define NC_GPENCIL (22<<24) #define NC_LINESTYLE (23<<24) +#define NC_CAMERA (24<<24) /* data type, 256 entries is enough, it can overlap */ #define NOTE_DATA 0x00FF0000 @@ -297,6 +298,7 @@ typedef struct wmNotifier { #define ND_POINTCACHE (28<<16) #define ND_PARENT (29<<16) #define ND_LOD (30<<16) +#define ND_DRAW_RENDER_VIEWPORT (31<<16) /* for camera & sequencer viewport update, also /w NC_SCENE */ /* NC_MATERIAL Material */ #define ND_SHADING (30<<16) diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index a6a3c6b0a28..34069e0b873 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -250,15 +250,14 @@ void WM_uilisttype_init(void) void WM_uilisttype_free(void) { - GHashIterator *iter = BLI_ghashIterator_new(uilisttypes_hash); + GHashIterator gh_iter; - for (; !BLI_ghashIterator_done(iter); BLI_ghashIterator_step(iter)) { - uiListType *ult = BLI_ghashIterator_getValue(iter); + GHASH_ITER (gh_iter, uilisttypes_hash) { + uiListType *ult = BLI_ghashIterator_getValue(&gh_iter); if (ult->ext.free) { ult->ext.free(ult->ext.data); } } - BLI_ghashIterator_free(iter); BLI_ghash_free(uilisttypes_hash, NULL, MEM_freeN); uilisttypes_hash = NULL; @@ -309,15 +308,14 @@ void WM_menutype_init(void) void WM_menutype_free(void) { - GHashIterator *iter = BLI_ghashIterator_new(menutypes_hash); + GHashIterator gh_iter; - for (; !BLI_ghashIterator_done(iter); BLI_ghashIterator_step(iter)) { - MenuType *mt = BLI_ghashIterator_getValue(iter); + GHASH_ITER (gh_iter, menutypes_hash) { + MenuType *mt = BLI_ghashIterator_getValue(&gh_iter); if (mt->ext.free) { mt->ext.free(mt->ext.data); } } - BLI_ghashIterator_free(iter); BLI_ghash_free(menutypes_hash, NULL, MEM_freeN); menutypes_hash = NULL; diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 68592b44845..cdc3c9eaaff 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -81,7 +81,7 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid) for (dm = dropboxes.first; dm; dm = dm->next) if (dm->spaceid == spaceid && dm->regionid == regionid) - if (0 == strncmp(idname, dm->idname, KMAP_MAX_NAME)) + if (STREQLEN(idname, dm->idname, KMAP_MAX_NAME)) return &dm->dropboxes; dm = MEM_callocN(sizeof(struct wmDropBoxMap), "dropmap list"); @@ -267,16 +267,11 @@ void wm_drags_check_ops(bContext *C, wmEvent *event) static void wm_drop_operator_draw(const char *name, int x, int y) { - int width = UI_fontstyle_string_width(name); - int padding = 4 * UI_DPI_FAC; - - glColor4ub(0, 0, 0, 50); - - UI_draw_roundbox_corner_set(UI_CNR_ALL | UI_RB_ALPHA); - UI_draw_roundbox(x, y, x + width + 2 * padding, y + 4 * padding, padding); - - glColor4ub(255, 255, 255, 255); - UI_draw_string(x + padding, y + padding, name); + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + const unsigned char fg[4] = {255, 255, 255, 255}; + const unsigned char bg[4] = {0, 0, 0, 50}; + + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg); } static const char *wm_drag_name(wmDrag *drag) @@ -284,13 +279,12 @@ static const char *wm_drag_name(wmDrag *drag) switch (drag->type) { case WM_DRAG_ID: { - ID *id = (ID *)drag->poin; + ID *id = drag->poin; return id->name + 2; } case WM_DRAG_PATH: - return drag->path; case WM_DRAG_NAME: - return (char *)drag->path; + return drag->path; } return ""; } @@ -311,6 +305,7 @@ static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2) /* if rect set, do not draw */ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) { + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; wmWindowManager *wm = CTX_wm_manager(C); wmDrag *drag; const int winsize_y = WM_window_pixels_y(win); @@ -326,7 +321,7 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) /* XXX todo, multiline drag draws... but maybe not, more types mixed wont work well */ glEnable(GL_BLEND); for (drag = wm->drags.first; drag; drag = drag->next) { - int iconsize = 16 * UI_DPI_FAC; /* assumed to be 16 pixels */ + int iconsize = UI_DPI_ICON_SIZE; int padding = 4 * UI_DPI_FAC; /* image or icon */ @@ -362,12 +357,12 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) } if (rect) { - int w = UI_fontstyle_string_width(wm_drag_name(drag)); + int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag)); drag_rect_minmax(rect, x, y, x + w, y + iconsize); } else { glColor4ub(255, 255, 255, 255); - UI_draw_string(x, y, wm_drag_name(drag)); + UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag)); } /* operator name with roundbox */ @@ -383,14 +378,16 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) else { x = cursorx - 2 * padding; - if (cursory + iconsize + iconsize < winsize_y) - y = cursory + iconsize; - else - y = cursory - iconsize - 2 * UI_DPI_FAC; + if (cursory + iconsize + iconsize < winsize_y) { + y = (cursory + iconsize) + padding; + } + else { + y = (cursory - iconsize) - padding; + } } if (rect) { - int w = UI_fontstyle_string_width(wm_drag_name(drag)); + int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag)); drag_rect_minmax(rect, x, y, x + w, y + iconsize); } else diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index d5c88ff156c..4b8af999205 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -729,7 +729,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons /* XXX Disabled the repeat check to address part 2 of #31840. * Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason * why this was needed, but worth to note it in case something turns bad. (mont29) */ - if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)/* && repeat == 0 */) + if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */) wm_operator_reports(C, op, retval, false); if (retval & OPERATOR_FINISHED) { @@ -870,7 +870,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, break; /* skip invalid properties */ - if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0) { + if (STREQ(RNA_property_identifier(prop), otmacro->idname)) { wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0); PointerRNA someptr = RNA_property_pointer_get(properties, prop); wmOperator *opm = wm_operator_create(wm, otm, &someptr, NULL); @@ -1662,7 +1662,6 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand int action = WM_HANDLER_CONTINUE; switch (val) { - case EVT_FILESELECT_OPEN: case EVT_FILESELECT_FULL_OPEN: { ScrArea *sa; @@ -1676,9 +1675,11 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand else { sa = handler->op_area; } - - if (val == EVT_FILESELECT_OPEN) { + + /* we already had a fullscreen here -> mark new space as a stacked fullscreen */ + if (sa->full) { ED_area_newspace(C, sa, SPACE_FILE); /* 'sa' is modified in-place */ + sa->flag |= AREA_FLAG_STACKED_FULLSCREEN; } else { sa = ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */ @@ -1702,15 +1703,11 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand case EVT_FILESELECT_CANCEL: case EVT_FILESELECT_EXTERNAL_CANCEL: { - /* XXX validate area and region? */ - bScreen *screen = CTX_wm_screen(C); - /* remlink now, for load file case before removing*/ BLI_remlink(handlers, handler); if (val != EVT_FILESELECT_EXTERNAL_CANCEL) { - ScrArea *sa = CTX_wm_area(C); - ED_screen_retore_temp_type(C, sa, screen != handler->filescreen); + ED_screen_full_prevspace(C, CTX_wm_area(C)); } wm_handler_op_context(C, handler); @@ -2459,7 +2456,6 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op) wmEventHandler *handler, *handlernext; wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); - int full = 1; // XXX preset? /* only allow 1 file selector open per window */ for (handler = win->modalhandlers.first; handler; handler = handlernext) { @@ -2494,7 +2490,6 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op) handler->op = op; handler->op_area = CTX_wm_area(C); handler->op_region = CTX_wm_region(C); - handler->filescreen = CTX_wm_screen(C); BLI_addhead(&win->modalhandlers, handler); @@ -2504,7 +2499,7 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op) op->type->check(C, op); /* ignore return value */ } - WM_event_fileselect_event(wm, op, full ? EVT_FILESELECT_FULL_OPEN : EVT_FILESELECT_OPEN); + WM_event_fileselect_event(wm, op, EVT_FILESELECT_FULL_OPEN); } #if 0 diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index c1c31f6795d..f5a7ad164d6 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -355,7 +355,7 @@ static int wm_read_exotic(Scene *UNUSED(scene), const char *name) else { len = gzread(gzfile, header, sizeof(header)); gzclose(gzfile); - if (len == sizeof(header) && strncmp(header, "BLENDER", 7) == 0) { + if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) { retval = BKE_READ_EXOTIC_OK_BLEND; } else { @@ -535,11 +535,13 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) } -/* called on startup, (context entirely filled with NULLs) */ -/* or called for 'New File' */ -/* both startup.blend and userpref.blend are checked */ -/* the optional paramater custom_file points to an alterntive startup page */ -/* custom_file can be NULL */ +/** + * called on startup, (context entirely filled with NULLs) + * or called for 'New File' + * both startup.blend and userpref.blend are checked + * the optional parameter custom_file points to an alternative startup page + * custom_file can be NULL + */ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const char *custom_file) { ListBase wmbase; @@ -561,6 +563,10 @@ int wm_homefile_read(bContext *C, ReportList *reports, bool from_memory, const c /* options exclude eachother */ BLI_assert((from_memory && custom_file) == 0); + if ((G.f & G_SCRIPT_OVERRIDE_PREF) == 0) { + BKE_BIT_TEST_SET(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_SCRIPT_AUTOEXEC); + } + BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_PRE); UI_view2d_zoom_cache_reset(); @@ -840,11 +846,11 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt) if (scene->camera) { ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, OB_SOLID, false, false, false, R_ADDSKY, err_out); + IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, err_out); } else { ibuf = ED_view3d_draw_offscreen_imbuf(scene, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, false, R_ADDSKY, err_out); + IB_rect, false, R_ALPHAPREMUL, err_out); } if (ibuf) { diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index b52f3819d3f..82e46c1b333 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -145,21 +145,15 @@ static void wm_keyconfig_properties_update_ot(ListBase *km_lb) } } -static int wm_keymap_item_equals_result(wmKeyMapItem *a, wmKeyMapItem *b) +static bool wm_keymap_item_equals_result(wmKeyMapItem *a, wmKeyMapItem *b) { - if (strcmp(a->idname, b->idname) != 0) - return 0; - - if (!RNA_struct_equals(a->ptr, b->ptr, RNA_EQ_UNSET_MATCH_NONE)) - return 0; - - if ((a->flag & KMI_INACTIVE) != (b->flag & KMI_INACTIVE)) - return 0; - - return (a->propvalue == b->propvalue); + return (STREQ(a->idname, b->idname) && + RNA_struct_equals(a->ptr, b->ptr, RNA_EQ_UNSET_MATCH_NONE) && + (a->flag & KMI_INACTIVE) == (b->flag & KMI_INACTIVE) && + a->propvalue == b->propvalue); } -static int wm_keymap_item_equals(wmKeyMapItem *a, wmKeyMapItem *b) +static bool wm_keymap_item_equals(wmKeyMapItem *a, wmKeyMapItem *b) { return (wm_keymap_item_equals_result(a, b) && a->type == b->type && @@ -266,7 +260,7 @@ wmKeyConfig *WM_keyconfig_new_user(wmWindowManager *wm, const char *idname) bool WM_keyconfig_remove(wmWindowManager *wm, wmKeyConfig *keyconf) { if (BLI_findindex(&wm->keyconfigs, keyconf) != -1) { - if (strncmp(U.keyconfigstr, keyconf->idname, sizeof(U.keyconfigstr)) == 0) { + if (STREQLEN(U.keyconfigstr, keyconf->idname, sizeof(U.keyconfigstr))) { BLI_strncpy(U.keyconfigstr, wm->defaultconf->idname, sizeof(U.keyconfigstr)); WM_keyconfig_update_tag(NULL, NULL); } @@ -421,7 +415,7 @@ wmKeyMapItem *WM_keymap_verify_item(wmKeyMap *keymap, const char *idname, int ty wmKeyMapItem *kmi; for (kmi = keymap->items.first; kmi; kmi = kmi->next) - if (strncmp(kmi->idname, idname, OP_MAX_TYPENAME) == 0) + if (STREQLEN(kmi->idname, idname, OP_MAX_TYPENAME)) break; if (kmi == NULL) { kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry"); @@ -728,7 +722,7 @@ wmKeyMap *WM_keymap_list_find(ListBase *lb, const char *idname, int spaceid, int for (km = lb->first; km; km = km->next) if (km->spaceid == spaceid && km->regionid == regionid) - if (0 == strncmp(idname, km->idname, KMAP_MAX_NAME)) + if (STREQLEN(idname, km->idname, KMAP_MAX_NAME)) return km; return NULL; @@ -787,7 +781,7 @@ wmKeyMap *WM_modalkeymap_get(wmKeyConfig *keyconf, const char *idname) for (km = keyconf->keymaps.first; km; km = km->next) if (km->flag & KEYMAP_MODAL) - if (0 == strncmp(idname, km->idname, KMAP_MAX_NAME)) + if (STREQLEN(idname, km->idname, KMAP_MAX_NAME)) break; return km; @@ -952,18 +946,17 @@ static wmKeyMapItem *wm_keymap_item_find_handlers( if (kmi->flag & KMI_INACTIVE) continue; - if (strcmp(kmi->idname, opname) == 0 && WM_key_event_string(kmi->type)[0]) { + if (STREQ(kmi->idname, opname) && WM_key_event_string(kmi->type)[0]) { if (is_hotkey) { if (!ISHOTKEY(kmi->type)) continue; } if (properties) { - /* example of debugging keymaps */ #if 0 if (kmi->ptr) { - if (strcmp("MESH_OT_rip_move", opname) == 0) { + if (STREQ("MESH_OT_rip_move", opname)) { printf("OPERATOR\n"); IDP_spit(properties); printf("KEYMAP\n"); @@ -976,6 +969,39 @@ static wmKeyMapItem *wm_keymap_item_find_handlers( if (keymap_r) *keymap_r = keymap; return kmi; } + /* Debug only, helps spotting mismatches between menu entries and shortcuts! */ + else if (G.debug & G_DEBUG_WM) { + if (is_strict && kmi->ptr) { + wmOperatorType *ot = WM_operatortype_find(opname, true); + if (ot) { + /* make a copy of the properties and set unset ones to their default values. */ + PointerRNA opptr; + IDProperty *properties_default = IDP_CopyProperty(kmi->ptr->data); + + RNA_pointer_create(NULL, ot->srna, properties_default, &opptr); + WM_operator_properties_default(&opptr, true); + + if (IDP_EqualsProperties_ex(properties, properties_default, is_strict)) { + char kmi_str[128]; + WM_keymap_item_to_string(kmi, kmi_str, sizeof(kmi_str)); + /* Note gievn properties could come from other things than menu entry... */ + printf("%s: Some set values in menu entry match default op values, " + "this might not be desired!\n", opname); + printf("\tkm: '%s', kmi: '%s'\n", keymap->idname, kmi_str); +#ifndef NDEBUG + printf("OPERATOR\n"); + IDP_spit(properties); + printf("KEYMAP\n"); + IDP_spit(kmi->ptr->data); +#endif + printf("\n"); + } + + IDP_FreeProperty(properties_default); + MEM_freeN(properties_default); + } + } + } } else { if (keymap_r) *keymap_r = keymap; @@ -1042,33 +1068,83 @@ static wmKeyMapItem *wm_keymap_item_find_props( static wmKeyMapItem *wm_keymap_item_find( const bContext *C, const char *opname, int opcontext, - IDProperty *properties, const bool is_hotkey, const bool is_strict, wmKeyMap **keymap_r) + IDProperty *properties, const bool is_hotkey, bool is_strict, wmKeyMap **keymap_r) { - wmKeyMapItem *found = wm_keymap_item_find_props(C, opname, opcontext, properties, is_strict, is_hotkey, keymap_r); + wmKeyMapItem *found; + /* XXX Hack! Macro operators in menu entry have their whole props defined, which is not the case for + * relevant keymap entries. Could be good to check and harmonize this, but for now always + * compare non-strict in this case. + */ + wmOperatorType *ot = WM_operatortype_find(opname, true); + if (ot) { + is_strict = is_strict && ((ot->flag & OPTYPE_MACRO) == 0); + } + + found = wm_keymap_item_find_props(C, opname, opcontext, properties, is_strict, is_hotkey, keymap_r); + + /* This block is *only* useful in one case: when op uses an enum menu in its prop member + * (then, we want to rerun a comparison with that 'prop' unset). Note this remains brittle, + * since now any enum prop may be used in UI (specified by name), ot->prop is not so much used... + * Otherwise: + * * If non-strict, unset properties always match set ones in IDP_EqualsProperties_ex. + * * If strict, unset properties never match set ones in IDP_EqualsProperties_ex, + * and we do not want that to change (else we get things like T41757)! + * ...so in either case, re-running a comparison with unset props set to default is useless. + */ if (!found && properties) { - wmOperatorType *ot = WM_operatortype_find(opname, true); - if (ot) { - /* make a copy of the properties and set any unset props - * to their default values, so the ID property compare function succeeds */ + if (ot && ot->prop) { /* XXX Shall we also check ot->prop is actually an enum? */ + /* make a copy of the properties and unset the 'ot->prop' one if set. */ PointerRNA opptr; - IDProperty *properties_default = IDP_CopyProperty(properties); + IDProperty *properties_temp = IDP_CopyProperty(properties); - RNA_pointer_create(NULL, ot->srna, properties_default, &opptr); + RNA_pointer_create(NULL, ot->srna, properties_temp, &opptr); - if (WM_operator_properties_default(&opptr, true) || - (!is_strict && ot->prop && RNA_property_is_set(&opptr, ot->prop))) - { - /* for operator that has enum menu, unset it so it always matches */ - if (!is_strict && ot->prop) { - RNA_property_unset(&opptr, ot->prop); - } + if (RNA_property_is_set(&opptr, ot->prop)) { + /* for operator that has enum menu, unset it so its value does not affect comparison result */ + RNA_property_unset(&opptr, ot->prop); - found = wm_keymap_item_find_props(C, opname, opcontext, properties_default, false, is_hotkey, keymap_r); + found = wm_keymap_item_find_props(C, opname, opcontext, properties_temp, + is_strict, is_hotkey, keymap_r); } - IDP_FreeProperty(properties_default); - MEM_freeN(properties_default); + IDP_FreeProperty(properties_temp); + MEM_freeN(properties_temp); + } + } + + /* Debug only, helps spotting mismatches between menu entries and shortcuts! */ + if (G.debug & G_DEBUG_WM) { + if (!found && is_strict && properties) { + wmKeyMap *km; + wmKeyMapItem *kmi; + if (ot) { + /* make a copy of the properties and set unset ones to their default values. */ + PointerRNA opptr; + IDProperty *properties_default = IDP_CopyProperty(properties); + + RNA_pointer_create(NULL, ot->srna, properties_default, &opptr); + WM_operator_properties_default(&opptr, true); + + kmi = wm_keymap_item_find_props(C, opname, opcontext, properties_default, is_strict, is_hotkey, &km); + if (kmi) { + char kmi_str[128]; + WM_keymap_item_to_string(kmi, kmi_str, sizeof(kmi_str)); + printf("%s: Some set values in keymap entry match default op values, " + "this might not be desired!\n", opname); + printf("\tkm: '%s', kmi: '%s'\n", km->idname, kmi_str); +#ifndef NDEBUG + printf("OPERATOR\n"); + IDP_spit(properties); + printf("KEYMAP\n"); + IDP_spit(kmi->ptr->data); +#endif + printf("\n"); + } + + IDP_FreeProperty(properties_default); + MEM_freeN(properties_default); + } } } @@ -1353,7 +1429,7 @@ void WM_keymap_restore_item_to_default(bContext *C, wmKeyMap *keymap, wmKeyMapIt if (orig) { /* restore to original */ - if (strcmp(orig->idname, kmi->idname) != 0) { + if (!STREQ(orig->idname, kmi->idname)) { BLI_strncpy(kmi->idname, orig->idname, sizeof(kmi->idname)); WM_keymap_properties_reset(kmi, NULL); } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index d49a298c424..c0f8709b0f7 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -102,6 +102,8 @@ #include "ED_util.h" #include "ED_view3d.h" +#include "GPU_material.h" + #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -1187,7 +1189,7 @@ bool WM_operator_filesel_ensure_ext_imtype(wmOperator *op, const struct ImageFor /* dont NULL check prop, this can only run on ops with a 'filepath' */ prop = RNA_struct_find_property(op->ptr, "filepath"); RNA_property_string_get(op->ptr, prop, filepath); - if (BKE_add_image_extension(filepath, im_format)) { + if (BKE_image_path_ensure_ext_from_imformat(filepath, im_format)) { RNA_property_string_set(op->ptr, prop, filepath); /* note, we could check for and update 'filename' here, * but so far nothing needs this. */ @@ -1675,7 +1677,7 @@ int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height) /** * For use by #WM_operator_props_popup_call, #WM_operator_props_popup only. * - * \note operator menu needs undo flag enabled , for redo callback */ + * \note operator menu needs undo flag enabled, for redo callback */ static int wm_operator_props_popup_ex(bContext *C, wmOperator *op, const bool do_call, const bool do_redo) { @@ -2026,6 +2028,14 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar uiItemO(col, NULL, ICON_RECOVER_LAST, "WM_OT_recover_last_session"); uiItemL(col, "", ICON_NONE); + mt = WM_menutype_find("USERPREF_MT_splash_footer", false); + if (mt) { + Menu menu = {NULL}; + menu.layout = uiLayoutColumn(layout, false); + menu.type = mt; + mt->draw(C, &menu); + } + UI_block_bounds_set_centered(block, 0); return block; @@ -2682,7 +2692,9 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) /* 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(); BLO_blendhandle_close(bh); /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ @@ -3804,8 +3816,8 @@ void WM_OT_straightline_gesture(wmOperatorType *ot) /* *********************** radial control ****************** */ -#define WM_RADIAL_CONTROL_DISPLAY_SIZE 200 -#define WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE 35 +#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 6 @@ -3856,7 +3868,7 @@ static void radial_control_set_initial_mouse(RadialControl *rc, const wmEvent *e case PROP_DISTANCE: case PROP_PERCENTAGE: case PROP_PIXEL: - d[0] = rc->initial_value; + d[0] = rc->initial_value * U.pixelsize; break; case PROP_FACTOR: d[0] = (1 - rc->initial_value) * WM_RADIAL_CONTROL_DISPLAY_WIDTH + WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE; @@ -3965,8 +3977,8 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd case PROP_DISTANCE: case PROP_PERCENTAGE: case PROP_PIXEL: - r1 = rc->current_value; - r2 = rc->initial_value; + r1 = rc->current_value * U.pixelsize; + r2 = rc->initial_value * U.pixelsize; tex_radius = r1; alpha = 0.75; break; @@ -3991,11 +4003,6 @@ static void radial_control_paint_cursor(bContext *C, int x, int y, void *customd break; } - /* adjust for DPI, like BKE_brush_size_get */ - r1 *= U.pixelsize; - r2 *= U.pixelsize; - tex_radius *= U.pixelsize; - /* Keep cursor in the original place */ x = rc->initial_mouse[0] - ar->winrct.xmin; y = rc->initial_mouse[1] - ar->winrct.ymin; @@ -4413,13 +4420,14 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even case PROP_PIXEL: new_value = dist; if (snap) new_value = ((int)new_value + 5) / 10 * 10; + new_value /= U.pixelsize; break; case PROP_FACTOR: new_value = (WM_RADIAL_CONTROL_DISPLAY_SIZE - dist) / WM_RADIAL_CONTROL_DISPLAY_WIDTH; if (snap) new_value = ((int)ceil(new_value * 10.f) * 10.0f) / 100.f; break; case PROP_ANGLE: - new_value = atan2f(delta[1], delta[0]) + M_PI + angle_precision; + new_value = atan2f(delta[1], delta[0]) + (float)M_PI + angle_precision; new_value = fmod(new_value, 2.0f * (float)M_PI); if (new_value < 0.0f) new_value += 2.0f * (float)M_PI; diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c index d2df020600a..4ce2415e310 100644 --- a/source/blender/windowmanager/intern/wm_subwindow.c +++ b/source/blender/windowmanager/intern/wm_subwindow.c @@ -367,7 +367,7 @@ static void wmOrtho2_offset(const float x, const float y, const float ofs) */ void wmOrtho2_region_pixelspace(const struct ARegion *ar) { - wmOrtho2_offset(ar->winx + 1, ar->winy + 1, -GLA_PIXEL_OFS); + wmOrtho2_offset(ar->winx, ar->winy, -0.01f); } void wmOrtho2_pixelspace(const float x, const float y) diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index d1a94194108..f89177a82ea 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -65,9 +65,6 @@ typedef struct wmEventHandler { struct ARegion *ui_region; /* for derived/modal handlers */ struct ARegion *ui_menu; /* for derived/modal handlers */ - /* fileselect handler re-uses modal operator data */ - struct bScreen *filescreen; /* screen it started in, to validate exec */ - /* drop box handler */ ListBase *dropboxes; diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 2301405a8ea..ecc29de0e7d 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -369,6 +369,18 @@ enum { (event_type >= LEFTCTRLKEY && event_type <= LEFTSHIFTKEY) == false && \ (event_type >= UNKNOWNKEY && event_type <= GRLESSKEY) == false) +/* internal helpers*/ +#define _VA_IS_EVENT_MOD2(v, a) (CHECK_TYPE_INLINE(v, wmEvent *), \ + ((v)->a)) +#define _VA_IS_EVENT_MOD3(v, a, b) \ + (_VA_IS_EVENT_MOD2(v, a) || ((v)->b)) +#define _VA_IS_EVENT_MOD4(v, a, b, c) \ + (_VA_IS_EVENT_MOD3(v, a, b) || ((v)->c)) +#define _VA_IS_EVENT_MOD5(v, a, b, c, d) \ + (_VA_IS_EVENT_MOD4(v, a, b, c) || ((v)->d)) + +/* reusable IS_EVENT_MOD(event, shift, ctrl, alt, oskey), macro */ +#define IS_EVENT_MOD(...) VA_NARGS_CALL_OVERLOAD(_VA_IS_EVENT_MOD, __VA_ARGS__) /* ********** wmEvent.val ********** */ @@ -397,11 +409,10 @@ enum { /* File select */ enum { - EVT_FILESELECT_OPEN = 1, - EVT_FILESELECT_FULL_OPEN = 2, - EVT_FILESELECT_EXEC = 3, - EVT_FILESELECT_CANCEL = 4, - EVT_FILESELECT_EXTERNAL_CANCEL = 5, + EVT_FILESELECT_FULL_OPEN = 1, + EVT_FILESELECT_EXEC = 2, + EVT_FILESELECT_CANCEL = 3, + EVT_FILESELECT_EXTERNAL_CANCEL = 4, }; /* Gesture */ diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index 4a154d778e7..25ee4f2ef21 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -106,6 +106,7 @@ endif() bf_intern_ghostndof3dconnexion bf_rna bf_blenkernel + bf_physics bf_intern_rigidbody bf_blenloader ge_blen_routines diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 9eff3405a2f..21c69ac6203 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -239,8 +239,8 @@ bool ED_texture_context_check_linestyle(const struct bContext *C) RET_ZERO void FRS_free_view_map_cache(void) RET_NONE /* texture.c */ -int multitex_ext(struct Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, struct ImagePool *pool, bool scene_color_manage) RET_ZERO -int multitex_ext_safe(struct Tex *tex, float texvec[3], struct TexResult *texres, struct ImagePool *pool, bool scene_color_manage) RET_ZERO +int multitex_ext(struct Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, struct ImagePool *pool, bool scene_color_manage, const bool skip_load_image) RET_ZERO +int multitex_ext_safe(struct Tex *tex, float texvec[3], struct TexResult *texres, struct ImagePool *pool, bool scene_color_manage, const bool skip_load_image) RET_ZERO int multitex_nodes(struct Tex *tex, float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, const short thread, short which_output, struct ShadeInput *shi, struct MTex *mtex, struct ImagePool *pool) RET_ZERO struct Material *RE_init_sample_material(struct Material *orig_mat, struct Scene *scene) RET_NULL @@ -263,7 +263,7 @@ struct Scene *RE_GetScene(struct Render *re) RET_NULL void RE_Database_Free(struct Render *re) RET_NONE void RE_FreeRender(struct Render *re) RET_NONE void RE_DataBase_GetView(struct Render *re, float mat[4][4]) RET_NONE -int externtex(struct MTex *mtex, const float vec[3], float *tin, float *tr, float *tg, float *tb, float *ta, const int thread, struct ImagePool *poold) RET_ZERO +int externtex(struct MTex *mtex, const float vec[3], float *tin, float *tr, float *tg, float *tb, float *ta, const int thread, struct ImagePool *pool, const bool skip_load_image) RET_ZERO float texture_value_blend(float tex, float out, float fact, float facg, int blendtype) RET_ZERO void texture_rgb_blend(float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype) RET_NONE const unsigned char stipple_quarttone[128]; @@ -341,6 +341,7 @@ bool ED_space_image_show_paint(struct SpaceImage *sima) RET_ZERO void ED_space_image_paint_update(struct wmWindowManager *wm, struct ToolSettings *settings) RET_NONE void ED_space_image_set(struct SpaceImage *sima, struct Scene *scene, struct Object *obedit, struct Image *ima) RET_NONE void ED_space_image_uv_sculpt_update(struct wmWindowManager *wm, struct ToolSettings *settings) RET_NONE +void ED_space_image_scopes_update(const struct bContext *C, struct SpaceImage *sima, struct ImBuf *ibuf, bool use_view_settings) RET_NONE void ED_uvedit_get_aspect(struct Scene *scene, struct Object *ob, struct BMesh *em, float *aspx, float *aspy) RET_NONE diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 21cb41ddfa2..f51b7bec7c9 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -640,9 +640,9 @@ elseif(WIN32) " message(STATUS \"Extracting Python to: \${CMAKE_INSTALL_PREFIX}/${BLENDER_VERSION}/python\") if(\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Dd][Ee][Bb][Uu][Gg])$\") - set(PYTHON_ZIP ${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}_d.tar.gz) + set(PYTHON_ZIP \"${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}_d.tar.gz\") else() - set(PYTHON_ZIP ${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}.tar.gz) + set(PYTHON_ZIP \"${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}.tar.gz\") endif() execute_process( @@ -761,7 +761,6 @@ elseif(WIN32) install( FILES ${LIBDIR}/openal/lib/OpenAL32.dll - ${LIBDIR}/openal/lib/wrap_oal.dll DESTINATION "." ) endif() diff --git a/source/creator/creator.c b/source/creator/creator.c index 83741a64ace..15406485c7f 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -755,7 +755,7 @@ static int set_engine(int argc, const char **argv, void *data) { bContext *C = data; if (argc >= 2) { - if (!strcmp(argv[1], "help")) { + if (STREQ(argv[1], "help")) { RenderEngineType *type = NULL; printf("Blender Engine Listing:\n"); for (type = R_engines.first; type; type = type->next) { @@ -773,6 +773,7 @@ static int set_engine(int argc, const char **argv, void *data) } else { printf("\nError: engine not found '%s'\n", argv[1]); + exit(1); } } else { @@ -924,11 +925,11 @@ static int set_ge_parameters(int argc, const char **argv, void *data) SYS_WriteCommandLineInt(syshandle, argv[a], 1); #endif /* doMipMap */ - if (!strcmp(argv[a], "nomipmap")) { + if (STREQ(argv[a], "nomipmap")) { GPU_set_mipmap(0); //doMipMap = 0; } /* linearMipMap */ - if (!strcmp(argv[a], "linearmipmap")) { + if (STREQ(argv[a], "linearmipmap")) { GPU_set_linear_mipmap(1); //linearMipMap = 1; } @@ -1554,7 +1555,7 @@ int main( #if defined(__APPLE__) && !defined(WITH_PYTHON_MODULE) /* patch to ignore argument finder gives us (pid?) */ - if (argc == 2 && strncmp(argv[1], "-psn_", 5) == 0) { + if (argc == 2 && STREQLEN(argv[1], "-psn_", 5)) { extern int GHOST_HACK_getFirstFile(char buf[]); static char firstfilebuf[512]; diff --git a/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp b/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp index 31f3b1b2047..5bb2d58cd8d 100644 --- a/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp +++ b/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp @@ -350,7 +350,7 @@ void KX_BlenderCanvas::MakeScreenShot(const char *filename) char path[FILE_MAX]; BLI_strncpy(path, filename, sizeof(path)); BLI_path_abs(path, G.main->name); - BKE_add_image_extension_from_type(path, im_format.imtype); + BKE_image_path_ensure_ext_from_imtype(path, im_format.imtype); /* create and save imbuf */ ImBuf *ibuf = IMB_allocImBuf(dumpsx, dumpsy, 24, 0); diff --git a/source/gameengine/GameLogic/SCA_IInputDevice.h b/source/gameengine/GameLogic/SCA_IInputDevice.h index 23346c29601..d4cd66fb564 100644 --- a/source/gameengine/GameLogic/SCA_IInputDevice.h +++ b/source/gameengine/GameLogic/SCA_IInputDevice.h @@ -25,7 +25,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file SCA_IController.h +/** \file SCA_IInputDevice.h * \ingroup gamelogic * \brief Interface for input devices. The defines for keyboard/system/mouse events * here are for internal use in the KX module. diff --git a/source/gameengine/GameLogic/SCA_IObject.h b/source/gameengine/GameLogic/SCA_IObject.h index 365e2b0c853..3ffe128c3b3 100644 --- a/source/gameengine/GameLogic/SCA_IObject.h +++ b/source/gameengine/GameLogic/SCA_IObject.h @@ -24,7 +24,7 @@ * * ***** END GPL LICENSE BLOCK ***** */ -/** \file SCA_IController.h +/** \file SCA_IObject.h * \ingroup gamelogic * \brief An abstract object that has some logic, python scripting and * reference counting Note: transformation stuff has been moved to diff --git a/source/gameengine/GameLogic/SCA_IScene.cpp b/source/gameengine/GameLogic/SCA_IScene.cpp index 3ca4b6607b3..b6ce1d86ba7 100644 --- a/source/gameengine/GameLogic/SCA_IScene.cpp +++ b/source/gameengine/GameLogic/SCA_IScene.cpp @@ -114,7 +114,7 @@ void SCA_IScene::RemoveDebugProperty(class CValue *gameobj, const STR_String &name) { vector<SCA_DebugProp*>::iterator it = m_debugList.begin(); - while(it != m_debugList.end()) { + while (it != m_debugList.end()) { STR_String debugname = (*it)->m_name; CValue *debugobj = (*it)->m_obj; diff --git a/source/gameengine/GameLogic/SCA_ISensor.h b/source/gameengine/GameLogic/SCA_ISensor.h index 091aa675741..7bbba7aaafe 100644 --- a/source/gameengine/GameLogic/SCA_ISensor.h +++ b/source/gameengine/GameLogic/SCA_ISensor.h @@ -25,7 +25,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file SCA_IController.h +/** \file SCA_ISensor.h * \ingroup gamelogic * \brief Interface Class for all logic Sensors. Implements * pulsemode and pulsefrequency, and event suppression. diff --git a/source/gameengine/GameLogic/SCA_JoystickSensor.cpp b/source/gameengine/GameLogic/SCA_JoystickSensor.cpp index 3cae3bcf160..e20dd8bcd74 100644 --- a/source/gameengine/GameLogic/SCA_JoystickSensor.cpp +++ b/source/gameengine/GameLogic/SCA_JoystickSensor.cpp @@ -351,8 +351,8 @@ PyObject *SCA_JoystickSensor::pyattr_get_axis_values(void *self_v, const KX_PYAT SCA_JoystickSensor* self = static_cast<SCA_JoystickSensor*>(self_v); SCA_Joystick *joy = ((SCA_JoystickManager *)self->m_eventmgr)->GetJoystickDevice(self->m_joyindex); - int axis_index= joy->GetNumberOfAxes(); - PyObject *list= PyList_New(axis_index); + int axis_index = (joy ? joy->GetNumberOfAxes() : 0); + PyObject *list = PyList_New(axis_index); while (axis_index--) { PyList_SET_ITEM(list, axis_index, PyLong_FromLong(joy->GetAxisPosition(axis_index))); @@ -371,7 +371,7 @@ PyObject *SCA_JoystickSensor::pyattr_get_axis_single(void *self_v, const KX_PYAT return NULL; } - return PyLong_FromLong(joy->GetAxisPosition(self->m_axis-1)); + return PyLong_FromLong(joy ? joy->GetAxisPosition(self->m_axis - 1) : 0); } PyObject *SCA_JoystickSensor::pyattr_get_hat_values(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) @@ -379,8 +379,8 @@ PyObject *SCA_JoystickSensor::pyattr_get_hat_values(void *self_v, const KX_PYATT SCA_JoystickSensor* self = static_cast<SCA_JoystickSensor*>(self_v); SCA_Joystick *joy = ((SCA_JoystickManager *)self->m_eventmgr)->GetJoystickDevice(self->m_joyindex); - int hat_index= joy->GetNumberOfHats(); - PyObject *list= PyList_New(hat_index); + int hat_index = (joy ? joy->GetNumberOfHats() : 0); + PyObject *list = PyList_New(hat_index); while (hat_index--) { PyList_SET_ITEM(list, hat_index, PyLong_FromLong(joy->GetHat(hat_index))); @@ -394,7 +394,7 @@ PyObject *SCA_JoystickSensor::pyattr_get_hat_single(void *self_v, const KX_PYATT SCA_JoystickSensor* self = static_cast<SCA_JoystickSensor*>(self_v); SCA_Joystick *joy = ((SCA_JoystickManager *)self->m_eventmgr)->GetJoystickDevice(self->m_joyindex); - return PyLong_FromLong(joy->GetHat(self->m_hat-1)); + return PyLong_FromLong(joy ? joy->GetHat(self->m_hat - 1) : 0); } PyObject *SCA_JoystickSensor::pyattr_get_num_axis(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) diff --git a/source/gameengine/GameLogic/SCA_LogicManager.h b/source/gameengine/GameLogic/SCA_LogicManager.h index 4d8c20065b5..1275a7c5085 100644 --- a/source/gameengine/GameLogic/SCA_LogicManager.h +++ b/source/gameengine/GameLogic/SCA_LogicManager.h @@ -24,7 +24,7 @@ * * ***** END GPL LICENSE BLOCK ***** */ -/** \file SCA_IController.h +/** \file SCA_LogicManager.h * \ingroup gamelogic * \brief Regulates the top-level logic behavior for one scene. */ diff --git a/source/gameengine/GamePlayer/common/GPC_Canvas.cpp b/source/gameengine/GamePlayer/common/GPC_Canvas.cpp index be7cf6629f0..c88d55da2b6 100644 --- a/source/gameengine/GamePlayer/common/GPC_Canvas.cpp +++ b/source/gameengine/GamePlayer/common/GPC_Canvas.cpp @@ -180,7 +180,7 @@ MakeScreenShot( char path[FILE_MAX]; BLI_strncpy(path, filename, sizeof(path)); BLI_path_abs(path, G.main->name); - BKE_add_image_extension_from_type(path, im_format.imtype); + BKE_image_path_ensure_ext_from_imtype(path, im_format.imtype); // create and save imbuf ImBuf *ibuf = IMB_allocImBuf(GetWidth(), GetHeight(), 24, 0); diff --git a/source/gameengine/Ketsji/BL_ActionManager.h b/source/gameengine/Ketsji/BL_ActionManager.h index 5b340257881..148097f995e 100644 --- a/source/gameengine/Ketsji/BL_ActionManager.h +++ b/source/gameengine/Ketsji/BL_ActionManager.h @@ -20,7 +20,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file BL_ActionManager.cpp +/** \file BL_ActionManager.h * \ingroup ketsji */ diff --git a/source/gameengine/Ketsji/BL_BlenderShader.cpp b/source/gameengine/Ketsji/BL_BlenderShader.cpp index bcdef85bc8a..de6b36b189d 100644 --- a/source/gameengine/Ketsji/BL_BlenderShader.cpp +++ b/source/gameengine/Ketsji/BL_BlenderShader.cpp @@ -77,7 +77,7 @@ void BL_BlenderShader::SetProg(bool enable, double time, RAS_IRasterizer* rasty) view.getValue((float*)viewmat); viewinv.getValue((float*)viewinvmat); - GPU_material_bind(mGPUMat, mLightLayer, mBlenderScene->lay, time, 1, viewmat, viewinvmat, false); + GPU_material_bind(mGPUMat, mLightLayer, mBlenderScene->lay, time, 1, viewmat, viewinvmat, NULL, false); } else GPU_material_unbind(mGPUMat); diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index e8b68d20e84..6d4b5564e19 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -77,6 +77,8 @@ typedef unsigned long uint_ptr; #include "BL_Action.h" #include "PyObjectPlus.h" /* python stuff */ +#include "BLI_utildefines.h" +#include "python_utildefines.h" // This file defines relationships between parents and children // in the game engine. @@ -1498,7 +1500,7 @@ void KX_GameObject::RegisterCollisionCallbacks() pe->AddSensor(spc); } } -void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider) +void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider, const MT_Vector3 &point, const MT_Vector3 &normal) { #ifdef WITH_PYTHON Py_ssize_t len; @@ -1506,15 +1508,50 @@ void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider) if (collision_callbacks && (len=PyList_GET_SIZE(collision_callbacks))) { - PyObject* args = Py_BuildValue("(O)", collider->GetProxy()); // save python creating each call + // Argument tuples are created lazily, only when they are needed. + PyObject *args_3 = NULL; + PyObject *args_1 = NULL; // Only for compatibility with pre-2.74 callbacks that take 1 argument. + PyObject *func; PyObject *ret; + int co_argcount; // Iterate the list and run the callbacks for (Py_ssize_t pos=0; pos < len; pos++) { func = PyList_GET_ITEM(collision_callbacks, pos); - ret = PyObject_Call(func, args, NULL); + + // Get the number of arguments, supporting functions, methods and generic callables. + if (PyMethod_Check(func)) { + // Take away the 'self' argument for methods. + co_argcount = ((PyCodeObject *)PyFunction_GET_CODE(PyMethod_GET_FUNCTION(func)))->co_argcount - 1; + } else if (PyFunction_Check(func)) { + co_argcount = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_argcount; + } else { + // We'll just assume the callable takes the correct number of arguments. + co_argcount = 3; + } + + // Check whether the function expects the colliding object only, + // or also the point and normal. + if (co_argcount <= 1) { + // One argument, or *args (which gives co_argcount == 0) + if (args_1 == NULL) { + args_1 = PyTuple_New(1); + PyTuple_SET_ITEMS(args_1, collider->GetProxy()); + } + ret = PyObject_Call(func, args_1, NULL); + } else { + // More than one argument, assume we can give point & normal. + if (args_3 == NULL) { + args_3 = PyTuple_New(3); + PyTuple_SET_ITEMS(args_3, + collider->GetProxy(), + PyObjectFrom(point), + PyObjectFrom(normal)); + } + ret = PyObject_Call(func, args_3, NULL); + } if (ret == NULL) { PyErr_Print(); @@ -1525,7 +1562,8 @@ void KX_GameObject::RunCollisionCallbacks(KX_GameObject *collider) } } - Py_DECREF(args); + if (args_3) Py_DECREF(args_3); + if (args_1) Py_DECREF(args_1); } #endif } diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 4538679303f..5800cbc6834 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -919,7 +919,7 @@ public: void RegisterCollisionCallbacks(); void UnregisterCollisionCallbacks(); - void RunCollisionCallbacks(KX_GameObject *collider); + void RunCollisionCallbacks(KX_GameObject *collider, const MT_Vector3 &point, const MT_Vector3 &normal); /** * Stop making progress */ diff --git a/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp b/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp index ebf1b9ec577..1b69eab8e28 100644 --- a/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp +++ b/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp @@ -56,38 +56,95 @@ // if there is a better way (without global), please do so! static PHY_IPhysicsEnvironment* g_CurrentActivePhysicsEnvironment = NULL; -static char PhysicsConstraints_module_documentation[] = -"This is the Python API for the Physics Constraints"; - - -static char gPySetGravity__doc__[] = "setGravity(float x,float y,float z)"; -static char gPySetDebugMode__doc__[] = "setDebugMode(int mode)"; - -static char gPySetNumIterations__doc__[] = "setNumIterations(int numiter) This sets the number of iterations for an iterative constraint solver"; -static char gPySetNumTimeSubSteps__doc__[] = "setNumTimeSubSteps(int numsubstep) This sets the number of substeps for each physics proceed. Tradeoff quality for performance."; - - -static char gPySetDeactivationTime__doc__[] = "setDeactivationTime(float time) This sets the time after which a resting rigidbody gets deactived"; -static char gPySetDeactivationLinearTreshold__doc__[] = "setDeactivationLinearTreshold(float linearTreshold)"; -static char gPySetDeactivationAngularTreshold__doc__[] = "setDeactivationAngularTreshold(float angularTreshold)"; -static char gPySetContactBreakingTreshold__doc__[] = "setContactBreakingTreshold(float breakingTreshold) Reasonable default is 0.02 (if units are meters)"; - -static char gPySetCcdMode__doc__[] = "setCcdMode(int ccdMode) Very experimental, not recommended"; -static char gPySetSorConstant__doc__[] = "setSorConstant(float sor) Very experimental, not recommended"; -static char gPySetSolverTau__doc__[] = "setTau(float tau) Very experimental, not recommended"; -static char gPySetSolverDamping__doc__[] = "setDamping(float damping) Very experimental, not recommended"; -static char gPySetLinearAirDamping__doc__[] = "setLinearAirDamping(float damping) Very experimental, not recommended"; -static char gPySetUseEpa__doc__[] = "setUseEpa(int epa) Very experimental, not recommended"; -static char gPySetSolverType__doc__[] = "setSolverType(int solverType) Very experimental, not recommended"; - - -static char gPyCreateConstraint__doc__[] = "createConstraint(ob1,ob2,float restLength,float restitution,float damping)"; -static char gPyGetVehicleConstraint__doc__[] = "getVehicleConstraint(int constraintId)"; -static char gPyGetCharacter__doc__[] = "getCharacter(KX_GameObject obj)"; -static char gPyRemoveConstraint__doc__[] = "removeConstraint(int constraintId)"; -static char gPyGetAppliedImpulse__doc__[] = "getAppliedImpulse(int constraintId)"; - +PyDoc_STRVAR(PhysicsConstraints_module_documentation, +"This is the Python API for the Physics Constraints" +); + +PyDoc_STRVAR(gPySetGravity__doc__, +"setGravity(float x,float y,float z)\n" +"" +); +PyDoc_STRVAR(gPySetDebugMode__doc__, +"setDebugMode(int mode)\n" +"" +); + +PyDoc_STRVAR(gPySetNumIterations__doc__, +"setNumIterations(int numiter)\n" +"This sets the number of iterations for an iterative constraint solver" +); +PyDoc_STRVAR(gPySetNumTimeSubSteps__doc__, +"setNumTimeSubSteps(int numsubstep)\n" +"This sets the number of substeps for each physics proceed. Tradeoff quality for performance." +); + +PyDoc_STRVAR(gPySetDeactivationTime__doc__, +"setDeactivationTime(float time)\n" +"This sets the time after which a resting rigidbody gets deactived" +); +PyDoc_STRVAR(gPySetDeactivationLinearTreshold__doc__, +"setDeactivationLinearTreshold(float linearTreshold)\n" +"" +); +PyDoc_STRVAR(gPySetDeactivationAngularTreshold__doc__, +"setDeactivationAngularTreshold(float angularTreshold)\n" +"" +); +PyDoc_STRVAR(gPySetContactBreakingTreshold__doc__, +"setContactBreakingTreshold(float breakingTreshold)\n" +"Reasonable default is 0.02 (if units are meters)" +); + +PyDoc_STRVAR(gPySetCcdMode__doc__, +"setCcdMode(int ccdMode)\n" +"Very experimental, not recommended" +); +PyDoc_STRVAR(gPySetSorConstant__doc__, +"setSorConstant(float sor)\n" +"Very experimental, not recommended" +); +PyDoc_STRVAR(gPySetSolverTau__doc__, +"setTau(float tau)\n" +"Very experimental, not recommended" +); +PyDoc_STRVAR(gPySetSolverDamping__doc__, +"setDamping(float damping)\n" +"Very experimental, not recommended" +); +PyDoc_STRVAR(gPySetLinearAirDamping__doc__, +"setLinearAirDamping(float damping)\n" +"Very experimental, not recommended" +); +PyDoc_STRVAR(gPySetUseEpa__doc__, +"setUseEpa(int epa)\n" +"Very experimental, not recommended" +); +PyDoc_STRVAR(gPySetSolverType__doc__, +"setSolverType(int solverType)\n" +"Very experimental, not recommended" +); + +PyDoc_STRVAR(gPyCreateConstraint__doc__, +"createConstraint(ob1,ob2,float restLength,float restitution,float damping)\n" +"" +); +PyDoc_STRVAR(gPyGetVehicleConstraint__doc__, +"getVehicleConstraint(int constraintId)\n" +"" +); +PyDoc_STRVAR(gPyGetCharacter__doc__, +"getCharacter(KX_GameObject obj)\n" +"" +); +PyDoc_STRVAR(gPyRemoveConstraint__doc__, +"removeConstraint(int constraintId)\n" +"" +); +PyDoc_STRVAR(gPyGetAppliedImpulse__doc__, +"getAppliedImpulse(int constraintId)\n" +"" +); @@ -677,7 +734,7 @@ static struct PyMethodDef physicsconstraints_methods[] = { }; static struct PyModuleDef PhysicsConstraints_module_def = { - {}, /* m_base */ + PyModuleDef_HEAD_INIT, "PhysicsConstraints", /* m_name */ PhysicsConstraints_module_documentation, /* m_doc */ 0, /* m_size */ @@ -688,7 +745,7 @@ static struct PyModuleDef PhysicsConstraints_module_def = { 0, /* m_free */ }; -PyObject *initPythonConstraintBinding() +PyMODINIT_FUNC initConstraintPythonBinding() { PyObject *ErrorObject; @@ -696,19 +753,8 @@ PyObject *initPythonConstraintBinding() PyObject *d; PyObject *item; - /* Use existing module where possible - * be careful not to init any runtime vars after this */ - m = PyImport_ImportModule( "PhysicsConstraints" ); - if (m) { - Py_DECREF(m); - return m; - } - else { - PyErr_Clear(); - - m = PyModule_Create(&PhysicsConstraints_module_def); - PyDict_SetItemString(PySys_GetObject("modules"), PhysicsConstraints_module_def.m_name, m); - } + m = PyModule_Create(&PhysicsConstraints_module_def); + PyDict_SetItemString(PySys_GetObject("modules"), PhysicsConstraints_module_def.m_name, m); // Add some symbolic constants to the module d = PyModule_GetDict(m); @@ -747,7 +793,7 @@ PyObject *initPythonConstraintBinding() Py_FatalError("can't initialize module PhysicsConstraints"); } - return d; + return m; } #if 0 diff --git a/source/gameengine/Ketsji/KX_PyConstraintBinding.h b/source/gameengine/Ketsji/KX_PyConstraintBinding.h index b4a520ce71b..2bf9f7e197d 100644 --- a/source/gameengine/Ketsji/KX_PyConstraintBinding.h +++ b/source/gameengine/Ketsji/KX_PyConstraintBinding.h @@ -36,7 +36,8 @@ #include <Python.h> -PyObject* initPythonConstraintBinding(); +PyMODINIT_FUNC initConstraintPythonBinding(); + void PHY_SetActiveEnvironment(class PHY_IPhysicsEnvironment* env); PHY_IPhysicsEnvironment* PHY_GetActiveEnvironment(); #endif /* WITH_PYTHON */ diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp index 3ddd53b971f..70d1aed88a0 100644 --- a/source/gameengine/Ketsji/KX_PythonInit.cpp +++ b/source/gameengine/Ketsji/KX_PythonInit.cpp @@ -223,8 +223,11 @@ static void KX_MACRO_addTypesToDict_fn(PyObject *dict, const char *name, long va // List of methods defined in the module static PyObject *ErrorObject; -static const char *gPyGetRandomFloat_doc="getRandomFloat returns a random floating point value in the range [0..1]"; +PyDoc_STRVAR(gPyGetRandomFloat_doc, +"getRandomFloat()\n" +"returns a random floating point value in the range [0..1]" +); static PyObject *gPyGetRandomFloat(PyObject *) { return PyFloat_FromDouble(MT_random()); @@ -242,15 +245,15 @@ static PyObject *gPySetGravity(PyObject *, PyObject *value) Py_RETURN_NONE; } -static char gPyExpandPath_doc[] = -"(path) - Converts a blender internal path into a proper file system path.\n\ -path - the string path to convert.\n\n\ -Use / as directory separator in path\n\ -You can use '//' at the start of the string to define a relative path;\n\ -Blender replaces that string by the directory of the current .blend or runtime\n\ -file to make a full path name.\n\ -The function also converts the directory separator to the local file system format."; - +PyDoc_STRVAR(gPyExpandPath_doc, +"expandPath(path)\n" +"Converts a blender internal path into a proper file system path.\n" +" path - the string path to convert.\n" +"Use / as directory separator in path\n" +"You can use '//' at the start of the string to define a relative path." +"Blender replaces that string by the directory of the current .blend or runtime file to make a full path name.\n" +"The function also converts the directory separator to the local file system format." +); static PyObject *gPyExpandPath(PyObject *, PyObject *args) { char expanded[FILE_MAX]; @@ -264,10 +267,10 @@ static PyObject *gPyExpandPath(PyObject *, PyObject *args) return PyC_UnicodeFromByte(expanded); } -static char gPyStartGame_doc[] = -"startGame(blend)\n\ -Loads the blend file"; - +PyDoc_STRVAR(gPyStartGame_doc, +"startGame(blend)\n" +"Loads the blend file" +); static PyObject *gPyStartGame(PyObject *, PyObject *args) { char* blendfile; @@ -281,10 +284,10 @@ static PyObject *gPyStartGame(PyObject *, PyObject *args) Py_RETURN_NONE; } -static char gPyEndGame_doc[] = -"endGame()\n\ -Ends the current game"; - +PyDoc_STRVAR(gPyEndGame_doc, +"endGame()\n" +"Ends the current game" +); static PyObject *gPyEndGame(PyObject *) { gp_KetsjiEngine->RequestExit(KX_EXIT_REQUEST_QUIT_GAME); @@ -294,10 +297,10 @@ static PyObject *gPyEndGame(PyObject *) Py_RETURN_NONE; } -static char gPyRestartGame_doc[] = -"restartGame()\n\ -Restarts the current game by reloading the .blend file"; - +PyDoc_STRVAR(gPyRestartGame_doc, +"restartGame()\n" +"Restarts the current game by reloading the .blend file" +); static PyObject *gPyRestartGame(PyObject *) { gp_KetsjiEngine->RequestExit(KX_EXIT_REQUEST_RESTART_GAME); @@ -306,10 +309,10 @@ static PyObject *gPyRestartGame(PyObject *) Py_RETURN_NONE; } -static char gPySaveGlobalDict_doc[] = - "saveGlobalDict()\n" - "Saves bge.logic.globalDict to a file"; - +PyDoc_STRVAR(gPySaveGlobalDict_doc, +"saveGlobalDict()\n" +"Saves bge.logic.globalDict to a file" +); static PyObject *gPySaveGlobalDict(PyObject *) { char marshal_path[512]; @@ -343,10 +346,10 @@ static PyObject *gPySaveGlobalDict(PyObject *) Py_RETURN_NONE; } -static char gPyLoadGlobalDict_doc[] = - "LoadGlobalDict()\n" - "Loads bge.logic.globalDict from a file"; - +PyDoc_STRVAR(gPyLoadGlobalDict_doc, +"LoadGlobalDict()\n" +"Loads bge.logic.globalDict from a file" +); static PyObject *gPyLoadGlobalDict(PyObject *) { char marshal_path[512]; @@ -384,23 +387,23 @@ static PyObject *gPyLoadGlobalDict(PyObject *) Py_RETURN_NONE; } -static char gPyGetProfileInfo_doc[] = +PyDoc_STRVAR(gPyGetProfileInfo_doc, "getProfileInfo()\n" -"returns a dictionary with profiling information"; - +"returns a dictionary with profiling information" +); static PyObject *gPyGetProfileInfo(PyObject *) { return gp_KetsjiEngine->GetPyProfileDict(); } -static char gPySendMessage_doc[] = -"sendMessage(subject, [body, to, from])\n\ -sends a message in same manner as a message actuator\ -subject = Subject of the message\ -body = Message body\ -to = Name of object to send the message to\ -from = Name of object to send the string from"; - +PyDoc_STRVAR(gPySendMessage_doc, +"sendMessage(subject, [body, to, from])\n" +"sends a message in same manner as a message actuator" +" subject = Subject of the message" +" body = Message body" +" to = Name of object to send the message to" +" from = Name of object to send the string from" +); static PyObject *gPySendMessage(PyObject *, PyObject *args) { char* subject; @@ -560,11 +563,12 @@ static PyObject *gPyGetBlendFileList(PyObject *, PyObject *args) return list; } -static char gPyAddScene_doc[] = -"addScene(name, [overlay])\n\ -adds a scene to the game engine\n\ -name = Name of the scene\n\ -overlay = Overlay or underlay"; +PyDoc_STRVAR(gPyAddScene_doc, +"addScene(name, [overlay])\n" +"Adds a scene to the game engine.\n" +" name = Name of the scene\n" +" overlay = Overlay or underlay" +); static PyObject *gPyAddScene(PyObject *, PyObject *args) { char* name; @@ -578,17 +582,19 @@ static PyObject *gPyAddScene(PyObject *, PyObject *args) Py_RETURN_NONE; } -static const char *gPyGetCurrentScene_doc = +PyDoc_STRVAR(gPyGetCurrentScene_doc, "getCurrentScene()\n" -"Gets a reference to the current scene.\n"; +"Gets a reference to the current scene." +); static PyObject *gPyGetCurrentScene(PyObject *self) { return gp_KetsjiScene->GetProxy(); } -static const char *gPyGetSceneList_doc = +PyDoc_STRVAR(gPyGetSceneList_doc, "getSceneList()\n" -"Return a list of converted scenes.\n"; +"Return a list of converted scenes." +); static PyObject *gPyGetSceneList(PyObject *self) { KX_KetsjiEngine* m_engine = KX_GetActiveEngine(); @@ -1006,6 +1012,21 @@ static PyObject *gPyGetFocalLength(PyObject *, PyObject *, PyObject *) Py_RETURN_NONE; } +static PyObject *gPyGetStereoEye(PyObject *, PyObject *, PyObject *) +{ + int flag = RAS_IRasterizer::RAS_STEREO_LEFTEYE; + + if (!gp_Rasterizer) { + PyErr_SetString(PyExc_RuntimeError, "Rasterizer.getStereoEye(), Rasterizer not available"); + return NULL; + } + + if (gp_Rasterizer->Stereo()) + flag = gp_Rasterizer->GetEye(); + + return PyLong_FromLong(flag); +} + static PyObject *gPySetBackgroundColor(PyObject *, PyObject *value) { @@ -1469,6 +1490,9 @@ static PyObject *gPyClearDebugList(PyObject *) Py_RETURN_NONE; } +PyDoc_STRVAR(Rasterizer_module_documentation, +"This is the Python API for the game engine of Rasterizer" +); static struct PyMethodDef rasterizer_methods[] = { {"getWindowWidth",(PyCFunction) gPyGetWindowWidth, @@ -1496,6 +1520,7 @@ static struct PyMethodDef rasterizer_methods[] = { {"getEyeSeparation", (PyCFunction) gPyGetEyeSeparation, METH_NOARGS, "get the eye separation for stereo mode"}, {"setFocalLength", (PyCFunction) gPySetFocalLength, METH_VARARGS, "set the focal length for stereo mode"}, {"getFocalLength", (PyCFunction) gPyGetFocalLength, METH_VARARGS, "get the focal length for stereo mode"}, + {"getStereoEye", (PyCFunction) gPyGetStereoEye, METH_VARARGS, "get the current stereoscopy eye being rendered"}, {"setMaterialMode",(PyCFunction) gPySetMaterialType, METH_VARARGS, "set the material mode to use for OpenGL rendering"}, {"getMaterialMode",(PyCFunction) gPyGetMaterialType, @@ -1525,15 +1550,11 @@ static struct PyMethodDef rasterizer_methods[] = { { NULL, (PyCFunction) NULL, 0, NULL } }; -// Initialization function for the module (*must* be called initGameLogic) -static char GameLogic_module_documentation[] = -"This is the Python API for the game engine of bge.logic" -; -static char Rasterizer_module_documentation[] = -"This is the Python API for the game engine of Rasterizer" -; +PyDoc_STRVAR(GameLogic_module_documentation, +"This is the Python API for the game engine of bge.logic" +); static struct PyModuleDef GameLogic_module_def = { {}, /* m_base */ @@ -1547,33 +1568,20 @@ static struct PyModuleDef GameLogic_module_def = { 0, /* m_free */ }; -PyObject *initGameLogic(KX_KetsjiEngine *engine, KX_Scene* scene) // quick hack to get gravity hook +PyMODINIT_FUNC initGameLogicPythonBinding() { PyObject *m; PyObject *d; PyObject *item; /* temp PyObject *storage */ - - gp_KetsjiEngine = engine; - gp_KetsjiScene = scene; gUseVisibilityTemp=false; - + PyObjectPlus::ClearDeprecationWarning(); /* Not that nice to call here but makes sure warnings are reset between loading scenes */ - - /* Use existing module where possible - * be careful not to init any runtime vars after this */ - m = PyImport_ImportModule( "GameLogic" ); - if (m) { - Py_DECREF(m); - return m; - } - else { - PyErr_Clear(); - // Create the module and add the functions - m = PyModule_Create(&GameLogic_module_def); - PyDict_SetItemString(PySys_GetObject("modules"), GameLogic_module_def.m_name, m); - } - + + m = PyModule_Create(&GameLogic_module_def); + PyDict_SetItemString(PySys_GetObject("modules"), GameLogic_module_def.m_name, m); + + // Add some symbolic constants to the module d = PyModule_GetDict(m); @@ -2053,7 +2061,60 @@ void removeImportMain(struct Main *maggie) bpy_import_main_extra_remove(maggie); } -// Copied from bpy_interface.c + +PyDoc_STRVAR(BGE_module_documentation, + "This module contains submodules for the Blender Game Engine.\n" +); + +static struct PyModuleDef BGE_module_def = { + PyModuleDef_HEAD_INIT, + "bge", /* m_name */ + BGE_module_documentation, /* m_doc */ + 0, /* m_size */ + NULL, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; + +PyMODINIT_FUNC initBGE(void) +{ + PyObject *mod; + PyObject *submodule; + PyObject *sys_modules = PyThreadState_GET()->interp->modules; + + mod = PyModule_Create(&BGE_module_def); + + PyModule_AddObject(mod, "constraints", (submodule = initConstraintPythonBinding())); + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + + PyModule_AddObject(mod, "events", (submodule = initGameKeysPythonBinding())); + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + + PyModule_AddObject(mod, "logic", (submodule = initGameLogicPythonBinding())); + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + + PyModule_AddObject(mod, "render", (submodule = initRasterizerPythonBinding())); + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + + PyModule_AddObject(mod, "texture", (submodule = initVideoTexturePythonBinding())); + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + + PyModule_AddObject(mod, "types", (submodule = initGameTypesPythonBinding())); + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + + return mod; +} + + +/* minimal required blender modules to run blenderplayer */ static struct _inittab bge_internal_modules[] = { {"mathutils", PyInit_mathutils}, {"bgl", BPyInit_bgl}, @@ -2066,7 +2127,7 @@ static struct _inittab bge_internal_modules[] = { * Python is not initialized. * see bpy_interface.c's BPY_python_start() which shares the same functionality in blender. */ -PyObject *initGamePlayerPythonScripting(const STR_String& progname, TPythonSecurityLevel level, Main *maggie, int argc, char** argv) +PyObject *initGamePlayerPythonScripting(Main *maggie, int argc, char** argv) { /* Yet another gotcha in the py api * Cant run PySys_SetArgv more than once because this adds the @@ -2077,14 +2138,20 @@ PyObject *initGamePlayerPythonScripting(const STR_String& progname, TPythonSecur static bool first_time = true; const char * const py_path_bundle = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, NULL); -#if 0 // TODO - py3 - STR_String pname = progname; - Py_SetProgramName(pname.Ptr()); -#endif + /* not essential but nice to set our name */ + static wchar_t program_path_wchar[FILE_MAX]; /* python holds a reference */ + BLI_strncpy_wchar_from_utf8(program_path_wchar, BKE_appdir_program_path(), ARRAY_SIZE(program_path_wchar)); + Py_SetProgramName(program_path_wchar); + /* Update, Py3.3 resolves attempting to parse non-existing header */ + #if 0 + /* Python 3.2 now looks for '2.xx/python/include/python3.2d/pyconfig.h' to + * parse from the 'sysconfig' module which is used by 'site', + * so for now disable site. alternatively we could copy the file. */ if (py_path_bundle != NULL) { - Py_NoSiteFlag = 1; + Py_NoSiteFlag = 1; /* inhibits the automatic importing of 'site' */ } + #endif Py_FrozenFlag = 1; @@ -2108,12 +2175,16 @@ PyObject *initGamePlayerPythonScripting(const STR_String& progname, TPythonSecur PySys_SetObject("argv", py_argv); Py_DECREF(py_argv); } - + /* Initialize thread support (also acquires lock) */ PyEval_InitThreads(); bpy_import_init(PyEval_GetBuiltins()); + bpy_import_main_set(maggie); + + initPySysObjects(maggie); + /* mathutils types are used by the BGE even if we don't import them */ { PyObject *mod = PyImport_ImportModuleLevel("mathutils", NULL, NULL, NULL, 0); @@ -2128,12 +2199,8 @@ PyObject *initGamePlayerPythonScripting(const STR_String& progname, TPythonSecur } #endif - initPyTypes(); - - bpy_import_main_set(maggie); - - initPySysObjects(maggie); - + PyDict_SetItemString(PyImport_GetModuleDict(), "bge", initBGE()); + first_time = false; PyObjectPlus::ClearDeprecationWarning(); @@ -2170,12 +2237,13 @@ void exitGamePlayerPythonScripting() /** * Python is already initialized. */ -PyObject *initGamePythonScripting(const STR_String& progname, TPythonSecurityLevel level, Main *maggie) +PyObject *initGamePythonScripting(Main *maggie) { -#if 0 // XXX TODO Py3 - STR_String pname = progname; - Py_SetProgramName(pname.Ptr()); -#endif + /* no need to Py_SetProgramName, it was already taken care of in BPY_python_start */ + + bpy_import_main_set(maggie); + + initPySysObjects(maggie); #ifdef WITH_AUDASPACE /* accessing a SoundActuator's sound results in a crash if aud is not initialized... */ @@ -2185,11 +2253,7 @@ PyObject *initGamePythonScripting(const STR_String& progname, TPythonSecurityLev } #endif - initPyTypes(); - - bpy_import_main_set(maggie); - - initPySysObjects(maggie); + PyDict_SetItemString(PyImport_GetModuleDict(), "bge", initBGE()); PyObjectPlus::NullDeprecationWarning(); @@ -2222,51 +2286,32 @@ void exitGamePythonScripting() void setupGamePython(KX_KetsjiEngine* ketsjiengine, KX_Scene *startscene, Main *blenderdata, PyObject *pyGlobalDict, PyObject **gameLogic, PyObject **gameLogic_keys, int argc, char** argv) { - PyObject *dictionaryobject; + PyObject *modules, *dictionaryobject; + + gp_Canvas = ketsjiengine->GetCanvas(); + gp_Rasterizer = ketsjiengine->GetRasterizer(); + gp_KetsjiEngine = ketsjiengine; + gp_KetsjiScene = startscene; if (argv) /* player only */ - dictionaryobject= initGamePlayerPythonScripting("Ketsji", psl_Lowest, blenderdata, argc, argv); + dictionaryobject= initGamePlayerPythonScripting(blenderdata, argc, argv); else - dictionaryobject= initGamePythonScripting("Ketsji", psl_Lowest, blenderdata); + dictionaryobject= initGamePythonScripting(blenderdata); ketsjiengine->SetPyNamespace(dictionaryobject); - initRasterizer(ketsjiengine->GetRasterizer(), ketsjiengine->GetCanvas()); - *gameLogic = initGameLogic(ketsjiengine, startscene); - /* is set in initGameLogic so only set here if we want it to persist between scenes */ + modules = PyImport_GetModuleDict(); + + *gameLogic = PyDict_GetItemString(modules, "GameLogic"); + /* is set in initGameLogicPythonBinding so only set here if we want it to persist between scenes */ if (pyGlobalDict) PyDict_SetItemString(PyModule_GetDict(*gameLogic), "globalDict", pyGlobalDict); // Same as importing the module. *gameLogic_keys = PyDict_Keys(PyModule_GetDict(*gameLogic)); - - initGameKeys(); - initPythonConstraintBinding(); - initVideoTexture(); - - /* could be done a lot more nicely, but for now a quick way to get bge.* working */ - PyRun_SimpleString("sys = __import__('sys');" - "bge = type(sys)('bge');" - "bge.__dict__.update({'logic':__import__('GameLogic'), " - "'render':__import__('Rasterizer'), " - "'events':__import__('GameKeys'), " - "'constraints':__import__('PhysicsConstraints'), " - "'physics':__import__('PhysicsConstraints')," - "'types':__import__('GameTypes'), " - "'texture':__import__('VideoTexture')});" - /* so we can do 'import bge.foo as bar' */ - "sys.modules.update({'bge': bge, " - "'bge.logic':bge.logic, " - "'bge.render':bge.render, " - "'bge.events':bge.events, " - "'bge.constraints':bge.constraints, " - "'bge.physics':bge.physics," - "'bge.types':bge.types, " - "'bge.texture':bge.texture})" - ); } static struct PyModuleDef Rasterizer_module_def = { - {}, /* m_base */ + PyModuleDef_HEAD_INIT, "Rasterizer", /* m_name */ Rasterizer_module_documentation, /* m_doc */ 0, /* m_size */ @@ -2277,29 +2322,14 @@ static struct PyModuleDef Rasterizer_module_def = { 0, /* m_free */ }; -PyObject *initRasterizer(RAS_IRasterizer* rasty,RAS_ICanvas* canvas) +PyMODINIT_FUNC initRasterizerPythonBinding() { - gp_Canvas = canvas; - gp_Rasterizer = rasty; - - PyObject *m; PyObject *d; - /* Use existing module where possible - * be careful not to init any runtime vars after this */ - m = PyImport_ImportModule( "Rasterizer" ); - if (m) { - Py_DECREF(m); - return m; - } - else { - PyErr_Clear(); + m = PyModule_Create(&Rasterizer_module_def); + PyDict_SetItemString(PySys_GetObject("modules"), Rasterizer_module_def.m_name, m); - // Create the module and add the functions - m = PyModule_Create(&Rasterizer_module_def); - PyDict_SetItemString(PySys_GetObject("modules"), Rasterizer_module_def.m_name, m); - } // Add some symbolic constants to the module d = PyModule_GetDict(m); @@ -2320,6 +2350,10 @@ PyObject *initRasterizer(RAS_IRasterizer* rasty,RAS_ICanvas* canvas) KX_MACRO_addTypesToDict(d, VSYNC_ON, VSYNC_ON); KX_MACRO_addTypesToDict(d, VSYNC_ADAPTIVE, VSYNC_ADAPTIVE); + /* stereoscopy */ + KX_MACRO_addTypesToDict(d, LEFT_EYE, RAS_IRasterizer::RAS_STEREO_LEFTEYE); + KX_MACRO_addTypesToDict(d, RIGHT_EYE, RAS_IRasterizer::RAS_STEREO_RIGHTEYE); + // XXXX Add constants here // Check for errors @@ -2328,7 +2362,7 @@ PyObject *initRasterizer(RAS_IRasterizer* rasty,RAS_ICanvas* canvas) Py_FatalError("can't initialize module Rasterizer"); } - return d; + return m; } @@ -2337,13 +2371,14 @@ PyObject *initRasterizer(RAS_IRasterizer* rasty,RAS_ICanvas* canvas) /* GameKeys: symbolic constants for key mapping */ /* ------------------------------------------------------------------------- */ -static char GameKeys_module_documentation[] = +PyDoc_STRVAR(GameKeys_module_documentation, "This modules provides defines for key-codes" -; +); -static char gPyEventToString_doc[] = -"EventToString(event) - Take a valid event from the GameKeys module or Keyboard Sensor and return a name" -; +PyDoc_STRVAR(gPyEventToString_doc, +"EventToString(event)\n" +"Take a valid event from the GameKeys module or Keyboard Sensor and return a name" +); static PyObject *gPyEventToString(PyObject *, PyObject *value) { @@ -2371,9 +2406,11 @@ static PyObject *gPyEventToString(PyObject *, PyObject *value) return ret; } -static char gPyEventToCharacter_doc[] = -"EventToCharacter(event, is_shift) - Take a valid event from the GameKeys module or Keyboard Sensor and return a character" -; + +PyDoc_STRVAR(gPyEventToCharacter_doc, +"EventToCharacter(event, is_shift)\n" +"Take a valid event from the GameKeys module or Keyboard Sensor and return a character" +); static PyObject *gPyEventToCharacter(PyObject *, PyObject *args) { @@ -2399,7 +2436,7 @@ static struct PyMethodDef gamekeys_methods[] = { }; static struct PyModuleDef GameKeys_module_def = { - {}, /* m_base */ + PyModuleDef_HEAD_INIT, "GameKeys", /* m_name */ GameKeys_module_documentation, /* m_doc */ 0, /* m_size */ @@ -2410,24 +2447,13 @@ static struct PyModuleDef GameKeys_module_def = { 0, /* m_free */ }; -PyObject *initGameKeys() +PyMODINIT_FUNC initGameKeysPythonBinding() { PyObject *m; PyObject *d; - - /* Use existing module where possible */ - m = PyImport_ImportModule( "GameKeys" ); - if (m) { - Py_DECREF(m); - return m; - } - else { - PyErr_Clear(); - - // Create the module and add the functions - m = PyModule_Create(&GameKeys_module_def); - PyDict_SetItemString(PySys_GetObject("modules"), GameKeys_module_def.m_name, m); - } + + m = PyModule_Create(&GameKeys_module_def); + PyDict_SetItemString(PySys_GetObject("modules"), GameKeys_module_def.m_name, m); // Add some symbolic constants to the module d = PyModule_GetDict(m); @@ -2572,7 +2598,7 @@ PyObject *initGameKeys() Py_FatalError("can't initialize module GameKeys"); } - return d; + return m; } // utility function for loading and saving the globalDict diff --git a/source/gameengine/Ketsji/KX_PythonInit.h b/source/gameengine/Ketsji/KX_PythonInit.h index 719a74ee219..f74a4b3865f 100644 --- a/source/gameengine/Ketsji/KX_PythonInit.h +++ b/source/gameengine/Ketsji/KX_PythonInit.h @@ -36,6 +36,9 @@ #include "STR_String.h" #include "MT_Vector3.h" +class KX_KetsjiEngine; +class KX_Scene; + typedef enum { psl_Lowest = 0, psl_Highest, @@ -44,13 +47,13 @@ typedef enum { extern bool gUseVisibilityTemp; #ifdef WITH_PYTHON -PyObject *initGameLogic(class KX_KetsjiEngine *engine, class KX_Scene *ketsjiscene); -PyObject *initGameKeys(); -PyObject *initRasterizer(class RAS_IRasterizer *rasty,class RAS_ICanvas *canvas); -PyObject *initGamePlayerPythonScripting(const STR_String &progname, TPythonSecurityLevel level, - struct Main *maggie, int argc, char **argv); -PyObject *initVideoTexture(void); -PyObject *initGamePythonScripting(const STR_String &progname, TPythonSecurityLevel level, struct Main *maggie); +PyMODINIT_FUNC initBGE(void); +PyMODINIT_FUNC initGameLogicPythonBinding(void); +PyMODINIT_FUNC initGameKeysPythonBinding(void); +PyMODINIT_FUNC initRasterizerPythonBinding(void); +PyMODINIT_FUNC initVideoTexturePythonBinding(void); +PyObject *initGamePlayerPythonScripting(struct Main *maggie, int argc, char **argv); +PyObject *initGamePythonScripting(struct Main *maggie); void exitGamePlayerPythonScripting(); void exitGamePythonScripting(); @@ -69,9 +72,9 @@ void removeImportMain(struct Main *maggie); class KX_KetsjiEngine; class KX_Scene; -void KX_SetActiveScene(class KX_Scene *scene); -class KX_Scene *KX_GetActiveScene(); -class KX_KetsjiEngine *KX_GetActiveEngine(); +void KX_SetActiveScene(KX_Scene *scene); +KX_Scene *KX_GetActiveScene(); +KX_KetsjiEngine *KX_GetActiveEngine(); typedef int (*PyNextFrameFunc)(void *); diff --git a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp index 7d38ce58eee..828fd62f205 100644 --- a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp +++ b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp @@ -163,21 +163,34 @@ static void PyType_Ready_ADD(PyObject *dict, PyTypeObject *tp, PyAttributeDef *a #define PyType_Ready_Attr(d, n, i) PyType_Ready_ADD(d, &n::Type, n::Attributes, NULL, i) #define PyType_Ready_AttrPtr(d, n, i) PyType_Ready_ADD(d, &n::Type, n::Attributes, n::AttributesPtr, i) -void initPyTypes(void) + + +PyDoc_STRVAR(GameTypes_module_documentation, +"This module provides access to the game engine data types." +); +static struct PyModuleDef GameTypes_module_def = { + PyModuleDef_HEAD_INIT, + "GameTypes", /* m_name */ + GameTypes_module_documentation, /* m_doc */ + 0, /* m_size */ + NULL, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; + + +PyMODINIT_FUNC initGameTypesPythonBinding(void) { + PyObject *m; + PyObject *dict; -/* - * initPyObjectPlusType(BL_ActionActuator::Parents); - * ..... - */ + m = PyModule_Create(&GameTypes_module_def); + PyDict_SetItemString(PySys_GetObject("modules"), GameTypes_module_def.m_name, m); + + dict = PyModule_GetDict(m); - /* For now just do PyType_Ready */ - PyObject *mod = PyModule_New("GameTypes"); - PyObject *dict = PyModule_GetDict(mod); - PyDict_SetItemString(PySys_GetObject("modules"), "GameTypes", mod); - Py_DECREF(mod); - - for (int init_getset= 1; init_getset > -1; init_getset--) { /* run twice, once to init the getsets another to run PyType_Ready */ PyType_Ready_Attr(dict, BL_ActionActuator, init_getset); PyType_Ready_Attr(dict, BL_Shader, init_getset); @@ -267,6 +280,8 @@ void initPyTypes(void) KX_GameObject_Mathutils_Callback_Init(); KX_ObjectActuator_Mathutils_Callback_Init(); #endif + + return m; } #endif // WITH_PYTHON diff --git a/source/gameengine/Ketsji/KX_PythonInitTypes.h b/source/gameengine/Ketsji/KX_PythonInitTypes.h index d8ee4f75fdd..cfc49a1dc93 100644 --- a/source/gameengine/Ketsji/KX_PythonInitTypes.h +++ b/source/gameengine/Ketsji/KX_PythonInitTypes.h @@ -33,7 +33,8 @@ #define __KX_PYTHON_INIT_TYPES__ #ifdef WITH_PYTHON -void initPyTypes(void); +#include <Python.h> +PyMODINIT_FUNC initGameTypesPythonBinding(void); #endif #endif /* __KX_PYTHON_INIT_TYPES__ */ diff --git a/source/gameengine/Ketsji/KX_TouchEventManager.cpp b/source/gameengine/Ketsji/KX_TouchEventManager.cpp index 7ec379eec26..eb774960d41 100644 --- a/source/gameengine/Ketsji/KX_TouchEventManager.cpp +++ b/source/gameengine/Ketsji/KX_TouchEventManager.cpp @@ -60,7 +60,7 @@ bool KX_TouchEventManager::NewHandleCollision(void* object1, void* object2, cons PHY_IPhysicsController* obj1 = static_cast<PHY_IPhysicsController*>(object1); PHY_IPhysicsController* obj2 = static_cast<PHY_IPhysicsController*>(object2); - m_newCollisions.insert(std::pair<PHY_IPhysicsController*, PHY_IPhysicsController*>(obj1, obj2)); + m_newCollisions.insert(NewCollision(obj1, obj2, coll_data)); return false; } @@ -209,9 +209,11 @@ void KX_TouchEventManager::NextFrame() } } // Run python callbacks - kxObj1->RunCollisionCallbacks(kxObj2); - kxObj2->RunCollisionCallbacks(kxObj1); + PHY_CollData *colldata = cit->colldata; + kxObj1->RunCollisionCallbacks(kxObj2, colldata->m_point1, colldata->m_normal); + kxObj2->RunCollisionCallbacks(kxObj1, colldata->m_point2, -colldata->m_normal); + delete cit->colldata; } m_newCollisions.clear(); @@ -219,3 +221,19 @@ void KX_TouchEventManager::NextFrame() for (it.begin();!it.end();++it) (*it)->Activate(m_logicmgr); } + + +KX_TouchEventManager::NewCollision::NewCollision(PHY_IPhysicsController *first, + PHY_IPhysicsController *second, + const PHY_CollData *colldata) + : first(first), second(second), colldata(new PHY_CollData(*colldata)) +{} + +KX_TouchEventManager::NewCollision::NewCollision(const NewCollision &to_copy) + : first(to_copy.first), second(to_copy.second), colldata(to_copy.colldata) +{} + +bool KX_TouchEventManager::NewCollision::operator<(const NewCollision &other) const +{ + return first < other.first || second < other.second || colldata < other.colldata; +} diff --git a/source/gameengine/Ketsji/KX_TouchEventManager.h b/source/gameengine/Ketsji/KX_TouchEventManager.h index bd4903c1545..d9c6fdad307 100644 --- a/source/gameengine/Ketsji/KX_TouchEventManager.h +++ b/source/gameengine/Ketsji/KX_TouchEventManager.h @@ -45,7 +45,29 @@ class PHY_IPhysicsEnvironment; class KX_TouchEventManager : public SCA_EventManager { - typedef std::pair<PHY_IPhysicsController*, PHY_IPhysicsController*> NewCollision; + /** + * Contains two colliding objects and the first contact point. + */ + class NewCollision { + public: + PHY_IPhysicsController *first; + PHY_IPhysicsController *second; + PHY_CollData *colldata; + + /** + * Creates a copy of the given PHY_CollData; freeing that copy should be done by the owner of + * the NewCollision object. + * + * This allows us to efficiently store NewCollision objects in a std::set without creating more + * copies of colldata, as the NewCollision copy constructor reuses the pointer and doesn't clone + * it again. */ + NewCollision(PHY_IPhysicsController *first, + PHY_IPhysicsController *second, + const PHY_CollData *colldata); + NewCollision(const NewCollision &to_copy); + bool operator<(const NewCollision &other) const; + }; + PHY_IPhysicsEnvironment* m_physEnv; std::set<NewCollision> m_newCollisions; diff --git a/source/gameengine/Ketsji/KX_VehicleWrapper.cpp b/source/gameengine/Ketsji/KX_VehicleWrapper.cpp index 535ed5ed39a..d10e51a491a 100644 --- a/source/gameengine/Ketsji/KX_VehicleWrapper.cpp +++ b/source/gameengine/Ketsji/KX_VehicleWrapper.cpp @@ -53,6 +53,22 @@ KX_VehicleWrapper::~KX_VehicleWrapper() #ifdef WITH_PYTHON + +static bool raise_exc_wheel(PHY_IVehicle* vehicle, int i, const char *method) +{ + if ( i < 0 || i >= vehicle->GetNumWheels() ) { + PyErr_Format(PyExc_ValueError, + "%s(...): wheel index %d out of range (0 to %d).", method, i, vehicle->GetNumWheels()-1); + return -1; + } else { + return 0; + } +} + +#define WHEEL_INDEX_CHECK_OR_RETURN(i, method) \ + if (raise_exc_wheel(m_vehicle, i, method) == -1) { return NULL; } (void)0 + + PyObject *KX_VehicleWrapper::PyAddWheel(PyObject *args) { @@ -67,22 +83,37 @@ PyObject *KX_VehicleWrapper::PyAddWheel(PyObject *args) KX_GameObject *gameOb; if (!ConvertPythonToGameObject(wheelGameObject, &gameOb, false, "vehicle.addWheel(...): KX_VehicleWrapper (first argument)")) return NULL; - if (gameOb->GetSGNode()) { PHY_IMotionState* motionState = new KX_MotionState(gameOb->GetSGNode()); - /* TODO - no error checking here! - bad juju */ MT_Vector3 attachPos,attachDir,attachAxle; - PyVecTo(pylistPos,attachPos); - PyVecTo(pylistDir,attachDir); - PyVecTo(pylistAxleDir,attachAxle); + if(!PyVecTo(pylistPos,attachPos)) { + PyErr_SetString(PyExc_AttributeError, + "addWheel(...) Unable to add wheel. attachPos must be a vector with 3 elements."); + return NULL; + } + if(!PyVecTo(pylistDir,attachDir)) { + PyErr_SetString(PyExc_AttributeError, + "addWheel(...) Unable to add wheel. downDir must be a vector with 3 elements."); + return NULL; + } + if(!PyVecTo(pylistAxleDir,attachAxle)) { + PyErr_SetString(PyExc_AttributeError, + "addWheel(...) Unable to add wheel. axleDir must be a vector with 3 elements."); + return NULL; + } //someone reverse some conventions inside Bullet (axle winding) attachAxle = -attachAxle; - printf("attempt for addWheel: suspensionRestLength%f wheelRadius %f, hasSteering:%d\n",suspensionRestLength,wheelRadius,hasSteering); + if(wheelRadius<=0) { + PyErr_SetString(PyExc_AttributeError, + "addWheel(...) Unable to add wheel. wheelRadius must be positive."); + return NULL; + } + m_vehicle->AddWheel(motionState,attachPos,attachDir,attachAxle,suspensionRestLength,wheelRadius,hasSteering); } @@ -93,8 +124,6 @@ PyObject *KX_VehicleWrapper::PyAddWheel(PyObject *args) } - - PyObject *KX_VehicleWrapper::PyGetWheelPosition(PyObject *args) { @@ -103,6 +132,8 @@ PyObject *KX_VehicleWrapper::PyGetWheelPosition(PyObject *args) if (PyArg_ParseTuple(args,"i:getWheelPosition",&wheelIndex)) { float position[3]; + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "getWheelPosition"); + m_vehicle->GetWheelPosition(wheelIndex,position[0],position[1],position[2]); MT_Vector3 pos(position[0],position[1],position[2]); return PyObjectFrom(pos); @@ -115,6 +146,8 @@ PyObject *KX_VehicleWrapper::PyGetWheelRotation(PyObject *args) int wheelIndex; if (PyArg_ParseTuple(args,"i:getWheelRotation",&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "getWheelRotation"); + return PyFloat_FromDouble(m_vehicle->GetWheelRotation(wheelIndex)); } return NULL; @@ -126,6 +159,8 @@ PyObject *KX_VehicleWrapper::PyGetWheelOrientationQuaternion(PyObject *args) if (PyArg_ParseTuple(args,"i:getWheelOrientationQuaternion",&wheelIndex)) { float orn[4]; + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "getWheelOrientationQuaternion"); + m_vehicle->GetWheelOrientationQuaternion(wheelIndex,orn[0],orn[1],orn[2],orn[3]); MT_Quaternion quatorn(orn[0],orn[1],orn[2],orn[3]); MT_Matrix3x3 ornmat(quatorn); @@ -148,7 +183,6 @@ PyObject *KX_VehicleWrapper::PyGetConstraintId(PyObject *args) } - PyObject *KX_VehicleWrapper::PyApplyEngineForce(PyObject *args) { float force; @@ -156,6 +190,8 @@ PyObject *KX_VehicleWrapper::PyApplyEngineForce(PyObject *args) if (PyArg_ParseTuple(args,"fi:applyEngineForce",&force,&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "applyEngineForce"); + force *= -1.f;//someone reverse some conventions inside Bullet (axle winding) m_vehicle->ApplyEngineForce(force,wheelIndex); } @@ -172,6 +208,8 @@ PyObject *KX_VehicleWrapper::PySetTyreFriction(PyObject *args) if (PyArg_ParseTuple(args,"fi:setTyreFriction",&wheelFriction,&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "setTyreFriction"); + m_vehicle->SetWheelFriction(wheelFriction,wheelIndex); } else { @@ -187,6 +225,8 @@ PyObject *KX_VehicleWrapper::PySetSuspensionStiffness(PyObject *args) if (PyArg_ParseTuple(args,"fi:setSuspensionStiffness",&suspensionStiffness,&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "setSuspensionStiffness"); + m_vehicle->SetSuspensionStiffness(suspensionStiffness,wheelIndex); } else { @@ -202,6 +242,8 @@ PyObject *KX_VehicleWrapper::PySetSuspensionDamping(PyObject *args) if (PyArg_ParseTuple(args,"fi:setSuspensionDamping",&suspensionDamping,&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "setSuspensionDamping"); + m_vehicle->SetSuspensionDamping(suspensionDamping,wheelIndex); } else { return NULL; @@ -216,6 +258,8 @@ PyObject *KX_VehicleWrapper::PySetSuspensionCompression(PyObject *args) if (PyArg_ParseTuple(args,"fi:setSuspensionCompression",&suspensionCompression,&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "setSuspensionCompression"); + m_vehicle->SetSuspensionCompression(suspensionCompression,wheelIndex); } else { return NULL; @@ -230,6 +274,8 @@ PyObject *KX_VehicleWrapper::PySetRollInfluence(PyObject *args) if (PyArg_ParseTuple(args,"fi:setRollInfluence",&rollInfluence,&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "setRollInfluence"); + m_vehicle->SetRollInfluence(rollInfluence,wheelIndex); } else { @@ -246,6 +292,8 @@ PyObject *KX_VehicleWrapper::PyApplyBraking(PyObject *args) if (PyArg_ParseTuple(args,"fi:applyBraking",&braking,&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "applyBraking"); + m_vehicle->ApplyBraking(braking,wheelIndex); } else { @@ -255,8 +303,6 @@ PyObject *KX_VehicleWrapper::PyApplyBraking(PyObject *args) } - - PyObject *KX_VehicleWrapper::PySetSteeringValue(PyObject *args) { float steeringValue; @@ -264,6 +310,8 @@ PyObject *KX_VehicleWrapper::PySetSteeringValue(PyObject *args) if (PyArg_ParseTuple(args,"fi:setSteeringValue",&steeringValue,&wheelIndex)) { + WHEEL_INDEX_CHECK_OR_RETURN(wheelIndex, "setSteeringValue"); + m_vehicle->SetSteeringValue(steeringValue,wheelIndex); } else { @@ -316,17 +364,11 @@ PyMethodDef KX_VehicleWrapper::Methods[] = { {"setSteeringValue",(PyCFunction) KX_VehicleWrapper::sPySetSteeringValue, METH_VARARGS}, {"applyEngineForce",(PyCFunction) KX_VehicleWrapper::sPyApplyEngineForce, METH_VARARGS}, {"applyBraking",(PyCFunction) KX_VehicleWrapper::sPyApplyBraking, METH_VARARGS}, - {"setTyreFriction",(PyCFunction) KX_VehicleWrapper::sPySetTyreFriction, METH_VARARGS}, - {"setSuspensionStiffness",(PyCFunction) KX_VehicleWrapper::sPySetSuspensionStiffness, METH_VARARGS}, - {"setSuspensionDamping",(PyCFunction) KX_VehicleWrapper::sPySetSuspensionDamping, METH_VARARGS}, - {"setSuspensionCompression",(PyCFunction) KX_VehicleWrapper::sPySetSuspensionCompression, METH_VARARGS}, - {"setRollInfluence",(PyCFunction) KX_VehicleWrapper::sPySetRollInfluence, METH_VARARGS}, - {NULL,NULL} //Sentinel }; diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index 38e7df6c573..f450e3ac12f 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -174,26 +174,27 @@ public: virtual void GetWheelPosition(int wheelIndex,float& posX,float& posY,float& posZ) const { - btTransform trans = m_vehicle->getWheelTransformWS(wheelIndex); - posX = trans.getOrigin().x(); - posY = trans.getOrigin().y(); - posZ = trans.getOrigin().z(); + if ((wheelIndex>=0) && (wheelIndex< m_vehicle->getNumWheels())) + { + btVector3 origin = m_vehicle->getWheelTransformWS(wheelIndex).getOrigin(); + + posX = origin.x(); + posY = origin.y(); + posZ = origin.z(); + } } + virtual void GetWheelOrientationQuaternion(int wheelIndex,float& quatX,float& quatY,float& quatZ,float& quatW) const { - btTransform trans = m_vehicle->getWheelTransformWS(wheelIndex); - btQuaternion quat = trans.getRotation(); - btMatrix3x3 orn2(quat); - - quatX = trans.getRotation().x(); - quatY = trans.getRotation().y(); - quatZ = trans.getRotation().z(); - quatW = trans.getRotation()[3]; - - - //printf("test"); - + if ((wheelIndex>=0) && (wheelIndex< m_vehicle->getNumWheels())) + { + btQuaternion quat = m_vehicle->getWheelTransformWS(wheelIndex).getRotation(); + quatX = quat.x(); + quatY = quat.y(); + quatZ = quat.z(); + quatW = quat.w(); + } } virtual float GetWheelRotation(int wheelIndex) const @@ -205,8 +206,8 @@ public: btWheelInfo& info = m_vehicle->getWheelInfo(wheelIndex); rotation = info.m_rotation; } - return rotation; + return rotation; } @@ -223,12 +224,16 @@ public: virtual void SetSteeringValue(float steering,int wheelIndex) { - m_vehicle->setSteeringValue(steering,wheelIndex); + if ((wheelIndex>=0) && (wheelIndex< m_vehicle->getNumWheels())) { + m_vehicle->setSteeringValue(steering,wheelIndex); + } } virtual void ApplyEngineForce(float force,int wheelIndex) { - m_vehicle->applyEngineForce(force,wheelIndex); + if ((wheelIndex>=0) && (wheelIndex< m_vehicle->getNumWheels())) { + m_vehicle->applyEngineForce(force,wheelIndex); + } } virtual void ApplyBraking(float braking,int wheelIndex) @@ -2251,64 +2256,77 @@ bool CcdPhysicsEnvironment::RequestCollisionCallback(PHY_IPhysicsController* ctr void CcdPhysicsEnvironment::CallbackTriggers() { - if (m_triggerCallbacks[PHY_OBJECT_RESPONSE] || (m_debugDrawer && (m_debugDrawer->getDebugMode() & btIDebugDraw::DBG_DrawContactPoints))) - { - //walk over all overlapping pairs, and if one of the involved bodies is registered for trigger callback, perform callback - btDispatcher* dispatcher = m_dynamicsWorld->getDispatcher(); - int numManifolds = dispatcher->getNumManifolds(); - for (int i=0;i<numManifolds;i++) - { - btPersistentManifold* manifold = dispatcher->getManifoldByIndexInternal(i); - int numContacts = manifold->getNumContacts(); - if (numContacts) - { - const btRigidBody* rb0 = static_cast<const btRigidBody*>(manifold->getBody0()); - const btRigidBody* rb1 = static_cast<const btRigidBody*>(manifold->getBody1()); - if (m_debugDrawer && (m_debugDrawer->getDebugMode() & btIDebugDraw::DBG_DrawContactPoints)) - { - for (int j=0;j<numContacts;j++) - { - btVector3 color(1,0,0); - const btManifoldPoint& cp = manifold->getContactPoint(j); - if (m_debugDrawer) - m_debugDrawer->drawContactPoint(cp.m_positionWorldOnB,cp.m_normalWorldOnB,cp.getDistance(),cp.getLifeTime(),color); - } - } - const btRigidBody* obj0 = rb0; - const btRigidBody* obj1 = rb1; + bool draw_contact_points = m_debugDrawer && (m_debugDrawer->getDebugMode() & btIDebugDraw::DBG_DrawContactPoints); - //m_internalOwner is set in 'addPhysicsController' - CcdPhysicsController* ctrl0 = static_cast<CcdPhysicsController*>(obj0->getUserPointer()); - CcdPhysicsController* ctrl1 = static_cast<CcdPhysicsController*>(obj1->getUserPointer()); + if (!m_triggerCallbacks[PHY_OBJECT_RESPONSE] && !draw_contact_points) + return; - std::set<CcdPhysicsController*>::const_iterator i = m_triggerControllers.find(ctrl0); - if (i == m_triggerControllers.end()) - { - i = m_triggerControllers.find(ctrl1); - } + //walk over all overlapping pairs, and if one of the involved bodies is registered for trigger callback, perform callback + btDispatcher* dispatcher = m_dynamicsWorld->getDispatcher(); + int numManifolds = dispatcher->getNumManifolds(); + for (int i=0;i<numManifolds;i++) + { + bool colliding_ctrl0 = true; + btPersistentManifold* manifold = dispatcher->getManifoldByIndexInternal(i); + int numContacts = manifold->getNumContacts(); + if (!numContacts) continue; - if (!(i == m_triggerControllers.end())) - { - m_triggerCallbacks[PHY_OBJECT_RESPONSE](m_triggerCallbacksUserPtrs[PHY_OBJECT_RESPONSE], - ctrl0,ctrl1,0); - } - // Bullet does not refresh the manifold contact point for object without contact response - // may need to remove this when a newer Bullet version is integrated - if (!dispatcher->needsResponse(rb0, rb1)) - { - // Refresh algorithm fails sometimes when there is penetration - // (usuall the case with ghost and sensor objects) - // Let's just clear the manifold, in any case, it is recomputed on each frame. - manifold->clearManifold(); //refreshContactPoints(rb0->getCenterOfMassTransform(),rb1->getCenterOfMassTransform()); - } + const btRigidBody* rb0 = static_cast<const btRigidBody*>(manifold->getBody0()); + const btRigidBody* rb1 = static_cast<const btRigidBody*>(manifold->getBody1()); + if (draw_contact_points) + { + for (int j=0;j<numContacts;j++) + { + btVector3 color(1,1,0); + const btManifoldPoint& cp = manifold->getContactPoint(j); + m_debugDrawer->drawContactPoint(cp.m_positionWorldOnB, + cp.m_normalWorldOnB, + cp.getDistance(), + cp.getLifeTime(), + color); } } + //m_internalOwner is set in 'addPhysicsController' + CcdPhysicsController* ctrl0 = static_cast<CcdPhysicsController*>(rb0->getUserPointer()); + CcdPhysicsController* ctrl1 = static_cast<CcdPhysicsController*>(rb1->getUserPointer()); + std::set<CcdPhysicsController*>::const_iterator iter = m_triggerControllers.find(ctrl0); + if (iter == m_triggerControllers.end()) + { + iter = m_triggerControllers.find(ctrl1); + colliding_ctrl0 = false; + } - } + if (iter != m_triggerControllers.end()) + { + static PHY_CollData coll_data; + const btManifoldPoint &cp = manifold->getContactPoint(0); + /* Make sure that "point1" is always on the object we report on, and + * "point2" on the other object. Also ensure the normal is oriented + * correctly. */ + btVector3 point1 = colliding_ctrl0 ? cp.m_positionWorldOnA : cp.m_positionWorldOnB; + btVector3 point2 = colliding_ctrl0 ? cp.m_positionWorldOnB : cp.m_positionWorldOnA; + btVector3 normal = colliding_ctrl0 ? -cp.m_normalWorldOnB : cp.m_normalWorldOnB; + coll_data.m_point1 = MT_Vector3(point1.m_floats); + coll_data.m_point2 = MT_Vector3(point2.m_floats); + coll_data.m_normal = MT_Vector3(normal.m_floats); + + m_triggerCallbacks[PHY_OBJECT_RESPONSE](m_triggerCallbacksUserPtrs[PHY_OBJECT_RESPONSE], + ctrl0, ctrl1, &coll_data); + } + // Bullet does not refresh the manifold contact point for object without contact response + // may need to remove this when a newer Bullet version is integrated + if (!dispatcher->needsResponse(rb0, rb1)) + { + // Refresh algorithm fails sometimes when there is penetration + // (usuall the case with ghost and sensor objects) + // Let's just clear the manifold, in any case, it is recomputed on each frame. + manifold->clearManifold(); //refreshContactPoints(rb0->getCenterOfMassTransform(),rb1->getCenterOfMassTransform()); + } + } } // This call back is called before a pair is added in the cache @@ -2999,7 +3017,8 @@ struct BlenderDebugDraw : public btIDebugDraw virtual void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,float distance,int lifeTime,const btVector3& color) { - //not yet + drawLine(PointOnB, PointOnB + normalOnB, color); + drawSphere(PointOnB, 0.1, color); } virtual void setDebugMode(int debugMode) diff --git a/source/gameengine/VideoTexture/ImageBase.cpp b/source/gameengine/VideoTexture/ImageBase.cpp index 0d46160a11e..23da528009d 100644 --- a/source/gameengine/VideoTexture/ImageBase.cpp +++ b/source/gameengine/VideoTexture/ImageBase.cpp @@ -713,25 +713,18 @@ static int Image_getbuffer(PyImage *self, Py_buffer *view, int flags) unsigned int * image; int ret; - try - { + try { // can throw in case of resize image = self->m_image->getImage(); } - catch (Exception & exp) - { - // cannot return -1, this creates a crash in Python, for now we will just return an empty buffer + catch (Exception & exp) { exp.report(); - //return -1; - goto error; + return -1; } - if (!image) - { - // same remark, see above - //PyErr_SetString(PyExc_BufferError, "Image buffer is not available"); - //return -1; - goto error; + if (!image) { + PyErr_SetString(PyExc_BufferError, "Image buffer is not available"); + return -1; } if (view == NULL) { @@ -742,17 +735,6 @@ static int Image_getbuffer(PyImage *self, Py_buffer *view, int flags) if (ret >= 0) self->m_image->m_exports++; return ret; - -error: - // Return a empty buffer to avoid a crash in Python 3.1 - // The bug is fixed in Python SVN 77916, as soon as the python revision used by Blender is - // updated, you can simply return -1 and set the error - static char* buf = (char *)""; - ret = PyBuffer_FillInfo(view, (PyObject *)self, buf, 0, 0, flags); - if (ret >= 0) - self->m_image->m_exports++; - return ret; - } static void Image_releaseBuffer(PyImage *self, Py_buffer *buffer) diff --git a/source/gameengine/VideoTexture/blendVideoTex.cpp b/source/gameengine/VideoTexture/blendVideoTex.cpp index ab6dca0958f..6c9294873a6 100644 --- a/source/gameengine/VideoTexture/blendVideoTex.cpp +++ b/source/gameengine/VideoTexture/blendVideoTex.cpp @@ -157,10 +157,14 @@ static void registerAllTypes(void) pyFilterTypes.add(&FilterBGR24Type, "FilterBGR24"); } +PyDoc_STRVAR(VideoTexture_module_documentation, +"Module that allows to play video files on textures in GameBlender." +); + static struct PyModuleDef VideoTexture_module_def = { - {}, /* m_base */ + PyModuleDef_HEAD_INIT, "VideoTexture", /* m_name */ - "Module that allows to play video files on textures in GameBlender.", /* m_doc */ + VideoTexture_module_documentation, /* m_doc */ 0, /* m_size */ moduleMethods, /* m_methods */ 0, /* m_reload */ @@ -169,7 +173,7 @@ static struct PyModuleDef VideoTexture_module_def = { 0, /* m_free */ }; -PyObject *initVideoTexture(void) +PyMODINIT_FUNC initVideoTexturePythonBinding(void) { PyObject *m; @@ -187,20 +191,9 @@ PyObject *initVideoTexture(void) if (PyType_Ready(&TextureType) < 0) return NULL; - /* Use existing module where possible - * be careful not to init any runtime vars after this */ - m = PyImport_ImportModule( "VideoTexture" ); - if (m) { - Py_DECREF(m); - return m; - } - else { - PyErr_Clear(); - - m = PyModule_Create(&VideoTexture_module_def); - PyDict_SetItemString(PySys_GetObject("modules"), VideoTexture_module_def.m_name, m); - } - + m = PyModule_Create(&VideoTexture_module_def); + PyDict_SetItemString(PySys_GetObject("modules"), VideoTexture_module_def.m_name, m); + if (m == NULL) return NULL; |