Adding subdivisions by correlation.

This is much nicer than subdivision by angle but is somewhat less intuitive for users.

Added Bucket arc iterator, removing a lot of weird duplicated code in skeleton generator.
This commit is contained in:
2007-11-20 22:25:25 +00:00
parent 45e14ed270
commit 11fc4432b9
7 changed files with 272 additions and 91 deletions

View File

@@ -44,7 +44,7 @@ struct ListBase;
struct MemFile;
#define BLENDER_VERSION 245
#define BLENDER_SUBVERSION 8
#define BLENDER_SUBVERSION 9
#define BLENDER_MINVERSION 240
#define BLENDER_MINSUBVERSION 0

View File

@@ -6768,7 +6768,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
}
}
if (main->versionfile < 245 || main->subversionfile < 8)
if (main->versionfile < 245 || main->subversionfile < 9)
{
/* initialize skeleton generation toolsettings */
for(sce=main->scene.first; sce; sce = sce->id.next)
@@ -6779,10 +6779,13 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
sce->toolsettings->skgen_angle_limit = 45.0f;
sce->toolsettings->skgen_length_ratio = 1.3f;
sce->toolsettings->skgen_length_limit = 1.5f;
sce->toolsettings->skgen_correlation_limit = 0.98f;
sce->toolsettings->skgen_postpro = SKGEN_SMOOTH;
sce->toolsettings->skgen_postpro_passes = 1;
sce->toolsettings->skgen_options = SKGEN_FILTER_INTERNAL|SKGEN_FILTER_EXTERNAL|SKGEN_REPOSITION|SKGEN_CUT_LENGTH|SKGEN_CUT_ANGLE;
sce->toolsettings->skgen_options = SKGEN_FILTER_INTERNAL|SKGEN_FILTER_EXTERNAL|SKGEN_REPOSITION|SKGEN_CUT_LENGTH|SKGEN_SUB_CORRELATION;
sce->toolsettings->skgen_subdivisions[0] = SKGEN_SUB_LENGTH;
sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_CORRELATION;
sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE;
}
}
}

View File

@@ -74,6 +74,14 @@ typedef struct ReebArc {
int flags;
} ReebArc;
typedef struct ReebArcIterator {
struct ReebArc *arc;
int index;
int start;
int end;
int stride;
} ReebArcIterator;
struct EditMesh;
void weightToHarmonic(struct EditMesh *em);
@@ -85,6 +93,10 @@ ReebGraph * generateReebGraph(struct EditMesh *me, int subdivisions);
#define OTHER_NODE(arc, node) ((arc->v1 == node) ? arc->v2 : arc->v1)
void initArcIterator(struct ReebArcIterator *iter, struct ReebArc *arc, struct ReebNode *head);
void initArcIterator2(struct ReebArcIterator *iter, struct ReebArc *arc, int start, int end);
struct EmbedBucket * nextBucket(struct ReebArcIterator *iter);
int subtreeDepth(ReebNode *node);
int countConnectedArcs(ReebGraph *rg, ReebNode *node);
int hasAdjacencyList(ReebGraph *rg);

View File

@@ -370,10 +370,13 @@ typedef struct ToolSettings {
float skgen_length_ratio;
float skgen_length_limit;
float skgen_angle_limit;
float skgen_correlation_limit;
short skgen_options;
char skgen_postpro;
char skgen_postpro_passes;
char skgen_subdivisions[3];
char pad3[1];
} ToolSettings;
/* Used by all brushes to store their properties, which can be directly set
@@ -682,7 +685,12 @@ typedef struct Scene {
#define SKGEN_SYMMETRY 8
#define SKGEN_CUT_LENGTH 16
#define SKGEN_CUT_ANGLE 32
#define SKGEN_LENGTH_FIRST 64
#define SKGEN_CUT_CORRELATION 64
#define SKGEN_SUB_LENGTH 0
#define SKGEN_SUB_ANGLE 1
#define SKGEN_SUB_CORRELATION 2
#define SKGEN_SUB_TOTAL 3
/* toolsettings->skgen_postpro */
#define SKGEN_SMOOTH 0

