diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index c71d3c7fdd2..1727edc10fc 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -259,6 +259,21 @@ Scene *add_scene(char *name) sce->toolsettings->select_thresh= 0.01f; sce->toolsettings->jointrilimit = 0.8f; + sce->toolsettings->skgen_resolution = 100; + sce->toolsettings->skgen_threshold_internal = 0.01f; + sce->toolsettings->skgen_threshold_external = 0.01f; + 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_symmetry_limit = 0.1f; + sce->toolsettings->skgen_postpro = SKGEN_SMOOTH; + sce->toolsettings->skgen_postpro_passes = 1; + sce->toolsettings->skgen_options = SKGEN_FILTER_INTERNAL|SKGEN_FILTER_EXTERNAL|SKGEN_FILTER_SMART|SKGEN_HARMONIC|SKGEN_SUB_CORRELATION|SKGEN_STICK_TO_EMBEDDING; + sce->toolsettings->skgen_subdivisions[0] = SKGEN_SUB_CORRELATION; + sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_LENGTH; + sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE; + pset= &sce->toolsettings->particle; pset->flag= PE_KEEP_LENGTHS|PE_LOCK_FIRST|PE_DEFLECT_EMITTER; pset->emitterdist= 0.25f; diff --git a/source/blender/blenlib/BLI_arithb.h b/source/blender/blenlib/BLI_arithb.h index e2e71a2fb1a..e4b983d9ba3 100644 --- a/source/blender/blenlib/BLI_arithb.h +++ b/source/blender/blenlib/BLI_arithb.h @@ -245,6 +245,7 @@ void VecMulf(float *v1, float f); int VecLenCompare(float *v1, float *v2, float limit); int VecCompare(float *v1, float *v2, float limit); int VecEqual(float *v1, float *v2); +int VecIsNull(float *v); void printvecf(char *str,float v[3]); void printvec4f(char *str, float v[4]); @@ -265,6 +266,7 @@ void Vec2Copyf(float *v1, float *v2); void Vec2Lerpf(float *target, float *a, float *b, float t); 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); float VecAngle2(float *v1, float *v2); diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index aec77f5f385..c77e82f0a2b 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -34,7 +34,12 @@ struct GHash; typedef struct GHash GHash; -typedef struct GHashIterator GHashIterator; + +typedef struct GHashIterator { + GHash *gh; + int curBucket; + struct Entry *curEntry; +} GHashIterator; typedef unsigned int (*GHashHashFP) (void *key); typedef int (*GHashCmpFP) (void *a, void *b); @@ -62,6 +67,15 @@ int BLI_ghash_size (GHash *gh); * @return Pointer to a new DynStr. */ GHashIterator* BLI_ghashIterator_new (GHash *gh); + /** + * Init an already allocated GHashIterator. The hash table must not + * be mutated while the iterator is in use, and the iterator will + * step exactly BLI_ghash_size(gh) times before becoming done. + * + * @param ghi The GHashIterator to initialize. + * @param gh The GHash to iterate over. + */ +void BLI_ghashIterator_init(GHashIterator *ghi, GHash *gh); /** * Free a GHashIterator. * diff --git a/source/blender/blenlib/BLI_graph.h b/source/blender/blenlib/BLI_graph.h new file mode 100644 index 00000000000..160c2e04cf5 --- /dev/null +++ b/source/blender/blenlib/BLI_graph.h @@ -0,0 +1,125 @@ +#ifndef BLI_GRAPH_H_ +#define BLI_GRAPH_H_ + +#include "DNA_listBase.h" + +struct BGraph; +struct BNode; +struct BArc; + +struct RadialArc; + +typedef void (*FreeArc)(struct BArc*); +typedef void (*FreeNode)(struct BNode*); +typedef void (*RadialSymmetry)(struct BNode* root_node, struct RadialArc* ring, int total); +typedef void (*AxialSymmetry)(struct BNode* root_node, struct BNode* node1, struct BNode* node2, struct BArc* arc1, struct BArc* arc2); + +/* IF YOU MODIFY THOSE TYPES, YOU NEED TO UPDATE ALL THOSE THAT "INHERIT" FROM THEM + * + * RigGraph, ReebGraph + * + * */ + +typedef struct BGraph { + ListBase arcs; + ListBase nodes; + + float length; + + /* function pointer to deal with custom fonctionnality */ + FreeArc free_arc; + FreeNode free_node; + RadialSymmetry radial_symmetry; + AxialSymmetry axial_symmetry; +} BGraph; + +typedef struct BNode { + void *next, *prev; + float p[3]; + int flag; + + int degree; + struct BArc **arcs; + + int subgraph_index; + + int symmetry_level; + int symmetry_flag; + float symmetry_axis[3]; +} BNode; + +typedef struct BArc { + void *next, *prev; + struct BNode *head, *tail; + int flag; + + float length; + + int symmetry_level; + int symmetry_group; + int symmetry_flag; +} BArc; + +/* Helper structure for radial symmetry */ +typedef struct RadialArc +{ + struct BArc *arc; + float n[3]; /* normalized vector joining the nodes of the arc */ +} RadialArc; + +BNode *BLI_otherNode(BArc *arc, BNode *node); + +void BLI_freeNode(BGraph *graph, BNode *node); +void BLI_removeNode(BGraph *graph, BNode *node); + +void BLI_removeArc(BGraph *graph, BArc *arc); + +void BLI_flagNodes(BGraph *graph, int flag); +void BLI_flagArcs(BGraph *graph, int flag); + +int BLI_hasAdjacencyList(BGraph *rg); +void BLI_buildAdjacencyList(BGraph *rg); +void BLI_rebuildAdjacencyList(BGraph* rg); +void BLI_rebuildAdjacencyListForNode(BGraph* rg, BNode *node); +void BLI_freeAdjacencyList(BGraph *rg); + +int BLI_FlagSubgraphs(BGraph *graph); +void BLI_ReflagSubgraph(BGraph *graph, int old_subgraph, int new_subgraph); + +#define SHAPE_RADIX 10 /* each shape level is encoded this base */ + +int BLI_subtreeShape(BGraph *graph, BNode *node, BArc *rootArc, int include_root); +float BLI_subtreeLength(BNode *node); +void BLI_calcGraphLength(BGraph *graph); + +void BLI_replaceNode(BGraph *graph, BNode *node_src, BNode *node_replaced); +void BLI_replaceNodeInArc(BGraph *graph, BArc *arc, BNode *node_src, BNode *node_replaced); +void BLI_removeDoubleNodes(BGraph *graph, float limit); +BNode * BLI_FindNodeByPosition(BGraph *graph, float *p, float limit); + +BArc * BLI_findConnectedArc(BGraph *graph, BArc *arc, BNode *v); + +int BLI_isGraphCyclic(BGraph *graph); + +/*------------ Symmetry handling ------------*/ +void BLI_markdownSymmetry(BGraph *graph, BNode *root_node, float limit); + +void BLI_mirrorAlongAxis(float v[3], float center[3], float axis[3]); + +/* BNode symmetry flags */ +#define SYM_TOPOLOGICAL 1 +#define SYM_PHYSICAL 2 + +/* the following two are exclusive */ +#define SYM_AXIAL 4 +#define SYM_RADIAL 8 + +/* BArc symmetry flags + * + * axial symetry sides */ +#define SYM_SIDE_POSITIVE 1 +#define SYM_SIDE_NEGATIVE 2 +/* Anything higher is the order in radial symmetry */ +#define SYM_SIDE_RADIAL 3 + +#endif /*BLI_GRAPH_H_*/ diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 39162b8bd91..5a7e84c42fb 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -39,17 +39,41 @@ #define BLENDER_MAX_THREADS 8 struct ListBase; - void BLI_init_threads (struct ListBase *threadbase, void *(*do_thread)(void *), int tot); int BLI_available_threads(struct ListBase *threadbase); int BLI_available_thread_index(struct ListBase *threadbase); void BLI_insert_thread (struct ListBase *threadbase, void *callerdata); void BLI_remove_thread (struct ListBase *threadbase, void *callerdata); +void BLI_remove_thread_index(struct ListBase *threadbase, int index); +void BLI_remove_threads(struct ListBase *threadbase); void BLI_end_threads (struct ListBase *threadbase); void BLI_lock_thread (int type); void BLI_unlock_thread (int type); int BLI_system_thread_count( void ); /* gets the number of threads the system can make use of */ + +/* ThreadedWorker is a simple tool for dispatching work to a limited number of threads in a transparent + * fashion from the caller's perspective + * */ + +struct ThreadedWorker; + +/* Create a new worker supporting tot parallel threads. + * When new work in inserted and all threads are busy, sleep(sleep_time) before checking again + */ +struct ThreadedWorker *BLI_create_worker(void *(*do_thread)(void *), int tot, int sleep_time); + +/* join all working threads */ +void BLI_end_worker(struct ThreadedWorker *worker); + +/* also ends all working threads */ +void BLI_destroy_worker(struct ThreadedWorker *worker); + +/* Spawns a new work thread if possible, sleeps until one is available otherwise + * NOTE: inserting work is NOT thread safe, so make sure it is only done from one thread */ +void BLI_insert_work(struct ThreadedWorker *worker, void *param); + + #endif diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index e9271ca3bb5..1967b8a88e2 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -200,12 +200,6 @@ void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreef /***/ -struct GHashIterator { - GHash *gh; - int curBucket; - Entry *curEntry; -}; - GHashIterator *BLI_ghashIterator_new(GHash *gh) { GHashIterator *ghi= malloc(sizeof(*ghi)); ghi->gh= gh; @@ -219,6 +213,17 @@ GHashIterator *BLI_ghashIterator_new(GHash *gh) { } return ghi; } +void BLI_ghashIterator_init(GHashIterator *ghi, GHash *gh) { + ghi->gh= gh; + ghi->curEntry= NULL; + ghi->curBucket= -1; + while (!ghi->curEntry) { + ghi->curBucket++; + if (ghi->curBucket==ghi->gh->nbuckets) + break; + ghi->curEntry= ghi->gh->buckets[ghi->curBucket]; + } +} void BLI_ghashIterator_free(GHashIterator *ghi) { free(ghi); } diff --git a/source/blender/blenlib/intern/arithb.c b/source/blender/blenlib/intern/arithb.c index 79517c4fde4..149d3cf1f8f 100644 --- a/source/blender/blenlib/intern/arithb.c +++ b/source/blender/blenlib/intern/arithb.c @@ -1371,6 +1371,18 @@ void NormalQuat(float *q) } } +void RotationBetweenVectorsToQuat(float *q, float v1[3], float v2[3]) +{ + float axis[3]; + float angle; + + Crossf(axis, v1, v2); + + angle = NormalizedVecAngle2(v1, v2); + + AxisAngleToQuat(q, axis, angle); +} + void AxisAngleToQuat(float *q, float *axis, float angle) { float nor[3]; @@ -2219,6 +2231,11 @@ int VecEqual(float *v1, float *v2) return ((v1[0]==v2[0]) && (v1[1]==v2[1]) && (v1[2]==v2[2])); } +int VecIsNull(float *v) +{ + return (v[0] == 0 && v[1] == 0 && v[2] == 0); +} + void CalcNormShort( short *v1, short *v2, short *v3, float *n) /* is also cross product */ { float n1[3],n2[3]; diff --git a/source/blender/blenlib/intern/graph.c b/source/blender/blenlib/intern/graph.c new file mode 100644 index 00000000000..8f35b38379e --- /dev/null +++ b/source/blender/blenlib/intern/graph.c @@ -0,0 +1,1087 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Martin Poirier + * + * ***** END GPL LICENSE BLOCK ***** + * graph.c: Common graph interface and methods + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_graph.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BKE_utildefines.h" + +static void testRadialSymmetry(BGraph *graph, BNode* root_node, RadialArc* ring, int total, float axis[3], float limit, int group); + +static void handleAxialSymmetry(BGraph *graph, BNode *root_node, int depth, float axis[3], float limit); +static void testAxialSymmetry(BGraph *graph, BNode* root_node, BNode* node1, BNode* node2, BArc* arc1, BArc* arc2, float axis[3], float limit, int group); +static void flagAxialSymmetry(BNode *root_node, BNode *end_node, BArc *arc, int group); + +void BLI_freeNode(BGraph *graph, BNode *node) +{ + if (node->arcs) + { + MEM_freeN(node->arcs); + } + + if (graph->free_node) + { + graph->free_node(node); + } +} + +void BLI_removeNode(BGraph *graph, BNode *node) +{ + BLI_freeNode(graph, node); + BLI_freelinkN(&graph->nodes, node); +} + +BNode *BLI_otherNode(BArc *arc, BNode *node) +{ + return (arc->head == node) ? arc->tail : arc->head; +} + +void BLI_removeArc(BGraph *graph, BArc *arc) +{ + if (graph->free_arc) + { + graph->free_arc(arc); + } + + BLI_freelinkN(&graph->arcs, arc); +} + +void BLI_flagNodes(BGraph *graph, int flag) +{ + BNode *node; + + for(node = graph->nodes.first; node; node = node->next) + { + node->flag = flag; + } +} + +void BLI_flagArcs(BGraph *graph, int flag) +{ + BArc *arc; + + for(arc = graph->arcs.first; arc; arc = arc->next) + { + arc->flag = flag; + } +} + +static void addArcToNodeAdjacencyList(BNode *node, BArc *arc) +{ + node->arcs[node->flag] = arc; + node->flag++; +} + +void BLI_buildAdjacencyList(BGraph *graph) +{ + BNode *node; + BArc *arc; + + for(node = graph->nodes.first; node; node = node->next) + { + if (node->arcs != NULL) + { + MEM_freeN(node->arcs); + } + + node->arcs = MEM_callocN((node->degree) * sizeof(BArc*), "adjacency list"); + + /* temporary use to indicate the first index available in the lists */ + node->flag = 0; + } + + for(arc = graph->arcs.first; arc; arc= arc->next) + { + addArcToNodeAdjacencyList(arc->head, arc); + addArcToNodeAdjacencyList(arc->tail, arc); + } + + for(node = graph->nodes.first; node; node = node->next) + { + if (node->degree != node->flag) + { + printf("error in node [%p]. Added only %i arcs out of %i\n", node, node->flag, node->degree); + } + } +} + +void BLI_rebuildAdjacencyListForNode(BGraph* graph, BNode *node) +{ + BArc *arc; + + if (node->arcs != NULL) + { + MEM_freeN(node->arcs); + } + + node->arcs = MEM_callocN((node->degree) * sizeof(BArc*), "adjacency list"); + + /* temporary use to indicate the first index available in the lists */ + node->flag = 0; + + for(arc = graph->arcs.first; arc; arc= arc->next) + { + if (arc->head == node) + { + addArcToNodeAdjacencyList(arc->head, arc); + } + else if (arc->tail == node) + { + addArcToNodeAdjacencyList(arc->tail, arc); + } + } + + if (node->degree != node->flag) + { + printf("error in node [%p]. Added only %i arcs out of %i\n", node, node->flag, node->degree); + } +} + +void BLI_freeAdjacencyList(BGraph *graph) +{ + BNode *node; + + for(node = graph->nodes.first; node; node = node->next) + { + if (node->arcs != NULL) + { + MEM_freeN(node->arcs); + node->arcs = NULL; + } + } +} + +int BLI_hasAdjacencyList(BGraph *graph) +{ + BNode *node; + + for(node = graph->nodes.first; node; node = node->next) + { + if (node->arcs == NULL) + { + return 0; + } + } + + return 1; +} + +void BLI_replaceNodeInArc(BGraph *graph, BArc *arc, BNode *node_src, BNode *node_replaced) +{ + if (arc->head == node_replaced) + { + arc->head = node_src; + node_src->degree++; + } + + if (arc->tail == node_replaced) + { + arc->tail = node_src; + node_src->degree++; + } + + if (arc->head == arc->tail) + { + node_src->degree -= 2; + + graph->free_arc(arc); + BLI_freelinkN(&graph->arcs, arc); + } + + if (node_replaced->degree == 0) + { + BLI_removeNode(graph, node_replaced); + } +} + +void BLI_replaceNode(BGraph *graph, BNode *node_src, BNode *node_replaced) +{ + BArc *arc, *next_arc; + + for (arc = graph->arcs.first; arc; arc = next_arc) + { + next_arc = arc->next; + + if (arc->head == node_replaced) + { + arc->head = node_src; + node_replaced->degree--; + node_src->degree++; + } + + if (arc->tail == node_replaced) + { + arc->tail = node_src; + node_replaced->degree--; + node_src->degree++; + } + + if (arc->head == arc->tail) + { + node_src->degree -= 2; + + graph->free_arc(arc); + BLI_freelinkN(&graph->arcs, arc); + } + } + + if (node_replaced->degree == 0) + { + BLI_removeNode(graph, node_replaced); + } +} + +void BLI_removeDoubleNodes(BGraph *graph, float limit) +{ + BNode *node_src, *node_replaced; + + for(node_src = graph->nodes.first; node_src; node_src = node_src->next) + { + for(node_replaced = graph->nodes.first; node_replaced; node_replaced = node_replaced->next) + { + if (node_replaced != node_src && VecLenf(node_replaced->p, node_src->p) <= limit) + { + BLI_replaceNode(graph, node_src, node_replaced); + } + } + } + +} + +BNode * BLI_FindNodeByPosition(BGraph *graph, float *p, float limit) +{ + BNode *closest_node = NULL, *node; + float min_distance; + + for(node = graph->nodes.first; node; node = node->next) + { + float distance = VecLenf(p, node->p); + if (distance <= limit && (closest_node == NULL || distance < min_distance)) + { + closest_node = node; + min_distance = distance; + } + } + + return closest_node; +} +/************************************* SUBGRAPH DETECTION **********************************************/ + +void flagSubgraph(BNode *node, int subgraph) +{ + if (node->subgraph_index == 0) + { + BArc *arc; + int i; + + node->subgraph_index = subgraph; + + for(i = 0; i < node->degree; i++) + { + arc = node->arcs[i]; + flagSubgraph(BLI_otherNode(arc, node), subgraph); + } + } +} + +int BLI_FlagSubgraphs(BGraph *graph) +{ + BNode *node; + int subgraph = 0; + + if (BLI_hasAdjacencyList(graph) == 0) + { + BLI_buildAdjacencyList(graph); + } + + for(node = graph->nodes.first; node; node = node->next) + { + node->subgraph_index = 0; + } + + for (node = graph->nodes.first; node; node = node->next) + { + if (node->subgraph_index == 0) + { + subgraph++; + flagSubgraph(node, subgraph); + } + } + + return subgraph; +} + +void BLI_ReflagSubgraph(BGraph *graph, int old_subgraph, int new_subgraph) +{ + BNode *node; + + for (node = graph->nodes.first; node; node = node->next) + { + if (node->flag == old_subgraph) + { + node->flag = new_subgraph; + } + } +} + +/*************************************** CYCLE DETECTION ***********************************************/ + +int detectCycle(BNode *node, BArc *src_arc) +{ + int value = 0; + + if (node->flag == 0) + { + int i; + + /* mark node as visited */ + node->flag = 1; + + for(i = 0; i < node->degree && value == 0; i++) + { + BArc *arc = node->arcs[i]; + + /* don't go back on the source arc */ + if (arc != src_arc) + { + value = detectCycle(BLI_otherNode(arc, node), arc); + } + } + } + else + { + value = 1; + } + + return value; +} + +int BLI_isGraphCyclic(BGraph *graph) +{ + BNode *node; + int value = 0; + + /* NEED TO CHECK IF ADJACENCY LIST EXIST */ + + /* Mark all nodes as not visited */ + BLI_flagNodes(graph, 0); + + /* detectCycles in subgraphs */ + for(node = graph->nodes.first; node && value == 0; node = node->next) + { + /* only for nodes in subgraphs that haven't been visited yet */ + if (node->flag == 0) + { + value = value || detectCycle(node, NULL); + } + } + + return value; +} + +BArc * BLI_findConnectedArc(BGraph *graph, BArc *arc, BNode *v) +{ + BArc *nextArc = arc->next; + + for(nextArc = graph->arcs.first; nextArc; nextArc = nextArc->next) + { + if (arc != nextArc && (nextArc->head == v || nextArc->tail == v)) + { + break; + } + } + + return nextArc; +} + +/*********************************** GRAPH AS TREE FUNCTIONS *******************************************/ + +int subtreeShape(BNode *node, BArc *rootArc, int include_root) +{ + int depth = 0; + + node->flag = 1; + + if (include_root) + { + BNode *newNode = BLI_otherNode(rootArc, node); + return subtreeShape(newNode, rootArc, 0); + } + else + { + /* Base case, no arcs leading away */ + if (node->arcs == NULL || *(node->arcs) == NULL) + { + return 0; + } + else + { + int i; + + for(i = 0; i < node->degree; i++) + { + BArc *arc = node->arcs[i]; + BNode *newNode = BLI_otherNode(arc, node); + + /* stop immediate and cyclic backtracking */ + if (arc != rootArc && newNode->flag == 0) + { + depth += subtreeShape(newNode, arc, 0); + } + } + } + + return SHAPE_RADIX * depth + 1; + } +} + +int BLI_subtreeShape(BGraph *graph, BNode *node, BArc *rootArc, int include_root) +{ + BNode *test_node; + + BLI_flagNodes(graph, 0); + return subtreeShape(node, rootArc, include_root); +} + +float BLI_subtreeLength(BNode *node) +{ + float length = 0; + int i; + + node->flag = 0; /* flag node as visited */ + + for(i = 0; i < node->degree; i++) + { + BArc *arc = node->arcs[i]; + BNode *other_node = BLI_otherNode(arc, node); + + if (other_node->flag != 0) + { + float subgraph_length = arc->length + BLI_subtreeLength(other_node); + length = MAX2(length, subgraph_length); + } + } + + return length; +} + +void BLI_calcGraphLength(BGraph *graph) +{ + float length = 0; + int nb_subgraphs; + int i; + + nb_subgraphs = BLI_FlagSubgraphs(graph); + + for (i = 1; i <= nb_subgraphs; i++) + { + BNode *node; + + for (node = graph->nodes.first; node; node = node->next) + { + /* start on an external node of the subgraph */ + if (node->subgraph_index == i && node->degree == 1) + { + float subgraph_length = BLI_subtreeLength(node); + length = MAX2(length, subgraph_length); + break; + } + } + } + + graph->length = length; +} + +/********************************* SYMMETRY DETECTION **************************************************/ + +void markdownSymmetryArc(BGraph *graph, BArc *arc, BNode *node, int level, float limit); + +void BLI_mirrorAlongAxis(float v[3], float center[3], float axis[3]) +{ + float dv[3], pv[3]; + + VecSubf(dv, v, center); + Projf(pv, dv, axis); + VecMulf(pv, -2); + VecAddf(v, v, pv); +} + +static void testRadialSymmetry(BGraph *graph, BNode* root_node, RadialArc* ring, int total, float axis[3], float limit, int group) +{ + int symmetric = 1; + int i; + + /* sort ring by angle */ + for (i = 0; i < total - 1; i++) + { + float minAngle = FLT_MAX; + int minIndex = -1; + int j; + + for (j = i + 1; j < total; j++) + { + float angle = Inpf(ring[i].n, ring[j].n); + + /* map negative values to 1..2 */ + if (angle < 0) + { + angle = 1 - angle; + } + + if (angle < minAngle) + { + minIndex = j; + minAngle = angle; + } + } + + /* swap if needed */ + if (minIndex != i + 1) + { + RadialArc tmp; + tmp = ring[i + 1]; + ring[i + 1] = ring[minIndex]; + ring[minIndex] = tmp; + } + } + + for (i = 0; i < total && symmetric; i++) + { + BNode *node1, *node2; + float tangent[3]; + float normal[3]; + float p[3]; + int j = (i + 1) % total; /* next arc in the circular list */ + + VecAddf(tangent, ring[i].n, ring[j].n); + Crossf(normal, tangent, axis); + + node1 = BLI_otherNode(ring[i].arc, root_node); + node2 = BLI_otherNode(ring[j].arc, root_node); + + VECCOPY(p, node2->p); + BLI_mirrorAlongAxis(p, root_node->p, normal); + + /* check if it's within limit before continuing */ + if (VecLenf(node1->p, p) > limit) + { + symmetric = 0; + } + + } + + if (symmetric) + { + /* mark node as symmetric physically */ + VECCOPY(root_node->symmetry_axis, axis); + root_node->symmetry_flag |= SYM_PHYSICAL; + root_node->symmetry_flag |= SYM_RADIAL; + + /* FLAG SYMMETRY GROUP */ + for (i = 0; i < total; i++) + { + ring[i].arc->symmetry_group = group; + ring[i].arc->symmetry_flag = SYM_SIDE_RADIAL + i; + } + + if (graph->radial_symmetry) + { + graph->radial_symmetry(root_node, ring, total); + } + } +} + +static void handleRadialSymmetry(BGraph *graph, BNode *root_node, int depth, float axis[3], float limit) +{ + RadialArc *ring = NULL; + RadialArc *unit; + int total = 0; + int group; + int first; + int i; + + /* mark topological symmetry */ + root_node->symmetry_flag |= SYM_TOPOLOGICAL; + + /* total the number of arcs in the symmetry ring */ + for (i = 0; i < root_node->degree; i++) + { + BArc *connectedArc = root_node->arcs[i]; + + /* depth is store as a negative in flag. symmetry level is positive */ + if (connectedArc->symmetry_level == -depth) + { + total++; + } + } + + ring = MEM_callocN(sizeof(RadialArc) * total, "radial symmetry ring"); + unit = ring; + + /* fill in the ring */ + for (unit = ring, i = 0; i < root_node->degree; i++) + { + BArc *connectedArc = root_node->arcs[i]; + + /* depth is store as a negative in flag. symmetry level is positive */ + if (connectedArc->symmetry_level == -depth) + { + BNode *otherNode = BLI_otherNode(connectedArc, root_node); + float vec[3]; + + unit->arc = connectedArc; + + /* project the node to node vector on the symmetry plane */ + VecSubf(unit->n, otherNode->p, root_node->p); + Projf(vec, unit->n, axis); + VecSubf(unit->n, unit->n, vec); + + Normalize(unit->n); + + unit++; + } + } + + /* sort ring by arc length + * using a rather bogus insertion sort + * butrings will never get too big to matter + * */ + for (i = 0; i < total; i++) + { + int j; + + for (j = i - 1; j >= 0; j--) + { + BArc *arc1, *arc2; + + arc1 = ring[j].arc; + arc2 = ring[j + 1].arc; + + if (arc1->length > arc2->length) + { + /* swap with smaller */ + RadialArc tmp; + + tmp = ring[j + 1]; + ring[j + 1] = ring[j]; + ring[j] = tmp; + } + else + { + break; + } + } + } + + /* Dispatch to specific symmetry tests */ + first = 0; + group = 0; + + for (i = 1; i < total; i++) + { + int dispatch = 0; + int last = i - 1; + + if (fabs(ring[first].arc->length - ring[i].arc->length) > limit) + { + dispatch = 1; + } + + /* if not dispatching already and on last arc + * Dispatch using current arc as last + * */ + if (dispatch == 0 && i == total - 1) + { + last = i; + dispatch = 1; + } + + if (dispatch) + { + int sub_total = last - first + 1; + + group += 1; + + if (sub_total == 1) + { + group -= 1; /* not really a group so decrement */ + /* NOTHING TO DO */ + } + else if (sub_total == 2) + { + BArc *arc1, *arc2; + BNode *node1, *node2; + + arc1 = ring[first].arc; + arc2 = ring[last].arc; + + node1 = BLI_otherNode(arc1, root_node); + node2 = BLI_otherNode(arc2, root_node); + + testAxialSymmetry(graph, root_node, node1, node2, arc1, arc2, axis, limit, group); + } + else if (sub_total != total) /* allocate a new sub ring if needed */ + { + RadialArc *sub_ring = MEM_callocN(sizeof(RadialArc) * sub_total, "radial symmetry ring"); + int sub_i; + + /* fill in the sub ring */ + for (sub_i = 0; sub_i < sub_total; sub_i++) + { + sub_ring[sub_i] = ring[first + sub_i]; + } + + testRadialSymmetry(graph, root_node, sub_ring, sub_total, axis, limit, group); + + MEM_freeN(sub_ring); + } + else if (sub_total == total) + { + testRadialSymmetry(graph, root_node, ring, total, axis, limit, group); + } + + first = i; + } + } + + + MEM_freeN(ring); +} + +static void flagAxialSymmetry(BNode *root_node, BNode *end_node, BArc *arc, int group) +{ + float vec[3]; + + arc->symmetry_group = group; + + VecSubf(vec, end_node->p, root_node->p); + + if (Inpf(vec, root_node->symmetry_axis) < 0) + { + arc->symmetry_flag |= SYM_SIDE_NEGATIVE; + } + else + { + arc->symmetry_flag |= SYM_SIDE_POSITIVE; + } +} + +static void testAxialSymmetry(BGraph *graph, BNode* root_node, BNode* node1, BNode* node2, BArc* arc1, BArc* arc2, float axis[3], float limit, int group) +{ + float nor[3], vec[3], p[3]; + + VecSubf(p, node1->p, root_node->p); + Crossf(nor, p, axis); + + VecSubf(p, root_node->p, node2->p); + Crossf(vec, p, axis); + VecAddf(vec, vec, nor); + + Crossf(nor, vec, axis); + + if (abs(nor[0]) > abs(nor[1]) && abs(nor[0]) > abs(nor[2]) && nor[0] < 0) + { + VecMulf(nor, -1); + } + else if (abs(nor[1]) > abs(nor[0]) && abs(nor[1]) > abs(nor[2]) && nor[1] < 0) + { + VecMulf(nor, -1); + } + else if (abs(nor[2]) > abs(nor[1]) && abs(nor[2]) > abs(nor[0]) && nor[2] < 0) + { + VecMulf(nor, -1); + } + + /* mirror node2 along axis */ + VECCOPY(p, node2->p); + BLI_mirrorAlongAxis(p, root_node->p, nor); + + /* check if it's within limit before continuing */ + if (VecLenf(node1->p, p) <= limit) + { + /* mark node as symmetric physically */ + VECCOPY(root_node->symmetry_axis, nor); + root_node->symmetry_flag |= SYM_PHYSICAL; + root_node->symmetry_flag |= SYM_AXIAL; + + /* flag side on arcs */ + flagAxialSymmetry(root_node, node1, arc1, group); + flagAxialSymmetry(root_node, node2, arc2, group); + + if (graph->axial_symmetry) + { + graph->axial_symmetry(root_node, node1, node2, arc1, arc2); + } + } + else + { + /* NOT SYMMETRIC */ + } +} + +static void handleAxialSymmetry(BGraph *graph, BNode *root_node, int depth, float axis[3], float limit) +{ + BArc *arc1 = NULL, *arc2 = NULL; + BNode *node1 = NULL, *node2 = NULL; + int i; + + /* mark topological symmetry */ + root_node->symmetry_flag |= SYM_TOPOLOGICAL; + + for (i = 0; i < root_node->degree; i++) + { + BArc *connectedArc = root_node->arcs[i]; + + /* depth is store as a negative in flag. symmetry level is positive */ + if (connectedArc->symmetry_level == -depth) + { + if (arc1 == NULL) + { + arc1 = connectedArc; + node1 = BLI_otherNode(arc1, root_node); + } + else + { + arc2 = connectedArc; + node2 = BLI_otherNode(arc2, root_node); + break; /* Can stop now, the two arcs have been found */ + } + } + } + + /* shouldn't happen, but just to be sure */ + if (node1 == NULL || node2 == NULL) + { + return; + } + + testAxialSymmetry(graph, root_node, node1, node2, arc1, arc2, axis, limit, 1); +} + +static void markdownSecondarySymmetry(BGraph *graph, BNode *node, int depth, int level, float limit) +{ + float axis[3] = {0, 0, 0}; + int count = 0; + int i; + + /* count the number of branches in this symmetry group + * and determinte the axis of symmetry + * */ + for (i = 0; i < node->degree; i++) + { + BArc *connectedArc = node->arcs[i]; + + /* depth is store as a negative in flag. symmetry level is positive */ + if (connectedArc->symmetry_level == -depth) + { + count++; + } + /* If arc is on the axis */ + else if (connectedArc->symmetry_level == level) + { + VecAddf(axis, axis, connectedArc->head->p); + VecSubf(axis, axis, connectedArc->tail->p); + } + } + + Normalize(axis); + + /* Split between axial and radial symmetry */ + if (count == 2) + { + handleAxialSymmetry(graph, node, depth, axis, limit); + } + else + { + handleRadialSymmetry(graph, node, depth, axis, limit); + } + + /* markdown secondary symetries */ + for (i = 0; i < node->degree; i++) + { + BArc *connectedArc = node->arcs[i]; + + if (connectedArc->symmetry_level == -depth) + { + /* markdown symmetry for branches corresponding to the depth */ + markdownSymmetryArc(graph, connectedArc, node, level + 1, limit); + } + } +} + +void markdownSymmetryArc(BGraph *graph, BArc *arc, BNode *node, int level, float limit) +{ + int i; + + /* if arc is null, we start straight from a node */ + if (arc) + { + arc->symmetry_level = level; + + node = BLI_otherNode(arc, node); + } + + for (i = 0; i < node->degree; i++) + { + BArc *connectedArc = node->arcs[i]; + + if (connectedArc != arc) + { + BNode *connectedNode = BLI_otherNode(connectedArc, node); + + /* symmetry level is positive value, negative values is subtree depth */ + connectedArc->symmetry_level = -BLI_subtreeShape(graph, connectedNode, connectedArc, 0); + } + } + + arc = NULL; + + for (i = 0; i < node->degree; i++) + { + int issymmetryAxis = 0; + BArc *connectedArc = node->arcs[i]; + + /* only arcs not already marked as symetric */ + if (connectedArc->symmetry_level < 0) + { + int j; + + /* true by default */ + issymmetryAxis = 1; + + for (j = 0; j < node->degree; j++) + { + BArc *otherArc = node->arcs[j]; + + /* different arc, same depth */ + if (otherArc != connectedArc && otherArc->symmetry_level == connectedArc->symmetry_level) + { + /* not on the symmetry axis */ + issymmetryAxis = 0; + break; + } + } + } + + /* arc could be on the symmetry axis */ + if (issymmetryAxis == 1) + { + /* no arc as been marked previously, keep this one */ + if (arc == NULL) + { + arc = connectedArc; + } + else if (connectedArc->symmetry_level < arc->symmetry_level) + { + /* go with more complex subtree as symmetry arc */ + arc = connectedArc; + } + } + } + + /* go down the arc continuing the symmetry axis */ + if (arc) + { + markdownSymmetryArc(graph, arc, node, level, limit); + } + + + /* secondary symmetry */ + for (i = 0; i < node->degree; i++) + { + BArc *connectedArc = node->arcs[i]; + + /* only arcs not already marked as symetric and is not the next arc on the symmetry axis */ + if (connectedArc->symmetry_level < 0) + { + /* subtree depth is store as a negative value in the symmetry */ + markdownSecondarySymmetry(graph, node, -connectedArc->symmetry_level, level, limit); + } + } +} + +void BLI_markdownSymmetry(BGraph *graph, BNode *root_node, float limit) +{ + BNode *node; + BArc *arc; + + if (BLI_isGraphCyclic(graph)) + { + return; + } + + /* mark down all arcs as non-symetric */ + BLI_flagArcs(graph, 0); + + /* mark down all nodes as not on the symmetry axis */ + BLI_flagNodes(graph, 0); + + node = root_node; + + /* sanity check REMOVE ME */ + if (node->degree > 0) + { + arc = node->arcs[0]; + + if (node->degree == 1) + { + markdownSymmetryArc(graph, arc, node, 1, limit); + } + else + { + markdownSymmetryArc(graph, NULL, node, 1, limit); + } + + + + /* mark down non-symetric arcs */ + for (arc = graph->arcs.first; arc; arc = arc->next) + { + if (arc->symmetry_level < 0) + { + arc->symmetry_level = 0; + } + else + { + /* mark down nodes with the lowest level symmetry axis */ + if (arc->head->symmetry_level == 0 || arc->head->symmetry_level > arc->symmetry_level) + { + arc->head->symmetry_level = arc->symmetry_level; + } + if (arc->tail->symmetry_level == 0 || arc->tail->symmetry_level > arc->symmetry_level) + { + arc->tail->symmetry_level = arc->symmetry_level; + } + } + } + } +} + diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c index 92fad291e83..9df8bbc81e3 100644 --- a/source/blender/blenlib/intern/threads.c +++ b/source/blender/blenlib/intern/threads.c @@ -38,6 +38,8 @@ #include "BLI_blenlib.h" #include "BLI_threads.h" +#include "PIL_time.h" + /* for checking system threads - BLI_system_thread_count */ #ifdef WIN32 #include "Windows.h" @@ -199,6 +201,34 @@ void BLI_remove_thread(ListBase *threadbase, void *callerdata) } } +void BLI_remove_thread_index(ListBase *threadbase, int index) +{ + ThreadSlot *tslot; + int counter=0; + + for(tslot = threadbase->first; tslot; tslot = tslot->next, counter++) { + if (counter == index && tslot->avail == 0) { + tslot->callerdata = NULL; + pthread_join(tslot->pthread, NULL); + tslot->avail = 1; + break; + } + } +} + +void BLI_remove_threads(ListBase *threadbase) +{ + ThreadSlot *tslot; + + for(tslot = threadbase->first; tslot; tslot = tslot->next) { + if (tslot->avail == 0) { + tslot->callerdata = NULL; + pthread_join(tslot->pthread, NULL); + tslot->avail = 1; + } + } +} + void BLI_end_threads(ListBase *threadbase) { ThreadSlot *tslot; @@ -265,4 +295,104 @@ int BLI_system_thread_count( void ) return t; } +/* ************************************************ */ + +typedef struct ThreadedWorker { + ListBase threadbase; + void *(*work_fnct)(void *); + char busy[RE_MAX_THREAD]; + int total; + int sleep_time; +} ThreadedWorker; + +typedef struct WorkParam { + ThreadedWorker *worker; + void *param; + int index; +} WorkParam; + +void *exec_work_fnct(void *v_param) +{ + WorkParam *p = (WorkParam*)v_param; + void *value; + + value = p->worker->work_fnct(p->param); + + p->worker->busy[p->index] = 0; + MEM_freeN(p); + + return value; +} + +ThreadedWorker *BLI_create_worker(void *(*do_thread)(void *), int tot, int sleep_time) +{ + ThreadedWorker *worker; + + worker = MEM_callocN(sizeof(ThreadedWorker), "threadedworker"); + + if (tot > RE_MAX_THREAD) + { + tot = RE_MAX_THREAD; + } + else if (tot < 1) + { + tot= 1; + } + + worker->total = tot; + worker->work_fnct = do_thread; + + BLI_init_threads(&worker->threadbase, exec_work_fnct, tot); + + return worker; +} + +void BLI_end_worker(ThreadedWorker *worker) +{ + BLI_remove_threads(&worker->threadbase); +} + +void BLI_destroy_worker(ThreadedWorker *worker) +{ + BLI_end_worker(worker); + BLI_freelistN(&worker->threadbase); + MEM_freeN(worker); +} + +void BLI_insert_work(ThreadedWorker *worker, void *param) +{ + WorkParam *p = MEM_callocN(sizeof(WorkParam), "workparam"); + int index; + + if (BLI_available_threads(&worker->threadbase) == 0) + { + index = worker->total; + while(index == worker->total) + { + PIL_sleep_ms(worker->sleep_time); + + for (index = 0; index < worker->total; index++) + { + if (worker->busy[index] == 0) + { + BLI_remove_thread_index(&worker->threadbase, index); + break; + } + } + } + } + else + { + index = BLI_available_thread_index(&worker->threadbase); + } + + worker->busy[index] = 1; + + p->param = param; + p->index = index; + p->worker = worker; + + BLI_insert_thread(&worker->threadbase, p); +} + /* eof */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index edb9c3ff898..3a9d0a6ae6a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -7379,6 +7379,24 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } } + + /* sanity check for skgen + * */ + { + Scene *sce; + for(sce=main->scene.first; sce; sce = sce->id.next) + { + if (sce->toolsettings->skgen_subdivisions[0] == sce->toolsettings->skgen_subdivisions[1] || + sce->toolsettings->skgen_subdivisions[0] == sce->toolsettings->skgen_subdivisions[2] || + sce->toolsettings->skgen_subdivisions[1] == sce->toolsettings->skgen_subdivisions[2]) + { + sce->toolsettings->skgen_subdivisions[0] = SKGEN_SUB_CORRELATION; + sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_LENGTH; + sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE; + } + } + } + if ((main->versionfile < 245) || (main->versionfile == 245 && main->subversionfile < 2)) { Image *ima; diff --git a/source/blender/include/BIF_editarmature.h b/source/blender/include/BIF_editarmature.h index d390b96f61f..02d73680818 100644 --- a/source/blender/include/BIF_editarmature.h +++ b/source/blender/include/BIF_editarmature.h @@ -68,6 +68,8 @@ typedef struct EditBone } EditBone; +void make_boneList(struct ListBase *list, struct ListBase *bones, EditBone *parent); +void editbones_to_armature (struct ListBase *list, struct Object *ob); void adduplicate_armature(void); void addvert_armature(void); @@ -148,6 +150,15 @@ void align_selected_bones(void); #define BONESEL_NOSEL 0x80000000 /* Indicates a negative number */ +/* from autoarmature */ +void BIF_retargetArmature(); +void BIF_adjustRetarget(); +void BIF_freeRetarget(); + +struct ReebArc; +float calcVariance(struct ReebArc *arc, int start, int end, float v0[3], float n[3]); +float calcDistance(struct ReebArc *arc, int start, int end, float head[3], float tail[3]); + /* useful macros */ #define EBONE_VISIBLE(arm, ebone) ((arm->layer & ebone->layer) && !(ebone->flag & BONE_HIDDEN_A)) #define EBONE_EDITABLE(ebone) ((ebone->flag & BONE_SELECTED) && !(ebone->flag & BONE_EDITMODE_LOCKED)) diff --git a/source/blender/include/butspace.h b/source/blender/include/butspace.h index 3de427476b9..8cac5a074ab 100644 --- a/source/blender/include/butspace.h +++ b/source/blender/include/butspace.h @@ -443,7 +443,8 @@ void curvemap_buttons(struct uiBlock *block, struct CurveMapping *cumap, char la #define B_SETMCOL_RND 2083 #define B_DRAWBWEIGHTS 2084 -#define B_GEN_SKELETON 2090 +#define B_GEN_SKELETON 2085 +#define B_RETARGET_SKELETON 2086 /* *********************** */ #define B_VGROUPBUTS 2100 diff --git a/source/blender/include/reeb.h b/source/blender/include/reeb.h index c8352aedec5..57f7d1b60cf 100644 --- a/source/blender/include/reeb.h +++ b/source/blender/include/reeb.h @@ -28,18 +28,35 @@ #ifndef REEB_H_ #define REEB_H_ +//#define WITH_BF_REEB + #include "DNA_listBase.h" +#include "BLI_graph.h" + +struct GHash; struct EdgeHash; struct ReebArc; struct ReebEdge; struct ReebNode; typedef struct ReebGraph { - ListBase arcs; - ListBase nodes; + ListBase arcs; + ListBase nodes; + + float length; + + FreeArc free_arc; + FreeNode free_node; + RadialSymmetry radial_symmetry; + AxialSymmetry axial_symmetry; + /*********************************/ + + int resolution; int totnodes; struct EdgeHash *emap; + int multi_level; + struct ReebGraph *link_up; /* for multi resolution filtering, points to higher levels */ } ReebGraph; typedef struct EmbedBucket { @@ -49,13 +66,25 @@ typedef struct EmbedBucket { } EmbedBucket; typedef struct ReebNode { - struct ReebNode *next, *prev; - struct ReebArc **arcs; - int index; - int degree; - float weight; + void *next, *prev; float p[3]; - int flags; + int flag; + + int degree; + struct ReebArc **arcs; + + int subgraph_index; + + int symmetry_level; + int symmetry_flag; + float symmetry_axis[3]; + /*********************************/ + + int index; + float weight; + int multi_level; + struct ReebNode *link_down; /* for multi resolution filtering, points to lower levels, if present */ + struct ReebNode *link_up; } ReebNode; typedef struct ReebEdge { @@ -63,15 +92,28 @@ typedef struct ReebEdge { struct ReebArc *arc; struct ReebNode *v1, *v2; struct ReebEdge *nextEdge; + int flag; } ReebEdge; typedef struct ReebArc { - struct ReebArc *next, *prev; + void *next, *prev; + struct ReebNode *head, *tail; + int flag; + + float length; + + int symmetry_level; + int symmetry_group; + int symmetry_flag; + /*********************************/ + ListBase edges; - struct ReebNode *v1, *v2; + int bcount; struct EmbedBucket *buckets; - int bcount; - int flags; + + struct GHash *faces; + float angle; + struct ReebArc *link_up; /* for multi resolution filtering, points to higher levels */ } ReebArc; typedef struct ReebArcIterator { @@ -79,29 +121,37 @@ typedef struct ReebArcIterator { int index; int start; int end; - int stride; + int stride; + int length; } ReebArcIterator; struct EditMesh; +struct EdgeIndex; -int weightToHarmonic(struct EditMesh *em); -int weightFromDistance(struct EditMesh *em); +int weightToHarmonic(struct EditMesh *em, struct EdgeIndex *indexed_edges); +int weightFromDistance(struct EditMesh *em, struct EdgeIndex *indexed_edges); int weightFromLoc(struct EditMesh *me, int axis); -void weightToVCol(struct EditMesh *em); +void weightToVCol(struct EditMesh *em, int index); +void arcToVCol(struct ReebGraph *rg, struct EditMesh *em, int index); +void angleToVCol(struct EditMesh *em, int index); void renormalizeWeight(struct EditMesh *em, float newmax); ReebGraph * generateReebGraph(struct EditMesh *me, int subdivisions); -void freeGraph(ReebGraph *rg); -void exportGraph(ReebGraph *rg, int count); - -#define OTHER_NODE(arc, node) ((arc->v1 == node) ? arc->v2 : arc->v1) +ReebGraph * newReebGraph(); void initArcIterator(struct ReebArcIterator *iter, struct ReebArc *arc, struct ReebNode *head); void initArcIterator2(struct ReebArcIterator *iter, struct ReebArc *arc, int start, int end); +void initArcIteratorStart(struct ReebArcIterator *iter, struct ReebArc *arc, struct ReebNode *head, int start); struct EmbedBucket * nextBucket(struct ReebArcIterator *iter); +struct EmbedBucket * nextNBucket(ReebArcIterator *iter, int n); +struct EmbedBucket * peekBucket(ReebArcIterator *iter, int n); +struct EmbedBucket * currentBucket(struct ReebArcIterator *iter); +struct EmbedBucket * previousBucket(struct ReebArcIterator *iter); +int iteratorStopped(struct ReebArcIterator *iter); /* Filtering */ void filterNullReebGraph(ReebGraph *rg); +int filterSmartReebGraph(ReebGraph *rg, float threshold); int filterExternalReebGraph(ReebGraph *rg, float threshold); int filterInternalReebGraph(ReebGraph *rg, float threshold); @@ -110,18 +160,33 @@ void repositionNodes(ReebGraph *rg); void postprocessGraph(ReebGraph *rg, char mode); void removeNormalNodes(ReebGraph *rg); -/* Graph processing */ -void buildAdjacencyList(ReebGraph *rg); - void sortNodes(ReebGraph *rg); void sortArcs(ReebGraph *rg); -int subtreeDepth(ReebNode *node, ReebArc *rootArc); -int countConnectedArcs(ReebGraph *rg, ReebNode *node); -int hasAdjacencyList(ReebGraph *rg); -int isGraphCyclic(ReebGraph *rg); - -/* Sanity check */ +/*------------ Sanity check ------------*/ void verifyBuckets(ReebGraph *rg); +void verifyFaces(ReebGraph *rg); + +/*********************** PUBLIC *********************************/ + +#define REEB_MAX_MULTI_LEVEL 10 + +ReebGraph *BIF_ReebGraphFromEditMesh(void); +ReebGraph *BIF_ReebGraphMultiFromEditMesh(void); +void BIF_flagMultiArcs(ReebGraph *rg, int flag); + +void BIF_GlobalReebGraphFromEditMesh(void); +void BIF_GlobalReebFree(void); + +ReebNode *BIF_otherNodeFromIndex(ReebArc *arc, ReebNode *node); +ReebNode *BIF_NodeFromIndex(ReebArc *arc, ReebNode *node); +ReebNode *BIF_lowestLevelNode(ReebNode *node); + +ReebGraph *BIF_graphForMultiNode(ReebGraph *rg, ReebNode *node); + +void REEB_freeGraph(ReebGraph *rg); +void REEB_exportGraph(ReebGraph *rg, int count); +void REEB_draw(); + #endif /*REEB_H_*/ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index a5eaf222941..c1c164f136f 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -433,14 +433,20 @@ typedef struct ToolSettings { float skgen_angle_limit; float skgen_correlation_limit; float skgen_symmetry_limit; + float skgen_retarget_angle_weight; + float skgen_retarget_length_weight; + float skgen_retarget_distance_weight; short skgen_options; char skgen_postpro; char skgen_postpro_passes; char skgen_subdivisions[3]; + char skgen_multi_level; + char skgen_optimisation_method; + + char tpad[6]; /* Alt+RMB option */ char edge_mode; - char pad3[4]; } ToolSettings; /* Used by all brushes to store their properties, which can be directly set @@ -837,12 +843,21 @@ typedef struct Scene { #define RETOPO_ELLIPSE 4 /* toolsettings->skgen_options */ -#define SKGEN_FILTER_INTERNAL 1 -#define SKGEN_FILTER_EXTERNAL 2 -#define SKGEN_SYMMETRY 4 -#define SKGEN_CUT_LENGTH 8 -#define SKGEN_CUT_ANGLE 16 -#define SKGEN_CUT_CORRELATION 32 +#define SKGEN_FILTER_INTERNAL (1 << 0) +#define SKGEN_FILTER_EXTERNAL (1 << 1) +#define SKGEN_SYMMETRY (1 << 2) +#define SKGEN_CUT_LENGTH (1 << 3) +#define SKGEN_CUT_ANGLE (1 << 4) +#define SKGEN_CUT_CORRELATION (1 << 5) +#define SKGEN_HARMONIC (1 << 6) +#define SKGEN_STICK_TO_EMBEDDING (1 << 7) +#define SKGEN_ADAPTIVE_DISTANCE (1 << 8) +#define SKGEN_FILTER_SMART (1 << 9) +#define SKGEN_DISP_LENGTH (1 << 10) +#define SKGEN_DISP_WEIGHT (1 << 11) +#define SKGEN_DISP_ORIG (1 << 12) +#define SKGEN_DISP_EMBED (1 << 13) +#define SKGEN_DISP_INDEX (1 << 14) #define SKGEN_SUB_LENGTH 0 #define SKGEN_SUB_ANGLE 1 diff --git a/source/blender/src/autoarmature.c b/source/blender/src/autoarmature.c new file mode 100644 index 00000000000..b0a7a2ab5cc --- /dev/null +++ b/source/blender/src/autoarmature.c @@ -0,0 +1,2968 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Martin Poirier + * + * ***** END GPL LICENSE BLOCK ***** + * autoarmature.c: Interface for automagically manipulating armature (retarget, created, ...) + */ + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +#include "DNA_ID.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" +#include "BLI_editVert.h" +#include "BLI_ghash.h" +#include "BLI_graph.h" +#include "BLI_rand.h" +#include "BLI_threads.h" + +#include "BDR_editobject.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_constraint.h" +#include "BKE_armature.h" + +#include "BIF_editarmature.h" +#include "BIF_space.h" + +#include "PIL_time.h" + +#include "mydevice.h" +#include "reeb.h" // FIX ME +#include "blendef.h" + +/************ RIG RETARGET DATA STRUCTURES ***************/ + +struct RigJoint; +struct RigGraph; +struct RigNode; +struct RigArc; +struct RigEdge; + +//#define USE_THREADS + +typedef struct RigGraph { + ListBase arcs; + ListBase nodes; + + float length; + + FreeArc free_arc; + FreeNode free_node; + RadialSymmetry radial_symmetry; + AxialSymmetry axial_symmetry; + /*********************************/ + + struct RigNode *head; + ReebGraph *link_mesh; + + ListBase editbones; + + ListBase controls; + struct ThreadedWorker *worker; + + GHash *bones_map; /* map of editbones by name */ + GHash *controls_map; /* map of rigcontrols by bone pointer */ + + Object *ob; +} RigGraph; + +typedef struct RigNode { + void *next, *prev; + float p[3]; + int flag; + + int degree; + struct BArc **arcs; + + int subgraph_index; + + int symmetry_level; + int symmetry_flag; + float symmetry_axis[3]; + /*********************************/ + + ReebNode *link_mesh; +} RigNode; + +typedef struct RigArc { + void *next, *prev; + RigNode *head, *tail; + int flag; + + float length; + + int symmetry_level; + int symmetry_group; + int symmetry_flag; + /*********************************/ + + ListBase edges; + int count; + ReebArc *link_mesh; +} RigArc; + +typedef struct RigEdge { + struct RigEdge *next, *prev; + float head[3], tail[3]; + float length; + float angle; + EditBone *bone; + float up_axis[3]; +} RigEdge; + +/* Control flags */ +#define RIG_CTRL_DONE 1 +#define RIG_CTRL_PARENT_DEFORM 2 +#define RIG_CTRL_FIT_ROOT 4 +#define RIG_CTRL_FIT_BONE 8 + +typedef struct RigControl { + struct RigControl *next, *prev; + float head[3], tail[3]; + EditBone *bone; + EditBone *link; + float up_axis[3]; + float offset[3]; + int flag; +} RigControl; + +typedef struct MemoNode { + float weight; + int next; +} MemoNode; + +typedef struct RetargetParam { + RigGraph *rigg; + RigArc *iarc; + RigNode *inode_start; +} RetargetParam; + +typedef enum +{ + RETARGET_LENGTH, + RETARGET_AGGRESSIVE +} RetargetMode; + +typedef enum +{ + METHOD_BRUTE_FORCE = 0, + METHOD_MEMOIZE = 1, + METHOD_ANNEALING = 2 +} RetargetMethod; + +typedef enum +{ + ARC_FREE = 0, + ARC_TAKEN = 1, + ARC_USED = 2 +} ArcUsageFlags; + + +RigGraph *GLOBAL_RIGG = NULL; + +/*******************************************************************************************************/ + +void *exec_retargetArctoArc(void *param); + +static void RIG_calculateEdgeAngle(RigEdge *edge_first, RigEdge *edge_second); + +/* two levels */ +#define SHAPE_LEVELS (SHAPE_RADIX * SHAPE_RADIX) + +/*********************************** EDITBONE UTILS ****************************************************/ + +int countEditBoneChildren(ListBase *list, EditBone *parent) +{ + EditBone *ebone; + int count = 0; + + for (ebone = list->first; ebone; ebone = ebone->next) + { + if (ebone->parent == parent) + { + count++; + } + } + + return count; +} + +EditBone* nextEditBoneChild(ListBase *list, EditBone *parent, int n) +{ + EditBone *ebone; + + for (ebone = list->first; ebone; ebone = ebone->next) + { + if (ebone->parent == parent) + { + if (n == 0) + { + return ebone; + } + n--; + } + } + + return NULL; +} + +void getEditBoneRollUpAxis(EditBone *bone, float roll, float up_axis[3]) +{ + float mat[3][3], nor[3]; + + VecSubf(nor, bone->tail, bone->head); + + vec_roll_to_mat3(nor, roll, mat); + VECCOPY(up_axis, mat[2]); +} + +float getNewBoneRoll(EditBone *bone, float old_up_axis[3], float quat[4]) +{ + float mat[3][3]; + float nor[3], up_axis[3], new_up_axis[3], vec[3]; + float roll; + + VECCOPY(new_up_axis, old_up_axis); + QuatMulVecf(quat, new_up_axis); + + VecSubf(nor, bone->tail, bone->head); + + vec_roll_to_mat3(nor, 0, mat); + VECCOPY(up_axis, mat[2]); + + roll = NormalizedVecAngle2(new_up_axis, up_axis); + + Crossf(vec, up_axis, new_up_axis); + + if (Inpf(vec, nor) < 0) + { + roll = -roll; + } + + return roll; +} + +/************************************ DESTRUCTORS ******************************************************/ + +void RIG_freeRigArc(BArc *arc) +{ + BLI_freelistN(&((RigArc*)arc)->edges); +} + +void RIG_freeRigGraph(BGraph *rg) +{ + BNode *node; + BArc *arc; + +#ifdef USE_THREADS + BLI_destroy_worker(((RigGraph*)rg)->worker); +#endif + + REEB_freeGraph(((RigGraph*)rg)->link_mesh); + + for (arc = rg->arcs.first; arc; arc = arc->next) + { + RIG_freeRigArc(arc); + } + BLI_freelistN(&rg->arcs); + + for (node = rg->nodes.first; node; node = node->next) + { + BLI_freeNode(rg, (BNode*)node); + } + BLI_freelistN(&rg->nodes); + + BLI_freelistN(&((RigGraph*)rg)->controls); + + BLI_ghash_free(((RigGraph*)rg)->bones_map, NULL, NULL); + BLI_ghash_free(((RigGraph*)rg)->controls_map, NULL, NULL); + + BLI_freelistN(&((RigGraph*)rg)->editbones); + + MEM_freeN(rg); +} + +/************************************* ALLOCATORS ******************************************************/ + +static RigGraph *newRigGraph() +{ + RigGraph *rg; + int totthread; + + rg = MEM_callocN(sizeof(RigGraph), "rig graph"); + + rg->head = NULL; + + rg->bones_map = BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp); + rg->controls_map = BLI_ghash_new(BLI_ghashutil_strhash, BLI_ghashutil_strcmp); + + rg->free_arc = RIG_freeRigArc; + rg->free_node = NULL; + +#ifdef USE_THREADS + if(G.scene->r.mode & R_FIXED_THREADS) + { + totthread = G.scene->r.threads; + } + else + { + totthread = BLI_system_thread_count(); + } + + rg->worker = BLI_create_worker(exec_retargetArctoArc, totthread, 20); /* fix number of threads */ +#endif + + return rg; +} + +static RigArc *newRigArc(RigGraph *rg) +{ + RigArc *arc; + + arc = MEM_callocN(sizeof(RigArc), "rig arc"); + arc->count = 0; + BLI_addtail(&rg->arcs, arc); + + return arc; +} + +static RigControl *newRigControl(RigGraph *rg) +{ + RigControl *ctrl; + + ctrl = MEM_callocN(sizeof(RigControl), "rig control"); + + BLI_addtail(&rg->controls, ctrl); + + return ctrl; +} + +static RigNode *newRigNodeHead(RigGraph *rg, RigArc *arc, float p[3]) +{ + RigNode *node; + node = MEM_callocN(sizeof(RigNode), "rig node"); + BLI_addtail(&rg->nodes, node); + + VECCOPY(node->p, p); + node->degree = 1; + node->arcs = NULL; + + arc->head = node; + + return node; +} + +static void addRigNodeHead(RigGraph *rg, RigArc *arc, RigNode *node) +{ + node->degree++; + + arc->head = node; +} + +static RigNode *newRigNode(RigGraph *rg, float p[3]) +{ + RigNode *node; + node = MEM_callocN(sizeof(RigNode), "rig node"); + BLI_addtail(&rg->nodes, node); + + VECCOPY(node->p, p); + node->degree = 0; + node->arcs = NULL; + + return node; +} + +static RigNode *newRigNodeTail(RigGraph *rg, RigArc *arc, float p[3]) +{ + RigNode *node = newRigNode(rg, p); + + node->degree = 1; + arc->tail = node; + + return node; +} + +static void RIG_appendEdgeToArc(RigArc *arc, RigEdge *edge) +{ + BLI_addtail(&arc->edges, edge); + + if (edge->prev == NULL) + { + VECCOPY(edge->head, arc->head->p); + } + else + { + RigEdge *last_edge = edge->prev; + VECCOPY(edge->head, last_edge->tail); + RIG_calculateEdgeAngle(last_edge, edge); + } + + edge->length = VecLenf(edge->head, edge->tail); + + arc->length += edge->length; + + arc->count += 1; +} + +static void RIG_addEdgeToArc(RigArc *arc, float tail[3], EditBone *bone) +{ + RigEdge *edge; + + edge = MEM_callocN(sizeof(RigEdge), "rig edge"); + + VECCOPY(edge->tail, tail); + edge->bone = bone; + + if (bone) + { + getEditBoneRollUpAxis(bone, bone->roll, edge->up_axis); + } + + RIG_appendEdgeToArc(arc, edge); +} + +/*******************************************************************************************************/ + +static void RIG_calculateEdgeAngle(RigEdge *edge_first, RigEdge *edge_second) +{ + float vec_first[3], vec_second[3]; + + VecSubf(vec_first, edge_first->tail, edge_first->head); + VecSubf(vec_second, edge_second->tail, edge_second->head); + + Normalize(vec_first); + Normalize(vec_second); + + edge_first->angle = saacos(Inpf(vec_first, vec_second)); +} + +/************************************ CONTROL BONES ****************************************************/ + +static void RIG_addControlBone(RigGraph *rg, EditBone *bone) +{ + RigControl *ctrl = newRigControl(rg); + ctrl->bone = bone; + VECCOPY(ctrl->head, bone->head); + VECCOPY(ctrl->tail, bone->tail); + getEditBoneRollUpAxis(bone, bone->roll, ctrl->up_axis); + + BLI_ghash_insert(rg->controls_map, bone->name, ctrl); +} + +static int RIG_parentControl(RigControl *ctrl, EditBone *link) +{ + if (link) + { + float offset[3]; + int flag = 0; + + VecSubf(offset, ctrl->bone->head, link->head); + + /* if root matches, check for direction too */ + if (Inpf(offset, offset) < 0.0001) + { + float vbone[3], vparent[3]; + + flag |= RIG_CTRL_FIT_ROOT; + + VecSubf(vbone, ctrl->bone->tail, ctrl->bone->head); + VecSubf(vparent, link->tail, link->head); + + /* test for opposite direction */ + if (Inpf(vbone, vparent) > 0) + { + float nor[3]; + float len; + + Crossf(nor, vbone, vparent); + + len = Inpf(nor, nor); + if (len < 0.0001) + { + flag |= RIG_CTRL_FIT_BONE; + } + } + } + + /* Bail out if old one is automatically better */ + if (flag < ctrl->flag) + { + return 0; + } + + /* if there's already a link + * overwrite only if new link is higher in the chain */ + if (ctrl->link && flag == ctrl->flag) + { + EditBone *bone = NULL; + + for (bone = ctrl->link; bone; bone = bone->parent) + { + /* if link is in the chain, break and use that one */ + if (bone == link) + { + break; + } + } + + /* not in chain, don't update link */ + if (bone == NULL) + { + return 0; + } + } + + + ctrl->link = link; + ctrl->flag = flag; + + VECCOPY(ctrl->offset, offset); + + return 1; + } + + return 0; +} + +static void RIG_reconnectControlBones(RigGraph *rg) +{ + RigControl *ctrl; + int change = 1; + + /* first pass, link to deform bones */ + for (ctrl = rg->controls.first; ctrl; ctrl = ctrl->next) + { + bPoseChannel *pchan; + bConstraint *con; + int found = 0; + + /* DO SOME MAGIC HERE */ + for (pchan= rg->ob->pose->chanbase.first; pchan; pchan= pchan->next) + { + for (con= pchan->constraints.first; con; con= con->next) + { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + /* constraint targets */ + if (cti && cti->get_constraint_targets) + { + cti->get_constraint_targets(con, &targets); + + for (ct= targets.first; ct; ct= ct->next) + { + if ((ct->tar == rg->ob) && strcmp(ct->subtarget, ctrl->bone->name) == 0) + { + /* SET bone link to bone corresponding to pchan */ + EditBone *link = BLI_ghash_lookup(rg->bones_map, pchan->name); + + found = RIG_parentControl(ctrl, link); + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } + } + } + + /* if not found yet, check parent */ + if (found == 0) + { + if (ctrl->bone->parent) + { + /* make sure parent is a deforming bone + * NULL if not + * */ + EditBone *link = BLI_ghash_lookup(rg->bones_map, ctrl->bone->parent->name); + + found = RIG_parentControl(ctrl, link); + } + + /* check if bone is not superposed on another one */ + { + RigArc *arc; + RigArc *best_arc = NULL; + EditBone *link = NULL; + + for (arc = rg->arcs.first; arc; arc = arc->next) + { + RigEdge *edge; + for (edge = arc->edges.first; edge; edge = edge->next) + { + if (edge->bone) + { + int fit = 0; + + fit = VecLenf(ctrl->bone->head, edge->bone->head) < 0.0001; + fit = fit || VecLenf(ctrl->bone->tail, edge->bone->tail) < 0.0001; + + if (fit) + { + /* pick the bone on the arc with the lowest symmetry level + * means you connect control to the trunk of the skeleton */ + if (best_arc == NULL || arc->symmetry_level < best_arc->symmetry_level) + { + best_arc = arc; + link = edge->bone; + } + } + } + } + } + + found = RIG_parentControl(ctrl, link); + } + } + + /* if not found yet, check child */ + if (found == 0) + { + RigArc *arc; + RigArc *best_arc = NULL; + EditBone *link = NULL; + + for (arc = rg->arcs.first; arc; arc = arc->next) + { + RigEdge *edge; + for (edge = arc->edges.first; edge; edge = edge->next) + { + if (edge->bone && edge->bone->parent == ctrl->bone) + { + /* pick the bone on the arc with the lowest symmetry level + * means you connect control to the trunk of the skeleton */ + if (best_arc == NULL || arc->symmetry_level < best_arc->symmetry_level) + { + best_arc = arc; + link = edge->bone; + } + } + } + } + + found = RIG_parentControl(ctrl, link); + } + + } + + + /* second pass, make chains in control bones */ + while (change) + { + change = 0; + + printf("-------------------------\n"); + + for (ctrl = rg->controls.first; ctrl; ctrl = ctrl->next) + { + /* if control is not linked yet */ + if (ctrl->link == NULL) + { + bPoseChannel *pchan; + bConstraint *con; + RigControl *ctrl_parent = NULL; + RigControl *ctrl_child; + int found = 0; + + if (ctrl->bone->parent) + { + ctrl_parent = BLI_ghash_lookup(rg->controls_map, ctrl->bone->parent->name); + } + + /* check constraints first */ + + /* DO SOME MAGIC HERE */ + for (pchan= rg->ob->pose->chanbase.first; pchan; pchan= pchan->next) + { + for (con= pchan->constraints.first; con; con= con->next) + { + bConstraintTypeInfo *cti= constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + /* constraint targets */ + if (cti && cti->get_constraint_targets) + { + cti->get_constraint_targets(con, &targets); + + for (ct= targets.first; ct; ct= ct->next) + { + if ((ct->tar == rg->ob) && strcmp(ct->subtarget, ctrl->bone->name) == 0) + { + /* SET bone link to ctrl corresponding to pchan */ + RigControl *link = BLI_ghash_lookup(rg->controls_map, pchan->name); + + /* if owner is a control bone, link with it */ + if (link && link->link) + { + printf("%s -constraint- %s\n", ctrl->bone->name, link->bone->name); + RIG_parentControl(ctrl, link->bone); + found = 1; + break; + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } + } + } + + if (found == 0) + { + /* check if parent is already linked */ + if (ctrl_parent && ctrl_parent->link) + { + printf("%s -parent- %s\n", ctrl->bone->name, ctrl_parent->bone->name); + RIG_parentControl(ctrl, ctrl_parent->bone); + change = 1; + } + else + { + /* check childs */ + for (ctrl_child = rg->controls.first; ctrl_child; ctrl_child = ctrl_child->next) + { + /* if a child is linked, link to that one */ + if (ctrl_child->link && ctrl_child->bone->parent == ctrl->bone) + { + printf("%s -child- %s\n", ctrl->bone->name, ctrl_child->bone->name); + RIG_parentControl(ctrl, ctrl_child->bone); + change = 1; + break; + } + } + } + } + } + } + + } +} + +/*******************************************************************************************************/ + +static void RIG_joinArcs(RigGraph *rg, RigNode *node, RigArc *joined_arc1, RigArc *joined_arc2) +{ + RigEdge *edge, *next_edge; + + /* ignore cases where joint is at start or end */ + if (joined_arc1->head == joined_arc2->head || joined_arc1->tail == joined_arc2->tail) + { + return; + } + + /* swap arcs to make sure arc1 is before arc2 */ + if (joined_arc1->head == joined_arc2->tail) + { + RigArc *tmp = joined_arc1; + joined_arc1 = joined_arc2; + joined_arc2 = tmp; + } + + for (edge = joined_arc2->edges.first; edge; edge = next_edge) + { + next_edge = edge->next; + + RIG_appendEdgeToArc(joined_arc1, edge); + } + + joined_arc1->tail = joined_arc2->tail; + + joined_arc2->edges.first = joined_arc2->edges.last = NULL; + + BLI_removeArc((BGraph*)rg, (BArc*)joined_arc2); + + BLI_removeNode((BGraph*)rg, (BNode*)node); +} + +static void RIG_removeNormalNodes(RigGraph *rg) +{ + RigNode *node, *next_node; + + for (node = rg->nodes.first; node; node = next_node) + { + next_node = node->next; + + if (node->degree == 2) + { + RigArc *arc, *joined_arc1 = NULL, *joined_arc2 = NULL; + + for (arc = rg->arcs.first; arc; arc = arc->next) + { + if (arc->head == node || arc->tail == node) + { + if (joined_arc1 == NULL) + { + joined_arc1 = arc; + } + else + { + joined_arc2 = arc; + break; + } + } + } + + RIG_joinArcs(rg, node, joined_arc1, joined_arc2); + } + } +} + +static void RIG_removeUneededOffsets(RigGraph *rg) +{ + RigArc *arc; + + for (arc = rg->arcs.first; arc; arc = arc->next) + { + RigEdge *first_edge, *last_edge; + + first_edge = arc->edges.first; + last_edge = arc->edges.last; + + if (first_edge->bone == NULL) + { + if (first_edge->bone == NULL && VecLenf(first_edge->tail, arc->head->p) <= 0.001) + { + BLI_remlink(&arc->edges, first_edge); + MEM_freeN(first_edge); + } + else if (arc->head->degree == 1) + { + RigNode *new_node = (RigNode*)BLI_FindNodeByPosition((BGraph*)rg, first_edge->tail, 0.001); + + if (new_node) + { + BLI_remlink(&arc->edges, first_edge); + MEM_freeN(first_edge); + BLI_replaceNodeInArc((BGraph*)rg, (BArc*)arc, (BNode*)new_node, (BNode*)arc->head); + } + else + { + RigEdge *next_edge = first_edge->next; + + if (next_edge) + { + BLI_remlink(&arc->edges, first_edge); + MEM_freeN(first_edge); + + VECCOPY(arc->head->p, next_edge->head); + } + } + } + else + { + /* check if all arc connected start with a null edge */ + RigArc *other_arc; + for (other_arc = rg->arcs.first; other_arc; other_arc = other_arc->next) + { + if (other_arc != arc) + { + RigEdge *test_edge; + if (other_arc->head == arc->head) + { + test_edge = other_arc->edges.first; + + if (test_edge->bone != NULL) + { + break; + } + } + else if (other_arc->tail == arc->head) + { + test_edge = other_arc->edges.last; + + if (test_edge->bone != NULL) + { + break; + } + } + } + } + + if (other_arc == NULL) + { + RigNode *new_node = (RigNode*)BLI_FindNodeByPosition((BGraph*)rg, first_edge->tail, 0.001); + + if (new_node) + { + /* remove null edge in other arcs too */ + for (other_arc = rg->arcs.first; other_arc; other_arc = other_arc->next) + { + if (other_arc != arc) + { + RigEdge *test_edge; + if (other_arc->head == arc->head) + { + BLI_replaceNodeInArc((BGraph*)rg, (BArc*)other_arc, (BNode*)new_node, (BNode*)other_arc->head); + test_edge = other_arc->edges.first; + BLI_remlink(&other_arc->edges, test_edge); + MEM_freeN(test_edge); + } + else if (other_arc->tail == arc->head) + { + BLI_replaceNodeInArc((BGraph*)rg, (BArc*)other_arc, (BNode*)new_node, (BNode*)other_arc->tail); + test_edge = other_arc->edges.last; + BLI_remlink(&other_arc->edges, test_edge); + MEM_freeN(test_edge); + } + } + } + + BLI_remlink(&arc->edges, first_edge); + MEM_freeN(first_edge); + BLI_replaceNodeInArc((BGraph*)rg, (BArc*)arc, (BNode*)new_node, (BNode*)arc->head); + } + else + { + RigEdge *next_edge = first_edge->next; + + if (next_edge) + { + BLI_remlink(&arc->edges, first_edge); + MEM_freeN(first_edge); + + VECCOPY(arc->head->p, next_edge->head); + + /* remove null edge in other arcs too */ + for (other_arc = rg->arcs.first; other_arc; other_arc = other_arc->next) + { + if (other_arc != arc) + { + RigEdge *test_edge; + if (other_arc->head == arc->head) + { + test_edge = other_arc->edges.first; + BLI_remlink(&other_arc->edges, test_edge); + MEM_freeN(test_edge); + } + else if (other_arc->tail == arc->head) + { + test_edge = other_arc->edges.last; + BLI_remlink(&other_arc->edges, test_edge); + MEM_freeN(test_edge); + } + } + } + } + } + } + } + } + + if (last_edge->bone == NULL) + { + if (VecLenf(last_edge->head, arc->tail->p) <= 0.001) + { + BLI_remlink(&arc->edges, last_edge); + MEM_freeN(last_edge); + } + else if (arc->tail->degree == 1) + { + RigNode *new_node = (RigNode*)BLI_FindNodeByPosition((BGraph*)rg, last_edge->head, 0.001); + + if (new_node) + { + RigEdge *previous_edge = last_edge->prev; + + BLI_remlink(&arc->edges, last_edge); + MEM_freeN(last_edge); + BLI_replaceNodeInArc((BGraph*)rg, (BArc*)arc, (BNode*)new_node, (BNode*)arc->tail); + + /* set previous angle to 0, since there's no following edges */ + if (previous_edge) + { + previous_edge->angle = 0; + } + } + else + { + RigEdge *previous_edge = last_edge->prev; + + if (previous_edge) + { + BLI_remlink(&arc->edges, last_edge); + MEM_freeN(last_edge); + + VECCOPY(arc->tail->p, previous_edge->tail); + previous_edge->angle = 0; + } + } + } + } + } +} + +static void RIG_arcFromBoneChain(RigGraph *rg, ListBase *list, EditBone *root_bone, RigNode *starting_node) +{ + EditBone *bone, *last_bone = root_bone; + RigArc *arc = NULL; + int contain_head = 0; + + for(bone = root_bone; bone; bone = nextEditBoneChild(list, bone, 0)) + { + int nb_children; + + if ((bone->flag & BONE_NO_DEFORM) == 0) + { + BLI_ghash_insert(rg->bones_map, bone->name, bone); + + if (arc == NULL) + { + arc = newRigArc(rg); + + if (starting_node == NULL) + { + starting_node = newRigNodeHead(rg, arc, root_bone->head); + } + else + { + addRigNodeHead(rg, arc, starting_node); + } + } + + if (bone->parent && (bone->flag & BONE_CONNECTED) == 0) + { + RIG_addEdgeToArc(arc, bone->head, NULL); + } + + RIG_addEdgeToArc(arc, bone->tail, bone); + + last_bone = bone; + + if (strcmp(bone->name, "head") == 0) + { + contain_head = 1; + } + } + else if ((bone->flag & BONE_EDITMODE_LOCKED) == 0) /* ignore locked bones */ + { + RIG_addControlBone(rg, bone); + } + + nb_children = countEditBoneChildren(list, bone); + if (nb_children > 1) + { + RigNode *end_node = NULL; + int i; + + if (arc != NULL) + { + end_node = newRigNodeTail(rg, arc, bone->tail); + } + else + { + end_node = newRigNode(rg, bone->tail); + } + + for (i = 0; i < nb_children; i++) + { + root_bone = nextEditBoneChild(list, bone, i); + RIG_arcFromBoneChain(rg, list, root_bone, end_node); + } + + /* arc ends here, break */ + break; + } + } + + /* If the loop exited without forking */ + if (arc != NULL && bone == NULL) + { + newRigNodeTail(rg, arc, last_bone->tail); + } + + if (contain_head) + { + rg->head = arc->tail; + } +} + +/*******************************************************************************************************/ +static void RIG_findHead(RigGraph *rg) +{ + if (rg->head == NULL) + { + if (BLI_countlist(&rg->arcs) == 1) + { + RigArc *arc = rg->arcs.first; + + rg->head = (RigNode*)arc->head; + } + else + { + RigArc *arc; + + for (arc = rg->arcs.first; arc; arc = arc->next) + { + RigEdge *edge = arc->edges.last; + + if (edge->bone->flag & (BONE_TIPSEL|BONE_SELECTED)) + { + rg->head = arc->tail; + break; + } + } + } + + if (rg->head == NULL) + { + rg->head = rg->nodes.first; + } + } +} + +/*******************************************************************************************************/ + +void RIG_printNode(RigNode *node, char name[]) +{ + printf("%s %p %i <%0.3f, %0.3f, %0.3f>\n", name, node, node->degree, node->p[0], node->p[1], node->p[2]); + + if (node->symmetry_flag & SYM_TOPOLOGICAL) + { + if (node->symmetry_flag & SYM_AXIAL) + printf("Symmetry AXIAL\n"); + else if (node->symmetry_flag & SYM_RADIAL) + printf("Symmetry RADIAL\n"); + + printvecf("symmetry axis", node->symmetry_axis); + } +} + +void RIG_printArcBones(RigArc *arc) +{ + RigEdge *edge; + + for (edge = arc->edges.first; edge; edge = edge->next) + { + if (edge->bone) + printf("%s ", edge->bone->name); + else + printf("---- "); + } + printf("\n"); +} + +void RIG_printCtrl(RigControl *ctrl, char *indent) +{ + char text[128]; + + printf("%sBone: %s\n", indent, ctrl->bone->name); + printf("%sLink: %s\n", indent, ctrl->link ? ctrl->link->name : "!NONE!"); + + sprintf(text, "%soffset", indent); + printvecf(text, ctrl->offset); + + printf("%sFlag: %i\n", indent, ctrl->flag); +} + +void RIG_printLinkedCtrl(RigGraph *rg, EditBone *bone, int tabs) +{ + RigControl *ctrl; + char indent[64]; + char *s = indent; + int i; + + for (i = 0; i < tabs; i++) + { + s[0] = '\t'; + s++; + } + s[0] = 0; + + for (ctrl = rg->controls.first; ctrl; ctrl = ctrl->next) + { + if (ctrl->link == bone) + { + RIG_printCtrl(ctrl, indent); + RIG_printLinkedCtrl(rg, ctrl->bone, tabs + 1); + } + } +} + +void RIG_printArc(RigGraph *rg, RigArc *arc) +{ + RigEdge *edge; + + RIG_printNode((RigNode*)arc->head, "head"); + + for (edge = arc->edges.first; edge; edge = edge->next) + { + printf("\tinner joints %0.3f %0.3f %0.3f\n", edge->tail[0], edge->tail[1], edge->tail[2]); + printf("\t\tlength %f\n", edge->length); + printf("\t\tangle %f\n", edge->angle * 180 / M_PI); + if (edge->bone) + { + printf("\t\t%s\n", edge->bone->name); + RIG_printLinkedCtrl(rg, edge->bone, 3); + } + } + printf("symmetry level: %i flag: %i group %i\n", arc->symmetry_level, arc->symmetry_flag, arc->symmetry_group); + + RIG_printNode((RigNode*)arc->tail, "tail"); +} + +void RIG_printGraph(RigGraph *rg) +{ + RigArc *arc; + + printf("---- ARCS ----\n"); + for (arc = rg->arcs.first; arc; arc = arc->next) + { + RIG_printArc(rg, arc); + printf("\n"); + } + + if (rg->head) + { + RIG_printNode(rg->head, "HEAD NODE:"); + } + else + { + printf("HEAD NODE: NONE\n"); + } +} + +/*******************************************************************************************************/ + +static RigGraph *armatureToGraph(Object *ob, bArmature *arm) +{ + EditBone *ebone; + RigGraph *rg; + + rg = newRigGraph(); + + make_boneList(&rg->editbones, &arm->bonebase, NULL); + rg->ob = ob; + + /* Do the rotations */ + for (ebone = rg->editbones.first; ebone; ebone=ebone->next){ + if (ebone->parent == NULL) + { + RIG_arcFromBoneChain(rg, &rg->editbones, ebone, NULL); + } + } + + BLI_removeDoubleNodes((BGraph*)rg, 0.001); + + RIG_removeNormalNodes(rg); + + RIG_removeUneededOffsets(rg); + + BLI_buildAdjacencyList((BGraph*)rg); + + RIG_findHead(rg); + + BLI_markdownSymmetry((BGraph*)rg, (BNode*)rg->head, G.scene->toolsettings->skgen_symmetry_limit); + + RIG_reconnectControlBones(rg); /* after symmetry, because we use levels to find best match */ + + if (BLI_isGraphCyclic((BGraph*)rg)) + { + printf("armature cyclic\n"); + } + + return rg; +} + +/************************************ GENERATING *****************************************************/ + +static EditBone *add_editbonetolist(char *name, ListBase *list) +{ + EditBone *bone= MEM_callocN(sizeof(EditBone), "eBone"); + + BLI_strncpy(bone->name, name, 32); + unique_editbone_name(list, bone->name); + + BLI_addtail(list, bone); + + bone->flag |= BONE_TIPSEL; + bone->weight= 1.0F; + bone->dist= 0.25F; + bone->xwidth= 0.1; + bone->zwidth= 0.1; + bone->ease1= 1.0; + bone->ease2= 1.0; + bone->rad_head= 0.10; + bone->rad_tail= 0.05; + bone->segments= 1; + bone->layer= 1;//arm->layer; + + return bone; +} + +EditBone * generateBonesForArc(RigGraph *rigg, ReebArc *arc, ReebNode *head, ReebNode *tail) +{ + ReebArcIterator iter; + float n[3]; + float ADAPTIVE_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 (1 /* G.scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION */ ) + { + EmbedBucket *bucket = NULL; + EmbedBucket *previous = NULL; + EditBone *child = NULL; + EditBone *parent = NULL; + float normal[3] = {0, 0, 0}; + float avg_normal[3]; + int total = 0; + int boneStart = iter.start; + + parent = add_editbonetolist("Bone", &rigg->editbones); + parent->flag = BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL; + VECCOPY(parent->head, head->p); + + for (previous = nextBucket(&iter), bucket = nextBucket(&iter); + bucket; + previous = bucket, bucket = nextBucket(&iter)) + { + float btail[3]; + float value = 0; + + if (G.scene->toolsettings->skgen_options & SKGEN_STICK_TO_EMBEDDING) + { + VECCOPY(btail, bucket->p); + } + else + { + float length; + + /* Calculate normal */ + VecSubf(n, bucket->p, parent->head); + length = Normalize(n); + + total += 1; + VecAddf(normal, normal, n); + VECCOPY(avg_normal, normal); + VecMulf(avg_normal, 1.0f / total); + + VECCOPY(btail, avg_normal); + VecMulf(btail, length); + VecAddf(btail, btail, parent->head); + } + + if (G.scene->toolsettings->skgen_options & SKGEN_ADAPTIVE_DISTANCE) + { + value = calcDistance(arc, boneStart, iter.index, parent->head, btail); + } + else + { + float n[3]; + + VecSubf(n, btail, parent->head); + value = calcVariance(arc, boneStart, iter.index, parent->head, n); + } + + if (value > ADAPTIVE_THRESHOLD) + { + VECCOPY(parent->tail, btail); + + child = add_editbonetolist("Bone", &rigg->editbones); + VECCOPY(child->head, parent->tail); + child->parent = parent; + child->flag |= BONE_CONNECTED|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL; + + parent = child; // new child is next parent + boneStart = iter.index; // start from end + + normal[0] = normal[1] = normal[2] = 0; + total = 0; + } + } + + VECCOPY(parent->tail, tail->p); + + lastBone = parent; /* set last bone in the chain */ + } + + return lastBone; +} + +void generateMissingArcsFromNode(RigGraph *rigg, ReebNode *node, int multi_level_limit) +{ + while (node->multi_level > multi_level_limit && node->link_up) + { + node = node->link_up; + } + + while (node->multi_level < multi_level_limit && node->link_down) + { + node = node->link_down; + } + + if (node->multi_level == multi_level_limit) + { + int i; + + for (i = 0; i < node->degree; i++) + { + ReebArc *earc = node->arcs[i]; + + if (earc->flag == ARC_FREE && earc->head == node) + { + ReebNode *other = BIF_otherNodeFromIndex(earc, node); + + earc->flag = ARC_USED; + + generateBonesForArc(rigg, earc, node, other); + generateMissingArcsFromNode(rigg, other, multi_level_limit); + } + } + } +} + +void generateMissingArcs(RigGraph *rigg) +{ + ReebGraph *reebg = rigg->link_mesh; + int multi_level_limit = 5; + + for (reebg = rigg->link_mesh; reebg; reebg = reebg->link_up) + { + ReebArc *earc; + + for (earc = reebg->arcs.first; earc; earc = earc->next) + { + if (earc->flag == ARC_USED) + { + generateMissingArcsFromNode(rigg, earc->head, multi_level_limit); + generateMissingArcsFromNode(rigg, earc->tail, multi_level_limit); + } + } + } +} + +/************************************ RETARGETTING *****************************************************/ + +static void repositionControl(RigGraph *rigg, RigControl *ctrl, float head[3], float tail[3], float qrot[4], float resize) +{ + RigControl *ctrl_child; + float parent_offset[3], tail_offset[3]; + + VecSubf(tail_offset, ctrl->tail, ctrl->head); + VecMulf(tail_offset, resize); + + VECCOPY(parent_offset, ctrl->offset); + VecMulf(parent_offset, resize); + + QuatMulVecf(qrot, parent_offset); + QuatMulVecf(qrot, tail_offset); + + VecAddf(ctrl->bone->head, head, parent_offset); + VecAddf(ctrl->bone->tail, ctrl->bone->head, tail_offset); + ctrl->bone->roll = getNewBoneRoll(ctrl->bone, ctrl->up_axis, qrot); + + ctrl->flag |= RIG_CTRL_DONE; + + /* Cascade to connected control bones */ + for (ctrl_child = rigg->controls.first; ctrl_child; ctrl_child = ctrl_child->next) + { + if (ctrl_child->link == ctrl->bone) + { + repositionControl(rigg, ctrl_child, ctrl->bone->head, ctrl->bone->tail, qrot, resize); + } + } + +} + +static void repositionBone(RigGraph *rigg, RigEdge *edge, float vec0[3], float vec1[3]) +{ + EditBone *bone; + RigControl *ctrl; + float qrot[4], resize; + float v1[3], v2[3]; + float l1, l2; + + bone = edge->bone; + + VecSubf(v1, edge->tail, edge->head); + VecSubf(v2, vec1, vec0); + + l1 = Normalize(v1); + l2 = Normalize(v2); + + resize = l2 / l1; + + RotationBetweenVectorsToQuat(qrot, v1, v2); + + for (ctrl = rigg->controls.first; ctrl; ctrl = ctrl->next) + { + if (ctrl->link == bone) + { + repositionControl(rigg, ctrl, vec0, vec1, qrot, resize); + } + } + + VECCOPY(bone->head, vec0); + VECCOPY(bone->tail, vec1); + bone->roll = getNewBoneRoll(bone, edge->up_axis, qrot); +} + +static RetargetMode detectArcRetargetMode(RigArc *arc); +static void retargetArctoArcLength(RigGraph *rigg, RigArc *iarc, RigNode *inode_start); + + +static RetargetMode detectArcRetargetMode(RigArc *iarc) +{ + RetargetMode mode = RETARGET_AGGRESSIVE; + ReebArc *earc = iarc->link_mesh; + RigEdge *edge; + int large_angle = 0; + float avg_angle = 0; + float avg_length = 0; + int nb_edges = 0; + + + for (edge = iarc->edges.first; edge; edge = edge->next) + { + avg_angle += edge->angle; + nb_edges++; + } + + avg_angle /= nb_edges - 1; /* -1 because last edge doesn't have an angle */ + + avg_length = iarc->length / nb_edges; + + + if (nb_edges > 2) + { + for (edge = iarc->edges.first; edge; edge = edge->next) + { + if (fabs(edge->angle - avg_angle) > M_PI / 6) + { + large_angle = 1; + } + } + } + else if (nb_edges == 2 && avg_angle > 0) + { + large_angle = 1; + } + + + if (large_angle == 0) + { + mode = RETARGET_LENGTH; + } + + if (earc->bcount <= (iarc->count - 1)) + { + mode = RETARGET_LENGTH; + } + + mode = RETARGET_AGGRESSIVE; + + return mode; +} + +#ifndef USE_THREADS +static void printCostCube(float *cost_cube, int nb_joints) +{ + int i; + + for (i = 0; i < nb_joints; i++) + { + printf("%0.3f ", cost_cube[3 * i]); + } + printf("\n"); + + for (i = 0; i < nb_joints; i++) + { + printf("%0.3f ", cost_cube[3 * i + 1]); + } + printf("\n"); + + for (i = 0; i < nb_joints; i++) + { + printf("%0.3f ", cost_cube[3 * i + 2]); + } + printf("\n"); +} + +static void printMovesNeeded(int *positions, int nb_positions) +{ + int moves = 0; + int i; + + for (i = 0; i < nb_positions; i++) + { + moves += positions[i] - (i + 1); + } + + printf("%i moves needed\n", moves); +} + +static void printPositions(int *positions, int nb_positions) +{ + int i; + + for (i = 0; i < nb_positions; i++) + { + printf("%i ", positions[i]); + } + printf("\n"); +} +#endif + +#define MAX_COST 100 /* FIX ME */ + +static float costDistance(ReebArcIterator *iter, float *vec0, float *vec1, int i0, int i1) +{ + EmbedBucket *bucket = NULL; + float max_dist = 0; + float v1[3], v2[3], c[3]; + float v1_inpf; + + if (G.scene->toolsettings->skgen_retarget_distance_weight > 0) + { + VecSubf(v1, vec0, vec1); + + v1_inpf = Inpf(v1, v1); + + if (v1_inpf > 0) + { + int j; + for (j = i0 + 1; j < i1 - 1; j++) + { + float dist; + + bucket = peekBucket(iter, j); + + VecSubf(v2, bucket->p, vec1); + + Crossf(c, v1, v2); + + dist = Inpf(c, c) / v1_inpf; + + max_dist = dist > max_dist ? dist : max_dist; + } + + return G.scene->toolsettings->skgen_retarget_distance_weight * max_dist; + } + else + { + return MAX_COST; + } + } + else + { + return 0; + } +} + +static float costAngle(float original_angle, float vec_first[3], float vec_second[3]) +{ + if (G.scene->toolsettings->skgen_retarget_angle_weight > 0) + { + float current_angle; + + if (!VecIsNull(vec_first) && !VecIsNull(vec_second)) + { + current_angle = saacos(Inpf(vec_first, vec_second)); + + return G.scene->toolsettings->skgen_retarget_angle_weight * fabs(current_angle - original_angle); + } + else + { + return G.scene->toolsettings->skgen_retarget_angle_weight * M_PI; + } + } + else + { + return 0; + } +} + +static float costLength(float original_length, float current_length) +{ + if (current_length == 0) + { + return MAX_COST; + } + else + { + float length_ratio = fabs((current_length - original_length) / original_length); + return G.scene->toolsettings->skgen_retarget_length_weight * length_ratio * length_ratio; + } +} + +static float calcCostLengthDistance(ReebArcIterator *iter, float **vec_cache, RigEdge *edge, float *vec1, float *vec2, int i1, int i2) +{ + float vec[3]; + float length; + + VecSubf(vec, vec2, vec1); + length = Normalize(vec); + + return costLength(edge->length, length) + costDistance(iter, vec1, vec2, i1, i2); +} + +static float calcCostAngleLengthDistance(ReebArcIterator *iter, float **vec_cache, RigEdge *edge, float *vec0, float *vec1, float *vec2, int i1, int i2) +{ + float vec_second[3], vec_first[3]; + float length2; + float new_cost = 0; + + VecSubf(vec_second, vec2, vec1); + length2 = Normalize(vec_second); + + + /* Angle cost */ + if (edge->prev) + { + VecSubf(vec_first, vec1, vec0); + Normalize(vec_first); + + new_cost += costAngle(edge->prev->angle, vec_first, vec_second); + } + + /* Length cost */ + new_cost += costLength(edge->length, length2); + + /* Distance cost */ + new_cost += costDistance(iter, vec1, vec2, i1, i2); + + return new_cost; +} + +static float calcCost(ReebArcIterator *iter, RigEdge *e1, RigEdge *e2, float *vec0, float *vec1, float *vec2, int i0, int i1, int i2) +{ + float vec_second[3], vec_first[3]; + float length1, length2; + float new_cost = 0; + + VecSubf(vec_second, vec2, vec1); + length2 = Normalize(vec_second); + + VecSubf(vec_first, vec1, vec0); + length1 = Normalize(vec_first); + + /* Angle cost */ + new_cost += costAngle(e1->angle, vec_first, vec_second); + + /* Length cost */ + new_cost += costLength(e1->length, length1); + new_cost += costLength(e2->length, length2); + + /* Distance cost */ + new_cost += costDistance(iter, vec0, vec1, i0, i1); + new_cost += costDistance(iter, vec1, vec2, i1, i2); + + return new_cost; +} + +static void calcGradient(RigEdge *e1, RigEdge *e2, ReebArcIterator *iter, int index, int nb_joints, float *cost_cube, int *positions, float **vec_cache) +{ + EmbedBucket *bucket = NULL; + float *vec0, *vec1, *vec2; + float current_cost; + int i0, i1, i2; + int next_position; + + vec0 = vec_cache[index]; + vec1 = vec_cache[index + 1]; + vec2 = vec_cache[index + 2]; + + if (index == 0) + { + i0 = 0; + } + else + { + i0 = positions[index - 1]; + } + + i1 = positions[index]; + + if (index +1 == nb_joints) + { + i2 = iter->length; + } + else + { + i2 = positions[index + 1]; + } + + + current_cost = calcCost(iter, e1, e2, vec0, vec1, vec2, i0, i1, i2); + cost_cube[index * 3 + 1] = current_cost; + + next_position = positions[index] + 1; + + if (index + 1 < nb_joints && next_position == positions[index + 1]) + { + cost_cube[index * 3 + 2] = MAX_COST; + } + else if (next_position > iter->length) /* positions are indexed at 1, so length is last */ + { + cost_cube[index * 3 + 2] = MAX_COST; + } + else + { + bucket = peekBucket(iter, next_position); + + if (bucket == NULL) + { + cost_cube[index * 3 + 2] = MAX_COST; + } + else + { + vec1 = bucket->p; + + cost_cube[index * 3 + 2] = calcCost(iter, e1, e2, vec0, vec1, vec2, i0, next_position, i2) - current_cost; + } + } + + next_position = positions[index] - 1; + + if (index - 1 > -1 && next_position == positions[index - 1]) + { + cost_cube[index * 3] = MAX_COST; + } + else if (next_position < 1) /* positions are indexed at 1, so 1 is first */ + { + cost_cube[index * 3] = MAX_COST; + } + else + { + bucket = peekBucket(iter, next_position); + + if (bucket == NULL) + { + cost_cube[index * 3] = MAX_COST; + } + else + { + vec1 = bucket->p; + + cost_cube[index * 3] = calcCost(iter, e1, e2, vec0, vec1, vec2, i0, next_position, i2) - current_cost; + } + } +} + +static float probability(float delta_cost, float temperature) +{ + if (delta_cost < 0) + { + return 1; + } + else + { + return (float)exp(delta_cost / temperature); + } +} + +static int neighbour(int nb_joints, float *cost_cube, int *moving_joint, int *moving_direction) +{ + int total = 0; + int chosen = 0; + int i; + + for (i = 0; i < nb_joints; i++) + { + if (cost_cube[i * 3] < MAX_COST) + { + total++; + } + + if (cost_cube[i * 3 + 2] < MAX_COST) + { + total++; + } + } + + if (total == 0) + { + return 0; + } + + chosen = (int)(BLI_drand() * total); + + for (i = 0; i < nb_joints; i++) + { + if (cost_cube[i * 3] < MAX_COST) + { + if (chosen == 0) + { + *moving_joint = i; + *moving_direction = -1; + break; + } + chosen--; + } + + if (cost_cube[i * 3 + 2] < MAX_COST) + { + if (chosen == 0) + { + *moving_joint = i; + *moving_direction = 1; + break; + } + chosen--; + } + } + + return 1; +} + +static int indexMemoNode(int nb_positions, int previous, int current, int joints_left) +{ + return joints_left * nb_positions * nb_positions + current * nb_positions + previous; +} + +static void copyMemoPositions(int *positions, MemoNode *table, int nb_positions, int joints_left) +{ + int previous = 0, current = 0; + int i = 0; + + for (i = 0; joints_left > 0; joints_left--, i++) + { + MemoNode *node; + node = table + indexMemoNode(nb_positions, previous, current, joints_left); + + positions[i] = node->next; + + previous = current; + current = node->next; + } +} + +static MemoNode * solveJoints(MemoNode *table, ReebArcIterator *iter, float **vec_cache, int nb_joints, int nb_positions, int previous, int current, RigEdge *edge, int joints_left) +{ + MemoNode *node; + int index = indexMemoNode(nb_positions, previous, current, joints_left); + + node = table + index; + + if (node->weight != 0) + { + return node; + } + else if (joints_left == 0) + { + float *vec1 = vec_cache[current]; + float *vec2 = vec_cache[nb_positions + 1]; + + node->weight = calcCostLengthDistance(iter, vec_cache, edge, vec1, vec2, current, iter->length); + + return node; + } + else + { + MemoNode *min_node = NULL; + float *vec0 = vec_cache[previous]; + float *vec1 = vec_cache[current]; + float min_weight; + int min_next; + int next; + + for (next = current + 1; next <= nb_positions - (joints_left - 1); next++) + { + MemoNode *next_node; + float *vec2 = vec_cache[next]; + float weight = 0; + + /* ADD WEIGHT OF PREVIOUS - CURRENT - NEXT triple */ + weight = calcCostAngleLengthDistance(iter, vec_cache, edge, vec0, vec1, vec2, current, next); + + if (weight >= MAX_COST) + { + continue; + } + + /* add node weight */ + next_node = solveJoints(table, iter, vec_cache, nb_joints, nb_positions, current, next, edge->next, joints_left - 1); + weight += next_node->weight; + + if (min_node == NULL || weight < min_weight) + { + min_weight = weight; + min_node = next_node; + min_next = next; + } + } + + if (min_node) + { + node->weight = min_weight; + node->next = min_next; + return node; + } + else + { + node->weight = MAX_COST; + return node; + } + } + +} + +static int testFlipArc(RigArc *iarc, RigNode *inode_start) +{ + ReebArc *earc = iarc->link_mesh; + ReebNode *enode_start = BIF_NodeFromIndex(earc, inode_start->link_mesh); + + /* no flip needed if both nodes are the same */ + if ((enode_start == earc->head && inode_start == iarc->head) || (enode_start == earc->tail && inode_start == iarc->tail)) + { + return 0; + } + else + { + return 1; + } +} + +static void retargetArctoArcAggresive(RigGraph *rigg, RigArc *iarc, RigNode *inode_start) +{ + ReebArcIterator iter; + RigEdge *edge; + EmbedBucket *bucket = NULL; + ReebNode *node_start, *node_end; + ReebArc *earc = iarc->link_mesh; + float min_cost = FLT_MAX; + float *vec0, *vec1, *vec2; + float **vec_cache; + float *cost_cache; + int *best_positions; + int *positions; + int nb_edges = BLI_countlist(&iarc->edges); + int nb_joints = nb_edges - 1; + RetargetMethod method = G.scene->toolsettings->skgen_optimisation_method; + int i; + + if (nb_joints > earc->bcount) + { + printf("NOT ENOUGH BUCKETS!\n"); + return; + } + + positions = MEM_callocN(sizeof(int) * nb_joints, "Aggresive positions"); + best_positions = MEM_callocN(sizeof(int) * nb_joints, "Best Aggresive positions"); + cost_cache = MEM_callocN(sizeof(float) * nb_edges, "Cost cache"); + vec_cache = MEM_callocN(sizeof(float*) * (nb_edges + 1), "Vec cache"); + + if (testFlipArc(iarc, inode_start)) + { + node_start = earc->tail; + node_end = earc->head; + } + else + { + node_start = earc->head; + node_end = earc->tail; + } + + /* init with first values */ + for (i = 0; i < nb_joints; i++) + { + positions[i] = i + 1; + //positions[i] = (earc->bcount / nb_edges) * (i + 1); + } + + /* init cost cache */ + for (i = 0; i < nb_edges; i++) + { + cost_cache[i] = 0; + } + + vec_cache[0] = node_start->p; + vec_cache[nb_edges] = node_end->p; + + if (method == METHOD_MEMOIZE) + { + int nb_positions = earc->bcount; + int nb_memo_nodes = nb_positions * nb_positions * (nb_joints + 1); + MemoNode *table = MEM_callocN(nb_memo_nodes * sizeof(MemoNode), "memoization table"); + MemoNode *result; + float **positions_cache = MEM_callocN(sizeof(float*) * (nb_positions + 2), "positions cache"); + int i; + + positions_cache[0] = node_start->p; + positions_cache[nb_positions + 1] = node_end->p; + + initArcIterator(&iter, earc, node_start); + + for (i = 1; i <= nb_positions; i++) + { + EmbedBucket *bucket = peekBucket(&iter, i); + positions_cache[i] = bucket->p; + } + + result = solveJoints(table, &iter, positions_cache, nb_joints, earc->bcount, 0, 0, iarc->edges.first, nb_joints); + + min_cost = result->weight; + copyMemoPositions(best_positions, table, earc->bcount, nb_joints); + + MEM_freeN(table); + MEM_freeN(positions_cache); + } + /* BRUTE FORCE */ + else if (method == METHOD_BRUTE_FORCE) + { + int last_index = 0; + int first_pass = 1; + int must_move = nb_joints - 1; + + while(1) + { + float cost = 0; + int need_calc = 0; + + /* increment to next possible solution */ + + i = nb_joints - 1; + + if (first_pass) + { + need_calc = 0; + first_pass = 0; + } + else + { + /* increment positions, starting from the last one + * until a valid increment is found + * */ + for (i = must_move; i >= 0; i--) + { + int remaining_joints = nb_joints - (i + 1); + + positions[i] += 1; + need_calc = i; + + if (positions[i] + remaining_joints <= earc->bcount) + { + break; + } + } + } + + if (i == -1) + { + break; + } + + /* reset joints following the last increment*/ + for (i = i + 1; i < nb_joints; i++) + { + positions[i] = positions[i - 1] + 1; + } + + /* calculating cost */ + initArcIterator(&iter, earc, node_start); + + vec0 = NULL; + vec1 = node_start->p; + vec2 = NULL; + + for (edge = iarc->edges.first, i = 0, last_index = 0; + edge; + edge = edge->next, i += 1) + { + + if (i >= need_calc) + { + float vec_first[3], vec_second[3]; + float length1, length2; + float new_cost = 0; + int i1, i2; + + if (i < nb_joints) + { + i2 = positions[i]; + bucket = peekBucket(&iter, positions[i]); + vec2 = bucket->p; + vec_cache[i + 1] = vec2; /* update cache for updated position */ + } + else + { + i2 = iter.length; + vec2 = node_end->p; + } + + if (i > 0) + { + i1 = positions[i - 1]; + } + else + { + i1 = 1; + } + + vec1 = vec_cache[i]; + + + VecSubf(vec_second, vec2, vec1); + length2 = Normalize(vec_second); + + /* check angle */ + if (i != 0 && G.scene->toolsettings->skgen_retarget_angle_weight > 0) + { + RigEdge *previous = edge->prev; + + vec0 = vec_cache[i - 1]; + VecSubf(vec_first, vec1, vec0); + length1 = Normalize(vec_first); + + /* Angle cost */ + new_cost += costAngle(previous->angle, vec_first, vec_second); + } + + /* Length Cost */ + new_cost += costLength(edge->length, length2); + + /* Distance Cost */ + new_cost += costDistance(&iter, vec1, vec2, i1, i2); + + cost_cache[i] = new_cost; + } + + cost += cost_cache[i]; + + if (cost > min_cost) + { + must_move = i; + break; + } + } + + if (must_move != i || must_move > nb_joints - 1) + { + must_move = nb_joints - 1; + } + + /* cost optimizing */ + if (cost < min_cost) + { + min_cost = cost; + memcpy(best_positions, positions, sizeof(int) * nb_joints); + } + } + } + /* SIMULATED ANNEALING */ + else if (method == METHOD_ANNEALING) + { + RigEdge *previous; + float *cost_cube; + float cost; + int k; + int kmax; + + kmax = 100000; + + BLI_srand(nb_joints); + + /* [joint: index][position: -1, 0, +1] */ + cost_cube = MEM_callocN(sizeof(float) * 3 * nb_joints, "Cost Cube"); + + initArcIterator(&iter, earc, node_start); + + /* init vec_cache */ + for (i = 0; i < nb_joints; i++) + { + bucket = peekBucket(&iter, positions[i]); + vec_cache[i + 1] = bucket->p; + } + + cost = 0; + + /* init cost cube */ + for (previous = iarc->edges.first, edge = previous->next, i = 0; + edge; + previous = edge, edge = edge->next, i += 1) + { + calcGradient(previous, edge, &iter, i, nb_joints, cost_cube, positions, vec_cache); + + cost += cost_cube[3 * i + 1]; + } + +#ifndef USE_THREADS + printf("initial cost: %f\n", cost); + printf("kmax: %i\n", kmax); +#endif + + for (k = 0; k < kmax; k++) + { + int status; + int moving_joint = -1; + int move_direction = -1; + float delta_cost; + float temperature; + + status = neighbour(nb_joints, cost_cube, &moving_joint, &move_direction); + + if (status == 0) + { + /* if current state is still a minimum, copy it */ + if (cost < min_cost) + { + min_cost = cost; + memcpy(best_positions, positions, sizeof(int) * nb_joints); + } + break; + } + + delta_cost = cost_cube[moving_joint * 3 + (1 + move_direction)]; + + temperature = 1 - (float)k / (float)kmax; + if (probability(delta_cost, temperature) > BLI_frand()) + { + /* update position */ + positions[moving_joint] += move_direction; + + /* update vector cache */ + bucket = peekBucket(&iter, positions[moving_joint]); + vec_cache[moving_joint + 1] = bucket->p; + + cost += delta_cost; + + /* cost optimizing */ + if (cost < min_cost) + { + min_cost = cost; + memcpy(best_positions, positions, sizeof(int) * nb_joints); + } + + /* update cost cube */ + for (previous = iarc->edges.first, edge = previous->next, i = 0; + edge; + previous = edge, edge = edge->next, i += 1) + { + if (i == moving_joint - 1 || + i == moving_joint || + i == moving_joint + 1) + { + calcGradient(previous, edge, &iter, i, nb_joints, cost_cube, positions, vec_cache); + } + } + } + } + + //min_cost = cost; + //memcpy(best_positions, positions, sizeof(int) * nb_joints); + +// printf("k = %i\n", k); + + + MEM_freeN(cost_cube); + } + + + vec0 = node_start->p; + initArcIterator(&iter, earc, node_start); + +#ifndef USE_THREADS + printPositions(best_positions, nb_joints); + printMovesNeeded(best_positions, nb_joints); + printf("min_cost %f\n", min_cost); + printf("buckets: %i\n", earc->bcount); +#endif + + /* set joints to best position */ + for (edge = iarc->edges.first, i = 0; + edge; + edge = edge->next, i++) + { + if (i < nb_joints) + { + bucket = peekBucket(&iter, best_positions[i]); + vec1 = bucket->p; + } + else + { + vec1 = node_end->p; + } + + if (edge->bone) + { + repositionBone(rigg, edge, vec0, vec1); + } + + vec0 = vec1; + } + + MEM_freeN(positions); + MEM_freeN(best_positions); + MEM_freeN(cost_cache); + MEM_freeN(vec_cache); +} + +static void retargetArctoArcLength(RigGraph *rigg, RigArc *iarc, RigNode *inode_start) +{ + ReebArcIterator iter; + ReebArc *earc = iarc->link_mesh; + ReebNode *node_start, *node_end; + RigEdge *edge; + EmbedBucket *bucket = NULL; + float embedding_length = 0; + float *vec0 = NULL; + float *vec1 = NULL; + float *previous_vec = NULL; + + + if (testFlipArc(iarc, inode_start)) + { + node_start = (ReebNode*)earc->tail; + node_end = (ReebNode*)earc->head; + } + else + { + node_start = (ReebNode*)earc->head; + node_end = (ReebNode*)earc->tail; + } + + initArcIterator(&iter, earc, node_start); + + bucket = nextBucket(&iter); + + vec0 = node_start->p; + + while (bucket != NULL) + { + vec1 = bucket->p; + + embedding_length += VecLenf(vec0, vec1); + + vec0 = vec1; + bucket = nextBucket(&iter); + } + + embedding_length += VecLenf(node_end->p, vec1); + + /* fit bones */ + initArcIterator(&iter, earc, node_start); + + bucket = nextBucket(&iter); + + vec0 = node_start->p; + previous_vec = vec0; + vec1 = bucket->p; + + for (edge = iarc->edges.first; edge; edge = edge->next) + { + float new_bone_length = edge->length / iarc->length * embedding_length; + + float length = 0; + + while (bucket && new_bone_length > length) + { + length += VecLenf(previous_vec, vec1); + bucket = nextBucket(&iter); + previous_vec = vec1; + vec1 = bucket->p; + } + + if (bucket == NULL) + { + vec1 = node_end->p; + } + + /* no need to move virtual edges (space between unconnected bones) */ + if (edge->bone) + { + repositionBone(rigg, edge, vec0, vec1); + } + + vec0 = vec1; + previous_vec = vec1; + } +} + +static void retargetArctoArc(RigGraph *rigg, RigArc *iarc, RigNode *inode_start) +{ +#ifdef USE_THREADS + RetargetParam *p = MEM_callocN(sizeof(RetargetParam), "RetargetParam"); + + p->rigg = rigg; + p->iarc = iarc; + p->inode_start = inode_start; + + BLI_insert_work(rigg->worker, p); +#else + RetargetParam p; + + p.rigg = rigg; + p.iarc = iarc; + p.inode_start = inode_start; + + exec_retargetArctoArc(&p); +#endif +} + +void *exec_retargetArctoArc(void *param) +{ + RetargetParam *p = (RetargetParam*)param; + RigGraph *rigg = p->rigg; + RigArc *iarc = p->iarc; + RigNode *inode_start = p->inode_start; + ReebArc *earc = iarc->link_mesh; + + if (BLI_countlist(&iarc->edges) == 1) + { + RigEdge *edge = iarc->edges.first; + + if (testFlipArc(iarc, inode_start)) + { + repositionBone(rigg, edge, earc->tail->p, earc->head->p); + } + else + { + repositionBone(rigg, edge, earc->head->p, earc->tail->p); + } + } + else + { + RetargetMode mode = detectArcRetargetMode(iarc); + + if (mode == RETARGET_AGGRESSIVE) + { + retargetArctoArcAggresive(rigg, iarc, inode_start); + } + else + { + retargetArctoArcLength(rigg, iarc, inode_start); + } + } + +#ifdef USE_THREADS + MEM_freeN(p); +#endif + + return NULL; +} + +static void matchMultiResolutionNode(RigGraph *rigg, RigNode *inode, ReebNode *top_node) +{ + ReebNode *enode = top_node; + ReebGraph *reebg = BIF_graphForMultiNode(rigg->link_mesh, enode); + int ishape, eshape; + + ishape = BLI_subtreeShape((BGraph*)rigg, (BNode*)inode, NULL, 0) % SHAPE_LEVELS; + eshape = BLI_subtreeShape((BGraph*)reebg, (BNode*)enode, NULL, 0) % SHAPE_LEVELS; + + inode->link_mesh = enode; + + while (ishape == eshape && enode->link_down) + { + inode->link_mesh = enode; + + enode = enode->link_down; + reebg = BIF_graphForMultiNode(rigg->link_mesh, enode); /* replace with call to link_down once that exists */ + eshape = BLI_subtreeShape((BGraph*)reebg, (BNode*)enode, NULL, 0) % SHAPE_LEVELS; + } +} + +static void markMultiResolutionChildArc(ReebNode *end_enode, ReebNode *enode) +{ + int i; + + for(i = 0; i < enode->degree; i++) + { + ReebArc *earc = (ReebArc*)enode->arcs[i]; + + if (earc->flag == ARC_FREE) + { + earc->flag = ARC_TAKEN; + + if (earc->tail->degree > 1 && earc->tail != end_enode) + { + markMultiResolutionChildArc(end_enode, earc->tail); + } + break; + } + } +} + +static void markMultiResolutionArc(ReebArc *start_earc) +{ + if (start_earc->link_up) + { + ReebArc *earc; + for (earc = start_earc->link_up ; earc; earc = earc->link_up) + { + earc->flag = ARC_TAKEN; + + if (earc->tail->index != start_earc->tail->index) + { + markMultiResolutionChildArc(earc->tail, earc->tail); + } + } + } +} + +static void matchMultiResolutionArc(RigGraph *rigg, RigNode *start_node, RigArc *next_iarc, ReebArc *next_earc) +{ + ReebNode *enode = next_earc->head; + ReebGraph *reebg = BIF_graphForMultiNode(rigg->link_mesh, enode); + int ishape, eshape; + + ishape = BLI_subtreeShape((BGraph*)rigg, (BNode*)start_node, (BArc*)next_iarc, 1) % SHAPE_LEVELS; + eshape = BLI_subtreeShape((BGraph*)reebg, (BNode*)enode, (BArc*)next_earc, 1) % SHAPE_LEVELS; + + while (ishape != eshape && next_earc->link_up) + { + next_earc->flag = ARC_TAKEN; // mark previous as taken, to prevent backtrack on lower levels + + next_earc = next_earc->link_up; + reebg = reebg->link_up; + enode = next_earc->head; + eshape = BLI_subtreeShape((BGraph*)reebg, (BNode*)enode, (BArc*)next_earc, 1) % SHAPE_LEVELS; + } + + next_earc->flag = ARC_USED; + next_iarc->link_mesh = next_earc; + + /* mark all higher levels as taken too */ + markMultiResolutionArc(next_earc); +// while (next_earc->link_up) +// { +// next_earc = next_earc->link_up; +// next_earc->flag = ARC_TAKEN; +// } +} + +static void matchMultiResolutionStartingNode(RigGraph *rigg, ReebGraph *reebg, RigNode *inode) +{ + ReebNode *enode; + int ishape, eshape; + + enode = reebg->nodes.first; + + ishape = BLI_subtreeShape((BGraph*)rigg, (BNode*)inode, NULL, 0) % SHAPE_LEVELS; + eshape = BLI_subtreeShape((BGraph*)rigg->link_mesh, (BNode*)enode, NULL, 0) % SHAPE_LEVELS; + + while (ishape != eshape && reebg->link_up) + { + reebg = reebg->link_up; + + enode = reebg->nodes.first; + + eshape = BLI_subtreeShape((BGraph*)reebg, (BNode*)enode, NULL, 0) % SHAPE_LEVELS; + } + + inode->link_mesh = enode; +} + +static void findCorrespondingArc(RigGraph *rigg, RigArc *start_arc, RigNode *start_node, RigArc *next_iarc, int root) +{ + ReebNode *enode = start_node->link_mesh; + ReebArc *next_earc; + int symmetry_level = next_iarc->symmetry_level; + int symmetry_group = next_iarc->symmetry_group; + int symmetry_flag = next_iarc->symmetry_flag; + int i; + + next_iarc->link_mesh = NULL; + +// if (root) +// { +// printf("-----------------------\n"); +// printf("MATCHING LIMB\n"); +// RIG_printArcBones(next_iarc); +// } + + for(i = 0; i < enode->degree; i++) + { + next_earc = (ReebArc*)enode->arcs[i]; + +// if (next_earc->flag == ARC_FREE) +// { +// printf("candidate (level %i ?= %i) (flag %i ?= %i) (group %i ?= %i)\n", +// symmetry_level, next_earc->symmetry_level, +// symmetry_flag, next_earc->symmetry_flag, +// symmetry_group, next_earc->symmetry_flag); +// } + + if (next_earc->flag == ARC_FREE && + next_earc->symmetry_flag == symmetry_flag && + next_earc->symmetry_group == symmetry_group && + next_earc->symmetry_level == symmetry_level) + { +// printf("CORRESPONDING ARC FOUND\n"); +// printf("flag %i -- level %i -- flag %i -- group %i\n", next_earc->flag, next_earc->symmetry_level, next_earc->symmetry_flag, next_earc->symmetry_group); + + matchMultiResolutionArc(rigg, start_node, next_iarc, next_earc); + break; + } + } + + /* not found, try at higher nodes (lower node might have filtered internal arcs, messing shape of tree */ + if (next_iarc->link_mesh == NULL) + { +// printf("NO CORRESPONDING ARC FOUND - GOING TO HIGHER LEVELS\n"); + + if (enode->link_up) + { + start_node->link_mesh = enode->link_up; + findCorrespondingArc(rigg, start_arc, start_node, next_iarc, 0); + } + } + + /* still not found, print debug info */ + if (root && next_iarc->link_mesh == NULL) + { + start_node->link_mesh = enode; /* linking back with root node */ + +// printf("NO CORRESPONDING ARC FOUND\n"); +// RIG_printArcBones(next_iarc); +// +// printf("ON NODE %i, multilevel %i\n", enode->index, enode->multi_level); +// +// printf("LOOKING FOR\n"); +// printf("flag %i -- level %i -- flag %i -- group %i\n", ARC_FREE, symmetry_level, symmetry_flag, symmetry_group); +// +// printf("CANDIDATES\n"); +// for(i = 0; i < enode->degree; i++) +// { +// next_earc = (ReebArc*)enode->arcs[i]; +// printf("flag %i -- level %i -- flag %i -- group %i\n", next_earc->flag, next_earc->symmetry_level, next_earc->symmetry_flag, next_earc->symmetry_group); +// } + + /* Emergency matching */ + for(i = 0; i < enode->degree; i++) + { + next_earc = (ReebArc*)enode->arcs[i]; + + if (next_earc->flag == ARC_FREE && next_earc->symmetry_level == symmetry_level) + { +// printf("USING: \n"); +// printf("flag %i -- level %i -- flag %i -- group %i\n", next_earc->flag, next_earc->symmetry_level, next_earc->symmetry_flag, next_earc->symmetry_group); + matchMultiResolutionArc(rigg, start_node, next_iarc, next_earc); + break; + } + } + } + +} + +static void retargetSubgraph(RigGraph *rigg, RigArc *start_arc, RigNode *start_node) +{ + RigNode *inode = start_node; + int i; + + /* no start arc on first node */ + if (start_arc) + { + ReebNode *enode = start_node->link_mesh; + ReebArc *earc = start_arc->link_mesh; + + retargetArctoArc(rigg, start_arc, start_node); + + enode = BIF_otherNodeFromIndex(earc, enode); + inode = (RigNode*)BLI_otherNode((BArc*)start_arc, (BNode*)inode); + + /* match with lowest node with correct shape */ + matchMultiResolutionNode(rigg, inode, enode); + } + + for(i = 0; i < inode->degree; i++) + { + RigArc *next_iarc = (RigArc*)inode->arcs[i]; + + /* no back tracking */ + if (next_iarc != start_arc) + { + findCorrespondingArc(rigg, start_arc, inode, next_iarc, 1); + if (next_iarc->link_mesh) + { + retargetSubgraph(rigg, next_iarc, inode); + } + } + } +} + +static void adjustGraphs(RigGraph *rigg) +{ + RigArc *arc; + + for (arc = rigg->arcs.first; arc; arc = arc->next) + { + if (arc->link_mesh) + { + retargetArctoArc(rigg, arc, arc->head); + } + } + +#ifdef USE_THREADS + BLI_end_worker(rigg->worker); +#endif + + /* Turn the list into an armature */ + editbones_to_armature(&rigg->editbones, rigg->ob); + + BIF_undo_push("Retarget Skeleton"); +} + +static void retargetGraphs(RigGraph *rigg) +{ + ReebGraph *reebg = rigg->link_mesh; + RigNode *inode; + + /* flag all ReebArcs as free */ + BIF_flagMultiArcs(reebg, ARC_FREE); + + /* return to first level */ + reebg = rigg->link_mesh; + + inode = rigg->head; + + matchMultiResolutionStartingNode(rigg, reebg, inode); + + retargetSubgraph(rigg, NULL, inode); + + //generateMissingArcs(rigg); + +#ifdef USE_THREADS + BLI_end_worker(rigg->worker); +#endif + + /* Turn the list into an armature */ + editbones_to_armature(&rigg->editbones, rigg->ob); +} + + +void BIF_retargetArmature() +{ + Object *ob; + Base *base; + ReebGraph *reebg; + double start_time, end_time; + double gstart_time, gend_time; + double reeb_time, rig_time, retarget_time, total_time; + + gstart_time = start_time = PIL_check_seconds_timer(); + + reebg = BIF_ReebGraphMultiFromEditMesh(); + + end_time = PIL_check_seconds_timer(); + reeb_time = end_time - start_time; + + printf("Reeb Graph created\n"); + + base= FIRSTBASE; + for (base = FIRSTBASE; base; base = base->next) + { + if TESTBASELIB(base) { + ob = base->object; + + if (ob->type==OB_ARMATURE) + { + RigGraph *rigg; + bArmature *arm; + + arm = ob->data; + + /* Put the armature into editmode */ + + + start_time = PIL_check_seconds_timer(); + + rigg = armatureToGraph(ob, arm); + + end_time = PIL_check_seconds_timer(); + rig_time = end_time - start_time; + + printf("Armature graph created\n"); + + //RIG_printGraph(rigg); + + rigg->link_mesh = reebg; + + printf("retargetting %s\n", ob->id.name); + + start_time = PIL_check_seconds_timer(); + + retargetGraphs(rigg); + + end_time = PIL_check_seconds_timer(); + retarget_time = end_time - start_time; + + BIF_freeRetarget(); + + GLOBAL_RIGG = rigg; + + break; /* only one armature at a time */ + } + } + } + + gend_time = PIL_check_seconds_timer(); + + total_time = gend_time - gstart_time; + + printf("-----------\n"); + printf("runtime: \t%.3f\n", total_time); + printf("reeb: \t\t%.3f (%.1f%%)\n", reeb_time, reeb_time / total_time * 100); + printf("rig: \t\t%.3f (%.1f%%)\n", rig_time, rig_time / total_time * 100); + printf("retarget: \t%.3f (%.1f%%)\n", retarget_time, retarget_time / total_time * 100); + printf("-----------\n"); + + BIF_undo_push("Retarget Skeleton"); + + allqueue(REDRAWVIEW3D, 0); +} + +void BIF_adjustRetarget() +{ + if (GLOBAL_RIGG) + { + adjustGraphs(GLOBAL_RIGG); + } +} + +void BIF_freeRetarget() +{ + if (GLOBAL_RIGG) + { + RIG_freeRigGraph((BGraph*)GLOBAL_RIGG); + GLOBAL_RIGG = NULL; + } +} diff --git a/source/blender/src/buttons_editing.c b/source/blender/src/buttons_editing.c index 3d60e9a8eea..45267f50622 100644 --- a/source/blender/src/buttons_editing.c +++ b/source/blender/src/buttons_editing.c @@ -180,6 +180,8 @@ #include "butspace.h" // own module #include "multires.h" +#include "reeb.h" + static float editbutweight= 1.0; float editbutvweight= 1; static int actmcol= 0, acttface= 0, acttface_rnd = 0, actmcol_rnd = 0; @@ -5052,6 +5054,9 @@ void do_meshbuts(unsigned short event) case B_GEN_SKELETON: generateSkeleton(); break; + case B_RETARGET_SKELETON: + BIF_retargetArmature(); + break; } /* WATCH IT: previous events only in editmode! */ @@ -5150,6 +5155,100 @@ static void skgen_reorder(void *option, void *arg2) } } +static void skgen_graphgen(void *arg1, void *arg2) +{ + BIF_GlobalReebGraphFromEditMesh(); + allqueue(REDRAWVIEW3D, 0); +} + +static void skgen_graphfree(void *arg1, void *arg2) +{ + BIF_GlobalReebFree(); + allqueue(REDRAWVIEW3D, 0); +} + +static void skgen_rigadjust(void *arg1, void *arg2) +{ + BIF_adjustRetarget(); +} + +static void skgen_rigfree(void *arg1, void *arg2) +{ + BIF_freeRetarget(); +} + +static void skgen_graph_block(uiBlock *block) +{ + uiBlockBeginAlign(block); + uiDefButS(block, NUM, B_DIFF, "Resolution:", 1025,150,225,19, &G.scene->toolsettings->skgen_resolution,10.0,1000.0, 0, 0, "Specifies the resolution of the graph's embedding"); + uiDefButBitS(block, TOG, SKGEN_HARMONIC, B_DIFF, "H", 1250,150, 25,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Apply harmonic smoothing to the weighting"); + 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, "", 1111,130,164,19, &G.scene->toolsettings->skgen_threshold_internal,0.0, 10.0, 10, 0, "Specify the threshold ratio for filtering internal arcs"); + uiDefButBitS(block, TOG, SKGEN_FILTER_EXTERNAL, B_DIFF, "Filter Ex", 1025,110, 53,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Filter external small arcs from graph"); + uiDefButBitS(block, TOG, SKGEN_FILTER_SMART, B_DIFF, "Sm", 1078,110, 30,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Smart Filtering"); + uiDefButF(block, NUM, B_DIFF, "", 1111,110,164,19, &G.scene->toolsettings->skgen_threshold_external,0.0, 10.0, 10, 0, "Specify the threshold ratio for filtering external arcs"); + + uiDefButBitS(block, TOG, SKGEN_SYMMETRY, B_DIFF, "Symmetry", 1025, 90,125,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Restore symmetries based on topology"); + uiDefButF(block, NUM, B_DIFF, "T:", 1150, 90,125,19, &G.scene->toolsettings->skgen_symmetry_limit,0.0, 1.0, 10, 0, "Specify the threshold distance for considering potential symmetric arcs"); + uiDefButC(block, NUM, B_DIFF, "P:", 1025, 70, 62,19, &G.scene->toolsettings->skgen_postpro_passes, 0, 10, 10, 0, "Specify the number of processing passes on the embeddings"); + uiDefButC(block, ROW, B_DIFF, "Smooth", 1087, 70, 63,19, &G.scene->toolsettings->skgen_postpro, 5.0, (float)SKGEN_SMOOTH, 0, 0, "Smooth embeddings"); + uiDefButC(block, ROW, B_DIFF, "Average", 1150, 70, 62,19, &G.scene->toolsettings->skgen_postpro, 5.0, (float)SKGEN_AVERAGE, 0, 0, "Average embeddings"); + uiDefButC(block, ROW, B_DIFF, "Sharpen", 1212, 70, 63,19, &G.scene->toolsettings->skgen_postpro, 5.0, (float)SKGEN_SHARPEN, 0, 0, "Sharpen embeddings"); + + uiBlockEndAlign(block); +} + +static void editing_panel_mesh_skgen_display(Object *ob, Mesh *me) +{ + uiBlock *block; + uiBut *but; + + block= uiNewBlock(&curarea->uiblocks, "editing_panel_mesh_skgen_display", UI_EMBOSS, UI_HELV, curarea->win); + uiNewPanelTabbed("Mesh Tools More", "Skgen"); + if(uiNewPanel(curarea, block, "Graph", "Editing", 960, 0, 318, 204)==0) return; + + but = uiDefBut(block, BUT, B_DIFF, "Generate", 1025,170,125,19, 0, 0, 0, 0, 0, "Generate Graph from Mesh"); + uiButSetFunc(but, skgen_graphgen, NULL, NULL); + but = uiDefBut(block, BUT, B_DIFF, "Free", 1150,170,125,19, 0, 0, 0, 0, 0, "Free Graph from Mesh"); + uiButSetFunc(but, skgen_graphfree, NULL, NULL); + + skgen_graph_block(block); + + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG, SKGEN_DISP_LENGTH, REDRAWVIEW3D, "Length", 1025, 40, 50,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Show Length"); + uiDefButBitS(block, TOG, SKGEN_DISP_WEIGHT, REDRAWVIEW3D, "Weight", 1075, 40, 50,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Show Weight"); + uiDefButBitS(block, TOG, SKGEN_DISP_EMBED, REDRAWVIEW3D, "Embed", 1125, 40, 50,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Show Arc Embedings"); + uiDefButBitS(block, TOG, SKGEN_DISP_INDEX, REDRAWVIEW3D, "Index", 1175, 40, 50,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Show Arc and Node indexes"); + uiDefButBitS(block, TOG, SKGEN_DISP_ORIG, REDRAWVIEW3D, "Original", 1225, 40, 50,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Show Original Graph"); + + uiBlockEndAlign(block); + + uiDefButC(block, NUM, REDRAWVIEW3D, "Level:", 1025, 20, 125,19, &G.scene->toolsettings->skgen_multi_level, 0, REEB_MAX_MULTI_LEVEL, 1, 0,"Specify the level to draw"); +} + +static void editing_panel_mesh_skgen_retarget(Object *ob, Mesh *me) +{ + uiBlock *block; + uiBut *but; + + block= uiNewBlock(&curarea->uiblocks, "editing_panel_mesh_skgen_retarget", UI_EMBOSS, UI_HELV, curarea->win); + uiNewPanelTabbed("Mesh Tools More", "Skgen"); + if(uiNewPanel(curarea, block, "Retarget", "Editing", 960, 0, 318, 204)==0) return; + + uiDefBut(block, BUT, B_RETARGET_SKELETON, "Retarget Skeleton", 1025,170,100,19, 0, 0, 0, 0, 0, "Retarget Selected Armature to this Mesh"); + but = uiDefBut(block, BUT, B_DIFF, "Adjust", 1125,170,100,19, 0, 0, 0, 0, 0, "Adjust Retarget using new weights"); + uiButSetFunc(but, skgen_rigadjust, NULL, NULL); + but = uiDefBut(block, BUT, B_DIFF, "Free", 1225,170,50,19, 0, 0, 0, 0, 0, "Free Retarget structure"); + uiButSetFunc(but, skgen_rigfree, NULL, NULL); + + skgen_graph_block(block); + + uiDefButF(block, NUM, B_DIFF, "Ang:", 1025, 40, 83,19, &G.scene->toolsettings->skgen_retarget_angle_weight, 0, 10, 1, 0, "Angle Weight"); + uiDefButF(block, NUM, B_DIFF, "Len:", 1108, 40, 83,19, &G.scene->toolsettings->skgen_retarget_length_weight, 0, 10, 1, 0, "Length Weight"); + uiDefButF(block, NUM, B_DIFF, "Dist:", 1191, 40, 84,19, &G.scene->toolsettings->skgen_retarget_distance_weight, 0, 10, 1, 0, "Distance Weight"); + uiDefButC(block, NUM, B_DIFF, "Method:", 1025, 20, 125,19, &G.scene->toolsettings->skgen_optimisation_method, 0, 2, 1, 0,"Optimisation Method (0: brute, 1: memoize, 2: annealing max fixed"); +} + static void editing_panel_mesh_skgen(Object *ob, Mesh *me) { uiBlock *block; @@ -5157,20 +5256,17 @@ static void editing_panel_mesh_skgen(Object *ob, Mesh *me) int i; block= uiNewBlock(&curarea->uiblocks, "editing_panel_mesh_skgen", UI_EMBOSS, UI_HELV, curarea->win); - if(uiNewPanel(curarea, block, "Skeleton Generator", "Editing", 960, 0, 318, 204)==0) return; + uiNewPanelTabbed("Mesh Tools More", "Skgen"); + if(uiNewPanel(curarea, block, "Generator", "Editing", 960, 0, 318, 204)==0) return; - uiDefBut(block, BUT, B_GEN_SKELETON, "Generate Skeleton", 1025,170,250,19, 0, 0, 0, 0, 0, "Generate Skeleton from Mesh"); + uiDefBut(block, BUT, B_GEN_SKELETON, "Generate", 1025,170,250,19, 0, 0, 0, 0, 0, "Generate Skeleton from Mesh"); + + skgen_graph_block(block); uiBlockBeginAlign(block); - 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, "T:", 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, "T:", 1111,110,164,19, &G.scene->toolsettings->skgen_threshold_external,0.0, 1.0, 10, 0, "Specify the threshold ratio for filtering external arcs"); - for(i = 0; i < SKGEN_SUB_TOTAL; i++) { - int y = 90 - 20 * i; + int y = 50 - 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, SET_INT_IN_POINTER(i), NULL); @@ -5187,18 +5283,14 @@ static void editing_panel_mesh_skgen(Object *ob, Mesh *me) uiDefButF(block, NUM, B_DIFF, "T:", 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, "T:", 1111, y,164,19, &G.scene->toolsettings->skgen_correlation_limit,0.0, 1.0, 0.01, 0, "Specify the threshold correlation for subdivision"); + uiDefButBitS(block, TOG, SKGEN_CUT_CORRELATION, B_DIFF, "Adaptative", 1041, y, 67,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Subdivide arcs adaptatively"); + uiDefButF(block, NUM, B_DIFF, "T:", 1111, y,114,19, &G.scene->toolsettings->skgen_correlation_limit,0.0, 1.0, 0.01, 0, "Specify the adaptive threshold for subdivision"); + uiDefButBitS(block, TOG, SKGEN_STICK_TO_EMBEDDING, B_DIFF, "E", 1225, y, 25,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Stick endpoint to embedding"); + uiDefButBitS(block, TOG, SKGEN_ADAPTIVE_DISTANCE, B_DIFF, "D", 1250, y, 25,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Adaptive distance (on) or variance(off)"); break; } } - uiDefButBitS(block, TOG, SKGEN_SYMMETRY, B_DIFF, "Symmetry", 1025, 30,125,19, &G.scene->toolsettings->skgen_options, 0, 0, 0, 0, "Restore symmetries based on topology"); - uiDefButF(block, NUM, B_DIFF, "T:", 1150, 30,125,19, &G.scene->toolsettings->skgen_symmetry_limit,0.0, 1.0, 10, 0, "Specify the threshold distance for considering potential symmetric arcs"); - uiDefButC(block, NUM, B_DIFF, "P:", 1025, 10, 62,19, &G.scene->toolsettings->skgen_postpro_passes, 0, 10, 10, 0, "Specify the number of processing passes on the embeddings"); - uiDefButC(block, ROW, B_DIFF, "Smooth", 1087, 10, 63,19, &G.scene->toolsettings->skgen_postpro, 5.0, (float)SKGEN_SMOOTH, 0, 0, "Smooth embeddings"); - uiDefButC(block, ROW, B_DIFF, "Average", 1150, 10, 62,19, &G.scene->toolsettings->skgen_postpro, 5.0, (float)SKGEN_AVERAGE, 0, 0, "Average embeddings"); - uiDefButC(block, ROW, B_DIFF, "Sharpen", 1212, 10, 63,19, &G.scene->toolsettings->skgen_postpro, 5.0, (float)SKGEN_SHARPEN, 0, 0, "Sharpen embeddings"); uiBlockEndAlign(block); } @@ -6623,8 +6715,11 @@ void editing_panels() editing_panel_mesh_tools1(ob, ob->data); uiNewPanelTabbed("Mesh Tools 1", "Editing"); - if (G.rt == 42) /* hidden for now, no time for docs */ - editing_panel_mesh_skgen(ob, ob->data); + #ifdef WITH_BF_REEB + editing_panel_mesh_skgen(ob, ob->data); + editing_panel_mesh_skgen_retarget(ob, ob->data); + editing_panel_mesh_skgen_display(ob, ob->data); + #endif editing_panel_mesh_uvautocalculation(); if (EM_texFaceCheck()) diff --git a/source/blender/src/drawview.c b/source/blender/src/drawview.c index 2e6f6c18073..de3e464060d 100644 --- a/source/blender/src/drawview.c +++ b/source/blender/src/drawview.c @@ -167,6 +167,8 @@ #include "RE_pipeline.h" // make_stars +#include "reeb.h" + #include "GPU_draw.h" #include "GPU_material.h" @@ -3240,6 +3242,8 @@ void drawview3dspace(ScrArea *sa, void *spacedata) BIF_drawPropCircle(); // only editmode and particles have proportional edit BIF_drawSnap(); } + + REEB_draw(); if(G.scene->radio) RAD_drawall(v3d->drawtype>=OB_SOLID); diff --git a/source/blender/src/editarmature.c b/source/blender/src/editarmature.c index 0b8c1339dae..5ddf522e4a8 100644 --- a/source/blender/src/editarmature.c +++ b/source/blender/src/editarmature.c @@ -4514,542 +4514,7 @@ void transform_armature_mirror_update(void) /*************************************** SKELETON GENERATOR ******************************************/ /*****************************************************************************************************/ -/**************************************** SYMMETRY HANDLING ******************************************/ -void markdownSymmetryArc(ReebArc *arc, ReebNode *node, int level); - -void mirrorAlongAxis(float v[3], float center[3], float axis[3]) -{ - float dv[3], pv[3]; - - VecSubf(dv, v, center); - Projf(pv, dv, axis); - VecMulf(pv, -2); - VecAddf(v, v, pv); -} - -/* Helper structure for radial symmetry */ -typedef struct RadialArc -{ - ReebArc *arc; - float n[3]; /* normalized vector joining the nodes of the arc */ -} RadialArc; - -void reestablishRadialSymmetry(ReebNode *node, int depth, float axis[3]) -{ - RadialArc *ring = NULL; - RadialArc *unit; - float limit = G.scene->toolsettings->skgen_symmetry_limit; - int symmetric = 1; - int count = 0; - int i; - - /* count the number of arcs in the symmetry ring */ - for (i = 0; node->arcs[i] != NULL; i++) - { - ReebArc *connectedArc = node->arcs[i]; - - /* depth is store as a negative in flag. symmetry level is positive */ - if (connectedArc->flags == -depth) - { - count++; - } - } - - ring = MEM_callocN(sizeof(RadialArc) * count, "radial symmetry ring"); - unit = ring; - - /* fill in the ring */ - for (unit = ring, i = 0; node->arcs[i] != NULL; i++) - { - ReebArc *connectedArc = node->arcs[i]; - - /* depth is store as a negative in flag. symmetry level is positive */ - if (connectedArc->flags == -depth) - { - ReebNode *otherNode = OTHER_NODE(connectedArc, node); - float vec[3]; - - unit->arc = connectedArc; - - /* project the node to node vector on the symmetry plane */ - VecSubf(unit->n, otherNode->p, node->p); - Projf(vec, unit->n, axis); - VecSubf(unit->n, unit->n, vec); - - Normalize(unit->n); - - unit++; - } - } - - /* sort ring */ - for (i = 0; i < count - 1; i++) - { - float minAngle = 3; /* arbitrary high value, higher than 2, at least */ - int minIndex = -1; - int j; - - for (j = i + 1; j < count; j++) - { - float angle = Inpf(ring[i].n, ring[j].n); - - /* map negative values to 1..2 */ - if (angle < 0) - { - angle = 1 - angle; - } - - if (angle < minAngle) - { - minIndex = j; - minAngle = angle; - } - } - - /* swap if needed */ - if (minIndex != i + 1) - { - RadialArc tmp; - tmp = ring[i + 1]; - ring[i + 1] = ring[minIndex]; - ring[minIndex] = tmp; - } - } - - for (i = 0; i < count && symmetric; i++) - { - ReebNode *node1, *node2; - float tangent[3]; - float normal[3]; - float p[3]; - int j = (i + 1) % count; /* next arc in the circular list */ - - VecAddf(tangent, ring[i].n, ring[j].n); - Crossf(normal, tangent, axis); - - node1 = OTHER_NODE(ring[i].arc, node); - node2 = OTHER_NODE(ring[j].arc, node); - - VECCOPY(p, node2->p); - mirrorAlongAxis(p, node->p, normal); - - /* check if it's within limit before continuing */ - if (VecLenf(node1->p, p) > limit) - { - symmetric = 0; - } - - } - - if (symmetric) - { - /* first pass, merge incrementally */ - for (i = 0; i < count - 1; i++) - { - ReebNode *node1, *node2; - float tangent[3]; - float normal[3]; - int j = i + 1; - - VecAddf(tangent, ring[i].n, ring[j].n); - Crossf(normal, tangent, axis); - - node1 = OTHER_NODE(ring[i].arc, node); - node2 = OTHER_NODE(ring[j].arc, node); - - /* mirror first node and mix with the second */ - mirrorAlongAxis(node1->p, node->p, normal); - VecLerpf(node2->p, node2->p, node1->p, 1.0f / (j + 1)); - - /* Merge buckets - * there shouldn't be any null arcs here, but just to be safe - * */ - if (ring[i].arc->bcount > 0 && ring[j].arc->bcount > 0) - { - ReebArcIterator iter1, iter2; - EmbedBucket *bucket1 = NULL, *bucket2 = NULL; - - initArcIterator(&iter1, ring[i].arc, node); - initArcIterator(&iter2, ring[j].arc, node); - - bucket1 = nextBucket(&iter1); - bucket2 = nextBucket(&iter2); - - /* Make sure they both start at the same value */ - while(bucket1 && bucket1->val < bucket2->val) - { - bucket1 = nextBucket(&iter1); - } - - while(bucket2 && bucket2->val < bucket1->val) - { - bucket2 = nextBucket(&iter2); - } - - - for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2)) - { - bucket2->nv += bucket1->nv; /* add counts */ - - /* mirror on axis */ - mirrorAlongAxis(bucket1->p, node->p, normal); - /* add bucket2 in bucket1 */ - VecLerpf(bucket2->p, bucket2->p, bucket1->p, (float)bucket1->nv / (float)(bucket2->nv)); - } - } - } - - /* second pass, mirror back on previous arcs */ - for (i = count - 1; i > 0; i--) - { - ReebNode *node1, *node2; - float tangent[3]; - float normal[3]; - int j = i - 1; - - VecAddf(tangent, ring[i].n, ring[j].n); - Crossf(normal, tangent, axis); - - node1 = OTHER_NODE(ring[i].arc, node); - node2 = OTHER_NODE(ring[j].arc, node); - - /* copy first node than mirror */ - VECCOPY(node2->p, node1->p); - mirrorAlongAxis(node2->p, node->p, normal); - - /* Copy buckets - * there shouldn't be any null arcs here, but just to be safe - * */ - if (ring[i].arc->bcount > 0 && ring[j].arc->bcount > 0) - { - ReebArcIterator iter1, iter2; - EmbedBucket *bucket1 = NULL, *bucket2 = NULL; - - initArcIterator(&iter1, ring[i].arc, node); - initArcIterator(&iter2, ring[j].arc, node); - - bucket1 = nextBucket(&iter1); - bucket2 = nextBucket(&iter2); - - /* Make sure they both start at the same value */ - while(bucket1 && bucket1->val < bucket2->val) - { - bucket1 = nextBucket(&iter1); - } - - while(bucket2 && bucket2->val < bucket1->val) - { - bucket2 = nextBucket(&iter2); - } - - - for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2)) - { - /* copy and mirror back to bucket2 */ - bucket2->nv = bucket1->nv; - VECCOPY(bucket2->p, bucket1->p); - mirrorAlongAxis(bucket2->p, node->p, normal); - } - } - } - } - - MEM_freeN(ring); -} - -void reestablishAxialSymmetry(ReebNode *node, int depth, float axis[3]) -{ - ReebArc *arc1 = NULL; - ReebArc *arc2 = NULL; - ReebNode *node1 = NULL, *node2 = NULL; - float limit = G.scene->toolsettings->skgen_symmetry_limit; - float nor[3], vec[3], p[3]; - int i; - - for (i = 0; node->arcs[i] != NULL; i++) - { - ReebArc *connectedArc = node->arcs[i]; - - /* depth is store as a negative in flag. symmetry level is positive */ - if (connectedArc->flags == -depth) - { - if (arc1 == NULL) - { - arc1 = connectedArc; - node1 = OTHER_NODE(arc1, node); - } - else - { - arc2 = connectedArc; - node2 = OTHER_NODE(arc2, node); - break; /* Can stop now, the two arcs have been found */ - } - } - } - - /* shouldn't happen, but just to be sure */ - if (node1 == NULL || node2 == NULL) - { - return; - } - - VecSubf(p, node1->p, node->p); - Crossf(vec, p, axis); - Crossf(nor, vec, axis); - - /* mirror node2 along axis */ - VECCOPY(p, node2->p); - mirrorAlongAxis(p, node->p, nor); - - /* check if it's within limit before continuing */ - if (VecLenf(node1->p, p) <= limit) - { - - /* average with node1 */ - VecAddf(node1->p, node1->p, p); - VecMulf(node1->p, 0.5f); - - /* mirror back on node2 */ - VECCOPY(node2->p, node1->p); - mirrorAlongAxis(node2->p, node->p, nor); - - /* Merge buckets - * there shouldn't be any null arcs here, but just to be safe - * */ - if (arc1->bcount > 0 && arc2->bcount > 0) - { - ReebArcIterator iter1, iter2; - EmbedBucket *bucket1 = NULL, *bucket2 = NULL; - - initArcIterator(&iter1, arc1, node); - initArcIterator(&iter2, arc2, node); - - bucket1 = nextBucket(&iter1); - bucket2 = nextBucket(&iter2); - - /* Make sure they both start at the same value */ - while(bucket1 && bucket1->val < bucket2->val) - { - bucket1 = nextBucket(&iter1); - } - - while(bucket2 && bucket2->val < bucket1->val) - { - bucket2 = nextBucket(&iter2); - } - - - for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2)) - { - bucket1->nv += bucket2->nv; /* add counts */ - - /* mirror on axis */ - mirrorAlongAxis(bucket2->p, node->p, nor); - /* add bucket2 in bucket1 */ - VecLerpf(bucket1->p, bucket1->p, bucket2->p, (float)bucket2->nv / (float)(bucket1->nv)); - - /* copy and mirror back to bucket2 */ - bucket2->nv = bucket1->nv; - VECCOPY(bucket2->p, bucket1->p); - mirrorAlongAxis(bucket2->p, node->p, nor); - } - } - } -} - -void markdownSecondarySymmetry(ReebNode *node, int depth, int level) -{ - float axis[3] = {0, 0, 0}; - int count = 0; - int i; - - /* Only reestablish spatial symmetry if needed */ - if (G.scene->toolsettings->skgen_options & SKGEN_SYMMETRY) - { - /* count the number of branches in this symmetry group - * and determinte the axis of symmetry - * */ - for (i = 0; node->arcs[i] != NULL; i++) - { - ReebArc *connectedArc = node->arcs[i]; - - /* depth is store as a negative in flag. symmetry level is positive */ - if (connectedArc->flags == -depth) - { - count++; - } - /* If arc is on the axis */ - else if (connectedArc->flags == level) - { - VecAddf(axis, axis, connectedArc->v1->p); - VecSubf(axis, axis, connectedArc->v2->p); - } - } - - Normalize(axis); - - /* Split between axial and radial symmetry */ - if (count == 2) - { - reestablishAxialSymmetry(node, depth, axis); - } - else - { - reestablishRadialSymmetry(node, depth, axis); - } - } - - /* markdown secondary symetries */ - for (i = 0; node->arcs[i] != NULL; i++) - { - ReebArc *connectedArc = node->arcs[i]; - - if (connectedArc->flags == -depth) - { - /* markdown symmetry for branches corresponding to the depth */ - markdownSymmetryArc(connectedArc, node, level + 1); - } - } -} - -void markdownSymmetryArc(ReebArc *arc, ReebNode *node, int level) -{ - int i; - arc->flags = level; - - node = OTHER_NODE(arc, node); - - for (i = 0; node->arcs[i] != NULL; i++) - { - ReebArc *connectedArc = node->arcs[i]; - - if (connectedArc != arc) - { - ReebNode *connectedNode = OTHER_NODE(connectedArc, node); - - /* symmetry level is positive value, negative values is subtree depth */ - connectedArc->flags = -subtreeDepth(connectedNode, connectedArc); - } - } - - arc = NULL; - - for (i = 0; node->arcs[i] != NULL; i++) - { - int issymmetryAxis = 0; - ReebArc *connectedArc = node->arcs[i]; - - /* only arcs not already marked as symetric */ - if (connectedArc->flags < 0) - { - int j; - - /* true by default */ - issymmetryAxis = 1; - - for (j = 0; node->arcs[j] != NULL && issymmetryAxis == 1; j++) - { - ReebArc *otherArc = node->arcs[j]; - - /* different arc, same depth */ - if (otherArc != connectedArc && otherArc->flags == connectedArc->flags) - { - /* not on the symmetry axis */ - issymmetryAxis = 0; - } - } - } - - /* arc could be on the symmetry axis */ - if (issymmetryAxis == 1) - { - /* no arc as been marked previously, keep this one */ - if (arc == NULL) - { - arc = connectedArc; - } - else - { - /* there can't be more than one symmetry arc */ - arc = NULL; - break; - } - } - } - - /* go down the arc continuing the symmetry axis */ - if (arc) - { - markdownSymmetryArc(arc, node, level); - } - - - /* secondary symmetry */ - for (i = 0; node->arcs[i] != NULL; i++) - { - ReebArc *connectedArc = node->arcs[i]; - - /* only arcs not already marked as symetric and is not the next arc on the symmetry axis */ - if (connectedArc->flags < 0) - { - /* subtree depth is store as a negative value in the flag */ - markdownSecondarySymmetry(node, -connectedArc->flags, level); - } - } -} - -void markdownSymmetry(ReebGraph *rg) -{ - ReebNode *node; - ReebArc *arc; - /* only for Acyclic graphs */ - int cyclic = isGraphCyclic(rg); - - /* mark down all arcs as non-symetric */ - for (arc = rg->arcs.first; arc; arc = arc->next) - { - arc->flags = 0; - } - - /* mark down all nodes as not on the symmetry axis */ - for (node = rg->nodes.first; node; node = node->next) - { - node->flags = 0; - } - - /* node list is sorted, so lowest node is always the head (by design) */ - node = rg->nodes.first; - - /* only work on acyclic graphs and if only one arc is incident on the first node */ - if (cyclic == 0 && countConnectedArcs(rg, node) == 1) - { - arc = node->arcs[0]; - - markdownSymmetryArc(arc, node, 1); - - /* mark down non-symetric arcs */ - for (arc = rg->arcs.first; arc; arc = arc->next) - { - if (arc->flags < 0) - { - arc->flags = 0; - } - else - { - /* mark down nodes with the lowest level symmetry axis */ - if (arc->v1->flags == 0 || arc->v1->flags > arc->flags) - { - arc->v1->flags = arc->flags; - } - if (arc->v2->flags == 0 || arc->v2->flags > arc->flags) - { - arc->v2->flags = arc->flags; - } - } - } - } -} /**************************************** SUBDIVISION ALGOS ******************************************/ @@ -5114,7 +4579,7 @@ EditBone * subdivideByAngle(ReebArc *arc, ReebNode *head, ReebNode *tail) return lastBone; } -float calcCorrelation(ReebArc *arc, int start, int end, float v0[3], float n[3]) +float calcVariance(ReebArc *arc, int start, int end, float v0[3], float n[3]) { int len = 2 + abs(end - start); @@ -5162,19 +4627,47 @@ float calcCorrelation(ReebArc *arc, int start, int end, float v0[3], float n[3]) /* 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; + return s_xyz / s_t; } else { - return 1.0f; + return 0; } } +float calcDistance(ReebArc *arc, int start, int end, float head[3], float tail[3]) +{ + ReebArcIterator iter; + EmbedBucket *bucket = NULL; + float max_dist = 0; + + /* calculate maximum distance */ + for (initArcIterator2(&iter, arc, start, end), bucket = nextBucket(&iter); + bucket; + bucket = nextBucket(&iter)) + { + float v1[3], v2[3], c[3]; + float dist; + + VecSubf(v1, head, tail); + VecSubf(v2, bucket->p, tail); + + Crossf(c, v1, v2); + + dist = Inpf(c, c) / Inpf(v1, v1); + + max_dist = dist > max_dist ? dist : max_dist; + } + + + return max_dist; +} + EditBone * subdivideByCorrelation(ReebArc *arc, ReebNode *head, ReebNode *tail) { ReebArcIterator iter; float n[3]; - float CORRELATION_THRESHOLD = G.scene->toolsettings->skgen_correlation_limit; + float ADAPTIVE_THRESHOLD = G.scene->toolsettings->skgen_correlation_limit; EditBone *lastBone = NULL; /* init iterator to get start and end from head */ @@ -5183,15 +4676,17 @@ EditBone * subdivideByCorrelation(ReebArc *arc, ReebNode *head, ReebNode *tail) /* 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) + if (G.scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION) { EmbedBucket *bucket = NULL; EmbedBucket *previous = NULL; EditBone *child = NULL; EditBone *parent = NULL; + float normal[3] = {0, 0, 0}; + float avg_normal[3]; + int total = 0; int boneStart = iter.start; - + parent = add_editbone("Bone"); parent->flag = BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL; VECCOPY(parent->head, head->p); @@ -5200,12 +4695,46 @@ EditBone * subdivideByCorrelation(ReebArc *arc, ReebNode *head, ReebNode *tail) bucket; previous = bucket, bucket = nextBucket(&iter)) { - /* Calculate normal */ - VecSubf(n, bucket->p, parent->head); + float btail[3]; + float value = 0; - if (calcCorrelation(arc, boneStart, iter.index, parent->head, n) < CORRELATION_THRESHOLD) + if (G.scene->toolsettings->skgen_options & SKGEN_STICK_TO_EMBEDDING) { - VECCOPY(parent->tail, previous->p); + VECCOPY(btail, bucket->p); + } + else + { + float length; + + /* Calculate normal */ + VecSubf(n, bucket->p, parent->head); + length = Normalize(n); + + total += 1; + VecAddf(normal, normal, n); + VECCOPY(avg_normal, normal); + VecMulf(avg_normal, 1.0f / total); + + VECCOPY(btail, avg_normal); + VecMulf(btail, length); + VecAddf(btail, btail, parent->head); + } + + if (G.scene->toolsettings->skgen_options & SKGEN_ADAPTIVE_DISTANCE) + { + value = calcDistance(arc, boneStart, iter.index, parent->head, btail); + } + else + { + float n[3]; + + VecSubf(n, btail, parent->head); + value = calcVariance(arc, boneStart, iter.index, parent->head, n); + } + + if (value > ADAPTIVE_THRESHOLD) + { + VECCOPY(parent->tail, btail); child = add_editbone("Bone"); VECCOPY(child->head, parent->tail); @@ -5214,6 +4743,9 @@ EditBone * subdivideByCorrelation(ReebArc *arc, ReebNode *head, ReebNode *tail) parent = child; // new child is next parent boneStart = iter.index; // start from end + + normal[0] = normal[1] = normal[2] = 0; + total = 0; } } @@ -5231,7 +4763,7 @@ float arcLengthRatio(ReebArc *arc) float embedLength = 0.0f; int i; - arcLength = VecLenf(arc->v1->p, arc->v2->p); + arcLength = VecLenf(arc->head->p, arc->tail->p); if (arc->bcount > 0) { @@ -5241,8 +4773,8 @@ float arcLengthRatio(ReebArc *arc) embedLength += VecLenf(arc->buckets[i - 1].p, arc->buckets[i].p); } /* Add head and tail -> embedding vectors */ - embedLength += VecLenf(arc->v1->p, arc->buckets[0].p); - embedLength += VecLenf(arc->v2->p, arc->buckets[arc->bcount - 1].p); + embedLength += VecLenf(arc->head->p, arc->buckets[0].p); + embedLength += VecLenf(arc->tail->p, arc->buckets[arc->bcount - 1].p); } else { @@ -5374,8 +4906,6 @@ void generateSkeletonFromReebGraph(ReebGraph *rg) { exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR); // freedata, and undo } - - setcursor_space(SPACE_VIEW3D, CURSOR_WAIT); dst = add_object(OB_ARMATURE); base_init_from_view3d(BASACT, G.vd); @@ -5392,7 +4922,7 @@ void generateSkeletonFromReebGraph(ReebGraph *rg) arcBoneMap = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); - markdownSymmetry(rg); + BLI_markdownSymmetry((BGraph*)rg, rg->nodes.first, G.scene->toolsettings->skgen_symmetry_limit); for (arc = rg->arcs.first; arc; arc = arc->next) { @@ -5402,43 +4932,43 @@ void generateSkeletonFromReebGraph(ReebGraph *rg) /* Find out the direction of the arc through simple heuristics (in order of priority) : * - * 1- Arcs on primary symmetry axis (flags == 1) point up (head: high weight -> tail: low weight) + * 1- Arcs on primary symmetry axis (symmetry == 1) point up (head: high weight -> tail: low weight) * 2- Arcs starting on a primary axis point away from it (head: node on primary axis) * 3- Arcs point down (head: low weight -> tail: high weight) * - * Finally, the arc direction is stored in its flags: 1 (low -> high), -1 (high -> low) + * Finally, the arc direction is stored in its flag: 1 (low -> high), -1 (high -> low) */ /* if arc is a symmetry axis, internal bones go up the tree */ - if (arc->flags == 1 && arc->v2->degree != 1) + if (arc->symmetry_level == 1 && arc->tail->degree != 1) { - head = arc->v2; - tail = arc->v1; + head = arc->tail; + tail = arc->head; - arc->flags = -1; /* mark arc direction */ + arc->flag = -1; /* mark arc direction */ } /* Bones point AWAY from the symmetry axis */ - else if (arc->v1->flags == 1) + else if (arc->head->symmetry_level == 1) { - head = arc->v1; - tail = arc->v2; + head = arc->head; + tail = arc->tail; - arc->flags = 1; /* mark arc direction */ + arc->flag = 1; /* mark arc direction */ } - else if (arc->v2->flags == 1) + else if (arc->tail->symmetry_level == 1) { - head = arc->v2; - tail = arc->v1; + head = arc->tail; + tail = arc->head; - arc->flags = -1; /* mark arc direction */ + arc->flag = -1; /* mark arc direction */ } /* otherwise, always go from low weight to high weight */ else { - head = arc->v1; - tail = arc->v2; + head = arc->head; + tail = arc->tail; - arc->flags = 1; /* mark arc direction */ + arc->flag = 1; /* mark arc direction */ } /* Loop over subdivision methods */ @@ -5480,12 +5010,12 @@ void generateSkeletonFromReebGraph(ReebGraph *rg) ReebArc *incomingArc = NULL; int i; - for (i = 0; node->arcs[i] != NULL; i++) + for (i = 0; i < node->degree; i++) { - arc = node->arcs[i]; + arc = (ReebArc*)node->arcs[i]; /* if arc is incoming into the node */ - if ((arc->v1 == node && arc->flags == -1) || (arc->v2 == node && arc->flags == 1)) + if ((arc->head == node && arc->flag == -1) || (arc->tail == node && arc->flag == 1)) { if (incomingArc == NULL) { @@ -5506,12 +5036,12 @@ void generateSkeletonFromReebGraph(ReebGraph *rg) EditBone *parentBone = BLI_ghash_lookup(arcBoneMap, incomingArc); /* Look for outgoing arcs and parent their bones */ - for (i = 0; node->arcs[i] != NULL; i++) + for (i = 0; i < node->degree; i++) { arc = node->arcs[i]; /* if arc is outgoing from the node */ - if ((arc->v1 == node && arc->flags == 1) || (arc->v2 == node && arc->flags == -1)) + if ((arc->head == node && arc->flag == 1) || (arc->tail == node && arc->flag == -1)) { EditBone *childBone = BLI_ghash_lookup(arcBoneMap, arc); @@ -5529,89 +5059,21 @@ void generateSkeletonFromReebGraph(ReebGraph *rg) } BLI_ghash_free(arcBoneMap, NULL, NULL); - - setcursor_space(SPACE_VIEW3D, CURSOR_EDIT); BIF_undo_push("Generate Skeleton"); } void generateSkeleton(void) { - EditMesh *em = G.editMesh; - ReebGraph *rg = NULL; - int i; + ReebGraph *reebg; - if (em == NULL) - return; - setcursor_space(SPACE_VIEW3D, CURSOR_WAIT); - - if (weightFromDistance(em) == 0) - { - error("No selected vertex\n"); - return; - } - - renormalizeWeight(em, 1.0f); - weightToHarmonic(em); - -#ifdef DEBUG_REEB - weightToVCol(em); -#endif - - rg = generateReebGraph(em, G.scene->toolsettings->skgen_resolution); + reebg = BIF_ReebGraphFromEditMesh(); - verifyBuckets(rg); - - /* Remove arcs without embedding */ - filterNullReebGraph(rg); + generateSkeletonFromReebGraph(reebg); - verifyBuckets(rg); + REEB_freeGraph(reebg); - - i = 1; - /* filter until there's nothing more to do */ - while (i == 1) - { - i = 0; /* no work done yet */ - - if (G.scene->toolsettings->skgen_options & SKGEN_FILTER_EXTERNAL) - { - i |= filterExternalReebGraph(rg, G.scene->toolsettings->skgen_threshold_external * G.scene->toolsettings->skgen_resolution); - } - - verifyBuckets(rg); - - if (G.scene->toolsettings->skgen_options & SKGEN_FILTER_INTERNAL) - { - i |= filterInternalReebGraph(rg, G.scene->toolsettings->skgen_threshold_internal * G.scene->toolsettings->skgen_resolution); - } - } - - verifyBuckets(rg); - - repositionNodes(rg); - - verifyBuckets(rg); - - /* Filtering might have created degree 2 nodes, so remove them */ - removeNormalNodes(rg); - - verifyBuckets(rg); - - for(i = 0; i < G.scene->toolsettings->skgen_postpro_passes; i++) - { - postprocessGraph(rg, G.scene->toolsettings->skgen_postpro); - } - - buildAdjacencyList(rg); - - sortNodes(rg); - - sortArcs(rg); - - generateSkeletonFromReebGraph(rg); - - freeGraph(rg); + setcursor_space(SPACE_VIEW3D, CURSOR_EDIT); } diff --git a/source/blender/src/reeb.c b/source/blender/src/reeb.c index 85fb5815c3e..54befb85a9b 100644 --- a/source/blender/src/reeb.c +++ b/source/blender/src/reeb.c @@ -30,10 +30,13 @@ #include #include // for qsort +#include "PIL_time.h" + #include "DNA_listBase.h" #include "DNA_scene_types.h" #include "DNA_space_types.h" #include "DNA_meshdata_types.h" +#include "DNA_armature_types.h" #include "MEM_guardedalloc.h" @@ -41,14 +44,20 @@ #include "BLI_arithb.h" #include "BLI_editVert.h" #include "BLI_edgehash.h" +#include "BLI_ghash.h" +#include "BLI_heap.h" #include "BDR_editobject.h" +#include "BMF_Api.h" + #include "BIF_editmesh.h" #include "BIF_editarmature.h" #include "BIF_interface.h" #include "BIF_toolbox.h" #include "BIF_graphics.h" +#include "BIF_gl.h" +#include "BIF_resources.h" #include "BKE_global.h" #include "BKE_utildefines.h" @@ -60,6 +69,12 @@ #include "reeb.h" +/* REPLACE WITH NEW ONE IN UTILDEFINES ONCE PATCH IS APPLIED */ +#define FTOCHAR(val) (val<=0.0f)? 0 : ((val>(1.0f-0.5f/255.0f))? 255 : (char)((255.0f*val)+0.5f)) + +ReebGraph *GLOBAL_RG = NULL; +ReebGraph *FILTERED_RG = NULL; + /* * Skeleton generation algorithm based on: * "Harmonic Skeleton for Realistic Character Animation" @@ -72,11 +87,491 @@ * SIGGRAPH 2007 * * */ + +#define DEBUG_REEB +#define DEBUG_REEB_NODE + +typedef struct EdgeIndex +{ + EditEdge **edges; + int *offset; +} EdgeIndex; + +typedef enum { + MERGE_LOWER, + MERGE_HIGHER, + MERGE_APPEND +} MergeDirection; int mergeArcs(ReebGraph *rg, ReebArc *a0, ReebArc *a1); +void mergeArcEdges(ReebGraph *rg, ReebArc *aDst, ReebArc *aSrc, MergeDirection direction); int mergeConnectedArcs(ReebGraph *rg, ReebArc *a0, ReebArc *a1); -EditEdge * NextEdgeForVert(EditMesh *em, EditVert *v); +EditEdge * NextEdgeForVert(EdgeIndex *indexed_edges, EditVert *eve); +void mergeArcFaces(ReebGraph *rg, ReebArc *aDst, ReebArc *aSrc); +void addFacetoArc(ReebArc *arc, EditFace *efa); +void REEB_RadialSymmetry(BNode* root_node, RadialArc* ring, int count); +void REEB_AxialSymmetry(BNode* root_node, BNode* node1, BNode* node2, struct BArc* barc1, BArc* barc2); + +void flipArcBuckets(ReebArc *arc); + + +/***************************************** UTILS **********************************************/ + +void REEB_freeArc(BArc *barc) +{ + ReebArc *arc = (ReebArc*)barc; + BLI_freelistN(&arc->edges); + + if (arc->buckets) + MEM_freeN(arc->buckets); + + if (arc->faces) + BLI_ghash_free(arc->faces, NULL, NULL); + + MEM_freeN(arc); +} + +void REEB_freeGraph(ReebGraph *rg) +{ + ReebArc *arc; + ReebNode *node; + + // free nodes + for( node = rg->nodes.first; node; node = node->next ) + { + BLI_freeNode((BGraph*)rg, (BNode*)node); + } + BLI_freelistN(&rg->nodes); + + // free arcs + arc = rg->arcs.first; + while( arc ) + { + ReebArc *next = arc->next; + REEB_freeArc((BArc*)arc); + arc = next; + } + + // free edge map + BLI_edgehash_free(rg->emap, NULL); + + /* free linked graph */ + if (rg->link_up) + { + REEB_freeGraph(rg->link_up); + } + + MEM_freeN(rg); +} + +ReebGraph * newReebGraph() +{ + ReebGraph *rg; + rg = MEM_callocN(sizeof(ReebGraph), "reeb graph"); + + rg->totnodes = 0; + rg->emap = BLI_edgehash_new(); + + + rg->free_arc = REEB_freeArc; + rg->free_node = NULL; + rg->radial_symmetry = REEB_RadialSymmetry; + rg->axial_symmetry = REEB_AxialSymmetry; + + return rg; +} + +void BIF_flagMultiArcs(ReebGraph *rg, int flag) +{ + for ( ; rg; rg = rg->link_up) + { + BLI_flagArcs((BGraph*)rg, flag); + } +} + +ReebNode * addNode(ReebGraph *rg, EditVert *eve, float weight) +{ + ReebNode *node = NULL; + + node = MEM_callocN(sizeof(ReebNode), "reeb node"); + + node->flag = 0; // clear flag on init + node->symmetry_level = 0; + node->arcs = NULL; + node->degree = 0; + node->weight = weight; + node->index = rg->totnodes; + VECCOPY(node->p, eve->co); + + BLI_addtail(&rg->nodes, node); + rg->totnodes++; + + return node; +} + +ReebNode * copyNode(ReebGraph *rg, ReebNode *node) +{ + ReebNode *cp_node = NULL; + + cp_node = MEM_callocN(sizeof(ReebNode), "reeb node copy"); + + memcpy(cp_node, node, sizeof(ReebNode)); + + cp_node->prev = NULL; + cp_node->next = NULL; + cp_node->arcs = NULL; + + cp_node->link_up = NULL; + cp_node->link_down = NULL; + + BLI_addtail(&rg->nodes, cp_node); + rg->totnodes++; + + return cp_node; +} + +void relinkNodes(ReebGraph *low_rg, ReebGraph *high_rg) +{ + ReebNode *low_node, *high_node; + + if (low_rg == NULL || high_rg == NULL) + { + return; + } + + for (low_node = low_rg->nodes.first; low_node; low_node = low_node->next) + { + for (high_node = high_rg->nodes.first; high_node; high_node = high_node->next) + { + if (low_node->index == high_node->index) + { + high_node->link_down = low_node; + low_node->link_up = high_node; + break; + } + } + } +} + +ReebNode *BIF_otherNodeFromIndex(ReebArc *arc, ReebNode *node) +{ + return (arc->head->index == node->index) ? arc->tail : arc->head; +} + +ReebNode *BIF_NodeFromIndex(ReebArc *arc, ReebNode *node) +{ + return (arc->head->index == node->index) ? arc->head : arc->tail; +} + +ReebNode *BIF_lowestLevelNode(ReebNode *node) +{ + while (node->link_down) + { + node = node->link_down; + } + + return node; +} + +ReebArc * copyArc(ReebGraph *rg, ReebArc *arc) +{ + ReebArc *cp_arc; + ReebNode *node; + + cp_arc = MEM_callocN(sizeof(ReebArc), "reeb arc copy"); + + memcpy(cp_arc, arc, sizeof(ReebArc)); + + cp_arc->link_up = arc; + + cp_arc->head = NULL; + cp_arc->tail = NULL; + + cp_arc->prev = NULL; + cp_arc->next = NULL; + + cp_arc->edges.first = NULL; + cp_arc->edges.last = NULL; + + /* copy buckets */ + cp_arc->buckets = MEM_callocN(sizeof(EmbedBucket) * cp_arc->bcount, "embed bucket"); + memcpy(cp_arc->buckets, arc->buckets, sizeof(EmbedBucket) * cp_arc->bcount); + + /* copy faces map */ + cp_arc->faces = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + mergeArcFaces(rg, cp_arc, arc); + + /* find corresponding head and tail */ + for (node = rg->nodes.first; node && (cp_arc->head == NULL || cp_arc->tail == NULL); node = node->next) + { + if (node->index == arc->head->index) + { + cp_arc->head = node; + } + else if (node->index == arc->tail->index) + { + cp_arc->tail = node; + } + } + + BLI_addtail(&rg->arcs, cp_arc); + + return cp_arc; +} + +ReebGraph * copyReebGraph(ReebGraph *rg, int level) +{ + ReebNode *node; + ReebArc *arc; + ReebGraph *cp_rg = newReebGraph(); + + cp_rg->resolution = rg->resolution; + cp_rg->length = rg->length; + cp_rg->link_up = rg; + cp_rg->multi_level = level; + + /* Copy nodes */ + for (node = rg->nodes.first; node; node = node->next) + { + ReebNode *cp_node = copyNode(cp_rg, node); + cp_node->multi_level = level; + } + + /* Copy arcs */ + for (arc = rg->arcs.first; arc; arc = arc->next) + { + copyArc(cp_rg, arc); + } + + BLI_buildAdjacencyList((BGraph*)cp_rg); + + return cp_rg; +} + +ReebGraph *BIF_graphForMultiNode(ReebGraph *rg, ReebNode *node) +{ + ReebGraph *multi_rg = rg; + + while(multi_rg && multi_rg->multi_level != node->multi_level) + { + multi_rg = multi_rg->link_up; + } + + return multi_rg; +} + +ReebEdge * copyEdge(ReebEdge *edge) +{ + ReebEdge *newEdge = NULL; + + newEdge = MEM_callocN(sizeof(ReebEdge), "reeb edge"); + memcpy(newEdge, edge, sizeof(ReebEdge)); + + newEdge->next = NULL; + newEdge->prev = NULL; + + return newEdge; +} + +void printArc(ReebArc *arc) +{ + ReebEdge *edge; + ReebNode *head = (ReebNode*)arc->head; + ReebNode *tail = (ReebNode*)arc->tail; + printf("arc: (%i) %f -> (%i) %f\n", head->index, head->weight, tail->index, tail->weight); + + for(edge = arc->edges.first; edge ; edge = edge->next) + { + printf("\tedge (%i, %i)\n", edge->v1->index, edge->v2->index); + } +} + +void flipArc(ReebArc *arc) +{ + ReebNode *tmp; + tmp = arc->head; + arc->head = arc->tail; + arc->tail = tmp; + + flipArcBuckets(arc); +} + +#ifdef DEBUG_REEB_NODE +void NodeDegreeDecrement(ReebGraph *rg, ReebNode *node) +{ + node->degree--; + +// if (node->degree == 0) +// { +// printf("would remove node %i\n", node->index); +// } +} + +void NodeDegreeIncrement(ReebGraph *rg, ReebNode *node) +{ +// if (node->degree == 0) +// { +// printf("first connect node %i\n", node->index); +// } + + node->degree++; +} + +#else +#define NodeDegreeDecrement(rg, node) {node->degree--;} +#define NodeDegreeIncrement(rg, node) {node->degree++;} +#endif + +void repositionNodes(ReebGraph *rg) +{ + BArc *arc = NULL; + BNode *node = NULL; + + // Reset node positions + for(node = rg->nodes.first; node; node = node->next) + { + node->p[0] = node->p[1] = node->p[2] = 0; + } + + for(arc = rg->arcs.first; arc; arc = arc->next) + { + if (((ReebArc*)arc)->bcount > 0) + { + float p[3]; + + VECCOPY(p, ((ReebArc*)arc)->buckets[0].p); + VecMulf(p, 1.0f / arc->head->degree); + VecAddf(arc->head->p, arc->head->p, p); + + VECCOPY(p, ((ReebArc*)arc)->buckets[((ReebArc*)arc)->bcount - 1].p); + VecMulf(p, 1.0f / arc->tail->degree); + VecAddf(arc->tail->p, arc->tail->p, p); + } + } +} + +void verifyNodeDegree(ReebGraph *rg) +{ +#ifdef DEBUG_REEB + ReebNode *node = NULL; + ReebArc *arc = NULL; + + for(node = rg->nodes.first; node; node = node->next) + { + int count = 0; + for(arc = rg->arcs.first; arc; arc = arc->next) + { + if (arc->head == node || arc->tail == node) + { + count++; + } + } + if (count != node->degree) + { + printf("degree error in node %i: expected %i got %i\n", node->index, count, node->degree); + } + if (node->degree == 0) + { + printf("zero degree node %i with weight %f\n", node->index, node->weight); + } + } +#endif +} + +void verifyBucketsArc(ReebGraph *rg, ReebArc *arc) +{ + ReebNode *head = (ReebNode*)arc->head; + ReebNode *tail = (ReebNode*)arc->tail; + + if (arc->bcount > 0) + { + int i; + for(i = 0; i < arc->bcount; i++) + { + if (arc->buckets[i].nv == 0) + { + printArc(arc); + printf("count error in bucket %i/%i\n", i+1, arc->bcount); + } + } + + if (ceil(head->weight) != arc->buckets[0].val) + { + printArc(arc); + printf("alloc error in first bucket: %f should be %f \n", arc->buckets[0].val, ceil(head->weight)); + } + if (floor(tail->weight) != arc->buckets[arc->bcount - 1].val) + { + printArc(arc); + printf("alloc error in last bucket: %f should be %f \n", arc->buckets[arc->bcount - 1].val, floor(tail->weight)); + } + } +} + +void verifyBuckets(ReebGraph *rg) +{ +#ifdef DEBUG_REEB + ReebArc *arc = NULL; + for(arc = rg->arcs.first; arc; arc = arc->next) + { + verifyBucketsArc(rg, arc); + } +#endif +} + +void verifyFaces(ReebGraph *rg) +{ +#ifdef DEBUG_REEB + int total = 0; + ReebArc *arc = NULL; + for(arc = rg->arcs.first; arc; arc = arc->next) + { + total += BLI_ghash_size(arc->faces); + } + +#endif +} + +void verifyArcs(ReebGraph *rg) +{ + ReebArc *arc; + + for (arc = rg->arcs.first; arc; arc = arc->next) + { + if (arc->head->weight > arc->tail->weight) + { + printf("FLIPPED ARC!\n"); + } + } +} + +void verifyMultiResolutionLinks(ReebGraph *rg, int level) +{ +#ifdef DEBUG_REEB + ReebGraph *lower_rg = rg->link_up; + + if (lower_rg) + { + ReebArc *arc; + + for (arc = rg->arcs.first; arc; arc = arc->next) + { + if (BLI_findindex(&lower_rg->arcs, arc->link_up) == -1) + { + printf("missing arc %p for level %i\n", arc->link_up, level); + printf("Source arc was ---\n"); + printArc(arc); + + arc->link_up = NULL; + } + } + + + verifyMultiResolutionLinks(lower_rg, level + 1); + } +#endif +} /***************************************** BUCKET UTILS **********************************************/ void addVertToBucket(EmbedBucket *b, float co[3]) @@ -137,11 +632,30 @@ void mergeArcBuckets(ReebArc *aDst, ReebArc *aSrc, float start, float end) } } +void flipArcBuckets(ReebArc *arc) +{ + int i, j; + + for (i = 0, j = arc->bcount - 1; i < j; i++, j--) + { + EmbedBucket tmp; + + tmp = arc->buckets[i]; + arc->buckets[i] = arc->buckets[j]; + arc->buckets[j] = tmp; + } +} + +int countArcBuckets(ReebArc *arc) +{ + return (int)(floor(arc->tail->weight) - ceil(arc->head->weight)) + 1; +} + void allocArcBuckets(ReebArc *arc) { int i; - float start = ceil(arc->v1->weight); - arc->bcount = (int)(floor(arc->v2->weight) - start) + 1; + float start = ceil(arc->head->weight); + arc->bcount = countArcBuckets(arc); if (arc->bcount > 0) { @@ -164,6 +678,11 @@ void resizeArcBuckets(ReebArc *arc) EmbedBucket *oldBuckets = arc->buckets; int oldBCount = arc->bcount; + if (countArcBuckets(arc) == oldBCount) + { + return; + } + allocArcBuckets(arc); if (oldBCount != 0 && arc->bcount != 0) @@ -195,233 +714,393 @@ void resizeArcBuckets(ReebArc *arc) MEM_freeN(oldBuckets); } } -/***************************************** UTILS **********************************************/ -ReebEdge * copyEdge(ReebEdge *edge) +void reweightBuckets(ReebArc *arc) { - ReebEdge *newEdge = NULL; + int i; + float start = ceil((arc->head)->weight); - newEdge = MEM_callocN(sizeof(ReebEdge), "reeb edge"); - memcpy(newEdge, edge, sizeof(ReebEdge)); - - newEdge->next = NULL; - newEdge->prev = NULL; - - return newEdge; -} - -void printArc(ReebArc *arc) -{ - ReebEdge *edge; - printf("arc: (%i)%f -> (%i)%f\n", arc->v1->index, arc->v1->weight, arc->v2->index, arc->v2->weight); - - for(edge = arc->edges.first; edge ; edge = edge->next) + if (arc->bcount > 0) { - printf("\tedge (%i, %i)\n", edge->v1->index, edge->v2->index); + for(i = 0; i < arc->bcount; i++) + { + arc->buckets[i].val = start + i; + } } } -void freeArc(ReebArc *arc) +static void interpolateBuckets(ReebArc *arc, float *start_p, float *end_p, int start_index, int end_index) { - BLI_freelistN(&arc->edges); + int total; + int j; - if (arc->buckets) - MEM_freeN(arc->buckets); + total = end_index - start_index + 2; - MEM_freeN(arc); + for (j = start_index; j <= end_index; j++) + { + EmbedBucket *empty = arc->buckets + j; + empty->nv = 1; + VecLerpf(empty->p, start_p, end_p, (float)(j - start_index + 1) / total); + } } -void freeGraph(ReebGraph *rg) +void fillArcEmptyBuckets(ReebArc *arc) +{ + float *start_p, *end_p; + int start_index, end_index; + int missing = 0; + int i; + + start_p = arc->head->p; + + for(i = 0; i < arc->bcount; i++) + { + EmbedBucket *bucket = arc->buckets + i; + + if (missing) + { + if (bucket->nv > 0) + { + missing = 0; + + end_p = bucket->p; + end_index = i - 1; + + interpolateBuckets(arc, start_p, end_p, start_index, end_index); + } + } + else + { + if (bucket->nv == 0) + { + missing = 1; + + if (i > 0) + { + start_p = arc->buckets[i - 1].p; + } + start_index = i; + } + } + } + + if (missing) + { + end_p = arc->tail->p; + end_index = arc->bcount - 1; + + interpolateBuckets(arc, start_p, end_p, start_index, end_index); + } +} + +static void ExtendArcBuckets(ReebArc *arc) +{ + ReebArcIterator iter; + EmbedBucket *previous, *bucket, *last_bucket, *first_bucket; + float average_length = 0, length; + int padding_head = 0, padding_tail = 0; + + if (arc->bcount == 0) + { + return; /* failsafe, shouldn't happen */ + } + + initArcIterator(&iter, arc, arc->head); + + for ( previous = nextBucket(&iter), bucket = nextBucket(&iter); + bucket; + previous = bucket, bucket = nextBucket(&iter) + ) + { + average_length += VecLenf(previous->p, bucket->p); + } + average_length /= (arc->bcount - 1); + + first_bucket = arc->buckets; + last_bucket = arc->buckets + (arc->bcount - 1); + + length = VecLenf(first_bucket->p, arc->head->p); + if (length > 2 * average_length) + { + padding_head = (int)floor(length / average_length); + } + + length = VecLenf(last_bucket->p, arc->tail->p); + if (length > 2 * average_length) + { + padding_tail = (int)floor(length / average_length); + } + + if (padding_head + padding_tail > 0) + { + EmbedBucket *old_buckets = arc->buckets; + + arc->buckets = MEM_callocN(sizeof(EmbedBucket) * (padding_head + arc->bcount + padding_tail), "embed bucket"); + memcpy(arc->buckets + padding_head, old_buckets, arc->bcount * sizeof(EmbedBucket)); + + arc->bcount = padding_head + arc->bcount + padding_tail; + + MEM_freeN(old_buckets); + } + + if (padding_head > 0) + { + interpolateBuckets(arc, arc->head->p, first_bucket->p, 0, padding_head); + } + + if (padding_tail > 0) + { + interpolateBuckets(arc, last_bucket->p, arc->tail->p, arc->bcount - padding_tail, arc->bcount - 1); + } +} + +/* CALL THIS ONLY AFTER FILTERING, SINCE IT MESSES UP WEIGHT DISTRIBUTION */ +void extendGraphBuckets(ReebGraph *rg) { ReebArc *arc; - ReebNode *node; - // free nodes - for( node = rg->nodes.first; node; node = node->next ) + for (arc = rg->arcs.first; arc; arc = arc->next) { - // Free adjacency lists - if (node->arcs != NULL) - { - MEM_freeN(node->arcs); - } + ExtendArcBuckets(arc); } - BLI_freelistN(&rg->nodes); - - // free arcs - arc = rg->arcs.first; - while( arc ) - { - ReebArc *next = arc->next; - freeArc(arc); - arc = next; - } - - // free edge map - BLI_edgehash_free(rg->emap, NULL); - - MEM_freeN(rg); } -void repositionNodes(ReebGraph *rg) +/**************************************** LENGTH CALCULATIONS ****************************************/ + +void calculateArcLength(ReebArc *arc) { - ReebArc *arc = NULL; - ReebNode *node = NULL; + ReebArcIterator iter; + EmbedBucket *bucket = NULL; + float *vec0, *vec1; + + arc->length = 0; - // Reset node positions - for(node = rg->nodes.first; node; node = node->next) + initArcIterator(&iter, arc, arc->head); + + bucket = nextBucket(&iter); + + vec0 = arc->head->p; + vec1 = arc->head->p; /* in case there's no embedding */ + + while (bucket != NULL) { - node->p[0] = node->p[1] = node->p[2] = 0; + vec1 = bucket->p; + + arc->length += VecLenf(vec0, vec1); + + vec0 = vec1; + bucket = nextBucket(&iter); } - for(arc = rg->arcs.first; arc; arc = arc->next) + arc->length += VecLenf(arc->tail->p, vec1); +} + +void calculateGraphLength(ReebGraph *rg) +{ + ReebArc *arc; + + for (arc = rg->arcs.first; arc; arc = arc->next) { - if (arc->bcount > 0) + calculateArcLength(arc); + } +} + +/**************************************** SYMMETRY HANDLING ******************************************/ + +void REEB_RadialSymmetry(BNode* root_node, RadialArc* ring, int count) +{ + ReebNode *node = (ReebNode*)root_node; + float axis[3]; + int i; + + VECCOPY(axis, root_node->symmetry_axis); + + /* first pass, merge incrementally */ + for (i = 0; i < count - 1; i++) + { + ReebNode *node1, *node2; + ReebArc *arc1, *arc2; + float tangent[3]; + float normal[3]; + int j = i + 1; + + VecAddf(tangent, ring[i].n, ring[j].n); + Crossf(normal, tangent, axis); + + node1 = (ReebNode*)BLI_otherNode(ring[i].arc, root_node); + node2 = (ReebNode*)BLI_otherNode(ring[j].arc, root_node); + + arc1 = (ReebArc*)ring[i].arc; + arc2 = (ReebArc*)ring[j].arc; + + /* mirror first node and mix with the second */ + BLI_mirrorAlongAxis(node1->p, root_node->p, normal); + VecLerpf(node2->p, node2->p, node1->p, 1.0f / (j + 1)); + + /* Merge buckets + * there shouldn't be any null arcs here, but just to be safe + * */ + if (arc1->bcount > 0 && arc2->bcount > 0) { - float p[3]; + ReebArcIterator iter1, iter2; + EmbedBucket *bucket1 = NULL, *bucket2 = NULL; - VECCOPY(p, arc->buckets[0].p); - VecMulf(p, 1.0f / arc->v1->degree); - VecAddf(arc->v1->p, arc->v1->p, p); + initArcIterator(&iter1, arc1, (ReebNode*)root_node); + initArcIterator(&iter2, arc2, (ReebNode*)root_node); - VECCOPY(p, arc->buckets[arc->bcount - 1].p); - VecMulf(p, 1.0f / arc->v2->degree); - VecAddf(arc->v2->p, arc->v2->p, p); + bucket1 = nextBucket(&iter1); + bucket2 = nextBucket(&iter2); + + /* Make sure they both start at the same value */ + while(bucket1 && bucket1->val < bucket2->val) + { + bucket1 = nextBucket(&iter1); + } + + while(bucket2 && bucket2->val < bucket1->val) + { + bucket2 = nextBucket(&iter2); + } + + + for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2)) + { + bucket2->nv += bucket1->nv; /* add counts */ + + /* mirror on axis */ + BLI_mirrorAlongAxis(bucket1->p, root_node->p, normal); + /* add bucket2 in bucket1 */ + VecLerpf(bucket2->p, bucket2->p, bucket1->p, (float)bucket1->nv / (float)(bucket2->nv)); + } + } + } + + /* second pass, mirror back on previous arcs */ + for (i = count - 1; i > 0; i--) + { + ReebNode *node1, *node2; + ReebArc *arc1, *arc2; + float tangent[3]; + float normal[3]; + int j = i - 1; + + VecAddf(tangent, ring[i].n, ring[j].n); + Crossf(normal, tangent, axis); + + node1 = (ReebNode*)BLI_otherNode(ring[i].arc, root_node); + node2 = (ReebNode*)BLI_otherNode(ring[j].arc, root_node); + + arc1 = (ReebArc*)ring[i].arc; + arc2 = (ReebArc*)ring[j].arc; + + /* copy first node than mirror */ + VECCOPY(node2->p, node1->p); + BLI_mirrorAlongAxis(node2->p, root_node->p, normal); + + /* Copy buckets + * there shouldn't be any null arcs here, but just to be safe + * */ + if (arc1->bcount > 0 && arc2->bcount > 0) + { + ReebArcIterator iter1, iter2; + EmbedBucket *bucket1 = NULL, *bucket2 = NULL; + + initArcIterator(&iter1, arc1, node); + initArcIterator(&iter2, arc2, node); + + bucket1 = nextBucket(&iter1); + bucket2 = nextBucket(&iter2); + + /* Make sure they both start at the same value */ + while(bucket1 && bucket1->val < bucket2->val) + { + bucket1 = nextBucket(&iter1); + } + + while(bucket2 && bucket2->val < bucket1->val) + { + bucket2 = nextBucket(&iter2); + } + + + for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2)) + { + /* copy and mirror back to bucket2 */ + bucket2->nv = bucket1->nv; + VECCOPY(bucket2->p, bucket1->p); + BLI_mirrorAlongAxis(bucket2->p, node->p, normal); + } } } } -void verifyNodeDegree(ReebGraph *rg) +void REEB_AxialSymmetry(BNode* root_node, BNode* node1, BNode* node2, struct BArc* barc1, BArc* barc2) { - ReebNode *node = NULL; - ReebArc *arc = NULL; + ReebArc *arc1, *arc2; + float nor[3], p[3]; - for(node = rg->nodes.first; node; node = node->next) - { - int count = 0; - for(arc = rg->arcs.first; arc; arc = arc->next) - { - if (arc->v1 == node || arc->v2 == node) - { - count++; - } - } - if (count != node->degree) - { - printf("degree error in node %i: expected %i got %i\n", node->index, count, node->degree); - } - } -} + arc1 = (ReebArc*)barc1; + arc2 = (ReebArc*)barc2; -void verifyBuckets(ReebGraph *rg) -{ -#ifdef DEBUG_REEB - ReebArc *arc = NULL; - for(arc = rg->arcs.first; arc; arc = arc->next) + VECCOPY(nor, root_node->symmetry_axis); + + /* mirror node2 along axis */ + VECCOPY(p, node2->p); + BLI_mirrorAlongAxis(p, root_node->p, nor); + + /* average with node1 */ + VecAddf(node1->p, node1->p, p); + VecMulf(node1->p, 0.5f); + + /* mirror back on node2 */ + VECCOPY(node2->p, node1->p); + BLI_mirrorAlongAxis(node2->p, root_node->p, nor); + + /* Merge buckets + * there shouldn't be any null arcs here, but just to be safe + * */ + if (arc1->bcount > 0 && arc2->bcount > 0) { - if (arc->bcount > 0) + ReebArcIterator iter1, iter2; + EmbedBucket *bucket1 = NULL, *bucket2 = NULL; + + initArcIterator(&iter1, arc1, (ReebNode*)root_node); + initArcIterator(&iter2, arc2, (ReebNode*)root_node); + + bucket1 = nextBucket(&iter1); + bucket2 = nextBucket(&iter2); + + /* Make sure they both start at the same value */ + while(bucket1 && bucket1->val < bucket2->val) { - int i; - for(i = 0; i < arc->bcount; i++) - { - if (arc->buckets[i].nv == 0) - { - printArc(arc); - printf("count error in bucket %i/%i\n", i+1, arc->bcount); - } - } + bucket1 = nextBucket(&iter1); + } + + while(bucket2 && bucket2->val < bucket1->val) + { + bucket2 = nextBucket(&iter2); + } + + + for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2)) + { + bucket1->nv += bucket2->nv; /* add counts */ - if (ceil(arc->v1->weight) < arc->buckets[0].val) - { - printArc(arc); - printf("alloc error in first bucket: %f should be %f \n", arc->buckets[0].val, ceil(arc->v1->weight)); - } - if (floor(arc->v2->weight) < arc->buckets[arc->bcount - 1].val) - { - printArc(arc); - printf("alloc error in last bucket: %f should be %f \n", arc->buckets[arc->bcount - 1].val, floor(arc->v2->weight)); - } + /* mirror on axis */ + BLI_mirrorAlongAxis(bucket2->p, root_node->p, nor); + /* add bucket2 in bucket1 */ + VecLerpf(bucket1->p, bucket1->p, bucket2->p, (float)bucket2->nv / (float)(bucket1->nv)); + + /* copy and mirror back to bucket2 */ + bucket2->nv = bucket1->nv; + VECCOPY(bucket2->p, bucket1->p); + BLI_mirrorAlongAxis(bucket2->p, root_node->p, nor); } } -#endif } /************************************** ADJACENCY LIST *************************************************/ -void addArcToNodeAdjacencyList(ReebNode *node, ReebArc *arc) -{ - ReebArc **arclist; - - for(arclist = node->arcs; *arclist; arclist++) - { } - - *arclist = arc; -} - -void buildAdjacencyList(ReebGraph *rg) -{ - ReebNode *node = NULL; - ReebArc *arc = NULL; - - for(node = rg->nodes.first; node; node = node->next) - { - if (node->arcs != NULL) - { - MEM_freeN(node->arcs); - } - - node->arcs = MEM_callocN((node->degree + 1) * sizeof(ReebArc*), "adjacency list"); - } - - for(arc = rg->arcs.first; arc; arc= arc->next) - { - addArcToNodeAdjacencyList(arc->v1, arc); - addArcToNodeAdjacencyList(arc->v2, arc); - } -} - -int hasAdjacencyList(ReebGraph *rg) -{ - ReebNode *node; - - for(node = rg->nodes.first; node; node = node->next) - { - if (node->arcs == NULL) - { - return 0; - } - } - - return 1; -} - -int countConnectedArcs(ReebGraph *rg, ReebNode *node) -{ - int count = 0; - - /* use adjacency list if present */ - if (node->arcs) - { - ReebArc **arcs; - - for(arcs = node->arcs; *arcs; arcs++) - { - count++; - } - } - else - { - ReebArc *arc; - for(arc = rg->arcs.first; arc; arc = arc->next) - { - if (arc->v1 == node || arc->v2 == node) - { - count++; - } - } - } - - return count; -} /****************************************** SMOOTHING **************************************************/ @@ -492,12 +1171,14 @@ int compareArcsWeight(void *varc1, void *varc2) { ReebArc *arc1 = (ReebArc*)varc1; ReebArc *arc2 = (ReebArc*)varc2; + ReebNode *node1 = (ReebNode*)arc1->head; + ReebNode *node2 = (ReebNode*)arc2->head; - if (arc1->v1->weight < arc2->v1->weight) + if (node1->weight < node2->weight) { return -1; } - if (arc1->v1->weight > arc2->v1->weight) + if (node1->weight > node2->weight) { return 1; } @@ -511,15 +1192,235 @@ void sortArcs(ReebGraph *rg) { BLI_sortlist(&rg->arcs, compareArcsWeight); } +/******************************************* JOINING ***************************************************/ + +void reweightArc(ReebGraph *rg, ReebArc *arc, ReebNode *start_node, float start_weight) +{ + ReebNode *node; + float old_weight; + float end_weight = start_weight + ABS(arc->tail->weight - arc->head->weight); + int i; + + node = (ReebNode*)BLI_otherNode((BArc*)arc, (BNode*)start_node); + + /* prevent backtracking */ + if (node->flag == 1) + { + return; + } + + if (arc->tail == start_node) + { + flipArc(arc); + } + + start_node->flag = 1; + + for (i = 0; i < node->degree; i++) + { + ReebArc *next_arc = node->arcs[i]; + + reweightArc(rg, next_arc, node, end_weight); + } + + /* update only if needed */ + if (arc->head->weight != start_weight || arc->tail->weight != end_weight) + { + old_weight = arc->head->weight; /* backup head weight, other arcs need it intact, it will be fixed by the source arc */ + + arc->head->weight = start_weight; + arc->tail->weight = end_weight; + + reweightBuckets(arc); + resizeArcBuckets(arc); + fillArcEmptyBuckets(arc); + + arc->head->weight = old_weight; + } +} + +void reweightSubgraph(ReebGraph *rg, ReebNode *start_node, float start_weight) +{ + int i; + + BLI_flagNodes((BGraph*)rg, 0); + + for (i = 0; i < start_node->degree; i++) + { + ReebArc *next_arc = start_node->arcs[i]; + + reweightArc(rg, next_arc, start_node, start_weight); + } + start_node->weight = start_weight; +} + +int joinSubgraphsEnds(ReebGraph *rg, float threshold, int nb_subgraphs) +{ + int joined = 0; + int subgraph; + + for (subgraph = 1; subgraph <= nb_subgraphs; subgraph++) + { + ReebNode *start_node, *end_node; + ReebNode *min_node_start, *min_node_end = NULL; + float min_distance = FLT_MAX; + + for (start_node = rg->nodes.first; start_node; start_node = start_node->next) + { + if (start_node->subgraph_index == subgraph && start_node->degree == 1) + { + + for (end_node = rg->nodes.first; end_node; end_node = end_node->next) + { + if (end_node->subgraph_index != subgraph) + { + float distance = VecLenf(start_node->p, end_node->p); + + if (distance < threshold && distance < min_distance) + { + min_distance = distance; + min_node_end = end_node; + min_node_start = start_node; + } + } + } + } + } + + end_node = min_node_end; + start_node = min_node_start; + + if (end_node && start_node) + { + ReebArc *start_arc, *end_arc; + int merging = 0; + + start_arc = start_node->arcs[0]; + end_arc = end_node->arcs[0]; + + if (start_arc->tail == start_node) + { + reweightSubgraph(rg, end_node, start_node->weight); + + start_arc->tail = end_node; + + merging = 1; + } + else if (start_arc->head == start_node) + { + reweightSubgraph(rg, start_node, end_node->weight); + + start_arc->head = end_node; + + merging = 2; + } + + if (merging) + { + BLI_ReflagSubgraph((BGraph*)rg, end_node->flag, subgraph); + + resizeArcBuckets(start_arc); + fillArcEmptyBuckets(start_arc); + + NodeDegreeIncrement(rg, end_node); + BLI_rebuildAdjacencyListForNode((BGraph*)rg, (BNode*)end_node); + + BLI_removeNode((BGraph*)rg, (BNode*)start_node); + } + + joined = 1; + } + } + + return joined; +} + +/* Reweight graph from smallest node, fix fliped arcs */ +void fixSubgraphsOrientation(ReebGraph *rg, int nb_subgraphs) +{ + int subgraph; + + for (subgraph = 1; subgraph <= nb_subgraphs; subgraph++) + { + ReebNode *node; + ReebNode *start_node = NULL; + + for (node = rg->nodes.first; node; node = node->next) + { + if (node->subgraph_index == subgraph) + { + if (start_node == NULL || node->weight < start_node->weight) + { + start_node = node; + } + } + } + + if (start_node) + { + reweightSubgraph(rg, start_node, start_node->weight); + } + } +} + +int joinSubgraphs(ReebGraph *rg, float threshold) +{ + int nb_subgraphs; + int joined = 0; + + BLI_buildAdjacencyList((BGraph*)rg); + + if (BLI_isGraphCyclic((BGraph*)rg)) + { + /* don't deal with cyclic graphs YET */ + return 0; + } + + /* sort nodes before flagging subgraphs to make sure root node is subgraph 0 */ + sortNodes(rg); + + nb_subgraphs = BLI_FlagSubgraphs((BGraph*)rg); + + /* Harmonic function can create flipped arcs, take the occasion to fix them */ + if (G.scene->toolsettings->skgen_options & SKGEN_HARMONIC) + { + fixSubgraphsOrientation(rg, nb_subgraphs); + } + + if (nb_subgraphs > 1) + { + joined |= joinSubgraphsEnds(rg, threshold, nb_subgraphs); + + if (joined) + { + removeNormalNodes(rg); + BLI_buildAdjacencyList((BGraph*)rg); + } + } + + return joined; +} /****************************************** FILTERING **************************************************/ +float lengthArc(ReebArc *arc) +{ +#if 0 + ReebNode *head = (ReebNode*)arc->head; + ReebNode *tail = (ReebNode*)arc->tail; + + return tail->weight - head->weight; +#else + return arc->length; +#endif +} + int compareArcs(void *varc1, void *varc2) { ReebArc *arc1 = (ReebArc*)varc1; ReebArc *arc2 = (ReebArc*)varc2; - float len1 = arc1->v2->weight - arc1->v1->weight; - float len2 = arc2->v2->weight - arc2->v1->weight; + float len1 = lengthArc(arc1); + float len2 = lengthArc(arc2); if (len1 < len2) { @@ -539,12 +1440,17 @@ void filterArc(ReebGraph *rg, ReebNode *newNode, ReebNode *removedNode, ReebArc { ReebArc *arc = NULL, *nextArc = NULL; - /* first pass, merge buckets for arcs that spawned the two nodes into the source arc*/ - for(arc = rg->arcs.first; arc; arc = arc->next) + if (merging) { - if (arc->v1 == srcArc->v1 && arc->v2 == srcArc->v2 && arc != srcArc) + /* first pass, merge buckets for arcs that spawned the two nodes into the source arc*/ + for(arc = rg->arcs.first; arc; arc = arc->next) { - mergeArcBuckets(srcArc, arc, srcArc->v1->weight, srcArc->v2->weight); + if (arc->head == srcArc->head && arc->tail == srcArc->tail && arc != srcArc) + { + ReebNode *head = srcArc->head; + ReebNode *tail = srcArc->tail; + mergeArcBuckets(srcArc, arc, head->weight, tail->weight); + } } } @@ -554,48 +1460,52 @@ void filterArc(ReebGraph *rg, ReebNode *newNode, ReebNode *removedNode, ReebArc { nextArc = arc->next; - if (arc->v1 == removedNode || arc->v2 == removedNode) + if (arc->head == removedNode || arc->tail == removedNode) { - if (arc->v1 == removedNode) + if (arc->head == removedNode) { - arc->v1 = newNode; + arc->head = newNode; } else { - arc->v2 = newNode; + arc->tail = newNode; } // Remove looped arcs - if (arc->v1 == arc->v2) + if (arc->head == arc->tail) { // v1 or v2 was already newNode, since we're removing an arc, decrement degree - newNode->degree--; + NodeDegreeDecrement(rg, newNode); - // If it's safeArc, it'll be removed later, so keep it for now + // If it's srcArc, it'll be removed later, so keep it for now if (arc != srcArc) { BLI_remlink(&rg->arcs, arc); - freeArc(arc); + REEB_freeArc((BArc*)arc); } } - // Remove flipped arcs - else if (arc->v1->weight > arc->v2->weight) - { - // Decrement degree from the other node - OTHER_NODE(arc, newNode)->degree--; - - BLI_remlink(&rg->arcs, arc); - freeArc(arc); - } else { - newNode->degree++; // incrementing degree since we're adding an arc + /* flip arcs that flipped, can happen on diamond shapes, mostly on null arcs */ + if (arc->head->weight > arc->tail->weight) + { + flipArc(arc); + } + //newNode->degree++; // incrementing degree since we're adding an arc + NodeDegreeIncrement(rg, newNode); + mergeArcFaces(rg, arc, srcArc); if (merging) { + ReebNode *head = arc->head; + ReebNode *tail = arc->tail; + // resize bucket list resizeArcBuckets(arc); - mergeArcBuckets(arc, srcArc, arc->v1->weight, arc->v2->weight); + mergeArcBuckets(arc, srcArc, head->weight, tail->weight); + + /* update length */ + arc->length += srcArc->length; } } } @@ -615,14 +1525,13 @@ void filterNullReebGraph(ReebGraph *rg) // Only collapse arcs too short to have any embed bucket if (arc->bcount == 0) { - ReebNode *newNode = arc->v1; - ReebNode *removedNode = arc->v2; + ReebNode *newNode = (ReebNode*)arc->head; + ReebNode *removedNode = (ReebNode*)arc->tail; float blend; blend = (float)newNode->degree / (float)(newNode->degree + removedNode->degree); // blending factors - //newNode->weight = FloatLerpf(newNode->weight, removedNode->weight, blend); - VecLerpf(newNode->p, newNode->p, removedNode->p, blend); + VecLerpf(newNode->p, removedNode->p, newNode->p, blend); filterArc(rg, newNode, removedNode, arc, 0); @@ -630,136 +1539,363 @@ void filterNullReebGraph(ReebGraph *rg) nextArc = arc->next; BLI_remlink(&rg->arcs, arc); - freeArc(arc); + REEB_freeArc((BArc*)arc); - BLI_freelinkN(&rg->nodes, removedNode); + BLI_removeNode((BGraph*)rg, (BNode*)removedNode); } arc = nextArc; } } -int filterInternalReebGraph(ReebGraph *rg, float threshold) +int filterInternalExternalReebGraph(ReebGraph *rg, float threshold_internal, float threshold_external) { ReebArc *arc = NULL, *nextArc = NULL; int value = 0; BLI_sortlist(&rg->arcs, compareArcs); - - arc = rg->arcs.first; - while(arc) + + for (arc = rg->arcs.first; arc; arc = nextArc) { nextArc = arc->next; // Only collapse non-terminal arcs that are shorter than threshold - if ((arc->v1->degree > 1 && arc->v2->degree > 1 && arc->v2->weight - arc->v1->weight < threshold)) + if (threshold_internal > 0 && arc->head->degree > 1 && arc->tail->degree > 1 && (lengthArc(arc) < threshold_internal)) { ReebNode *newNode = NULL; ReebNode *removedNode = NULL; - /* Keep the node with the highestn number of connected arcs */ - if (arc->v1->degree >= arc->v2->degree) - { - newNode = arc->v1; - removedNode = arc->v2; - } - else - { - newNode = arc->v2; - removedNode = arc->v1; - } - + /* Always remove lower node, so arcs don't flip */ + newNode = arc->head; + removedNode = arc->tail; + filterArc(rg, newNode, removedNode, arc, 1); // Reset nextArc, it might have changed nextArc = arc->next; BLI_remlink(&rg->arcs, arc); - freeArc(arc); + REEB_freeArc((BArc*)arc); - BLI_freelinkN(&rg->nodes, removedNode); + BLI_removeNode((BGraph*)rg, (BNode*)removedNode); value = 1; } + // Only collapse terminal arcs that are shorter than threshold + else if (threshold_external > 0 && (arc->head->degree == 1 || arc->tail->degree == 1) && (lengthArc(arc) < threshold_external)) + { + ReebNode *terminalNode = NULL; + ReebNode *middleNode = NULL; + ReebNode *removedNode = NULL; + + // Assign terminal and middle nodes + if (arc->head->degree == 1) + { + terminalNode = arc->head; + middleNode = arc->tail; + } + else + { + terminalNode = arc->tail; + middleNode = arc->head; + } + + if (middleNode->degree == 2) + { +#if 1 + // If middle node is a normal node, it will be removed later + /* USE THIS IF YOU WANT TO PROLONG ARCS TO THEIR TERMINAL NODES + * FOR HANDS, THIS IS NOT THE BEST RESULT + * */ + continue; +#else + removedNode = terminalNode; + + // removing arc, so we need to decrease the degree of the remaining node + NodeDegreeDecrement(rg, middleNode); +#endif + } + // Otherwise, just plain remove of the arc + else + { + removedNode = terminalNode; + + // removing arc, so we need to decrease the degree of the remaining node + NodeDegreeDecrement(rg, middleNode); + } + + // Reset nextArc, it might have changed + nextArc = arc->next; + + BLI_remlink(&rg->arcs, arc); + REEB_freeArc((BArc*)arc); + + BLI_removeNode((BGraph*)rg, (BNode*)removedNode); + value = 1; + } + } + + return value; +} + +int filterCyclesReebGraph(ReebGraph *rg, float distance_threshold) +{ + ReebArc *arc1, *arc2; + ReebArc *next2; + int filtered = 0; + + for (arc1 = rg->arcs.first; arc1; arc1 = arc1->next) + { + for (arc2 = arc1->next; arc2; arc2 = next2) + { + next2 = arc2->next; + if (arc1 != arc2 && arc1->head == arc2->head && arc1->tail == arc2->tail) + { + mergeArcEdges(rg, arc1, arc2, MERGE_APPEND); + mergeArcFaces(rg, arc1, arc2); + mergeArcBuckets(arc1, arc2, arc1->head->weight, arc1->tail->weight); + + NodeDegreeDecrement(rg, arc1->head); + NodeDegreeDecrement(rg, arc1->tail); + + BLI_remlink(&rg->arcs, arc2); + REEB_freeArc((BArc*)arc2); + + filtered = 1; + } + } + } + + return filtered; +} + +int filterSmartReebGraph(ReebGraph *rg, float threshold) +{ + ReebArc *arc = NULL, *nextArc = NULL; + int value = 0; + + BLI_sortlist(&rg->arcs, compareArcs); + +#ifdef DEBUG_REEB + { + EditFace *efa; + for(efa=G.editMesh->faces.first; efa; efa=efa->next) { + efa->tmp.fp = -1; + } + } +#endif + + arc = rg->arcs.first; + while(arc) + { + nextArc = arc->next; + + /* need correct normals and center */ + recalc_editnormals(); + + // Only test terminal arcs + if (arc->head->degree == 1 || arc->tail->degree == 1) + { + GHashIterator ghi; + int merging = 0; + int total = BLI_ghash_size(arc->faces); + float avg_angle = 0; + float avg_vec[3] = {0,0,0}; + + for(BLI_ghashIterator_init(&ghi, arc->faces); + !BLI_ghashIterator_isDone(&ghi); + BLI_ghashIterator_step(&ghi)) + { + EditFace *efa = BLI_ghashIterator_getValue(&ghi); + +#if 0 + ReebArcIterator iter; + EmbedBucket *bucket = NULL; + EmbedBucket *previous = NULL; + float min_distance = -1; + float angle = 0; + + initArcIterator(&iter, arc, arc->head); + + bucket = nextBucket(&iter); + + while (bucket != NULL) + { + float *vec0 = NULL; + float *vec1 = bucket->p; + float midpoint[3], tangent[3]; + float distance; + + /* first bucket. Previous is head */ + if (previous == NULL) + { + vec0 = arc->head->p; + } + /* Previous is a valid bucket */ + else + { + vec0 = previous->p; + } + + VECCOPY(midpoint, vec1); + + distance = VecLenf(midpoint, efa->cent); + + if (min_distance == -1 || distance < min_distance) + { + min_distance = distance; + + VecSubf(tangent, vec1, vec0); + Normalize(tangent); + + angle = Inpf(tangent, efa->n); + } + + previous = bucket; + bucket = nextBucket(&iter); + } + + avg_angle += saacos(fabs(angle)); +#ifdef DEBUG_REEB + efa->tmp.fp = saacos(fabs(angle)); +#endif +#else + VecAddf(avg_vec, avg_vec, efa->n); +#endif + } + + +#if 0 + avg_angle /= total; +#else + VecMulf(avg_vec, 1.0 / total); + avg_angle = Inpf(avg_vec, avg_vec); +#endif + + arc->angle = avg_angle; + + if (avg_angle > threshold) + merging = 1; + + if (merging) + { + ReebNode *terminalNode = NULL; + ReebNode *middleNode = NULL; + ReebNode *newNode = NULL; + ReebNode *removedNode = NULL; + int merging = 0; + + // Assign terminal and middle nodes + if (arc->head->degree == 1) + { + terminalNode = arc->head; + middleNode = arc->tail; + } + else + { + terminalNode = arc->tail; + middleNode = arc->head; + } + + // If middle node is a normal node, merge to terminal node + if (middleNode->degree == 2) + { + merging = 1; + newNode = terminalNode; + removedNode = middleNode; + } + // Otherwise, just plain remove of the arc + else + { + merging = 0; + newNode = middleNode; + removedNode = terminalNode; + } + + // Merging arc + if (merging) + { + filterArc(rg, newNode, removedNode, arc, 1); + } + else + { + // removing arc, so we need to decrease the degree of the remaining node + //newNode->degree--; + NodeDegreeDecrement(rg, newNode); + } + + // Reset nextArc, it might have changed + nextArc = arc->next; + + BLI_remlink(&rg->arcs, arc); + REEB_freeArc((BArc*)arc); + + BLI_freelinkN(&rg->nodes, removedNode); + value = 1; + } + } + arc = nextArc; } return value; } -int filterExternalReebGraph(ReebGraph *rg, float threshold) +void filterGraph(ReebGraph *rg, short options, float threshold_internal, float threshold_external) { - ReebArc *arc = NULL, *nextArc = NULL; - int value = 0; + int done = 1; - BLI_sortlist(&rg->arcs, compareArcs); + calculateGraphLength(rg); - arc = rg->arcs.first; - while(arc) + if ((options & SKGEN_FILTER_EXTERNAL) == 0) { - nextArc = arc->next; + threshold_external = 0; + } - // Only collapse terminal arcs that are shorter than threshold - if ((arc->v1->degree == 1 || arc->v2->degree == 1) && arc->v2->weight - arc->v1->weight < threshold) + if ((options & SKGEN_FILTER_INTERNAL) == 0) + { + threshold_internal = 0; + } + + if (threshold_internal > 0 || threshold_external > 0) + { + /* filter until there's nothing more to do */ + while (done == 1) { - ReebNode *terminalNode = NULL; - ReebNode *middleNode = NULL; - ReebNode *newNode = NULL; - ReebNode *removedNode = NULL; - int merging = 0; + done = 0; /* no work done yet */ - // Assign terminal and middle nodes - if (arc->v1->degree == 1) - { - terminalNode = arc->v1; - middleNode = arc->v2; - } - else - { - terminalNode = arc->v2; - middleNode = arc->v1; - } - - // If middle node is a normal node, merge to terminal node - if (middleNode->degree == 2) - { - merging = 1; - newNode = terminalNode; - removedNode = middleNode; - } - // Otherwise, just plain remove of the arc - else - { - merging = 0; - newNode = middleNode; - removedNode = terminalNode; - } - - // Merging arc - if (merging) - { - filterArc(rg, newNode, removedNode, arc, 1); - } - else - { - // removing arc, so we need to decrease the degree of the remaining node - newNode->degree--; - } - - // Reset nextArc, it might have changed - nextArc = arc->next; - - BLI_remlink(&rg->arcs, arc); - freeArc(arc); - - BLI_freelinkN(&rg->nodes, removedNode); - value = 1; + done = filterInternalExternalReebGraph(rg, threshold_internal, threshold_external); } - - arc = nextArc; + } + + if (options & SKGEN_FILTER_SMART) + { + filterSmartReebGraph(rg, 0.5); + filterCyclesReebGraph(rg, 0.5); + } + + repositionNodes(rg); + + /* Filtering might have created degree 2 nodes, so remove them */ + removeNormalNodes(rg); +} + +void finalizeGraph(ReebGraph *rg, char passes, char method) +{ + int i; + + BLI_buildAdjacencyList((BGraph*)rg); + + sortNodes(rg); + + sortArcs(rg); + + for(i = 0; i < passes; i++) + { + postprocessGraph(rg, method); } - return value; + extendGraphBuckets(rg); } /************************************** WEIGHT SPREADING ***********************************************/ @@ -821,94 +1957,6 @@ void spreadWeight(EditMesh *em) MEM_freeN(verts); } -/*********************************** GRAPH AS TREE FUNCTIONS *******************************************/ - -int subtreeDepth(ReebNode *node, ReebArc *rootArc) -{ - int depth = 0; - - /* Base case, no arcs leading away */ - if (node->arcs == NULL || *(node->arcs) == NULL) - { - return 0; - } - else - { - ReebArc ** pArc; - - for(pArc = node->arcs; *pArc; pArc++) - { - ReebArc *arc = *pArc; - - /* only arcs that go down the tree */ - if (arc != rootArc) - { - ReebNode *newNode = OTHER_NODE(arc, node); - depth = MAX2(depth, subtreeDepth(newNode, arc)); - } - } - } - - return depth + 1; -} - -/*************************************** CYCLE DETECTION ***********************************************/ - -int detectCycle(ReebNode *node, ReebArc *srcArc) -{ - int value = 0; - - if (node->flags == 0) - { - ReebArc ** pArc; - - /* mark node as visited */ - node->flags = 1; - - for(pArc = node->arcs; *pArc && value == 0; pArc++) - { - ReebArc *arc = *pArc; - - /* don't go back on the source arc */ - if (arc != srcArc) - { - value = detectCycle(OTHER_NODE(arc, node), arc); - } - } - } - else - { - value = 1; - } - - return value; -} - -int isGraphCyclic(ReebGraph *rg) -{ - ReebNode *node; - int value = 0; - - /* NEED TO CHECK IF ADJACENCY LIST EXIST */ - - /* Mark all nodes as not visited */ - for(node = rg->nodes.first; node; node = node->next) - { - node->flags = 0; - } - - /* detectCycles in subgraphs */ - for(node = rg->nodes.first; node && value == 0; node = node->next) - { - /* only for nodes in subgraphs that haven't been visited yet */ - if (node->flags == 0) - { - value = value || detectCycle(node, NULL); - } - } - - return value; -} /******************************************** EXPORT ***************************************************/ @@ -917,9 +1965,8 @@ void exportNode(FILE *f, char *text, ReebNode *node) fprintf(f, "%s i:%i w:%f d:%i %f %f %f\n", text, node->index, node->weight, node->degree, node->p[0], node->p[1], node->p[2]); } -void exportGraph(ReebGraph *rg, int count) +void REEB_exportGraph(ReebGraph *rg, int count) { -#ifdef DEBUG_REEB ReebArc *arc; char filename[128]; FILE *f; @@ -937,78 +1984,107 @@ void exportGraph(ReebGraph *rg, int count) for(arc = rg->arcs.first; arc; arc = arc->next) { int i; + float p[3]; - exportNode(f, "v1", arc->v1); + exportNode(f, "v1", arc->head); for(i = 0; i < arc->bcount; i++) { fprintf(f, "b nv:%i %f %f %f\n", arc->buckets[i].nv, arc->buckets[i].p[0], arc->buckets[i].p[1], arc->buckets[i].p[2]); } - exportNode(f, "v2", arc->v2); + VecAddf(p, arc->tail->p, arc->head->p); + VecMulf(p, 0.5f); + + fprintf(f, "angle %0.3f %0.3f %0.3f %0.3f %i\n", p[0], p[1], p[2], arc->angle, BLI_ghash_size(arc->faces)); + exportNode(f, "v2", arc->tail); } fclose(f); -#endif } /***************************************** MAIN ALGORITHM **********************************************/ -ReebArc * findConnectedArc(ReebGraph *rg, ReebArc *arc, ReebNode *v) +/* edges alone will create zero degree nodes, use this function to remove them */ +void removeZeroNodes(ReebGraph *rg) { - ReebArc *nextArc = arc->next; + ReebNode *node, *next_node; - for(nextArc = rg->arcs.first; nextArc; nextArc = nextArc->next) + for (node = rg->nodes.first; node; node = next_node) { - if (arc != nextArc && (nextArc->v1 == v || nextArc->v2 == v)) + next_node = node->next; + + if (node->degree == 0) { - break; + BLI_removeNode((BGraph*)rg, (BNode*)node); } } - - return nextArc; } void removeNormalNodes(ReebGraph *rg) { - ReebArc *arc; + ReebArc *arc, *nextArc; // Merge degree 2 nodes - for(arc = rg->arcs.first; arc; arc = arc->next) + for(arc = rg->arcs.first; arc; arc = nextArc) { - while (arc->v1->degree == 2 || arc->v2->degree == 2) + nextArc = arc->next; + + while (arc->head->degree == 2 || arc->tail->degree == 2) { // merge at v1 - if (arc->v1->degree == 2) + if (arc->head->degree == 2) { - ReebArc *nextArc = findConnectedArc(rg, arc, arc->v1); + ReebArc *connectedArc = (ReebArc*)BLI_findConnectedArc((BGraph*)rg, (BArc*)arc, (BNode*)arc->head); - // Merge arc only if needed - if (arc->v1 == nextArc->v2) - { - mergeConnectedArcs(rg, arc, nextArc); + /* If arcs are one after the other */ + if (arc->head == connectedArc->tail) + { + /* remove furthest arc */ + if (arc->tail->weight < connectedArc->head->weight) + { + mergeConnectedArcs(rg, arc, connectedArc); + nextArc = arc->next; + } + else + { + mergeConnectedArcs(rg, connectedArc, arc); + break; /* arc was removed, move to next */ + } } - // Otherwise, mark down vert + /* Otherwise, arcs are side by side */ else { - arc->v1->degree = 3; + /* Don't do anything, we need to keep the lowest node, even if degree 2 */ + break; } } // merge at v2 - if (arc->v2->degree == 2) + if (arc->tail->degree == 2) { - ReebArc *nextArc = findConnectedArc(rg, arc, arc->v2); + ReebArc *connectedArc = (ReebArc*)BLI_findConnectedArc((BGraph*)rg, (BArc*)arc, (BNode*)arc->tail); - // Merge arc only if needed - if (arc->v2 == nextArc->v1) + /* If arcs are one after the other */ + if (arc->tail == connectedArc->head) { - mergeConnectedArcs(rg, arc, nextArc); + /* remove furthest arc */ + if (arc->head->weight < connectedArc->tail->weight) + { + mergeConnectedArcs(rg, arc, connectedArc); + nextArc = arc->next; + } + else + { + mergeConnectedArcs(rg, connectedArc, arc); + break; /* arc was removed, move to next */ + } } - // Otherwise, mark down vert + /* Otherwise, arcs are side by side */ else { - arc->v2->degree = 3; + /* Don't do anything, we need to keep the lowest node, even if degree 2 */ + break; } } } @@ -1041,11 +2117,23 @@ ReebArc *nextArcMappedToEdge(ReebArc *arc, ReebEdge *e) return result; } -typedef enum { - MERGE_LOWER, - MERGE_HIGHER, - MERGE_APPEND -} MergeDirection; +void addFacetoArc(ReebArc *arc, EditFace *efa) +{ + BLI_ghash_insert(arc->faces, efa, efa); +} + +void mergeArcFaces(ReebGraph *rg, ReebArc *aDst, ReebArc *aSrc) +{ + GHashIterator ghi; + + for(BLI_ghashIterator_init(&ghi, aSrc->faces); + !BLI_ghashIterator_isDone(&ghi); + BLI_ghashIterator_step(&ghi)) + { + EditFace *efa = BLI_ghashIterator_getValue(&ghi); + BLI_ghash_insert(aDst->faces, efa, efa); + } +} void mergeArcEdges(ReebGraph *rg, ReebArc *aDst, ReebArc *aSrc, MergeDirection direction) { @@ -1109,29 +2197,32 @@ int mergeConnectedArcs(ReebGraph *rg, ReebArc *a0, ReebArc *a1) int result = 0; ReebNode *removedNode = NULL; + a0->length += a1->length; + mergeArcEdges(rg, a0, a1, MERGE_APPEND); + mergeArcFaces(rg, a0, a1); // Bring a0 to the combine length of both arcs - if (a0->v2 == a1->v1) + if (a0->tail == a1->head) { - removedNode = a0->v2; - a0->v2 = a1->v2; + removedNode = a0->tail; + a0->tail = a1->tail; } - else if (a0->v1 == a1->v2) + else if (a0->head == a1->tail) { - removedNode = a0->v1; - a0->v1 = a1->v1; + removedNode = a0->head; + a0->head = a1->head; } resizeArcBuckets(a0); // Merge a1 in a0 - mergeArcBuckets(a0, a1, a0->v1->weight, a0->v2->weight); + mergeArcBuckets(a0, a1, a0->head->weight, a0->tail->weight); // remove a1 from graph BLI_remlink(&rg->arcs, a1); - freeArc(a1); + REEB_freeArc((BArc*)a1); - BLI_freelinkN(&rg->nodes, removedNode); + BLI_removeNode((BGraph*)rg, (BNode*)removedNode); result = 1; return result; @@ -1141,74 +2232,89 @@ int mergeArcs(ReebGraph *rg, ReebArc *a0, ReebArc *a1) { int result = 0; // TRIANGLE POINTS DOWN - if (a0->v1->weight == a1->v1->weight) // heads are the same + if (a0->head->weight == a1->head->weight) // heads are the same { - if (a0->v2->weight == a1->v2->weight) // tails also the same, arcs can be totally merge together + if (a0->tail->weight == a1->tail->weight) // tails also the same, arcs can be totally merge together { mergeArcEdges(rg, a0, a1, MERGE_APPEND); + mergeArcFaces(rg, a0, a1); - mergeArcBuckets(a0, a1, a0->v1->weight, a0->v2->weight); + mergeArcBuckets(a0, a1, a0->head->weight, a0->tail->weight); // Adjust node degree - a1->v1->degree--; - a1->v2->degree--; + //a1->head->degree--; + NodeDegreeDecrement(rg, a1->head); + //a1->tail->degree--; + NodeDegreeDecrement(rg, a1->tail); // remove a1 from graph BLI_remlink(&rg->arcs, a1); - freeArc(a1); + REEB_freeArc((BArc*)a1); result = 1; } - else if (a0->v2->weight > a1->v2->weight) // a1->v2->weight is in the middle + else if (a0->tail->weight > a1->tail->weight) // a1->tail->weight is in the middle { mergeArcEdges(rg, a1, a0, MERGE_LOWER); + mergeArcFaces(rg, a1, a0); // Adjust node degree - a0->v1->degree--; - a1->v2->degree++; + //a0->head->degree--; + NodeDegreeDecrement(rg, a0->head); + //a1->tail->degree++; + NodeDegreeIncrement(rg, a1->tail); - mergeArcBuckets(a1, a0, a1->v1->weight, a1->v2->weight); - a0->v1 = a1->v2; + mergeArcBuckets(a1, a0, a1->head->weight, a1->tail->weight); + a0->head = a1->tail; resizeArcBuckets(a0); } else // a0>n2 is in the middle { mergeArcEdges(rg, a0, a1, MERGE_LOWER); + mergeArcFaces(rg, a0, a1); // Adjust node degree - a1->v1->degree--; - a0->v2->degree++; + //a1->head->degree--; + NodeDegreeDecrement(rg, a1->head); + //a0->tail->degree++; + NodeDegreeIncrement(rg, a0->tail); - mergeArcBuckets(a0, a1, a0->v1->weight, a0->v2->weight); - a1->v1 = a0->v2; + mergeArcBuckets(a0, a1, a0->head->weight, a0->tail->weight); + a1->head = a0->tail; resizeArcBuckets(a1); } } // TRIANGLE POINTS UP - else if (a0->v2->weight == a1->v2->weight) // tails are the same + else if (a0->tail->weight == a1->tail->weight) // tails are the same { - if (a0->v1->weight > a1->v1->weight) // a0->v1->weight is in the middle + if (a0->head->weight > a1->head->weight) // a0->head->weight is in the middle { mergeArcEdges(rg, a0, a1, MERGE_HIGHER); + mergeArcFaces(rg, a0, a1); // Adjust node degree - a1->v2->degree--; - a0->v1->degree++; + //a1->tail->degree--; + NodeDegreeDecrement(rg, a1->tail); + //a0->head->degree++; + NodeDegreeIncrement(rg, a0->head); - mergeArcBuckets(a0, a1, a0->v1->weight, a0->v2->weight); - a1->v2 = a0->v1; + mergeArcBuckets(a0, a1, a0->head->weight, a0->tail->weight); + a1->tail = a0->head; resizeArcBuckets(a1); } - else // a1->v1->weight is in the middle + else // a1->head->weight is in the middle { mergeArcEdges(rg, a1, a0, MERGE_HIGHER); + mergeArcFaces(rg, a1, a0); // Adjust node degree - a0->v2->degree--; - a1->v1->degree++; + //a0->tail->degree--; + NodeDegreeDecrement(rg, a0->tail); + //a1->head->degree++; + NodeDegreeIncrement(rg, a1->head); - mergeArcBuckets(a1, a0, a1->v1->weight, a1->v2->weight); - a0->v2 = a1->v1; + mergeArcBuckets(a1, a0, a1->head->weight, a1->tail->weight); + a0->tail = a1->head; resizeArcBuckets(a0); } } @@ -1229,7 +2335,7 @@ void glueByMergeSort(ReebGraph *rg, ReebArc *a0, ReebArc *a1, ReebEdge *e0, Reeb if (total == 0) // if it wasn't a total merge, go forward { - if (a0->v2->weight < a1->v2->weight) + if (a0->tail->weight < a1->tail->weight) { a0 = nextArcMappedToEdge(a0, e0); } @@ -1252,25 +2358,6 @@ void mergePaths(ReebGraph *rg, ReebEdge *e0, ReebEdge *e1, ReebEdge *e2) glueByMergeSort(rg, a0, a2, e0, e2); } -ReebNode * addNode(ReebGraph *rg, EditVert *eve, float weight) -{ - ReebNode *node = NULL; - - node = MEM_callocN(sizeof(ReebNode), "reeb node"); - - node->flags = 0; // clear flags on init - node->arcs = NULL; - node->degree = 0; - node->weight = weight; - node->index = rg->totnodes; - VECCOPY(node->p, eve->co); - - BLI_addtail(&rg->nodes, node); - rg->totnodes++; - - return node; -} - ReebEdge * createArc(ReebGraph *rg, ReebNode *node1, ReebNode *node2) { ReebEdge *edge; @@ -1288,7 +2375,9 @@ ReebEdge * createArc(ReebGraph *rg, ReebNode *node1, ReebNode *node2) arc = MEM_callocN(sizeof(ReebArc), "reeb arc"); edge = MEM_callocN(sizeof(ReebEdge), "reeb edge"); - arc->flags = 0; // clear flags on init + arc->flag = 0; // clear flag on init + arc->symmetry_level = 0; + arc->faces = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); if (node1->weight <= node2->weight) { @@ -1301,12 +2390,14 @@ ReebEdge * createArc(ReebGraph *rg, ReebNode *node1, ReebNode *node2) v2 = node1; } - arc->v1 = v1; - arc->v2 = v2; + arc->head = v1; + arc->tail = v2; // increase node degree - v1->degree++; - v2->degree++; + //v1->degree++; + NodeDegreeIncrement(rg, v1); + //v2->degree++; + NodeDegreeIncrement(rg, v2); BLI_edgehash_insert(rg->emap, node1->index, node2->index, edge); @@ -1321,8 +2412,8 @@ ReebEdge * createArc(ReebGraph *rg, ReebNode *node1, ReebNode *node2) /* adding buckets for embedding */ allocArcBuckets(arc); - offset = arc->v1->weight; - len = arc->v2->weight - arc->v1->weight; + offset = arc->head->weight; + len = arc->tail->weight - arc->head->weight; #if 0 /* This is the actual embedding filling described in the paper @@ -1330,8 +2421,8 @@ ReebEdge * createArc(ReebGraph *rg, ReebNode *node1, ReebNode *node2) */ if (arc->bcount > 0) { - addVertToBucket(&(arc->buckets[0]), arc->v1->co); - addVertToBucket(&(arc->buckets[arc->bcount - 1]), arc->v2->co); + addVertToBucket(&(arc->buckets[0]), arc->head->co); + addVertToBucket(&(arc->buckets[arc->bcount - 1]), arc->tail->co); } #else for(i = 0; i < arc->bcount; i++) @@ -1349,7 +2440,7 @@ ReebEdge * createArc(ReebGraph *rg, ReebNode *node1, ReebNode *node2) return edge; } -void addTriangleToGraph(ReebGraph *rg, ReebNode * n1, ReebNode * n2, ReebNode * n3) +void addTriangleToGraph(ReebGraph *rg, ReebNode * n1, ReebNode * n2, ReebNode * n3, EditFace *efa) { ReebEdge *re1, *re2, *re3; ReebEdge *e1, *e2, *e3; @@ -1359,6 +2450,10 @@ void addTriangleToGraph(ReebGraph *rg, ReebNode * n1, ReebNode * n2, ReebNode * re2 = createArc(rg, n2, n3); re3 = createArc(rg, n3, n1); + addFacetoArc(re1->arc, efa); + addFacetoArc(re2->arc, efa); + addFacetoArc(re3->arc, efa); + len1 = (float)fabs(n1->weight - n2->weight); len2 = (float)fabs(n2->weight - n3->weight); len3 = (float)fabs(n3->weight - n1->weight); @@ -1412,10 +2507,9 @@ ReebGraph * generateReebGraph(EditMesh *em, int subdivisions) int countfaces = 0; #endif - rg = MEM_callocN(sizeof(ReebGraph), "reeb graph"); + rg = newReebGraph(); - rg->totnodes = 0; - rg->emap = BLI_edgehash_new(); + rg->resolution = subdivisions; totvert = BLI_countlist(&em->verts); totfaces = BLI_countlist(&em->faces); @@ -1425,14 +2519,18 @@ ReebGraph * generateReebGraph(EditMesh *em, int subdivisions) /* Spread weight to minimize errors */ spreadWeight(em); - renormalizeWeight(em, (float)subdivisions); + renormalizeWeight(em, (float)rg->resolution); /* Adding vertice */ - for(index = 0, eve = em->verts.first; eve; index++, eve = eve->next) + for(index = 0, eve = em->verts.first; eve; eve = eve->next) { - eve->hash = index; - eve->f2 = 0; - eve->tmp.p = addNode(rg, eve, eve->tmp.fp); + if (eve->h == 0) + { + eve->hash = index; + eve->f2 = 0; + eve->tmp.p = addNode(rg, eve, eve->tmp.fp); + index++; + } } /* Temporarely convert node list to dynamic list, for indexed access */ @@ -1441,32 +2539,37 @@ ReebGraph * generateReebGraph(EditMesh *em, int subdivisions) /* Adding face, edge per edge */ for(efa = em->faces.first; efa; efa = efa->next) { - ReebNode *n1, *n2, *n3; - - n1 = (ReebNode*)BLI_dlist_find_link(dlist, efa->v1->hash); - n2 = (ReebNode*)BLI_dlist_find_link(dlist, efa->v2->hash); - n3 = (ReebNode*)BLI_dlist_find_link(dlist, efa->v3->hash); - - addTriangleToGraph(rg, n1, n2, n3); - - if (efa->v4) + if (efa->h == 0) { - ReebNode *n4 = (ReebNode*)efa->v4->tmp.p; - addTriangleToGraph(rg, n1, n3, n4); - } - + ReebNode *n1, *n2, *n3; + + n1 = (ReebNode*)BLI_dlist_find_link(dlist, efa->v1->hash); + n2 = (ReebNode*)BLI_dlist_find_link(dlist, efa->v2->hash); + n3 = (ReebNode*)BLI_dlist_find_link(dlist, efa->v3->hash); + + addTriangleToGraph(rg, n1, n2, n3, efa); + + if (efa->v4) + { + ReebNode *n4 = (ReebNode*)efa->v4->tmp.p; + addTriangleToGraph(rg, n1, n3, n4, efa); + } #ifdef DEBUG_REEB - countfaces++; - if (countfaces % 100 == 0) - { - printf("face %i of %i\n", countfaces, totfaces); - } + countfaces++; + if (countfaces % 100 == 0) + { + printf("\rface %i of %i", countfaces, totfaces); + } #endif - - + } } + + printf("\n"); + BLI_listbase_from_dlist(dlist, &rg->nodes); + removeZeroNodes(rg); + removeNormalNodes(rg); return rg; @@ -1534,7 +2637,36 @@ static float cotan_weight(float *v1, float *v2, float *v3) return Inpf(a, b)/clen; } -int weightToHarmonic(EditMesh *em) +void addTriangle(EditVert *v1, EditVert *v2, EditVert *v3, long e1, long e2, long e3) +{ + /* Angle opposite e1 */ + float t1= cotan_weight(v1->co, v2->co, v3->co) / e2; + + /* Angle opposite e2 */ + float t2 = cotan_weight(v2->co, v3->co, v1->co) / e3; + + /* Angle opposite e3 */ + float t3 = cotan_weight(v3->co, v1->co, v2->co) / e1; + + int i1 = v1->hash; + int i2 = v2->hash; + int i3 = v3->hash; + + nlMatrixAdd(i1, i1, t2+t3); + nlMatrixAdd(i2, i2, t1+t3); + nlMatrixAdd(i3, i3, t1+t2); + + nlMatrixAdd(i1, i2, -t3); + nlMatrixAdd(i2, i1, -t3); + + nlMatrixAdd(i2, i3, -t1); + nlMatrixAdd(i3, i2, -t1); + + nlMatrixAdd(i3, i1, -t2); + nlMatrixAdd(i1, i3, -t2); +} + +int weightToHarmonic(EditMesh *em, EdgeIndex *indexed_edges) { NLboolean success; EditVert *eve; @@ -1561,48 +2693,52 @@ int weightToHarmonic(EditMesh *em) /* Find local extrema */ for(index = 0, eve = em->verts.first; eve; index++, eve = eve->next) { - EditEdge *eed; - int maximum = 1; - int minimum = 1; - - eve->hash = index; /* Assign index to vertex */ - - NextEdgeForVert(NULL, NULL); /* Reset next edge */ - for(eed = NextEdgeForVert(em, eve); eed && (maximum || minimum); eed = NextEdgeForVert(em, eve)) + if (eve->h == 0) { - EditVert *eve2; + EditEdge *eed; + int maximum = 1; + int minimum = 1; - if (eed->v1 == eve) + NextEdgeForVert(indexed_edges, NULL); /* Reset next edge */ + for(eed = NextEdgeForVert(indexed_edges, eve); eed && (maximum || minimum); eed = NextEdgeForVert(indexed_edges, eve)) { - eve2 = eed->v2; + EditVert *eve2; + + if (eed->v1 == eve) + { + eve2 = eed->v2; + } + else + { + eve2 = eed->v1; + } + + if (eve2->h == 0) + { + /* Adjacent vertex is bigger, not a local maximum */ + if (eve2->tmp.fp > eve->tmp.fp) + { + maximum = 0; + } + /* Adjacent vertex is smaller, not a local minimum */ + else if (eve2->tmp.fp < eve->tmp.fp) + { + minimum = 0; + } + } + } + + if (maximum || minimum) + { + float w = eve->tmp.fp; + eve->f1 = 0; + nlSetVariable(0, index, w); + nlLockVariable(index); } else { - eve2 = eed->v1; + eve->f1 = 1; } - - /* Adjacent vertex is bigger, not a local maximum */ - if (eve2->tmp.fp > eve->tmp.fp) - { - maximum = 0; - } - /* Adjacent vertex is smaller, not a local minimum */ - else if (eve2->tmp.fp < eve->tmp.fp) - { - minimum = 0; - } - } - - if (maximum || minimum) - { - float w = eve->tmp.fp; - eve->f1 = 0; - nlSetVariable(0, index, w); - nlLockVariable(index); - } - else - { - eve->f1 = 1; } } @@ -1617,39 +2753,34 @@ int weightToHarmonic(EditMesh *em) /* Add faces count to the edge weight */ for(efa = em->faces.first; efa; efa = efa->next) { - efa->e1->tmp.l++; - efa->e2->tmp.l++; - efa->e3->tmp.l++; + if (efa->h == 0) + { + efa->e1->tmp.l++; + efa->e2->tmp.l++; + efa->e3->tmp.l++; + + if (efa->e4) + { + efa->e4->tmp.l++; + } + } } /* Add faces angle to the edge weight */ for(efa = em->faces.first; efa; efa = efa->next) { - /* Angle opposite e1 */ - float t1= cotan_weight(efa->v1->co, efa->v2->co, efa->v3->co) / efa->e2->tmp.l; - - /* Angle opposite e2 */ - float t2 = cotan_weight(efa->v2->co, efa->v3->co, efa->v1->co) / efa->e3->tmp.l; - - /* Angle opposite e3 */ - float t3 = cotan_weight(efa->v3->co, efa->v1->co, efa->v2->co) / efa->e1->tmp.l; - - int i1 = efa->v1->hash; - int i2 = efa->v2->hash; - int i3 = efa->v3->hash; - - nlMatrixAdd(i1, i1, t2+t3); - nlMatrixAdd(i2, i2, t1+t3); - nlMatrixAdd(i3, i3, t1+t2); - - nlMatrixAdd(i1, i2, -t3); - nlMatrixAdd(i2, i1, -t3); - - nlMatrixAdd(i2, i3, -t1); - nlMatrixAdd(i3, i2, -t1); - - nlMatrixAdd(i3, i1, -t2); - nlMatrixAdd(i1, i3, -t2); + if (efa->h == 0) + { + if (efa->v4 == NULL) + { + addTriangle(efa->v1, efa->v2, efa->v3, efa->e1->tmp.l, efa->e2->tmp.l, efa->e3->tmp.l); + } + else + { + addTriangle(efa->v1, efa->v2, efa->v3, efa->e1->tmp.l, efa->e2->tmp.l, 2); + addTriangle(efa->v3, efa->v4, efa->v1, efa->e3->tmp.l, efa->e4->tmp.l, 2); + } + } } nlEnd(NL_MATRIX); @@ -1677,40 +2808,168 @@ int weightToHarmonic(EditMesh *em) } -EditEdge * NextEdgeForVert(EditMesh *em, EditVert *v) +EditEdge * NextEdgeForVert(EdgeIndex *indexed_edges, EditVert *eve) { - static EditEdge *e = NULL; + static int offset = -1; /* Reset method, call with NULL mesh pointer */ - if (em == NULL) + if (eve == NULL) { - e = NULL; + offset = -1; return NULL; } /* first pass, start at the head of the list */ - if (e == NULL) + if (offset == -1) { - e = em->edges.first; + offset = indexed_edges->offset[eve->hash]; } /* subsequent passes, start on the next edge */ else { - e = e->next; + offset++; } - - for( ; e ; e = e->next) - { - if (e->v1 == v || e->v2 == v) - { - break; - } - } - return e; + return indexed_edges->edges[offset]; } -int weightFromDistance(EditMesh *em) +void shortestPathsFromVert(EditMesh *em, EditVert *starting_vert, EdgeIndex *indexed_edges) +{ + Heap *edge_heap; + EditVert *current_eve = NULL; + EditEdge *eed = NULL; + EditEdge *select_eed = NULL; + + edge_heap = BLI_heap_new(); + + current_eve = starting_vert; + + /* insert guard in heap, when that is returned, no more edges */ + BLI_heap_insert(edge_heap, FLT_MAX, NULL); + + /* Initialize edge flag */ + for(eed= em->edges.first; eed; eed= eed->next) + { + eed->f1 = 0; + } + + while (BLI_heap_size(edge_heap) > 0) + { + float current_weight; + + current_eve->f1 = 1; /* mark vertex as selected */ + + /* Add all new edges connected to current_eve to the list */ + NextEdgeForVert(indexed_edges, NULL); // Reset next edge + for(eed = NextEdgeForVert(indexed_edges, current_eve); eed; eed = NextEdgeForVert(indexed_edges, current_eve)) + { + if (eed->f1 == 0) + { + BLI_heap_insert(edge_heap, current_eve->tmp.fp + eed->tmp.fp, eed); + eed->f1 = 1; + } + } + + /* Find next shortest edge with unselected verts */ + do + { + current_weight = BLI_heap_node_value(BLI_heap_top(edge_heap)); + select_eed = BLI_heap_popmin(edge_heap); + } while (select_eed != NULL && select_eed->v1->f1 != 0 && select_eed->v2->f1); + + if (select_eed != NULL) + { + select_eed->f1 = 2; + + if (select_eed->v1->f1 == 0) /* v1 is the new vertex */ + { + current_eve = select_eed->v1; + } + else /* otherwise, it's v2 */ + { + current_eve = select_eed->v2; + } + current_eve->tmp.fp = current_weight; + } + } + + BLI_heap_free(edge_heap, NULL); +} + +void freeEdgeIndex(EdgeIndex *indexed_edges) +{ + MEM_freeN(indexed_edges->offset); + MEM_freeN(indexed_edges->edges); +} + +void buildIndexedEdges(EditMesh *em, EdgeIndex *indexed_edges) +{ + EditVert *eve; + EditEdge *eed; + int tot_vert = 0; + int tot_indexed = 0; + int offset = 0; + + for(tot_vert = 0, eve = em->verts.first; eve; tot_vert++, eve = eve->next) + { + eve->hash = tot_vert; + } + + indexed_edges->offset = MEM_callocN(tot_vert * sizeof(int), "EdgeIndex offset"); + + for(eed = em->edges.first; eed; eed= eed->next) + { + if (eed->v1->h == 0 && eed->v2->h == 0) + { + tot_indexed += 2; + indexed_edges->offset[eed->v1->hash]++; + indexed_edges->offset[eed->v2->hash]++; + } + } + + tot_indexed += tot_vert; + + indexed_edges->edges = MEM_callocN(tot_indexed * sizeof(EditEdge*), "EdgeIndex edges"); + + /* setting vert offsets */ + for(eve = em->verts.first; eve; eve = eve->next) + { + if (eve->h == 0) + { + int d = indexed_edges->offset[eve->hash]; + indexed_edges->offset[eve->hash] = offset; + offset += d + 1; + } + } + + /* adding edges in array */ + for(eed = em->edges.first; eed; eed= eed->next) + { + if (eed->v1->h == 0 && eed->v2->h == 0) + { + int i; + for (i = indexed_edges->offset[eed->v1->hash]; i < tot_indexed; i++) + { + if (indexed_edges->edges[i] == NULL) + { + indexed_edges->edges[i] = eed; + break; + } + } + + for (i = indexed_edges->offset[eed->v2->hash]; i < tot_indexed; i++) + { + if (indexed_edges->edges[i] == NULL) + { + indexed_edges->edges[i] = eed; + break; + } + } + } + } +} + +int weightFromDistance(EditMesh *em, EdgeIndex *indexed_edges) { EditVert *eve; int totedge = 0; @@ -1728,9 +2987,10 @@ int weightFromDistance(EditMesh *em) return 0; } - /* Initialize vertice flags and find at least one selected vertex */ - for(eve = em->verts.first; eve && vCount == 0; eve = eve->next) + /* Initialize vertice flag and find at least one selected vertex */ + for(eve = em->verts.first; eve; eve = eve->next) { + eve->tmp.fp = 0; eve->f1 = 0; if (eve->f & SELECT) { @@ -1744,110 +3004,94 @@ int weightFromDistance(EditMesh *em) } else { - EditVert *eve, *current_eve = NULL; + EditEdge *eed; + int allDone = 0; + + /* Calculate edge weight */ + for(eed = em->edges.first; eed; eed= eed->next) + { + if (eed->v1->h == 0 && eed->v2->h == 0) + { + eed->tmp.fp = VecLenf(eed->v1->co, eed->v2->co); + } + } + /* Apply dijkstra spf for each selected vert */ for(eve = em->verts.first; eve; eve = eve->next) { if (eve->f & SELECT) { - current_eve = eve; - eve->f1 = 1; - + shortestPathsFromVert(em, eve, indexed_edges); + } + } + + /* connect unselected islands */ + while (allDone == 0) + { + EditVert *selected_eve = NULL; + float selected_weight = 0; + float min_distance = FLT_MAX; + + allDone = 1; + + for (eve = em->verts.first; eve; eve = eve->next) + { + /* for every vertex visible that hasn't been processed yet */ + if (eve->h == 0 && eve->f1 != 1) { - EditEdge *eed = NULL; - EditEdge *select_eed = NULL; - EditEdge **edges = NULL; - float currentWeight = 0; - int eIndex = 0; + EditVert *closest_eve; - edges = MEM_callocN(totedge * sizeof(EditEdge*), "Edges"); - - /* Calculate edge weight and initialize edge flags */ - for(eed= em->edges.first; eed; eed= eed->next) + /* find the closest processed vertex */ + for (closest_eve = em->verts.first; closest_eve; closest_eve = closest_eve->next) { - eed->tmp.fp = VecLenf(eed->v1->co, eed->v2->co); - eed->f1 = 0; + /* vertex is already processed and distance is smaller than current minimum */ + if (closest_eve->f1 == 1) + { + float distance = VecLenf(closest_eve->co, eve->co); + if (distance < min_distance) + { + min_distance = distance; + selected_eve = eve; + selected_weight = closest_eve->tmp.fp; + } + } } - - do { - int i; - - current_eve->f1 = 1; /* mark vertex as selected */ - - /* Add all new edges connected to current_eve to the list */ - NextEdgeForVert(NULL, NULL); // Reset next edge - for(eed = NextEdgeForVert(em, current_eve); eed; eed = NextEdgeForVert(em, current_eve)) - { - if (eed->f1 == 0) - { - edges[eIndex] = eed; - eed->f1 = 1; - eIndex++; - } - } - - /* Find next shortest edge */ - select_eed = NULL; - for(i = 0; i < eIndex; i++) - { - eed = edges[i]; - - if (eed->f1 != 2 && (eed->v1->f1 == 0 || eed->v2->f1 == 0)) /* eed is not selected yet and leads to a new node */ - { - float newWeight = 0; - if (eed->v1->f1 == 1) - { - newWeight = eed->v1->tmp.fp + eed->tmp.fp; - } - else - { - newWeight = eed->v2->tmp.fp + eed->tmp.fp; - } - - if (select_eed == NULL || newWeight < currentWeight) /* no selected edge or current smaller than selected */ - { - currentWeight = newWeight; - select_eed = eed; - } - } - } - - if (select_eed != NULL) - { - select_eed->f1 = 2; - - if (select_eed->v1->f1 == 0) /* v1 is the new vertex */ - { - current_eve = select_eed->v1; - } - else /* otherwise, it's v2 */ - { - current_eve = select_eed->v2; - } - current_eve->tmp.fp = currentWeight; - } - } while (select_eed != NULL); - - MEM_freeN(edges); } } + + if (selected_eve) + { + allDone = 0; + + selected_eve->tmp.fp = selected_weight + min_distance; + shortestPathsFromVert(em, selected_eve, indexed_edges); + } + } + } + + for(eve = em->verts.first; eve && vCount == 0; eve = eve->next) + { + if (eve->f1 == 0) + { + printf("vertex not reached\n"); + break; } } return 1; } -MCol MColFromWeight(EditVert *eve) +MCol MColFromVal(float val) { MCol col; col.a = 255; - col.b = (char)(eve->tmp.fp * 255); + col.b = (char)(val * 255); col.g = 0; - col.r = (char)((1.0f - eve->tmp.fp) * 255); + col.r = (char)((1.0f - val) * 255); return col; } -void weightToVCol(EditMesh *em) +void weightToVCol(EditMesh *em, int index) { EditFace *efa; MCol *mcol; @@ -1855,15 +3099,149 @@ void weightToVCol(EditMesh *em) return; } + for(efa=em->faces.first; efa; efa=efa->next) { + mcol = CustomData_em_get_n(&em->fdata, efa->data, CD_MCOL, index); + + if (mcol) + { + mcol[0] = MColFromVal(efa->v1->tmp.fp); + mcol[1] = MColFromVal(efa->v2->tmp.fp); + mcol[2] = MColFromVal(efa->v3->tmp.fp); + + if(efa->v4) { + mcol[3] = MColFromVal(efa->v4->tmp.fp); + } + } + } +} + +void angleToVCol(EditMesh *em, int index) +{ + EditFace *efa; + MCol *mcol; + + if (!EM_vertColorCheck()) { + return; + } + + for(efa=em->faces.first; efa; efa=efa->next) { + MCol col; + if (efa->tmp.fp > 0) + { + col = MColFromVal(efa->tmp.fp / (M_PI / 2 + 0.1)); + } + else + { + col.a = 255; + col.r = 0; + col.g = 255; + col.b = 0; + } + + mcol = CustomData_em_get_n(&em->fdata, efa->data, CD_MCOL, index); + + if (mcol) + { + mcol[0] = col; + mcol[1] = col; + mcol[2] = col; + + if(efa->v4) { + mcol[3] = col; + } + } + } +} + +void blendColor(MCol *dst, MCol *src) +{ +#if 1 + float blend_src = (float)src->a / (float)(src->a + dst->a); + float blend_dst = (float)dst->a / (float)(src->a + dst->a); + dst->a += src->a; + dst->r = (char)(dst->r * blend_dst + src->r * blend_src); + dst->g = (char)(dst->g * blend_dst + src->g * blend_src); + dst->b = (char)(dst->b * blend_dst + src->b * blend_src); +#else + dst->r = src->r; + dst->g = src->g; + dst->b = src->b; +#endif +} + +void arcToVCol(ReebGraph *rg, EditMesh *em, int index) +{ + GHashIterator ghi; + EditFace *efa; + ReebArc *arc; + MCol *mcol; + MCol col; + int total = BLI_countlist(&rg->arcs); + int i = 0; + + if (!EM_vertColorCheck()) { + return; + } + + col.a = 0; + + col.r = 0; + col.g = 0; + col.b = 0; + + for(efa=em->faces.first; efa; efa=efa->next) { + mcol = CustomData_em_get_n(&em->fdata, efa->data, CD_MCOL, index); + + if (mcol) + { + mcol[0] = col; + mcol[1] = col; + mcol[2] = col; + + if(efa->v4) { + mcol[3] = col; + } + } + } + + for (arc = rg->arcs.first; arc; arc = arc->next, i++) + { + float r,g,b; + col.a = 1; + + hsv_to_rgb((float)i / (float)total, 1, 1, &r, &g, &b); + + col.r = FTOCHAR(r); + col.g = FTOCHAR(g); + col.b = FTOCHAR(b); + + for(BLI_ghashIterator_init(&ghi, arc->faces); + !BLI_ghashIterator_isDone(&ghi); + BLI_ghashIterator_step(&ghi)) + { + efa = BLI_ghashIterator_getValue(&ghi); + + mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); + + blendColor(&mcol[0], &col); + blendColor(&mcol[1], &col); + blendColor(&mcol[2], &col); + + if(efa->v4) { + blendColor(&mcol[3], &col); + } + } + } + for(efa=em->faces.first; efa; efa=efa->next) { mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); - mcol[0] = MColFromWeight(efa->v1); - mcol[1] = MColFromWeight(efa->v2); - mcol[2] = MColFromWeight(efa->v3); + mcol[0].a = 255; + mcol[1].a = 255; + mcol[2].a = 255; if(efa->v4) { - mcol[3] = MColFromWeight(efa->v4); + mcol[3].a = 255; } } } @@ -1874,7 +3252,7 @@ void initArcIterator(ReebArcIterator *iter, ReebArc *arc, ReebNode *head) { iter->arc = arc; - if (head == arc->v1) + if (head == arc->head) { iter->start = 0; iter->end = arc->bcount - 1; @@ -1887,9 +3265,38 @@ void initArcIterator(ReebArcIterator *iter, ReebArc *arc, ReebNode *head) iter->stride = -1; } + iter->length = arc->bcount; + iter->index = iter->start - iter->stride; } +void initArcIteratorStart(struct ReebArcIterator *iter, struct ReebArc *arc, struct ReebNode *head, int start) +{ + iter->arc = arc; + + if (head == arc->head) + { + iter->start = start; + iter->end = arc->bcount - 1; + iter->stride = 1; + } + else + { + iter->start = arc->bcount - 1 - start; + iter->end = 0; + iter->stride = -1; + } + + iter->index = iter->start - iter->stride; + + iter->length = arc->bcount - start; + + if (start >= arc->bcount) + { + iter->start = iter->end; /* stop iterator since it's past its end */ + } +} + void initArcIterator2(ReebArcIterator *iter, ReebArc *arc, int start, int end) { iter->arc = arc; @@ -1907,6 +3314,8 @@ void initArcIterator2(ReebArcIterator *iter, ReebArc *arc, int start, int end) } iter->index = iter->start - iter->stride; + + iter->length = abs(iter->end - iter->start) + 1; } EmbedBucket * nextBucket(ReebArcIterator *iter) @@ -1921,3 +3330,402 @@ EmbedBucket * nextBucket(ReebArcIterator *iter) return result; } + +EmbedBucket * nextNBucket(ReebArcIterator *iter, int n) +{ + EmbedBucket *result = NULL; + + iter->index += n * iter->stride; + + /* check if passed end */ + if ((iter->stride == 1 && iter->index <= iter->end) || + (iter->stride == -1 && iter->index >= iter->end)) + { + result = &(iter->arc->buckets[iter->index]); + } + else + { + /* stop iterator if passed end */ + iter->index = iter->end; + } + + return result; +} + +EmbedBucket * peekBucket(ReebArcIterator *iter, int n) +{ + EmbedBucket *result = NULL; + int index = iter->index + n * iter->stride; + + /* check if passed end */ + if ((iter->stride == 1 && index <= iter->end && index >= iter->start) || + (iter->stride == -1 && index >= iter->end && index <= iter->start)) + { + result = &(iter->arc->buckets[index]); + } + + return result; +} + +EmbedBucket * previousBucket(struct ReebArcIterator *iter) +{ + EmbedBucket *result = NULL; + + if (iter->index != iter->start) + { + iter->index -= iter->stride; + result = &(iter->arc->buckets[iter->index]); + } + + return result; +} + +int iteratorStopped(struct ReebArcIterator *iter) +{ + if (iter->index == iter->end) + { + return 1; + } + else + { + return 0; + } +} + +struct EmbedBucket * currentBucket(struct ReebArcIterator *iter) +{ + EmbedBucket *result = NULL; + + if (iter->index != iter->end) + { + result = &(iter->arc->buckets[iter->index]); + } + + return result; +} + +/************************ PUBLIC FUNCTIONS *********************************************/ + +ReebGraph *BIF_ReebGraphMultiFromEditMesh(void) +{ + EditMesh *em = G.editMesh; + EdgeIndex indexed_edges; + ReebGraph *rg = NULL; + ReebGraph *rgi, *previous; + int i, nb_levels = REEB_MAX_MULTI_LEVEL; + + if (em == NULL) + return NULL; + + buildIndexedEdges(em, &indexed_edges); + + if (weightFromDistance(em, &indexed_edges) == 0) + { + error("No selected vertex\n"); + freeEdgeIndex(&indexed_edges); + return NULL; + } + + renormalizeWeight(em, 1.0f); + + if (G.scene->toolsettings->skgen_options & SKGEN_HARMONIC) + { + weightToHarmonic(em, &indexed_edges); + } + + freeEdgeIndex(&indexed_edges); + +#ifdef DEBUG_REEB + weightToVCol(em, 0); +#endif + + rg = generateReebGraph(em, G.scene->toolsettings->skgen_resolution); + + /* Remove arcs without embedding */ + filterNullReebGraph(rg); + + /* smart filter and loop filter on basic level */ + filterGraph(rg, SKGEN_FILTER_SMART, 0, 0); + + repositionNodes(rg); + + /* Filtering might have created degree 2 nodes, so remove them */ + removeNormalNodes(rg); + + joinSubgraphs(rg, 1.0); + + BLI_buildAdjacencyList((BGraph*)rg); + + /* calc length before copy, so we have same length on all levels */ + BLI_calcGraphLength((BGraph*)rg); + + previous = NULL; + for (i = 0; i <= nb_levels; i++) + { + rgi = rg; + + /* don't filter last level */ + if (i > 0) + { + float internal_threshold; + float external_threshold; + + /* filter internal progressively in second half only*/ + if (i > nb_levels / 2) + { + internal_threshold = rg->length * G.scene->toolsettings->skgen_threshold_internal; + } + else + { + internal_threshold = rg->length * G.scene->toolsettings->skgen_threshold_internal * (2 * i / (float)nb_levels); + } + + external_threshold = rg->length * G.scene->toolsettings->skgen_threshold_external * (i / (float)nb_levels); + + filterGraph(rgi, G.scene->toolsettings->skgen_options, internal_threshold, external_threshold); + } + + if (i < nb_levels) + { + rg = copyReebGraph(rgi, i + 1); + } + + finalizeGraph(rgi, G.scene->toolsettings->skgen_postpro_passes, G.scene->toolsettings->skgen_postpro); + + BLI_markdownSymmetry((BGraph*)rgi, rgi->nodes.first, G.scene->toolsettings->skgen_symmetry_limit); + + if (previous != NULL) + { + relinkNodes(rgi, previous); + } + previous = rgi; + } + + verifyMultiResolutionLinks(rg, 0); + + return rg; +} + +ReebGraph *BIF_ReebGraphFromEditMesh(void) +{ + EditMesh *em = G.editMesh; + EdgeIndex indexed_edges; + ReebGraph *rg = NULL; + + if (em == NULL) + return NULL; + + buildIndexedEdges(em, &indexed_edges); + + if (weightFromDistance(em, &indexed_edges) == 0) + { + error("No selected vertex\n"); + freeEdgeIndex(&indexed_edges); + freeEdgeIndex(&indexed_edges); + return NULL; + } + + renormalizeWeight(em, 1.0f); + + if (G.scene->toolsettings->skgen_options & SKGEN_HARMONIC) + { + weightToHarmonic(em, &indexed_edges); + } + + freeEdgeIndex(&indexed_edges); + +#ifdef DEBUG_REEB + weightToVCol(em, 1); +#endif + + rg = generateReebGraph(em, G.scene->toolsettings->skgen_resolution); + + REEB_exportGraph(rg, -1); + + printf("GENERATED\n"); + printf("%i subgraphs\n", BLI_FlagSubgraphs((BGraph*)rg)); + + /* Remove arcs without embedding */ + filterNullReebGraph(rg); + + BLI_freeAdjacencyList((BGraph*)rg); + + printf("NULL FILTERED\n"); + printf("%i subgraphs\n", BLI_FlagSubgraphs((BGraph*)rg)); + + filterGraph(rg, G.scene->toolsettings->skgen_options, G.scene->toolsettings->skgen_threshold_internal, G.scene->toolsettings->skgen_threshold_external); + + finalizeGraph(rg, G.scene->toolsettings->skgen_postpro_passes, G.scene->toolsettings->skgen_postpro); + + REEB_exportGraph(rg, -1); + +#ifdef DEBUG_REEB + arcToVCol(rg, em, 0); + //angleToVCol(em, 1); +#endif + + printf("DONE\n"); + printf("%i subgraphs\n", BLI_FlagSubgraphs((BGraph*)rg)); + + return rg; +} + +void BIF_GlobalReebFree() +{ + if (GLOBAL_RG != NULL) + { + REEB_freeGraph(GLOBAL_RG); + GLOBAL_RG = NULL; + } +} + +void BIF_GlobalReebGraphFromEditMesh(void) +{ + ReebGraph *rg; + + BIF_GlobalReebFree(); + + rg = BIF_ReebGraphMultiFromEditMesh(); + + GLOBAL_RG = rg; +} + +void REEB_draw() +{ + ReebGraph *rg; + ReebArc *arc; + int i = 0; + + if (GLOBAL_RG == NULL) + { + return; + } + + if (GLOBAL_RG->link_up && G.scene->toolsettings->skgen_options & SKGEN_DISP_ORIG) + { + for (rg = GLOBAL_RG; rg->link_up; rg = rg->link_up) ; + } + else + { + i = G.scene->toolsettings->skgen_multi_level; + + for (rg = GLOBAL_RG; rg->multi_level != i && rg->link_up; rg = rg->link_up) ; + } + + glPointSize(BIF_GetThemeValuef(TH_VERTEX_SIZE)); + + glDisable(GL_DEPTH_TEST); + for (arc = rg->arcs.first; arc; arc = arc->next, i++) + { + ReebArcIterator iter; + EmbedBucket *bucket; + float vec[3]; + char text[128]; + char *s = text; + + glLineWidth(BIF_GetThemeValuef(TH_VERTEX_SIZE) + 2); + glColor3f(0, 0, 0); + glBegin(GL_LINE_STRIP); + glVertex3fv(arc->head->p); + + if (arc->bcount) + { + initArcIterator(&iter, arc, arc->head); + for (bucket = nextBucket(&iter); bucket; bucket = nextBucket(&iter)) + { + glVertex3fv(bucket->p); + } + } + + glVertex3fv(arc->tail->p); + glEnd(); + + glLineWidth(BIF_GetThemeValuef(TH_VERTEX_SIZE)); + + if (arc->symmetry_level == 1) + { + glColor3f(1, 0, 0); + } + else if (arc->symmetry_flag == SYM_SIDE_POSITIVE || arc->symmetry_flag == SYM_SIDE_NEGATIVE) + { + glColor3f(1, 0.5f, 0); + } + else if (arc->symmetry_flag >= SYM_SIDE_RADIAL) + { + glColor3f(0.5f, 1, 0); + } + else + { + glColor3f(1, 1, 0); + } + glBegin(GL_LINE_STRIP); + glVertex3fv(arc->head->p); + + if (arc->bcount) + { + initArcIterator(&iter, arc, arc->head); + for (bucket = nextBucket(&iter); bucket; bucket = nextBucket(&iter)) + { + glVertex3fv(bucket->p); + } + } + + glVertex3fv(arc->tail->p); + glEnd(); + + + if (G.scene->toolsettings->skgen_options & SKGEN_DISP_EMBED) + { + glColor3f(1, 1, 1); + glBegin(GL_POINTS); + glVertex3fv(arc->head->p); + glVertex3fv(arc->tail->p); + + glColor3f(0.5f, 0.5f, 1); + if (arc->bcount) + { + initArcIterator(&iter, arc, arc->head); + for (bucket = nextBucket(&iter); bucket; bucket = nextBucket(&iter)) + { + glVertex3fv(bucket->p); + } + } + glEnd(); + } + + if (G.scene->toolsettings->skgen_options & SKGEN_DISP_INDEX) + { + VecLerpf(vec, arc->head->p, arc->tail->p, 0.5f); + s += sprintf(s, "%i (%i-%i-%i) ", i, arc->symmetry_level, arc->symmetry_flag, arc->symmetry_group); + + if (G.scene->toolsettings->skgen_options & SKGEN_DISP_WEIGHT) + { + s += sprintf(s, "w:%0.3f ", arc->tail->weight - arc->head->weight); + } + + if (G.scene->toolsettings->skgen_options & SKGEN_DISP_LENGTH) + { + s += sprintf(s, "l:%0.3f", arc->length); + } + + glColor3f(0, 1, 0); + glRasterPos3fv(vec); + BMF_DrawString( G.fonts, text); + } + + if (G.scene->toolsettings->skgen_options & SKGEN_DISP_INDEX) + { + sprintf(text, " %i", arc->head->index); + glRasterPos3fv(arc->head->p); + BMF_DrawString( G.fonts, text); + + sprintf(text, " %i", arc->tail->index); + glRasterPos3fv(arc->tail->p); + BMF_DrawString( G.fonts, text); + } + } + glEnable(GL_DEPTH_TEST); + + glLineWidth(1.0); + glPointSize(1.0); +} diff --git a/source/blender/src/usiblender.c b/source/blender/src/usiblender.c index cd7f80344dc..16c26336b89 100644 --- a/source/blender/src/usiblender.c +++ b/source/blender/src/usiblender.c @@ -149,6 +149,8 @@ #include "PIL_time.h" +#include "reeb.h" + #include "GPU_extensions.h" #include "GPU_draw.h" @@ -1099,6 +1101,9 @@ void exit_usiblender(void) BIF_clear_tempfiles(); + BIF_GlobalReebFree(); + BIF_freeRetarget(); + tf= G.ttfdata.first; while(tf) {