Patch #6113 - Cast & Smooth modifiers

This patch adds two modifiers: Cast and Smooth.

The Cast modifier deforms vertices into a sphere, cylinder or cuboid shape.
The location and orientation of the projection shape can be controlled by a
specified control object.

The Smooth modifier smooths the mesh in a similar way to the Edit mode "Smooth"
button.

Thanks to Willian Padovani Germano (ianwill) for the patch!
This commit is contained in:
2007-04-29 18:13:55 +00:00
parent d0ac7ef087
commit 125c77bca3
5 changed files with 1101 additions and 7 deletions

View File

@@ -3318,6 +3318,809 @@ exit:
return result;
}
/* Smooth */
static void smoothModifier_initData(ModifierData *md)
{
SmoothModifierData *smd = (SmoothModifierData*) md;
smd->fac = 0.5f;
smd->repeat = 1;
smd->flag = MOD_SMOOTH_X | MOD_SMOOTH_Y | MOD_SMOOTH_Z;
smd->defgrp_name[0] = '\0';
}
static void smoothModifier_copyData(ModifierData *md, ModifierData *target)
{
SmoothModifierData *smd = (SmoothModifierData*) md;
SmoothModifierData *tsmd = (SmoothModifierData*) target;
tsmd->fac = smd->fac;
tsmd->repeat = smd->repeat;
tsmd->flag = smd->flag;
strncpy(tsmd->defgrp_name, smd->defgrp_name, 32);
}
int smoothModifier_isDisabled(ModifierData *md)
{
SmoothModifierData *smd = (SmoothModifierData*) md;
short flag;
flag = smd->flag & (MOD_SMOOTH_X|MOD_SMOOTH_Y|MOD_SMOOTH_Z);
/* disable if modifier is off for X, Y and Z or if factor is 0 */
if((smd->fac == 0.0f) || flag == 0) return 1;
return 0;
}
CustomDataMask smoothModifier_requiredDataMask(ModifierData *md)
{
SmoothModifierData *smd = (SmoothModifierData *)md;
CustomDataMask dataMask = 0;
/* ask for vertexgroups if we need them */
if(smd->defgrp_name[0]) dataMask |= (1 << CD_MDEFORMVERT);
return dataMask;
}
static void smoothModifier_do(
SmoothModifierData *smd, Object *ob, DerivedMesh *dm,
float (*vertexCos)[3], int numVerts)
{
MDeformVert *dvert = NULL;
MEdge *medges = NULL;
int i, j, numDMEdges, defgrp_index;
unsigned char *uctmp;
float *ftmp, fac, facm;
ftmp = (float*)MEM_callocN(3*sizeof(float)*numVerts,
"smoothmodifier_f");
if (!ftmp) return;
uctmp = (unsigned char*)MEM_callocN(sizeof(unsigned char)*numVerts,
"smoothmodifier_uc");
if (!uctmp) {
if (ftmp) MEM_freeN(ftmp);
return;
}
fac = smd->fac;
facm = 1 - fac;
medges = CDDM_get_edges(dm);
numDMEdges = dm->getNumEdges(dm);
defgrp_index = -1;
if (smd->defgrp_name[0]) {
bDeformGroup *def;
for (i = 0, def = ob->defbase.first; def; def = def->next, i++) {
if (!strcmp(def->name, smd->defgrp_name)) {
defgrp_index = i;
break;
}
}
}
if (defgrp_index >= 0)
dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
/* NOTICE: this can be optimized a little bit by moving the
* if (dvert) out of the loop, if needed */
for (j = 0; j < smd->repeat; j++) {
for (i = 0; i < numDMEdges; i++) {
float fvec[3];
float *v1, *v2;
unsigned int idx1, idx2;
idx1 = medges[i].v1;
idx2 = medges[i].v2;
v1 = vertexCos[idx1];
v2 = vertexCos[idx2];
fvec[0] = (v1[0] + v2[0]) / 2.0;
fvec[1] = (v1[1] + v2[1]) / 2.0;
fvec[2] = (v1[2] + v2[2]) / 2.0;
v1 = &ftmp[idx1*3];
v2 = &ftmp[idx2*3];
if (uctmp[idx1] < 255) {
uctmp[idx1]++;
VecAddf(v1, v1, fvec);
}
if (uctmp[idx2] < 255) {
uctmp[idx2]++;
VecAddf(v2, v2, fvec);
}
}
if (dvert) {
for (i = 0; i < numVerts; i++) {
MDeformWeight *dw = NULL;
float f, fm, facw, *fp, *v;
int k;
short flag = smd->flag;
v = vertexCos[i];
fp = &ftmp[i*3];
for (k = 0; k < dvert[i].totweight; ++k) {
if(dvert[i].dw[k].def_nr == defgrp_index) {
dw = &dvert[i].dw[k];
break;
}
}
if (!dw) continue;
f = fac * dw->weight;
fm = 1.0f - f;
/* fp is the sum of uctmp[i] verts, so must be averaged */
facw = 0.0f;
if (uctmp[i])
facw = f / (float)uctmp[i];
if (flag & MOD_SMOOTH_X)
v[0] = fm * v[0] + facw * fp[0];
if (flag & MOD_SMOOTH_Y)
v[1] = fm * v[1] + facw * fp[1];
if (flag & MOD_SMOOTH_Z)
v[2] = fm * v[2] + facw * fp[2];
}
}
else { /* no vertex group */
for (i = 0; i < numVerts; i++) {
float facw, *fp, *v;
short flag = smd->flag;
v = vertexCos[i];
fp = &ftmp[i*3];
/* fp is the sum of uctmp[i] verts, so must be averaged */
facw = 0.0f;
if (uctmp[i])
facw = fac / (float)uctmp[i];
if (flag & MOD_SMOOTH_X)
v[0] = facm * v[0] + facw * fp[0];
if (flag & MOD_SMOOTH_Y)
v[1] = facm * v[1] + facw * fp[1];
if (flag & MOD_SMOOTH_Z)
v[2] = facm * v[2] + facw * fp[2];
}
}
memset(ftmp, 0, 3*sizeof(float)*numVerts);
memset(uctmp, 0, sizeof(unsigned char)*numVerts);
}
MEM_freeN(ftmp);
MEM_freeN(uctmp);
}
static void smoothModifier_deformVerts(
ModifierData *md, Object *ob, DerivedMesh *derivedData,
float (*vertexCos)[3], int numVerts)
{
DerivedMesh *dm;
if(derivedData) dm = CDDM_copy(derivedData);
else dm = CDDM_from_mesh(ob->data, ob);
CDDM_apply_vert_coords(dm, vertexCos);
CDDM_calc_normals(dm);
smoothModifier_do((SmoothModifierData *)md, ob, dm,
vertexCos, numVerts);
dm->release(dm);
}
static void smoothModifier_deformVertsEM(
ModifierData *md, Object *ob, EditMesh *editData,
DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts)
{
DerivedMesh *dm;
if(derivedData) dm = CDDM_copy(derivedData);
else dm = CDDM_from_editmesh(editData, ob->data);
CDDM_apply_vert_coords(dm, vertexCos);
CDDM_calc_normals(dm);
smoothModifier_do((SmoothModifierData *)md, ob, dm,
vertexCos, numVerts);
dm->release(dm);
}
/* Cast */
static void castModifier_initData(ModifierData *md)
{
CastModifierData *cmd = (CastModifierData*) md;
cmd->fac = 0.5f;
cmd->radius = 0.0f;
cmd->size = 0.0f;
cmd->flag = MOD_CAST_X | MOD_CAST_Y | MOD_CAST_Z
| MOD_CAST_SIZE_FROM_RADIUS;
cmd->type = MOD_CAST_TYPE_SPHERE;
cmd->defgrp_name[0] = '\0';
cmd->object = NULL;
}
static void castModifier_copyData(ModifierData *md, ModifierData *target)
{
CastModifierData *cmd = (CastModifierData*) md;
CastModifierData *tcmd = (CastModifierData*) target;
tcmd->fac = cmd->fac;
tcmd->radius = cmd->radius;
tcmd->size = cmd->size;
tcmd->flag = cmd->flag;
tcmd->type = cmd->type;
tcmd->object = cmd->object;
strncpy(tcmd->defgrp_name, cmd->defgrp_name, 32);
}
int castModifier_isDisabled(ModifierData *md)
{
CastModifierData *cmd = (CastModifierData*) md;
short flag;
flag = cmd->flag & (MOD_CAST_X|MOD_CAST_Y|MOD_CAST_Z);
if((cmd->fac == 0.0f) || flag == 0) return 1;
return 0;
}
CustomDataMask castModifier_requiredDataMask(ModifierData *md)
{
CastModifierData *cmd = (CastModifierData *)md;
CustomDataMask dataMask = 0;
/* ask for vertexgroups if we need them */
if(cmd->defgrp_name[0]) dataMask |= (1 << CD_MDEFORMVERT);
return dataMask;
}
static void castModifier_foreachObjectLink(
ModifierData *md, Object *ob,
void (*walk)(void *userData, Object *ob, Object **obpoin),
void *userData)
{
CastModifierData *cmd = (CastModifierData*) md;
walk (userData, ob, &cmd->object);
}
static void castModifier_updateDepgraph(
ModifierData *md, DagForest *forest, Object *ob,
DagNode *obNode)
{
CastModifierData *cmd = (CastModifierData*) md;
if (cmd->object) {
DagNode *curNode = dag_get_node(forest, cmd->object);
dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA);
}
}
static void castModifier_sphere_do(
CastModifierData *cmd, Object *ob, DerivedMesh *dm,
float (*vertexCos)[3], int numVerts)
{
MDeformVert *dvert = NULL;
Object *ctrl_ob = NULL;
int i, defgrp_index = -1;
int has_radius = 0;
short flag, type;
float fac, facm, len = 0.0f;
float vec[3], center[3] = {0.0f, 0.0f, 0.0f};
float mat[4][4], imat[4][4];
fac = cmd->fac;
facm = 1.0f - fac;
flag = cmd->flag;
type = cmd->type; /* projection type: sphere or cylinder */
ctrl_ob = cmd->object;
/* spherify's center is {0, 0, 0} (the ob's own center in its local
* space), by default, but if the user defined a control object,
* we use its location, transformed to ob's local space */
if (ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4Invert(ctrl_ob->imat, ctrl_ob->obmat);
Mat4MulMat4(mat, ob->obmat, ctrl_ob->imat);
Mat4Invert(imat, mat);
}
Mat4Invert(ob->imat, ob->obmat);
VECCOPY(center, ctrl_ob->obmat[3]);
Mat4MulVecfl(ob->imat, center);
}
/* now we check which options the user wants */
/* 1) (flag was checked in the "if (ctrl_ob)" block above) */
/* 2) cmd->radius > 0.0f: only the vertices within this radius from
* the center of the effect should be deformed */
if (cmd->radius > FLT_EPSILON) has_radius = 1;
/* 3) if we were given a vertex group name,
* only those vertices should be affected */
if (cmd->defgrp_name[0]) {
bDeformGroup *def;
for (i = 0, def = ob->defbase.first; def; def = def->next, i++) {
if (!strcmp(def->name, cmd->defgrp_name)) {
defgrp_index = i;
break;
}
}
}
if ((ob->type == OB_MESH) && dm && defgrp_index >= 0)
dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
if(cmd->flag & MOD_CAST_SIZE_FROM_RADIUS) {
len = cmd->radius;
}
else {
len = cmd->size;
}
if(len <= 0) {
for (i = 0; i < numVerts; i++) {
len += VecLenf(center, vertexCos[i]);
}
len /= numVerts;
if (len == 0.0f) len = 10.0f;
}
/* ready to apply the effect, one vertex at a time;
* tiny optimization: the code is separated (with parts repeated)
* in two possible cases:
* with or w/o a vgroup. With lots of if's in the code below,
* further optimizations are possible, if needed */
if (dvert) { /* with a vgroup */
float fac_orig = fac;
for (i = 0; i < numVerts; i++) {
MDeformWeight *dw = NULL;
int j;
float tmp_co[3];
VECCOPY(tmp_co, vertexCos[i]);
if(ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4MulVecfl(mat, tmp_co);
} else {
VecSubf(tmp_co, tmp_co, center);
}
}
VECCOPY(vec, tmp_co);
if (type == MOD_CAST_TYPE_CYLINDER)
vec[2] = 0.0f;
if (has_radius) {
if (VecLength(vec) > cmd->radius) continue;
}
for (j = 0; j < dvert[i].totweight; ++j) {
if(dvert[i].dw[j].def_nr == defgrp_index) {
dw = &dvert[i].dw[j];
break;
}
}
if (!dw) continue;
fac = fac_orig * dw->weight;
facm = 1.0f - fac;
Normalize(vec);
if (flag & MOD_CAST_X)
tmp_co[0] = fac*vec[0]*len + facm*tmp_co[0];
if (flag & MOD_CAST_Y)
tmp_co[1] = fac*vec[1]*len + facm*tmp_co[1];
if (flag & MOD_CAST_Z && type != MOD_CAST_TYPE_CYLINDER)
tmp_co[2] = fac*vec[2]*len + facm*tmp_co[2];
if(ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4MulVecfl(imat, tmp_co);
} else {
VecAddf(tmp_co, tmp_co, center);
}
}
VECCOPY(vertexCos[i], tmp_co);
}
return;
}
/* no vgroup */
for (i = 0; i < numVerts; i++) {
float tmp_co[3];
VECCOPY(tmp_co, vertexCos[i]);
if(ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4MulVecfl(mat, tmp_co);
} else {
VecSubf(tmp_co, tmp_co, center);
}
}
VECCOPY(vec, tmp_co);
if (type == MOD_CAST_TYPE_CYLINDER)
vec[2] = 0.0f;
if (has_radius) {
if (VecLength(vec) > cmd->radius) continue;
}
Normalize(vec);
if (flag & MOD_CAST_X)
tmp_co[0] = fac*vec[0]*len + facm*tmp_co[0];
if (flag & MOD_CAST_Y)
tmp_co[1] = fac*vec[1]*len + facm*tmp_co[1];
if (flag & MOD_CAST_Z && type != MOD_CAST_TYPE_CYLINDER)
tmp_co[2] = fac*vec[2]*len + facm*tmp_co[2];
if(ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4MulVecfl(imat, tmp_co);
} else {
VecAddf(tmp_co, tmp_co, center);
}
}
VECCOPY(vertexCos[i], tmp_co);
}
}
static void castModifier_cuboid_do(
CastModifierData *cmd, Object *ob, DerivedMesh *dm,
float (*vertexCos)[3], int numVerts)
{
MDeformVert *dvert = NULL;
Object *ctrl_ob = NULL;
int i, defgrp_index = -1;
int has_radius = 0;
short flag, type;
float fac, facm;
float min[3], max[3], bb[8][3];
float center[3] = {0.0f, 0.0f, 0.0f};
float mat[4][4], imat[4][4];
fac = cmd->fac;
facm = 1.0f - fac;
flag = cmd->flag;
type = cmd->type; /* projection type: sphere or cylinder */
ctrl_ob = cmd->object;
/* now we check which options the user wants */
/* 1) (flag was checked in the "if (ctrl_ob)" block above) */
/* 2) cmd->radius > 0.0f: only the vertices within this radius from
* the center of the effect should be deformed */
if (cmd->radius > FLT_EPSILON) has_radius = 1;
/* 3) if we were given a vertex group name,
* only those vertices should be affected */
if (cmd->defgrp_name[0]) {
bDeformGroup *def;
for (i = 0, def = ob->defbase.first; def; def = def->next, i++) {
if (!strcmp(def->name, cmd->defgrp_name)) {
defgrp_index = i;
break;
}
}
}
if ((ob->type == OB_MESH) && dm && defgrp_index >= 0)
dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
if (ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4Invert(ctrl_ob->imat, ctrl_ob->obmat);
Mat4MulMat4(mat, ob->obmat, ctrl_ob->imat);
Mat4Invert(imat, mat);
}
Mat4Invert(ob->imat, ob->obmat);
VECCOPY(center, ctrl_ob->obmat[3]);
Mat4MulVecfl(ob->imat, center);
}
if((cmd->flag & MOD_CAST_SIZE_FROM_RADIUS) && has_radius) {
for(i = 0; i < 3; i++) {
min[i] = -cmd->radius;
max[i] = cmd->radius;
}
} else if(!(cmd->flag & MOD_CAST_SIZE_FROM_RADIUS) && cmd->size > 0) {
for(i = 0; i < 3; i++) {
min[i] = -cmd->size;
max[i] = cmd->size;
}
} else {
/* get bound box */
/* We can't use the object's bound box because other modifiers
* may have changed the vertex data. */
INIT_MINMAX(min, max);
/* Cast's center is the ob's own center in its local space,by default,
* but if the user defined a control object, we use its location,
* transformed to ob's local space. */
if (ctrl_ob) {
float vec[3];
/* let the center of the ctrl_ob be part of the bound box: */
DO_MINMAX(center, min, max);
for (i = 0; i < numVerts; i++) {
VecSubf(vec, vertexCos[i], center);
DO_MINMAX(vec, min, max);
}
}
else {
for (i = 0; i < numVerts; i++) {
DO_MINMAX(vertexCos[i], min, max);
}
}
/* we want a symmetric bound box around the origin */
if (fabs(min[0]) > fabs(max[0])) max[0] = fabs(min[0]);
if (fabs(min[1]) > fabs(max[1])) max[1] = fabs(min[1]);
if (fabs(min[2]) > fabs(max[2])) max[2] = fabs(min[2]);
min[0] = -max[0];
min[1] = -max[1];
min[2] = -max[2];
}
/* building our custom bounding box */
bb[0][0] = bb[2][0] = bb[4][0] = bb[6][0] = min[0];
bb[1][0] = bb[3][0] = bb[5][0] = bb[7][0] = max[0];
bb[0][1] = bb[1][1] = bb[4][1] = bb[5][1] = min[1];
bb[2][1] = bb[3][1] = bb[6][1] = bb[7][1] = max[1];
bb[0][2] = bb[1][2] = bb[2][2] = bb[3][2] = min[2];
bb[4][2] = bb[5][2] = bb[6][2] = bb[7][2] = max[2];
/* ready to apply the effect, one vertex at a time;
* tiny optimization: the code is separated (with parts repeated)
* in two possible cases:
* with or w/o a vgroup. With lots of if's in the code below,
* further optimizations are possible, if needed */
if (dvert) { /* with a vgroup */
float fac_orig = fac;
for (i = 0; i < numVerts; i++) {
MDeformWeight *dw = NULL;
int j, octant, coord;
float d[3], dmax, apex[3], fbb;
float tmp_co[3];
VECCOPY(tmp_co, vertexCos[i]);
if(ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4MulVecfl(mat, tmp_co);
} else {
VecSubf(tmp_co, tmp_co, center);
}
}
if (has_radius) {
if (fabs(tmp_co[0]) > cmd->radius ||
fabs(tmp_co[1]) > cmd->radius ||
fabs(tmp_co[2]) > cmd->radius) continue;
}
for (j = 0; j < dvert[i].totweight; ++j) {
if(dvert[i].dw[j].def_nr == defgrp_index) {
dw = &dvert[i].dw[j];
break;
}
}
if (!dw) continue;
fac = fac_orig * dw->weight;
facm = 1.0f - fac;
/* The algo used to project the vertices to their
* bounding box (bb) is pretty simple:
* for each vertex v:
* 1) find in which octant v is in;
* 2) find which outer "wall" of that octant is closer to v;
* 3) calculate factor (var fbb) to project v to that wall;
* 4) project. */
/* find in which octant this vertex is in */
octant = 0;
if (tmp_co[0] > 0.0f) octant += 1;
if (tmp_co[1] > 0.0f) octant += 2;
if (tmp_co[2] > 0.0f) octant += 4;
/* apex is the bb's vertex at the chosen octant */
VecCopyf(apex, bb[octant]);
/* find which bb plane is closest to this vertex ... */
d[0] = tmp_co[0] / apex[0];
d[1] = tmp_co[1] / apex[1];
d[2] = tmp_co[2] / apex[2];
/* ... (the closest has the higher (closer to 1) d value) */
dmax = d[0];
coord = 0;
if (d[1] > dmax) {
dmax = d[1];
coord = 1;
}
if (d[2] > dmax) {
/* dmax = d[2]; */ /* commented, we don't need it */
coord = 2;
}
/* ok, now we know which coordinate of the vertex to use */
if (fabs(tmp_co[coord]) < FLT_EPSILON) /* avoid division by zero */
continue;
/* finally, this is the factor we wanted, to project the vertex
* to its bounding box (bb) */
fbb = apex[coord] / tmp_co[coord];
/* calculate the new vertex position */
if (flag & MOD_CAST_X)
tmp_co[0] = facm * tmp_co[0] + fac * tmp_co[0] * fbb;
if (flag & MOD_CAST_Y)
tmp_co[1] = facm * tmp_co[1] + fac * tmp_co[1] * fbb;
if (flag & MOD_CAST_Z)
tmp_co[2] = facm * tmp_co[2] + fac * tmp_co[2] * fbb;
if(ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4MulVecfl(imat, tmp_co);
} else {
VecAddf(tmp_co, tmp_co, center);
}
}
VECCOPY(vertexCos[i], tmp_co);
}
return;
}
/* no vgroup (check previous case for comments about the code) */
for (i = 0; i < numVerts; i++) {
int octant, coord;
float d[3], dmax, fbb, apex[3];
float tmp_co[3];
VECCOPY(tmp_co, vertexCos[i]);
if(ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4MulVecfl(mat, tmp_co);
} else {
VecSubf(tmp_co, tmp_co, center);
}
}
if (has_radius) {
if (fabs(tmp_co[0]) > cmd->radius ||
fabs(tmp_co[1]) > cmd->radius ||
fabs(tmp_co[2]) > cmd->radius) continue;
}
octant = 0;
if (tmp_co[0] > 0.0f) octant += 1;
if (tmp_co[1] > 0.0f) octant += 2;
if (tmp_co[2] > 0.0f) octant += 4;
VecCopyf(apex, bb[octant]);
d[0] = tmp_co[0] / apex[0];
d[1] = tmp_co[1] / apex[1];
d[2] = tmp_co[2] / apex[2];
dmax = d[0];
coord = 0;
if (d[1] > dmax) {
dmax = d[1];
coord = 1;
}
if (d[2] > dmax) {
/* dmax = d[2]; */ /* commented, we don't need it */
coord = 2;
}
if (fabs(tmp_co[coord]) < FLT_EPSILON)
continue;
fbb = apex[coord] / tmp_co[coord];
if (flag & MOD_CAST_X)
tmp_co[0] = facm * tmp_co[0] + fac * tmp_co[0] * fbb;
if (flag & MOD_CAST_Y)
tmp_co[1] = facm * tmp_co[1] + fac * tmp_co[1] * fbb;
if (flag & MOD_CAST_Z)
tmp_co[2] = facm * tmp_co[2] + fac * tmp_co[2] * fbb;
if(ctrl_ob) {
if(flag & MOD_CAST_USE_OB_TRANSFORM) {
Mat4MulVecfl(imat, tmp_co);
} else {
VecAddf(tmp_co, tmp_co, center);
}
}
VECCOPY(vertexCos[i], tmp_co);
}
}
static void castModifier_deformVerts(
ModifierData *md, Object *ob, DerivedMesh *derivedData,
float (*vertexCos)[3], int numVerts)
{
DerivedMesh *dm = derivedData;
CastModifierData *cmd = (CastModifierData *)md;
if (!dm && ob->type == OB_MESH)
dm = CDDM_from_mesh(ob->data, ob);
if (cmd->type == MOD_CAST_TYPE_CUBOID) {
castModifier_cuboid_do(cmd, ob, dm, vertexCos, numVerts);
} else { /* MOD_CAST_TYPE_SPHERE or MOD_CAST_TYPE_CYLINDER */
castModifier_sphere_do(cmd, ob, dm, vertexCos, numVerts);
}
if (!derivedData && dm) dm->release(dm);
}
static void castModifier_deformVertsEM(
ModifierData *md, Object *ob, EditMesh *editData,
DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts)
{
DerivedMesh *dm = derivedData;
CastModifierData *cmd = (CastModifierData *)md;
if (!dm && ob->type == OB_MESH)
dm = CDDM_from_editmesh(editData, ob->data);
if (cmd->type == MOD_CAST_TYPE_CUBOID) {
castModifier_cuboid_do(cmd, ob, dm, vertexCos, numVerts);
} else { /* MOD_CAST_TYPE_SPHERE or MOD_CAST_TYPE_CYLINDER */
castModifier_sphere_do(cmd, ob, dm, vertexCos, numVerts);
}
if (!derivedData && dm) dm->release(dm);
}
/* Wave */
static void waveModifier_initData(ModifierData *md)
@@ -4130,6 +4933,28 @@ ModifierTypeInfo *modifierType_getInfo(ModifierType type)
mti->copyData = decimateModifier_copyData;
mti->applyModifier = decimateModifier_applyModifier;
mti = INIT_TYPE(Smooth);
mti->type = eModifierTypeType_OnlyDeform;
mti->flags = eModifierTypeFlag_AcceptsMesh
| eModifierTypeFlag_SupportsEditmode;
mti->initData = smoothModifier_initData;
mti->copyData = smoothModifier_copyData;
mti->requiredDataMask = smoothModifier_requiredDataMask;
mti->deformVerts = smoothModifier_deformVerts;
mti->deformVertsEM = smoothModifier_deformVertsEM;
mti = INIT_TYPE(Cast);
mti->type = eModifierTypeType_OnlyDeform;
mti->flags = eModifierTypeFlag_AcceptsCVs
| eModifierTypeFlag_SupportsEditmode;
mti->initData = castModifier_initData;
mti->copyData = castModifier_copyData;
mti->requiredDataMask = castModifier_requiredDataMask;
mti->foreachObjectLink = castModifier_foreachObjectLink;
mti->updateDepgraph = castModifier_updateDepgraph;
mti->deformVerts = castModifier_deformVerts;
mti->deformVertsEM = castModifier_deformVertsEM;
mti = INIT_TYPE(Wave);
mti->type = eModifierTypeType_OnlyDeform;
mti->flags = eModifierTypeFlag_AcceptsCVs