View File

@@ -4418,54 +4418,71 @@ static void verify_vertexgroup_name_func(void *datav, void *data2_unused)
unique_vertexgroup_name((bDeformGroup*)datav, OBACT);
}
static void skgen_reorder(void *arg1, void *arg2)
static void skgen_reorder(void *option, void *arg2)
{
G.scene->toolsettings->skgen_options ^= SKGEN_LENGTH_FIRST;
char tmp;
switch ((int)option)
{
case 0:
tmp = G.scene->toolsettings->skgen_subdivisions[0];
G.scene->toolsettings->skgen_subdivisions[0] = G.scene->toolsettings->skgen_subdivisions[1];
G.scene->toolsettings->skgen_subdivisions[1] = tmp;
break;
case 1:
tmp = G.scene->toolsettings->skgen_subdivisions[2];
G.scene->toolsettings->skgen_subdivisions[2] = G.scene->toolsettings->skgen_subdivisions[1];
G.scene->toolsettings->skgen_subdivisions[1] = tmp;
break;
case 2:
tmp = G.scene->toolsettings->skgen_subdivisions[0];
G.scene->toolsettings->skgen_subdivisions[0] = G.scene->toolsettings->skgen_subdivisions[2];
G.scene->toolsettings->skgen_subdivisions[2] = G.scene->toolsettings->skgen_subdivisions[1];
G.scene->toolsettings->skgen_subdivisions[1] = tmp;
break;
}
}
static void editing_panel_mesh_skgen(Object *ob, Mesh *me)
{
uiBlock *block;
uiBut *but;
int i;
block= uiNewBlock(&curarea->uiblocks, "editing_panel_mesh_skgen", UI_EMBOSS, UI_HELV, curarea->win);
if(uiNewPanel(curarea, block, "Skeleton Generation", "Editing", 960, 0, 318, 204)==0) return;
uiDefBut(block, BUT, B_GEN_SKELETON, "Generate Skeleton", 1025,160,250,39, 0, 0, 0, 0, 0, "Generate Skeleton from Mesh");
uiDefBut(block, BUT, B_GEN_SKELETON, "Generate Skeleton", 1025,170,250,19, 0, 0, 0, 0, 0, "Generate Skeleton from Mesh");
uiBlockBeginAlign(block);
uiDefButS(block, NUM, B_DIFF, "Resolution:", 1025,130,250,19, &G.scene->toolsettings->skgen_resolution,10.0,1000.0, 0, 0, "Specifies the resolution of the graph's embedding");
uiDefButBitS(block, TOG, SKGEN_FILTER_INTERNAL, B_DIFF, "Filter In", 1025,110, 83,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Filter internal small arcs from graph");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111,110,164,19, &G.scene->toolsettings->skgen_threshold_internal,0.0, 1.0, 10, 0, "Specify the threshold ratio for filtering internal arcs");
uiDefButBitS(block, TOG, SKGEN_FILTER_EXTERNAL, B_DIFF, "Filter Ex", 1025, 90, 83,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Filter external small arcs from graph");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111, 90,164,19, &G.scene->toolsettings->skgen_threshold_external,0.0, 1.0, 10, 0, "Specify the threshold ratio for filtering external arcs");
uiDefButS(block, NUM, B_DIFF, "Resolution:", 1025,150,250,19, &G.scene->toolsettings->skgen_resolution,10.0,1000.0, 0, 0, "Specifies the resolution of the graph's embedding");
uiDefButBitS(block, TOG, SKGEN_FILTER_INTERNAL, B_DIFF, "Filter In", 1025,130, 83,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Filter internal small arcs from graph");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111,130,164,19, &G.scene->toolsettings->skgen_threshold_internal,0.0, 1.0, 10, 0, "Specify the threshold ratio for filtering internal arcs");
uiDefButBitS(block, TOG, SKGEN_FILTER_EXTERNAL, B_DIFF, "Filter Ex", 1025,110, 83,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Filter external small arcs from graph");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111,110,164,19, &G.scene->toolsettings->skgen_threshold_external,0.0, 1.0, 10, 0, "Specify the threshold ratio for filtering external arcs");
but = uiDefIconBut(block, BUT, B_MODIFIER_RECALC, VICON_MOVE_DOWN, 1025, 70, 16, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Change the order the subdivisions algorithm are applied");
uiButSetFunc(but, skgen_reorder, NULL, NULL);
if (G.scene->toolsettings->skgen_options & SKGEN_LENGTH_FIRST)
for(i = 0; i < SKGEN_SUB_TOTAL; i++)
{
uiDefButBitS(block, TOG, SKGEN_CUT_LENGTH, B_DIFF, "Length", 1041, 70, 67,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Subdivide arcs based on embedding");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111, 70, 82,19, &G.scene->toolsettings->skgen_length_ratio,1.0, 4.0, 10, 0, "Specify the ratio limit between straight arc and embeddings to trigger equal subdivisions");
uiDefButF(block, NUM, B_DIFF, "Len:", 1193, 70, 82,19, &G.scene->toolsettings->skgen_length_limit,0.1,50.0, 10, 0, "Maximum length of the bones when subdividing");
}
else
{
uiDefButBitS(block, TOG, SKGEN_CUT_ANGLE, B_DIFF, "Angle", 1041, 70, 67,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Subdivide arcs based on angle");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111, 70,164,19, &G.scene->toolsettings->skgen_angle_limit,0.0, 90.0, 10, 0, "Specify the threshold angle in degrees for subdivision");
}
but = uiDefIconBut(block, BUT, B_MODIFIER_RECALC, VICON_MOVE_UP, 1025, 50, 16, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Change the order the subdivisions algorithm are applied");
uiButSetFunc(but, skgen_reorder, NULL, NULL);
if (G.scene->toolsettings->skgen_options & SKGEN_LENGTH_FIRST)
{
uiDefButBitS(block, TOG, SKGEN_CUT_ANGLE, B_DIFF, "Angle", 1041, 50, 67,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Subdivide arcs based on angle");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111, 50,164,19, &G.scene->toolsettings->skgen_angle_limit,0.0, 90.0, 10, 0, "Specify the threshold angle in degrees for subdivision");
}
else
{
uiDefButBitS(block, TOG, SKGEN_CUT_LENGTH, B_DIFF, "Length", 1041, 50, 67,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Subdivide arcs based on embedding");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111, 50, 82,19, &G.scene->toolsettings->skgen_length_ratio,1.0, 4.0, 10, 0, "Specify the ratio limit between straight arc and embeddings to trigger equal subdivisions");
uiDefButF(block, NUM, B_DIFF, "Len:", 1193, 50, 82,19, &G.scene->toolsettings->skgen_length_limit,0.1,50.0, 10, 0, "Maximum length of the bones when subdividing");
int y = 90 - 20 * i;
but = uiDefIconBut(block, BUT, B_MODIFIER_RECALC, VICON_MOVE_DOWN, 1025, y, 16, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Change the order the subdivisions algorithm are applied");
uiButSetFunc(but, skgen_reorder, (void *)i, NULL);
switch(G.scene->toolsettings->skgen_subdivisions[i])
{
case SKGEN_SUB_LENGTH:
uiDefButBitS(block, TOG, SKGEN_CUT_LENGTH, B_DIFF, "Length", 1041, y, 67,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Subdivide arcs based on embedding length");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111, y, 82,19, &G.scene->toolsettings->skgen_length_ratio,1.0, 4.0, 10, 0, "Specify the ratio limit between straight arc and embeddings to trigger equal subdivisions");
uiDefButF(block, NUM, B_DIFF, "Len:", 1193, y, 82,19, &G.scene->toolsettings->skgen_length_limit,0.1,50.0, 10, 0, "Maximum length of the bones when subdividing");
break;
case SKGEN_SUB_ANGLE:
uiDefButBitS(block, TOG, SKGEN_CUT_ANGLE, B_DIFF, "Angle", 1041, y, 67,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Subdivide arcs based on angle");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111, y,164,19, &G.scene->toolsettings->skgen_angle_limit,0.0, 90.0, 10, 0, "Specify the threshold angle in degrees for subdivision");
break;
case SKGEN_SUB_CORRELATION:
uiDefButBitS(block, TOG, SKGEN_CUT_CORRELATION, B_DIFF, "Correlation", 1041, y, 67,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Subdivide arcs based on correlation");
uiDefButF(block, NUM, B_DIFF, "Thresh:", 1111, y,164,19, &G.scene->toolsettings->skgen_correlation_limit,0.0, 1.0, 0.01, 0, "Specify the threshold correlation for subdivision");
break;
}
}
uiDefButBitS(block, TOG, SKGEN_REPOSITION, B_DIFF, "Reposition", 1025, 30,250,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Reposition nodes based on embedding instead of original vertice positions");

View File

@@ -3291,29 +3291,23 @@ EditBone * subdivideByAngle(ReebArc *arc, ReebNode *head, ReebNode *tail)
EditBone *lastBone = NULL;
if (G.scene->toolsettings->skgen_options & SKGEN_CUT_ANGLE)
{
ReebArcIterator iter;
EmbedBucket *current = NULL;
EmbedBucket *previous = NULL;
EditBone *child = NULL;
EditBone *parent = NULL;
float angleLimit = (float)cos(G.scene->toolsettings->skgen_angle_limit * M_PI / 180.0f);
int index = 0;
int stride = 1;
// If head is the highest node, invert stride and start index
if (head == arc->v2)
{
stride *= -1;
index = arc->bcount -1;
}
parent = add_editbone("Bone");
VECCOPY(parent->head, head->p);
for(index += stride; index >= 0 && index < arc->bcount; index += stride)
for(initArcIterator(&iter, arc, head), previous = nextBucket(&iter), current = nextBucket(&iter); current; previous = current, current = nextBucket(&iter))
{
float vec1[3], vec2[3];
float len1, len2;
VecSubf(vec1, arc->buckets[index - stride].p, parent->head);
VecSubf(vec2, arc->buckets[index].p, arc->buckets[index - stride].p);
VecSubf(vec1, previous->p, parent->head);
VecSubf(vec2, current->p, previous->p);
len1 = Normalize(vec1);
len2 = Normalize(vec2);
@@ -3322,7 +3316,7 @@ EditBone * subdivideByAngle(ReebArc *arc, ReebNode *head, ReebNode *tail)
if (len1 > 0.0f && len2 > 0.0f && Inpf(vec1, vec2) < angleLimit)
{
VECCOPY(parent->tail, arc->buckets[index - stride].p);
VECCOPY(parent->tail, previous->p);
child = add_editbone("Bone");
VECCOPY(child->head, parent->tail);
@@ -3340,48 +3334,146 @@ EditBone * subdivideByAngle(ReebArc *arc, ReebNode *head, ReebNode *tail)
return lastBone;
}
float calcCorrelation(ReebArc *arc, int start, int end, float v0[3], float n[3])
{
int len = 2 + abs(end - start);
if (len > 2)
{
ReebArcIterator iter;
EmbedBucket *bucket = NULL;
float avg_t = 0.0f;
float s_t = 0.0f;
float s_xyz = 0.0f;
/* First pass, calculate average */
for(initArcIterator2(&iter, arc, start, end), bucket = nextBucket(&iter); bucket; bucket = nextBucket(&iter))
{
float v[3];
VecSubf(v, bucket->p, v0);
avg_t += Inpf(v, n);
}
avg_t /= Inpf(n, n);
avg_t += 1.0f; /* adding start (0) and end (1) values */
avg_t /= len;
/* Second pass, calculate s_xyz and s_t */
for(initArcIterator2(&iter, arc, start, end), bucket = nextBucket(&iter); bucket; bucket = nextBucket(&iter))
{
float v[3], d[3];
float dt;
VecSubf(v, bucket->p, v0);
Projf(d, v, n);
VecSubf(v, v, d);
dt = VecLength(d) - avg_t;
s_t += dt * dt;
s_xyz += Inpf(v, v);
}
/* adding start(0) and end(1) values to s_t */
s_t += (avg_t * avg_t) + (1 - avg_t) * (1 - avg_t);
return 1.0f - s_xyz / s_t;
}
else
{
return 1.0f;
}
}
EditBone * subdivideByCorrelation(ReebArc *arc, ReebNode *head, ReebNode *tail)
{
ReebArcIterator iter;
float n[3];
float CORRELATION_THRESHOLD = G.scene->toolsettings->skgen_correlation_limit;
EditBone *lastBone = NULL;
/* init iterator to get start and end from head */
initArcIterator(&iter, arc, head);
/* Calculate overall */
VecSubf(n, arc->buckets[iter.end].p, head->p);
if (G.scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION &&
calcCorrelation(arc, iter.start, iter.end, head->p, n) < CORRELATION_THRESHOLD)
{
EmbedBucket *bucket = NULL;
EmbedBucket *previous = NULL;
EditBone *child = NULL;
EditBone *parent = NULL;
int boneStart = iter.start;
parent = add_editbone("Bone");
VECCOPY(parent->head, head->p);
for(previous = nextBucket(&iter), bucket = nextBucket(&iter); bucket; previous = bucket, bucket = nextBucket(&iter))
{
/* Calculate normal */
VecSubf(n, bucket->p, parent->head);
if (calcCorrelation(arc, boneStart, iter.index, parent->head, n) < CORRELATION_THRESHOLD)
{
VECCOPY(parent->tail, previous->p);
child = add_editbone("Bone");
VECCOPY(child->head, parent->tail);
child->parent = parent;
child->flag |= BONE_CONNECTED;
parent = child; // new child is next parent
boneStart = iter.index; // start from end
}
}
VECCOPY(parent->tail, tail->p);
lastBone = parent; /* set last bone in the chain */
}
return lastBone;
}
EditBone * subdivideByLength(ReebArc *arc, ReebNode *head, ReebNode *tail)
{
EditBone *lastBone = NULL;
if ((G.scene->toolsettings->skgen_options & SKGEN_CUT_LENGTH) &&
arcLengthRatio(arc) >= G.scene->toolsettings->skgen_length_ratio)
{
ReebArcIterator iter;
EmbedBucket *bucket = NULL;
EmbedBucket *previous = NULL;
EditBone *child = NULL;
EditBone *parent = NULL;
int same = 0;
int index = 0;
int stride = 1;
float lengthLimit = G.scene->toolsettings->skgen_length_limit;
int same = 0;
//printf("----------new arc---------\n");
// If head is the highest node, invert stride and start index
if (head == arc->v2)
{
stride *= -1;
index = arc->bcount -1;
}
initArcIterator(&iter, arc, head);
parent = add_editbone("Bone");
VECCOPY(parent->head, head->p);
while (index >= 0 && index < arc->bcount)
bucket = nextBucket(&iter);
while (bucket != NULL)
{
float *vec0 = NULL;
float *vec1 = arc->buckets[index].p;
float *vec1 = bucket->p;
/* first bucket. Previous is head */
if (index == 0 || index == arc->bcount - 1)
if (previous == NULL)
{
vec0 = head->p;
}
/* Previous are valid buckets */
/* Previous is a valid bucket */
else
{
vec0 = arc->buckets[index - 1].p;
vec0 = previous->p;
}
//printf("distance vec1 -> head = %f\n", VecLenf(vec1, parent->head));
/* If lengthLimit hits the current segment */
if (VecLenf(vec1, parent->head) > lengthLimit)
{
@@ -3418,8 +3510,6 @@ EditBone * subdivideByLength(ReebArc *arc, ReebNode *head, ReebNode *tail)
{
float dv[3];
//printf("same\n");
VecSubf(dv, vec1, vec0);
Normalize(dv);
@@ -3428,8 +3518,6 @@ EditBone * subdivideByLength(ReebArc *arc, ReebNode *head, ReebNode *tail)
VecAddf(parent->tail, parent->tail, parent->head);
}
//printvecf("new joint", parent->tail);
child = add_editbone("Bone");
VECCOPY(child->head, parent->tail);
child->parent = parent;
@@ -3441,9 +3529,8 @@ EditBone * subdivideByLength(ReebArc *arc, ReebNode *head, ReebNode *tail)
}
else
{
//printf("++next bucket++\n");
// Next bucket
index += stride;
previous = bucket;
bucket = nextBucket(&iter);
same = 0; // Reset same
}
}
@@ -3494,6 +3581,7 @@ void generateSkeletonFromReebGraph(ReebGraph *rg)
EditBone *firstBone = NULL;
EditBone *parentBone = NULL;
ReebNode *head, *tail;
int i;
/* if arc is a symetry axis, internal bones go up the tree */
if (arc->flags == 1 && arc->v2->degree != 1)
@@ -3527,23 +3615,20 @@ void generateSkeletonFromReebGraph(ReebGraph *rg)
}
}
/* Subdivide by length, then by angle */
if (G.scene->toolsettings->skgen_options & SKGEN_LENGTH_FIRST)
/* Loop over subdivision methods */
for(i = 0; lastBone == NULL && i < SKGEN_SUB_TOTAL; i++)
{
lastBone = subdivideByLength(arc, head, tail);
if (lastBone == NULL)
switch(G.scene->toolsettings->skgen_subdivisions[i])
{
lastBone = subdivideByAngle(arc, head, tail);
}
}
/* Or angle, then length */
else
{
lastBone = subdivideByAngle(arc, head, tail);
if (lastBone == NULL)
{
lastBone = subdivideByLength(arc, head, tail);
case SKGEN_SUB_LENGTH:
lastBone = subdivideByLength(arc, head, tail);
break;
case SKGEN_SUB_ANGLE:
lastBone = subdivideByAngle(arc, head, tail);
break;
case SKGEN_SUB_CORRELATION:
lastBone = subdivideByCorrelation(arc, head, tail);
break;
}
}

View File

@@ -1922,6 +1922,62 @@ void weightToVCol(EditMesh *em)
}
}
/****************************************** BUCKET ITERATOR **************************************************/
void initArcIterator(ReebArcIterator *iter, ReebArc *arc, ReebNode *head)
{
iter->arc = arc;
if (head == arc->v1)
{
iter->start = 0;
iter->end = arc->bcount - 1;
iter->stride = 1;
}
else
{
iter->start = arc->bcount - 1;
iter->end = 0;
iter->stride = -1;
}
iter->index = iter->start - iter->stride;
}
void initArcIterator2(ReebArcIterator *iter, ReebArc *arc, int start, int end)
{
iter->arc = arc;
iter->start = start;
iter->end = end;
if (end > start)
{
iter->stride = 1;
}
else
{
iter->stride = -1;
}
iter->index = iter->start - iter->stride;
}
EmbedBucket * nextBucket(ReebArcIterator *iter)
{
EmbedBucket *result = NULL;
if (iter->index != iter->end)
{
iter->index += iter->stride;
result = &(iter->arc->buckets[iter->index]);
}
return result;
}
/****************************************** MAIN EDIT METHOD **************************************************/
void generateSkeleton(void)
{
EditMesh *em = G.editMesh;