diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2016-05-24 17:48:10 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2016-05-24 17:48:10 +0300 |
commit | faec4309147988fbab7b7d7ec661f5130358d169 (patch) | |
tree | 0c839f8f88fe80f4a3762980adb5efe729ce1b44 /source/blender/blenkernel | |
parent | f85745b17bfe68673bf5f799e98c617d9471ddf1 (diff) | |
parent | e1dd83b399d46d81ea51f6c41725eec5c1a1db7a (diff) |
Merge branch 'master' into blender2.8
Conflicts:
intern/cycles/blender/blender_curves.cpp
source/blender/blenkernel/intern/dynamicpaint.c
source/blender/blenkernel/intern/particle.c
source/blender/blenloader/intern/versioning_270.c
source/blender/editors/physics/particle_edit.c
source/blender/editors/transform/transform_snap_object.c
source/blender/editors/util/undo.c
source/blender/makesrna/intern/rna_object_force.c
Diffstat (limited to 'source/blender/blenkernel')
45 files changed, 3984 insertions, 2732 deletions
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 7419b182c04..2b13a847e14 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -181,7 +181,7 @@ struct DerivedMesh { int numVertData, numEdgeData, numTessFaceData, numLoopData, numPolyData; int needsFree; /* checked on ->release, is set to 0 for cached results */ int deformedOnly; /* set by modifier stack if only deformed from original */ - BVHCache bvhCache; + BVHCache *bvhCache; struct GPUDrawObject *drawObject; DerivedMeshType type; float auto_bump_scale; diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index 3fceef5d95d..cb282b46bec 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -79,12 +79,15 @@ typedef enum eAction_TransformFlags { ACT_TRANS_ROT = (1 << 1), /* scaling */ ACT_TRANS_SCALE = (1 << 2), - + + /* bbone shape - for all the parameters, provided one is set */ + ACT_TRANS_BBONE = (1 << 3), + /* strictly not a transform, but custom properties are also * quite often used in modern rigs */ - ACT_TRANS_PROP = (1 << 3), - + ACT_TRANS_PROP = (1 << 4), + /* all flags */ ACT_TRANS_ONLY = (ACT_TRANS_LOC | ACT_TRANS_ROT | ACT_TRANS_SCALE), ACT_TRANS_ALL = (ACT_TRANS_ONLY | ACT_TRANS_PROP) @@ -144,6 +147,8 @@ void BKE_pose_channels_remove( struct Object *ob, bool (*filter_fn)(const char *bone_name, void *user_data), void *user_data); +void BKE_pose_free_data_ex(struct bPose *pose, bool do_id_user); +void BKE_pose_free_data(struct bPose *pose); void BKE_pose_free(struct bPose *pose); void BKE_pose_free_ex(struct bPose *pose, bool do_id_user); void BKE_pose_copy_data(struct bPose **dst, struct bPose *src, const bool copy_constraints); diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 6d00110e318..cc082c084ce 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -135,6 +135,7 @@ typedef struct Mat4 { float mat[4][4]; } Mat4; +void equalize_bbone_bezier(float *data, int desired); void b_bone_spline_setup(struct bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BBONE_SUBDIV]); /* like EBONE_VISIBLE */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 5e7fdb91645..618b36c5851 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -28,7 +28,7 @@ * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 277 -#define BLENDER_SUBVERSION 0 +#define BLENDER_SUBVERSION 1 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 6 diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 08c0fcc0f3c..7bd3ca88076 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -39,10 +39,30 @@ */ struct DerivedMesh; +struct BMEditMesh; struct MVert; struct MFace; /** +* struct that kepts basic information about a BVHTree build from a editmesh +*/ +typedef struct BVHTreeFromEditMesh { + struct BVHTree *tree; + + /* default callbacks to bvh nearest and raycast */ + BVHTree_NearestPointCallback nearest_callback; + BVHTree_RayCastCallback raycast_callback; + BVHTree_NearestToRayCallback nearest_to_ray_callback; + + /* radius for raycast */ + float sphere_radius; + + /* Private data */ + struct BMEditMesh *em; + +} BVHTreeFromEditMesh; + +/** * struct that kepts basic information about a BVHTree build from a mesh */ typedef struct BVHTreeFromMesh { @@ -69,8 +89,6 @@ typedef struct BVHTreeFromMesh { float sphere_radius; /* Private data */ - void *em_evil; - bool em_evil_all; /* ignore selection/hidden state, adding all loops to the tree */ bool cached; } BVHTreeFromMesh; @@ -85,11 +103,19 @@ typedef struct BVHTreeFromMesh { * * free_bvhtree_from_mesh should be called when the tree is no longer needed. */ +BVHTree *bvhtree_from_editmesh_verts( + BVHTreeFromEditMesh *data, struct BMEditMesh *em, + float epsilon, int tree_type, int axis); +BVHTree *bvhtree_from_editmesh_verts_ex( + BVHTreeFromEditMesh *data, struct BMEditMesh *em, + const BLI_bitmap *mask, int verts_num_active, + float epsilon, int tree_type, int axis); + BVHTree *bvhtree_from_mesh_verts( struct BVHTreeFromMesh *data, struct DerivedMesh *mesh, float epsilon, int tree_type, int axis); BVHTree *bvhtree_from_mesh_verts_ex( struct BVHTreeFromMesh *data, struct MVert *vert, const int numVerts, - const bool vert_allocated, BLI_bitmap *mask, int numVerts_active, + const bool vert_allocated, const BLI_bitmap *mask, int verts_num_active, float epsilon, int tree_type, int axis); BVHTree *bvhtree_from_mesh_edges( @@ -103,7 +129,15 @@ BVHTree *bvhtree_from_mesh_faces_ex( struct BVHTreeFromMesh *data, struct MVert *vert, const bool vert_allocated, struct MFace *face, const int numFaces, const bool face_allocated, - BLI_bitmap *mask, int numFaces_active, + const BLI_bitmap *mask, int numFaces_active, + float epsilon, int tree_type, int axis); + +BVHTree *bvhtree_from_editmesh_looptri( + BVHTreeFromEditMesh *data, struct BMEditMesh *em, float epsilon, + int tree_type, int axis); +BVHTree *bvhtree_from_editmesh_looptri_ex( + BVHTreeFromEditMesh *data, struct BMEditMesh *em, + const BLI_bitmap *mask, int looptri_num_active, float epsilon, int tree_type, int axis); BVHTree *bvhtree_from_mesh_looptri( @@ -113,12 +147,13 @@ BVHTree *bvhtree_from_mesh_looptri_ex( const struct MVert *vert, const bool vert_allocated, const struct MLoop *mloop, const bool loop_allocated, const struct MLoopTri *looptri, const int looptri_num, const bool looptri_allocated, - BLI_bitmap *mask, int looptri_num_active, + const BLI_bitmap *mask, int looptri_num_active, float epsilon, int tree_type, int axis); /** * Frees data allocated by a call to bvhtree_from_mesh_*. */ +void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data); void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data); /** @@ -144,36 +179,14 @@ enum { BVHTREE_FROM_EDGES = 1, BVHTREE_FROM_FACES = 2, BVHTREE_FROM_LOOPTRI = 3, - /* all faces */ - BVHTREE_FROM_FACES_EDITMESH_ALL = 4, - /* visible unselected, only used for transform snapping */ - BVHTREE_FROM_FACES_EDITMESH_SNAP = 5, - // BVHTREE_FROM_EDGES_EDITMESH_SNAP = 6, - BVHTREE_FROM_VERTS_EDITMESH_SNAP = 7, }; -typedef struct LinkNode *BVHCache; +typedef struct LinkNode BVHCache; - -/** - * Queries a bvhcache for the cache bvhtree of the request type - */ BVHTree *bvhcache_find(BVHCache *cache, int type); - -/** - * Inserts a BVHTree of the given type under the cache - * After that the caller no longer needs to worry when to free the BVHTree - * as that will be done when the cache is freed. - * - * A call to this assumes that there was no previous cached tree of the given type - */ -void bvhcache_insert(BVHCache *cache, BVHTree *tree, int type); - -/** - * inits and frees a bvhcache - */ -void bvhcache_init(BVHCache *cache); -void bvhcache_free(BVHCache *cache); +bool bvhcache_has_tree(const BVHCache *cache, const BVHTree *tree); +void bvhcache_insert(BVHCache **cache_p, BVHTree *tree, int type); +void bvhcache_init(BVHCache **cache_p); +void bvhcache_free(BVHCache **cache_p); #endif - diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index 38ee690b857..ddc9fa530de 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -115,7 +115,7 @@ typedef struct ClothVertex { float mass; /* mass / weight of the vertex */ float goal; /* goal, from SB */ float impulse[3]; /* used in collision.c */ - float *xrest; /* temporary valid for building springs */ + float xrest[3]; /* rest position of the vertex */ unsigned int impulse_count; /* same as above */ float avg_spring_len; /* average length of connected springs */ float struct_stiff; diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 17ad51a7a16..2cdda34b9b5 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -244,7 +244,7 @@ void CustomData_free_elem(struct CustomData *data, int index, int count); */ void CustomData_interp( const struct CustomData *source, struct CustomData *dest, - int *src_indices, float *weights, float *sub_weights, + const int *src_indices, const float *weights, const float *sub_weights, int count, int dest_index); void CustomData_bmesh_interp_n( struct CustomData *data, const void **src_blocks, const float *weights, @@ -358,7 +358,7 @@ void CustomData_file_write_prepare( struct CustomDataLayer **r_write_layers, struct CustomDataLayer *write_layers_buff, size_t write_layers_size); /* query info over types */ -void CustomData_file_write_info(int type, const char **structname, int *structnum); +void CustomData_file_write_info(int type, const char **r_struct_name, int *r_struct_num); int CustomData_sizeof(int type); /* get the name of a layer type */ diff --git a/source/blender/blenkernel/BKE_dynamicpaint.h b/source/blender/blenkernel/BKE_dynamicpaint.h index 142ef1893ce..c552a618cd8 100644 --- a/source/blender/blenkernel/BKE_dynamicpaint.h +++ b/source/blender/blenkernel/BKE_dynamicpaint.h @@ -81,7 +81,7 @@ void dynamicPaint_resetPreview(struct DynamicPaintCanvasSettings *canvas); struct DynamicPaintSurface *get_activeSurface(struct DynamicPaintCanvasSettings *canvas); /* image sequence baking */ -int dynamicPaint_createUVSurface(struct Scene *scene, struct DynamicPaintSurface *surface); +int dynamicPaint_createUVSurface(struct Scene *scene, struct DynamicPaintSurface *surface, float *progress, short *do_update); int dynamicPaint_calculateFrame(struct DynamicPaintSurface *surface, struct Scene *scene, struct Object *cObject, int frame); void dynamicPaint_outputSurfaceImage(struct DynamicPaintSurface *surface, char *filename, short output_layer); diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index c7b49e00da8..727818383b1 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -119,7 +119,8 @@ void BKE_main_lib_objects_recalc_all(struct Main *bmain); /* (MAX_ID_NAME - 2) + 3 */ void BKE_id_ui_prefix(char name[66 + 1], const struct ID *id); -void BKE_library_make_local(struct Main *bmain, struct Library *lib, bool untagged_only, bool set_fake); +void BKE_library_make_local( + struct Main *bmain, const struct Library *lib, const bool untagged_only, const bool set_fake); typedef void (*BKE_library_free_window_manager_cb)(struct bContext *, struct wmWindowManager *); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index d7dd9ed3ac5..dff79b6cc22 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -114,6 +114,11 @@ void BKE_mesh_vert_loop_map_create( MeshElemMap **r_map, int **r_mem, const struct MPoly *mface, const struct MLoop *mloop, int totvert, int totface, int totloop); +void BKE_mesh_vert_looptri_map_create( + MeshElemMap **r_map, int **r_mem, + const struct MVert *mvert, const int totvert, + const struct MLoopTri *mlooptri, const int totlooptri, + const struct MLoop *mloop, const int totloop); void BKE_mesh_vert_edge_map_create( MeshElemMap **r_map, int **r_mem, const struct MEdge *medge, int totvert, int totedge); diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index c6f8befe964..ab0b120faf3 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -101,7 +101,8 @@ typedef enum { eModifierTypeFlag_NoUserAdd = (1 << 8), /* For modifiers that use CD_PREVIEW_MCOL for preview. */ - eModifierTypeFlag_UsesPreview = (1 << 9) + eModifierTypeFlag_UsesPreview = (1 << 9), + eModifierTypeFlag_AcceptsLattice = (1 << 10), } ModifierTypeFlag; /* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2ece0f7a028..76e49566d19 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -642,12 +642,12 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree); * Examples: * * \code{.c} - * FOREACH_NODETREE(bmain, nodetree) { + * FOREACH_NODETREE(bmain, nodetree, id) { * if (id == nodetree) * printf("This is a linkable node tree"); * } FOREACH_NODETREE_END * - * FOREACH_NODETREE(bmain, nodetree) { + * FOREACH_NODETREE(bmain, nodetree, id) { * if (nodetree->idname == "ShaderNodeTree") * printf("This is a shader node tree); * if (GS(id) == ID_MA) diff --git a/source/blender/blenkernel/BKE_sketch.h b/source/blender/blenkernel/BKE_sketch.h index c4cfcba4b35..9b9c125fbe6 100644 --- a/source/blender/blenkernel/BKE_sketch.h +++ b/source/blender/blenkernel/BKE_sketch.h @@ -82,7 +82,6 @@ typedef struct SK_Intersection { typedef struct SK_Sketch { ListBase strokes; - ListBase depth_peels; SK_Stroke *active_stroke; SK_Stroke *gesture; SK_Point next_point; diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h index b351bc6fe3e..278b303a315 100644 --- a/source/blender/blenkernel/BKE_unit.h +++ b/source/blender/blenkernel/BKE_unit.h @@ -54,11 +54,11 @@ bool bUnit_IsValid(int system, int type); /* loop over scales, coudl add names later */ //double bUnit_Iter(void **unit, char **name, int system, int type); -void bUnit_GetSystem(void **usys_pt, int *len, int system, int type); -int bUnit_GetBaseUnit(void *usys_pt); -const char *bUnit_GetName(void *usys_pt, int index); -const char *bUnit_GetNameDisplay(void *usys_pt, int index); -double bUnit_GetScaler(void *usys_pt, int index); +void bUnit_GetSystem(int system, int type, void const **r_usys_pt, int *r_len); +int bUnit_GetBaseUnit(const void *usys_pt); +const char *bUnit_GetName(const void *usys_pt, int index); +const char *bUnit_GetNameDisplay(const void *usys_pt, int index); +double bUnit_GetScaler(const void *usys_pt, int index); /* aligned with PropertyUnit */ enum { diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index a32fd99aa04..bcc3b60b34e 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -647,13 +647,15 @@ void DM_generate_tangent_tessface_data(DerivedMesh *dm, bool generate) void DM_update_materials(DerivedMesh *dm, Object *ob) { int i, totmat = ob->totcol + 1; /* materials start from 1, default material is 0 */ - dm->totmat = totmat; - /* invalidate old materials */ - if (dm->mat) - MEM_freeN(dm->mat); + if (dm->totmat != totmat) { + dm->totmat = totmat; + /* invalidate old materials */ + if (dm->mat) + MEM_freeN(dm->mat); - dm->mat = MEM_callocN(totmat * sizeof(*dm->mat), "DerivedMesh.mat"); + dm->mat = MEM_mallocN(totmat * sizeof(*dm->mat), "DerivedMesh.mat"); + } /* we leave last material as empty - rationale here is being able to index * the materials by using the mf->mat_nr directly and leaving the last @@ -661,6 +663,7 @@ void DM_update_materials(DerivedMesh *dm, Object *ob) for (i = 0; i < totmat - 1; i++) { dm->mat[i] = give_current_material(ob, i + 1); } + dm->mat[i] = NULL; } MLoopUV *DM_paint_uvlayer_active_get(DerivedMesh *dm, int mat_nr) @@ -3626,12 +3629,41 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, dm->calcLoopTangents(dm, false, (const char (*)[MAX_NAME])tangent_names, tangent_names_count); for (b = 0; b < gattribs->totlayer; b++) { - if (gattribs->layer[b].type == CD_MTFACE) { + int type = gattribs->layer[b].type; + layer = -1; + if (type == CD_AUTO_FROM_NAME) { + /* We need to deduct what exact layer is used. + * + * We do it based on the specified name. + */ + if (gattribs->layer[b].name[0]) { + layer = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, gattribs->layer[b].name); + type = CD_TANGENT; + if (layer == -1) { + layer = CustomData_get_named_layer_index(ldata, CD_MLOOPCOL, gattribs->layer[b].name); + type = CD_MCOL; + } + if (layer == -1) { + layer = CustomData_get_named_layer_index(ldata, CD_MLOOPUV, gattribs->layer[b].name); + type = CD_MTFACE; + } + if (layer == -1) { + continue; + } + } + else { + /* Fall back to the UV layer, which matches old behavior. */ + type = CD_MTFACE; + } + } + if (type == CD_MTFACE) { /* uv coordinates */ - if (gattribs->layer[b].name[0]) - layer = CustomData_get_named_layer_index(ldata, CD_MLOOPUV, gattribs->layer[b].name); - else - layer = CustomData_get_active_layer_index(ldata, CD_MLOOPUV); + if (layer == -1) { + if (gattribs->layer[b].name[0]) + layer = CustomData_get_named_layer_index(ldata, CD_MLOOPUV, gattribs->layer[b].name); + else + layer = CustomData_get_active_layer_index(ldata, CD_MLOOPUV); + } a = attribs->tottface++; @@ -3647,11 +3679,13 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, attribs->tface[a].gl_index = gattribs->layer[b].glindex; attribs->tface[a].gl_texco = gattribs->layer[b].gltexco; } - else if (gattribs->layer[b].type == CD_MCOL) { - if (gattribs->layer[b].name[0]) - layer = CustomData_get_named_layer_index(ldata, CD_MLOOPCOL, gattribs->layer[b].name); - else - layer = CustomData_get_active_layer_index(ldata, CD_MLOOPCOL); + else if (type == CD_MCOL) { + if (layer == -1) { + if (gattribs->layer[b].name[0]) + layer = CustomData_get_named_layer_index(ldata, CD_MLOOPCOL, gattribs->layer[b].name); + else + layer = CustomData_get_active_layer_index(ldata, CD_MLOOPCOL); + } a = attribs->totmcol++; @@ -3667,13 +3701,14 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, attribs->mcol[a].gl_index = gattribs->layer[b].glindex; } - else if (gattribs->layer[b].type == CD_TANGENT) { + else if (type == CD_TANGENT) { /* note, even with 'is_editmesh' this uses the derived-meshes loop data */ - - if (gattribs->layer[b].name[0]) - layer = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, gattribs->layer[b].name); - else - layer = CustomData_get_active_layer_index(&dm->loopData, CD_TANGENT); + if (layer == -1) { + if (gattribs->layer[b].name[0]) + layer = CustomData_get_named_layer_index(&dm->loopData, CD_TANGENT, gattribs->layer[b].name); + else + layer = CustomData_get_active_layer_index(&dm->loopData, CD_TANGENT); + } a = attribs->tottang++; @@ -3688,9 +3723,11 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, attribs->tang[a].gl_index = gattribs->layer[b].glindex; } - else if (gattribs->layer[b].type == CD_ORCO) { + else if (type == CD_ORCO) { /* original coordinates */ - layer = CustomData_get_layer_index(vdata, CD_ORCO); + if (layer == -1) { + layer = CustomData_get_layer_index(vdata, CD_ORCO); + } attribs->totorco = 1; if (layer != -1) { @@ -3754,17 +3791,17 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, /* vertex colors */ for (b = 0; b < attribs->totmcol; b++) { - GLubyte col[4]; + GLfloat col[4]; if (attribs->mcol[b].array) { const MLoopCol *cp = &attribs->mcol[b].array[loop]; - copy_v4_v4_uchar(col, &cp->r); + rgba_uchar_to_float(col, &cp->r); } else { - col[0] = 0; col[1] = 0; col[2] = 0; col[3] = 0; + zero_v4(col); } - glVertexAttrib4ubv(attribs->mcol[b].gl_index, col); + glVertexAttrib4fv(attribs->mcol[b].gl_index, col); } /* tangent for normal mapping */ @@ -3996,7 +4033,7 @@ void DM_init_origspace(DerivedMesh *dm) float p_nor[3], co[3]; float mat[3][3]; - float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {FLT_MIN, FLT_MIN}; + float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {-FLT_MAX, -FLT_MAX}; float translate[2], scale[2]; BKE_mesh_calc_poly_normal(mp, l, mv, p_nor); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 5b1bb8cb2bb..df9b9683687 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -493,6 +493,8 @@ bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name) unit_axis_angle(chan->rotAxis, &chan->rotAngle); chan->size[0] = chan->size[1] = chan->size[2] = 1.0f; + chan->scaleIn = chan->scaleOut = 1.0f; + chan->limitmin[0] = chan->limitmin[1] = chan->limitmin[2] = -180.0f; chan->limitmax[0] = chan->limitmax[1] = chan->limitmax[2] = 180.0f; chan->stiffness[0] = chan->stiffness[1] = chan->stiffness[2] = 0.0f; @@ -596,7 +598,16 @@ void BKE_pose_copy_data(bPose **dst, bPose *src, const bool copy_constraints) outPose = MEM_callocN(sizeof(bPose), "pose"); BLI_duplicatelist(&outPose->chanbase, &src->chanbase); - + + /* Rebuild ghash here too, so that name lookups below won't be too bad... + * BUT this will have the penalty that the ghash will be built twice + * if BKE_pose_rebuild() gets called after this... + */ + if (outPose->chanbase.first != outPose->chanbase.last) { + outPose->chanhash = NULL; + BKE_pose_channels_hash_make(outPose); + } + outPose->iksolver = src->iksolver; outPose->ikdata = NULL; outPose->ikparam = MEM_dupallocN(src->ikparam); @@ -608,10 +619,16 @@ void BKE_pose_copy_data(bPose **dst, bPose *src, const bool copy_constraints) id_us_plus(&pchan->custom->id); } - /* warning, O(n2) here, but it's a rarely used feature. */ + /* warning, O(n2) here, if done without the hash, but these are rarely used features. */ if (pchan->custom_tx) { pchan->custom_tx = BKE_pose_channel_find_name(outPose, pchan->custom_tx->name); } + if (pchan->bbone_prev) { + pchan->bbone_prev = BKE_pose_channel_find_name(outPose, pchan->bbone_prev->name); + } + if (pchan->bbone_next) { + pchan->bbone_next = BKE_pose_channel_find_name(outPose, pchan->bbone_next->name); + } if (copy_constraints) { BKE_constraints_copy(&listb, &pchan->constraints, true); // BKE_constraints_copy NULLs listb @@ -726,7 +743,7 @@ void BKE_pose_channels_remove( Object *ob, bool (*filter_fn)(const char *bone_name, void *user_data), void *user_data) { - /* First erase any associated pose channel */ + /* Erase any associated pose channel, along with any references to them */ if (ob->pose) { bPoseChannel *pchan, *pchan_next; bConstraint *con; @@ -735,6 +752,7 @@ void BKE_pose_channels_remove( pchan_next = pchan->next; if (filter_fn(pchan->name, user_data)) { + /* Bone itself is being removed */ BKE_pose_channel_free(pchan); if (ob->pose->chanhash) { BLI_ghash_remove(ob->pose->chanhash, pchan->name, NULL, NULL); @@ -742,6 +760,7 @@ void BKE_pose_channels_remove( BLI_freelinkN(&ob->pose->chanbase, pchan); } else { + /* Maybe something the bone references is being removed instead? */ for (con = pchan->constraints.first; con; con = con->next) { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; @@ -765,6 +784,20 @@ void BKE_pose_channels_remove( cti->flush_constraint_targets(con, &targets, 0); } } + + if (pchan->bbone_prev) { + if (filter_fn(pchan->bbone_prev->name, user_data)) + pchan->bbone_prev = NULL; + } + if (pchan->bbone_next) { + if (filter_fn(pchan->bbone_next->name, user_data)) + pchan->bbone_next = NULL; + } + + if (pchan->custom_tx) { + if (filter_fn(pchan->custom_tx->name, user_data)) + pchan->custom_tx = NULL; + } } } } @@ -824,26 +857,35 @@ void BKE_pose_channels_free(bPose *pose) BKE_pose_channels_free_ex(pose, true); } +void BKE_pose_free_data_ex(bPose *pose, bool do_id_user) +{ + /* free pose-channels */ + BKE_pose_channels_free_ex(pose, do_id_user); + + /* free pose-groups */ + if (pose->agroups.first) + BLI_freelistN(&pose->agroups); + + /* free IK solver state */ + BIK_clear_data(pose); + + /* free IK solver param */ + if (pose->ikparam) + MEM_freeN(pose->ikparam); +} + +void BKE_pose_free_data(bPose *pose) +{ + BKE_pose_free_data_ex(pose, true); +} + /** * Removes and deallocates all data from a pose, and also frees the pose. */ void BKE_pose_free_ex(bPose *pose, bool do_id_user) { if (pose) { - /* free pose-channels */ - BKE_pose_channels_free_ex(pose, do_id_user); - - /* free pose-groups */ - if (pose->agroups.first) - BLI_freelistN(&pose->agroups); - - /* free IK solver state */ - BIK_clear_data(pose); - - /* free IK solver param */ - if (pose->ikparam) - MEM_freeN(pose->ikparam); - + BKE_pose_free_data_ex(pose, do_id_user); /* free pose */ MEM_freeN(pose); } @@ -869,6 +911,15 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan copy_m4_m4(pchan->pose_mat, (float(*)[4])chan->pose_mat); pchan->flag = chan->flag; + pchan->roll1 = chan->roll1; + pchan->roll2 = chan->roll2; + pchan->curveInX = chan->curveInX; + pchan->curveInY = chan->curveInY; + pchan->curveOutX = chan->curveOutX; + pchan->curveOutY = chan->curveOutY; + pchan->scaleIn = chan->scaleIn; + pchan->scaleOut = chan->scaleOut; + con = chan->constraints.first; for (pcon = pchan->constraints.first; pcon && con; pcon = pcon->next, con = con->next) { pcon->enforce = con->enforce; @@ -879,7 +930,7 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan /** * Copy the internal members of each pose channel including constraints * and ID-Props, used when duplicating bones in editmode. - * (unlike copy_pose_channel_data which only). + * (unlike copy_pose_channel_data which only does posing-related stuff). * * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify) */ @@ -902,6 +953,11 @@ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_f pchan->ikstretch = pchan_from->ikstretch; pchan->ikrotweight = pchan_from->ikrotweight; pchan->iklinweight = pchan_from->iklinweight; + + /* bbone settings (typically not animated) */ + pchan->bboneflag = pchan_from->bboneflag; + pchan->bbone_next = pchan_from->bbone_next; + pchan->bbone_prev = pchan_from->bbone_prev; /* constraints */ BKE_constraints_copy(&pchan->constraints, &pchan_from->constraints, true); @@ -1271,6 +1327,18 @@ short action_get_item_transforms(bAction *act, Object *ob, bPoseChannel *pchan, } } + if ((curves) || (flags & ACT_TRANS_BBONE) == 0) { + /* bbone shape properties */ + pPtr = strstr(bPtr, "bbone_"); + if (pPtr) { + flags |= ACT_TRANS_BBONE; + + if (curves) + BLI_addtail(curves, BLI_genericNodeN(fcu)); + continue; + } + } + if ((curves) || (flags & ACT_TRANS_PROP) == 0) { /* custom properties only */ pPtr = strstr(bPtr, "[\""); /* extra '"' comment here to keep my texteditor functionlist working :) */ @@ -1330,8 +1398,13 @@ void BKE_pose_rest(bPose *pose) unit_qt(pchan->quat); unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f; - - pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE); + + pchan->roll1 = pchan->roll2 = 0.0f; + pchan->curveInX = pchan->curveInY = 0.0f; + pchan->curveOutX = pchan->curveOutY = 0.0f; + pchan->scaleIn = pchan->scaleOut = 1.0f; + + pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); } } @@ -1366,9 +1439,19 @@ bool BKE_pose_copy_result(bPose *to, bPose *from) copy_v3_v3(pchanto->pose_head, pchanfrom->pose_head); copy_v3_v3(pchanto->pose_tail, pchanfrom->pose_tail); + pchanto->roll1 = pchanfrom->roll1; + pchanto->roll2 = pchanfrom->roll2; + pchanto->curveInX = pchanfrom->curveInX; + pchanto->curveInY = pchanfrom->curveInY; + pchanto->curveOutX = pchanfrom->curveOutX; + pchanto->curveOutY = pchanfrom->curveOutY; + pchanto->scaleIn = pchanfrom->scaleIn; + pchanto->scaleOut = pchanfrom->scaleOut; + pchanto->rotmode = pchanfrom->rotmode; pchanto->flag = pchanfrom->flag; pchanto->protectflag = pchanfrom->protectflag; + pchanto->bboneflag = pchanfrom->bboneflag; } } return true; @@ -1415,7 +1498,13 @@ void what_does_obaction(Object *ob, Object *workob, bPose *pose, bAction *act, c workob->pose = pose; /* need to set pose too, since this is used for both types of Action Constraint */ if (pose) { - BKE_pose_channels_hash_make(pose); + /* This function is most likely to be used with a temporary pose with a single bone in there. + * For such cases it makes no sense to create hash since it'll only waste CPU ticks on memory + * allocation and also will make lookup slower. + */ + if (pose->chanbase.first != pose->chanbase.last) { + BKE_pose_channels_hash_make(pose); + } if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) { BKE_pose_update_constraint_flags(pose); } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 564c670fb3a..04b4733fd44 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -41,6 +41,7 @@ #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_ghash.h" +#include "BLI_task.h" #include "BLI_utildefines.h" #include "DNA_anim_types.h" @@ -48,6 +49,7 @@ #include "DNA_constraint_types.h" #include "DNA_mesh_types.h" #include "DNA_lattice_types.h" +#include "DNA_listBase.h" #include "DNA_meshdata_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -427,7 +429,7 @@ int bone_autoside_name(char name[MAXBONENAME], int UNUSED(strip_number), short a /* ************* B-Bone support ******************* */ /* data has MAX_BBONE_SUBDIV+1 interpolated points, will become desired amount with equal distances */ -static void equalize_bezier(float *data, int desired) +void equalize_bbone_bezier(float *data, int desired) { float *fp, totdist, ddist, dist, fac1, fac2; float pdist[MAX_BBONE_SUBDIV + 1]; @@ -473,7 +475,7 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB { bPoseChannel *next, *prev; Bone *bone = pchan->bone; - float h1[3], h2[3], scale[3], length, hlength1, hlength2, roll1 = 0.0f, roll2; + float h1[3], h2[3], scale[3], length, roll1 = 0.0f, roll2; float mat3[3][3], imat[4][4], posemat[4][4], scalemat[4][4], iscalemat[4][4]; float data[MAX_BBONE_SUBDIV + 1][4], *fp; int a; @@ -494,16 +496,21 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB } } - hlength1 = bone->ease1 * length * 0.390464f; /* 0.5f * sqrt(2) * kappa, the handle length for near-perfect circles */ - hlength2 = bone->ease2 * length * 0.390464f; - - /* evaluate next and prev bones */ - if (bone->flag & BONE_CONNECTED) - prev = pchan->parent; - else - prev = NULL; + /* get "next" and "prev" bones - these are used for handle calculations */ + if (pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) { + /* use the provided bones as the next/prev - leave blank to eliminate this effect altogether */ + prev = pchan->bbone_prev; + next = pchan->bbone_next; + } + else { + /* evaluate next and prev bones */ + if (bone->flag & BONE_CONNECTED) + prev = pchan->parent; + else + prev = NULL; - next = pchan->child; + next = pchan->child; + } /* find the handle points, since this is inside bone space, the * first point = (0, 0, 0) @@ -523,10 +530,27 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB float difmat[4][4], result[3][3], imat3[3][3]; /* transform previous point inside this bone space */ - if (rest) - copy_v3_v3(h1, prev->bone->arm_head); - else - copy_v3_v3(h1, prev->pose_head); + if ((pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) && + (pchan->bboneflag & PCHAN_BBONE_CUSTOM_START_REL)) + { + /* Use delta movement (from restpose), and apply this relative to the current bone's head */ + if (rest) { + /* in restpose, arm_head == pose_head */ + h1[0] = h1[1] = h1[2] = 0.0f; + } + else { + float delta[3]; + sub_v3_v3v3(delta, prev->pose_head, prev->bone->arm_head); + sub_v3_v3v3(h1, pchan->pose_head, delta); + } + } + else { + /* Use bone head as absolute position */ + if (rest) + copy_v3_v3(h1, prev->bone->arm_head); + else + copy_v3_v3(h1, prev->pose_head); + } mul_m4_v3(imat, h1); if (prev->bone->segments > 1) { @@ -536,7 +560,7 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB } normalize_v3(h1); - mul_v3_fl(h1, -hlength1); + negate_v3(h1); if (prev->bone->segments == 1) { /* find the previous roll to interpolate */ @@ -555,17 +579,34 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB } } else { - h1[0] = 0.0f; h1[1] = hlength1; h1[2] = 0.0f; + h1[0] = 0.0f; h1[1] = 1.0; h1[2] = 0.0f; roll1 = 0.0f; } if (next) { float difmat[4][4], result[3][3], imat3[3][3]; /* transform next point inside this bone space */ - if (rest) - copy_v3_v3(h2, next->bone->arm_tail); - else - copy_v3_v3(h2, next->pose_tail); + if ((pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) && + (pchan->bboneflag & PCHAN_BBONE_CUSTOM_END_REL)) + { + /* Use delta movement (from restpose), and apply this relative to the current bone's tail */ + if (rest) { + /* in restpose, arm_tail == pose_tail */ + h2[0] = h2[1] = h2[2] = 0.0f; + } + else { + float delta[3]; + sub_v3_v3v3(delta, next->pose_tail, next->bone->arm_tail); + add_v3_v3v3(h2, pchan->pose_tail, delta); + } + } + else { + /* Use bone tail as absolute position */ + if (rest) + copy_v3_v3(h2, next->bone->arm_tail); + else + copy_v3_v3(h2, next->pose_tail); + } mul_m4_v3(imat, h2); /* if next bone is B-bone too, use average handle direction */ @@ -591,14 +632,66 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB roll2 = atan2f(mat3[2][0], mat3[2][2]); - /* and only now negate handle */ - mul_v3_fl(h2, -hlength2); } else { - h2[0] = 0.0f; h2[1] = -hlength2; h2[2] = 0.0f; + h2[0] = 0.0f; h2[1] = 1.0f; h2[2] = 0.0f; roll2 = 0.0; } + { + const float circle_factor = length * (cubic_tangent_factor_circle_v3(h1, h2) / 0.75f); + const float hlength1 = bone->ease1 * circle_factor; + const float hlength2 = bone->ease2 * circle_factor; + + /* and only now negate h2 */ + mul_v3_fl(h1, hlength1); + mul_v3_fl(h2, -hlength2); + } + + /* Add effects from bbone properties over the top + * - These properties allow users to hand-animate the + * bone curve/shape, without having to resort to using + * extra bones + * - The "bone" level offsets are for defining the restpose + * shape of the bone (e.g. for curved eyebrows for example). + * -> In the viewport, it's needed to define what the rest pose + * looks like + * -> For "rest == 0", we also still need to have it present + * so that we can "cancel out" this restpose when it comes + * time to deform some geometry, it won't cause double transforms. + * - The "pchan" level offsets are the ones that animators actually + * end up animating + */ + { + /* add extra rolls */ + roll1 += bone->roll1 + (!rest ? pchan->roll1 : 0.0f); + roll2 += bone->roll2 + (!rest ? pchan->roll2 : 0.0f); + + if (bone->flag & BONE_ADD_PARENT_END_ROLL) { + if (prev) { + if (prev->bone) + roll1 += prev->bone->roll2; + + if (!rest) + roll1 += prev->roll2; + } + } + + /* extra curve x / y */ + /* NOTE: Scale correction factors here are to compensate for some random floating-point glitches + * when scaling up the bone or it's parent by a factor of approximately 8.15/6, which results + * in the bone length getting scaled up too (from 1 to 8), causing the curve to flatten out. + */ + const float xscale_correction = (do_scale) ? scale[0] : 1.0f; + const float yscale_correction = (do_scale) ? scale[2] : 1.0f; + + h1[0] += (bone->curveInX + (!rest ? pchan->curveInX : 0.0f)) * xscale_correction; + h1[2] += (bone->curveInY + (!rest ? pchan->curveInY : 0.0f)) * yscale_correction; + + h2[0] += (bone->curveOutX + (!rest ? pchan->curveOutX : 0.0f)) * xscale_correction; + h2[2] += (bone->curveOutY + (!rest ? pchan->curveOutY : 0.0f)) * yscale_correction; + } + /* make curve */ if (bone->segments > MAX_BBONE_SUBDIV) bone->segments = MAX_BBONE_SUBDIV; @@ -608,20 +701,45 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB BKE_curve_forward_diff_bezier(0.0f, h1[2], h2[2], 0.0f, data[0] + 2, MAX_BBONE_SUBDIV, 4 * sizeof(float)); BKE_curve_forward_diff_bezier(roll1, roll1 + 0.390464f * (roll2 - roll1), roll2 - 0.390464f * (roll2 - roll1), roll2, data[0] + 3, MAX_BBONE_SUBDIV, 4 * sizeof(float)); - equalize_bezier(data[0], bone->segments); /* note: does stride 4! */ + equalize_bbone_bezier(data[0], bone->segments); /* note: does stride 4! */ /* make transformation matrices for the segments for drawing */ for (a = 0, fp = data[0]; a < bone->segments; a++, fp += 4) { sub_v3_v3v3(h1, fp + 4, fp); vec_roll_to_mat3(h1, fp[3], mat3); /* fp[3] is roll */ - + copy_m4_m3(result_array[a].mat, mat3); copy_v3_v3(result_array[a].mat[3], fp); - + if (do_scale) { /* correct for scaling when this matrix is used in scaled space */ mul_m4_series(result_array[a].mat, iscalemat, result_array[a].mat, scalemat); } + + /* BBone scale... */ + { + const int num_segments = bone->segments; + + const float scaleIn = bone->scaleIn * (!rest ? pchan->scaleIn : 1.0f); + const float scaleFactorIn = 1.0f + (scaleIn - 1.0f) * ((float)(num_segments - a) / (float)num_segments); + + const float scaleOut = bone->scaleOut * (!rest ? pchan->scaleOut : 1.0f); + const float scaleFactorOut = 1.0f + (scaleOut - 1.0f) * ((float)(a + 1) / (float)num_segments); + + const float scalefac = scaleFactorIn * scaleFactorOut; + float bscalemat[4][4], bscale[3]; + + bscale[0] = scalefac; + bscale[1] = 1.0f; + bscale[2] = scalefac; + + size_to_mat4(bscalemat, bscale); + + /* Note: don't multiply by inverse scale mat here, as it causes problems with scaling shearing and breaking segment chains */ + /*mul_m4_series(result_array[a].mat, ibscalemat, result_array[a].mat, bscalemat);*/ + mul_m4_series(result_array[a].mat, result_array[a].mat, bscalemat); + } + } } @@ -667,7 +785,6 @@ static void pchan_b_bone_defmats(bPoseChannel *pchan, bPoseChanDeform *pdef_info float tmat[4][4]; invert_m4_m4(tmat, b_bone_rest[a].mat); - mul_m4_series(b_bone_mats[a + 1].mat, pchan->chan_mat, bone->arm_mat, b_bone[a].mat, tmat, b_bone_mats[0].mat); if (use_quaternion) @@ -681,10 +798,10 @@ static void b_bone_deform(bPoseChanDeform *pdef_info, Bone *bone, float co[3], D float (*mat)[4] = b_bone[0].mat; float segment, y; int a; - + /* need to transform co back to bonespace, only need y */ y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1]; - + /* now calculate which of the b_bones are deforming this */ segment = bone->length / ((float)bone->segments); a = (int)(y / segment); @@ -854,6 +971,32 @@ static void pchan_bone_deform(bPoseChannel *pchan, bPoseChanDeform *pdef_info, f (*contrib) += weight; } +typedef struct ArmatureBBoneDefmatsData { + bPoseChanDeform *pdef_info_array; + DualQuat *dualquats; + bool use_quaternion; +} ArmatureBBoneDefmatsData; + +static void armature_bbone_defmats_cb(void *userdata, Link *iter, int index) +{ + ArmatureBBoneDefmatsData *data = userdata; + bPoseChannel *pchan = (bPoseChannel *)iter; + + if (!(pchan->bone->flag & BONE_NO_DEFORM)) { + bPoseChanDeform *pdef_info = &data->pdef_info_array[index]; + const bool use_quaternion = data->use_quaternion; + + if (pchan->bone->segments > 1) { + pchan_b_bone_defmats(pchan, pdef_info, use_quaternion); + } + + if (use_quaternion) { + pdef_info->dual_quat = &data->dualquats[index]; + mat4_to_dquat(pdef_info->dual_quat, pchan->bone->arm_mat, pchan->chan_mat); + } + } +} + void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, float (*vertexCos)[3], float (*defMats)[3][3], int numVerts, int deformflag, float (*prevCos)[3], const char *defgrp_name) @@ -897,19 +1040,10 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, float pdef_info_array = MEM_callocN(sizeof(bPoseChanDeform) * totchan, "bPoseChanDeform"); - totchan = 0; - pdef_info = pdef_info_array; - for (pchan = armOb->pose->chanbase.first; pchan; pchan = pchan->next, pdef_info++) { - if (!(pchan->bone->flag & BONE_NO_DEFORM)) { - if (pchan->bone->segments > 1) - pchan_b_bone_defmats(pchan, pdef_info, use_quaternion); - - if (use_quaternion) { - pdef_info->dual_quat = &dualquats[totchan++]; - mat4_to_dquat(pdef_info->dual_quat, pchan->bone->arm_mat, pchan->chan_mat); - } - } - } + ArmatureBBoneDefmatsData data = { + .pdef_info_array = pdef_info_array, .dualquats = dualquats, .use_quaternion = use_quaternion + }; + BLI_task_parallel_listbase(&armOb->pose->chanbase, &data, armature_bbone_defmats_cb, totchan > 512); /* get the def_nr for the overall armature vertex group if present */ armature_def_nr = defgroup_name_index(target, defgrp_name); diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index ceda9f056bb..826bb12a912 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -619,7 +619,9 @@ void BKE_pose_eval_bone(EvaluationContext *UNUSED(eval_ctx), else { /* TODO(sergey): Use time source node for time. */ float ctime = BKE_scene_frame_get(scene); /* not accurate... */ - BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + if ((pchan->flag & POSE_DONE) == 0) { + BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + } } } } @@ -631,12 +633,18 @@ void BKE_pose_constraints_evaluate(EvaluationContext *UNUSED(eval_ctx), { Scene *scene = G.main->scene.first; DEBUG_PRINT("%s on %s pchan %s\n", __func__, ob->id.name, pchan->name); - if (pchan->flag & POSE_IKTREE || pchan->flag & POSE_IKSPLINE) { + bArmature *arm = (bArmature *)ob->data; + if (arm->flag & ARM_RESTPOS) { + return; + } + else if (pchan->flag & POSE_IKTREE || pchan->flag & POSE_IKSPLINE) { /* IK are being solved separately/ */ } else { float ctime = BKE_scene_frame_get(scene); /* not accurate... */ - BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + if ((pchan->flag & POSE_DONE) == 0) { + BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); + } } } diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 4605a12f9a0..bedf262541f 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -492,6 +492,9 @@ static void blendfile_write_partial_cb(void *UNUSED(handle), Main *UNUSED(bmain) /* only tag for need-expand if not done, prevents eternal loops */ if ((id->tag & LIB_TAG_DOIT) == 0) id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT; + + if (id->lib && (id->lib->id.tag & LIB_TAG_DOIT) == 0) + id->lib->id.tag |= LIB_TAG_DOIT; } } diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c index abba61310a4..7821946eb6e 100644 --- a/source/blender/blenkernel/intern/bvhutils.c +++ b/source/blender/blenkernel/intern/bvhutils.c @@ -151,10 +151,10 @@ static void mesh_looptri_nearest_point(void *userdata, int index, const float co } } /* copy of function above (warning, should de-duplicate with editmesh_bvh.c) */ -static void editmesh_faces_nearest_point(void *userdata, int index, const float co[3], BVHTreeNearest *nearest) +static void editmesh_looptri_nearest_point(void *userdata, int index, const float co[3], BVHTreeNearest *nearest) { - const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata; - BMEditMesh *em = data->em_evil; + const BVHTreeFromEditMesh *data = userdata; + BMEditMesh *em = data->em; const BMLoop **ltri = (const BMLoop **)em->looptris[index]; const float *t0, *t1, *t2; @@ -240,10 +240,10 @@ static void mesh_looptri_spherecast(void *userdata, int index, const BVHTreeRay } } /* copy of function above (warning, should de-duplicate with editmesh_bvh.c) */ -static void editmesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +static void editmesh_looptri_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { - const BVHTreeFromMesh *data = (BVHTreeFromMesh *) userdata; - BMEditMesh *em = data->em_evil; + const BVHTreeFromEditMesh *data = (BVHTreeFromEditMesh *)userdata; + BMEditMesh *em = data->em; const BMLoop **ltri = (const BMLoop **)em->looptris[index]; const float *t0, *t1, *t2; @@ -296,7 +296,7 @@ static void mesh_edges_nearest_point(void *userdata, int index, const float co[3 /* Helper, does all the point-spherecast work actually. */ static void mesh_verts_spherecast_do( - const BVHTreeFromMesh *UNUSED(data), int index, const float v[3], const BVHTreeRay *ray, BVHTreeRayHit *hit) + int index, const float v[3], const BVHTreeRay *ray, BVHTreeRayHit *hit) { float dist; const float *r1; @@ -314,6 +314,14 @@ static void mesh_verts_spherecast_do( } } +static void editmesh_verts_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + const BVHTreeFromEditMesh *data = userdata; + BMVert *eve = BM_vert_at_index(data->em->bm, index); + + mesh_verts_spherecast_do(index, eve->co, ray, hit); +} + /* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_verts. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ static void mesh_verts_spherecast(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) @@ -321,7 +329,7 @@ static void mesh_verts_spherecast(void *userdata, int index, const BVHTreeRay *r const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata; const float *v = data->vert[index].co; - mesh_verts_spherecast_do(data, index, v, ray, hit); + mesh_verts_spherecast_do(index, v, ray, hit); } /* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_edges. @@ -341,7 +349,7 @@ static void mesh_edges_spherecast(void *userdata, int index, const BVHTreeRay *r /* In case we get a zero-length edge, handle it as a point! */ if (equals_v3v3(v1, v2)) { - mesh_verts_spherecast_do(data, index, v1, ray, hit); + mesh_verts_spherecast_do(index, v1, ray, hit); return; } @@ -380,78 +388,64 @@ static void mesh_edges_spherecast(void *userdata, int index, const BVHTreeRay *r /** \name Vertex Builder * \{ */ -static BVHTree *bvhtree_from_mesh_verts_create_tree( +static BVHTree *bvhtree_from_editmesh_verts_create_tree( float epsilon, int tree_type, int axis, - BMEditMesh *em, const int *index_array, - MVert *vert, const int numVerts, - BLI_bitmap *mask, int numVerts_active) + BMEditMesh *em, const int verts_num, + const BLI_bitmap *verts_mask, int verts_num_active) { BVHTree *tree = NULL; - BMVert *eve = NULL; int i; - int index = 0; - if (em != NULL) { - BM_mesh_elem_table_ensure(em->bm, BM_VERT); + BM_mesh_elem_table_ensure(em->bm, BM_VERT); + if (verts_mask) { + BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num)); } - if (vert) { - if (mask && numVerts_active < 0) { - numVerts_active = 0; - for (i = 0; i < numVerts; i++) { - if (BLI_BITMAP_TEST_BOOL(mask, i)) { - if (em != NULL) { - if (index_array) { - index = index_array[i]; - if (index == ORIGINDEX_NONE) { - continue; - } - } - else { - index = i; - } - - eve = BM_vert_at_index(em->bm, index); - if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN) || - BM_elem_flag_test(eve, BM_ELEM_SELECT)) - { - continue; - } - } - numVerts_active++; - } + else { + verts_num_active = verts_num; + } + + tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis); + + if (tree) { + BMIter iter; + BMVert *eve; + BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { + if (verts_mask && !BLI_BITMAP_TEST_BOOL(verts_mask, i)) { + continue; } + BLI_bvhtree_insert(tree, i, eve->co, 1); } - else if (!mask) { - numVerts_active = numVerts; + BLI_assert(BLI_bvhtree_get_size(tree) == verts_num_active); + BLI_bvhtree_balance(tree); + } + + return tree; +} + +static BVHTree *bvhtree_from_mesh_verts_create_tree( + float epsilon, int tree_type, int axis, + MVert *vert, const int verts_num, + const BLI_bitmap *verts_mask, int verts_num_active) +{ + BVHTree *tree = NULL; + int i; + if (vert) { + if (verts_mask) { + BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num)); + } + else { + verts_num_active = verts_num; } - tree = BLI_bvhtree_new(numVerts_active, epsilon, tree_type, axis); + tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis); if (tree) { - for (i = 0; i < numVerts; i++) { - if (mask && !BLI_BITMAP_TEST_BOOL(mask, i)) { + for (i = 0; i < verts_num; i++) { + if (verts_mask && !BLI_BITMAP_TEST_BOOL(verts_mask, i)) { continue; } - if (em != NULL) { - if (index_array) { - index = index_array[i]; - if (index == ORIGINDEX_NONE) { - continue; - } - } - else { - index = i; - } - - eve = BM_vert_at_index(em->bm, index); - if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN) || - BM_elem_flag_test(eve, BM_ELEM_SELECT)) - { - continue; - } - } BLI_bvhtree_insert(tree, i, vert[i].co, 1); } - + BLI_assert(BLI_bvhtree_get_size(tree) == verts_num_active); BLI_bvhtree_balance(tree); } } @@ -488,17 +482,51 @@ static void bvhtree_from_mesh_verts_setup_data( } } +/* Builds a bvh tree where nodes are the vertices of the given em */ +BVHTree *bvhtree_from_editmesh_verts_ex( + BVHTreeFromEditMesh *data, BMEditMesh *em, + const BLI_bitmap *verts_mask, int verts_num_active, + float epsilon, int tree_type, int axis) +{ + int vert_num = em->bm->totvert; + + BVHTree *tree = bvhtree_from_editmesh_verts_create_tree( + epsilon, tree_type, axis, + em, vert_num, verts_mask, verts_num_active); + + if (tree) { + memset(data, 0, sizeof(*data)); + data->tree = tree; + data->em = em; + data->nearest_callback = NULL; + data->raycast_callback = editmesh_verts_spherecast; + data->nearest_to_ray_callback = NULL; + } + + return tree; +} +BVHTree *bvhtree_from_editmesh_verts( + BVHTreeFromEditMesh *data, BMEditMesh *em, + float epsilon, int tree_type, int axis) +{ + return bvhtree_from_editmesh_verts_ex( + data, em, + NULL, -1, + epsilon, tree_type, axis); +} + + /* Builds a bvh tree where nodes are the vertices of the given dm */ -BVHTree *bvhtree_from_mesh_verts(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) +BVHTree *bvhtree_from_mesh_verts( + BVHTreeFromMesh *data, DerivedMesh *dm, + float epsilon, int tree_type, int axis) { - BMEditMesh *em = data->em_evil; - const int bvhcache_type = em ? BVHTREE_FROM_VERTS_EDITMESH_SNAP : BVHTREE_FROM_VERTS; BVHTree *tree; MVert *vert; bool vert_allocated; BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_VERTS); BLI_rw_mutex_unlock(&cache_rwlock); vert = DM_get_vert_array(dm, &vert_allocated); @@ -506,26 +534,20 @@ BVHTree *bvhtree_from_mesh_verts(BVHTreeFromMesh *data, DerivedMesh *dm, float e /* Not in cache */ if (tree == NULL) { BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_VERTS); if (tree == NULL) { - int vert_num, *index_array = NULL; - if (em != NULL) { - vert_num = em->bm->totvert; - index_array = dm->getVertDataArray(dm, CD_ORIGINDEX); - } - else { - vert_num = dm->getNumVerts(dm); - BLI_assert(vert_num != 0); - } + + int vert_num = dm->getNumVerts(dm); + BLI_assert(vert_num != 0); + tree = bvhtree_from_mesh_verts_create_tree( epsilon, tree_type, axis, - em, index_array, vert, vert_num, NULL, -1); if (tree) { /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ - bvhcache_insert(&dm->bvhCache, tree, bvhcache_type); + bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_VERTS); } } BLI_rw_mutex_unlock(&cache_rwlock); @@ -544,14 +566,15 @@ BVHTree *bvhtree_from_mesh_verts(BVHTreeFromMesh *data, DerivedMesh *dm, float e * Builds a bvh tree where nodes are the given vertices (note: does not copy given mverts!). * \param vert_allocated if true, vert freeing will be done when freeing data. * \param mask if not null, true elements give which vert to add to BVH tree. - * \param numVerts_active if >= 0, number of active verts to add to BVH tree (else will be computed from mask). + * \param verts_num_active if >= 0, number of active verts to add to BVH tree (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_verts_ex( - BVHTreeFromMesh *data, MVert *vert, const int numVerts, const bool vert_allocated, - BLI_bitmap *mask, int numVerts_active, + BVHTreeFromMesh *data, MVert *vert, const int verts_num, const bool vert_allocated, + const BLI_bitmap *verts_mask, int verts_num_active, float epsilon, int tree_type, int axis) { - BVHTree *tree = bvhtree_from_mesh_verts_create_tree(epsilon, tree_type, axis, NULL, NULL, vert, numVerts, mask, numVerts_active); + BVHTree *tree = bvhtree_from_mesh_verts_create_tree( + epsilon, tree_type, axis, vert, verts_num, verts_mask, verts_num_active); /* Setup BVHTreeFromMesh */ bvhtree_from_mesh_verts_setup_data(data, tree, false, epsilon, vert, vert_allocated); @@ -568,7 +591,9 @@ BVHTree *bvhtree_from_mesh_verts_ex( * \{ */ /* Builds a bvh tree where nodes are the edges of the given dm */ -BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) +BVHTree *bvhtree_from_mesh_edges( + BVHTreeFromMesh *data, DerivedMesh *dm, + float epsilon, int tree_type, int axis) { BVHTree *tree; MVert *vert; @@ -576,7 +601,7 @@ BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float e bool vert_allocated, edge_allocated; BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); - tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_EDGES); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_EDGES); BLI_rw_mutex_unlock(&cache_rwlock); vert = DM_get_vert_array(dm, &vert_allocated); @@ -585,7 +610,7 @@ BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float e /* Not in cache */ if (tree == NULL) { BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); - tree = bvhcache_find(&dm->bvhCache, BVHTREE_FROM_EDGES); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_EDGES); if (tree == NULL) { int i; int numEdges = dm->getNumEdges(dm); @@ -655,109 +680,41 @@ BVHTree *bvhtree_from_mesh_edges(BVHTreeFromMesh *data, DerivedMesh *dm, float e static BVHTree *bvhtree_from_mesh_faces_create_tree( float epsilon, int tree_type, int axis, - BMEditMesh *em, const bool em_all, - MVert *vert, MFace *face, const int numFaces, - BLI_bitmap *mask, int numFaces_active) + MVert *vert, MFace *face, const int faces_num, + const BLI_bitmap *faces_mask, int faces_num_active) { BVHTree *tree = NULL; int i; - if (numFaces) { - if (mask && numFaces_active < 0) { - numFaces_active = 0; - for (i = 0; i < numFaces; i++) { - if (BLI_BITMAP_TEST_BOOL(mask, i)) { - numFaces_active++; - } - } + if (faces_num) { + if (faces_mask) { + BLI_assert(IN_RANGE_INCL(faces_num_active, 0, faces_num)); } - else if (!mask) { - numFaces_active = numFaces; + else { + faces_num_active = faces_num; } /* Create a bvh-tree of the given target */ /* printf("%s: building BVH, total=%d\n", __func__, numFaces); */ - tree = BLI_bvhtree_new(numFaces_active, epsilon, tree_type, axis); + tree = BLI_bvhtree_new(faces_num_active, epsilon, tree_type, axis); if (tree) { - if (em) { - const struct BMLoop *(*looptris)[3] = (void *)em->looptris; - - /* avoid double-up on face searches for quads-ngons */ - bool insert_prev = false; - BMFace *f_prev = NULL; - - /* data->em_evil is only set for snapping, and only for the mesh of the object - * which is currently open in edit mode. When set, the bvhtree should not contain - * faces that will interfere with snapping (e.g. faces that are hidden/selected - * or faces that have selected verts). */ - - /* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden - * and/or selected. Even if the faces themselves are not selected for the snapped - * transform, having a vertex selected means the face (and thus it's tessellated - * triangles) will be moving and will not be a good snap targets. */ - for (i = 0; i < numFaces; i++) { - const BMLoop **ltri = looptris[i]; - BMFace *f = ltri[0]->f; - bool insert = mask ? BLI_BITMAP_TEST_BOOL(mask, i) : true; - - /* Start with the assumption the triangle should be included for snapping. */ - if (f == f_prev) { - insert = insert_prev; - } - else if (insert) { - if (em_all) { - /* pass */ - } - else if (BM_elem_flag_test(f, BM_ELEM_SELECT) || BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - /* Don't insert triangles tessellated from faces that are hidden or selected */ - insert = false; - } - else { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { - /* Don't insert triangles tessellated from faces that have any selected verts */ - insert = false; - break; - } - } while ((l_iter = l_iter->next) != l_first); - } - - /* skip if face doesn't change */ - f_prev = f; - insert_prev = insert; + if (vert && face) { + for (i = 0; i < faces_num; i++) { + float co[4][3]; + if (faces_mask && !BLI_BITMAP_TEST_BOOL(faces_mask, i)) { + continue; } - if (insert) { - /* No reason found to block hit-testing the triangle for snap, so insert it now.*/ - float co[3][3]; - copy_v3_v3(co[0], ltri[0]->v->co); - copy_v3_v3(co[1], ltri[1]->v->co); - copy_v3_v3(co[2], ltri[2]->v->co); + copy_v3_v3(co[0], vert[face[i].v1].co); + copy_v3_v3(co[1], vert[face[i].v2].co); + copy_v3_v3(co[2], vert[face[i].v3].co); + if (face[i].v4) + copy_v3_v3(co[3], vert[face[i].v4].co); - BLI_bvhtree_insert(tree, i, co[0], 3); - } - } - } - else { - if (vert && face) { - for (i = 0; i < numFaces; i++) { - float co[4][3]; - if (mask && !BLI_BITMAP_TEST_BOOL(mask, i)) { - continue; - } - - copy_v3_v3(co[0], vert[face[i].v1].co); - copy_v3_v3(co[1], vert[face[i].v2].co); - copy_v3_v3(co[2], vert[face[i].v3].co); - if (face[i].v4) - copy_v3_v3(co[3], vert[face[i].v4].co); - - BLI_bvhtree_insert(tree, i, co[0], face[i].v4 ? 4 : 3); - } + BLI_bvhtree_insert(tree, i, co[0], face[i].v4 ? 4 : 3); } } + BLI_assert(BLI_bvhtree_get_size(tree) == faces_num_active); BLI_bvhtree_balance(tree); } } @@ -766,33 +723,24 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree( } static void bvhtree_from_mesh_faces_setup_data( - BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, - float epsilon, BMEditMesh *em, + BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon, MVert *vert, const bool vert_allocated, MFace *face, const bool face_allocated) { memset(data, 0, sizeof(*data)); - data->em_evil = em; if (tree) { data->tree = tree; data->cached = is_cached; - if (em) { - data->nearest_callback = editmesh_faces_nearest_point; - data->raycast_callback = editmesh_faces_spherecast; - data->nearest_to_ray_callback = NULL; - } - else { - data->nearest_callback = mesh_faces_nearest_point; - data->raycast_callback = mesh_faces_spherecast; - data->nearest_to_ray_callback = NULL; - - data->vert = vert; - data->vert_allocated = vert_allocated; - data->face = face; - data->face_allocated = face_allocated; - } + data->nearest_callback = mesh_faces_nearest_point; + data->raycast_callback = mesh_faces_spherecast; + data->nearest_to_ray_callback = NULL; + + data->vert = vert; + data->vert_allocated = vert_allocated; + data->face = face; + data->face_allocated = face_allocated; data->sphere_radius = epsilon; } @@ -807,54 +755,37 @@ static void bvhtree_from_mesh_faces_setup_data( } /* Builds a bvh tree where nodes are the tesselated faces of the given dm */ -BVHTree *bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) +BVHTree *bvhtree_from_mesh_faces( + BVHTreeFromMesh *data, DerivedMesh *dm, + float epsilon, int tree_type, int axis) { - BMEditMesh *em = data->em_evil; - const int bvhcache_type = em ? - (data->em_evil_all ? BVHTREE_FROM_FACES_EDITMESH_ALL : BVHTREE_FROM_FACES_EDITMESH_SNAP) : - BVHTREE_FROM_FACES; BVHTree *tree; MVert *vert = NULL; MFace *face = NULL; bool vert_allocated = false, face_allocated = false; BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_FACES); BLI_rw_mutex_unlock(&cache_rwlock); - if (em == NULL) { - vert = DM_get_vert_array(dm, &vert_allocated); - face = DM_get_tessface_array(dm, &face_allocated); - } + vert = DM_get_vert_array(dm, &vert_allocated); + face = DM_get_tessface_array(dm, &face_allocated); /* Not in cache */ if (tree == NULL) { BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_FACES); if (tree == NULL) { - int numFaces; - - /* BMESH specific check that we have tessfaces, - * we _could_ tessellate here but rather not - campbell - * - * this assert checks we have tessfaces, - * if not caller should use DM_ensure_tessface() */ - if (em) { - numFaces = em->tottri; - } - else { - numFaces = dm->getNumTessFaces(dm); - BLI_assert(!(numFaces == 0 && dm->getNumPolys(dm) != 0)); - } + int numFaces = dm->getNumTessFaces(dm); + BLI_assert(!(numFaces == 0 && dm->getNumPolys(dm) != 0)); tree = bvhtree_from_mesh_faces_create_tree( epsilon, tree_type, axis, - em, (bvhcache_type == BVHTREE_FROM_FACES_EDITMESH_ALL), vert, face, numFaces, NULL, -1); if (tree) { /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ - bvhcache_insert(&dm->bvhCache, tree, bvhcache_type); + bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_FACES); } } BLI_rw_mutex_unlock(&cache_rwlock); @@ -864,7 +795,7 @@ BVHTree *bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *dm, float e } /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_faces_setup_data(data, tree, true, epsilon, em, vert, vert_allocated, face, face_allocated); + bvhtree_from_mesh_faces_setup_data(data, tree, true, epsilon, vert, vert_allocated, face, face_allocated); return data->tree; } @@ -879,16 +810,16 @@ BVHTree *bvhtree_from_mesh_faces(BVHTreeFromMesh *data, DerivedMesh *dm, float e BVHTree *bvhtree_from_mesh_faces_ex( BVHTreeFromMesh *data, MVert *vert, const bool vert_allocated, MFace *face, const int numFaces, const bool face_allocated, - BLI_bitmap *mask, int numFaces_active, float epsilon, int tree_type, int axis) + const BLI_bitmap *faces_mask, int faces_num_active, + float epsilon, int tree_type, int axis) { BVHTree *tree = bvhtree_from_mesh_faces_create_tree( epsilon, tree_type, axis, - NULL, false, vert, face, numFaces, - mask, numFaces_active); + faces_mask, faces_num_active); /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_faces_setup_data(data, tree, false, epsilon, NULL, vert, vert_allocated, face, face_allocated); + bvhtree_from_mesh_faces_setup_data(data, tree, false, epsilon, vert, vert_allocated, face, face_allocated); return data->tree; } @@ -901,25 +832,19 @@ BVHTree *bvhtree_from_mesh_faces_ex( /** \name LoopTri Face Builder * \{ */ -static BVHTree *bvhtree_from_mesh_looptri_create_tree( +static BVHTree *bvhtree_from_editmesh_looptri_create_tree( float epsilon, int tree_type, int axis, - BMEditMesh *em, const bool em_all, - const MVert *vert, const MLoop *mloop, const MLoopTri *looptri, const int looptri_num, - BLI_bitmap *mask, int looptri_num_active) + BMEditMesh *em, const int looptri_num, + const BLI_bitmap *looptri_mask, int looptri_num_active) { BVHTree *tree = NULL; int i; if (looptri_num) { - if (mask && looptri_num_active < 0) { - looptri_num_active = 0; - for (i = 0; i < looptri_num; i++) { - if (BLI_BITMAP_TEST_BOOL(mask, i)) { - looptri_num_active++; - } - } + if (looptri_mask) { + BLI_assert(IN_RANGE_INCL(looptri_num_active, 0, looptri_num)); } - else if (!mask) { + else { looptri_num_active = looptri_num; } @@ -930,52 +855,13 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree( if (em) { const struct BMLoop *(*looptris)[3] = (void *)em->looptris; - /* avoid double-up on face searches for quads-ngons */ - bool insert_prev = false; - BMFace *f_prev = NULL; - - /* data->em_evil is only set for snapping, and only for the mesh of the object - * which is currently open in edit mode. When set, the bvhtree should not contain - * faces that will interfere with snapping (e.g. faces that are hidden/selected - * or faces that have selected verts). */ - /* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden * and/or selected. Even if the faces themselves are not selected for the snapped * transform, having a vertex selected means the face (and thus it's tessellated * triangles) will be moving and will not be a good snap targets. */ for (i = 0; i < looptri_num; i++) { const BMLoop **ltri = looptris[i]; - BMFace *f = ltri[0]->f; - bool insert = mask ? BLI_BITMAP_TEST_BOOL(mask, i) : true; - - /* Start with the assumption the triangle should be included for snapping. */ - if (f == f_prev) { - insert = insert_prev; - } - else if (insert) { - if (em_all) { - /* pass */ - } - else if (BM_elem_flag_test(f, BM_ELEM_SELECT) || BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - /* Don't insert triangles tessellated from faces that are hidden or selected */ - insert = false; - } - else { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) { - /* Don't insert triangles tessellated from faces that have any selected verts */ - insert = false; - break; - } - } while ((l_iter = l_iter->next) != l_first); - } - - /* skip if face doesn't change */ - f_prev = f; - insert_prev = insert; - } + bool insert = looptri_mask ? BLI_BITMAP_TEST_BOOL(looptri_mask, i) : true; if (insert) { /* No reason found to block hit-testing the triangle for snap, so insert it now.*/ @@ -988,22 +874,49 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree( } } } - else { - if (vert && looptri) { - for (i = 0; i < looptri_num; i++) { - float co[3][3]; - if (mask && !BLI_BITMAP_TEST_BOOL(mask, i)) { - continue; - } + BLI_assert(BLI_bvhtree_get_size(tree) == looptri_num_active); + BLI_bvhtree_balance(tree); + } + } - copy_v3_v3(co[0], vert[mloop[looptri[i].tri[0]].v].co); - copy_v3_v3(co[1], vert[mloop[looptri[i].tri[1]].v].co); - copy_v3_v3(co[2], vert[mloop[looptri[i].tri[2]].v].co); + return tree; +} - BLI_bvhtree_insert(tree, i, co[0], 3); +static BVHTree *bvhtree_from_mesh_looptri_create_tree( + float epsilon, int tree_type, int axis, + const MVert *vert, const MLoop *mloop, const MLoopTri *looptri, const int looptri_num, + const BLI_bitmap *looptri_mask, int looptri_num_active) +{ + BVHTree *tree = NULL; + int i; + + if (looptri_num) { + if (looptri_mask) { + BLI_assert(IN_RANGE_INCL(looptri_num_active, 0, looptri_num)); + } + else { + looptri_num_active = looptri_num; + } + + /* Create a bvh-tree of the given target */ + /* printf("%s: building BVH, total=%d\n", __func__, numFaces); */ + tree = BLI_bvhtree_new(looptri_num_active, epsilon, tree_type, axis); + if (tree) { + if (vert && looptri) { + for (i = 0; i < looptri_num; i++) { + float co[3][3]; + if (looptri_mask && !BLI_BITMAP_TEST_BOOL(looptri_mask, i)) { + continue; } + + copy_v3_v3(co[0], vert[mloop[looptri[i].tri[0]].v].co); + copy_v3_v3(co[1], vert[mloop[looptri[i].tri[1]].v].co); + copy_v3_v3(co[2], vert[mloop[looptri[i].tri[2]].v].co); + + BLI_bvhtree_insert(tree, i, co[0], 3); } } + BLI_assert(BLI_bvhtree_get_size(tree) == looptri_num_active); BLI_bvhtree_balance(tree); } } @@ -1012,36 +925,27 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree( } static void bvhtree_from_mesh_looptri_setup_data( - BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, - float epsilon, BMEditMesh *em, + BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon, const MVert *vert, const bool vert_allocated, const MLoop *mloop, const bool loop_allocated, const MLoopTri *looptri, const bool looptri_allocated) { memset(data, 0, sizeof(*data)); - data->em_evil = em; if (tree) { data->tree = tree; data->cached = is_cached; - if (em) { - data->nearest_callback = editmesh_faces_nearest_point; - data->raycast_callback = editmesh_faces_spherecast; - data->nearest_to_ray_callback = NULL; - } - else { - data->nearest_callback = mesh_looptri_nearest_point; - data->raycast_callback = mesh_looptri_spherecast; - data->nearest_to_ray_callback = NULL; - - data->vert = vert; - data->vert_allocated = vert_allocated; - data->loop = mloop; - data->loop_allocated = loop_allocated; - data->looptri = looptri; - data->looptri_allocated = looptri_allocated; - } + data->nearest_callback = mesh_looptri_nearest_point; + data->raycast_callback = mesh_looptri_spherecast; + data->nearest_to_ray_callback = NULL; + + data->vert = vert; + data->vert_allocated = vert_allocated; + data->loop = mloop; + data->loop_allocated = loop_allocated; + data->looptri = looptri; + data->looptri_allocated = looptri_allocated; data->sphere_radius = epsilon; } @@ -1059,16 +963,52 @@ static void bvhtree_from_mesh_looptri_setup_data( } /** + * Builds a bvh tree where nodes are the looptri faces of the given bm + */ +BVHTree *bvhtree_from_editmesh_looptri_ex( + BVHTreeFromEditMesh *data, BMEditMesh *em, + const BLI_bitmap *looptri_mask, int looptri_num_active, + float epsilon, int tree_type, int axis) +{ + /* BMESH specific check that we have tessfaces, + * we _could_ tessellate here but rather not - campbell + * + * this assert checks we have tessfaces, + * if not caller should use DM_ensure_tessface() */ + + BVHTree *tree = bvhtree_from_editmesh_looptri_create_tree( + epsilon, tree_type, axis, + em, em->tottri, looptri_mask, looptri_num_active); + + if (tree) { + data->tree = tree; + data->nearest_callback = editmesh_looptri_nearest_point; + data->raycast_callback = editmesh_looptri_spherecast; + data->nearest_to_ray_callback = NULL; + data->sphere_radius = 0.0f; + data->em = em; + } + return tree; +} + +BVHTree *bvhtree_from_editmesh_looptri( + BVHTreeFromEditMesh *data, BMEditMesh *em, + float epsilon, int tree_type, int axis) +{ + return bvhtree_from_editmesh_looptri_ex( + data, em, NULL, -1, + epsilon, tree_type, axis); +} + +/** * Builds a bvh tree where nodes are the looptri faces of the given dm * * \note for editmesh this is currently a duplicate of bvhtree_from_mesh_faces */ -BVHTree *bvhtree_from_mesh_looptri(BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) +BVHTree *bvhtree_from_mesh_looptri( + BVHTreeFromMesh *data, DerivedMesh *dm, + float epsilon, int tree_type, int axis) { - BMEditMesh *em = data->em_evil; - const int bvhcache_type = em ? - (data->em_evil_all ? BVHTREE_FROM_FACES_EDITMESH_ALL : BVHTREE_FROM_FACES_EDITMESH_SNAP) : - BVHTREE_FROM_LOOPTRI; BVHTree *tree; MVert *mvert = NULL; MLoop *mloop = NULL; @@ -1078,58 +1018,42 @@ BVHTree *bvhtree_from_mesh_looptri(BVHTreeFromMesh *data, DerivedMesh *dm, float bool looptri_allocated = false; BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_READ); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_LOOPTRI); BLI_rw_mutex_unlock(&cache_rwlock); - if (em == NULL) { - MPoly *mpoly; - bool poly_allocated = false; - - mvert = DM_get_vert_array(dm, &vert_allocated); - mpoly = DM_get_poly_array(dm, &poly_allocated); + MPoly *mpoly; + bool poly_allocated = false; - mloop = DM_get_loop_array(dm, &loop_allocated); - looptri = DM_get_looptri_array( - dm, - mvert, - mpoly, dm->getNumPolys(dm), - mloop, dm->getNumLoops(dm), - &looptri_allocated); + mvert = DM_get_vert_array(dm, &vert_allocated); + mpoly = DM_get_poly_array(dm, &poly_allocated); - if (poly_allocated) { - MEM_freeN(mpoly); - } + mloop = DM_get_loop_array(dm, &loop_allocated); + looptri = DM_get_looptri_array( + dm, + mvert, + mpoly, dm->getNumPolys(dm), + mloop, dm->getNumLoops(dm), + &looptri_allocated); + if (poly_allocated) { + MEM_freeN(mpoly); } /* Not in cache */ if (tree == NULL) { BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); - tree = bvhcache_find(&dm->bvhCache, bvhcache_type); + tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_LOOPTRI); if (tree == NULL) { - int looptri_num; - - /* BMESH specific check that we have tessfaces, - * we _could_ tessellate here but rather not - campbell - * - * this assert checks we have tessfaces, - * if not caller should use DM_ensure_tessface() */ - if (em) { - looptri_num = em->tottri; - } - else { - looptri_num = dm->getNumLoopTri(dm); - BLI_assert(!(looptri_num == 0 && dm->getNumPolys(dm) != 0)); - } + int looptri_num = dm->getNumLoopTri(dm); + BLI_assert(!(looptri_num == 0 && dm->getNumPolys(dm) != 0)); tree = bvhtree_from_mesh_looptri_create_tree( epsilon, tree_type, axis, - em, (bvhcache_type == BVHTREE_FROM_FACES_EDITMESH_ALL), mvert, mloop, looptri, looptri_num, NULL, -1); if (tree) { /* Save on cache for later use */ /* printf("BVHTree built and saved on cache\n"); */ - bvhcache_insert(&dm->bvhCache, tree, bvhcache_type); + bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_LOOPTRI); } } BLI_rw_mutex_unlock(&cache_rwlock); @@ -1140,7 +1064,7 @@ BVHTree *bvhtree_from_mesh_looptri(BVHTreeFromMesh *data, DerivedMesh *dm, float /* Setup BVHTreeFromMesh */ bvhtree_from_mesh_looptri_setup_data( - data, tree, true, epsilon, em, + data, tree, true, epsilon, mvert, vert_allocated, mloop, loop_allocated, looptri, looptri_allocated); @@ -1153,18 +1077,17 @@ BVHTree *bvhtree_from_mesh_looptri_ex( const struct MVert *vert, const bool vert_allocated, const struct MLoop *mloop, const bool loop_allocated, const struct MLoopTri *looptri, const int looptri_num, const bool looptri_allocated, - BLI_bitmap *mask, int looptri_num_active, + const BLI_bitmap *looptri_mask, int looptri_num_active, float epsilon, int tree_type, int axis) { BVHTree *tree = bvhtree_from_mesh_looptri_create_tree( epsilon, tree_type, axis, - NULL, false, vert, mloop, looptri, looptri_num, - mask, looptri_num_active); + looptri_mask, looptri_num_active); /* Setup BVHTreeFromMesh */ bvhtree_from_mesh_looptri_setup_data( - data, tree, false, epsilon, NULL, + data, tree, false, epsilon, vert, vert_allocated, mloop, loop_allocated, looptri, looptri_allocated); @@ -1175,6 +1098,15 @@ BVHTree *bvhtree_from_mesh_looptri_ex( /** \} */ +/* Frees data allocated by a call to bvhtree_from_editmesh_*. */ +void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data) +{ + if (data->tree) { + BLI_bvhtree_free(data->tree); + memset(data, 0, sizeof(*data)); + } +} + /* Frees data allocated by a call to bvhtree_from_mesh_*. */ void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data) { @@ -1215,32 +1147,46 @@ typedef struct BVHCacheItem { } BVHCacheItem; -static void bvhcacheitem_set_if_match(void *_cached, void *_search) +/** + * Queries a bvhcache for the cache bvhtree of the request type + */ +BVHTree *bvhcache_find(BVHCache *cache, int type) { - BVHCacheItem *cached = (BVHCacheItem *)_cached; - BVHCacheItem *search = (BVHCacheItem *)_search; - - if (search->type == cached->type) { - search->tree = cached->tree; + while (cache) { + const BVHCacheItem *item = cache->link; + if (item->type == type) { + return item->tree; + } + cache = cache->next; } -} + return NULL; +} -BVHTree *bvhcache_find(BVHCache *cache, int type) +bool bvhcache_has_tree(const BVHCache *cache, const BVHTree *tree) { - BVHCacheItem item; - item.type = type; - item.tree = NULL; - - BLI_linklist_apply(*cache, bvhcacheitem_set_if_match, &item); - return item.tree; + while (cache) { + const BVHCacheItem *item = cache->link; + if (item->tree == tree) { + return true; + } + cache = cache->next; + } + return false; } -void bvhcache_insert(BVHCache *cache, BVHTree *tree, int type) +/** + * Inserts a BVHTree of the given type under the cache + * After that the caller no longer needs to worry when to free the BVHTree + * as that will be done when the cache is freed. + * + * A call to this assumes that there was no previous cached tree of the given type + */ +void bvhcache_insert(BVHCache **cache_p, BVHTree *tree, int type) { BVHCacheItem *item = NULL; assert(tree != NULL); - assert(bvhcache_find(cache, type) == NULL); + assert(bvhcache_find(*cache_p, type) == NULL); item = MEM_mallocN(sizeof(BVHCacheItem), "BVHCacheItem"); assert(item != NULL); @@ -1248,13 +1194,15 @@ void bvhcache_insert(BVHCache *cache, BVHTree *tree, int type) item->type = type; item->tree = tree; - BLI_linklist_prepend(cache, item); + BLI_linklist_prepend(cache_p, item); } - -void bvhcache_init(BVHCache *cache) +/** + * inits and frees a bvhcache + */ +void bvhcache_init(BVHCache **cache_p) { - *cache = NULL; + *cache_p = NULL; } static void bvhcacheitem_free(void *_item) @@ -1266,10 +1214,10 @@ static void bvhcacheitem_free(void *_item) } -void bvhcache_free(BVHCache *cache) +void bvhcache_free(BVHCache **cache_p) { - BLI_linklist_free(*cache, (LinkNodeFreeFP)bvhcacheitem_free); - *cache = NULL; + BLI_linklist_free(*cache_p, (LinkNodeFreeFP)bvhcacheitem_free); + *cache_p = NULL; } /** \} */ diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 6fd756e2788..96bac2c2f41 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -76,6 +76,8 @@ void BKE_camera_init(Camera *cam) /* stereoscopy 3d */ cam->stereo.interocular_distance = 0.065f; cam->stereo.convergence_distance = 30.f * 0.065f; + cam->stereo.pole_merge_angle_from = DEG2RAD(60.0f); + cam->stereo.pole_merge_angle_to = DEG2RAD(75.0f); } void *BKE_camera_add(Main *bmain, const char *name) @@ -904,7 +906,7 @@ static Object *camera_multiview_advanced(Scene *scene, Object *camera, const cha /* returns the camera to be used for render */ Object *BKE_camera_multiview_render(Scene *scene, Object *camera, const char *viewname) { - const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + const bool is_multiview = (camera != NULL) && (scene->r.scemode & R_MULTIVIEW) != 0; if (!is_multiview) { return camera; diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 87dc08e5124..9c163990dd2 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -491,11 +491,12 @@ static void cdDM_drawFacesTex_common( CDDerivedMesh *cddm = (CDDerivedMesh *) dm; const MPoly *mpoly = cddm->mpoly; MTexPoly *mtexpoly = DM_get_poly_data_layer(dm, CD_MTEXPOLY); - const MLoopCol *mloopcol; + const MLoopCol *mloopcol = NULL; int i; int colType, start_element, tot_drawn; const bool use_hide = (flag & DM_DRAW_SKIP_HIDDEN) != 0; const bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_colors = (flag & DM_DRAW_USE_COLORS) != 0; int totpoly; int next_actualFace; int mat_index; @@ -525,15 +526,17 @@ static void cdDM_drawFacesTex_common( } } - colType = CD_TEXTURE_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - if (!mloopcol) { - colType = CD_PREVIEW_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - } - if (!mloopcol) { - colType = CD_MLOOPCOL; + if (use_colors) { + colType = CD_TEXTURE_MLOOPCOL; mloopcol = dm->getLoopDataArray(dm, colType); + if (!mloopcol) { + colType = CD_PREVIEW_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } + if (!mloopcol) { + colType = CD_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } } GPU_vertex_setup(dm); diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 9454ad62ed3..fa0960515b4 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -57,6 +57,7 @@ static void cloth_to_object (Object *ob, ClothModifierData *clmd, float (*verte static void cloth_from_mesh ( ClothModifierData *clmd, DerivedMesh *dm ); static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float framenr, int first); static void cloth_update_springs( ClothModifierData *clmd ); +static void cloth_update_spring_lengths( ClothModifierData *clmd, DerivedMesh *dm ); static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ); static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ); @@ -346,6 +347,10 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul /* Support for dynamic vertex groups, changing from frame to frame */ cloth_apply_vgroup ( clmd, result ); + + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW ) + cloth_update_spring_lengths ( clmd, result ); + cloth_update_springs( clmd ); // TIMEIT_START(cloth_step) @@ -569,6 +574,7 @@ int cloth_uses_vgroup(ClothModifierData *clmd) { return (((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SCALING ) || (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL ) || + (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW) || (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF)) && ((clmd->sim_parms->vgroup_mass>0) || (clmd->sim_parms->vgroup_struct>0)|| @@ -609,6 +615,9 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) else verts->goal= 0.0f; + /* Compute base cloth shrink weight */ + verts->shrink_factor = 0.0f; + /* Reset vertex flags */ verts->flags &= ~CLOTH_VERT_FLAG_PINNED; verts->flags &= ~CLOTH_VERT_FLAG_NOSELFCOLL; @@ -646,16 +655,14 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) verts->flags |= CLOTH_VERT_FLAG_NOSELFCOLL; } } - + } + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW ) { if (clmd->sim_parms->vgroup_shrink > 0) { if (dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_shrink - 1)) { - /* linear interpolation between min and max shrink factor based on weight */ - verts->shrink_factor = clmd->sim_parms->shrink_min * (1.0f - dvert->dw[j].weight) + clmd->sim_parms->shrink_max * dvert->dw [j].weight; + /* used for linear interpolation between min and max shrink factor based on weight */ + verts->shrink_factor = dvert->dw[j].weight; } } - else { - verts->shrink_factor = clmd->sim_parms->shrink_min; - } } } } @@ -663,6 +670,23 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm ) } } +static float cloth_shrink_factor(ClothModifierData *clmd, ClothVertex *verts, int i1, int i2) +{ + if ( clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW ) { + /* linear interpolation between min and max shrink factor based on weight */ + float base = 1.0f - clmd->sim_parms->shrink_min; + float delta = clmd->sim_parms->shrink_min - clmd->sim_parms->shrink_max; + + float k1 = base + delta * verts[i1].shrink_factor; + float k2 = base + delta * verts[i2].shrink_factor; + + /* Use geometrical mean to average two factors since it behaves better + for diagonals when a rectangle transforms into a trapezoid. */ + return sqrtf(k1 * k2); + } + else + return 1.0f; +} static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *dm, float UNUSED(framenr), int first) { @@ -720,11 +744,11 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d mul_m4_v3(ob->obmat, verts->x); if ( shapekey_rest ) { - verts->xrest= shapekey_rest[i]; + copy_v3_v3(verts->xrest, shapekey_rest[i]); mul_m4_v3(ob->obmat, verts->xrest); } else - verts->xrest = verts->x; + copy_v3_v3(verts->xrest, verts->x); } /* no GUI interface yet */ @@ -736,6 +760,8 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d else verts->goal= 0.0f; + verts->shrink_factor = 0.0f; + verts->flags = 0; copy_v3_v3 ( verts->xold, verts->x ); copy_v3_v3 ( verts->xconst, verts->x ); @@ -1079,6 +1105,53 @@ static void cloth_update_springs( ClothModifierData *clmd ) cloth_hair_update_bending_targets(clmd); } +/* Update spring rest lenght, for dynamically deformable cloth */ +static void cloth_update_spring_lengths( ClothModifierData *clmd, DerivedMesh *dm ) +{ + Cloth *cloth = clmd->clothObject; + LinkNode *search = cloth->springs; + unsigned int struct_springs = 0; + unsigned int i = 0; + unsigned int mvert_num = (unsigned int)dm->getNumVerts(dm); + float shrink_factor; + + clmd->sim_parms->avg_spring_len = 0.0f; + + for (i = 0; i < mvert_num; i++) { + cloth->verts[i].avg_spring_len = 0.0f; + } + + while (search) { + ClothSpring *spring = search->link; + + if ( spring->type != CLOTH_SPRING_TYPE_SEWING ) { + if ( spring->type & (CLOTH_SPRING_TYPE_STRUCTURAL | CLOTH_SPRING_TYPE_SHEAR | CLOTH_SPRING_TYPE_BENDING) ) + shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); + else + shrink_factor = 1.0f; + + spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor; + } + + if ( spring->type == CLOTH_SPRING_TYPE_STRUCTURAL ) { + clmd->sim_parms->avg_spring_len += spring->restlen; + cloth->verts[spring->ij].avg_spring_len += spring->restlen; + cloth->verts[spring->kl].avg_spring_len += spring->restlen; + struct_springs++; + } + + search = search->next; + } + + if (struct_springs > 0) + clmd->sim_parms->avg_spring_len /= struct_springs; + + for (i = 0; i < mvert_num; i++) { + if (cloth->verts[i].spring_count > 0) + cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count); + } +} + BLI_INLINE void cross_identity_v3(float r[3][3], const float v[3]) { zero_m3(r); @@ -1118,7 +1191,7 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) { Cloth *cloth = clmd->clothObject; ClothSpring *spring = NULL, *tspring = NULL, *tspring2 = NULL; - unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0; + unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0, struct_springs_real = 0; unsigned int i = 0; unsigned int mvert_num = (unsigned int)dm->getNumVerts(dm); unsigned int numedges = (unsigned int)dm->getNumEdges (dm); @@ -1162,19 +1235,19 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) spring->type = CLOTH_SPRING_TYPE_SEWING; } else { - if (clmd->sim_parms->vgroup_shrink > 0) - shrink_factor = 1.0f - ((cloth->verts[spring->ij].shrink_factor + cloth->verts[spring->kl].shrink_factor) / 2.0f); - else - shrink_factor = 1.0f - clmd->sim_parms->shrink_min; + shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor; spring->stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f; spring->type = CLOTH_SPRING_TYPE_STRUCTURAL; + + clmd->sim_parms->avg_spring_len += spring->restlen; + cloth->verts[spring->ij].avg_spring_len += spring->restlen; + cloth->verts[spring->kl].avg_spring_len += spring->restlen; + cloth->verts[spring->ij].spring_count++; + cloth->verts[spring->kl].spring_count++; + struct_springs_real++; } - clmd->sim_parms->avg_spring_len += spring->restlen; - cloth->verts[spring->ij].avg_spring_len += spring->restlen; - cloth->verts[spring->kl].avg_spring_len += spring->restlen; - cloth->verts[spring->ij].spring_count++; - cloth->verts[spring->kl].spring_count++; + spring->flags = 0; struct_springs++; @@ -1186,11 +1259,12 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) } } - if (struct_springs > 0) - clmd->sim_parms->avg_spring_len /= struct_springs; + if (struct_springs_real > 0) + clmd->sim_parms->avg_spring_len /= struct_springs_real; for (i = 0; i < mvert_num; i++) { - cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count); + if (cloth->verts[i].spring_count > 0) + cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count); } // shear springs @@ -1212,10 +1286,7 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) mloop[mpoly[i].loopstart + (j + 0)].v, mloop[mpoly[i].loopstart + (j + 2)].v); - if (clmd->sim_parms->vgroup_shrink > 0) - shrink_factor = 1.0f - ((cloth->verts[spring->ij].shrink_factor + cloth->verts[spring->kl].shrink_factor) / 2.0f); - else - shrink_factor = 1.0f - clmd->sim_parms->shrink_min; + shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor; spring->type = CLOTH_SPRING_TYPE_SHEAR; spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f; @@ -1259,7 +1330,8 @@ static int cloth_build_springs ( ClothModifierData *clmd, DerivedMesh *dm ) } spring_verts_ordered_set(spring, tspring2->ij, index2); - spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest); + shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); + spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor; spring->type = CLOTH_SPRING_TYPE_BENDING; spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f; BLI_edgeset_insert(edgeset, spring->ij, spring->kl); diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index d35762a6f13..8cac856b560 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -224,7 +224,7 @@ static int cloth_collision_response_static ( ClothModifierData *clmd, CollisionM float w1, w2, w3, u1, u2, u3; float v1[3], v2[3], relativeVelocity[3]; float magrelVel; - float epsilon2 = BLI_bvhtree_getepsilon ( collmd->bvhtree ); + float epsilon2 = BLI_bvhtree_get_epsilon ( collmd->bvhtree ); cloth1 = clmd->clothObject; @@ -396,7 +396,7 @@ static CollPair* cloth_collision(ModifierData *md1, ModifierData *md2, #endif double distance = 0; float epsilon1 = clmd->coll_parms->epsilon; - float epsilon2 = BLI_bvhtree_getepsilon ( collmd->bvhtree ); + float epsilon2 = BLI_bvhtree_get_epsilon ( collmd->bvhtree ); tri_a = &clmd->clothObject->tri[overlap->indexA]; tri_b = &collmd->tri[overlap->indexB]; @@ -909,7 +909,7 @@ static bool cloth_points_collision_response_static(ClothModifierData *clmd, Coll // float w1, w2; float u1, u2, u3; float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3]; - float epsilon2 = BLI_bvhtree_getepsilon ( collmd->bvhtree ); + float epsilon2 = BLI_bvhtree_get_epsilon ( collmd->bvhtree ); for ( ; collpair != collision_end; collpair++ ) { float margin_distance = (float)(collpair->distance - (double)epsilon2); @@ -1249,7 +1249,7 @@ int cloth_points_objcollision(Object *ob, ClothModifierData *clmd, float step, f /* search for overlapping collision pairs */ overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result, NULL, NULL); - epsilon = BLI_bvhtree_getepsilon(collmd->bvhtree); + epsilon = BLI_bvhtree_get_epsilon(collmd->bvhtree); // go to next object if no overlap is there if (result && overlap) { @@ -1375,7 +1375,7 @@ void cloth_find_point_contacts(Object *ob, ClothModifierData *clmd, float step, /* search for overlapping collision pairs */ overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result, NULL, NULL); - epsilon = BLI_bvhtree_getepsilon(collmd->bvhtree); + epsilon = BLI_bvhtree_get_epsilon(collmd->bvhtree); // go to next object if no overlap is there if (result && overlap) { diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 1b9ac499e1f..a591d536b43 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -535,10 +535,10 @@ static void contarget_get_lattice_mat(Object *ob, const char *substring, float m /* generic function to get the appropriate matrix for most target cases */ /* The cases where the target can be object data have not been implemented */ -static void constraint_target_to_mat4(Object *ob, const char *substring, float mat[4][4], short from, short to, float headtail) +static void constraint_target_to_mat4(Object *ob, const char *substring, float mat[4][4], short from, short to, short flag, float headtail) { /* Case OBJECT */ - if (!strlen(substring)) { + if (substring[0] == '\0') { copy_m4_m4(mat, ob->obmat); BKE_constraint_mat_convertspace(ob, NULL, mat, from, to, false); } @@ -573,6 +573,58 @@ static void constraint_target_to_mat4(Object *ob, const char *substring, float m /* skip length interpolation if set to head */ mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); } + else if ((pchan->bone) && (pchan->bone->segments > 1) && (flag & CONSTRAINT_BBONE_SHAPE)) { + /* use point along bbone */ + Mat4 bbone[MAX_BBONE_SUBDIV]; + float tempmat[4][4]; + float loc[3], fac; + + /* get bbone segments */ + b_bone_spline_setup(pchan, 0, bbone); + + /* figure out which segment(s) the headtail value falls in */ + fac = (float)pchan->bone->segments * headtail; + + if (fac >= pchan->bone->segments - 1) { + /* special case: end segment doesn't get created properly... */ + float pt[3], sfac; + int index; + + /* bbone points are in bonespace, so need to move to posespace first */ + index = pchan->bone->segments - 1; + mul_v3_m4v3(pt, pchan->pose_mat, bbone[index].mat[3]); + + /* interpolate between last segment point and the endpoint */ + sfac = fac - (float)(pchan->bone->segments - 1); /* fac is just the "leftover" between penultimate and last points */ + interp_v3_v3v3(loc, pt, pchan->pose_tail, sfac); + } + else { + /* get indices for finding interpolating between points along the bbone */ + float pt_a[3], pt_b[3], pt[3]; + int index_a, index_b; + + index_a = floorf(fac); + CLAMP(index_a, 0, MAX_BBONE_SUBDIV - 1); + + index_b = ceilf(fac); + CLAMP(index_b, 0, MAX_BBONE_SUBDIV - 1); + + /* interpolate between these points */ + copy_v3_v3(pt_a, bbone[index_a].mat[3]); + copy_v3_v3(pt_b, bbone[index_b].mat[3]); + + interp_v3_v3v3(pt, pt_a, pt_b, fac - floorf(fac)); + + /* move the point from bone local space to pose space... */ + mul_v3_m4v3(loc, pchan->pose_mat, pt); + } + + /* use interpolated distance for subtarget */ + copy_m4_m4(tempmat, pchan->pose_mat); + copy_v3_v3(tempmat[3], loc); + + mul_m4_m4m4(mat, ob->obmat, tempmat); + } else { float tempmat[4][4], loc[3]; @@ -634,7 +686,7 @@ static bConstraintTypeInfo CTI_CONSTRNAME = { static void default_get_tarmat(bConstraint *con, bConstraintOb *UNUSED(cob), bConstraintTarget *ct, float UNUSED(ctime)) { if (VALID_CONS_TARGET(ct)) - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); else if (ct) unit_m4(ct->matrix); } @@ -1102,7 +1154,7 @@ static void kinematic_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstrai bKinematicConstraint *data = con->data; if (VALID_CONS_TARGET(ct)) - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); else if (ct) { if (data->flag & CONSTRAINT_IK_AUTO) { Object *ob = cob->ob; @@ -1985,7 +2037,7 @@ static void pycon_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstraintTa /* firstly calculate the matrix the normal way, then let the py-function override * this matrix if it needs to do so */ - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); /* only execute target calculation if allowed */ #ifdef WITH_PYTHON @@ -2097,7 +2149,7 @@ static void actcon_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstraintT unit_m4(ct->matrix); /* get the transform matrix of the target */ - constraint_target_to_mat4(ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); /* determine where in transform range target is */ /* data->type is mapped as follows for backwards compatibility: @@ -2143,29 +2195,26 @@ static void actcon_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstraintT } else if (cob->type == CONSTRAINT_OBTYPE_BONE) { Object workob; - bPose *pose; + bPose pose = {0}; bPoseChannel *pchan, *tchan; - - /* make a temporary pose and evaluate using that */ - pose = MEM_callocN(sizeof(bPose), "pose"); - + /* make a copy of the bone of interest in the temp pose before evaluating action, so that it can get set * - we need to manually copy over a few settings, including rotation order, otherwise this fails */ pchan = cob->pchan; - tchan = BKE_pose_channel_verify(pose, pchan->name); + tchan = BKE_pose_channel_verify(&pose, pchan->name); tchan->rotmode = pchan->rotmode; /* evaluate action using workob (it will only set the PoseChannel in question) */ - what_does_obaction(cob->ob, &workob, pose, data->act, pchan->name, t); + what_does_obaction(cob->ob, &workob, &pose, data->act, pchan->name, t); /* convert animation to matrices for use here */ BKE_pchan_calc_mat(tchan); copy_m4_m4(ct->matrix, tchan->chan_mat); /* Clean up */ - BKE_pose_free(pose); + BKE_pose_free_data(&pose); } else { /* behavior undefined... */ diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 4c099987404..8afb451f768 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1402,14 +1402,14 @@ void BKE_nurb_makeCurve(Nurb *nu, float *coord_array, float *tilt_array, float * } } - coord_fp = (float *)(((char *)coord_fp) + stride); + coord_fp = POINTER_OFFSET(coord_fp, stride); if (tilt_fp) - tilt_fp = (float *)(((char *)tilt_fp) + stride); + tilt_fp = POINTER_OFFSET(tilt_fp, stride); if (radius_fp) - radius_fp = (float *)(((char *)radius_fp) + stride); + radius_fp = POINTER_OFFSET(radius_fp, stride); if (weight_fp) - weight_fp = (float *)(((char *)weight_fp) + stride); + weight_fp = POINTER_OFFSET(weight_fp, stride); u += ustep; } @@ -1440,7 +1440,7 @@ void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float for (a = 0; a <= it; a++) { *p = q0; - p = (float *)(((char *)p) + stride); + p = POINTER_OFFSET(p, stride); q0 += q1; q1 += q2; q2 += q3; @@ -1465,7 +1465,7 @@ void BKE_curve_forward_diff_tangent_bezier(float q0, float q1, float q2, float q for (a = 0; a <= it; a++) { *p = q0; - p = (float *)(((char *)p) + stride); + p = POINTER_OFFSET(p, stride); q0 += q1; q1 += q2; } @@ -1490,7 +1490,7 @@ static void forward_diff_bezier_cotangent(const float p0[3], const float p1[3], ( 6.0f * t) * p3[i]; } normalize_v3(p); - p = (float *)(((char *)p) + stride); + p = POINTER_OFFSET(p, stride); } } @@ -2102,7 +2102,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * *tilt_array = t[0] * pprev->alfa + t[1] * prevbezt->alfa + t[2] * bezt->alfa + t[3] * next->alfa; } - tilt_array = (float *)(((char *)tilt_array) + stride); + tilt_array = POINTER_OFFSET(tilt_array, stride); } if (radius_array) { @@ -2123,7 +2123,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * t[2] * bezt->radius + t[3] * next->radius; } - radius_array = (float *)(((char *)radius_array) + stride); + radius_array = POINTER_OFFSET(radius_array, stride); } if (weight_array) { @@ -2131,7 +2131,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * *weight_array = prevbezt->weight + (bezt->weight - prevbezt->weight) * (3.0f * fac * fac - 2.0f * fac * fac * fac); - weight_array = (float *)(((char *)weight_array) + stride); + weight_array = POINTER_OFFSET(weight_array, stride); } } } diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index b0d0dc08126..de79a30bd60 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1530,13 +1530,11 @@ void CustomData_realloc(CustomData *data, int totelem) for (i = 0; i < data->totlayer; ++i) { CustomDataLayer *layer = &data->layers[i]; const LayerTypeInfo *typeInfo; - int size; if (layer->flag & CD_FLAG_NOFREE) { continue; } typeInfo = layerType_getInfo(layer->type); - size = totelem * typeInfo->size; - layer->data = MEM_reallocN(layer->data, size); + layer->data = MEM_reallocN(layer->data, (size_t)totelem * typeInfo->size); } } @@ -1844,7 +1842,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, int typ int totelem, const char *name) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); - const int size = totelem * typeInfo->size; + const size_t size = (size_t)totelem * typeInfo->size; int flag = 0, index = data->totlayer; void *newlayerdata = NULL; @@ -2064,7 +2062,7 @@ static void *customData_duplicate_referenced_layer_index(CustomData *data, const const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if (typeInfo->copy) { - void *dst_data = MEM_mallocN(totelem * typeInfo->size, "CD duplicate ref layer"); + void *dst_data = MEM_mallocN((size_t)totelem * typeInfo->size, "CD duplicate ref layer"); typeInfo->copy(layer->data, dst_data, totelem); layer->data = dst_data; } @@ -2172,7 +2170,7 @@ void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs, if (typeInfo->copy) typeInfo->copy(src_data_ofs, dst_data_ofs, count); else - memcpy(dst_data_ofs, src_data_ofs, count * typeInfo->size); + memcpy(dst_data_ofs, src_data_ofs, (size_t)count * typeInfo->size); } static void CustomData_copy_data_layer( @@ -2181,16 +2179,14 @@ static void CustomData_copy_data_layer( int src_index, int dst_index, int count) { const LayerTypeInfo *typeInfo; - int src_offset; - int dst_offset; const void *src_data = source->layers[src_i].data; void *dst_data = dest->layers[dst_i].data; typeInfo = layerType_getInfo(source->layers[src_i].type); - src_offset = src_index * typeInfo->size; - dst_offset = dst_index * typeInfo->size; + const size_t src_offset = (size_t)src_index * typeInfo->size; + const size_t dst_offset = (size_t)dst_index * typeInfo->size; if (!count || !src_data || !dst_data) { if (count && !(src_data == NULL && dst_data == NULL)) { @@ -2201,14 +2197,16 @@ static void CustomData_copy_data_layer( return; } - if (typeInfo->copy) + if (typeInfo->copy) { typeInfo->copy(POINTER_OFFSET(src_data, src_offset), POINTER_OFFSET(dst_data, dst_offset), count); - else + } + else { memcpy(POINTER_OFFSET(dst_data, dst_offset), POINTER_OFFSET(src_data, src_offset), - count * typeInfo->size); + (size_t)count * typeInfo->size); + } } void CustomData_copy_data_named(const CustomData *source, CustomData *dest, @@ -2270,7 +2268,7 @@ void CustomData_free_elem(CustomData *data, int index, int count) typeInfo = layerType_getInfo(data->layers[i].type); if (typeInfo->free) { - int offset = index * typeInfo->size; + size_t offset = (size_t)index * typeInfo->size; typeInfo->free(POINTER_OFFSET(data->layers[i].data, offset), count, typeInfo->size); } @@ -2281,7 +2279,7 @@ void CustomData_free_elem(CustomData *data, int index, int count) #define SOURCE_BUF_SIZE 100 void CustomData_interp(const CustomData *source, CustomData *dest, - int *src_indices, float *weights, float *sub_weights, + const int *src_indices, const float *weights, const float *sub_weights, int count, int dest_index) { int src_i, dest_i; @@ -2316,11 +2314,11 @@ void CustomData_interp(const CustomData *source, CustomData *dest, void *src_data = source->layers[src_i].data; for (j = 0; j < count; ++j) { - sources[j] = POINTER_OFFSET(src_data, src_indices[j] * typeInfo->size); + sources[j] = POINTER_OFFSET(src_data, (size_t)src_indices[j] * typeInfo->size); } typeInfo->interp(sources, weights, sub_weights, count, - POINTER_OFFSET(dest->layers[dest_i].data, dest_index * typeInfo->size)); + POINTER_OFFSET(dest->layers[dest_i].data, (size_t)dest_index * typeInfo->size)); /* if there are multiple source & dest layers of the same type, * we don't want to copy all source layers to the same dest, so @@ -2349,7 +2347,7 @@ void CustomData_swap_corners(struct CustomData *data, int index, const int *corn typeInfo = layerType_getInfo(data->layers[i].type); if (typeInfo->swap) { - const int offset = index * typeInfo->size; + const size_t offset = (size_t)index * typeInfo->size; typeInfo->swap(POINTER_OFFSET(data->layers[i].data, offset), corner_indices); } @@ -2387,7 +2385,6 @@ void CustomData_swap(struct CustomData *data, const int index_a, const int index void *CustomData_get(const CustomData *data, int index, int type) { - int offset; int layer_index; BLI_assert(index >= 0); @@ -2397,7 +2394,7 @@ void *CustomData_get(const CustomData *data, int index, int type) if (layer_index == -1) return NULL; /* get the offset of the desired element */ - offset = layerType_getInfo(type)->size * index; + const size_t offset = (size_t)index * layerType_getInfo(type)->size; return POINTER_OFFSET(data->layers[layer_index].data, offset); } @@ -2405,7 +2402,6 @@ void *CustomData_get(const CustomData *data, int index, int type) void *CustomData_get_n(const CustomData *data, int type, int index, int n) { int layer_index; - int offset; BLI_assert(index >= 0 && n >= 0); @@ -2413,7 +2409,7 @@ void *CustomData_get_n(const CustomData *data, int type, int index, int n) layer_index = data->typemap[type]; if (layer_index == -1) return NULL; - offset = layerType_getInfo(type)->size * index; + const size_t offset = (size_t)index * layerType_getInfo(type)->size; return POINTER_OFFSET(data->layers[layer_index + n].data, offset); } @@ -2838,7 +2834,7 @@ void CustomData_bmesh_free_block_data(CustomData *data, void *block) typeInfo = layerType_getInfo(data->layers[i].type); if (typeInfo->free) { - int offset = data->layers[i].offset; + const size_t offset = data->layers[i].offset; typeInfo->free(POINTER_OFFSET(block, offset), 1, typeInfo->size); } } @@ -3218,7 +3214,7 @@ void CustomData_to_bmesh_block(const CustomData *source, CustomData *dest, int src_index, void **dest_block, bool use_default_init) { const LayerTypeInfo *typeInfo; - int dest_i, src_i, src_offset; + int dest_i, src_i; if (*dest_block == NULL) CustomData_bmesh_alloc_block(dest, dest_block); @@ -3247,7 +3243,7 @@ void CustomData_to_bmesh_block(const CustomData *source, CustomData *dest, void *dest_data = POINTER_OFFSET(*dest_block, offset); typeInfo = layerType_getInfo(dest->layers[dest_i].type); - src_offset = src_index * typeInfo->size; + const size_t src_offset = (size_t)src_index * typeInfo->size; if (typeInfo->copy) typeInfo->copy(POINTER_OFFSET(src_data, src_offset), dest_data, 1); @@ -3294,7 +3290,7 @@ void CustomData_from_bmesh_block(const CustomData *source, CustomData *dest, const LayerTypeInfo *typeInfo = layerType_getInfo(dest->layers[dest_i].type); int offset = source->layers[src_i].offset; const void *src_data = POINTER_OFFSET(src_block, offset); - void *dst_data = POINTER_OFFSET(dest->layers[dest_i].data, dst_index * typeInfo->size); + void *dst_data = POINTER_OFFSET(dest->layers[dest_i].data, (size_t)dst_index * typeInfo->size); if (typeInfo->copy) typeInfo->copy(src_data, dst_data, 1); @@ -3311,12 +3307,12 @@ void CustomData_from_bmesh_block(const CustomData *source, CustomData *dest, } -void CustomData_file_write_info(int type, const char **structname, int *structnum) +void CustomData_file_write_info(int type, const char **r_struct_name, int *r_struct_num) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); - *structname = typeInfo->structname; - *structnum = typeInfo->structnum; + *r_struct_name = typeInfo->structname; + *r_struct_num = typeInfo->structnum; } /** diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 73d64c23840..7842d561557 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -32,6 +32,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_kdtree.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -65,6 +66,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_mesh_mapping.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_scene.h" @@ -78,13 +80,11 @@ #include "RE_render_ext.h" #include "RE_shader_ext.h" -#ifdef _OPENMP -# include <omp.h> -#endif +#include "atomic_ops.h" /* could enable at some point but for now there are far too many conversions */ #ifdef __GNUC__ -# pragma GCC diagnostic ignored "-Wdouble-promotion" +//# pragma GCC diagnostic ignored "-Wdouble-promotion" #endif /* precalculated gaussian factors for 5x super sampling */ @@ -122,7 +122,7 @@ static int neighY[8] = {0, 1, 1, 1, 0, -1, -1, -1}; /* dissolve inline function */ -BLI_INLINE void value_dissolve(float *r_value, const float time, const float scale, const int is_log) +BLI_INLINE void value_dissolve(float *r_value, const float time, const float scale, const bool is_log) { *r_value = (is_log) ? (*r_value) * (powf(MIN_WETNESS, 1.0f / (1.2f * time / scale))) : @@ -137,19 +137,20 @@ typedef struct Bounds2D { } Bounds2D; typedef struct Bounds3D { - int valid; float min[3], max[3]; + bool valid; } Bounds3D; typedef struct VolumeGrid { int dim[3]; - Bounds3D grid_bounds; /* whole grid bounds */ + Bounds3D grid_bounds; /* whole grid bounds */ + + Bounds3D *bounds; /* (x*y*z) precalculated grid cell bounds */ + int *s_pos; /* (x*y*z) t_index begin id */ + int *s_num; /* (x*y*z) number of t_index points */ + int *t_index; /* actual surface point index, access: (s_pos + s_num) */ - Bounds3D *bounds; /* (x*y*z) precalculated grid cell bounds */ - int *s_pos; /* (x*y*z) t_index begin id */ - int *s_num; /* (x*y*z) number of t_index points */ - int *t_index; /* actual surface point index, - * access: (s_pos+s_num) */ + int *temp_t_index; } VolumeGrid; typedef struct Vec3f { @@ -164,21 +165,21 @@ typedef struct BakeAdjPoint { /* Surface data used while processing a frame */ typedef struct PaintBakeNormal { float invNorm[3]; /* current pixel world-space inverted normal */ - float normal_scale; /* normal directional scale for displace mapping */ + float normal_scale; /* normal directional scale for displace mapping */ } PaintBakeNormal; /* Temp surface data used to process a frame */ typedef struct PaintBakeData { /* point space data */ PaintBakeNormal *bNormal; - int *s_pos; /* index to start reading point sample realCoord */ - int *s_num; /* num of realCoord samples */ - Vec3f *realCoord; /* current pixel center world-space coordinates for each sample - * ordered as (s_pos+s_num)*/ + int *s_pos; /* index to start reading point sample realCoord */ + int *s_num; /* num of realCoord samples */ + Vec3f *realCoord; /* current pixel center world-space coordinates for each sample ordered as (s_pos + s_num) */ Bounds3D mesh_bounds; + float dim[3]; /* adjacency info */ - BakeAdjPoint *bNeighs; /* current global neighbor distances and directions, if required */ + BakeAdjPoint *bNeighs; /* current global neighbor distances and directions, if required */ double average_dist; /* space partitioning */ VolumeGrid *grid; /* space partitioning grid to optimize brush checks */ @@ -187,11 +188,10 @@ typedef struct PaintBakeData { Vec3f *velocity; /* speed vector in global space movement per frame, if required */ Vec3f *prev_velocity; float *brush_velocity; /* special temp data for post-p velocity based brushes like smudge - * 3 float dir vec + 1 float str */ + * 3 float dir vec + 1 float str */ MVert *prev_verts; /* copy of previous frame vertices. used to observe surface movement */ float prev_obmat[4][4]; /* previous frame object matrix */ int clear; /* flag to check if surface was cleared/reset -> have to redo velocity etc. */ - } PaintBakeData; /* UV Image sequence format point */ @@ -200,8 +200,7 @@ typedef struct PaintUVPoint { unsigned int tri_index, pixel_index; /* tri index on domain derived mesh */ unsigned int v1, v2, v3; /* vertex indexes */ - unsigned int neighbour_pixel; /* If this pixel isn't uv mapped to any face, - * but it's neighboring pixel is */ + unsigned int neighbour_pixel; /* If this pixel isn't uv mapped to any face, but it's neighboring pixel is */ } PaintUVPoint; typedef struct ImgSeqFormatData { @@ -213,8 +212,7 @@ typedef struct ImgSeqFormatData { #define ADJ_ON_MESH_EDGE (1 << 0) typedef struct PaintAdjData { - int *n_target; /* array of neighboring point indexes, - * for single sample use (n_index + neigh_num) */ + int *n_target; /* array of neighboring point indexes, for single sample use (n_index + neigh_num) */ int *n_index; /* index to start reading n_target for each point */ int *n_num; /* num of neighs for each point */ int *flags; /* vertex adjacency flags */ @@ -238,11 +236,10 @@ static int dynamicPaint_surfaceNumOfPoints(DynamicPaintSurface *surface) return 0; /* not supported atm */ } else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - if (!surface->canvas->dm) return 0; /* invalid derived mesh */ - return surface->canvas->dm->getNumVerts(surface->canvas->dm); + return (surface->canvas->dm) ? surface->canvas->dm->getNumVerts(surface->canvas->dm) : 0; } - else - return 0; + + return 0; } /* checks whether surface's format/type has realtime preview */ @@ -252,32 +249,16 @@ bool dynamicPaint_surfaceHasColorPreview(DynamicPaintSurface *surface) return false; } else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) - { - return false; - } - else { - return true; - } - } - else { - return true; + return !ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE); } + + return true; } /* get currently active surface (in user interface) */ DynamicPaintSurface *get_activeSurface(DynamicPaintCanvasSettings *canvas) { - DynamicPaintSurface *surface = canvas->surfaces.first; - int i; - - for (i = 0; surface; surface = surface->next) { - if (i == canvas->active_sur) - return surface; - i++; - } - return NULL; + return BLI_findlink(&canvas->surfaces, canvas->active_sur); } /* set preview to first previewable surface */ @@ -291,8 +272,9 @@ void dynamicPaint_resetPreview(DynamicPaintCanvasSettings *canvas) surface->flags |= MOD_DPAINT_PREVIEW; done = true; } - else + else { surface->flags &= ~MOD_DPAINT_PREVIEW; + } } } @@ -324,8 +306,9 @@ bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface, Object Mesh *me = ob->data; return (CustomData_get_named_layer_index(&me->ldata, CD_MLOOPCOL, name) != -1); } - else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) - return (defgroup_name_index(ob, surface->output_name) != -1); + else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { + return (defgroup_name_index(ob, name) != -1); + } } return false; @@ -333,15 +316,16 @@ bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface, Object static bool surface_duplicateOutputExists(void *arg, const char *name) { - DynamicPaintSurface *t_surface = (DynamicPaintSurface *)arg; + DynamicPaintSurface *t_surface = arg; DynamicPaintSurface *surface = t_surface->canvas->surfaces.first; for (; surface; surface = surface->next) { - if (surface != t_surface && surface->type == t_surface->type && - surface->format == t_surface->format) - { - if (surface->output_name[0] != '\0' && !BLI_path_cmp(name, surface->output_name)) return true; - if (surface->output_name2[0] != '\0' && !BLI_path_cmp(name, surface->output_name2)) return true; + if (surface != t_surface && surface->type == t_surface->type && surface->format == t_surface->format) { + if ((surface->output_name[0] != '\0' && !BLI_path_cmp(name, surface->output_name)) || + (surface->output_name2[0] != '\0' && !BLI_path_cmp(name, surface->output_name2))) + { + return true; + } } } return false; @@ -351,20 +335,25 @@ static void surface_setUniqueOutputName(DynamicPaintSurface *surface, char *base { char name[64]; BLI_strncpy(name, basename, sizeof(name)); /* in case basename is surface->name use a copy */ - if (!output) - BLI_uniquename_cb(surface_duplicateOutputExists, surface, name, '.', surface->output_name, sizeof(surface->output_name)); - if (output) - BLI_uniquename_cb(surface_duplicateOutputExists, surface, name, '.', surface->output_name2, sizeof(surface->output_name2)); + if (output == 0) { + BLI_uniquename_cb(surface_duplicateOutputExists, surface, name, '.', + surface->output_name, sizeof(surface->output_name)); + } + else if (output == 1) { + BLI_uniquename_cb(surface_duplicateOutputExists, surface, name, '.', + surface->output_name2, sizeof(surface->output_name2)); + } } static bool surface_duplicateNameExists(void *arg, const char *name) { - DynamicPaintSurface *t_surface = (DynamicPaintSurface *)arg; + DynamicPaintSurface *t_surface = arg; DynamicPaintSurface *surface = t_surface->canvas->surfaces.first; for (; surface; surface = surface->next) { - if (surface != t_surface && STREQ(name, surface->name)) return true; + if (surface != t_surface && STREQ(name, surface->name)) + return true; } return false; } @@ -419,9 +408,7 @@ void dynamicPaintSurface_updateType(struct DynamicPaintSurface *surface) static int surface_totalSamples(DynamicPaintSurface *surface) { - if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ && - surface->flags & MOD_DPAINT_ANTIALIAS) - { + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ && surface->flags & MOD_DPAINT_ANTIALIAS) { return (surface->data->total_points * 5); } if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && @@ -433,8 +420,10 @@ static int surface_totalSamples(DynamicPaintSurface *surface) return surface->data->total_points; } -static void blendColors(const float t_color[3], float t_alpha, const float s_color[3], float s_alpha, float result[4]) +static void blendColors( + const float t_color[3], const float t_alpha, const float s_color[3], const float s_alpha, float result[4]) { + /* Same thing as BLI's blend_color_mix_float(), but for non-premultiplied alpha. */ int i; float i_alpha = 1.0f - s_alpha; float f_alpha = t_alpha * i_alpha + s_alpha; @@ -507,15 +496,11 @@ static int surface_getBrushFlags(DynamicPaintSurface *surface, const Scene *scen /* select object */ if (surface->brush_group) { - if (go->ob) brushObj = go->ob; + if (go->ob) + brushObj = go->ob; } - else + else { brushObj = base->object; - - if (!brushObj) { - if (surface->brush_group) go = go->next; - else base = base->next; - continue; } if (surface->brush_group) @@ -523,6 +508,10 @@ static int surface_getBrushFlags(DynamicPaintSurface *surface, const Scene *scen else base = base->next; + if (!brushObj) { + continue; + } + md = modifiers_findByType(brushObj, eModifierType_DynamicPaint); if (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) { DynamicPaintModifierData *pmd2 = (DynamicPaintModifierData *)md; @@ -539,56 +528,58 @@ static int surface_getBrushFlags(DynamicPaintSurface *surface, const Scene *scen return flags; } -static int brush_usesMaterial(DynamicPaintBrushSettings *brush, Scene *scene) +static int brush_usesMaterial(const DynamicPaintBrushSettings *brush, const Scene *scene) { return ((brush->flags & MOD_DPAINT_USE_MATERIAL) && (!BKE_scene_use_new_shading_nodes(scene))); } /* check whether two bounds intersect */ -static int boundsIntersect(Bounds3D *b1, Bounds3D *b2) +static bool boundsIntersect(Bounds3D *b1, Bounds3D *b2) { - int i = 2; - if (!b1->valid || !b2->valid) return 0; - for (; i >= 0; i -= 1) - if (!(b1->min[i] <= b2->max[i] && b1->max[i] >= b2->min[i])) return 0; - return 1; + if (!b1->valid || !b2->valid) + return false; + for (int i = 2; i--;) { + if (!(b1->min[i] <= b2->max[i] && b1->max[i] >= b2->min[i])) + return false; + } + return true; } /* check whether two bounds intersect inside defined proximity */ -static int boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, float dist) +static bool boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, const float dist) { - int i = 2; - if (!b1->valid || !b2->valid) return 0; - for (; i >= 0; i -= 1) - if (!(b1->min[i] <= (b2->max[i] + dist) && b1->max[i] >= (b2->min[i] - dist))) return 0; - return 1; + if (!b1->valid || !b2->valid) + return false; + for (int i = 2; i--;) { + if (!(b1->min[i] <= (b2->max[i] + dist) && b1->max[i] >= (b2->min[i] - dist))) + return false; + } + return true; } /* check whether bounds intersects a point with given radius */ -static int UNUSED_FUNCTION(boundIntersectPoint)(Bounds3D *b, float point[3], float radius) +static bool UNUSED_FUNCTION(boundIntersectPoint)(Bounds3D *b, float point[3], const float radius) { - int i = 2; - if (!b->valid) return 0; - for (; i >= 0; i -= 1) - if (!(b->min[i] <= (point[i] + radius) && b->max[i] >= (point[i] - radius))) return 0; - return 1; + if (!b->valid) + return false; + for (int i = 2; i--;) { + if (!(b->min[i] <= (point[i] + radius) && b->max[i] >= (point[i] - radius))) + return false; + } + return true; } /* expand bounds by a new point */ static void boundInsert(Bounds3D *b, float point[3]) { - int i = 2; if (!b->valid) { copy_v3_v3(b->min, point); copy_v3_v3(b->max, point); - b->valid = 1; - } - else { - for (; i >= 0; i -= 1) { - if (point[i] < b->min[i]) b->min[i] = point[i]; - if (point[i] > b->max[i]) b->max[i] = point[i]; - } + b->valid = true; + return; } + + minmax_v3v3_v3(b->min, b->max, point); } static float getSurfaceDimension(PaintSurfaceData *sData) @@ -611,65 +602,123 @@ static void freeGrid(PaintSurfaceData *data) bData->grid = NULL; } +static void grid_bound_insert_cb_ex(void *userdata, void *userdata_chunk, const int i, const int UNUSED(thread_id)) +{ + PaintBakeData *bData = userdata; + + Bounds3D *grid_bound = userdata_chunk; + + boundInsert(grid_bound, bData->realCoord[bData->s_pos[i]].v); +} + +static void grid_bound_insert_finalize(void *userdata, void *userdata_chunk) +{ + PaintBakeData *bData = userdata; + VolumeGrid *grid = bData->grid; + + Bounds3D *grid_bound = userdata_chunk; + + boundInsert(&grid->grid_bounds, grid_bound->min); + boundInsert(&grid->grid_bounds, grid_bound->max); +} + +static void grid_cell_points_cb_ex(void *userdata, void *userdata_chunk, const int i, const int UNUSED(thread_id)) +{ + PaintBakeData *bData = userdata; + VolumeGrid *grid = bData->grid; + int *temp_t_index = grid->temp_t_index; + int *s_num = userdata_chunk; + + int co[3]; + + for (int j = 3; j--;) { + co[j] = (int)floorf((bData->realCoord[bData->s_pos[i]].v[j] - grid->grid_bounds.min[j]) / + bData->dim[j] * grid->dim[j]); + CLAMP(co[j], 0, grid->dim[j] - 1); + } + + temp_t_index[i] = co[0] + co[1] * grid->dim[0] + co[2] * grid->dim[0] * grid->dim[1]; + s_num[temp_t_index[i]]++; +} + +static void grid_cell_points_finalize(void *userdata, void *userdata_chunk) +{ + PaintBakeData *bData = userdata; + VolumeGrid *grid = bData->grid; + const int grid_cells = grid->dim[0] * grid->dim[1] * grid->dim[2]; + + int *s_num = userdata_chunk; + + /* calculate grid indexes */ + for (int i = 0; i < grid_cells; i++) { + grid->s_num[i] += s_num[i]; + } +} + +static void grid_cell_bounds_cb(void *userdata, const int x) +{ + PaintBakeData *bData = userdata; + VolumeGrid *grid = bData->grid; + float *dim = bData->dim; + int *grid_dim = grid->dim; + + for (int y = 0; y < grid_dim[1]; y++) { + for (int z = 0; z < grid_dim[2]; z++) { + const int b_index = x + y * grid_dim[0] + z * grid_dim[0] * grid_dim[1]; + /* set bounds */ + for (int j = 3; j--;) { + const int s = (j == 0) ? x : ((j == 1) ? y : z); + grid->bounds[b_index].min[j] = grid->grid_bounds.min[j] + dim[j] / grid_dim[j] * s; + grid->bounds[b_index].max[j] = grid->grid_bounds.min[j] + dim[j] / grid_dim[j] * (s + 1); + } + grid->bounds[b_index].valid = true; + } + } +} + static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) { PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; - Bounds3D *grid_bounds; VolumeGrid *grid; int grid_cells, axis = 3; int *temp_t_index = NULL; int *temp_s_num = NULL; -#ifdef _OPENMP - int num_of_threads = omp_get_max_threads(); -#else - int num_of_threads = 1; -#endif - if (bData->grid) freeGrid(sData); - /* allocate separate bounds for each thread */ - grid_bounds = MEM_callocN(sizeof(Bounds3D) * num_of_threads, "Grid Bounds"); bData->grid = MEM_callocN(sizeof(VolumeGrid), "Surface Grid"); grid = bData->grid; - if (grid && grid_bounds) { + { int i, error = 0; float dim_factor, volume, dim[3]; float td[3]; float min_dim; /* calculate canvas dimensions */ -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { -#ifdef _OPENMP - int id = omp_get_thread_num(); - boundInsert(&grid_bounds[id], (bData->realCoord[bData->s_pos[i]].v)); -#else - boundInsert(&grid_bounds[0], (bData->realCoord[bData->s_pos[i]].v)); -#endif - } - - /* get final dimensions */ - for (i = 0; i < num_of_threads; i++) { - boundInsert(&grid->grid_bounds, grid_bounds[i].min); - boundInsert(&grid->grid_bounds, grid_bounds[i].max); - } + /* Important to init correctly our ref grid_bound... */ + boundInsert(&grid->grid_bounds, bData->realCoord[bData->s_pos[0]].v); + BLI_task_parallel_range_finalize( + 0, sData->total_points, bData, &grid->grid_bounds, sizeof(grid->grid_bounds), + grid_bound_insert_cb_ex, grid_bound_insert_finalize, sData->total_points > 1000, false); /* get dimensions */ sub_v3_v3v3(dim, grid->grid_bounds.max, grid->grid_bounds.min); copy_v3_v3(td, dim); + copy_v3_v3(bData->dim, dim); min_dim = max_fff(td[0], td[1], td[2]) / 1000.f; /* deactivate zero axises */ for (i = 0; i < 3; i++) { - if (td[i] < min_dim) { td[i] = 1.0f; axis -= 1; } + if (td[i] < min_dim) { + td[i] = 1.0f; + axis--; + } } if (axis == 0 || max_fff(td[0], td[1], td[2]) < 0.0001f) { - MEM_freeN(grid_bounds); MEM_freeN(bData->grid); bData->grid = NULL; return; @@ -691,10 +740,11 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) /* allocate memory for grids */ grid->bounds = MEM_callocN(sizeof(Bounds3D) * grid_cells, "Surface Grid Bounds"); grid->s_pos = MEM_callocN(sizeof(int) * grid_cells, "Surface Grid Position"); - grid->s_num = MEM_callocN(sizeof(int) * grid_cells * num_of_threads, "Surface Grid Points"); + + grid->s_num = MEM_callocN(sizeof(int) * grid_cells, "Surface Grid Points"); temp_s_num = MEM_callocN(sizeof(int) * grid_cells, "Temp Surface Grid Points"); grid->t_index = MEM_callocN(sizeof(int) * sData->total_points, "Surface Grid Target Ids"); - temp_t_index = MEM_callocN(sizeof(int) * sData->total_points, "Temp Surface Grid Target Ids"); + grid->temp_t_index = temp_t_index = MEM_callocN(sizeof(int) * sData->total_points, "Temp Surface Grid Target Ids"); /* in case of an allocation failure abort here */ if (!grid->bounds || !grid->s_pos || !grid->s_num || !grid->t_index || !temp_s_num || !temp_t_index) @@ -702,33 +752,12 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) if (!error) { /* calculate number of points withing each cell */ -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - int co[3], j; - for (j = 0; j < 3; j++) { - co[j] = (int)floor((bData->realCoord[bData->s_pos[i]].v[j] - grid->grid_bounds.min[j]) / dim[j] * grid->dim[j]); - CLAMP(co[j], 0, grid->dim[j] - 1); - } + BLI_task_parallel_range_finalize( + 0, sData->total_points, bData, grid->s_num, sizeof(*grid->s_num) * grid_cells, + grid_cell_points_cb_ex, grid_cell_points_finalize, sData->total_points > 1000, false); - temp_t_index[i] = co[0] + co[1] * grid->dim[0] + co[2] * grid->dim[0] * grid->dim[1]; -#ifdef _OPENMP - grid->s_num[temp_t_index[i] + omp_get_thread_num() * grid_cells]++; -#else - grid->s_num[temp_t_index[i]]++; -#endif - } - - /* for first cell only calc s_num */ - for (i = 1; i < num_of_threads; i++) { - grid->s_num[0] += grid->s_num[i * grid_cells]; - } - - /* calculate grid indexes */ + /* calculate grid indexes (not needed for first cell, which is zero). */ for (i = 1; i < grid_cells; i++) { - int id; - for (id = 1; id < num_of_threads; id++) { - grid->s_num[i] += grid->s_num[i + id * grid_cells]; - } grid->s_pos[i] = grid->s_pos[i - 1] + grid->s_num[i - 1]; } @@ -741,41 +770,20 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) } /* calculate cell bounds */ - { - int x; -#pragma omp parallel for schedule(static) - for (x = 0; x < grid->dim[0]; x++) { - int y; - for (y = 0; y < grid->dim[1]; y++) { - int z; - for (z = 0; z < grid->dim[2]; z++) { - int j, b_index = x + y * grid->dim[0] + z * grid->dim[0] * grid->dim[1]; - /* set bounds */ - for (j = 0; j < 3; j++) { - int s = (j == 0) ? x : ((j == 1) ? y : z); - grid->bounds[b_index].min[j] = grid->grid_bounds.min[j] + dim[j] / grid->dim[j] * s; - grid->bounds[b_index].max[j] = grid->grid_bounds.min[j] + dim[j] / grid->dim[j] * (s + 1); - } - grid->bounds[b_index].valid = 1; - } - } - } - } + BLI_task_parallel_range(0, grid->dim[0], bData, grid_cell_bounds_cb, grid_cells > 1000); } - if (temp_s_num) MEM_freeN(temp_s_num); - if (temp_t_index) MEM_freeN(temp_t_index); - - /* free per thread s_num values */ - grid->s_num = MEM_reallocN(grid->s_num, sizeof(int) * grid_cells); + if (temp_s_num) + MEM_freeN(temp_s_num); + if (temp_t_index) + MEM_freeN(temp_t_index); + grid->temp_t_index = NULL; if (error || !grid->s_num) { setError(surface->canvas, N_("Not enough free memory")); freeGrid(sData); } } - - if (grid_bounds) MEM_freeN(grid_bounds); } /***************************** Freeing data ******************************/ @@ -790,10 +798,8 @@ void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd) if (pmd->brush->paint_ramp) MEM_freeN(pmd->brush->paint_ramp); - pmd->brush->paint_ramp = NULL; if (pmd->brush->vel_ramp) MEM_freeN(pmd->brush->vel_ramp); - pmd->brush->vel_ramp = NULL; MEM_freeN(pmd->brush); pmd->brush = NULL; @@ -803,10 +809,14 @@ void dynamicPaint_freeBrush(struct DynamicPaintModifierData *pmd) static void dynamicPaint_freeAdjData(PaintSurfaceData *data) { if (data->adj_data) { - if (data->adj_data->n_index) MEM_freeN(data->adj_data->n_index); - if (data->adj_data->n_num) MEM_freeN(data->adj_data->n_num); - if (data->adj_data->n_target) MEM_freeN(data->adj_data->n_target); - if (data->adj_data->flags) MEM_freeN(data->adj_data->flags); + if (data->adj_data->n_index) + MEM_freeN(data->adj_data->n_index); + if (data->adj_data->n_num) + MEM_freeN(data->adj_data->n_num); + if (data->adj_data->n_target) + MEM_freeN(data->adj_data->n_target); + if (data->adj_data->flags) + MEM_freeN(data->adj_data->flags); MEM_freeN(data->adj_data); data->adj_data = NULL; } @@ -816,15 +826,24 @@ static void free_bakeData(PaintSurfaceData *data) { PaintBakeData *bData = data->bData; if (bData) { - if (bData->bNormal) MEM_freeN(bData->bNormal); - if (bData->s_pos) MEM_freeN(bData->s_pos); - if (bData->s_num) MEM_freeN(bData->s_num); - if (bData->realCoord) MEM_freeN(bData->realCoord); - if (bData->bNeighs) MEM_freeN(bData->bNeighs); - if (bData->grid) freeGrid(data); - if (bData->prev_verts) MEM_freeN(bData->prev_verts); - if (bData->velocity) MEM_freeN(bData->velocity); - if (bData->prev_velocity) MEM_freeN(bData->prev_velocity); + if (bData->bNormal) + MEM_freeN(bData->bNormal); + if (bData->s_pos) + MEM_freeN(bData->s_pos); + if (bData->s_num) + MEM_freeN(bData->s_num); + if (bData->realCoord) + MEM_freeN(bData->realCoord); + if (bData->bNeighs) + MEM_freeN(bData->bNeighs); + if (bData->grid) + freeGrid(data); + if (bData->prev_verts) + MEM_freeN(bData->prev_verts); + if (bData->velocity) + MEM_freeN(bData->velocity); + if (bData->prev_velocity) + MEM_freeN(bData->prev_velocity); MEM_freeN(data->bData); data->bData = NULL; @@ -834,11 +853,11 @@ static void free_bakeData(PaintSurfaceData *data) /* free surface data if it's not used anymore */ static void surface_freeUnusedData(DynamicPaintSurface *surface) { - if (!surface->data) return; + if (!surface->data) + return; /* free bakedata if not active or surface is baked */ - if (!(surface->flags & MOD_DPAINT_ACTIVE)) - { + if (!(surface->flags & MOD_DPAINT_ACTIVE)) { free_bakeData(surface->data); } } @@ -846,7 +865,9 @@ static void surface_freeUnusedData(DynamicPaintSurface *surface) void dynamicPaint_freeSurfaceData(DynamicPaintSurface *surface) { PaintSurfaceData *data = surface->data; - if (!data) return; + if (!data) + return; + if (data->format_data) { /* format specific free */ if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { @@ -859,7 +880,8 @@ void dynamicPaint_freeSurfaceData(DynamicPaintSurface *surface) MEM_freeN(data->format_data); } /* type data */ - if (data->type_data) MEM_freeN(data->type_data); + if (data->type_data) + MEM_freeN(data->type_data); dynamicPaint_freeAdjData(data); /* bake data */ free_bakeData(data); @@ -1022,7 +1044,7 @@ bool dynamicPaint_createType(struct DynamicPaintModifierData *pmd, int type, str brush->flags = MOD_DPAINT_ABS_ALPHA | MOD_DPAINT_RAMP_ALPHA; brush->collision = MOD_DPAINT_COL_VOLUME; - + brush->mat = NULL; brush->r = 0.15f; brush->g = 0.4f; @@ -1210,31 +1232,24 @@ static void dynamicPaint_allocateSurfaceType(DynamicPaintSurface *surface) break; } - if (sData->type_data == NULL) setError(surface->canvas, N_("Not enough free memory")); + if (sData->type_data == NULL) + setError(surface->canvas, N_("Not enough free memory")); } -static int surface_usesAdjDistance(DynamicPaintSurface *surface) +static bool surface_usesAdjDistance(DynamicPaintSurface *surface) { - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && surface->effect) return 1; - if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) return 1; - return 0; + return ((surface->type == MOD_DPAINT_SURFACE_T_PAINT && surface->effect) || + (surface->type == MOD_DPAINT_SURFACE_T_WAVE)); } -static int surface_usesAdjData(DynamicPaintSurface *surface) +static bool surface_usesAdjData(DynamicPaintSurface *surface) { - if (surface_usesAdjDistance(surface)) return 1; - if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && - surface->flags & MOD_DPAINT_ANTIALIAS) - { - return 1; - } - else { - return 0; - } + return (surface_usesAdjDistance(surface) || + (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && surface->flags & MOD_DPAINT_ANTIALIAS)); } /* initialize surface adjacency data */ -static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int force_init) +static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const bool force_init) { PaintSurfaceData *sData = surface->data; DerivedMesh *dm = surface->canvas->dm; @@ -1242,20 +1257,24 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for int *temp_data; int neigh_points = 0; - if (!surface_usesAdjData(surface) && !force_init) return; + if (!force_init && !surface_usesAdjData(surface)) + return; if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { /* For vertex format, neighbors are connected by edges */ neigh_points = 2 * dm->getNumEdges(dm); } - else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { neigh_points = sData->total_points * 8; + } - if (!neigh_points) return; + if (!neigh_points) + return; /* allocate memory */ ad = sData->adj_data = MEM_callocN(sizeof(PaintAdjData), "Surface Adj Data"); - if (!ad) return; + if (!ad) + return; ad->n_index = MEM_callocN(sizeof(int) * sData->total_points, "Surface Adj Index"); ad->n_num = MEM_callocN(sizeof(int) * sData->total_points, "Surface Adj Counts"); temp_data = MEM_callocN(sizeof(int) * sData->total_points, "Temp Adj Data"); @@ -1266,7 +1285,8 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for /* in case of allocation error, free memory */ if (!ad->n_index || !ad->n_num || !ad->n_target || !temp_data) { dynamicPaint_freeAdjData(sData); - if (temp_data) MEM_freeN(temp_data); + if (temp_data) + MEM_freeN(temp_data); setError(surface->canvas, N_("Not enough free memory")); return; } @@ -1294,8 +1314,7 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for /* also add number of vertices to temp_data * to locate points on "mesh edge" */ for (i = 0; i < numOfPolys; i++) { - int j = 0; - for (; j < mpoly[i].totloop; j++) { + for (int j = 0; j < mpoly[i].totloop; j++) { temp_data[mloop[mpoly[i].loopstart + j].v]++; } } @@ -1303,13 +1322,11 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for /* now check if total number of edges+faces for * each vertex is even, if not -> vertex is on mesh edge */ for (i = 0; i < sData->total_points; i++) { - if ((temp_data[i] % 2) || - (temp_data[i] < 4)) - { + if ((temp_data[i] % 2) || (temp_data[i] < 4)) { ad->flags[i] |= ADJ_ON_MESH_EDGE; } - /* reset temp data */ + /* reset temp data */ temp_data[i] = 0; } @@ -1343,6 +1360,117 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, int for MEM_freeN(temp_data); } +typedef struct DynamicPaintSetInitColorData { + const DynamicPaintSurface *surface; + + const MLoop *mloop; + const MLoopUV *mloopuv; + const MLoopTri *mlooptri; + const MLoopCol *mloopcol; + struct ImagePool *pool; + + const bool scene_color_manage; +} DynamicPaintSetInitColorData; + +static void dynamic_paint_set_init_color_tex_to_vcol_cb(void *userdata, const int i) +{ + const DynamicPaintSetInitColorData *data = userdata; + + const PaintSurfaceData *sData = data->surface->data; + PaintPoint *pPoint = (PaintPoint *)sData->type_data; + + const MLoop *mloop = data->mloop; + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + struct ImagePool *pool = data->pool; + Tex *tex = data->surface->init_texture; + + const bool scene_color_manage = data->scene_color_manage; + + float uv[3] = {0.0f}; + + for (int j = 3; j--;) { + TexResult texres = {0}; + const unsigned int vert = mloop[mlooptri[i].tri[j]].v; + + /* remap to [-1.0, 1.0] */ + uv[0] = mloopuv[mlooptri[i].tri[j]].uv[0] * 2.0f - 1.0f; + uv[1] = mloopuv[mlooptri[i].tri[j]].uv[1] * 2.0f - 1.0f; + + multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage, false); + + if (texres.tin > pPoint[vert].color[3]) { + copy_v3_v3(pPoint[vert].color, &texres.tr); + pPoint[vert].color[3] = texres.tin; + } + } +} + +static void dynamic_paint_set_init_color_tex_to_imseq_cb(void *userdata, const int i) +{ + const DynamicPaintSetInitColorData *data = userdata; + + const PaintSurfaceData *sData = data->surface->data; + PaintPoint *pPoint = (PaintPoint *)sData->type_data; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + Tex *tex = data->surface->init_texture; + ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; + const int samples = (data->surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + + const bool scene_color_manage = data->scene_color_manage; + + float uv[9] = {0.0f}; + float uv_final[3] = {0.0f}; + + TexResult texres = {0}; + + /* collect all uvs */ + for (int j = 3; j--;) { + copy_v2_v2(&uv[j * 3], mloopuv[mlooptri[f_data->uv_p[i].tri_index].tri[j]].uv); + } + + /* interpolate final uv pos */ + interp_v3_v3v3v3(uv_final, &uv[0], &uv[3], &uv[6], f_data->barycentricWeights[i * samples].v); + /* remap to [-1.0, 1.0] */ + uv_final[0] = uv_final[0] * 2.0f - 1.0f; + uv_final[1] = uv_final[1] * 2.0f - 1.0f; + + multitex_ext_safe(tex, uv_final, &texres, NULL, scene_color_manage, false); + + /* apply color */ + copy_v3_v3(pPoint[i].color, &texres.tr); + pPoint[i].color[3] = texres.tin; +} + +static void dynamic_paint_set_init_color_vcol_to_imseq_cb(void *userdata, const int i) +{ + const DynamicPaintSetInitColorData *data = userdata; + + const PaintSurfaceData *sData = data->surface->data; + PaintPoint *pPoint = (PaintPoint *)sData->type_data; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopCol *mloopcol = data->mloopcol; + ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; + const int samples = (data->surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + + const int tri_idx = f_data->uv_p[i].tri_index; + float colors[3][4]; + float final_color[4]; + + /* collect color values */ + for (int j = 3; j--;) { + rgba_uchar_to_float(colors[j], (const unsigned char *)&mloopcol[mlooptri[tri_idx].tri[j]].r); + } + + /* interpolate final color */ + interp_v4_v4v4v4(final_color, UNPACK3(colors), f_data->barycentricWeights[i * samples].v); + + copy_v4_v4(pPoint[i].color, final_color); +} + static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface *surface) { PaintSurfaceData *sData = surface->data; @@ -1356,10 +1484,10 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface if (surface->init_color_type == MOD_DPAINT_INITIAL_NONE) return; + /* Single color */ - else if (surface->init_color_type == MOD_DPAINT_INITIAL_COLOR) { + if (surface->init_color_type == MOD_DPAINT_INITIAL_COLOR) { /* apply color to every surface point */ -#pragma omp parallel for schedule(static) for (i = 0; i < sData->total_points; i++) { copy_v4_v4(pPoint[i].color, surface->init_color); } @@ -1375,69 +1503,36 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - if (!tex) return; + if (!tex) + return; /* get uv map */ CustomData_validate_layer_name(&dm->loopData, CD_MLOOPUV, surface->init_layername, uvname); mloopuv = CustomData_get_layer_named(&dm->loopData, CD_MLOOPUV, uvname); - if (!mloopuv) return; + if (!mloopuv) + return; /* for vertex surface loop through tfaces and find uv color * that provides highest alpha */ if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { struct ImagePool *pool = BKE_image_pool_new(); -#pragma omp parallel for schedule(static) shared(pool) - for (i = 0; i < tottri; i++) { - float uv[3] = {0.0f}; - int j; - for (j = 0; j < 3; j++) { - TexResult texres = {0}; - unsigned int vert = mloop[mlooptri[i].tri[j]].v; - - /* remap to -1.0 to 1.0 */ - uv[0] = mloopuv[mlooptri[i].tri[j]].uv[0] * 2.0f - 1.0f; - uv[1] = mloopuv[mlooptri[i].tri[j]].uv[1] * 2.0f - 1.0f; - - multitex_ext_safe(tex, uv, &texres, pool, scene_color_manage, false); - - if (texres.tin > pPoint[vert].color[3]) { - copy_v3_v3(pPoint[vert].color, &texres.tr); - pPoint[vert].color[3] = texres.tin; - } - } - } + DynamicPaintSetInitColorData data = { + .surface = surface, + .mloop = mloop, .mlooptri = mlooptri, .mloopuv = mloopuv, .pool = pool, + .scene_color_manage = scene_color_manage + }; + BLI_task_parallel_range(0, tottri, &data, dynamic_paint_set_init_color_tex_to_vcol_cb, tottri > 1000); BKE_image_pool_free(pool); } else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; - int samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; - -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - float uv[9] = {0.0f}; - float uv_final[3] = {0.0f}; - int j; - TexResult texres = {0}; - - /* collect all uvs */ - for (j = 0; j < 3; j++) { - copy_v2_v2(&uv[j * 3], mloopuv[mlooptri[f_data->uv_p[i].tri_index].tri[j]].uv); - } - - /* interpolate final uv pos */ - interp_v3_v3v3v3(uv_final, &uv[0], &uv[3], &uv[6], - f_data->barycentricWeights[i * samples].v); - /* remap to -1.0 to 1.0 */ - uv_final[0] = uv_final[0] * 2.0f - 1.0f; - uv_final[1] = uv_final[1] * 2.0f - 1.0f; - - multitex_ext_safe(tex, uv_final, &texres, NULL, scene_color_manage, false); - - /* apply color */ - copy_v3_v3(pPoint[i].color, &texres.tr); - pPoint[i].color[3] = texres.tin; - } + DynamicPaintSetInitColorData data = { + .surface = surface, + .mlooptri = mlooptri, .mloopuv = mloopuv, + .scene_color_manage = scene_color_manage + }; + BLI_task_parallel_range(0, sData->total_points, &data, dynamic_paint_set_init_color_tex_to_imseq_cb, + sData->total_points > 1000); } } /* vertex color layer */ @@ -1448,37 +1543,25 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface const MLoop *mloop = dm->getLoopArray(dm); const int totloop = dm->getNumLoops(dm); const MLoopCol *col = CustomData_get_layer_named(&dm->loopData, CD_MLOOPCOL, surface->init_layername); - if (!col) return; + if (!col) + return; -#pragma omp parallel for schedule(static) for (i = 0; i < totloop; i++) { rgba_uchar_to_float(pPoint[mloop[i].v].color, (const unsigned char *)&col[mloop[i].v].r); } } else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { const MLoopTri *mlooptri = dm->getLoopTriArray(dm); - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; - int samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; MLoopCol *col = CustomData_get_layer_named(&dm->loopData, CD_MLOOPCOL, surface->init_layername); - if (!col) return; - -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - int tri_ind = f_data->uv_p[i].tri_index; - float colors[3][4]; - float final_color[4]; - int j; - - /* collect color values */ - for (j = 0; j < 3; j++) { - rgba_uchar_to_float(colors[j], (const unsigned char *)&col[mlooptri[tri_ind].tri[j]].r); - } - - /* interpolate final color */ - interp_v4_v4v4v4(final_color, UNPACK3(colors), f_data->barycentricWeights[i * samples].v); - - copy_v4_v4(pPoint[i].color, final_color); - } + if (!col) + return; + + DynamicPaintSetInitColorData data = { + .surface = surface, + .mlooptri = mlooptri, .mloopcol = col, + }; + BLI_task_parallel_range(0, sData->total_points, &data, dynamic_paint_set_init_color_vcol_to_imseq_cb, + sData->total_points > 1000); } } } @@ -1513,20 +1596,24 @@ bool dynamicPaint_resetSurface(const Scene *scene, DynamicPaintSurface *surface) { int numOfPoints = dynamicPaint_surfaceNumOfPoints(surface); /* free existing data */ - if (surface->data) dynamicPaint_freeSurfaceData(surface); + if (surface->data) + dynamicPaint_freeSurfaceData(surface); /* don't reallocate for image sequence types. they get handled only on bake */ - if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) return true; - if (numOfPoints < 1) return false; + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + return true; + if (numOfPoints < 1) + return false; /* allocate memory */ surface->data = MEM_callocN(sizeof(PaintSurfaceData), "PaintSurfaceData"); - if (!surface->data) return false; + if (!surface->data) + return false; /* allocate data depending on surface type and format */ surface->data->total_points = numOfPoints; dynamicPaint_allocateSurfaceType(surface); - dynamicPaint_initAdjacencyData(surface, 0); + dynamicPaint_initAdjacencyData(surface, false); /* set initial color */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) @@ -1547,39 +1634,153 @@ static bool dynamicPaint_checkSurfaceData(const Scene *scene, DynamicPaintSurfac /***************************** Modifier processing ******************************/ +typedef struct DynamicPaintModifierApplyData { + const DynamicPaintSurface *surface; + Object *ob; + + MVert *mvert; + const MLoop *mloop; + const MPoly *mpoly; + + float (*fcolor)[4]; + MLoopCol *mloopcol; + MLoopCol *mloopcol_wet; + MLoopCol *mloopcol_preview; +} DynamicPaintModifierApplyData; + +static void dynamic_paint_apply_surface_displace_cb(void *userdata, const int i) +{ + const DynamicPaintModifierApplyData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + MVert *mvert = data->mvert; + + float normal[3]; + const float *value = (float *)surface->data->type_data; + const float val = value[i] * surface->disp_factor; + + normal_short_to_float_v3(normal, mvert[i].no); + + /* same as 'mvert[i].co[0] -= normal[0] * val' etc. */ + madd_v3_v3fl(mvert[i].co, normal, -val); +} /* apply displacing vertex surface to the derived mesh */ static void dynamicPaint_applySurfaceDisplace(DynamicPaintSurface *surface, DerivedMesh *result) { PaintSurfaceData *sData = surface->data; - if (!sData || surface->format != MOD_DPAINT_SURFACE_F_VERTEX) return; + if (!sData || surface->format != MOD_DPAINT_SURFACE_F_VERTEX) + return; /* displace paint */ if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { MVert *mvert = result->getVertArray(result); - int i; - const float *value = (float *)sData->type_data; -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - float normal[3], val = value[i] * surface->disp_factor; - normal_short_to_float_v3(normal, mvert[i].no); - normalize_v3(normal); + DynamicPaintModifierApplyData data = {.surface = surface, .mvert = mvert}; + BLI_task_parallel_range(0, sData->total_points, &data, dynamic_paint_apply_surface_displace_cb, + sData->total_points > 10000); + } +} + +static void dynamic_paint_apply_surface_vpaint_blend_cb(void *userdata, const int i) +{ + const DynamicPaintModifierApplyData *data = userdata; + + PaintPoint *pPoint = (PaintPoint *)data->surface->data->type_data; + float (*fcolor)[4] = data->fcolor; + + /* blend dry and wet layer */ + blendColors(pPoint[i].color, pPoint[i].color[3], pPoint[i].e_color, pPoint[i].e_color[3], fcolor[i]); +} + +static void dynamic_paint_apply_surface_vpaint_cb(void *userdata, const int p_index) +{ + const DynamicPaintModifierApplyData *data = userdata; + Object *ob = data->ob; + + const MLoop *mloop = data->mloop; + const MPoly *mpoly = data->mpoly; - mvert[i].co[0] -= normal[0] * val; - mvert[i].co[1] -= normal[1] * val; - mvert[i].co[2] -= normal[2] * val; + const DynamicPaintSurface *surface = data->surface; + PaintPoint *pPoint = (PaintPoint *)surface->data->type_data; + float (*fcolor)[4] = data->fcolor; + + MLoopCol *mloopcol = data->mloopcol; + MLoopCol *mloopcol_wet = data->mloopcol_wet; + MLoopCol *mloopcol_preview = data->mloopcol_preview; + + const Material *material = mloopcol_preview ? + give_current_material(ob, mpoly[p_index].mat_nr + 1) : NULL; + + for (int j = 0; j < mpoly[p_index].totloop; j++) { + const int l_index = mpoly[p_index].loopstart + j; + const int v_index = mloop[l_index].v; + + /* save layer data to output layer */ + /* apply color */ + if (mloopcol) { + rgba_float_to_uchar((unsigned char *)&mloopcol[l_index].r, fcolor[v_index]); + } + /* apply wetness */ + if (mloopcol_wet) { + const char c = FTOCHAR(pPoint[v_index].wetness); + mloopcol_wet[l_index].r = c; + mloopcol_wet[l_index].g = c; + mloopcol_wet[l_index].b = c; + mloopcol_wet[l_index].a = 255; + } + + /* viewport preview */ + if (mloopcol_preview) { + if (surface->preview_id == MOD_DPAINT_SURFACE_PREV_PAINT) { + float c[3]; + + /* Apply material color as base vertex color for preview */ + mloopcol_preview[l_index].a = 255; + if (material) { + c[0] = material->r; + c[1] = material->g; + c[2] = material->b; + } + else { /* default gray */ + c[0] = 0.65f; + c[1] = 0.65f; + c[2] = 0.65f; + } + /* mix surface color */ + interp_v3_v3v3(c, c, fcolor[v_index], fcolor[v_index][3]); + + rgb_float_to_uchar((unsigned char *)&mloopcol_preview[l_index].r, c); + } + else { + const char c = FTOCHAR(pPoint[v_index].wetness); + mloopcol_preview[l_index].r = c; + mloopcol_preview[l_index].g = c; + mloopcol_preview[l_index].b = c; + mloopcol_preview[l_index].a = 255; + } } } } +static void dynamic_paint_apply_surface_wave_cb(void *userdata, const int i) +{ + const DynamicPaintModifierApplyData *data = userdata; + + PaintWavePoint *wPoint = (PaintWavePoint *)data->surface->data->type_data; + MVert *mvert = data->mvert; + float normal[3]; + + normal_short_to_float_v3(normal, mvert[i].no); + madd_v3_v3fl(mvert[i].co, normal, wPoint[i].height); +} + /* * Apply canvas data to the object derived mesh */ -static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, - Object *ob, - DerivedMesh *dm) +static DerivedMesh *dynamicPaint_Modifier_apply( + DynamicPaintModifierData *pmd, Object *ob, DerivedMesh *dm) { DerivedMesh *result = CDDM_copy(dm); @@ -1593,129 +1794,65 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, PaintSurfaceData *sData = surface->data; if (surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ && sData) { - if (!(surface->flags & (MOD_DPAINT_ACTIVE))) continue; + if (!(surface->flags & MOD_DPAINT_ACTIVE)) + continue; /* process vertex surface previews */ if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { /* vertex color paint */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - - int i; - PaintPoint *pPoint = (PaintPoint *)sData->type_data; - MLoopCol *col = NULL; MLoop *mloop = CDDM_get_loops(result); - int totloop = result->numLoopData; + const int totloop = result->numLoopData; + MPoly *mpoly = CDDM_get_polys(result); + const int totpoly = result->numPolyData; /* paint is stored on dry and wet layers, so mix final color first */ - float *fcolor = MEM_callocN(sizeof(float) * sData->total_points * 4, "Temp paint color"); - -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - /* blend dry and wet layer */ - blendColors(pPoint[i].color, pPoint[i].color[3], pPoint[i].e_color, pPoint[i].e_color[3], &fcolor[i * 4]); - } - - /* viewport preview */ - if (surface->flags & MOD_DPAINT_PREVIEW) { - MPoly *mp = CDDM_get_polys(result); - int totpoly = result->numPolyData; - -#if 0 - /* XXX We have to create a CD_PREVIEW_MCOL, else it might sigsev - * (after a SubSurf mod, eg)... */ - if (!result->getTessFaceDataArray(result, CD_PREVIEW_MCOL)) { - int numFaces = result->getNumTessFaces(result); - CustomData_add_layer(&result->faceData, CD_PREVIEW_MCOL, CD_CALLOC, NULL, numFaces); - } -#endif - - /* Save preview results to weight layer to be - * able to share same drawing methods */ - col = CustomData_get_layer(&result->loopData, CD_PREVIEW_MLOOPCOL); - if (!col) - col = CustomData_add_layer(&result->loopData, CD_PREVIEW_MLOOPCOL, CD_CALLOC, - NULL, totloop); - - if (col) { -#pragma omp parallel for schedule(static) - for (i = 0; i < totpoly; i++) { - int j = 0; - Material *material = give_current_material(ob, mp[i].mat_nr + 1); - - for (; j < mp[i].totloop; j++) { - int l_index = mp[i].loopstart + j; - int v_index = mloop[l_index].v; - - if (surface->preview_id == MOD_DPAINT_SURFACE_PREV_PAINT) { - float c[3]; - v_index *= 4; - - /* Apply material color as base vertex color for preview */ - col[l_index].a = 255; - if (material) { - c[0] = material->r; - c[1] = material->g; - c[2] = material->b; - } - else { /* default gray */ - c[0] = 0.65f; - c[1] = 0.65f; - c[2] = 0.65f; - } - /* mix surface color */ - interp_v3_v3v3(c, c, &fcolor[v_index], fcolor[v_index + 3]); - - rgb_float_to_uchar((unsigned char *)&col[l_index].r, c); - } - else { - col[l_index].r = - col[l_index].g = - col[l_index].b = FTOCHAR(pPoint[v_index].wetness); - col[l_index].a = 255; - } - } - } - } - } + float (*fcolor)[4] = MEM_callocN(sizeof(*fcolor) * sData->total_points, "Temp paint color"); - - /* save layer data to output layer */ + DynamicPaintModifierApplyData data = {.surface = surface, .fcolor = fcolor}; + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_apply_surface_vpaint_blend_cb, + sData->total_points > 1000); /* paint layer */ - col = CustomData_get_layer_named(&result->loopData, CD_MLOOPCOL, surface->output_name); + MLoopCol *mloopcol = CustomData_get_layer_named(&result->loopData, CD_MLOOPCOL, surface->output_name); /* if output layer is lost from a constructive modifier, re-add it */ - if (!col && dynamicPaint_outputLayerExists(surface, ob, 0)) - col = CustomData_add_layer_named(&result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name); - /* apply color */ - if (col) { -#pragma omp parallel for schedule(static) - for (i = 0; i < totloop; i++) { - int index = mloop[i].v * 4; - rgb_float_to_uchar((unsigned char *)&col[i].r, &fcolor[index]); - col[i].a = FTOCHAR(fcolor[index + 3]); /* IS THIS NEEDED? */ - } + if (!mloopcol && dynamicPaint_outputLayerExists(surface, ob, 0)) { + mloopcol = CustomData_add_layer_named( + &result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name); } - - MEM_freeN(fcolor); /* wet layer */ - col = CustomData_get_layer_named(&result->loopData, CD_MLOOPCOL, surface->output_name2); + MLoopCol *mloopcol_wet = CustomData_get_layer_named(&result->loopData, CD_MLOOPCOL, surface->output_name2); /* if output layer is lost from a constructive modifier, re-add it */ - if (!col && dynamicPaint_outputLayerExists(surface, ob, 1)) - col = CustomData_add_layer_named(&result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name2); - /* apply color */ - if (col) { -#pragma omp parallel for schedule(static) - for (i = 0; i < totloop; i++) { - int index = mloop[i].v; - col[i].r = - col[i].g = - col[i].b = FTOCHAR(pPoint[index].wetness); - col[i].a = 255; + if (!mloopcol_wet && dynamicPaint_outputLayerExists(surface, ob, 1)) { + mloopcol_wet = CustomData_add_layer_named( + &result->loopData, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name2); + } + + /* Save preview results to weight layer to be able to share same drawing methods */ + MLoopCol *mloopcol_preview = NULL; + if (surface->flags & MOD_DPAINT_PREVIEW) { + mloopcol_preview = CustomData_get_layer(&result->loopData, CD_PREVIEW_MLOOPCOL); + if (!mloopcol_preview) { + mloopcol_preview = CustomData_add_layer( + &result->loopData, CD_PREVIEW_MLOOPCOL, CD_CALLOC, NULL, totloop); } } + data.ob = ob; + data.mloop = mloop; + data.mpoly = mpoly; + data.mloopcol = mloopcol; + data.mloopcol_wet = mloopcol_wet; + data.mloopcol_preview = mloopcol_preview; + + BLI_task_parallel_range(0, totpoly, &data, dynamic_paint_apply_surface_vpaint_cb, + totpoly > 1000); + + MEM_freeN(fcolor); + /* Mark tessellated CD layers as dirty. */ result->dirty |= DM_DIRTY_TESS_CDLAYERS; } @@ -1734,9 +1871,10 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, } /* apply weights into a vertex group, if doesnt exists add a new layer */ - if (defgrp_index != -1 && !dvert && (surface->output_name[0] != '\0')) + if (defgrp_index != -1 && !dvert && (surface->output_name[0] != '\0')) { dvert = CustomData_add_layer_named(&result->vertData, CD_MDEFORMVERT, CD_CALLOC, NULL, sData->total_points, surface->output_name); + } if (defgrp_index != -1 && dvert) { int i; for (i = 0; i < sData->total_points; i++) { @@ -1745,7 +1883,6 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, /* skip if weight value is 0 and no existing weight is found */ if ((def_weight != NULL) || (weight[i] != 0.0f)) { - /* if not found, add a weight for it */ if (def_weight == NULL) { def_weight = defvert_verify_index(dv, defgrp_index); @@ -1760,15 +1897,10 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, /* wave simulation */ else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { MVert *mvert = result->getVertArray(result); - int i; - PaintWavePoint *wPoint = (PaintWavePoint *)sData->type_data; - -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - float normal[3]; - normal_short_to_float_v3(normal, mvert[i].no); - madd_v3_v3fl(mvert[i].co, normal, wPoint[i].height); - } + + DynamicPaintModifierApplyData data = {.surface = surface, .mvert = mvert}; + BLI_task_parallel_range(0, sData->total_points, &data, dynamic_paint_apply_surface_wave_cb, + sData->total_points > 1000); update_normals = true; } @@ -1787,7 +1919,8 @@ static DerivedMesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, } /* make a copy of dm to use as brush data */ if (pmd->brush) { - if (pmd->brush->dm) pmd->brush->dm->release(pmd->brush->dm); + if (pmd->brush->dm) + pmd->brush->dm->release(pmd->brush->dm); pmd->brush->dm = CDDM_copy(result); } @@ -1816,7 +1949,8 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene canvas_copyDerivedMesh(canvas, dm); /* in case image sequence baking, stop here */ - if (canvas->flags & MOD_DPAINT_BAKING) return; + if (canvas->flags & MOD_DPAINT_BAKING) + return; /* loop through surfaces */ for (; surface; surface = surface->next) { @@ -1827,17 +1961,20 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene surface_freeUnusedData(surface); /* image sequences are handled by bake operator */ - if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) continue; - if (!(surface->flags & MOD_DPAINT_ACTIVE)) continue; + if ((surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) || !(surface->flags & MOD_DPAINT_ACTIVE)) + continue; /* make sure surface is valid */ no_surface_data = surface->data == NULL; - if (!dynamicPaint_checkSurfaceData(scene, surface)) continue; + if (!dynamicPaint_checkSurfaceData(scene, surface)) + continue; /* limit frame range */ CLAMP(current_frame, surface->start_frame, surface->end_frame); - if (no_surface_data || current_frame != surface->current_frame || (int)scene->r.cfra == surface->start_frame) { + if (no_surface_data || current_frame != surface->current_frame || + (int)scene->r.cfra == surface->start_frame) + { surface->current_frame = current_frame; /* if we're on surface range do recalculate */ @@ -1862,263 +1999,462 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene /* Modifier call. Processes dynamic paint modifier step. */ DerivedMesh *dynamicPaint_Modifier_do(DynamicPaintModifierData *pmd, Scene *scene, Object *ob, DerivedMesh *dm) { - /* For now generate looptris in every case */ - DM_ensure_looptri(dm); + if (pmd->canvas) { + DerivedMesh *ret; + + /* For now generate looptris in every case */ + DM_ensure_looptri(dm); - /* Update canvas data for a new frame */ - dynamicPaint_frameUpdate(pmd, scene, ob, dm); + /* Update canvas data for a new frame */ + dynamicPaint_frameUpdate(pmd, scene, ob, dm); + + /* Return output mesh */ + ret = dynamicPaint_Modifier_apply(pmd, ob, dm); + + return ret; + } + else { + /* For now generate looptris in every case */ + DM_ensure_looptri(dm); - /* Return output mesh */ - return dynamicPaint_Modifier_apply(pmd, ob, dm); + /* Update canvas data for a new frame */ + dynamicPaint_frameUpdate(pmd, scene, ob, dm); + + /* Return output mesh */ + return dynamicPaint_Modifier_apply(pmd, ob, dm); + } } /***************************** Image Sequence / UV Image Surface Calls ******************************/ /* - * Tries to find the neighboring pixel in given (uv space) direction. - * Result is used by effect system to move paint on the surface. + * Create a surface for uv image sequence format + */ +#define JITTER_SAMPLES { \ + 0.0f, 0.0f, \ + -0.2f, -0.4f, \ + 0.2f, 0.4f, \ + 0.4f, -0.2f, \ + -0.4f, 0.3f, \ +} + +typedef struct DynamicPaintCreateUVSurfaceData { + const DynamicPaintSurface *surface; + + PaintUVPoint *tempPoints; + Vec3f *tempWeights; + + const MLoopTri *mlooptri; + const MLoopUV *mloopuv; + const MLoop *mloop; + const int tottri; + + const Bounds2D *faceBB; + uint32_t *active_points; +} DynamicPaintCreateUVSurfaceData; + +static void dynamic_paint_create_uv_surface_direct_cb(void *userdata, const int ty) +{ + const DynamicPaintCreateUVSurfaceData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + PaintUVPoint *tempPoints = data->tempPoints; + Vec3f *tempWeights = data->tempWeights; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + const MLoop *mloop = data->mloop; + const int tottri = data->tottri; + + const Bounds2D *faceBB = data->faceBB; + + const float jitter5sample[10] = JITTER_SAMPLES; + const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + const int w = surface->image_resolution; + const int h = w; + + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; + PaintUVPoint *tPoint = &tempPoints[index]; + float point[5][2]; + + /* Init per pixel settings */ + tPoint->tri_index = -1; + tPoint->neighbour_pixel = -1; + tPoint->pixel_index = index; + + /* Actual pixel center, used when collision is found */ + point[0][0] = ((float)tx + 0.5f) / w; + point[0][1] = ((float)ty + 0.5f) / h; + + /* + * A pixel middle sample isn't enough to find very narrow polygons + * So using 4 samples of each corner too + */ + point[1][0] = ((float)tx) / w; + point[1][1] = ((float)ty) / h; + + point[2][0] = ((float)tx + 1) / w; + point[2][1] = ((float)ty) / h; + + point[3][0] = ((float)tx) / w; + point[3][1] = ((float)ty + 1) / h; + + point[4][0] = ((float)tx + 1) / w; + point[4][1] = ((float)ty + 1) / h; + + + /* Loop through samples, starting from middle point */ + for (int sample = 0; sample < 5; sample++) { + /* Loop through every face in the mesh */ + /* XXX TODO This is *horrible* with big meshes, should use a 2D BVHTree over UV tris here! */ + for (int i = 0; i < tottri; i++) { + /* Check uv bb */ + if ((faceBB[i].min[0] > point[sample][0]) || + (faceBB[i].min[1] > point[sample][1]) || + (faceBB[i].max[0] < point[sample][0]) || + (faceBB[i].max[1] < point[sample][1])) + { + continue; + } + + const float *uv1 = mloopuv[mlooptri[i].tri[0]].uv; + const float *uv2 = mloopuv[mlooptri[i].tri[1]].uv; + const float *uv3 = mloopuv[mlooptri[i].tri[2]].uv; + + /* If point is inside the face */ + if (isect_point_tri_v2(point[sample], uv1, uv2, uv3) != 0) { + float uv[2]; + + /* Add b-weights per anti-aliasing sample */ + for (int j = 0; j < aa_samples; j++) { + uv[0] = point[0][0] + jitter5sample[j * 2] / w; + uv[1] = point[0][1] + jitter5sample[j * 2 + 1] / h; + + barycentric_weights_v2(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v); + } + + /* Set surface point face values */ + tPoint->tri_index = i; + + /* save vertex indexes */ + tPoint->v1 = mloop[mlooptri[i].tri[0]].v; + tPoint->v2 = mloop[mlooptri[i].tri[1]].v; + tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + + sample = 5; /* make sure we exit sample loop as well */ + break; + } + } + } + } +} + +static void dynamic_paint_create_uv_surface_neighbor_cb(void *userdata, const int ty) +{ + const DynamicPaintCreateUVSurfaceData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + PaintUVPoint *tempPoints = data->tempPoints; + Vec3f *tempWeights = data->tempWeights; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + const MLoop *mloop = data->mloop; + + uint32_t *active_points = data->active_points; + + const float jitter5sample[10] = JITTER_SAMPLES; + const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + const int w = surface->image_resolution; + const int h = w; + + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; + PaintUVPoint *tPoint = &tempPoints[index]; + + /* If point isn't on canvas mesh */ + if (tPoint->tri_index == -1) { + float point[2]; + + /* get loop area */ + const int u_min = (tx > 0) ? -1 : 0; + const int u_max = (tx < (w - 1)) ? 1 : 0; + const int v_min = (ty > 0) ? -1 : 0; + const int v_max = (ty < (h - 1)) ? 1 : 0; + + point[0] = ((float)tx + 0.5f) / w; + point[1] = ((float)ty + 0.5f) / h; + + /* search through defined area for neighbor */ + for (int u = u_min; u <= u_max; u++) { + for (int v = v_min; v <= v_max; v++) { + /* if not this pixel itself */ + if (u != 0 || v != 0) { + const int ind = (tx + u) + w * (ty + v); + + /* if neighbor has index */ + if (tempPoints[ind].neighbour_pixel == -1 && tempPoints[ind].tri_index != -1) { + float uv[2]; + const int i = tempPoints[ind].tri_index; + const float *uv1 = mloopuv[mlooptri[i].tri[0]].uv; + const float *uv2 = mloopuv[mlooptri[i].tri[1]].uv; + const float *uv3 = mloopuv[mlooptri[i].tri[2]].uv; + + /* tri index */ + /* There is a low possibility of actually having a neighbor point which tri is + * already set from another neighbor in a separate thread here. + * Cheking for both tri_index and neighbour_pixel above reduces that probability + * but it remains possible. + * That atomic op (and its memory fence) ensures tPoint->neighbour_pixel is set + * to non--1 *before* its tri_index is set (i.e. that it cannot be used a neighbour). + */ + tPoint->neighbour_pixel = ind - 1; + atomic_add_uint32(&tPoint->neighbour_pixel, 1); + tPoint->tri_index = i; + + /* Now calculate pixel data for this pixel as it was on polygon surface */ + /* Add b-weights per anti-aliasing sample */ + for (int j = 0; j < aa_samples; j++) { + uv[0] = point[0] + jitter5sample[j * 2] / w; + uv[1] = point[1] + jitter5sample[j * 2 + 1] / h; + barycentric_weights_v2(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v); + } + + /* save vertex indexes */ + tPoint->v1 = mloop[mlooptri[i].tri[0]].v; + tPoint->v2 = mloop[mlooptri[i].tri[1]].v; + tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + + u = u_max + 1; /* make sure we exit outer loop as well */ + break; + } + } + } + } + } + + /* Increase the final number of active surface points if relevant. */ + if (tPoint->tri_index != -1) + atomic_add_uint32(active_points, 1); + } +} + +#undef JITTER_SAMPLES + +/* Tries to find the neighboring pixel in given (uv space) direction. + * Result is used by effect system to move paint on the surface. * - * px, py : origin pixel x and y - * n_index : lookup direction index (use neighX, neighY to get final index) + * px, py : origin pixel x and y + * n_index : lookup direction index (use neighX, neighY to get final index) */ -static int dynamicPaint_findNeighbourPixel(PaintUVPoint *tempPoints, DerivedMesh *dm, - const char *uvname, int w, int h, int px, int py, int n_index) +static int dynamic_paint_find_neighbour_pixel( + const DynamicPaintCreateUVSurfaceData *data, const MeshElemMap *vert_to_looptri_map, + const int w, const int h, const int px, const int py, const int n_index) { /* Note: Current method only uses polygon edges to detect neighboring pixels. - * -> It doesn't always lead to the optimum pixel but is accurate enough - * and faster/simpler than including possible face tip point links) + * -> It doesn't always lead to the optimum pixel but is accurate enough + * and faster/simpler than including possible face tip point links) */ - int x, y; - PaintUVPoint *tPoint = NULL; - PaintUVPoint *cPoint = NULL; - /* shift position by given n_index */ - x = px + neighX[n_index]; - y = py + neighY[n_index]; + const int x = px + neighX[n_index]; + const int y = py + neighY[n_index]; - if (x < 0 || x >= w) return OUT_OF_TEXTURE; - if (y < 0 || y >= h) return OUT_OF_TEXTURE; + if (x < 0 || x >= w || y < 0 || y >= h) + return OUT_OF_TEXTURE; - tPoint = &tempPoints[x + w * y]; /* UV neighbor */ - cPoint = &tempPoints[px + w * py]; /* Origin point */ + const PaintUVPoint *tempPoints = data->tempPoints; + const PaintUVPoint *tPoint = &tempPoints[x + w * y]; /* UV neighbor */ + const PaintUVPoint *cPoint = &tempPoints[px + w * py]; /* Origin point */ - /* - * Check if shifted point is on same face -> it's a correct neighbor - * (and if it isn't marked as an "edge pixel") - */ + /* Check if shifted point is on same face -> it's a correct neighbor (and if it isn't marked as an "edge pixel") */ if ((tPoint->tri_index == cPoint->tri_index) && (tPoint->neighbour_pixel == -1)) return (x + w * y); - /* - * Even if shifted point is on another face - * -> use this point. + /* Even if shifted point is on another face + * -> use this point. * - * !! Replace with "is uv faces linked" check !! - * This should work fine as long as uv island - * margin is > 1 pixel. + * !! Replace with "is uv faces linked" check !! + * This should work fine as long as uv island margin is > 1 pixel. */ if ((tPoint->tri_index != -1) && (tPoint->neighbour_pixel == -1)) { return (x + w * y); } - /* - * If we get here, the actual neighboring pixel - * is located on a non-linked uv face, and we have to find - * it's "real" position. + /* If we get here, the actual neighboring pixel is located on a non-linked uv face, + * and we have to find its "real" position. * - * Simple neighboring face finding algorithm: - * - find closest uv edge to shifted pixel and get - * the another face that shares that edge - * - find corresponding position of that new face edge - * in uv space + * Simple neighboring face finding algorithm: + * - find closest uv edge to shifted pixel and get the another face that shares that edge + * - find corresponding position of that new face edge in uv space * - * TODO: Implement something more accurate / optimized? + * TODO: Implement something more accurate / optimized? */ { - const MLoop *mloop = dm->getLoopArray(dm); - const MLoopTri *mlooptri = dm->getLoopTriArray(dm); - const int tottri = dm->getNumLoopTri(dm); - const MLoopUV *mloopuv = CustomData_get_layer_named(&dm->loopData, CD_MLOOPUV, uvname); + const MLoop *mloop = data->mloop; + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; /* Get closest edge to that subpixel on UV map */ - { - float pixel[2]; - /* distances only used for comparison */ - float dist_squared, t_dist_squared; - - int i, edge1_index, edge2_index, - e1_index, e2_index, target_tri; - float closest_point[2], lambda, dir_vec[2]; - int target_uv1 = 0, target_uv2 = 0, final_pixel[2], final_index; - - const float *s_uv1, *s_uv2, *t_uv1, *t_uv2; - - pixel[0] = ((float)(px + neighX[n_index]) + 0.5f) / (float)w; - pixel[1] = ((float)(py + neighY[n_index]) + 0.5f) / (float)h; - - /* - * Find closest edge to that pixel - */ - - /* Dist to first edge */ - e1_index = cPoint->v1; - e2_index = cPoint->v2; - edge1_index = 0; - edge2_index = 1; - dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv); - - /* Dist to second edge */ - t_dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv); - if (t_dist_squared < dist_squared) { - e1_index = cPoint->v2; - e2_index = cPoint->v3; - edge1_index = 1; - edge2_index = 2; - dist_squared = t_dist_squared; - } - /* Dist to third edge */ - t_dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv); - if (t_dist_squared < dist_squared) { - e1_index = cPoint->v3; - e2_index = cPoint->v1; - edge1_index = 2; - edge2_index = 0; - dist_squared = t_dist_squared; - } + float pixel[2]; + /* distances only used for comparison */ + float dist_squared, t_dist_squared; + int edge1_index, edge2_index; + int e1_index, e2_index, target_tri; + float closest_point[2], lambda, dir_vec[2]; + int target_uv1 = 0, target_uv2 = 0, final_pixel[2], final_index; - /* - * Now find another face that is linked to that edge - */ - target_tri = -1; + const float *s_uv1, *s_uv2, *t_uv1, *t_uv2; - for (i = 0; i < tottri; i++) { - /* - * Check if both edge vertices share this face - */ - if ((e1_index == mloop[mlooptri[i].tri[0]].v || e1_index == mloop[mlooptri[i].tri[1]].v || e1_index == mloop[mlooptri[i].tri[2]].v) && - (e2_index == mloop[mlooptri[i].tri[0]].v || e2_index == mloop[mlooptri[i].tri[1]].v || e2_index == mloop[mlooptri[i].tri[2]].v)) - { - if (i == cPoint->tri_index) continue; + pixel[0] = ((float)(px + neighX[n_index]) + 0.5f) / (float)w; + pixel[1] = ((float)(py + neighY[n_index]) + 0.5f) / (float)h; - target_tri = i; + /* + * Find closest edge to that pixel + */ - /* - * Get edge UV index - */ - if (e1_index == mloop[mlooptri[i].tri[0]].v) target_uv1 = 0; - else if (e1_index == mloop[mlooptri[i].tri[1]].v) target_uv1 = 1; - else if (e1_index == mloop[mlooptri[i].tri[2]].v) target_uv1 = 2; + /* Dist to first edge */ + e1_index = cPoint->v1; + e2_index = cPoint->v2; + edge1_index = 0; + edge2_index = 1; + dist_squared = dist_squared_to_line_segment_v2( + pixel, + mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv, + mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv); + + /* Dist to second edge */ + t_dist_squared = dist_squared_to_line_segment_v2( + pixel, + mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv, + mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv); + if (t_dist_squared < dist_squared) { + e1_index = cPoint->v2; + e2_index = cPoint->v3; + edge1_index = 1; + edge2_index = 2; + dist_squared = t_dist_squared; + } - if (e2_index == mloop[mlooptri[i].tri[0]].v) target_uv2 = 0; - else if (e2_index == mloop[mlooptri[i].tri[1]].v) target_uv2 = 1; - else if (e2_index == mloop[mlooptri[i].tri[2]].v) target_uv2 = 2; + /* Dist to third edge */ + t_dist_squared = dist_squared_to_line_segment_v2( + pixel, + mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv, + mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv); + if (t_dist_squared < dist_squared) { + e1_index = cPoint->v3; + e2_index = cPoint->v1; + edge1_index = 2; + edge2_index = 0; + dist_squared = t_dist_squared; + } - break; - } - } + /* + * Now find another face that is linked to that edge + */ + target_tri = -1; - /* If none found pixel is on mesh edge */ - if (target_tri == -1) return ON_MESH_EDGE; + /* Use a pre-computed vert-to-looptri mapping, speeds up things a lot compared to looping over all loopti. */ + for (int i = 0; i < vert_to_looptri_map[e1_index].count; i++) { + const int lt_index = vert_to_looptri_map[e1_index].indices[i]; + const int v0 = mloop[mlooptri[lt_index].tri[0]].v; + const int v1 = mloop[mlooptri[lt_index].tri[1]].v; + const int v2 = mloop[mlooptri[lt_index].tri[2]].v; - /* - * If target face is connected in UV space as well, just use original index - */ - s_uv1 = mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv; - s_uv2 = mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv; - t_uv1 = mloopuv[mlooptri[target_tri].tri[target_uv1]].uv; - t_uv2 = mloopuv[mlooptri[target_tri].tri[target_uv2]].uv; + BLI_assert(ELEM(e1_index, v0, v1, v2)); - //printf("connected UV : %f,%f & %f,%f - %f,%f & %f,%f\n", s_uv1[0], s_uv1[1], s_uv2[0], s_uv2[1], t_uv1[0], t_uv1[1], t_uv2[0], t_uv2[1]); + if (ELEM(e2_index, v0, v1, v2)) { + if (lt_index == cPoint->tri_index) + continue; - if (((s_uv1[0] == t_uv1[0] && s_uv1[1] == t_uv1[1]) && - (s_uv2[0] == t_uv2[0] && s_uv2[1] == t_uv2[1]) ) || - ((s_uv2[0] == t_uv1[0] && s_uv2[1] == t_uv1[1]) && - (s_uv1[0] == t_uv2[0] && s_uv1[1] == t_uv2[1]) )) - { - return ((px + neighX[n_index]) + w * (py + neighY[n_index])); - } + target_tri = lt_index; - /* - * Find a point that is relatively at same edge position - * on this other face UV - */ - lambda = closest_to_line_v2( - closest_point, pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv); - if (lambda < 0.0f) lambda = 0.0f; - if (lambda > 1.0f) lambda = 1.0f; - - sub_v2_v2v2( - dir_vec, - mloopuv[mlooptri[target_tri].tri[target_uv2]].uv, - mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); - - mul_v2_fl(dir_vec, lambda); - - copy_v2_v2(pixel, mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); - add_v2_v2(pixel, dir_vec); - pixel[0] = (pixel[0] * (float)w) - 0.5f; - pixel[1] = (pixel[1] * (float)h) - 0.5f; - - final_pixel[0] = (int)floor(pixel[0]); - final_pixel[1] = (int)floor(pixel[1]); - - /* If current pixel uv is outside of texture */ - if (final_pixel[0] < 0 || final_pixel[0] >= w) return OUT_OF_TEXTURE; - if (final_pixel[1] < 0 || final_pixel[1] >= h) return OUT_OF_TEXTURE; - - final_index = final_pixel[0] + w * final_pixel[1]; - - /* If we ended up to our origin point ( mesh has smaller than pixel sized faces) */ - if (final_index == (px + w * py)) return NOT_FOUND; - /* If found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ - if (tempPoints[final_index].tri_index != target_tri) { - return NOT_FOUND; + /* Get edge UV index */ + target_uv1 = (e1_index == v0) ? 0 : ((e1_index == v1) ? 1 : 2); + target_uv2 = (e2_index == v0) ? 0 : ((e2_index == v1) ? 1 : 2); + break; } + } - /* - * If final point is an "edge pixel", use it's "real" neighbor instead - */ - if (tempPoints[final_index].neighbour_pixel != -1) final_index = cPoint->neighbour_pixel; + /* If none found pixel is on mesh edge */ + if (target_tri == -1) + return ON_MESH_EDGE; - return final_index; + /* + * If target face is connected in UV space as well, just use original index + */ + s_uv1 = mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv; + s_uv2 = mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv; + t_uv1 = mloopuv[mlooptri[target_tri].tri[target_uv1]].uv; + t_uv2 = mloopuv[mlooptri[target_tri].tri[target_uv2]].uv; + + //printf("connected UV : %f,%f & %f,%f - %f,%f & %f,%f\n", s_uv1[0], s_uv1[1], s_uv2[0], s_uv2[1], t_uv1[0], t_uv1[1], t_uv2[0], t_uv2[1]); + + if (((s_uv1[0] == t_uv1[0] && s_uv1[1] == t_uv1[1]) && + (s_uv2[0] == t_uv2[0] && s_uv2[1] == t_uv2[1])) || + ((s_uv2[0] == t_uv1[0] && s_uv2[1] == t_uv1[1]) && + (s_uv1[0] == t_uv2[0] && s_uv1[1] == t_uv2[1]))) + { + return ((px + neighX[n_index]) + w * (py + neighY[n_index])); } + + /* + * Find a point that is relatively at same edge position + * on this other face UV + */ + lambda = closest_to_line_v2( + closest_point, pixel, + mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv, + mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv); + CLAMP(lambda, 0.0f, 1.0f); + + sub_v2_v2v2( + dir_vec, + mloopuv[mlooptri[target_tri].tri[target_uv2]].uv, + mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); + + mul_v2_fl(dir_vec, lambda); + + copy_v2_v2(pixel, mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); + add_v2_v2(pixel, dir_vec); + pixel[0] = (pixel[0] * (float)w) - 0.5f; + pixel[1] = (pixel[1] * (float)h) - 0.5f; + + final_pixel[0] = (int)floorf(pixel[0]); + final_pixel[1] = (int)floorf(pixel[1]); + + /* If current pixel uv is outside of texture */ + if (final_pixel[0] < 0 || final_pixel[0] >= w || final_pixel[1] < 0 || final_pixel[1] >= h) + return OUT_OF_TEXTURE; + + final_index = final_pixel[0] + w * final_pixel[1]; + + /* If we ended up to our origin point ( mesh has smaller than pixel sized faces) */ + if (final_index == (px + w * py)) + return NOT_FOUND; + /* If found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ + if (tempPoints[final_index].tri_index != target_tri) + return NOT_FOUND; + + /* If final point is an "edge pixel", use it's "real" neighbor instead */ + if (tempPoints[final_index].neighbour_pixel != -1) + final_index = cPoint->neighbour_pixel; + + return final_index; } } -/* - * Create a surface for uv image sequence format - */ -int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) +int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface, float *progress, short *do_update) { /* Antialias jitter point relative coords */ - const float jitter5sample[10] = { - 0.0f, 0.0f, - -0.2f, -0.4f, - 0.2f, 0.4f, - 0.4f, -0.2f, - -0.4f, 0.3f, - }; - int ty; - int w, h; - int tottri; + const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - int active_points = 0; - int error = 0; + uint32_t active_points = 0; + bool error = false; PaintSurfaceData *sData; DynamicPaintCanvasSettings *canvas = surface->canvas; @@ -2132,7 +2468,9 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) Bounds2D *faceBB = NULL; int *final_index; - int aa_samples; + + *progress = 0.0f; + *do_update = true; if (!dm) return setError(canvas, N_("Canvas mesh not updated")); @@ -2141,7 +2479,7 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) mloop = dm->getLoopArray(dm); mlooptri = dm->getLoopTriArray(dm); - tottri = dm->getNumLoopTri(dm); + const int tottri = dm->getNumLoopTri(dm); /* get uv map */ if (CustomData_has_layer(&dm->loopData, CD_MLOOPUV)) { @@ -2155,7 +2493,8 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) if (surface->image_resolution < 16 || surface->image_resolution > 8192) return setError(canvas, N_("Invalid resolution")); - w = h = surface->image_resolution; + const int w = surface->image_resolution; + const int h = w; /* * Start generating the surface @@ -2163,156 +2502,60 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) printf("DynamicPaint: Preparing UV surface of %ix%i pixels and %i tris.\n", w, h, tottri); /* Init data struct */ - if (surface->data) dynamicPaint_freeSurfaceData(surface); + if (surface->data) + dynamicPaint_freeSurfaceData(surface); sData = surface->data = MEM_callocN(sizeof(PaintSurfaceData), "PaintSurfaceData"); if (!surface->data) return setError(canvas, N_("Not enough free memory")); - aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; - tempPoints = (struct PaintUVPoint *) MEM_callocN(w * h * sizeof(struct PaintUVPoint), "Temp PaintUVPoint"); - if (!tempPoints) error = 1; + tempPoints = MEM_callocN(w * h * sizeof(*tempPoints), "Temp PaintUVPoint"); + if (!tempPoints) + error = true; - final_index = (int *) MEM_callocN(w * h * sizeof(int), "Temp UV Final Indexes"); - if (!final_index) error = 1; + final_index = MEM_callocN(w * h * sizeof(*final_index), "Temp UV Final Indexes"); + if (!final_index) + error = true; - tempWeights = (struct Vec3f *) MEM_mallocN(w * h * aa_samples * sizeof(struct Vec3f), "Temp bWeights"); - if (!tempWeights) error = 1; + tempWeights = MEM_mallocN(w * h * aa_samples * sizeof(*tempWeights), "Temp bWeights"); + if (!tempWeights) + error = true; /* * Generate a temporary bounding box array for UV faces to optimize * the pixel-inside-a-face search. */ if (!error) { - faceBB = (struct Bounds2D *) MEM_mallocN(tottri * sizeof(struct Bounds2D), "MPCanvasFaceBB"); - if (!faceBB) error = 1; + faceBB = MEM_mallocN(tottri * sizeof(*faceBB), "MPCanvasFaceBB"); + if (!faceBB) + error = true; } - if (!error) - for (ty = 0; ty < tottri; ty++) { - int i; + *progress = 0.01f; + *do_update = true; - copy_v2_v2(faceBB[ty].min, mloopuv[mlooptri[ty].tri[0]].uv); - copy_v2_v2(faceBB[ty].max, mloopuv[mlooptri[ty].tri[0]].uv); + if (!error) { + for (int i = 0; i < tottri; i++) { + copy_v2_v2(faceBB[i].min, mloopuv[mlooptri[i].tri[0]].uv); + copy_v2_v2(faceBB[i].max, mloopuv[mlooptri[i].tri[0]].uv); - for (i = 1; i < 3; i++) { - CLAMP_MAX(faceBB[ty].min[0], mloopuv[mlooptri[ty].tri[i]].uv[0]); - CLAMP_MAX(faceBB[ty].min[1], mloopuv[mlooptri[ty].tri[i]].uv[1]); - CLAMP_MIN(faceBB[ty].max[0], mloopuv[mlooptri[ty].tri[i]].uv[0]); - CLAMP_MIN(faceBB[ty].max[1], mloopuv[mlooptri[ty].tri[i]].uv[1]); + for (int j = 1; j < 3; j++) { + minmax_v2v2_v2(faceBB[i].min, faceBB[i].max, mloopuv[mlooptri[i].tri[j]].uv); } } - /* - * Loop through every pixel and check - * if pixel is uv-mapped on a canvas face. - */ - if (!error) { -#pragma omp parallel for schedule(static) - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - int i, sample; - int index = tx + w * ty; - PaintUVPoint *tPoint = (&tempPoints[index]); - - bool isInside = false; /* if point is inside a uv face */ - - float d1[2], d2[2], d3[2], point[5][2]; - float dot00, dot01, dot02, dot11, dot12, invDenom, u, v; - - /* Init per pixel settings */ - tPoint->tri_index = -1; - tPoint->neighbour_pixel = -1; - tPoint->pixel_index = index; - - /* Actual pixel center, used when collision is found */ - point[0][0] = ((float)tx + 0.5f) / w; - point[0][1] = ((float)ty + 0.5f) / h; - - /* - * A pixel middle sample isn't enough to find very narrow polygons - * So using 4 samples of each corner too - */ - point[1][0] = ((float)tx) / w; - point[1][1] = ((float)ty) / h; - - point[2][0] = ((float)tx + 1) / w; - point[2][1] = ((float)ty) / h; - - point[3][0] = ((float)tx) / w; - point[3][1] = ((float)ty + 1) / h; - - point[4][0] = ((float)tx + 1) / w; - point[4][1] = ((float)ty + 1) / h; - - - /* Loop through samples, starting from middle point */ - for (sample = 0; sample < 5; sample++) { - - /* Loop through every face in the mesh */ - for (i = 0; i < tottri; i++) { - - /* Check uv bb */ - if (faceBB[i].min[0] > (point[sample][0])) continue; - if (faceBB[i].min[1] > (point[sample][1])) continue; - if (faceBB[i].max[0] < (point[sample][0])) continue; - if (faceBB[i].max[1] < (point[sample][1])) continue; - - /* Calculate point inside a triangle check - * for uv0, 1, 2 */ - sub_v2_v2v2(d1, mloopuv[mlooptri[i].tri[2]].uv, mloopuv[mlooptri[i].tri[0]].uv); /* uv2 - uv0 */ - sub_v2_v2v2(d2, mloopuv[mlooptri[i].tri[1]].uv, mloopuv[mlooptri[i].tri[0]].uv); /* uv1 - uv0 */ - sub_v2_v2v2(d3, point[sample], mloopuv[mlooptri[i].tri[0]].uv); /* point - uv0 */ - - dot00 = d1[0] * d1[0] + d1[1] * d1[1]; - dot01 = d1[0] * d2[0] + d1[1] * d2[1]; - dot02 = d1[0] * d3[0] + d1[1] * d3[1]; - dot11 = d2[0] * d2[0] + d2[1] * d2[1]; - dot12 = d2[0] * d3[0] + d2[1] * d3[1]; - - invDenom = (dot00 * dot11 - dot01 * dot01); - invDenom = invDenom ? 1.0f / invDenom : 1.0f; - u = (dot11 * dot02 - dot01 * dot12) * invDenom; - v = (dot00 * dot12 - dot01 * dot02) * invDenom; - - if ((u > 0) && (v > 0) && (u + v < 1)) { isInside = true; } /* is inside a triangle */ - - /* - * If point was inside the face - */ - if (isInside) { - - float uv1co[2], uv2co[2], uv3co[2], uv[2]; - int j; - - /* Get triagnle uvs */ - copy_v2_v2(uv1co, mloopuv[mlooptri[i].tri[0]].uv); - copy_v2_v2(uv2co, mloopuv[mlooptri[i].tri[1]].uv); - copy_v2_v2(uv3co, mloopuv[mlooptri[i].tri[2]].uv); - - /* Add b-weights per anti-aliasing sample */ - for (j = 0; j < aa_samples; j++) { - uv[0] = point[0][0] + jitter5sample[j * 2] / w; - uv[1] = point[0][1] + jitter5sample[j * 2 + 1] / h; - - barycentric_weights_v2(uv1co, uv2co, uv3co, uv, tempWeights[index * aa_samples + j].v); - } - - /* Set surface point face values */ - tPoint->tri_index = i; + *progress = 0.02f; + *do_update = true; - /* save vertex indexes */ - tPoint->v1 = mloop[mlooptri[i].tri[0]].v; - tPoint->v2 = mloop[mlooptri[i].tri[1]].v; - tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + /* Loop through every pixel and check if pixel is uv-mapped on a canvas face. */ + DynamicPaintCreateUVSurfaceData data = { + .surface = surface, .tempPoints = tempPoints, .tempWeights = tempWeights, + .mlooptri = mlooptri, .mloopuv = mloopuv, .mloop = mloop, .tottri = tottri, + .faceBB = faceBB, + }; + BLI_task_parallel_range(0, h, &data, dynamic_paint_create_uv_surface_direct_cb, h > 64 || tottri > 1000); - sample = 5; /* make sure we exit sample loop as well */ - break; - } - } - } /* sample loop */ - } - } + *progress = 0.04f; + *do_update = true; /* * Now loop through every pixel that was left without index @@ -2320,87 +2563,11 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) * If so use that polygon as pixel surface. * (To avoid seams on uv island edges) */ -#pragma omp parallel for schedule(static) - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - int index = tx + w * ty; - PaintUVPoint *tPoint = (&tempPoints[index]); - - /* If point isn't't on canvas mesh */ - if (tPoint->tri_index == -1) { - int u_min, u_max, v_min, v_max; - int u, v, ind; - float point[2]; - - /* get loop area */ - u_min = (tx > 0) ? -1 : 0; - u_max = (tx < (w - 1)) ? 1 : 0; - v_min = (ty > 0) ? -1 : 0; - v_max = (ty < (h - 1)) ? 1 : 0; - - point[0] = ((float)tx + 0.5f) / w; - point[1] = ((float)ty + 0.5f) / h; - - /* search through defined area for neighbor */ - for (u = u_min; u <= u_max; u++) - for (v = v_min; v <= v_max; v++) { - /* if not this pixel itself */ - if (u != 0 || v != 0) { - ind = (tx + u) + w * (ty + v); - - /* if neighbor has index */ - if (tempPoints[ind].tri_index != -1) { - - float uv1co[2], uv2co[2], uv3co[2], uv[2]; - int i = tempPoints[ind].tri_index, j; - - /* Now calculate pixel data for this pixel as it was on polygon surface */ - copy_v2_v2(uv1co, mloopuv[mlooptri[i].tri[0]].uv); - copy_v2_v2(uv2co, mloopuv[mlooptri[i].tri[1]].uv); - copy_v2_v2(uv3co, mloopuv[mlooptri[i].tri[2]].uv); - - /* Add b-weights per anti-aliasing sample */ - for (j = 0; j < aa_samples; j++) { - - uv[0] = point[0] + jitter5sample[j * 2] / w; - uv[1] = point[1] + jitter5sample[j * 2 + 1] / h; - barycentric_weights_v2(uv1co, uv2co, uv3co, uv, tempWeights[index * aa_samples + j].v); - } - - /* Set values */ - tPoint->neighbour_pixel = ind; /* tri index */ - - /* save vertex indexes */ - tPoint->v1 = mloop[mlooptri[i].tri[0]].v; - tPoint->v2 = mloop[mlooptri[i].tri[1]].v; - tPoint->v3 = mloop[mlooptri[i].tri[2]].v; - - u = u_max + 1; /* make sure we exit outer loop as well */ - break; - } - } - } - } - } - } + data.active_points = &active_points; + BLI_task_parallel_range(0, h, &data, dynamic_paint_create_uv_surface_neighbor_cb, h > 64); - /* - * When base loop is over convert found neighbor indexes to real ones - * Also count the final number of active surface points - */ - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - int index = tx + w * ty; - PaintUVPoint *tPoint = &tempPoints[index]; - - if (tPoint->tri_index == -1 && tPoint->neighbour_pixel != -1) - tPoint->tri_index = tempPoints[tPoint->neighbour_pixel].tri_index; - if (tPoint->tri_index != -1) - active_points++; - } - } + *progress = 0.06f; + *do_update = true; /* Generate surface adjacency data. */ { @@ -2416,25 +2583,31 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) } /* allocate memory */ sData->total_points = w * h; - dynamicPaint_initAdjacencyData(surface, 1); + dynamicPaint_initAdjacencyData(surface, true); if (sData->adj_data) { PaintAdjData *ed = sData->adj_data; - unsigned int n_pos = 0; - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - int index = tx + w * ty; + int n_pos = 0; + + MeshElemMap *vert_to_looptri_map; + int *vert_to_looptri_map_mem; + + BKE_mesh_vert_looptri_map_create( + &vert_to_looptri_map, &vert_to_looptri_map_mem, + dm->getVertArray(dm), dm->getNumVerts(dm), mlooptri, tottri, mloop, dm->getNumLoops(dm)); + + for (int ty = 0; ty < h; ty++) { + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; if (tempPoints[index].tri_index != -1) { ed->n_index[final_index[index]] = n_pos; ed->n_num[final_index[index]] = 0; for (int i = 0; i < 8; i++) { - - /* Try to find a neighboring pixel in defined direction - * If not found, -1 is returned */ - int n_target = dynamicPaint_findNeighbourPixel(tempPoints, dm, uvname, w, h, tx, ty, i); + /* Try to find a neighboring pixel in defined direction. If not found, -1 is returned */ + const int n_target = dynamic_paint_find_neighbour_pixel( + &data, vert_to_looptri_map, w, h, tx, ty, i); if (n_target >= 0) { ed->n_target[n_pos] = final_index[n_target]; @@ -2448,43 +2621,50 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) } } } + + MEM_freeN(vert_to_looptri_map); + MEM_freeN(vert_to_looptri_map_mem); } } + *progress = 0.08f; + *do_update = true; + /* Create final surface data without inactive points */ - { - ImgSeqFormatData *f_data = MEM_callocN(sizeof(struct ImgSeqFormatData), "ImgSeqFormatData"); - if (f_data) { - f_data->uv_p = MEM_callocN(active_points * sizeof(struct PaintUVPoint), "PaintUVPoint"); - f_data->barycentricWeights = MEM_callocN(active_points * aa_samples * sizeof(struct Vec3f), "PaintUVPoint"); + ImgSeqFormatData *f_data = MEM_callocN(sizeof(*f_data), "ImgSeqFormatData"); + if (f_data) { + f_data->uv_p = MEM_callocN(active_points * sizeof(*f_data->uv_p), "PaintUVPoint"); + f_data->barycentricWeights = MEM_callocN(active_points * aa_samples * sizeof(*f_data->barycentricWeights), + "PaintUVPoint"); - if (!f_data->uv_p || !f_data->barycentricWeights) error = 1; - } - else { + if (!f_data->uv_p || !f_data->barycentricWeights) error = 1; - } + } + else { + error = 1; + } - sData->total_points = active_points; - - /* in case of allocation error, free everything */ - if (error) { - if (f_data) { - if (f_data->uv_p) MEM_freeN(f_data->uv_p); - if (f_data->barycentricWeights) MEM_freeN(f_data->barycentricWeights); - MEM_freeN(f_data); - } + /* in case of allocation error, free everything */ + if (error) { + if (f_data) { + if (f_data->uv_p) + MEM_freeN(f_data->uv_p); + if (f_data->barycentricWeights) + MEM_freeN(f_data->barycentricWeights); + MEM_freeN(f_data); } - else { - int index, cursor = 0; - sData->total_points = active_points; - sData->format_data = f_data; - - for (index = 0; index < (w * h); index++) { - if (tempPoints[index].tri_index != -1) { - memcpy(&f_data->uv_p[cursor], &tempPoints[index], sizeof(PaintUVPoint)); - memcpy(&f_data->barycentricWeights[cursor * aa_samples], &tempWeights[index * aa_samples], sizeof(Vec3f) * aa_samples); - cursor++; - } + sData->total_points = 0; + } + else { + sData->total_points = (int)active_points; + sData->format_data = f_data; + + for (int index = 0, cursor = 0; index < (w * h); index++) { + if (tempPoints[index].tri_index != -1) { + memcpy(&f_data->uv_p[cursor], &tempPoints[index], sizeof(PaintUVPoint)); + memcpy(&f_data->barycentricWeights[cursor * aa_samples], &tempWeights[index * aa_samples], + sizeof(*tempWeights) * aa_samples); + cursor++; } } } @@ -2492,10 +2672,14 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) if (error == 1) setError(canvas, N_("Not enough free memory")); - if (faceBB) MEM_freeN(faceBB); - if (tempPoints) MEM_freeN(tempPoints); - if (tempWeights) MEM_freeN(tempWeights); - if (final_index) MEM_freeN(final_index); + if (faceBB) + MEM_freeN(faceBB); + if (tempPoints) + MEM_freeN(tempPoints); + if (tempWeights) + MEM_freeN(tempWeights); + if (final_index) + MEM_freeN(final_index); /* Init surface type data */ if (!error) { @@ -2505,38 +2689,126 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) /* ----------------------------------------------------------------- * For debug, output pixel statuses to the color map * -----------------------------------------------------------------*/ -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) - { + for (index = 0; index < sData->total_points; index++) { ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; PaintUVPoint *uvPoint = &((PaintUVPoint *)f_data->uv_p)[index]; PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; pPoint->alpha = 1.0f; /* Every pixel that is assigned as "edge pixel" gets blue color */ - if (uvPoint->neighbour_pixel != -1) pPoint->color[2] = 1.0f; + if (uvPoint->neighbour_pixel != -1) + pPoint->color[2] = 1.0f; /* and every pixel that finally got an polygon gets red color */ - if (uvPoint->tri_index != -1) pPoint->color[0] = 1.0f; /* green color shows pixel face index hash */ - if (uvPoint->tri_index != -1) pPoint->color[1] = (float)(uvPoint->tri_index % 255) / 256.0f; + if (uvPoint->tri_index != -1) { + pPoint->color[0] = 1.0f; + pPoint->color[1] = (float)(uvPoint->tri_index % 255) / 256.0f; + } } - #endif + dynamicPaint_setInitialColor(scene, surface); } + *progress = 0.09f; + *do_update = true; + return (error == 0); } /* * Outputs an image file from uv surface data. */ +typedef struct DynamicPaintOutputSurfaceImageData { + const DynamicPaintSurface *surface; + ImBuf *ibuf; +} DynamicPaintOutputSurfaceImageData; + +static void dynamic_paint_output_surface_image_paint_cb(void *userdata, const int index) +{ + const DynamicPaintOutputSurfaceImageData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintPoint *point = &((PaintPoint *)surface->data->type_data)[index]; + + ImBuf *ibuf = data->ibuf; + /* image buffer position */ + const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4; + + /* blend wet and dry layers */ + blendColors(point->color, point->color[3], point->e_color, point->e_color[3], &ibuf->rect_float[pos]); + + /* Multiply color by alpha if enabled */ + if (surface->flags & MOD_DPAINT_MULALPHA) { + mul_v3_fl(&ibuf->rect_float[pos], ibuf->rect_float[pos + 3]); + } +} + +static void dynamic_paint_output_surface_image_displace_cb(void *userdata, const int index) +{ + const DynamicPaintOutputSurfaceImageData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + float depth = ((float *)surface->data->type_data)[index]; + + ImBuf *ibuf = data->ibuf; + /* image buffer position */ + const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4; + + if (surface->depth_clamp) + depth /= surface->depth_clamp; + + if (surface->disp_type == MOD_DPAINT_DISP_DISPLACE) { + depth = (0.5f - depth / 2.0f); + } + + CLAMP(depth, 0.0f, 1.0f); + + copy_v3_fl(&ibuf->rect_float[pos], depth); + ibuf->rect_float[pos + 3] = 1.0f; +} + +static void dynamic_paint_output_surface_image_wave_cb(void *userdata, const int index) +{ + const DynamicPaintOutputSurfaceImageData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintWavePoint *wPoint = &((PaintWavePoint *)surface->data->type_data)[index]; + float depth = wPoint->height; + + ImBuf *ibuf = data->ibuf; + /* image buffer position */ + const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4; + + if (surface->depth_clamp) + depth /= surface->depth_clamp; + + depth = (0.5f + depth / 2.0f); + CLAMP(depth, 0.0f, 1.0f); + + copy_v3_fl(&ibuf->rect_float[pos], depth); + ibuf->rect_float[pos + 3] = 1.0f; +} + +static void dynamic_paint_output_surface_image_wetmap_cb(void *userdata, const int index) +{ + const DynamicPaintOutputSurfaceImageData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintPoint *point = &((PaintPoint *)surface->data->type_data)[index]; + + ImBuf *ibuf = data->ibuf; + /* image buffer position */ + const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4; + + copy_v3_fl(&ibuf->rect_float[pos], (point->wetness > 1.0f) ? 1.0f : point->wetness); + ibuf->rect_float[pos + 3] = 1.0f; +} + void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filename, short output_layer) { - int index; ImBuf *ibuf = NULL; PaintSurfaceData *sData = surface->data; - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; /* OpenEXR or PNG */ int format = (surface->image_fileformat & MOD_DPAINT_IMGFORMAT_OPENEXR) ? R_IMF_IMTYPE_OPENEXR : R_IMF_IMTYPE_PNG; char output_file[FILE_MAX]; @@ -2547,7 +2819,8 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam } /* if selected format is openexr, but current build doesnt support one */ #ifndef WITH_OPENEXR - if (format == R_IMF_IMTYPE_OPENEXR) format = R_IMF_IMTYPE_PNG; + if (format == R_IMF_IMTYPE_OPENEXR) + format = R_IMF_IMTYPE_PNG; #endif BLI_strncpy(output_file, filename, sizeof(output_file)); BKE_image_path_ensure_ext_from_imtype(output_file, format); @@ -2563,82 +2836,66 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam return; } -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int pos = f_data->uv_p[index].pixel_index * 4; /* image buffer position */ - - /* Set values of preferred type */ - if (output_layer == 1) { - /* wetmap */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - PaintPoint *point = &((PaintPoint *)sData->type_data)[index]; - float value = (point->wetness > 1.0f) ? 1.0f : point->wetness; - - ibuf->rect_float[pos] = value; - ibuf->rect_float[pos + 1] = value; - ibuf->rect_float[pos + 2] = value; - ibuf->rect_float[pos + 3] = 1.0f; - } - } - else if (output_layer == 0) { - /* Paintmap */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - PaintPoint *point = &((PaintPoint *)sData->type_data)[index]; - - /* blend wet and dry layers */ - blendColors(point->color, point->color[3], point->e_color, point->e_color[3], &ibuf->rect_float[pos]); - - /* Multiply color by alpha if enabled */ - if (surface->flags & MOD_DPAINT_MULALPHA) { - ibuf->rect_float[pos] *= ibuf->rect_float[pos + 3]; - ibuf->rect_float[pos + 1] *= ibuf->rect_float[pos + 3]; - ibuf->rect_float[pos + 2] *= ibuf->rect_float[pos + 3]; - } + DynamicPaintOutputSurfaceImageData data = {.surface = surface, .ibuf = ibuf}; + switch(surface->type) { + case MOD_DPAINT_SURFACE_T_PAINT: + switch (output_layer) { + case 0: + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_output_surface_image_paint_cb, sData->total_points > 10000); + break; + case 1: + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_output_surface_image_wetmap_cb, sData->total_points > 10000); + break; + default: + BLI_assert(0); + break; } - /* displace */ - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { - float depth = ((float *)sData->type_data)[index]; - if (surface->depth_clamp) - depth /= surface->depth_clamp; - - if (surface->disp_type == MOD_DPAINT_DISP_DISPLACE) { - depth = (0.5f - depth / 2.0f); - } - - CLAMP(depth, 0.0f, 1.0f); - - ibuf->rect_float[pos] = depth; - ibuf->rect_float[pos + 1] = depth; - ibuf->rect_float[pos + 2] = depth; - ibuf->rect_float[pos + 3] = 1.0f; + break; + case MOD_DPAINT_SURFACE_T_DISPLACE: + switch (output_layer) { + case 0: + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_output_surface_image_displace_cb, sData->total_points > 10000); + break; + case 1: + break; + default: + BLI_assert(0); + break; } - /* waves */ - else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { - PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; - float depth = wPoint->height; - if (surface->depth_clamp) - depth /= surface->depth_clamp; - depth = (0.5f + depth / 2.0f); - CLAMP(depth, 0.0f, 1.0f); - - ibuf->rect_float[pos] = depth; - ibuf->rect_float[pos + 1] = depth; - ibuf->rect_float[pos + 2] = depth; - ibuf->rect_float[pos + 3] = 1.0f; + break; + case MOD_DPAINT_SURFACE_T_WAVE: + switch (output_layer) { + case 0: + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_output_surface_image_wave_cb, sData->total_points > 10000); + break; + case 1: + break; + default: + BLI_assert(0); + break; } - } + break; + default: + BLI_assert(0); + break; } /* Set output format, png in case exr isn't supported */ - ibuf->ftype = IMB_FTYPE_PNG; - ibuf->foptions.quality = 15; - #ifdef WITH_OPENEXR if (format == R_IMF_IMTYPE_OPENEXR) { /* OpenEXR 32-bit float */ ibuf->ftype = IMB_FTYPE_OPENEXR; ibuf->foptions.flag |= OPENEXR_COMPRESS; } + else #endif + { + ibuf->ftype = IMB_FTYPE_PNG; + ibuf->foptions.quality = 15; + } /* Save image */ IMB_saveiff(ibuf, output_file, IB_rectfloat); @@ -2704,7 +2961,7 @@ static void dynamicPaint_freeBrushMaterials(BrushMaterials *bMats) * Get material diffuse color and alpha (including linked textures) in given coordinates */ static void dynamicPaint_doMaterialTex( - BrushMaterials *bMats, float color[3], float *alpha, Object *brushOb, + const BrushMaterials *bMats, float color[3], float *alpha, Object *brushOb, const float volume_co[3], const float surface_co[3], int triIndex, DerivedMesh *orcoDm) { @@ -2717,9 +2974,11 @@ static void dynamicPaint_doMaterialTex( if (mat == NULL) { if (bMats->ob_mats) { int mat_nr = mpoly[mlooptri[triIndex].poly].mat_nr; - if (mat_nr >= (*give_totcolp(brushOb))) return; + if (mat_nr >= (*give_totcolp(brushOb))) + return; mat = bMats->ob_mats[mat_nr]; - if (mat == NULL) return; /* No material assigned */ + if (mat == NULL) + return; /* No material assigned */ } else { return; @@ -2732,7 +2991,7 @@ static void dynamicPaint_doMaterialTex( /***************************** Ray / Nearest Point Utils ******************************/ -/* A modified callback to bvh tree raycast. The tree must bust have been built using bvhtree_from_mesh_looptri. +/* A modified callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_looptri. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. * * To optimize brush detection speed this doesn't calculate hit coordinates or normal. @@ -2758,10 +3017,9 @@ static void mesh_tris_spherecast_dp(void *userdata, int index, const BVHTreeRay hit->dist = dist; hit->no[0] = 0.0f; } - } -/* A modified callback to bvh tree nearest point. The tree must bust have been built using bvhtree_from_mesh_looptri. +/* A modified callback to bvh tree nearest point. The tree must have been built using bvhtree_from_mesh_looptri. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. * * To optimize brush detection speed this doesn't calculate hit normal. @@ -2804,15 +3062,15 @@ static void mesh_tris_nearest_point_dp(void *userdata, int index, const float co * operations when using substeps */ static void dynamicPaint_mixPaintColors( - DynamicPaintSurface *surface, int index, int paintFlags, - const float paintColor[3], float *paintAlpha, float *paintWetness, float *timescale) + const DynamicPaintSurface *surface, const int index, const int paintFlags, + const float paintColor[3], const float paintAlpha, const float paintWetness, const float timescale) { PaintPoint *pPoint = &((PaintPoint *)surface->data->type_data)[index]; /* Add paint */ if (!(paintFlags & MOD_DPAINT_ERASE)) { float mix[4]; - float temp_alpha = (*paintAlpha) * ((paintFlags & MOD_DPAINT_ABS_ALPHA) ? 1.0f : (*timescale)); + float temp_alpha = paintAlpha * ((paintFlags & MOD_DPAINT_ABS_ALPHA) ? 1.0f : timescale); /* mix brush color with wet layer color */ blendColors(pPoint->e_color, pPoint->e_color[3], paintColor, temp_alpha, mix); @@ -2821,11 +3079,11 @@ static void dynamicPaint_mixPaintColors( /* mix wetness and alpha depending on selected alpha mode */ if (paintFlags & MOD_DPAINT_ABS_ALPHA) { /* update values to the brush level unless theyre higher already */ - CLAMP_MIN(pPoint->e_color[3], *paintAlpha); - CLAMP_MIN(pPoint->wetness, *paintWetness); + CLAMP_MIN(pPoint->e_color[3], paintAlpha); + CLAMP_MIN(pPoint->wetness, paintWetness); } else { - float wetness = (*paintWetness); + float wetness = paintWetness; CLAMP(wetness, 0.0f, 1.0f); pPoint->e_color[3] = mix[3]; pPoint->wetness = pPoint->wetness * (1.0f - wetness) + wetness; @@ -2839,7 +3097,7 @@ static void dynamicPaint_mixPaintColors( else { float a_ratio, a_highest; float wetness; - float invFact = 1.0f - (*paintAlpha); + float invFact = 1.0f - paintAlpha; /* * Make highest alpha to match erased value @@ -2855,47 +3113,57 @@ static void dynamicPaint_mixPaintColors( } } else { - pPoint->e_color[3] -= (*paintAlpha) * (*timescale); + pPoint->e_color[3] -= paintAlpha * timescale; CLAMP_MIN(pPoint->e_color[3], 0.0f); - pPoint->color[3] -= (*paintAlpha) * (*timescale); + pPoint->color[3] -= paintAlpha * timescale; CLAMP_MIN(pPoint->color[3], 0.0f); } - wetness = (1.0f - (*paintWetness)) * pPoint->e_color[3]; + wetness = (1.0f - paintWetness) * pPoint->e_color[3]; CLAMP_MAX(pPoint->wetness, wetness); } } /* applies given brush intersection value for wave surface */ -static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, DynamicPaintBrushSettings *brush, float isect_height) +static void dynamicPaint_mixWaveHeight( + PaintWavePoint *wPoint, const DynamicPaintBrushSettings *brush, float isect_height) { - float isect_change = isect_height - wPoint->brush_isect; - int hit = 0; + const float isect_change = isect_height - wPoint->brush_isect; + const float wave_factor = brush->wave_factor; + bool hit = false; + /* intersection marked regardless of brush type or hit */ wPoint->brush_isect = isect_height; wPoint->state = DPAINT_WAVE_ISECT_CHANGED; - isect_height *= brush->wave_factor; + isect_height *= wave_factor; /* determine hit depending on wave_factor */ - if (brush->wave_factor > 0.0f && wPoint->height > isect_height) - hit = 1; - else if (brush->wave_factor < 0.0f && wPoint->height < isect_height) - hit = 1; + if (wave_factor > 0.0f && wPoint->height > isect_height) + hit = true; + else if (wave_factor < 0.0f && wPoint->height < isect_height) + hit = true; if (hit) { - if (brush->wave_type == MOD_DPAINT_WAVEB_DEPTH) { - wPoint->height = isect_height; - wPoint->state = DPAINT_WAVE_OBSTACLE; - wPoint->velocity = 0.0f; - } - else if (brush->wave_type == MOD_DPAINT_WAVEB_FORCE) - wPoint->velocity = isect_height; - else if (brush->wave_type == MOD_DPAINT_WAVEB_REFLECT) - wPoint->state = DPAINT_WAVE_REFLECT_ONLY; - else if (brush->wave_type == MOD_DPAINT_WAVEB_CHANGE) { - if (isect_change < 0.0f) - wPoint->height += isect_change * brush->wave_factor; + switch (brush->wave_type) { + case MOD_DPAINT_WAVEB_DEPTH: + wPoint->height = isect_height; + wPoint->state = DPAINT_WAVE_OBSTACLE; + wPoint->velocity = 0.0f; + break; + case MOD_DPAINT_WAVEB_FORCE: + wPoint->velocity = isect_height; + break; + case MOD_DPAINT_WAVEB_REFLECT: + wPoint->state = DPAINT_WAVE_REFLECT_ONLY; + break; + case MOD_DPAINT_WAVEB_CHANGE: + if (isect_change < 0.0f) + wPoint->height += isect_change * wave_factor; + break; + default: + BLI_assert(0); + break; } } } @@ -2903,8 +3171,9 @@ static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, DynamicPaintBrush /* * add brush results to the surface data depending on surface type */ -static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned int index, DynamicPaintBrushSettings *brush, - float paint[3], float influence, float depth, float vel_factor, float timescale) +static void dynamicPaint_updatePointData( + const DynamicPaintSurface *surface, const int index, const DynamicPaintBrushSettings *brush, + float paint[3], float influence, float depth, float vel_factor, const float timescale) { PaintSurfaceData *sData = surface->data; float strength; @@ -2924,9 +3193,7 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned if (do_colorband(brush->vel_ramp, vel_factor, coba_res)) { if (brush->flags & MOD_DPAINT_VELOCITY_COLOR) { - paint[0] = coba_res[0]; - paint[1] = coba_res[1]; - paint[2] = coba_res[2]; + copy_v3_v3(paint, coba_res); } if (brush->flags & MOD_DPAINT_VELOCITY_ALPHA) strength *= coba_res[3]; @@ -2937,12 +3204,10 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned /* mix paint surface */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - float paintWetness = brush->wetness * strength; float paintAlpha = strength; - dynamicPaint_mixPaintColors(surface, index, brush->flags, paint, &paintAlpha, &paintWetness, ×cale); - + dynamicPaint_mixPaintColors(surface, index, brush->flags, paint, paintAlpha, paintWetness, timescale); } /* displace surface */ else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { @@ -2957,10 +3222,10 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned if (brush->flags & MOD_DPAINT_ERASE) { value[index] *= (1.0f - strength); - if (value[index] < 0.0f) value[index] = 0.0f; + CLAMP_MIN(value[index], 0.0f); } else { - if (value[index] < depth) value[index] = depth; + CLAMP_MIN(value[index], depth); } } /* vertex weight group surface */ @@ -2969,10 +3234,10 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned if (brush->flags & MOD_DPAINT_ERASE) { value[index] *= (1.0f - strength); - if (value[index] < 0.0f) value[index] = 0.0f; + CLAMP_MIN(value[index], 0.0f); } else { - if (value[index] < strength) value[index] = strength; + CLAMP_MIN(value[index], strength); } } /* wave surface */ @@ -2981,8 +3246,7 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned CLAMP(depth, 0.0f - brush->wave_clamp, brush->wave_clamp); } - dynamicPaint_mixWaveHeight(&((PaintWavePoint *)sData->type_data)[index], - brush, 0.0f - depth); + dynamicPaint_mixWaveHeight(&((PaintWavePoint *)sData->type_data)[index], brush, 0.0f - depth); } /* doing velocity based painting */ @@ -2992,19 +3256,57 @@ static void dynamicPaint_updatePointData(DynamicPaintSurface *surface, unsigned } /* checks whether surface and brush bounds intersect depending on brush type */ -static int meshBrush_boundsIntersect(Bounds3D *b1, Bounds3D *b2, DynamicPaintBrushSettings *brush, float brush_radius) +static bool meshBrush_boundsIntersect(Bounds3D *b1, Bounds3D *b2, DynamicPaintBrushSettings *brush, float brush_radius) { if (brush->collision == MOD_DPAINT_COL_VOLUME) return boundsIntersect(b1, b2); else if (brush->collision == MOD_DPAINT_COL_DIST || brush->collision == MOD_DPAINT_COL_VOLDIST) return boundsIntersectDist(b1, b2, brush_radius); - else return 1; + return true; } /* calculate velocity for mesh vertices */ -static void dynamicPaint_brushMeshCalculateVelocity(Scene *scene, Object *ob, DynamicPaintBrushSettings *brush, Vec3f **brushVel, float timescale) +typedef struct DynamicPaintBrushVelocityData { + Vec3f *brush_vel; + + const MVert *mvert_p; + const MVert *mvert_c; + + float (*obmat)[4]; + float (*prev_obmat)[4]; + + const float timescale; +} DynamicPaintBrushVelocityData; + +static void dynamic_paint_brush_velocity_compute_cb(void *userdata, const int i) +{ + const DynamicPaintBrushVelocityData *data = userdata; + + Vec3f *brush_vel = data->brush_vel; + + const MVert *mvert_p = data->mvert_p; + const MVert *mvert_c = data->mvert_c; + + float (*obmat)[4] = data->obmat; + float (*prev_obmat)[4] = data->prev_obmat; + + const float timescale = data->timescale; + + float p1[3], p2[3]; + + copy_v3_v3(p1, mvert_p[i].co); + mul_m4_v3(prev_obmat, p1); + + copy_v3_v3(p2, mvert_c[i].co); + mul_m4_v3(obmat, p2); + + sub_v3_v3v3(brush_vel[i].v, p2, p1); + mul_v3_fl(brush_vel[i].v, 1.0f / timescale); +} + +static void dynamicPaint_brushMeshCalculateVelocity( + Scene *scene, Object *ob, DynamicPaintBrushSettings *brush, Vec3f **brushVel, float timescale) { - int i; float prev_obmat[4][4]; DerivedMesh *dm_p, *dm_c; MVert *mvert_p, *mvert_c; @@ -3024,7 +3326,8 @@ static void dynamicPaint_brushMeshCalculateVelocity(Scene *scene, Object *ob, Dy scene->r.cfra = prev_fra; scene->r.subframe = prev_sfra; - BKE_object_modifier_update_subframe(scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe( + scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); dm_p = CDDM_copy(brush->dm); numOfVerts_p = dm_p->getNumVerts(dm_p); mvert_p = dm_p->getVertArray(dm_p); @@ -3034,33 +3337,27 @@ static void dynamicPaint_brushMeshCalculateVelocity(Scene *scene, Object *ob, Dy scene->r.cfra = cur_fra; scene->r.subframe = cur_sfra; - BKE_object_modifier_update_subframe(scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe( + scene, ob, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); dm_c = brush->dm; numOfVerts_c = dm_c->getNumVerts(dm_c); mvert_c = dm_p->getVertArray(dm_c); (*brushVel) = (struct Vec3f *) MEM_mallocN(numOfVerts_c * sizeof(Vec3f), "Dynamic Paint brush velocity"); - if (!(*brushVel)) return; + if (!(*brushVel)) + return; - /* if mesh is constructive -> num of verts has changed, - * only use current frame derived mesh */ + /* if mesh is constructive -> num of verts has changed, only use current frame derived mesh */ if (numOfVerts_p != numOfVerts_c) mvert_p = mvert_c; /* calculate speed */ -#pragma omp parallel for schedule(static) - for (i = 0; i < numOfVerts_c; i++) { - float p1[3], p2[3]; - - copy_v3_v3(p1, mvert_p[i].co); - mul_m4_v3(prev_obmat, p1); - - copy_v3_v3(p2, mvert_c[i].co); - mul_m4_v3(ob->obmat, p2); - - sub_v3_v3v3((*brushVel)[i].v, p2, p1); - mul_v3_fl((*brushVel)[i].v, 1.0f / timescale); - } + DynamicPaintBrushVelocityData data = { + .brush_vel = *brushVel, + .mvert_p = mvert_p, .mvert_c = mvert_c, .obmat = ob->obmat, .prev_obmat = prev_obmat, + .timescale = timescale, + }; + BLI_task_parallel_range(0, numOfVerts_c, &data, dynamic_paint_brush_velocity_compute_cb, numOfVerts_c > 10000); dm_p->release(dm_p); } @@ -3084,13 +3381,15 @@ static void dynamicPaint_brushObjectCalculateVelocity(Scene *scene, Object *ob, /* previous frame dm */ scene->r.cfra = prev_fra; scene->r.subframe = prev_sfra; - BKE_object_modifier_update_subframe(scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe( + scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); copy_m4_m4(prev_obmat, ob->obmat); /* current frame dm */ scene->r.cfra = cur_fra; scene->r.subframe = cur_sfra; - BKE_object_modifier_update_subframe(scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe( + scene, ob, false, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); /* calculate speed */ mul_m4_v3(prev_obmat, prev_loc); @@ -3100,9 +3399,365 @@ static void dynamicPaint_brushObjectCalculateVelocity(Scene *scene, Object *ob, mul_v3_fl(brushVel->v, 1.0f / timescale); } +typedef struct DynamicPaintPaintData { + const DynamicPaintSurface *surface; + const DynamicPaintBrushSettings *brush; + Object *brushOb; + const BrushMaterials *bMats; + const Scene *scene; + const float timescale; + const int c_index; + + DerivedMesh *dm; + const MVert *mvert; + const MLoop *mloop; + const MLoopTri *mlooptri; + const float brush_radius; + const float *avg_brushNor; + const Vec3f *brushVelocity; + + const float solidradius; + + void *treeData; + + float *pointCoord; +} DynamicPaintPaintData; + /* * Paint a brush object mesh to the surface */ +static void dynamic_paint_paint_mesh_cell_point_cb_ex( + void *userdata, void *UNUSED(userdata_chunk), const int id, const int UNUSED(threadid)) +{ + const DynamicPaintPaintData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; + VolumeGrid *grid = bData->grid; + + const DynamicPaintBrushSettings *brush = data->brush; + Object *brushOb = data->brushOb; + const BrushMaterials *bMats = data->bMats; + + const Scene *scene = data->scene; + const float timescale = data->timescale; + const int c_index = data->c_index; + + DerivedMesh *dm = data->dm; + const MVert *mvert = data->mvert; + const MLoop *mloop = data->mloop; + const MLoopTri *mlooptri = data->mlooptri; + const float brush_radius = data->brush_radius; + const float *avg_brushNor = data->avg_brushNor; + const Vec3f *brushVelocity = data->brushVelocity; + + BVHTreeFromMesh *treeData = data->treeData; + + const int index = grid->t_index[grid->s_pos[c_index] + id]; + const int samples = bData->s_num[index]; + int ss; + float total_sample = (float)samples; + float brushStrength = 0.0f; /* brush influence factor */ + float depth = 0.0f; /* brush intersection depth */ + float velocity_val = 0.0f; + + float paintColor[3] = {0.0f}; + int numOfHits = 0; + + /* for image sequence anti-aliasing, use gaussian factors */ + if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + total_sample = gaussianTotal; + + /* Supersampling */ + for (ss = 0; ss < samples; ss++) { + float ray_start[3], ray_dir[3]; + float sample_factor = 0.0f; + float sampleStrength = 0.0f; + BVHTreeRayHit hit; + BVHTreeNearest nearest; + short hit_found = 0; + + /* volume sample */ + float volume_factor = 0.0f; + /* proximity sample */ + float proximity_factor = 0.0f; + float prox_colorband[4] = {0.0f}; + const bool inner_proximity = (brush->flags & MOD_DPAINT_INVERSE_PROX && + brush->collision == MOD_DPAINT_COL_VOLDIST); + + /* hit data */ + float hitCoord[3]; + int hitTri = -1; + + /* Supersampling factor */ + if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + sample_factor = gaussianFactors[ss]; + else + sample_factor = 1.0f; + + /* Get current sample position in world coordinates */ + copy_v3_v3(ray_start, bData->realCoord[bData->s_pos[index] + ss].v); + copy_v3_v3(ray_dir, bData->bNormal[index].invNorm); + + /* a simple hack to minimize chance of ray leaks at identical ray <-> edge locations */ + add_v3_fl(ray_start, 0.001f); + + hit.index = -1; + hit.dist = BVH_RAYCAST_DIST_MAX; + nearest.index = -1; + nearest.dist_sq = brush_radius * brush_radius; /* find_nearest uses squared distance */ + + /* Check volume collision */ + if (ELEM(brush->collision, MOD_DPAINT_COL_VOLUME, MOD_DPAINT_COL_VOLDIST)) { + BLI_bvhtree_ray_cast(treeData->tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); + if (hit.index != -1) { + /* We hit a triangle, now check if collision point normal is facing the point */ + + /* For optimization sake, hit point normal isn't calculated in ray cast loop */ + const int vtri[3] = { + mloop[mlooptri[hit.index].tri[0]].v, + mloop[mlooptri[hit.index].tri[1]].v, + mloop[mlooptri[hit.index].tri[2]].v, + }; + float dot; + + normal_tri_v3(hit.no, mvert[vtri[0]].co, mvert[vtri[1]].co, mvert[vtri[2]].co); + dot = dot_v3v3(ray_dir, hit.no); + + /* If ray and hit face normal are facing same direction + * hit point is inside a closed mesh. */ + if (dot >= 0.0f) { + const float dist = hit.dist; + const int f_index = hit.index; + + /* Also cast a ray in opposite direction to make sure + * point is at least surrounded by two brush faces */ + negate_v3(ray_dir); + hit.index = -1; + hit.dist = BVH_RAYCAST_DIST_MAX; + + BLI_bvhtree_ray_cast( + treeData->tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); + + if (hit.index != -1) { + /* Add factor on supersample filter */ + volume_factor = 1.0f; + hit_found = HIT_VOLUME; + + /* Mark hit info */ + madd_v3_v3v3fl(hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ + depth += dist * sample_factor; + hitTri = f_index; + } + } + } + } + + /* Check proximity collision */ + if (ELEM(brush->collision, MOD_DPAINT_COL_DIST, MOD_DPAINT_COL_VOLDIST) && + (!hit_found || (brush->flags & MOD_DPAINT_INVERSE_PROX))) + { + float proxDist = -1.0f; + float hitCo[3] = {0.0f, 0.0f, 0.0f}; + int tri = 0; + + /* if inverse prox and no hit found, skip this sample */ + if (inner_proximity && !hit_found) + continue; + + /* If pure distance proximity, find the nearest point on the mesh */ + if (!(brush->flags & MOD_DPAINT_PROX_PROJECT)) { + BLI_bvhtree_find_nearest(treeData->tree, ray_start, &nearest, mesh_tris_nearest_point_dp, treeData); + if (nearest.index != -1) { + proxDist = sqrtf(nearest.dist_sq); + copy_v3_v3(hitCo, nearest.co); + tri = nearest.index; + } + } + else { /* else cast a ray in defined projection direction */ + float proj_ray[3] = {0.0f}; + + if (brush->ray_dir == MOD_DPAINT_RAY_CANVAS) { + copy_v3_v3(proj_ray, bData->bNormal[index].invNorm); + negate_v3(proj_ray); + } + else if (brush->ray_dir == MOD_DPAINT_RAY_BRUSH_AVG) { + copy_v3_v3(proj_ray, avg_brushNor); + } + else { /* MOD_DPAINT_RAY_ZPLUS */ + proj_ray[2] = 1.0f; + } + hit.index = -1; + hit.dist = brush_radius; + + /* Do a face normal directional raycast, and use that distance */ + BLI_bvhtree_ray_cast( + treeData->tree, ray_start, proj_ray, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); + if (hit.index != -1) { + proxDist = hit.dist; + madd_v3_v3v3fl(hitCo, ray_start, proj_ray, hit.dist); /* Calculate final hit coordinates */ + tri = hit.index; + } + } + + /* If a hit was found, calculate required values */ + if (proxDist >= 0.0f && proxDist <= brush_radius) { + proximity_factor = proxDist / brush_radius; + CLAMP(proximity_factor, 0.0f, 1.0f); + if (!inner_proximity) + proximity_factor = 1.0f - proximity_factor; + + hit_found = HIT_PROXIMITY; + + /* if no volume hit, use prox point face info */ + if (hitTri == -1) { + copy_v3_v3(hitCoord, hitCo); + hitTri = tri; + } + } + } + + /* mix final sample strength depending on brush settings */ + if (hit_found) { + /* if "negate volume" enabled, negate all factors within volume*/ + if (brush->collision == MOD_DPAINT_COL_VOLDIST && + brush->flags & MOD_DPAINT_NEGATE_VOLUME) + { + volume_factor = 1.0f - volume_factor; + if (inner_proximity) + proximity_factor = 1.0f - proximity_factor; + } + + /* apply final sample depending on final hit type */ + if (hit_found == HIT_VOLUME) { + sampleStrength = volume_factor; + } + else if (hit_found == HIT_PROXIMITY) { + /* apply falloff curve to the proximity_factor */ + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && + do_colorband(brush->paint_ramp, (1.0f - proximity_factor), prox_colorband)) + { + proximity_factor = prox_colorband[3]; + } + else if (brush->proximity_falloff == MOD_DPAINT_PRFALL_CONSTANT) { + proximity_factor = (!inner_proximity || brush->flags & MOD_DPAINT_NEGATE_VOLUME) ? 1.0f : 0.0f; + } + /* apply sample */ + sampleStrength = proximity_factor; + } + + sampleStrength *= sample_factor; + } + else { + continue; + } + + /* velocity brush, only do on main sample */ + if (brush->flags & MOD_DPAINT_USES_VELOCITY && ss == 0 && brushVelocity) { + float weights[4]; + float brushPointVelocity[3]; + float velocity[3]; + + const int v1 = mloop[mlooptri[hitTri].tri[0]].v; + const int v2 = mloop[mlooptri[hitTri].tri[1]].v; + const int v3 = mloop[mlooptri[hitTri].tri[2]].v; + + /* calculate barycentric weights for hit point */ + interp_weights_face_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, NULL, hitCoord); + + /* simple check based on brush surface velocity, + * todo: perhaps implement something that handles volume movement as well */ + + /* interpolate vertex speed vectors to get hit point velocity */ + interp_v3_v3v3v3(brushPointVelocity, + brushVelocity[v1].v, + brushVelocity[v2].v, + brushVelocity[v3].v, weights); + + /* substract canvas point velocity */ + if (bData->velocity) { + sub_v3_v3v3(velocity, brushPointVelocity, bData->velocity[index].v); + } + else { + copy_v3_v3(velocity, brushPointVelocity); + } + velocity_val = normalize_v3(velocity); + + /* if brush has smudge enabled store brush velocity */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && + brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity) + { + copy_v3_v3(&bData->brush_velocity[index * 4], velocity); + bData->brush_velocity[index * 4 + 3] = velocity_val; + } + } + + /* + * Process hit color and alpha + */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + float sampleColor[3]; + float alpha_factor = 1.0f; + + sampleColor[0] = brush->r; + sampleColor[1] = brush->g; + sampleColor[2] = brush->b; + + /* Get material+textures color on hit point if required */ + if (brush_usesMaterial(brush, scene)) { + dynamicPaint_doMaterialTex(bMats, sampleColor, &alpha_factor, brushOb, + bData->realCoord[bData->s_pos[index] + ss].v, + hitCoord, hitTri, dm); + } + + /* Sample proximity colorband if required */ + if ((hit_found == HIT_PROXIMITY) && + (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP)) + { + if (!(brush->flags & MOD_DPAINT_RAMP_ALPHA)) { + sampleColor[0] = prox_colorband[0]; + sampleColor[1] = prox_colorband[1]; + sampleColor[2] = prox_colorband[2]; + } + } + + /* Add AA sample */ + paintColor[0] += sampleColor[0]; + paintColor[1] += sampleColor[1]; + paintColor[2] += sampleColor[2]; + sampleStrength *= alpha_factor; + numOfHits++; + } + + /* apply sample strength */ + brushStrength += sampleStrength; + } // end supersampling + + + /* if any sample was inside paint range */ + if (brushStrength > 0.0f || depth > 0.0f) { + /* apply supersampling results */ + if (samples > 1) { + brushStrength /= total_sample; + } + CLAMP(brushStrength, 0.0f, 1.0f); + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + /* Get final pixel color and alpha */ + paintColor[0] /= numOfHits; + paintColor[1] /= numOfHits; + paintColor[2] /= numOfHits; + } + /* get final object space depth */ + else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + depth /= bData->bNormal[index].normal_scale * total_sample; + } + + dynamicPaint_updatePointData(surface, index, brush, paintColor, brushStrength, depth, velocity_val, timescale); + } +} + static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, DynamicPaintBrushSettings *brush, Object *brushOb, @@ -3121,11 +3776,13 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, if (brush->flags & MOD_DPAINT_USES_VELOCITY) dynamicPaint_brushMeshCalculateVelocity(scene, brushOb, brush, &brushVelocity, timescale); - if (!brush->dm) return 0; + if (!brush->dm) + return 0; + { BVHTreeFromMesh treeData = {NULL}; float avg_brushNor[3] = {0.0f}; - float brush_radius = brush->paint_distance * surface->radius_scale; + const float brush_radius = brush->paint_distance * surface->radius_scale; int numOfVerts; int ii; Bounds3D mesh_bb = {0}; @@ -3172,305 +3829,25 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, /* loop through space partitioning grid */ for (c_index = 0; c_index < total_cells; c_index++) { - int id; - /* check grid cell bounding box */ - if (!grid->s_num[c_index] || !meshBrush_boundsIntersect(&grid->bounds[c_index], &mesh_bb, brush, brush_radius)) + if (!grid->s_num[c_index] || + !meshBrush_boundsIntersect(&grid->bounds[c_index], &mesh_bb, brush, brush_radius)) + { continue; + } /* loop through cell points and process brush */ -#pragma omp parallel for schedule(static) - for (id = 0; id < grid->s_num[c_index]; id++) { - int index = grid->t_index[grid->s_pos[c_index] + id]; - int ss, samples = bData->s_num[index]; - float total_sample = (float)samples; - float brushStrength = 0.0f; /* brush influence factor */ - float depth = 0.0f; /* brush intersection depth */ - float velocity_val = 0.0f; - - float paintColor[3] = {0.0f}; - int numOfHits = 0; - - /* for image sequence anti-aliasing, use gaussian factors */ - if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) - total_sample = gaussianTotal; - - /* Supersampling */ - for (ss = 0; ss < samples; ss++) { - - float ray_start[3], ray_dir[3]; - float sample_factor = 0.0f; - float sampleStrength = 0.0f; - BVHTreeRayHit hit; - BVHTreeNearest nearest; - short hit_found = 0; - - /* volume sample */ - float volume_factor = 0.0f; - /* proximity sample */ - float proximity_factor = 0.0f; - float prox_colorband[4] = {0.0f}; - int inner_proximity = (brush->flags & MOD_DPAINT_INVERSE_PROX && - brush->collision == MOD_DPAINT_COL_VOLDIST); - - /* hit data */ - float hitCoord[3]; - int hitTri = -1; - - /* Supersampling factor */ - if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) - sample_factor = gaussianFactors[ss]; - else - sample_factor = 1.0f; - - /* Get current sample position in world coordinates */ - copy_v3_v3(ray_start, bData->realCoord[bData->s_pos[index] + ss].v); - copy_v3_v3(ray_dir, bData->bNormal[index].invNorm); - - /* a simple hack to minimize chance of ray leaks at identical ray <-> edge locations */ - add_v3_fl(ray_start, 0.001f); - - hit.index = -1; - hit.dist = BVH_RAYCAST_DIST_MAX; - nearest.index = -1; - nearest.dist_sq = brush_radius * brush_radius; /* find_nearest uses squared distance */ - - /* Check volume collision */ - if (brush->collision == MOD_DPAINT_COL_VOLUME || brush->collision == MOD_DPAINT_COL_VOLDIST) - if (BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, &treeData) != -1) { - /* We hit a triangle, now check if collision point normal is facing the point */ - - /* For optimization sake, hit point normal isn't calculated in ray cast loop */ - const int vtri[3] = { - mloop[mlooptri[hit.index].tri[0]].v, - mloop[mlooptri[hit.index].tri[1]].v, - mloop[mlooptri[hit.index].tri[2]].v, - }; - float dot; - - normal_tri_v3(hit.no, mvert[vtri[0]].co, mvert[vtri[1]].co, mvert[vtri[2]].co); - dot = dot_v3v3(ray_dir, hit.no); - - /* If ray and hit face normal are facing same direction - * hit point is inside a closed mesh. */ - if (dot >= 0) { - float dist = hit.dist; - int f_index = hit.index; - - /* Also cast a ray in opposite direction to make sure - * point is at least surrounded by two brush faces */ - negate_v3(ray_dir); - hit.index = -1; - hit.dist = BVH_RAYCAST_DIST_MAX; - - BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, &treeData); - - if (hit.index != -1) { - /* Add factor on supersample filter */ - volume_factor = 1.0f; - hit_found = HIT_VOLUME; - - /* Mark hit info */ - madd_v3_v3v3fl(hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ - depth += dist * sample_factor; - hitTri = f_index; - } - } - } - - /* Check proximity collision */ - if ((brush->collision == MOD_DPAINT_COL_DIST || brush->collision == MOD_DPAINT_COL_VOLDIST) && - (!hit_found || (brush->flags & MOD_DPAINT_INVERSE_PROX))) - { - float proxDist = -1.0f; - float hitCo[3] = {0.0f, 0.0f, 0.0f}; - int tri = 0; - - /* if inverse prox and no hit found, skip this sample */ - if (inner_proximity && !hit_found) continue; - - /* If pure distance proximity, find the nearest point on the mesh */ - if (!(brush->flags & MOD_DPAINT_PROX_PROJECT)) { - if (BLI_bvhtree_find_nearest(treeData.tree, ray_start, &nearest, mesh_tris_nearest_point_dp, &treeData) != -1) { - proxDist = sqrtf(nearest.dist_sq); - copy_v3_v3(hitCo, nearest.co); - tri = nearest.index; - } - } - else { /* else cast a ray in defined projection direction */ - float proj_ray[3] = {0.0f}; - - if (brush->ray_dir == MOD_DPAINT_RAY_CANVAS) { - copy_v3_v3(proj_ray, bData->bNormal[index].invNorm); - negate_v3(proj_ray); - } - else if (brush->ray_dir == MOD_DPAINT_RAY_BRUSH_AVG) { - copy_v3_v3(proj_ray, avg_brushNor); - } - else { /* MOD_DPAINT_RAY_ZPLUS */ - proj_ray[2] = 1.0f; - } - hit.index = -1; - hit.dist = brush_radius; - - /* Do a face normal directional raycast, and use that distance */ - if (BLI_bvhtree_ray_cast(treeData.tree, ray_start, proj_ray, 0.0f, &hit, mesh_tris_spherecast_dp, &treeData) != -1) { - proxDist = hit.dist; - madd_v3_v3v3fl(hitCo, ray_start, proj_ray, hit.dist); /* Calculate final hit coordinates */ - tri = hit.index; - } - } - - /* If a hit was found, calculate required values */ - if (proxDist >= 0.0f && proxDist <= brush_radius) { - proximity_factor = proxDist / brush_radius; - CLAMP(proximity_factor, 0.0f, 1.0f); - if (!inner_proximity) - proximity_factor = 1.0f - proximity_factor; - - hit_found = HIT_PROXIMITY; - - /* if no volume hit, use prox point face info */ - if (hitTri == -1) { - copy_v3_v3(hitCoord, hitCo); - hitTri = tri; - } - } - } - - /* mix final sample strength depending on brush settings */ - if (hit_found) { - /* if "negate volume" enabled, negate all factors within volume*/ - if (brush->collision == MOD_DPAINT_COL_VOLDIST && brush->flags & MOD_DPAINT_NEGATE_VOLUME) { - volume_factor = 1.0f - volume_factor; - if (inner_proximity) - proximity_factor = 1.0f - proximity_factor; - } - - /* apply final sample depending on final hit type */ - if (hit_found == HIT_VOLUME) { - sampleStrength = volume_factor; - } - else if (hit_found == HIT_PROXIMITY) { - /* apply falloff curve to the proximity_factor */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && do_colorband(brush->paint_ramp, (1.0f - proximity_factor), prox_colorband)) - proximity_factor = prox_colorband[3]; - else if (brush->proximity_falloff == MOD_DPAINT_PRFALL_CONSTANT) - proximity_factor = (!inner_proximity || brush->flags & MOD_DPAINT_NEGATE_VOLUME) ? 1.0f : 0.0f; - /* apply sample */ - sampleStrength = proximity_factor; - } - - sampleStrength *= sample_factor; - } - else { - continue; - } - - /* velocity brush, only do on main sample */ - if (brush->flags & MOD_DPAINT_USES_VELOCITY && ss == 0 && brushVelocity) { - int v1, v2, v3; - float weights[4]; - float brushPointVelocity[3]; - float velocity[3]; - - v1 = mloop[mlooptri[hitTri].tri[0]].v; - v2 = mloop[mlooptri[hitTri].tri[1]].v; - v3 = mloop[mlooptri[hitTri].tri[2]].v; - - /* calculate barycentric weights for hit point */ - interp_weights_face_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, NULL, hitCoord); - - /* simple check based on brush surface velocity, - * todo: perhaps implement something that handles volume movement as well */ - - /* interpolate vertex speed vectors to get hit point velocity */ - interp_v3_v3v3v3(brushPointVelocity, - brushVelocity[v1].v, - brushVelocity[v2].v, - brushVelocity[v3].v, weights); - - /* substract canvas point velocity */ - if (bData->velocity) { - sub_v3_v3v3(velocity, brushPointVelocity, bData->velocity[index].v); - } - else { - copy_v3_v3(velocity, brushPointVelocity); - } - velocity_val = len_v3(velocity); - - /* if brush has smudge enabled store brush velocity */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && - brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity) - { - copy_v3_v3(&bData->brush_velocity[index * 4], velocity); - mul_v3_fl(&bData->brush_velocity[index * 4], 1.0f / velocity_val); - bData->brush_velocity[index * 4 + 3] = velocity_val; - } - } - - /* - * Process hit color and alpha - */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - float sampleColor[3]; - float alpha_factor = 1.0f; - - sampleColor[0] = brush->r; - sampleColor[1] = brush->g; - sampleColor[2] = brush->b; - - /* Get material+textures color on hit point if required */ - if (brush_usesMaterial(brush, scene)) - dynamicPaint_doMaterialTex(bMats, sampleColor, &alpha_factor, brushOb, bData->realCoord[bData->s_pos[index] + ss].v, hitCoord, hitTri, brush->dm); - - /* Sample proximity colorband if required */ - if ((hit_found == HIT_PROXIMITY) && (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP)) { - if (!(brush->flags & MOD_DPAINT_RAMP_ALPHA)) { - sampleColor[0] = prox_colorband[0]; - sampleColor[1] = prox_colorband[1]; - sampleColor[2] = prox_colorband[2]; - } - } - - /* Add AA sample */ - paintColor[0] += sampleColor[0]; - paintColor[1] += sampleColor[1]; - paintColor[2] += sampleColor[2]; - sampleStrength *= alpha_factor; - numOfHits++; - } - - /* apply sample strength */ - brushStrength += sampleStrength; - } // end supersampling - - - /* if any sample was inside paint range */ - if (brushStrength > 0.0f || depth > 0.0f) { - - /* apply supersampling results */ - if (samples > 1) { - brushStrength /= total_sample; - } - CLAMP(brushStrength, 0.0f, 1.0f); - - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - /* Get final pixel color and alpha */ - paintColor[0] /= numOfHits; - paintColor[1] /= numOfHits; - paintColor[2] /= numOfHits; - } - /* get final object space depth */ - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) - { - depth /= bData->bNormal[index].normal_scale * total_sample; - } - - dynamicPaint_updatePointData(surface, index, brush, paintColor, brushStrength, depth, velocity_val, timescale); - } - } + DynamicPaintPaintData data = { + .surface = surface, + .brush = brush, .brushOb = brushOb, .bMats = bMats, + .scene = scene, .timescale = timescale, .c_index = c_index, + .dm = dm, .mvert = mvert, .mloop = mloop, .mlooptri = mlooptri, + .brush_radius = brush_radius, .avg_brushNor = avg_brushNor, .brushVelocity = brushVelocity, + .treeData = &treeData + }; + BLI_task_parallel_range_ex(0, grid->s_num[c_index], &data, NULL, 0, + dynamic_paint_paint_mesh_cell_point_cb_ex, + grid->s_num[c_index] > 250, true); } } } @@ -3488,110 +3865,142 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, } /* paint a single point of defined proximity radius to the surface */ -static int dynamicPaint_paintSinglePoint(DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush, - Object *brushOb, BrushMaterials *bMats, Scene *scene, float timescale) +static void dynamic_paint_paint_single_point_cb_ex( + void *userdata, void *UNUSED(userdata_chunk), const int index, const int UNUSED(threadid)) { - int index; - float brush_radius = brush->paint_distance * surface->radius_scale; - PaintSurfaceData *sData = surface->data; - PaintBakeData *bData = sData->bData; - Vec3f brushVel; + const DynamicPaintPaintData *data = userdata; - if (brush->flags & MOD_DPAINT_USES_VELOCITY) - dynamicPaint_brushObjectCalculateVelocity(scene, brushOb, &brushVel, timescale); + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; - /* - * Loop through every surface point - */ -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - float distance = len_v3v3(pointCoord, bData->realCoord[bData->s_pos[index]].v); - float colorband[4] = {0.0f}; - float strength; + const DynamicPaintBrushSettings *brush = data->brush; + Object *brushOb = data->brushOb; + const BrushMaterials *bMats = data->bMats; - if (distance > brush_radius) continue; + const Scene *scene = data->scene; + const float timescale = data->timescale; - /* Smooth range or color ramp */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH || - brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP) - { - strength = 1.0f - distance / brush_radius; - CLAMP(strength, 0.0f, 1.0f); - } - else { - strength = 1.0f; - } + const MVert *mvert = data->mvert; + const float brush_radius = data->brush_radius; + const Vec3f *brushVelocity = data->brushVelocity; - if (strength >= 0.001f) { - float paintColor[3] = {0.0f}; - float depth = 0.0f; - float velocity_val = 0.0f; + float *pointCoord = data->pointCoord; - /* material */ - if (brush_usesMaterial(brush, scene)) { - float alpha_factor = 1.0f; - float hit_coord[3]; - MVert *mvert = brush->dm->getVertArray(brush->dm); - /* use dummy coord of first vertex */ - copy_v3_v3(hit_coord, mvert[0].co); - mul_m4_v3(brushOb->obmat, hit_coord); - - dynamicPaint_doMaterialTex(bMats, paintColor, &alpha_factor, brushOb, bData->realCoord[bData->s_pos[index]].v, hit_coord, 0, brush->dm); - } + const float distance = len_v3v3(pointCoord, bData->realCoord[bData->s_pos[index]].v); + float colorband[4] = {0.0f}; + float strength; - /* color ramp */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && do_colorband(brush->paint_ramp, (1.0f - strength), colorband)) - strength = colorband[3]; + if (distance > brush_radius) + return; - if (brush->flags & MOD_DPAINT_USES_VELOCITY) { - float velocity[3]; + /* Smooth range or color ramp */ + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_SMOOTH || + brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP) + { + strength = 1.0f - distance / brush_radius; + CLAMP(strength, 0.0f, 1.0f); + } + else { + strength = 1.0f; + } - /* substract canvas point velocity */ - if (bData->velocity) { - sub_v3_v3v3(velocity, brushVel.v, bData->velocity[index].v); - } - else { - copy_v3_v3(velocity, brushVel.v); - } - velocity_val = len_v3(velocity); + if (strength >= 0.001f) { + float paintColor[3] = {0.0f}; + float depth = 0.0f; + float velocity_val = 0.0f; - /* store brush velocity for smudge */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && - brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity) - { - copy_v3_v3(&bData->brush_velocity[index * 4], velocity); - mul_v3_fl(&bData->brush_velocity[index * 4], 1.0f / velocity_val); - bData->brush_velocity[index * 4 + 3] = velocity_val; - } + /* material */ + if (brush_usesMaterial(brush, scene)) { + float alpha_factor = 1.0f; + float hit_coord[3]; + /* use dummy coord of first vertex */ + mul_v3_m4v3(hit_coord, brushOb->obmat, mvert[0].co); + + dynamicPaint_doMaterialTex(bMats, paintColor, &alpha_factor, brushOb, + bData->realCoord[bData->s_pos[index]].v, hit_coord, 0, brush->dm); + } + + /* color ramp */ + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && + do_colorband(brush->paint_ramp, (1.0f - strength), colorband)) + { + strength = colorband[3]; + } + + if (brush->flags & MOD_DPAINT_USES_VELOCITY) { + float velocity[3]; + + /* substract canvas point velocity */ + if (bData->velocity) { + sub_v3_v3v3(velocity, brushVelocity->v, bData->velocity[index].v); + } + else { + copy_v3_v3(velocity, brushVelocity->v); } + velocity_val = len_v3(velocity); - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && - !(brush->flags & MOD_DPAINT_RAMP_ALPHA)) - { - paintColor[0] = colorband[0]; - paintColor[1] = colorband[1]; - paintColor[2] = colorband[2]; - } - else { - if (!brush_usesMaterial(brush, scene)) { - paintColor[0] = brush->r; - paintColor[1] = brush->g; - paintColor[2] = brush->b; - } - } + /* store brush velocity for smudge */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && + brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity) + { + mul_v3_v3fl(&bData->brush_velocity[index * 4], velocity, 1.0f / velocity_val); + bData->brush_velocity[index * 4 + 3] = velocity_val; } - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) + } + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && + !(brush->flags & MOD_DPAINT_RAMP_ALPHA)) { - /* get displace depth */ - float disp_intersect = (1.0f - sqrtf((brush_radius - distance) / brush_radius)) * brush_radius; - depth = (brush_radius - disp_intersect) / bData->bNormal[index].normal_scale; - if (depth < 0.0f) depth = 0.0f; + paintColor[0] = colorband[0]; + paintColor[1] = colorband[1]; + paintColor[2] = colorband[2]; + } + else { + if (!brush_usesMaterial(brush, scene)) { + paintColor[0] = brush->r; + paintColor[1] = brush->g; + paintColor[2] = brush->b; + } } - dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); } + else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + /* get displace depth */ + const float disp_intersect = (1.0f - sqrtf((brush_radius - distance) / brush_radius)) * brush_radius; + depth = max_ff(0.0f, (brush_radius - disp_intersect) / bData->bNormal[index].normal_scale); + } + dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); } +} + +static int dynamicPaint_paintSinglePoint( + DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush, + Object *brushOb, BrushMaterials *bMats, Scene *scene, float timescale) +{ + PaintSurfaceData *sData = surface->data; + float brush_radius = brush->paint_distance * surface->radius_scale; + Vec3f brushVel; + + if (brush->flags & MOD_DPAINT_USES_VELOCITY) + dynamicPaint_brushObjectCalculateVelocity(scene, brushOb, &brushVel, timescale); + + const MVert *mvert = brush->dm->getVertArray(brush->dm); + + /* + * Loop through every surface point + */ + DynamicPaintPaintData data = { + .surface = surface, + .brush = brush, .brushOb = brushOb, .bMats = bMats, + .scene = scene, .timescale = timescale, + .mvert = mvert, + .brush_radius = brush_radius, .brushVelocity = &brushVel, + .pointCoord = pointCoord, + }; + BLI_task_parallel_range_ex(0, sData->total_points, &data, NULL, 0, + dynamic_paint_paint_single_point_cb_ex, + sData->total_points > 1000, true); return 1; } @@ -3602,57 +4011,68 @@ static int dynamicPaint_paintSinglePoint(DynamicPaintSurface *surface, float *po /* * Calculate current frame distances and directions for adjacency data */ -static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, int force_init) + +static void dynamic_paint_prepare_adjacency_cb(void *userdata, const int index) +{ + PaintSurfaceData *sData = userdata; + PaintBakeData *bData = sData->bData; + BakeAdjPoint *bNeighs = bData->bNeighs; + PaintAdjData *adj_data = sData->adj_data; + Vec3f *realCoord = bData->realCoord; + + const int num_neighs = adj_data->n_num[index]; + + for (int i = 0; i < num_neighs; i++) { + const int n_index = adj_data->n_index[index] + i; + const int t_index = adj_data->n_target[n_index]; + + /* dir vec */ + sub_v3_v3v3(bNeighs[n_index].dir, realCoord[bData->s_pos[t_index]].v, realCoord[bData->s_pos[index]].v); + /* dist */ + bNeighs[n_index].dist = normalize_v3(bNeighs[n_index].dir); + } +} + +static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, const bool force_init) { PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; BakeAdjPoint *bNeighs; PaintAdjData *adj_data = sData->adj_data; - Vec3f *realCoord = bData->realCoord; - int index; - if ((!surface_usesAdjDistance(surface) && !force_init) || !sData->adj_data) return; + int index; - if (bData->bNeighs) MEM_freeN(bData->bNeighs); - bNeighs = bData->bNeighs = MEM_mallocN(sData->adj_data->total_targets * sizeof(struct BakeAdjPoint), "PaintEffectBake"); - if (!bNeighs) return; + if ((!surface_usesAdjDistance(surface) && !force_init) || !sData->adj_data) + return; -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int i; - int numOfNeighs = adj_data->n_num[index]; + if (bData->bNeighs) + MEM_freeN(bData->bNeighs); + bNeighs = bData->bNeighs = MEM_mallocN(sData->adj_data->total_targets * sizeof(*bNeighs), "PaintEffectBake"); + if (!bNeighs) + return; - for (i = 0; i < numOfNeighs; i++) { - int n_index = adj_data->n_index[index] + i; - int t_index = adj_data->n_target[n_index]; - - /* dir vec */ - sub_v3_v3v3(bNeighs[n_index].dir, realCoord[bData->s_pos[t_index]].v, realCoord[bData->s_pos[index]].v); - /* dist */ - bNeighs[n_index].dist = len_v3(bNeighs[n_index].dir); - /* normalize dir */ - if (bNeighs[n_index].dist) mul_v3_fl(bNeighs[n_index].dir, 1.0f / bNeighs[n_index].dist); - } - } + BLI_task_parallel_range( + 0, sData->total_points, sData, dynamic_paint_prepare_adjacency_cb, sData->total_points > 1000); - /* calculate average values (single thread) */ - bData->average_dist = 0.0f; + /* calculate average values (single thread). + * Note: tried to put this in threaded callback (using _finalize feature), but gave ~30% slower result! */ + bData->average_dist = 0.0; for (index = 0; index < sData->total_points; index++) { - int i; int numOfNeighs = adj_data->n_num[index]; - for (i = 0; i < numOfNeighs; i++) { + for (int i = 0; i < numOfNeighs; i++) { bData->average_dist += (double)bNeighs[adj_data->n_index[index] + i].dist; } } - bData->average_dist /= adj_data->total_targets; + bData->average_dist /= adj_data->total_targets; } /* find two adjacency points (closest_id) and influence (closest_d) to move paint towards when affected by a force */ -static void surface_determineForceTargetPoints(PaintSurfaceData *sData, int index, float force[3], float closest_d[2], int closest_id[2]) +static void surface_determineForceTargetPoints( + const PaintSurfaceData *sData, const int index, const float force[3], float closest_d[2], int closest_id[2]) { BakeAdjPoint *bNeighs = sData->bData->bNeighs; - int numOfNeighs = sData->adj_data->n_num[index]; + const int numOfNeighs = sData->adj_data->n_num[index]; int i; closest_id[0] = closest_id[1] = -1; @@ -3660,35 +4080,42 @@ static void surface_determineForceTargetPoints(PaintSurfaceData *sData, int inde /* find closest neigh */ for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); + const int n_index = sData->adj_data->n_index[index] + i; + const float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); - if (dir_dot > closest_d[0] && dir_dot > 0.0f) { closest_d[0] = dir_dot; closest_id[0] = n_index; } + if (dir_dot > closest_d[0] && dir_dot > 0.0f) { + closest_d[0] = dir_dot; + closest_id[0] = n_index; + } } - if (closest_d[0] < 0.0f) return; + if (closest_d[0] < 0.0f) + return; /* find second closest neigh */ for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); - float closest_dot = dot_v3v3(bNeighs[n_index].dir, bNeighs[closest_id[0]].dir); + const int n_index = sData->adj_data->n_index[index] + i; - if (n_index == closest_id[0]) continue; + if (n_index == closest_id[0]) + continue; + + const float dir_dot = dot_v3v3(bNeighs[n_index].dir, force); + const float closest_dot = dot_v3v3(bNeighs[n_index].dir, bNeighs[closest_id[0]].dir); /* only accept neighbor at "other side" of the first one in relation to force dir * so make sure angle between this and closest neigh is greater than first angle */ if (dir_dot > closest_d[1] && closest_dot < closest_d[0] && dir_dot > 0.0f) { - closest_d[1] = dir_dot; closest_id[1] = n_index; + closest_d[1] = dir_dot; + closest_id[1] = n_index; } } - /* if two valid neighs found, calculate how force effect is divided - * evenly between them (so that d[0]+d[1] = 1.0)*/ + /* if two valid neighs found, calculate how force effect is divided evenly between them + * (so that d[0] + d[1] = 1.0) */ if (closest_id[1] != -1) { float force_proj[3]; float tangent[3]; - float neigh_diff = acosf(dot_v3v3(bNeighs[closest_id[0]].dir, bNeighs[closest_id[1]].dir)); + const float neigh_diff = acosf(dot_v3v3(bNeighs[closest_id[0]].dir, bNeighs[closest_id[1]].dir)); float force_intersect; float temp; @@ -3725,12 +4152,13 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus int index, steps, step; float eff_scale, max_velocity = 0.0f; - if (!sData->adj_data) return; + if (!sData->adj_data) + return; /* find max velocity */ for (index = 0; index < sData->total_points; index++) { float vel = bData->brush_velocity[index * 4 + 3]; - if (vel > max_velocity) max_velocity = vel; + CLAMP_MIN(max_velocity, vel); } steps = (int)ceil(max_velocity / bData->average_dist * timescale); @@ -3738,7 +4166,6 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus eff_scale = brush->smudge_strength / (float)steps * timescale; for (step = 0; step < steps; step++) { - for (index = 0; index < sData->total_points; index++) { int i; PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; @@ -3748,8 +4175,9 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus int closest_id[2]; float closest_d[2]; - if (!smudge_str) continue; - + if (!smudge_str) + continue; + /* get force affect points */ surface_determineForceTargetPoints(sData, index, &bData->brush_velocity[index * 4], closest_d, closest_id); @@ -3762,7 +4190,8 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus PaintPoint *ePoint = &((PaintPoint *)sData->type_data)[sData->adj_data->n_target[n_index]]; /* just skip if angle is too extreme */ - if (dir_dot <= 0.0f) continue; + if (dir_dot <= 0.0f) + continue; dir_factor = dir_dot * speed_scale; CLAMP_MAX(dir_factor, brush->smudge_strength); @@ -3781,11 +4210,83 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus } } +typedef struct DynamicPaintEffectData { + const DynamicPaintSurface *surface; + Scene *scene; + + float *force; + ListBase *effectors; + const void *prevPoint; + const float eff_scale; + + uint8_t *point_locks; + + const float wave_speed; + const float wave_scale; + const float wave_max_slope; + + const float dt; + const float min_dist; + const float damp_factor; + const bool reset_wave; +} DynamicPaintEffectData; + /* * Prepare data required by effects for current frame. * Returns number of steps required */ -static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *scene, Object *ob, float **force, float timescale) +static void dynamic_paint_prepare_effect_cb(void *userdata, const int index) +{ + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; + Vec3f *realCoord = bData->realCoord; + + Scene *scene = data->scene; + + float *force = data->force; + ListBase *effectors = data->effectors; + + float forc[3] = {0}; + float vel[3] = {0}; + + /* apply force fields */ + if (effectors) { + EffectedPoint epoint; + pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint); + epoint.vel_to_sec = 1.0f; + pdDoEffectors(effectors, NULL, surface->effector_weights, &epoint, forc, NULL); + } + + /* if global gravity is enabled, add it too */ + if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) + /* also divide by 10 to about match default grav + * with default force strength (1.0) */ + madd_v3_v3fl(forc, scene->physics_settings.gravity, + surface->effector_weights->global_gravity * surface->effector_weights->weight[0] / 10.f); + + /* add surface point velocity and acceleration if enabled */ + if (bData->velocity) { + if (surface->drip_vel) + madd_v3_v3fl(forc, bData->velocity[index].v, surface->drip_vel * (-1.0f)); + + /* acceleration */ + if (bData->prev_velocity && surface->drip_acc) { + float acc[3]; + copy_v3_v3(acc, bData->velocity[index].v); + sub_v3_v3(acc, bData->prev_velocity[index].v); + madd_v3_v3fl(forc, acc, surface->drip_acc * (-1.0f)); + } + } + + /* force strength, and normalize force vec */ + force[index * 4 + 3] = normalize_v3_v3(&force[index * 4], forc); +} + +static int dynamicPaint_prepareEffectStep( + DynamicPaintSurface *surface, Scene *scene, Object *ob, float **force, float timescale) { double average_force = 0.0f; float shrink_speed = 0.0f, spread_speed = 0.0f; @@ -3793,60 +4294,24 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s int steps; PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; - Vec3f *realCoord = bData->realCoord; - int index; /* Init force data if required */ if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { - float vel[3] = {0}; ListBase *effectors = pdInitEffectors(scene, ob, surface->effector_weights, true); /* allocate memory for force data (dir vector + strength) */ *force = MEM_mallocN(sData->total_points * 4 * sizeof(float), "PaintEffectForces"); if (*force) { -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - float forc[3] = {0}; - - /* apply force fields */ - if (effectors) { - EffectedPoint epoint; - pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint); - epoint.vel_to_sec = 1.0f; - pdDoEffectors(effectors, NULL, surface->effector_weights, &epoint, forc, NULL); - } - - /* if global gravity is enabled, add it too */ - if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) - /* also divide by 10 to about match default grav - * with default force strength (1.0) */ - madd_v3_v3fl(forc, scene->physics_settings.gravity, - surface->effector_weights->global_gravity * surface->effector_weights->weight[0] / 10.f); - - /* add surface point velocity and acceleration if enabled */ - if (bData->velocity) { - if (surface->drip_vel) - madd_v3_v3fl(forc, bData->velocity[index].v, surface->drip_vel * (-1.0f)); - - /* acceleration */ - if (bData->prev_velocity && surface->drip_acc) { - float acc[3]; - copy_v3_v3(acc, bData->velocity[index].v); - sub_v3_v3(acc, bData->prev_velocity[index].v); - madd_v3_v3fl(forc, acc, surface->drip_acc * (-1.0f)); - } - } - - /* force strength */ - (*force)[index * 4 + 3] = len_v3(forc); - /* normalize and copy */ - if ((*force)[index * 4 + 3]) mul_v3_fl(forc, 1.0f / (*force)[index * 4 + 3]); - copy_v3_v3(&((*force)[index * 4]), forc); - } + DynamicPaintEffectData data = { + .surface = surface, .scene = scene, + .force = *force, .effectors = effectors, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_prepare_effect_cb, sData->total_points > 1000); /* calculate average values (single thread) */ - for (index = 0; index < sData->total_points; index++) { + for (int index = 0; index < sData->total_points; index++) { average_force += (*force)[index * 4 + 3]; } average_force /= sData->total_points; @@ -3866,7 +4331,7 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s fastest_effect = max_fff(spread_speed, shrink_speed, average_force); avg_dist = bData->average_dist * CANVAS_REL_SIZE / getSurfaceDimension(sData); - steps = (int)ceil(1.5f * EFF_MOVEMENT_PER_FRAME * fastest_effect / avg_dist * timescale); + steps = (int)ceilf(1.5f * EFF_MOVEMENT_PER_FRAME * fastest_effect / avg_dist * timescale); CLAMP(steps, 1, 20); return steps; @@ -3875,166 +4340,337 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s /** * Processes active effect step. */ -static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force, PaintPoint *prevPoint, float timescale, float steps) +static void dynamic_paint_effect_spread_cb(void *userdata, const int index) { - PaintSurfaceData *sData = surface->data; + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + + const int numOfNeighs = sData->adj_data->n_num[index]; BakeAdjPoint *bNeighs = sData->bData->bNeighs; - float distance_scale = getSurfaceDimension(sData) / CANVAS_REL_SIZE; - int index; + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + const PaintPoint *prevPoint = data->prevPoint; + const float eff_scale = data->eff_scale; + + const int *n_index = sData->adj_data->n_index; + const int *n_target = sData->adj_data->n_target; + + /* Loop through neighboring points */ + for (int i = 0; i < numOfNeighs; i++) { + const int n_idx = n_index[index] + i; + float w_factor; + const PaintPoint *pPoint_prev = &prevPoint[n_target[n_idx]]; + const float speed_scale = (bNeighs[n_idx].dist < eff_scale) ? 1.0f : eff_scale / bNeighs[n_idx].dist; + const float color_mix = min_fff(pPoint_prev->wetness, pPoint->wetness, 1.0f) * 0.25f * surface->color_spread_speed; + + /* do color mixing */ + if (color_mix) + mixColors(pPoint->e_color, pPoint->e_color[3], pPoint_prev->e_color, pPoint_prev->e_color[3], color_mix); + + /* Only continue if surrounding point has higher wetness */ + if (pPoint_prev->wetness < pPoint->wetness || pPoint_prev->wetness < MIN_WETNESS) + continue; + + w_factor = 1.0f / numOfNeighs * min_ff(pPoint_prev->wetness, 1.0f) * speed_scale; + CLAMP(w_factor, 0.0f, 1.0f); + + /* mix new wetness and color */ + pPoint->wetness = (1.0f - w_factor) * pPoint->wetness + w_factor * pPoint_prev->wetness; + pPoint->e_color[3] = mixColors(pPoint->e_color, pPoint->e_color[3], + pPoint_prev->e_color, pPoint_prev->e_color[3], w_factor); + } +} + +static void dynamic_paint_effect_shrink_cb(void *userdata, const int index) +{ + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + + const int numOfNeighs = sData->adj_data->n_num[index]; + BakeAdjPoint *bNeighs = sData->bData->bNeighs; + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + const PaintPoint *prevPoint = data->prevPoint; + const float eff_scale = data->eff_scale; + float totalAlpha = 0.0f; + + const int *n_index = sData->adj_data->n_index; + const int *n_target = sData->adj_data->n_target; + + /* Loop through neighboring points */ + for (int i = 0; i < numOfNeighs; i++) { + const int n_idx = n_index[index] + i; + const float speed_scale = (bNeighs[n_idx].dist < eff_scale) ? 1.0f : eff_scale / bNeighs[n_idx].dist; + const PaintPoint *pPoint_prev = &prevPoint[n_target[n_idx]]; + float a_factor, ea_factor, w_factor; + + totalAlpha += pPoint_prev->e_color[3]; + + /* Check if neighboring point has lower alpha, + * if so, decrease this point's alpha as well*/ + if (pPoint->color[3] <= 0.0f && pPoint->e_color[3] <= 0.0f && pPoint->wetness <= 0.0f) + continue; + + /* decrease factor for dry paint alpha */ + a_factor = max_ff((1.0f - pPoint_prev->color[3]) / numOfNeighs * (pPoint->color[3] - pPoint_prev->color[3]) * speed_scale, 0.0f); + /* decrease factor for wet paint alpha */ + ea_factor = max_ff((1.0f - pPoint_prev->e_color[3]) / 8 * (pPoint->e_color[3] - pPoint_prev->e_color[3]) * speed_scale, 0.0f); + /* decrease factor for paint wetness */ + w_factor = max_ff((1.0f - pPoint_prev->wetness) / 8 * (pPoint->wetness - pPoint_prev->wetness) * speed_scale, 0.0f); + + pPoint->color[3] -= a_factor; + CLAMP_MIN(pPoint->color[3], 0.0f); + pPoint->e_color[3] -= ea_factor; + CLAMP_MIN(pPoint->e_color[3], 0.0f); + pPoint->wetness -= w_factor; + CLAMP_MIN(pPoint->wetness, 0.0f); + } +} + +static void dynamic_paint_effect_drip_cb(void *userdata, const int index) +{ + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + BakeAdjPoint *bNeighs = sData->bData->bNeighs; + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + const PaintPoint *prevPoint = data->prevPoint; + const PaintPoint *pPoint_prev = &prevPoint[index]; + const float *force = data->force; + const float eff_scale = data->eff_scale; + + const int *n_target = sData->adj_data->n_target; + + uint8_t *point_locks = data->point_locks; + + int closest_id[2]; + float closest_d[2]; + + /* adjust drip speed depending on wetness */ + float w_factor = pPoint_prev->wetness - 0.025f; + if (w_factor <= 0) + return; + CLAMP(w_factor, 0.0f, 1.0f); + + /* get force affect points */ + surface_determineForceTargetPoints(sData, index, &force[index * 4], closest_d, closest_id); + + /* Apply movement towards those two points */ + for (int i = 0; i < 2; i++) { + const int n_idx = closest_id[i]; + if (n_idx != -1 && closest_d[i] > 0.0f) { + const float dir_dot = closest_d[i]; + + /* just skip if angle is too extreme */ + if (dir_dot <= 0.0f) + continue; + + float dir_factor, a_factor; + const float speed_scale = eff_scale * force[index * 4 + 3] / bNeighs[n_idx].dist; + + const unsigned int n_trgt = (unsigned int)n_target[n_idx]; + + /* Sort of spinlock, but only for given ePoint. + * Since the odds a same ePoint is modified at the same time by several threads is very low, this is + * much more eficient than a global spin lock. */ + const unsigned int pointlock_idx = n_trgt / 8; + const uint8_t pointlock_bitmask = 1 << (n_trgt & 7); /* 7 == 0b111 */ + while (atomic_fetch_and_or_uint8(&point_locks[pointlock_idx], pointlock_bitmask) & pointlock_bitmask); + + PaintPoint *ePoint = &((PaintPoint *)sData->type_data)[n_trgt]; + const float e_wet = ePoint->wetness; + + dir_factor = min_ff(0.5f, dir_dot * min_ff(speed_scale, 1.0f) * w_factor); + + /* mix new wetness */ + ePoint->wetness += dir_factor; + CLAMP(ePoint->wetness, 0.0f, MAX_WETNESS); + + /* mix new color */ + a_factor = dir_factor / pPoint_prev->wetness; + CLAMP(a_factor, 0.0f, 1.0f); + mixColors(ePoint->e_color, ePoint->e_color[3], pPoint_prev->e_color, pPoint_prev->e_color[3], a_factor); + /* dripping is supposed to preserve alpha level */ + if (pPoint_prev->e_color[3] > ePoint->e_color[3]) { + ePoint->e_color[3] += a_factor * pPoint_prev->e_color[3]; + CLAMP_MAX(ePoint->e_color[3], pPoint_prev->e_color[3]); + } + + /* decrease paint wetness on current point */ + pPoint->wetness -= (ePoint->wetness - e_wet); + CLAMP(pPoint->wetness, 0.0f, MAX_WETNESS); + +#ifndef NDEBUG + uint8_t ret = atomic_fetch_and_and_uint8(&point_locks[pointlock_idx], ~pointlock_bitmask); + BLI_assert(ret & pointlock_bitmask); +#else + atomic_fetch_and_and_uint8(&point_locks[pointlock_idx], ~pointlock_bitmask); +#endif + } + } +} + +static void dynamicPaint_doEffectStep( + DynamicPaintSurface *surface, float *force, PaintPoint *prevPoint, float timescale, float steps) +{ + PaintSurfaceData *sData = surface->data; + + const float distance_scale = getSurfaceDimension(sData) / CANVAS_REL_SIZE; timescale /= steps; - if (!sData->adj_data) return; + if (!sData->adj_data) + return; /* * Spread Effect */ if (surface->effect & MOD_DPAINT_EFFECT_DO_SPREAD) { - float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->spread_speed * timescale; + const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->spread_speed * timescale; /* Copy current surface to the previous points array to read unmodified values */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(struct PaintPoint)); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int i; - int numOfNeighs = sData->adj_data->n_num[index]; - PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; - - /* Only reads values from the surface copy (prevPoint[]), - * so this one is thread safe */ - - /* Loop through neighboring points */ - for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float w_factor; - PaintPoint *ePoint = &prevPoint[sData->adj_data->n_target[n_index]]; - float speed_scale = (bNeighs[n_index].dist < eff_scale) ? 1.0f : eff_scale / bNeighs[n_index].dist; - float color_mix = (MIN3(ePoint->wetness, pPoint->wetness, 1.0f)) * 0.25f * surface->color_spread_speed; - - /* do color mixing */ - if (color_mix) mixColors(pPoint->e_color, pPoint->e_color[3], ePoint->e_color, ePoint->e_color[3], color_mix); - - /* Only continue if surrounding point has higher wetness */ - if (ePoint->wetness < pPoint->wetness || ePoint->wetness < MIN_WETNESS) continue; - - w_factor = 1.0f / numOfNeighs * MIN2(ePoint->wetness, 1.0f) * speed_scale; - CLAMP(w_factor, 0.0f, 1.0f); - - /* mix new wetness and color */ - pPoint->wetness = (1.0f - w_factor) * pPoint->wetness + w_factor * ePoint->wetness; - pPoint->e_color[3] = mixColors(pPoint->e_color, pPoint->e_color[3], ePoint->e_color, ePoint->e_color[3], w_factor); - } - } + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, .eff_scale = eff_scale, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_effect_spread_cb, sData->total_points > 1000); } /* * Shrink Effect */ if (surface->effect & MOD_DPAINT_EFFECT_DO_SHRINK) { - float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->shrink_speed * timescale; + const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->shrink_speed * timescale; /* Copy current surface to the previous points array to read unmodified values */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(struct PaintPoint)); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int i; - int numOfNeighs = sData->adj_data->n_num[index]; - float totalAlpha = 0.0f; - PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; - - for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float speed_scale = (bNeighs[n_index].dist < eff_scale) ? 1.0f : eff_scale / bNeighs[n_index].dist; - PaintPoint *ePoint = &prevPoint[sData->adj_data->n_target[n_index]]; - float a_factor, ea_factor, w_factor; - - totalAlpha += ePoint->e_color[3]; - - /* Check if neighboring point has lower alpha, - * if so, decrease this point's alpha as well*/ - if (pPoint->color[3] <= 0.0f && pPoint->e_color[3] <= 0.0f && pPoint->wetness <= 0.0f) continue; - - /* decrease factor for dry paint alpha */ - a_factor = (1.0f - ePoint->color[3]) / numOfNeighs * (pPoint->color[3] - ePoint->color[3]) * speed_scale; - CLAMP_MIN(a_factor, 0.0f); - /* decrease factor for wet paint alpha */ - ea_factor = (1.0f - ePoint->e_color[3]) / 8 * (pPoint->e_color[3] - ePoint->e_color[3]) * speed_scale; - CLAMP_MIN(ea_factor, 0.0f); - /* decrease factor for paint wetness */ - w_factor = (1.0f - ePoint->wetness) / 8 * (pPoint->wetness - ePoint->wetness) * speed_scale; - CLAMP_MIN(w_factor, 0.0f); - - pPoint->color[3] -= a_factor; - CLAMP_MIN(pPoint->color[3], 0.0f); - pPoint->e_color[3] -= ea_factor; - CLAMP_MIN(pPoint->e_color[3], 0.0f); - pPoint->wetness -= w_factor; - CLAMP_MIN(pPoint->wetness, 0.0f); - } - } + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, .eff_scale = eff_scale, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_effect_shrink_cb, sData->total_points > 1000); } /* * Drip Effect */ if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP && force) { - float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * timescale / 2.0f; + const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * timescale / 2.0f; + + /* Same as BLI_bitmask, but handled atomicaly as 'ePoint' locks. */ + const size_t point_locks_size = (sData->total_points / 8) + 1; + uint8_t *point_locks = MEM_callocN(sizeof(*point_locks) * point_locks_size, __func__); + /* Copy current surface to the previous points array to read unmodified values */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(struct PaintPoint)); - for (index = 0; index < sData->total_points; index++) { - int i; - PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; - PaintPoint *pPoint_prev = &prevPoint[index]; + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, + .eff_scale = eff_scale, .force = force, + .point_locks = point_locks, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_effect_drip_cb, sData->total_points > 1000); - int closest_id[2]; - float closest_d[2]; + MEM_freeN(point_locks); + } +} - /* adjust drip speed depending on wetness */ - float w_factor = pPoint_prev->wetness - 0.025f; - if (w_factor <= 0) continue; - CLAMP(w_factor, 0.0f, 1.0f); +static void dynamic_paint_wave_step_cb(void *userdata, const int index) +{ + const DynamicPaintEffectData *data = userdata; - /* get force affect points */ - surface_determineForceTargetPoints(sData, index, &force[index * 4], closest_d, closest_id); + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + BakeAdjPoint *bNeighs = sData->bData->bNeighs; + const PaintWavePoint *prevPoint = data->prevPoint; - /* Apply movement towards those two points */ - for (i = 0; i < 2; i++) { - int n_index = closest_id[i]; - if (n_index != -1 && closest_d[i] > 0.0f) { - float dir_dot = closest_d[i], dir_factor, a_factor; - float speed_scale = eff_scale * force[index * 4 + 3] / bNeighs[n_index].dist; - PaintPoint *ePoint = &((PaintPoint *)sData->type_data)[sData->adj_data->n_target[n_index]]; - float e_wet = ePoint->wetness; + const float wave_speed = data->wave_speed; + const float wave_scale = data->wave_scale; + const float wave_max_slope = data->wave_max_slope; - /* just skip if angle is too extreme */ - if (dir_dot <= 0.0f) continue; - - dir_factor = dir_dot * MIN2(speed_scale, 1.0f) * w_factor; - CLAMP_MAX(dir_factor, 0.5f); - - /* mix new wetness */ - ePoint->wetness += dir_factor; - CLAMP(ePoint->wetness, 0.0f, MAX_WETNESS); - - /* mix new color */ - a_factor = dir_factor / pPoint_prev->wetness; - CLAMP(a_factor, 0.0f, 1.0f); - mixColors(ePoint->e_color, ePoint->e_color[3], pPoint_prev->e_color, pPoint_prev->e_color[3], a_factor); - /* dripping is supposed to preserve alpha level */ - if (pPoint_prev->e_color[3] > ePoint->e_color[3]) { - ePoint->e_color[3] += a_factor * pPoint_prev->e_color[3]; - CLAMP_MAX(ePoint->e_color[3], pPoint_prev->e_color[3]); - } + const float dt = data->dt; + const float min_dist = data->min_dist; + const float damp_factor = data->damp_factor; - /* decrease paint wetness on current point */ - pPoint->wetness -= (ePoint->wetness - e_wet); - CLAMP(pPoint->wetness, 0.0f, MAX_WETNESS); - } - } + PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; + const int numOfNeighs = sData->adj_data->n_num[index]; + float force = 0.0f, avg_dist = 0.0f, avg_height = 0.0f, avg_n_height = 0.0f; + int numOfN = 0, numOfRN = 0; + + if (wPoint->state > 0) + return; + + const int *n_index = sData->adj_data->n_index; + const int *n_target = sData->adj_data->n_target; + const int *adj_flags = sData->adj_data->flags; + + /* calculate force from surrounding points */ + for (int i = 0; i < numOfNeighs; i++) { + const int n_idx = n_index[index] + i; + float dist = bNeighs[n_idx].dist * wave_scale; + const PaintWavePoint *tPoint = &prevPoint[n_target[n_idx]]; + + if (!dist || tPoint->state > 0) + continue; + + CLAMP_MIN(dist, min_dist); + avg_dist += dist; + numOfN++; + + /* count average height for edge points for open borders */ + if (!(adj_flags[n_target[n_idx]] & ADJ_ON_MESH_EDGE)) { + avg_n_height += tPoint->height; + numOfRN++; + } + + force += (tPoint->height - wPoint->height) / (dist * dist); + avg_height += tPoint->height; + } + avg_dist = (numOfN) ? avg_dist / numOfN : 0.0f; + + if (surface->flags & MOD_DPAINT_WAVE_OPEN_BORDERS && adj_flags[index] & ADJ_ON_MESH_EDGE) { + /* if open borders, apply a fake height to keep waves going on */ + avg_n_height = (numOfRN) ? avg_n_height / numOfRN : 0.0f; + wPoint->height = (dt * wave_speed * avg_n_height + wPoint->height * avg_dist) / + (avg_dist + dt * wave_speed); + } + /* else do wave eq */ + else { + /* add force towards zero height based on average dist */ + if (avg_dist) + force += (0.0f - wPoint->height) * surface->wave_spring / (avg_dist * avg_dist) / 2.0f; + + /* change point velocity */ + wPoint->velocity += force * dt * wave_speed * wave_speed; + /* damping */ + wPoint->velocity *= damp_factor; + /* and new height */ + wPoint->height += wPoint->velocity * dt; + + /* limit wave slope steepness */ + if (wave_max_slope && avg_dist) { + const float max_offset = wave_max_slope * avg_dist; + const float offset = (numOfN) ? (avg_height / numOfN - wPoint->height) : 0.0f; + if (offset > max_offset) + wPoint->height += offset - max_offset; + else if (offset < -max_offset) + wPoint->height += offset + max_offset; } } + + if (data->reset_wave) { + /* if there wasnt any brush intersection, clear isect height */ + if (wPoint->state == DPAINT_WAVE_NONE) { + wPoint->brush_isect = 0.0f; + } + wPoint->state = DPAINT_WAVE_NONE; + } } static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescale) @@ -4044,15 +4680,16 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal int index; int steps, ss; float dt, min_dist, damp_factor; - float wave_speed = surface->wave_speed; - float wave_max_slope = (surface->wave_smoothness >= 0.01f) ? (0.5f / surface->wave_smoothness) : 0.0f; + const float wave_speed = surface->wave_speed; + const float wave_max_slope = (surface->wave_smoothness >= 0.01f) ? (0.5f / surface->wave_smoothness) : 0.0f; double average_dist = 0.0f; const float canvas_size = getSurfaceDimension(sData); - float wave_scale = CANVAS_REL_SIZE / canvas_size; + const float wave_scale = CANVAS_REL_SIZE / canvas_size; /* allocate memory */ PaintWavePoint *prevPoint = MEM_mallocN(sData->total_points * sizeof(PaintWavePoint), "Temp previous points for wave simulation"); - if (!prevPoint) return; + if (!prevPoint) + return; /* calculate average neigh distance (single thread) */ for (index = 0; index < sData->total_points; index++) { @@ -4076,170 +4713,120 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal damp_factor = pow((1.0f - surface->wave_damping), timescale * surface->wave_timescale); for (ss = 0; ss < steps; ss++) { - /* copy previous frame data */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(PaintWavePoint)); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; - int numOfNeighs = sData->adj_data->n_num[index]; - float force = 0.0f, avg_dist = 0.0f, avg_height = 0.0f, avg_n_height = 0.0f; - int numOfN = 0, numOfRN = 0; - int i; - - if (wPoint->state > 0) continue; + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, + .wave_speed = wave_speed, .wave_scale = wave_scale, .wave_max_slope = wave_max_slope, + .dt = dt, .min_dist = min_dist, .damp_factor = damp_factor, .reset_wave = (ss == steps - 1), + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_wave_step_cb, sData->total_points > 1000); + } - /* calculate force from surrounding points */ - for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float dist = bNeighs[n_index].dist * wave_scale; - PaintWavePoint *tPoint = &prevPoint[sData->adj_data->n_target[n_index]]; + MEM_freeN(prevPoint); +} - if (!dist || tPoint->state > 0) continue; - if (dist < min_dist) dist = min_dist; - avg_dist += dist; - numOfN++; +/* Do dissolve and fading effects */ +static bool dynamic_paint_surface_needs_dry_dissolve(DynamicPaintSurface *surface) +{ + return (((surface->type == MOD_DPAINT_SURFACE_T_PAINT) && + (surface->flags & (MOD_DPAINT_USE_DRYING | MOD_DPAINT_DISSOLVE))) || + (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WEIGHT) && + (surface->flags & MOD_DPAINT_DISSOLVE))); +} - /* count average height for edge points for open borders */ - if (!(sData->adj_data->flags[sData->adj_data->n_target[n_index]] & ADJ_ON_MESH_EDGE)) { - avg_n_height += tPoint->height; - numOfRN++; - } +typedef struct DynamicPaintDissolveDryData { + const DynamicPaintSurface *surface; + const float timescale; +} DynamicPaintDissolveDryData; - force += (tPoint->height - wPoint->height) / (dist * dist); - avg_height += tPoint->height; - } - avg_dist = (numOfN) ? avg_dist / numOfN : 0.0f; +static void dynamic_paint_surface_pre_step_cb(void *userdata, const int index) +{ + const DynamicPaintDissolveDryData *data = userdata; - if (surface->flags & MOD_DPAINT_WAVE_OPEN_BORDERS && - sData->adj_data->flags[index] & ADJ_ON_MESH_EDGE) - { - /* if open borders, apply a fake height to keep waves going on */ - avg_n_height = (numOfRN) ? avg_n_height / numOfRN : 0.0f; - wPoint->height = (dt * wave_speed * avg_n_height + wPoint->height * avg_dist) / (avg_dist + dt * wave_speed); - } - /* else do wave eq */ - else { - /* add force towards zero height based on average dist */ - if (avg_dist) - force += (0.0f - wPoint->height) * surface->wave_spring / (avg_dist * avg_dist) / 2.0f; - - /* change point velocity */ - wPoint->velocity += force * dt * wave_speed * wave_speed; - /* damping */ - wPoint->velocity *= damp_factor; - /* and new height */ - wPoint->height += wPoint->velocity * dt; - - /* limit wave slope steepness */ - if (wave_max_slope && avg_dist) { - float max_offset = wave_max_slope * avg_dist; - float offset = (numOfN) ? (avg_height / numOfN - wPoint->height) : 0.0f; - if (offset > max_offset) wPoint->height += offset - max_offset; - if (offset < -max_offset) wPoint->height += offset + max_offset; - } - } - } - } + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const float timescale = data->timescale; - /* reset state */ -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; - /* if there wasnt any brush intersection, clear isect height */ - if (wPoint->state == DPAINT_WAVE_NONE) { - wPoint->brush_isect = 0.0f; - } - wPoint->state = DPAINT_WAVE_NONE; - } + /* Do drying dissolve effects */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + /* drying */ + if (surface->flags & MOD_DPAINT_USE_DRYING) { + if (pPoint->wetness >= MIN_WETNESS) { + int i; + float dry_ratio, f_color[4]; + float p_wetness = pPoint->wetness; + + value_dissolve(&pPoint->wetness, surface->dry_speed, timescale, + (surface->flags & MOD_DPAINT_DRY_LOG) != 0); + CLAMP_MIN(pPoint->wetness, 0.0f); - MEM_freeN(prevPoint); -} + if (pPoint->wetness < surface->color_dry_threshold) { + dry_ratio = pPoint->wetness / p_wetness; -/* Do dissolve and fading effects */ -static void dynamicPaint_surfacePreStep(DynamicPaintSurface *surface, float timescale) -{ - PaintSurfaceData *sData = surface->data; - int index; + /* + * Slowly "shift" paint from wet layer to dry layer as it drys: + */ + /* make sure alpha values are within proper range */ + CLAMP(pPoint->color[3], 0.0f, 1.0f); + CLAMP(pPoint->e_color[3], 0.0f, 1.0f); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - /* Do drying dissolve effects */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; - /* drying */ - if (surface->flags & MOD_DPAINT_USE_DRYING) { - if (pPoint->wetness >= MIN_WETNESS) { - int i; - float dry_ratio, f_color[4]; - float p_wetness = pPoint->wetness; - value_dissolve(&pPoint->wetness, surface->dry_speed, timescale, (surface->flags & MOD_DPAINT_DRY_LOG)); - if (pPoint->wetness < 0.0f) pPoint->wetness = 0.0f; - - if (pPoint->wetness < surface->color_dry_threshold) { - dry_ratio = pPoint->wetness / p_wetness; - - /* - * Slowly "shift" paint from wet layer to dry layer as it drys: - */ - /* make sure alpha values are within proper range */ - CLAMP(pPoint->color[3], 0.0f, 1.0f); - CLAMP(pPoint->e_color[3], 0.0f, 1.0f); - - /* get current final blended color of these layers */ - blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color); - /* reduce wet layer alpha by dry factor */ - pPoint->e_color[3] *= dry_ratio; - - /* now calculate new alpha for dry layer that keeps final blended color unchanged */ - pPoint->color[3] = (f_color[3] - pPoint->e_color[3]) / (1.0f - pPoint->e_color[3]); - /* for each rgb component, calculate a new dry layer color that keeps the final blend color - * with these new alpha values. (wet layer color doesnt change)*/ - if (pPoint->color[3]) { - for (i = 0; i < 3; i++) { - pPoint->color[i] = (f_color[i] * f_color[3] - pPoint->e_color[i] * pPoint->e_color[3]) / (pPoint->color[3] * (1.0f - pPoint->e_color[3])); - } + /* get current final blended color of these layers */ + blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color); + /* reduce wet layer alpha by dry factor */ + pPoint->e_color[3] *= dry_ratio; + + /* now calculate new alpha for dry layer that keeps final blended color unchanged */ + pPoint->color[3] = (f_color[3] - pPoint->e_color[3]) / (1.0f - pPoint->e_color[3]); + /* for each rgb component, calculate a new dry layer color that keeps the final blend color + * with these new alpha values. (wet layer color doesnt change)*/ + if (pPoint->color[3]) { + for (i = 0; i < 3; i++) { + pPoint->color[i] = (f_color[i] * f_color[3] - pPoint->e_color[i] * pPoint->e_color[3]) / + (pPoint->color[3] * (1.0f - pPoint->e_color[3])); } } - - pPoint->state = DPAINT_PAINT_WET; } - /* in case of just dryed paint, just mix it to the dry layer and mark it empty */ - else if (pPoint->state > 0) { - float f_color[4]; - blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color); - copy_v4_v4(pPoint->color, f_color); - /* clear wet layer */ - pPoint->wetness = 0.0f; - pPoint->e_color[3] = 0.0f; - pPoint->state = DPAINT_PAINT_DRY; - } - } - if (surface->flags & MOD_DPAINT_DISSOLVE) { - value_dissolve(&pPoint->color[3], surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); - CLAMP_MIN(pPoint->color[3], 0.0f); - - value_dissolve(&pPoint->e_color[3], surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); - CLAMP_MIN(pPoint->e_color[3], 0.0f); + pPoint->state = DPAINT_PAINT_WET; + } + /* in case of just dryed paint, just mix it to the dry layer and mark it empty */ + else if (pPoint->state > 0) { + float f_color[4]; + blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color); + copy_v4_v4(pPoint->color, f_color); + /* clear wet layer */ + pPoint->wetness = 0.0f; + pPoint->e_color[3] = 0.0f; + pPoint->state = DPAINT_PAINT_DRY; } } - /* dissolve for float types */ - else if (surface->flags & MOD_DPAINT_DISSOLVE && - (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)) - { - float *point = &((float *)sData->type_data)[index]; - /* log or linear */ - value_dissolve(point, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG)); - CLAMP_MIN(*point, 0.0f); + if (surface->flags & MOD_DPAINT_DISSOLVE) { + value_dissolve(&pPoint->color[3], surface->diss_speed, timescale, + (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0); + CLAMP_MIN(pPoint->color[3], 0.0f); + + value_dissolve(&pPoint->e_color[3], surface->diss_speed, timescale, + (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0); + CLAMP_MIN(pPoint->e_color[3], 0.0f); } } + /* dissolve for float types */ + else if (surface->flags & MOD_DPAINT_DISSOLVE && + (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)) + { + float *point = &((float *)sData->type_data)[index]; + /* log or linear */ + value_dissolve(point, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0); + CLAMP_MIN(*point, 0.0f); + } } -static int dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob) +static bool dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob) { PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; @@ -4248,67 +4835,159 @@ static int dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob int numOfVerts = dm->getNumVerts(dm); int i; - int ret = 0; - if (!bData->prev_verts) return 1; + if (!bData->prev_verts) + return true; /* matrix comparison */ - for (i = 0; i < 4; i++) { - int j; - for (j = 0; j < 4; j++) - if (bData->prev_obmat[i][j] != ob->obmat[i][j]) return 1; - } + if (!equals_m4m4(bData->prev_obmat, ob->obmat)) + return true; /* vertices */ -#pragma omp parallel for schedule(static) for (i = 0; i < numOfVerts; i++) { - int j; - for (j = 0; j < 3; j++) - if (bData->prev_verts[i].co[j] != mvert[i].co[j]) { - ret = 1; - break; - } + if (!equals_v3v3(bData->prev_verts[i].co, mvert[i].co)) { + return true; + } } - return ret; + return false; } -static int surface_needsVelocityData(DynamicPaintSurface *surface, const Scene *scene) -{ - if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) - return 1; +/* Prepare for surface step by creating PaintBakeNormal data */ +typedef struct DynamicPaintGenerateBakeData { + const DynamicPaintSurface *surface; + Object *ob; - if (surface_getBrushFlags(surface, scene) & BRUSH_USES_VELOCITY) - return 1; + const MVert *mvert; + const Vec3f *canvas_verts; - return 0; -} + const bool do_velocity_data; + const bool new_bdata; +} DynamicPaintGenerateBakeData; -static int surface_needsAccelerationData(DynamicPaintSurface *surface) +static void dynamic_paint_generate_bake_data_cb(void *userdata, const int index) { - if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) - return 1; + const DynamicPaintGenerateBakeData *data = userdata; - return 0; + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintAdjData *adj_data = sData->adj_data; + const PaintBakeData *bData = sData->bData; + + Object *ob = data->ob; + + const MVert *mvert = data->mvert; + const Vec3f *canvas_verts = data->canvas_verts; + + const bool do_velocity_data = data->do_velocity_data; + const bool new_bdata = data->new_bdata; + + float prev_point[3] = {0.0f, 0.0f, 0.0f}; + float temp_nor[3]; + + if (do_velocity_data && !new_bdata) { + copy_v3_v3(prev_point, bData->realCoord[bData->s_pos[index]].v); + } + + /* + * Calculate current 3D-position and normal of each surface point + */ + if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { + float n1[3], n2[3], n3[3]; + const ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; + const PaintUVPoint *tPoint = &((PaintUVPoint *)f_data->uv_p)[index]; + + bData->s_num[index] = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + bData->s_pos[index] = index * bData->s_num[index]; + + /* per sample coordinates */ + for (int ss = 0; ss < bData->s_num[index]; ss++) { + interp_v3_v3v3v3(bData->realCoord[bData->s_pos[index] + ss].v, + canvas_verts[tPoint->v1].v, + canvas_verts[tPoint->v2].v, + canvas_verts[tPoint->v3].v, + f_data->barycentricWeights[index * bData->s_num[index] + ss].v); + } + + /* Calculate current pixel surface normal */ + normal_short_to_float_v3(n1, mvert[tPoint->v1].no); + normal_short_to_float_v3(n2, mvert[tPoint->v2].no); + normal_short_to_float_v3(n3, mvert[tPoint->v3].no); + + interp_v3_v3v3v3(temp_nor, n1, n2, n3, f_data->barycentricWeights[index * bData->s_num[index]].v); + normalize_v3(temp_nor); + if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + /* Prepare surface normal directional scale to easily convert + * brush intersection amount between global and local space */ + float scaled_nor[3]; + mul_v3_v3v3(scaled_nor, temp_nor, ob->size); + bData->bNormal[index].normal_scale = len_v3(scaled_nor); + } + mul_mat3_m4_v3(ob->obmat, temp_nor); + normalize_v3(temp_nor); + negate_v3_v3(bData->bNormal[index].invNorm, temp_nor); + } + else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { + int ss; + if (surface->flags & MOD_DPAINT_ANTIALIAS && adj_data) { + bData->s_num[index] = adj_data->n_num[index] + 1; + bData->s_pos[index] = adj_data->n_index[index] + index; + } + else { + bData->s_num[index] = 1; + bData->s_pos[index] = index; + } + + /* calculate position for each sample */ + for (ss = 0; ss < bData->s_num[index]; ss++) { + /* first sample is always point center */ + copy_v3_v3(bData->realCoord[bData->s_pos[index] + ss].v, canvas_verts[index].v); + if (ss > 0) { + int t_index = adj_data->n_index[index] + (ss - 1); + /* get vertex position at 1/3 of each neigh edge */ + mul_v3_fl(bData->realCoord[bData->s_pos[index] + ss].v, 2.0f / 3.0f); + madd_v3_v3fl(bData->realCoord[bData->s_pos[index] + ss].v, + canvas_verts[adj_data->n_target[t_index]].v, 1.0f / 3.0f); + } + } + + /* normal */ + normal_short_to_float_v3(temp_nor, mvert[index].no); + if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + /* Prepare surface normal directional scale to easily convert + * brush intersection amount between global and local space */ + float scaled_nor[3]; + mul_v3_v3v3(scaled_nor, temp_nor, ob->size); + bData->bNormal[index].normal_scale = len_v3(scaled_nor); + } + mul_mat3_m4_v3(ob->obmat, temp_nor); + normalize_v3(temp_nor); + negate_v3_v3(bData->bNormal[index].invNorm, temp_nor); + } + + /* calculate speed vector */ + if (do_velocity_data && !new_bdata && !bData->clear) { + sub_v3_v3v3(bData->velocity[index].v, bData->realCoord[bData->s_pos[index]].v, prev_point); + } } -/* Prepare for surface step by creating PaintBakeNormal data */ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Scene *scene, Object *ob) { PaintSurfaceData *sData = surface->data; - PaintAdjData *adj_data = sData->adj_data; PaintBakeData *bData = sData->bData; DerivedMesh *dm = surface->canvas->dm; - int index, new_bdata = 0; - int do_velocity_data = surface_needsVelocityData(surface, scene); - int do_accel_data = surface_needsAccelerationData(surface); + int index; + bool new_bdata = false; + const bool do_velocity_data = ((surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) || + (surface_getBrushFlags(surface, scene) & BRUSH_USES_VELOCITY)); + const bool do_accel_data = (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) != 0; int canvasNumOfVerts = dm->getNumVerts(dm); MVert *mvert = dm->getVertArray(dm); Vec3f *canvas_verts; if (bData) { - int surface_moved = dynamicPaint_surfaceHasMoved(surface, ob); + const bool surface_moved = dynamicPaint_surfaceHasMoved(surface, ob); /* get previous speed for accelertaion */ if (do_accel_data && bData->prev_velocity && bData->velocity) @@ -4324,13 +5003,15 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce } canvas_verts = (struct Vec3f *) MEM_mallocN(canvasNumOfVerts * sizeof(struct Vec3f), "Dynamic Paint transformed canvas verts"); - if (!canvas_verts) return 0; + if (!canvas_verts) + return 0; /* allocate memory if required */ if (!bData) { sData->bData = bData = (struct PaintBakeData *) MEM_callocN(sizeof(struct PaintBakeData), "Dynamic Paint bake data"); if (!bData) { - if (canvas_verts) MEM_freeN(canvas_verts); + if (canvas_verts) + MEM_freeN(canvas_verts); return 0; } @@ -4343,16 +5024,21 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce /* if any allocation failed, free everything */ if (!bData->bNormal || !bData->s_pos || !bData->s_num || !bData->realCoord || !canvas_verts) { - if (bData->bNormal) MEM_freeN(bData->bNormal); - if (bData->s_pos) MEM_freeN(bData->s_pos); - if (bData->s_num) MEM_freeN(bData->s_num); - if (bData->realCoord) MEM_freeN(bData->realCoord); - if (canvas_verts) MEM_freeN(canvas_verts); + if (bData->bNormal) + MEM_freeN(bData->bNormal); + if (bData->s_pos) + MEM_freeN(bData->s_pos); + if (bData->s_num) + MEM_freeN(bData->s_num); + if (bData->realCoord) + MEM_freeN(bData->realCoord); + if (canvas_verts) + MEM_freeN(canvas_verts); return setError(surface->canvas, N_("Not enough free memory")); } - new_bdata = 1; + new_bdata = true; } if (do_velocity_data && !bData->velocity) { @@ -4368,7 +5054,7 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce /* * Make a transformed copy of canvas derived mesh vertices to avoid recalculation. */ - bData->mesh_bounds.valid = 0; + bData->mesh_bounds.valid = false; for (index = 0; index < canvasNumOfVerts; index++) { copy_v3_v3(canvas_verts[index].v, mvert[index].co); mul_m4_v3(ob->obmat, canvas_verts[index].v); @@ -4378,112 +5064,20 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce /* * Prepare each surface point for a new step */ -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - float prev_point[3] = {0.0f, 0.0f, 0.0f}; - if (do_velocity_data && !new_bdata) { - copy_v3_v3(prev_point, bData->realCoord[bData->s_pos[index]].v); - } - /* - * Calculate current 3D-position and normal of each surface point - */ - if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { - float n1[3], n2[3], n3[3]; - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; - PaintUVPoint *tPoint = &((PaintUVPoint *)f_data->uv_p)[index]; - int ss; - - bData->s_num[index] = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; - bData->s_pos[index] = index * bData->s_num[index]; - - /* per sample coordinates */ - for (ss = 0; ss < bData->s_num[index]; ss++) { - interp_v3_v3v3v3(bData->realCoord[bData->s_pos[index] + ss].v, - canvas_verts[tPoint->v1].v, - canvas_verts[tPoint->v2].v, - canvas_verts[tPoint->v3].v, - f_data->barycentricWeights[index * bData->s_num[index] + ss].v); - } - - /* Calculate current pixel surface normal */ - normal_short_to_float_v3(n1, mvert[tPoint->v1].no); - normal_short_to_float_v3(n2, mvert[tPoint->v2].no); - normal_short_to_float_v3(n3, mvert[tPoint->v3].no); - - interp_v3_v3v3v3(bData->bNormal[index].invNorm, - n1, n2, n3, f_data->barycentricWeights[index * bData->s_num[index]].v); - mul_mat3_m4_v3(ob->obmat, bData->bNormal[index].invNorm); - normalize_v3(bData->bNormal[index].invNorm); - negate_v3(bData->bNormal[index].invNorm); - } - else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - int ss; - if (surface->flags & MOD_DPAINT_ANTIALIAS && adj_data) { - bData->s_num[index] = adj_data->n_num[index] + 1; - bData->s_pos[index] = adj_data->n_index[index] + index; - } - else { - bData->s_num[index] = 1; - bData->s_pos[index] = index; - } - - /* calculate position for each sample */ - for (ss = 0; ss < bData->s_num[index]; ss++) { - /* first sample is always point center */ - copy_v3_v3(bData->realCoord[bData->s_pos[index] + ss].v, canvas_verts[index].v); - if (ss > 0) { - int t_index = adj_data->n_index[index] + (ss - 1); - /* get vertex position at 1/3 of each neigh edge */ - mul_v3_fl(bData->realCoord[bData->s_pos[index] + ss].v, 2.0f / 3.0f); - madd_v3_v3fl(bData->realCoord[bData->s_pos[index] + ss].v, canvas_verts[adj_data->n_target[t_index]].v, 1.0f / 3.0f); - } - } - - /* normal */ - normal_short_to_float_v3(bData->bNormal[index].invNorm, mvert[index].no); - mul_mat3_m4_v3(ob->obmat, bData->bNormal[index].invNorm); - normalize_v3(bData->bNormal[index].invNorm); - negate_v3(bData->bNormal[index].invNorm); - } - - /* Prepare surface normal directional scale to easily convert - * brush intersection amount between global and local space */ - if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) - { - float temp_nor[3]; - if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { - normal_short_to_float_v3(temp_nor, mvert[index].no); - normalize_v3(temp_nor); - } - else { - float n1[3], n2[3], n3[3]; - ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; - PaintUVPoint *tPoint = &((PaintUVPoint *)f_data->uv_p)[index]; - - normal_short_to_float_v3(n1, mvert[tPoint->v1].no); - normal_short_to_float_v3(n2, mvert[tPoint->v2].no); - normal_short_to_float_v3(n3, mvert[tPoint->v3].no); - interp_v3_v3v3v3(temp_nor, - n1, n2, n3, f_data->barycentricWeights[index * bData->s_num[index]].v); - } - - mul_v3_v3(temp_nor, ob->size); - bData->bNormal[index].normal_scale = len_v3(temp_nor); - } - - /* calculate speed vector */ - if (do_velocity_data && !new_bdata && !bData->clear) { - sub_v3_v3v3(bData->velocity[index].v, bData->realCoord[bData->s_pos[index]].v, prev_point); - } - } + DynamicPaintGenerateBakeData data = { + .surface = surface, .ob = ob, + .mvert = mvert, .canvas_verts = canvas_verts, + .do_velocity_data = do_velocity_data, .new_bdata = new_bdata, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_generate_bake_data_cb, sData->total_points > 1000); MEM_freeN(canvas_verts); /* generate surface space partitioning grid */ surfaceGenerateGrid(surface); /* calculate current frame adjacency point distances and global dirs */ - dynamicPaint_prepareAdjacencyData(surface, 0); + dynamicPaint_prepareAdjacencyData(surface, false); /* Copy current frame vertices to check against in next frame */ copy_m4_m4(bData->prev_obmat, ob->obmat); @@ -4503,9 +5097,16 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su PaintBakeData *bData = sData->bData; DynamicPaintCanvasSettings *canvas = surface->canvas; int ret = 1; - if (sData->total_points < 1) return 0; - dynamicPaint_surfacePreStep(surface, timescale); + if (sData->total_points < 1) + return 0; + + if (dynamic_paint_surface_needs_dry_dissolve(surface)) { + DynamicPaintDissolveDryData data = {.surface = surface, .timescale = timescale}; + BLI_task_parallel_range(0, sData->total_points, &data, + dynamic_paint_surface_pre_step_cb, sData->total_points > 1000); + } + /* * Loop through surface's target paint objects and do painting */ @@ -4529,24 +5130,23 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su brushObj = NULL; /* select object */ if (surface->brush_group) { - if (go->ob) brushObj = go->ob; + if (go->ob) + brushObj = go->ob; } else brushObj = base->object; - if (!brushObj) { - /* skip item */ - if (surface->brush_group) go = go->next; - else base = base->next; - continue; - } - /* next item */ if (surface->brush_group) go = go->next; else base = base->next; + if (!brushObj) { + /* skip item */ + continue; + } + /* check if target has an active dp modifier */ md = modifiers_findByType(brushObj, eModifierType_DynamicPaint); if (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) { @@ -4561,15 +5161,16 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su bData->brush_velocity = MEM_callocN(sData->total_points * sizeof(float) * 4, "Dynamic Paint brush velocity"); /* init adjacency data if not already */ if (!sData->adj_data) - dynamicPaint_initAdjacencyData(surface, 1); + dynamicPaint_initAdjacencyData(surface, true); if (!bData->bNeighs) - dynamicPaint_prepareAdjacencyData(surface, 1); + dynamicPaint_prepareAdjacencyData(surface, true); } /* update object data on this subframe */ if (subframe) { scene_setSubframe(scene, subframe); - BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION, + BKE_scene_frame_get(scene), eModifierType_DynamicPaint); } /* Prepare materials if required */ if (brush_usesMaterial(brush, scene)) @@ -4595,7 +5196,8 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su if (subframe) { scene->r.cfra = scene_frame; scene->r.subframe = scene_subframe; - BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION, BKE_scene_frame_get(scene), eModifierType_DynamicPaint); + BKE_object_modifier_update_subframe(scene, brushObj, true, SUBFRAME_RECURSION, + BKE_scene_frame_get(scene), eModifierType_DynamicPaint); } /* process special brush effects, like smudge */ @@ -4635,8 +5237,10 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su } /* Free temporary effect data */ - if (prevPoint) MEM_freeN(prevPoint); - if (force) MEM_freeN(force); + if (prevPoint) + MEM_freeN(prevPoint); + if (force) + MEM_freeN(force); } } @@ -4655,8 +5259,8 @@ int dynamicPaint_calculateFrame(DynamicPaintSurface *surface, Scene *scene, Obje dynamicPaint_applySurfaceDisplace(surface, surface->canvas->dm); /* update bake data */ - dynamicPaint_generateBakeData(surface, scene, cObject); - + dynamicPaint_generateBakeData(surface, scene, cObject); + /* don't do substeps for first frame */ if (surface->substeps && (frame != surface->start_frame)) { int st; @@ -4664,7 +5268,8 @@ int dynamicPaint_calculateFrame(DynamicPaintSurface *surface, Scene *scene, Obje for (st = 1; st <= surface->substeps; st++) { float subframe = ((float) st) / (surface->substeps + 1); - if (!dynamicPaint_doStep(scene, cObject, surface, timescale, subframe)) return 0; + if (!dynamicPaint_doStep(scene, cObject, surface, timescale, subframe)) + return 0; } } diff --git a/source/blender/blenkernel/intern/editmesh_bvh.c b/source/blender/blenkernel/intern/editmesh_bvh.c index ccea2d4ece4..2927354241c 100644 --- a/source/blender/blenkernel/intern/editmesh_bvh.c +++ b/source/blender/blenkernel/intern/editmesh_bvh.c @@ -544,7 +544,7 @@ BVHTreeOverlap *BKE_bmbvh_overlap(const BMBVHTree *bmtree_a, const BMBVHTree *bm data.tree_pair[0] = bmtree_a; data.tree_pair[1] = bmtree_b; - data.epsilon = max_ff(BLI_bvhtree_getepsilon(bmtree_a->tree), BLI_bvhtree_getepsilon(bmtree_b->tree)); + data.epsilon = max_ff(BLI_bvhtree_get_epsilon(bmtree_a->tree), BLI_bvhtree_get_epsilon(bmtree_b->tree)); return BLI_bvhtree_overlap(bmtree_a->tree, bmtree_b->tree, r_overlap_tot, bmbvh_overlap_cb, &data); }
\ No newline at end of file diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 66e37203dbb..af5089f742f 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -607,13 +607,15 @@ static void do_texture_effector(EffectorCache *eff, EffectorData *efd, EffectedP copy_v3_v3(tex_co, point->loc); - if (eff->pd->flag & PFIELD_TEX_2D) { - float fac=-dot_v3v3(tex_co, efd->nor); - madd_v3_v3fl(tex_co, efd->nor, fac); - } - if (eff->pd->flag & PFIELD_TEX_OBJECT) { mul_m4_v3(eff->ob->imat, tex_co); + + if (eff->pd->flag & PFIELD_TEX_2D) + tex_co[2] = 0.0f; + } + else if (eff->pd->flag & PFIELD_TEX_2D) { + float fac=-dot_v3v3(tex_co, efd->nor); + madd_v3_v3fl(tex_co, efd->nor, fac); } scene_color_manage = BKE_scene_check_color_management_enabled(eff->scene); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 485c4f5b29f..f3eb5430bce 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -68,6 +68,7 @@ bool free_gpencil_strokes(bGPDframe *gpf) /* free stroke memory arrays, then stroke itself */ if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } @@ -261,6 +262,11 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ ARRAY_SET_ITEMS(gpl->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ + /* high quality fill by default */ + gpl->flag |= GP_LAYER_HQ_FILL; + + /* default smooth iterations */ + gpl->draw_smoothlvl = 1; /* auto-name */ BLI_strncpy(gpl->info, name, sizeof(gpl->info)); @@ -315,7 +321,8 @@ bGPDframe *gpencil_frame_duplicate(bGPDframe *src) /* make copy of source stroke, then adjust pointer to points too */ gpsd = MEM_dupallocN(gps); gpsd->points = MEM_dupallocN(gps->points); - + gpsd->triangles = MEM_dupallocN(gps->triangles); + gpsd->flag |= GP_STROKE_RECALC_CACHES; BLI_addtail(&dst->strokes, gpsd); } @@ -424,6 +431,7 @@ void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) /* free the stroke and its data */ MEM_freeN(gps->points); + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); /* if frame has no strokes after this, delete it */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index d5f9a2dac3e..1ae7ca189fb 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -842,13 +842,6 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char break; } - if (rect_float) { - /* both byte and float buffers are filling in sRGB space, need to linearize float buffer after BKE_image_buf_fill* functions */ - - IMB_buffer_float_from_float(rect_float, rect_float, ibuf->channels, IB_PROFILE_LINEAR_RGB, IB_PROFILE_SRGB, - true, ibuf->x, ibuf->y, ibuf->x, ibuf->x); - } - return ibuf; } @@ -1755,6 +1748,7 @@ typedef struct StampData { char scene[STAMP_NAME_SIZE]; char strip[STAMP_NAME_SIZE]; char rendertime[STAMP_NAME_SIZE]; + char memory[STAMP_NAME_SIZE]; } StampData; #undef STAMP_NAME_SIZE @@ -1876,6 +1870,13 @@ static void stampdata(Scene *scene, Object *camera, StampData *stamp_data, int d else { stamp_data->rendertime[0] = '\0'; } + + if (stats && (scene->r.stamp & R_STAMP_MEMORY)) { + BLI_snprintf(stamp_data->memory, sizeof(stamp_data->memory), do_prefix ? "Peak Memory %.2fM" : "%.2fM", stats->mem_peak); + } + else { + stamp_data->memory[0] = '\0'; + } } } @@ -1950,6 +1951,12 @@ static void stampdata_from_template(StampData *stamp_data, else { stamp_data->rendertime[0] = '\0'; } + if (scene->r.stamp & R_STAMP_MEMORY) { + BLI_snprintf(stamp_data->memory, sizeof(stamp_data->memory), "Peak Memory %s", stamp_data_template->memory); + } + else { + stamp_data->memory[0] = '\0'; + } } void BKE_image_stamp_buf( @@ -2063,6 +2070,21 @@ void BKE_image_stamp_buf( } /* Top left corner, below File, Date, Rendertime */ + if (TEXT_SIZE_CHECK(stamp_data.memory, w, h)) { + y -= h; + + /* and space for background. */ + buf_rectfill_area(rect, rectf, width, height, scene->r.bg_stamp, display, + 0, y - BUFF_MARGIN_Y, w + BUFF_MARGIN_X, y + h + BUFF_MARGIN_Y); + + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.memory, BLF_DRAW_STR_DUMMY_MAX); + + /* the extra pixel for background. */ + y -= BUFF_MARGIN_Y * 2; + } + + /* Top left corner, below File, Date, Memory, Rendertime */ BLF_enable(mono, BLF_WORD_WRAP); if (TEXT_SIZE_CHECK_WORD_WRAP(stamp_data.note, w, h)) { y -= h; @@ -2226,6 +2248,7 @@ void BKE_stamp_info_callback(void *data, struct StampData *stamp_data, StampCall CALL(scene, "Scene"); CALL(strip, "Strip"); CALL(rendertime, "RenderTime"); + CALL(memory, "Memory"); #undef CALL } diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c index 10e7d46b315..2c8399adece 100644 --- a/source/blender/blenkernel/intern/image_gen.c +++ b/source/blender/blenkernel/intern/image_gen.c @@ -33,30 +33,42 @@ #include "BKE_image.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "BLF_api.h" -void BKE_image_buf_fill_color(unsigned char *rect, float *rect_float, int width, int height, const float color[4]) +typedef struct FillColorThreadData { + unsigned char *rect; + float *rect_float; + int width; + float color[4]; +} FillColorThreadData; + +static void image_buf_fill_color_slice(unsigned char *rect, + float *rect_float, + int width, int height, + const float color[4]) { int x, y; /* blank image */ if (rect_float) { + float linear_color[4]; + srgb_to_linearrgb_v4(linear_color, color); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - copy_v4_v4(rect_float, color); + copy_v4_v4(rect_float, linear_color); rect_float += 4; } } } - + if (rect) { unsigned char ccol[4]; - rgba_float_to_uchar(ccol, color); - for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - rect[0] = ccol[0]; rect[1] = ccol[1]; rect[2] = ccol[2]; @@ -67,37 +79,79 @@ void BKE_image_buf_fill_color(unsigned char *rect, float *rect_float, int width, } } +static void image_buf_fill_color_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + FillColorThreadData *data = (FillColorThreadData *)data_v; + size_t offset = ((size_t)start_scanline) * data->width * 4; + unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; + float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; + image_buf_fill_color_slice(rect, + rect_float, + data->width, + num_scanlines, + data->color); +} -void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int width, int height) +void BKE_image_buf_fill_color(unsigned char *rect, + float *rect_float, + int width, int height, + const float color[4]) { - /* these two passes could be combined into one, but it's more readable and + if (((size_t)width) * height < 64 * 64) { + image_buf_fill_color_slice(rect, rect_float, width, height, color); + } + else { + FillColorThreadData data; + data.rect = rect; + data.rect_float = rect_float; + data.width = width; + copy_v4_v4(data.color, color); + IMB_processor_apply_threaded_scanlines( + height, image_buf_fill_color_thread_do, &data); + } +} + +static void image_buf_fill_checker_slice(unsigned char *rect, + float *rect_float, + int width, int height, + int offset) +{ + /* these two passes could be combined into one, but it's more readable and * easy to tweak like this, speed isn't really that much of an issue in this situation... */ - + int checkerwidth = 32, dark = 1; int x, y; unsigned char *rect_orig = rect; float *rect_float_orig = rect_float; - + float h = 0.0, hoffs = 0.0; float hsv[3] = {0.0f, 0.9f, 0.9f}; float rgb[3]; + float dark_linear_color, bright_linear_color; + if (rect_float != NULL) { + dark_linear_color = srgb_to_linearrgb(0.25f); + bright_linear_color = srgb_to_linearrgb(0.58f); + } + /* checkers */ - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { dark = powf(-1.0f, floorf(y / checkerwidth)); - + for (x = 0; x < width; x++) { if (x % checkerwidth == 0) dark = -dark; - + if (rect_float) { if (dark > 0) { - rect_float[0] = rect_float[1] = rect_float[2] = 0.25f; + rect_float[0] = rect_float[1] = rect_float[2] = dark_linear_color; rect_float[3] = 1.0f; } else { - rect_float[0] = rect_float[1] = rect_float[2] = 0.58f; + rect_float[0] = rect_float[1] = rect_float[2] = bright_linear_color; rect_float[3] = 1.0f; } rect_float += 4; @@ -120,12 +174,12 @@ void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int widt rect_float = rect_float_orig; /* 2nd pass, colored + */ - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { hoffs = 0.125f * floorf(y / checkerwidth); - + for (x = 0; x < width; x++) { h = 0.125f * floorf(x / checkerwidth); - + if ((abs((x % checkerwidth) - (checkerwidth / 2)) < 4) && (abs((y % checkerwidth) - (checkerwidth / 2)) < 4)) { @@ -134,18 +188,16 @@ void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int widt { hsv[0] = fmodf(fabsf(h - hoffs), 1.0f); hsv_to_rgb_v(hsv, rgb); - + if (rect) { rect[0] = (char)(rgb[0] * 255.0f); rect[1] = (char)(rgb[1] * 255.0f); rect[2] = (char)(rgb[2] * 255.0f); rect[3] = 255; } - + if (rect_float) { - rect_float[0] = rgb[0]; - rect_float[1] = rgb[1]; - rect_float[2] = rgb[2]; + srgb_to_linearrgb_v3_v3(rect_float, rgb); rect_float[3] = 1.0f; } } @@ -157,13 +209,55 @@ void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int widt } } +typedef struct FillCheckerThreadData { + unsigned char *rect; + float *rect_float; + int width; +} FillCheckerThreadData; + +static void image_buf_fill_checker_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + FillCheckerThreadData *data = (FillCheckerThreadData *)data_v; + size_t offset = ((size_t)start_scanline) * data->width * 4; + unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; + float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; + image_buf_fill_checker_slice(rect, + rect_float, + data->width, + num_scanlines, + start_scanline); +} + +void BKE_image_buf_fill_checker(unsigned char *rect, + float *rect_float, + int width, int height) +{ + if (((size_t)width) * height < 64 * 64) { + image_buf_fill_checker_slice(rect, rect_float, width, height, 0); + } + else { + FillCheckerThreadData data; + data.rect = rect; + data.rect_float = rect_float; + data.width = width; + IMB_processor_apply_threaded_scanlines( + height, image_buf_fill_checker_thread_do, &data); + } +} /* Utility functions for BKE_image_buf_fill_checker_color */ #define BLEND_FLOAT(real, add) (real + add <= 1.0f) ? (real + add) : 1.0f #define BLEND_CHAR(real, add) ((real + (char)(add * 255.0f)) <= 255) ? (real + (char)(add * 255.0f)) : 255 -static void checker_board_color_fill(unsigned char *rect, float *rect_float, int width, int height) +static void checker_board_color_fill(unsigned char *rect, + float *rect_float, + int width, + int height, + int offset, + int total_height) { int hue_step, y, x; float hsv[3], rgb[3]; @@ -173,9 +267,9 @@ static void checker_board_color_fill(unsigned char *rect, float *rect_float, int hue_step = power_of_2_max_i(width / 8); if (hue_step < 8) hue_step = 8; - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { - hsv[2] = 0.1 + (y * (0.4 / height)); /* use a number lower then 1.0 else its too bright */ + hsv[2] = 0.1 + (y * (0.4 / total_height)); /* use a number lower then 1.0 else its too bright */ for (x = 0; x < width; x++) { hsv[0] = (float)((double)(x / hue_step) * 1.0 / width * hue_step); hsv_to_rgb_v(hsv, rgb); @@ -185,7 +279,7 @@ static void checker_board_color_fill(unsigned char *rect, float *rect_float, int rect[1] = (char)(rgb[1] * 255.0f); rect[2] = (char)(rgb[2] * 255.0f); rect[3] = 255; - + rect += 4; } @@ -194,27 +288,35 @@ static void checker_board_color_fill(unsigned char *rect, float *rect_float, int rect_float[1] = rgb[1]; rect_float[2] = rgb[2]; rect_float[3] = 1.0f; - + rect_float += 4; } } } } -static void checker_board_color_tint(unsigned char *rect, float *rect_float, int width, int height, int size, float blend) +static void checker_board_color_tint(unsigned char *rect, + float *rect_float, + int width, + int height, + int size, + float blend, + int offset) { int x, y; float blend_half = blend * 0.5f; - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { for (x = 0; x < width; x++) { - if (((y / size) % 2 == 1 && (x / size) % 2 == 1) || ( (y / size) % 2 == 0 && (x / size) % 2 == 0)) { + if (((y / size) % 2 == 1 && (x / size) % 2 == 1) || + ((y / size) % 2 == 0 && (x / size) % 2 == 0)) + { if (rect) { rect[0] = (char)BLEND_CHAR(rect[0], blend); rect[1] = (char)BLEND_CHAR(rect[1], blend); rect[2] = (char)BLEND_CHAR(rect[2], blend); rect[3] = 255; - + rect += 4; } if (rect_float) { @@ -222,7 +324,7 @@ static void checker_board_color_tint(unsigned char *rect, float *rect_float, int rect_float[1] = BLEND_FLOAT(rect_float[1], blend); rect_float[2] = BLEND_FLOAT(rect_float[2], blend); rect_float[3] = 1.0f; - + rect_float += 4; } } @@ -232,7 +334,7 @@ static void checker_board_color_tint(unsigned char *rect, float *rect_float, int rect[1] = (char)BLEND_CHAR(rect[1], blend_half); rect[2] = (char)BLEND_CHAR(rect[2], blend_half); rect[3] = 255; - + rect += 4; } if (rect_float) { @@ -240,19 +342,24 @@ static void checker_board_color_tint(unsigned char *rect, float *rect_float, int rect_float[1] = BLEND_FLOAT(rect_float[1], blend_half); rect_float[2] = BLEND_FLOAT(rect_float[2], blend_half); rect_float[3] = 1.0f; - + rect_float += 4; } } - + } } } -static void checker_board_grid_fill(unsigned char *rect, float *rect_float, int width, int height, float blend) +static void checker_board_grid_fill(unsigned char *rect, + float *rect_float, + int width, + int height, + float blend, + int offset) { int x, y; - for (y = 0; y < height; y++) { + for (y = offset; y < height + offset; y++) { for (x = 0; x < width; x++) { if (((y % 32) == 0) || ((x % 32) == 0) || x == 0) { if (rect) { @@ -268,7 +375,7 @@ static void checker_board_grid_fill(unsigned char *rect, float *rect_float, int rect_float[1] = BLEND_FLOAT(rect_float[1], blend); rect_float[2] = BLEND_FLOAT(rect_float[2], blend); rect_float[3] = 1.0f; - + rect_float += 4; } } @@ -282,7 +389,12 @@ static void checker_board_grid_fill(unsigned char *rect, float *rect_float, int /* defined in image.c */ -static void checker_board_text(unsigned char *rect, float *rect_float, int width, int height, int step, int outline) +static void checker_board_text(unsigned char *rect, + float *rect_float, + int width, + int height, + int step, + int outline) { int x, y; int pen_x, pen_y; @@ -342,14 +454,71 @@ static void checker_board_text(unsigned char *rect, float *rect_float, int width BLF_buffer(mono, NULL, NULL, 0, 0, 0, NULL); } +static void checker_board_color_prepare_slice(unsigned char *rect, + float *rect_float, + int width, + int height, + int offset, + int total_height) +{ + checker_board_color_fill(rect, rect_float, width, height, offset, total_height); + checker_board_color_tint(rect, rect_float, width, height, 1, 0.03f, offset); + checker_board_color_tint(rect, rect_float, width, height, 4, 0.05f, offset); + checker_board_color_tint(rect, rect_float, width, height, 32, 0.07f, offset); + checker_board_color_tint(rect, rect_float, width, height, 128, 0.15f, offset); + checker_board_grid_fill(rect, rect_float, width, height, 1.0f / 4.0f, offset); +} + +typedef struct FillCheckerColorThreadData { + unsigned char *rect; + float *rect_float; + int width, height; +} FillCheckerColorThreadData; + +static void checker_board_color_prepare_thread_do(void *data_v, + int start_scanline, + int num_scanlines) +{ + FillCheckerColorThreadData *data = (FillCheckerColorThreadData *)data_v; + size_t offset = ((size_t)data->width) * start_scanline * 4; + unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL; + float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL; + checker_board_color_prepare_slice(rect, + rect_float, + data->width, + num_scanlines, + start_scanline, + data->height); +} + void BKE_image_buf_fill_checker_color(unsigned char *rect, float *rect_float, int width, int height) { - checker_board_color_fill(rect, rect_float, width, height); - checker_board_color_tint(rect, rect_float, width, height, 1, 0.03f); - checker_board_color_tint(rect, rect_float, width, height, 4, 0.05f); - checker_board_color_tint(rect, rect_float, width, height, 32, 0.07f); - checker_board_color_tint(rect, rect_float, width, height, 128, 0.15f); - checker_board_grid_fill(rect, rect_float, width, height, 1.0f / 4.0f); + if (((size_t)width) * height < 64 * 64) { + checker_board_color_prepare_slice(rect, rect_float, width, height, 0, height); + } + else { + FillCheckerColorThreadData data; + data.rect = rect; + data.rect_float = rect_float; + data.width = width; + data.height = height; + IMB_processor_apply_threaded_scanlines( + height, checker_board_color_prepare_thread_do, &data); + } checker_board_text(rect, rect_float, width, height, 128, 2); + + if (rect_float != NULL) { + /* TODO(sergey): Currently it's easier to fill in form buffer and + * linearize it afterwards. This could be optimized with some smart + * trickery around blending factors and such. + */ + IMB_buffer_float_from_float_threaded(rect_float, rect_float, + 4, + IB_PROFILE_LINEAR_RGB, + IB_PROFILE_SRGB, + true, + width, height, + width, width); + } } diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 57c02ec6329..b350e932281 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -1082,6 +1082,7 @@ void BKE_lattice_modifiers_calc(Scene *scene, Object *ob) md->scene = scene; + if (!(mti->flags & eModifierTypeFlag_AcceptsLattice)) continue; if (!(md->mode & eModifierMode_Realtime)) continue; if (editmode && !(md->mode & eModifierMode_Editmode)) continue; if (mti->isDisabled && mti->isDisabled(md, 0)) continue; diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 071071d9d0f..64c9ddb5b38 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -1764,7 +1764,7 @@ void BKE_main_id_clear_newpoins(Main *bmain) } } -static void lib_indirect_test_id(ID *id, Library *lib) +static void lib_indirect_test_id(ID *id, const Library *lib) { #define LIBTAG(a) \ if (a && a->id.lib) { a->id.tag &= ~LIB_TAG_INDIRECT; a->id.tag |= LIB_TAG_EXTERN; } (void)0 @@ -1811,9 +1811,14 @@ static void lib_indirect_test_id(ID *id, Library *lib) #undef LIBTAG } -/* if lib!=NULL, only all from lib local - * bmain is almost certainly G.main */ -void BKE_library_make_local(Main *bmain, Library *lib, bool untagged_only, bool set_fake) +/** Make linked datablocks local. + * + * \param bmain Almost certainly G.main. + * \param lib If not NULL, only make local datablocks from this library. + * \param untagged_only If true, only make local datablocks not tagged with LIB_TAG_PRE_EXISTING. + * \param set_fake If true, set fake user on all localized datablocks (except group and objects ones). + */ +void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged_only, const bool set_fake) { ListBase *lbarray[MAX_LIBARRAY]; ID *id, *idn; diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index c8bb2e0e758..a472b6e5bc1 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -262,6 +262,51 @@ void BKE_mesh_vert_loop_map_create(MeshElemMap **r_map, int **r_mem, } /** + * Generates a map where the key is the edge and the value is a list of looptris that use that edge. + * The lists are allocated from one memory pool. + */ +void BKE_mesh_vert_looptri_map_create( + MeshElemMap **r_map, int **r_mem, + const MVert *UNUSED(mvert), const int totvert, + const MLoopTri *mlooptri, const int totlooptri, + const MLoop *mloop, const int UNUSED(totloop)) +{ + MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, __func__); + int *indices = MEM_mallocN(sizeof(int) * (size_t)totlooptri * 3, __func__); + int *index_step; + const MLoopTri *mlt; + int i; + + /* count face users */ + for (i = 0, mlt = mlooptri; i < totlooptri; mlt++, i++) { + for (int j = 3; j--;) { + map[mloop[mlt->tri[j]].v].count++; + } + } + + /* create offsets */ + index_step = indices; + for (i = 0; i < totvert; i++) { + map[i].indices = index_step; + index_step += map[i].count; + + /* re-count, using this as an index below */ + map[i].count = 0; + } + + /* assign looptri-edge users */ + for (i = 0, mlt = mlooptri; i < totlooptri; mlt++, i++) { + for (int j = 3; j--;) { + MeshElemMap *map_ele = &map[mloop[mlt->tri[j]].v]; + map_ele->indices[map_ele->count++] = i; + } + } + + *r_map = map; + *r_mem = indices; +} + +/** * Generates a map where the key is the vertex and the value is a list of edges that use that vertex as an endpoint. * The lists are allocated from one memory pool. */ diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index 49dd0b104ab..ba890b005d8 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -284,7 +284,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, bool fix_normal = true; for (j = 0; j < 3; j++) { - if (!finite(mv->co[j])) { + if (!isfinite(mv->co[j])) { PRINT_ERR("\tVertex %u: has invalid coordinate\n", i); if (do_fixes) { @@ -765,7 +765,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, for (j = 0, dw = dv->dw; j < dv->totweight; j++, dw++) { /* note, greater than max defgroups is accounted for in our code, but not < 0 */ - if (!finite(dw->weight)) { + if (!isfinite(dw->weight)) { PRINT_ERR("\tVertex deform %u, group %d has weight: %f\n", i, dw->def_nr, dw->weight); if (do_fixes) { dw->weight = 0.0f; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index a3597541529..51bc0b597c7 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -232,6 +232,11 @@ bool BKE_object_support_modifier_type_check(Object *ob, int modifier_type) mti = modifierType_getInfo(modifier_type); + + if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsLattice) == 0) { + return false; + } + if (!((mti->flags & eModifierTypeFlag_AcceptsCVs) || (ob->type == OB_MESH && (mti->flags & eModifierTypeFlag_AcceptsMesh)))) { diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 330b5922c9a..d73f087a3fe 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -979,16 +979,7 @@ static void pbvh_update_normals_accum_task_cb(void *userdata, const int n) * Not exact equivalent though, since atomicity is only ensured for one component * of the vector at a time, but here it shall not make any sensible difference. */ for (int k = 3; k--; ) { - /* Atomic float addition. - * Note that since collision are unlikely, loop will nearly always run once. */ - float oldval, newval; - uint32_t prevval; - do { - oldval = vnors[v][k]; - newval = oldval + fn[k]; - prevval = atomic_cas_uint32( - (uint32_t *)&vnors[v][k], *(uint32_t *)(&oldval), *(uint32_t *)(&newval)); - } while (UNLIKELY(prevval != *(uint32_t *)(&oldval))); + atomic_add_fl(&vnors[v][k], fn[k]); } } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 1a452fbdc96..e06c7a43dd8 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -101,11 +101,6 @@ #include "bmesh.h" -#ifdef WIN32 -#else -# include <sys/time.h> -#endif - const char *RE_engine_id_BLENDER_RENDER = "BLENDER_RENDER"; const char *RE_engine_id_BLENDER_GAME = "BLENDER_GAME"; const char *RE_engine_id_CYCLES = "CYCLES"; @@ -547,7 +542,7 @@ void BKE_scene_init(Scene *sce) sce->r.bake.im_format.compress = 15; sce->r.scemode = R_DOCOMP | R_DOSEQ | R_EXTENSION; - sce->r.stamp = R_STAMP_TIME | R_STAMP_FRAME | R_STAMP_DATE | R_STAMP_CAMERA | R_STAMP_SCENE | R_STAMP_FILENAME | R_STAMP_RENDERTIME; + sce->r.stamp = R_STAMP_TIME | R_STAMP_FRAME | R_STAMP_DATE | R_STAMP_CAMERA | R_STAMP_SCENE | R_STAMP_FILENAME | R_STAMP_RENDERTIME | R_STAMP_MEMORY; sce->r.stamp_font_id = 12; sce->r.fg_stamp[0] = sce->r.fg_stamp[1] = sce->r.fg_stamp[2] = 0.8f; sce->r.fg_stamp[3] = 1.0f; @@ -2409,7 +2404,6 @@ SceneRenderView *BKE_scene_multiview_render_view_findindex(const RenderData *rd, if ((rd->scemode & R_MULTIVIEW) == 0) return NULL; - nr = 0; for (srv = rd->views.first, nr = 0; srv; srv = srv->next) { if (BKE_scene_multiview_is_render_view_active(rd, srv)) { if (nr++ == view_id) @@ -2440,7 +2434,6 @@ int BKE_scene_multiview_view_id_get(const RenderData *rd, const char *viewname) if ((!viewname) || (!viewname[0])) return 0; - nr = 0; for (srv = rd->views.first, nr = 0; srv; srv = srv->next) { if (BKE_scene_multiview_is_render_view_active(rd, srv)) { if (STREQ(viewname, srv->name)) { diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index 4086fc2b0f3..3de4a426973 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -3060,7 +3060,7 @@ static ImBuf *do_gaussian_blur_effect(const SeqRenderData *context, ibuf1 = out; init_data.ibuf = ibuf1; - out = prepare_effect_imbufs(context, ibuf1, NULL, NULL);; + out = prepare_effect_imbufs(context, ibuf1, NULL, NULL); init_data.out = out; IMB_processor_apply_threaded(out->y, diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index badf78edfb1..e855f6faa22 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -245,11 +245,11 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for * for finding the best hit, to get the real dist, * measure the len_v3v3() from the input coord to hit.co */ BVHTreeRayHit hit; - BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; + void *treeData = NULL; /* auxiliary target */ DerivedMesh *auxMesh = NULL; - BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; + void *auxData = NULL; SpaceTransform local2aux; /* If the user doesn't allows to project in any direction of projection axis @@ -285,19 +285,44 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for } /* use editmesh to avoid array allocation */ + BMEditMesh *emtarget = NULL, *emaux = NULL; + BVHTreeFromEditMesh emtreedata_stack, emauxdata_stack; + BVHTreeFromMesh dmtreedata_stack, dmauxdata_stack; + BVHTree *targ_tree; + void *targ_callback; if (calc->smd->target && calc->target->type == DM_TYPE_EDITBMESH) { - treeData.em_evil = BKE_editmesh_from_object(calc->smd->target); - treeData.em_evil_all = true; + emtarget = BKE_editmesh_from_object(calc->smd->target); + if ((targ_tree = bvhtree_from_editmesh_looptri(&emtreedata_stack, emtarget, 0.0, 4, 6))) { + targ_callback = emtreedata_stack.raycast_callback; + treeData = &emtreedata_stack; + } } - if (calc->smd->auxTarget && auxMesh->type == DM_TYPE_EDITBMESH) { - auxData.em_evil = BKE_editmesh_from_object(calc->smd->auxTarget); - auxData.em_evil_all = true; + else { + if ((targ_tree = bvhtree_from_mesh_looptri(&dmtreedata_stack, calc->target, 0.0, 4, 6))) { + targ_callback = dmtreedata_stack.raycast_callback; + treeData = &dmtreedata_stack; + } } - - /* After sucessufuly build the trees, start projection vertexs */ - if (bvhtree_from_mesh_looptri(&treeData, calc->target, 0.0, 4, 6) && - (auxMesh == NULL || bvhtree_from_mesh_looptri(&auxData, auxMesh, 0.0, 4, 6))) - { + if (targ_tree) { + BVHTree *aux_tree = NULL; + void *aux_callback; + if (auxMesh != NULL) { + /* use editmesh to avoid array allocation */ + if (calc->smd->auxTarget && auxMesh->type == DM_TYPE_EDITBMESH) { + emaux = BKE_editmesh_from_object(calc->smd->auxTarget); + if ((aux_tree = bvhtree_from_editmesh_looptri(&emauxdata_stack, emaux, 0.0, 4, 6)) != NULL) { + aux_callback = emauxdata_stack.raycast_callback; + auxData = &emauxdata_stack; + } + } + else { + if ((aux_tree = bvhtree_from_mesh_looptri(&dmauxdata_stack, calc->target, 0.0, 4, 6)) != NULL) { + aux_callback = dmauxdata_stack.raycast_callback; + auxData = &dmauxdata_stack; + } + } + } + /* After sucessufuly build the trees, start projection vertexs */ #ifndef __APPLE__ #pragma omp parallel for private(i, hit) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) @@ -340,15 +365,17 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for /* Project over positive direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { - if (auxData.tree) { - BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, - &local2aux, auxData.tree, &hit, - auxData.raycast_callback, &auxData); + if (aux_tree) { + BKE_shrinkwrap_project_normal( + 0, tmp_co, tmp_no, + &local2aux, aux_tree, &hit, + aux_callback, auxData); } - BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, tmp_no, - &calc->local2target, treeData.tree, &hit, - treeData.raycast_callback, &treeData); + BKE_shrinkwrap_project_normal( + calc->smd->shrinkOpts, tmp_co, tmp_no, + &calc->local2target, targ_tree, &hit, + targ_callback, treeData); } /* Project over negative direction of axis */ @@ -356,15 +383,17 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for float inv_no[3]; negate_v3_v3(inv_no, tmp_no); - if (auxData.tree) { - BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, - &local2aux, auxData.tree, &hit, - auxData.raycast_callback, &auxData); + if (aux_tree) { + BKE_shrinkwrap_project_normal( + 0, tmp_co, inv_no, + &local2aux, aux_tree, &hit, + aux_callback, auxData); } - BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, inv_no, - &calc->local2target, treeData.tree, &hit, - treeData.raycast_callback, &treeData); + BKE_shrinkwrap_project_normal( + calc->smd->shrinkOpts, tmp_co, inv_no, + &calc->local2target, targ_tree, &hit, + targ_callback, treeData); } /* don't set the initial dist (which is more efficient), @@ -383,8 +412,14 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for } /* free data structures */ - free_bvhtree_from_mesh(&treeData); - free_bvhtree_from_mesh(&auxData); + if (treeData) { + if (emtarget) free_bvhtree_from_editmesh(treeData); + else free_bvhtree_from_mesh(treeData); + } + if (auxData) { + if (emaux) free_bvhtree_from_editmesh(auxData); + else free_bvhtree_from_mesh(auxData); + } } /* diff --git a/source/blender/blenkernel/intern/sketch.c b/source/blender/blenkernel/intern/sketch.c index 0d355abb49b..6f5c264f658 100644 --- a/source/blender/blenkernel/intern/sketch.c +++ b/source/blender/blenkernel/intern/sketch.c @@ -50,8 +50,6 @@ void freeSketch(SK_Sketch *sketch) sk_freeStroke(stk); } - BLI_freelistN(&sketch->depth_peels); - MEM_freeN(sketch); } diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index a8e8f10962d..5b6adb3778a 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -3369,13 +3369,14 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, CCGSubSurf *ss = ccgdm->ss; CCGKey key; int colType; - const MLoopCol *mloopcol; + const MLoopCol *mloopcol = NULL; MTexPoly *mtexpoly = DM_get_poly_data_layer(dm, CD_MTEXPOLY); DMFlagMat *faceFlags = ccgdm->faceFlags; DMDrawOption draw_option; int i, totpoly; bool flush; - bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_colors = (flag & DM_DRAW_USE_COLORS) != 0; unsigned int next_actualFace; unsigned int gridFaces = ccgSubSurf_getGridSize(ss) - 1; int mat_index; @@ -3394,15 +3395,17 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, CCG_key_top_level(&key, ss); ccgdm_pbvh_update(ccgdm); - colType = CD_TEXTURE_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - if (!mloopcol) { - colType = CD_PREVIEW_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - } - if (!mloopcol) { - colType = CD_MLOOPCOL; + if (use_colors) { + colType = CD_TEXTURE_MLOOPCOL; mloopcol = dm->getLoopDataArray(dm, colType); + if (!mloopcol) { + colType = CD_PREVIEW_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } + if (!mloopcol) { + colType = CD_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } } GPU_vertex_setup(dm); diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index cf11fe2323d..c0a373395dc 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -102,7 +102,7 @@ typedef struct bUnitDef { /* define a single unit */ typedef struct bUnitCollection { - struct bUnitDef *units; + const struct bUnitDef *units; int base_unit; /* basic unit index (when user doesn't specify unit explicitly) */ int flag; /* options for this system */ int length; /* to quickly find the last item */ @@ -113,7 +113,7 @@ static struct bUnitDef buDummyDef[] = { {"", NULL, "", NULL, NULL, 1.0, 0.0}, {N static struct bUnitCollection buDummyCollection = {buDummyDef, 0, 0, sizeof(buDummyDef)}; /* Lengths */ -static struct bUnitDef buMetricLenDef[] = { +static const struct bUnitDef buMetricLenDef[] = { {"kilometer", "kilometers", "km", NULL, "Kilometers", UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, {"hectometer", "hectometers", "hm", NULL, "100 Meters", UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, {"dekameter", "dekameters", "dam", NULL, "10 Meters", UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, @@ -131,7 +131,7 @@ static struct bUnitDef buMetricLenDef[] = { #endif {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0} }; -static struct bUnitCollection buMetricLenCollection = {buMetricLenDef, 3, 0, sizeof(buMetricLenDef) / sizeof(bUnitDef)}; +static const struct bUnitCollection buMetricLenCollection = {buMetricLenDef, 3, 0, sizeof(buMetricLenDef) / sizeof(bUnitDef)}; static struct bUnitDef buImperialLenDef[] = { {"mile", "miles", "mi", "m", "Miles", UN_SC_MI, 0.0, B_UNIT_DEF_NONE}, @@ -289,7 +289,7 @@ static struct bUnitCollection buCameraLenCollection = {buCameraLenDef, 3, 0, siz #define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / B_UNIT_TYPE_TOT) / sizeof(void *)) - 1) -static struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { +static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { {NULL, NULL, NULL, NULL, NULL, &buNaturalRotCollection, &buNaturalTimeCollection, NULL, NULL, NULL}, {NULL, &buMetricLenCollection, &buMetricAreaCollection, &buMetricVolCollection, &buMetricMassCollection, &buNaturalRotCollection, &buNaturalTimeCollection, &buMetricVelCollection, &buMetricAclCollection, &buCameraLenCollection}, /* metric */ {NULL, &buImperialLenCollection, &buImperialAreaCollection, &buImperialVolCollection, &buImperialMassCollection, &buNaturalRotCollection, &buNaturalTimeCollection, &buImperialVelCollection, &buImperialAclCollection, &buCameraLenCollection}, /* imperial */ @@ -299,20 +299,21 @@ static struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { /* internal, has some option not exposed */ -static bUnitCollection *unit_get_system(int system, int type) +static const bUnitCollection *unit_get_system(int system, int type) { assert((system > -1) && (system < UNIT_SYSTEM_TOT) && (type > -1) && (type < B_UNIT_TYPE_TOT)); return bUnitSystems[system][type]; /* select system to use, metric/imperial/other? */ } -static bUnitDef *unit_default(bUnitCollection *usys) +static const bUnitDef *unit_default(const bUnitCollection *usys) { return &usys->units[usys->base_unit]; } -static bUnitDef *unit_best_fit(double value, bUnitCollection *usys, bUnitDef *unit_start, int suppress) +static const bUnitDef *unit_best_fit( + double value, const bUnitCollection *usys, const bUnitDef *unit_start, int suppress) { - bUnitDef *unit; + const bUnitDef *unit; double value_abs = value > 0.0 ? value : -value; for (unit = unit_start ? unit_start : usys->units; unit->name; unit++) { @@ -337,19 +338,21 @@ static bUnitDef *unit_best_fit(double value, bUnitCollection *usys, bUnitDef *un } /* convert into 2 units and 2 values for "2ft, 3inch" syntax */ -static void unit_dual_convert(double value, bUnitCollection *usys, bUnitDef **unit_a, bUnitDef **unit_b, - double *value_a, double *value_b) +static void unit_dual_convert( + double value, const bUnitCollection *usys, + bUnitDef const **r_unit_a, bUnitDef const **r_unit_b, + double *r_value_a, double *r_value_b) { - bUnitDef *unit = unit_best_fit(value, usys, NULL, 1); + const bUnitDef *unit = unit_best_fit(value, usys, NULL, 1); - *value_a = (value < 0.0 ? ceil : floor)(value / unit->scalar) * unit->scalar; - *value_b = value - (*value_a); + *r_value_a = (value < 0.0 ? ceil : floor)(value / unit->scalar) * unit->scalar; + *r_value_b = value - (*r_value_a); - *unit_a = unit; - *unit_b = unit_best_fit(*value_b, usys, *unit_a, 1); + *r_unit_a = unit; + *r_unit_b = unit_best_fit(*r_value_b, usys, *r_unit_a, 1); } -static size_t unit_as_string(char *str, int len_max, double value, int prec, bUnitCollection *usys, +static size_t unit_as_string(char *str, int len_max, double value, int prec, const bUnitCollection *usys, /* non exposed options */ const bUnitDef *unit, char pad) { @@ -422,14 +425,14 @@ static size_t unit_as_string(char *str, int len_max, double value, int prec, bUn */ size_t bUnit_AsString(char *str, int len_max, double value, int prec, int system, int type, bool split, bool pad) { - bUnitCollection *usys = unit_get_system(system, type); + const bUnitCollection *usys = unit_get_system(system, type); if (usys == NULL || usys->units[0].name == NULL) usys = &buDummyCollection; /* split output makes sense only for length, mass and time */ if (split && (type == B_UNIT_LENGTH || type == B_UNIT_MASS || type == B_UNIT_TIME || type == B_UNIT_CAMERA)) { - bUnitDef *unit_a, *unit_b; + const bUnitDef *unit_a, *unit_b; double value_a, value_b; unit_dual_convert(value, usys, &unit_a, &unit_b, &value_a, &value_b); @@ -522,7 +525,7 @@ static bool ch_is_op(char op) } } -static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pref, bUnitDef *unit, +static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit, const char *replace_str) { char *str_found; @@ -571,7 +574,7 @@ static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pr return 0; } -static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref, bUnitDef *unit) +static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit) { int ofs = 0; ofs += unit_scale_str(str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_short); @@ -581,7 +584,7 @@ static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref return ofs; } -static bool unit_find(const char *str, bUnitDef *unit) +static bool unit_find(const char *str, const bUnitDef *unit) { if (unit_find_str(str, unit->name_short)) return true; if (unit_find_str(str, unit->name_plural)) return true; @@ -591,12 +594,12 @@ static bool unit_find(const char *str, bUnitDef *unit) return false; } -static bUnitDef *unit_detect_from_str(bUnitCollection *usys, const char *str, const char *str_prev) +static const bUnitDef *unit_detect_from_str(const bUnitCollection *usys, const char *str, const char *str_prev) { /* Try to find a default unit from current or previous string. * This allows us to handle cases like 2 + 2mm, people would expect to get 4mm, not 2.002m! * Note this does not handle corner cases like 2 + 2cm + 1 + 2.5mm... We can't support everything. */ - bUnitDef *unit = NULL; + const bUnitDef *unit = NULL; /* see which units the new value has */ for (unit = usys->units; unit->name; unit++) { @@ -636,9 +639,9 @@ static bUnitDef *unit_detect_from_str(bUnitCollection *usys, const char *str, co */ bool bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) { - bUnitCollection *usys = unit_get_system(system, type); + const bUnitCollection *usys = unit_get_system(system, type); - bUnitDef *unit = NULL, *default_unit; + const bUnitDef *unit = NULL, *default_unit; double scale_pref_base = scale_pref; char str_tmp[TEMP_STR_SIZE]; bool changed = false; @@ -679,7 +682,7 @@ bool bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double sc * In other words, when in metrics, typing '2+2in' will give 2 meters 2 inches, not 4 inches. * I do think this is the desired behavior! */ - bUnitCollection *usys_iter; + const bUnitCollection *usys_iter; int system_iter; for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) { @@ -730,9 +733,9 @@ bool bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double sc /* 45µm --> 45um */ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int system, int type) { - bUnitCollection *usys = unit_get_system(system, type); + const bUnitCollection *usys = unit_get_system(system, type); - bUnitDef *unit; + const bUnitDef *unit; /* find and substitute all units */ for (unit = usys->units; unit->name; unit++) { @@ -769,8 +772,8 @@ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int syste double bUnit_ClosestScalar(double value, int system, int type) { - bUnitCollection *usys = unit_get_system(system, type); - bUnitDef *unit; + const bUnitCollection *usys = unit_get_system(system, type); + const bUnitDef *unit; if (usys == NULL) return -1; @@ -784,7 +787,7 @@ double bUnit_ClosestScalar(double value, int system, int type) double bUnit_BaseScalar(int system, int type) { - bUnitCollection *usys = unit_get_system(system, type); + const bUnitCollection *usys = unit_get_system(system, type); return unit_default(usys)->scalar; } @@ -794,34 +797,34 @@ bool bUnit_IsValid(int system, int type) return !(system < 0 || system > UNIT_SYSTEM_TOT || type < 0 || type > B_UNIT_TYPE_TOT); } -void bUnit_GetSystem(void **usys_pt, int *len, int system, int type) +void bUnit_GetSystem(int system, int type, void const **r_usys_pt, int *r_len) { - bUnitCollection *usys = unit_get_system(system, type); - *usys_pt = usys; + const bUnitCollection *usys = unit_get_system(system, type); + *r_usys_pt = usys; if (usys == NULL) { - *len = 0; + *r_len = 0; return; } - *len = usys->length; + *r_len = usys->length; } -int bUnit_GetBaseUnit(void *usys_pt) +int bUnit_GetBaseUnit(const void *usys_pt) { return ((bUnitCollection *)usys_pt)->base_unit; } -const char *bUnit_GetName(void *usys_pt, int index) +const char *bUnit_GetName(const void *usys_pt, int index) { return ((bUnitCollection *)usys_pt)->units[index].name; } -const char *bUnit_GetNameDisplay(void *usys_pt, int index) +const char *bUnit_GetNameDisplay(const void *usys_pt, int index) { return ((bUnitCollection *)usys_pt)->units[index].name_display; } -double bUnit_GetScaler(void *usys_pt, int index) +double bUnit_GetScaler(const void *usys_pt, int index) { return ((bUnitCollection *)usys_pt)->units[index].scalar; } |