View File

@@ -26,6 +26,8 @@ typedef enum ModifierType {
eModifierType_EdgeSplit,
eModifierType_Displace,
eModifierType_UVProject,
eModifierType_Smooth,
eModifierType_Cast,
NUM_MODIFIER_TYPES
} ModifierType;
@@ -233,6 +235,42 @@ typedef struct DecimateModifierData {
int faceCount;
} DecimateModifierData;
/* Smooth modifier flags */
#define MOD_SMOOTH_X 1<<1
#define MOD_SMOOTH_Y 1<<2
#define MOD_SMOOTH_Z 1<<3
typedef struct SmoothModifierData {
ModifierData modifier;
float fac;
char defgrp_name[32];
short flag, repeat;
} SmoothModifierData;
/* Cast modifier flags */
#define MOD_CAST_X 1<<1
#define MOD_CAST_Y 1<<2
#define MOD_CAST_Z 1<<3
#define MOD_CAST_USE_OB_TRANSFORM 1<<4
#define MOD_CAST_SIZE_FROM_RADIUS 1<<5
/* Cast modifier projection types */
#define MOD_CAST_TYPE_SPHERE 0
#define MOD_CAST_TYPE_CYLINDER 1
#define MOD_CAST_TYPE_CUBOID 2
typedef struct CastModifierData {
ModifierData modifier;
struct Object *object;
float fac;
float radius;
float size;
char defgrp_name[32];
short flag, type;
} CastModifierData;
enum {
MOD_WAV_MAP_LOCAL,
MOD_WAV_MAP_GLOBAL,

View File

@@ -67,14 +67,18 @@ enum mod_constants {
/*GENERIC*/
EXPP_MOD_OBJECT, /*ARMATURE, LATTICE, CURVE, BOOLEAN, ARRAY*/
EXPP_MOD_VERTGROUP, /*ARMATURE, LATTICE, CURVE*/
EXPP_MOD_VERTGROUP, /*ARMATURE, LATTICE, CURVE, SMOOTH, CAST*/
EXPP_MOD_LIMIT, /*ARRAY, MIRROR*/
EXPP_MOD_FLAG, /*MIRROR, WAVE*/
EXPP_MOD_COUNT, /*DECIMATOR, ARRAY*/
EXPP_MOD_LENGTH, /*BUILD, ARRAY*/
EXPP_MOD_FACTOR, /*SMOOTH, CAST*/
EXPP_MOD_ENABLE_X, /*SMOOTH, CAST*/
EXPP_MOD_ENABLE_Y, /*SMOOTH, CAST*/
EXPP_MOD_ENABLE_Z, /*SMOOTH, CAST*/
EXPP_MOD_TYPES, /*SUBSURF, CAST*/
/*SUBSURF SPESIFIC*/
EXPP_MOD_TYPES,
EXPP_MOD_LEVELS,
EXPP_MOD_RENDLEVELS,
EXPP_MOD_OPTIMAL,
@@ -126,7 +130,16 @@ enum mod_constants {
EXPP_MOD_STRENGTH,
EXPP_MOD_TEXTURE,
EXPP_MOD_MAPPING,
EXPP_MOD_DIRECTION
EXPP_MOD_DIRECTION,
/* SMOOTH */
EXPP_MOD_REPEAT,
/* CAST */
EXPP_MOD_RADIUS,
EXPP_MOD_SIZE,
EXPP_MOD_USE_OB_TRANSFORM,
EXPP_MOD_SIZE_FROM_RADIUS
/* yet to be implemented */
/* EXPP_MOD_HOOK_,*/
@@ -548,6 +561,133 @@ static int decimate_setter( BPy_Modifier *self, int type, PyObject *value )
return EXPP_ReturnIntError( PyExc_KeyError, "key not found" );
}
static PyObject *smooth_getter( BPy_Modifier * self, int type )
{
SmoothModifierData *md = (SmoothModifierData *)(self->md);
switch( type ) {
case EXPP_MOD_FACTOR:
return PyFloat_FromDouble( (double)md->fac );
case EXPP_MOD_REPEAT:
return PyInt_FromLong( (long)md->repeat );
case EXPP_MOD_VERTGROUP:
return PyString_FromString( md->defgrp_name ) ;
case EXPP_MOD_ENABLE_X:
return EXPP_getBitfield( &md->flag, MOD_SMOOTH_X, 'h' );
case EXPP_MOD_ENABLE_Y:
return EXPP_getBitfield( &md->flag, MOD_SMOOTH_Y, 'h' );
case EXPP_MOD_ENABLE_Z:
return EXPP_getBitfield( &md->flag, MOD_SMOOTH_Z, 'h' );
default:
return EXPP_ReturnPyObjError( PyExc_KeyError, "key not found" );
}
}
static int smooth_setter( BPy_Modifier *self, int type, PyObject *value )
{
SmoothModifierData *md = (SmoothModifierData *)(self->md);
switch( type ) {
case EXPP_MOD_FACTOR:
return EXPP_setFloatClamped( value, &md->fac, -10.0, 10.0 );
case EXPP_MOD_REPEAT:
return EXPP_setIValueRange( value, &md->repeat, 0, 30, 'h' );
case EXPP_MOD_VERTGROUP: {
char *name = PyString_AsString( value );
if( !name ) return EXPP_ReturnIntError( PyExc_TypeError, "expected string arg" );
BLI_strncpy( md->defgrp_name, name, sizeof( md->defgrp_name ) );
return 0;
}
case EXPP_MOD_ENABLE_X:
return EXPP_setBitfield( value, &md->flag, MOD_SMOOTH_X, 'h' );
case EXPP_MOD_ENABLE_Y:
return EXPP_setBitfield( value, &md->flag, MOD_SMOOTH_Y, 'h' );
case EXPP_MOD_ENABLE_Z:
return EXPP_setBitfield( value, &md->flag, MOD_SMOOTH_Z, 'h' );
default:
return EXPP_ReturnIntError( PyExc_KeyError, "key not found" );
}
}
static PyObject *cast_getter( BPy_Modifier * self, int type )
{
CastModifierData *md = (CastModifierData *)(self->md);
switch( type ) {
case EXPP_MOD_TYPES:
return PyInt_FromLong( (long)md->type );
case EXPP_MOD_FACTOR:
return PyFloat_FromDouble( (double)md->fac );
case EXPP_MOD_RADIUS:
return PyFloat_FromDouble( (double)md->radius );
case EXPP_MOD_SIZE:
return PyFloat_FromDouble( (double)md->size );
case EXPP_MOD_OBJECT:
return Object_CreatePyObject( md->object );
case EXPP_MOD_VERTGROUP:
return PyString_FromString( md->defgrp_name ) ;
case EXPP_MOD_ENABLE_X:
return EXPP_getBitfield( &md->flag, MOD_CAST_X, 'h' );
case EXPP_MOD_ENABLE_Y:
return EXPP_getBitfield( &md->flag, MOD_CAST_Y, 'h' );
case EXPP_MOD_ENABLE_Z:
return EXPP_getBitfield( &md->flag, MOD_CAST_Z, 'h' );
case EXPP_MOD_USE_OB_TRANSFORM:
return EXPP_getBitfield( &md->flag, MOD_CAST_USE_OB_TRANSFORM, 'h' );
case EXPP_MOD_SIZE_FROM_RADIUS:
return EXPP_getBitfield( &md->flag, MOD_CAST_SIZE_FROM_RADIUS, 'h' );
default:
return EXPP_ReturnPyObjError( PyExc_KeyError, "key not found" );
}
}
static int cast_setter( BPy_Modifier *self, int type, PyObject *value )
{
CastModifierData *md = (CastModifierData *)(self->md);
switch( type ) {
case EXPP_MOD_TYPES:
return EXPP_setIValueRange( value, &md->type, 0, MOD_CAST_TYPE_CUBOID, 'h' );
case EXPP_MOD_FACTOR:
return EXPP_setFloatClamped( value, &md->fac, -10.0, 10.0 );
case EXPP_MOD_RADIUS:
return EXPP_setFloatClamped( value, &md->radius, 0.0, 100.0 );
case EXPP_MOD_SIZE:
return EXPP_setFloatClamped( value, &md->size, 0.0, 100.0 );
case EXPP_MOD_OBJECT: {
Object *ob_new=NULL;
if (value == Py_None) {
md->object = NULL;
} else if (BPy_Object_Check( value )) {
ob_new = ((( BPy_Object * )value)->object);
md->object = ob_new;
} else {
return EXPP_ReturnIntError( PyExc_TypeError,
"Expected an Object or None value" );
}
return 0;
}
case EXPP_MOD_VERTGROUP: {
char *name = PyString_AsString( value );
if( !name ) return EXPP_ReturnIntError( PyExc_TypeError, "expected string arg" );
BLI_strncpy( md->defgrp_name, name, sizeof( md->defgrp_name ) );
return 0;
}
case EXPP_MOD_ENABLE_X:
return EXPP_setBitfield( value, &md->flag, MOD_CAST_X, 'h' );
case EXPP_MOD_ENABLE_Y:
return EXPP_setBitfield( value, &md->flag, MOD_CAST_Y, 'h' );
case EXPP_MOD_ENABLE_Z:
return EXPP_setBitfield( value, &md->flag, MOD_CAST_Z, 'h' );
case EXPP_MOD_USE_OB_TRANSFORM:
return EXPP_setBitfield( value, &md->flag, MOD_CAST_USE_OB_TRANSFORM, 'h' );
case EXPP_MOD_SIZE_FROM_RADIUS:
return EXPP_setBitfield( value, &md->flag, MOD_CAST_SIZE_FROM_RADIUS, 'h' );
default:
return EXPP_ReturnIntError( PyExc_KeyError, "key not found" );
}
}
static PyObject *wave_getter( BPy_Modifier * self, int type )
{
WaveModifierData *md = (WaveModifierData *)(self->md);
@@ -866,6 +1006,10 @@ static PyObject *Modifier_getData( BPy_Modifier * self, PyObject * key )
return mirror_getter( self, setting );
case eModifierType_Decimate:
return decimate_getter( self, setting );
case eModifierType_Smooth:
return smooth_getter( self, setting );
case eModifierType_Cast:
return cast_getter( self, setting );
case eModifierType_Wave:
return wave_getter( self, setting );
case eModifierType_Boolean:
@@ -934,6 +1078,10 @@ static int Modifier_setData( BPy_Modifier * self, PyObject * key,
return array_setter( self, key_int, arg );
case eModifierType_Decimate:
return decimate_setter( self, key_int, arg );
case eModifierType_Smooth:
return smooth_setter( self, key_int, arg );
case eModifierType_Cast:
return cast_setter( self, key_int, arg );
case eModifierType_Wave:
return wave_setter( self, key_int, arg );
case eModifierType_Boolean:
@@ -1347,6 +1495,10 @@ static PyObject *M_Modifier_TypeDict( void )
PyInt_FromLong( eModifierType_Array ) );
PyConstant_Insert( d, "EDGESPLIT",
PyInt_FromLong( eModifierType_EdgeSplit ) );
PyConstant_Insert( d, "SMOOTH",
PyInt_FromLong( eModifierType_Smooth ) );
PyConstant_Insert( d, "CAST",
PyInt_FromLong( eModifierType_Cast ) );
}
return S;
}
@@ -1407,6 +1559,14 @@ for var in st.replace(',','').split('\n'):
PyInt_FromLong( EXPP_MOD_COUNT ) );
PyConstant_Insert( d, "LENGTH",
PyInt_FromLong( EXPP_MOD_LENGTH ) );
PyConstant_Insert( d, "FACTOR",
PyInt_FromLong( EXPP_MOD_FACTOR ) );
PyConstant_Insert( d, "ENABLE_X",
PyInt_FromLong( EXPP_MOD_ENABLE_X ) );
PyConstant_Insert( d, "ENABLE_Y",
PyInt_FromLong( EXPP_MOD_ENABLE_Y ) );
PyConstant_Insert( d, "ENABLE_Z",
PyInt_FromLong( EXPP_MOD_ENABLE_Z ) );
PyConstant_Insert( d, "TYPES",
PyInt_FromLong( EXPP_MOD_TYPES ) );
PyConstant_Insert( d, "LEVELS",
@@ -1471,6 +1631,22 @@ for var in st.replace(',','').split('\n'):
PyInt_FromLong( EXPP_MOD_MID_LEVEL ) );
PyConstant_Insert( d, "STRENGTH",
PyInt_FromLong( EXPP_MOD_STRENGTH ) );
PyConstant_Insert( d, "TEXTURE",
PyInt_FromLong( EXPP_MOD_TEXTURE ) );
PyConstant_Insert( d, "MAPPING",
PyInt_FromLong( EXPP_MOD_MAPPING ) );
PyConstant_Insert( d, "DIRECTION",
PyInt_FromLong( EXPP_MOD_DIRECTION ) );
PyConstant_Insert( d, "REPEAT",
PyInt_FromLong( EXPP_MOD_REPEAT ) );
PyConstant_Insert( d, "RADIUS",
PyInt_FromLong( EXPP_MOD_RADIUS ) );
PyConstant_Insert( d, "SIZE",
PyInt_FromLong( EXPP_MOD_SIZE ) );
PyConstant_Insert( d, "USE_OB_TRANSFORM",
PyInt_FromLong( EXPP_MOD_USE_OB_TRANSFORM ) );
PyConstant_Insert( d, "SIZE_FROM_RADIUS",
PyInt_FromLong( EXPP_MOD_SIZE_FROM_RADIUS ) );
/*End Auto generated code*/
}
return S;

