diff options
author | Lukas Tönne <lukas.toenne@gmail.com> | 2016-05-27 17:01:49 +0300 |
---|---|---|
committer | Lukas Tönne <lukas.toenne@gmail.com> | 2016-05-27 17:01:49 +0300 |
commit | d8aaed17eb889c7acab08c89a8efd42fb5f9716d (patch) | |
tree | 4a77d67d7623546e3cdfb5cb5be64e9e4c9d65bb /source/blender/blenkernel | |
parent | e6777be1d11e8f23fb54b64cf59ad7fe084ce7c8 (diff) | |
parent | 3d86a5bc72a63b1ef8b165d68a1806d0abf0a8ac (diff) |
Merge branch 'master' into object_nodes
Diffstat (limited to 'source/blender/blenkernel')
24 files changed, 3076 insertions, 2092 deletions
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index c164cd542f3..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) 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_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 17ad51a7a16..2cdda34b9b5 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -244,7 +244,7 @@ void CustomData_free_elem(struct CustomData *data, int index, int count); */ void CustomData_interp( const struct CustomData *source, struct CustomData *dest, - int *src_indices, float *weights, float *sub_weights, + const int *src_indices, const float *weights, const float *sub_weights, int count, int dest_index); void CustomData_bmesh_interp_n( struct CustomData *data, const void **src_blocks, const float *weights, @@ -358,7 +358,7 @@ void CustomData_file_write_prepare( struct CustomDataLayer **r_write_layers, struct CustomDataLayer *write_layers_buff, size_t write_layers_size); /* query info over types */ -void CustomData_file_write_info(int type, const char **structname, int *structnum); +void CustomData_file_write_info(int type, const char **r_struct_name, int *r_struct_num); int CustomData_sizeof(int type); /* get the name of a layer type */ diff --git a/source/blender/blenkernel/BKE_dynamicpaint.h b/source/blender/blenkernel/BKE_dynamicpaint.h index 00256176d98..5abb53d4c52 100644 --- a/source/blender/blenkernel/BKE_dynamicpaint.h +++ b/source/blender/blenkernel/BKE_dynamicpaint.h @@ -82,7 +82,7 @@ void dynamicPaint_resetPreview(struct DynamicPaintCanvasSettings *canvas); struct DynamicPaintSurface *get_activeSurface(struct DynamicPaintCanvasSettings *canvas); /* image sequence baking */ -int dynamicPaint_createUVSurface(struct Scene *scene, struct DynamicPaintSurface *surface); +int dynamicPaint_createUVSurface(struct Scene *scene, struct DynamicPaintSurface *surface, float *progress, short *do_update); int dynamicPaint_calculateFrame(struct DynamicPaintSurface *surface, struct Scene *scene, struct Object *cObject, int frame); void dynamicPaint_outputSurfaceImage(struct DynamicPaintSurface *surface, char *filename, short output_layer); diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index d7dd9ed3ac5..dff79b6cc22 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -114,6 +114,11 @@ void BKE_mesh_vert_loop_map_create( MeshElemMap **r_map, int **r_mem, const struct MPoly *mface, const struct MLoop *mloop, int totvert, int totface, int totloop); +void BKE_mesh_vert_looptri_map_create( + MeshElemMap **r_map, int **r_mem, + const struct MVert *mvert, const int totvert, + const struct MLoopTri *mlooptri, const int totlooptri, + const struct MLoop *mloop, const int totloop); void BKE_mesh_vert_edge_map_create( MeshElemMap **r_map, int **r_mem, const struct MEdge *medge, int totvert, int totedge); diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 455912ab819..f6c08909d23 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -101,7 +101,8 @@ typedef enum { eModifierTypeFlag_NoUserAdd = (1 << 8), /* For modifiers that use CD_PREVIEW_MCOL for preview. */ - eModifierTypeFlag_UsesPreview = (1 << 9) + eModifierTypeFlag_UsesPreview = (1 << 9), + eModifierTypeFlag_AcceptsLattice = (1 << 10), } ModifierTypeFlag; /* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */ diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 745bb8967f3..195b5e75352 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -3333,17 +3333,18 @@ void DM_calc_loop_tangents_step_0( bool *rcalc_act, bool *rcalc_ren, int *ract_uv_n, int *rren_uv_n, char *ract_uv_name, char *rren_uv_name, char *rtangent_mask) { /* Active uv in viewport */ + int layer_index = CustomData_get_layer_index(loopData, CD_MLOOPUV); *ract_uv_n = CustomData_get_active_layer(loopData, CD_MLOOPUV); ract_uv_name[0] = 0; if (*ract_uv_n != -1) { - strcpy(ract_uv_name, loopData->layers[*ract_uv_n].name); + strcpy(ract_uv_name, loopData->layers[*ract_uv_n + layer_index].name); } /* Active tangent in render */ *rren_uv_n = CustomData_get_render_layer(loopData, CD_MLOOPUV); rren_uv_name[0] = 0; if (*rren_uv_n != -1) { - strcpy(rren_uv_name, loopData->layers[*rren_uv_n].name); + strcpy(rren_uv_name, loopData->layers[*rren_uv_n + layer_index].name); } /* If active tangent not in tangent_names we take it into account */ @@ -3693,12 +3694,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++; @@ -3714,11 +3744,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++; @@ -3734,13 +3766,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++; @@ -3755,9 +3788,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) { @@ -3821,17 +3856,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 */ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index b969c9ec787..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; + } } } } @@ -878,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; @@ -888,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) */ @@ -911,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); @@ -1280,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 :) */ @@ -1339,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); } } @@ -1375,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; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 1a3afbb876e..04b4733fd44 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -429,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]; @@ -475,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; @@ -496,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) @@ -525,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) { @@ -538,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 */ @@ -557,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 */ @@ -593,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; @@ -610,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); + } + } } @@ -669,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) @@ -683,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); diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 34d9962139c..826bb12a912 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -633,7 +633,11 @@ 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 { diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index bdf3432e6ea..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) diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index e6741657f47..af1ad4900b3 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -495,11 +495,12 @@ static void cdDM_drawFacesTex_common( CDDerivedMesh *cddm = (CDDerivedMesh *) dm; const MPoly *mpoly = cddm->mpoly; MTexPoly *mtexpoly = DM_get_poly_data_layer(dm, CD_MTEXPOLY); - const MLoopCol *mloopcol; + const MLoopCol *mloopcol = NULL; int i; int colType, start_element, tot_drawn; const bool use_hide = (flag & DM_DRAW_SKIP_HIDDEN) != 0; const bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_colors = (flag & DM_DRAW_USE_COLORS) != 0; int totpoly; int next_actualFace; int mat_index; @@ -529,15 +530,17 @@ static void cdDM_drawFacesTex_common( } } - colType = CD_TEXTURE_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - if (!mloopcol) { - colType = CD_PREVIEW_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - } - if (!mloopcol) { - colType = CD_MLOOPCOL; + if (use_colors) { + colType = CD_TEXTURE_MLOOPCOL; mloopcol = dm->getLoopDataArray(dm, colType); + if (!mloopcol) { + colType = CD_PREVIEW_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } + if (!mloopcol) { + colType = CD_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } } GPU_vertex_setup(dm); diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index bac59c8c62d..c1f1f0128f5 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -43,6 +43,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BKE_colortools.h" @@ -53,10 +54,6 @@ #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" -#ifdef _OPENMP -# include <omp.h> -#endif - /* ********************************* color curve ********************* */ /* ***************** operations on full struct ************* */ @@ -1089,31 +1086,170 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorM } /* if view_settings, it also applies this to byte buffers */ +typedef struct ScopesUpdateData { + Scopes *scopes; + const ImBuf *ibuf; + struct ColormanageProcessor *cm_processor; + const unsigned char *display_buffer; + const int ycc_mode; + + unsigned int *bin_lum, *bin_r, *bin_g, *bin_b, *bin_a; +} ScopesUpdateData; + +typedef struct ScopesUpdateDataChunk { + unsigned int bin_lum[256]; + unsigned int bin_r[256]; + unsigned int bin_g[256]; + unsigned int bin_b[256]; + unsigned int bin_a[256]; + float min[3], max[3]; +} ScopesUpdateDataChunk; + +static void scopes_update_cb(void *userdata, void *userdata_chunk, const int y, const int UNUSED(threadid)) +{ + const ScopesUpdateData *data = userdata; + + Scopes *scopes = data->scopes; + const ImBuf *ibuf = data->ibuf; + struct ColormanageProcessor *cm_processor = data->cm_processor; + const unsigned char *display_buffer = data->display_buffer; + const int ycc_mode = data->ycc_mode; + + ScopesUpdateDataChunk *data_chunk = userdata_chunk; + unsigned int *bin_lum = data_chunk->bin_lum; + unsigned int *bin_r = data_chunk->bin_r; + unsigned int *bin_g = data_chunk->bin_g; + unsigned int *bin_b = data_chunk->bin_b; + unsigned int *bin_a = data_chunk->bin_a; + float *min = data_chunk->min; + float *max = data_chunk->max; + + const float *rf = NULL; + const unsigned char *rc = NULL; + const int rows_per_sample_line = ibuf->y / scopes->sample_lines; + const int savedlines = y / rows_per_sample_line; + const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0; + const bool is_float = (ibuf->rect_float != NULL); + + if (is_float) + rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels; + else { + rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels; + } + + for (int x = 0; x < ibuf->x; x++) { + float rgba[4], ycc[3], luma; + + if (is_float) { + switch (ibuf->channels) { + case 4: + copy_v4_v4(rgba, rf); + IMB_colormanagement_processor_apply_v4(cm_processor, rgba); + break; + case 3: + copy_v3_v3(rgba, rf); + IMB_colormanagement_processor_apply_v3(cm_processor, rgba); + rgba[3] = 1.0f; + break; + case 2: + copy_v3_fl(rgba, rf[0]); + rgba[3] = rf[1]; + break; + case 1: + copy_v3_fl(rgba, rf[0]); + rgba[3] = 1.0f; + break; + default: + BLI_assert(0); + } + } + else { + for (int c = 4; c--;) + rgba[c] = rc[c] * INV_255; + } + + /* we still need luma for histogram */ + luma = IMB_colormanagement_get_luminance(rgba); + + /* check for min max */ + if (ycc_mode == -1) { + minmax_v3v3_v3(min, max, rgba); + } + else { + rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode); + mul_v3_fl(ycc, INV_255); + minmax_v3v3_v3(min, max, ycc); + } + /* increment count for histo*/ + bin_lum[get_bin_float(luma)]++; + bin_r[get_bin_float(rgba[0])]++; + bin_g[get_bin_float(rgba[1])]++; + bin_b[get_bin_float(rgba[2])]++; + bin_a[get_bin_float(rgba[3])]++; + + /* save sample if needed */ + if (do_sample_line) { + const float fx = (float)x / (float)ibuf->x; + const int idx = 2 * (ibuf->x * savedlines + x); + save_sample_line(scopes, idx, fx, rgba, ycc); + } + + rf += ibuf->channels; + rc += ibuf->channels; + } +} + +static void scopes_update_finalize(void *userdata, void *userdata_chunk) +{ + const ScopesUpdateData *data = userdata; + const ScopesUpdateDataChunk *data_chunk = userdata_chunk; + + unsigned int *bin_lum = data->bin_lum; + unsigned int *bin_r = data->bin_r; + unsigned int *bin_g = data->bin_g; + unsigned int *bin_b = data->bin_b; + unsigned int *bin_a = data->bin_a; + const unsigned int *bin_lum_c = data_chunk->bin_lum; + const unsigned int *bin_r_c = data_chunk->bin_r; + const unsigned int *bin_g_c = data_chunk->bin_g; + const unsigned int *bin_b_c = data_chunk->bin_b; + const unsigned int *bin_a_c = data_chunk->bin_a; + + float (*minmax)[2] = data->scopes->minmax; + const float *min = data_chunk->min; + const float *max = data_chunk->max; + + for (int b = 256; b--;) { + bin_lum[b] += bin_lum_c[b]; + bin_r[b] += bin_r_c[b]; + bin_g[b] += bin_g_c[b]; + bin_b[b] += bin_b_c[b]; + bin_a[b] += bin_a_c[b]; + } + + for (int c = 3; c--;) { + if (min[c] < minmax[c][0]) + minmax[c][0] = min[c]; + if (max[c] > minmax[c][1]) + minmax[c][1] = max[c]; + } +} + void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { -#ifdef _OPENMP - const int num_threads = BLI_system_thread_count(); -#endif - int a, y; + int a; unsigned int nl, na, nr, ng, nb; double divl, diva, divr, divg, divb; - unsigned char *display_buffer; + const unsigned char *display_buffer = NULL; unsigned int bin_lum[256] = {0}, bin_r[256] = {0}, bin_g[256] = {0}, bin_b[256] = {0}, bin_a[256] = {0}; - unsigned int bin_lum_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_r_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_g_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_b_t[BLENDER_MAX_THREADS][256] = {{0}}, - bin_a_t[BLENDER_MAX_THREADS][256] = {{0}}; int ycc_mode = -1; - const bool is_float = (ibuf->rect_float != NULL); void *cache_handle = NULL; struct ColormanageProcessor *cm_processor = NULL; - int rows_per_sample_line; if (ibuf->rect == NULL && ibuf->rect_float == NULL) return; @@ -1151,7 +1287,6 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings * scopes->sample_lines = ibuf->y; /* scan the image */ - rows_per_sample_line = ibuf->y / scopes->sample_lines; for (a = 0; a < 3; a++) { scopes->minmax[a][0] = 25500.0f; scopes->minmax[a][1] = -25500.0f; @@ -1177,129 +1312,21 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings * cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); } else { - display_buffer = (unsigned char *)IMB_display_buffer_acquire(ibuf, - view_settings, - display_settings, - &cache_handle); + display_buffer = (const unsigned char *)IMB_display_buffer_acquire( + ibuf, view_settings, display_settings, &cache_handle); } /* Keep number of threads in sync with the merge parts below. */ -#pragma omp parallel for private(y) schedule(static) num_threads(num_threads) if (ibuf->y > 256) - for (y = 0; y < ibuf->y; y++) { -#ifdef _OPENMP - const int thread_idx = omp_get_thread_num(); -#else - const int thread_idx = 0; -#endif - const float *rf = NULL; - const unsigned char *rc = NULL; - const int savedlines = y / rows_per_sample_line; - const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0; - float min[3] = { FLT_MAX, FLT_MAX, FLT_MAX}, - max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX}; - int x, c; - if (is_float) - rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels; - else { - rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels; - } - for (x = 0; x < ibuf->x; x++) { - float rgba[4], ycc[3], luma; - if (is_float) { - - switch (ibuf->channels) { - case 4: - copy_v4_v4(rgba, rf); - IMB_colormanagement_processor_apply_v4(cm_processor, rgba); - break; - case 3: - copy_v3_v3(rgba, rf); - IMB_colormanagement_processor_apply_v3(cm_processor, rgba); - rgba[3] = 1.0f; - break; - case 2: - copy_v3_fl(rgba, rf[0]); - rgba[3] = rf[1]; - break; - case 1: - copy_v3_fl(rgba, rf[0]); - rgba[3] = 1.0f; - break; - default: - BLI_assert(0); - } - } - else { - for (c = 0; c < 4; c++) - rgba[c] = rc[c] * INV_255; - } - - /* we still need luma for histogram */ - luma = IMB_colormanagement_get_luminance(rgba); - - /* check for min max */ - if (ycc_mode == -1) { - for (c = 0; c < 3; c++) { - if (rgba[c] < min[c]) min[c] = rgba[c]; - if (rgba[c] > max[c]) max[c] = rgba[c]; - } - } - else { - rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode); - for (c = 0; c < 3; c++) { - ycc[c] *= INV_255; - if (ycc[c] < min[c]) min[c] = ycc[c]; - if (ycc[c] > max[c]) max[c] = ycc[c]; - } - } - /* increment count for histo*/ - bin_lum_t[thread_idx][get_bin_float(luma)] += 1; - bin_r_t[thread_idx][get_bin_float(rgba[0])] += 1; - bin_g_t[thread_idx][get_bin_float(rgba[1])] += 1; - bin_b_t[thread_idx][get_bin_float(rgba[2])] += 1; - bin_a_t[thread_idx][get_bin_float(rgba[3])] += 1; - - /* save sample if needed */ - if (do_sample_line) { - const float fx = (float)x / (float)ibuf->x; - const int idx = 2 * (ibuf->x * savedlines + x); - save_sample_line(scopes, idx, fx, rgba, ycc); - } - - rf += ibuf->channels; - rc += ibuf->channels; - } -#pragma omp critical - { - for (c = 0; c < 3; c++) { - if (min[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = min[c]; - if (max[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = max[c]; - } - } - } - -#ifdef _OPENMP - if (ibuf->y > 256) { - for (a = 0; a < num_threads; a++) { - int b; - for (b = 0; b < 256; b++) { - bin_lum[b] += bin_lum_t[a][b]; - bin_r[b] += bin_r_t[a][b]; - bin_g[b] += bin_g_t[a][b]; - bin_b[b] += bin_b_t[a][b]; - bin_a[b] += bin_a_t[a][b]; - } - } - } - else -#endif - { - memcpy(bin_lum, bin_lum_t[0], sizeof(bin_lum)); - memcpy(bin_r, bin_r_t[0], sizeof(bin_r)); - memcpy(bin_g, bin_g_t[0], sizeof(bin_g)); - memcpy(bin_b, bin_b_t[0], sizeof(bin_b)); - memcpy(bin_a, bin_a_t[0], sizeof(bin_a)); - } + ScopesUpdateData data = { + .scopes = scopes, . ibuf = ibuf, + .cm_processor = cm_processor, .display_buffer = display_buffer, .ycc_mode = ycc_mode, + .bin_lum = bin_lum, .bin_r = bin_r, .bin_g = bin_g, .bin_b = bin_b, .bin_a = bin_a, + }; + ScopesUpdateDataChunk data_chunk = {0}; + INIT_MINMAX(data_chunk.min, data_chunk.max); + + BLI_task_parallel_range_finalize(0, ibuf->y, &data, &data_chunk, sizeof(data_chunk), + scopes_update_cb, scopes_update_finalize, ibuf->y > 256, false); /* test for nicer distribution even - non standard, leave it out for a while */ #if 0 diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 7144e25ba7f..a591d536b43 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -535,7 +535,7 @@ 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 (substring[0] == '\0') { @@ -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: 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 ff036828ebd..1df4effebf1 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -32,6 +32,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_kdtree.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -64,6 +65,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_mesh_mapping.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" @@ -79,9 +81,7 @@ #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__ @@ -138,8 +138,8 @@ typedef struct Bounds2D { } Bounds2D; typedef struct Bounds3D { - int valid; float min[3], max[3]; + bool valid; } Bounds3D; typedef struct VolumeGrid { @@ -150,6 +150,8 @@ typedef struct VolumeGrid { 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 { @@ -175,6 +177,7 @@ typedef struct PaintBakeData { 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 */ @@ -190,7 +193,6 @@ typedef struct PaintBakeData { 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 */ @@ -419,7 +421,8 @@ 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; @@ -526,7 +529,7 @@ 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))); } @@ -573,7 +576,7 @@ static void boundInsert(Bounds3D *b, float point[3]) if (!b->valid) { copy_v3_v3(b->min, point); copy_v3_v3(b->max, point); - b->valid = 1; + b->valid = true; return; } @@ -600,27 +603,92 @@ 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; @@ -631,27 +699,16 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) 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); - } - - MEM_freeN(grid_bounds); + /* 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 */ @@ -684,10 +741,11 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) /* allocate memory for grids */ grid->bounds = MEM_callocN(sizeof(Bounds3D) * grid_cells, "Surface Grid Bounds"); grid->s_pos = MEM_callocN(sizeof(int) * grid_cells, "Surface Grid Position"); - grid->s_num = MEM_callocN(sizeof(int) * grid_cells * num_of_threads, "Surface Grid Points"); + + grid->s_num = MEM_callocN(sizeof(int) * grid_cells, "Surface Grid Points"); temp_s_num = MEM_callocN(sizeof(int) * grid_cells, "Temp Surface Grid Points"); grid->t_index = MEM_callocN(sizeof(int) * sData->total_points, "Surface Grid Target Ids"); - temp_t_index = MEM_callocN(sizeof(int) * sData->total_points, "Temp Surface Grid Target Ids"); + grid->temp_t_index = temp_t_index = MEM_callocN(sizeof(int) * sData->total_points, "Temp Surface Grid Target Ids"); /* in case of an allocation failure abort here */ if (!grid->bounds || !grid->s_pos || !grid->s_num || !grid->t_index || !temp_s_num || !temp_t_index) @@ -695,33 +753,12 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) if (!error) { /* calculate number of points withing each cell */ -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - int co[3], j; - for (j = 0; j < 3; j++) { - co[j] = (int)floor((bData->realCoord[bData->s_pos[i]].v[j] - grid->grid_bounds.min[j]) / dim[j] * grid->dim[j]); - CLAMP(co[j], 0, grid->dim[j] - 1); - } + 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]; } @@ -734,35 +771,14 @@ 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); + grid->temp_t_index = NULL; if (error || !grid->s_num) { setError(surface->canvas, N_("Not enough free memory")); @@ -1040,7 +1056,7 @@ bool dynamicPaint_createType(struct DynamicPaintModifierData *pmd, int type, str brush->flags = MOD_DPAINT_ABS_ALPHA | MOD_DPAINT_RAMP_ALPHA; brush->collision = MOD_DPAINT_COL_VOLUME; - + brush->mat = NULL; brush->r = 0.15f; brush->g = 0.4f; @@ -1254,7 +1270,7 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const b int *temp_data; int neigh_points = 0; - if (!surface_usesAdjData(surface) && !force_init) + if (!force_init && !surface_usesAdjData(surface)) return; if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { @@ -1323,7 +1339,7 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const b ad->flags[i] |= ADJ_ON_MESH_EDGE; } - /* reset temp data */ + /* reset temp data */ temp_data[i] = 0; } @@ -1357,6 +1373,117 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const b 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; @@ -1374,7 +1501,6 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface /* Single 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); } @@ -1404,57 +1530,22 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface 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 */ @@ -1468,36 +1559,22 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface 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); - } + 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); } } } @@ -1570,6 +1647,36 @@ 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) @@ -1582,22 +1689,106 @@ static void dynamicPaint_applySurfaceDisplace(DynamicPaintSurface *surface, Deri /* 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 */ @@ -1624,127 +1815,57 @@ static DerivedMesh *dynamicPaint_Modifier_apply( /* vertex color paint */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - - int i; - PaintPoint *pPoint = (PaintPoint *)sData->type_data; - MLoopCol *col = NULL; MLoop *mloop = CDDM_get_loops(result); - int totloop = result->numLoopData; + const int totloop = result->numLoopData; + MPoly *mpoly = CDDM_get_polys(result); + const int totpoly = result->numPolyData; /* paint is stored on dry and wet layers, so mix final color first */ - float *fcolor = MEM_callocN(sizeof(float) * sData->total_points * 4, "Temp paint color"); + float (*fcolor)[4] = MEM_callocN(sizeof(*fcolor) * sData->total_points, "Temp paint color"); -#pragma omp parallel for schedule(static) - for (i = 0; i < sData->total_points; i++) { - /* blend dry and wet layer */ - blendColors(pPoint[i].color, pPoint[i].color[3], - pPoint[i].e_color, pPoint[i].e_color[3], &fcolor[i * 4]); - } - - /* viewport preview */ - if (surface->flags & MOD_DPAINT_PREVIEW) { - MPoly *mp = CDDM_get_polys(result); - int totpoly = result->numPolyData; - -#if 0 - /* XXX We have to create a CD_PREVIEW_MCOL, else it might sigsev - * (after a SubSurf mod, eg)... */ - if (!result->getTessFaceDataArray(result, CD_PREVIEW_MCOL)) { - int numFaces = result->getNumTessFaces(result); - CustomData_add_layer(&result->faceData, CD_PREVIEW_MCOL, CD_CALLOC, NULL, numFaces); - } -#endif - - /* Save preview results to weight layer to be - * able to share same drawing methods */ - col = CustomData_get_layer(&result->loopData, CD_PREVIEW_MLOOPCOL); - if (!col) { - col = CustomData_add_layer( - &result->loopData, CD_PREVIEW_MLOOPCOL, CD_CALLOC, NULL, totloop); - } - - if (col) { -#pragma omp parallel for schedule(static) - for (i = 0; i < totpoly; i++) { - Material *material = give_current_material(ob, mp[i].mat_nr + 1); - - for (int j = 0; 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 { - const char c = FTOCHAR(pPoint[v_index].wetness); - col[l_index].r = c; - col[l_index].g = c; - col[l_index].b = c; - col[l_index].a = 255; - } - } - } - } - } - - - /* 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); + if (!mloopcol && dynamicPaint_outputLayerExists(surface, ob, 0)) { + mloopcol = 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; - rgba_float_to_uchar((unsigned char *)&col[i].r, &fcolor[index]); - } - } - - 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); + 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); } - /* apply color */ - if (col) { -#pragma omp parallel for schedule(static) - for (i = 0; i < totloop; i++) { - const char c = FTOCHAR(pPoint[mloop[i].v].wetness); - col[i].r = c; - col[i].g = c; - col[i].b = c; - col[i].a = 255; + + /* 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; } @@ -1789,15 +1910,10 @@ static DerivedMesh *dynamicPaint_Modifier_apply( /* 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; } @@ -1927,255 +2043,461 @@ 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; - /* Update canvas data for a new frame */ - dynamicPaint_frameUpdate(pmd, scene, ob, dm); + /* 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 */ + ret = dynamicPaint_Modifier_apply(pmd, ob, dm); + + return ret; + } + else { + /* For now generate looptris in every case */ + DM_ensure_looptri(dm); + + /* Update canvas data for a new frame */ + dynamicPaint_frameUpdate(pmd, scene, ob, dm); + + /* Return output mesh */ + return dynamicPaint_Modifier_apply(pmd, ob, dm); + } } /***************************** Image Sequence / UV Image Surface Calls ******************************/ /* - * Tries to find the neighboring pixel in given (uv space) direction. - * Result is used by effect system to move paint on the surface. + * Create a surface for uv image sequence format + */ +#define JITTER_SAMPLES { \ + 0.0f, 0.0f, \ + -0.2f, -0.4f, \ + 0.2f, 0.4f, \ + 0.4f, -0.2f, \ + -0.4f, 0.3f, \ +} + +typedef struct DynamicPaintCreateUVSurfaceData { + const DynamicPaintSurface *surface; + + PaintUVPoint *tempPoints; + Vec3f *tempWeights; + + const MLoopTri *mlooptri; + const MLoopUV *mloopuv; + const MLoop *mloop; + const int tottri; + + const Bounds2D *faceBB; + uint32_t *active_points; +} DynamicPaintCreateUVSurfaceData; + +static void dynamic_paint_create_uv_surface_direct_cb(void *userdata, const int ty) +{ + const DynamicPaintCreateUVSurfaceData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + PaintUVPoint *tempPoints = data->tempPoints; + Vec3f *tempWeights = data->tempWeights; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + const MLoop *mloop = data->mloop; + const int tottri = data->tottri; + + const Bounds2D *faceBB = data->faceBB; + + const float jitter5sample[10] = JITTER_SAMPLES; + const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + const int w = surface->image_resolution; + const int h = w; + + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; + PaintUVPoint *tPoint = &tempPoints[index]; + float point[5][2]; + + /* Init per pixel settings */ + tPoint->tri_index = -1; + tPoint->neighbour_pixel = -1; + tPoint->pixel_index = index; + + /* Actual pixel center, used when collision is found */ + point[0][0] = ((float)tx + 0.5f) / w; + point[0][1] = ((float)ty + 0.5f) / h; + + /* + * A pixel middle sample isn't enough to find very narrow polygons + * So using 4 samples of each corner too + */ + point[1][0] = ((float)tx) / w; + point[1][1] = ((float)ty) / h; + + point[2][0] = ((float)tx + 1) / w; + point[2][1] = ((float)ty) / h; + + point[3][0] = ((float)tx) / w; + point[3][1] = ((float)ty + 1) / h; + + point[4][0] = ((float)tx + 1) / w; + point[4][1] = ((float)ty + 1) / h; + + + /* Loop through samples, starting from middle point */ + for (int sample = 0; sample < 5; sample++) { + /* Loop through every face in the mesh */ + /* XXX TODO This is *horrible* with big meshes, should use a 2D BVHTree over UV tris here! */ + for (int i = 0; i < tottri; i++) { + /* Check uv bb */ + if ((faceBB[i].min[0] > point[sample][0]) || + (faceBB[i].min[1] > point[sample][1]) || + (faceBB[i].max[0] < point[sample][0]) || + (faceBB[i].max[1] < point[sample][1])) + { + continue; + } + + const float *uv1 = mloopuv[mlooptri[i].tri[0]].uv; + const float *uv2 = mloopuv[mlooptri[i].tri[1]].uv; + const float *uv3 = mloopuv[mlooptri[i].tri[2]].uv; + + /* If point is inside the face */ + if (isect_point_tri_v2(point[sample], uv1, uv2, uv3) != 0) { + float uv[2]; + + /* Add b-weights per anti-aliasing sample */ + for (int j = 0; j < aa_samples; j++) { + uv[0] = point[0][0] + jitter5sample[j * 2] / w; + uv[1] = point[0][1] + jitter5sample[j * 2 + 1] / h; + + barycentric_weights_v2(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v); + } + + /* Set surface point face values */ + tPoint->tri_index = i; + + /* save vertex indexes */ + tPoint->v1 = mloop[mlooptri[i].tri[0]].v; + tPoint->v2 = mloop[mlooptri[i].tri[1]].v; + tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + + sample = 5; /* make sure we exit sample loop as well */ + break; + } + } + } + } +} + +static void dynamic_paint_create_uv_surface_neighbor_cb(void *userdata, const int ty) +{ + const DynamicPaintCreateUVSurfaceData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + PaintUVPoint *tempPoints = data->tempPoints; + Vec3f *tempWeights = data->tempWeights; + + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; + const MLoop *mloop = data->mloop; + + uint32_t *active_points = data->active_points; + + const float jitter5sample[10] = JITTER_SAMPLES; + const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1; + const int w = surface->image_resolution; + const int h = w; + + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; + PaintUVPoint *tPoint = &tempPoints[index]; + + /* If point isn't on canvas mesh */ + if (tPoint->tri_index == -1) { + float point[2]; + + /* get loop area */ + const int u_min = (tx > 0) ? -1 : 0; + const int u_max = (tx < (w - 1)) ? 1 : 0; + const int v_min = (ty > 0) ? -1 : 0; + const int v_max = (ty < (h - 1)) ? 1 : 0; + + point[0] = ((float)tx + 0.5f) / w; + point[1] = ((float)ty + 0.5f) / h; + + /* search through defined area for neighbor */ + for (int u = u_min; u <= u_max; u++) { + for (int v = v_min; v <= v_max; v++) { + /* if not this pixel itself */ + if (u != 0 || v != 0) { + const int ind = (tx + u) + w * (ty + v); + + /* if neighbor has index */ + if (tempPoints[ind].neighbour_pixel == -1 && tempPoints[ind].tri_index != -1) { + float uv[2]; + const int i = tempPoints[ind].tri_index; + const float *uv1 = mloopuv[mlooptri[i].tri[0]].uv; + const float *uv2 = mloopuv[mlooptri[i].tri[1]].uv; + const float *uv3 = mloopuv[mlooptri[i].tri[2]].uv; + + /* tri index */ + /* There is a low possibility of actually having a neighbor point which tri is + * already set from another neighbor in a separate thread here. + * Cheking for both tri_index and neighbour_pixel above reduces that probability + * but it remains possible. + * That atomic op (and its memory fence) ensures tPoint->neighbour_pixel is set + * to non--1 *before* its tri_index is set (i.e. that it cannot be used a neighbour). + */ + tPoint->neighbour_pixel = ind - 1; + atomic_add_uint32(&tPoint->neighbour_pixel, 1); + tPoint->tri_index = i; + + /* Now calculate pixel data for this pixel as it was on polygon surface */ + /* Add b-weights per anti-aliasing sample */ + for (int j = 0; j < aa_samples; j++) { + uv[0] = point[0] + jitter5sample[j * 2] / w; + uv[1] = point[1] + jitter5sample[j * 2 + 1] / h; + barycentric_weights_v2(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v); + } + + /* save vertex indexes */ + tPoint->v1 = mloop[mlooptri[i].tri[0]].v; + tPoint->v2 = mloop[mlooptri[i].tri[1]].v; + tPoint->v3 = mloop[mlooptri[i].tri[2]].v; + + u = u_max + 1; /* make sure we exit outer loop as well */ + break; + } + } + } + } + } + + /* Increase the final number of active surface points if relevant. */ + if (tPoint->tri_index != -1) + atomic_add_uint32(active_points, 1); + } +} + +#undef JITTER_SAMPLES + +/* Tries to find the neighboring pixel in given (uv space) direction. + * Result is used by effect system to move paint on the surface. * - * px, py : origin pixel x and y - * n_index : lookup direction index (use neighX, neighY to get final index) + * px, py : origin pixel x and y + * n_index : lookup direction index (use neighX, neighY to get final index) */ -static int dynamicPaint_findNeighbourPixel(PaintUVPoint *tempPoints, DerivedMesh *dm, - const char *uvname, int w, int h, int px, int py, int n_index) +static int dynamic_paint_find_neighbour_pixel( + const DynamicPaintCreateUVSurfaceData *data, const MeshElemMap *vert_to_looptri_map, + const int w, const int h, const int px, const int py, const int n_index) { /* Note: Current method only uses polygon edges to detect neighboring pixels. - * -> It doesn't always lead to the optimum pixel but is accurate enough - * and faster/simpler than including possible face tip point links) + * -> It doesn't always lead to the optimum pixel but is accurate enough + * and faster/simpler than including possible face tip point links) */ - int x, y; - PaintUVPoint *tPoint = NULL; - PaintUVPoint *cPoint = NULL; - /* shift position by given n_index */ - x = px + neighX[n_index]; - y = py + neighY[n_index]; + const int x = px + neighX[n_index]; + const int y = py + neighY[n_index]; if (x < 0 || x >= w || 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; - 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; - - 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; - - for (i = 0; i < tottri; i++) { - const int v0 = mloop[mlooptri[i].tri[0]].v; - const int v1 = mloop[mlooptri[i].tri[1]].v; - const int v2 = mloop[mlooptri[i].tri[2]].v; - /* - * Check if both edge vertices share this face - */ - if (ELEM(e1_index, v0, v1, v2) && ELEM(e2_index, v0, v1, v2)) { - if (i == cPoint->tri_index) - continue; + const float *s_uv1, *s_uv2, *t_uv1, *t_uv2; - target_tri = i; + pixel[0] = ((float)(px + neighX[n_index]) + 0.5f) / (float)w; + pixel[1] = ((float)(py + neighY[n_index]) + 0.5f) / (float)h; - /* 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; - } - } + /* + * Find closest edge to that pixel + */ - /* If none found pixel is on mesh edge */ - if (target_tri == -1) - return ON_MESH_EDGE; + /* 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 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; + /* 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; + } - //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]); + /* + * Now find another face that is linked to that edge + */ + target_tri = -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])); + /* 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; + + BLI_assert(ELEM(e1_index, v0, v1, v2)); + + if (ELEM(e2_index, v0, v1, v2)) { + if (lt_index == cPoint->tri_index) + continue; + + target_tri = lt_index; + + /* Get edge UV index */ + target_uv1 = (e1_index == v0) ? 0 : ((e1_index == v1) ? 1 : 2); + target_uv2 = (e2_index == v0) ? 0 : ((e2_index == v1) ? 1 : 2); + break; } + } + + /* If none found pixel is on mesh edge */ + if (target_tri == -1) + return ON_MESH_EDGE; - /* - * 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; + /* + * 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; + uint32_t active_points = 0; bool error = false; PaintSurfaceData *sData; @@ -2190,7 +2512,9 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) Bounds2D *faceBB = NULL; int *final_index; - int aa_samples; + + *progress = 0.0f; + *do_update = true; if (!dm) return setError(canvas, N_("Canvas mesh not updated")); @@ -2199,7 +2523,7 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) mloop = dm->getLoopArray(dm); mlooptri = dm->getLoopTriArray(dm); - tottri = dm->getNumLoopTri(dm); + const int tottri = dm->getNumLoopTri(dm); /* get uv map */ if (CustomData_has_layer(&dm->loopData, CD_MLOOPUV)) { @@ -2213,7 +2537,8 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) if (surface->image_resolution < 16 || surface->image_resolution > 8192) return setError(canvas, N_("Invalid resolution")); - w = h = surface->image_resolution; + const int w = surface->image_resolution; + const int h = w; /* * Start generating the surface @@ -2227,16 +2552,15 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) 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"); + 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"); + 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"); + tempWeights = MEM_mallocN(w * h * aa_samples * sizeof(*tempWeights), "Temp bWeights"); if (!tempWeights) error = true; @@ -2245,103 +2569,37 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) * the pixel-inside-a-face search. */ if (!error) { - faceBB = (struct Bounds2D *) MEM_mallocN(tottri * sizeof(struct Bounds2D), "MPCanvasFaceBB"); + 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++) { - minmax_v2v2_v2(faceBB[ty].min, faceBB[ty].max, mloopuv[mlooptri[ty].tri[i]].uv); + 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. */ -#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]); - 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 (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])) || - (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]; + *progress = 0.02f; + *do_update = true; - /* 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; + /* 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 @@ -2349,84 +2607,11 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) * If so use that polygon as pixel surface. * (To avoid seams on uv island edges) */ -#pragma omp parallel for schedule(static) - for (ty = 0; ty < h; ty++) { - int tx; - for (tx = 0; tx < w; tx++) { - int index = tx + w * ty; - PaintUVPoint *tPoint = (&tempPoints[index]); - - /* If point isn't't on canvas mesh */ - if (tPoint->tri_index == -1) { - int u_min, u_max, v_min, v_max; - int u, v, ind; - float point[2]; - - /* get loop area */ - u_min = (tx > 0) ? -1 : 0; - u_max = (tx < (w - 1)) ? 1 : 0; - v_min = (ty > 0) ? -1 : 0; - v_max = (ty < (h - 1)) ? 1 : 0; - - point[0] = ((float)tx + 0.5f) / w; - point[1] = ((float)ty + 0.5f) / h; - - /* search through defined area for neighbor */ - for (u = u_min; u <= u_max; u++) - for (v = v_min; v <= v_max; v++) { - /* if not this pixel itself */ - if (u != 0 || v != 0) { - ind = (tx + u) + w * (ty + v); - - /* if neighbor has index */ - if (tempPoints[ind].tri_index != -1) { - float uv[2]; - const int i = tempPoints[ind].tri_index; - const float *uv1 = mloopuv[mlooptri[i].tri[0]].uv; - const float *uv2 = mloopuv[mlooptri[i].tri[1]].uv; - const float *uv3 = mloopuv[mlooptri[i].tri[2]].uv; - - /* Now calculate pixel data for this pixel as it was on polygon surface */ - /* 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); - } - - /* Set values */ - tPoint->neighbour_pixel = ind; /* tri index */ + data.active_points = &active_points; + BLI_task_parallel_range(0, h, &data, dynamic_paint_create_uv_surface_neighbor_cb, h > 64); - /* 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; - } - } - } - } - } - } - - /* - * 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. */ { @@ -2446,21 +2631,27 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) 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]; @@ -2474,47 +2665,50 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) } } } + + MEM_freeN(vert_to_looptri_map); + MEM_freeN(vert_to_looptri_map_mem); } } + *progress = 0.08f; + *do_update = true; + /* Create final surface data without inactive points */ - { - ImgSeqFormatData *f_data = MEM_callocN(sizeof(struct ImgSeqFormatData), "ImgSeqFormatData"); - if (f_data) { - f_data->uv_p = MEM_callocN(active_points * sizeof(struct PaintUVPoint), "PaintUVPoint"); - f_data->barycentricWeights = MEM_callocN(active_points * aa_samples * sizeof(struct Vec3f), "PaintUVPoint"); + ImgSeqFormatData *f_data = MEM_callocN(sizeof(*f_data), "ImgSeqFormatData"); + if (f_data) { + f_data->uv_p = MEM_callocN(active_points * sizeof(*f_data->uv_p), "PaintUVPoint"); + f_data->barycentricWeights = MEM_callocN(active_points * aa_samples * sizeof(*f_data->barycentricWeights), + "PaintUVPoint"); - if (!f_data->uv_p || !f_data->barycentricWeights) - error = 1; - } - else { + if (!f_data->uv_p || !f_data->barycentricWeights) error = 1; - } + } + else { + error = 1; + } - sData->total_points = active_points; - - /* in case of allocation error, free everything */ - if (error) { - if (f_data) { - if (f_data->uv_p) - MEM_freeN(f_data->uv_p); - if (f_data->barycentricWeights) - MEM_freeN(f_data->barycentricWeights); - MEM_freeN(f_data); - } + /* in case of allocation error, free everything */ + if (error) { + if (f_data) { + if (f_data->uv_p) + MEM_freeN(f_data->uv_p); + if (f_data->barycentricWeights) + MEM_freeN(f_data->barycentricWeights); + MEM_freeN(f_data); } - else { - int index, cursor = 0; - sData->total_points = active_points; - sData->format_data = f_data; - - for (index = 0; index < (w * h); index++) { - if (tempPoints[index].tri_index != -1) { - memcpy(&f_data->uv_p[cursor], &tempPoints[index], sizeof(PaintUVPoint)); - memcpy(&f_data->barycentricWeights[cursor * aa_samples], &tempWeights[index * aa_samples], - sizeof(Vec3f) * aa_samples); - cursor++; - } + sData->total_points = 0; + } + else { + sData->total_points = (int)active_points; + sData->format_data = f_data; + + for (int index = 0, cursor = 0; index < (w * h); index++) { + if (tempPoints[index].tri_index != -1) { + memcpy(&f_data->uv_p[cursor], &tempPoints[index], sizeof(PaintUVPoint)); + memcpy(&f_data->barycentricWeights[cursor * aa_samples], &tempWeights[index * aa_samples], + sizeof(*tempWeights) * aa_samples); + cursor++; } } } @@ -2539,7 +2733,6 @@ 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++) { ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data; PaintUVPoint *uvPoint = &((PaintUVPoint *)f_data->uv_p)[index]; @@ -2550,29 +2743,116 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface) 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) + 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]; @@ -2600,74 +2880,66 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, char *filenam return; } -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int pos = f_data->uv_p[index].pixel_index * 4; /* image buffer position */ - - /* Set values of preferred type */ - if (output_layer == 1) { - /* wetmap */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - PaintPoint *point = &((PaintPoint *)sData->type_data)[index]; - float value = (point->wetness > 1.0f) ? 1.0f : point->wetness; - - copy_v3_fl(&ibuf->rect_float[pos], 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) { - mul_v3_fl(&ibuf->rect_float[pos], 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); - - copy_v3_fl(&ibuf->rect_float[pos], 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); - - copy_v3_fl(&ibuf->rect_float[pos], 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); @@ -2733,7 +3005,7 @@ static void dynamicPaint_freeBrushMaterials(BrushMaterials *bMats) * Get material diffuse color and alpha (including linked textures) in given coordinates */ static void dynamicPaint_doMaterialTex( - BrushMaterials *bMats, float color[3], float *alpha, Object *brushOb, + const BrushMaterials *bMats, float color[3], float *alpha, Object *brushOb, const float volume_co[3], const float surface_co[3], int triIndex, DerivedMesh *orcoDm) { @@ -2834,15 +3106,15 @@ static void mesh_tris_nearest_point_dp(void *userdata, int index, const float co * operations when using substeps */ static void dynamicPaint_mixPaintColors( - DynamicPaintSurface *surface, int index, int paintFlags, - const float paintColor[3], float *paintAlpha, float *paintWetness, float *timescale) + const DynamicPaintSurface *surface, const int index, const int paintFlags, + const float paintColor[3], const float paintAlpha, const float paintWetness, const float timescale) { PaintPoint *pPoint = &((PaintPoint *)surface->data->type_data)[index]; /* Add paint */ if (!(paintFlags & MOD_DPAINT_ERASE)) { float mix[4]; - float temp_alpha = (*paintAlpha) * ((paintFlags & MOD_DPAINT_ABS_ALPHA) ? 1.0f : (*timescale)); + float temp_alpha = paintAlpha * ((paintFlags & MOD_DPAINT_ABS_ALPHA) ? 1.0f : timescale); /* mix brush color with wet layer color */ blendColors(pPoint->e_color, pPoint->e_color[3], paintColor, temp_alpha, mix); @@ -2851,11 +3123,11 @@ static void dynamicPaint_mixPaintColors( /* mix wetness and alpha depending on selected alpha mode */ if (paintFlags & MOD_DPAINT_ABS_ALPHA) { /* update values to the brush level unless theyre higher already */ - CLAMP_MIN(pPoint->e_color[3], *paintAlpha); - CLAMP_MIN(pPoint->wetness, *paintWetness); + CLAMP_MIN(pPoint->e_color[3], paintAlpha); + CLAMP_MIN(pPoint->wetness, paintWetness); } else { - float wetness = (*paintWetness); + float wetness = paintWetness; CLAMP(wetness, 0.0f, 1.0f); pPoint->e_color[3] = mix[3]; pPoint->wetness = pPoint->wetness * (1.0f - wetness) + wetness; @@ -2869,7 +3141,7 @@ static void dynamicPaint_mixPaintColors( else { float a_ratio, a_highest; float wetness; - float invFact = 1.0f - (*paintAlpha); + float invFact = 1.0f - paintAlpha; /* * Make highest alpha to match erased value @@ -2885,48 +3157,57 @@ static void dynamicPaint_mixPaintColors( } } else { - pPoint->e_color[3] -= (*paintAlpha) * (*timescale); + pPoint->e_color[3] -= paintAlpha * timescale; CLAMP_MIN(pPoint->e_color[3], 0.0f); - pPoint->color[3] -= (*paintAlpha) * (*timescale); + pPoint->color[3] -= paintAlpha * timescale; CLAMP_MIN(pPoint->color[3], 0.0f); } - wetness = (1.0f - (*paintWetness)) * pPoint->e_color[3]; + wetness = (1.0f - paintWetness) * pPoint->e_color[3]; CLAMP_MAX(pPoint->wetness, wetness); } } /* applies given brush intersection value for wave surface */ -static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, DynamicPaintBrushSettings *brush, float isect_height) +static void dynamicPaint_mixWaveHeight( + PaintWavePoint *wPoint, const DynamicPaintBrushSettings *brush, float isect_height) { - float isect_change = isect_height - wPoint->brush_isect; + 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) + if (wave_factor > 0.0f && wPoint->height > isect_height) hit = true; - else if (brush->wave_factor < 0.0f && wPoint->height < isect_height) + 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; } } } @@ -2935,8 +3216,8 @@ 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) + 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; @@ -2970,7 +3251,7 @@ static void dynamicPaint_updatePointData( 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) { @@ -3029,10 +3310,47 @@ static bool meshBrush_boundsIntersect(Bounds3D *b1, Bounds3D *b2, DynamicPaintBr } /* calculate velocity for mesh vertices */ +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; @@ -3073,25 +3391,17 @@ static void dynamicPaint_brushMeshCalculateVelocity( 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); } @@ -3133,9 +3443,366 @@ static void dynamicPaint_brushObjectCalculateVelocity(Scene *scene, Object *ob, mul_v3_fl(brushVel->v, 1.0f / timescale); } +typedef struct DynamicPaintPaintData { + const DynamicPaintSurface *surface; + const DynamicPaintBrushSettings *brush; + Object *brushOb; + const BrushMaterials *bMats; + const Scene *scene; + const float timescale; + const int c_index; + + DerivedMesh *dm; + const MVert *mvert; + const MLoop *mloop; + const MLoopTri *mlooptri; + const float brush_radius; + const float *avg_brushNor; + const Vec3f *brushVelocity; + + const ParticleSystem *psys; + const float solidradius; + + void *treeData; + + float *pointCoord; +} DynamicPaintPaintData; + /* * Paint a brush object mesh to the surface */ +static void dynamic_paint_paint_mesh_cell_point_cb_ex( + void *userdata, void *UNUSED(userdata_chunk), const int id, const int UNUSED(threadid)) +{ + const DynamicPaintPaintData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; + VolumeGrid *grid = bData->grid; + + const DynamicPaintBrushSettings *brush = data->brush; + Object *brushOb = data->brushOb; + const BrushMaterials *bMats = data->bMats; + + const Scene *scene = data->scene; + const float timescale = data->timescale; + const int c_index = data->c_index; + + DerivedMesh *dm = data->dm; + const MVert *mvert = data->mvert; + const MLoop *mloop = data->mloop; + const MLoopTri *mlooptri = data->mlooptri; + const float brush_radius = data->brush_radius; + const float *avg_brushNor = data->avg_brushNor; + const Vec3f *brushVelocity = data->brushVelocity; + + BVHTreeFromMesh *treeData = data->treeData; + + const int index = grid->t_index[grid->s_pos[c_index] + id]; + const int samples = bData->s_num[index]; + int ss; + float total_sample = (float)samples; + float brushStrength = 0.0f; /* brush influence factor */ + float depth = 0.0f; /* brush intersection depth */ + float velocity_val = 0.0f; + + float paintColor[3] = {0.0f}; + int numOfHits = 0; + + /* for image sequence anti-aliasing, use gaussian factors */ + if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + total_sample = gaussianTotal; + + /* Supersampling */ + for (ss = 0; ss < samples; ss++) { + float ray_start[3], ray_dir[3]; + float sample_factor = 0.0f; + float sampleStrength = 0.0f; + BVHTreeRayHit hit; + BVHTreeNearest nearest; + short hit_found = 0; + + /* volume sample */ + float volume_factor = 0.0f; + /* proximity sample */ + float proximity_factor = 0.0f; + float prox_colorband[4] = {0.0f}; + const bool inner_proximity = (brush->flags & MOD_DPAINT_INVERSE_PROX && + brush->collision == MOD_DPAINT_COL_VOLDIST); + + /* hit data */ + float hitCoord[3]; + int hitTri = -1; + + /* Supersampling factor */ + if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) + sample_factor = gaussianFactors[ss]; + else + sample_factor = 1.0f; + + /* Get current sample position in world coordinates */ + copy_v3_v3(ray_start, bData->realCoord[bData->s_pos[index] + ss].v); + copy_v3_v3(ray_dir, bData->bNormal[index].invNorm); + + /* a simple hack to minimize chance of ray leaks at identical ray <-> edge locations */ + add_v3_fl(ray_start, 0.001f); + + hit.index = -1; + hit.dist = BVH_RAYCAST_DIST_MAX; + nearest.index = -1; + nearest.dist_sq = brush_radius * brush_radius; /* find_nearest uses squared distance */ + + /* Check volume collision */ + if (ELEM(brush->collision, MOD_DPAINT_COL_VOLUME, MOD_DPAINT_COL_VOLDIST)) { + BLI_bvhtree_ray_cast(treeData->tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); + if (hit.index != -1) { + /* We hit a triangle, now check if collision point normal is facing the point */ + + /* For optimization sake, hit point normal isn't calculated in ray cast loop */ + const int vtri[3] = { + mloop[mlooptri[hit.index].tri[0]].v, + mloop[mlooptri[hit.index].tri[1]].v, + mloop[mlooptri[hit.index].tri[2]].v, + }; + float dot; + + normal_tri_v3(hit.no, mvert[vtri[0]].co, mvert[vtri[1]].co, mvert[vtri[2]].co); + dot = dot_v3v3(ray_dir, hit.no); + + /* If ray and hit face normal are facing same direction + * hit point is inside a closed mesh. */ + if (dot >= 0.0f) { + const float dist = hit.dist; + const int f_index = hit.index; + + /* Also cast a ray in opposite direction to make sure + * point is at least surrounded by two brush faces */ + negate_v3(ray_dir); + hit.index = -1; + hit.dist = BVH_RAYCAST_DIST_MAX; + + BLI_bvhtree_ray_cast( + treeData->tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); + + if (hit.index != -1) { + /* Add factor on supersample filter */ + volume_factor = 1.0f; + hit_found = HIT_VOLUME; + + /* Mark hit info */ + madd_v3_v3v3fl(hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ + depth += dist * sample_factor; + hitTri = f_index; + } + } + } + } + + /* Check proximity collision */ + if (ELEM(brush->collision, MOD_DPAINT_COL_DIST, MOD_DPAINT_COL_VOLDIST) && + (!hit_found || (brush->flags & MOD_DPAINT_INVERSE_PROX))) + { + float proxDist = -1.0f; + float hitCo[3] = {0.0f, 0.0f, 0.0f}; + int tri = 0; + + /* if inverse prox and no hit found, skip this sample */ + if (inner_proximity && !hit_found) + continue; + + /* If pure distance proximity, find the nearest point on the mesh */ + if (!(brush->flags & MOD_DPAINT_PROX_PROJECT)) { + BLI_bvhtree_find_nearest(treeData->tree, ray_start, &nearest, mesh_tris_nearest_point_dp, treeData); + if (nearest.index != -1) { + proxDist = sqrtf(nearest.dist_sq); + copy_v3_v3(hitCo, nearest.co); + tri = nearest.index; + } + } + else { /* else cast a ray in defined projection direction */ + float proj_ray[3] = {0.0f}; + + if (brush->ray_dir == MOD_DPAINT_RAY_CANVAS) { + copy_v3_v3(proj_ray, bData->bNormal[index].invNorm); + negate_v3(proj_ray); + } + else if (brush->ray_dir == MOD_DPAINT_RAY_BRUSH_AVG) { + copy_v3_v3(proj_ray, avg_brushNor); + } + else { /* MOD_DPAINT_RAY_ZPLUS */ + proj_ray[2] = 1.0f; + } + hit.index = -1; + hit.dist = brush_radius; + + /* Do a face normal directional raycast, and use that distance */ + BLI_bvhtree_ray_cast( + treeData->tree, ray_start, proj_ray, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); + if (hit.index != -1) { + proxDist = hit.dist; + madd_v3_v3v3fl(hitCo, ray_start, proj_ray, hit.dist); /* Calculate final hit coordinates */ + tri = hit.index; + } + } + + /* If a hit was found, calculate required values */ + if (proxDist >= 0.0f && proxDist <= brush_radius) { + proximity_factor = proxDist / brush_radius; + CLAMP(proximity_factor, 0.0f, 1.0f); + if (!inner_proximity) + proximity_factor = 1.0f - proximity_factor; + + hit_found = HIT_PROXIMITY; + + /* if no volume hit, use prox point face info */ + if (hitTri == -1) { + copy_v3_v3(hitCoord, hitCo); + hitTri = tri; + } + } + } + + /* mix final sample strength depending on brush settings */ + if (hit_found) { + /* if "negate volume" enabled, negate all factors within volume*/ + if (brush->collision == MOD_DPAINT_COL_VOLDIST && + brush->flags & MOD_DPAINT_NEGATE_VOLUME) + { + volume_factor = 1.0f - volume_factor; + if (inner_proximity) + proximity_factor = 1.0f - proximity_factor; + } + + /* apply final sample depending on final hit type */ + if (hit_found == HIT_VOLUME) { + sampleStrength = volume_factor; + } + else if (hit_found == HIT_PROXIMITY) { + /* apply falloff curve to the proximity_factor */ + if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && + do_colorband(brush->paint_ramp, (1.0f - proximity_factor), prox_colorband)) + { + proximity_factor = prox_colorband[3]; + } + else if (brush->proximity_falloff == MOD_DPAINT_PRFALL_CONSTANT) { + proximity_factor = (!inner_proximity || brush->flags & MOD_DPAINT_NEGATE_VOLUME) ? 1.0f : 0.0f; + } + /* apply sample */ + sampleStrength = proximity_factor; + } + + sampleStrength *= sample_factor; + } + else { + continue; + } + + /* velocity brush, only do on main sample */ + if (brush->flags & MOD_DPAINT_USES_VELOCITY && ss == 0 && brushVelocity) { + float weights[4]; + float brushPointVelocity[3]; + float velocity[3]; + + const int v1 = mloop[mlooptri[hitTri].tri[0]].v; + const int v2 = mloop[mlooptri[hitTri].tri[1]].v; + const int v3 = mloop[mlooptri[hitTri].tri[2]].v; + + /* calculate barycentric weights for hit point */ + interp_weights_face_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, NULL, hitCoord); + + /* simple check based on brush surface velocity, + * todo: perhaps implement something that handles volume movement as well */ + + /* interpolate vertex speed vectors to get hit point velocity */ + interp_v3_v3v3v3(brushPointVelocity, + brushVelocity[v1].v, + brushVelocity[v2].v, + brushVelocity[v3].v, weights); + + /* substract canvas point velocity */ + if (bData->velocity) { + sub_v3_v3v3(velocity, brushPointVelocity, bData->velocity[index].v); + } + else { + copy_v3_v3(velocity, brushPointVelocity); + } + velocity_val = normalize_v3(velocity); + + /* if brush has smudge enabled store brush velocity */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && + brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity) + { + copy_v3_v3(&bData->brush_velocity[index * 4], velocity); + bData->brush_velocity[index * 4 + 3] = velocity_val; + } + } + + /* + * Process hit color and alpha + */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + float sampleColor[3]; + float alpha_factor = 1.0f; + + sampleColor[0] = brush->r; + sampleColor[1] = brush->g; + sampleColor[2] = brush->b; + + /* Get material+textures color on hit point if required */ + if (brush_usesMaterial(brush, scene)) { + dynamicPaint_doMaterialTex(bMats, sampleColor, &alpha_factor, brushOb, + bData->realCoord[bData->s_pos[index] + ss].v, + hitCoord, hitTri, dm); + } + + /* Sample proximity colorband if required */ + if ((hit_found == HIT_PROXIMITY) && + (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP)) + { + if (!(brush->flags & MOD_DPAINT_RAMP_ALPHA)) { + sampleColor[0] = prox_colorband[0]; + sampleColor[1] = prox_colorband[1]; + sampleColor[2] = prox_colorband[2]; + } + } + + /* Add AA sample */ + paintColor[0] += sampleColor[0]; + paintColor[1] += sampleColor[1]; + paintColor[2] += sampleColor[2]; + sampleStrength *= alpha_factor; + numOfHits++; + } + + /* apply sample strength */ + brushStrength += sampleStrength; + } // end supersampling + + + /* if any sample was inside paint range */ + if (brushStrength > 0.0f || depth > 0.0f) { + /* apply supersampling results */ + if (samples > 1) { + brushStrength /= total_sample; + } + CLAMP(brushStrength, 0.0f, 1.0f); + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + /* Get final pixel color and alpha */ + paintColor[0] /= numOfHits; + paintColor[1] /= numOfHits; + paintColor[2] /= numOfHits; + } + /* get final object space depth */ + else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + depth /= bData->bNormal[index].normal_scale * total_sample; + } + + dynamicPaint_updatePointData(surface, index, brush, paintColor, brushStrength, depth, velocity_val, timescale); + } +} + static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, DynamicPaintBrushSettings *brush, Object *brushOb, @@ -3160,7 +3827,7 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, { 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}; @@ -3207,8 +3874,6 @@ 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)) @@ -3217,335 +3882,203 @@ static int dynamicPaint_paintMesh(DynamicPaintSurface *surface, } /* 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) - 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) { - float dist = hit.dist; - int f_index = hit.index; - - /* Also cast a ray in opposite direction to make sure - * point is at least surrounded by two brush faces */ - negate_v3(ray_dir); - hit.index = -1; - hit.dist = BVH_RAYCAST_DIST_MAX; - - BLI_bvhtree_ray_cast(treeData.tree, ray_start, ray_dir, 0.0f, - &hit, mesh_tris_spherecast_dp, &treeData); - - if (hit.index != -1) { - /* Add factor on supersample filter */ - volume_factor = 1.0f; - hit_found = HIT_VOLUME; - - /* Mark hit info */ - madd_v3_v3v3fl(hitCoord, ray_start, ray_dir, hit.dist); /* Calculate final hit coordinates */ - depth += dist * sample_factor; - hitTri = f_index; - } - } - } + DynamicPaintPaintData data = { + .surface = surface, + .brush = brush, .brushOb = brushOb, .bMats = bMats, + .scene = scene, .timescale = timescale, .c_index = c_index, + .dm = dm, .mvert = mvert, .mloop = mloop, .mlooptri = mlooptri, + .brush_radius = brush_radius, .avg_brushNor = avg_brushNor, .brushVelocity = brushVelocity, + .treeData = &treeData + }; + BLI_task_parallel_range_ex(0, grid->s_num[c_index], &data, NULL, 0, + dynamic_paint_paint_mesh_cell_point_cb_ex, + grid->s_num[c_index] > 250, true); + } + } + } + /* free bvh tree */ + free_bvhtree_from_mesh(&treeData); + dm->release(dm); - /* Check proximity collision */ - if ((brush->collision == MOD_DPAINT_COL_DIST || brush->collision == MOD_DPAINT_COL_VOLDIST) && - (!hit_found || (brush->flags & MOD_DPAINT_INVERSE_PROX))) - { - float proxDist = -1.0f; - float hitCo[3] = {0.0f, 0.0f, 0.0f}; - int tri = 0; - - /* if inverse prox and no hit found, skip this sample */ - if (inner_proximity && !hit_found) continue; - - /* If pure distance proximity, find the nearest point on the mesh */ - if (!(brush->flags & MOD_DPAINT_PROX_PROJECT)) { - 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; - } - } + /* free brush velocity data */ + if (brushVelocity) + MEM_freeN(brushVelocity); - /* If a hit was found, calculate required values */ - if (proxDist >= 0.0f && proxDist <= brush_radius) { - proximity_factor = proxDist / brush_radius; - CLAMP(proximity_factor, 0.0f, 1.0f); - if (!inner_proximity) - proximity_factor = 1.0f - proximity_factor; + return 1; +} - hit_found = HIT_PROXIMITY; +/* + * Paint a particle system to the surface + */ +static void dynamic_paint_paint_particle_cell_point_cb_ex( + void *userdata, void *UNUSED(userdata_chunk), const int id, const int UNUSED(threadid)) +{ + const DynamicPaintPaintData *data = userdata; - /* if no volume hit, use prox point face info */ - if (hitTri == -1) { - copy_v3_v3(hitCoord, hitCo); - hitTri = tri; - } - } - } + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; + VolumeGrid *grid = bData->grid; - /* mix final sample strength depending on brush settings */ - if (hit_found) { - /* if "negate volume" enabled, negate all factors within volume*/ - if (brush->collision == MOD_DPAINT_COL_VOLDIST && - brush->flags & MOD_DPAINT_NEGATE_VOLUME) - { - volume_factor = 1.0f - volume_factor; - if (inner_proximity) - proximity_factor = 1.0f - proximity_factor; - } + const DynamicPaintBrushSettings *brush = data->brush; - /* apply final sample depending on final hit type */ - if (hit_found == HIT_VOLUME) { - sampleStrength = volume_factor; - } - else if (hit_found == HIT_PROXIMITY) { - /* apply falloff curve to the proximity_factor */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && - do_colorband(brush->paint_ramp, (1.0f - proximity_factor), prox_colorband)) - { - proximity_factor = prox_colorband[3]; - } - else if (brush->proximity_falloff == MOD_DPAINT_PRFALL_CONSTANT) { - proximity_factor = (!inner_proximity || brush->flags & MOD_DPAINT_NEGATE_VOLUME) ? 1.0f : 0.0f; - } - /* apply sample */ - sampleStrength = proximity_factor; - } + const ParticleSystem *psys = data->psys; - sampleStrength *= sample_factor; - } - else { - continue; - } + const float timescale = data->timescale; + const int c_index = data->c_index; - /* velocity brush, only do on main sample */ - if (brush->flags & MOD_DPAINT_USES_VELOCITY && ss == 0 && brushVelocity) { - int v1, v2, v3; - float weights[4]; - float brushPointVelocity[3]; - float velocity[3]; - - v1 = mloop[mlooptri[hitTri].tri[0]].v; - v2 = mloop[mlooptri[hitTri].tri[1]].v; - v3 = mloop[mlooptri[hitTri].tri[2]].v; - - /* calculate barycentric weights for hit point */ - interp_weights_face_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, NULL, hitCoord); - - /* simple check based on brush surface velocity, - * todo: perhaps implement something that handles volume movement as well */ - - /* interpolate vertex speed vectors to get hit point velocity */ - interp_v3_v3v3v3(brushPointVelocity, - brushVelocity[v1].v, - brushVelocity[v2].v, - brushVelocity[v3].v, weights); - - /* substract canvas point velocity */ - if (bData->velocity) { - sub_v3_v3v3(velocity, brushPointVelocity, bData->velocity[index].v); - } - else { - copy_v3_v3(velocity, brushPointVelocity); - } - velocity_val = len_v3(velocity); - - /* if brush has smudge enabled store brush velocity */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && - brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity) - { - copy_v3_v3(&bData->brush_velocity[index * 4], velocity); - mul_v3_fl(&bData->brush_velocity[index * 4], 1.0f / velocity_val); - bData->brush_velocity[index * 4 + 3] = velocity_val; - } - } + KDTree *tree = data->treeData; - /* - * Process hit color and alpha - */ - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - float sampleColor[3]; - float alpha_factor = 1.0f; - - sampleColor[0] = brush->r; - sampleColor[1] = brush->g; - sampleColor[2] = brush->b; - - /* Get material+textures color on hit point if required */ - if (brush_usesMaterial(brush, scene)) { - dynamicPaint_doMaterialTex(bMats, sampleColor, &alpha_factor, brushOb, - bData->realCoord[bData->s_pos[index] + ss].v, - hitCoord, hitTri, dm); - } + const float solidradius = data->solidradius; + const float smooth = brush->particle_smooth * surface->radius_scale; + const float range = solidradius + smooth; + const float particle_timestep = 0.04f * psys->part->timetweak; - /* Sample proximity colorband if required */ - if ((hit_found == HIT_PROXIMITY) && - (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP)) - { - if (!(brush->flags & MOD_DPAINT_RAMP_ALPHA)) { - sampleColor[0] = prox_colorband[0]; - sampleColor[1] = prox_colorband[1]; - sampleColor[2] = prox_colorband[2]; - } - } + const int index = grid->t_index[grid->s_pos[c_index] + id]; + float disp_intersect = 0.0f; + float radius = 0.0f; + float strength = 0.0f; + int part_index = -1; - /* Add AA sample */ - paintColor[0] += sampleColor[0]; - paintColor[1] += sampleColor[1]; - paintColor[2] += sampleColor[2]; - sampleStrength *= alpha_factor; - numOfHits++; - } + /* + * With predefined radius, there is no variation between particles. + * It's enough to just find the nearest one. + */ + { + KDTreeNearest nearest; + float smooth_range, part_solidradius; - /* apply sample strength */ - brushStrength += sampleStrength; - } // end supersampling + /* Find nearest particle and get distance to it */ + BLI_kdtree_find_nearest(tree, bData->realCoord[bData->s_pos[index]].v, &nearest); + /* if outside maximum range, no other particle can influence either */ + if (nearest.dist > range) + return; + if (brush->flags & MOD_DPAINT_PART_RAD) { + /* use particles individual size */ + ParticleData *pa = psys->particles + nearest.index; + part_solidradius = pa->size; + } + else { + part_solidradius = solidradius; + } + radius = part_solidradius + smooth; + if (nearest.dist < radius) { + /* distances inside solid radius has maximum influence -> dist = 0 */ + smooth_range = max_ff(0.0f, (nearest.dist - part_solidradius)); + /* do smoothness if enabled */ + if (smooth) + smooth_range /= smooth; + + strength = 1.0f - smooth_range; + disp_intersect = radius - nearest.dist; + part_index = nearest.index; + } + } + /* If using random per particle radius and closest particle didn't give max influence */ + if (brush->flags & MOD_DPAINT_PART_RAD && strength < 1.0f && psys->part->randsize > 0.0f) { + /* + * If we use per particle radius, we have to sample all particles + * within max radius range + */ + KDTreeNearest *nearest; - /* 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); + float smooth_range = smooth * (1.0f - strength), dist; + /* calculate max range that can have particles with higher influence than the nearest one */ + const float max_range = smooth - strength * smooth + solidradius; + /* Make gcc happy! */ + dist = max_range; - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - /* Get final pixel color and alpha */ - paintColor[0] /= numOfHits; - paintColor[1] /= numOfHits; - paintColor[2] /= numOfHits; - } - /* get final object space depth */ - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) - { - depth /= bData->bNormal[index].normal_scale * total_sample; - } + const int particles = BLI_kdtree_range_search( + tree, bData->realCoord[bData->s_pos[index]].v, &nearest, max_range); - dynamicPaint_updatePointData(surface, index, brush, paintColor, - brushStrength, depth, velocity_val, timescale); - } - } - } + /* Find particle that produces highest influence */ + for (int n = 0; n < particles; n++) { + ParticleData *pa = &psys->particles[nearest[n].index]; + + /* skip if out of range */ + if (nearest[n].dist > (pa->size + smooth)) + continue; + + /* update hit data */ + const float s_range = nearest[n].dist - pa->size; + /* skip if higher influence is already found */ + if (smooth_range < s_range) + continue; + + /* update hit data */ + smooth_range = s_range; + dist = nearest[n].dist; + part_index = nearest[n].index; + + /* If inside solid range and no disp depth required, no need to seek further */ + if ((s_range < 0.0f) && !ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + break; } } - /* free bvh tree */ - free_bvhtree_from_mesh(&treeData); - dm->release(dm); + if (nearest) + MEM_freeN(nearest); + + /* now calculate influence for this particle */ + const float rad = radius + smooth; + if ((rad - dist) > disp_intersect) { + disp_intersect = radius - dist; + radius = rad; + } + + /* do smoothness if enabled */ + CLAMP_MIN(smooth_range, 0.0f); + if (smooth) + smooth_range /= smooth; + + const float str = 1.0f - smooth_range; + /* if influence is greater, use this one */ + if (str > strength) + strength = str; } - /* free brush velocity data */ - if (brushVelocity) - MEM_freeN(brushVelocity); + if (strength > 0.001f) { + float paintColor[4] = {0.0f}; + float depth = 0.0f; + float velocity_val = 0.0f; - return 1; + /* apply velocity */ + if ((brush->flags & MOD_DPAINT_USES_VELOCITY) && (part_index != -1)) { + float velocity[3]; + ParticleData *pa = psys->particles + part_index; + mul_v3_v3fl(velocity, pa->state.vel, particle_timestep); + + /* substract canvas point velocity */ + if (bData->velocity) { + sub_v3_v3(velocity, bData->velocity[index].v); + } + velocity_val = normalize_v3(velocity); + + /* store brush velocity for smudge */ + if ((surface->type == MOD_DPAINT_SURFACE_T_PAINT) && + (brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity)) + { + copy_v3_v3(&bData->brush_velocity[index * 4], velocity); + bData->brush_velocity[index * 4 + 3] = velocity_val; + } + } + + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + copy_v3_v3(paintColor, &brush->r); + } + else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WAVE)) { + /* get displace depth */ + disp_intersect = (1.0f - sqrtf(disp_intersect / radius)) * radius; + depth = max_ff(0.0f, (radius - disp_intersect) / bData->bNormal[index].normal_scale); + } + + dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); + } } -/* - * Paint a particle system to the surface - */ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, ParticleSystem *psys, DynamicPaintBrushSettings *brush, @@ -3554,19 +4087,18 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, ParticleSettings *part = psys->part; PaintSurfaceData *sData = surface->data; PaintBakeData *bData = sData->bData; - VolumeGrid *grid = bData->grid; + VolumeGrid *grid = bData->grid; KDTree *tree; int particlesAdded = 0; int invalidParticles = 0; int p = 0; - float solidradius = surface->radius_scale * - ((brush->flags & MOD_DPAINT_PART_RAD) ? psys->part->size : brush->particle_radius); - float smooth = brush->particle_smooth * surface->radius_scale; + const float solidradius = surface->radius_scale * + ((brush->flags & MOD_DPAINT_PART_RAD) ? part->size : brush->particle_radius); + const float smooth = brush->particle_smooth * surface->radius_scale; - float range = solidradius + smooth; - float particle_timestep = 0.04f * part->timetweak; + const float range = solidradius + smooth; Bounds3D part_bb = {0}; @@ -3623,14 +4155,12 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, if (boundsIntersectDist(&grid->grid_bounds, &part_bb, range)) { int c_index; int total_cells = grid->dim[0] * grid->dim[1] * grid->dim[2]; - + /* balance tree */ BLI_kdtree_balance(tree); /* loop through space partitioning grid */ for (c_index = 0; c_index < total_cells; c_index++) { - int id; - /* check cell bounding box */ if (!grid->s_num[c_index] || !boundsIntersectDist(&grid->bounds[c_index], &part_bb, range)) @@ -3639,297 +4169,199 @@ static int dynamicPaint_paintParticles(DynamicPaintSurface *surface, } /* loop through cell points */ -#pragma omp parallel for schedule(static) - for (id = 0; id < grid->s_num[c_index]; id++) { - int index = grid->t_index[grid->s_pos[c_index] + id]; - float disp_intersect = 0.0f; - float radius = 0.0f; - float strength = 0.0f; - float velocity_val = 0.0f; - int part_index = -1; - - /* - * With predefined radius, there is no variation between particles. - * It's enough to just find the nearest one. - */ - { - KDTreeNearest nearest; - float smooth_range, part_solidradius; + DynamicPaintPaintData data = { + .surface = surface, + .brush = brush, .psys = psys, + .solidradius = solidradius, .timescale = timescale, .c_index = c_index, + .treeData = tree, + }; + BLI_task_parallel_range_ex(0, grid->s_num[c_index], &data, NULL, 0, + dynamic_paint_paint_particle_cell_point_cb_ex, + grid->s_num[c_index] > 250, true); + } + } + BLI_end_threaded_malloc(); + BLI_kdtree_free(tree); - /* Find nearest particle and get distance to it */ - BLI_kdtree_find_nearest(tree, bData->realCoord[bData->s_pos[index]].v, &nearest); - /* if outside maximum range, no other particle can influence either */ - if (nearest.dist > range) - continue; + return 1; +} - if (brush->flags & MOD_DPAINT_PART_RAD) { - /* use particles individual size */ - ParticleData *pa = psys->particles + nearest.index; - part_solidradius = pa->size; - } - else { - part_solidradius = solidradius; - } - radius = part_solidradius + smooth; - if (nearest.dist < radius) { - /* distances inside solid radius has maximum influence -> dist = 0 */ - smooth_range = (nearest.dist - part_solidradius); - if (smooth_range < 0.0f) smooth_range = 0.0f; - /* do smoothness if enabled */ - if (smooth) smooth_range /= smooth; - - strength = 1.0f - smooth_range; - disp_intersect = radius - nearest.dist; - part_index = nearest.index; - } - } - /* If using random per particle radius and closest particle didn't give max influence */ - if (brush->flags & MOD_DPAINT_PART_RAD && strength < 1.0f && psys->part->randsize > 0.0f) { - /* - * If we use per particle radius, we have to sample all particles - * within max radius range - */ - KDTreeNearest *nearest; - - int n, particles; - float smooth_range = smooth * (1.0f - strength), dist; - /* calculate max range that can have particles with higher influence than the nearest one */ - float max_range = smooth - strength * smooth + solidradius; - /* Make gcc happy! */ - dist = max_range; - - particles = BLI_kdtree_range_search(tree, bData->realCoord[bData->s_pos[index]].v, - &nearest, max_range); - - /* Find particle that produces highest influence */ - for (n = 0; n < particles; n++) { - ParticleData *pa = psys->particles + nearest[n].index; - float s_range; - - /* skip if out of range */ - if (nearest[n].dist > (pa->size + smooth)) - continue; - - /* update hit data */ - s_range = nearest[n].dist - pa->size; - /* skip if higher influence is already found */ - if (smooth_range < s_range) - continue; - - /* update hit data */ - smooth_range = s_range; - dist = nearest[n].dist; - part_index = nearest[n].index; - - /* If inside solid range and no disp depth required, no need to seek further */ - if ( (s_range < 0.0f) && - (surface->type != MOD_DPAINT_SURFACE_T_DISPLACE) && - (surface->type != MOD_DPAINT_SURFACE_T_WAVE)) - { - break; - } - } +/* paint a single point of defined proximity radius to the surface */ +static void dynamic_paint_paint_single_point_cb_ex( + void *userdata, void *UNUSED(userdata_chunk), const int index, const int UNUSED(threadid)) +{ + const DynamicPaintPaintData *data = userdata; - if (nearest) MEM_freeN(nearest); + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + const PaintBakeData *bData = sData->bData; - /* now calculate influence for this particle */ - { - float rad = radius + smooth, str; - if ((rad - dist) > disp_intersect) { - disp_intersect = radius - dist; - radius = rad; - } + const DynamicPaintBrushSettings *brush = data->brush; + Object *brushOb = data->brushOb; + const BrushMaterials *bMats = data->bMats; - /* do smoothness if enabled */ - if (smooth_range < 0.0f) - smooth_range = 0.0f; - if (smooth) - smooth_range /= smooth; - str = 1.0f - smooth_range; - /* if influence is greater, use this one */ - if (str > strength) - strength = str; - } - } + const Scene *scene = data->scene; + const float timescale = data->timescale; - if (strength > 0.001f) { - float paintColor[4] = {0.0f}; - float depth = 0.0f; + const MVert *mvert = data->mvert; + const float brush_radius = data->brush_radius; + const Vec3f *brushVelocity = data->brushVelocity; - /* apply velocity */ - if ((brush->flags & MOD_DPAINT_USES_VELOCITY) && (part_index != -1)) { - float velocity[3]; - ParticleData *pa = psys->particles + part_index; - mul_v3_v3fl(velocity, pa->state.vel, particle_timestep); + float *pointCoord = data->pointCoord; - /* substract canvas point velocity */ - if (bData->velocity) { - sub_v3_v3(velocity, bData->velocity[index].v); - } - velocity_val = len_v3(velocity); + const float distance = len_v3v3(pointCoord, bData->realCoord[bData->s_pos[index]].v); + float colorband[4] = {0.0f}; + float strength; - /* store brush velocity for smudge */ - if ((surface->type == MOD_DPAINT_SURFACE_T_PAINT) && - (brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity)) - { - copy_v3_v3(&bData->brush_velocity[index * 4], velocity); - mul_v3_fl(&bData->brush_velocity[index * 4], 1.0f / velocity_val); - bData->brush_velocity[index * 4 + 3] = velocity_val; - } - } + if (distance > brush_radius) + return; - if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { - copy_v3_v3(paintColor, &brush->r); - } - else if ((surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) || - (surface->type == MOD_DPAINT_SURFACE_T_WAVE)) - { - /* get displace depth */ - disp_intersect = (1.0f - sqrtf(disp_intersect / radius)) * radius; - depth = (radius - disp_intersect) / bData->bNormal[index].normal_scale; - if (depth < 0.0f) depth = 0.0f; - } - - dynamicPaint_updatePointData(surface, index, brush, paintColor, - strength, depth, velocity_val, timescale); + /* 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; + } + + if (strength >= 0.001f) { + float paintColor[3] = {0.0f}; + float depth = 0.0f; + float velocity_val = 0.0f; + + /* 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); + + /* 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; + } + } + + 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; } } } + 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); } - BLI_end_threaded_malloc(); - BLI_kdtree_free(tree); - - return 1; } -/* 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) { - int index; - float brush_radius = brush->paint_distance * surface->radius_scale; PaintSurfaceData *sData = surface->data; - PaintBakeData *bData = sData->bData; + 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 */ -#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; - - if (distance > brush_radius) - continue; + 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); - /* 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; - } + return 1; +} - if (strength >= 0.001f) { - float paintColor[3] = {0.0f}; - float depth = 0.0f; - float velocity_val = 0.0f; - /* 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); - } +/***************************** Dynamic Paint Step / Baking ******************************/ - /* color ramp */ - if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP && - do_colorband(brush->paint_ramp, (1.0f - strength), colorband)) - { - strength = colorband[3]; - } +/* + * Calculate current frame distances and directions for adjacency data + */ - if (brush->flags & MOD_DPAINT_USES_VELOCITY) { - float velocity[3]; +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; - /* 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); + const int num_neighs = adj_data->n_num[index]; - /* 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; - } - } + 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]; - 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; - } - } - } - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WAVE) - { - /* 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; - CLAMP_MIN(depth, 0.0f); - } - dynamicPaint_updatePointData(surface, index, brush, paintColor, strength, depth, velocity_val, timescale); - } + /* 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); } - - return 1; } - -/***************************** Dynamic Paint Step / Baking ******************************/ - -/* - * Calculate current frame distances and directions for adjacency data - */ 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) @@ -3937,48 +4369,32 @@ static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, cons if (bData->bNeighs) MEM_freeN(bData->bNeighs); - bNeighs = bData->bNeighs = MEM_mallocN(sData->adj_data->total_targets * sizeof(struct BakeAdjPoint), "PaintEffectBake"); + bNeighs = bData->bNeighs = MEM_mallocN(sData->adj_data->total_targets * sizeof(*bNeighs), "PaintEffectBake"); if (!bNeighs) return; -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - int i; - int numOfNeighs = adj_data->n_num[index]; + BLI_task_parallel_range( + 0, sData->total_points, sData, dynamic_paint_prepare_adjacency_cb, sData->total_points > 1000); - for (i = 0; i < numOfNeighs; i++) { - int n_index = adj_data->n_index[index] + i; - int t_index = adj_data->n_target[n_index]; - - /* dir vec */ - sub_v3_v3v3(bNeighs[n_index].dir, realCoord[bData->s_pos[t_index]].v, realCoord[bData->s_pos[index]].v); - /* dist */ - bNeighs[n_index].dist = len_v3(bNeighs[n_index].dir); - /* normalize dir */ - if (bNeighs[n_index].dist) - mul_v3_fl(bNeighs[n_index].dir, 1.0f / bNeighs[n_index].dist); - } - } - - /* calculate average values (single thread) */ - bData->average_dist = 0.0f; + /* calculate average values (single thread). + * Note: tried to put this in threaded callback (using _finalize feature), but gave ~30% slower result! */ + bData->average_dist = 0.0; for (index = 0; index < sData->total_points; index++) { - int i; int numOfNeighs = adj_data->n_num[index]; - for (i = 0; i < numOfNeighs; i++) { + for (int i = 0; i < numOfNeighs; i++) { bData->average_dist += (double)bNeighs[adj_data->n_index[index] + i].dist; } } - bData->average_dist /= adj_data->total_targets; + bData->average_dist /= adj_data->total_targets; } /* find two adjacency points (closest_id) and influence (closest_d) to move paint towards when affected by a force */ static void surface_determineForceTargetPoints( - PaintSurfaceData *sData, int index, float force[3], float closest_d[2], int closest_id[2]) + 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; @@ -3986,8 +4402,8 @@ static void surface_determineForceTargetPoints( /* 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; @@ -4000,26 +4416,28 @@ static void surface_determineForceTargetPoints( /* 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; + 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; @@ -4081,7 +4499,7 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus if (!smudge_str) continue; - + /* get force affect points */ surface_determineForceTargetPoints(sData, index, &bData->brush_velocity[index * 4], closest_d, closest_id); @@ -4114,10 +4532,80 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus } } +typedef struct DynamicPaintEffectData { + const DynamicPaintSurface *surface; + Scene *scene; + + float *force; + EffectorContext *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 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; + EffectorContext *effectors = data->effectors; + float *force = data->force; + + 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) { @@ -4127,63 +4615,24 @@ static int dynamicPaint_prepareEffectStep( 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}; - EffectorContext *effectors; - - effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true); + EffectorContext *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true); /* allocate memory for force data (dir vector + strength) */ *force = MEM_mallocN(sData->total_points * 4 * sizeof(float), "PaintEffectForces"); if (*force) { -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - float forc[3] = {0}; - - /* apply force fields */ - if (effectors) { - EffectedPoint epoint; - pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint); - epoint.vel_to_sec = 1.0f; - pdDoEffectors(effectors, NULL, surface->effector_weights, &epoint, forc, NULL); - } - - /* if global gravity is enabled, add it too */ - if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) - /* also divide by 10 to about match default grav - * with default force strength (1.0) */ - madd_v3_v3fl(forc, scene->physics_settings.gravity, - surface->effector_weights->global_gravity * surface->effector_weights->weight[0] / 10.f); - - /* add surface point velocity and acceleration if enabled */ - if (bData->velocity) { - if (surface->drip_vel) - madd_v3_v3fl(forc, bData->velocity[index].v, surface->drip_vel * (-1.0f)); - - /* acceleration */ - if (bData->prev_velocity && surface->drip_acc) { - float acc[3]; - copy_v3_v3(acc, bData->velocity[index].v); - sub_v3_v3(acc, bData->prev_velocity[index].v); - madd_v3_v3fl(forc, acc, surface->drip_acc * (-1.0f)); - } - } - - /* force strength */ - (*force)[index * 4 + 3] = len_v3(forc); - /* normalize and copy */ - if ((*force)[index * 4 + 3]) - mul_v3_fl(forc, 1.0f / (*force)[index * 4 + 3]); - copy_v3_v3(&((*force)[index * 4]), forc); - } + DynamicPaintEffectData data = { + .surface = surface, .scene = scene, + .force = *force, .effectors = effectors, + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_prepare_effect_cb, sData->total_points > 1000); /* calculate average values (single thread) */ - for (index = 0; index < sData->total_points; index++) { + for (int index = 0; index < sData->total_points; index++) { average_force += (*force)[index * 4 + 3]; } average_force /= sData->total_points; @@ -4203,7 +4652,7 @@ static int dynamicPaint_prepareEffectStep( 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; @@ -4212,13 +4661,185 @@ static int dynamicPaint_prepareEffectStep( /** * Processes active effect step. */ +static void dynamic_paint_effect_spread_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; + + 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; - BakeAdjPoint *bNeighs = sData->bData->bNeighs; - float distance_scale = getSurfaceDimension(sData) / CANVAS_REL_SIZE; - int index; + + const float distance_scale = getSurfaceDimension(sData) / CANVAS_REL_SIZE; timescale /= steps; if (!sData->adj_data) @@ -4228,159 +4849,149 @@ static void dynamicPaint_doEffectStep( * 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; + const float dt = data->dt; + const float min_dist = data->min_dist; + const float damp_factor = data->damp_factor; - 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]); - } + 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; - /* decrease paint wetness on current point */ - pPoint->wetness -= (ePoint->wetness - e_wet); - CLAMP(pPoint->wetness, 0.0f, MAX_WETNESS); - } - } + 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) @@ -4390,11 +5001,11 @@ 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"); @@ -4426,172 +5037,114 @@ static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescal /* copy previous frame data */ memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(PaintWavePoint)); -#pragma omp parallel for schedule(static) - for (index = 0; index < sData->total_points; index++) { - PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index]; - int numOfNeighs = sData->adj_data->n_num[index]; - float force = 0.0f, avg_dist = 0.0f, avg_height = 0.0f, avg_n_height = 0.0f; - int numOfN = 0, numOfRN = 0; - int i; + DynamicPaintEffectData data = { + .surface = surface, .prevPoint = prevPoint, + .wave_speed = wave_speed, .wave_scale = wave_scale, .wave_max_slope = wave_max_slope, + .dt = dt, .min_dist = min_dist, .damp_factor = damp_factor, .reset_wave = (ss == steps - 1), + }; + BLI_task_parallel_range( + 0, sData->total_points, &data, dynamic_paint_wave_step_cb, sData->total_points > 1000); + } - if (wPoint->state > 0) - continue; + MEM_freeN(prevPoint); +} - /* calculate force from surrounding points */ - for (i = 0; i < numOfNeighs; i++) { - int n_index = sData->adj_data->n_index[index] + i; - float dist = bNeighs[n_index].dist * wave_scale; - PaintWavePoint *tPoint = &prevPoint[sData->adj_data->n_target[n_index]]; +/* Do dissolve and fading effects */ +static bool dynamic_paint_surface_needs_dry_dissolve(DynamicPaintSurface *surface) +{ + return (((surface->type == MOD_DPAINT_SURFACE_T_PAINT) && + (surface->flags & (MOD_DPAINT_USE_DRYING | MOD_DPAINT_DISSOLVE))) || + (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WEIGHT) && + (surface->flags & MOD_DPAINT_DISSOLVE))); +} - if (!dist || tPoint->state > 0) - continue; - CLAMP_MIN(dist, min_dist); - avg_dist += dist; - numOfN++; - - /* 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) != 0); - CLAMP_MIN(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) != 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); + 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) != 0); - 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 bool dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob) @@ -4603,7 +5156,6 @@ static bool dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *o int numOfVerts = dm->getNumVerts(dm); int i; - bool ret = false; if (!bData->prev_verts) return true; @@ -4613,24 +5165,140 @@ static bool dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *o return true; /* vertices */ -#pragma omp parallel for schedule(static) for (i = 0; i < numOfVerts; i++) { if (!equals_v3v3(bData->prev_verts[i].co, mvert[i].co)) { - ret = true; + return true; } } - return ret; + return false; } /* Prepare for surface step by creating PaintBakeNormal data */ +typedef struct DynamicPaintGenerateBakeData { + const DynamicPaintSurface *surface; + Object *ob; + + const MVert *mvert; + const Vec3f *canvas_verts; + + const bool do_velocity_data; + const bool new_bdata; +} DynamicPaintGenerateBakeData; + +static void dynamic_paint_generate_bake_data_cb(void *userdata, const int index) +{ + const DynamicPaintGenerateBakeData *data = userdata; + + 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); + } +} + 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 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; @@ -4691,7 +5359,7 @@ static int dynamicPaint_generateBakeData(DynamicPaintSurface *surface, const Sce return setError(surface->canvas, N_("Not enough free memory")); } - new_bdata = 1; + new_bdata = true; } if (do_velocity_data && !bData->velocity) { @@ -4707,7 +5375,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); @@ -4717,105 +5385,13 @@ 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); @@ -4842,10 +5418,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 (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 */ @@ -5007,8 +5589,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; diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 45007b327ff..911bf21b731 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -1748,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 @@ -1869,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'; + } } } @@ -1943,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( @@ -2056,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; @@ -2219,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/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/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 1ba4852623c..65ee153e00b 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -244,6 +244,11 @@ bool BKE_object_support_modifier_type_check(Object *ob, int modifier_type) mti = modifierType_getInfo(modifier_type); + + if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsLattice) == 0) { + return false; + } + if (!((mti->flags & eModifierTypeFlag_AcceptsCVs) || (ob->type == OB_MESH && (mti->flags & eModifierTypeFlag_AcceptsMesh)))) { diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 8901b274db9..af30c1af66b 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -4225,8 +4225,8 @@ void psys_make_billboard(ParticleBillboardData *bb, float xvec[3], float yvec[3] /* can happen with bad pointcache or physics calculation * since this becomes geometry, nan's and inf's crash raytrace code. * better not allow this. */ - if ((!finite(bb->vec[0])) || (!finite(bb->vec[1])) || (!finite(bb->vec[2])) || - (!finite(bb->vel[0])) || (!finite(bb->vel[1])) || (!finite(bb->vel[2])) ) + if ((!isfinite(bb->vec[0])) || (!isfinite(bb->vec[1])) || (!isfinite(bb->vec[2])) || + (!isfinite(bb->vel[0])) || (!isfinite(bb->vel[1])) || (!isfinite(bb->vel[2])) ) { zero_v3(bb->vec); zero_v3(bb->vel); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 27abbb62762..d307ba1811b 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -545,7 +545,7 @@ void BKE_scene_init(Scene *sce) sce->r.bake.im_format.compress = 15; sce->r.scemode = R_DOCOMP | R_DOSEQ | R_EXTENSION; - sce->r.stamp = R_STAMP_TIME | R_STAMP_FRAME | R_STAMP_DATE | R_STAMP_CAMERA | R_STAMP_SCENE | R_STAMP_FILENAME | R_STAMP_RENDERTIME; + sce->r.stamp = R_STAMP_TIME | R_STAMP_FRAME | R_STAMP_DATE | R_STAMP_CAMERA | R_STAMP_SCENE | R_STAMP_FILENAME | R_STAMP_RENDERTIME | R_STAMP_MEMORY; sce->r.stamp_font_id = 12; sce->r.fg_stamp[0] = sce->r.fg_stamp[1] = sce->r.fg_stamp[2] = 0.8f; sce->r.fg_stamp[3] = 1.0f; diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index a84b8352417..5fd418fadfc 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -3370,13 +3370,14 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, CCGSubSurf *ss = ccgdm->ss; CCGKey key; int colType; - const MLoopCol *mloopcol; + const MLoopCol *mloopcol = NULL; MTexPoly *mtexpoly = DM_get_poly_data_layer(dm, CD_MTEXPOLY); DMFlagMat *faceFlags = ccgdm->faceFlags; DMDrawOption draw_option; int i, totpoly; bool flush; - bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_tface = (flag & DM_DRAW_USE_ACTIVE_UV) != 0; + const bool use_colors = (flag & DM_DRAW_USE_COLORS) != 0; unsigned int next_actualFace; unsigned int gridFaces = ccgSubSurf_getGridSize(ss) - 1; int mat_index; @@ -3395,15 +3396,17 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, CCG_key_top_level(&key, ss); ccgdm_pbvh_update(ccgdm); - colType = CD_TEXTURE_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - if (!mloopcol) { - colType = CD_PREVIEW_MLOOPCOL; - mloopcol = dm->getLoopDataArray(dm, colType); - } - if (!mloopcol) { - colType = CD_MLOOPCOL; + if (use_colors) { + colType = CD_TEXTURE_MLOOPCOL; mloopcol = dm->getLoopDataArray(dm, colType); + if (!mloopcol) { + colType = CD_PREVIEW_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } + if (!mloopcol) { + colType = CD_MLOOPCOL; + mloopcol = dm->getLoopDataArray(dm, colType); + } } GPU_vertex_setup(dm); |