Changeset View
Changeset View
Standalone View
Standalone View
source/blender/modifiers/intern/MOD_simpledeform.c
| Context not available. | |||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_stack.h" | |||||
| #include "BKE_cdderivedmesh.h" | #include "BKE_cdderivedmesh.h" | ||||
| #include "BKE_library_query.h" | #include "BKE_library_query.h" | ||||
| Context not available. | |||||
| #define BEND_EPS 0.000001f | #define BEND_EPS 0.000001f | ||||
| /* Clamps/Limits the given coordinate to: limits[0] <= co[axis] <= limits[1] | /* Scale the mesh linearly down from scale 1 to factor along Z-axis, with z reaching from abs_limit[0] to abs_limit[1]. */ | ||||
| * The amount of clamp is saved on dcut */ | static void simpleDeform_taper(float r_co[3], const float factor, const float abs_limit[2], const char locked_axis) | ||||
| static void axis_limit(int axis, const float limits[2], float co[3], float dcut[3]) | |||||
| { | |||||
| float val = co[axis]; | |||||
| if (limits[0] > val) val = limits[0]; | |||||
| if (limits[1] < val) val = limits[1]; | |||||
| dcut[axis] = co[axis] - val; | |||||
| co[axis] = val; | |||||
| } | |||||
| static void simpleDeform_taper(const float factor, const float dcut[3], float r_co[3]) | |||||
| { | { | ||||
| float x = r_co[0], y = r_co[1], z = r_co[2]; | float x = r_co[0], y = r_co[1], z = r_co[2]; | ||||
| float scale = z * factor; | float scale = 1; | ||||
| r_co[0] = x + x * scale; | |||||
| r_co[1] = y + y * scale; | |||||
| r_co[2] = z; | |||||
| { | if( abs_limit[1] <= z ) { | ||||
| r_co[0] += dcut[0]; | scale = factor; | ||||
| r_co[1] += dcut[1]; | if(factor == 0) r_co[2] = abs_limit[1]; | ||||
| r_co[2] += dcut[2]; | } | ||||
| else if (abs_limit[0] < z) { | |||||
| scale = (z * (factor-1) + abs_limit[1] - factor * abs_limit[0]) / (abs_limit[1] - abs_limit[0]); | |||||
| } | } | ||||
| } | |||||
| static void simpleDeform_stretch(const float factor, const float dcut[3], float r_co[3]) | |||||
| { | |||||
| float x = r_co[0], y = r_co[1], z = r_co[2]; | |||||
| float scale; | |||||
| scale = (z * z * factor - factor + 1.0f); | |||||
| r_co[0] = x * scale; | if(!(locked_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X)) r_co[0] = x * scale; | ||||
| r_co[1] = y * scale; | if(!(locked_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y)) r_co[1] = y * scale; | ||||
| r_co[2] = z * (1.0f + factor); | |||||
| { | |||||
| r_co[0] += dcut[0]; | |||||
| r_co[1] += dcut[1]; | |||||
| r_co[2] += dcut[2]; | |||||
| } | |||||
| } | } | ||||
| static void simpleDeform_twist(const float factor, const float *dcut, float r_co[3]) | /* Stretch the mesh factor times along the Z axis (preserving the area of the cross section with the X axis or Y axis, this does not preserve the volume). */ | ||||
| static void simpleDeform_stretch(float r_co[3], const float factor, const float abs_limit[2], const char locked_axis) | |||||
| { | { | ||||
| float x = r_co[0], y = r_co[1], z = r_co[2]; | float x = r_co[0], y = r_co[1], z = r_co[2]; | ||||
| float theta, sint, cost; | float a, b, c, l_1, l_0, scale; | ||||
| if(abs_limit[1] <= z) { | |||||
| r_co[2] += (abs_limit[1] - abs_limit[0]) * (factor -1); | |||||
| } | |||||
| else if(abs_limit[0] < z && z < abs_limit[1]) { | |||||
| z = (z - abs_limit[0]) * factor; | |||||
| l_1 = factor * (abs_limit[1] - abs_limit[0]); | |||||
| theta = z * factor; | a = - 6 * ( abs_limit[1] - abs_limit[0] - l_1) / pow3f(l_1); | ||||
| sint = sinf(theta); | b = - a * l_1; | ||||
| cost = cosf(theta); | |||||
| r_co[0] = x * cost - y * sint; | scale = a * pow2f(z) + b * z + 1; | ||||
| r_co[1] = x * sint + y * cost; | |||||
| r_co[2] = z; | |||||
| { | r_co[0] = x * scale; | ||||
| r_co[0] += dcut[0]; | r_co[1] = y * scale; | ||||
| r_co[1] += dcut[1]; | r_co[2] = abs_limit[0] + z ; | ||||
| r_co[2] += dcut[2]; | |||||
| } | } | ||||
| } | } | ||||
| static void simpleDeform_bend(const float factor, const float dcut[3], float r_co[3]) | /* Twist the mesh around the Z-axis with angle radians, starting the twist at abs_limit[0] and ending it at abs_limit[1]. */ | ||||
| static void simpleDeform_twist(float r_co[3], const float angle, const float abs_limit[2], const char locked_axis) | |||||
| { | { | ||||
| float x = r_co[0], y = r_co[1], z = r_co[2]; | float x = r_co[0], y = r_co[1], z = r_co[2]; | ||||
| float theta, sint, cost; | float theta = 0, cost, sint; | ||||
| BLI_assert(!(fabsf(factor) < BEND_EPS)); | if( abs_limit[1] <= z ) { | ||||
| theta = angle; | |||||
| } | |||||
| else if (abs_limit[0] < z) { | |||||
| theta = angle * (z - abs_limit[0]) / (abs_limit[1] - abs_limit[0]); | |||||
| } | |||||
| theta = x * factor; | |||||
| sint = sinf(theta); | |||||
| cost = cosf(theta); | cost = cosf(theta); | ||||
| sint = sinf(theta); | |||||
| r_co[0] = -(y - 1.0f / factor) * sint; | if(!(locked_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X)) r_co[0] = x * cost - y * sint ; | ||||
| r_co[1] = (y - 1.0f / factor) * cost + 1.0f / factor; | if(!(locked_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y)) r_co[1] = x * sint + y * cost ; | ||||
| r_co[2] = z; | } | ||||
| { | |||||
| r_co[0] += cost * dcut[0]; | |||||
| r_co[1] += sint * dcut[0]; | |||||
| r_co[2] += dcut[2]; | |||||
| } | |||||
| /* Bend the Z space of the mesh angle radians around the Y-axis. */ | |||||
| static void simpleDeform_bend(float r_co[3], const float angle, const float abs_limit[2], const char locked_axis) | |||||
| { | |||||
| float x = r_co[0], y = r_co[1], z = r_co[2]; | |||||
| float theta, cost, sint; | |||||
| if( abs_limit[1] <= z && fabsf(abs_limit[0]-abs_limit[1]) > FLT_EPSILON && fabsf(angle) > FLT_EPSILON ) { | |||||
| float tmp_r = (abs_limit[1] - abs_limit[0]) / angle - x; | |||||
| theta = angle; | |||||
| cost = cosf(theta); | |||||
| sint = sinf(theta); | |||||
| r_co[0] = -cost * tmp_r + tmp_r + x + (z - abs_limit[1]) * sint ; | |||||
| r_co[2] = sint * tmp_r + abs_limit[0] + (z - abs_limit[1]) * cost; | |||||
| } | |||||
| else if( abs_limit[0] < z && fabsf(abs_limit[0]-abs_limit[1]) > FLT_EPSILON && fabsf(angle) > FLT_EPSILON ) { | |||||
| float tmp_r = (abs_limit[1] - abs_limit[0]) / angle; | |||||
| theta = (z - abs_limit[0]) / tmp_r; | |||||
| r_co[0] = -cosf(theta) * (tmp_r - x) + tmp_r; | |||||
| r_co[2] = sinf(theta) * (tmp_r - x) + abs_limit[0]; | |||||
| } | |||||
| } | } | ||||
| static struct { | |||||
| int index; | |||||
| float weight; | |||||
| } vert_data; | |||||
| /* simple deform modifier */ | /* simple deform modifier */ | ||||
| static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm, | static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm, float (*vertexCos)[3], int numVerts) | ||||
| float (*vertexCos)[3], int numVerts) | |||||
| { | { | ||||
| static const float lock_axis[2] = {0.0f, 0.0f}; | |||||
| int i; | int i; | ||||
| int limit_axis = 0; | int limit_axis = 2; /* Modify along Z-axis */ | ||||
| float smd_limit[2], smd_factor; | float smd_abs_limit[2]; | ||||
| SpaceTransform *transf = NULL, tmp_transf; | SpaceTransform *transf = NULL, tmp_transf; | ||||
| void (*simpleDeform_callback)(const float factor, const float dcut[3], float co[3]) = NULL; /* Mode callback */ | void (*simpleDeform_callback)(float r_co[3], const float factor, const float abs_limit[2], const char locked_axis) = NULL; /* Mode callback */ | ||||
| int vgroup; | int vgroup; | ||||
| MDeformVert *dvert; | MDeformVert *dvert; | ||||
| BLI_Stack* vert_ind; | |||||
| /* Safe-check */ | /* Safe-check */ | ||||
| switch (smd->mode) { | |||||
| case MOD_SIMPLEDEFORM_MODE_TWIST: simpleDeform_callback = simpleDeform_twist; break; | |||||
| case MOD_SIMPLEDEFORM_MODE_BEND: simpleDeform_callback = simpleDeform_bend; break; | |||||
| case MOD_SIMPLEDEFORM_MODE_TAPER: simpleDeform_callback = simpleDeform_taper; break; | |||||
| case MOD_SIMPLEDEFORM_MODE_STRETCH: simpleDeform_callback = simpleDeform_stretch; break; | |||||
| default: | |||||
| return; /* No simpledeform mode? */ | |||||
| } | |||||
| if (smd->origin == ob) smd->origin = NULL; /* No self references */ | if (smd->origin == ob) smd->origin = NULL; /* No self references */ | ||||
| if (smd->limit[0] < 0.0f) smd->limit[0] = 0.0f; | if (smd->limit[0] < 0.0f) smd->limit[0] = 0.0f; | ||||
| if (smd->limit[0] > 1.0f) smd->limit[0] = 1.0f; | if (smd->limit[0] > 1.0f) smd->limit[0] = 1.0f; | ||||
| smd->limit[0] = min_ff(smd->limit[0], smd->limit[1]); /* Upper limit >= than lower limit */ | smd->limit[0] = min_ff(smd->limit[0], smd->limit[1]); /* Upper limit >= lower limit */ | ||||
| /* Calculate matrixs do convert between coordinate spaces */ | if (smd->limit[0] == 1) return; | ||||
| /* Calculate matrix to convert between coordinate spaces */ | |||||
| if (smd->origin) { | if (smd->origin) { | ||||
| transf = &tmp_transf; | transf = &tmp_transf; | ||||
| BLI_SPACE_TRANSFORM_SETUP(transf, ob, smd->origin); | BLI_SPACE_TRANSFORM_SETUP(transf, ob, smd->origin); | ||||
| } | } | ||||
| /* Setup vars, | /* Get the vgroup and create a stack to save index and weight of every vertex in the group.*/ | ||||
| * Bend limits on X.. all other modes limit on Z */ | modifier_get_vgroup(ob, dm, smd->vgroup_name, &dvert, &vgroup); | ||||
| limit_axis = (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) ? 0 : 2; | |||||
| /* Update limits if needed */ | vert_ind = BLI_stack_new(sizeof(vert_data),"SimpleDeformModifier_do:Temp Stack for Vgroup indices"); | ||||
| { | { | ||||
| /* Update limits relative to the vgroup */ | |||||
| float lower = FLT_MAX; | float lower = FLT_MAX; | ||||
| float upper = -FLT_MAX; | float upper = -FLT_MAX; | ||||
| for (i = 0; i < numVerts; i++) { | for (i = 0; i < numVerts; i++) { | ||||
| float tmp[3]; | float weight = defvert_array_find_weight_safe(dvert, i, vgroup); | ||||
| copy_v3_v3(tmp, vertexCos[i]); | |||||
| if (transf) { | if (weight != 0.0f) { | ||||
| BLI_space_transform_apply(transf, tmp); | vert_data.index = i; | ||||
| } | vert_data.weight = weight; | ||||
| BLI_stack_push(vert_ind, &vert_data); | |||||
| lower = min_ff(lower, tmp[limit_axis]); | |||||
| upper = max_ff(upper, tmp[limit_axis]); | |||||
| } | |||||
| /* SMD values are normalized to the BV, calculate the absolut values */ | |||||
| smd_limit[1] = lower + (upper - lower) * smd->limit[1]; | |||||
| smd_limit[0] = lower + (upper - lower) * smd->limit[0]; | |||||
| smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]); | |||||
| } | |||||
| switch (smd->mode) { | if (transf) { | ||||
| case MOD_SIMPLEDEFORM_MODE_TWIST: simpleDeform_callback = simpleDeform_twist; break; | /* Backtransformation in the deformation loop */ | ||||
| case MOD_SIMPLEDEFORM_MODE_BEND: simpleDeform_callback = simpleDeform_bend; break; | BLI_space_transform_apply(transf, vertexCos[i]); | ||||
| case MOD_SIMPLEDEFORM_MODE_TAPER: simpleDeform_callback = simpleDeform_taper; break; | } | ||||
| case MOD_SIMPLEDEFORM_MODE_STRETCH: simpleDeform_callback = simpleDeform_stretch; break; | |||||
| default: | |||||
| return; /* No simpledeform mode? */ | |||||
| } | |||||
| if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) { | lower = min_ff(lower, vertexCos[i][limit_axis]); | ||||
| if (fabsf(smd_factor) < BEND_EPS) { | upper = max_ff(upper, vertexCos[i][limit_axis]); | ||||
| return; | } | ||||
| } | } | ||||
| /* SMD values are normalized to the BV, calculate the absolut values */ | |||||
| smd_abs_limit[1] = lower + (upper - lower) * smd->limit[1]; | |||||
| smd_abs_limit[0] = lower + (upper - lower) * smd->limit[0]; | |||||
| } | } | ||||
| modifier_get_vgroup(ob, dm, smd->vgroup_name, &dvert, &vgroup); | while (!BLI_stack_is_empty(vert_ind)) { | ||||
| float weight; | |||||
| for (i = 0; i < numVerts; i++) { | float co[3]; | ||||
| float weight = defvert_array_find_weight_safe(dvert, i, vgroup); | |||||
| if (weight != 0.0f) { | BLI_stack_pop(vert_ind, &vert_data); | ||||
| float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; | |||||
| if (transf) { | i = vert_data.index; | ||||
| BLI_space_transform_apply(transf, vertexCos[i]); | weight = vert_data.weight; | ||||
| } | |||||
| copy_v3_v3(co, vertexCos[i]); | |||||
| /* Apply axis limits */ | copy_v3_v3(co, vertexCos[i]); | ||||
| if (smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) { /* Bend mode shoulnt have any lock axis */ | |||||
| if (smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) axis_limit(0, lock_axis, co, dcut); | |||||
| if (smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) axis_limit(1, lock_axis, co, dcut); | |||||
| } | |||||
| axis_limit(limit_axis, smd_limit, co, dcut); | |||||
| simpleDeform_callback(smd_factor, dcut, co); /* apply deform */ | simpleDeform_callback(co, smd->factor, smd_abs_limit, smd->axis ); /* apply deform */ | ||||
| interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight); /* Use vertex weight has coef of linear interpolation */ | interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight); /* Use vertex weight as coef of linear interpolation */ | ||||
| if (transf) { | if (transf) { | ||||
| BLI_space_transform_invert(transf, vertexCos[i]); | BLI_space_transform_invert(transf, vertexCos[i]); | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| BLI_stack_free(vert_ind); | |||||
| } | } | ||||
| Context not available. | |||||