View File

@@ -4,7 +4,7 @@
The Blender.Modifier submodule
B{New}:
- provides access to Blender's modifier stack
- Supports the new Cast and Smooth modifiers.
This module provides access to the Modifier Data in Blender.
@@ -57,6 +57,9 @@ Example::
- SUBSURF - type value for Subsurf modifiers
- WAVE - type value for Wave modifiers
- EDGESPLIT - type value for Edge Split modifiers
- DISPLACE - type value for Displace modifiers
- SMOOTH - type value for Smooth modifiers
- CAST - type value for Cast modifiers
@type Settings: readonly dictionary
@var Settings: Constant Modifier dict used for changing modifier settings.
@@ -66,13 +69,17 @@ Example::
- ONCAGE - Used for all modifiers (bool) If true, the modifier is enabled for the editing cage during edit mode.
- OBJECT - Used for Armature, Lattice, Curve, Boolean and Array (Object)
- VERTGROUP - Used for Armature, Lattice and Curve (String)
- VERTGROUP - Used for Armature, Lattice, Curve, Smooth and Cast (String)
- LIMIT - Array and Mirror (float [0.0 - 1.0])
- FLAG - Mirror and Wave (int)
- COUNT - Decimator Polycount (readonly) and Array (int)
- LENGTH - Build [1.0-300000.0] and Array [0.0 - 10000.0] (float)
- TYPES - Used for Subsurf only. Determines the subdivision algorithm. Integer: 0 = Catmull-Clark; 1 = simple subdivision.
- FACTOR - Smooth [-10.0, 10.0] and Cast [-10.0, 10.0] (float)
- ENABLE_X = Smooth and Cast (bool, default: True)
- ENABLE_Y = Smooth and Cast (bool, default: True)
- ENABLE_Z = Smooth and Cast (bool, default: True)
- TYPES - Subsurf and Cast. For Subsurf it determines the subdivision algorithm - (int): 0 = Catmull-Clark; 1 = simple subdivision. For Cast it determines the shape to deform to = (int): 0 = Sphere; 1 = Cylinder; 2 = Cuboid
- LEVELS - Used for Subsurf only (int [0 - 6]). The number of subdivision levels used for interactive display.
- RENDLEVELS - Used for Subsurf only (int [0 - 6]). The number of subdivision levels used for rendering.
- OPTIMAL - Used for Subsurf only (bool). Enables Optimal Draw.
@@ -109,6 +116,18 @@ Example::
- EDGESPLIT_ANGLE - Used for edge split only (float 0.0 - 180)
- EDGESPLIT_FROM_ANGLE - Used for edge split only, should the modifier use the edge angle (bool)
- EDGESPLIT_FROM_SHARP - Used for edge split only, should the modifier use the edge sharp flag (bool)
- UVLAYER - Used for Displace only
- MID_LEVEL - Used for Displace only (float [0.0, 1.0], default: 0.5)
- STRENGTH - Used for Displace only (float [-1000.0, 1000.0, default: 1.0)
- TEXTURE - Used for Displace only (string)
- MAPPING - Used for Displace only
- DIRECTION - Used for Displace only
- REPEAT - Used for Smooth only (int [0, 30], default: 1)
- RADIUS - Used for Cast only (float [0.0, 100.0], default: 0.0)
- USE_OB_SCALE - Used for Cast only (bool, default: False)
"""
class ModSeq:

View File

@@ -1601,6 +1601,11 @@ static void draw_modifier(uiBlock *block, Object *ob, ModifierData *md, int *xco
height = 114 + ((UVProjectModifierData *)md)->num_projectors * 19;
} else if (md->type==eModifierType_Decimate) {
height = 48;
} else if (md->type==eModifierType_Smooth) {
height = 86;
} else if (md->type==eModifierType_Cast) {
height = 124;
height += 19;
} else if (md->type==eModifierType_Wave) {
WaveModifierData *wmd = (WaveModifierData *)md;
height = 280;
@@ -1825,6 +1830,37 @@ static void draw_modifier(uiBlock *block, Object *ob, ModifierData *md, int *xco
uiDefButF(block, NUM, B_MODIFIER_RECALC, "Ratio:", lx,(cy-=19),buttonWidth,19, &dmd->percent, 0.0, 1.0, 10, 0, "Defines the percentage of triangles to reduce to");
sprintf(str, "Face Count: %d", dmd->faceCount);
uiDefBut(block, LABEL, 1, str, lx, (cy-=19), 160,19, NULL, 0.0, 0.0, 0, 0, "Displays the current number of faces in the decimated mesh");
} else if (md->type==eModifierType_Smooth) {
SmoothModifierData *smd = (SmoothModifierData*) md;
uiDefButBitS(block, TOG, MOD_SMOOTH_X, B_MODIFIER_RECALC, "X", lx,(cy-=19),45,19, &smd->flag, 0, 0, 0, 0, "Enable X axis smoothing");
uiDefButBitS(block, TOG, MOD_SMOOTH_Y, B_MODIFIER_RECALC, "Y", lx+45,cy,45,19, &smd->flag, 0, 0, 0, 0, "Enable Y axis smoothing");
uiDefButBitS(block, TOG, MOD_SMOOTH_Z, B_MODIFIER_RECALC, "Z", lx+90,cy,45,19, &smd->flag, 0, 0, 0, 0, "Enable Z axis smoothing");
uiDefButF(block, NUM, B_MODIFIER_RECALC, "Factor:", lx,(cy-=19),buttonWidth, 19, &smd->fac, -10.0, 10.0, 0.5, 0, "Define the amount of smoothing, from 0.0 to 1.0 (lower / higher values can deform the mesh)");
uiDefButS(block, NUM, B_MODIFIER_RECALC, "Repeat:", lx,(cy-=19),buttonWidth, 19, &smd->repeat, 0.0, 30.0, 1, 0, "Number of smoothing iterations");
but=uiDefBut(block, TEX, B_MODIFIER_RECALC, "VGroup: ", lx, (cy-=19), buttonWidth,19, &smd->defgrp_name, 0.0, 31.0, 0, 0, "Vertex Group name to define which vertices are affected");
} else if (md->type==eModifierType_Cast) {
CastModifierData *cmd = (CastModifierData*) md;
char casttypemenu[]="Projection Type%t|Sphere%x0|Cylinder%x1|Cuboid%x2";
uiDefButS(block, MENU, B_MODIFIER_RECALC, casttypemenu, lx,(cy-=19),buttonWidth - 30,19, &cmd->type, 0, 0, 0, 0, "Projection type to apply");
uiDefButBitS(block, TOG, MOD_CAST_X, B_MODIFIER_RECALC, "X", lx,(cy-=19),45,19, &cmd->flag, 0, 0, 0, 0, "Enable (local) X axis deformation");
uiDefButBitS(block, TOG, MOD_CAST_Y, B_MODIFIER_RECALC, "Y", lx+45,cy,45,19, &cmd->flag, 0, 0, 0, 0, "Enable (local) Y axis deformation");
if (cmd->type != MOD_CAST_TYPE_CYLINDER) {
uiDefButBitS(block, TOG, MOD_CAST_Z, B_MODIFIER_RECALC, "Z", lx+90,cy,45,19, &cmd->flag, 0, 0, 0, 0, "Enable (local) Z axis deformation");
}
uiDefButF(block, NUM, B_MODIFIER_RECALC, "Factor:", lx,(cy-=19),buttonWidth, 19, &cmd->fac, -10.0, 10.0, 5, 0, "Define the amount of deformation");
uiDefButF(block, NUM, B_MODIFIER_RECALC, "Radius:", lx,(cy-=19),buttonWidth, 19, &cmd->radius, 0.0, 100.0, 10.0, 0, "Only deform vertices within this distance from the center of the effect (leave as 0 for infinite)");
uiDefButF(block, NUM, B_MODIFIER_RECALC, "Size:", lx,(cy-=19),buttonWidth, 19, &cmd->size, 0.0, 100.0, 10.0, 0, "Size of projection shape (leave as 0 for auto)");
uiDefButBitS(block, TOG, MOD_CAST_SIZE_FROM_RADIUS, B_MODIFIER_RECALC, "From radius", lx+buttonWidth,cy,80,19, &cmd->flag, 0, 0, 0, 0, "Use radius as size of projection shape (0 = auto)");
if (ob->type == OB_MESH) {
but=uiDefBut(block, TEX, B_MODIFIER_RECALC, "VGroup: ", lx, (cy-=19), buttonWidth,19, &cmd->defgrp_name, 0.0, 31.0, 0, 0, "Vertex Group name to define which vertices are affected");
}
uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_CHANGEDEP, "Ob: ", lx,(cy-=19), buttonWidth,19, &cmd->object, "Control object: if available, its location determines the center of the effect");
if(cmd->object) {
uiDefButBitS(block, TOG, MOD_CAST_USE_OB_TRANSFORM, B_MODIFIER_RECALC, "Use transform", lx+buttonWidth,cy,80,19, &cmd->flag, 0, 0, 0, 0, "Use object transform to control projection shape");
}
} else if (md->type==eModifierType_Wave) {
WaveModifierData *wmd = (WaveModifierData*) md;
uiDefButBitS(block, TOG, WAV_X, B_MODIFIER_RECALC, "X", lx,(cy-=19),45,19, &wmd->flag, 0, 0, 0, 0, "Enable X axis motion");