From 3b743ac5565663d2fd0be4bbbc92e404afafbce4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 5 Sep 2009 09:54:01 +0000 Subject: Option to correct for 3D curve twist error. example before and after. http://www.graphicall.org/ftp/ideasman42/curve_auto_twist.png Access next to the "3D" edit button. details... - open curves use the first points orientation and minimize twist for each new segment. - cyclic curves calculate the least twist in both directions and blend between them - AxisAngleToQuat replaced inline code. - Notice the model on the right now has more even corners. added Vec3ToTangent to arithb.c. --- source/blender/blenkernel/intern/curve.c | 172 ++++++++++++++++++++++++------ source/blender/blenlib/BLI_arithb.h | 1 + source/blender/blenlib/intern/arithb.c | 13 +++ source/blender/makesdna/DNA_curve_types.h | 2 + source/blender/src/buttons_editing.c | 3 +- 5 files changed, 156 insertions(+), 35 deletions(-) (limited to 'source/blender') diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 0d6382a8d37..709507a9b26 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1513,7 +1513,7 @@ void makeBevelList(Object *ob) BPoint *bp; BevList *bl, *blnew, *blnext; BevPoint *bevp, *bevp2, *bevp1 = NULL, *bevp0; - float min, inp, x1, x2, y1, y2, vec[3]; + float min, inp, x1, x2, y1, y2, vec[3], vec_prev[3], q[4], quat[4], quat_prev[4], cross[3]; float *coord_array, *tilt_array=NULL, *radius_array=NULL, *coord_fp, *tilt_fp=NULL, *radius_fp=NULL; float *v1, *v2; struct bevelsort *sortdata, *sd, *sd1; @@ -1872,7 +1872,10 @@ void makeBevelList(Object *ob) bl= cu->bev.first; while(bl) { - if(bl->nr==2) { /* 2 pnt, treat separate */ + if(bl->nr < 2) { + /* do nothing */ + } + else if(bl->nr==2) { /* 2 pnt, treat separate */ bevp2= (BevPoint *)(bl+1); bevp1= bevp2+1; @@ -1886,68 +1889,169 @@ void makeBevelList(Object *ob) if(cu->flag & CU_3D) { /* 3D */ float quat[4], q[4]; - vec[0]= bevp1->x - bevp2->x; - vec[1]= bevp1->y - bevp2->y; - vec[2]= bevp1->z - bevp2->z; + VecSubf(vec, &bevp1->x, &bevp2->x); vectoquat(vec, 5, 1, quat); - Normalize(vec); - q[0]= (float)cos(0.5*bevp1->alfa); - x1= (float)sin(0.5*bevp1->alfa); - q[1]= x1*vec[0]; - q[2]= x1*vec[1]; - q[3]= x1*vec[2]; + AxisAngleToQuat(q, vec, bevp1->alfa); QuatMul(quat, q, quat); QuatToMat3(quat, bevp1->mat); Mat3CpyMat3(bevp2->mat, bevp1->mat); } + } /* this has to be >2 points */ + else if(cu->flag & CU_NO_TWIST && cu->flag & CU_3D && bl->poly != -1) { + + /* Special case, cyclic curve with no twisy. tricky... */ + + float quat[4], q[4], cross[3]; + + /* correcting a cyclic curve is more complicated, need to be corrected from both ends */ + float *quat_tmp1, *quat_tmp2; /* store a quat in the matrix temporarily */ + int iter_dir; + BevPoint *bevp_start= (BevPoint *)(bl+1); + + /* loop over the points twice, once up, once back, accumulate the quat rotations + * in both directions, then blend them in the 3rd loop and apply the tilt */ + for(iter_dir = 0; iter_dir < 2; iter_dir++) { + + bevp2= (BevPoint *)(bl+1); + bevp1= bevp2+(bl->nr-1); + bevp0= bevp1-1; + + nr= bl->nr; + while(nr--) { + + /* Normalizes */ + Vec3ToTangent(vec, &bevp0->x, &bevp1->x, &bevp2->x); + + if(bl->nr==nr+1) { /* first time */ + vectoquat(vec, 5, 1, quat); + } + else { + float angle = NormalizedVecAngle2(vec_prev, vec); + + if(angle > 0.0f) { /* otherwise we can keep as is */ + Crossf(cross, vec_prev, vec); + AxisAngleToQuat(q, cross, angle); + QuatMul(quat, q, quat_prev); + } + else { + QUATCOPY(quat, quat_prev); + } + } + QUATCOPY(quat_prev, quat); /* quat_prev can't have the tilt applied */ + VECCOPY(vec_prev, vec); + + if(iter_dir==0) { /* up, first time */ + quat_tmp1= (float *)bevp1->mat; + + bevp0= bevp1; + bevp1= bevp2; + bevp2++; + } + else { /* down second time */ + quat_tmp1= ((float *)bevp1->mat)+4; + + bevp2= bevp1; + bevp1= bevp0; + bevp0--; + + /* wrap around */ + if (bevp0 < bevp_start) + bevp0= bevp_start+(bl->nr-1); + } + + QUATCOPY(quat_tmp1, quat); + } + } + + /* Now interpolate the 2 quats and apply tilt */ + + bevp2= (BevPoint *)(bl+1); + bevp1= bevp2+(bl->nr-1); + bevp0= bevp1-1; + + nr= bl->nr; + while(nr--) { + + Vec3ToTangent(vec, &bevp0->x, &bevp1->x, &bevp2->x); + + quat_tmp1= (float *)bevp1->mat; + quat_tmp2= quat_tmp1+4; + + /* blend the 2 rotations gathered from both directions */ + QuatInterpol(quat, quat_tmp1, quat_tmp2, 1.0 - (((float)nr)/bl->nr)); + + AxisAngleToQuat(q, vec, bevp1->alfa); + QuatMul(quat, q, quat); + QuatToMat3(quat, bevp1->mat); + + /* generic */ + x1= bevp1->x- bevp0->x; + x2= bevp1->x- bevp2->x; + y1= bevp1->y- bevp0->y; + y2= bevp1->y- bevp2->y; + + calc_bevel_sin_cos(x1, y1, x2, y2, &(bevp1->sina), &(bevp1->cosa)); + + bevp0= bevp1; + bevp1= bevp2; + bevp2++; + } } - else if(bl->nr>2) { + else { + /* Any curve with 3 or more points */ + bevp2= (BevPoint *)(bl+1); bevp1= bevp2+(bl->nr-1); bevp0= bevp1-1; - nr= bl->nr; - while(nr--) { - + if(cu->flag & CU_3D) { /* 3D */ - float quat[4], q[4]; - - vec[0]= bevp2->x - bevp0->x; - vec[1]= bevp2->y - bevp0->y; - vec[2]= bevp2->z - bevp0->z; - - Normalize(vec); - vectoquat(vec, 5, 1, quat); - - q[0]= (float)cos(0.5*bevp1->alfa); - x1= (float)sin(0.5*bevp1->alfa); - q[1]= x1*vec[0]; - q[2]= x1*vec[1]; - q[3]= x1*vec[2]; + /* Normalizes */ + Vec3ToTangent(vec, &bevp0->x, &bevp1->x, &bevp2->x); + + if(bl->nr==nr+1 || !(cu->flag & CU_NO_TWIST)) { /* first time */ + vectoquat(vec, 5, 1, quat); + } + else { + float angle = NormalizedVecAngle2(vec_prev, vec); + + if(angle > 0.0f) { /* otherwise we can keep as is */ + Crossf(cross, vec_prev, vec); + AxisAngleToQuat(q, cross, angle); + QuatMul(quat, q, quat_prev); + } + else { + QUATCOPY(quat, quat_prev); + } + } + QUATCOPY(quat_prev, quat); /* quat_prev can't have the tilt applied */ + VECCOPY(vec_prev, vec); + + AxisAngleToQuat(q, vec, bevp1->alfa); QuatMul(quat, q, quat); - QuatToMat3(quat, bevp1->mat); } - + x1= bevp1->x- bevp0->x; x2= bevp1->x- bevp2->x; y1= bevp1->y- bevp0->y; y2= bevp1->y- bevp2->y; - + calc_bevel_sin_cos(x1, y1, x2, y2, &(bevp1->sina), &(bevp1->cosa)); - - + + bevp0= bevp1; bevp1= bevp2; bevp2++; } + /* correct non-cyclic cases */ if(bl->poly== -1) { if(bl->nr>2) { diff --git a/source/blender/blenlib/BLI_arithb.h b/source/blender/blenlib/BLI_arithb.h index 092ed00fbe5..63b351016d4 100644 --- a/source/blender/blenlib/BLI_arithb.h +++ b/source/blender/blenlib/BLI_arithb.h @@ -270,6 +270,7 @@ void AxisAngleToQuat(float *q, float *axis, float angle); void RotationBetweenVectorsToQuat(float *q, float v1[3], float v2[3]); void vectoquat(float *vec, short axis, short upflag, float *q); +void Vec3ToTangent(float *v, float *v1, float *v2, float *v3); float VecAngle2(float *v1, float *v2); float VecAngle3(float *v1, float *v2, float *v3); float NormalizedVecAngle2(float *v1, float *v2); diff --git a/source/blender/blenlib/intern/arithb.c b/source/blender/blenlib/intern/arithb.c index ad1dc1ca12c..970d8f7d0de 100644 --- a/source/blender/blenlib/intern/arithb.c +++ b/source/blender/blenlib/intern/arithb.c @@ -2969,6 +2969,19 @@ void VecRotToQuat( float *vec, float phi, float *quat) } } +/* get a direction from 3 vectors that wont depend + * on the distance between the points */ +void Vec3ToTangent(float *v, float *v1, float *v2, float *v3) +{ + float d_12[3], d_23[3]; + VecSubf(d_12, v2, v1); + VecSubf(d_23, v3, v2); + Normalize(d_12); + Normalize(d_23); + VecAddf(v, d_12, d_23); + Normalize(v); +} + /* Return the angle in degrees between vecs 1-2 and 2-3 in degrees If v1 is a shoulder, v2 is the elbow and v3 is the hand, this would return the angle at the elbow */ diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index f809cac037d..88c03a41160 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -205,6 +205,8 @@ typedef struct Curve { #define CU_FAST 512 /* Font: no filling inside editmode */ #define CU_RETOPO 1024 +#define CU_NO_TWIST 4096 + /* spacemode */ #define CU_LEFT 0 #define CU_MIDDLE 1 diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c index 2f9eac67d94..1e37fd0b9f8 100644 --- a/source/blender/src/buttons_editing.c +++ b/source/blender/src/buttons_editing.c @@ -3671,7 +3671,8 @@ static void editing_panel_curve_type(Object *ob, Curve *cu) uiBlockSetCol(block, TH_BUT_SETTING1); uiDefButBitS(block, TOG, CU_BACK, B_MAKEDISP, "Back", 760,115,50,19, &cu->flag, 0, 0, 0, 0, "Draw filled back for extruded/beveled curves"); uiDefButBitS(block, TOG, CU_FRONT, B_MAKEDISP, "Front",810,115,50,19, &cu->flag, 0, 0, 0, 0, "Draw filled front for extruded/beveled curves"); - uiDefButBitS(block, TOG, CU_3D, B_CU3D, "3D", 860,115,50,19, &cu->flag, 0, 0, 0, 0, "Allow Curve to be 3d, it doesn't fill then"); + uiDefButBitS(block, TOG, CU_3D, B_CU3D, "3D", 860,115,30,19, &cu->flag, 0, 0, 0, 0, "Allow Curve to be 3d, it doesn't fill then"); + uiDefIconButBitS(block, TOG, CU_NO_TWIST, B_MAKEDISP, ICON_AUTO, 890,115,20,19, &cu->flag, 0.0, 0.0, 0, 0, "Avoid twisting artifacts for 3D beveled/extruded curves"); } } -- cgit v1.2.3