diff options
author | Tamito Kajiyama <rd6t-kjym@asahi-net.or.jp> | 2013-03-03 19:07:49 +0400 |
---|---|---|
committer | Tamito Kajiyama <rd6t-kjym@asahi-net.or.jp> | 2013-03-03 19:07:49 +0400 |
commit | 66a2b848972d47a033f617a40f9a9044756d1d32 (patch) | |
tree | bf30f5bc13e9c0b4f52aacead676a2c07cbb331a /source/blender | |
parent | 0c5dfc8a639b04e1767852121b9dee7cd5bb050f (diff) | |
parent | 4774357b594c5cd5a38b21df131b539a197c101b (diff) |
Merged changes in the trunk up to revision 54992.
Resolved conflicts:
release/scripts/startup/bl_ui/space_view3d.py
Diffstat (limited to 'source/blender')
214 files changed, 11643 insertions, 11780 deletions
diff --git a/source/blender/blenkernel/BKE_depsgraph.h b/source/blender/blenkernel/BKE_depsgraph.h index 4cdfc1cba95..6baf20aeb2c 100644 --- a/source/blender/blenkernel/BKE_depsgraph.h +++ b/source/blender/blenkernel/BKE_depsgraph.h @@ -30,114 +30,94 @@ * \ingroup bke */ +/* Dependency Graph + * + * The dependency graph tracks relations between datablocks, and is used to + * determine which datablocks need to be update based on dependencies and + * visibility. + * + * It does not itself execute changes in objects, but rather sorts the objects + * in the appropriate order and sets flags indicating they should be updated. + */ + #ifdef __cplusplus extern "C" { #endif -// #define DEPS_DEBUG - -struct DagForest; -struct DagNode; -struct DagNodeQueue; -struct GHash; struct ID; struct Main; struct Object; struct Scene; -/* **** DAG relation types *** */ - -/* scene link to object */ -#define DAG_RL_SCENE (1 << 0) -/* object link to data */ -#define DAG_RL_DATA (1 << 1) - -/* object changes object (parent, track, constraints) */ -#define DAG_RL_OB_OB (1 << 2) -/* object changes obdata (hooks, constraints) */ -#define DAG_RL_OB_DATA (1 << 3) -/* data changes object (vertex parent) */ -#define DAG_RL_DATA_OB (1 << 4) -/* data changes data (deformers) */ -#define DAG_RL_DATA_DATA (1 << 5) - -#define DAG_NO_RELATION (1 << 6) - -#define DAG_RL_ALL_BUT_DATA (DAG_RL_SCENE | DAG_RL_OB_OB | DAG_RL_OB_DATA | DAG_RL_DATA_OB | DAG_RL_DATA_DATA) -#define DAG_RL_ALL (DAG_RL_ALL_BUT_DATA | DAG_RL_DATA) - - -typedef void (*graph_action_func)(void *ob, void **data); - -// queues are returned by all BFS & DFS queries -// opaque type -void *pop_ob_queue(struct DagNodeQueue *queue); -int queue_count(struct DagNodeQueue *queue); -void queue_delete(struct DagNodeQueue *queue); - -// queries -struct DagForest *build_dag(struct Main *bmain, struct Scene *sce, short mask); -void free_forest(struct DagForest *Dag); - -// note : -// the meanings of the 2 returning values is a bit different : -// BFS return 1 for cross-edges and back-edges. the latter are considered harmfull, not the former -// DFS return 1 only for back-edges -int pre_and_post_BFS(struct DagForest *dag, short mask, graph_action_func pre_func, graph_action_func post_func, void **data); -int pre_and_post_DFS(struct DagForest *dag, short mask, graph_action_func pre_func, graph_action_func post_func, void **data); - -int pre_and_post_source_BFS(struct DagForest *dag, short mask, struct DagNode *source, graph_action_func pre_func, graph_action_func post_func, void **data); -int pre_and_post_source_DFS(struct DagForest *dag, short mask, struct DagNode *source, graph_action_func pre_func, graph_action_func post_func, void **data); - -struct DagNodeQueue *get_obparents(struct DagForest *dag, void *ob); -struct DagNodeQueue *get_first_ancestors(struct DagForest *dag, void *ob); -struct DagNodeQueue *get_all_childs(struct DagForest *dag, void *ob); -short are_obs_related(struct DagForest *dag, void *ob1, void *ob2); -int is_acyclic(struct DagForest *dag); -//int get_cycles(struct DagForest *dag, struct DagNodeQueue **queues, int *count); // - -/* ********** API *************** */ -/* Note that the DAG never executes changes in Objects, only sets flags in Objects */ - -/* clear all dependency graphs, call this when changing relations between objects. - * the dependency graphs will be rebuilt just before they are used to avoid them - * getting rebuild many times during operators */ -void DAG_relations_tag_update(struct Main *bmain); - -/* (re)-create the dependency graph before using it */ -void DAG_scene_relations_update(struct Main *bmain, struct Scene *sce); - -/* force an immediate rebuild of the dependency graph, only needed in rare cases */ -void DAG_scene_relations_rebuild(struct Main *bmain, struct Scene *scene); - -/* flag all objects that need recalc because they're animated */ -void DAG_scene_update_flags(struct Main *bmain, struct Scene *sce, unsigned int lay, const short do_time); -/* flushes all recalc flags in objects down the dependency tree */ -void DAG_scene_flush_update(struct Main *bmain, struct Scene *sce, unsigned int lay, const short do_time); -/* tag objects for update on file load */ -void DAG_on_visible_update(struct Main *bmain, const short do_time); - -/* tag datablock to get updated for the next redraw */ -void DAG_id_tag_update_ex(struct Main *bmain, struct ID *id, short flag); -void DAG_id_tag_update(struct ID *id, short flag); -/* flush all tagged updates */ -void DAG_ids_flush_tagged(struct Main *bmain); -/* check and clear ID recalc flags */ -void DAG_ids_check_recalc(struct Main *bmain, struct Scene *scene, int time); -void DAG_ids_clear_recalc(struct Main *bmain); -/* test if any of this id type is tagged for update */ -void DAG_id_type_tag(struct Main *bmain, short idtype); -int DAG_id_type_tagged(struct Main *bmain, short idtype); - -/* (re)-create dependency graph for armature pose */ -void DAG_pose_sort(struct Object *ob); - -/* callback for editors module to do updates */ -void DAG_editors_update_cb(void (*id_func)(struct Main *bmain, struct ID *id), - void (*scene_func)(struct Main *bmain, struct Scene *scene, int updated)); - -/* debugging */ -void DAG_print_dependencies(struct Main *bmain, struct Scene *scene, struct Object *ob); +/* Build and Update + * + * DAG_scene_relations_update will rebuild the dependency graph for a given + * scene if needed, and sort objects in the scene. + * + * DAG_relations_tag_update will clear all dependency graphs and mark them to + * be rebuilt later. The graph is not rebuilt immediately to avoid slowdowns + * when this function is call multiple times from different operators. + * + * DAG_scene_relations_rebuild forces an immediaterebuild of the dependency + * graph, this is only needed in rare cases + */ + +void DAG_scene_relations_update(struct Main *bmain, struct Scene *sce); +void DAG_relations_tag_update(struct Main *bmain); +void DAG_scene_relations_rebuild(struct Main *bmain, struct Scene *scene); +void DAG_scene_free(struct Scene *sce); + +/* Update Tagging + * + * DAG_scene_update_flags will mark all objects that depend on time (animation, + * physics, ..) to be recalculated, used when changing the current frame. + * + * DAG_on_visible_update will mark all objects that are visible for the first + * time to be updated, for example on file load or changing layer visibility. + * + * DAG_id_tag_update will mark a given datablock to be updated. The flag indicates + * a specific subset to be update (only object transform and data for now). + * + * DAG_id_type_tag marks a particular datablock type as having changing. This does + * not cause any updates but is used by external render engines to detect if for + * example a datablock was removed. */ + +void DAG_scene_update_flags(struct Main *bmain, struct Scene *sce, unsigned int lay, const short do_time); +void DAG_on_visible_update(struct Main *bmain, const short do_time); + +void DAG_id_tag_update(struct ID *id, short flag); +void DAG_id_tag_update_ex(struct Main *bmain, struct ID *id, short flag); +void DAG_id_type_tag(struct Main *bmain, short idtype); +int DAG_id_type_tagged(struct Main *bmain, short idtype); + +/* Flushing Tags + * + * DAG_scene_flush_update flushes object recalculation flags immediately to other + * dependencies. Do not use outside of depsgraph.c, this will be removed. + * + * DAG_ids_flush_tagged will flush datablock update flags flags to dependencies, + * use this right before updating to mark all the needed datablocks for update. + * + * DAG_ids_check_recalc and DAG_ids_clear_recalc are used for external render + * engines to detect changes. */ + +void DAG_scene_flush_update(struct Main *bmain, struct Scene *sce, unsigned int lay, const short do_time); +void DAG_ids_flush_tagged(struct Main *bmain); +void DAG_ids_check_recalc(struct Main *bmain, struct Scene *scene, int time); +void DAG_ids_clear_recalc(struct Main *bmain); + +/* Armature: sorts the bones according to dependencies between them */ + +void DAG_pose_sort(struct Object *ob); + +/* Editors: callbacks to notify editors of datablock changes */ + +void DAG_editors_update_cb(void (*id_func)(struct Main *bmain, struct ID *id), + void (*scene_func)(struct Main *bmain, struct Scene *scene, int updated)); + +/* Debugging: print dependency graph for scene or armature object to console */ + +void DAG_print_dependencies(struct Main *bmain, struct Scene *scene, struct Object *ob); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_report.h b/source/blender/blenkernel/BKE_report.h index e659954a3ac..4dc16f6266f 100644 --- a/source/blender/blenkernel/BKE_report.h +++ b/source/blender/blenkernel/BKE_report.h @@ -34,6 +34,8 @@ extern "C" { #endif +#include <stdio.h> + #include "DNA_windowmanager_types.h" #include "BLI_utildefines.h" diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h index 12779a697b6..bab961bcbd2 100644 --- a/source/blender/blenkernel/BKE_rigidbody.h +++ b/source/blender/blenkernel/BKE_rigidbody.h @@ -91,6 +91,7 @@ void BKE_rigidbody_remove_constraint(struct Scene *scene, struct Object *ob); void BKE_rigidbody_aftertrans_update(struct Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle); void BKE_rigidbody_sync_transforms(struct RigidBodyWorld *rbw, struct Object *ob, float ctime); void BKE_rigidbody_cache_reset(struct RigidBodyWorld *rbw); +void BKE_rigidbody_rebuild_world(struct Scene *scene, float ctime); void BKE_rigidbody_do_simulation(struct Scene *scene, float ctime); #endif /* __BKE_RIGIDBODY_H__ */ diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index eef134a6872..c0ff634671d 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -36,6 +36,7 @@ struct Editing; struct ImBuf; struct Main; struct Mask; +struct MovieClip; struct Scene; struct Sequence; struct SequenceModifierData; @@ -322,6 +323,8 @@ void BKE_sequence_base_dupli_recursive(struct Scene *scene, struct Scene *scene_ int BKE_sequence_is_valid_check(struct Sequence *seq); void BKE_sequencer_clear_scene_in_allseqs(struct Main *bmain, struct Scene *sce); +void BKE_sequencer_clear_movieclip_in_clipboard(struct MovieClip *clip); +void BKE_sequencer_clear_mask_in_clipboard(struct Mask *mask); struct Sequence *BKE_sequence_get_by_name(struct ListBase *seqbase, const char *name, int recursive); diff --git a/source/blender/blenkernel/depsgraph_private.h b/source/blender/blenkernel/depsgraph_private.h index 12c111f5f16..ad5369c0820 100644 --- a/source/blender/blenkernel/depsgraph_private.h +++ b/source/blender/blenkernel/depsgraph_private.h @@ -34,9 +34,27 @@ #include "DNA_constraint_types.h" #include "BKE_constraint.h" +/* **** DAG relation types *** */ + +/* scene link to object */ +#define DAG_RL_SCENE (1 << 0) +/* object link to data */ +#define DAG_RL_DATA (1 << 1) + +/* object changes object (parent, track, constraints) */ +#define DAG_RL_OB_OB (1 << 2) +/* object changes obdata (hooks, constraints) */ +#define DAG_RL_OB_DATA (1 << 3) +/* data changes object (vertex parent) */ +#define DAG_RL_DATA_OB (1 << 4) +/* data changes data (deformers) */ +#define DAG_RL_DATA_DATA (1 << 5) + +#define DAG_NO_RELATION (1 << 6) + +#define DAG_RL_ALL_BUT_DATA (DAG_RL_SCENE | DAG_RL_OB_OB | DAG_RL_OB_DATA | DAG_RL_DATA_OB | DAG_RL_DATA_DATA) +#define DAG_RL_ALL (DAG_RL_ALL_BUT_DATA | DAG_RL_DATA) -#define DEPSX 5.0f -#define DEPSY 1.8f #define DAGQUEUEALLOC 50 @@ -46,8 +64,6 @@ enum { DAG_BLACK = 2 }; - - typedef struct DagAdjList { struct DagNode *node; short type; @@ -108,11 +124,13 @@ void push_queue(DagNodeQueue *queue, DagNode *node); void push_stack(DagNodeQueue *queue, DagNode *node); DagNode *pop_queue(DagNodeQueue *queue); DagNode *get_top_node_queue(DagNodeQueue *queue); +int queue_count(DagNodeQueue *queue); +void queue_delete(DagNodeQueue *queue); // Dag management -DagForest *getMainDag(void); -void setMainDag(DagForest *dag); DagForest *dag_init(void); +DagForest *build_dag(struct Main *bmain, struct Scene *sce, short mask); +void free_forest(struct DagForest *Dag); DagNode *dag_find_node(DagForest *forest, void *fob); DagNode *dag_add_node(DagForest *forest, void *fob); DagNode *dag_get_node(DagForest *forest, void *fob); @@ -126,6 +144,6 @@ DagNodeQueue *graph_dfs(void); void set_node_xy(DagNode *node, float x, float y); void graph_print_queue(DagNodeQueue *nqueue); void graph_print_queue_dist(DagNodeQueue *nqueue); -void graph_print_adj_list(void); +void graph_print_adj_list(DagForest *dag); #endif /* __DEPSGRAPH_PRIVATE_H__ */ diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 6b3c95a5cd3..ffd86ea9ce0 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -491,7 +491,7 @@ void DM_to_mesh(DerivedMesh *dm, Mesh *me, Object *ob) CustomData_copy(&dm->edgeData, &tmp.edata, CD_MASK_MESH, CD_DUPLICATE, totedge); CustomData_copy(&dm->loopData, &tmp.ldata, CD_MASK_MESH, CD_DUPLICATE, totloop); CustomData_copy(&dm->polyData, &tmp.pdata, CD_MASK_MESH, CD_DUPLICATE, totpoly); - me->cd_flag = dm->cd_flag; + tmp.cd_flag = dm->cd_flag; if (CustomData_has_layer(&dm->vertData, CD_SHAPEKEY)) { KeyBlock *kb; diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 8c55ad02dc6..b919b9ffd8f 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -278,22 +278,11 @@ DagNode *pop_queue(DagNodeQueue *queue) } } -void *pop_ob_queue(struct DagNodeQueue *queue) -{ - return(pop_queue(queue)->ob); -} - DagNode *get_top_node_queue(DagNodeQueue *queue) { return queue->first->node; } -int queue_count(struct DagNodeQueue *queue) -{ - return queue->count; -} - - DagForest *dag_init(void) { DagForest *forest; @@ -1190,539 +1179,6 @@ static void dag_check_cycle(DagForest *dag) } } -/* - * MainDAG is the DAG of all objects in current scene - * used only for drawing there is one also in each scene - */ -static DagForest *MainDag = NULL; - -DagForest *getMainDag(void) -{ - return MainDag; -} - - -void setMainDag(DagForest *dag) -{ - MainDag = dag; -} - - -/* - * note for BFS/DFS - * in theory we should sweep the whole array - * but in our case the first node is the scene - * and is linked to every other object - * - * for general case we will need to add outer loop - */ - -/* - * ToDo : change pos kludge - */ - -/* adjust levels for drawing in oops space */ -void graph_bfs(void) -{ - DagNode *node; - DagNodeQueue *nqueue; - int pos[50]; - int i; - DagAdjList *itA; - int minheight; - - /* fprintf(stderr, "starting BFS\n ------------\n"); */ - nqueue = queue_create(DAGQUEUEALLOC); - for (i = 0; i < 50; i++) - pos[i] = 0; - - /* Init - * dagnode.first is always the root (scene) - */ - node = MainDag->DagNode.first; - while (node) { - node->color = DAG_WHITE; - node->BFS_dist = 9999; - node->k = 0; - node = node->next; - } - - node = MainDag->DagNode.first; - if (node->color == DAG_WHITE) { - node->color = DAG_GRAY; - node->BFS_dist = 1; - push_queue(nqueue, node); - while (nqueue->count) { - node = pop_queue(nqueue); - - minheight = pos[node->BFS_dist]; - itA = node->child; - while (itA != NULL) { - if (itA->node->color == DAG_WHITE) { - itA->node->color = DAG_GRAY; - itA->node->BFS_dist = node->BFS_dist + 1; - itA->node->k = (float) minheight; - push_queue(nqueue, itA->node); - } - - else { - fprintf(stderr, "bfs not dag tree edge color :%i\n", itA->node->color); - } - - - itA = itA->next; - } - if (pos[node->BFS_dist] > node->k) { - pos[node->BFS_dist] += 1; - node->k = (float) pos[node->BFS_dist]; - } - else { - pos[node->BFS_dist] = (int) node->k + 1; - } - set_node_xy(node, node->BFS_dist * DEPSX * 2, pos[node->BFS_dist] * DEPSY * 2); - node->color = DAG_BLACK; - - // fprintf(stderr, "BFS node : %20s %i %5.0f %5.0f\n", ((ID *) node->ob)->name, node->BFS_dist, node->x, node->y); - } - } - queue_delete(nqueue); -} - -int pre_and_post_BFS(DagForest *dag, short mask, graph_action_func pre_func, graph_action_func post_func, void **data) -{ - DagNode *node; - - node = dag->DagNode.first; - return pre_and_post_source_BFS(dag, mask, node, pre_func, post_func, data); -} - - -int pre_and_post_source_BFS(DagForest *dag, short mask, DagNode *source, graph_action_func pre_func, graph_action_func post_func, void **data) -{ - DagNode *node; - DagNodeQueue *nqueue; - DagAdjList *itA; - int retval = 0; - /* fprintf(stderr, "starting BFS\n ------------\n"); */ - - /* Init - * dagnode.first is always the root (scene) - */ - node = dag->DagNode.first; - nqueue = queue_create(DAGQUEUEALLOC); - while (node) { - node->color = DAG_WHITE; - node->BFS_dist = 9999; - node = node->next; - } - - node = source; - if (node->color == DAG_WHITE) { - node->color = DAG_GRAY; - node->BFS_dist = 1; - pre_func(node->ob, data); - - while (nqueue->count) { - node = pop_queue(nqueue); - - itA = node->child; - while (itA != NULL) { - if ((itA->node->color == DAG_WHITE) && (itA->type & mask)) { - itA->node->color = DAG_GRAY; - itA->node->BFS_dist = node->BFS_dist + 1; - push_queue(nqueue, itA->node); - pre_func(node->ob, data); - } - - else { /* back or cross edge */ - retval = 1; - } - itA = itA->next; - } - post_func(node->ob, data); - node->color = DAG_BLACK; - - // fprintf(stderr, "BFS node : %20s %i %5.0f %5.0f\n", ((ID *) node->ob)->name, node->BFS_dist, node->x, node->y); - } - } - queue_delete(nqueue); - return retval; -} - -/* non recursive version of DFS, return queue -- outer loop present to catch odd cases (first level cycles)*/ -DagNodeQueue *graph_dfs(void) -{ - DagNode *node; - DagNodeQueue *nqueue; - DagNodeQueue *retqueue; - int pos[50]; - int i; - DagAdjList *itA; - int time; - int skip = 0; - int minheight; - int maxpos = 0; - /* int is_cycle = 0; */ /* UNUSED */ - /* - *fprintf(stderr, "starting DFS\n ------------\n"); - */ - nqueue = queue_create(DAGQUEUEALLOC); - retqueue = queue_create(MainDag->numNodes); - for (i = 0; i < 50; i++) - pos[i] = 0; - - /* Init - * dagnode.first is always the root (scene) - */ - node = MainDag->DagNode.first; - while (node) { - node->color = DAG_WHITE; - node->DFS_dist = 9999; - node->DFS_dvtm = node->DFS_fntm = 9999; - node->k = 0; - node = node->next; - } - - time = 1; - - node = MainDag->DagNode.first; - - do { - if (node->color == DAG_WHITE) { - node->color = DAG_GRAY; - node->DFS_dist = 1; - node->DFS_dvtm = time; - time++; - push_stack(nqueue, node); - - while (nqueue->count) { - //graph_print_queue(nqueue); - - skip = 0; - node = get_top_node_queue(nqueue); - - minheight = pos[node->DFS_dist]; - - itA = node->child; - while (itA != NULL) { - if (itA->node->color == DAG_WHITE) { - itA->node->DFS_dvtm = time; - itA->node->color = DAG_GRAY; - - time++; - itA->node->DFS_dist = node->DFS_dist + 1; - itA->node->k = (float) minheight; - push_stack(nqueue, itA->node); - skip = 1; - break; - } - else { - if (itA->node->color == DAG_GRAY) { /* back edge */ - fprintf(stderr, "dfs back edge :%15s %15s\n", ((ID *) node->ob)->name, ((ID *) itA->node->ob)->name); - /* is_cycle = 1; */ /* UNUSED */ - } - else if (itA->node->color == DAG_BLACK) { - /* already processed node but we may want later to change distance either to shorter to longer. - * DFS_dist is the first encounter - */ -#if 0 - if (node->DFS_dist >= itA->node->DFS_dist) - itA->node->DFS_dist = node->DFS_dist + 1; - - fprintf(stderr, "dfs forward or cross edge :%15s %i-%i %15s %i-%i\n", - ((ID *) node->ob)->name, - node->DFS_dvtm, - node->DFS_fntm, - ((ID *) itA->node->ob)->name, - itA->node->DFS_dvtm, - itA->node->DFS_fntm); -#endif - } - else - fprintf(stderr, "dfs unknown edge\n"); - } - itA = itA->next; - } - - if (!skip) { - node = pop_queue(nqueue); - node->color = DAG_BLACK; - - node->DFS_fntm = time; - time++; - if (node->DFS_dist > maxpos) - maxpos = node->DFS_dist; - if (pos[node->DFS_dist] > node->k) { - pos[node->DFS_dist] += 1; - node->k = (float) pos[node->DFS_dist]; - } - else { - pos[node->DFS_dist] = (int) node->k + 1; - } - set_node_xy(node, node->DFS_dist * DEPSX * 2, pos[node->DFS_dist] * DEPSY * 2); - - // fprintf(stderr, "DFS node : %20s %i %i %i %i\n", ((ID *) node->ob)->name, node->BFS_dist, node->DFS_dist, node->DFS_dvtm, node->DFS_fntm ); - - push_stack(retqueue, node); - - } - } - } - node = node->next; - } while (node); -// fprintf(stderr, "i size : %i\n", maxpos); - - queue_delete(nqueue); - return(retqueue); -} - -/* unused */ -int pre_and_post_DFS(DagForest *dag, short mask, graph_action_func pre_func, graph_action_func post_func, void **data) -{ - DagNode *node; - - node = dag->DagNode.first; - return pre_and_post_source_DFS(dag, mask, node, pre_func, post_func, data); -} - -int pre_and_post_source_DFS(DagForest *dag, short mask, DagNode *source, graph_action_func pre_func, graph_action_func post_func, void **data) -{ - DagNode *node; - DagNodeQueue *nqueue; - DagAdjList *itA; - int time; - int skip = 0; - int retval = 0; - /* - * fprintf(stderr, "starting DFS\n ------------\n"); - */ - nqueue = queue_create(DAGQUEUEALLOC); - - /* Init - * dagnode.first is always the root (scene) - */ - node = dag->DagNode.first; - while (node) { - node->color = DAG_WHITE; - node->DFS_dist = 9999; - node->DFS_dvtm = node->DFS_fntm = 9999; - node->k = 0; - node = node->next; - } - - time = 1; - - node = source; - do { - if (node->color == DAG_WHITE) { - node->color = DAG_GRAY; - node->DFS_dist = 1; - node->DFS_dvtm = time; - time++; - push_stack(nqueue, node); - pre_func(node->ob, data); - - while (nqueue->count) { - skip = 0; - node = get_top_node_queue(nqueue); - - itA = node->child; - while (itA != NULL) { - if ((itA->node->color == DAG_WHITE) && (itA->type & mask) ) { - itA->node->DFS_dvtm = time; - itA->node->color = DAG_GRAY; - - time++; - itA->node->DFS_dist = node->DFS_dist + 1; - push_stack(nqueue, itA->node); - pre_func(node->ob, data); - - skip = 1; - break; - } - else { - if (itA->node->color == DAG_GRAY) { // back edge - retval = 1; - } -// else if (itA->node->color == DAG_BLACK) { // cross or forward -// -// } - } - itA = itA->next; - } - - if (!skip) { - node = pop_queue(nqueue); - node->color = DAG_BLACK; - - node->DFS_fntm = time; - time++; - post_func(node->ob, data); - } - } - } - node = node->next; - } while (node); - queue_delete(nqueue); - return(retval); -} - - -/* used to get the obs owning a datablock */ -DagNodeQueue *get_obparents(struct DagForest *dag, void *ob) -{ - DagNode *node, *node1; - DagNodeQueue *nqueue; - DagAdjList *itA; - - node = dag_find_node(dag, ob); - if (node == NULL) { - return NULL; - } - else if (node->ancestor_count == 1) { /* simple case */ - nqueue = queue_create(1); - push_queue(nqueue, node); - } - else { /* need to go over the whole dag for adj list */ - nqueue = queue_create(node->ancestor_count); - - node1 = dag->DagNode.first; - do { - if (node1->DFS_fntm > node->DFS_fntm) { /* a parent is finished after child. must check adj list */ - itA = node->child; - while (itA != NULL) { - if ((itA->node == node) && (itA->type == DAG_RL_DATA)) { - push_queue(nqueue, node); - } - itA = itA->next; - } - } - node1 = node1->next; - } while (node1); - } - return nqueue; -} - -DagNodeQueue *get_first_ancestors(struct DagForest *dag, void *ob) -{ - DagNode *node, *node1; - DagNodeQueue *nqueue; - DagAdjList *itA; - - node = dag_find_node(dag, ob); - - /* need to go over the whole dag for adj list */ - nqueue = queue_create(node->ancestor_count); - - node1 = dag->DagNode.first; - do { - if (node1->DFS_fntm > node->DFS_fntm) { - itA = node->child; - while (itA != NULL) { - if (itA->node == node) { - push_queue(nqueue, node); - } - itA = itA->next; - } - } - node1 = node1->next; - } while (node1); - - return nqueue; -} - -/* standard DFS list */ -DagNodeQueue *get_all_childs(struct DagForest *dag, void *ob) -{ - DagNode *node; - DagNodeQueue *nqueue; - DagNodeQueue *retqueue; - DagAdjList *itA; - int time; - int skip = 0; - - nqueue = queue_create(DAGQUEUEALLOC); - retqueue = queue_create(dag->numNodes); /* was MainDag... why? (ton) */ - - node = dag->DagNode.first; - while (node) { - node->color = DAG_WHITE; - node = node->next; - } - - time = 1; - - node = dag_find_node(dag, ob); /* could be done in loop above (ton) */ - if (node) { /* can be null for newly added objects */ - - node->color = DAG_GRAY; - time++; - push_stack(nqueue, node); - - while (nqueue->count) { - - skip = 0; - node = get_top_node_queue(nqueue); - - itA = node->child; - while (itA != NULL) { - if (itA->node->color == DAG_WHITE) { - itA->node->DFS_dvtm = time; - itA->node->color = DAG_GRAY; - - time++; - push_stack(nqueue, itA->node); - skip = 1; - break; - } - itA = itA->next; - } - - if (!skip) { - node = pop_queue(nqueue); - node->color = DAG_BLACK; - - time++; - push_stack(retqueue, node); - } - } - } - queue_delete(nqueue); - return(retqueue); -} - -/* unused */ -#if 0 -short are_obs_related(struct DagForest *dag, void *ob1, void *ob2) -{ - DagNode *node; - DagAdjList *itA; - - node = dag_find_node(dag, ob1); - - itA = node->child; - while (itA != NULL) { - if (itA->node->ob == ob2) { - return itA->node->type; - } - itA = itA->next; - } - return DAG_NO_RELATION; -} -#endif - -int is_acyclic(DagForest *dag) -{ - return dag->is_acyclic; -} - -void set_node_xy(DagNode *node, float x, float y) -{ - node->x = x; - node->y = y; -} - - /* debug test functions */ void graph_print_queue(DagNodeQueue *nqueue) @@ -1757,12 +1213,12 @@ void graph_print_queue_dist(DagNodeQueue *nqueue) fprintf(stderr, "\n"); } -void graph_print_adj_list(void) +void graph_print_adj_list(DagForest *dag) { DagNode *node; DagAdjList *itA; - node = (getMainDag())->DagNode.first; + node = dag->DagNode.first; while (node) { fprintf(stderr, "node : %s col: %i", ((ID *) node->ob)->name, node->color); itA = node->child; @@ -1973,6 +1429,15 @@ void DAG_scene_relations_update(Main *bmain, Scene *sce) dag_scene_build(bmain, sce); } +void DAG_scene_free(Scene *sce) +{ + if (sce->theDag) { + free_forest(sce->theDag); + MEM_freeN(sce->theDag); + sce->theDag = NULL; + } +} + static void lib_id_recalc_tag(Main *bmain, ID *id) { id->flag |= LIB_ID_RECALC; diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 23f3a3ad3fd..bc396d218df 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -986,9 +986,9 @@ typedef struct DriverVarTypeInfo { float (*get_value)(ChannelDriver *driver, DriverVar *dvar); /* allocation of target slots */ - int num_targets; /* number of target slots required */ + int num_targets; /* number of target slots required */ const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ - int target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ + short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ } DriverVarTypeInfo; /* Macro to begin definitions */ @@ -1014,7 +1014,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) PointerRNA id_ptr, ptr; PropertyRNA *prop; ID *id; - int index; + int index = -1; float value = 0.0f; /* sanity check */ @@ -1029,6 +1029,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) printf("Error: driver has an invalid target to use\n"); if (G.debug & G_DEBUG) printf("\tpath = %s\n", dtar->rna_path); driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; return 0.0f; } @@ -1039,7 +1040,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) if (RNA_path_resolve_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { if (RNA_property_array_check(prop)) { /* array */ - if (index < RNA_property_array_length(&ptr, prop)) { + if ((index >= 0) && (index < RNA_property_array_length(&ptr, prop))) { switch (RNA_property_type(prop)) { case PROP_BOOLEAN: value = (float)RNA_property_boolean_get_index(&ptr, prop, index); @@ -1054,6 +1055,17 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) break; } } + else { + /* out of bounds */ + if (G.debug & G_DEBUG) { + printf("Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", + id->name, dtar->rna_path, index); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } } else { /* not an array */ @@ -1074,16 +1086,19 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) break; } } - } else { + /* path couldn't be resolved */ if (G.debug & G_DEBUG) printf("Driver Evaluation Error: cannot resolve target for %s -> %s\n", id->name, dtar->rna_path); driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; return 0.0f; } + /* if we're still here, we should be ok... */ + dtar->flag &= ~DTAR_FLAG_INVALID; return value; } @@ -1122,25 +1137,39 @@ static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar) /* evaluate 'rotation difference' driver variable */ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) { + DriverTarget *dtar1 = &dvar->targets[0]; + DriverTarget *dtar2 = &dvar->targets[1]; bPoseChannel *pchan, *pchan2; float q1[4], q2[4], quat[4], angle; /* get pose channels, and check if we've got two */ - pchan = dtar_get_pchan_ptr(driver, &dvar->targets[0]); - pchan2 = dtar_get_pchan_ptr(driver, &dvar->targets[1]); + pchan = dtar_get_pchan_ptr(driver, dtar1); + pchan2 = dtar_get_pchan_ptr(driver, dtar2); if (ELEM(NULL, pchan, pchan2)) { /* disable this driver, since it doesn't work correctly... */ driver->flag |= DRIVER_FLAG_INVALID; /* check what the error was */ - if ((pchan == NULL) && (pchan2 == NULL)) + if ((pchan == NULL) && (pchan2 == NULL)) { printf("Driver Evaluation Error: Rotational difference failed - first 2 targets invalid\n"); - else if (pchan == NULL) + + dtar1->flag |= DTAR_FLAG_INVALID; + dtar2->flag |= DTAR_FLAG_INVALID; + } + else if (pchan == NULL) { printf("Driver Evaluation Error: Rotational difference failed - first target not valid PoseChannel\n"); - else if (pchan2 == NULL) + + dtar1->flag |= DTAR_FLAG_INVALID; + dtar2->flag &= ~DTAR_FLAG_INVALID; + } + else if (pchan2 == NULL) { printf("Driver Evaluation Error: Rotational difference failed - second target not valid PoseChannel\n"); + dtar1->flag &= ~DTAR_FLAG_INVALID; + dtar2->flag |= DTAR_FLAG_INVALID; + } + /* stop here... */ return 0.0f; } @@ -1163,22 +1192,53 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) { float loc1[3] = {0.0f, 0.0f, 0.0f}; float loc2[3] = {0.0f, 0.0f, 0.0f}; + short valid_targets = 0; - /* get two location values */ - /* NOTE: for now, these are all just worldspace */ + /* Perform two passes + * + * FIRST PASS - to just check that everything works... + * NOTE: we use loops here to reduce code duplication, though in practice, + * there can only be 2 items or else we run into some problems later + */ DRIVER_TARGETS_USED_LOOPER(dvar) { - /* get pointer to loc values to store in */ Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - bPoseChannel *pchan; - float tmp_loc[3]; /* check if this target has valid data */ if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { /* invalid target, so will not have enough targets */ driver->flag |= DRIVER_FLAG_INVALID; - return 0.0f; + dtar->flag |= DTAR_FLAG_INVALID; + } + else { + /* target seems to be OK now... */ + dtar->flag &= ~DTAR_FLAG_INVALID; + valid_targets++; } + } + DRIVER_TARGETS_LOOPER_END + + /* make sure we have enough valid targets to use - all or nothing for now... */ + if (valid_targets < dvar->num_targets) { + if (G.debug & G_DEBUG) { + printf("LocDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)\n", + valid_targets, dvar->targets[0].id, dvar->targets[1].id); + } + return 0.0f; + } + + + /* SECOND PASS: get two location values */ + /* NOTE: for now, these are all just worldspace */ + DRIVER_TARGETS_USED_LOOPER(dvar) + { + /* get pointer to loc values to store in */ + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + bPoseChannel *pchan; + float tmp_loc[3]; + + /* after the checks above, the targets should be valid here... */ + BLI_assert((ob != NULL) && (GS(ob->id.name) != ID_OB)); /* try to get posechannel */ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); @@ -1264,8 +1324,13 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { /* invalid target, so will not have enough targets */ driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; return 0.0f; } + else { + /* target should be valid now */ + dtar->flag &= ~DTAR_FLAG_INVALID; + } /* try to get posechannel */ pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); @@ -1458,12 +1523,12 @@ void driver_change_variable_type(DriverVar *dvar, int type) */ DRIVER_TARGETS_USED_LOOPER(dvar) { - int flags = dvti->target_flags[tarIndex]; + short flags = dvti->target_flags[tarIndex]; /* store the flags */ dtar->flag = flags; - /* object ID types only, or idtype not yet initialized*/ + /* object ID types only, or idtype not yet initialized */ if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) dtar->idtype = ID_OB; } diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 960432d6b3d..87802ab8ee6 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -923,6 +923,8 @@ void BKE_mask_free(Main *bmain, Mask *mask) SpaceLink *sl; Scene *scene; + BKE_sequencer_clear_mask_in_clipboard(mask); + for (scr = bmain->screen.first; scr; scr = scr->id.next) { for (area = scr->areabase.first; area; area = area->next) { for (sl = area->spacedata.first; sl; sl = sl->next) { diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index c9c86e6739f..f6f60d03cf7 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -887,12 +887,11 @@ static void make_edges_mdata(MVert *UNUSED(allvert), MFace *allface, MLoop *alll int old, MEdge **alledge, int *_totedge) { MPoly *mpoly; - MLoop *mloop; MFace *mface; MEdge *medge; EdgeHash *hash = BLI_edgehash_new(); struct edgesort *edsort, *ed; - int a, b, totedge = 0, final = 0; + int a, totedge = 0, final = 0; /* we put all edges in array, sort them, and detect doubles that way */ @@ -973,13 +972,16 @@ static void make_edges_mdata(MVert *UNUSED(allvert), MFace *allface, MLoop *alll mpoly = allpoly; for (a = 0; a < totpoly; a++, mpoly++) { - mloop = allloop + mpoly->loopstart; - for (b = 0; b < mpoly->totloop; b++) { - int v1, v2; - - v1 = mloop[b].v; - v2 = ME_POLY_LOOP_NEXT(mloop, mpoly, b)->v; - mloop[b].e = GET_INT_FROM_POINTER(BLI_edgehash_lookup(hash, v1, v2)); + MLoop *ml, *ml_next; + int i = mpoly->totloop; + + ml_next = allloop + mpoly->loopstart; /* first loop */ + ml = &ml_next[i - 1]; /* last loop */ + + while (i-- != 0) { + ml->e = GET_INT_FROM_POINTER(BLI_edgehash_lookup(hash, ml->v, ml_next->v)); + ml = ml_next; + ml_next++; } } diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c index 128b9ae242e..74d3645ea66 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.c @@ -908,6 +908,12 @@ int BKE_mesh_validate_dm(DerivedMesh *dm) TRUE, FALSE); } +/** + * Calculate edges from polygons + * + * \param mesh The mesh to add edges into + * \param update When true create new edges co-exist + */ void BKE_mesh_calc_edges(Mesh *mesh, int update) { CustomData edata; diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 2aeda614f07..b0566a6495f 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -424,7 +424,9 @@ ModifierData *modifiers_getLastPreview(struct Scene *scene, ModifierData *md, in return tmp_md; } -/* NOTE: these aren't used anymore */ +/* NOTE: This is to support old files from before Blender supported modifiers, + * in some cases versioning code updates these so for new files this will + * return an empty list. */ ModifierData *modifiers_getVirtualModifierList(Object *ob) { /* Kinda hacky, but should be fine since we are never diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index eceac61ddb6..5519edca7ff 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -75,12 +75,17 @@ #include "BKE_node.h" #include "BKE_image.h" /* openanim */ #include "BKE_tracking.h" +#include "BKE_sequencer.h" #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" #include "IMB_moviecache.h" +#ifdef WITH_OPENEXR +#include "intern/openexr/openexr_multi.h" +#endif + /*********************** movieclip buffer loaders *************************/ static int sequence_guess_offset(const char *full_name, int head_len, unsigned short numlen) @@ -221,6 +226,15 @@ static ImBuf *movieclip_load_sequence_file(MovieClip *clip, MovieClipUser *user, /* read ibuf */ ibuf = IMB_loadiffname(name, loadflag, colorspace); +#ifdef WITH_OPENEXR + if (ibuf) { + if (ibuf->ftype == OPENEXR && ibuf->userdata) { + IMB_exr_close(ibuf->userdata); + ibuf->userdata = NULL; + } + } +#endif + return ibuf; } @@ -1281,6 +1295,8 @@ void BKE_movieclip_build_proxy_frame(MovieClip *clip, int clip_flag, struct Movi void BKE_movieclip_free(MovieClip *clip) { + BKE_sequencer_clear_movieclip_in_clipboard(clip); + free_buffers(clip); BKE_tracking_free(&clip->tracking); diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 86fe47268d6..3dbaab008f8 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -991,6 +991,12 @@ void nodeFreeNode(bNodeTree *ntree, bNode *node) if (treetype->free_node_cache) treetype->free_node_cache(ntree, node); + + /* texture node has bad habit of keeping exec data around */ + if (ntree->type == NTREE_TEXTURE && ntree->execdata) { + ntreeTexEndExecTree(ntree->execdata, 1); + ntree->execdata = NULL; + } } /* since it is called while free database, node->id is undefined */ @@ -1040,6 +1046,7 @@ void ntreeFreeTree_ex(bNodeTree *ntree, const short do_id_user) break; case NTREE_TEXTURE: ntreeTexEndExecTree(ntree->execdata, 1); + ntree->execdata = NULL; break; } } diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 483dd2570e2..477ae27394f 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1057,10 +1057,10 @@ static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode) node->prim_indices, node->totprim); break; - case PBVH_BMESH: + case PBVH_BMESH: node->draw_buffers = - GPU_build_bmesh_buffers(bvh->flags & - PBVH_DYNTOPO_SMOOTH_SHADING); + GPU_build_bmesh_buffers(bvh->flags & + PBVH_DYNTOPO_SMOOTH_SHADING); break; } @@ -1089,12 +1089,12 @@ static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode) node->face_vert_indices, bvh->show_diffuse_color); break; - case PBVH_BMESH: + case PBVH_BMESH: GPU_update_bmesh_buffers(node->draw_buffers, - bvh->bm, - node->bm_faces, - node->bm_unique_verts, - node->bm_other_verts); + bvh->bm, + node->bm_faces, + node->bm_unique_verts, + node->bm_other_verts); break; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 666c85d2f6b..386a8b27870 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -412,7 +412,7 @@ static void pbvh_bmesh_face_remove(PBVH *bvh, BMFace *f) do { v = l_iter->v; if (pbvh_bmesh_node_vert_use_count(bvh, f_node, v) == 1) { - if (BLI_ghash_lookup(f_node->bm_unique_verts, v)) { + if (BLI_ghash_haskey(f_node->bm_unique_verts, v)) { /* Find a different node that uses 'v' */ PBVHNode *new_node; @@ -805,7 +805,8 @@ static void pbvh_bmesh_collapse_edge(PBVH *bvh, BMEdge *e, BMVert *v1, /* Ensure that v1 is in the new face's node */ if (!BLI_ghash_haskey(n->bm_unique_verts, v1) && - !BLI_ghash_haskey(n->bm_other_verts, v1)) { + !BLI_ghash_haskey(n->bm_other_verts, v1)) + { BLI_ghash_insert(n->bm_other_verts, v1, NULL); } } diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 27c5e03132e..c8947730ea8 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -3566,7 +3566,7 @@ void BKE_ptcache_update_info(PTCacheID *pid) BLI_snprintf(cache->info, sizeof(cache->info), IFACE_("%s, cache is outdated!"), mem_info); } else if (cache->flag & PTCACHE_FRAMES_SKIPPED) { - BLI_snprintf(cache->info, sizeof(cache->info), IFACE_("%s, not exact since frame %i."), + BLI_snprintf(cache->info, sizeof(cache->info), IFACE_("%s, not exact since frame %i"), mem_info, cache->last_exact); } else { diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 4c6bae122cd..d144ac927a6 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -347,6 +347,7 @@ void BKE_rigidbody_validate_sim_shape(Object *ob, short rebuild) float capsule_height; float hull_margin = 0.0f; bool can_embed = true; + bool has_volume; /* sanity check */ if (rbo == NULL) @@ -404,11 +405,13 @@ void BKE_rigidbody_validate_sim_shape(Object *ob, short rebuild) case RB_SHAPE_CONVEXH: /* try to emged collision margin */ - if (!(rbo->flag & RBO_FLAG_USE_MARGIN)) + has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f); + + if (!(rbo->flag & RBO_FLAG_USE_MARGIN) && has_volume) hull_margin = 0.04f; new_shape = rigidbody_get_shape_convexhull_from_mesh(ob, hull_margin, &can_embed); if (!(rbo->flag & RBO_FLAG_USE_MARGIN)) - rbo->margin = (can_embed) ? 0.04f : 0.0f; /* RB_TODO ideally we shouldn't directly change the margin here */ + rbo->margin = (can_embed && has_volume) ? 0.04f : 0.0f; /* RB_TODO ideally we shouldn't directly change the margin here */ break; case RB_SHAPE_TRIMESH: new_shape = rigidbody_get_shape_trimesh_from_mesh(ob); @@ -1217,10 +1220,10 @@ void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw) /* ------------------ */ -/* Run RigidBody simulation for the specified physics world */ -void BKE_rigidbody_do_simulation(Scene *scene, float ctime) +/* Rebuild rigid body world */ +/* NOTE: this needs to be called before frame update to work correctly */ +void BKE_rigidbody_rebuild_world(Scene *scene, float ctime) { - float timestep; RigidBodyWorld *rbw = scene->rigidbody_world; PointCache *cache; PTCacheID pid; @@ -1230,16 +1233,12 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime) BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL); cache = rbw->pointcache; - rbw->flag &= ~RBW_FLAG_FRAME_UPDATE; - /* flag cache as outdated if we don't have a world or number of objects in the simulation has changed */ if (rbw->physics_world == NULL || rbw->numbodies != BLI_countlist(&rbw->group->gobject)) { cache->flag |= PTCACHE_OUTDATED; } - if (ctime <= startframe) { - rbw->ltime = startframe; - /* reset and rebuild simulation if necessary */ + if (ctime <= startframe + 1 && rbw->ltime == startframe) { if (cache->flag & PTCACHE_OUTDATED) { BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); rigidbody_update_simulation(scene, rbw, true); @@ -1247,12 +1246,25 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime) cache->last_exact = 0; cache->flag &= ~PTCACHE_REDO_NEEDED; } - return; } - /* rebuild world if it's outdated on second frame */ - else if (ctime == startframe + 1 && rbw->ltime == startframe && cache->flag & PTCACHE_OUTDATED) { - BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); - rigidbody_update_simulation(scene, rbw, true); +} + +/* Run RigidBody simulation for the specified physics world */ +void BKE_rigidbody_do_simulation(Scene *scene, float ctime) +{ + float timestep; + RigidBodyWorld *rbw = scene->rigidbody_world; + PointCache *cache; + PTCacheID pid; + int startframe, endframe; + + BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw); + BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL); + cache = rbw->pointcache; + + if (ctime <= startframe) { + rbw->ltime = startframe; + return; } /* make sure we don't go out of cache frame range */ else if (ctime > endframe) { diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index e1e4563e89f..19eea279f79 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -306,6 +306,9 @@ void BKE_scene_free(Scene *sce) { Base *base; + /* check all sequences */ + BKE_sequencer_clear_scene_in_allseqs(G.main, sce); + base = sce->base.first; while (base) { base->object->id.us--; @@ -385,10 +388,7 @@ void BKE_scene_free(Scene *sce) sce->toolsettings = NULL; } - if (sce->theDag) { - free_forest(sce->theDag); - MEM_freeN(sce->theDag); - } + DAG_scene_free(sce); if (sce->nodetree) { ntreeFreeTree(sce->nodetree); @@ -722,9 +722,6 @@ void BKE_scene_unlink(Main *bmain, Scene *sce, Scene *newsce) if (sce1->set == sce) sce1->set = NULL; - /* check all sequences */ - BKE_sequencer_clear_scene_in_allseqs(bmain, sce); - /* check render layer nodes in other scenes */ clear_scene_in_nodes(bmain, sce); @@ -1110,13 +1107,22 @@ static void scene_depsgraph_hack(Scene *scene, Scene *scene_parent) } -static void scene_flag_rbw_recursive(Scene *scene) +static void scene_rebuild_rbw_recursive(Scene *scene, float ctime) { if (scene->set) - scene_flag_rbw_recursive(scene->set); + scene_rebuild_rbw_recursive(scene->set, ctime); if (BKE_scene_check_rigidbody_active(scene)) - scene->rigidbody_world->flag |= RBW_FLAG_FRAME_UPDATE; + BKE_rigidbody_rebuild_world(scene, ctime); +} + +static void scene_do_rb_simulation_recursive(Scene *scene, float ctime) +{ + if (scene->set) + scene_do_rb_simulation_recursive(scene->set, ctime); + + if (BKE_scene_check_rigidbody_active(scene)) + BKE_rigidbody_do_simulation(scene, ctime); } static void scene_update_tagged_recursive(Main *bmain, Scene *scene, Scene *scene_parent) @@ -1130,22 +1136,6 @@ static void scene_update_tagged_recursive(Main *bmain, Scene *scene, Scene *scen if (scene->set) scene_update_tagged_recursive(bmain, scene->set, scene_parent); - /* run rigidbody sim - * - calculate/read values from cache into RBO's, to get flushed - * later when objects are evaluated (if they're tagged for eval) - */ - // XXX: this position may still change, objects not being updated correctly before simulation is run - // NOTE: current position is so that rigidbody sim affects other objects - if (BKE_scene_check_rigidbody_active(scene) && scene->rigidbody_world->flag & RBW_FLAG_FRAME_UPDATE) { - /* we use frame time of parent (this is "scene" itself for top-level of sets recursion), - * as that is the active scene controlling all timing in file at the moment - */ - float ctime = BKE_scene_frame_get(scene_parent); - - /* however, "scene" contains the rigidbody world needed for eval... */ - BKE_rigidbody_do_simulation(scene, ctime); - } - /* scene objects */ for (base = scene->base.first; base; base = base->next) { Object *ob = base->object; @@ -1224,6 +1214,12 @@ void BKE_scene_update_for_newframe(Main *bmain, Scene *sce, unsigned int lay) { float ctime = BKE_scene_frame_get(sce); Scene *sce_iter; + + /* rebuild rigid body worlds before doing the actual frame update + * this needs to be done on start frame but animation playback usually starts one frame later + * we need to do it here to avoid rebuilding the world on every simulation change, which can be very expensive + */ + scene_rebuild_rbw_recursive(sce, ctime); /* keep this first */ BLI_callback_exec(bmain, &sce->id, BLI_CB_EVT_FRAME_CHANGE_PRE); @@ -1263,8 +1259,9 @@ void BKE_scene_update_for_newframe(Main *bmain, Scene *sce, unsigned int lay) tag_main_idcode(bmain, ID_MA, FALSE); tag_main_idcode(bmain, ID_LA, FALSE); - /* flag rigid body worlds for update */ - scene_flag_rbw_recursive(sce); + /* run rigidbody sim */ + /* NOTE: current position is so that rigidbody sim affects other objects, might change in the future */ + scene_do_rb_simulation_recursive(sce, ctime); /* BKE_object_handle_update() on all objects, groups and sets */ scene_update_tagged_recursive(bmain, sce, sce); diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c index 9ea405ef636..a64a4895e9b 100644 --- a/source/blender/blenkernel/intern/seqmodifier.c +++ b/source/blender/blenkernel/intern/seqmodifier.c @@ -237,9 +237,9 @@ static void curves_apply_threaded(int width, int height, unsigned char *rect, fl rgb_uchar_to_float(t, mask_rect + pixel_index); - tempc[0] = pixel[0] * (1.0f - t[0]) + result[0] * t[0]; - tempc[1] = pixel[1] * (1.0f - t[1]) + result[1] * t[1]; - tempc[2] = pixel[2] * (1.0f - t[2]) + result[2] * t[2]; + tempc[0] = tempc[0] * (1.0f - t[0]) + result[0] * t[0]; + tempc[1] = tempc[1] * (1.0f - t[1]) + result[1] * t[1]; + tempc[2] = tempc[2] * (1.0f - t[2]) + result[2] * t[2]; } else { tempc[0] = result[0]; @@ -438,7 +438,7 @@ static void brightcontrast_apply_threaded(int width, int height, unsigned char * unsigned char *m = mask_rect + pixel_index; float t = (float) m[c] / 255.0f; - v = (float) pixel[c] * (1.0f - t) + v * t; + v = (float) pixel[c] / 255.0f * (1.0f - t) + v * t; } pixel[c] = FTOCHAR(v); diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 68618287546..613c02c173b 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -815,6 +815,33 @@ void BKE_sequencer_clear_scene_in_allseqs(Main *bmain, Scene *scene) BKE_sequencer_base_recursive_apply(&scene_iter->ed->seqbase, clear_scene_in_allseqs_cb, scene); } } + + /* also clear clipboard */ + BKE_sequencer_base_recursive_apply(&seqbase_clipboard, clear_scene_in_allseqs_cb, scene); +} + +static int clear_movieclip_in_clipboard_cb(Sequence *seq, void *arg_pt) +{ + if (seq->clip == (MovieClip *)arg_pt) + seq->clip = NULL; + return 1; +} + +void BKE_sequencer_clear_movieclip_in_clipboard(MovieClip *clip) +{ + BKE_sequencer_base_recursive_apply(&seqbase_clipboard, clear_movieclip_in_clipboard_cb, clip); +} + +static int clear_mask_in_clipboard_cb(Sequence *seq, void *arg_pt) +{ + if (seq->mask == (Mask *)arg_pt) + seq->mask = NULL; + return 1; +} + +void BKE_sequencer_clear_mask_in_clipboard(Mask *mask) +{ + BKE_sequencer_base_recursive_apply(&seqbase_clipboard, clear_mask_in_clipboard_cb, mask); } typedef struct SeqUniqueInfo { @@ -1544,8 +1571,11 @@ static void color_balance_byte_byte(StripColorBalance *cb_, unsigned char *rect, for (c = 0; c < 3; c++) { float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); - if (m) - p[c] = p[c] * (1.0f - (float)m[c] / 255.0f) + t * m[c]; + if (m) { + float m_normal = (float) m[c] / 255.0f; + + p[c] = p[c] * (1.0f - m_normal) + t * m_normal; + } else p[c] = t; } diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 74c0a76f82d..184984352ba 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -2560,6 +2560,7 @@ int txt_replace_char(Text *text, unsigned int add) memcpy(text->curl->line + text->curc, ch, add_size); text->curc += add_size; + text->curl->len += add_size - del_size; txt_pop_sel(text); txt_make_dirty(text); diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 1a7458f531c..f63a1f2cec0 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -1429,6 +1429,27 @@ void BKE_tracking_camera_get_reconstructed_interpolate(MovieTracking *tracking, /*********************** Distortion/Undistortion *************************/ +#ifdef WITH_LIBMV +static void cameraIntrinscisOptionsFromTracking(libmv_cameraIntrinsicsOptions *camera_intrinsics_options, + MovieTracking *tracking, int calibration_width, int calibration_height) +{ + MovieTrackingCamera *camera = &tracking->camera; + float aspy = 1.0f / tracking->camera.pixel_aspect; + + camera_intrinsics_options->focal_length = camera->focal; + + camera_intrinsics_options->principal_point_x = camera->principal[0]; + camera_intrinsics_options->principal_point_y = camera->principal[1] * aspy; + + camera_intrinsics_options->k1 = camera->k1; + camera_intrinsics_options->k2 = camera->k2; + camera_intrinsics_options->k3 = camera->k3; + + camera_intrinsics_options->image_width = calibration_width; + camera_intrinsics_options->image_height = (double) calibration_height * aspy; +} +#endif + MovieDistortion *BKE_tracking_distortion_new(void) { MovieDistortion *distortion; @@ -1441,28 +1462,23 @@ MovieDistortion *BKE_tracking_distortion_new(void) void BKE_tracking_distortion_update(MovieDistortion *distortion, MovieTracking *tracking, int calibration_width, int calibration_height) { - MovieTrackingCamera *camera = &tracking->camera; - float aspy = 1.0f / tracking->camera.pixel_aspect; - #ifdef WITH_LIBMV + libmv_cameraIntrinsicsOptions camera_intrinsics_options; + + cameraIntrinscisOptionsFromTracking(&camera_intrinsics_options, tracking, + calibration_width, calibration_height); + if (!distortion->intrinsics) { - distortion->intrinsics = libmv_CameraIntrinsicsNew(camera->focal, - camera->principal[0], camera->principal[1] * aspy, - camera->k1, camera->k2, camera->k3, - calibration_width, calibration_height * aspy); + distortion->intrinsics = libmv_CameraIntrinsicsNew(&camera_intrinsics_options); } else { - libmv_CameraIntrinsicsUpdate(distortion->intrinsics, camera->focal, - camera->principal[0], camera->principal[1] * aspy, - camera->k1, camera->k2, camera->k3, - calibration_width, calibration_height * aspy); + libmv_CameraIntrinsicsUpdate(distortion->intrinsics, &camera_intrinsics_options); } #else (void) distortion; + (void) tracking; (void) calibration_width; (void) calibration_height; - (void) camera; - (void) aspy; #endif } @@ -1543,15 +1559,17 @@ void BKE_tracking_distort_v2(MovieTracking *tracking, const float co[2], float r MovieTrackingCamera *camera = &tracking->camera; #ifdef WITH_LIBMV + libmv_cameraIntrinsicsOptions camera_intrinsics_options; double x, y; float aspy = 1.0f / tracking->camera.pixel_aspect; + cameraIntrinscisOptionsFromTracking(&camera_intrinsics_options, tracking, 0, 0); + /* normalize coords */ x = (co[0] - camera->principal[0]) / camera->focal; y = (co[1] - camera->principal[1] * aspy) / camera->focal; - libmv_applyCameraIntrinsics(camera->focal, camera->principal[0], camera->principal[1] * aspy, - camera->k1, camera->k2, camera->k3, x, y, &x, &y); + libmv_applyCameraIntrinsics(&camera_intrinsics_options, x, y, &x, &y); /* result is in image coords already */ r_co[0] = x; @@ -1568,11 +1586,13 @@ void BKE_tracking_undistort_v2(MovieTracking *tracking, const float co[2], float MovieTrackingCamera *camera = &tracking->camera; #ifdef WITH_LIBMV + libmv_cameraIntrinsicsOptions camera_intrinsics_options; double x = co[0], y = co[1]; float aspy = 1.0f / tracking->camera.pixel_aspect; - libmv_InvertIntrinsics(camera->focal, camera->principal[0], camera->principal[1] * aspy, - camera->k1, camera->k2, camera->k3, x, y, &x, &y); + cameraIntrinscisOptionsFromTracking(&camera_intrinsics_options, tracking, 0, 0); + + libmv_InvertIntrinsics(&camera_intrinsics_options, x, y, &x, &y); r_co[0] = x * camera->focal + camera->principal[0]; r_co[1] = y * camera->focal + camera->principal[1] * aspy; @@ -2642,6 +2662,8 @@ typedef struct MovieReconstructContext { float principal_point[2]; float k1, k2, k3; + int width, height; + float reprojection_error; TracksMap *tracks_map; @@ -2702,11 +2724,13 @@ static void reconstruct_retrieve_libmv_intrinscis(MovieReconstructContext *conte &k1, &k2, &k3, &width, &height); tracking->camera.focal = focal_length; - tracking->camera.principal[0] = principal_x; + tracking->camera.principal[0] = principal_x; tracking->camera.principal[1] = principal_y / aspy; + tracking->camera.k1 = k1; tracking->camera.k2 = k2; + tracking->camera.k3 = k3; } static int reconstruct_retrieve_libmv_tracks(MovieReconstructContext *context, MovieTracking *tracking) @@ -2840,10 +2864,10 @@ static int reconstruct_refine_intrinsics_get_flags(MovieTracking *tracking, Movi flags |= LIBMV_REFINE_PRINCIPAL_POINT; if (refine & REFINE_RADIAL_DISTORTION_K1) - flags |= REFINE_RADIAL_DISTORTION_K1; + flags |= LIBMV_REFINE_RADIAL_DISTORTION_K1; if (refine & REFINE_RADIAL_DISTORTION_K2) - flags |= REFINE_RADIAL_DISTORTION_K2; + flags |= LIBMV_REFINE_RADIAL_DISTORTION_K2; return flags; } @@ -2914,6 +2938,9 @@ MovieReconstructContext *BKE_tracking_reconstruction_context_new(MovieTracking * context->principal_point[0] = camera->principal[0]; context->principal_point[1] = camera->principal[1] * aspy; + context->width = width; + context->height = height; + context->k1 = camera->k1; context->k2 = camera->k2; context->k3 = camera->k3; @@ -2996,6 +3023,34 @@ static void reconstruct_update_solve_cb(void *customdata, double progress, const BLI_snprintf(progressdata->stats_message, progressdata->message_size, "Solving camera | %s", message); } + +static void camraIntrincicsOptionsFromContext(libmv_cameraIntrinsicsOptions *camera_intrinsics_options, + MovieReconstructContext *context) +{ + camera_intrinsics_options->focal_length = context->focal_length; + + camera_intrinsics_options->principal_point_x = context->principal_point[0]; + camera_intrinsics_options->principal_point_y = context->principal_point[1]; + + camera_intrinsics_options->k1 = context->k1; + camera_intrinsics_options->k2 = context->k2; + camera_intrinsics_options->k3 = context->k3; + + camera_intrinsics_options->image_width = context->width; + camera_intrinsics_options->image_height = context->height; +} + +static void reconstructionOptionsFromContext(libmv_reconstructionOptions *reconstruction_options, + MovieReconstructContext *context) +{ + reconstruction_options->keyframe1 = context->keyframe1; + reconstruction_options->keyframe2 = context->keyframe2; + + reconstruction_options->refine_intrinsics = context->refine_flags; + + reconstruction_options->success_threshold = context->success_threshold; + reconstruction_options->use_fallback_reconstruction = context->use_fallback_reconstruction; +} #endif void BKE_tracking_reconstruction_solve(MovieReconstructContext *context, short *stop, short *do_update, @@ -3006,32 +3061,28 @@ void BKE_tracking_reconstruction_solve(MovieReconstructContext *context, short * ReconstructProgressData progressdata; + libmv_cameraIntrinsicsOptions camera_intrinsics_options; + libmv_reconstructionOptions reconstruction_options; + progressdata.stop = stop; progressdata.do_update = do_update; progressdata.progress = progress; progressdata.stats_message = stats_message; progressdata.message_size = message_size; + camraIntrincicsOptionsFromContext(&camera_intrinsics_options, context); + reconstructionOptionsFromContext(&reconstruction_options, context); + if (context->motion_flag & TRACKING_MOTION_MODAL) { context->reconstruction = libmv_solveModal(context->tracks, - context->focal_length, - context->principal_point[0], context->principal_point[1], - context->k1, context->k2, context->k3, + &camera_intrinsics_options, + &reconstruction_options, reconstruct_update_solve_cb, &progressdata); } else { - struct libmv_reconstructionOptions options; - - options.success_threshold = context->success_threshold; - options.use_fallback_reconstruction = context->use_fallback_reconstruction; - context->reconstruction = libmv_solveReconstruction(context->tracks, - context->keyframe1, context->keyframe2, - context->refine_flags, - context->focal_length, - context->principal_point[0], context->principal_point[1], - context->k1, context->k2, context->k3, - &options, + &camera_intrinsics_options, + &reconstruction_options, reconstruct_update_solve_cb, &progressdata); } diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index c4e17b1ed48..13f47d94160 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -260,6 +260,11 @@ MINLINE void madd_sh_shfl(float r[9], const float sh[3], const float f); /********************************* Form Factor *******************************/ +float form_factor_quad(const float p[3], const float n[3], + const float q0[3], const float q1[3], const float q2[3], const float q3[3]); +int form_factor_visible_quad(const float p[3], const float n[3], + const float v0[3], const float v1[3], const float v2[3], + float q0[3], float q1[3], float q2[3], float q3[3]); float form_factor_hemi_poly(float p[3], float n[3], float v1[3], float v2[3], float v3[3], float v4[3]); diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index 95ad786c7c2..bbafd28ddbc 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -399,7 +399,8 @@ typedef bool _BLI_Bool; # define BLI_assert(a) (void)0 #endif -#if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 406)) /* gcc4.6+ only */ +/* C++ can't use _Static_assert, expects static_assert() but c++0x only */ +#if (!defined(__cplusplus)) && (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 406)) /* gcc4.6+ only */ # define BLI_STATIC_ASSERT(a, msg) _Static_assert(a, msg); #else /* TODO msvc, clang */ diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index baca7bb8f8a..8f7ecdcfde8 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -3171,9 +3171,9 @@ static void vec_add_dir(float r[3], const float v1[3], const float v2[3], const r[2] = v1[2] + fac * (v2[2] - v1[2]); } -static int ff_visible_quad(const float p[3], const float n[3], - const float v0[3], const float v1[3], const float v2[3], - float q0[3], float q1[3], float q2[3], float q3[3]) +int form_factor_visible_quad(const float p[3], const float n[3], + const float v0[3], const float v1[3], const float v2[3], + float q0[3], float q1[3], float q2[3], float q3[3]) { static const float epsilon = 1e-6f; float c, sd[3]; @@ -3532,8 +3532,8 @@ static void ff_normalize(float n[3]) } } -static float ff_quad_form_factor(const float p[3], const float n[3], - const float q0[3], const float q1[3], const float q2[3], const float q3[3]) +float form_factor_quad(const float p[3], const float n[3], + const float q0[3], const float q1[3], const float q2[3], const float q3[3]) { float r0[3], r1[3], r2[3], r3[3], g0[3], g1[3], g2[3], g3[3]; float a1, a2, a3, a4, dot1, dot2, dot3, dot4, result; @@ -3579,11 +3579,11 @@ float form_factor_hemi_poly(float p[3], float n[3], float v1[3], float v2[3], fl * covered by a quad or triangle, cosine weighted */ float q0[3], q1[3], q2[3], q3[3], contrib = 0.0f; - if (ff_visible_quad(p, n, v1, v2, v3, q0, q1, q2, q3)) - contrib += ff_quad_form_factor(p, n, q0, q1, q2, q3); + if (form_factor_visible_quad(p, n, v1, v2, v3, q0, q1, q2, q3)) + contrib += form_factor_quad(p, n, q0, q1, q2, q3); - if (v4 && ff_visible_quad(p, n, v1, v3, v4, q0, q1, q2, q3)) - contrib += ff_quad_form_factor(p, n, q0, q1, q2, q3); + if (v4 && form_factor_visible_quad(p, n, v1, v3, v4, q0, q1, q2, q3)) + contrib += form_factor_quad(p, n, q0, q1, q2, q3); return contrib; } diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c index 686484ef1db..a74f82ae965 100644 --- a/source/blender/blenlib/intern/threads.c +++ b/source/blender/blenlib/intern/threads.c @@ -429,12 +429,16 @@ void BLI_spin_unlock(SpinLock *spin) #endif } +#ifndef __APPLE__ void BLI_spin_end(SpinLock *spin) { -#ifndef __APPLE__ pthread_spin_destroy(spin); -#endif } +#else +void BLI_spin_end(SpinLock *UNUSED(spin)) +{ +} +#endif /* Read/Write Mutex Lock */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 44a73fa14a0..b804a751b18 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -7564,6 +7564,23 @@ static void do_version_node_fix_translate_wrapping(void *UNUSED(data), ID *UNUSE } } +static void do_version_node_straight_image_alpha_workaround(void *data, ID *UNUSED(id), bNodeTree *ntree) +{ + FileData *fd = (FileData *) data; + bNode *node; + + for (node = ntree->nodes.first; node; node = node->next) { + if (node->type == CMP_NODE_IMAGE) { + Image *image = blo_do_versions_newlibadr(fd, ntree->id.lib, node->id); + + if (image) { + if ((image->flag & IMA_DO_PREMUL) == 0 && image->alpha_mode == IMA_ALPHA_STRAIGHT) + node->custom1 |= CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT; + } + } + } +} + static void do_version_node_fix_internal_links_264(void *UNUSED(data), ID *UNUSED(id), bNodeTree *ntree) { bNode *node; @@ -7954,7 +7971,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) prop = BKE_bproperty_object_get(ob, "Text"); if (prop) { BKE_reportf_wrap(fd->reports, RPT_WARNING, - TIP_("Game property name conflict in object '%s':\ntext objects reserve the " + TIP_("Game property name conflict in object '%s': text objects reserve the " "['Text'] game property to change their content through logic bricks"), ob->id.name + 2); } @@ -8857,6 +8874,8 @@ static void do_versions(FileData *fd, Library *lib, Main *main) Scene *scene; Image *image, *nimage; Tex *tex, *otex; + bNodeTreeType *ntreetype; + bNodeTree *ntree; for (scene = main->scene.first; scene; scene = scene->id.next) { Sequence *seq; @@ -8961,6 +8980,13 @@ static void do_versions(FileData *fd, Library *lib, Main *main) for (image = main->image.first; image; image = image->id.next) image->flag &= ~IMA_DONE_TAG; + + ntreetype = ntreeGetType(NTREE_COMPOSIT); + if (ntreetype && ntreetype->foreach_nodetree) + ntreetype->foreach_nodetree(main, fd, do_version_node_straight_image_alpha_workaround); + + for (ntree = main->nodetree.first; ntree; ntree = ntree->id.next) + do_version_node_straight_image_alpha_workaround(fd, NULL, ntree); } if (main->versionfile < 265 || (main->versionfile == 265 && main->subversionfile < 7)) { @@ -10450,7 +10476,8 @@ static ID *append_named_part(Main *mainl, FileData *fd, const char *idname, cons } else { /* already linked */ - printf("append: already linked\n"); + if (G.debug) + printf("append: already linked\n"); oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); if (id->flag & LIB_INDIRECT) { id->flag -= LIB_INDIRECT; diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h index eef6544be89..faa109617d5 100644 --- a/source/blender/bmesh/intern/bmesh_iterators_inline.h +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h @@ -80,61 +80,61 @@ BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *da BLI_assert(data != NULL); iter->begin = bmiter__edge_of_vert_begin; iter->step = bmiter__edge_of_vert_step; - iter->vdata = data; + iter->vdata = (BMVert *)data; break; case BM_FACES_OF_VERT: BLI_assert(data != NULL); iter->begin = bmiter__face_of_vert_begin; iter->step = bmiter__face_of_vert_step; - iter->vdata = data; + iter->vdata = (BMVert *)data; break; case BM_LOOPS_OF_VERT: BLI_assert(data != NULL); iter->begin = bmiter__loop_of_vert_begin; iter->step = bmiter__loop_of_vert_step; - iter->vdata = data; + iter->vdata = (BMVert *)data; break; case BM_VERTS_OF_EDGE: BLI_assert(data != NULL); iter->begin = bmiter__vert_of_edge_begin; iter->step = bmiter__vert_of_edge_step; - iter->edata = data; + iter->edata = (BMEdge *)data; break; case BM_FACES_OF_EDGE: BLI_assert(data != NULL); iter->begin = bmiter__face_of_edge_begin; iter->step = bmiter__face_of_edge_step; - iter->edata = data; + iter->edata = (BMEdge *)data; break; case BM_VERTS_OF_FACE: BLI_assert(data != NULL); iter->begin = bmiter__vert_of_face_begin; iter->step = bmiter__vert_of_face_step; - iter->pdata = data; + iter->pdata = (BMFace *)data; break; case BM_EDGES_OF_FACE: BLI_assert(data != NULL); iter->begin = bmiter__edge_of_face_begin; iter->step = bmiter__edge_of_face_step; - iter->pdata = data; + iter->pdata = (BMFace *)data; break; case BM_LOOPS_OF_FACE: BLI_assert(data != NULL); iter->begin = bmiter__loop_of_face_begin; iter->step = bmiter__loop_of_face_step; - iter->pdata = data; + iter->pdata = (BMFace *)data; break; case BM_LOOPS_OF_LOOP: BLI_assert(data != NULL); iter->begin = bmiter__loops_of_loop_begin; iter->step = bmiter__loops_of_loop_step; - iter->ldata = data; + iter->ldata = (BMLoop *)data; break; case BM_LOOPS_OF_EDGE: BLI_assert(data != NULL); iter->begin = bmiter__loops_of_edge_begin; iter->step = bmiter__loops_of_edge_step; - iter->edata = data; + iter->edata = (BMEdge *)data; break; default: /* should never happen */ diff --git a/source/blender/bmesh/operators/bmo_hull.c b/source/blender/bmesh/operators/bmo_hull.c index 5d8689b9da6..5e2404c9919 100644 --- a/source/blender/bmesh/operators/bmo_hull.c +++ b/source/blender/bmesh/operators/bmo_hull.c @@ -154,8 +154,9 @@ static void hull_output_triangles(BMesh *bm, GHash *hull_triangles) const int next = (i == 2 ? 0 : i + 1); BMEdge *e = BM_edge_exists(t->v[i], t->v[next]); if (e && - BMO_elem_flag_test(bm, e, HULL_FLAG_INPUT) && - !BMO_elem_flag_test(bm, e, HULL_FLAG_HOLE)) { + BMO_elem_flag_test(bm, e, HULL_FLAG_INPUT) && + !BMO_elem_flag_test(bm, e, HULL_FLAG_HOLE)) + { BMO_elem_flag_enable(bm, e, HULL_FLAG_OUTPUT_GEOM); } } diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp index c270a1e6b20..221a5008f10 100644 --- a/source/blender/collada/ArmatureImporter.cpp +++ b/source/blender/collada/ArmatureImporter.cpp @@ -49,8 +49,8 @@ static const char *bc_get_joint_name(T *node) return id.size() ? id.c_str() : node->getOriginalId().c_str(); } -ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, AnimationImporterBase *anim, Scene *sce) : - TransformReader(conv), scene(sce), empty(NULL), mesh_importer(mesh), anim_importer(anim) { +ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, Scene *sce) : + TransformReader(conv), scene(sce), empty(NULL), mesh_importer(mesh) { } ArmatureImporter::~ArmatureImporter() diff --git a/source/blender/collada/ArmatureImporter.h b/source/blender/collada/ArmatureImporter.h index b07edfbf34d..2a8f1a65e21 100644 --- a/source/blender/collada/ArmatureImporter.h +++ b/source/blender/collada/ArmatureImporter.h @@ -96,7 +96,6 @@ private: std::map<COLLADAFW::UniqueId, Object*> unskinned_armature_map; MeshImporterBase *mesh_importer; - AnimationImporterBase *anim_importer; // This is used to store data passed in write_controller_data. // Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members @@ -138,7 +137,7 @@ private: TagsMap uid_tags_map; public: - ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, AnimationImporterBase *anim, Scene *sce); + ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, Scene *sce); ~ArmatureImporter(); void add_joint(COLLADAFW::Node *node, bool root, Object *parent, Scene *sce); diff --git a/source/blender/collada/CMakeLists.txt b/source/blender/collada/CMakeLists.txt index 326ca2b9937..8747d2995f2 100644 --- a/source/blender/collada/CMakeLists.txt +++ b/source/blender/collada/CMakeLists.txt @@ -38,7 +38,7 @@ set(INC ../../../intern/guardedalloc ../ikplugin ../../../intern/iksolver/extern - + ../bmesh ) set(INC_SYS diff --git a/source/blender/collada/ControllerExporter.cpp b/source/blender/collada/ControllerExporter.cpp index 41693d4d680..6b367b9cea9 100644 --- a/source/blender/collada/ControllerExporter.cpp +++ b/source/blender/collada/ControllerExporter.cpp @@ -201,13 +201,12 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) bool use_instantiation = this->export_settings->use_object_instantiation; Mesh *me; - if (this->export_settings->apply_modifiers) - me = bc_to_mesh_apply_modifiers(scene, ob, this->export_settings->export_mesh_type); - else - me = (Mesh *)ob->data; + me = bc_get_mesh_copy(scene, + ob, + this->export_settings->export_mesh_type, + this->export_settings->apply_modifiers, + this->export_settings->triangulate); - BKE_mesh_tessface_ensure(me); - if (!me->dvert) return; std::string controller_name = id_name(ob_arm); @@ -239,6 +238,7 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) joint_index_by_def_index.push_back(-1); } + int oob_counter = 0; for (i = 0; i < me->totvert; i++) { MDeformVert *vert = &me->dvert[i]; std::map<int, float> jw; @@ -248,11 +248,18 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) for (j = 0; j < vert->totweight; j++) { int idx = vert->dw[j].def_nr; - if (idx >= 0) { - int joint_index = joint_index_by_def_index[idx]; - if (joint_index != -1 && vert->dw[j].weight > 0.0f) { - jw[joint_index] += vert->dw[j].weight; - sumw += vert->dw[j].weight; + if (idx >= joint_index_by_def_index.size()) { + // XXX: Maybe better find out where and + // why the Out Of Bound indexes get created ? + oob_counter += 1; + } + else { + if (idx >= 0) { + int joint_index = joint_index_by_def_index[idx]; + if (joint_index != -1 && vert->dw[j].weight > 0.0f) { + jw[joint_index] += vert->dw[j].weight; + sumw += vert->dw[j].weight; + } } } } @@ -274,16 +281,18 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) #endif } } + + if (oob_counter > 0) { + fprintf(stderr, "Ignored %d Vertex weigths which use index to non existing VGroup %ld.\n", oob_counter, joint_index_by_def_index.size()); + } } std::string weights_source_id = add_weights_source(me, controller_id, weights); add_joints_element(&ob->defbase, joints_source_id, inv_bind_mat_source_id); add_vertex_weights_element(weights_source_id, joints_source_id, vcounts, joints); - if (this->export_settings->apply_modifiers) - { - BKE_libblock_free_us(&(G.main->mesh), me); - } + BKE_libblock_free_us(&(G.main->mesh), me); + closeSkin(); closeController(); } @@ -293,13 +302,11 @@ void ControllerExporter::export_morph_controller(Object *ob, Key *key) bool use_instantiation = this->export_settings->use_object_instantiation; Mesh *me; - if (this->export_settings->apply_modifiers) { - me = bc_to_mesh_apply_modifiers(scene, ob, this->export_settings->export_mesh_type); - } - else { - me = (Mesh *)ob->data; - } - BKE_mesh_tessface_ensure(me); + me = bc_get_mesh_copy(scene, + ob, + this->export_settings->export_mesh_type, + this->export_settings->apply_modifiers, + this->export_settings->triangulate); std::string controller_name = id_name(ob) + "-morph"; std::string controller_id = get_controller_id(key, ob); @@ -320,10 +327,8 @@ void ControllerExporter::export_morph_controller(Object *ob, Key *key) COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, morph_weights_id))); targets.add(); - if (this->export_settings->apply_modifiers) - { - BKE_libblock_free_us(&(G.main->mesh), me); - } + BKE_libblock_free_us(&(G.main->mesh), me); + //support for animations //can also try the base element and param alternative diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp index 2b906fa9ac2..49db6f033d9 100644 --- a/source/blender/collada/DocumentImporter.cpp +++ b/source/blender/collada/DocumentImporter.cpp @@ -104,7 +104,7 @@ DocumentImporter::DocumentImporter(bContext *C, const ImportSettings *import_set import_settings(import_settings), mImportStage(General), mContext(C), - armature_importer(&unit_converter, &mesh_importer, &anim_importer, CTX_data_scene(C)), + armature_importer(&unit_converter, &mesh_importer, CTX_data_scene(C)), mesh_importer(&unit_converter, &armature_importer, CTX_data_scene(C)), anim_importer(&unit_converter, &armature_importer, CTX_data_scene(C)) { @@ -157,7 +157,8 @@ bool DocumentImporter::import() delete ehandler; - mesh_importer.bmeshConversion(); + //XXX No longer needed (geometries are now created as bmesh) + //mesh_importer.bmeshConversion(); return true; } diff --git a/source/blender/collada/ExportSettings.h b/source/blender/collada/ExportSettings.h index cf45b9b8391..ec71fb2747c 100644 --- a/source/blender/collada/ExportSettings.h +++ b/source/blender/collada/ExportSettings.h @@ -45,6 +45,7 @@ public: bool include_material_textures; bool use_texture_copies; + bool triangulate; bool use_object_instantiation; bool sort_by_name; bool second_life; diff --git a/source/blender/collada/GeometryExporter.cpp b/source/blender/collada/GeometryExporter.cpp index 4a6c5b43ce2..751628acf53 100644 --- a/source/blender/collada/GeometryExporter.cpp +++ b/source/blender/collada/GeometryExporter.cpp @@ -57,7 +57,6 @@ GeometryExporter::GeometryExporter(COLLADASW::StreamWriter *sw, const ExportSett { } - void GeometryExporter::exportGeom(Scene *sce) { openLibrary(); @@ -77,18 +76,16 @@ void GeometryExporter::operator()(Object *ob) #endif bool use_instantiation = this->export_settings->use_object_instantiation; - Mesh *me; - if (this->export_settings->apply_modifiers) { - me = bc_to_mesh_apply_modifiers(mScene, ob, this->export_settings->export_mesh_type); - } - else { - me = (Mesh *)ob->data; - } - BKE_mesh_tessface_ensure(me); + bool triangulate = this->export_settings->triangulate; + Mesh *me = bc_get_mesh_copy( mScene, + ob, + this->export_settings->export_mesh_type, + this->export_settings->apply_modifiers, + this->export_settings->triangulate); std::string geom_id = get_geometry_id(ob, use_instantiation); std::vector<Normal> nor; - std::vector<Face> norind; + std::vector<BCPolygonNormalsIndices> norind; // Skip if linked geometry was already exported from another reference if (use_instantiation && @@ -117,12 +114,13 @@ void GeometryExporter::operator()(Object *ob) bool has_uvs = (bool)CustomData_has_layer(&me->fdata, CD_MTFACE); // writes <source> for uv coords if mesh has uv coords - if (has_uvs) + if (has_uvs) { createTexcoordsSource(geom_id, me); + } - if (has_color) + if (has_color) { createVertexColorSource(geom_id, me); - + } // <vertices> COLLADASW::Vertices verts(mSW); @@ -132,7 +130,7 @@ void GeometryExporter::operator()(Object *ob) input_list.push_back(input); verts.add(); - createLooseEdgeList(ob, me, geom_id, norind); + createLooseEdgeList(ob, me, geom_id); // Only create Polylists if number of faces > 0 if (me->totface > 0) { @@ -155,10 +153,6 @@ void GeometryExporter::operator()(Object *ob) closeGeometry(); - if (this->export_settings->apply_modifiers) { - BKE_libblock_free_us(&(G.main->mesh), me); - } - if (this->export_settings->include_shapekeys) { Key * key = BKE_key_from_object(ob); if (key) { @@ -171,16 +165,16 @@ void GeometryExporter::operator()(Object *ob) } } } -#if 0 - dm->release(dm); -#endif + + BKE_libblock_free_us(&(G.main->mesh), me); + } void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb) { std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name); std::vector<Normal> nor; - std::vector<Face> norind; + std::vector<BCPolygonNormalsIndices> norind; if (exportedGeometry.find(geom_id) != exportedGeometry.end()) { @@ -207,11 +201,13 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb) bool has_uvs = (bool)CustomData_has_layer(&me->fdata, CD_MTFACE); // writes <source> for uv coords if mesh has uv coords - if (has_uvs) + if (has_uvs) { createTexcoordsSource(geom_id, me); + } - if (has_color) + if (has_color) { createVertexColorSource(geom_id, me); + } // <vertices> @@ -245,8 +241,7 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb) void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, - std::string& geom_id, - std::vector<Face>& norind) + std::string& geom_id) { MEdge *medges = me->medge; @@ -302,10 +297,12 @@ void GeometryExporter::createPolylist(short material_index, Object *ob, Mesh *me, std::string& geom_id, - std::vector<Face>& norind) + std::vector<BCPolygonNormalsIndices>& norind) { - MFace *mfaces = me->mface; - int totfaces = me->totface; + + MPoly *mpolys = me->mpoly; + MLoop *mloops = me->mloop; + int totpolys = me->totpoly; // <vcount> int i; @@ -313,17 +310,12 @@ void GeometryExporter::createPolylist(short material_index, std::vector<unsigned long> vcount_list; // count faces with this material - for (i = 0; i < totfaces; i++) { - MFace *f = &mfaces[i]; + for (i = 0; i < totpolys; i++) { + MPoly *p = &mpolys[i]; - if (f->mat_nr == material_index) { + if (p->mat_nr == material_index) { faces_in_polylist++; - if (f->v4 == 0) { - vcount_list.push_back(3); - } - else { - vcount_list.push_back(4); - } + vcount_list.push_back(p->totloop); } } @@ -385,21 +377,21 @@ void GeometryExporter::createPolylist(short material_index, // performs the actual writing polylist.prepareToAppendValues(); - - // <p> int texindex = 0; - for (i = 0; i < totfaces; i++) { - MFace *f = &mfaces[i]; - - if (f->mat_nr == material_index) { - - unsigned int *v = &f->v1; - unsigned int *n = &norind[i].v1; - for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) { - polylist.appendValues(v[j]); - polylist.appendValues(n[j]); - + unsigned int vi = 0; + unsigned int ni = 0; + for (i = 0; i < totpolys; i++) { + MPoly *p = &mpolys[i]; + int loop_count = p->totloop; + + if (p->mat_nr == material_index) { + MLoop *l = &mloops[p->loopstart]; + BCPolygonNormalsIndices normal_indices = norind[i]; + + for (int j = 0; j < loop_count; j++) { + polylist.appendValues(l[j].v); + polylist.appendValues(normal_indices[j]); if (has_uvs) polylist.appendValues(texindex + j); @@ -408,14 +400,13 @@ void GeometryExporter::createPolylist(short material_index, } } - texindex += 3; - if (f->v4 != 0) - texindex++; + texindex += loop_count; } polylist.finish(); } + // creates <source> for positions void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me) { @@ -485,6 +476,7 @@ void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) source.finish(); } + std::string GeometryExporter::makeTexcoordSourceId(std::string& geom_id, int layer_index) { char suffix[20]; @@ -495,38 +487,21 @@ std::string GeometryExporter::makeTexcoordSourceId(std::string& geom_id, int lay //creates <source> for texcoords void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me) { -#if 0 - int totfaces = dm->getNumTessFaces(dm); - MFace *mfaces = dm->getTessFaceArray(dm); -#endif - int totfaces = me->totface; - MFace *mfaces = me->mface; - int totuv = 0; - int i; + int totpoly = me->totpoly; + int totuv = me->totloop; + MPoly *mpolys = me->mpoly; - // count totuv - for (i = 0; i < totfaces; i++) { - MFace *f = &mfaces[i]; - if (f->v4 == 0) { - totuv += 3; - } - else { - totuv += 4; - } - } - - int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE); + int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); // write <source> for each layer // each <source> will get id like meshName + "map-channel-1" int map_index = 0; - int active_uv_index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE)-1; + int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); for (int a = 0; a < num_layers; a++) { if (!this->export_settings->active_uv_only || a == active_uv_index) { - MTFace *tface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, a); - // char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, a); + MLoopUV *mloops = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, a); COLLADASW::FloatSourceF source(mSW); std::string layer_id = makeTexcoordSourceId(geom_id, map_index++); @@ -541,12 +516,12 @@ void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me) source.prepareToAppendValues(); - for (i = 0; i < totfaces; i++) { - MFace *f = &mfaces[i]; - - for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) { - source.appendValues(tface[i].uv[j][0], - tface[i].uv[j][1]); + for (int index = 0; index < totpoly; index++) { + MPoly *mpoly = mpolys+index; + MLoopUV *mloop = mloops+mpoly->loopstart; + for (int j = 0; j < mpoly->totloop; j++) { + source.appendValues(mloop[j].uv[0], + mloop[j].uv[1]); } } @@ -586,53 +561,55 @@ void GeometryExporter::createNormalsSource(std::string geom_id, Mesh *me, std::v source.finish(); } -void GeometryExporter::create_normals(std::vector<Normal> &nor, std::vector<Face> &ind, Mesh *me) +void GeometryExporter::create_normals(std::vector<Normal> &normals, std::vector<BCPolygonNormalsIndices> &polygons_normals, Mesh *me) { - int i, j, v; MVert *vert = me->mvert; - std::map<unsigned int, unsigned int> nshar; - - for (i = 0; i < me->totface; i++) { - MFace *fa = &me->mface[i]; - Face f; - unsigned int *nn = &f.v1; - unsigned int *vv = &fa->v1; - - memset(&f, 0, sizeof(f)); - v = fa->v4 == 0 ? 3 : 4; - - if (!(fa->flag & ME_SMOOTH)) { - Normal n; - if (v == 4) - normal_quad_v3(&n.x, vert[fa->v1].co, vert[fa->v2].co, vert[fa->v3].co, vert[fa->v4].co); - else - normal_tri_v3(&n.x, vert[fa->v1].co, vert[fa->v2].co, vert[fa->v3].co); - nor.push_back(n); + std::map<unsigned int, unsigned int> shared_normal_indices; + + for (int poly_index = 0; poly_index < me->totpoly; poly_index++) { + MPoly *mpoly = &me->mpoly[poly_index]; + MLoop *mloops = me->mloop; + + unsigned int last_normal_index = -1; + if (!(mpoly->flag & ME_SMOOTH)) { + // For flat faces calculate use face normal as vertex normal: + + float vector[3]; + BKE_mesh_calc_poly_normal(mpoly, mloops, vert, vector); + + Normal n = { vector[0], vector[1], vector[2] }; + normals.push_back(n); + last_normal_index++; } - for (j = 0; j < v; j++) { - if (fa->flag & ME_SMOOTH) { - if (nshar.find(*vv) != nshar.end()) - *nn = nshar[*vv]; + + MLoop *mloop = mloops + mpoly->loopstart; + BCPolygonNormalsIndices poly_indices; + for (int loop_index = 0; loop_index < mpoly->totloop; loop_index++) { + unsigned int vertex_index = mloop[loop_index].v; + if (mpoly->flag & ME_SMOOTH) { + if (shared_normal_indices.find(vertex_index) != shared_normal_indices.end()) + poly_indices.add_index (shared_normal_indices[vertex_index]); else { - Normal n = { - (float)vert[*vv].no[0] / 32767.0f, - (float)vert[*vv].no[1] / 32767.0f, - (float)vert[*vv].no[2] / 32767.0f - }; - nor.push_back(n); - *nn = (unsigned int)nor.size() - 1; - nshar[*vv] = *nn; + + float vector[3]; + normal_short_to_float_v3(vector, vert[vertex_index].no); + normalize_v3(vector); + + Normal n = { vector[0], vector[1], vector[2] }; + normals.push_back(n); + last_normal_index++; + + poly_indices.add_index(last_normal_index); + shared_normal_indices[vertex_index] = last_normal_index; } - vv++; } else { - *nn = (unsigned int)nor.size() - 1; + poly_indices.add_index(last_normal_index); } - nn++; } - ind.push_back(f); + polygons_normals.push_back(poly_indices); } } @@ -655,19 +632,4 @@ COLLADASW::URI GeometryExporter::makeUrl(std::string id) return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); } -#if 0 -int GeometryExporter::getTriCount(MFace *faces, int totface) -{ - int i; - int tris = 0; - for (i = 0; i < totface; i++) { - // if quad - if (faces[i].v4 != 0) - tris += 2; - else - tris++; - } - return tris; -} -#endif diff --git a/source/blender/collada/GeometryExporter.h b/source/blender/collada/GeometryExporter.h index 7cbbf0da8fa..4b1427c11ca 100644 --- a/source/blender/collada/GeometryExporter.h +++ b/source/blender/collada/GeometryExporter.h @@ -42,6 +42,7 @@ #include "DNA_key_types.h" #include "ExportSettings.h" +#include "collada_utils.h" #include "BKE_key.h" @@ -71,8 +72,7 @@ public: void createLooseEdgeList(Object *ob, Mesh *me, - std::string& geom_id, - std::vector<Face>& norind); + std::string& geom_id); // powerful because it handles both cases when there is material and when there's not void createPolylist(short material_index, @@ -81,7 +81,7 @@ public: Object *ob, Mesh *me, std::string& geom_id, - std::vector<Face>& norind); + std::vector<BCPolygonNormalsIndices>& norind); // creates <source> for positions void createVertsSource(std::string geom_id, Mesh *me); @@ -92,11 +92,12 @@ public: //creates <source> for texcoords void createTexcoordsSource(std::string geom_id, Mesh *me); + void createTesselatedTexcoordsSource(std::string geom_id, Mesh *me); //creates <source> for normals void createNormalsSource(std::string geom_id, Mesh *me, std::vector<Normal>& nor); - void create_normals(std::vector<Normal> &nor, std::vector<Face> &ind, Mesh *me); + void create_normals(std::vector<Normal> &nor, std::vector<BCPolygonNormalsIndices> &ind, Mesh *me); std::string getIdBySemantics(std::string geom_id, COLLADASW::InputSemantic::Semantics type, std::string other_suffix = ""); @@ -106,7 +107,6 @@ public: void export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb); - /* int getTriCount(MFace *faces, int totface);*/ private: std::set<std::string> exportedGeometry; diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp index 26915f37002..69944563b6a 100644 --- a/source/blender/collada/MeshImporter.cpp +++ b/source/blender/collada/MeshImporter.cpp @@ -173,83 +173,30 @@ void UVDataWrapper::getUV(int uv_index, float *uv) } } -void MeshImporter::set_face_indices(MFace *mface, unsigned int *indices, bool quad) -{ - mface->v1 = indices[0]; - mface->v2 = indices[1]; - mface->v3 = indices[2]; - if (quad) mface->v4 = indices[3]; - else mface->v4 = 0; -#ifdef COLLADA_DEBUG - // fprintf(stderr, "%u, %u, %u\n", indices[0], indices[1], indices[2]); -#endif -} - -// not used anymore, test_index_face from blenkernel is better -#if 0 -// change face indices order so that v4 is not 0 -void MeshImporter::rotate_face_indices(MFace *mface) -{ - mface->v4 = mface->v1; - mface->v1 = mface->v2; - mface->v2 = mface->v3; - mface->v3 = 0; +MeshImporter::MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce) : unitconverter(unitconv), scene(sce), armature_importer(arm) { } -#endif -void MeshImporter::set_face_uv(MTFace *mtface, UVDataWrapper &uvs, - COLLADAFW::IndexList& index_list, unsigned int *tris_indices) +void MeshImporter::set_poly_indices(MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count) { - // per face vertex indices, this means for quad we have 4 indices, not 8 - COLLADAFW::UIntValuesArray& indices = index_list.getIndices(); + mpoly->loopstart = loop_index; + mpoly->totloop = loop_count; - uvs.getUV(indices[tris_indices[0]], mtface->uv[0]); - uvs.getUV(indices[tris_indices[1]], mtface->uv[1]); - uvs.getUV(indices[tris_indices[2]], mtface->uv[2]); + for (int index=0; index < loop_count; index++) { + mloop->v = indices[index]; + mloop++; + } } -void MeshImporter::set_face_uv(MTFace *mtface, UVDataWrapper &uvs, - COLLADAFW::IndexList& index_list, int index, bool quad) +void MeshImporter::set_face_uv(MLoopUV *mloopuv, UVDataWrapper &uvs, + int start_index, COLLADAFW::IndexList& index_list, int count) { // per face vertex indices, this means for quad we have 4 indices, not 8 COLLADAFW::UIntValuesArray& indices = index_list.getIndices(); - uvs.getUV(indices[index + 0], mtface->uv[0]); - uvs.getUV(indices[index + 1], mtface->uv[1]); - uvs.getUV(indices[index + 2], mtface->uv[2]); - - if (quad) uvs.getUV(indices[index + 3], mtface->uv[3]); - -#ifdef COLLADA_DEBUG - if (quad) { - fprintf(stderr, "face uv:\n" - "((%d, %d, %d, %d))\n" - "((%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f))\n", - - indices[index + 0], - indices[index + 1], - indices[index + 2], - indices[index + 3], - - mtface->uv[0][0], mtface->uv[0][1], - mtface->uv[1][0], mtface->uv[1][1], - mtface->uv[2][0], mtface->uv[2][1], - mtface->uv[3][0], mtface->uv[3][1]); + for (int index = 0; index < count; index++) { + int uv_index = indices[index+start_index]; + uvs.getUV(uv_index, mloopuv[index].uv); } - else { - fprintf(stderr, "face uv:\n" - "((%d, %d, %d))\n" - "((%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f))\n", - - indices[index + 0], - indices[index + 1], - indices[index + 2], - - mtface->uv[0][0], mtface->uv[0][1], - mtface->uv[1][0], mtface->uv[1][1], - mtface->uv[2][0], mtface->uv[2][1]); - } -#endif } #ifdef COLLADA_DEBUG @@ -329,91 +276,6 @@ void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *me) } } -int MeshImporter::triangulate_poly(unsigned int *indices, int totvert, MVert *verts, std::vector<unsigned int>& tri) -{ - ListBase dispbase; - DispList *dl; - float *vert; - int i = 0; - - dispbase.first = dispbase.last = NULL; - - dl = (DispList *)MEM_callocN(sizeof(DispList), "poly disp"); - dl->nr = totvert; - dl->type = DL_POLY; - dl->parts = 1; - dl->verts = vert = (float *)MEM_callocN(totvert * 3 * sizeof(float), "poly verts"); - dl->index = (int *)MEM_callocN(sizeof(int) * 3 * totvert, "dl index"); - - BLI_addtail(&dispbase, dl); - - for (i = 0; i < totvert; i++) { - copy_v3_v3(vert, verts[indices[i]].co); - vert += 3; - } - - BKE_displist_fill(&dispbase, &dispbase, 0); - - int tottri = 0; - dl = (DispList *)dispbase.first; - - if (dl->type == DL_INDEX3) { - tottri = dl->parts; - - int *index = dl->index; - for (i = 0; i < tottri; i++) { - int t[3] = {*index, *(index + 1), *(index + 2)}; - - std::sort(t, t + 3); - - tri.push_back(t[0]); - tri.push_back(t[1]); - tri.push_back(t[2]); - - index += 3; - } - } - - BKE_displist_free(&dispbase); - - return tottri; -} - -int MeshImporter::count_new_tris(COLLADAFW::Mesh *mesh, Mesh *me) -{ - COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); - unsigned int i; - int tottri = 0; - - for (i = 0; i < prim_arr.getCount(); i++) { - - COLLADAFW::MeshPrimitive *mp = prim_arr[i]; - int type = mp->getPrimitiveType(); - size_t prim_totface = mp->getFaceCount(); - unsigned int *indices = mp->getPositionIndices().getData(); - - if (type == COLLADAFW::MeshPrimitive::POLYLIST || - type == COLLADAFW::MeshPrimitive::POLYGONS) - { - COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; - COLLADAFW::Polygons::VertexCountArray& vcounta = mpvc->getGroupedVerticesVertexCountArray(); - - for (unsigned int j = 0; j < prim_totface; j++) { - int vcount = vcounta[j]; - - if (vcount > 4) { - std::vector<unsigned int> tri; - - // tottri += triangulate_poly(indices, vcount, me->mvert, tri) - 1; // XXX why - 1?! - tottri += triangulate_poly(indices, vcount, me->mvert, tri); - } - - indices += vcount; - } - } - } - return tottri; -} // ===================================================================== // condition 1: The Primitive has normals @@ -471,10 +333,11 @@ bool MeshImporter::primitive_has_faces(COLLADAFW::MeshPrimitive *mp) { // hint: This is done because mesh->getFacesCount() does // count loose edges as extra faces, which is not what we want here. // ================================================================= -void MeshImporter::allocate_face_data(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris) +void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me) { - COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); - int total_facecount = 0; + COLLADAFW::MeshPrimitiveArray& prim_arr = collada_mesh->getMeshPrimitives(); + int total_poly_count = 0; + int total_loop_count = 0; // collect edge_count and face_count from all parts for (int i = 0; i < prim_arr.getCount(); i++) { @@ -485,21 +348,77 @@ void MeshImporter::allocate_face_data(COLLADAFW::Mesh *mesh, Mesh *me, int new_t case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: case COLLADAFW::MeshPrimitive::POLYLIST: case COLLADAFW::MeshPrimitive::POLYGONS: { - size_t prim_totface = mp->getFaceCount(); - total_facecount += prim_totface; + + COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; + size_t prim_poly_count = mpvc->getFaceCount(); + + size_t prim_loop_count = 0; + for(int index=0; index < prim_poly_count; index++) { + prim_loop_count += get_vertex_count(mpvc, index); + } + + total_poly_count += prim_poly_count; + total_loop_count += prim_loop_count; break; } default: break; } } - // allocate space for faces - if (total_facecount > 0) { - me->totface = total_facecount + new_tris; - me->mface = (MFace *)CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, me->totface); + // Add the data containers + if (total_poly_count > 0) { + me->totpoly = total_poly_count; + me->totloop = total_loop_count; + me->mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, me->totpoly); + me->mloop = (MLoop *)CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, me->totloop); + + unsigned int totuvset = collada_mesh->getUVCoords().getInputInfosArray().getCount(); + for (int i = 0; i < totuvset; i++) { + if (collada_mesh->getUVCoords().getLength(i) == 0) { + totuvset = 0; + break; + } + } + + if (totuvset > 0) { + for (int i = 0; i < totuvset; i++) { + COLLADAFW::MeshVertexData::InputInfos *info = collada_mesh->getUVCoords().getInputInfosArray()[i]; + COLLADAFW::String &uvname = info->mName; + int x = 0; + // Allocate space for UV_data + CustomData_add_layer_named(&me->pdata, CD_MTEXPOLY, CD_DEFAULT, NULL, me->totpoly, uvname.c_str()); + CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, uvname.c_str()); + } + // activate the first uv map + me->mtpoly = (MTexPoly *)CustomData_get_layer_n(&me->pdata, CD_MTEXPOLY, 0); + me->mloopuv = (MLoopUV *) CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, 0); + } } } +unsigned int MeshImporter::get_vertex_count(COLLADAFW::Polygons *mp, int index) { + int type = mp->getPrimitiveType(); + int result; + switch (type) { + case COLLADAFW::MeshPrimitive::TRIANGLES: + case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: { + result = 3; + break; + } + case COLLADAFW::MeshPrimitive::POLYLIST: + case COLLADAFW::MeshPrimitive::POLYGONS: { + result = mp->getGroupedVerticesVertexCountArray()[index]; + break; + } + default: { + result = -1; + break; + } + } + return result; +} + + unsigned int MeshImporter::get_loose_edge_count(COLLADAFW::Mesh *mesh) { COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); int loose_edge_count = 0; @@ -606,256 +525,118 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me) // // TODO: import uv set names // ======================================================================== -void MeshImporter::read_faces(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris) //TODO:: Refactor. Possibly replace by iterators +void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) { unsigned int i; - allocate_face_data(mesh, me, new_tris); - - // allocate UV Maps - unsigned int totuvset = mesh->getUVCoords().getInputInfosArray().getCount(); + allocate_poly_data(collada_mesh, me); - for (i = 0; i < totuvset; i++) { - if (mesh->getUVCoords().getLength(i) == 0) { - totuvset = 0; - break; - } - } - - for (i = 0; i < totuvset; i++) { - COLLADAFW::MeshVertexData::InputInfos *info = mesh->getUVCoords().getInputInfosArray()[i]; - CustomData_add_layer_named(&me->fdata, CD_MTFACE, CD_CALLOC, NULL, me->totface, info->mName.c_str()); - //this->set_layername_map[i] = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i); - } - - // activate the first uv map - if (totuvset) me->mtface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, 0); - - UVDataWrapper uvs(mesh->getUVCoords()); - -#ifdef COLLADA_DEBUG - // uvs.print(); -#endif + UVDataWrapper uvs(collada_mesh->getUVCoords()); - MFace *mface = me->mface; + MPoly *mpoly = me->mpoly; + MLoop *mloop = me->mloop; + int loop_index = 0; MaterialIdPrimitiveArrayMap mat_prim_map; - int face_index = 0; - - COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); - - COLLADAFW::MeshVertexData& nor = mesh->getNormals(); + COLLADAFW::MeshPrimitiveArray& prim_arr = collada_mesh->getMeshPrimitives(); + COLLADAFW::MeshVertexData& nor = collada_mesh->getNormals(); for (i = 0; i < prim_arr.getCount(); i++) { COLLADAFW::MeshPrimitive *mp = prim_arr[i]; // faces - size_t prim_totface = mp->getFaceCount(); - unsigned int *indices = mp->getPositionIndices().getData(); - unsigned int *nind = mp->getNormalIndices().getData(); + size_t prim_totpoly = mp->getFaceCount(); + unsigned int *position_indices = mp->getPositionIndices().getData(); + unsigned int *normal_indices = mp->getNormalIndices().getData(); bool mp_has_normals = primitive_has_useable_normals(mp); bool mp_has_faces = primitive_has_faces(mp); - int type = mp->getPrimitiveType(); - int index = 0; + int collada_meshtype = mp->getPrimitiveType(); - // since we cannot set mface->mat_nr here, we store a portion of me->mface in Primitive - Primitive prim = {mface, 0}; + // since we cannot set mpoly->mat_nr here, we store a portion of me->mface in Primitive + Primitive prim = {mpoly, 0}; COLLADAFW::IndexListArray& index_list_array = mp->getUVCoordIndicesArray(); -#ifdef COLLADA_DEBUG - /* - fprintf(stderr, "Primitive %d:\n", i); - for (unsigned int j = 0; j < totuvset; j++) { - print_index_list(*index_list_array[j]); - } - */ -#endif - - if (type == COLLADAFW::MeshPrimitive::TRIANGLES) { - for (unsigned int j = 0; j < prim_totface; j++) { - - set_face_indices(mface, indices, false); - indices += 3; - -#if 0 - for (unsigned int k = 0; k < totuvset; k++) { - if (!index_list_array.empty() && index_list_array[k]) { - // get mtface by face index and uv set index - MTFace *mtface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); - set_face_uv(&mtface[face_index], uvs, k, *index_list_array[k], index, false); - } - } -#else - for (unsigned int k = 0; k < index_list_array.getCount(); k++) { - // get mtface by face index and uv set index - MTFace *mtface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); - set_face_uv(&mtface[face_index], uvs, *index_list_array[k], index, false); - } -#endif - - test_index_face(mface, &me->fdata, face_index, 3); - - if (mp_has_normals) { - if (!flat_face(nind, nor, 3)) - mface->flag |= ME_SMOOTH; - - nind += 3; - } - - index += 3; - mface++; - face_index++; - prim.totface++; - } - } - // If MeshPrimitive is TRIANGLE_FANS we split it into triangles // The first trifan vertex will be the first vertex in every triangle - if (type == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { + // XXX The proper function of TRIANGLE_FANS is not tested!!! + // XXX In particular the handling of the normal_indices looks very wrong to me + if (collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { unsigned grouped_vertex_count = mp->getGroupedVertexElementsCount(); for (unsigned int group_index = 0; group_index < grouped_vertex_count; group_index++) { - unsigned int first_vertex = indices[0]; // Store first trifan vertex - unsigned int first_normal = nind[0]; // Store first trifan vertex normal + unsigned int first_vertex = position_indices[0]; // Store first trifan vertex + unsigned int first_normal = normal_indices[0]; // Store first trifan vertex normal unsigned int vertex_count = mp->getGroupedVerticesVertexCount(group_index); for (unsigned int vertex_index = 0; vertex_index < vertex_count - 2; vertex_index++) { // For each triangle store indeces of its 3 vertices - unsigned int triangle_vertex_indices[3] = {first_vertex, indices[1], indices[2]}; - set_face_indices(mface, triangle_vertex_indices, false); - test_index_face(mface, &me->fdata, face_index, 3); + unsigned int triangle_vertex_indices[3] = {first_vertex, position_indices[1], position_indices[2]}; + set_poly_indices(mpoly, mloop, loop_index, triangle_vertex_indices, 3); if (mp_has_normals) { // vertex normals, same inplementation as for the triangles // the same for vertces normals - unsigned int vertex_normal_indices[3] = {first_normal, nind[1], nind[2]}; - if (!flat_face(vertex_normal_indices, nor, 3)) - mface->flag |= ME_SMOOTH; - nind++; + unsigned int vertex_normal_indices[3] = {first_normal, normal_indices[1], normal_indices[2]}; + if (!is_flat_face(vertex_normal_indices, nor, 3)) + mpoly->flag |= ME_SMOOTH; + normal_indices++; } - mface++; // same inplementation as for the triangles - indices++; - face_index++; - prim.totface++; + mpoly++; + mloop += 3; + loop_index += 3; + prim.totpoly++; + } // Moving cursor to the next triangle fan. if (mp_has_normals) - nind += 2; + normal_indices += 2; - indices += 2; + position_indices += 2; } } - else if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) { - COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; - COLLADAFW::Polygons::VertexCountArray& vcounta = mpvc->getGroupedVerticesVertexCountArray(); - for (unsigned int j = 0; j < prim_totface; j++) { + if (collada_meshtype == COLLADAFW::MeshPrimitive::POLYLIST || + collada_meshtype == COLLADAFW::MeshPrimitive::POLYGONS || + collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLES) { + COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; + for (unsigned int j = 0; j < prim_totpoly; j++) { - // face - int vcount = vcounta[j]; - if (vcount == 3 || vcount == 4) { - - set_face_indices(mface, indices, vcount == 4); - - // set mtface for each uv set - // it is assumed that all primitives have equal number of UV sets - -#if 0 - for (unsigned int k = 0; k < totuvset; k++) { - if (!index_list_array.empty() && index_list_array[k]) { - // get mtface by face index and uv set index - MTFace *mtface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); - set_face_uv(&mtface[face_index], uvs, k, *index_list_array[k], index, mface->v4 != 0); - } - } -#else - for (unsigned int k = 0; k < index_list_array.getCount(); k++) { - // get mtface by face index and uv set index - MTFace *mtface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); - set_face_uv(&mtface[face_index], uvs, *index_list_array[k], index, vcount == 4); - } -#endif + // Vertices in polygon: + int vcount = get_vertex_count(mpvc, j); + set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount); - test_index_face(mface, &me->fdata, face_index, vcount); - if (mp_has_normals) { - if (!flat_face(nind, nor, vcount)) - mface->flag |= ME_SMOOTH; + for (unsigned int l = 0; l < index_list_array.getCount(); l++) { + int uvset_index = index_list_array[l]->getSetIndex(); - nind += vcount; - } - - mface++; - face_index++; - prim.totface++; - - } - else { - std::vector<unsigned int> tri; - - triangulate_poly(indices, vcount, me->mvert, tri); - - for (unsigned int k = 0; k < tri.size() / 3; k++) { - int v = k * 3; - unsigned int uv_indices[3] = { - index + tri[v], - index + tri[v + 1], - index + tri[v + 2] - }; - unsigned int tri_indices[3] = { - indices[tri[v]], - indices[tri[v + 1]], - indices[tri[v + 2]] - }; - - set_face_indices(mface, tri_indices, false); - -#if 0 - for (unsigned int l = 0; l < totuvset; l++) { - if (!index_list_array.empty() && index_list_array[l]) { - // get mtface by face index and uv set index - MTFace *mtface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, l); - set_face_uv(&mtface[face_index], uvs, l, *index_list_array[l], uv_indices); - } - } -#else - for (unsigned int l = 0; l < index_list_array.getCount(); l++) { - int uvset_index = index_list_array[l]->getSetIndex(); - - // get mtface by face index and uv set index - MTFace *mtface = (MTFace *)CustomData_get_layer_n(&me->fdata, CD_MTFACE, uvset_index); - set_face_uv(&mtface[face_index], uvs, *index_list_array[l], uv_indices); - } -#endif - - - test_index_face(mface, &me->fdata, face_index, 3); - - if (mp_has_normals) { - unsigned int ntri[3] = {nind[tri[v]], nind[tri[v + 1]], nind[tri[v + 2]]}; + // get mtface by face index and uv set index + MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, uvset_index); - if (!flat_face(ntri, nor, 3)) - mface->flag |= ME_SMOOTH; - } - - mface++; - face_index++; - prim.totface++; - } + set_face_uv(mloopuv+loop_index, uvs, loop_index, *index_list_array[l], vcount); + } - if (mp_has_normals) - nind += vcount; + if (mp_has_normals) { + if (!is_flat_face(normal_indices, nor, vcount)) + mpoly->flag |= ME_SMOOTH; } + + mpoly++; + mloop += vcount; + loop_index += vcount; + prim.totpoly++; + + if (mp_has_normals) + normal_indices += vcount; - index += vcount; - indices += vcount; + position_indices += vcount; } } - else if (type == COLLADAFW::MeshPrimitive::LINES) { + + else if (collada_meshtype == COLLADAFW::MeshPrimitive::LINES) { continue; // read the lines later after all the rest is done } @@ -863,7 +644,7 @@ void MeshImporter::read_faces(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris) //T mat_prim_map[mp->getMaterialId()].push_back(prim); } - geom_uid_mat_mapping_map[mesh->getUniqueId()] = mat_prim_map; + geom_uid_mat_mapping_map[collada_mesh->getUniqueId()] = mat_prim_map; } void MeshImporter::get_vector(float v[3], COLLADAFW::MeshVertexData& arr, int i, int stride) @@ -896,8 +677,7 @@ void MeshImporter::get_vector(float v[3], COLLADAFW::MeshVertexData& arr, int i, break; } } - -bool MeshImporter::flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, int count) +bool MeshImporter::is_flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, int count) { float a[3], b[3]; @@ -919,8 +699,6 @@ bool MeshImporter::flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, return true; } -MeshImporter::MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce) : unitconverter(unitconv), scene(sce), armature_importer(arm) { -} void MeshImporter::bmeshConversion() { @@ -929,10 +707,9 @@ void MeshImporter::bmeshConversion() { if ((*m).second) { Mesh *me = (*m).second; - BKE_mesh_convert_mfaces_to_mpolys(me); BKE_mesh_tessface_clear(me); - BKE_mesh_calc_normals_mapping(me->mvert, me->totvert, me->mloop, me->mpoly, me->totloop, me->totpoly, NULL, NULL, 0, NULL, NULL); + //BKE_mesh_validate(me, 1); } } } @@ -1137,9 +914,10 @@ void MeshImporter::optimize_material_assignements() MTFace *MeshImporter::assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial, std::map<COLLADAFW::UniqueId, Material *>& uid_material_map, Object *ob, const COLLADAFW::UniqueId *geom_uid, - MTex **color_texture, char *layername, MTFace *texture_face, + char *layername, MTFace *texture_face, std::map<Material *, TexIndexTextureArrayMap>& material_texture_mapping_map, short mat_index) { + MTex *color_texture = NULL; Mesh *me = (Mesh *)ob->data; const COLLADAFW::UniqueId& ma_uid = cmaterial.getReferencedMaterial(); @@ -1167,17 +945,17 @@ MTFace *MeshImporter::assign_material_to_geom(COLLADAFW::MaterialBinding cmateri // loop through <bind_vertex_inputs> for (i = 0; i < tex_array.getCount(); i++) { - *color_texture = assign_textures_to_uvlayer(tex_array[i], me, texindex_texarray_map, - *color_texture); + color_texture = assign_textures_to_uvlayer(tex_array[i], me, texindex_texarray_map, + color_texture); } // set texture face - if (*color_texture && - strlen((*color_texture)->uvname) && - strcmp(layername, (*color_texture)->uvname) != 0) { + if (color_texture && + strlen((color_texture)->uvname) && + strcmp(layername, color_texture->uvname) != 0) { texture_face = (MTFace *)CustomData_get_layer_named(&me->fdata, CD_MTFACE, - (*color_texture)->uvname); - strcpy(layername, (*color_texture)->uvname); + color_texture->uvname); + strcpy(layername, color_texture->uvname); } MaterialIdPrimitiveArrayMap& mat_prim_map = geom_uid_mat_mapping_map[*geom_uid]; @@ -1192,19 +970,18 @@ MTFace *MeshImporter::assign_material_to_geom(COLLADAFW::MaterialBinding cmateri for (it = prims.begin(); it != prims.end(); it++) { Primitive& prim = *it; - MFace *mface = prim.mface; + MPoly *mpoly = prim.mpoly; - for (i = 0; i < prim.totface; i++, mface++) { - mface->mat_nr = mat_index; + for (i = 0; i < prim.totpoly; i++, mpoly++) { + mpoly->mat_nr = mat_index; // bind texture images to faces - if (texture_face && (*color_texture)) { - texture_face->tpage = (Image *)(*color_texture)->tex->ima; + if (texture_face && color_texture) { + texture_face->tpage = (Image *)color_texture->tex->ima; texture_face++; } } } - } - + } return texture_face; } @@ -1251,15 +1028,15 @@ Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::Insta // replace ob->data freeing the old one Mesh *old_mesh = (Mesh *)ob->data; + Mesh *new_mesh = uid_mesh_map[*geom_uid]; - set_mesh(ob, uid_mesh_map[*geom_uid]); + set_mesh(ob, new_mesh); if (old_mesh->id.us == 0) BKE_libblock_free(&G.main->mesh, old_mesh); char layername[100]; layername[0] = '\0'; MTFace *texture_face = NULL; - MTex *color_texture = NULL; COLLADAFW::MaterialBindingArray& mat_array = geom->getMaterialBindings(); @@ -1269,7 +1046,7 @@ Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::Insta if (mat_array[i].getReferencedMaterial().isValid()) { texture_face = assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid, - &color_texture, layername, texture_face, + layername, texture_face, material_texture_mapping_map, i); } else { @@ -1283,11 +1060,7 @@ Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::Insta // create a mesh storing a pointer in a map so it can be retrieved later by geometry UID bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom) { - // TODO: import also uvs, normals - // XXX what to do with normal indices? - // XXX num_normals may be != num verts, then what to do? - // check geometry->getType() first if (geom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) { // TODO: report warning fprintf(stderr, "Mesh type %s is not supported\n", bc_geomTypeToStr(geom->getType())); @@ -1306,23 +1079,16 @@ bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom) me->id.us--; // is already 1 here, but will be set later in set_mesh // store the Mesh pointer to link it later with an Object + // mesh_geom_map needed to map mesh to its geometry name (for shape key naming) this->uid_mesh_map[mesh->getUniqueId()] = me; - // needed to map mesh to its geometry name (needed for shape key naming) this->mesh_geom_map[std::string(me->id.name)] = str_geom_id; - - int new_tris = 0; read_vertices(mesh, me); - - new_tris = count_new_tris(mesh, me); - - read_faces(mesh, me, new_tris); - - BKE_mesh_make_edges(me, 0); + read_polys(mesh, me); + BKE_mesh_calc_edges(me, 0); // read_lines() must be called after the face edges have been generated. // Oterwise the loose edges will be silently deleted again. read_lines(mesh, me); - return true; } diff --git a/source/blender/collada/MeshImporter.h b/source/blender/collada/MeshImporter.h index 8b0f5cdc200..751e6faf02b 100644 --- a/source/blender/collada/MeshImporter.h +++ b/source/blender/collada/MeshImporter.h @@ -31,6 +31,7 @@ #include <vector> #include "COLLADAFWIndexList.h" +#include "COLLADAFWPolygons.h" #include "COLLADAFWInstanceGeometry.h" #include "COLLADAFWMaterialBinding.h" #include "COLLADAFWMesh.h" @@ -89,30 +90,28 @@ private: std::map<COLLADAFW::UniqueId, Mesh*> uid_mesh_map; // geometry unique id-to-mesh map std::map<COLLADAFW::UniqueId, Object*> uid_object_map; // geom uid-to-object std::vector<Object*> imported_objects; // list of imported objects - // this structure is used to assign material indices to faces + + // this structure is used to assign material indices to polygons // it holds a portion of Mesh faces and corresponds to a DAE primitive list (<triangles>, <polylist>, etc.) struct Primitive { - MFace *mface; - unsigned int totface; + MPoly *mpoly; + unsigned int totpoly; }; typedef std::map<COLLADAFW::MaterialId, std::vector<Primitive> > MaterialIdPrimitiveArrayMap; std::map<COLLADAFW::UniqueId, MaterialIdPrimitiveArrayMap> geom_uid_mat_mapping_map; // crazy name! std::multimap<COLLADAFW::UniqueId, COLLADAFW::UniqueId> materials_mapped_to_geom; //< materials that have already been mapped to a geometry. A pair of geom uid and mat uid, one geometry can have several materials - - void set_face_indices(MFace *mface, unsigned int *indices, bool quad); - - // not used anymore, test_index_face from blenkernel is better -#if 0 - // change face indices order so that v4 is not 0 - void rotate_face_indices(MFace *mface); -#endif - - void set_face_uv(MTFace *mtface, UVDataWrapper &uvs, - COLLADAFW::IndexList& index_list, unsigned int *tris_indices); - - void set_face_uv(MTFace *mtface, UVDataWrapper &uvs, - COLLADAFW::IndexList& index_list, int index, bool quad); + void set_poly_indices(MPoly *mpoly, + MLoop *mloop, + int loop_index, + unsigned int *indices, + int loop_count); + + void set_face_uv(MLoopUV *mloopuv, + UVDataWrapper &uvs, + int loop_index, + COLLADAFW::IndexList& index_list, + int count); #ifdef COLLADA_DEBUG void print_index_list(COLLADAFW::IndexList& index_list); @@ -121,11 +120,7 @@ private: bool is_nice_mesh(COLLADAFW::Mesh *mesh); void read_vertices(COLLADAFW::Mesh *mesh, Mesh *me); - - int triangulate_poly(unsigned int *indices, int totvert, MVert *verts, std::vector<unsigned int>& tri); - - int count_new_tris(COLLADAFW::Mesh *mesh, Mesh *me); - + bool primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp); bool primitive_has_faces(COLLADAFW::MeshPrimitive *mp); @@ -135,15 +130,16 @@ private: CustomData create_edge_custom_data(EdgeHash *eh); - void allocate_face_data(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris); + void allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me); // TODO: import uv set names - void read_faces(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris); + void read_polys(COLLADAFW::Mesh *mesh, Mesh *me); void read_lines(COLLADAFW::Mesh *mesh, Mesh *me); + unsigned int get_vertex_count(COLLADAFW::Polygons *mp, int index); void get_vector(float v[3], COLLADAFW::MeshVertexData& arr, int i, int stride); - bool flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, int count); + bool is_flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, int count); std::vector<Object *> get_all_users_of(Mesh *reference_mesh); @@ -166,7 +162,7 @@ public: MTFace *assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial, std::map<COLLADAFW::UniqueId, Material*>& uid_material_map, Object *ob, const COLLADAFW::UniqueId *geom_uid, - MTex **color_texture, char *layername, MTFace *texture_face, + char *layername, MTFace *texture_face, std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map, short mat_index); diff --git a/source/blender/collada/SConscript b/source/blender/collada/SConscript index 18ef62e6c85..4da99a21517 100644 --- a/source/blender/collada/SConscript +++ b/source/blender/collada/SConscript @@ -33,9 +33,9 @@ defs = [] # TODO sanitize inc path building # relative paths to include dirs, space-separated, string if env['OURPLATFORM']=='darwin': - incs = '../ikplugin ../../../intern/iksolver/extern ../blenlib ../blenkernel ../windowmanager ../blenloader ../makesdna ../makesrna ../editors/include ../imbuf ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter [OPENCOLLADA]/COLLADABaseUtils [OPENCOLLADA]/COLLADAFramework [OPENCOLLADA]/COLLADASaxFrameworkLoader [OPENCOLLADA]/GeneratedSaxParser '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC']) + incs = '../ikplugin ../../../intern/iksolver/extern ../blenlib ../blenkernel ../windowmanager ../blenloader ../makesdna ../makesrna ../editors/include ../imbuf ../bmesh ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter [OPENCOLLADA]/COLLADABaseUtils [OPENCOLLADA]/COLLADAFramework [OPENCOLLADA]/COLLADASaxFrameworkLoader [OPENCOLLADA]/GeneratedSaxParser '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC']) else: - incs = '../ikplugin ../../../intern/iksolver/extern ../blenlib ../blenkernel ../windowmanager ../makesdna ../blenloader ../makesrna ../editors/include ../imbuf ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter/include [OPENCOLLADA]/COLLADABaseUtils/include [OPENCOLLADA]/COLLADAFramework/include [OPENCOLLADA]/COLLADASaxFrameworkLoader/include [OPENCOLLADA]/GeneratedSaxParser/include '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC']) + incs = '../ikplugin ../../../intern/iksolver/extern ../blenlib ../blenkernel ../windowmanager ../makesdna ../blenloader ../makesrna ../editors/include ../imbuf ../bmesh ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter/include [OPENCOLLADA]/COLLADABaseUtils/include [OPENCOLLADA]/COLLADAFramework/include [OPENCOLLADA]/COLLADASaxFrameworkLoader/include [OPENCOLLADA]/GeneratedSaxParser/include '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC']) if env['BF_BUILDINFO']: defs.append('WITH_BUILDINFO') diff --git a/source/blender/collada/TransformReader.cpp b/source/blender/collada/TransformReader.cpp index 67166d819a6..e0ba77e2554 100644 --- a/source/blender/collada/TransformReader.cpp +++ b/source/blender/collada/TransformReader.cpp @@ -46,30 +46,28 @@ void TransformReader::get_node_mat(float mat[4][4], COLLADAFW::Node *node, std:: COLLADAFW::Transformation *tm = node->getTransformations()[i]; COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); - if (type == COLLADAFW::Transformation::MATRIX) { - // XXX why does this return and discard all following transformations? - dae_matrix_to_mat4(tm, mat); - return; - } - else { - switch (type) { - case COLLADAFW::Transformation::TRANSLATE: - dae_translate_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::ROTATE: - dae_rotate_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::SCALE: - dae_scale_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::LOOKAT: - case COLLADAFW::Transformation::SKEW: - fprintf(stderr, "LOOKAT and SKEW transformations are not supported yet.\n"); - break; - } - copy_m4_m4(copy, mat); - mult_m4_m4m4(mat, copy, cur); + switch (type) { + case COLLADAFW::Transformation::MATRIX: + // XXX why does this return and discard all following transformations? + dae_matrix_to_mat4(tm, mat); + return; + case COLLADAFW::Transformation::TRANSLATE: + dae_translate_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::ROTATE: + dae_rotate_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::SCALE: + dae_scale_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::LOOKAT: + case COLLADAFW::Transformation::SKEW: + fprintf(stderr, "LOOKAT and SKEW transformations are not supported yet.\n"); + break; } + + copy_m4_m4(copy, mat); + mult_m4_m4m4(mat, copy, cur); if (animation_map) { // AnimationList that drives this Transformation diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index b3c288c8fc5..e75123c1e76 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -75,6 +75,7 @@ int collada_export(Scene *sce, int include_material_textures, int use_texture_copies, + int triangulate, int use_object_instantiation, int sort_by_name, int second_life) @@ -106,6 +107,7 @@ int collada_export(Scene *sce, export_settings.include_material_textures= include_material_textures != 0; export_settings.use_texture_copies = use_texture_copies != 0; + export_settings.triangulate = triangulate != 0; export_settings.use_object_instantiation = use_object_instantiation != 0; export_settings.sort_by_name = sort_by_name != 0; export_settings.second_life = second_life != 0; diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h index 2fa5b22bb15..642259da7fc 100644 --- a/source/blender/collada/collada.h +++ b/source/blender/collada/collada.h @@ -67,6 +67,7 @@ int collada_export(Scene *sce, int include_material_textures, int use_texture_copies, + int triangulate, int use_object_instantiation, int sort_by_name, int second_life); diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp index f43878943c1..c56aa3fdaf0 100644 --- a/source/blender/collada/collada_utils.cpp +++ b/source/blender/collada/collada_utils.cpp @@ -56,6 +56,7 @@ extern "C" { #include "WM_api.h" // XXX hrm, see if we can do without this #include "WM_types.h" +#include "bmesh.h" } float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index) @@ -137,25 +138,38 @@ Object *bc_add_object(Scene *scene, int type, const char *name) return ob; } -Mesh *bc_to_mesh_apply_modifiers(Scene *scene, Object *ob, BC_export_mesh_type export_mesh_type) +Mesh *bc_get_mesh_copy(Scene *scene, Object *ob, BC_export_mesh_type export_mesh_type, bool apply_modifiers, bool triangulate) { Mesh *tmpmesh; CustomDataMask mask = CD_MASK_MESH; DerivedMesh *dm = NULL; - switch (export_mesh_type) { - case BC_MESH_TYPE_VIEW: { - dm = mesh_create_derived_view(scene, ob, mask); - break; - } - case BC_MESH_TYPE_RENDER: { - dm = mesh_create_derived_render(scene, ob, mask); - break; + if(apply_modifiers) { + switch (export_mesh_type) { + case BC_MESH_TYPE_VIEW: { + dm = mesh_create_derived_view(scene, ob, mask); + break; + } + case BC_MESH_TYPE_RENDER: { + dm = mesh_create_derived_render(scene, ob, mask); + break; + } } } + else { + dm = mesh_create_derived((Mesh *)ob->data, ob, NULL); + } tmpmesh = BKE_mesh_add(G.main, "ColladaMesh"); // name is not important here DM_to_mesh(dm, tmpmesh, ob); dm->release(dm); + + if (triangulate) { + bc_triangulate_mesh(tmpmesh); + } + + // XXX Not sure if we need that for ngon_export as well. + BKE_mesh_tessface_ensure(tmpmesh); + return tmpmesh; } @@ -366,3 +380,14 @@ void bc_match_scale(std::vector<Object *> *objects_done, } } + +void bc_triangulate_mesh(Mesh *me) { + bool use_beauty = false; + bool tag_only = false; + + BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default); + BM_mesh_bm_from_me(bm, me, FALSE, 0); + BM_mesh_triangulate(bm, use_beauty, tag_only, NULL, NULL); + BM_mesh_bm_to_me(bm, me, FALSE); + BM_mesh_free(bm); +} diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h index 892b57e6a4a..f8e6f09e498 100644 --- a/source/blender/collada/collada_utils.h +++ b/source/blender/collada/collada_utils.h @@ -62,7 +62,7 @@ extern float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsi extern int bc_test_parent_loop(Object *par, Object *ob); extern int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space = true); extern Object *bc_add_object(Scene *scene, int type, const char *name); -extern Mesh *bc_to_mesh_apply_modifiers(Scene *scene, Object *ob, BC_export_mesh_type export_mesh_type); +extern Mesh *bc_get_mesh_copy(Scene *scene, Object *ob, BC_export_mesh_type export_mesh_type, bool apply_modifiers, bool triangulate); extern Object *bc_get_assigned_armature(Object *ob); extern Object *bc_get_highest_selected_ancestor_or_self(LinkNode *export_set, Object *ob); @@ -84,4 +84,24 @@ extern int bc_get_active_UVLayer(Object *ob); extern std::string bc_replace_string(std::string data, const std::string& pattern, const std::string& replacement); extern std::string bc_url_encode(std::string data); extern void bc_match_scale(std::vector<Object *> *objects_done, Scene &sce, UnitConverter &unit_converter); + +extern void bc_triangulate_mesh(Mesh *me); + + +class BCPolygonNormalsIndices +{ + std::vector<unsigned int> normal_indices; + + public: + + void add_index(unsigned int index) { + normal_indices.push_back(index); + } + + unsigned int operator[](unsigned int i) { + return normal_indices[i]; + } + +}; + #endif diff --git a/source/blender/compositor/nodes/COM_ImageNode.cpp b/source/blender/compositor/nodes/COM_ImageNode.cpp index 864d6f08311..e4dd72d79e9 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cpp +++ b/source/blender/compositor/nodes/COM_ImageNode.cpp @@ -25,6 +25,7 @@ #include "COM_ExecutionSystem.h" #include "COM_ImageOperation.h" #include "COM_MultilayerImageOperation.h" +#include "COM_ConvertPremulToStraightOperation.h" #include "BKE_node.h" #include "BLI_utildefines.h" @@ -72,6 +73,7 @@ void ImageNode::convertToOperations(ExecutionSystem *graph, CompositorContext *c ImageUser *imageuser = (ImageUser *)editorNode->storage; int framenumber = context->getFramenumber(); int numberOfOutputs = this->getNumberOfOutputSockets(); + bool outputStraightAlpha = editorNode->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT; BKE_image_user_frame_calc(imageuser, context->getFramenumber(), 0); /* force a load, we assume iuser index will be set OK anyway */ @@ -138,7 +140,15 @@ void ImageNode::convertToOperations(ExecutionSystem *graph, CompositorContext *c if (numberOfOutputs > 0) { ImageOperation *operation = new ImageOperation(); if (outputImage->isConnected()) { - outputImage->relinkConnections(operation->getOutputSocket()); + if (outputStraightAlpha) { + NodeOperation *alphaConvertOperation = new ConvertPremulToStraightOperation(); + addLink(graph, operation->getOutputSocket(0), alphaConvertOperation->getInputSocket(0)); + outputImage->relinkConnections(alphaConvertOperation->getOutputSocket()); + graph->addOperation(alphaConvertOperation); + } + else { + outputImage->relinkConnections(operation->getOutputSocket()); + } } operation->setImage(image); operation->setImageUser(imageuser); diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h index d53c3e464fc..a638ae7ce69 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.h +++ b/source/blender/compositor/operations/COM_TranslateOperation.h @@ -33,8 +33,6 @@ private: float m_deltaX; float m_deltaY; bool m_isDeltaSet; - float m_relativeOffsetX; - float m_relativeOffsetY; float m_factorX; float m_factorY; public: diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index bc7b62df185..533420ad641 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -36,6 +36,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_camera_types.h" @@ -3341,21 +3343,21 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann icon = ICON_VISIBLE_IPO_OFF; if (ale->type == ANIMTYPE_FCURVE) - tooltip = "Channel is visible in Graph Editor for editing"; + tooltip = TIP_("Channel is visible in Graph Editor for editing"); else - tooltip = "Channel(s) are visible in Graph Editor for editing"; + tooltip = TIP_("Channels are visible in Graph Editor for editing"); break; case ACHANNEL_SETTING_EXPAND: /* expanded triangle */ //icon = ((enabled)? ICON_TRIA_DOWN : ICON_TRIA_RIGHT); icon = ICON_TRIA_RIGHT; - tooltip = "Make channels grouped under this channel visible"; + tooltip = TIP_("Make channels grouped under this channel visible"); break; case ACHANNEL_SETTING_SOLO: /* NLA Tracks only */ //icon = ((enabled)? ICON_LAYER_ACTIVE : ICON_LAYER_USED); icon = ICON_LAYER_USED; - tooltip = "NLA Track is the only one evaluated for the AnimData block it belongs to"; + tooltip = TIP_("NLA Track is the only one evaluated for the AnimData block it belongs to"); break; /* --- */ @@ -3364,7 +3366,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann // TODO: what about when there's no protect needed? //icon = ((enabled)? ICON_LOCKED : ICON_UNLOCKED); icon = ICON_UNLOCKED; - tooltip = "Editability of keyframes for this channel"; + tooltip = TIP_("Editability of keyframes for this channel"); break; case ACHANNEL_SETTING_MUTE: /* muted speaker */ @@ -3372,9 +3374,9 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann icon = ICON_MUTE_IPO_OFF; if (ale->type == ALE_FCURVE) - tooltip = "Does F-Curve contribute to result"; + tooltip = TIP_("Does F-Curve contribute to result"); else - tooltip = "Do channels contribute to result"; + tooltip = TIP_("Do channels contribute to result"); break; default: diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index cc2366affe6..dc0fba0930f 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -584,9 +584,9 @@ void ANIM_uiTemplate_fmodifier_draw(uiLayout *layout, ID *id, ListBase *modifier /* name */ if (fmi) - uiItemL(sub, fmi->name, ICON_NONE); + uiItemL(sub, IFACE_(fmi->name), ICON_NONE); else - uiItemL(sub, "<Unknown Modifier>", ICON_NONE); + uiItemL(sub, IFACE_("<Unknown Modifier>"), ICON_NONE); /* right-align ------------------------------------------- */ sub = uiLayoutRow(row, TRUE); diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index 2bd9956ef5a..d9555108733 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -36,16 +36,25 @@ set(INC_SYS ) set(SRC + armature_add.c + armature_edit.c + armature_naming.c armature_ops.c - editarmature.c + armature_relations.c + armature_select.c + armature_skinning.c + armature_utils.c editarmature_generate.c editarmature_retarget.c editarmature_sketch.c meshlaplacian.c - poseSlide.c - poseUtils.c - poselib.c - poseobject.c + pose_edit.c + pose_lib.c + pose_group.c + pose_select.c + pose_slide.c + pose_transform.c + pose_utils.c reeb.c BIF_generate.h diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c new file mode 100644 index 00000000000..531c0551c87 --- /dev/null +++ b/source/blender/editors/armature/armature_add.c @@ -0,0 +1,849 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operators and API's for creating bones + */ + +/** \file blender/editors/armature/armature_add.c + * \ingroup edarmature + */ + +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BKE_action.h" +#include "BKE_constraint.h" +#include "BKE_context.h" +#include "BKE_idprop.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "armature_intern.h" + +/* *************** Adding stuff in editmode *************** */ + +/* default bone add, returns it selected, but without tail set */ +/* XXX should be used everywhere, now it mallocs bones still locally in functions */ +EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name) +{ + EditBone *bone = MEM_callocN(sizeof(EditBone), "eBone"); + + BLI_strncpy(bone->name, name, sizeof(bone->name)); + unique_editbone_name(arm->edbo, bone->name, NULL); + + BLI_addtail(arm->edbo, bone); + + bone->flag |= BONE_TIPSEL; + bone->weight = 1.0f; + bone->dist = 0.25f; + bone->xwidth = 0.1f; + bone->zwidth = 0.1f; + bone->ease1 = 1.0f; + bone->ease2 = 1.0f; + bone->rad_head = 0.10f; + bone->rad_tail = 0.05f; + bone->segments = 1; + bone->layer = arm->layer; + + return bone; +} + +/* v3d and rv3d are allowed to be NULL */ +void add_primitive_bone(Scene *scene, View3D *v3d, RegionView3D *rv3d) +{ + Object *obedit = scene->obedit; // XXX get from context + bArmature *arm = obedit->data; + float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3]; + EditBone *bone; + + /* Get inverse point for head and orientation for tail */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d)); + + if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) + copy_m3_m4(obmat, rv3d->viewmat); + else unit_m3(obmat); + + copy_m3_m4(viewmat, obedit->obmat); + mul_m3_m3m3(totmat, obmat, viewmat); + invert_m3_m3(imat, totmat); + + ED_armature_deselect_all(obedit, 0); + + /* Create a bone */ + bone = ED_armature_edit_bone_add(arm, "Bone"); + + arm->act_edbone = bone; + + copy_v3_v3(bone->head, curs); + + if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) + add_v3_v3v3(bone->tail, bone->head, imat[1]); // bone with unit length 1 + else + add_v3_v3v3(bone->tail, bone->head, imat[2]); // bone with unit length 1, pointing up Z +} + + +/* previously addvert_armature */ +/* the ctrl-click method */ +static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op)) +{ + View3D *v3d; + bArmature *arm; + EditBone *ebone, *newbone, *flipbone; + float mat[3][3], imat[3][3]; + const float *curs; + int a, to_root = 0; + Object *obedit; + Scene *scene; + + scene = CTX_data_scene(C); + v3d = CTX_wm_view3d(C); + obedit = CTX_data_edit_object(C); + arm = obedit->data; + + /* find the active or selected bone */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (ebone->flag & BONE_TIPSEL || arm->act_edbone == ebone) + break; + } + } + + if (ebone == NULL) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (ebone->flag & BONE_ROOTSEL || arm->act_edbone == ebone) + break; + } + } + if (ebone == NULL) + return OPERATOR_CANCELLED; + + to_root = 1; + } + + ED_armature_deselect_all(obedit, 0); + + /* we re-use code for mirror editing... */ + flipbone = NULL; + if (arm->flag & ARM_MIRROR_EDIT) + flipbone = ED_armature_bone_get_mirrored(arm->edbo, ebone); + + for (a = 0; a < 2; a++) { + if (a == 1) { + if (flipbone == NULL) + break; + else { + SWAP(EditBone *, flipbone, ebone); + } + } + + newbone = ED_armature_edit_bone_add(arm, ebone->name); + arm->act_edbone = newbone; + + if (to_root) { + copy_v3_v3(newbone->head, ebone->head); + newbone->rad_head = ebone->rad_tail; + newbone->parent = ebone->parent; + } + else { + copy_v3_v3(newbone->head, ebone->tail); + newbone->rad_head = ebone->rad_tail; + newbone->parent = ebone; + newbone->flag |= BONE_CONNECTED; + } + + curs = give_cursor(scene, v3d); + copy_v3_v3(newbone->tail, curs); + sub_v3_v3v3(newbone->tail, newbone->tail, obedit->obmat[3]); + + if (a == 1) + newbone->tail[0] = -newbone->tail[0]; + + copy_m3_m4(mat, obedit->obmat); + invert_m3_m3(imat, mat); + mul_m3_v3(imat, newbone->tail); + + newbone->length = len_v3v3(newbone->head, newbone->tail); + newbone->rad_tail = newbone->length * 0.05f; + newbone->dist = newbone->length * 0.25f; + + } + + ED_armature_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +static int armature_click_extrude_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + /* TODO most of this code is copied from set3dcursor_invoke, + * it would be better to reuse code in set3dcursor_invoke */ + + /* temporarily change 3d cursor position */ + Scene *scene; + ARegion *ar; + View3D *v3d; + float *fp, tvec[3], oldcurs[3], mval_f[2]; + int retv; + + scene = CTX_data_scene(C); + ar = CTX_wm_region(C); + v3d = CTX_wm_view3d(C); + + fp = give_cursor(scene, v3d); + + copy_v3_v3(oldcurs, fp); + + VECCOPY2D(mval_f, event->mval); + ED_view3d_win_to_3d(ar, fp, mval_f, tvec); + copy_v3_v3(fp, tvec); + + /* extrude to the where new cursor is and store the operation result */ + retv = armature_click_extrude_exec(C, op); + + /* restore previous 3d cursor position */ + copy_v3_v3(fp, oldcurs); + + return retv; +} + +void ARMATURE_OT_click_extrude(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Click-Extrude"; + ot->idname = "ARMATURE_OT_click_extrude"; + ot->description = "Create a new bone going from the last selected joint to the mouse position"; + + /* api callbacks */ + ot->invoke = armature_click_extrude_invoke; + ot->exec = armature_click_extrude_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ +} + +/* adds an EditBone between the nominated locations (should be in the right space) */ +EditBone *add_points_bone(Object *obedit, float head[3], float tail[3]) +{ + EditBone *ebo; + + ebo = ED_armature_edit_bone_add(obedit->data, "Bone"); + + copy_v3_v3(ebo->head, head); + copy_v3_v3(ebo->tail, tail); + + return ebo; +} + + +static EditBone *get_named_editbone(ListBase *edbo, char *name) +{ + EditBone *eBone; + + if (name) { + for (eBone = edbo->first; eBone; eBone = eBone->next) { + if (!strcmp(name, eBone->name)) + return eBone; + } + } + + return NULL; +} + +/* Call this before doing any duplications + * */ +void preEditBoneDuplicate(ListBase *editbones) +{ + EditBone *eBone; + + /* clear temp */ + for (eBone = editbones->first; eBone; eBone = eBone->next) { + eBone->temp = NULL; + } +} + +/* + * Note: When duplicating cross objects, editbones here is the list of bones + * from the SOURCE object but ob is the DESTINATION object + * */ +void updateDuplicateSubtargetObjects(EditBone *dupBone, ListBase *editbones, Object *src_ob, Object *dst_ob) +{ + /* If an edit bone has been duplicated, lets + * update it's constraints if the subtarget + * they point to has also been duplicated + */ + EditBone *oldtarget, *newtarget; + bPoseChannel *pchan; + bConstraint *curcon; + ListBase *conlist; + + if ( (pchan = BKE_pose_channel_verify(dst_ob->pose, dupBone->name)) ) { + if ( (conlist = &pchan->constraints) ) { + for (curcon = conlist->first; curcon; curcon = curcon->next) { + /* does this constraint have a subtarget in + * this armature? + */ + bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(curcon); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(curcon, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + if ((ct->tar == src_ob) && (ct->subtarget[0])) { + ct->tar = dst_ob; /* update target */ + oldtarget = get_named_editbone(editbones, ct->subtarget); + if (oldtarget) { + /* was the subtarget bone duplicated too? If + * so, update the constraint to point at the + * duplicate of the old subtarget. + */ + if (oldtarget->temp) { + newtarget = (EditBone *) oldtarget->temp; + BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget)); + } + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(curcon, &targets, 0); + } + } + } + } +} + +void updateDuplicateSubtarget(EditBone *dupBone, ListBase *editbones, Object *ob) +{ + updateDuplicateSubtargetObjects(dupBone, editbones, ob, ob); +} + + +EditBone *duplicateEditBoneObjects(EditBone *curBone, const char *name, ListBase *editbones, + Object *src_ob, Object *dst_ob) +{ + EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone"); + + /* Copy data from old bone to new bone */ + memcpy(eBone, curBone, sizeof(EditBone)); + + curBone->temp = eBone; + eBone->temp = curBone; + + if (name != NULL) { + BLI_strncpy(eBone->name, name, sizeof(eBone->name)); + } + + unique_editbone_name(editbones, eBone->name, NULL); + BLI_addtail(editbones, eBone); + + /* copy the ID property */ + if (curBone->prop) + eBone->prop = IDP_CopyProperty(curBone->prop); + + /* Lets duplicate the list of constraints that the + * current bone has. + */ + if (src_ob->pose) { + bPoseChannel *chanold, *channew; + + chanold = BKE_pose_channel_verify(src_ob->pose, curBone->name); + if (chanold) { + /* WARNING: this creates a new posechannel, but there will not be an attached bone + * yet as the new bones created here are still 'EditBones' not 'Bones'. + */ + channew = BKE_pose_channel_verify(dst_ob->pose, eBone->name); + + if (channew) { + BKE_pose_channel_copy_data(channew, chanold); + } + } + } + + return eBone; +} + +EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob) +{ + return duplicateEditBoneObjects(curBone, name, editbones, ob, ob); +} + +/* previously adduplicate_armature */ +static int armature_duplicate_selected_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bArmature *arm; + EditBone *eBone = NULL; + EditBone *curBone; + EditBone *firstDup = NULL; /* The beginning of the duplicated bones in the edbo list */ + + Object *obedit = CTX_data_edit_object(C); + arm = obedit->data; + + /* cancel if nothing selected */ + if (CTX_DATA_COUNT(C, selected_bones) == 0) + return OPERATOR_CANCELLED; + + ED_armature_sync_selection(arm->edbo); // XXX why is this needed? + + preEditBoneDuplicate(arm->edbo); + + /* Select mirrored bones */ + if (arm->flag & ARM_MIRROR_EDIT) { + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (EBONE_VISIBLE(arm, curBone)) { + if (curBone->flag & BONE_SELECTED) { + eBone = ED_armature_bone_get_mirrored(arm->edbo, curBone); + if (eBone) + eBone->flag |= BONE_SELECTED; + } + } + } + } + + + /* Find the selected bones and duplicate them as needed */ + for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { + if (EBONE_VISIBLE(arm, curBone)) { + if (curBone->flag & BONE_SELECTED) { + + eBone = duplicateEditBone(curBone, curBone->name, arm->edbo, obedit); + + if (!firstDup) + firstDup = eBone; + + } + } + } + + /* Run though the list and fix the pointers */ + for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { + if (EBONE_VISIBLE(arm, curBone)) { + if (curBone->flag & BONE_SELECTED) { + eBone = (EditBone *) curBone->temp; + + if (!curBone->parent) { + /* If this bone has no parent, + * Set the duplicate->parent to NULL + */ + eBone->parent = NULL; + } + else if (curBone->parent->temp) { + /* If this bone has a parent that was duplicated, + * Set the duplicate->parent to the curBone->parent->temp + */ + eBone->parent = (EditBone *)curBone->parent->temp; + } + else { + /* If this bone has a parent that IS not selected, + * Set the duplicate->parent to the curBone->parent + */ + eBone->parent = (EditBone *) curBone->parent; + eBone->flag &= ~BONE_CONNECTED; + } + + /* Lets try to fix any constraint subtargets that might + * have been duplicated + */ + updateDuplicateSubtarget(eBone, arm->edbo, obedit); + } + } + } + + /* correct the active bone */ + if (arm->act_edbone) { + eBone = arm->act_edbone; + if (eBone->temp) + arm->act_edbone = eBone->temp; + } + + /* Deselect the old bones and select the new ones */ + for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { + if (EBONE_VISIBLE(arm, curBone)) + curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + + ED_armature_validate_active(arm); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + + +void ARMATURE_OT_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Selected Bone(s)"; + ot->idname = "ARMATURE_OT_duplicate"; + ot->description = "Make copies of the selected bones within the same armature"; + + /* api callbacks */ + ot->exec = armature_duplicate_selected_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ------------------------------------------ */ + +/* previously extrude_armature */ +/* context; editmode armature */ +/* if forked && mirror-edit: makes two bones with flipped names */ +static int armature_extrude_exec(bContext *C, wmOperator *op) +{ + Object *obedit; + bArmature *arm; + EditBone *newbone, *ebone, *flipbone, *first = NULL; + int a, totbone = 0, do_extrude; + int forked = RNA_boolean_get(op->ptr, "forked"); + + obedit = CTX_data_edit_object(C); + arm = obedit->data; + + /* since we allow root extrude too, we have to make sure selection is OK */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if (ebone->flag & BONE_ROOTSEL) { + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + if (ebone->parent->flag & BONE_TIPSEL) + ebone->flag &= ~BONE_ROOTSEL; + } + } + } + } + + /* Duplicate the necessary bones */ + for (ebone = arm->edbo->first; ((ebone) && (ebone != first)); ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + /* we extrude per definition the tip */ + do_extrude = FALSE; + if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED)) { + do_extrude = TRUE; + } + else if (ebone->flag & BONE_ROOTSEL) { + /* but, a bone with parent deselected we do the root... */ + if (ebone->parent && (ebone->parent->flag & BONE_TIPSEL)) { + /* pass */ + } + else { + do_extrude = 2; + } + } + + if (do_extrude) { + /* we re-use code for mirror editing... */ + flipbone = NULL; + if (arm->flag & ARM_MIRROR_EDIT) { + flipbone = ED_armature_bone_get_mirrored(arm->edbo, ebone); + if (flipbone) { + forked = 0; // we extrude 2 different bones + if (flipbone->flag & (BONE_TIPSEL | BONE_ROOTSEL | BONE_SELECTED)) + /* don't want this bone to be selected... */ + flipbone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + } + if ((flipbone == NULL) && (forked)) + flipbone = ebone; + } + + for (a = 0; a < 2; a++) { + if (a == 1) { + if (flipbone == NULL) + break; + else { + SWAP(EditBone *, flipbone, ebone); + } + } + + totbone++; + newbone = MEM_callocN(sizeof(EditBone), "extrudebone"); + + if (do_extrude == TRUE) { + copy_v3_v3(newbone->head, ebone->tail); + copy_v3_v3(newbone->tail, newbone->head); + newbone->parent = ebone; + + newbone->flag = ebone->flag & (BONE_TIPSEL | BONE_RELATIVE_PARENTING); // copies it, in case mirrored bone + + if (newbone->parent) newbone->flag |= BONE_CONNECTED; + } + else { + copy_v3_v3(newbone->head, ebone->head); + copy_v3_v3(newbone->tail, ebone->head); + newbone->parent = ebone->parent; + + newbone->flag = BONE_TIPSEL; + + if (newbone->parent && (ebone->flag & BONE_CONNECTED)) { + newbone->flag |= BONE_CONNECTED; + } + } + + newbone->weight = ebone->weight; + newbone->dist = ebone->dist; + newbone->xwidth = ebone->xwidth; + newbone->zwidth = ebone->zwidth; + newbone->ease1 = ebone->ease1; + newbone->ease2 = ebone->ease2; + newbone->rad_head = ebone->rad_tail; // don't copy entire bone... + newbone->rad_tail = ebone->rad_tail; + newbone->segments = 1; + newbone->layer = ebone->layer; + + BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name)); + + if (flipbone && forked) { // only set if mirror edit + if (strlen(newbone->name) < 30) { + if (a == 0) strcat(newbone->name, "_L"); + else strcat(newbone->name, "_R"); + } + } + unique_editbone_name(arm->edbo, newbone->name, NULL); + + /* Add the new bone to the list */ + BLI_addtail(arm->edbo, newbone); + if (!first) + first = newbone; + + /* restore ebone if we were flipping */ + if (a == 1 && flipbone) + SWAP(EditBone *, flipbone, ebone); + } + } + + /* Deselect the old bone */ + ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + } + } + /* if only one bone, make this one active */ + if (totbone == 1 && first) arm->act_edbone = first; + + if (totbone == 0) return OPERATOR_CANCELLED; + + /* Transform the endpoints */ + ED_armature_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_extrude(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Extrude"; + ot->idname = "ARMATURE_OT_extrude"; + ot->description = "Create new bones from the selected joints"; + + /* api callbacks */ + ot->exec = armature_extrude_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "forked", 0, "Forked", ""); +} + +/* ********************** Bone Add *************************************/ + +/*op makes a new bone and returns it with its tip selected */ + +static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) +{ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + Object *obedit = CTX_data_edit_object(C); + EditBone *bone; + float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3]; + char name[MAXBONENAME]; + + RNA_string_get(op->ptr, "name", name); + + copy_v3_v3(curs, give_cursor(CTX_data_scene(C), CTX_wm_view3d(C))); + + /* Get inverse point for head and orientation for tail */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_m4_v3(obedit->imat, curs); + + if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) + copy_m3_m4(obmat, rv3d->viewmat); + else unit_m3(obmat); + + copy_m3_m4(viewmat, obedit->obmat); + mul_m3_m3m3(totmat, obmat, viewmat); + invert_m3_m3(imat, totmat); + + ED_armature_deselect_all(obedit, 0); + + /* Create a bone */ + bone = ED_armature_edit_bone_add(obedit->data, name); + + copy_v3_v3(bone->head, curs); + + if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) + add_v3_v3v3(bone->tail, bone->head, imat[1]); // bone with unit length 1 + else + add_v3_v3v3(bone->tail, bone->head, imat[2]); // bone with unit length 1, pointing up Z + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_bone_primitive_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Bone"; + ot->idname = "ARMATURE_OT_bone_primitive_add"; + ot->description = "Add a new bone located at the 3D-Cursor"; + + /* api callbacks */ + ot->exec = armature_bone_primitive_add_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_string(ot->srna, "name", "Bone", MAXBONENAME, "Name", "Name of the newly created bone"); + +} + +/* ********************** Subdivide *******************************/ + +/* Subdivide Operators: + * This group of operators all use the same 'exec' callback, but they are called + * through several different operators - a combined menu (which just calls the exec in the + * appropriate ways), and two separate ones. + */ + +static int armature_subdivide_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = obedit->data; + EditBone *newbone, *tbone; + int cuts, i; + + /* there may not be a number_cuts property defined (for 'simple' subdivide) */ + cuts = RNA_int_get(op->ptr, "number_cuts"); + + /* loop over all editable bones */ + // XXX the old code did this in reverse order though! + CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) + { + for (i = cuts + 1; i > 1; i--) { + /* compute cut ratio first */ + float cutratio = 1.0f / (float)i; + float cutratioI = 1.0f - cutratio; + + float val1[3]; + float val2[3]; + float val3[3]; + + newbone = MEM_mallocN(sizeof(EditBone), "ebone subdiv"); + *newbone = *ebone; + BLI_addtail(arm->edbo, newbone); + + /* calculate location of newbone->head */ + copy_v3_v3(val1, ebone->head); + copy_v3_v3(val2, ebone->tail); + copy_v3_v3(val3, newbone->head); + + val3[0] = val1[0] * cutratio + val2[0] * cutratioI; + val3[1] = val1[1] * cutratio + val2[1] * cutratioI; + val3[2] = val1[2] * cutratio + val2[2] * cutratioI; + + copy_v3_v3(newbone->head, val3); + copy_v3_v3(newbone->tail, ebone->tail); + copy_v3_v3(ebone->tail, newbone->head); + + newbone->rad_head = 0.5f * (ebone->rad_head + ebone->rad_tail); + ebone->rad_tail = newbone->rad_head; + + newbone->flag |= BONE_CONNECTED; + + unique_editbone_name(arm->edbo, newbone->name, NULL); + + /* correct parent bones */ + for (tbone = arm->edbo->first; tbone; tbone = tbone->next) { + if (tbone->parent == ebone) + tbone->parent = newbone; + } + newbone->parent = ebone; + } + } + CTX_DATA_END; + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_subdivide(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Subdivide Multi"; + ot->idname = "ARMATURE_OT_subdivide"; + ot->description = "Break selected bones into chains of smaller bones"; + + /* api callbacks */ + ot->exec = armature_subdivide_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties */ + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10); + /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c new file mode 100644 index 00000000000..12602c10955 --- /dev/null +++ b/source/blender/editors/armature/armature_edit.c @@ -0,0 +1,1245 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + * + * Armature EditMode tools - transforms, chain based editing, and other settings + */ + +/** \file blender/editors/armature/armature_edit.c + * \ingroup edarmature + */ + +#include <assert.h> + +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_constraint.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_report.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "armature_intern.h" + +/* ************************** Object Tools Exports ******************************* */ +/* NOTE: these functions are exported to the Object module to be called from the tools there */ + +void ED_armature_apply_transform(Object *ob, float mat[4][4]) +{ + EditBone *ebone; + bArmature *arm = ob->data; + float scale = mat4_to_scale(mat); /* store the scale of the matrix here to use on envelopes */ + float mat3[3][3]; + + copy_m3_m4(mat3, mat); + normalize_m3(mat3); + + /* Put the armature into editmode */ + ED_armature_to_edit(ob); + + /* Do the rotations */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + float delta[3], tmat[3][3]; + + /* find the current bone's roll matrix */ + sub_v3_v3v3(delta, ebone->tail, ebone->head); + vec_roll_to_mat3(delta, ebone->roll, tmat); + + /* transform the roll matrix */ + mul_m3_m3m3(tmat, mat3, tmat); + + /* transform the bone */ + mul_m4_v3(mat, ebone->head); + mul_m4_v3(mat, ebone->tail); + + /* apply the transfiormed roll back */ + mat3_to_vec_roll(tmat, NULL, &ebone->roll); + + ebone->rad_head *= scale; + ebone->rad_tail *= scale; + ebone->dist *= scale; + + /* we could be smarter and scale by the matrix along the x & z axis */ + ebone->xwidth *= scale; + ebone->zwidth *= scale; + } + + /* Turn the list into an armature */ + ED_armature_from_edit(ob); + ED_armature_edit_free(ob); +} + +/* exported for use in editors/object/ */ +/* 0 == do center, 1 == center new, 2 == center cursor */ +void ED_armature_origin_set(Scene *scene, Object *ob, float cursor[3], int centermode, int around) +{ + Object *obedit = scene->obedit; // XXX get from context + EditBone *ebone; + bArmature *arm = ob->data; + float cent[3]; + + /* Put the armature into editmode */ + if (ob != obedit) { + ED_armature_to_edit(ob); + obedit = NULL; /* we cant use this so behave as if there is no obedit */ + } + + /* Find the centerpoint */ + if (centermode == 2) { + copy_v3_v3(cent, cursor); + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_v3(ob->imat, cent); + } + else { + if (around == V3D_CENTROID) { + int total = 0; + zero_v3(cent); + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + total += 2; + add_v3_v3(cent, ebone->head); + add_v3_v3(cent, ebone->tail); + } + if (total) { + mul_v3_fl(cent, 1.0f / (float)total); + } + } + else { + float min[3], max[3]; + INIT_MINMAX(min, max); + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + minmax_v3v3_v3(min, max, ebone->head); + minmax_v3v3_v3(min, max, ebone->tail); + } + mid_v3_v3v3(cent, min, max); + } + } + + /* Do the adjustments */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + sub_v3_v3(ebone->head, cent); + sub_v3_v3(ebone->tail, cent); + } + + /* Turn the list into an armature */ + if (obedit == NULL) { + ED_armature_from_edit(ob); + ED_armature_edit_free(ob); + } + + /* Adjust object location for new centerpoint */ + if (centermode && obedit == NULL) { + mul_mat3_m4_v3(ob->obmat, cent); /* ommit translation part */ + add_v3_v3(ob->loc, cent); + } +} + +/* ********************************* Roll ******************************* */ + +/* adjust bone roll to align Z axis with vector + * vec is in local space and is normalized + */ +float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const short axis_only) +{ + float mat[3][3], nor[3]; + + sub_v3_v3v3(nor, bone->tail, bone->head); + vec_roll_to_mat3(nor, 0.0f, mat); + + /* check the bone isn't aligned with the axis */ + if (!is_zero_v3(align_axis) && angle_v3v3(align_axis, mat[2]) > FLT_EPSILON) { + float vec[3], align_axis_proj[3], roll; + + /* project the new_up_axis along the normal */ + project_v3_v3v3(vec, align_axis, nor); + sub_v3_v3v3(align_axis_proj, align_axis, vec); + + if (axis_only) { + if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI / 2.0)) { + negate_v3(align_axis_proj); + } + } + + roll = angle_v3v3(align_axis_proj, mat[2]); + + cross_v3_v3v3(vec, mat[2], align_axis_proj); + + if (dot_v3v3(vec, nor) < 0) { + roll = -roll; + } + + return roll; + } + + return 0.0f; +} + + +typedef enum eCalcRollTypes { + CALC_ROLL_X = 0, + CALC_ROLL_Y = 1, + CALC_ROLL_Z = 2, + + CALC_ROLL_ACTIVE = 5, + CALC_ROLL_VIEW = 6, + CALC_ROLL_CURSOR = 7 +} eCalcRollTypes; + +static EnumPropertyItem prop_calc_roll_types[] = { + {CALC_ROLL_X, "X", 0, "X Axis", ""}, + {CALC_ROLL_Y, "Y", 0, "Y Axis", ""}, + {CALC_ROLL_Z, "Z", 0, "Z Axis", ""}, + {CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""}, + {CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""}, + {CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""}, + {0, NULL, 0, NULL, NULL} +}; + + +static int armature_calc_roll_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_edit_object(C); + const short type = RNA_enum_get(op->ptr, "type"); + const short axis_only = RNA_boolean_get(op->ptr, "axis_only"); + const short axis_flip = RNA_boolean_get(op->ptr, "axis_flip"); + + float imat[3][3]; + + bArmature *arm = ob->data; + EditBone *ebone; + + copy_m3_m4(imat, ob->obmat); + invert_m3(imat); + + if (type == CALC_ROLL_CURSOR) { /* Cursor */ + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); /* can be NULL */ + float cursor_local[3]; + const float *cursor = give_cursor(scene, v3d); + + + copy_v3_v3(cursor_local, cursor); + mul_m3_v3(imat, cursor_local); + + /* cursor */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { + float cursor_rel[3]; + sub_v3_v3v3(cursor_rel, cursor_local, ebone->head); + if (axis_flip) negate_v3(cursor_rel); + ebone->roll = ED_rollBoneToVector(ebone, cursor_rel, axis_only); + } + } + } + else { + float vec[3] = {0.0f, 0.0f, 0.0f}; + if (type == CALC_ROLL_VIEW) { /* View */ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d == NULL) { + BKE_report(op->reports, RPT_ERROR, "No region view3d available"); + return OPERATOR_CANCELLED; + } + + copy_v3_v3(vec, rv3d->viewinv[2]); + mul_m3_v3(imat, vec); + } + else if (type == CALC_ROLL_ACTIVE) { + float mat[3][3], nor[3]; + ebone = (EditBone *)arm->act_edbone; + if (ebone == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active bone set"); + return OPERATOR_CANCELLED; + } + + sub_v3_v3v3(nor, ebone->tail, ebone->head); + vec_roll_to_mat3(nor, ebone->roll, mat); + copy_v3_v3(vec, mat[2]); + } + else { /* Axis */ + assert(type >= 0 && type <= 5); + if (type < 3) vec[type] = 1.0f; + else vec[type - 2] = -1.0f; + mul_m3_v3(imat, vec); + } + + if (axis_flip) negate_v3(vec); + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { + /* roll func is a callback which assumes that all is well */ + ebone->roll = ED_rollBoneToVector(ebone, vec, axis_only); + } + } + } + + if (arm->flag & ARM_MIRROR_EDIT) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if ((EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) == 0) { + EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, ebone); + if (ebone_mirr && (EBONE_VISIBLE(arm, ebone_mirr) && EBONE_EDITABLE(ebone_mirr))) { + ebone->roll = -ebone_mirr->roll; + } + } + } + } + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_calculate_roll(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Recalculate Roll"; + ot->idname = "ARMATURE_OT_calculate_roll"; + ot->description = "Automatically fix alignment of select bones' axes"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_calc_roll_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, 0, "Type", ""); + RNA_def_boolean(ot->srna, "axis_flip", 0, "Flip Axis", "Negate the alignment axis"); + RNA_def_boolean(ot->srna, "axis_only", 0, "Shortest Rotation", "Ignore the axis direction, use the shortest rotation to align"); +} + +/* ******************************** Chain-Based Tools ********************************* */ + +/* temporary data-structure for merge/fill bones */ +typedef struct EditBonePoint { + struct EditBonePoint *next, *prev; + + EditBone *head_owner; /* EditBone which uses this point as a 'head' point */ + EditBone *tail_owner; /* EditBone which uses this point as a 'tail' point */ + + float vec[3]; /* the actual location of the point in local/EditMode space */ +} EditBonePoint; + +/* find chain-tips (i.e. bones without children) */ +static void chains_find_tips(ListBase *edbo, ListBase *list) +{ + EditBone *curBone, *ebo; + LinkData *ld; + + /* note: this is potentially very slow ... there's got to be a better way */ + for (curBone = edbo->first; curBone; curBone = curBone->next) { + short stop = 0; + + /* is this bone contained within any existing chain? (skip if so) */ + for (ld = list->first; ld; ld = ld->next) { + for (ebo = ld->data; ebo; ebo = ebo->parent) { + if (ebo == curBone) { + stop = 1; + break; + } + } + + if (stop) break; + } + /* skip current bone if it is part of an existing chain */ + if (stop) continue; + + /* is any existing chain part of the chain formed by this bone? */ + stop = 0; + for (ebo = curBone->parent; ebo; ebo = ebo->parent) { + for (ld = list->first; ld; ld = ld->next) { + if (ld->data == ebo) { + ld->data = curBone; + stop = 1; + break; + } + } + + if (stop) break; + } + /* current bone has already been added to a chain? */ + if (stop) continue; + + /* add current bone to a new chain */ + ld = MEM_callocN(sizeof(LinkData), "BoneChain"); + ld->data = curBone; + BLI_addtail(list, ld); + } +} + +/* --------------------- */ + +static void fill_add_joint(EditBone *ebo, short eb_tail, ListBase *points) +{ + EditBonePoint *ebp; + float vec[3]; + short found = 0; + + if (eb_tail) { + copy_v3_v3(vec, ebo->tail); + } + else { + copy_v3_v3(vec, ebo->head); + } + + for (ebp = points->first; ebp; ebp = ebp->next) { + if (equals_v3v3(ebp->vec, vec)) { + if (eb_tail) { + if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) { + /* so this bone's tail owner is this bone */ + ebp->tail_owner = ebo; + found = 1; + break; + } + } + else { + if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) { + /* so this bone's head owner is this bone */ + ebp->head_owner = ebo; + found = 1; + break; + } + } + } + } + + /* allocate a new point if no existing point was related */ + if (found == 0) { + ebp = MEM_callocN(sizeof(EditBonePoint), "EditBonePoint"); + + if (eb_tail) { + copy_v3_v3(ebp->vec, ebo->tail); + ebp->tail_owner = ebo; + } + else { + copy_v3_v3(ebp->vec, ebo->head); + ebp->head_owner = ebo; + } + + BLI_addtail(points, ebp); + } +} + +/* bone adding between selected joints */ +static int armature_fill_bones_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = (obedit) ? obedit->data : NULL; + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + ListBase points = {NULL, NULL}; + int count; + + /* sanity checks */ + if (ELEM(NULL, obedit, arm)) + return OPERATOR_CANCELLED; + + /* loop over all bones, and only consider if visible */ + CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) + { + if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL)) + fill_add_joint(ebone, 0, &points); + if (ebone->flag & BONE_TIPSEL) + fill_add_joint(ebone, 1, &points); + } + CTX_DATA_END; + + /* the number of joints determines how we fill: + * 1) between joint and cursor (joint=head, cursor=tail) + * 2) between the two joints (order is dependent on active-bone/hierachy) + * 3+) error (a smarter method involving finding chains needs to be worked out + */ + count = BLI_countlist(&points); + + if (count == 0) { + BKE_report(op->reports, RPT_ERROR, "No joints selected"); + return OPERATOR_CANCELLED; + } + else if (count == 1) { + EditBonePoint *ebp; + float curs[3]; + + /* Get Points - selected joint */ + ebp = (EditBonePoint *)points.first; + + /* Get points - cursor (tail) */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d)); + + /* Create a bone */ + /* newbone = */ add_points_bone(obedit, ebp->vec, curs); + } + else if (count == 2) { + EditBonePoint *ebp, *ebp2; + float head[3], tail[3]; + short headtail = 0; + + /* check that the points don't belong to the same bone */ + ebp = (EditBonePoint *)points.first; + ebp2 = ebp->next; + + if ((ebp->head_owner == ebp2->tail_owner) && (ebp->head_owner != NULL)) { + BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); + BLI_freelistN(&points); + return OPERATOR_CANCELLED; + } + if ((ebp->tail_owner == ebp2->head_owner) && (ebp->tail_owner != NULL)) { + BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); + BLI_freelistN(&points); + return OPERATOR_CANCELLED; + } + + /* find which one should be the 'head' */ + if ((ebp->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) { + /* rule: whichever one is closer to 3d-cursor */ + float curs[3]; + float vecA[3], vecB[3]; + float distA, distB; + + /* get cursor location */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d)); + + /* get distances */ + sub_v3_v3v3(vecA, ebp->vec, curs); + sub_v3_v3v3(vecB, ebp2->vec, curs); + distA = len_v3(vecA); + distB = len_v3(vecB); + + /* compare distances - closer one therefore acts as direction for bone to go */ + headtail = (distA < distB) ? 2 : 1; + } + else if (ebp->head_owner) { + headtail = 1; + } + else if (ebp2->head_owner) { + headtail = 2; + } + + /* assign head/tail combinations */ + if (headtail == 2) { + copy_v3_v3(head, ebp->vec); + copy_v3_v3(tail, ebp2->vec); + } + else if (headtail == 1) { + copy_v3_v3(head, ebp2->vec); + copy_v3_v3(tail, ebp->vec); + } + + /* add new bone and parent it to the appropriate end */ + if (headtail) { + EditBone *newbone = add_points_bone(obedit, head, tail); + + /* do parenting (will need to set connected flag too) */ + if (headtail == 2) { + /* ebp tail or head - tail gets priority */ + if (ebp->tail_owner) + newbone->parent = ebp->tail_owner; + else + newbone->parent = ebp->head_owner; + } + else { + /* ebp2 tail or head - tail gets priority */ + if (ebp2->tail_owner) + newbone->parent = ebp2->tail_owner; + else + newbone->parent = ebp2->head_owner; + } + + newbone->flag |= BONE_CONNECTED; + } + } + else { + /* FIXME.. figure out a method for multiple bones */ + BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count); + BLI_freelistN(&points); + return OPERATOR_CANCELLED; + } + + /* updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); + + /* free points */ + BLI_freelistN(&points); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_fill(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Fill Between Joints"; + ot->idname = "ARMATURE_OT_fill"; + ot->description = "Add bone between selected joint(s) and/or 3D-Cursor"; + + /* callbacks */ + ot->exec = armature_fill_bones_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* --------------------- */ + +/* this function merges between two bones, removes them and those in-between, + * and adjusts the parent relationships for those in-between + */ +static void bones_merge(Object *obedit, EditBone *start, EditBone *end, EditBone *endchild, ListBase *chains) +{ + bArmature *arm = obedit->data; + EditBone *ebo, *ebone, *newbone; + LinkData *chain; + float head[3], tail[3]; + + /* check if same bone */ + if (start == end) { + if (G.debug & G_DEBUG) { + printf("Error: same bone!\n"); + printf("\tstart = %s, end = %s\n", start->name, end->name); + } + } + + /* step 1: add a new bone + * - head = head/tail of start (default head) + * - tail = head/tail of end (default tail) + * - parent = parent of start + */ + if ((start->flag & BONE_TIPSEL) && (start->flag & BONE_SELECTED) == 0) { + copy_v3_v3(head, start->tail); + } + else { + copy_v3_v3(head, start->head); + } + if ((end->flag & BONE_ROOTSEL) && (end->flag & BONE_SELECTED) == 0) { + copy_v3_v3(tail, end->head); + } + else { + copy_v3_v3(tail, end->tail); + } + newbone = add_points_bone(obedit, head, tail); + newbone->parent = start->parent; + + /* TODO, copy more things to the new bone */ + newbone->flag = start->flag & (BONE_HINGE | BONE_NO_DEFORM | BONE_NO_SCALE | + BONE_NO_CYCLICOFFSET | BONE_NO_LOCAL_LOCATION | BONE_DONE); + + /* step 2a: reparent any side chains which may be parented to any bone in the chain of bones to merge + * - potentially several tips for side chains leading to some tree exist... + */ + for (chain = chains->first; chain; chain = chain->next) { + /* traverse down chain until we hit the bottom or if we run into the tip of the chain of bones we're + * merging (need to stop in this case to avoid corrupting this chain too!) + */ + for (ebone = chain->data; (ebone) && (ebone != end); ebone = ebone->parent) { + short found = 0; + + /* check if this bone is parented to one in the merging chain + * ! WATCHIT: must only go check until end of checking chain + */ + for (ebo = end; (ebo) && (ebo != start->parent); ebo = ebo->parent) { + /* side-chain found? --> remap parent to new bone, then we're done with this chain :) */ + if (ebone->parent == ebo) { + ebone->parent = newbone; + found = 1; + break; + } + } + + /* carry on to the next tip now */ + if (found) + break; + } + } + + /* step 2b: parent child of end to newbone (child from this chain) */ + if (endchild) + endchild->parent = newbone; + + /* step 3: delete all bones between and including start and end */ + for (ebo = end; ebo; ebo = ebone) { + ebone = (ebo == start) ? (NULL) : (ebo->parent); + bone_free(arm, ebo); + } + + newbone->flag |= (BONE_ROOTSEL | BONE_TIPSEL | BONE_SELECTED); + ED_armature_sync_selection(arm->edbo); +} + + +static int armature_merge_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = (obedit) ? obedit->data : NULL; + short type = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, obedit, arm)) + return OPERATOR_CANCELLED; + + /* for now, there's only really one type of merging that's performed... */ + if (type == 1) { + /* go down chains, merging bones */ + ListBase chains = {NULL, NULL}; + LinkData *chain, *nchain; + EditBone *ebo; + + armature_tag_select_mirrored(arm); + + /* get chains (ends on chains) */ + chains_find_tips(arm->edbo, &chains); + if (chains.first == NULL) return OPERATOR_CANCELLED; + + /* each 'chain' is the last bone in the chain (with no children) */ + for (chain = chains.first; chain; chain = nchain) { + EditBone *bstart = NULL, *bend = NULL; + EditBone *bchild = NULL, *child = NULL; + + /* temporarily remove chain from list of chains */ + nchain = chain->next; + BLI_remlink(&chains, chain); + + /* only consider bones that are visible and selected */ + for (ebo = chain->data; ebo; child = ebo, ebo = ebo->parent) { + /* check if visible + selected */ + if (EBONE_VISIBLE(arm, ebo) && + ((ebo->flag & BONE_CONNECTED) || (ebo->parent == NULL)) && + (ebo->flag & BONE_SELECTED) ) + { + /* set either end or start (end gets priority, unless it is already set) */ + if (bend == NULL) { + bend = ebo; + bchild = child; + } + else + bstart = ebo; + } + else { + /* chain is broken... merge any continous segments then clear */ + if (bstart && bend) + bones_merge(obedit, bstart, bend, bchild, &chains); + + bstart = NULL; + bend = NULL; + bchild = NULL; + } + } + + /* merge from bstart to bend if something not merged */ + if (bstart && bend) + bones_merge(obedit, bstart, bend, bchild, &chains); + + /* put back link */ + BLI_insertlinkbefore(&chains, nchain, chain); + } + + armature_tag_unselect(arm); + + BLI_freelistN(&chains); + } + + /* updates */ + ED_armature_sync_selection(arm->edbo); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_merge(wmOperatorType *ot) +{ + static EnumPropertyItem merge_types[] = { + {1, "WITHIN_CHAIN", 0, "Within Chains", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Merge Bones"; + ot->idname = "ARMATURE_OT_merge"; + ot->description = "Merge continuous chains of selected bones"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_merge_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", merge_types, 0, "Type", ""); +} + +/* --------------------- */ + +/* Switch Direction operator: + * Currently, this does not use context loops, as context loops do not make it + * easy to retrieve any hierarchical/chain relationships which are necessary for + * this to be done easily. + */ + +/* helper to clear BONE_TRANSFORM flags */ +static void armature_clear_swap_done_flags(bArmature *arm) +{ + EditBone *ebone; + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->flag &= ~BONE_TRANSFORM; + } +} + +static int armature_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_edit_object(C); + bArmature *arm = (bArmature *)ob->data; + ListBase chains = {NULL, NULL}; + LinkData *chain; + + /* get chains of bones (ends on chains) */ + chains_find_tips(arm->edbo, &chains); + if (chains.first == NULL) return OPERATOR_CANCELLED; + + /* ensure that mirror bones will also be operated on */ + armature_tag_select_mirrored(arm); + + /* clear BONE_TRANSFORM flags + * - used to prevent duplicate/cancelling operations from occurring [#34123] + * - BONE_DONE cannot be used here as that's already used for mirroring + */ + armature_clear_swap_done_flags(arm); + + /* loop over chains, only considering selected and visible bones */ + for (chain = chains.first; chain; chain = chain->next) { + EditBone *ebo, *child = NULL, *parent = NULL; + + /* loop over bones in chain */ + for (ebo = chain->data; ebo; ebo = parent) { + /* parent is this bone's original parent + * - we store this, as the next bone that is checked is this one + * but the value of ebo->parent may change here... + */ + parent = ebo->parent; + + /* skip bone if already handled... [#34123] */ + if ((ebo->flag & BONE_TRANSFORM) == 0) { + /* only if selected and editable */ + if (EBONE_VISIBLE(arm, ebo) && EBONE_EDITABLE(ebo)) { + /* swap head and tail coordinates */ + SWAP(float, ebo->head[0], ebo->tail[0]); + SWAP(float, ebo->head[1], ebo->tail[1]); + SWAP(float, ebo->head[2], ebo->tail[2]); + + /* do parent swapping: + * - use 'child' as new parent + * - connected flag is only set if points are coincidental + */ + ebo->parent = child; + if ((child) && equals_v3v3(ebo->head, child->tail)) + ebo->flag |= BONE_CONNECTED; + else + ebo->flag &= ~BONE_CONNECTED; + + /* get next bones + * - child will become the new parent of next bone + */ + child = ebo; + } + else { + /* not swapping this bone, however, if its 'parent' got swapped, unparent us from it + * as it will be facing in opposite direction + */ + if ((parent) && (EBONE_VISIBLE(arm, parent) && EBONE_EDITABLE(parent))) { + ebo->parent = NULL; + ebo->flag &= ~BONE_CONNECTED; + } + + /* get next bones + * - child will become new parent of next bone (not swapping occurred, + * so set to NULL to prevent infinite-loop) + */ + child = NULL; + } + + /* tag as done (to prevent double-swaps) */ + ebo->flag |= BONE_TRANSFORM; + } + } + } + + /* free chains */ + BLI_freelistN(&chains); + + /* clear temp flags */ + armature_clear_swap_done_flags(arm); + armature_tag_unselect(arm); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_switch_direction(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Switch Direction"; + ot->idname = "ARMATURE_OT_switch_direction"; + ot->description = "Change the direction that a chain of bones points in (head <-> tail swap)"; + + /* api callbacks */ + ot->exec = armature_switch_direction_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************************* Align ******************************* */ + +/* helper to fix a ebone position if its parent has moved due to alignment*/ +static void fix_connected_bone(EditBone *ebone) +{ + float diff[3]; + + if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) || equals_v3v3(ebone->parent->tail, ebone->head)) + return; + + /* if the parent has moved we translate child's head and tail accordingly */ + sub_v3_v3v3(diff, ebone->parent->tail, ebone->head); + add_v3_v3(ebone->head, diff); + add_v3_v3(ebone->tail, diff); +} + +/* helper to recursively find chains of connected bones starting at ebone and fix their position */ +static void fix_editbone_connected_children(ListBase *edbo, EditBone *ebone) +{ + EditBone *selbone; + + for (selbone = edbo->first; selbone; selbone = selbone->next) { + if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) { + fix_connected_bone(selbone); + fix_editbone_connected_children(edbo, selbone); + } + } +} + +static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone) +{ + float selboneaxis[3], actboneaxis[3], length; + + sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head); + normalize_v3(actboneaxis); + + sub_v3_v3v3(selboneaxis, selbone->tail, selbone->head); + length = len_v3(selboneaxis); + + mul_v3_fl(actboneaxis, length); + add_v3_v3v3(selbone->tail, selbone->head, actboneaxis); + selbone->roll = actbone->roll; + + /* if the bone being aligned has connected descendants they must be moved + * according to their parent new position, otherwise they would be left + * in an inconsistent state: connected but away from the parent*/ + fix_editbone_connected_children(edbo, selbone); +} + +static int armature_align_bones_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_edit_object(C); + bArmature *arm = (bArmature *)ob->data; + EditBone *actbone = CTX_data_active_bone(C); + EditBone *actmirb = NULL; + + /* there must be an active bone */ + if (actbone == NULL) { + BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); + return OPERATOR_CANCELLED; + } + else if (arm->flag & ARM_MIRROR_EDIT) { + /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone + * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone + * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R"). + * This is useful for arm-chains, for example parenting lower arm to upper arm + * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent") + * then just use actbone. Useful when doing upper arm to spine. + */ + actmirb = ED_armature_bone_get_mirrored(arm->edbo, actbone); + if (actmirb == NULL) + actmirb = actbone; + } + + /* if there is only 1 selected bone, we assume that that is the active bone, + * since a user will need to have clicked on a bone (thus selecting it) to make it active + */ + if (CTX_DATA_COUNT(C, selected_editable_bones) <= 1) { + /* When only the active bone is selected, and it has a parent, + * align it to the parent, as that is the only possible outcome. + */ + if (actbone->parent) { + bone_align_to_bone(arm->edbo, actbone, actbone->parent); + + if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) + bone_align_to_bone(arm->edbo, actmirb, actmirb->parent); + } + } + else { + /* Align 'selected' bones to the active one + * - the context iterator contains both selected bones and their mirrored copies, + * so we assume that unselected bones are mirrored copies of some selected bone + * - since the active one (and/or its mirror) will also be selected, we also need + * to check that we are not trying to operate on them, since such an operation + * would cause errors + */ + + /* align selected bones to the active one */ + CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) + { + if (ELEM(ebone, actbone, actmirb) == 0) { + if (ebone->flag & BONE_SELECTED) + bone_align_to_bone(arm->edbo, ebone, actbone); + else + bone_align_to_bone(arm->edbo, ebone, actmirb); + } + } + CTX_DATA_END; + } + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_align(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Align Bones"; + ot->idname = "ARMATURE_OT_align"; + ot->description = "Align selected bones to the active bone (or to their parent)"; + + /* api callbacks */ + ot->invoke = WM_operator_confirm; + ot->exec = armature_align_bones_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************************* Delete ******************************* */ + +/* previously delete_armature */ +/* only editmode! */ +static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bArmature *arm; + EditBone *curBone, *ebone_next; + bConstraint *con; + Object *obedit = CTX_data_edit_object(C); // XXX get from context + arm = obedit->data; + + /* cancel if nothing selected */ + if (CTX_DATA_COUNT(C, selected_bones) == 0) + return OPERATOR_CANCELLED; + + armature_select_mirrored(arm); + + /* First erase any associated pose channel */ + if (obedit->pose) { + bPoseChannel *pchan, *pchan_next; + for (pchan = obedit->pose->chanbase.first; pchan; pchan = pchan_next) { + pchan_next = pchan->next; + curBone = editbone_name_exists(arm->edbo, pchan->name); + + if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) { + BKE_pose_channel_free(pchan); + BKE_pose_channels_hash_free(obedit->pose); + BLI_freelinkN(&obedit->pose->chanbase, pchan); + } + else { + for (con = pchan->constraints.first; con; con = con->next) { + bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + if (ct->tar == obedit) { + if (ct->subtarget[0]) { + curBone = editbone_name_exists(arm->edbo, ct->subtarget); + if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) { + con->flag |= CONSTRAINT_DISABLE; + ct->subtarget[0] = 0; + } + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } + } + } + } + } + + + for (curBone = arm->edbo->first; curBone; curBone = ebone_next) { + ebone_next = curBone->next; + if (arm->layer & curBone->layer) { + if (curBone->flag & BONE_SELECTED) { + if (curBone == arm->act_edbone) arm->act_edbone = NULL; + ED_armature_edit_bone_remove(arm, curBone); + } + } + } + + + ED_armature_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Selected Bone(s)"; + ot->idname = "ARMATURE_OT_delete"; + ot->description = "Remove selected bones from the armature"; + + /* api callbacks */ + ot->invoke = WM_operator_confirm; + ot->exec = armature_delete_selected_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************************* Show/Hide ******************************* */ + +static int armature_hide_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = obedit->data; + EditBone *ebone; + const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0; + + /* cancel if nothing selected */ + if (CTX_DATA_COUNT(C, selected_bones) == 0) + return OPERATOR_CANCELLED; + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_VISIBLE(arm, ebone)) { + if ((ebone->flag & BONE_SELECTED) != invert) { + ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + ebone->flag |= BONE_HIDDEN_A; + } + } + } + ED_armature_validate_active(arm); + ED_armature_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Selected Bones"; + ot->idname = "ARMATURE_OT_hide"; + ot->description = "Tag selected bones to not be visible in Edit Mode"; + + /* api callbacks */ + ot->exec = armature_hide_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected"); +} + +static int armature_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = obedit->data; + EditBone *ebone; + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (arm->layer & ebone->layer) { + if (ebone->flag & BONE_HIDDEN_A) { + ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + ebone->flag &= ~BONE_HIDDEN_A; + } + } + } + ED_armature_validate_active(arm); + ED_armature_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Reveal Bones"; + ot->idname = "ARMATURE_OT_reveal"; + ot->description = "Unhide all bones that have been tagged to be hidden in Edit Mode"; + + /* api callbacks */ + ot->exec = armature_reveal_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + +} diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index ddf66a6169b..bfebc68ea46 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -37,17 +37,19 @@ struct wmOperatorType; struct bContext; struct Scene; struct Object; +struct Base; struct bAction; struct bPoseChannel; struct bArmature; struct EditBone; +struct Bone; struct ListBase; struct LinkData; /* ******************************************************* */ -/* editarmature.c operators */ +/* Armature EditMode Operators */ void ARMATURE_OT_bone_primitive_add(struct wmOperatorType *ot); void ARMATURE_OT_align(struct wmOperatorType *ot); @@ -131,7 +133,7 @@ void POSE_OT_armature_layers(struct wmOperatorType *ot); void POSE_OT_bone_layers(struct wmOperatorType *ot); /* ******************************************************* */ -/* Etch-A-Ton */ +/* Etch-A-Ton (Skeleton Sketching) Operators */ void SKETCH_OT_gesture(struct wmOperatorType *ot); void SKETCH_OT_delete(struct wmOperatorType *ot); @@ -144,7 +146,7 @@ void SKETCH_OT_select(struct wmOperatorType *ot); /* ******************************************************* */ /* Pose Tool Utilities (for PoseLib, Pose Sliding, etc.) */ -/* poseUtils.c */ +/* pose_utils.c */ /* Temporary data linking PoseChannels with the F-Curves they affect */ typedef struct tPChanFCurveLink { @@ -178,7 +180,7 @@ LinkData *poseAnim_mapping_getNextFCurve(ListBase *fcuLinks, LinkData *prev, con /* ******************************************************* */ /* PoseLib */ -/* poselib.c */ +/* pose_lib.c */ void POSELIB_OT_new(struct wmOperatorType *ot); void POSELIB_OT_unlink(struct wmOperatorType *ot); @@ -194,7 +196,7 @@ void POSELIB_OT_apply_pose(struct wmOperatorType *ot); /* ******************************************************* */ /* Pose Sliding Tools */ -/* poseSlide.c */ +/* pose_slide.c */ void POSE_OT_push(struct wmOperatorType *ot); void POSE_OT_relax(struct wmOperatorType *ot); @@ -203,7 +205,11 @@ void POSE_OT_breakdown(struct wmOperatorType *ot); void POSE_OT_propagate(struct wmOperatorType *ot); /* ******************************************************* */ -/* editarmature.c */ +/* Various Armature Edit/Pose Editing API's */ + +/* Ideally, many of these defines would not be needed as everything would be strictly self-contained + * within each file, but some tools still have a bit of overlap which makes things messy -- Feb 2013 + */ EditBone *make_boneList(struct ListBase *edbo, struct ListBase *bones, struct EditBone *parent, struct Bone *actBone); void BIF_sk_selectStroke(struct bContext *C, const int mval[2], short extend); @@ -213,13 +219,29 @@ void preEditBoneDuplicate(struct ListBase *editbones); struct EditBone *duplicateEditBone(struct EditBone *curBone, const char *name, struct ListBase *editbones, struct Object *ob); void updateDuplicateSubtarget(struct EditBone *dupBone, struct ListBase *editbones, struct Object *ob); -/* duplicate method (cross objects */ - +/* duplicate method (cross objects) */ /* editbones is the target list */ struct EditBone *duplicateEditBoneObjects(struct EditBone *curBone, const char *name, struct ListBase *editbones, struct Object *src_ob, struct Object *dst_ob); /* editbones is the source list */ void updateDuplicateSubtargetObjects(struct EditBone *dupBone, struct ListBase *editbones, struct Object *src_ob, struct Object *dst_ob); + +EditBone *editbone_name_exists(struct ListBase *edbo, const char *name); + +EditBone *add_points_bone(struct Object *obedit, float head[3], float tail[3]); +void bone_free(struct bArmature *arm, struct EditBone *bone); + +void armature_tag_select_mirrored(struct bArmature *arm); +void armature_select_mirrored(struct bArmature *arm); +void armature_tag_unselect(struct bArmature *arm); + +void *get_nearest_bone(struct bContext *C, short findunsel, int x, int y); +void *get_bone_from_selectbuffer(struct Scene *scene, struct Base *base, unsigned int *buffer, short hits, short findunsel); + +int bone_looper(struct Object *ob, struct Bone *bone, void *data, + int (*bone_func)(struct Object *, struct Bone *, void *)); + + #endif /* __ARMATURE_INTERN_H__ */ diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c new file mode 100644 index 00000000000..6417a795712 --- /dev/null +++ b/source/blender/editors/armature/armature_naming.c @@ -0,0 +1,368 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operators and API's for renaming bones both in and out of Edit Mode + */ + +/** \file blender/editors/armature/armature_naming.c + * \ingroup edarmature + */ + +#include <string.h> + +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" + +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_constraint.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_modifier.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_screen.h" + +#include "armature_intern.h" + +/* This file contains functions/API's for renaming bones and/or working with them */ + +/* ************************************************** */ +/* EditBone Names */ + +/* checks if an EditBone with a matching name already, returning the matching bone if it exists */ +EditBone *editbone_name_exists(ListBase *edbo, const char *name) +{ + return BLI_findstring(edbo, name, offsetof(EditBone, name)); +} + +/* note: there's a unique_bone_name() too! */ +static int editbone_unique_check(void *arg, const char *name) +{ + struct {ListBase *lb; void *bone; } *data = arg; + EditBone *dupli = editbone_name_exists(data->lb, name); + return dupli && dupli != data->bone; +} + +void unique_editbone_name(ListBase *edbo, char *name, EditBone *bone) +{ + struct {ListBase *lb; void *bone; } data; + data.lb = edbo; + data.bone = bone; + + BLI_uniquename_cb(editbone_unique_check, &data, "Bone", '.', name, sizeof(bone->name)); +} + +/* ************************************************** */ +/* Bone Renaming - API */ + +static int bone_unique_check(void *arg, const char *name) +{ + return BKE_armature_find_bone_name((bArmature *)arg, name) != NULL; +} + +static void unique_bone_name(bArmature *arm, char *name) +{ + BLI_uniquename_cb(bone_unique_check, (void *)arm, "Bone", '.', name, sizeof(((Bone *)NULL)->name)); +} + +/* helper call for armature_bone_rename */ +static void constraint_bone_name_fix(Object *ob, ListBase *conlist, char *oldname, char *newname) +{ + bConstraint *curcon; + bConstraintTarget *ct; + + for (curcon = conlist->first; curcon; curcon = curcon->next) { + bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(curcon); + ListBase targets = {NULL, NULL}; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(curcon, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + if (ct->tar == ob) { + if (!strcmp(ct->subtarget, oldname) ) + BLI_strncpy(ct->subtarget, newname, MAXBONENAME); + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(curcon, &targets, 0); + } + } +} + +/* called by UI for renaming a bone */ +/* warning: make sure the original bone was not renamed yet! */ +/* seems messy, but thats what you get with not using pointers but channel names :) */ +void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *newnamep) +{ + Object *ob; + char newname[MAXBONENAME]; + char oldname[MAXBONENAME]; + + /* names better differ! */ + if (strncmp(oldnamep, newnamep, MAXBONENAME)) { + + /* we alter newname string... so make copy */ + BLI_strncpy(newname, newnamep, MAXBONENAME); + /* we use oldname for search... so make copy */ + BLI_strncpy(oldname, oldnamep, MAXBONENAME); + + /* now check if we're in editmode, we need to find the unique name */ + if (arm->edbo) { + EditBone *eBone = editbone_name_exists(arm->edbo, oldname); + + if (eBone) { + unique_editbone_name(arm->edbo, newname, NULL); + BLI_strncpy(eBone->name, newname, MAXBONENAME); + } + else return; + } + else { + Bone *bone = BKE_armature_find_bone_name(arm, oldname); + + if (bone) { + unique_bone_name(arm, newname); + BLI_strncpy(bone->name, newname, MAXBONENAME); + } + else return; + } + + /* do entire dbase - objects */ + for (ob = G.main->object.first; ob; ob = ob->id.next) { + ModifierData *md; + + /* we have the object using the armature */ + if (arm == ob->data) { + Object *cob; + + /* Rename the pose channel, if it exists */ + if (ob->pose) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, oldname); + if (pchan) { + BLI_strncpy(pchan->name, newname, MAXBONENAME); + + if (ob->pose->chanhash) { + GHash *gh = ob->pose->chanhash; + + /* remove the old hash entry, and replace with the new name */ + BLI_ghash_remove(gh, oldname, NULL, NULL); + BLI_ghash_insert(gh, pchan->name, pchan); + } + } + } + + /* Update any object constraints to use the new bone name */ + for (cob = G.main->object.first; cob; cob = cob->id.next) { + if (cob->constraints.first) + constraint_bone_name_fix(ob, &cob->constraints, oldname, newname); + if (cob->pose) { + bPoseChannel *pchan; + for (pchan = cob->pose->chanbase.first; pchan; pchan = pchan->next) { + constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname); + } + } + } + } + + /* See if an object is parented to this armature */ + if (ob->parent && (ob->parent->data == arm)) { + if (ob->partype == PARBONE) { + /* bone name in object */ + if (!strcmp(ob->parsubstr, oldname)) + BLI_strncpy(ob->parsubstr, newname, MAXBONENAME); + } + } + + if (modifiers_usesArmature(ob, arm)) { + bDeformGroup *dg = defgroup_find_name(ob, oldname); + if (dg) { + BLI_strncpy(dg->name, newname, MAXBONENAME); + } + } + + /* fix modifiers that might be using this name */ + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Hook) { + HookModifierData *hmd = (HookModifierData *)md; + + /* uses armature, so may use the affected bone name */ + if (hmd->object && (hmd->object->data == arm)) { + if (!strcmp(hmd->subtarget, oldname)) + BLI_strncpy(hmd->subtarget, newname, MAXBONENAME); + } + } + } + } + + /* Fix all animdata that may refer to this bone - we can't just do the ones attached to objects, since + * other ID-blocks may have drivers referring to this bone [#29822] + */ + { + + BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname); + } + + /* correct view locking */ + { + bScreen *screen; + for (screen = G.main->screen.first; screen; screen = screen->id.next) { + ScrArea *sa; + /* add regions */ + for (sa = screen->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + if (v3d->ob_centre && v3d->ob_centre->data == arm) { + if (!strcmp(v3d->ob_centre_bone, oldname)) { + BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME); + } + } + } + } + } + } + } + } +} + +/* ************************************************** */ +/* Bone Renaming - EditMode */ + +static int armature_flip_names_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_edit_object(C); + bArmature *arm; + char newname[MAXBONENAME]; + + /* paranoia checks */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + arm = ob->data; + + /* loop through selected bones, auto-naming them */ + CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) + { + flip_side_name(newname, ebone->name, TRUE); // 1 = do strip off number extensions + ED_armature_bone_rename(arm, ebone->name, newname); + } + CTX_DATA_END; + + /* since we renamed stuff... */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_flip_names(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Flip Names"; + ot->idname = "ARMATURE_OT_flip_names"; + ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones"; + + /* api callbacks */ + ot->exec = armature_flip_names_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +static int armature_autoside_names_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_edit_object(C); + bArmature *arm; + char newname[MAXBONENAME]; + short axis = RNA_enum_get(op->ptr, "type"); + + /* paranoia checks */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + arm = ob->data; + + /* loop through selected bones, auto-naming them */ + CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) + { + BLI_strncpy(newname, ebone->name, sizeof(newname)); + if (bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis])) + ED_armature_bone_rename(arm, ebone->name, newname); + } + CTX_DATA_END; + + /* since we renamed stuff... */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_autoside_names(wmOperatorType *ot) +{ + static EnumPropertyItem axis_items[] = { + {0, "XAXIS", 0, "X-Axis", "Left/Right"}, + {1, "YAXIS", 0, "Y-Axis", "Front/Back"}, + {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "AutoName by Axis"; + ot->idname = "ARMATURE_OT_autoside_names"; + ot->description = "Automatically renames the selected bones according to which side of the target axis they fall on"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_autoside_names_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* settings */ + ot->prop = RNA_def_enum(ot->srna, "type", axis_items, 0, "Axis", "Axis tag names with"); +} + diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index 7c5b75e56ae..f87d4bc4d47 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -28,15 +28,7 @@ * \ingroup edarmature */ - -#include <stdlib.h> -#include <math.h> - -#include "BLO_sys_types.h" - #include "BLI_math.h" -#include "BLI_blenlib.h" - #include "RNA_access.h" diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c new file mode 100644 index 00000000000..c64bb5c4683 --- /dev/null +++ b/source/blender/editors/armature/armature_relations.c @@ -0,0 +1,774 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operators for relations between bones and for transferring bones between armature objects + */ + +/** \file blender/editors/armature/armature_relations.c + * \ingroup edarmature + */ + +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BLF_translation.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_constraint.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_object.h" +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "armature_intern.h" + +/* *************************************** Join *************************************** */ +/* NOTE: no operator define here as this is exported to the Object-level operator */ + +/* Helper function for armature joining - link fixing */ +static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone) +{ + Object *ob; + bPose *pose; + bPoseChannel *pchant; + bConstraint *con; + + /* let's go through all objects in database */ + for (ob = G.main->object.first; ob; ob = ob->id.next) { + /* do some object-type specific things */ + if (ob->type == OB_ARMATURE) { + pose = ob->pose; + for (pchant = pose->chanbase.first; pchant; pchant = pchant->next) { + for (con = pchant->constraints.first; con; con = con->next) { + bConstraintTypeInfo *cti = BKE_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 == srcArm) { + if (ct->subtarget[0] == '\0') { + ct->tar = tarArm; + } + else if (strcmp(ct->subtarget, pchan->name) == 0) { + ct->tar = tarArm; + BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget)); + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } + + /* action constraint? */ + if (con->type == CONSTRAINT_TYPE_ACTION) { + bActionConstraint *data = con->data; // XXX old animation system + bAction *act; + bActionChannel *achan; + + if (data->act) { + act = data->act; + + for (achan = act->chanbase.first; achan; achan = achan->next) { + if (strcmp(achan->name, pchan->name) == 0) + BLI_strncpy(achan->name, curbone->name, sizeof(achan->name)); + } + } + } + + } + } + } + + /* fix object-level constraints */ + if (ob != srcArm) { + for (con = ob->constraints.first; con; con = con->next) { + bConstraintTypeInfo *cti = BKE_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 == srcArm) { + if (ct->subtarget[0] == '\0') { + ct->tar = tarArm; + } + else if (strcmp(ct->subtarget, pchan->name) == 0) { + ct->tar = tarArm; + BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget)); + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } + } + } + + /* See if an object is parented to this armature */ + if (ob->parent && (ob->parent == srcArm)) { + /* Is object parented to a bone of this src armature? */ + if (ob->partype == PARBONE) { + /* bone name in object */ + if (!strcmp(ob->parsubstr, pchan->name)) + BLI_strncpy(ob->parsubstr, curbone->name, sizeof(ob->parsubstr)); + } + + /* make tar armature be new parent */ + ob->parent = tarArm; + } + } +} + +/* join armature exec is exported for use in object->join objects operator... */ +int join_armature_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + bArmature *arm = (ob) ? ob->data : NULL; + bPose *pose, *opose; + bPoseChannel *pchan, *pchann; + EditBone *curbone; + float mat[4][4], oimat[4][4]; + + /* Ensure we're not in editmode and that the active object is an armature*/ + if (!ob || ob->type != OB_ARMATURE) + return OPERATOR_CANCELLED; + if (!arm || arm->edbo) + return OPERATOR_CANCELLED; + + /* Get editbones of active armature to add editbones to */ + ED_armature_to_edit(ob); + + /* get pose of active object and move it out of posemode */ + pose = ob->pose; + ob->mode &= ~OB_MODE_POSE; + + CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) + { + if ((base->object->type == OB_ARMATURE) && (base->object != ob)) { + bArmature *curarm = base->object->data; + + /* Make a list of editbones in current armature */ + ED_armature_to_edit(base->object); + + /* Get Pose of current armature */ + opose = base->object->pose; + base->object->mode &= ~OB_MODE_POSE; + //BASACT->flag &= ~OB_MODE_POSE; + + /* Find the difference matrix */ + invert_m4_m4(oimat, ob->obmat); + mult_m4_m4m4(mat, oimat, base->object->obmat); + + /* Copy bones and posechannels from the object to the edit armature */ + for (pchan = opose->chanbase.first; pchan; pchan = pchann) { + pchann = pchan->next; + curbone = editbone_name_exists(curarm->edbo, pchan->name); + + /* Get new name */ + unique_editbone_name(arm->edbo, curbone->name, NULL); + + /* Transform the bone */ + { + float premat[4][4]; + float postmat[4][4]; + float difmat[4][4]; + float imat[4][4]; + float temp[3][3]; + float delta[3]; + + /* Get the premat */ + sub_v3_v3v3(delta, curbone->tail, curbone->head); + vec_roll_to_mat3(delta, curbone->roll, temp); + + unit_m4(premat); /* Mat4MulMat34 only sets 3x3 part */ + mul_m4_m3m4(premat, temp, mat); + + mul_m4_v3(mat, curbone->head); + mul_m4_v3(mat, curbone->tail); + + /* Get the postmat */ + sub_v3_v3v3(delta, curbone->tail, curbone->head); + vec_roll_to_mat3(delta, curbone->roll, temp); + copy_m4_m3(postmat, temp); + + /* Find the roll */ + invert_m4_m4(imat, premat); + mult_m4_m4m4(difmat, imat, postmat); + + curbone->roll -= (float)atan2(difmat[2][0], difmat[2][2]); + } + + /* Fix Constraints and Other Links to this Bone and Armature */ + joined_armature_fix_links(ob, base->object, pchan, curbone); + + /* Rename pchan */ + BLI_strncpy(pchan->name, curbone->name, sizeof(pchan->name)); + + /* Jump Ship! */ + BLI_remlink(curarm->edbo, curbone); + BLI_addtail(arm->edbo, curbone); + + BLI_remlink(&opose->chanbase, pchan); + BLI_addtail(&pose->chanbase, pchan); + BKE_pose_channels_hash_free(opose); + BKE_pose_channels_hash_free(pose); + } + + ED_base_object_free_and_unlink(bmain, scene, base); + } + } + CTX_DATA_END; + + DAG_relations_tag_update(bmain); /* because we removed object(s) */ + + ED_armature_from_edit(ob); + ED_armature_edit_free(ob); + + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + return OPERATOR_FINISHED; +} + +/* *********************************** Separate *********************************************** */ + +/* Helper function for armature separating - link fixing */ +static void separated_armature_fix_links(Object *origArm, Object *newArm) +{ + Object *ob; + bPoseChannel *pchan; + bConstraint *con; + ListBase *opchans, *npchans; + + /* get reference to list of bones in original and new armatures */ + opchans = &origArm->pose->chanbase; + npchans = &newArm->pose->chanbase; + + /* let's go through all objects in database */ + for (ob = G.main->object.first; ob; ob = ob->id.next) { + /* do some object-type specific things */ + if (ob->type == OB_ARMATURE) { + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + for (con = pchan->constraints.first; con; con = con->next) { + bConstraintTypeInfo *cti = BKE_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) { + /* any targets which point to original armature are redirected to the new one only if: + * - the target isn't origArm/newArm itself + * - the target is one that can be found in newArm/origArm + */ + if (ct->subtarget[0] != 0) { + if (ct->tar == origArm) { + if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) { + ct->tar = newArm; + } + } + else if (ct->tar == newArm) { + if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) { + ct->tar = origArm; + } + } + } + } + + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(con, &targets, 0); + } + } + } + } + } + + /* fix object-level constraints */ + if (ob != origArm) { + for (con = ob->constraints.first; con; con = con->next) { + bConstraintTypeInfo *cti = BKE_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) { + /* any targets which point to original armature are redirected to the new one only if: + * - the target isn't origArm/newArm itself + * - the target is one that can be found in newArm/origArm + */ + if (ct->subtarget[0] != '\0') { + if (ct->tar == origArm) { + if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) { + ct->tar = newArm; + } + } + else if (ct->tar == newArm) { + if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) { + ct->tar = origArm; + } + } + } + } + + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(con, &targets, 0); + } + } + } + } + + /* See if an object is parented to this armature */ + if (ob->parent && (ob->parent == origArm)) { + /* Is object parented to a bone of this src armature? */ + if ((ob->partype == PARBONE) && (ob->parsubstr[0] != '\0')) { + if (BLI_findstring(npchans, ob->parsubstr, offsetof(bPoseChannel, name))) { + ob->parent = newArm; + } + } + } + } +} + +/* Helper function for armature separating - remove certain bones from the given armature + * sel: remove selected bones from the armature, otherwise the unselected bones are removed + * (ob is not in editmode) + */ +static void separate_armature_bones(Object *ob, short sel) +{ + bArmature *arm = (bArmature *)ob->data; + bPoseChannel *pchan, *pchann; + EditBone *curbone; + + /* make local set of editbones to manipulate here */ + ED_armature_to_edit(ob); + + /* go through pose-channels, checking if a bone should be removed */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchann) { + pchann = pchan->next; + curbone = editbone_name_exists(arm->edbo, pchan->name); + + /* check if bone needs to be removed */ + if ( (sel && (curbone->flag & BONE_SELECTED)) || + (!sel && !(curbone->flag & BONE_SELECTED)) ) + { + EditBone *ebo; + bPoseChannel *pchn; + + /* clear the bone->parent var of any bone that had this as its parent */ + for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { + if (ebo->parent == curbone) { + ebo->parent = NULL; + ebo->temp = NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */ + ebo->flag &= ~BONE_CONNECTED; + } + } + + /* clear the pchan->parent var of any pchan that had this as its parent */ + for (pchn = ob->pose->chanbase.first; pchn; pchn = pchn->next) { + if (pchn->parent == pchan) + pchn->parent = NULL; + } + + /* free any of the extra-data this pchan might have */ + BKE_pose_channel_free(pchan); + BKE_pose_channels_hash_free(ob->pose); + + /* get rid of unneeded bone */ + bone_free(arm, curbone); + BLI_freelinkN(&ob->pose->chanbase, pchan); + } + } + + /* exit editmode (recalculates pchans too) */ + ED_armature_from_edit(ob); + ED_armature_edit_free(ob); +} + +/* separate selected bones into their armature */ +static int separate_armature_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + Object *oldob, *newob; + Base *oldbase, *newbase; + + /* sanity checks */ + if (obedit == NULL) + return OPERATOR_CANCELLED; + + /* set wait cursor in case this takes a while */ + WM_cursor_wait(1); + + /* we are going to do this as follows (unlike every other instance of separate): + * 1. exit editmode +posemode for active armature/base. Take note of what this is. + * 2. duplicate base - BASACT is the new one now + * 3. for each of the two armatures, enter editmode -> remove appropriate bones -> exit editmode + recalc + * 4. fix constraint links + * 5. make original armature active and enter editmode + */ + + /* 1) only edit-base selected */ + /* TODO: use context iterators for this? */ + CTX_DATA_BEGIN(C, Base *, base, visible_bases) + { + if (base->object == obedit) base->flag |= 1; + else base->flag &= ~1; + } + CTX_DATA_END; + + /* 1) store starting settings and exit editmode */ + oldob = obedit; + oldbase = BASACT; + oldob->mode &= ~OB_MODE_POSE; + //oldbase->flag &= ~OB_POSEMODE; + + ED_armature_from_edit(obedit); + ED_armature_edit_free(obedit); + + /* 2) duplicate base */ + newbase = ED_object_add_duplicate(bmain, scene, oldbase, USER_DUP_ARM); /* only duplicate linked armature */ + DAG_relations_tag_update(bmain); + + newob = newbase->object; + newbase->flag &= ~SELECT; + + + /* 3) remove bones that shouldn't still be around on both armatures */ + separate_armature_bones(oldob, 1); + separate_armature_bones(newob, 0); + + + /* 4) fix links before depsgraph flushes */ // err... or after? + separated_armature_fix_links(oldob, newob); + + DAG_id_tag_update(&oldob->id, OB_RECALC_DATA); /* this is the original one */ + DAG_id_tag_update(&newob->id, OB_RECALC_DATA); /* this is the separated one */ + + + /* 5) restore original conditions */ + obedit = oldob; + + ED_armature_to_edit(obedit); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); + + /* recalc/redraw + cleanup */ + WM_cursor_wait(0); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_separate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Separate Bones"; + ot->idname = "ARMATURE_OT_separate"; + ot->description = "Isolate selected bones into a separate armature"; + + /* callbacks */ + ot->invoke = WM_operator_confirm; + ot->exec = separate_armature_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************************************** Parenting ************************************************* */ + +/* armature parenting options */ +#define ARM_PAR_CONNECT 1 +#define ARM_PAR_OFFSET 2 + + +/* check for null, before calling! */ +static void bone_connect_to_existing_parent(EditBone *bone) +{ + bone->flag |= BONE_CONNECTED; + copy_v3_v3(bone->head, bone->parent->tail); + bone->rad_head = bone->parent->rad_tail; +} + +static void bone_connect_to_new_parent(ListBase *edbo, EditBone *selbone, EditBone *actbone, short mode) +{ + EditBone *ebone; + float offset[3]; + + if ((selbone->parent) && (selbone->flag & BONE_CONNECTED)) + selbone->parent->flag &= ~(BONE_TIPSEL); + + /* make actbone the parent of selbone */ + selbone->parent = actbone; + + /* in actbone tree we cannot have a loop */ + for (ebone = actbone->parent; ebone; ebone = ebone->parent) { + if (ebone->parent == selbone) { + ebone->parent = NULL; + ebone->flag &= ~BONE_CONNECTED; + } + } + + if (mode == ARM_PAR_CONNECT) { + /* Connected: Child bones will be moved to the parent tip */ + selbone->flag |= BONE_CONNECTED; + sub_v3_v3v3(offset, actbone->tail, selbone->head); + + copy_v3_v3(selbone->head, actbone->tail); + selbone->rad_head = actbone->rad_tail; + + add_v3_v3(selbone->tail, offset); + + /* offset for all its children */ + for (ebone = edbo->first; ebone; ebone = ebone->next) { + EditBone *par; + + for (par = ebone->parent; par; par = par->parent) { + if (par == selbone) { + add_v3_v3(ebone->head, offset); + add_v3_v3(ebone->tail, offset); + break; + } + } + } + } + else { + /* Offset: Child bones will retain their distance from the parent tip */ + selbone->flag &= ~BONE_CONNECTED; + } +} + + +static EnumPropertyItem prop_editarm_make_parent_types[] = { + {ARM_PAR_CONNECT, "CONNECTED", 0, "Connected", ""}, + {ARM_PAR_OFFSET, "OFFSET", 0, "Keep Offset", ""}, + {0, NULL, 0, NULL, NULL} +}; + +static int armature_parent_set_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_edit_object(C); + bArmature *arm = (bArmature *)ob->data; + EditBone *actbone = CTX_data_active_bone(C); + EditBone *actmirb = NULL; + short val = RNA_enum_get(op->ptr, "type"); + + /* there must be an active bone */ + if (actbone == NULL) { + BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); + return OPERATOR_CANCELLED; + } + else if (arm->flag & ARM_MIRROR_EDIT) { + /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone + * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone + * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R"). + * This is useful for arm-chains, for example parenting lower arm to upper arm + * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent") + * then just use actbone. Useful when doing upper arm to spine. + */ + actmirb = ED_armature_bone_get_mirrored(arm->edbo, actbone); + if (actmirb == NULL) + actmirb = actbone; + } + + /* if there is only 1 selected bone, we assume that that is the active bone, + * since a user will need to have clicked on a bone (thus selecting it) to make it active + */ + if (CTX_DATA_COUNT(C, selected_editable_bones) <= 1) { + /* When only the active bone is selected, and it has a parent, + * connect it to the parent, as that is the only possible outcome. + */ + if (actbone->parent) { + bone_connect_to_existing_parent(actbone); + + if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) + bone_connect_to_existing_parent(actmirb); + } + } + else { + /* Parent 'selected' bones to the active one + * - the context iterator contains both selected bones and their mirrored copies, + * so we assume that unselected bones are mirrored copies of some selected bone + * - since the active one (and/or its mirror) will also be selected, we also need + * to check that we are not trying to operate on them, since such an operation + * would cause errors + */ + + /* parent selected bones to the active one */ + CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) + { + if (ELEM(ebone, actbone, actmirb) == 0) { + if (ebone->flag & BONE_SELECTED) + bone_connect_to_new_parent(arm->edbo, ebone, actbone, val); + else + bone_connect_to_new_parent(arm->edbo, ebone, actmirb, val); + } + } + CTX_DATA_END; + } + + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; +} + +static int armature_parent_set_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event)) +{ + EditBone *actbone = CTX_data_active_bone(C); + uiPopupMenu *pup = uiPupMenuBegin(C, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Make Parent"), ICON_NONE); + uiLayout *layout = uiPupMenuLayout(pup); + int allchildbones = 0; + + CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) + { + if (ebone != actbone) { + if (ebone->parent != actbone) allchildbones = 1; + } + } + CTX_DATA_END; + + uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_CONNECT); + + /* ob becomes parent, make the associated menus */ + if (allchildbones) + uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_OFFSET); + + uiPupMenuEnd(C, pup); + + return OPERATOR_CANCELLED; +} + +void ARMATURE_OT_parent_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Make Parent"; + ot->idname = "ARMATURE_OT_parent_set"; + ot->description = "Set the active bone as the parent of the selected bones"; + + /* api callbacks */ + ot->invoke = armature_parent_set_invoke; + ot->exec = armature_parent_set_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "type", prop_editarm_make_parent_types, 0, "ParentType", "Type of parenting"); +} + + + +static EnumPropertyItem prop_editarm_clear_parent_types[] = { + {1, "CLEAR", 0, "Clear Parent", ""}, + {2, "DISCONNECT", 0, "Disconnect Bone", ""}, + {0, NULL, 0, NULL, NULL} +}; + +static void editbone_clear_parent(EditBone *ebone, int mode) +{ + if (ebone->parent) { + /* for nice selection */ + ebone->parent->flag &= ~(BONE_TIPSEL); + } + + if (mode == 1) ebone->parent = NULL; + ebone->flag &= ~BONE_CONNECTED; +} + +static int armature_parent_clear_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_edit_object(C); + bArmature *arm = (bArmature *)ob->data; + int val = RNA_enum_get(op->ptr, "type"); + + CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) + { + editbone_clear_parent(ebone, val); + } + CTX_DATA_END; + + ED_armature_sync_selection(arm->edbo); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_parent_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Parent"; + ot->idname = "ARMATURE_OT_parent_clear"; + ot->description = "Remove the parent-child relationship between selected bones and their parents"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_parent_clear_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", prop_editarm_clear_parent_types, 0, "ClearType", "What way to clear parenting"); +} + diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c new file mode 100644 index 00000000000..b7a436cc209 --- /dev/null +++ b/source/blender/editors/armature/armature_select.c @@ -0,0 +1,940 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + * + * API's and Operators for selecting armature bones in EditMode + */ + +/** \file blender/editors/armature/armature_select.c + * \ingroup edarmature + */ + +#include "DNA_armature_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_report.h" + +#include "BIF_gl.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "armature_intern.h" + +/* **************** PoseMode & EditMode Selection Buffer Queries *************************** */ + +/* only for opengl selection indices */ +Bone *get_indexed_bone(Object *ob, int index) +{ + bPoseChannel *pchan; + if (ob->pose == NULL) return NULL; + index >>= 16; // bone selection codes use left 2 bytes + + pchan = BLI_findlink(&ob->pose->chanbase, index); + return pchan ? pchan->bone : NULL; +} + +/* See if there are any selected bones in this buffer */ +/* only bones from base are checked on */ +void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel) +{ + Object *obedit = scene->obedit; // XXX get from context + Bone *bone; + EditBone *ebone; + void *firstunSel = NULL, *firstSel = NULL, *data; + unsigned int hitresult; + short i, takeNext = 0, sel; + + for (i = 0; i < hits; i++) { + hitresult = buffer[3 + (i * 4)]; + + if (!(hitresult & BONESEL_NOSEL)) { // -1 + if (hitresult & BONESEL_ANY) { // to avoid including objects in selection + + hitresult &= ~(BONESEL_ANY); + /* Determine what the current bone is */ + if (obedit == NULL || base->object != obedit) { + /* no singular posemode, so check for correct object */ + if (base->selcol == (hitresult & 0xFFFF)) { + bone = get_indexed_bone(base->object, hitresult); + + if (findunsel) + sel = (bone->flag & BONE_SELECTED); + else + sel = !(bone->flag & BONE_SELECTED); + + data = bone; + } + else { + data = NULL; + sel = 0; + } + } + else { + bArmature *arm = obedit->data; + + ebone = BLI_findlink(arm->edbo, hitresult); + if (findunsel) + sel = (ebone->flag & BONE_SELECTED); + else + sel = !(ebone->flag & BONE_SELECTED); + + data = ebone; + } + + if (data) { + if (sel) { + if (!firstSel) firstSel = data; + takeNext = 1; + } + else { + if (!firstunSel) + firstunSel = data; + if (takeNext) + return data; + } + } + } + } + } + + if (firstunSel) + return firstunSel; + else + return firstSel; +} + +/* used by posemode as well editmode */ +/* only checks scene->basact! */ +/* x and y are mouse coords (area space) */ +void *get_nearest_bone(bContext *C, short findunsel, int x, int y) +{ + ViewContext vc; + rcti rect; + unsigned int buffer[MAXPICKBUF]; + short hits; + + view3d_set_viewcontext(C, &vc); + + // rect.xmin = ... mouseco! + rect.xmin = rect.xmax = x; + rect.ymin = rect.ymax = y; + + glInitNames(); + hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect); + + if (hits > 0) + return get_bone_from_selectbuffer(vc.scene, vc.scene->basact, buffer, hits, findunsel); + + return NULL; +} + +/* **************** EditMode stuff ********************** */ + +/* called in space.c */ +/* previously "selectconnected_armature" */ +static int armature_select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + bArmature *arm; + EditBone *bone, *curBone, *next; + int extend = RNA_boolean_get(op->ptr, "extend"); + Object *obedit = CTX_data_edit_object(C); + arm = obedit->data; + + view3d_operator_needs_opengl(C); + + if (extend) + bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]); + else + bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]); + + if (!bone) + return OPERATOR_CANCELLED; + + /* Select parents */ + for (curBone = bone; curBone; curBone = next) { + if ((curBone->flag & BONE_UNSELECTABLE) == 0) { + if (extend) { + curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + + if (curBone->flag & BONE_CONNECTED) + next = curBone->parent; + else + next = NULL; + } + + /* Select children */ + while (bone) { + for (curBone = arm->edbo->first; curBone; curBone = next) { + next = curBone->next; + if ((curBone->parent == bone) && (curBone->flag & BONE_UNSELECTABLE) == 0) { + if (curBone->flag & BONE_CONNECTED) { + if (extend) + curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + else + curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + bone = curBone; + break; + } + else { + bone = NULL; + break; + } + } + } + if (!curBone) + bone = NULL; + } + + ED_armature_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +static int armature_select_linked_poll(bContext *C) +{ + return (ED_operator_view3d_active(C) && ED_operator_editarmature(C) ); +} + +void ARMATURE_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Connected"; + ot->idname = "ARMATURE_OT_select_linked"; + ot->description = "Select bones related to selected ones by parent/child relationships"; + + /* api callbacks */ + ot->exec = NULL; + ot->invoke = armature_select_linked_invoke; + ot->poll = armature_select_linked_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first"); +} + +/* does bones and points */ +/* note that BONE ROOT only gets drawn for root bones (or without IK) */ +static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2], + ListBase *edbo, int findunsel, int *selmask) +{ + bArmature *arm = (bArmature *)vc->obedit->data; + EditBone *ebone_next_act = arm->act_edbone; + + EditBone *ebone; + rcti rect; + unsigned int buffer[MAXPICKBUF]; + unsigned int hitresult, besthitresult = BONESEL_NOSEL; + int i, mindep = 4; + short hits; + + glInitNames(); + + /* find the bone after the current active bone, so as to bump up its chances in selection. + * this way overlapping bones will cycle selection state as with objects. */ + if (ebone_next_act && + EBONE_VISIBLE(arm, ebone_next_act) && + ebone_next_act->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) + { + ebone_next_act = ebone_next_act->next ? ebone_next_act->next : arm->edbo->first; + } + else { + ebone_next_act = NULL; + } + + rect.xmin = mval[0] - 5; + rect.xmax = mval[0] + 5; + rect.ymin = mval[1] - 5; + rect.ymax = mval[1] + 5; + + hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect); + if (hits == 0) { + rect.xmin = mval[0] - 12; + rect.xmax = mval[0] + 12; + rect.ymin = mval[1] - 12; + rect.ymax = mval[1] + 12; + hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect); + } + /* See if there are any selected bones in this group */ + if (hits > 0) { + + if (hits == 1) { + if (!(buffer[3] & BONESEL_NOSEL)) + besthitresult = buffer[3]; + } + else { + for (i = 0; i < hits; i++) { + hitresult = buffer[3 + (i * 4)]; + if (!(hitresult & BONESEL_NOSEL)) { + int dep; + + ebone = BLI_findlink(edbo, hitresult & ~BONESEL_ANY); + + /* clicks on bone points get advantage */ + if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) { + /* but also the unselected one */ + if (findunsel) { + if ( (hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) + dep = 1; + else if ( (hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) + dep = 1; + else + dep = 2; + } + else dep = 2; + } + else { + /* bone found */ + if (findunsel) { + if ((ebone->flag & BONE_SELECTED) == 0) + dep = 2; + else + dep = 3; + } + else dep = 3; + } + + if (ebone == ebone_next_act) { + dep -= 1; + } + + if (dep < mindep) { + mindep = dep; + besthitresult = hitresult; + } + } + } + } + + if (!(besthitresult & BONESEL_NOSEL)) { + + ebone = BLI_findlink(edbo, besthitresult & ~BONESEL_ANY); + + *selmask = 0; + if (besthitresult & BONESEL_ROOT) + *selmask |= BONE_ROOTSEL; + if (besthitresult & BONESEL_TIP) + *selmask |= BONE_TIPSEL; + if (besthitresult & BONESEL_BONE) + *selmask |= BONE_SELECTED; + return ebone; + } + } + *selmask = 0; + return NULL; +} + + + +/* toggle==0: deselect + * toggle==1: swap (based on test) + * toggle==2: swap (no test), CURRENTLY UNUSED + */ +void ED_armature_deselect_all(Object *obedit, int toggle) +{ + bArmature *arm = obedit->data; + EditBone *eBone; + int sel = 1; + + if (toggle == 1) { + /* Determine if there are any selected bones + * and therefore whether we are selecting or deselecting */ + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + // if (arm->layer & eBone->layer) { + if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { + sel = 0; + break; + } + // } + } + } + else sel = toggle; + + /* Set the flags */ + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + if (sel == 2) { + /* invert selection of bone */ + if (EBONE_VISIBLE(arm, eBone)) { + eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if (arm->act_edbone == eBone) + arm->act_edbone = NULL; + } + } + else if (sel == 1) { + /* select bone */ + if (EBONE_VISIBLE(arm, eBone)) { + eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if (eBone->parent) + eBone->parent->flag |= (BONE_TIPSEL); + } + } + else { + /* deselect bone */ + eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if (arm->act_edbone == eBone) + arm->act_edbone = NULL; + } + } + + ED_armature_sync_selection(arm->edbo); +} + +void ED_armature_deselect_all_visible(Object *obedit) +{ + bArmature *arm = obedit->data; + EditBone *ebone; + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + /* first and foremost, bone must be visible and selected */ + if (EBONE_SELECTABLE(arm, ebone)) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + + ED_armature_sync_selection(arm->edbo); +} + +/* accounts for connected parents */ +static int ebone_select_flag(EditBone *ebone) +{ + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + return ((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0) | (ebone->flag & (BONE_SELECTED | BONE_TIPSEL)); + } + else { + return ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + } +} + +/* context: editmode armature in view3d */ +int mouse_armature(bContext *C, const int mval[2], int extend, int deselect, int toggle) +{ + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = obedit->data; + ViewContext vc; + EditBone *nearBone = NULL; + int selmask; + + view3d_set_viewcontext(C, &vc); + + BIF_sk_selectStroke(C, mval, extend); + + nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask); + if (nearBone) { + + if (!extend && !deselect && !toggle) + ED_armature_deselect_all(obedit, 0); + + /* by definition the non-root connected bones have no root point drawn, + * so a root selection needs to be delivered to the parent tip */ + + if (selmask & BONE_SELECTED) { + if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) { + /* click in a chain */ + if (extend) { + /* select this bone */ + nearBone->flag |= BONE_TIPSEL; + nearBone->parent->flag |= BONE_TIPSEL; + } + else if (deselect) { + /* deselect this bone */ + nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); + /* only deselect parent tip if it is not selected */ + if (!(nearBone->parent->flag & BONE_SELECTED)) + nearBone->parent->flag &= ~BONE_TIPSEL; + } + else if (toggle) { + /* hold shift inverts this bone's selection */ + if (nearBone->flag & BONE_SELECTED) { + /* deselect this bone */ + nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); + /* only deselect parent tip if it is not selected */ + if (!(nearBone->parent->flag & BONE_SELECTED)) + nearBone->parent->flag &= ~BONE_TIPSEL; + } + else { + /* select this bone */ + nearBone->flag |= BONE_TIPSEL; + nearBone->parent->flag |= BONE_TIPSEL; + } + } + else { + /* select this bone */ + nearBone->flag |= BONE_TIPSEL; + nearBone->parent->flag |= BONE_TIPSEL; + } + } + else { + if (extend) { + nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + } + else if (deselect) { + nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); + } + else if (toggle) { + /* hold shift inverts this bone's selection */ + if (nearBone->flag & BONE_SELECTED) + nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); + else + nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + } + else + nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); + } + } + else { + if (extend) + nearBone->flag |= selmask; + else if (deselect) + nearBone->flag &= ~selmask; + else if (toggle && (nearBone->flag & selmask)) + nearBone->flag &= ~selmask; + else + nearBone->flag |= selmask; + } + + ED_armature_sync_selection(arm->edbo); + + if (nearBone) { + /* then now check for active status */ + if (ebone_select_flag(nearBone)) { + arm->act_edbone = nearBone; + } + } + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); + return 1; + } + + return 0; +} + + +/* **************** Selections ******************/ + +static int armature_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) +{ + /* Set the flags */ + CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) + { + /* ignore bone if selection can't change */ + if ((ebone->flag & BONE_UNSELECTABLE) == 0) { + /* select bone */ + ebone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + CTX_DATA_END; + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_select_inverse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Inverse"; + ot->idname = "ARMATURE_OT_select_inverse"; + ot->description = "Flip the selection status of bones (selected -> unselected, unselected -> selected)"; + + /* api callbacks */ + ot->exec = armature_select_inverse_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + +} +static int armature_de_select_all_exec(bContext *C, wmOperator *op) +{ + int action = RNA_enum_get(op->ptr, "action"); + + if (action == SEL_TOGGLE) { + action = SEL_SELECT; + /* Determine if there are any selected bones + * And therefore whether we are selecting or deselecting */ + if (CTX_DATA_COUNT(C, selected_bones) > 0) + action = SEL_DESELECT; + } + + /* Set the flags */ + CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) + { + /* ignore bone if selection can't change */ + if ((ebone->flag & BONE_UNSELECTABLE) == 0) { + switch (action) { + case SEL_SELECT: + ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if (ebone->parent) + ebone->parent->flag |= (BONE_TIPSEL); + break; + case SEL_DESELECT: + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + break; + case SEL_INVERT: + if (ebone->flag & BONE_SELECTED) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + if (ebone->parent) + ebone->parent->flag |= (BONE_TIPSEL); + } + break; + } + } + } + CTX_DATA_END; + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "(De)select All"; + ot->idname = "ARMATURE_OT_select_all"; + ot->description = "Toggle selection status of all bones"; + + /* api callbacks */ + ot->exec = armature_de_select_all_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_select_all(ot); +} + +enum { + SIMEDBONE_LENGTH = 1, + SIMEDBONE_DIRECTION, + SIMEDBONE_PREFIX, + SIMEDBONE_SUFFIX, + SIMEDBONE_LAYER +}; + +static EnumPropertyItem prop_similar_types[] = { + {SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""}, + {SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y axis)", ""}, + {SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""}, + {SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""}, + {SIMEDBONE_LAYER, "LAYER", 0, "Layer", ""}, + {0, NULL, 0, NULL, NULL} +}; + +/* could be used in more places */ +static void ED_armature_edit_bone_select(EditBone *ebone) +{ + BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0); + ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + + if ((ebone->flag & BONE_CONNECTED) && (ebone->parent != NULL)) { + ebone->parent->flag |= BONE_TIPSEL; + } +} + +static void select_similar_length(bArmature *arm, EditBone *ebone_act, const float thresh) +{ + EditBone *ebone; + + /* thresh is always relative to current length */ + const float len_min = ebone_act->length / (1.0f + thresh); + const float len_max = ebone_act->length * (1.0f + thresh); + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + if ((ebone->length >= len_min) && + (ebone->length <= len_max)) + { + ED_armature_edit_bone_select(ebone); + } + } + } +} + +static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh) +{ + EditBone *ebone; + float dir_act[3]; + sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail); + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + float dir[3]; + sub_v3_v3v3(dir, ebone->head, ebone->tail); + + if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) { + ED_armature_edit_bone_select(ebone); + } + } + } +} + +static void select_similar_layer(bArmature *arm, EditBone *ebone_act) +{ + EditBone *ebone; + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + if (ebone->layer & ebone_act->layer) { + ED_armature_edit_bone_select(ebone); + } + } + } +} + +static void select_similar_prefix(bArmature *arm, EditBone *ebone_act) +{ + EditBone *ebone; + + char body_tmp[MAX_VGROUP_NAME]; + char prefix_act[MAX_VGROUP_NAME]; + + BKE_deform_split_prefix(ebone_act->name, prefix_act, body_tmp); + + if (prefix_act[0] == '\0') + return; + + /* Find matches */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + char prefix_other[MAX_VGROUP_NAME]; + BKE_deform_split_prefix(ebone->name, prefix_other, body_tmp); + if (!strcmp(prefix_act, prefix_other)) { + ED_armature_edit_bone_select(ebone); + } + } + } +} + +static void select_similar_suffix(bArmature *arm, EditBone *ebone_act) +{ + EditBone *ebone; + + char body_tmp[MAX_VGROUP_NAME]; + char suffix_act[MAX_VGROUP_NAME]; + + BKE_deform_split_suffix(ebone_act->name, body_tmp, suffix_act); + + if (suffix_act[0] == '\0') + return; + + /* Find matches */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (EBONE_SELECTABLE(arm, ebone)) { + char suffix_other[MAX_VGROUP_NAME]; + BKE_deform_split_suffix(ebone->name, body_tmp, suffix_other); + if (!strcmp(suffix_act, suffix_other)) { + ED_armature_edit_bone_select(ebone); + } + } + } +} + +static int armature_select_similar_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + bArmature *arm = obedit->data; + EditBone *ebone_act = CTX_data_active_bone(C); + + /* Get props */ + int type = RNA_enum_get(op->ptr, "type"); + float thresh = RNA_float_get(op->ptr, "threshold"); + + /* Check for active bone */ + if (ebone_act == NULL) { + BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); + return OPERATOR_CANCELLED; + } + + switch (type) { + case SIMEDBONE_LENGTH: + select_similar_length(arm, ebone_act, thresh); + break; + case SIMEDBONE_DIRECTION: + select_similar_direction(arm, ebone_act, thresh); + break; + case SIMEDBONE_PREFIX: + select_similar_prefix(arm, ebone_act); + break; + case SIMEDBONE_SUFFIX: + select_similar_suffix(arm, ebone_act); + break; + case SIMEDBONE_LAYER: + select_similar_layer(arm, ebone_act); + break; + } + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_select_similar(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Similar"; + ot->idname = "ARMATURE_OT_select_similar"; + + /* callback functions */ + ot->invoke = WM_menu_invoke; + ot->exec = armature_select_similar_exec; + ot->poll = ED_operator_editarmature; + ot->description = "Select similar bones by property types"; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", ""); + RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); +} + +/* ********************* select hierarchy operator ************** */ + +/* Get the first available child of an editbone */ +static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility) +{ + EditBone *curbone, *chbone = NULL; + + for (curbone = arm->edbo->first; curbone; curbone = curbone->next) { + if (curbone->parent == pabone) { + if (use_visibility) { + if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) { + chbone = curbone; + } + } + else + chbone = curbone; + } + } + + return chbone; +} + +static int armature_select_hierarchy_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + Object *ob; + bArmature *arm; + EditBone *curbone, *pabone, *chbone; + int direction = RNA_enum_get(op->ptr, "direction"); + int add_to_sel = RNA_boolean_get(op->ptr, "extend"); + + ob = obedit; + arm = (bArmature *)ob->data; + + for (curbone = arm->edbo->first; curbone; curbone = curbone->next) { + /* only work on bone if it is visible and its selection can change */ + if (EBONE_SELECTABLE(arm, curbone)) { + if (curbone == arm->act_edbone) { + if (direction == BONE_SELECT_PARENT) { + if (curbone->parent == NULL) continue; + else pabone = curbone->parent; + + if (EBONE_VISIBLE(arm, pabone)) { + pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_edbone = pabone; + if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL; + + if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + break; + } + + } + else { // BONE_SELECT_CHILD + chbone = editbone_get_child(arm, curbone, 1); + if (chbone == NULL) continue; + + if (EBONE_SELECTABLE(arm, chbone)) { + chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_edbone = chbone; + + if (!add_to_sel) { + curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL); + if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL; + } + break; + } + } + } + } + } + + ED_armature_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_select_hierarchy(wmOperatorType *ot) +{ + static EnumPropertyItem direction_items[] = { + {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""}, + {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Select Hierarchy"; + ot->idname = "ARMATURE_OT_select_hierarchy"; + ot->description = "Select immediate parent/children of selected bones"; + + /* api callbacks */ + ot->exec = armature_select_hierarchy_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_enum(ot->srna, "direction", direction_items, + BONE_SELECT_PARENT, "Direction", ""); + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); +} + diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c new file mode 100644 index 00000000000..2e43c388fa8 --- /dev/null +++ b/source/blender/editors/armature/armature_skinning.c @@ -0,0 +1,441 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + * + * API's for creating vertex groups from bones + * - Interfaces with heat weighting in meshlaplacian + */ + +/** \file blender/editors/armature/armature_skinning.c + * \ingroup edarmature + */ + +#include "DNA_mesh_types.h" +#include "DNA_armature_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_deform.h" +#include "BKE_report.h" +#include "BKE_subsurf.h" +#include "BKE_modifier.h" + +#include "ED_armature.h" +#include "ED_mesh.h" + + +#include "armature_intern.h" +#include "meshlaplacian.h" + +#if 0 +#include "reeb.h" +#endif + +/* ********************************** Bone Skinning *********************************************** */ + +static int bone_skinnable_cb(Object *ob, Bone *bone, void *datap) +{ + /* Bones that are deforming + * are regarded to be "skinnable" and are eligible for + * auto-skinning. + * + * This function performs 2 functions: + * + * a) It returns 1 if the bone is skinnable. + * If we loop over all bones with this + * function, we can count the number of + * skinnable bones. + * b) If the pointer data is non null, + * it is treated like a handle to a + * bone pointer -- the bone pointer + * is set to point at this bone, and + * the pointer the handle points to + * is incremented to point to the + * next member of an array of pointers + * to bones. This way we can loop using + * this function to construct an array of + * pointers to bones that point to all + * skinnable bones. + */ + Bone ***hbone; + int a, segments; + struct { Object *armob; void *list; int heat; } *data = datap; + + if (!(ob->mode & OB_MODE_WEIGHT_PAINT) || !(bone->flag & BONE_HIDDEN_P)) { + if (!(bone->flag & BONE_NO_DEFORM)) { + if (data->heat && data->armob->pose && BKE_pose_channel_find_name(data->armob->pose, bone->name)) + segments = bone->segments; + else + segments = 1; + + if (data->list != NULL) { + hbone = (Bone ***) &data->list; + + for (a = 0; a < segments; a++) { + **hbone = bone; + ++*hbone; + } + } + return segments; + } + } + return 0; +} + +static int vgroup_add_unique_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) +{ + /* This group creates a vertex group to ob that has the + * same name as bone (provided the bone is skinnable). + * If such a vertex group aleady exist the routine exits. + */ + if (!(bone->flag & BONE_NO_DEFORM)) { + if (!defgroup_find_name(ob, bone->name)) { + ED_vgroup_add_name(ob, bone->name); + return 1; + } + } + return 0; +} + +static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap) +{ + /* Bones that are deforming + * are regarded to be "skinnable" and are eligible for + * auto-skinning. + * + * This function performs 2 functions: + * + * a) If the bone is skinnable, it creates + * a vertex group for ob that has + * the name of the skinnable bone + * (if one doesn't exist already). + * b) If the pointer data is non null, + * it is treated like a handle to a + * bDeformGroup pointer -- the + * bDeformGroup pointer is set to point + * to the deform group with the bone's + * name, and the pointer the handle + * points to is incremented to point to the + * next member of an array of pointers + * to bDeformGroups. This way we can loop using + * this function to construct an array of + * pointers to bDeformGroups, all with names + * of skinnable bones. + */ + bDeformGroup ***hgroup, *defgroup = NULL; + int a, segments; + struct { Object *armob; void *list; int heat; } *data = datap; + int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT); + bArmature *arm = data->armob->data; + + if (!wpmode || !(bone->flag & BONE_HIDDEN_P)) { + if (!(bone->flag & BONE_NO_DEFORM)) { + if (data->heat && data->armob->pose && BKE_pose_channel_find_name(data->armob->pose, bone->name)) + segments = bone->segments; + else + segments = 1; + + if (!wpmode || ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED))) + if (!(defgroup = defgroup_find_name(ob, bone->name))) + defgroup = ED_vgroup_add_name(ob, bone->name); + + if (data->list != NULL) { + hgroup = (bDeformGroup ***) &data->list; + + for (a = 0; a < segments; a++) { + **hgroup = defgroup; + ++*hgroup; + } + } + return segments; + } + } + return 0; +} + +static void add_vgroups__mapFunc(void *userData, int index, const float co[3], + const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) +{ + /* DerivedMesh mapFunc for getting final coords in weight paint mode */ + + float (*verts)[3] = userData; + copy_v3_v3(verts[index], co); +} + +static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist, + bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, + float (*root)[3], float (*tip)[3], int *selected, float scale) +{ + /* Create vertex group weights from envelopes */ + + Bone *bone; + bDeformGroup *dgroup; + float distance; + int i, iflip, j; + + /* for each vertex in the mesh */ + for (i = 0; i < mesh->totvert; i++) { + iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, i) : 0; + + /* for each skinnable bone */ + for (j = 0; j < numbones; ++j) { + if (!selected[j]) + continue; + + bone = bonelist[j]; + dgroup = dgrouplist[j]; + + /* store the distance-factor from the vertex to the bone */ + distance = distfactor_to_bone(verts[i], root[j], tip[j], + bone->rad_head * scale, bone->rad_tail * scale, bone->dist * scale); + + /* add the vert to the deform group if (weight != 0.0) */ + if (distance != 0.0f) + ED_vgroup_vert_add(ob, dgroup, i, distance, WEIGHT_REPLACE); + else + ED_vgroup_vert_remove(ob, dgroup, i); + + /* do same for mirror */ + if (dgroupflip && dgroupflip[j] && iflip >= 0) { + if (distance != 0.0f) + ED_vgroup_vert_add(ob, dgroupflip[j], iflip, distance, + WEIGHT_REPLACE); + else + ED_vgroup_vert_remove(ob, dgroupflip[j], iflip); + } + } + } +} + +static void add_verts_to_dgroups(ReportList *reports, Scene *scene, Object *ob, Object *par, int heat, int mirror) +{ + /* This functions implements the automatic computation of vertex group + * weights, either through envelopes or using a heat equilibrium. + * + * This function can be called both when parenting a mesh to an armature, + * or in weightpaint + posemode. In the latter case selection is taken + * into account and vertex weights can be mirrored. + * + * The mesh vertex positions used are either the final deformed coords + * from the derivedmesh in weightpaint mode, the final subsurf coords + * when parenting, or simply the original mesh coords. + */ + + bArmature *arm = par->data; + Bone **bonelist, *bone; + bDeformGroup **dgrouplist, **dgroupflip; + bDeformGroup *dgroup; + bPoseChannel *pchan; + Mesh *mesh; + Mat4 *bbone = NULL; + float (*root)[3], (*tip)[3], (*verts)[3]; + int *selected; + int numbones, vertsfilled = 0, i, j, segments = 0; + int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT); + struct { Object *armob; void *list; int heat; } looper_data; + + looper_data.armob = par; + looper_data.heat = heat; + looper_data.list = NULL; + + /* count the number of skinnable bones */ + numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb); + + if (numbones == 0) + return; + + if (ED_vgroup_data_create(ob->data) == FALSE) + return; + + /* create an array of pointer to bones that are skinnable + * and fill it with all of the skinnable bones */ + bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist"); + looper_data.list = bonelist; + bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb); + + /* create an array of pointers to the deform groups that + * correspond to the skinnable bones (creating them + * as necessary. */ + dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist"); + dgroupflip = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgroupflip"); + + looper_data.list = dgrouplist; + bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb); + + /* create an array of root and tip positions transformed into + * global coords */ + root = MEM_callocN(numbones * sizeof(float) * 3, "root"); + tip = MEM_callocN(numbones * sizeof(float) * 3, "tip"); + selected = MEM_callocN(numbones * sizeof(int), "selected"); + + for (j = 0; j < numbones; ++j) { + bone = bonelist[j]; + dgroup = dgrouplist[j]; + + /* handle bbone */ + if (heat) { + if (segments == 0) { + segments = 1; + bbone = NULL; + + if ((par->pose) && (pchan = BKE_pose_channel_find_name(par->pose, bone->name))) { + if (bone->segments > 1) { + segments = bone->segments; + bbone = b_bone_spline_setup(pchan, 1); + } + } + } + + segments--; + } + + /* compute root and tip */ + if (bbone) { + mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]); + if ((segments + 1) < bone->segments) { + mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]); + } + else { + copy_v3_v3(tip[j], bone->arm_tail); + } + } + else { + copy_v3_v3(root[j], bone->arm_head); + copy_v3_v3(tip[j], bone->arm_tail); + } + + mul_m4_v3(par->obmat, root[j]); + mul_m4_v3(par->obmat, tip[j]); + + /* set selected */ + if (wpmode) { + if ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED)) + selected[j] = 1; + } + else + selected[j] = 1; + + /* find flipped group */ + if (dgroup && mirror) { + char name[MAXBONENAME]; + + // 0 = don't strip off number extensions + flip_side_name(name, dgroup->name, FALSE); + dgroupflip[j] = defgroup_find_name(ob, name); + } + } + + /* create verts */ + mesh = (Mesh *)ob->data; + verts = MEM_callocN(mesh->totvert * sizeof(*verts), "closestboneverts"); + + if (wpmode) { + /* if in weight paint mode, use final verts from derivedmesh */ + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + + if (dm->foreachMappedVert) { + dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void *)verts); + vertsfilled = 1; + } + + dm->release(dm); + } + else if (modifiers_findByType(ob, eModifierType_Subsurf)) { + /* is subsurf on? Lets use the verts on the limit surface then. + * = same amount of vertices as mesh, but vertices moved to the + * subsurfed position, like for 'optimal'. */ + subsurf_calculate_limit_positions(mesh, verts); + vertsfilled = 1; + } + + /* transform verts to global space */ + for (i = 0; i < mesh->totvert; i++) { + if (!vertsfilled) + copy_v3_v3(verts[i], mesh->mvert[i].co); + mul_m4_v3(ob->obmat, verts[i]); + } + + /* compute the weights based on gathered vertices and bones */ + if (heat) { + const char *error = NULL; + heat_bone_weighting(ob, mesh, verts, numbones, dgrouplist, dgroupflip, + root, tip, selected, &error); + + if (error) { + BKE_report(reports, RPT_WARNING, error); + } + } + else { + envelope_bone_weighting(ob, mesh, verts, numbones, bonelist, dgrouplist, + dgroupflip, root, tip, selected, mat4_to_scale(par->obmat)); + } + + /* only generated in some cases but can call anyway */ + mesh_octree_table(ob, NULL, NULL, 'e'); + + /* free the memory allocated */ + MEM_freeN(bonelist); + MEM_freeN(dgrouplist); + MEM_freeN(dgroupflip); + MEM_freeN(root); + MEM_freeN(tip); + MEM_freeN(selected); + MEM_freeN(verts); +} + +void create_vgroups_from_armature(ReportList *reports, Scene *scene, Object *ob, Object *par, int mode, int mirror) +{ + /* Lets try to create some vertex groups + * based on the bones of the parent armature. + */ + bArmature *arm = par->data; + + if (mode == ARM_GROUPS_NAME) { + const int defbase_tot = BLI_countlist(&ob->defbase); + int defbase_add; + /* Traverse the bone list, trying to create empty vertex + * groups corresponding to the bone. + */ + defbase_add = bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb); + + if (defbase_add) { + /* its possible there are DWeight's outside the range of the current + * objects deform groups, in this case the new groups wont be empty [#33889] */ + ED_vgroup_data_clamp_range(ob->data, defbase_tot); + } + } + else if (mode == ARM_GROUPS_ENVELOPE || mode == ARM_GROUPS_AUTO) { + /* Traverse the bone list, trying to create vertex groups + * that are populated with the vertices for which the + * bone is closest. + */ + add_verts_to_dgroups(reports, scene, ob, par, (mode == ARM_GROUPS_AUTO), mirror); + } +} diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c new file mode 100644 index 00000000000..05f48ad73f4 --- /dev/null +++ b/source/blender/editors/armature/armature_utils.c @@ -0,0 +1,671 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/armature/armature_utils.c + * \ingroup edarmature + */ + +#include "DNA_armature_types.h" +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BKE_armature.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_main.h" + +#include "ED_armature.h" +#include "ED_util.h" + +#include "armature_intern.h" + +/* *************************************************************** */ +/* Validation */ + +/* Sync selection to parent for connected children */ +void ED_armature_sync_selection(ListBase *edbo) +{ + EditBone *ebo; + + for (ebo = edbo->first; ebo; ebo = ebo->next) { + /* if bone is not selectable, we shouldn't alter this setting... */ + if ((ebo->flag & BONE_UNSELECTABLE) == 0) { + if ((ebo->flag & BONE_CONNECTED) && (ebo->parent)) { + if (ebo->parent->flag & BONE_TIPSEL) + ebo->flag |= BONE_ROOTSEL; + else + ebo->flag &= ~BONE_ROOTSEL; + } + + if ((ebo->flag & BONE_TIPSEL) && (ebo->flag & BONE_ROOTSEL)) + ebo->flag |= BONE_SELECTED; + else + ebo->flag &= ~BONE_SELECTED; + } + } +} + +void ED_armature_validate_active(struct bArmature *arm) +{ + EditBone *ebone = arm->act_edbone; + + if (ebone) { + if (ebone->flag & BONE_HIDDEN_A) + arm->act_edbone = NULL; + } +} + +/* *************************************************************** */ +/* Bone Operations */ + +/* XXX bone_looper is only to be used when we want to access settings + * (i.e. editability/visibility/selected) that context doesn't offer */ +int bone_looper(Object *ob, Bone *bone, void *data, + int (*bone_func)(Object *, Bone *, void *)) +{ + /* We want to apply the function bone_func to every bone + * in an armature -- feed bone_looper the first bone and + * a pointer to the bone_func and watch it go!. The int count + * can be useful for counting bones with a certain property + * (e.g. skinnable) + */ + int count = 0; + + if (bone) { + /* only do bone_func if the bone is non null */ + count += bone_func(ob, bone, data); + + /* try to execute bone_func for the first child */ + count += bone_looper(ob, bone->childbase.first, data, bone_func); + + /* try to execute bone_func for the next bone at this + * depth of the recursion. + */ + count += bone_looper(ob, bone->next, data, bone_func); + } + + return count; +} + +/* *************************************************************** */ +/* Bone Removal */ + +void bone_free(bArmature *arm, EditBone *bone) +{ + if (arm->act_edbone == bone) + arm->act_edbone = NULL; + + if (bone->prop) { + IDP_FreeProperty(bone->prop); + MEM_freeN(bone->prop); + } + + BLI_freelinkN(arm->edbo, bone); +} + +void ED_armature_edit_bone_remove(bArmature *arm, EditBone *exBone) +{ + EditBone *curBone; + + /* Find any bones that refer to this bone */ + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (curBone->parent == exBone) { + curBone->parent = exBone->parent; + curBone->flag &= ~BONE_CONNECTED; + } + } + + bone_free(arm, exBone); +} + +/* *************************************************************** */ +/* Mirroring */ + +/* context: editmode armature */ +EditBone *ED_armature_bone_get_mirrored(ListBase *edbo, EditBone *ebo) +{ + EditBone *eboflip = NULL; + char name[MAXBONENAME]; + + if (ebo == NULL) + return NULL; + + flip_side_name(name, ebo->name, FALSE); + + for (eboflip = edbo->first; eboflip; eboflip = eboflip->next) { + if (ebo != eboflip) { + if (!strcmp(name, eboflip->name)) + break; + } + } + + return eboflip; +} + +/* ------------------------------------- */ + +/* helper function for tools to work on mirrored parts. + * it leaves mirrored bones selected then too, which is a good indication of what happened */ +void armature_select_mirrored(bArmature *arm) +{ + /* Select mirrored bones */ + if (arm->flag & ARM_MIRROR_EDIT) { + EditBone *curBone, *ebone_mirr; + + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (arm->layer & curBone->layer) { + if (curBone->flag & BONE_SELECTED) { + ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone); + if (ebone_mirr) + ebone_mirr->flag |= BONE_SELECTED; + } + } + } + } + +} + +void armature_tag_select_mirrored(bArmature *arm) +{ + EditBone *curBone; + + /* always untag */ + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + curBone->flag &= ~BONE_DONE; + } + + /* Select mirrored bones */ + if (arm->flag & ARM_MIRROR_EDIT) { + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (arm->layer & curBone->layer) { + if (curBone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) { + EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone); + if (ebone_mirr && (ebone_mirr->flag & BONE_SELECTED) == 0) { + ebone_mirr->flag |= BONE_DONE; + } + } + } + } + + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (curBone->flag & BONE_DONE) { + EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone); + curBone->flag |= ebone_mirr->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + } + } + } +} + +/* only works when tagged */ +void armature_tag_unselect(bArmature *arm) +{ + EditBone *curBone; + + for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { + if (curBone->flag & BONE_DONE) { + curBone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL | BONE_DONE); + } + } +} + +/* ------------------------------------- */ + +/* if editbone (partial) selected, copy data */ +/* context; editmode armature, with mirror editing enabled */ +void transform_armature_mirror_update(Object *obedit) +{ + bArmature *arm = obedit->data; + EditBone *ebo, *eboflip; + + for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { + /* no layer check, correct mirror is more important */ + if (ebo->flag & (BONE_TIPSEL | BONE_ROOTSEL)) { + eboflip = ED_armature_bone_get_mirrored(arm->edbo, ebo); + + if (eboflip) { + /* we assume X-axis flipping for now */ + if (ebo->flag & BONE_TIPSEL) { + EditBone *children; + + eboflip->tail[0] = -ebo->tail[0]; + eboflip->tail[1] = ebo->tail[1]; + eboflip->tail[2] = ebo->tail[2]; + eboflip->rad_tail = ebo->rad_tail; + eboflip->roll = -ebo->roll; + + /* Also move connected children, in case children's name aren't mirrored properly */ + for (children = arm->edbo->first; children; children = children->next) { + if (children->parent == eboflip && children->flag & BONE_CONNECTED) { + copy_v3_v3(children->head, eboflip->tail); + children->rad_head = ebo->rad_tail; + } + } + } + if (ebo->flag & BONE_ROOTSEL) { + eboflip->head[0] = -ebo->head[0]; + eboflip->head[1] = ebo->head[1]; + eboflip->head[2] = ebo->head[2]; + eboflip->rad_head = ebo->rad_head; + eboflip->roll = -ebo->roll; + + /* Also move connected parent, in case parent's name isn't mirrored properly */ + if (eboflip->parent && eboflip->flag & BONE_CONNECTED) { + EditBone *parent = eboflip->parent; + copy_v3_v3(parent->tail, eboflip->head); + parent->rad_tail = ebo->rad_head; + } + } + if (ebo->flag & BONE_SELECTED) { + eboflip->dist = ebo->dist; + eboflip->roll = -ebo->roll; + eboflip->xwidth = ebo->xwidth; + eboflip->zwidth = ebo->zwidth; + } + } + } + } +} + +/* *************************************************************** */ +/* Armature EditMode Conversions */ + +/* converts Bones to EditBone list, used for tools as well */ +EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone *actBone) +{ + EditBone *eBone; + EditBone *eBoneAct = NULL; + EditBone *eBoneTest = NULL; + Bone *curBone; + + for (curBone = bones->first; curBone; curBone = curBone->next) { + eBone = MEM_callocN(sizeof(EditBone), "make_editbone"); + + /* Copy relevant data from bone to eBone */ + eBone->parent = parent; + BLI_strncpy(eBone->name, curBone->name, sizeof(eBone->name)); + eBone->flag = curBone->flag; + + /* fix selection flags */ + if (eBone->flag & BONE_SELECTED) { + /* if the bone is selected the copy its root selection to the parents tip */ + eBone->flag |= BONE_TIPSEL; + if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { + eBone->parent->flag |= BONE_TIPSEL; + eBone->flag &= ~BONE_ROOTSEL; /* this is ignored when there is a connected parent, so unset it */ + } + else { + eBone->flag |= BONE_ROOTSEL; + } + } + else { + /* if the bone is not selected, but connected to its parent + * copy the parents tip selection state */ + if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { + /* selecting with the mouse gives this behavior */ + if (eBone->parent->flag & BONE_TIPSEL) { + eBone->flag |= BONE_ROOTSEL; + } + else { + eBone->flag &= ~BONE_ROOTSEL; + } + + /* probably not selected but just in case */ + eBone->flag &= ~BONE_TIPSEL; + } + } + + copy_v3_v3(eBone->head, curBone->arm_head); + copy_v3_v3(eBone->tail, curBone->arm_tail); + eBone->roll = curBone->arm_roll; + + /* rest of stuff copy */ + eBone->length = curBone->length; + eBone->dist = curBone->dist; + eBone->weight = curBone->weight; + eBone->xwidth = curBone->xwidth; + eBone->zwidth = curBone->zwidth; + eBone->ease1 = curBone->ease1; + eBone->ease2 = curBone->ease2; + eBone->rad_head = curBone->rad_head; + eBone->rad_tail = curBone->rad_tail; + eBone->segments = curBone->segments; + eBone->layer = curBone->layer; + + if (curBone->prop) + eBone->prop = IDP_CopyProperty(curBone->prop); + + BLI_addtail(edbo, eBone); + + /* Add children if necessary */ + if (curBone->childbase.first) { + eBoneTest = make_boneList(edbo, &curBone->childbase, eBone, actBone); + if (eBoneTest) + eBoneAct = eBoneTest; + } + + if (curBone == actBone) + eBoneAct = eBone; + } + + return eBoneAct; +} + +/* nasty stuff for converting roll in editbones into bones */ +/* also sets restposition in armature (arm_mat) */ +static void fix_bonelist_roll(ListBase *bonelist, ListBase *editbonelist) +{ + Bone *curBone; + EditBone *ebone; + float premat[3][3]; + float postmat[3][3]; + float difmat[3][3]; + float imat[3][3]; + float delta[3]; + + for (curBone = bonelist->first; curBone; curBone = curBone->next) { + /* sets local matrix and arm_mat (restpos) */ + BKE_armature_where_is_bone(curBone, curBone->parent); + + /* Find the associated editbone */ + for (ebone = editbonelist->first; ebone; ebone = ebone->next) + if ((Bone *)ebone->temp == curBone) + break; + + if (ebone) { + /* Get the ebone premat */ + sub_v3_v3v3(delta, ebone->tail, ebone->head); + vec_roll_to_mat3(delta, ebone->roll, premat); + + /* Get the bone postmat */ + copy_m3_m4(postmat, curBone->arm_mat); + + invert_m3_m3(imat, premat); + mul_m3_m3m3(difmat, imat, postmat); +#if 0 + printf("Bone %s\n", curBone->name); + print_m4("premat", premat); + print_m4("postmat", postmat); + print_m4("difmat", difmat); + printf("Roll = %f\n", RAD2DEGF(-atan2(difmat[2][0], difmat[2][2]))); +#endif + curBone->roll = (float)-atan2(difmat[2][0], difmat[2][2]); + + /* and set restposition again */ + BKE_armature_where_is_bone(curBone, curBone->parent); + } + fix_bonelist_roll(&curBone->childbase, editbonelist); + } +} + +/* put EditMode back in Object */ +void ED_armature_from_edit(Object *obedit) +{ + bArmature *arm = obedit->data; + EditBone *eBone, *neBone; + Bone *newBone; + Object *obt; + + /* armature bones */ + BKE_armature_bonelist_free(&arm->bonebase); + + /* remove zero sized bones, this gives instable restposes */ + for (eBone = arm->edbo->first; eBone; eBone = neBone) { + float len = len_v3v3(eBone->head, eBone->tail); + neBone = eBone->next; + if (len <= 0.000001f) { /* FLT_EPSILON is too large? */ + EditBone *fBone; + + /* Find any bones that refer to this bone */ + for (fBone = arm->edbo->first; fBone; fBone = fBone->next) { + if (fBone->parent == eBone) + fBone->parent = eBone->parent; + } + if (G.debug & G_DEBUG) + printf("Warning: removed zero sized bone: %s\n", eBone->name); + bone_free(arm, eBone); + } + } + + /* Copy the bones from the editData into the armature */ + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + newBone = MEM_callocN(sizeof(Bone), "bone"); + eBone->temp = newBone; /* Associate the real Bones with the EditBones */ + + BLI_strncpy(newBone->name, eBone->name, sizeof(newBone->name)); + copy_v3_v3(newBone->arm_head, eBone->head); + copy_v3_v3(newBone->arm_tail, eBone->tail); + newBone->arm_roll = eBone->roll; + + newBone->flag = eBone->flag; + + if (eBone == arm->act_edbone) { + /* don't change active selection, this messes up separate which uses + * editmode toggle and can separate active bone which is de-selected originally */ + /* newBone->flag |= BONE_SELECTED; */ /* important, editbones can be active with only 1 point selected */ + arm->act_edbone = NULL; + arm->act_bone = newBone; + } + newBone->roll = 0.0f; + + newBone->weight = eBone->weight; + newBone->dist = eBone->dist; + + newBone->xwidth = eBone->xwidth; + newBone->zwidth = eBone->zwidth; + newBone->ease1 = eBone->ease1; + newBone->ease2 = eBone->ease2; + newBone->rad_head = eBone->rad_head; + newBone->rad_tail = eBone->rad_tail; + newBone->segments = eBone->segments; + newBone->layer = eBone->layer; + + if (eBone->prop) + newBone->prop = IDP_CopyProperty(eBone->prop); + } + + /* Fix parenting in a separate pass to ensure ebone->bone connections + * are valid at this point */ + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + newBone = (Bone *)eBone->temp; + if (eBone->parent) { + newBone->parent = (Bone *)eBone->parent->temp; + BLI_addtail(&newBone->parent->childbase, newBone); + + { + float M_parentRest[3][3]; + float iM_parentRest[3][3]; + float delta[3]; + + /* Get the parent's matrix (rotation only) */ + sub_v3_v3v3(delta, eBone->parent->tail, eBone->parent->head); + vec_roll_to_mat3(delta, eBone->parent->roll, M_parentRest); + + /* Invert the parent matrix */ + invert_m3_m3(iM_parentRest, M_parentRest); + + /* Get the new head and tail */ + sub_v3_v3v3(newBone->head, eBone->head, eBone->parent->tail); + sub_v3_v3v3(newBone->tail, eBone->tail, eBone->parent->tail); + + mul_m3_v3(iM_parentRest, newBone->head); + mul_m3_v3(iM_parentRest, newBone->tail); + } + } + /* ...otherwise add this bone to the armature's bonebase */ + else { + copy_v3_v3(newBone->head, eBone->head); + copy_v3_v3(newBone->tail, eBone->tail); + BLI_addtail(&arm->bonebase, newBone); + } + } + + /* Make a pass through the new armature to fix rolling */ + /* also builds restposition again (like BKE_armature_where_is) */ + fix_bonelist_roll(&arm->bonebase, arm->edbo); + + /* so all users of this armature should get rebuilt */ + for (obt = G.main->object.first; obt; obt = obt->id.next) { + if (obt->data == arm) + BKE_pose_rebuild(obt, arm); + } + + DAG_id_tag_update(&obedit->id, OB_RECALC_DATA); +} + +void ED_armature_edit_free(struct Object *ob) +{ + bArmature *arm = ob->data; + EditBone *eBone; + + /* Clear the editbones list */ + if (arm->edbo) { + if (arm->edbo->first) { + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + if (eBone->prop) { + IDP_FreeProperty(eBone->prop); + MEM_freeN(eBone->prop); + } + } + + BLI_freelistN(arm->edbo); + } + MEM_freeN(arm->edbo); + arm->edbo = NULL; + } +} + +/* Put armature in EditMode */ +void ED_armature_to_edit(Object *ob) +{ + bArmature *arm = ob->data; + + ED_armature_edit_free(ob); + arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature"); + arm->act_edbone = make_boneList(arm->edbo, &arm->bonebase, NULL, arm->act_bone); + arm->act_bone = NULL; + +// BIF_freeTemplates(); /* force template update when entering editmode */ +} + +/* *************************************************************** */ +/* Undo for Armature EditMode*/ + +typedef struct UndoArmature { + EditBone *act_edbone; + ListBase lb; +} UndoArmature; + +static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data)) +{ + UndoArmature *uarm = uarmv; + bArmature *arm = armv; + EditBone *ebo, *newebo; + + BLI_freelistN(arm->edbo); + + /* copy */ + for (ebo = uarm->lb.first; ebo; ebo = ebo->next) { + newebo = MEM_dupallocN(ebo); + ebo->temp = newebo; + BLI_addtail(arm->edbo, newebo); + } + + /* active bone */ + if (uarm->act_edbone) { + ebo = uarm->act_edbone; + arm->act_edbone = ebo->temp; + } + else + arm->act_edbone = NULL; + + /* set pointers */ + for (newebo = arm->edbo->first; newebo; newebo = newebo->next) { + if (newebo->parent) newebo->parent = newebo->parent->temp; + } + /* be sure they don't hang ever */ + for (newebo = arm->edbo->first; newebo; newebo = newebo->next) { + newebo->temp = NULL; + } +} + +static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata)) +{ + bArmature *arm = armv; + UndoArmature *uarm; + EditBone *ebo, *newebo; + + uarm = MEM_callocN(sizeof(UndoArmature), "listbase undo"); + + /* copy */ + for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { + newebo = MEM_dupallocN(ebo); + ebo->temp = newebo; + BLI_addtail(&uarm->lb, newebo); + } + + /* active bone */ + if (arm->act_edbone) { + ebo = arm->act_edbone; + uarm->act_edbone = ebo->temp; + } + + /* set pointers */ + for (newebo = uarm->lb.first; newebo; newebo = newebo->next) { + if (newebo->parent) newebo->parent = newebo->parent->temp; + } + + return uarm; +} + +static void free_undoBones(void *uarmv) +{ + UndoArmature *uarm = uarmv; + + BLI_freelistN(&uarm->lb); + MEM_freeN(uarm); +} + +static void *get_armature_edit(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_ARMATURE) { + return obedit->data; + } + return NULL; +} + +/* and this is all the undo system needs to know */ +void undo_push_armature(bContext *C, const char *name) +{ + // XXX solve getdata() + undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL); +} diff --git a/source/blender/editors/armature/editarmature.c b/source/blender/editors/armature/editarmature.c deleted file mode 100644 index 2eac6ba87ee..00000000000 --- a/source/blender/editors/armature/editarmature.c +++ /dev/null @@ -1,6197 +0,0 @@ -/* - * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - * - * Contributor(s): Blender Foundation, 2002-2009 full recode. - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/editors/armature/editarmature.c - * \ingroup edarmature - */ - - -#include <ctype.h> -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <math.h> -#include <float.h> -#include <assert.h> - - -#include "DNA_anim_types.h" -#include "DNA_mesh_types.h" -#include "DNA_armature_types.h" -#include "DNA_constraint_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" -#include "BLI_ghash.h" - -#include "BKE_animsys.h" -#include "BKE_action.h" -#include "BKE_armature.h" -#include "BKE_constraint.h" -#include "BKE_context.h" -#include "BKE_deform.h" -#include "BKE_depsgraph.h" -#include "BKE_DerivedMesh.h" -#include "BKE_global.h" -#include "BKE_idprop.h" -#include "BKE_main.h" -#include "BKE_object.h" -#include "BKE_report.h" -#include "BKE_subsurf.h" -#include "BKE_modifier.h" -#include "DNA_object_types.h" - -#include "BIF_gl.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_armature.h" -#include "ED_keyframing.h" -#include "ED_mesh.h" -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_util.h" -#include "ED_view3d.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "armature_intern.h" -#include "meshlaplacian.h" - -#if 0 -#include "reeb.h" -#endif - -/* **************** tools on Editmode Armature **************** */ - -/* Sync selection to parent for connected children */ -void ED_armature_sync_selection(ListBase *edbo) -{ - EditBone *ebo; - - for (ebo = edbo->first; ebo; ebo = ebo->next) { - /* if bone is not selectable, we shouldn't alter this setting... */ - if ((ebo->flag & BONE_UNSELECTABLE) == 0) { - if ((ebo->flag & BONE_CONNECTED) && (ebo->parent)) { - if (ebo->parent->flag & BONE_TIPSEL) - ebo->flag |= BONE_ROOTSEL; - else - ebo->flag &= ~BONE_ROOTSEL; - } - - if ((ebo->flag & BONE_TIPSEL) && (ebo->flag & BONE_ROOTSEL)) - ebo->flag |= BONE_SELECTED; - else - ebo->flag &= ~BONE_SELECTED; - } - } -} - -void ED_armature_validate_active(struct bArmature *arm) -{ - EditBone *ebone = arm->act_edbone; - - if (ebone) { - if (ebone->flag & BONE_HIDDEN_A) - arm->act_edbone = NULL; - } -} - -static void bone_free(bArmature *arm, EditBone *bone) -{ - if (arm->act_edbone == bone) - arm->act_edbone = NULL; - - if (bone->prop) { - IDP_FreeProperty(bone->prop); - MEM_freeN(bone->prop); - } - - BLI_freelinkN(arm->edbo, bone); -} - -void ED_armature_edit_bone_remove(bArmature *arm, EditBone *exBone) -{ - EditBone *curBone; - - /* Find any bones that refer to this bone */ - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (curBone->parent == exBone) { - curBone->parent = exBone->parent; - curBone->flag &= ~BONE_CONNECTED; - } - } - - bone_free(arm, exBone); -} - -/* context: editmode armature */ -EditBone *ED_armature_bone_get_mirrored(ListBase *edbo, EditBone *ebo) -{ - EditBone *eboflip = NULL; - char name[MAXBONENAME]; - - if (ebo == NULL) - return NULL; - - flip_side_name(name, ebo->name, FALSE); - - for (eboflip = edbo->first; eboflip; eboflip = eboflip->next) { - if (ebo != eboflip) { - if (!strcmp(name, eboflip->name)) - break; - } - } - - return eboflip; -} - -/* helper function for tools to work on mirrored parts. - * it leaves mirrored bones selected then too, which is a good indication of what happened */ -static void armature_select_mirrored(bArmature *arm) -{ - /* Select mirrored bones */ - if (arm->flag & ARM_MIRROR_EDIT) { - EditBone *curBone, *ebone_mirr; - - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (arm->layer & curBone->layer) { - if (curBone->flag & BONE_SELECTED) { - ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone); - if (ebone_mirr) - ebone_mirr->flag |= BONE_SELECTED; - } - } - } - } - -} - -static void armature_tag_select_mirrored(bArmature *arm) -{ - EditBone *curBone; - - /* always untag */ - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - curBone->flag &= ~BONE_DONE; - } - - /* Select mirrored bones */ - if (arm->flag & ARM_MIRROR_EDIT) { - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (arm->layer & curBone->layer) { - if (curBone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) { - EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone); - if (ebone_mirr && (ebone_mirr->flag & BONE_SELECTED) == 0) { - ebone_mirr->flag |= BONE_DONE; - } - } - } - } - - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (curBone->flag & BONE_DONE) { - EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone); - curBone->flag |= ebone_mirr->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - } - } - } -} - - -/* only works when tagged */ -static void armature_tag_unselect(bArmature *arm) -{ - EditBone *curBone; - - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (curBone->flag & BONE_DONE) { - curBone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL | BONE_DONE); - } - } -} - -/* converts Bones to EditBone list, used for tools as well */ -EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone *actBone) -{ - EditBone *eBone; - EditBone *eBoneAct = NULL; - EditBone *eBoneTest = NULL; - Bone *curBone; - - for (curBone = bones->first; curBone; curBone = curBone->next) { - eBone = MEM_callocN(sizeof(EditBone), "make_editbone"); - - /* Copy relevant data from bone to eBone */ - eBone->parent = parent; - BLI_strncpy(eBone->name, curBone->name, sizeof(eBone->name)); - eBone->flag = curBone->flag; - - /* fix selection flags */ - - if (eBone->flag & BONE_SELECTED) { - /* if the bone is selected the copy its root selection to the parents tip */ - eBone->flag |= BONE_TIPSEL; - if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { - eBone->parent->flag |= BONE_TIPSEL; - eBone->flag &= ~BONE_ROOTSEL; /* this is ignored when there is a connected parent, so unset it */ - } - else { - eBone->flag |= BONE_ROOTSEL; - } - } - else { - /* if the bone is not selected, but connected to its parent - * copy the parents tip selection state */ - if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { - /* selecting with the mouse gives this behavior */ - if (eBone->parent->flag & BONE_TIPSEL) { - eBone->flag |= BONE_ROOTSEL; - } - else { - eBone->flag &= ~BONE_ROOTSEL; - } - - /* probably not selected but just in case */ - eBone->flag &= ~BONE_TIPSEL; - } - } - - copy_v3_v3(eBone->head, curBone->arm_head); - copy_v3_v3(eBone->tail, curBone->arm_tail); - eBone->roll = curBone->arm_roll; - - /* rest of stuff copy */ - eBone->length = curBone->length; - eBone->dist = curBone->dist; - eBone->weight = curBone->weight; - eBone->xwidth = curBone->xwidth; - eBone->zwidth = curBone->zwidth; - eBone->ease1 = curBone->ease1; - eBone->ease2 = curBone->ease2; - eBone->rad_head = curBone->rad_head; - eBone->rad_tail = curBone->rad_tail; - eBone->segments = curBone->segments; - eBone->layer = curBone->layer; - - if (curBone->prop) - eBone->prop = IDP_CopyProperty(curBone->prop); - - BLI_addtail(edbo, eBone); - - /* Add children if necessary */ - if (curBone->childbase.first) { - eBoneTest = make_boneList(edbo, &curBone->childbase, eBone, actBone); - if (eBoneTest) - eBoneAct = eBoneTest; - } - - if (curBone == actBone) - eBoneAct = eBone; - } - - return eBoneAct; -} - -/* nasty stuff for converting roll in editbones into bones */ -/* also sets restposition in armature (arm_mat) */ -static void fix_bonelist_roll(ListBase *bonelist, ListBase *editbonelist) -{ - Bone *curBone; - EditBone *ebone; - float premat[3][3]; - float postmat[3][3]; - float difmat[3][3]; - float imat[3][3]; - float delta[3]; - - for (curBone = bonelist->first; curBone; curBone = curBone->next) { - /* sets local matrix and arm_mat (restpos) */ - BKE_armature_where_is_bone(curBone, curBone->parent); - - /* Find the associated editbone */ - for (ebone = editbonelist->first; ebone; ebone = ebone->next) - if ((Bone *)ebone->temp == curBone) - break; - - if (ebone) { - /* Get the ebone premat */ - sub_v3_v3v3(delta, ebone->tail, ebone->head); - vec_roll_to_mat3(delta, ebone->roll, premat); - - /* Get the bone postmat */ - copy_m3_m4(postmat, curBone->arm_mat); - - invert_m3_m3(imat, premat); - mul_m3_m3m3(difmat, imat, postmat); -#if 0 - printf("Bone %s\n", curBone->name); - print_m4("premat", premat); - print_m4("postmat", postmat); - print_m4("difmat", difmat); - printf("Roll = %f\n", RAD2DEGF(-atan2(difmat[2][0], difmat[2][2]))); -#endif - curBone->roll = (float)-atan2(difmat[2][0], difmat[2][2]); - - /* and set restposition again */ - BKE_armature_where_is_bone(curBone, curBone->parent); - } - fix_bonelist_roll(&curBone->childbase, editbonelist); - } -} - -/* put EditMode back in Object */ -void ED_armature_from_edit(Object *obedit) -{ - bArmature *arm = obedit->data; - EditBone *eBone, *neBone; - Bone *newBone; - Object *obt; - - /* armature bones */ - BKE_armature_bonelist_free(&arm->bonebase); - - /* remove zero sized bones, this gives instable restposes */ - for (eBone = arm->edbo->first; eBone; eBone = neBone) { - float len = len_v3v3(eBone->head, eBone->tail); - neBone = eBone->next; - if (len <= 0.000001f) { /* FLT_EPSILON is too large? */ - EditBone *fBone; - - /* Find any bones that refer to this bone */ - for (fBone = arm->edbo->first; fBone; fBone = fBone->next) { - if (fBone->parent == eBone) - fBone->parent = eBone->parent; - } - if (G.debug & G_DEBUG) - printf("Warning: removed zero sized bone: %s\n", eBone->name); - bone_free(arm, eBone); - } - } - - /* Copy the bones from the editData into the armature */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - newBone = MEM_callocN(sizeof(Bone), "bone"); - eBone->temp = newBone; /* Associate the real Bones with the EditBones */ - - BLI_strncpy(newBone->name, eBone->name, sizeof(newBone->name)); - copy_v3_v3(newBone->arm_head, eBone->head); - copy_v3_v3(newBone->arm_tail, eBone->tail); - newBone->arm_roll = eBone->roll; - - newBone->flag = eBone->flag; - - if (eBone == arm->act_edbone) { - /* don't change active selection, this messes up separate which uses - * editmode toggle and can separate active bone which is de-selected originally */ - /* newBone->flag |= BONE_SELECTED; */ /* important, editbones can be active with only 1 point selected */ - arm->act_edbone = NULL; - arm->act_bone = newBone; - } - newBone->roll = 0.0f; - - newBone->weight = eBone->weight; - newBone->dist = eBone->dist; - - newBone->xwidth = eBone->xwidth; - newBone->zwidth = eBone->zwidth; - newBone->ease1 = eBone->ease1; - newBone->ease2 = eBone->ease2; - newBone->rad_head = eBone->rad_head; - newBone->rad_tail = eBone->rad_tail; - newBone->segments = eBone->segments; - newBone->layer = eBone->layer; - - if (eBone->prop) - newBone->prop = IDP_CopyProperty(eBone->prop); - } - - /* Fix parenting in a separate pass to ensure ebone->bone connections - * are valid at this point */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - newBone = (Bone *)eBone->temp; - if (eBone->parent) { - newBone->parent = (Bone *)eBone->parent->temp; - BLI_addtail(&newBone->parent->childbase, newBone); - - { - float M_parentRest[3][3]; - float iM_parentRest[3][3]; - float delta[3]; - - /* Get the parent's matrix (rotation only) */ - sub_v3_v3v3(delta, eBone->parent->tail, eBone->parent->head); - vec_roll_to_mat3(delta, eBone->parent->roll, M_parentRest); - - /* Invert the parent matrix */ - invert_m3_m3(iM_parentRest, M_parentRest); - - /* Get the new head and tail */ - sub_v3_v3v3(newBone->head, eBone->head, eBone->parent->tail); - sub_v3_v3v3(newBone->tail, eBone->tail, eBone->parent->tail); - - mul_m3_v3(iM_parentRest, newBone->head); - mul_m3_v3(iM_parentRest, newBone->tail); - } - } - /* ...otherwise add this bone to the armature's bonebase */ - else { - copy_v3_v3(newBone->head, eBone->head); - copy_v3_v3(newBone->tail, eBone->tail); - BLI_addtail(&arm->bonebase, newBone); - } - } - - /* Make a pass through the new armature to fix rolling */ - /* also builds restposition again (like BKE_armature_where_is) */ - fix_bonelist_roll(&arm->bonebase, arm->edbo); - - /* so all users of this armature should get rebuilt */ - for (obt = G.main->object.first; obt; obt = obt->id.next) { - if (obt->data == arm) - BKE_pose_rebuild(obt, arm); - } - - DAG_id_tag_update(&obedit->id, OB_RECALC_DATA); -} - -void ED_armature_apply_transform(Object *ob, float mat[4][4]) -{ - EditBone *ebone; - bArmature *arm = ob->data; - float scale = mat4_to_scale(mat); /* store the scale of the matrix here to use on envelopes */ - float mat3[3][3]; - - copy_m3_m4(mat3, mat); - normalize_m3(mat3); - - /* Put the armature into editmode */ - ED_armature_to_edit(ob); - - /* Do the rotations */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - float delta[3], tmat[3][3]; - - /* find the current bone's roll matrix */ - sub_v3_v3v3(delta, ebone->tail, ebone->head); - vec_roll_to_mat3(delta, ebone->roll, tmat); - - /* transform the roll matrix */ - mul_m3_m3m3(tmat, mat3, tmat); - - /* transform the bone */ - mul_m4_v3(mat, ebone->head); - mul_m4_v3(mat, ebone->tail); - - /* apply the transfiormed roll back */ - mat3_to_vec_roll(tmat, NULL, &ebone->roll); - - ebone->rad_head *= scale; - ebone->rad_tail *= scale; - ebone->dist *= scale; - - /* we could be smarter and scale by the matrix along the x & z axis */ - ebone->xwidth *= scale; - ebone->zwidth *= scale; - } - - /* Turn the list into an armature */ - ED_armature_from_edit(ob); - ED_armature_edit_free(ob); -} - -/* exported for use in editors/object/ */ -/* 0 == do center, 1 == center new, 2 == center cursor */ -void docenter_armature(Scene *scene, Object *ob, float cursor[3], int centermode, int around) -{ - Object *obedit = scene->obedit; // XXX get from context - EditBone *ebone; - bArmature *arm = ob->data; - float cent[3]; - - /* Put the armature into editmode */ - if (ob != obedit) { - ED_armature_to_edit(ob); - obedit = NULL; /* we cant use this so behave as if there is no obedit */ - } - - /* Find the centerpoint */ - if (centermode == 2) { - copy_v3_v3(cent, cursor); - invert_m4_m4(ob->imat, ob->obmat); - mul_m4_v3(ob->imat, cent); - } - else { - if (around == V3D_CENTROID) { - int total = 0; - zero_v3(cent); - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - total += 2; - add_v3_v3(cent, ebone->head); - add_v3_v3(cent, ebone->tail); - } - if (total) { - mul_v3_fl(cent, 1.0f / (float)total); - } - } - else { - float min[3], max[3]; - INIT_MINMAX(min, max); - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - minmax_v3v3_v3(min, max, ebone->head); - minmax_v3v3_v3(min, max, ebone->tail); - } - mid_v3_v3v3(cent, min, max); - } - } - - /* Do the adjustments */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - sub_v3_v3(ebone->head, cent); - sub_v3_v3(ebone->tail, cent); - } - - /* Turn the list into an armature */ - if (obedit == NULL) { - ED_armature_from_edit(ob); - ED_armature_edit_free(ob); - } - - /* Adjust object location for new centerpoint */ - if (centermode && obedit == NULL) { - mul_mat3_m4_v3(ob->obmat, cent); /* ommit translation part */ - add_v3_v3(ob->loc, cent); - } -} - -/* ---------------------- */ - -/* checks if an EditBone with a matching name already, returning the matching bone if it exists */ -static EditBone *editbone_name_exists(ListBase *edbo, const char *name) -{ - return BLI_findstring(edbo, name, offsetof(EditBone, name)); -} - -/* note: there's a unique_bone_name() too! */ -static int editbone_unique_check(void *arg, const char *name) -{ - struct {ListBase *lb; void *bone; } *data = arg; - EditBone *dupli = editbone_name_exists(data->lb, name); - return dupli && dupli != data->bone; -} - -void unique_editbone_name(ListBase *edbo, char *name, EditBone *bone) -{ - struct {ListBase *lb; void *bone; } data; - data.lb = edbo; - data.bone = bone; - - BLI_uniquename_cb(editbone_unique_check, &data, "Bone", '.', name, sizeof(bone->name)); -} - -/* helper for apply_armature_pose2bones - fixes parenting of objects that are bone-parented to armature */ -static void applyarmature_fix_boneparents(Scene *scene, Object *armob) -{ - Object workob, *ob; - - /* go through all objects in database */ - for (ob = G.main->object.first; ob; ob = ob->id.next) { - /* if parent is bone in this armature, apply corrections */ - if ((ob->parent == armob) && (ob->partype == PARBONE)) { - /* apply current transform from parent (not yet destroyed), - * then calculate new parent inverse matrix - */ - BKE_object_apply_mat4(ob, ob->obmat, FALSE, FALSE); - - BKE_object_workob_calc_parent(scene, ob, &workob); - invert_m4_m4(ob->parentinv, workob.obmat); - } - } -} - -/* set the current pose as the restpose */ -static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object - bArmature *arm = BKE_armature_from_object(ob); - bPose *pose; - bPoseChannel *pchan; - EditBone *curbone; - - /* don't check if editmode (should be done by caller) */ - if (ob->type != OB_ARMATURE) - return OPERATOR_CANCELLED; - if (BKE_object_obdata_is_libdata(ob)) { - BKE_report(op->reports, RPT_ERROR, "Cannot apply pose to lib-linked armature"); /* error_libdata(); */ - return OPERATOR_CANCELLED; - } - - /* helpful warnings... */ - /* TODO: add warnings to be careful about actions, applying deforms first, etc. */ - if (ob->adt && ob->adt->action) - BKE_report(op->reports, RPT_WARNING, - "Actions on this armature will be destroyed by this new rest pose as the " - "transforms stored are relative to the old rest pose"); - - /* Get editbones of active armature to alter */ - ED_armature_to_edit(ob); - - /* get pose of active object and move it out of posemode */ - pose = ob->pose; - - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { - curbone = editbone_name_exists(arm->edbo, pchan->name); - - /* simply copy the head/tail values from pchan over to curbone */ - copy_v3_v3(curbone->head, pchan->pose_head); - copy_v3_v3(curbone->tail, pchan->pose_tail); - - /* fix roll: - * 1. find auto-calculated roll value for this bone now - * 2. remove this from the 'visual' y-rotation - */ - { - float premat[3][3], imat[3][3], pmat[3][3], tmat[3][3]; - float delta[3], eul[3]; - - /* obtain new auto y-rotation */ - sub_v3_v3v3(delta, curbone->tail, curbone->head); - vec_roll_to_mat3(delta, 0.0f, premat); - invert_m3_m3(imat, premat); - - /* get pchan 'visual' matrix */ - copy_m3_m4(pmat, pchan->pose_mat); - - /* remove auto from visual and get euler rotation */ - mul_m3_m3m3(tmat, imat, pmat); - mat3_to_eul(eul, tmat); - - /* just use this euler-y as new roll value */ - curbone->roll = eul[1]; - } - - /* clear transform values for pchan */ - zero_v3(pchan->loc); - zero_v3(pchan->eul); - unit_qt(pchan->quat); - unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); - pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f; - - /* set anim lock */ - curbone->flag |= BONE_UNKEYED; - } - - /* convert editbones back to bones, and then free the edit-data */ - ED_armature_from_edit(ob); - ED_armature_edit_free(ob); - - /* flush positions of posebones */ - BKE_pose_where_is(scene, ob); - - /* fix parenting of objects which are bone-parented */ - applyarmature_fix_boneparents(scene, ob); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_armature_apply(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Apply Pose as Rest Pose"; - ot->idname = "POSE_OT_armature_apply"; - ot->description = "Apply the current pose as the new rest pose"; - - /* callbacks */ - ot->exec = apply_armature_pose2bones_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - - -/* set the current pose as the restpose */ -static int pose_visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object - - /* don't check if editmode (should be done by caller) */ - if (ob->type != OB_ARMATURE) - return OPERATOR_CANCELLED; - - /* loop over all selected pchans - * - * TODO, loop over children before parents if multiple bones - * at once are to be predictable*/ - CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones) - { - float delta_mat[4][4]; - - /* chan_mat already contains the delta transform from rest pose to pose-mode pose - * as that is baked into there so that B-Bones will work. Once we've set this as the - * new raw-transform components, don't recalc the poses yet, otherwise IK result will - * change, thus changing the result we may be trying to record. - */ - copy_m4_m4(delta_mat, pchan->chan_mat); - BKE_pchan_apply_mat4(pchan, delta_mat, TRUE); - } - CTX_DATA_END; - - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_visual_transform_apply(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Apply Visual Transform to Pose"; - ot->idname = "POSE_OT_visual_transform_apply"; - ot->description = "Apply final constrained position of pose bones to their transform"; - - /* callbacks */ - ot->exec = pose_visual_transform_apply_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ---------------------- */ - -/* Helper function for armature joining - link fixing */ -static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone) -{ - Object *ob; - bPose *pose; - bPoseChannel *pchant; - bConstraint *con; - - /* let's go through all objects in database */ - for (ob = G.main->object.first; ob; ob = ob->id.next) { - /* do some object-type specific things */ - if (ob->type == OB_ARMATURE) { - pose = ob->pose; - for (pchant = pose->chanbase.first; pchant; pchant = pchant->next) { - for (con = pchant->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_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 == srcArm) { - if (ct->subtarget[0] == '\0') { - ct->tar = tarArm; - } - else if (strcmp(ct->subtarget, pchan->name) == 0) { - ct->tar = tarArm; - BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget)); - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 0); - } - - /* action constraint? */ - if (con->type == CONSTRAINT_TYPE_ACTION) { - bActionConstraint *data = con->data; // XXX old animation system - bAction *act; - bActionChannel *achan; - - if (data->act) { - act = data->act; - - for (achan = act->chanbase.first; achan; achan = achan->next) { - if (strcmp(achan->name, pchan->name) == 0) - BLI_strncpy(achan->name, curbone->name, sizeof(achan->name)); - } - } - } - - } - } - } - - /* fix object-level constraints */ - if (ob != srcArm) { - for (con = ob->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_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 == srcArm) { - if (ct->subtarget[0] == '\0') { - ct->tar = tarArm; - } - else if (strcmp(ct->subtarget, pchan->name) == 0) { - ct->tar = tarArm; - BLI_strncpy(ct->subtarget, curbone->name, sizeof(ct->subtarget)); - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - - /* See if an object is parented to this armature */ - if (ob->parent && (ob->parent == srcArm)) { - /* Is object parented to a bone of this src armature? */ - if (ob->partype == PARBONE) { - /* bone name in object */ - if (!strcmp(ob->parsubstr, pchan->name)) - BLI_strncpy(ob->parsubstr, curbone->name, sizeof(ob->parsubstr)); - } - - /* make tar armature be new parent */ - ob->parent = tarArm; - } - } -} - -/* join armature exec is exported for use in object->join objects operator... */ -int join_armature_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - bArmature *arm = (ob) ? ob->data : NULL; - bPose *pose, *opose; - bPoseChannel *pchan, *pchann; - EditBone *curbone; - float mat[4][4], oimat[4][4]; - - /* Ensure we're not in editmode and that the active object is an armature*/ - if (!ob || ob->type != OB_ARMATURE) - return OPERATOR_CANCELLED; - if (!arm || arm->edbo) - return OPERATOR_CANCELLED; - - /* Get editbones of active armature to add editbones to */ - ED_armature_to_edit(ob); - - /* get pose of active object and move it out of posemode */ - pose = ob->pose; - ob->mode &= ~OB_MODE_POSE; - - CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) - { - if ((base->object->type == OB_ARMATURE) && (base->object != ob)) { - bArmature *curarm = base->object->data; - - /* Make a list of editbones in current armature */ - ED_armature_to_edit(base->object); - - /* Get Pose of current armature */ - opose = base->object->pose; - base->object->mode &= ~OB_MODE_POSE; - //BASACT->flag &= ~OB_MODE_POSE; - - /* Find the difference matrix */ - invert_m4_m4(oimat, ob->obmat); - mult_m4_m4m4(mat, oimat, base->object->obmat); - - /* Copy bones and posechannels from the object to the edit armature */ - for (pchan = opose->chanbase.first; pchan; pchan = pchann) { - pchann = pchan->next; - curbone = editbone_name_exists(curarm->edbo, pchan->name); - - /* Get new name */ - unique_editbone_name(arm->edbo, curbone->name, NULL); - - /* Transform the bone */ - { - float premat[4][4]; - float postmat[4][4]; - float difmat[4][4]; - float imat[4][4]; - float temp[3][3]; - float delta[3]; - - /* Get the premat */ - sub_v3_v3v3(delta, curbone->tail, curbone->head); - vec_roll_to_mat3(delta, curbone->roll, temp); - - unit_m4(premat); /* Mat4MulMat34 only sets 3x3 part */ - mul_m4_m3m4(premat, temp, mat); - - mul_m4_v3(mat, curbone->head); - mul_m4_v3(mat, curbone->tail); - - /* Get the postmat */ - sub_v3_v3v3(delta, curbone->tail, curbone->head); - vec_roll_to_mat3(delta, curbone->roll, temp); - copy_m4_m3(postmat, temp); - - /* Find the roll */ - invert_m4_m4(imat, premat); - mult_m4_m4m4(difmat, imat, postmat); - - curbone->roll -= (float)atan2(difmat[2][0], difmat[2][2]); - } - - /* Fix Constraints and Other Links to this Bone and Armature */ - joined_armature_fix_links(ob, base->object, pchan, curbone); - - /* Rename pchan */ - BLI_strncpy(pchan->name, curbone->name, sizeof(pchan->name)); - - /* Jump Ship! */ - BLI_remlink(curarm->edbo, curbone); - BLI_addtail(arm->edbo, curbone); - - BLI_remlink(&opose->chanbase, pchan); - BLI_addtail(&pose->chanbase, pchan); - BKE_pose_channels_hash_free(opose); - BKE_pose_channels_hash_free(pose); - } - - ED_base_object_free_and_unlink(bmain, scene, base); - } - } - CTX_DATA_END; - - DAG_relations_tag_update(bmain); /* because we removed object(s) */ - - ED_armature_from_edit(ob); - ED_armature_edit_free(ob); - - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - - return OPERATOR_FINISHED; -} - -/* ---------------------- */ - -/* Helper function for armature separating - link fixing */ -static void separated_armature_fix_links(Object *origArm, Object *newArm) -{ - Object *ob; - bPoseChannel *pchan; - bConstraint *con; - ListBase *opchans, *npchans; - - /* get reference to list of bones in original and new armatures */ - opchans = &origArm->pose->chanbase; - npchans = &newArm->pose->chanbase; - - /* let's go through all objects in database */ - for (ob = G.main->object.first; ob; ob = ob->id.next) { - /* do some object-type specific things */ - if (ob->type == OB_ARMATURE) { - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - for (con = pchan->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_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) { - /* any targets which point to original armature are redirected to the new one only if: - * - the target isn't origArm/newArm itself - * - the target is one that can be found in newArm/origArm - */ - if (ct->subtarget[0] != 0) { - if (ct->tar == origArm) { - if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) { - ct->tar = newArm; - } - } - else if (ct->tar == newArm) { - if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) { - ct->tar = origArm; - } - } - } - } - - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - } - } - - /* fix object-level constraints */ - if (ob != origArm) { - for (con = ob->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_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) { - /* any targets which point to original armature are redirected to the new one only if: - * - the target isn't origArm/newArm itself - * - the target is one that can be found in newArm/origArm - */ - if (ct->subtarget[0] != '\0') { - if (ct->tar == origArm) { - if (BLI_findstring(npchans, ct->subtarget, offsetof(bPoseChannel, name))) { - ct->tar = newArm; - } - } - else if (ct->tar == newArm) { - if (BLI_findstring(opchans, ct->subtarget, offsetof(bPoseChannel, name))) { - ct->tar = origArm; - } - } - } - } - - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - } - - /* See if an object is parented to this armature */ - if (ob->parent && (ob->parent == origArm)) { - /* Is object parented to a bone of this src armature? */ - if ((ob->partype == PARBONE) && (ob->parsubstr[0] != '\0')) { - if (BLI_findstring(npchans, ob->parsubstr, offsetof(bPoseChannel, name))) { - ob->parent = newArm; - } - } - } - } -} - -/* Helper function for armature separating - remove certain bones from the given armature - * sel: remove selected bones from the armature, otherwise the unselected bones are removed - * (ob is not in editmode) - */ -static void separate_armature_bones(Object *ob, short sel) -{ - bArmature *arm = (bArmature *)ob->data; - bPoseChannel *pchan, *pchann; - EditBone *curbone; - - /* make local set of editbones to manipulate here */ - ED_armature_to_edit(ob); - - /* go through pose-channels, checking if a bone should be removed */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchann) { - pchann = pchan->next; - curbone = editbone_name_exists(arm->edbo, pchan->name); - - /* check if bone needs to be removed */ - if ( (sel && (curbone->flag & BONE_SELECTED)) || - (!sel && !(curbone->flag & BONE_SELECTED)) ) - { - EditBone *ebo; - bPoseChannel *pchn; - - /* clear the bone->parent var of any bone that had this as its parent */ - for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { - if (ebo->parent == curbone) { - ebo->parent = NULL; - ebo->temp = NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */ - ebo->flag &= ~BONE_CONNECTED; - } - } - - /* clear the pchan->parent var of any pchan that had this as its parent */ - for (pchn = ob->pose->chanbase.first; pchn; pchn = pchn->next) { - if (pchn->parent == pchan) - pchn->parent = NULL; - } - - /* free any of the extra-data this pchan might have */ - BKE_pose_channel_free(pchan); - BKE_pose_channels_hash_free(ob->pose); - - /* get rid of unneeded bone */ - bone_free(arm, curbone); - BLI_freelinkN(&ob->pose->chanbase, pchan); - } - } - - /* exit editmode (recalculates pchans too) */ - ED_armature_from_edit(ob); - ED_armature_edit_free(ob); -} - -/* separate selected bones into their armature */ -static int separate_armature_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - Object *oldob, *newob; - Base *oldbase, *newbase; - - /* sanity checks */ - if (obedit == NULL) - return OPERATOR_CANCELLED; - - /* set wait cursor in case this takes a while */ - WM_cursor_wait(1); - - /* we are going to do this as follows (unlike every other instance of separate): - * 1. exit editmode +posemode for active armature/base. Take note of what this is. - * 2. duplicate base - BASACT is the new one now - * 3. for each of the two armatures, enter editmode -> remove appropriate bones -> exit editmode + recalc - * 4. fix constraint links - * 5. make original armature active and enter editmode - */ - - /* 1) only edit-base selected */ - /* TODO: use context iterators for this? */ - CTX_DATA_BEGIN(C, Base *, base, visible_bases) - { - if (base->object == obedit) base->flag |= 1; - else base->flag &= ~1; - } - CTX_DATA_END; - - /* 1) store starting settings and exit editmode */ - oldob = obedit; - oldbase = BASACT; - oldob->mode &= ~OB_MODE_POSE; - //oldbase->flag &= ~OB_POSEMODE; - - ED_armature_from_edit(obedit); - ED_armature_edit_free(obedit); - - /* 2) duplicate base */ - newbase = ED_object_add_duplicate(bmain, scene, oldbase, USER_DUP_ARM); /* only duplicate linked armature */ - DAG_relations_tag_update(bmain); - - newob = newbase->object; - newbase->flag &= ~SELECT; - - - /* 3) remove bones that shouldn't still be around on both armatures */ - separate_armature_bones(oldob, 1); - separate_armature_bones(newob, 0); - - - /* 4) fix links before depsgraph flushes */ // err... or after? - separated_armature_fix_links(oldob, newob); - - DAG_id_tag_update(&oldob->id, OB_RECALC_DATA); /* this is the original one */ - DAG_id_tag_update(&newob->id, OB_RECALC_DATA); /* this is the separated one */ - - - /* 5) restore original conditions */ - obedit = oldob; - - ED_armature_to_edit(obedit); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); - - /* recalc/redraw + cleanup */ - WM_cursor_wait(0); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_separate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Separate Bones"; - ot->idname = "ARMATURE_OT_separate"; - ot->description = "Isolate selected bones into a separate armature"; - - /* callbacks */ - ot->invoke = WM_operator_confirm; - ot->exec = separate_armature_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* **************** END tools on Editmode Armature **************** */ -/* **************** PoseMode & EditMode *************************** */ - -/* only for opengl selection indices */ -Bone *get_indexed_bone(Object *ob, int index) -{ - bPoseChannel *pchan; - if (ob->pose == NULL) return NULL; - index >>= 16; // bone selection codes use left 2 bytes - - pchan = BLI_findlink(&ob->pose->chanbase, index); - return pchan ? pchan->bone : NULL; -} - -/* See if there are any selected bones in this buffer */ -/* only bones from base are checked on */ -static void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel) -{ - Object *obedit = scene->obedit; // XXX get from context - Bone *bone; - EditBone *ebone; - void *firstunSel = NULL, *firstSel = NULL, *data; - unsigned int hitresult; - short i, takeNext = 0, sel; - - for (i = 0; i < hits; i++) { - hitresult = buffer[3 + (i * 4)]; - - if (!(hitresult & BONESEL_NOSEL)) { // -1 - if (hitresult & BONESEL_ANY) { // to avoid including objects in selection - - hitresult &= ~(BONESEL_ANY); - /* Determine what the current bone is */ - if (obedit == NULL || base->object != obedit) { - /* no singular posemode, so check for correct object */ - if (base->selcol == (hitresult & 0xFFFF)) { - bone = get_indexed_bone(base->object, hitresult); - - if (findunsel) - sel = (bone->flag & BONE_SELECTED); - else - sel = !(bone->flag & BONE_SELECTED); - - data = bone; - } - else { - data = NULL; - sel = 0; - } - } - else { - bArmature *arm = obedit->data; - - ebone = BLI_findlink(arm->edbo, hitresult); - if (findunsel) - sel = (ebone->flag & BONE_SELECTED); - else - sel = !(ebone->flag & BONE_SELECTED); - - data = ebone; - } - - if (data) { - if (sel) { - if (!firstSel) firstSel = data; - takeNext = 1; - } - else { - if (!firstunSel) - firstunSel = data; - if (takeNext) - return data; - } - } - } - } - } - - if (firstunSel) - return firstunSel; - else - return firstSel; -} - - - -/* used by posemode as well editmode */ -/* only checks scene->basact! */ -/* x and y are mouse coords (area space) */ -static void *get_nearest_bone(bContext *C, short findunsel, int x, int y) -{ - ViewContext vc; - rcti rect; - unsigned int buffer[MAXPICKBUF]; - short hits; - - view3d_set_viewcontext(C, &vc); - - // rect.xmin = ... mouseco! - rect.xmin = rect.xmax = x; - rect.ymin = rect.ymax = y; - - glInitNames(); - hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect); - - if (hits > 0) - return get_bone_from_selectbuffer(vc.scene, vc.scene->basact, buffer, hits, findunsel); - - return NULL; -} - -/* Get the first available child of an editbone */ -static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility) -{ - EditBone *curbone, *chbone = NULL; - - for (curbone = arm->edbo->first; curbone; curbone = curbone->next) { - if (curbone->parent == pabone) { - if (use_visibility) { - if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) { - chbone = curbone; - } - } - else - chbone = curbone; - } - } - - return chbone; -} - -/* **************** END PoseMode & EditMode *************************** */ -/* **************** Posemode stuff ********************** */ - - -static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend) -{ - Bone *curBone; - - /* stop when unconnected child is encontered, or when unselectable bone is encountered */ - if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE)) - return; - - /* XXX old cruft! use notifiers instead */ - //select_actionchannel_by_name (ob->action, bone->name, !(shift)); - - if (extend) - bone->flag &= ~BONE_SELECTED; - else - bone->flag |= BONE_SELECTED; - - for (curBone = bone->childbase.first; curBone; curBone = curBone->next) - selectconnected_posebonechildren(ob, curBone, extend); -} - -/* within active object context */ -/* previously known as "selectconnected_posearmature" */ -static int pose_select_connected_invoke(bContext *C, wmOperator *op, wmEvent *event) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (bArmature *)ob->data; - Bone *bone, *curBone, *next = NULL; - int extend = RNA_boolean_get(op->ptr, "extend"); - - view3d_operator_needs_opengl(C); - - if (extend) - bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]); - else - bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]); - - if (!bone) - return OPERATOR_CANCELLED; - - /* Select parents */ - for (curBone = bone; curBone; curBone = next) { - /* ignore bone if cannot be selected */ - if ((curBone->flag & BONE_UNSELECTABLE) == 0) { - // XXX old cruft! use notifiers instead - //select_actionchannel_by_name (ob->action, curBone->name, !(shift)); - - if (extend) - curBone->flag &= ~BONE_SELECTED; - else - curBone->flag |= BONE_SELECTED; - - if (curBone->flag & BONE_CONNECTED) - next = curBone->parent; - else - next = NULL; - } - else - next = NULL; - } - - /* Select children */ - for (curBone = bone->childbase.first; curBone; curBone = next) - selectconnected_posebonechildren(ob, curBone, extend); - - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - return OPERATOR_FINISHED; -} - -static int pose_select_linked_poll(bContext *C) -{ - return (ED_operator_view3d_active(C) && ED_operator_posemode(C)); -} - -void POSE_OT_select_linked(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Connected"; - ot->idname = "POSE_OT_select_linked"; - ot->description = "Select bones related to selected ones by parent/child relationships"; - - /* api callbacks */ - ot->exec = NULL; - ot->invoke = pose_select_connected_invoke; - ot->poll = pose_select_linked_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first"); -} - -/* **************** END Posemode stuff ********************** */ -/* **************** EditMode stuff ********************** */ - -/* called in space.c */ -/* previously "selectconnected_armature" */ -static int armature_select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event) -{ - bArmature *arm; - EditBone *bone, *curBone, *next; - int extend = RNA_boolean_get(op->ptr, "extend"); - Object *obedit = CTX_data_edit_object(C); - arm = obedit->data; - - view3d_operator_needs_opengl(C); - - if (extend) - bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]); - else - bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]); - - if (!bone) - return OPERATOR_CANCELLED; - - /* Select parents */ - for (curBone = bone; curBone; curBone = next) { - if ((curBone->flag & BONE_UNSELECTABLE) == 0) { - if (extend) { - curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - - if (curBone->flag & BONE_CONNECTED) - next = curBone->parent; - else - next = NULL; - } - - /* Select children */ - while (bone) { - for (curBone = arm->edbo->first; curBone; curBone = next) { - next = curBone->next; - if ((curBone->parent == bone) && (curBone->flag & BONE_UNSELECTABLE) == 0) { - if (curBone->flag & BONE_CONNECTED) { - if (extend) - curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - else - curBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - bone = curBone; - break; - } - else { - bone = NULL; - break; - } - } - } - if (!curBone) - bone = NULL; - } - - ED_armature_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -static int armature_select_linked_poll(bContext *C) -{ - return (ED_operator_view3d_active(C) && ED_operator_editarmature(C) ); -} - -void ARMATURE_OT_select_linked(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Connected"; - ot->idname = "ARMATURE_OT_select_linked"; - ot->description = "Select bones related to selected ones by parent/child relationships"; - - /* api callbacks */ - ot->exec = NULL; - ot->invoke = armature_select_linked_invoke; - ot->poll = armature_select_linked_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first"); -} - -/* does bones and points */ -/* note that BONE ROOT only gets drawn for root bones (or without IK) */ -static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2], - ListBase *edbo, int findunsel, int *selmask) -{ - EditBone *ebone; - rcti rect; - unsigned int buffer[MAXPICKBUF]; - unsigned int hitresult, besthitresult = BONESEL_NOSEL; - int i, mindep = 4; - short hits; - - glInitNames(); - - rect.xmin = mval[0] - 5; - rect.xmax = mval[0] + 5; - rect.ymin = mval[1] - 5; - rect.ymax = mval[1] + 5; - - hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect); - if (hits == 0) { - rect.xmin = mval[0] - 12; - rect.xmax = mval[0] + 12; - rect.ymin = mval[1] - 12; - rect.ymax = mval[1] + 12; - hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect); - } - /* See if there are any selected bones in this group */ - if (hits > 0) { - - if (hits == 1) { - if (!(buffer[3] & BONESEL_NOSEL)) - besthitresult = buffer[3]; - } - else { - for (i = 0; i < hits; i++) { - hitresult = buffer[3 + (i * 4)]; - if (!(hitresult & BONESEL_NOSEL)) { - int dep; - - ebone = BLI_findlink(edbo, hitresult & ~BONESEL_ANY); - - /* clicks on bone points get advantage */ - if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) { - /* but also the unselected one */ - if (findunsel) { - if ( (hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) - dep = 1; - else if ( (hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) - dep = 1; - else - dep = 2; - } - else dep = 2; - } - else { - /* bone found */ - if (findunsel) { - if ((ebone->flag & BONE_SELECTED) == 0) - dep = 2; - else - dep = 3; - } - else dep = 3; - } - if (dep < mindep) { - mindep = dep; - besthitresult = hitresult; - } - } - } - } - - if (!(besthitresult & BONESEL_NOSEL)) { - - ebone = BLI_findlink(edbo, besthitresult & ~BONESEL_ANY); - - *selmask = 0; - if (besthitresult & BONESEL_ROOT) - *selmask |= BONE_ROOTSEL; - if (besthitresult & BONESEL_TIP) - *selmask |= BONE_TIPSEL; - if (besthitresult & BONESEL_BONE) - *selmask |= BONE_SELECTED; - return ebone; - } - } - *selmask = 0; - return NULL; -} - -/* previously delete_armature */ -/* only editmode! */ -static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bArmature *arm; - EditBone *curBone, *ebone_next; - bConstraint *con; - Object *obedit = CTX_data_edit_object(C); // XXX get from context - arm = obedit->data; - - /* cancel if nothing selected */ - if (CTX_DATA_COUNT(C, selected_bones) == 0) - return OPERATOR_CANCELLED; - - armature_select_mirrored(arm); - - /* First erase any associated pose channel */ - if (obedit->pose) { - bPoseChannel *pchan, *pchan_next; - for (pchan = obedit->pose->chanbase.first; pchan; pchan = pchan_next) { - pchan_next = pchan->next; - curBone = editbone_name_exists(arm->edbo, pchan->name); - - if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) { - BKE_pose_channel_free(pchan); - BKE_pose_channels_hash_free(obedit->pose); - BLI_freelinkN(&obedit->pose->chanbase, pchan); - } - else { - for (con = pchan->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == obedit) { - if (ct->subtarget[0]) { - curBone = editbone_name_exists(arm->edbo, ct->subtarget); - if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) { - con->flag |= CONSTRAINT_DISABLE; - ct->subtarget[0] = 0; - } - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - } - } - - - for (curBone = arm->edbo->first; curBone; curBone = ebone_next) { - ebone_next = curBone->next; - if (arm->layer & curBone->layer) { - if (curBone->flag & BONE_SELECTED) { - if (curBone == arm->act_edbone) arm->act_edbone = NULL; - ED_armature_edit_bone_remove(arm, curBone); - } - } - } - - - ED_armature_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_delete(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Delete Selected Bone(s)"; - ot->idname = "ARMATURE_OT_delete"; - ot->description = "Remove selected bones from the armature"; - - /* api callbacks */ - ot->invoke = WM_operator_confirm; - ot->exec = armature_delete_selected_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* toggle==0: deselect - * toggle==1: swap (based on test) - * toggle==2: swap (no test), CURRENTLY UNUSED - */ -void ED_armature_deselect_all(Object *obedit, int toggle) -{ - bArmature *arm = obedit->data; - EditBone *eBone; - int sel = 1; - - if (toggle == 1) { - /* Determine if there are any selected bones - * and therefore whether we are selecting or deselecting */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - // if (arm->layer & eBone->layer) { - if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { - sel = 0; - break; - } - // } - } - } - else sel = toggle; - - /* Set the flags */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - if (sel == 2) { - /* invert selection of bone */ - if (EBONE_VISIBLE(arm, eBone)) { - eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (arm->act_edbone == eBone) - arm->act_edbone = NULL; - } - } - else if (sel == 1) { - /* select bone */ - if (EBONE_VISIBLE(arm, eBone)) { - eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (eBone->parent) - eBone->parent->flag |= (BONE_TIPSEL); - } - } - else { - /* deselect bone */ - eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (arm->act_edbone == eBone) - arm->act_edbone = NULL; - } - } - - ED_armature_sync_selection(arm->edbo); -} - -void ED_armature_deselect_all_visible(Object *obedit) -{ - bArmature *arm = obedit->data; - EditBone *ebone; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - /* first and foremost, bone must be visible and selected */ - if (EBONE_SELECTABLE(arm, ebone)) { - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - - ED_armature_sync_selection(arm->edbo); -} - -/* accounts for connected parents */ -static int ebone_select_flag(EditBone *ebone) -{ - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - return ((ebone->parent->flag & BONE_TIPSEL) ? BONE_ROOTSEL : 0) | (ebone->flag & (BONE_SELECTED | BONE_TIPSEL)); - } - else { - return ebone->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); - } -} - -/* context: editmode armature in view3d */ -int mouse_armature(bContext *C, const int mval[2], int extend, int deselect, int toggle) -{ - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - ViewContext vc; - EditBone *nearBone = NULL; - int selmask; - - view3d_set_viewcontext(C, &vc); - - BIF_sk_selectStroke(C, mval, extend); - - nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask); - if (nearBone) { - - if (!extend && !deselect && !toggle) - ED_armature_deselect_all(obedit, 0); - - /* by definition the non-root connected bones have no root point drawn, - * so a root selection needs to be delivered to the parent tip */ - - if (selmask & BONE_SELECTED) { - if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) { - /* click in a chain */ - if (extend) { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; - } - else if (deselect) { - /* deselect this bone */ - nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); - /* only deselect parent tip if it is not selected */ - if (!(nearBone->parent->flag & BONE_SELECTED)) - nearBone->parent->flag &= ~BONE_TIPSEL; - } - else if (toggle) { - /* hold shift inverts this bone's selection */ - if (nearBone->flag & BONE_SELECTED) { - /* deselect this bone */ - nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED); - /* only deselect parent tip if it is not selected */ - if (!(nearBone->parent->flag & BONE_SELECTED)) - nearBone->parent->flag &= ~BONE_TIPSEL; - } - else { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; - } - } - else { - /* select this bone */ - nearBone->flag |= BONE_TIPSEL; - nearBone->parent->flag |= BONE_TIPSEL; - } - } - else { - if (extend) { - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - else if (deselect) { - nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); - } - else if (toggle) { - /* hold shift inverts this bone's selection */ - if (nearBone->flag & BONE_SELECTED) - nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL); - else - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - else - nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL); - } - } - else { - if (extend) - nearBone->flag |= selmask; - else if (deselect) - nearBone->flag &= ~selmask; - else if (toggle && (nearBone->flag & selmask)) - nearBone->flag &= ~selmask; - else - nearBone->flag |= selmask; - } - - ED_armature_sync_selection(arm->edbo); - - if (nearBone) { - /* then now check for active status */ - if (ebone_select_flag(nearBone)) { - arm->act_edbone = nearBone; - } - } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); - return 1; - } - - return 0; -} - -void ED_armature_edit_free(struct Object *ob) -{ - bArmature *arm = ob->data; - EditBone *eBone; - - /* Clear the editbones list */ - if (arm->edbo) { - if (arm->edbo->first) { - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - if (eBone->prop) { - IDP_FreeProperty(eBone->prop); - MEM_freeN(eBone->prop); - } - } - - BLI_freelistN(arm->edbo); - } - MEM_freeN(arm->edbo); - arm->edbo = NULL; - } -} - -/* Put armature in EditMode */ -void ED_armature_to_edit(Object *ob) -{ - bArmature *arm = ob->data; - - ED_armature_edit_free(ob); - arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature"); - arm->act_edbone = make_boneList(arm->edbo, &arm->bonebase, NULL, arm->act_bone); - arm->act_bone = NULL; - -// BIF_freeTemplates(); /* force template update when entering editmode */ -} - - -/* adjust bone roll to align Z axis with vector - * vec is in local space and is normalized - */ - -float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const short axis_only) -{ - float mat[3][3], nor[3]; - - sub_v3_v3v3(nor, bone->tail, bone->head); - vec_roll_to_mat3(nor, 0.0f, mat); - - /* check the bone isn't aligned with the axis */ - if (!is_zero_v3(align_axis) && angle_v3v3(align_axis, mat[2]) > FLT_EPSILON) { - float vec[3], align_axis_proj[3], roll; - - /* project the new_up_axis along the normal */ - project_v3_v3v3(vec, align_axis, nor); - sub_v3_v3v3(align_axis_proj, align_axis, vec); - - if (axis_only) { - if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI / 2.0)) { - negate_v3(align_axis_proj); - } - } - - roll = angle_v3v3(align_axis_proj, mat[2]); - - cross_v3_v3v3(vec, mat[2], align_axis_proj); - - if (dot_v3v3(vec, nor) < 0) { - roll = -roll; - } - - return roll; - } - - return 0.0f; -} - - -static EnumPropertyItem prop_calc_roll_types[] = { - {0, "X", 0, "X Axis", ""}, - {1, "Y", 0, "Y Axis", ""}, - {2, "Z", 0, "Z Axis", ""}, - {5, "ACTIVE", 0, "Active Bone", ""}, - {6, "VIEW", 0, "View Axis", ""}, - {7, "CURSOR", 0, "Cursor", ""}, - {0, NULL, 0, NULL, NULL} -}; - - -static int armature_calc_roll_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_edit_object(C); - const short type = RNA_enum_get(op->ptr, "type"); - const short axis_only = RNA_boolean_get(op->ptr, "axis_only"); - const short axis_flip = RNA_boolean_get(op->ptr, "axis_flip"); - - float imat[3][3]; - - bArmature *arm = ob->data; - EditBone *ebone; - - copy_m3_m4(imat, ob->obmat); - invert_m3(imat); - - if (type == 7) { /* Cursor */ - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); /* can be NULL */ - float cursor_local[3]; - const float *cursor = give_cursor(scene, v3d); - - - copy_v3_v3(cursor_local, cursor); - mul_m3_v3(imat, cursor_local); - - /* cursor */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { - float cursor_rel[3]; - sub_v3_v3v3(cursor_rel, cursor_local, ebone->head); - if (axis_flip) negate_v3(cursor_rel); - ebone->roll = ED_rollBoneToVector(ebone, cursor_rel, axis_only); - } - } - } - else { - float vec[3] = {0.0f, 0.0f, 0.0f}; - if (type == 6) { /* View */ - RegionView3D *rv3d = CTX_wm_region_view3d(C); - if (rv3d == NULL) { - BKE_report(op->reports, RPT_ERROR, "No region view3d available"); - return OPERATOR_CANCELLED; - } - - copy_v3_v3(vec, rv3d->viewinv[2]); - mul_m3_v3(imat, vec); - } - else if (type == 5) { - float mat[3][3], nor[3]; - ebone = (EditBone *)arm->act_edbone; - if (ebone == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active bone set"); - return OPERATOR_CANCELLED; - } - - sub_v3_v3v3(nor, ebone->tail, ebone->head); - vec_roll_to_mat3(nor, ebone->roll, mat); - copy_v3_v3(vec, mat[2]); - } - else { /* Axis */ - assert(type >= 0 && type <= 5); - if (type < 3) vec[type] = 1.0f; - else vec[type - 2] = -1.0f; - mul_m3_v3(imat, vec); - } - - if (axis_flip) negate_v3(vec); - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) { - /* roll func is a callback which assumes that all is well */ - ebone->roll = ED_rollBoneToVector(ebone, vec, axis_only); - } - } - } - - if (arm->flag & ARM_MIRROR_EDIT) { - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if ((EBONE_VISIBLE(arm, ebone) && EBONE_EDITABLE(ebone)) == 0) { - EditBone *ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, ebone); - if (ebone_mirr && (EBONE_VISIBLE(arm, ebone_mirr) && EBONE_EDITABLE(ebone_mirr))) { - ebone->roll = -ebone_mirr->roll; - } - } - } - } - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_calculate_roll(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Recalculate Roll"; - ot->idname = "ARMATURE_OT_calculate_roll"; - ot->description = "Automatically fix alignment of select bones' axes"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_calc_roll_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_calc_roll_types, 0, "Type", ""); - RNA_def_boolean(ot->srna, "axis_flip", 0, "Flip Axis", "Negate the alignment axis"); - RNA_def_boolean(ot->srna, "axis_only", 0, "Shortest Rotation", "Ignore the axis direction, use the shortest rotation to align"); -} - -/* **************** undo for armatures ************** */ - -typedef struct UndoArmature { - EditBone *act_edbone; - ListBase lb; -} UndoArmature; - -static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data)) -{ - UndoArmature *uarm = uarmv; - bArmature *arm = armv; - EditBone *ebo, *newebo; - - BLI_freelistN(arm->edbo); - - /* copy */ - for (ebo = uarm->lb.first; ebo; ebo = ebo->next) { - newebo = MEM_dupallocN(ebo); - ebo->temp = newebo; - BLI_addtail(arm->edbo, newebo); - } - - /* active bone */ - if (uarm->act_edbone) { - ebo = uarm->act_edbone; - arm->act_edbone = ebo->temp; - } - else - arm->act_edbone = NULL; - - /* set pointers */ - for (newebo = arm->edbo->first; newebo; newebo = newebo->next) { - if (newebo->parent) newebo->parent = newebo->parent->temp; - } - /* be sure they don't hang ever */ - for (newebo = arm->edbo->first; newebo; newebo = newebo->next) { - newebo->temp = NULL; - } -} - -static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata)) -{ - bArmature *arm = armv; - UndoArmature *uarm; - EditBone *ebo, *newebo; - - uarm = MEM_callocN(sizeof(UndoArmature), "listbase undo"); - - /* copy */ - for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { - newebo = MEM_dupallocN(ebo); - ebo->temp = newebo; - BLI_addtail(&uarm->lb, newebo); - } - - /* active bone */ - if (arm->act_edbone) { - ebo = arm->act_edbone; - uarm->act_edbone = ebo->temp; - } - - /* set pointers */ - for (newebo = uarm->lb.first; newebo; newebo = newebo->next) { - if (newebo->parent) newebo->parent = newebo->parent->temp; - } - - return uarm; -} - -static void free_undoBones(void *uarmv) -{ - UndoArmature *uarm = uarmv; - - BLI_freelistN(&uarm->lb); - MEM_freeN(uarm); -} - -static void *get_armature_edit(bContext *C) -{ - Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_ARMATURE) { - return obedit->data; - } - return NULL; -} - -/* and this is all the undo system needs to know */ -void undo_push_armature(bContext *C, const char *name) -{ - // XXX solve getdata() - undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL); -} - - - -/* **************** END EditMode stuff ********************** */ -/* *************** Adding stuff in editmode *************** */ - -/* default bone add, returns it selected, but without tail set */ -/* XXX should be used everywhere, now it mallocs bones still locally in functions */ -EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name) -{ - EditBone *bone = MEM_callocN(sizeof(EditBone), "eBone"); - - BLI_strncpy(bone->name, name, sizeof(bone->name)); - unique_editbone_name(arm->edbo, bone->name, NULL); - - BLI_addtail(arm->edbo, bone); - - bone->flag |= BONE_TIPSEL; - bone->weight = 1.0f; - bone->dist = 0.25f; - bone->xwidth = 0.1f; - bone->zwidth = 0.1f; - bone->ease1 = 1.0f; - bone->ease2 = 1.0f; - bone->rad_head = 0.10f; - bone->rad_tail = 0.05f; - bone->segments = 1; - bone->layer = arm->layer; - - return bone; -} - -/* v3d and rv3d are allowed to be NULL */ -void add_primitive_bone(Scene *scene, View3D *v3d, RegionView3D *rv3d) -{ - Object *obedit = scene->obedit; // XXX get from context - bArmature *arm = obedit->data; - float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3]; - EditBone *bone; - - /* Get inverse point for head and orientation for tail */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d)); - - if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) - copy_m3_m4(obmat, rv3d->viewmat); - else unit_m3(obmat); - - copy_m3_m4(viewmat, obedit->obmat); - mul_m3_m3m3(totmat, obmat, viewmat); - invert_m3_m3(imat, totmat); - - ED_armature_deselect_all(obedit, 0); - - /* Create a bone */ - bone = ED_armature_edit_bone_add(arm, "Bone"); - - arm->act_edbone = bone; - - copy_v3_v3(bone->head, curs); - - if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) - add_v3_v3v3(bone->tail, bone->head, imat[1]); // bone with unit length 1 - else - add_v3_v3v3(bone->tail, bone->head, imat[2]); // bone with unit length 1, pointing up Z -} - - -/* previously addvert_armature */ -/* the ctrl-click method */ -static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op)) -{ - View3D *v3d; - bArmature *arm; - EditBone *ebone, *newbone, *flipbone; - float mat[3][3], imat[3][3]; - const float *curs; - int a, to_root = 0; - Object *obedit; - Scene *scene; - - scene = CTX_data_scene(C); - v3d = CTX_wm_view3d(C); - obedit = CTX_data_edit_object(C); - arm = obedit->data; - - /* find the active or selected bone */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & BONE_TIPSEL || arm->act_edbone == ebone) - break; - } - } - - if (ebone == NULL) { - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & BONE_ROOTSEL || arm->act_edbone == ebone) - break; - } - } - if (ebone == NULL) - return OPERATOR_CANCELLED; - - to_root = 1; - } - - ED_armature_deselect_all(obedit, 0); - - /* we re-use code for mirror editing... */ - flipbone = NULL; - if (arm->flag & ARM_MIRROR_EDIT) - flipbone = ED_armature_bone_get_mirrored(arm->edbo, ebone); - - for (a = 0; a < 2; a++) { - if (a == 1) { - if (flipbone == NULL) - break; - else { - SWAP(EditBone *, flipbone, ebone); - } - } - - newbone = ED_armature_edit_bone_add(arm, ebone->name); - arm->act_edbone = newbone; - - if (to_root) { - copy_v3_v3(newbone->head, ebone->head); - newbone->rad_head = ebone->rad_tail; - newbone->parent = ebone->parent; - } - else { - copy_v3_v3(newbone->head, ebone->tail); - newbone->rad_head = ebone->rad_tail; - newbone->parent = ebone; - newbone->flag |= BONE_CONNECTED; - } - - curs = give_cursor(scene, v3d); - copy_v3_v3(newbone->tail, curs); - sub_v3_v3v3(newbone->tail, newbone->tail, obedit->obmat[3]); - - if (a == 1) - newbone->tail[0] = -newbone->tail[0]; - - copy_m3_m4(mat, obedit->obmat); - invert_m3_m3(imat, mat); - mul_m3_v3(imat, newbone->tail); - - newbone->length = len_v3v3(newbone->head, newbone->tail); - newbone->rad_tail = newbone->length * 0.05f; - newbone->dist = newbone->length * 0.25f; - - } - - ED_armature_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -static int armature_click_extrude_invoke(bContext *C, wmOperator *op, wmEvent *event) -{ - /* TODO most of this code is copied from set3dcursor_invoke, - * it would be better to reuse code in set3dcursor_invoke */ - - /* temporarily change 3d cursor position */ - Scene *scene; - ARegion *ar; - View3D *v3d; - float *fp, tvec[3], oldcurs[3], mval_f[2]; - int retv; - - scene = CTX_data_scene(C); - ar = CTX_wm_region(C); - v3d = CTX_wm_view3d(C); - - fp = give_cursor(scene, v3d); - - copy_v3_v3(oldcurs, fp); - - VECCOPY2D(mval_f, event->mval); - ED_view3d_win_to_3d(ar, fp, mval_f, tvec); - copy_v3_v3(fp, tvec); - - /* extrude to the where new cursor is and store the operation result */ - retv = armature_click_extrude_exec(C, op); - - /* restore previous 3d cursor position */ - copy_v3_v3(fp, oldcurs); - - return retv; -} - -void ARMATURE_OT_click_extrude(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Click-Extrude"; - ot->idname = "ARMATURE_OT_click_extrude"; - ot->description = "Create a new bone going from the last selected joint to the mouse position"; - - /* api callbacks */ - ot->invoke = armature_click_extrude_invoke; - ot->exec = armature_click_extrude_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ -} - -/* adds an EditBone between the nominated locations (should be in the right space) */ -static EditBone *add_points_bone(Object *obedit, float head[], float tail[]) -{ - EditBone *ebo; - - ebo = ED_armature_edit_bone_add(obedit->data, "Bone"); - - copy_v3_v3(ebo->head, head); - copy_v3_v3(ebo->tail, tail); - - return ebo; -} - - -static EditBone *get_named_editbone(ListBase *edbo, char *name) -{ - EditBone *eBone; - - if (name) { - for (eBone = edbo->first; eBone; eBone = eBone->next) { - if (!strcmp(name, eBone->name)) - return eBone; - } - } - - return NULL; -} - -/* Call this before doing any duplications - * */ -void preEditBoneDuplicate(ListBase *editbones) -{ - EditBone *eBone; - - /* clear temp */ - for (eBone = editbones->first; eBone; eBone = eBone->next) { - eBone->temp = NULL; - } -} - -/* - * Note: When duplicating cross objects, editbones here is the list of bones - * from the SOURCE object but ob is the DESTINATION object - * */ -void updateDuplicateSubtargetObjects(EditBone *dupBone, ListBase *editbones, Object *src_ob, Object *dst_ob) -{ - /* If an edit bone has been duplicated, lets - * update it's constraints if the subtarget - * they point to has also been duplicated - */ - EditBone *oldtarget, *newtarget; - bPoseChannel *pchan; - bConstraint *curcon; - ListBase *conlist; - - if ( (pchan = BKE_pose_channel_verify(dst_ob->pose, dupBone->name)) ) { - if ( (conlist = &pchan->constraints) ) { - for (curcon = conlist->first; curcon; curcon = curcon->next) { - /* does this constraint have a subtarget in - * this armature? - */ - bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(curcon); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(curcon, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if ((ct->tar == src_ob) && (ct->subtarget[0])) { - ct->tar = dst_ob; /* update target */ - oldtarget = get_named_editbone(editbones, ct->subtarget); - if (oldtarget) { - /* was the subtarget bone duplicated too? If - * so, update the constraint to point at the - * duplicate of the old subtarget. - */ - if (oldtarget->temp) { - newtarget = (EditBone *) oldtarget->temp; - BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget)); - } - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(curcon, &targets, 0); - } - } - } - } -} - -void updateDuplicateSubtarget(EditBone *dupBone, ListBase *editbones, Object *ob) -{ - updateDuplicateSubtargetObjects(dupBone, editbones, ob, ob); -} - - -EditBone *duplicateEditBoneObjects(EditBone *curBone, const char *name, ListBase *editbones, - Object *src_ob, Object *dst_ob) -{ - EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone"); - - /* Copy data from old bone to new bone */ - memcpy(eBone, curBone, sizeof(EditBone)); - - curBone->temp = eBone; - eBone->temp = curBone; - - if (name != NULL) { - BLI_strncpy(eBone->name, name, sizeof(eBone->name)); - } - - unique_editbone_name(editbones, eBone->name, NULL); - BLI_addtail(editbones, eBone); - - /* copy the ID property */ - if (curBone->prop) - eBone->prop = IDP_CopyProperty(curBone->prop); - - /* Lets duplicate the list of constraints that the - * current bone has. - */ - if (src_ob->pose) { - bPoseChannel *chanold, *channew; - - chanold = BKE_pose_channel_verify(src_ob->pose, curBone->name); - if (chanold) { - /* WARNING: this creates a new posechannel, but there will not be an attached bone - * yet as the new bones created here are still 'EditBones' not 'Bones'. - */ - channew = BKE_pose_channel_verify(dst_ob->pose, eBone->name); - - if (channew) { - BKE_pose_channel_copy_data(channew, chanold); - } - } - } - - return eBone; -} - -EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob) -{ - return duplicateEditBoneObjects(curBone, name, editbones, ob, ob); -} - -/* previously adduplicate_armature */ -static int armature_duplicate_selected_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bArmature *arm; - EditBone *eBone = NULL; - EditBone *curBone; - EditBone *firstDup = NULL; /* The beginning of the duplicated bones in the edbo list */ - - Object *obedit = CTX_data_edit_object(C); - arm = obedit->data; - - /* cancel if nothing selected */ - if (CTX_DATA_COUNT(C, selected_bones) == 0) - return OPERATOR_CANCELLED; - - ED_armature_sync_selection(arm->edbo); // XXX why is this needed? - - preEditBoneDuplicate(arm->edbo); - - /* Select mirrored bones */ - if (arm->flag & ARM_MIRROR_EDIT) { - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (EBONE_VISIBLE(arm, curBone)) { - if (curBone->flag & BONE_SELECTED) { - eBone = ED_armature_bone_get_mirrored(arm->edbo, curBone); - if (eBone) - eBone->flag |= BONE_SELECTED; - } - } - } - } - - - /* Find the selected bones and duplicate them as needed */ - for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { - if (EBONE_VISIBLE(arm, curBone)) { - if (curBone->flag & BONE_SELECTED) { - - eBone = duplicateEditBone(curBone, curBone->name, arm->edbo, obedit); - - if (!firstDup) - firstDup = eBone; - - } - } - } - - /* Run though the list and fix the pointers */ - for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { - if (EBONE_VISIBLE(arm, curBone)) { - if (curBone->flag & BONE_SELECTED) { - eBone = (EditBone *) curBone->temp; - - if (!curBone->parent) { - /* If this bone has no parent, - * Set the duplicate->parent to NULL - */ - eBone->parent = NULL; - } - else if (curBone->parent->temp) { - /* If this bone has a parent that was duplicated, - * Set the duplicate->parent to the curBone->parent->temp - */ - eBone->parent = (EditBone *)curBone->parent->temp; - } - else { - /* If this bone has a parent that IS not selected, - * Set the duplicate->parent to the curBone->parent - */ - eBone->parent = (EditBone *) curBone->parent; - eBone->flag &= ~BONE_CONNECTED; - } - - /* Lets try to fix any constraint subtargets that might - * have been duplicated - */ - updateDuplicateSubtarget(eBone, arm->edbo, obedit); - } - } - } - - /* correct the active bone */ - if (arm->act_edbone) { - eBone = arm->act_edbone; - if (eBone->temp) - arm->act_edbone = eBone->temp; - } - - /* Deselect the old bones and select the new ones */ - for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { - if (EBONE_VISIBLE(arm, curBone)) - curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - - ED_armature_validate_active(arm); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - - -void ARMATURE_OT_duplicate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Duplicate Selected Bone(s)"; - ot->idname = "ARMATURE_OT_duplicate"; - ot->description = "Make copies of the selected bones within the same armature"; - - /* api callbacks */ - ot->exec = armature_duplicate_selected_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - - -/* *************** END Adding stuff in editmode *************** */ -/* ************** Add/Remove stuff in editmode **************** */ - -/* temporary data-structure for merge/fill bones */ -typedef struct EditBonePoint { - struct EditBonePoint *next, *prev; - - EditBone *head_owner; /* EditBone which uses this point as a 'head' point */ - EditBone *tail_owner; /* EditBone which uses this point as a 'tail' point */ - - float vec[3]; /* the actual location of the point in local/EditMode space */ -} EditBonePoint; - -/* find chain-tips (i.e. bones without children) */ -static void chains_find_tips(ListBase *edbo, ListBase *list) -{ - EditBone *curBone, *ebo; - LinkData *ld; - - /* note: this is potentially very slow ... there's got to be a better way */ - for (curBone = edbo->first; curBone; curBone = curBone->next) { - short stop = 0; - - /* is this bone contained within any existing chain? (skip if so) */ - for (ld = list->first; ld; ld = ld->next) { - for (ebo = ld->data; ebo; ebo = ebo->parent) { - if (ebo == curBone) { - stop = 1; - break; - } - } - - if (stop) break; - } - /* skip current bone if it is part of an existing chain */ - if (stop) continue; - - /* is any existing chain part of the chain formed by this bone? */ - stop = 0; - for (ebo = curBone->parent; ebo; ebo = ebo->parent) { - for (ld = list->first; ld; ld = ld->next) { - if (ld->data == ebo) { - ld->data = curBone; - stop = 1; - break; - } - } - - if (stop) break; - } - /* current bone has already been added to a chain? */ - if (stop) continue; - - /* add current bone to a new chain */ - ld = MEM_callocN(sizeof(LinkData), "BoneChain"); - ld->data = curBone; - BLI_addtail(list, ld); - } -} - -/* --------------------- */ - -static void fill_add_joint(EditBone *ebo, short eb_tail, ListBase *points) -{ - EditBonePoint *ebp; - float vec[3]; - short found = 0; - - if (eb_tail) { - copy_v3_v3(vec, ebo->tail); - } - else { - copy_v3_v3(vec, ebo->head); - } - - for (ebp = points->first; ebp; ebp = ebp->next) { - if (equals_v3v3(ebp->vec, vec)) { - if (eb_tail) { - if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) { - /* so this bone's tail owner is this bone */ - ebp->tail_owner = ebo; - found = 1; - break; - } - } - else { - if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) { - /* so this bone's head owner is this bone */ - ebp->head_owner = ebo; - found = 1; - break; - } - } - } - } - - /* allocate a new point if no existing point was related */ - if (found == 0) { - ebp = MEM_callocN(sizeof(EditBonePoint), "EditBonePoint"); - - if (eb_tail) { - copy_v3_v3(ebp->vec, ebo->tail); - ebp->tail_owner = ebo; - } - else { - copy_v3_v3(ebp->vec, ebo->head); - ebp->head_owner = ebo; - } - - BLI_addtail(points, ebp); - } -} - -/* bone adding between selected joints */ -static int armature_fill_bones_exec(bContext *C, wmOperator *op) -{ - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = (obedit) ? obedit->data : NULL; - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - ListBase points = {NULL, NULL}; - int count; - - /* sanity checks */ - if (ELEM(NULL, obedit, arm)) - return OPERATOR_CANCELLED; - - /* loop over all bones, and only consider if visible */ - CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) - { - if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL)) - fill_add_joint(ebone, 0, &points); - if (ebone->flag & BONE_TIPSEL) - fill_add_joint(ebone, 1, &points); - } - CTX_DATA_END; - - /* the number of joints determines how we fill: - * 1) between joint and cursor (joint=head, cursor=tail) - * 2) between the two joints (order is dependent on active-bone/hierachy) - * 3+) error (a smarter method involving finding chains needs to be worked out - */ - count = BLI_countlist(&points); - - if (count == 0) { - BKE_report(op->reports, RPT_ERROR, "No joints selected"); - return OPERATOR_CANCELLED; - } - else if (count == 1) { - EditBonePoint *ebp; - float curs[3]; - - /* Get Points - selected joint */ - ebp = (EditBonePoint *)points.first; - - /* Get points - cursor (tail) */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d)); - - /* Create a bone */ - /* newbone = */ add_points_bone(obedit, ebp->vec, curs); - } - else if (count == 2) { - EditBonePoint *ebp, *ebp2; - float head[3], tail[3]; - short headtail = 0; - - /* check that the points don't belong to the same bone */ - ebp = (EditBonePoint *)points.first; - ebp2 = ebp->next; - - if ((ebp->head_owner == ebp2->tail_owner) && (ebp->head_owner != NULL)) { - BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); - BLI_freelistN(&points); - return OPERATOR_CANCELLED; - } - if ((ebp->tail_owner == ebp2->head_owner) && (ebp->tail_owner != NULL)) { - BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); - BLI_freelistN(&points); - return OPERATOR_CANCELLED; - } - - /* find which one should be the 'head' */ - if ((ebp->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) { - /* rule: whichever one is closer to 3d-cursor */ - float curs[3]; - float vecA[3], vecB[3]; - float distA, distB; - - /* get cursor location */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_v3_m4v3(curs, obedit->imat, give_cursor(scene, v3d)); - - /* get distances */ - sub_v3_v3v3(vecA, ebp->vec, curs); - sub_v3_v3v3(vecB, ebp2->vec, curs); - distA = len_v3(vecA); - distB = len_v3(vecB); - - /* compare distances - closer one therefore acts as direction for bone to go */ - headtail = (distA < distB) ? 2 : 1; - } - else if (ebp->head_owner) { - headtail = 1; - } - else if (ebp2->head_owner) { - headtail = 2; - } - - /* assign head/tail combinations */ - if (headtail == 2) { - copy_v3_v3(head, ebp->vec); - copy_v3_v3(tail, ebp2->vec); - } - else if (headtail == 1) { - copy_v3_v3(head, ebp2->vec); - copy_v3_v3(tail, ebp->vec); - } - - /* add new bone and parent it to the appropriate end */ - if (headtail) { - EditBone *newbone = add_points_bone(obedit, head, tail); - - /* do parenting (will need to set connected flag too) */ - if (headtail == 2) { - /* ebp tail or head - tail gets priority */ - if (ebp->tail_owner) - newbone->parent = ebp->tail_owner; - else - newbone->parent = ebp->head_owner; - } - else { - /* ebp2 tail or head - tail gets priority */ - if (ebp2->tail_owner) - newbone->parent = ebp2->tail_owner; - else - newbone->parent = ebp2->head_owner; - } - - newbone->flag |= BONE_CONNECTED; - } - } - else { - /* FIXME.. figure out a method for multiple bones */ - BKE_reportf(op->reports, RPT_ERROR, "Too many points selected: %d", count); - BLI_freelistN(&points); - return OPERATOR_CANCELLED; - } - - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); - - /* free points */ - BLI_freelistN(&points); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_fill(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Fill Between Joints"; - ot->idname = "ARMATURE_OT_fill"; - ot->description = "Add bone between selected joint(s) and/or 3D-Cursor"; - - /* callbacks */ - ot->exec = armature_fill_bones_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* --------------------- */ - -/* this function merges between two bones, removes them and those in-between, - * and adjusts the parent relationships for those in-between - */ -static void bones_merge(Object *obedit, EditBone *start, EditBone *end, EditBone *endchild, ListBase *chains) -{ - bArmature *arm = obedit->data; - EditBone *ebo, *ebone, *newbone; - LinkData *chain; - float head[3], tail[3]; - - /* check if same bone */ - if (start == end) { - if (G.debug & G_DEBUG) { - printf("Error: same bone!\n"); - printf("\tstart = %s, end = %s\n", start->name, end->name); - } - } - - /* step 1: add a new bone - * - head = head/tail of start (default head) - * - tail = head/tail of end (default tail) - * - parent = parent of start - */ - if ((start->flag & BONE_TIPSEL) && (start->flag & BONE_SELECTED) == 0) { - copy_v3_v3(head, start->tail); - } - else { - copy_v3_v3(head, start->head); - } - if ((end->flag & BONE_ROOTSEL) && (end->flag & BONE_SELECTED) == 0) { - copy_v3_v3(tail, end->head); - } - else { - copy_v3_v3(tail, end->tail); - } - newbone = add_points_bone(obedit, head, tail); - newbone->parent = start->parent; - - /* TODO, copy more things to the new bone */ - newbone->flag = start->flag & (BONE_HINGE | BONE_NO_DEFORM | BONE_NO_SCALE | - BONE_NO_CYCLICOFFSET | BONE_NO_LOCAL_LOCATION | BONE_DONE); - - /* step 2a: reparent any side chains which may be parented to any bone in the chain of bones to merge - * - potentially several tips for side chains leading to some tree exist... - */ - for (chain = chains->first; chain; chain = chain->next) { - /* traverse down chain until we hit the bottom or if we run into the tip of the chain of bones we're - * merging (need to stop in this case to avoid corrupting this chain too!) - */ - for (ebone = chain->data; (ebone) && (ebone != end); ebone = ebone->parent) { - short found = 0; - - /* check if this bone is parented to one in the merging chain - * ! WATCHIT: must only go check until end of checking chain - */ - for (ebo = end; (ebo) && (ebo != start->parent); ebo = ebo->parent) { - /* side-chain found? --> remap parent to new bone, then we're done with this chain :) */ - if (ebone->parent == ebo) { - ebone->parent = newbone; - found = 1; - break; - } - } - - /* carry on to the next tip now */ - if (found) - break; - } - } - - /* step 2b: parent child of end to newbone (child from this chain) */ - if (endchild) - endchild->parent = newbone; - - /* step 3: delete all bones between and including start and end */ - for (ebo = end; ebo; ebo = ebone) { - ebone = (ebo == start) ? (NULL) : (ebo->parent); - bone_free(arm, ebo); - } - - newbone->flag |= (BONE_ROOTSEL | BONE_TIPSEL | BONE_SELECTED); - ED_armature_sync_selection(arm->edbo); -} - - -static int armature_merge_exec(bContext *C, wmOperator *op) -{ - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = (obedit) ? obedit->data : NULL; - short type = RNA_enum_get(op->ptr, "type"); - - /* sanity checks */ - if (ELEM(NULL, obedit, arm)) - return OPERATOR_CANCELLED; - - /* for now, there's only really one type of merging that's performed... */ - if (type == 1) { - /* go down chains, merging bones */ - ListBase chains = {NULL, NULL}; - LinkData *chain, *nchain; - EditBone *ebo; - - armature_tag_select_mirrored(arm); - - /* get chains (ends on chains) */ - chains_find_tips(arm->edbo, &chains); - if (chains.first == NULL) return OPERATOR_CANCELLED; - - /* each 'chain' is the last bone in the chain (with no children) */ - for (chain = chains.first; chain; chain = nchain) { - EditBone *bstart = NULL, *bend = NULL; - EditBone *bchild = NULL, *child = NULL; - - /* temporarily remove chain from list of chains */ - nchain = chain->next; - BLI_remlink(&chains, chain); - - /* only consider bones that are visible and selected */ - for (ebo = chain->data; ebo; child = ebo, ebo = ebo->parent) { - /* check if visible + selected */ - if (EBONE_VISIBLE(arm, ebo) && - ((ebo->flag & BONE_CONNECTED) || (ebo->parent == NULL)) && - (ebo->flag & BONE_SELECTED) ) - { - /* set either end or start (end gets priority, unless it is already set) */ - if (bend == NULL) { - bend = ebo; - bchild = child; - } - else - bstart = ebo; - } - else { - /* chain is broken... merge any continous segments then clear */ - if (bstart && bend) - bones_merge(obedit, bstart, bend, bchild, &chains); - - bstart = NULL; - bend = NULL; - bchild = NULL; - } - } - - /* merge from bstart to bend if something not merged */ - if (bstart && bend) - bones_merge(obedit, bstart, bend, bchild, &chains); - - /* put back link */ - BLI_insertlinkbefore(&chains, nchain, chain); - } - - armature_tag_unselect(arm); - - BLI_freelistN(&chains); - } - - /* updates */ - ED_armature_sync_selection(arm->edbo); - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_merge(wmOperatorType *ot) -{ - static EnumPropertyItem merge_types[] = { - {1, "WITHIN_CHAIN", 0, "Within Chains", ""}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Merge Bones"; - ot->idname = "ARMATURE_OT_merge"; - ot->description = "Merge continuous chains of selected bones"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_merge_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", merge_types, 0, "Type", ""); -} - -/* ************** END Add/Remove stuff in editmode ************ */ -/* *************** Tools in editmode *********** */ - -static int armature_hide_exec(bContext *C, wmOperator *op) -{ - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *ebone; - const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0; - - /* cancel if nothing selected */ - if (CTX_DATA_COUNT(C, selected_bones) == 0) - return OPERATOR_CANCELLED; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if ((ebone->flag & BONE_SELECTED) != invert) { - ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - ebone->flag |= BONE_HIDDEN_A; - } - } - } - ED_armature_validate_active(arm); - ED_armature_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_hide(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Hide Selected Bones"; - ot->idname = "ARMATURE_OT_hide"; - ot->description = "Tag selected bones to not be visible in Edit Mode"; - - /* api callbacks */ - ot->exec = armature_hide_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected"); -} - -static int armature_reveal_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *ebone; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (arm->layer & ebone->layer) { - if (ebone->flag & BONE_HIDDEN_A) { - ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - ebone->flag &= ~BONE_HIDDEN_A; - } - } - } - ED_armature_validate_active(arm); - ED_armature_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_reveal(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Reveal Bones"; - ot->idname = "ARMATURE_OT_reveal"; - ot->description = "Unhide all bones that have been tagged to be hidden in Edit Mode"; - - /* api callbacks */ - ot->exec = armature_reveal_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - -} -#if 0 // remove this? -static void hide_selected_armature_bones(Scene *scene) -{ - Object *obedit = scene->obedit; // XXX get from context - bArmature *arm = obedit->data; - EditBone *ebone; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & BONE_SELECTED) { - ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - ebone->flag |= BONE_HIDDEN_A; - } - } - } - ED_armature_validate_active(arm); - ED_armature_sync_selection(arm->edbo); -} - -static void hide_unselected_armature_bones(Scene *scene) -{ - Object *obedit = scene->obedit; // XXX get from context - bArmature *arm = obedit->data; - EditBone *ebone; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - bArmature *arm = obedit->data; - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL)) { - /* pass */ - } - else { - ebone->flag |= BONE_HIDDEN_A; - } - } - } - - ED_armature_validate_active(arm); - ED_armature_sync_selection(arm->edbo); -} - -void show_all_armature_bones(Scene *scene) -{ - Object *obedit = scene->obedit; // XXX get from context - bArmature *arm = obedit->data; - EditBone *ebone; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (arm->layer & ebone->layer) { - if (ebone->flag & BONE_HIDDEN_A) { - ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - ebone->flag &= ~BONE_HIDDEN_A; - } - } - } - ED_armature_validate_active(arm); - ED_armature_sync_selection(arm->edbo); -} -#endif - -/* previously extrude_armature */ -/* context; editmode armature */ -/* if forked && mirror-edit: makes two bones with flipped names */ -static int armature_extrude_exec(bContext *C, wmOperator *op) -{ - Object *obedit; - bArmature *arm; - EditBone *newbone, *ebone, *flipbone, *first = NULL; - int a, totbone = 0, do_extrude; - int forked = RNA_boolean_get(op->ptr, "forked"); - - obedit = CTX_data_edit_object(C); - arm = obedit->data; - - /* since we allow root extrude too, we have to make sure selection is OK */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - if (ebone->flag & BONE_ROOTSEL) { - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - if (ebone->parent->flag & BONE_TIPSEL) - ebone->flag &= ~BONE_ROOTSEL; - } - } - } - } - - /* Duplicate the necessary bones */ - for (ebone = arm->edbo->first; ((ebone) && (ebone != first)); ebone = ebone->next) { - if (EBONE_VISIBLE(arm, ebone)) { - /* we extrude per definition the tip */ - do_extrude = FALSE; - if (ebone->flag & (BONE_TIPSEL | BONE_SELECTED)) { - do_extrude = TRUE; - } - else if (ebone->flag & BONE_ROOTSEL) { - /* but, a bone with parent deselected we do the root... */ - if (ebone->parent && (ebone->parent->flag & BONE_TIPSEL)) { - /* pass */ - } - else { - do_extrude = 2; - } - } - - if (do_extrude) { - /* we re-use code for mirror editing... */ - flipbone = NULL; - if (arm->flag & ARM_MIRROR_EDIT) { - flipbone = ED_armature_bone_get_mirrored(arm->edbo, ebone); - if (flipbone) { - forked = 0; // we extrude 2 different bones - if (flipbone->flag & (BONE_TIPSEL | BONE_ROOTSEL | BONE_SELECTED)) - /* don't want this bone to be selected... */ - flipbone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - } - if ((flipbone == NULL) && (forked)) - flipbone = ebone; - } - - for (a = 0; a < 2; a++) { - if (a == 1) { - if (flipbone == NULL) - break; - else { - SWAP(EditBone *, flipbone, ebone); - } - } - - totbone++; - newbone = MEM_callocN(sizeof(EditBone), "extrudebone"); - - if (do_extrude == TRUE) { - copy_v3_v3(newbone->head, ebone->tail); - copy_v3_v3(newbone->tail, newbone->head); - newbone->parent = ebone; - - newbone->flag = ebone->flag & (BONE_TIPSEL | BONE_RELATIVE_PARENTING); // copies it, in case mirrored bone - - if (newbone->parent) newbone->flag |= BONE_CONNECTED; - } - else { - copy_v3_v3(newbone->head, ebone->head); - copy_v3_v3(newbone->tail, ebone->head); - newbone->parent = ebone->parent; - - newbone->flag = BONE_TIPSEL; - - if (newbone->parent && (ebone->flag & BONE_CONNECTED)) { - newbone->flag |= BONE_CONNECTED; - } - } - - newbone->weight = ebone->weight; - newbone->dist = ebone->dist; - newbone->xwidth = ebone->xwidth; - newbone->zwidth = ebone->zwidth; - newbone->ease1 = ebone->ease1; - newbone->ease2 = ebone->ease2; - newbone->rad_head = ebone->rad_tail; // don't copy entire bone... - newbone->rad_tail = ebone->rad_tail; - newbone->segments = 1; - newbone->layer = ebone->layer; - - BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name)); - - if (flipbone && forked) { // only set if mirror edit - if (strlen(newbone->name) < 30) { - if (a == 0) strcat(newbone->name, "_L"); - else strcat(newbone->name, "_R"); - } - } - unique_editbone_name(arm->edbo, newbone->name, NULL); - - /* Add the new bone to the list */ - BLI_addtail(arm->edbo, newbone); - if (!first) - first = newbone; - - /* restore ebone if we were flipping */ - if (a == 1 && flipbone) - SWAP(EditBone *, flipbone, ebone); - } - } - - /* Deselect the old bone */ - ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); - } - } - /* if only one bone, make this one active */ - if (totbone == 1 && first) arm->act_edbone = first; - - if (totbone == 0) return OPERATOR_CANCELLED; - - /* Transform the endpoints */ - ED_armature_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_extrude(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Extrude"; - ot->idname = "ARMATURE_OT_extrude"; - ot->description = "Create new bones from the selected joints"; - - /* api callbacks */ - ot->exec = armature_extrude_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_boolean(ot->srna, "forked", 0, "Forked", ""); -} -/* ********************** Bone Add ********************/ - -/*op makes a new bone and returns it with its tip selected */ - -static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) -{ - RegionView3D *rv3d = CTX_wm_region_view3d(C); - Object *obedit = CTX_data_edit_object(C); - EditBone *bone; - float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3]; - char name[MAXBONENAME]; - - RNA_string_get(op->ptr, "name", name); - - copy_v3_v3(curs, give_cursor(CTX_data_scene(C), CTX_wm_view3d(C))); - - /* Get inverse point for head and orientation for tail */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_m4_v3(obedit->imat, curs); - - if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) - copy_m3_m4(obmat, rv3d->viewmat); - else unit_m3(obmat); - - copy_m3_m4(viewmat, obedit->obmat); - mul_m3_m3m3(totmat, obmat, viewmat); - invert_m3_m3(imat, totmat); - - ED_armature_deselect_all(obedit, 0); - - /* Create a bone */ - bone = ED_armature_edit_bone_add(obedit->data, name); - - copy_v3_v3(bone->head, curs); - - if (rv3d && (U.flag & USER_ADD_VIEWALIGNED)) - add_v3_v3v3(bone->tail, bone->head, imat[1]); // bone with unit length 1 - else - add_v3_v3v3(bone->tail, bone->head, imat[2]); // bone with unit length 1, pointing up Z - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_bone_primitive_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Bone"; - ot->idname = "ARMATURE_OT_bone_primitive_add"; - ot->description = "Add a new bone located at the 3D-Cursor"; - - /* api callbacks */ - ot->exec = armature_bone_primitive_add_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_string(ot->srna, "name", "Bone", MAXBONENAME, "Name", "Name of the newly created bone"); - -} - - -/* ----------- */ - -/* Subdivide Operators: - * This group of operators all use the same 'exec' callback, but they are called - * through several different operators - a combined menu (which just calls the exec in the - * appropriate ways), and two separate ones. - */ - -static int armature_subdivide_exec(bContext *C, wmOperator *op) -{ - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *newbone, *tbone; - int cuts, i; - - /* there may not be a number_cuts property defined (for 'simple' subdivide) */ - cuts = RNA_int_get(op->ptr, "number_cuts"); - - /* loop over all editable bones */ - // XXX the old code did this in reverse order though! - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - for (i = cuts + 1; i > 1; i--) { - /* compute cut ratio first */ - float cutratio = 1.0f / (float)i; - float cutratioI = 1.0f - cutratio; - - float val1[3]; - float val2[3]; - float val3[3]; - - newbone = MEM_mallocN(sizeof(EditBone), "ebone subdiv"); - *newbone = *ebone; - BLI_addtail(arm->edbo, newbone); - - /* calculate location of newbone->head */ - copy_v3_v3(val1, ebone->head); - copy_v3_v3(val2, ebone->tail); - copy_v3_v3(val3, newbone->head); - - val3[0] = val1[0] * cutratio + val2[0] * cutratioI; - val3[1] = val1[1] * cutratio + val2[1] * cutratioI; - val3[2] = val1[2] * cutratio + val2[2] * cutratioI; - - copy_v3_v3(newbone->head, val3); - copy_v3_v3(newbone->tail, ebone->tail); - copy_v3_v3(ebone->tail, newbone->head); - - newbone->rad_head = 0.5f * (ebone->rad_head + ebone->rad_tail); - ebone->rad_tail = newbone->rad_head; - - newbone->flag |= BONE_CONNECTED; - - unique_editbone_name(arm->edbo, newbone->name, NULL); - - /* correct parent bones */ - for (tbone = arm->edbo->first; tbone; tbone = tbone->next) { - if (tbone->parent == ebone) - tbone->parent = newbone; - } - newbone->parent = ebone; - } - } - CTX_DATA_END; - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_subdivide(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Subdivide Multi"; - ot->idname = "ARMATURE_OT_subdivide"; - ot->description = "Break selected bones into chains of smaller bones"; - - /* api callbacks */ - ot->exec = armature_subdivide_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* Properties */ - prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10); - /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -/* ----------- */ - -/* Switch Direction operator: - * Currently, this does not use context loops, as context loops do not make it - * easy to retrieve any hierarchical/chain relationships which are necessary for - * this to be done easily. - */ - -/* helper to clear BONE_TRANSFORM flags */ -static void armature_clear_swap_done_flags(bArmature *arm) -{ - EditBone *ebone; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - ebone->flag &= ~BONE_TRANSFORM; - } -} - -static int armature_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_edit_object(C); - bArmature *arm = (bArmature *)ob->data; - ListBase chains = {NULL, NULL}; - LinkData *chain; - - /* get chains of bones (ends on chains) */ - chains_find_tips(arm->edbo, &chains); - if (chains.first == NULL) return OPERATOR_CANCELLED; - - /* ensure that mirror bones will also be operated on */ - armature_tag_select_mirrored(arm); - - /* clear BONE_TRANSFORM flags - * - used to prevent duplicate/cancelling operations from occurring [#34123] - * - BONE_DONE cannot be used here as that's already used for mirroring - */ - armature_clear_swap_done_flags(arm); - - /* loop over chains, only considering selected and visible bones */ - for (chain = chains.first; chain; chain = chain->next) { - EditBone *ebo, *child = NULL, *parent = NULL; - - /* loop over bones in chain */ - for (ebo = chain->data; ebo; ebo = parent) { - /* parent is this bone's original parent - * - we store this, as the next bone that is checked is this one - * but the value of ebo->parent may change here... - */ - parent = ebo->parent; - - /* skip bone if already handled... [#34123] */ - if ((ebo->flag & BONE_TRANSFORM) == 0) { - /* only if selected and editable */ - if (EBONE_VISIBLE(arm, ebo) && EBONE_EDITABLE(ebo)) { - /* swap head and tail coordinates */ - SWAP(float, ebo->head[0], ebo->tail[0]); - SWAP(float, ebo->head[1], ebo->tail[1]); - SWAP(float, ebo->head[2], ebo->tail[2]); - - /* do parent swapping: - * - use 'child' as new parent - * - connected flag is only set if points are coincidental - */ - ebo->parent = child; - if ((child) && equals_v3v3(ebo->head, child->tail)) - ebo->flag |= BONE_CONNECTED; - else - ebo->flag &= ~BONE_CONNECTED; - - /* get next bones - * - child will become the new parent of next bone - */ - child = ebo; - } - else { - /* not swapping this bone, however, if its 'parent' got swapped, unparent us from it - * as it will be facing in opposite direction - */ - if ((parent) && (EBONE_VISIBLE(arm, parent) && EBONE_EDITABLE(parent))) { - ebo->parent = NULL; - ebo->flag &= ~BONE_CONNECTED; - } - - /* get next bones - * - child will become new parent of next bone (not swapping occurred, - * so set to NULL to prevent infinite-loop) - */ - child = NULL; - } - - /* tag as done (to prevent double-swaps) */ - ebo->flag |= BONE_TRANSFORM; - } - } - } - - /* free chains */ - BLI_freelistN(&chains); - - /* clear temp flags */ - armature_clear_swap_done_flags(arm); - armature_tag_unselect(arm); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_switch_direction(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Switch Direction"; - ot->idname = "ARMATURE_OT_switch_direction"; - ot->description = "Change the direction that a chain of bones points in (head <-> tail swap)"; - - /* api callbacks */ - ot->exec = armature_switch_direction_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} -/* ***************** Parenting *********************** */ - -/* armature parenting options */ -#define ARM_PAR_CONNECT 1 -#define ARM_PAR_OFFSET 2 - -/* check for null, before calling! */ -static void bone_connect_to_existing_parent(EditBone *bone) -{ - bone->flag |= BONE_CONNECTED; - copy_v3_v3(bone->head, bone->parent->tail); - bone->rad_head = bone->parent->rad_tail; -} - -static void bone_connect_to_new_parent(ListBase *edbo, EditBone *selbone, EditBone *actbone, short mode) -{ - EditBone *ebone; - float offset[3]; - - if ((selbone->parent) && (selbone->flag & BONE_CONNECTED)) - selbone->parent->flag &= ~(BONE_TIPSEL); - - /* make actbone the parent of selbone */ - selbone->parent = actbone; - - /* in actbone tree we cannot have a loop */ - for (ebone = actbone->parent; ebone; ebone = ebone->parent) { - if (ebone->parent == selbone) { - ebone->parent = NULL; - ebone->flag &= ~BONE_CONNECTED; - } - } - - if (mode == ARM_PAR_CONNECT) { - /* Connected: Child bones will be moved to the parent tip */ - selbone->flag |= BONE_CONNECTED; - sub_v3_v3v3(offset, actbone->tail, selbone->head); - - copy_v3_v3(selbone->head, actbone->tail); - selbone->rad_head = actbone->rad_tail; - - add_v3_v3(selbone->tail, offset); - - /* offset for all its children */ - for (ebone = edbo->first; ebone; ebone = ebone->next) { - EditBone *par; - - for (par = ebone->parent; par; par = par->parent) { - if (par == selbone) { - add_v3_v3(ebone->head, offset); - add_v3_v3(ebone->tail, offset); - break; - } - } - } - } - else { - /* Offset: Child bones will retain their distance from the parent tip */ - selbone->flag &= ~BONE_CONNECTED; - } -} - -static EnumPropertyItem prop_editarm_make_parent_types[] = { - {ARM_PAR_CONNECT, "CONNECTED", 0, "Connected", ""}, - {ARM_PAR_OFFSET, "OFFSET", 0, "Keep Offset", ""}, - {0, NULL, 0, NULL, NULL} -}; - -static int armature_parent_set_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_edit_object(C); - bArmature *arm = (bArmature *)ob->data; - EditBone *actbone = CTX_data_active_bone(C); - EditBone *actmirb = NULL; - short val = RNA_enum_get(op->ptr, "type"); - - /* there must be an active bone */ - if (actbone == NULL) { - BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); - return OPERATOR_CANCELLED; - } - else if (arm->flag & ARM_MIRROR_EDIT) { - /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone - * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone - * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R"). - * This is useful for arm-chains, for example parenting lower arm to upper arm - * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent") - * then just use actbone. Useful when doing upper arm to spine. - */ - actmirb = ED_armature_bone_get_mirrored(arm->edbo, actbone); - if (actmirb == NULL) - actmirb = actbone; - } - - /* if there is only 1 selected bone, we assume that that is the active bone, - * since a user will need to have clicked on a bone (thus selecting it) to make it active - */ - if (CTX_DATA_COUNT(C, selected_editable_bones) <= 1) { - /* When only the active bone is selected, and it has a parent, - * connect it to the parent, as that is the only possible outcome. - */ - if (actbone->parent) { - bone_connect_to_existing_parent(actbone); - - if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) - bone_connect_to_existing_parent(actmirb); - } - } - else { - /* Parent 'selected' bones to the active one - * - the context iterator contains both selected bones and their mirrored copies, - * so we assume that unselected bones are mirrored copies of some selected bone - * - since the active one (and/or its mirror) will also be selected, we also need - * to check that we are not trying to operate on them, since such an operation - * would cause errors - */ - - /* parent selected bones to the active one */ - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - if (ELEM(ebone, actbone, actmirb) == 0) { - if (ebone->flag & BONE_SELECTED) - bone_connect_to_new_parent(arm->edbo, ebone, actbone, val); - else - bone_connect_to_new_parent(arm->edbo, ebone, actmirb, val); - } - } - CTX_DATA_END; - } - - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; -} - -static int armature_parent_set_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event)) -{ - EditBone *actbone = CTX_data_active_bone(C); - uiPopupMenu *pup = uiPupMenuBegin(C, "Make Parent ", ICON_NONE); - uiLayout *layout = uiPupMenuLayout(pup); - int allchildbones = 0; - - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - if (ebone != actbone) { - if (ebone->parent != actbone) allchildbones = 1; - } - } - CTX_DATA_END; - - uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_CONNECT); - - /* ob becomes parent, make the associated menus */ - if (allchildbones) - uiItemEnumO(layout, "ARMATURE_OT_parent_set", NULL, 0, "type", ARM_PAR_OFFSET); - - uiPupMenuEnd(C, pup); - - return OPERATOR_CANCELLED; -} - -void ARMATURE_OT_parent_set(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Make Parent"; - ot->idname = "ARMATURE_OT_parent_set"; - ot->description = "Set the active bone as the parent of the selected bones"; - - /* api callbacks */ - ot->invoke = armature_parent_set_invoke; - ot->exec = armature_parent_set_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "type", prop_editarm_make_parent_types, 0, "ParentType", "Type of parenting"); -} - -static EnumPropertyItem prop_editarm_clear_parent_types[] = { - {1, "CLEAR", 0, "Clear Parent", ""}, - {2, "DISCONNECT", 0, "Disconnect Bone", ""}, - {0, NULL, 0, NULL, NULL} -}; - -static void editbone_clear_parent(EditBone *ebone, int mode) -{ - if (ebone->parent) { - /* for nice selection */ - ebone->parent->flag &= ~(BONE_TIPSEL); - } - - if (mode == 1) ebone->parent = NULL; - ebone->flag &= ~BONE_CONNECTED; -} - -static int armature_parent_clear_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_edit_object(C); - bArmature *arm = (bArmature *)ob->data; - int val = RNA_enum_get(op->ptr, "type"); - - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - editbone_clear_parent(ebone, val); - } - CTX_DATA_END; - - ED_armature_sync_selection(arm->edbo); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_parent_clear(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Clear Parent"; - ot->idname = "ARMATURE_OT_parent_clear"; - ot->description = "Remove the parent-child relationship between selected bones and their parents"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_parent_clear_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum(ot->srna, "type", prop_editarm_clear_parent_types, 0, "ClearType", "What way to clear parenting"); -} - -/* **************** Selections ******************/ - -static int armature_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) -{ - /* Set the flags */ - CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) - { - /* ignore bone if selection can't change */ - if ((ebone->flag & BONE_UNSELECTABLE) == 0) { - /* select bone */ - ebone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - CTX_DATA_END; - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_select_inverse(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Inverse"; - ot->idname = "ARMATURE_OT_select_inverse"; - ot->description = "Flip the selection status of bones (selected -> unselected, unselected -> selected)"; - - /* api callbacks */ - ot->exec = armature_select_inverse_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - -} -static int armature_de_select_all_exec(bContext *C, wmOperator *op) -{ - int action = RNA_enum_get(op->ptr, "action"); - - if (action == SEL_TOGGLE) { - action = SEL_SELECT; - /* Determine if there are any selected bones - * And therefore whether we are selecting or deselecting */ - if (CTX_DATA_COUNT(C, selected_bones) > 0) - action = SEL_DESELECT; - } - - /* Set the flags */ - CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) - { - /* ignore bone if selection can't change */ - if ((ebone->flag & BONE_UNSELECTABLE) == 0) { - switch (action) { - case SEL_SELECT: - ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (ebone->parent) - ebone->parent->flag |= (BONE_TIPSEL); - break; - case SEL_DESELECT: - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - break; - case SEL_INVERT: - if (ebone->flag & BONE_SELECTED) { - ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (ebone->parent) - ebone->parent->flag |= (BONE_TIPSEL); - } - break; - } - } - } - CTX_DATA_END; - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_select_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "(De)select All"; - ot->idname = "ARMATURE_OT_select_all"; - ot->description = "Toggle selection status of all bones"; - - /* api callbacks */ - ot->exec = armature_de_select_all_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - WM_operator_properties_select_all(ot); -} - -enum { - SIMEDBONE_LENGTH = 1, - SIMEDBONE_DIRECTION, - SIMEDBONE_PREFIX, - SIMEDBONE_SUFFIX, - SIMEDBONE_LAYER -}; - -static EnumPropertyItem prop_similar_types[] = { - {SIMEDBONE_LENGTH, "LENGTH", 0, "Length", ""}, - {SIMEDBONE_DIRECTION, "DIRECTION", 0, "Direction (Y axis)", ""}, - {SIMEDBONE_PREFIX, "PREFIX", 0, "Prefix", ""}, - {SIMEDBONE_SUFFIX, "SUFFIX", 0, "Suffix", ""}, - {SIMEDBONE_LAYER, "LAYER", 0, "Layer", ""}, - {0, NULL, 0, NULL, NULL} -}; - -/* could be used in more places */ -static void ED_armature_edit_bone_select(EditBone *ebone) -{ - BLI_assert((ebone->flag & BONE_UNSELECTABLE) == 0); - ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - - if ((ebone->flag & BONE_CONNECTED) && (ebone->parent != NULL)) { - ebone->parent->flag |= BONE_TIPSEL; - } -} - -static void select_similar_length(bArmature *arm, EditBone *ebone_act, const float thresh) -{ - EditBone *ebone; - - /* thresh is always relative to current length */ - const float len_min = ebone_act->length / (1.0f + thresh); - const float len_max = ebone_act->length * (1.0f + thresh); - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - if ((ebone->length >= len_min) && - (ebone->length <= len_max)) - { - ED_armature_edit_bone_select(ebone); - } - } - } -} - -static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh) -{ - EditBone *ebone; - float dir_act[3]; - sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail); - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - float dir[3]; - sub_v3_v3v3(dir, ebone->head, ebone->tail); - - if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) { - ED_armature_edit_bone_select(ebone); - } - } - } -} - -static void select_similar_layer(bArmature *arm, EditBone *ebone_act) -{ - EditBone *ebone; - - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - if (ebone->layer & ebone_act->layer) { - ED_armature_edit_bone_select(ebone); - } - } - } -} - -static void select_similar_prefix(bArmature *arm, EditBone *ebone_act) -{ - EditBone *ebone; - - char body_tmp[MAX_VGROUP_NAME]; - char prefix_act[MAX_VGROUP_NAME]; - - BKE_deform_split_prefix(ebone_act->name, prefix_act, body_tmp); - - if (prefix_act[0] == '\0') - return; - - /* Find matches */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - char prefix_other[MAX_VGROUP_NAME]; - BKE_deform_split_prefix(ebone->name, prefix_other, body_tmp); - if (!strcmp(prefix_act, prefix_other)) { - ED_armature_edit_bone_select(ebone); - } - } - } -} - -static void select_similar_suffix(bArmature *arm, EditBone *ebone_act) -{ - EditBone *ebone; - - char body_tmp[MAX_VGROUP_NAME]; - char suffix_act[MAX_VGROUP_NAME]; - - BKE_deform_split_suffix(ebone_act->name, body_tmp, suffix_act); - - if (suffix_act[0] == '\0') - return; - - /* Find matches */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (EBONE_SELECTABLE(arm, ebone)) { - char suffix_other[MAX_VGROUP_NAME]; - BKE_deform_split_suffix(ebone->name, body_tmp, suffix_other); - if (!strcmp(suffix_act, suffix_other)) { - ED_armature_edit_bone_select(ebone); - } - } - } -} - -static int armature_select_similar_exec(bContext *C, wmOperator *op) -{ - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; - EditBone *ebone_act = CTX_data_active_bone(C); - - /* Get props */ - int type = RNA_enum_get(op->ptr, "type"); - float thresh = RNA_float_get(op->ptr, "threshold"); - - /* Check for active bone */ - if (ebone_act == NULL) { - BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); - return OPERATOR_CANCELLED; - } - - switch (type) { - case SIMEDBONE_LENGTH: - select_similar_length(arm, ebone_act, thresh); - break; - case SIMEDBONE_DIRECTION: - select_similar_direction(arm, ebone_act, thresh); - break; - case SIMEDBONE_PREFIX: - select_similar_prefix(arm, ebone_act); - break; - case SIMEDBONE_SUFFIX: - select_similar_suffix(arm, ebone_act); - break; - case SIMEDBONE_LAYER: - select_similar_layer(arm, ebone_act); - break; - } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_select_similar(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Similar"; - ot->idname = "ARMATURE_OT_select_similar"; - - /* callback functions */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_select_similar_exec; - ot->poll = ED_operator_editarmature; - ot->description = "Select similar bones by property types"; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", ""); - RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); -} - -/* ********************* select hierarchy operator ************** */ - -static int armature_select_hierarchy_exec(bContext *C, wmOperator *op) -{ - Object *obedit = CTX_data_edit_object(C); - Object *ob; - bArmature *arm; - EditBone *curbone, *pabone, *chbone; - int direction = RNA_enum_get(op->ptr, "direction"); - int add_to_sel = RNA_boolean_get(op->ptr, "extend"); - - ob = obedit; - arm = (bArmature *)ob->data; - - for (curbone = arm->edbo->first; curbone; curbone = curbone->next) { - /* only work on bone if it is visible and its selection can change */ - if (EBONE_SELECTABLE(arm, curbone)) { - if (curbone == arm->act_edbone) { - if (direction == BONE_SELECT_PARENT) { - if (curbone->parent == NULL) continue; - else pabone = curbone->parent; - - if (EBONE_VISIBLE(arm, pabone)) { - pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_edbone = pabone; - if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL; - - if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - break; - } - - } - else { // BONE_SELECT_CHILD - chbone = editbone_get_child(arm, curbone, 1); - if (chbone == NULL) continue; - - if (EBONE_SELECTABLE(arm, chbone)) { - chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_edbone = chbone; - - if (!add_to_sel) { - curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL); - if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL; - } - break; - } - } - } - } - } - - ED_armature_sync_selection(arm->edbo); - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_select_hierarchy(wmOperatorType *ot) -{ - static EnumPropertyItem direction_items[] = { - {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""}, - {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Select Hierarchy"; - ot->idname = "ARMATURE_OT_select_hierarchy"; - ot->description = "Select immediate parent/children of selected bones"; - - /* api callbacks */ - ot->exec = armature_select_hierarchy_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_enum(ot->srna, "direction", direction_items, - BONE_SELECT_PARENT, "Direction", ""); - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); -} - -/* ***************** EditBone Alignment ********************* */ - -/* helper to fix a ebone position if its parent has moved due to alignment*/ -static void fix_connected_bone(EditBone *ebone) -{ - float diff[3]; - - if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) || equals_v3v3(ebone->parent->tail, ebone->head)) - return; - - /* if the parent has moved we translate child's head and tail accordingly*/ - sub_v3_v3v3(diff, ebone->parent->tail, ebone->head); - add_v3_v3(ebone->head, diff); - add_v3_v3(ebone->tail, diff); - return; -} - -/* helper to recursively find chains of connected bones starting at ebone and fix their position */ -static void fix_editbone_connected_children(ListBase *edbo, EditBone *ebone) -{ - EditBone *selbone; - - for (selbone = edbo->first; selbone; selbone = selbone->next) { - if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) { - fix_connected_bone(selbone); - fix_editbone_connected_children(edbo, selbone); - } - } - return; -} - -static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone) -{ - float selboneaxis[3], actboneaxis[3], length; - - sub_v3_v3v3(actboneaxis, actbone->tail, actbone->head); - normalize_v3(actboneaxis); - - sub_v3_v3v3(selboneaxis, selbone->tail, selbone->head); - length = len_v3(selboneaxis); - - mul_v3_fl(actboneaxis, length); - add_v3_v3v3(selbone->tail, selbone->head, actboneaxis); - selbone->roll = actbone->roll; - - /* if the bone being aligned has connected descendants they must be moved - * according to their parent new position, otherwise they would be left - * in an inconsistent state: connected but away from the parent*/ - fix_editbone_connected_children(edbo, selbone); - return; -} - -static int armature_align_bones_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_edit_object(C); - bArmature *arm = (bArmature *)ob->data; - EditBone *actbone = CTX_data_active_bone(C); - EditBone *actmirb = NULL; - - /* there must be an active bone */ - if (actbone == NULL) { - BKE_report(op->reports, RPT_ERROR, "Operation requires an active bone"); - return OPERATOR_CANCELLED; - } - else if (arm->flag & ARM_MIRROR_EDIT) { - /* For X-Axis Mirror Editing option, we may need a mirror copy of actbone - * - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone - * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R"). - * This is useful for arm-chains, for example parenting lower arm to upper arm - * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent") - * then just use actbone. Useful when doing upper arm to spine. - */ - actmirb = ED_armature_bone_get_mirrored(arm->edbo, actbone); - if (actmirb == NULL) - actmirb = actbone; - } - - /* if there is only 1 selected bone, we assume that that is the active bone, - * since a user will need to have clicked on a bone (thus selecting it) to make it active - */ - if (CTX_DATA_COUNT(C, selected_editable_bones) <= 1) { - /* When only the active bone is selected, and it has a parent, - * align it to the parent, as that is the only possible outcome. - */ - if (actbone->parent) { - bone_align_to_bone(arm->edbo, actbone, actbone->parent); - - if ((arm->flag & ARM_MIRROR_EDIT) && (actmirb->parent)) - bone_align_to_bone(arm->edbo, actmirb, actmirb->parent); - } - } - else { - /* Align 'selected' bones to the active one - * - the context iterator contains both selected bones and their mirrored copies, - * so we assume that unselected bones are mirrored copies of some selected bone - * - since the active one (and/or its mirror) will also be selected, we also need - * to check that we are not trying to operate on them, since such an operation - * would cause errors - */ - - /* align selected bones to the active one */ - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - if (ELEM(ebone, actbone, actmirb) == 0) { - if (ebone->flag & BONE_SELECTED) - bone_align_to_bone(arm->edbo, ebone, actbone); - else - bone_align_to_bone(arm->edbo, ebone, actmirb); - } - } - CTX_DATA_END; - } - - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_align(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Align Bones"; - ot->idname = "ARMATURE_OT_align"; - ot->description = "Align selected bones to the active bone (or to their parent)"; - - /* api callbacks */ - ot->invoke = WM_operator_confirm; - ot->exec = armature_align_bones_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ***************** Pose tools ********************* */ - -/* XXX bone_looper is only to be used when we want to access settings - * (i.e. editability/visibility/selected) that context doesn't offer */ -static int bone_looper(Object *ob, Bone *bone, void *data, - int (*bone_func)(Object *, Bone *, void *)) -{ - /* We want to apply the function bone_func to every bone - * in an armature -- feed bone_looper the first bone and - * a pointer to the bone_func and watch it go!. The int count - * can be useful for counting bones with a certain property - * (e.g. skinnable) - */ - int count = 0; - - if (bone) { - /* only do bone_func if the bone is non null */ - count += bone_func(ob, bone, data); - - /* try to execute bone_func for the first child */ - count += bone_looper(ob, bone->childbase.first, data, bone_func); - - /* try to execute bone_func for the next bone at this - * depth of the recursion. - */ - count += bone_looper(ob, bone->next, data, bone_func); - } - - return count; -} - -/* called from editview.c, for mode-less pose selection */ -/* assumes scene obact and basact is still on old situation */ -int ED_do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, - short extend, short deselect, short toggle) -{ - Object *ob = base->object; - Bone *nearBone; - - if (!ob || !ob->pose) return 0; - - nearBone = get_bone_from_selectbuffer(scene, base, buffer, hits, 1); - - /* if the bone cannot be affected, don't do anything */ - if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) { - Object *ob_act = OBACT; - bArmature *arm = ob->data; - - /* since we do unified select, we don't shift+select a bone if the - * armature object was not active yet. - * note, special exception for armature mode so we can do multi-select - * we could check for multi-select explicitly but think its fine to - * always give predictable behavior in weight paint mode - campbell */ - if ((!extend && !deselect && !toggle) || - ((ob_act && (ob_act != ob) && (ob_act->mode & OB_MODE_WEIGHT_PAINT) == 0))) - { - ED_pose_deselectall(ob, 0); - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - else { - if (extend) { - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - else if (deselect) { - nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else if (toggle) { - if (nearBone->flag & BONE_SELECTED) { - /* if not active, we make it active */ - if (nearBone != arm->act_bone) { - arm->act_bone = nearBone; - } - else { - nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - } - else { - nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_bone = nearBone; - } - } - } - - if (ob_act) { - /* in weightpaint we select the associated vertex group too */ - if (ob_act->mode & OB_MODE_WEIGHT_PAINT) { - if (nearBone == arm->act_bone) { - ED_vgroup_select_by_name(ob_act, nearBone->name); - DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA); - } - } - /* if there are some dependencies for visualizing armature state - * (e.g. Mask Modifier in 'Armature' mode), force update - */ - else if (arm->flag & ARM_HAS_VIZ_DEPS) { - DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA); - } - } - } - - return nearBone != NULL; -} - -/* test==0: deselect all - * test==1: swap select (apply to all the opposite of current situation) - * test==2: only clear active tag - * test==3: swap select (no test / inverse selection status of all independently) - */ -void ED_pose_deselectall(Object *ob, int test) -{ - bArmature *arm = ob->data; - bPoseChannel *pchan; - int selectmode = 0; - - /* we call this from outliner too */ - if (ob->pose == NULL) { - return; - } - - /* Determine if we're selecting or deselecting */ - if (test == 1) { - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (PBONE_VISIBLE(arm, pchan->bone)) { - if (pchan->bone->flag & BONE_SELECTED) - break; - } - } - - if (pchan == NULL) - selectmode = 1; - } - else if (test == 2) - selectmode = 2; - - /* Set the flags accordingly */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - /* ignore the pchan if it isn't visible or if its selection cannot be changed */ - if ((pchan->bone->layer & arm->layer) && !(pchan->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) { - if (test == 3) { - pchan->bone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - if (selectmode == 0) pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - else if (selectmode == 1) pchan->bone->flag |= BONE_SELECTED; - } - } - } -} - -static int bone_skinnable_cb(Object *ob, Bone *bone, void *datap) -{ - /* Bones that are deforming - * are regarded to be "skinnable" and are eligible for - * auto-skinning. - * - * This function performs 2 functions: - * - * a) It returns 1 if the bone is skinnable. - * If we loop over all bones with this - * function, we can count the number of - * skinnable bones. - * b) If the pointer data is non null, - * it is treated like a handle to a - * bone pointer -- the bone pointer - * is set to point at this bone, and - * the pointer the handle points to - * is incremented to point to the - * next member of an array of pointers - * to bones. This way we can loop using - * this function to construct an array of - * pointers to bones that point to all - * skinnable bones. - */ - Bone ***hbone; - int a, segments; - struct { Object *armob; void *list; int heat; } *data = datap; - - if (!(ob->mode & OB_MODE_WEIGHT_PAINT) || !(bone->flag & BONE_HIDDEN_P)) { - if (!(bone->flag & BONE_NO_DEFORM)) { - if (data->heat && data->armob->pose && BKE_pose_channel_find_name(data->armob->pose, bone->name)) - segments = bone->segments; - else - segments = 1; - - if (data->list != NULL) { - hbone = (Bone ***) &data->list; - - for (a = 0; a < segments; a++) { - **hbone = bone; - ++*hbone; - } - } - return segments; - } - } - return 0; -} - -static int vgroup_add_unique_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) -{ - /* This group creates a vertex group to ob that has the - * same name as bone (provided the bone is skinnable). - * If such a vertex group aleady exist the routine exits. - */ - if (!(bone->flag & BONE_NO_DEFORM)) { - if (!defgroup_find_name(ob, bone->name)) { - ED_vgroup_add_name(ob, bone->name); - return 1; - } - } - return 0; -} - -static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap) -{ - /* Bones that are deforming - * are regarded to be "skinnable" and are eligible for - * auto-skinning. - * - * This function performs 2 functions: - * - * a) If the bone is skinnable, it creates - * a vertex group for ob that has - * the name of the skinnable bone - * (if one doesn't exist already). - * b) If the pointer data is non null, - * it is treated like a handle to a - * bDeformGroup pointer -- the - * bDeformGroup pointer is set to point - * to the deform group with the bone's - * name, and the pointer the handle - * points to is incremented to point to the - * next member of an array of pointers - * to bDeformGroups. This way we can loop using - * this function to construct an array of - * pointers to bDeformGroups, all with names - * of skinnable bones. - */ - bDeformGroup ***hgroup, *defgroup = NULL; - int a, segments; - struct { Object *armob; void *list; int heat; } *data = datap; - int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT); - bArmature *arm = data->armob->data; - - if (!wpmode || !(bone->flag & BONE_HIDDEN_P)) { - if (!(bone->flag & BONE_NO_DEFORM)) { - if (data->heat && data->armob->pose && BKE_pose_channel_find_name(data->armob->pose, bone->name)) - segments = bone->segments; - else - segments = 1; - - if (!wpmode || ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED))) - if (!(defgroup = defgroup_find_name(ob, bone->name))) - defgroup = ED_vgroup_add_name(ob, bone->name); - - if (data->list != NULL) { - hgroup = (bDeformGroup ***) &data->list; - - for (a = 0; a < segments; a++) { - **hgroup = defgroup; - ++*hgroup; - } - } - return segments; - } - } - return 0; -} - -static void add_vgroups__mapFunc(void *userData, int index, const float co[3], - const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) -{ - /* DerivedMesh mapFunc for getting final coords in weight paint mode */ - - float (*verts)[3] = userData; - copy_v3_v3(verts[index], co); -} - -static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist, - bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, - float (*root)[3], float (*tip)[3], int *selected, float scale) -{ - /* Create vertex group weights from envelopes */ - - Bone *bone; - bDeformGroup *dgroup; - float distance; - int i, iflip, j; - - /* for each vertex in the mesh */ - for (i = 0; i < mesh->totvert; i++) { - iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, i) : 0; - - /* for each skinnable bone */ - for (j = 0; j < numbones; ++j) { - if (!selected[j]) - continue; - - bone = bonelist[j]; - dgroup = dgrouplist[j]; - - /* store the distance-factor from the vertex to the bone */ - distance = distfactor_to_bone(verts[i], root[j], tip[j], - bone->rad_head * scale, bone->rad_tail * scale, bone->dist * scale); - - /* add the vert to the deform group if (weight != 0.0) */ - if (distance != 0.0f) - ED_vgroup_vert_add(ob, dgroup, i, distance, WEIGHT_REPLACE); - else - ED_vgroup_vert_remove(ob, dgroup, i); - - /* do same for mirror */ - if (dgroupflip && dgroupflip[j] && iflip >= 0) { - if (distance != 0.0f) - ED_vgroup_vert_add(ob, dgroupflip[j], iflip, distance, - WEIGHT_REPLACE); - else - ED_vgroup_vert_remove(ob, dgroupflip[j], iflip); - } - } - } -} - -static void add_verts_to_dgroups(ReportList *reports, Scene *scene, Object *ob, Object *par, int heat, int mirror) -{ - /* This functions implements the automatic computation of vertex group - * weights, either through envelopes or using a heat equilibrium. - * - * This function can be called both when parenting a mesh to an armature, - * or in weightpaint + posemode. In the latter case selection is taken - * into account and vertex weights can be mirrored. - * - * The mesh vertex positions used are either the final deformed coords - * from the derivedmesh in weightpaint mode, the final subsurf coords - * when parenting, or simply the original mesh coords. - */ - - bArmature *arm = par->data; - Bone **bonelist, *bone; - bDeformGroup **dgrouplist, **dgroupflip; - bDeformGroup *dgroup; - bPoseChannel *pchan; - Mesh *mesh; - Mat4 *bbone = NULL; - float (*root)[3], (*tip)[3], (*verts)[3]; - int *selected; - int numbones, vertsfilled = 0, i, j, segments = 0; - int wpmode = (ob->mode & OB_MODE_WEIGHT_PAINT); - struct { Object *armob; void *list; int heat; } looper_data; - - looper_data.armob = par; - looper_data.heat = heat; - looper_data.list = NULL; - - /* count the number of skinnable bones */ - numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb); - - if (numbones == 0) - return; - - if (ED_vgroup_data_create(ob->data) == FALSE) - return; - - /* create an array of pointer to bones that are skinnable - * and fill it with all of the skinnable bones */ - bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist"); - looper_data.list = bonelist; - bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable_cb); - - /* create an array of pointers to the deform groups that - * correspond to the skinnable bones (creating them - * as necessary. */ - dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist"); - dgroupflip = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgroupflip"); - - looper_data.list = dgrouplist; - bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb); - - /* create an array of root and tip positions transformed into - * global coords */ - root = MEM_callocN(numbones * sizeof(float) * 3, "root"); - tip = MEM_callocN(numbones * sizeof(float) * 3, "tip"); - selected = MEM_callocN(numbones * sizeof(int), "selected"); - - for (j = 0; j < numbones; ++j) { - bone = bonelist[j]; - dgroup = dgrouplist[j]; - - /* handle bbone */ - if (heat) { - if (segments == 0) { - segments = 1; - bbone = NULL; - - if ((par->pose) && (pchan = BKE_pose_channel_find_name(par->pose, bone->name))) { - if (bone->segments > 1) { - segments = bone->segments; - bbone = b_bone_spline_setup(pchan, 1); - } - } - } - - segments--; - } - - /* compute root and tip */ - if (bbone) { - mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]); - if ((segments + 1) < bone->segments) { - mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]); - } - else { - copy_v3_v3(tip[j], bone->arm_tail); - } - } - else { - copy_v3_v3(root[j], bone->arm_head); - copy_v3_v3(tip[j], bone->arm_tail); - } - - mul_m4_v3(par->obmat, root[j]); - mul_m4_v3(par->obmat, tip[j]); - - /* set selected */ - if (wpmode) { - if ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED)) - selected[j] = 1; - } - else - selected[j] = 1; - - /* find flipped group */ - if (dgroup && mirror) { - char name[MAXBONENAME]; - - // 0 = don't strip off number extensions - flip_side_name(name, dgroup->name, FALSE); - dgroupflip[j] = defgroup_find_name(ob, name); - } - } - - /* create verts */ - mesh = (Mesh *)ob->data; - verts = MEM_callocN(mesh->totvert * sizeof(*verts), "closestboneverts"); - - if (wpmode) { - /* if in weight paint mode, use final verts from derivedmesh */ - DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); - - if (dm->foreachMappedVert) { - dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void *)verts); - vertsfilled = 1; - } - - dm->release(dm); - } - else if (modifiers_findByType(ob, eModifierType_Subsurf)) { - /* is subsurf on? Lets use the verts on the limit surface then. - * = same amount of vertices as mesh, but vertices moved to the - * subsurfed position, like for 'optimal'. */ - subsurf_calculate_limit_positions(mesh, verts); - vertsfilled = 1; - } - - /* transform verts to global space */ - for (i = 0; i < mesh->totvert; i++) { - if (!vertsfilled) - copy_v3_v3(verts[i], mesh->mvert[i].co); - mul_m4_v3(ob->obmat, verts[i]); - } - - /* compute the weights based on gathered vertices and bones */ - if (heat) { - const char *error = NULL; - heat_bone_weighting(ob, mesh, verts, numbones, dgrouplist, dgroupflip, - root, tip, selected, &error); - - if (error) { - BKE_report(reports, RPT_WARNING, error); - } - } - else { - envelope_bone_weighting(ob, mesh, verts, numbones, bonelist, dgrouplist, - dgroupflip, root, tip, selected, mat4_to_scale(par->obmat)); - } - - /* only generated in some cases but can call anyway */ - mesh_octree_table(ob, NULL, NULL, 'e'); - - /* free the memory allocated */ - MEM_freeN(bonelist); - MEM_freeN(dgrouplist); - MEM_freeN(dgroupflip); - MEM_freeN(root); - MEM_freeN(tip); - MEM_freeN(selected); - MEM_freeN(verts); -} - -void create_vgroups_from_armature(ReportList *reports, Scene *scene, Object *ob, Object *par, int mode, int mirror) -{ - /* Lets try to create some vertex groups - * based on the bones of the parent armature. - */ - bArmature *arm = par->data; - - if (mode == ARM_GROUPS_NAME) { - const int defbase_tot = BLI_countlist(&ob->defbase); - int defbase_add; - /* Traverse the bone list, trying to create empty vertex - * groups corresponding to the bone. - */ - defbase_add = bone_looper(ob, arm->bonebase.first, NULL, vgroup_add_unique_bone_cb); - - if (defbase_add) { - /* its possible there are DWeight's outside the range of the current - * objects deform groups, in this case the new groups wont be empty [#33889] */ - ED_vgroup_data_clamp_range(ob->data, defbase_tot); - } - } - else if (mode == ARM_GROUPS_ENVELOPE || mode == ARM_GROUPS_AUTO) { - /* Traverse the bone list, trying to create vertex groups - * that are populated with the vertices for which the - * bone is closest. - */ - add_verts_to_dgroups(reports, scene, ob, par, (mode == ARM_GROUPS_AUTO), mirror); - } -} -/* ************* Clear Pose *****************************/ - -/* clear scale of pose-channel */ -static void pchan_clear_scale(bPoseChannel *pchan) -{ - if ((pchan->protectflag & OB_LOCK_SCALEX) == 0) - pchan->size[0] = 1.0f; - if ((pchan->protectflag & OB_LOCK_SCALEY) == 0) - pchan->size[1] = 1.0f; - if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0) - pchan->size[2] = 1.0f; -} - -/* clear location of pose-channel */ -static void pchan_clear_loc(bPoseChannel *pchan) -{ - if ((pchan->protectflag & OB_LOCK_LOCX) == 0) - pchan->loc[0] = 0.0f; - if ((pchan->protectflag & OB_LOCK_LOCY) == 0) - pchan->loc[1] = 0.0f; - if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) - pchan->loc[2] = 0.0f; -} - -/* clear rotation of pose-channel */ -static void pchan_clear_rot(bPoseChannel *pchan) -{ - if (pchan->protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) { - /* check if convert to eulers for locking... */ - if (pchan->protectflag & OB_LOCK_ROT4D) { - /* perform clamping on a component by component basis */ - if (pchan->rotmode == ROT_MODE_AXISANGLE) { - if ((pchan->protectflag & OB_LOCK_ROTW) == 0) - pchan->rotAngle = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTX) == 0) - pchan->rotAxis[0] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTY) == 0) - pchan->rotAxis[1] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) - pchan->rotAxis[2] = 0.0f; - - /* check validity of axis - axis should never be 0,0,0 (if so, then we make it rotate about y) */ - if (IS_EQF(pchan->rotAxis[0], pchan->rotAxis[1]) && IS_EQF(pchan->rotAxis[1], pchan->rotAxis[2])) - pchan->rotAxis[1] = 1.0f; - } - else if (pchan->rotmode == ROT_MODE_QUAT) { - if ((pchan->protectflag & OB_LOCK_ROTW) == 0) - pchan->quat[0] = 1.0f; - if ((pchan->protectflag & OB_LOCK_ROTX) == 0) - pchan->quat[1] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTY) == 0) - pchan->quat[2] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) - pchan->quat[3] = 0.0f; - } - else { - /* the flag may have been set for the other modes, so just ignore the extra flag... */ - if ((pchan->protectflag & OB_LOCK_ROTX) == 0) - pchan->eul[0] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTY) == 0) - pchan->eul[1] = 0.0f; - if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) - pchan->eul[2] = 0.0f; - } - } - else { - /* perform clamping using euler form (3-components) */ - float eul[3], oldeul[3], quat1[4] = {0}; - float qlen = 0.0f; - - if (pchan->rotmode == ROT_MODE_QUAT) { - qlen = normalize_qt_qt(quat1, pchan->quat); - quat_to_eul(oldeul, quat1); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); - } - else { - copy_v3_v3(oldeul, pchan->eul); - } - - eul[0] = eul[1] = eul[2] = 0.0f; - - if (pchan->protectflag & OB_LOCK_ROTX) - eul[0] = oldeul[0]; - if (pchan->protectflag & OB_LOCK_ROTY) - eul[1] = oldeul[1]; - if (pchan->protectflag & OB_LOCK_ROTZ) - eul[2] = oldeul[2]; - - if (pchan->rotmode == ROT_MODE_QUAT) { - eul_to_quat(pchan->quat, eul); - - /* restore original quat size */ - mul_qt_fl(pchan->quat, qlen); - - /* quaternions flip w sign to accumulate rotations correctly */ - if ((quat1[0] < 0.0f && pchan->quat[0] > 0.0f) || (quat1[0] > 0.0f && pchan->quat[0] < 0.0f)) { - mul_qt_fl(pchan->quat, -1.0f); - } - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); - } - else { - copy_v3_v3(pchan->eul, eul); - } - } - } /* Duplicated in source/blender/editors/object/object_transform.c */ - else { - if (pchan->rotmode == ROT_MODE_QUAT) { - unit_qt(pchan->quat); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - /* by default, make rotation of 0 radians around y-axis (roll) */ - unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); - } - else { - zero_v3(pchan->eul); - } - } -} - -/* clear loc/rot/scale of pose-channel */ -static void pchan_clear_transforms(bPoseChannel *pchan) -{ - pchan_clear_loc(pchan); - pchan_clear_rot(pchan); - pchan_clear_scale(pchan); -} - -/* --------------- */ - -/* generic exec for clear-pose operators */ -static int pose_clear_transform_generic_exec(bContext *C, wmOperator *op, - void (*clear_func)(bPoseChannel *), const char default_ksName[]) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - short autokey = 0; - - /* sanity checks */ - if (ELEM(NULL, clear_func, default_ksName)) { - BKE_report(op->reports, RPT_ERROR, "Programming error: missing clear transform function or keying set name"); - return OPERATOR_CANCELLED; - } - - /* only clear relevant transforms for selected bones */ - CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones) - { - /* run provided clearing function */ - clear_func(pchan); - - /* do auto-keyframing as appropriate */ - if (autokeyframe_cfra_can_key(scene, &ob->id)) { - /* clear any unkeyed tags */ - if (pchan->bone) - pchan->bone->flag &= ~BONE_UNKEYED; - - /* tag for autokeying later */ - autokey = 1; - } - else { - /* add unkeyed tags */ - if (pchan->bone) - pchan->bone->flag |= BONE_UNKEYED; - } - } - CTX_DATA_END; - - /* perform autokeying on the bones if needed */ - if (autokey) { - /* get KeyingSet to use */ - KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName); - - /* insert keyframes */ - ANIM_apply_keyingset(C, NULL, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); - - /* now recalculate paths */ - if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) - ED_pose_recalculate_paths(scene, ob); - } - - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); - - return OPERATOR_FINISHED; -} - -/* --------------- */ - -static int pose_clear_scale_exec(bContext *C, wmOperator *op) -{ - return pose_clear_transform_generic_exec(C, op, pchan_clear_scale, ANIM_KS_SCALING_ID); -} - -void POSE_OT_scale_clear(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Clear Pose Scale"; - ot->idname = "POSE_OT_scale_clear"; - ot->description = "Reset scaling of selected bones to their default values"; - - /* api callbacks */ - ot->exec = pose_clear_scale_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - - -static int pose_clear_rot_exec(bContext *C, wmOperator *op) -{ - return pose_clear_transform_generic_exec(C, op, pchan_clear_rot, ANIM_KS_ROTATION_ID); -} - -void POSE_OT_rot_clear(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Clear Pose Rotation"; - ot->idname = "POSE_OT_rot_clear"; - ot->description = "Reset rotations of selected bones to their default values"; - - /* api callbacks */ - ot->exec = pose_clear_rot_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - - -static int pose_clear_loc_exec(bContext *C, wmOperator *op) -{ - return pose_clear_transform_generic_exec(C, op, pchan_clear_loc, ANIM_KS_LOCATION_ID); -} - -void POSE_OT_loc_clear(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Clear Pose Location"; - ot->idname = "POSE_OT_loc_clear"; - ot->description = "Reset locations of selected bones to their default values"; - - /* api callbacks */ - ot->exec = pose_clear_loc_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - - -static int pose_clear_transforms_exec(bContext *C, wmOperator *op) -{ - return pose_clear_transform_generic_exec(C, op, pchan_clear_transforms, ANIM_KS_LOC_ROT_SCALE_ID); -} - -void POSE_OT_transforms_clear(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Clear Pose Transforms"; - ot->idname = "POSE_OT_transforms_clear"; - ot->description = "Reset location, rotation, and scaling of selected bones to their default values"; - - /* api callbacks */ - ot->exec = pose_clear_transforms_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ***************** selections ********************** */ - -static int pose_de_select_all_exec(bContext *C, wmOperator *op) -{ - int action = RNA_enum_get(op->ptr, "action"); - - Scene *scene = CTX_data_scene(C); - Object *ob = ED_object_context(C); - bArmature *arm = ob->data; - int multipaint = scene->toolsettings->multipaint; - - if (action == SEL_TOGGLE) { - action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT; - } - - /* Set the flags */ - CTX_DATA_BEGIN(C, bPoseChannel *, pchan, visible_pose_bones) - { - /* select pchan only if selectable, but deselect works always */ - switch (action) { - case SEL_SELECT: - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) - pchan->bone->flag |= BONE_SELECTED; - break; - case SEL_DESELECT: - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - break; - case SEL_INVERT: - if (pchan->bone->flag & BONE_SELECTED) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - pchan->bone->flag |= BONE_SELECTED; - } - break; - } - } - CTX_DATA_END; - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); - - /* weightpaint or mask modifiers need depsgraph updates */ - if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) { - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - return OPERATOR_FINISHED; -} - -void POSE_OT_select_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "(De)select All"; - ot->idname = "POSE_OT_select_all"; - ot->description = "Toggle selection status of all bones"; - - /* api callbacks */ - ot->exec = pose_de_select_all_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - WM_operator_properties_select_all(ot); -} - -static int pose_select_parent_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (bArmature *)ob->data; - bPoseChannel *pchan, *parent; - - /* Determine if there is an active bone */ - pchan = CTX_data_active_pose_bone(C); - if (pchan) { - parent = pchan->parent; - if ((parent) && !(parent->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) { - parent->bone->flag |= BONE_SELECTED; - arm->act_bone = parent->bone; - } - else { - return OPERATOR_CANCELLED; - } - } - else { - return OPERATOR_CANCELLED; - } - - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - return OPERATOR_FINISHED; -} - -void POSE_OT_select_parent(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Parent Bone"; - ot->idname = "POSE_OT_select_parent"; - ot->description = "Select bones that are parents of the currently selected bones"; - - /* api callbacks */ - ot->exec = pose_select_parent_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - -} - -/* ************* hide/unhide pose bones ******************* */ - -static int hide_selected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) -{ - bArmature *arm = ob->data; - - if (arm->layer & bone->layer) { - if (bone->flag & BONE_SELECTED) { - bone->flag |= BONE_HIDDEN_P; - bone->flag &= ~BONE_SELECTED; - if (arm->act_bone == bone) - arm->act_bone = NULL; - } - } - return 0; -} - -static int hide_unselected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) -{ - bArmature *arm = ob->data; - - if (arm->layer & bone->layer) { - /* hrm... typo here? */ - if ((bone->flag & BONE_SELECTED) == 0) { - bone->flag |= BONE_HIDDEN_P; - if (arm->act_bone == bone) - arm->act_bone = NULL; - } - } - return 0; -} - -/* active object is armature in posemode, poll checked */ -static int pose_hide_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = ob->data; - - if (RNA_boolean_get(op->ptr, "unselected")) - bone_looper(ob, arm->bonebase.first, NULL, hide_unselected_pose_bone_cb); - else - bone_looper(ob, arm->bonebase.first, NULL, hide_selected_pose_bone_cb); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_hide(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Hide Selected"; - ot->idname = "POSE_OT_hide"; - ot->description = "Tag selected bones to not be visible in Pose Mode"; - - /* api callbacks */ - ot->exec = pose_hide_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", ""); -} - -static int show_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) -{ - bArmature *arm = ob->data; - - if (arm->layer & bone->layer) { - if (bone->flag & BONE_HIDDEN_P) { - bone->flag &= ~BONE_HIDDEN_P; - bone->flag |= BONE_SELECTED; - } - } - - return 0; -} - -/* active object is armature in posemode, poll checked */ -static int pose_reveal_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = ob->data; - - bone_looper(ob, arm->bonebase.first, NULL, show_pose_bone_cb); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_reveal(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Reveal Selected"; - ot->idname = "POSE_OT_reveal"; - ot->description = "Unhide all bones that have been tagged to be hidden in Pose Mode"; - - /* api callbacks */ - ot->exec = pose_reveal_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ************* RENAMING DISASTERS ************ */ - -static int bone_unique_check(void *arg, const char *name) -{ - return BKE_armature_find_bone_name((bArmature *)arg, name) != NULL; -} - -static void unique_bone_name(bArmature *arm, char *name) -{ - BLI_uniquename_cb(bone_unique_check, (void *)arm, "Bone", '.', name, sizeof(((Bone *)NULL)->name)); -} - -/* helper call for armature_bone_rename */ -static void constraint_bone_name_fix(Object *ob, ListBase *conlist, char *oldname, char *newname) -{ - bConstraint *curcon; - bConstraintTarget *ct; - - for (curcon = conlist->first; curcon; curcon = curcon->next) { - bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(curcon); - ListBase targets = {NULL, NULL}; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(curcon, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == ob) { - if (!strcmp(ct->subtarget, oldname) ) - BLI_strncpy(ct->subtarget, newname, MAXBONENAME); - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(curcon, &targets, 0); - } - } -} - -/* called by UI for renaming a bone */ -/* warning: make sure the original bone was not renamed yet! */ -/* seems messy, but thats what you get with not using pointers but channel names :) */ -void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *newnamep) -{ - Object *ob; - char newname[MAXBONENAME]; - char oldname[MAXBONENAME]; - - /* names better differ! */ - if (strncmp(oldnamep, newnamep, MAXBONENAME)) { - - /* we alter newname string... so make copy */ - BLI_strncpy(newname, newnamep, MAXBONENAME); - /* we use oldname for search... so make copy */ - BLI_strncpy(oldname, oldnamep, MAXBONENAME); - - /* now check if we're in editmode, we need to find the unique name */ - if (arm->edbo) { - EditBone *eBone = editbone_name_exists(arm->edbo, oldname); - - if (eBone) { - unique_editbone_name(arm->edbo, newname, NULL); - BLI_strncpy(eBone->name, newname, MAXBONENAME); - } - else return; - } - else { - Bone *bone = BKE_armature_find_bone_name(arm, oldname); - - if (bone) { - unique_bone_name(arm, newname); - BLI_strncpy(bone->name, newname, MAXBONENAME); - } - else return; - } - - /* do entire dbase - objects */ - for (ob = G.main->object.first; ob; ob = ob->id.next) { - ModifierData *md; - - /* we have the object using the armature */ - if (arm == ob->data) { - Object *cob; - - /* Rename the pose channel, if it exists */ - if (ob->pose) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, oldname); - if (pchan) { - BLI_strncpy(pchan->name, newname, MAXBONENAME); - - if (ob->pose->chanhash) { - GHash *gh = ob->pose->chanhash; - - /* remove the old hash entry, and replace with the new name */ - BLI_ghash_remove(gh, oldname, NULL, NULL); - BLI_ghash_insert(gh, pchan->name, pchan); - } - } - } - - /* Update any object constraints to use the new bone name */ - for (cob = G.main->object.first; cob; cob = cob->id.next) { - if (cob->constraints.first) - constraint_bone_name_fix(ob, &cob->constraints, oldname, newname); - if (cob->pose) { - bPoseChannel *pchan; - for (pchan = cob->pose->chanbase.first; pchan; pchan = pchan->next) { - constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname); - } - } - } - } - - /* See if an object is parented to this armature */ - if (ob->parent && (ob->parent->data == arm)) { - if (ob->partype == PARBONE) { - /* bone name in object */ - if (!strcmp(ob->parsubstr, oldname)) - BLI_strncpy(ob->parsubstr, newname, MAXBONENAME); - } - } - - if (modifiers_usesArmature(ob, arm)) { - bDeformGroup *dg = defgroup_find_name(ob, oldname); - if (dg) { - BLI_strncpy(dg->name, newname, MAXBONENAME); - } - } - - /* fix modifiers that might be using this name */ - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Hook) { - HookModifierData *hmd = (HookModifierData *)md; - - /* uses armature, so may use the affected bone name */ - if (hmd->object && (hmd->object->data == arm)) { - if (!strcmp(hmd->subtarget, oldname)) - BLI_strncpy(hmd->subtarget, newname, MAXBONENAME); - } - } - } - } - - /* Fix all animdata that may refer to this bone - we can't just do the ones attached to objects, since - * other ID-blocks may have drivers referring to this bone [#29822] - */ - { - - BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname); - } - - /* correct view locking */ - { - bScreen *screen; - for (screen = G.main->screen.first; screen; screen = screen->id.next) { - ScrArea *sa; - /* add regions */ - for (sa = screen->areabase.first; sa; sa = sa->next) { - SpaceLink *sl; - for (sl = sa->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)sl; - if (v3d->ob_centre && v3d->ob_centre->data == arm) { - if (!strcmp(v3d->ob_centre_bone, oldname)) { - BLI_strncpy(v3d->ob_centre_bone, newname, MAXBONENAME); - } - } - } - } - } - } - } - } -} - - -static int armature_flip_names_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_edit_object(C); - bArmature *arm; - char newname[MAXBONENAME]; - - /* paranoia checks */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - arm = ob->data; - - /* loop through selected bones, auto-naming them */ - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - flip_side_name(newname, ebone->name, TRUE); // 1 = do strip off number extensions - ED_armature_bone_rename(arm, ebone->name, newname); - } - CTX_DATA_END; - - /* since we renamed stuff... */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_flip_names(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Flip Names"; - ot->idname = "ARMATURE_OT_flip_names"; - ot->description = "Flips (and corrects) the axis suffixes of the names of selected bones"; - - /* api callbacks */ - ot->exec = armature_flip_names_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - - -static int armature_autoside_names_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_edit_object(C); - bArmature *arm; - char newname[MAXBONENAME]; - short axis = RNA_enum_get(op->ptr, "type"); - - /* paranoia checks */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - arm = ob->data; - - /* loop through selected bones, auto-naming them */ - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) - { - BLI_strncpy(newname, ebone->name, sizeof(newname)); - if (bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis])) - ED_armature_bone_rename(arm, ebone->name, newname); - } - CTX_DATA_END; - - /* since we renamed stuff... */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_autoside_names(wmOperatorType *ot) -{ - static EnumPropertyItem axis_items[] = { - {0, "XAXIS", 0, "X-Axis", "Left/Right"}, - {1, "YAXIS", 0, "Y-Axis", "Front/Back"}, - {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "AutoName by Axis"; - ot->idname = "ARMATURE_OT_autoside_names"; - ot->description = "Automatically renames the selected bones according to which side of the target axis they fall on"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = armature_autoside_names_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* settings */ - ot->prop = RNA_def_enum(ot->srna, "type", axis_items, 0, "Axis", "Axis tag names with"); -} - - - -/* if editbone (partial) selected, copy data */ -/* context; editmode armature, with mirror editing enabled */ -void transform_armature_mirror_update(Object *obedit) -{ - bArmature *arm = obedit->data; - EditBone *ebo, *eboflip; - - for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { - /* no layer check, correct mirror is more important */ - if (ebo->flag & (BONE_TIPSEL | BONE_ROOTSEL)) { - eboflip = ED_armature_bone_get_mirrored(arm->edbo, ebo); - - if (eboflip) { - /* we assume X-axis flipping for now */ - if (ebo->flag & BONE_TIPSEL) { - EditBone *children; - - eboflip->tail[0] = -ebo->tail[0]; - eboflip->tail[1] = ebo->tail[1]; - eboflip->tail[2] = ebo->tail[2]; - eboflip->rad_tail = ebo->rad_tail; - eboflip->roll = -ebo->roll; - - /* Also move connected children, in case children's name aren't mirrored properly */ - for (children = arm->edbo->first; children; children = children->next) { - if (children->parent == eboflip && children->flag & BONE_CONNECTED) { - copy_v3_v3(children->head, eboflip->tail); - children->rad_head = ebo->rad_tail; - } - } - } - if (ebo->flag & BONE_ROOTSEL) { - eboflip->head[0] = -ebo->head[0]; - eboflip->head[1] = ebo->head[1]; - eboflip->head[2] = ebo->head[2]; - eboflip->rad_head = ebo->rad_head; - eboflip->roll = -ebo->roll; - - /* Also move connected parent, in case parent's name isn't mirrored properly */ - if (eboflip->parent && eboflip->flag & BONE_CONNECTED) { - EditBone *parent = eboflip->parent; - copy_v3_v3(parent->tail, eboflip->head); - parent->rad_tail = ebo->rad_head; - } - } - if (ebo->flag & BONE_SELECTED) { - eboflip->dist = ebo->dist; - eboflip->roll = -ebo->roll; - eboflip->xwidth = ebo->xwidth; - eboflip->zwidth = ebo->zwidth; - } - } - } - } -} - - -/*****************************************************************************************************/ -/*************************************** SKELETON GENERATOR ******************************************/ -/*****************************************************************************************************/ - -#if 0 - -/**************************************** SUBDIVISION ALGOS ******************************************/ - -EditBone *subdivideByAngle(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail) -{ - bArmature *arm = obedit->data; - EditBone *lastBone = NULL; - - if (scene->toolsettings->skgen_options & SKGEN_CUT_ANGLE) { - ReebArcIterator arc_iter; - BArcIterator *iter = (BArcIterator *)&arc_iter; - float *previous = NULL, *current = NULL; - EditBone *child = NULL; - EditBone *parent = NULL; - EditBone *root = NULL; - float angleLimit = (float)cos(scene->toolsettings->skgen_angle_limit * M_PI / 180.0f); - - parent = ED_armature_edit_bone_add(arm, "Bone"); - parent->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; - copy_v3_v3(parent->head, head->p); - - root = parent; - - initArcIterator(iter, arc, head); - IT_next(iter); - previous = iter->p; - - for (IT_next(iter); - IT_stopped(iter) == 0; - previous = iter->p, IT_next(iter)) - { - float vec1[3], vec2[3]; - float len1, len2; - - current = iter->p; - - sub_v3_v3v3(vec1, previous, parent->head); - sub_v3_v3v3(vec2, current, previous); - - len1 = normalize_v3(vec1); - len2 = normalize_v3(vec2); - - if (len1 > 0.0f && len2 > 0.0f && dot_v3v3(vec1, vec2) < angleLimit) { - copy_v3_v3(parent->tail, previous); - - child = ED_armature_edit_bone_add(arm, "Bone"); - copy_v3_v3(child->head, parent->tail); - child->parent = parent; - child->flag |= BONE_CONNECTED | BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; - - parent = child; /* new child is next parent */ - } - } - copy_v3_v3(parent->tail, tail->p); - - /* If the bone wasn't subdivided, delete it and return NULL - * to let subsequent subdivision methods do their thing. - * */ - if (parent == root) { - if (parent == arm->act_edbone) arm->act_edbone = NULL; - ED_armature_edit_bone_remove(arm, parent); - parent = NULL; - } - - lastBone = parent; /* set last bone in the chain */ - } - - return lastBone; -} - -EditBone *test_subdivideByCorrelation(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail) -{ - EditBone *lastBone = NULL; - - if (scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION) { - float invmat[4][4] = MAT4_UNITY; - float tmat[3][3] = MAT3_UNITY; - ReebArcIterator arc_iter; - BArcIterator *iter = (BArcIterator *)&arc_iter; - bArmature *arm = obedit->data; - - initArcIterator(iter, arc, head); - - lastBone = subdivideArcBy(arm, arm->edbo, iter, invmat, tmat, nextAdaptativeSubdivision); - } - - return lastBone; -} - -float arcLengthRatio(ReebArc *arc) -{ - float arcLength = 0.0f; - float embedLength = 0.0f; - int i; - - arcLength = len_v3v3(arc->head->p, arc->tail->p); - - if (arc->bcount > 0) { - /* Add the embedding */ - for (i = 1; i < arc->bcount; i++) { - embedLength += len_v3v3(arc->buckets[i - 1].p, arc->buckets[i].p); - } - /* Add head and tail -> embedding vectors */ - embedLength += len_v3v3(arc->head->p, arc->buckets[0].p); - embedLength += len_v3v3(arc->tail->p, arc->buckets[arc->bcount - 1].p); - } - else { - embedLength = arcLength; - } - - return embedLength / arcLength; -} - -EditBone *test_subdivideByLength(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail) -{ - EditBone *lastBone = NULL; - if ((scene->toolsettings->skgen_options & SKGEN_CUT_LENGTH) && - arcLengthRatio(arc) >= G.scene->toolsettings->skgen_length_ratio) - { - float invmat[4][4] = MAT4_UNITY; - float tmat[3][3] = MAT3_UNITY; - ReebArcIterator arc_iter; - BArcIterator *iter = (BArcIterator *)&arc_iter; - bArmature *arm = obedit->data; - - initArcIterator(iter, arc, head); - - lastBone = subdivideArcBy(arm, arm->edbo, iter, invmat, tmat, nextLengthSubdivision); - } - - return lastBone; -} - -/***************************************** MAIN ALGORITHM ********************************************/ - -void generateSkeletonFromReebGraph(Scene *scene, ReebGraph *rg) -{ - Object *obedit = scene->obedit; // XXX get from context - GHash *arcBoneMap = NULL; - ReebArc *arc = NULL; - ReebNode *node = NULL; - Object *src = NULL; - Object *dst = NULL; - - src = scene->basact->object; - - if (obedit != NULL) { - ED_armature_from_edit(obedit); - ED_armature_edit_free(obedit); - } - - dst = BKE_object_add(scene, OB_ARMATURE); - ED_object_base_init_transform(NULL, scene->basact, NULL, NULL); // XXX NULL is C, loc, rot - obedit = scene->basact->object; - - /* Copy orientation from source */ - copy_v3_v3(dst->loc, src->obmat[3]); - mat4_to_eul(dst->rot, src->obmat); - mat4_to_size(dst->size, src->obmat); - - BKE_object_where_is_calc(scene, obedit); - - ED_armature_to_edit(obedit); - - arcBoneMap = BLI_ghash_ptr_new("SkeletonFromReebGraph gh"); - - BLI_markdownSymmetry((BGraph *)rg, rg->nodes.first, scene->toolsettings->skgen_symmetry_limit); - - for (arc = rg->arcs.first; arc; arc = arc->next) - { - EditBone *lastBone = NULL; - ReebNode *head, *tail; - int i; - - /* Find out the direction of the arc through simple heuristics (in order of priority) : - * - * 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 flag: 1 (low -> high), -1 (high -> low) - */ - - /* if arc is a symmetry axis, internal bones go up the tree */ - if (arc->symmetry_level == 1 && arc->tail->degree != 1) { - head = arc->tail; - tail = arc->head; - - arc->flag = -1; /* mark arc direction */ - } - /* Bones point AWAY from the symmetry axis */ - else if (arc->head->symmetry_level == 1) { - head = arc->head; - tail = arc->tail; - - arc->flag = 1; /* mark arc direction */ - } - else if (arc->tail->symmetry_level == 1) { - head = arc->tail; - tail = arc->head; - - arc->flag = -1; /* mark arc direction */ - } - /* otherwise, always go from low weight to high weight */ - else { - head = arc->head; - tail = arc->tail; - - arc->flag = 1; /* mark arc direction */ - } - - /* Loop over subdivision methods */ - for (i = 0; lastBone == NULL && i < SKGEN_SUB_TOTAL; i++) { - switch (scene->toolsettings->skgen_subdivisions[i]) { - case SKGEN_SUB_LENGTH: - lastBone = test_subdivideByLength(scene, obedit, arc, head, tail); - break; - case SKGEN_SUB_ANGLE: - lastBone = subdivideByAngle(scene, obedit, arc, head, tail); - break; - case SKGEN_SUB_CORRELATION: - lastBone = test_subdivideByCorrelation(scene, obedit, arc, head, tail); - break; - } - } - - if (lastBone == NULL) { - EditBone *bone; - bone = ED_armature_edit_bone_add(obedit->data, "Bone"); - bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; - - copy_v3_v3(bone->head, head->p); - copy_v3_v3(bone->tail, tail->p); - - /* set first and last bone, since there's only one */ - lastBone = bone; - } - - BLI_ghash_insert(arcBoneMap, arc, lastBone); - } - - /* Second pass, setup parent relationship between arcs */ - for (node = rg->nodes.first; node; node = node->next) - { - ReebArc *incomingArc = NULL; - int i; - - for (i = 0; i < node->degree; i++) { - arc = (ReebArc *)node->arcs[i]; - - /* if arc is incoming into the node */ - if ((arc->head == node && arc->flag == -1) || - (arc->tail == node && arc->flag == 1)) - { - if (incomingArc == NULL) { - incomingArc = arc; - /* loop further to make sure there's only one incoming arc */ - } - else { - /* skip this node if more than one incomingArc */ - incomingArc = NULL; - break; /* No need to look further, we are skipping already */ - } - } - } - - if (incomingArc != NULL) { - EditBone *parentBone = BLI_ghash_lookup(arcBoneMap, incomingArc); - - /* Look for outgoing arcs and parent their bones */ - for (i = 0; i < node->degree; i++) - { - arc = node->arcs[i]; - - /* if arc is outgoing from the node */ - if ((arc->head == node && arc->flag == 1) || (arc->tail == node && arc->flag == -1)) { - EditBone *childBone = BLI_ghash_lookup(arcBoneMap, arc); - - /* find the root bone */ - while (childBone->parent != NULL) - { - childBone = childBone->parent; - } - - childBone->parent = parentBone; - childBone->flag |= BONE_CONNECTED; - } - } - } - } - - BLI_ghash_free(arcBoneMap, NULL, NULL); -} - -void generateSkeleton(Scene *scene) -{ - ReebGraph *reebg; - -// setcursor_space(SPACE_VIEW3D, CURSOR_WAIT); - - reebg = BIF_ReebGraphFromEditMesh(); - - generateSkeletonFromReebGraph(scene, reebg); - - REEB_freeGraph(reebg); - - //setcursor_space(SPACE_VIEW3D, CURSOR_EDIT); -} - -#endif diff --git a/source/blender/editors/armature/editarmature_generate.c b/source/blender/editors/armature/editarmature_generate.c index 979c352c4b2..89772d38e8f 100644 --- a/source/blender/editors/armature/editarmature_generate.c +++ b/source/blender/editors/armature/editarmature_generate.c @@ -30,24 +30,13 @@ * \ingroup edarmature */ - -#include <string.h> -#include <math.h> -#include <float.h> - - #include "DNA_scene_types.h" #include "DNA_armature_types.h" -#include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_graph.h" -#include "BLI_utildefines.h" - - #include "ED_armature.h" -#include "armature_intern.h" #include "BIF_generate.h" void setBoneRollFromNormal(EditBone *bone, const float no[3], float UNUSED(invmat[4][4]), float tmat[3][3]) diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c index 3e34a4c6808..62cce6b2c1e 100644 --- a/source/blender/editors/armature/editarmature_retarget.c +++ b/source/blender/editors/armature/editarmature_retarget.c @@ -25,12 +25,6 @@ * \ingroup edarmature */ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <float.h> - #include "MEM_guardedalloc.h" #include "PIL_time.h" @@ -42,11 +36,6 @@ #include "BLI_blenlib.h" #include "BLI_math.h" -#include "BLI_utildefines.h" -#include "BLI_ghash.h" -#include "BLI_graph.h" -#include "BLI_rand.h" -#include "BLI_threads.h" #include "BKE_constraint.h" #include "BKE_armature.h" @@ -57,8 +46,6 @@ #include "BIF_retarget.h" -#include "reeb.h" /* FIX ME */ - #include "armature_intern.h" /************ RIG RETARGET DATA STRUCTURES ***************/ diff --git a/source/blender/editors/armature/editarmature_sketch.c b/source/blender/editors/armature/editarmature_sketch.c index b61aa86a52f..ec96c574f75 100644 --- a/source/blender/editors/armature/editarmature_sketch.c +++ b/source/blender/editors/armature/editarmature_sketch.c @@ -22,10 +22,6 @@ * \ingroup edarmature */ -#include <string.h> -#include <math.h> -#include <float.h> - #include "MEM_guardedalloc.h" #include "DNA_object_types.h" @@ -37,9 +33,6 @@ #include "BLI_blenlib.h" #include "BLI_math.h" -#include "BLI_utildefines.h" -#include "BLI_graph.h" -#include "BLI_ghash.h" #include "BLF_translation.h" @@ -60,8 +53,6 @@ #include "WM_api.h" #include "WM_types.h" -#include "reeb.h" - typedef int (*GestureDetectFct)(bContext *, SK_Gesture *, SK_Sketch *); typedef void (*GestureApplyFct)(bContext *, SK_Gesture *, SK_Sketch *); diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index a7e84d590b5..3ac514f1465 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -30,18 +30,12 @@ * \ingroup edarmature */ - -#include <math.h> -#include <string.h> - #include "MEM_guardedalloc.h" #include "DNA_object_types.h" #include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_scene_types.h" -#include "BLI_utildefines.h" #include "BLI_math.h" #include "BLI_edgehash.h" #include "BLI_memarena.h" @@ -59,8 +53,6 @@ #include "ONL_opennl.h" -#include "BLO_sys_types.h" // for intptr_t support - #include "ED_mesh.h" #include "ED_armature.h" diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c new file mode 100644 index 00000000000..3d1d5d2f6ba --- /dev/null +++ b/source/blender/editors/armature/pose_edit.c @@ -0,0 +1,1180 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Ton Roosendaal, Blender Foundation '05, full recode. + * Joshua Leung + * Reevan McKay (original NaN code) + * + * ***** END GPL LICENSE BLOCK ***** + * + * Pose Mode API's and Operators for Pose Mode armatures + */ + +/** \file blender/editors/armature/pose_edit.c + * \ingroup edarmature + */ + +#include "BLI_math.h" +#include "BLI_blenlib.h" + +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" + +#include "BKE_anim.h" +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" +#include "BKE_report.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_keyframing.h" +#include "ED_screen.h" +#include "ED_object.h" + +#include "UI_interface.h" + +#include "armature_intern.h" + +/* matches logic with ED_operator_posemode_context() */ +Object *ED_pose_object_from_context(bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + Object *ob; + + /* since this call may also be used from the buttons window, we need to check for where to get the object */ + if (sa && sa->spacetype == SPACE_BUTS) { + ob = ED_object_context(C); + } + else { + ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + } + + return ob; +} + +/* This function is used to process the necessary updates for */ +void ED_armature_enter_posemode(bContext *C, Base *base) +{ + ReportList *reports = CTX_wm_reports(C); + Object *ob = base->object; + + if (ob->id.lib) { + BKE_report(reports, RPT_WARNING, "Cannot pose libdata"); + return; + } + + switch (ob->type) { + case OB_ARMATURE: + ob->restore_mode = ob->mode; + ob->mode |= OB_MODE_POSE; + + WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL); + + break; + default: + return; + } + + /* XXX: disabled as this would otherwise cause a nasty loop... */ + //ED_object_toggle_modes(C, ob->mode); +} + +void ED_armature_exit_posemode(bContext *C, Base *base) +{ + if (base) { + Object *ob = base->object; + + ob->restore_mode = ob->mode; + ob->mode &= ~OB_MODE_POSE; + + WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL); + } +} + +/* if a selected or active bone is protected, throw error (oonly if warn == 1) and return 1 */ +/* only_selected == 1: the active bone is allowed to be protected */ +#if 0 /* UNUSED 2.5 */ +static short pose_has_protected_selected(Object *ob, short warn) +{ + /* check protection */ + if (ob->proxy) { + bPoseChannel *pchan; + bArmature *arm = ob->data; + + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->bone && (pchan->bone->layer & arm->layer)) { + if (pchan->bone->layer & arm->layer_protected) { + if (pchan->bone->flag & BONE_SELECTED) + break; + } + } + } + if (pchan) { + if (warn) error("Cannot change Proxy protected bones"); + return 1; + } + } + return 0; +} +#endif + +/* only for real IK, not for auto-IK */ +static int pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan, int level) +{ + bConstraint *con; + Bone *bone; + + /* No need to check if constraint is active (has influence), + * since all constraints with CONSTRAINT_IK_AUTO are active */ + for (con = pchan->constraints.first; con; con = con->next) { + if (con->type == CONSTRAINT_TYPE_KINEMATIC) { + bKinematicConstraint *data = con->data; + if (data->rootbone == 0 || data->rootbone > level) { + if ((data->flag & CONSTRAINT_IK_AUTO) == 0) + return 1; + } + } + } + for (bone = pchan->bone->childbase.first; bone; bone = bone->next) { + pchan = BKE_pose_channel_find_name(ob->pose, bone->name); + if (pchan && pose_channel_in_IK_chain(ob, pchan, level + 1)) + return 1; + } + return 0; +} + +int ED_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan) +{ + return pose_channel_in_IK_chain(ob, pchan, 0); +} + +/* ********************************************** */ +/* Motion Paths */ + +/* For the object with pose/action: update paths for those that have got them + * This should selectively update paths that exist... + * + * To be called from various tools that do incremental updates + */ +void ED_pose_recalculate_paths(Scene *scene, Object *ob) +{ + ListBase targets = {NULL, NULL}; + + /* set flag to force recalc, then grab the relevant bones to target */ + ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS; + animviz_get_object_motionpaths(ob, &targets); + + /* recalculate paths, then free */ + animviz_calc_motionpaths(scene, &targets); + BLI_freelistN(&targets); +} + + +/* show popup to determine settings */ +static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + /* set default settings from existing/stored settings */ + { + bAnimVizSettings *avs = &ob->pose->avs; + PointerRNA avs_ptr; + + RNA_int_set(op->ptr, "start_frame", avs->path_sf); + RNA_int_set(op->ptr, "end_frame", avs->path_ef); + + RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); + RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location")); + } + + /* show popup dialog to allow editing of range... */ + // FIXME: hardcoded dimensions here are just arbitrary + return WM_operator_props_dialog_popup(C, op, 10 * UI_UNIT_X, 10 * UI_UNIT_Y); +} + +/* For the object with pose/action: create path curves for selected bones + * This recalculates the WHOLE path within the pchan->pathsf and pchan->pathef range + */ +static int pose_calculate_paths_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + Scene *scene = CTX_data_scene(C); + + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + /* grab baking settings from operator settings */ + { + bAnimVizSettings *avs = &ob->pose->avs; + PointerRNA avs_ptr; + + avs->path_sf = RNA_int_get(op->ptr, "start_frame"); + avs->path_ef = RNA_int_get(op->ptr, "end_frame"); + + RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); + RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location")); + } + + /* set up path data for bones being calculated */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + /* verify makes sure that the selected bone has a bone with the appropriate settings */ + animviz_verify_motionpaths(op->reports, scene, ob, pchan); + } + CTX_DATA_END; + + /* calculate the bones that now have motionpaths... */ + /* TODO: only make for the selected bones? */ + ED_pose_recalculate_paths(scene, ob); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_paths_calculate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Calculate Bone Paths"; + ot->idname = "POSE_OT_paths_calculate"; + ot->description = "Calculate paths for the selected bones"; + + /* api callbacks */ + ot->invoke = pose_calculate_paths_invoke; + ot->exec = pose_calculate_paths_exec; + ot->poll = ED_operator_posemode_exclusive; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "start_frame", 1, MINAFRAME, MAXFRAME, "Start", + "First frame to calculate bone paths on", MINFRAME, MAXFRAME / 2.0); + RNA_def_int(ot->srna, "end_frame", 250, MINAFRAME, MAXFRAME, "End", + "Last frame to calculate bone paths on", MINFRAME, MAXFRAME / 2.0); + + RNA_def_enum(ot->srna, "bake_location", motionpath_bake_location_items, 0, + "Bake Location", + "Which point on the bones is used when calculating paths"); +} + +/* --------- */ + +static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + Scene *scene = CTX_data_scene(C); + + if (ELEM(NULL, ob, scene)) + return OPERATOR_CANCELLED; + + /* calculate the bones that now have motionpaths... */ + /* TODO: only make for the selected bones? */ + ED_pose_recalculate_paths(scene, ob); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_paths_update(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Update Bone Paths"; + ot->idname = "POSE_OT_paths_update"; + ot->description = "Recalculate paths for bones that already have them"; + + /* api callbakcs */ + ot->exec = pose_update_paths_exec; + ot->poll = ED_operator_posemode_exclusive; /* TODO: this should probably check for active bone and/or existing paths */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* --------- */ + +/* for the object with pose/action: clear path curves for selected bones only */ +static void ED_pose_clear_paths(Object *ob) +{ + bPoseChannel *pchan; + short skipped = 0; + + if (ELEM(NULL, ob, ob->pose)) + return; + + /* free the motionpath blocks, but also take note of whether we skipped some... */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->mpath) { + if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) { + animviz_free_motionpath(pchan->mpath); + pchan->mpath = NULL; + } + else + skipped = 1; + } + } + + /* if we didn't skip any, we shouldn't have any paths left */ + if (skipped == 0) + ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS; +} + +/* operator callback for this */ +static int pose_clear_paths_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + + /* only continue if there's an object */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + /* use the backend function for this */ + ED_pose_clear_paths(ob); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_paths_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Bone Paths"; + ot->idname = "POSE_OT_paths_clear"; + ot->description = "Clear path caches for selected bones"; + + /* api callbacks */ + ot->exec = pose_clear_paths_exec; + ot->poll = ED_operator_posemode_exclusive; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************************************** */ +#if 0 /* UNUSED 2.5 */ +static void pose_copy_menu(Scene *scene) +{ + Object *obedit = scene->obedit; // XXX context + Object *ob = OBACT; + bArmature *arm; + bPoseChannel *pchan, *pchanact; + short nr = 0; + int i = 0; + + /* paranoia checks */ + if (ELEM(NULL, ob, ob->pose)) return; + if ((ob == obedit) || (ob->mode & OB_MODE_POSE) == 0) return; + + pchan = BKE_pose_channel_active(ob); + + if (pchan == NULL) return; + pchanact = pchan; + arm = ob->data; + + /* if proxy-protected bones selected, some things (such as locks + displays) shouldn't be changeable, + * but for constraints (just add local constraints) + */ + if (pose_has_protected_selected(ob, 0)) { + i = BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */ + if (i < 25) + nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|Constraints... %x5"); + else + nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4"); + } + else { + i = BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */ + if (i < 25) + nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|Constraints... %x5|%l|Transform Locks %x6|IK Limits %x7|Bone Shape %x8"); + else + nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|%l|Transform Locks %x6|IK Limits %x7|Bone Shape %x8"); + } + + if (nr <= 0) + return; + + if (nr != 5) { + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if ((arm->layer & pchan->bone->layer) && + (pchan->bone->flag & BONE_SELECTED) && + (pchan != pchanact) ) + { + switch (nr) { + case 1: /* Local Location */ + copy_v3_v3(pchan->loc, pchanact->loc); + break; + case 2: /* Local Rotation */ + copy_qt_qt(pchan->quat, pchanact->quat); + copy_v3_v3(pchan->eul, pchanact->eul); + break; + case 3: /* Local Size */ + copy_v3_v3(pchan->size, pchanact->size); + break; + case 4: /* All Constraints */ + { + ListBase tmp_constraints = {NULL, NULL}; + + /* copy constraints to tmpbase and apply 'local' tags before + * appending to list of constraints for this channel + */ + BKE_copy_constraints(&tmp_constraints, &pchanact->constraints, TRUE); + if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) { + bConstraint *con; + + /* add proxy-local tags */ + for (con = tmp_constraints.first; con; con = con->next) + con->flag |= CONSTRAINT_PROXY_LOCAL; + } + BLI_movelisttolist(&pchan->constraints, &tmp_constraints); + + /* update flags (need to add here, not just copy) */ + pchan->constflag |= pchanact->constflag; + + if (ob->pose) + ob->pose->flag |= POSE_RECALC; + } + break; + case 6: /* Transform Locks */ + pchan->protectflag = pchanact->protectflag; + break; + case 7: /* IK (DOF) settings */ + { + pchan->ikflag = pchanact->ikflag; + copy_v3_v3(pchan->limitmin, pchanact->limitmin); + copy_v3_v3(pchan->limitmax, pchanact->limitmax); + copy_v3_v3(pchan->stiffness, pchanact->stiffness); + pchan->ikstretch = pchanact->ikstretch; + pchan->ikrotweight = pchanact->ikrotweight; + pchan->iklinweight = pchanact->iklinweight; + } + break; + case 8: /* Custom Bone Shape */ + pchan->custom = pchanact->custom; + break; + case 9: /* Visual Location */ + BKE_armature_loc_pose_to_bone(pchan, pchanact->pose_mat[3], pchan->loc); + break; + case 10: /* Visual Rotation */ + { + float delta_mat[4][4]; + + BKE_armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat); + + if (pchan->rotmode == ROT_MODE_AXISANGLE) { + float tmp_quat[4]; + + /* need to convert to quat first (in temp var)... */ + mat4_to_quat(tmp_quat, delta_mat); + quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, tmp_quat); + } + else if (pchan->rotmode == ROT_MODE_QUAT) + mat4_to_quat(pchan->quat, delta_mat); + else + mat4_to_eulO(pchan->eul, pchan->rotmode, delta_mat); + } + break; + case 11: /* Visual Size */ + { + float delta_mat[4][4], size[4]; + + BKE_armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat); + mat4_to_size(size, delta_mat); + copy_v3_v3(pchan->size, size); + } + } + } + } + } + else { /* constraints, optional (note: max we can have is 24 constraints) */ + bConstraint *con, *con_back; + int const_toggle[24] = {0}; /* XXX, initialize as 0 to quiet errors */ + ListBase const_copy = {NULL, NULL}; + + BLI_duplicatelist(&const_copy, &(pchanact->constraints)); + + /* build the puplist of constraints */ + for (con = pchanact->constraints.first, i = 0; con; con = con->next, i++) { + const_toggle[i] = 1; +// add_numbut(i, TOG|INT, con->name, 0, 0, &(const_toggle[i]), ""); + } + +// if (!do_clever_numbuts("Select Constraints", i, REDRAW)) { +// BLI_freelistN(&const_copy); +// return; +// } + + /* now build a new listbase from the options selected */ + for (i = 0, con = const_copy.first; con; i++) { + /* if not selected, free/remove it from the list */ + if (!const_toggle[i]) { + con_back = con->next; + BLI_freelinkN(&const_copy, con); + con = con_back; + } + else + con = con->next; + } + + /* Copy the temo listbase to the selected posebones */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if ((arm->layer & pchan->bone->layer) && + (pchan->bone->flag & BONE_SELECTED) && + (pchan != pchanact) ) + { + ListBase tmp_constraints = {NULL, NULL}; + + /* copy constraints to tmpbase and apply 'local' tags before + * appending to list of constraints for this channel + */ + BKE_copy_constraints(&tmp_constraints, &const_copy, TRUE); + if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) { + /* add proxy-local tags */ + for (con = tmp_constraints.first; con; con = con->next) + con->flag |= CONSTRAINT_PROXY_LOCAL; + } + BLI_movelisttolist(&pchan->constraints, &tmp_constraints); + + /* update flags (need to add here, not just copy) */ + pchan->constflag |= pchanact->constflag; + } + } + BLI_freelistN(&const_copy); + BKE_pose_update_constraint_flags(ob->pose); /* we could work out the flags but its simpler to do this */ + + if (ob->pose) + ob->pose->flag |= POSE_RECALC; + } + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); // and all its relations + + BIF_undo_push("Copy Pose Attributes"); + +} +#endif + +/* ********************************************** */ + +static int pose_flip_names_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm; + + /* paranoia checks */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + arm = ob->data; + + /* loop through selected bones, auto-naming them */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + char newname[MAXBONENAME]; + flip_side_name(newname, pchan->name, TRUE); + ED_armature_bone_rename(arm, pchan->name, newname); + } + CTX_DATA_END; + + /* since we renamed stuff... */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_flip_names(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Flip Names"; + ot->idname = "POSE_OT_flip_names"; + ot->description = "Flips (and corrects) the axis suffixes of the the names of selected bones"; + + /* api callbacks */ + ot->exec = pose_flip_names_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ------------------ */ + +static int pose_autoside_names_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm; + char newname[MAXBONENAME]; + short axis = RNA_enum_get(op->ptr, "axis"); + + /* paranoia checks */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + arm = ob->data; + + /* loop through selected bones, auto-naming them */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + BLI_strncpy(newname, pchan->name, sizeof(newname)); + if (bone_autoside_name(newname, 1, axis, pchan->bone->head[axis], pchan->bone->tail[axis])) + ED_armature_bone_rename(arm, pchan->name, newname); + } + CTX_DATA_END; + + /* since we renamed stuff... */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_autoside_names(wmOperatorType *ot) +{ + static EnumPropertyItem axis_items[] = { + {0, "XAXIS", 0, "X-Axis", "Left/Right"}, + {1, "YAXIS", 0, "Y-Axis", "Front/Back"}, + {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "AutoName by Axis"; + ot->idname = "POSE_OT_autoside_names"; + ot->description = "Automatically renames the selected bones according to which side of the target axis they fall on"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = pose_autoside_names_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* settings */ + ot->prop = RNA_def_enum(ot->srna, "axis", axis_items, 0, "Axis", "Axis tag names with"); +} + +/* ********************************************** */ + +static int pose_bone_rotmode_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + int mode = RNA_enum_get(op->ptr, "type"); + + /* set rotation mode of selected bones */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + pchan->rotmode = mode; + } + CTX_DATA_END; + + /* notifiers and updates */ + DAG_id_tag_update((ID *)ob, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_rotation_mode_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Rotation Mode"; + ot->idname = "POSE_OT_rotation_mode_set"; + ot->description = "Set the rotation representation used by selected bones"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = pose_bone_rotmode_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", posebone_rotmode_items, 0, "Rotation Mode", ""); +} + +/* ********************************************** */ + +/* Show all armature layers */ +static int pose_armature_layers_showall_poll(bContext *C) +{ + /* this single operator can be used in posemode OR editmode for armatures */ + return ED_operator_posemode(C) || ED_operator_editarmature(C); +} + +static int pose_armature_layers_showall_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = (ob) ? ob->data : NULL; + PointerRNA ptr; + int maxLayers = (RNA_boolean_get(op->ptr, "all")) ? 32 : 16; + int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + int i; + + /* sanity checking */ + if (arm == NULL) + return OPERATOR_CANCELLED; + + /* use RNA to set the layers + * although it would be faster to just set directly using bitflags, we still + * need to setup a RNA pointer so that we get the "update" callbacks for free... + */ + RNA_id_pointer_create(&arm->id, &ptr); + + for (i = 0; i < maxLayers; i++) + layers[i] = 1; + + RNA_boolean_set_array(&ptr, "layers", layers); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + /* done */ + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_layers_show_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Show All Layers"; + ot->idname = "ARMATURE_OT_layers_show_all"; + ot->description = "Make all armature layers visible"; + + /* callbacks */ + ot->exec = pose_armature_layers_showall_exec; + ot->poll = pose_armature_layers_showall_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All Layers", "Enable all layers or just the first 16 (top row)"); +} + +/* ------------------- */ + +/* Present a popup to get the layers that should be used */ +static int pose_armature_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = (ob) ? ob->data : NULL; + PointerRNA ptr; + int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + + /* sanity checking */ + if (arm == NULL) + return OPERATOR_CANCELLED; + + /* get RNA pointer to armature data to use that to retrieve the layers as ints to init the operator */ + RNA_id_pointer_create((ID *)arm, &ptr); + RNA_boolean_get_array(&ptr, "layers", layers); + RNA_boolean_set_array(op->ptr, "layers", layers); + + /* part to sync with other similar operators... */ + return WM_operator_props_popup(C, op, evt); +} + +/* Set the visible layers for the active armature (edit and pose modes) */ +static int pose_armature_layers_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + PointerRNA ptr; + int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + + if (ELEM(NULL, ob, ob->data)) { + return OPERATOR_CANCELLED; + } + + /* get the values set in the operator properties */ + RNA_boolean_get_array(op->ptr, "layers", layers); + + /* get pointer for armature, and write data there... */ + RNA_id_pointer_create((ID *)ob->data, &ptr); + RNA_boolean_set_array(&ptr, "layers", layers); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + + +void POSE_OT_armature_layers(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Armature Layers"; + ot->idname = "POSE_OT_armature_layers"; + ot->description = "Change the visible armature layers"; + + /* callbacks */ + ot->invoke = pose_armature_layers_invoke; + ot->exec = pose_armature_layers_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible"); +} + +void ARMATURE_OT_armature_layers(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Armature Layers"; + ot->idname = "ARMATURE_OT_armature_layers"; + ot->description = "Change the visible armature layers"; + + /* callbacks */ + ot->invoke = pose_armature_layers_invoke; + ot->exec = pose_armature_layers_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible"); +} + +/* ------------------- */ + +/* Present a popup to get the layers that should be used */ +static int pose_bone_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + + /* get layers that are active already */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + short bit; + + /* loop over the bits for this pchan's layers, adding layers where they're needed */ + for (bit = 0; bit < 32; bit++) { + if (pchan->bone->layer & (1 << bit)) + layers[bit] = 1; + } + } + CTX_DATA_END; + + /* copy layers to operator */ + RNA_boolean_set_array(op->ptr, "layers", layers); + + /* part to sync with other similar operators... */ + return WM_operator_props_popup(C, op, evt); +} + +/* Set the visible layers for the active armature (edit and pose modes) */ +static int pose_bone_layers_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + PointerRNA ptr; + int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + + if (ob == NULL || ob->data == NULL) { + return OPERATOR_CANCELLED; + } + + /* get the values set in the operator properties */ + RNA_boolean_get_array(op->ptr, "layers", layers); + + /* set layers of pchans based on the values set in the operator props */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + /* get pointer for pchan, and write flags this way */ + RNA_pointer_create((ID *)ob->data, &RNA_Bone, pchan->bone, &ptr); + RNA_boolean_set_array(&ptr, "layers", layers); + } + CTX_DATA_END; + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_bone_layers(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Bone Layers"; + ot->idname = "POSE_OT_bone_layers"; + ot->description = "Change the layers that the selected bones belong to"; + + /* callbacks */ + ot->invoke = pose_bone_layers_invoke; + ot->exec = pose_bone_layers_exec; + ot->poll = ED_operator_posemode_exclusive; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to"); +} + +/* ------------------- */ + +/* Present a popup to get the layers that should be used */ +static int armature_bone_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt) +{ + int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + + /* get layers that are active already */ + CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) + { + short bit; + + /* loop over the bits for this pchan's layers, adding layers where they're needed */ + for (bit = 0; bit < 32; bit++) { + if (ebone->layer & (1 << bit)) + layers[bit] = 1; + } + } + CTX_DATA_END; + + /* copy layers to operator */ + RNA_boolean_set_array(op->ptr, "layers", layers); + + /* part to sync with other similar operators... */ + return WM_operator_props_popup(C, op, evt); +} + +/* Set the visible layers for the active armature (edit and pose modes) */ +static int armature_bone_layers_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_edit_object(C); + bArmature *arm = (ob) ? ob->data : NULL; + PointerRNA ptr; + int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ + + /* get the values set in the operator properties */ + RNA_boolean_get_array(op->ptr, "layers", layers); + + /* set layers of pchans based on the values set in the operator props */ + CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) + { + /* get pointer for pchan, and write flags this way */ + RNA_pointer_create((ID *)arm, &RNA_EditBone, ebone, &ptr); + RNA_boolean_set_array(&ptr, "layers", layers); + } + CTX_DATA_END; + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_bone_layers(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Bone Layers"; + ot->idname = "ARMATURE_OT_bone_layers"; + ot->description = "Change the layers that the selected bones belong to"; + + /* callbacks */ + ot->invoke = armature_bone_layers_invoke; + ot->exec = armature_bone_layers_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to"); +} + +/* ********************************************** */ +/* Show/Hide Bones */ + +static int hide_selected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) +{ + bArmature *arm = ob->data; + + if (arm->layer & bone->layer) { + if (bone->flag & BONE_SELECTED) { + bone->flag |= BONE_HIDDEN_P; + bone->flag &= ~BONE_SELECTED; + if (arm->act_bone == bone) + arm->act_bone = NULL; + } + } + return 0; +} + +static int hide_unselected_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) +{ + bArmature *arm = ob->data; + + if (arm->layer & bone->layer) { + /* hrm... typo here? */ + if ((bone->flag & BONE_SELECTED) == 0) { + bone->flag |= BONE_HIDDEN_P; + if (arm->act_bone == bone) + arm->act_bone = NULL; + } + } + return 0; +} + +/* active object is armature in posemode, poll checked */ +static int pose_hide_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = ob->data; + + if (RNA_boolean_get(op->ptr, "unselected")) + bone_looper(ob, arm->bonebase.first, NULL, hide_unselected_pose_bone_cb); + else + bone_looper(ob, arm->bonebase.first, NULL, hide_selected_pose_bone_cb); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Selected"; + ot->idname = "POSE_OT_hide"; + ot->description = "Tag selected bones to not be visible in Pose Mode"; + + /* api callbacks */ + ot->exec = pose_hide_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", ""); +} + +static int show_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) +{ + bArmature *arm = ob->data; + + if (arm->layer & bone->layer) { + if (bone->flag & BONE_HIDDEN_P) { + bone->flag &= ~BONE_HIDDEN_P; + bone->flag |= BONE_SELECTED; + } + } + + return 0; +} + +/* active object is armature in posemode, poll checked */ +static int pose_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = ob->data; + + bone_looper(ob, arm->bonebase.first, NULL, show_pose_bone_cb); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Reveal Selected"; + ot->idname = "POSE_OT_reveal"; + ot->description = "Unhide all bones that have been tagged to be hidden in Pose Mode"; + + /* api callbacks */ + ot->exec = pose_reveal_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************************************** */ +/* Flip Quats */ + +static int pose_flip_quats_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID); + + /* loop through all selected pchans, flipping and keying (as needed) */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + /* only if bone is using quaternion rotation */ + if (pchan->rotmode == ROT_MODE_QUAT) { + /* quaternions have 720 degree range */ + negate_v4(pchan->quat); + + ED_autokeyframe_pchan(C, scene, ob, pchan, ks); + } + } + CTX_DATA_END; + + /* notifiers and updates */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_quaternions_flip(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Flip Quats"; + ot->idname = "POSE_OT_quaternions_flip"; + ot->description = "Flip quaternion values to achieve desired rotations, while maintaining the same orientations"; + + /* callbacks */ + ot->exec = pose_flip_quats_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c new file mode 100644 index 00000000000..06a88f013d7 --- /dev/null +++ b/source/blender/editors/armature/pose_group.c @@ -0,0 +1,523 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation + * All rights reserved. + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + * + * Implementation of Bone Groups operators and editing API's + */ + +/** \file blender/editors/armature/pose_group.c + * \ingroup edarmature + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" + +#include "DNA_armature_types.h" +#include "DNA_object_types.h" + +#include "BKE_action.h" +#include "BKE_context.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "armature_intern.h" + +/* ********************************************** */ +/* Bone Groups */ + +static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_pose_object_from_context(C); + + /* only continue if there's an object */ + if (ob == NULL) + return OPERATOR_CANCELLED; + + /* for now, just call the API function for this */ + BKE_pose_add_group(ob); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_group_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Bone Group"; + ot->idname = "POSE_OT_group_add"; + ot->description = "Add a new bone group"; + + /* api callbacks */ + ot->exec = pose_group_add_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +static int pose_group_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_pose_object_from_context(C); + + /* only continue if there's an object */ + if (ob == NULL) + return OPERATOR_CANCELLED; + + /* for now, just call the API function for this */ + BKE_pose_remove_group(ob); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_group_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Bone Group"; + ot->idname = "POSE_OT_group_remove"; + ot->description = "Remove the active bone group"; + + /* api callbacks */ + ot->exec = pose_group_remove_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ------------ */ + +/* invoke callback which presents a list of bone-groups for the user to choose from */ +static int pose_groups_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt)) +{ + Object *ob = ED_pose_object_from_context(C); + bPose *pose; + + uiPopupMenu *pup; + uiLayout *layout; + bActionGroup *grp; + int i; + + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + pose = ob->pose; + + /* if there's no active group (or active is invalid), create a new menu to find it */ + if (pose->active_group <= 0) { + /* create a new menu, and start populating it with group names */ + pup = uiPupMenuBegin(C, op->type->name, ICON_NONE); + layout = uiPupMenuLayout(pup); + + /* special entry - allow to create new group, then use that + * (not to be used for removing though) + */ + if (strstr(op->idname, "assign")) { + uiItemIntO(layout, "New Group", ICON_NONE, op->idname, "type", 0); + uiItemS(layout); + } + + /* add entries for each group */ + for (grp = pose->agroups.first, i = 1; grp; grp = grp->next, i++) + uiItemIntO(layout, grp->name, ICON_NONE, op->idname, "type", i); + + /* finish building the menu, and process it (should result in calling self again) */ + uiPupMenuEnd(C, pup); + + return OPERATOR_CANCELLED; + } + else { + /* just use the active group index, and call the exec callback for the calling operator */ + RNA_int_set(op->ptr, "type", pose->active_group); + return op->type->exec(C, op); + } +} + +/* Assign selected pchans to the bone group that the user selects */ +static int pose_group_assign_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_pose_object_from_context(C); + bPose *pose; + short done = FALSE; + + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + pose = ob->pose; + + /* set the active group number to the one from operator props + * - if 0 after this, make a new group... + */ + pose->active_group = RNA_int_get(op->ptr, "type"); + if (pose->active_group == 0) + BKE_pose_add_group(ob); + + /* add selected bones to group then */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + pchan->agrp_index = pose->active_group; + done = TRUE; + } + CTX_DATA_END; + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + /* report done status */ + if (done) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; +} + +void POSE_OT_group_assign(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Selected to Bone Group"; + ot->idname = "POSE_OT_group_assign"; + ot->description = "Add selected bones to the chosen bone group"; + + /* api callbacks */ + ot->invoke = pose_groups_menu_invoke; + ot->exec = pose_group_assign_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "type", 0, 0, INT_MAX, "Bone Group Index", "", 0, 10); +} + + +static int pose_group_unassign_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_pose_object_from_context(C); + short done = FALSE; + + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + /* find selected bones to remove from all bone groups */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) + { + if (pchan->agrp_index) { + pchan->agrp_index = 0; + done = TRUE; + } + } + CTX_DATA_END; + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + /* report done status */ + if (done) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; +} + +void POSE_OT_group_unassign(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Selected from Bone Groups"; + ot->idname = "POSE_OT_group_unassign"; + ot->description = "Remove selected bones from all bone groups"; + + /* api callbacks */ + ot->exec = pose_group_unassign_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int group_move_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_pose_object_from_context(C); + bPose *pose = (ob) ? ob->pose : NULL; + bPoseChannel *pchan; + bActionGroup *grp; + int dir = RNA_enum_get(op->ptr, "direction"); + int grpIndexA, grpIndexB; + + if (ELEM(NULL, ob, pose)) + return OPERATOR_CANCELLED; + if (pose->active_group <= 0) + return OPERATOR_CANCELLED; + + /* get group to move */ + grp = BLI_findlink(&pose->agroups, pose->active_group - 1); + if (grp == NULL) + return OPERATOR_CANCELLED; + + /* move bone group */ + grpIndexA = pose->active_group; + if (dir == 1) { /* up */ + void *prev = grp->prev; + + if (prev == NULL) + return OPERATOR_FINISHED; + + BLI_remlink(&pose->agroups, grp); + BLI_insertlinkbefore(&pose->agroups, prev, grp); + + grpIndexB = grpIndexA - 1; + pose->active_group--; + } + else { /* down */ + void *next = grp->next; + + if (next == NULL) + return OPERATOR_FINISHED; + + BLI_remlink(&pose->agroups, grp); + BLI_insertlinkafter(&pose->agroups, next, grp); + + grpIndexB = grpIndexA + 1; + pose->active_group++; + } + + /* fix changed bone group indices in bones (swap grpIndexA with grpIndexB) */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->agrp_index == grpIndexB) + pchan->agrp_index = grpIndexA; + else if (pchan->agrp_index == grpIndexA) + pchan->agrp_index = grpIndexB; + } + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_group_move(wmOperatorType *ot) +{ + static EnumPropertyItem group_slot_move[] = { + {1, "UP", 0, "Up", ""}, + {-1, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Move Bone Group"; + ot->idname = "POSE_OT_group_move"; + ot->description = "Change position of active Bone Group in list of Bone Groups"; + + /* api callbacks */ + ot->exec = group_move_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "direction", group_slot_move, 0, "Direction", "Direction to move, UP or DOWN"); +} + +/* bone group sort element */ +typedef struct tSortActionGroup { + bActionGroup *agrp; + int index; +} tSortActionGroup; + +/* compare bone groups by name */ +static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr) +{ + tSortActionGroup *sgrp_a = (tSortActionGroup *)sgrp_a_ptr; + tSortActionGroup *sgrp_b = (tSortActionGroup *)sgrp_b_ptr; + + return strcmp(sgrp_a->agrp->name, sgrp_b->agrp->name); +} + +static int group_sort_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_pose_object_from_context(C); + bPose *pose = (ob) ? ob->pose : NULL; + bPoseChannel *pchan; + tSortActionGroup *agrp_array; + bActionGroup *agrp; + int agrp_count; + int i; + + if (ELEM(NULL, ob, pose)) + return OPERATOR_CANCELLED; + if (pose->active_group <= 0) + return OPERATOR_CANCELLED; + + /* create temporary array with bone groups and indices */ + agrp_count = BLI_countlist(&pose->agroups); + agrp_array = MEM_mallocN(sizeof(tSortActionGroup) * agrp_count, "sort bone groups"); + for (agrp = pose->agroups.first, i = 0; agrp; agrp = agrp->next, i++) { + BLI_assert(i < agrp_count); + agrp_array[i].agrp = agrp; + agrp_array[i].index = i + 1; + } + + /* sort bone groups by name */ + qsort(agrp_array, agrp_count, sizeof(tSortActionGroup), compare_agroup); + + /* create sorted bone group list from sorted array */ + pose->agroups.first = pose->agroups.last = NULL; + for (i = 0; i < agrp_count; i++) { + BLI_addtail(&pose->agroups, agrp_array[i].agrp); + } + + /* fix changed bone group indizes in bones */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + for (i = 0; i < agrp_count; i++) { + if (pchan->agrp_index == agrp_array[i].index) { + pchan->agrp_index = i + 1; + break; + } + } + } + + /* free temp resources */ + MEM_freeN(agrp_array); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_group_sort(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Sort Bone Groups"; + ot->idname = "POSE_OT_group_sort"; + ot->description = "Sort Bone Groups by their names in ascending order"; + + /* api callbacks */ + ot->exec = group_sort_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static void pose_group_select(bContext *C, Object *ob, int select) +{ + bPose *pose = ob->pose; + + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + { + if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { + if (select) { + if (pchan->agrp_index == pose->active_group) + pchan->bone->flag |= BONE_SELECTED; + } + else { + if (pchan->agrp_index == pose->active_group) + pchan->bone->flag &= ~BONE_SELECTED; + } + } + } + CTX_DATA_END; +} + +static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_pose_object_from_context(C); + + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + pose_group_select(C, ob, 1); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_group_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Bones of Bone Group"; + ot->idname = "POSE_OT_group_select"; + ot->description = "Select bones in active Bone Group"; + + /* api callbacks */ + ot->exec = pose_group_select_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int pose_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_pose_object_from_context(C); + + /* only continue if there's an object, and a pose there too */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + pose_group_select(C, ob, 0); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_group_deselect(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Deselect Bone Group"; + ot->idname = "POSE_OT_group_deselect"; + ot->description = "Deselect bones of active Bone Group"; + + /* api callbacks */ + ot->exec = pose_group_deselect_exec; + ot->poll = ED_operator_posemode_context; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************************************** */ diff --git a/source/blender/editors/armature/poselib.c b/source/blender/editors/armature/pose_lib.c index 48c0a4a38c3..457874c7ae6 100644 --- a/source/blender/editors/armature/poselib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -23,25 +23,17 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/editors/armature/poselib.c +/** \file blender/editors/armature/pose_lib.c * \ingroup edarmature */ - -#include <stdlib.h> -#include <stdio.h> -#include <stddef.h> #include <string.h> #include <math.h> -#include <float.h> #include "MEM_guardedalloc.h" -#include "BLI_math.h" #include "BLI_blenlib.h" -#include "BLI_dynstr.h" #include "BLI_dlrbTree.h" -#include "BLI_utildefines.h" #include "BLF_translation.h" diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c new file mode 100644 index 00000000000..186c7a1fe89 --- /dev/null +++ b/source/blender/editors/armature/pose_select.c @@ -0,0 +1,853 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/armature/pose_select.c + * \ingroup edarmature + */ + +#include <string.h> + +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_constraint.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_keyframing.h" +#include "ED_mesh.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "armature_intern.h" + +/* ***************** Pose Select Utilities ********************* */ + +/* called from editview.c, for mode-less pose selection */ +/* assumes scene obact and basact is still on old situation */ +int ED_do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, + short extend, short deselect, short toggle) +{ + Object *ob = base->object; + Bone *nearBone; + + if (!ob || !ob->pose) return 0; + + nearBone = get_bone_from_selectbuffer(scene, base, buffer, hits, 1); + + /* if the bone cannot be affected, don't do anything */ + if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) { + Object *ob_act = OBACT; + bArmature *arm = ob->data; + + /* since we do unified select, we don't shift+select a bone if the + * armature object was not active yet. + * note, special exception for armature mode so we can do multi-select + * we could check for multi-select explicitly but think its fine to + * always give predictable behavior in weight paint mode - campbell */ + if ((!extend && !deselect && !toggle) || + ((ob_act && (ob_act != ob) && (ob_act->mode & OB_MODE_WEIGHT_PAINT) == 0))) + { + ED_pose_deselectall(ob, 0); + nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = nearBone; + } + else { + if (extend) { + nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = nearBone; + } + else if (deselect) { + nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else if (toggle) { + if (nearBone->flag & BONE_SELECTED) { + /* if not active, we make it active */ + if (nearBone != arm->act_bone) { + arm->act_bone = nearBone; + } + else { + nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + else { + nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + arm->act_bone = nearBone; + } + } + } + + if (ob_act) { + /* in weightpaint we select the associated vertex group too */ + if (ob_act->mode & OB_MODE_WEIGHT_PAINT) { + if (nearBone == arm->act_bone) { + ED_vgroup_select_by_name(ob_act, nearBone->name); + DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA); + } + } + /* if there are some dependencies for visualizing armature state + * (e.g. Mask Modifier in 'Armature' mode), force update + */ + else if (arm->flag & ARM_HAS_VIZ_DEPS) { + DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA); + } + } + } + + return nearBone != NULL; +} + +/* test==0: deselect all + * test==1: swap select (apply to all the opposite of current situation) + * test==2: only clear active tag + * test==3: swap select (no test / inverse selection status of all independently) + */ +void ED_pose_deselectall(Object *ob, int test) +{ + bArmature *arm = ob->data; + bPoseChannel *pchan; + int selectmode = 0; + + /* we call this from outliner too */ + if (ob->pose == NULL) { + return; + } + + /* Determine if we're selecting or deselecting */ + if (test == 1) { + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (PBONE_VISIBLE(arm, pchan->bone)) { + if (pchan->bone->flag & BONE_SELECTED) + break; + } + } + + if (pchan == NULL) + selectmode = 1; + } + else if (test == 2) + selectmode = 2; + + /* Set the flags accordingly */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + /* ignore the pchan if it isn't visible or if its selection cannot be changed */ + if ((pchan->bone->layer & arm->layer) && !(pchan->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) { + if (test == 3) { + pchan->bone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + if (selectmode == 0) pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + else if (selectmode == 1) pchan->bone->flag |= BONE_SELECTED; + } + } + } +} + +/* ***************** Selections ********************** */ + +static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend) +{ + Bone *curBone; + + /* stop when unconnected child is encontered, or when unselectable bone is encountered */ + if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE)) + return; + + /* XXX old cruft! use notifiers instead */ + //select_actionchannel_by_name (ob->action, bone->name, !(shift)); + + if (extend) + bone->flag &= ~BONE_SELECTED; + else + bone->flag |= BONE_SELECTED; + + for (curBone = bone->childbase.first; curBone; curBone = curBone->next) + selectconnected_posebonechildren(ob, curBone, extend); +} + +/* within active object context */ +/* previously known as "selectconnected_posearmature" */ +static int pose_select_connected_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = (bArmature *)ob->data; + Bone *bone, *curBone, *next = NULL; + int extend = RNA_boolean_get(op->ptr, "extend"); + + view3d_operator_needs_opengl(C); + + if (extend) + bone = get_nearest_bone(C, 0, event->mval[0], event->mval[1]); + else + bone = get_nearest_bone(C, 1, event->mval[0], event->mval[1]); + + if (!bone) + return OPERATOR_CANCELLED; + + /* Select parents */ + for (curBone = bone; curBone; curBone = next) { + /* ignore bone if cannot be selected */ + if ((curBone->flag & BONE_UNSELECTABLE) == 0) { + if (extend) + curBone->flag &= ~BONE_SELECTED; + else + curBone->flag |= BONE_SELECTED; + + if (curBone->flag & BONE_CONNECTED) + next = curBone->parent; + else + next = NULL; + } + else + next = NULL; + } + + /* Select children */ + for (curBone = bone->childbase.first; curBone; curBone = next) + selectconnected_posebonechildren(ob, curBone, extend); + + /* updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* mask modifier ('armature' mode), etc. */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + + return OPERATOR_FINISHED; +} + +static int pose_select_linked_poll(bContext *C) +{ + return (ED_operator_view3d_active(C) && ED_operator_posemode(C)); +} + +void POSE_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Connected"; + ot->idname = "POSE_OT_select_linked"; + ot->description = "Select bones related to selected ones by parent/child relationships"; + + /* api callbacks */ + ot->exec = NULL; + ot->invoke = pose_select_connected_invoke; + ot->poll = pose_select_linked_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first"); +} + +/* -------------------------------------- */ + +static int pose_de_select_all_exec(bContext *C, wmOperator *op) +{ + int action = RNA_enum_get(op->ptr, "action"); + + Scene *scene = CTX_data_scene(C); + Object *ob = ED_object_context(C); + bArmature *arm = ob->data; + int multipaint = scene->toolsettings->multipaint; + + if (action == SEL_TOGGLE) { + action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT; + } + + /* Set the flags */ + CTX_DATA_BEGIN(C, bPoseChannel *, pchan, visible_pose_bones) + { + /* select pchan only if selectable, but deselect works always */ + switch (action) { + case SEL_SELECT: + if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) + pchan->bone->flag |= BONE_SELECTED; + break; + case SEL_DESELECT: + pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + break; + case SEL_INVERT: + if (pchan->bone->flag & BONE_SELECTED) { + pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { + pchan->bone->flag |= BONE_SELECTED; + } + break; + } + } + CTX_DATA_END; + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); + + /* weightpaint or mask modifiers need depsgraph updates */ + if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) { + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + + return OPERATOR_FINISHED; +} + +void POSE_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "(De)select All"; + ot->idname = "POSE_OT_select_all"; + ot->description = "Toggle selection status of all bones"; + + /* api callbacks */ + ot->exec = pose_de_select_all_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_select_all(ot); +} + +/* -------------------------------------- */ + +static int pose_select_parent_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = (bArmature *)ob->data; + bPoseChannel *pchan, *parent; + + /* Determine if there is an active bone */ + pchan = CTX_data_active_pose_bone(C); + if (pchan) { + parent = pchan->parent; + if ((parent) && !(parent->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) { + parent->bone->flag |= BONE_SELECTED; + arm->act_bone = parent->bone; + } + else { + return OPERATOR_CANCELLED; + } + } + else { + return OPERATOR_CANCELLED; + } + + /* updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* mask modifier ('armature' mode), etc. */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + + return OPERATOR_FINISHED; +} + +void POSE_OT_select_parent(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Parent Bone"; + ot->idname = "POSE_OT_select_parent"; + ot->description = "Select bones that are parents of the currently selected bones"; + + /* api callbacks */ + ot->exec = pose_select_parent_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------------------- */ + +static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = (bArmature *)ob->data; + bConstraint *con; + int found = 0; + + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + { + if (pchan->bone->flag & BONE_SELECTED) { + for (con = pchan->constraints.first; con; con = con->next) { + bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + if (cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + if ((ct->tar == ob) && (ct->subtarget[0])) { + bPoseChannel *pchanc = BKE_pose_channel_find_name(ob->pose, ct->subtarget); + if ((pchanc) && !(pchanc->bone->flag & BONE_UNSELECTABLE)) { + pchanc->bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; + found = 1; + } + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 1); + } + } + } + } + CTX_DATA_END; + + if (!found) + return OPERATOR_CANCELLED; + + /* updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* mask modifier ('armature' mode), etc. */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + + return OPERATOR_FINISHED; +} + +void POSE_OT_select_constraint_target(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Constraint Target"; + ot->idname = "POSE_OT_select_constraint_target"; + ot->description = "Select bones used as targets for the currently selected bones"; + + /* api callbacks */ + ot->exec = pose_select_constraint_target_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------------------- */ + +static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = ob->data; + Bone *curbone, *pabone, *chbone; + int direction = RNA_enum_get(op->ptr, "direction"); + int add_to_sel = RNA_boolean_get(op->ptr, "extend"); + int found = 0; + + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + { + curbone = pchan->bone; + + if ((curbone->flag & BONE_UNSELECTABLE) == 0) { + if (curbone == arm->act_bone) { + if (direction == BONE_SELECT_PARENT) { + if (pchan->parent == NULL) continue; + else pabone = pchan->parent->bone; + + if (PBONE_SELECTABLE(arm, pabone)) { + if (!add_to_sel) curbone->flag &= ~BONE_SELECTED; + pabone->flag |= BONE_SELECTED; + arm->act_bone = pabone; + + found = 1; + break; + } + } + else { /* direction == BONE_SELECT_CHILD */ + /* the child member is only assigned to connected bones, see [#30340] */ +#if 0 + if (pchan->child == NULL) continue; + else chbone = pchan->child->bone; +#else + /* instead. find _any_ visible child bone, using the first one is a little arbitrary - campbell */ + chbone = pchan->child ? pchan->child->bone : NULL; + if (chbone == NULL) { + bPoseChannel *pchan_child; + + for (pchan_child = ob->pose->chanbase.first; pchan_child; pchan_child = pchan_child->next) { + /* possible we have multiple children, some invisible */ + if (PBONE_SELECTABLE(arm, pchan_child->bone)) { + if (pchan_child->parent == pchan) { + chbone = pchan_child->bone; + break; + } + } + } + } + + if (chbone == NULL) continue; +#endif + + if (PBONE_SELECTABLE(arm, chbone)) { + if (!add_to_sel) curbone->flag &= ~BONE_SELECTED; + chbone->flag |= BONE_SELECTED; + arm->act_bone = chbone; + + found = 1; + break; + } + } + } + } + } + CTX_DATA_END; + + if (found == 0) + return OPERATOR_CANCELLED; + + /* updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* mask modifier ('armature' mode), etc. */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + + return OPERATOR_FINISHED; +} + +void POSE_OT_select_hierarchy(wmOperatorType *ot) +{ + static EnumPropertyItem direction_items[] = { + {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""}, + {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Select Hierarchy"; + ot->idname = "POSE_OT_select_hierarchy"; + ot->description = "Select immediate parent/children of selected bones"; + + /* api callbacks */ + ot->exec = pose_select_hierarchy_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + ot->prop = RNA_def_enum(ot->srna, "direction", direction_items, BONE_SELECT_PARENT, "Direction", ""); + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); +} + +/* -------------------------------------- */ + +static short pose_select_same_group(bContext *C, Object *ob, short extend) +{ + bArmature *arm = (ob) ? ob->data : NULL; + bPose *pose = (ob) ? ob->pose : NULL; + char *group_flags; + int numGroups = 0; + short changed = 0, tagged = 0; + + /* sanity checks */ + if (ELEM3(NULL, ob, pose, arm)) + return 0; + + /* count the number of groups */ + numGroups = BLI_countlist(&pose->agroups); + if (numGroups == 0) + return 0; + + /* alloc a small array to keep track of the groups to use + * - each cell stores on/off state for whether group should be used + * - size is (numGroups + 1), since (index = 0) is used for no-group + */ + group_flags = MEM_callocN(numGroups + 1, "pose_select_same_group"); + + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + { + /* keep track of group as group to use later? */ + if (pchan->bone->flag & BONE_SELECTED) { + group_flags[pchan->agrp_index] = 1; + tagged = 1; + } + + /* deselect all bones before selecting new ones? */ + if ((extend == 0) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) + pchan->bone->flag &= ~BONE_SELECTED; + } + CTX_DATA_END; + + /* small optimization: only loop through bones a second time if there are any groups tagged */ + if (tagged) { + /* only if group matches (and is not selected or current bone) */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + { + if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { + /* check if the group used by this bone is counted */ + if (group_flags[pchan->agrp_index]) { + pchan->bone->flag |= BONE_SELECTED; + changed = 1; + } + } + } + CTX_DATA_END; + } + + /* free temp info */ + MEM_freeN(group_flags); + + return changed; +} + +static short pose_select_same_layer(bContext *C, Object *ob, short extend) +{ + bPose *pose = (ob) ? ob->pose : NULL; + bArmature *arm = (ob) ? ob->data : NULL; + short changed = 0; + int layers = 0; + + if (ELEM3(NULL, ob, pose, arm)) + return 0; + + /* figure out what bones are selected */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + { + /* keep track of layers to use later? */ + if (pchan->bone->flag & BONE_SELECTED) + layers |= pchan->bone->layer; + + /* deselect all bones before selecting new ones? */ + if ((extend == 0) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) + pchan->bone->flag &= ~BONE_SELECTED; + } + CTX_DATA_END; + if (layers == 0) + return 0; + + /* select bones that are on same layers as layers flag */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + { + /* if bone is on a suitable layer, and the bone can have its selection changed, select it */ + if ((layers & pchan->bone->layer) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) { + pchan->bone->flag |= BONE_SELECTED; + changed = 1; + } + } + CTX_DATA_END; + + return changed; +} + +static int pose_select_same_keyingset(bContext *C, Object *ob, short extend) +{ + KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C)); + KS_Path *ksp; + + bArmature *arm = (ob) ? ob->data : NULL; + bPose *pose = (ob) ? ob->pose : NULL; + short changed = 0; + + /* sanity checks: validate Keying Set and object */ + if ((ks == NULL) || (ANIM_validate_keyingset(C, NULL, ks) != 0)) + return 0; + + if (ELEM3(NULL, ob, pose, arm)) + return 0; + + /* if not extending selection, deselect all selected first */ + if (extend == 0) { + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + { + if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) + pchan->bone->flag &= ~BONE_SELECTED; + } + CTX_DATA_END; + } + + /* iterate over elements in the Keying Set, setting selection depending on whether + * that bone is visible or not... + */ + for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + /* only items related to this object will be relevant */ + if ((ksp->id == &ob->id) && (ksp->rna_path != NULL)) { + if (strstr(ksp->rna_path, "bones")) { + char *boneName = BLI_str_quoted_substrN(ksp->rna_path, "bones["); + + if (boneName) { + bPoseChannel *pchan = BKE_pose_channel_find_name(pose, boneName); + + if (pchan) { + /* select if bone is visible and can be affected */ + if (PBONE_SELECTABLE(arm, pchan->bone)) { + pchan->bone->flag |= BONE_SELECTED; + changed = 1; + } + } + + /* free temp memory */ + MEM_freeN(boneName); + } + } + } + } + + return changed; +} + +static int pose_select_grouped_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + bArmature *arm = (bArmature *)ob->data; + short extend = RNA_boolean_get(op->ptr, "extend"); + short changed = 0; + + /* sanity check */ + if (ob->pose == NULL) + return OPERATOR_CANCELLED; + + /* selection types + * NOTE: for the order of these, see the enum in POSE_OT_select_grouped() + */ + switch (RNA_enum_get(op->ptr, "type")) { + case 1: /* group */ + changed = pose_select_same_group(C, ob, extend); + break; + case 2: /* Keying Set */ + changed = pose_select_same_keyingset(C, ob, extend); + break; + default: /* layer */ + changed = pose_select_same_layer(C, ob, extend); + break; + } + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* mask modifier ('armature' mode), etc. */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + + /* report done status */ + if (changed) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; +} + +void POSE_OT_select_grouped(wmOperatorType *ot) +{ + static EnumPropertyItem prop_select_grouped_types[] = { + {0, "LAYER", 0, "Layer", "Shared layers"}, + {1, "GROUP", 0, "Group", "Shared group"}, + {2, "KEYINGSET", 0, "Keying Set", "All bones affected by active Keying Set"}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Select Grouped"; + ot->description = "Select all visible bones grouped by similar properties"; + ot->idname = "POSE_OT_select_grouped"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = pose_select_grouped_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first"); + ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", ""); +} + +/* -------------------------------------- */ + +/* context active object, or weightpainted object with armature in posemode */ +static int pose_bone_flip_active_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob_act = CTX_data_active_object(C); + Object *ob = BKE_object_pose_armature_get(ob_act); + + if (ob && (ob->mode & OB_MODE_POSE)) { + bArmature *arm = ob->data; + + if (arm->act_bone) { + bPoseChannel *pchanf; + char name[MAXBONENAME]; + flip_side_name(name, arm->act_bone->name, TRUE); + + pchanf = BKE_pose_channel_find_name(ob->pose, name); + if (pchanf && pchanf->bone != arm->act_bone) { + arm->act_bone->flag &= ~BONE_SELECTED; + pchanf->bone->flag |= BONE_SELECTED; + + arm->act_bone = pchanf->bone; + + /* in weightpaint we select the associated vertex group too */ + if (ob_act->mode & OB_MODE_WEIGHT_PAINT) { + ED_vgroup_select_by_name(ob_act, name); + DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA); + } + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + + return OPERATOR_FINISHED; + } + } + } + + return OPERATOR_CANCELLED; +} + +void POSE_OT_select_flip_active(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Flip Selected Active Bone"; + ot->idname = "POSE_OT_select_flip_active"; + ot->description = "Activate the bone with a flipped name"; + + /* api callbacks */ + ot->exec = pose_bone_flip_active_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + diff --git a/source/blender/editors/armature/poseSlide.c b/source/blender/editors/armature/pose_slide.c index 3fd65de6c04..fee37a9c0fb 100644 --- a/source/blender/editors/armature/poseSlide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -23,25 +23,15 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/editors/armature/poseSlide.c +/** \file blender/editors/armature/pose_slide.c * \ingroup edarmature */ - -#include <stdlib.h> -#include <stdio.h> -#include <stddef.h> -#include <string.h> -#include <math.h> -#include <float.h> - #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_blenlib.h" -#include "BLI_dynstr.h" #include "BLI_dlrbTree.h" -#include "BLI_utildefines.h" #include "DNA_anim_types.h" #include "DNA_armature_types.h" diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c new file mode 100644 index 00000000000..2525641e58c --- /dev/null +++ b/source/blender/editors/armature/pose_transform.c @@ -0,0 +1,879 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 full recode. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/armature/pose_transform.c + * \ingroup edarmature + */ + +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_report.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_armature.h" +#include "ED_keyframing.h" +#include "ED_screen.h" +#include "ED_util.h" + +#include "armature_intern.h" + + +/* ********************************************** */ +/* Pose Apply */ + +/* helper for apply_armature_pose2bones - fixes parenting of objects that are bone-parented to armature */ +static void applyarmature_fix_boneparents(Scene *scene, Object *armob) +{ + Object workob, *ob; + + /* go through all objects in database */ + for (ob = G.main->object.first; ob; ob = ob->id.next) { + /* if parent is bone in this armature, apply corrections */ + if ((ob->parent == armob) && (ob->partype == PARBONE)) { + /* apply current transform from parent (not yet destroyed), + * then calculate new parent inverse matrix + */ + BKE_object_apply_mat4(ob, ob->obmat, FALSE, FALSE); + + BKE_object_workob_calc_parent(scene, ob, &workob); + invert_m4_m4(ob->parentinv, workob.obmat); + } + } +} + +/* set the current pose as the restpose */ +static int apply_armature_pose2bones_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object + bArmature *arm = BKE_armature_from_object(ob); + bPose *pose; + bPoseChannel *pchan; + EditBone *curbone; + + /* don't check if editmode (should be done by caller) */ + if (ob->type != OB_ARMATURE) + return OPERATOR_CANCELLED; + if (BKE_object_obdata_is_libdata(ob)) { + BKE_report(op->reports, RPT_ERROR, "Cannot apply pose to lib-linked armature"); /* error_libdata(); */ + return OPERATOR_CANCELLED; + } + + /* helpful warnings... */ + /* TODO: add warnings to be careful about actions, applying deforms first, etc. */ + if (ob->adt && ob->adt->action) + BKE_report(op->reports, RPT_WARNING, + "Actions on this armature will be destroyed by this new rest pose as the " + "transforms stored are relative to the old rest pose"); + + /* Get editbones of active armature to alter */ + ED_armature_to_edit(ob); + + /* get pose of active object and move it out of posemode */ + pose = ob->pose; + + for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { + curbone = editbone_name_exists(arm->edbo, pchan->name); + + /* simply copy the head/tail values from pchan over to curbone */ + copy_v3_v3(curbone->head, pchan->pose_head); + copy_v3_v3(curbone->tail, pchan->pose_tail); + + /* fix roll: + * 1. find auto-calculated roll value for this bone now + * 2. remove this from the 'visual' y-rotation + */ + { + float premat[3][3], imat[3][3], pmat[3][3], tmat[3][3]; + float delta[3], eul[3]; + + /* obtain new auto y-rotation */ + sub_v3_v3v3(delta, curbone->tail, curbone->head); + vec_roll_to_mat3(delta, 0.0f, premat); + invert_m3_m3(imat, premat); + + /* get pchan 'visual' matrix */ + copy_m3_m4(pmat, pchan->pose_mat); + + /* remove auto from visual and get euler rotation */ + mul_m3_m3m3(tmat, imat, pmat); + mat3_to_eul(eul, tmat); + + /* just use this euler-y as new roll value */ + curbone->roll = eul[1]; + } + + /* clear transform values for pchan */ + zero_v3(pchan->loc); + zero_v3(pchan->eul); + unit_qt(pchan->quat); + unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); + pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f; + + /* set anim lock */ + curbone->flag |= BONE_UNKEYED; + } + + /* convert editbones back to bones, and then free the edit-data */ + ED_armature_from_edit(ob); + ED_armature_edit_free(ob); + + /* flush positions of posebones */ + BKE_pose_where_is(scene, ob); + + /* fix parenting of objects which are bone-parented */ + applyarmature_fix_boneparents(scene, ob); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_armature_apply(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Pose as Rest Pose"; + ot->idname = "POSE_OT_armature_apply"; + ot->description = "Apply the current pose as the new rest pose"; + + /* callbacks */ + ot->exec = apply_armature_pose2bones_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +/* set the current pose as the restpose */ +static int pose_visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); // must be active object, not edit-object + + /* don't check if editmode (should be done by caller) */ + if (ob->type != OB_ARMATURE) + return OPERATOR_CANCELLED; + + /* loop over all selected pchans + * + * TODO, loop over children before parents if multiple bones + * at once are to be predictable*/ + CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones) + { + float delta_mat[4][4]; + + /* chan_mat already contains the delta transform from rest pose to pose-mode pose + * as that is baked into there so that B-Bones will work. Once we've set this as the + * new raw-transform components, don't recalc the poses yet, otherwise IK result will + * change, thus changing the result we may be trying to record. + */ + copy_m4_m4(delta_mat, pchan->chan_mat); + BKE_pchan_apply_mat4(pchan, delta_mat, TRUE); + } + CTX_DATA_END; + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_visual_transform_apply(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Visual Transform to Pose"; + ot->idname = "POSE_OT_visual_transform_apply"; + ot->description = "Apply final constrained position of pose bones to their transform"; + + /* callbacks */ + ot->exec = pose_visual_transform_apply_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************************************** */ +/* Copy/Paste */ + +/* Global copy/paste buffer for pose - cleared on start/end session + before every copy operation */ +static bPose *g_posebuf = NULL; + +void ED_clipboard_posebuf_free(void) +{ + if (g_posebuf) { + bPoseChannel *pchan; + + for (pchan = g_posebuf->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->prop) { + IDP_FreeProperty(pchan->prop); + MEM_freeN(pchan->prop); + } + } + + /* was copied without constraints */ + BLI_freelistN(&g_posebuf->chanbase); + MEM_freeN(g_posebuf); + } + + g_posebuf = NULL; +} + +/* This function is used to indicate that a bone is selected + * and needs to be included in copy buffer (used to be for inserting keys) + */ +static void set_pose_keys(Object *ob) +{ + bArmature *arm = ob->data; + bPoseChannel *chan; + + if (ob->pose) { + for (chan = ob->pose->chanbase.first; chan; chan = chan->next) { + Bone *bone = chan->bone; + if ((bone) && (bone->flag & BONE_SELECTED) && (arm->layer & bone->layer)) + chan->flag |= POSE_KEY; + else + chan->flag &= ~POSE_KEY; + } + } +} + +/* perform paste pose, for a single bone + * < ob: object where bone to paste to lives + * < chan: bone that pose to paste comes from + * < selOnly: only paste on selected bones + * < flip: flip on x-axis + * + * > returns: whether the bone that we pasted to if we succeeded + */ +static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, short selOnly, short flip) +{ + bPoseChannel *pchan; + char name[MAXBONENAME]; + short paste_ok; + + /* get the name - if flipping, we must flip this first */ + if (flip) + flip_side_name(name, chan->name, 0); /* 0 = don't strip off number extensions */ + else + BLI_strncpy(name, chan->name, sizeof(name)); + + /* only copy when: + * 1) channel exists - poses are not meant to add random channels to anymore + * 2) if selection-masking is on, channel is selected - only selected bones get pasted on, allowing making both sides symmetrical + */ + pchan = BKE_pose_channel_find_name(ob->pose, name); + + if (selOnly) + paste_ok = ((pchan) && (pchan->bone->flag & BONE_SELECTED)); + else + paste_ok = ((pchan != NULL)); + + /* continue? */ + if (paste_ok) { + /* only loc rot size + * - only copies transform info for the pose + */ + copy_v3_v3(pchan->loc, chan->loc); + copy_v3_v3(pchan->size, chan->size); + pchan->flag = chan->flag; + + /* check if rotation modes are compatible (i.e. do they need any conversions) */ + if (pchan->rotmode == chan->rotmode) { + /* copy the type of rotation in use */ + if (pchan->rotmode > 0) { + copy_v3_v3(pchan->eul, chan->eul); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + copy_v3_v3(pchan->rotAxis, chan->rotAxis); + pchan->rotAngle = chan->rotAngle; + } + else { + copy_qt_qt(pchan->quat, chan->quat); + } + } + else if (pchan->rotmode > 0) { + /* quat/axis-angle to euler */ + if (chan->rotmode == ROT_MODE_AXISANGLE) + axis_angle_to_eulO(pchan->eul, pchan->rotmode, chan->rotAxis, chan->rotAngle); + else + quat_to_eulO(pchan->eul, pchan->rotmode, chan->quat); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + /* quat/euler to axis angle */ + if (chan->rotmode > 0) + eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->eul, chan->rotmode); + else + quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->quat); + } + else { + /* euler/axis-angle to quat */ + if (chan->rotmode > 0) + eulO_to_quat(pchan->quat, chan->eul, chan->rotmode); + else + axis_angle_to_quat(pchan->quat, chan->rotAxis, pchan->rotAngle); + } + + /* paste flipped pose? */ + if (flip) { + pchan->loc[0] *= -1; + + /* has to be done as eulers... */ + if (pchan->rotmode > 0) { + pchan->eul[1] *= -1; + pchan->eul[2] *= -1; + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + float eul[3]; + + axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); + eul[1] *= -1; + eul[2] *= -1; + eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); + } + else { + float eul[3]; + + normalize_qt(pchan->quat); + quat_to_eul(eul, pchan->quat); + eul[1] *= -1; + eul[2] *= -1; + eul_to_quat(pchan->quat, eul); + } + } + + /* ID properties */ + if (chan->prop) { + if (pchan->prop) { + /* if we have existing properties on a bone, just copy over the values of + * matching properties (i.e. ones which will have some impact) on to the + * target instead of just blinding replacing all [ + */ + IDP_SyncGroupValues(pchan->prop, chan->prop); + } + else { + /* no existing properties, so assume that we want copies too? */ + pchan->prop = IDP_CopyProperty(chan->prop); + } + } + } + + /* return whether paste went ahead */ + return pchan; +} + +/* ---- */ + +static int pose_copy_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + + /* sanity checking */ + if (ELEM(NULL, ob, ob->pose)) { + BKE_report(op->reports, RPT_ERROR, "No pose to copy"); + return OPERATOR_CANCELLED; + } + + /* free existing pose buffer */ + ED_clipboard_posebuf_free(); + + /* sets chan->flag to POSE_KEY if bone selected, then copy those bones to the buffer */ + set_pose_keys(ob); + BKE_pose_copy_data(&g_posebuf, ob->pose, 0); + + + return OPERATOR_FINISHED; +} + +void POSE_OT_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Pose"; + ot->idname = "POSE_OT_copy"; + ot->description = "Copies the current pose of the selected bones to copy/paste buffer"; + + /* api callbacks */ + ot->exec = pose_copy_exec; + ot->poll = ED_operator_posemode; + + /* flag */ + ot->flag = OPTYPE_REGISTER; +} + +/* ---- */ + +static int pose_paste_exec(bContext *C, wmOperator *op) +{ + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + Scene *scene = CTX_data_scene(C); + bPoseChannel *chan; + int flip = RNA_boolean_get(op->ptr, "flipped"); + int selOnly = RNA_boolean_get(op->ptr, "selected_mask"); + + /* get KeyingSet to use */ + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOC_ROT_SCALE_ID); + + /* sanity checks */ + if (ELEM(NULL, ob, ob->pose)) + return OPERATOR_CANCELLED; + + if (g_posebuf == NULL) { + BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty"); + return OPERATOR_CANCELLED; + } + + /* if selOnly option is enabled, if user hasn't selected any bones, + * just go back to default behavior to be more in line with other pose tools + */ + if (selOnly) { + if (CTX_DATA_COUNT(C, selected_pose_bones) == 0) + selOnly = 0; + } + + /* Safely merge all of the channels in the buffer pose into any existing pose */ + for (chan = g_posebuf->chanbase.first; chan; chan = chan->next) { + if (chan->flag & POSE_KEY) { + /* try to perform paste on this bone */ + bPoseChannel *pchan = pose_bone_do_paste(ob, chan, selOnly, flip); + + if (pchan) { + /* keyframing tagging for successful paste */ + ED_autokeyframe_pchan(C, scene, ob, pchan, ks); + } + } + } + + /* Update event for pose and deformation children */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + + /* notifiers for updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_paste(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Paste Pose"; + ot->idname = "POSE_OT_paste"; + ot->description = "Paste the stored pose on to the current pose"; + + /* api callbacks */ + ot->exec = pose_paste_exec; + ot->poll = ED_operator_posemode; + + /* flag */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_boolean(ot->srna, "flipped", FALSE, "Flipped on X-Axis", "Paste the stored pose flipped on to current pose"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + RNA_def_boolean(ot->srna, "selected_mask", FALSE, "On Selected Only", "Only paste the stored pose on to selected bones in the current pose"); +} + +/* ********************************************** */ +/* Clear Pose Transforms */ + +/* clear scale of pose-channel */ +static void pchan_clear_scale(bPoseChannel *pchan) +{ + if ((pchan->protectflag & OB_LOCK_SCALEX) == 0) + pchan->size[0] = 1.0f; + if ((pchan->protectflag & OB_LOCK_SCALEY) == 0) + pchan->size[1] = 1.0f; + if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0) + pchan->size[2] = 1.0f; +} + +/* clear location of pose-channel */ +static void pchan_clear_loc(bPoseChannel *pchan) +{ + if ((pchan->protectflag & OB_LOCK_LOCX) == 0) + pchan->loc[0] = 0.0f; + if ((pchan->protectflag & OB_LOCK_LOCY) == 0) + pchan->loc[1] = 0.0f; + if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) + pchan->loc[2] = 0.0f; +} + +/* clear rotation of pose-channel */ +static void pchan_clear_rot(bPoseChannel *pchan) +{ + if (pchan->protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) { + /* check if convert to eulers for locking... */ + if (pchan->protectflag & OB_LOCK_ROT4D) { + /* perform clamping on a component by component basis */ + if (pchan->rotmode == ROT_MODE_AXISANGLE) { + if ((pchan->protectflag & OB_LOCK_ROTW) == 0) + pchan->rotAngle = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTX) == 0) + pchan->rotAxis[0] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTY) == 0) + pchan->rotAxis[1] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) + pchan->rotAxis[2] = 0.0f; + + /* check validity of axis - axis should never be 0,0,0 (if so, then we make it rotate about y) */ + if (IS_EQF(pchan->rotAxis[0], pchan->rotAxis[1]) && IS_EQF(pchan->rotAxis[1], pchan->rotAxis[2])) + pchan->rotAxis[1] = 1.0f; + } + else if (pchan->rotmode == ROT_MODE_QUAT) { + if ((pchan->protectflag & OB_LOCK_ROTW) == 0) + pchan->quat[0] = 1.0f; + if ((pchan->protectflag & OB_LOCK_ROTX) == 0) + pchan->quat[1] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTY) == 0) + pchan->quat[2] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) + pchan->quat[3] = 0.0f; + } + else { + /* the flag may have been set for the other modes, so just ignore the extra flag... */ + if ((pchan->protectflag & OB_LOCK_ROTX) == 0) + pchan->eul[0] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTY) == 0) + pchan->eul[1] = 0.0f; + if ((pchan->protectflag & OB_LOCK_ROTZ) == 0) + pchan->eul[2] = 0.0f; + } + } + else { + /* perform clamping using euler form (3-components) */ + float eul[3], oldeul[3], quat1[4] = {0}; + float qlen = 0.0f; + + if (pchan->rotmode == ROT_MODE_QUAT) { + qlen = normalize_qt_qt(quat1, pchan->quat); + quat_to_eul(oldeul, quat1); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); + } + else { + copy_v3_v3(oldeul, pchan->eul); + } + + eul[0] = eul[1] = eul[2] = 0.0f; + + if (pchan->protectflag & OB_LOCK_ROTX) + eul[0] = oldeul[0]; + if (pchan->protectflag & OB_LOCK_ROTY) + eul[1] = oldeul[1]; + if (pchan->protectflag & OB_LOCK_ROTZ) + eul[2] = oldeul[2]; + + if (pchan->rotmode == ROT_MODE_QUAT) { + eul_to_quat(pchan->quat, eul); + + /* restore original quat size */ + mul_qt_fl(pchan->quat, qlen); + + /* quaternions flip w sign to accumulate rotations correctly */ + if ((quat1[0] < 0.0f && pchan->quat[0] > 0.0f) || (quat1[0] > 0.0f && pchan->quat[0] < 0.0f)) { + mul_qt_fl(pchan->quat, -1.0f); + } + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); + } + else { + copy_v3_v3(pchan->eul, eul); + } + } + } /* Duplicated in source/blender/editors/object/object_transform.c */ + else { + if (pchan->rotmode == ROT_MODE_QUAT) { + unit_qt(pchan->quat); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + /* by default, make rotation of 0 radians around y-axis (roll) */ + unit_axis_angle(pchan->rotAxis, &pchan->rotAngle); + } + else { + zero_v3(pchan->eul); + } + } +} + +/* clear loc/rot/scale of pose-channel */ +static void pchan_clear_transforms(bPoseChannel *pchan) +{ + pchan_clear_loc(pchan); + pchan_clear_rot(pchan); + pchan_clear_scale(pchan); +} + +/* --------------- */ + +/* generic exec for clear-pose operators */ +static int pose_clear_transform_generic_exec(bContext *C, wmOperator *op, + void (*clear_func)(bPoseChannel *), const char default_ksName[]) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); + short autokey = 0; + + /* sanity checks */ + if (ELEM(NULL, clear_func, default_ksName)) { + BKE_report(op->reports, RPT_ERROR, "Programming error: missing clear transform function or keying set name"); + return OPERATOR_CANCELLED; + } + + /* only clear relevant transforms for selected bones */ + CTX_DATA_BEGIN(C, bPoseChannel *, pchan, selected_pose_bones) + { + /* run provided clearing function */ + clear_func(pchan); + + /* do auto-keyframing as appropriate */ + if (autokeyframe_cfra_can_key(scene, &ob->id)) { + /* clear any unkeyed tags */ + if (pchan->bone) + pchan->bone->flag &= ~BONE_UNKEYED; + + /* tag for autokeying later */ + autokey = 1; + } + else { + /* add unkeyed tags */ + if (pchan->bone) + pchan->bone->flag |= BONE_UNKEYED; + } + } + CTX_DATA_END; + + /* perform autokeying on the bones if needed */ + if (autokey) { + /* get KeyingSet to use */ + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, default_ksName); + + /* insert keyframes */ + ANIM_apply_keyingset(C, NULL, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + + /* now recalculate paths */ + if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) + ED_pose_recalculate_paths(scene, ob); + } + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + + /* note, notifier might evolve */ + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + + return OPERATOR_FINISHED; +} + +/* --------------- */ + +static int pose_clear_scale_exec(bContext *C, wmOperator *op) +{ + return pose_clear_transform_generic_exec(C, op, pchan_clear_scale, ANIM_KS_SCALING_ID); +} + +void POSE_OT_scale_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Pose Scale"; + ot->idname = "POSE_OT_scale_clear"; + ot->description = "Reset scaling of selected bones to their default values"; + + /* api callbacks */ + ot->exec = pose_clear_scale_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +static int pose_clear_rot_exec(bContext *C, wmOperator *op) +{ + return pose_clear_transform_generic_exec(C, op, pchan_clear_rot, ANIM_KS_ROTATION_ID); +} + +void POSE_OT_rot_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Pose Rotation"; + ot->idname = "POSE_OT_rot_clear"; + ot->description = "Reset rotations of selected bones to their default values"; + + /* api callbacks */ + ot->exec = pose_clear_rot_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +static int pose_clear_loc_exec(bContext *C, wmOperator *op) +{ + return pose_clear_transform_generic_exec(C, op, pchan_clear_loc, ANIM_KS_LOCATION_ID); +} + +void POSE_OT_loc_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Pose Location"; + ot->idname = "POSE_OT_loc_clear"; + ot->description = "Reset locations of selected bones to their default values"; + + /* api callbacks */ + ot->exec = pose_clear_loc_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +static int pose_clear_transforms_exec(bContext *C, wmOperator *op) +{ + return pose_clear_transform_generic_exec(C, op, pchan_clear_transforms, ANIM_KS_LOC_ROT_SCALE_ID); +} + +void POSE_OT_transforms_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Pose Transforms"; + ot->idname = "POSE_OT_transforms_clear"; + ot->description = "Reset location, rotation, and scaling of selected bones to their default values"; + + /* api callbacks */ + ot->exec = pose_clear_transforms_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************************************** */ +/* Clear User Transforms */ + +static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + float cframe = (float)CFRA; + const short only_select = RNA_boolean_get(op->ptr, "only_selected"); + + if ((ob->adt) && (ob->adt->action)) { + /* XXX: this is just like this to avoid contaminating anything else; + * just pose values should change, so this should be fine + */ + bPose *dummyPose = NULL; + Object workob = {{0}}; + bPoseChannel *pchan; + + /* execute animation step for current frame using a dummy copy of the pose */ + BKE_pose_copy_data(&dummyPose, ob->pose, 0); + + BLI_strncpy(workob.id.name, "OB<ClearTfmWorkOb>", sizeof(workob.id.name)); + workob.type = OB_ARMATURE; + workob.data = ob->data; + workob.adt = ob->adt; + workob.pose = dummyPose; + + BKE_animsys_evaluate_animdata(scene, &workob.id, workob.adt, cframe, ADT_RECALC_ANIM); + + /* copy back values, but on selected bones only */ + for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { + pose_bone_do_paste(ob, pchan, only_select, 0); + } + + /* free temp data - free manually as was copied without constraints */ + for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->prop) { + IDP_FreeProperty(pchan->prop); + MEM_freeN(pchan->prop); + } + } + + /* was copied without constraints */ + BLI_freelistN(&dummyPose->chanbase); + MEM_freeN(dummyPose); + } + else { + /* no animation, so just reset whole pose to rest pose + * (cannot just restore for selected though) + */ + BKE_pose_rest(ob->pose); + } + + /* notifiers and updates */ + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + + return OPERATOR_FINISHED; +} + +void POSE_OT_user_transforms_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear User Transforms"; + ot->idname = "POSE_OT_user_transforms_clear"; + ot->description = "Reset pose on selected bones to keyframed state"; + + /* callbacks */ + ot->exec = pose_clear_user_transforms_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "only_selected", TRUE, "Only Selected", "Only visible/selected bones"); +} diff --git a/source/blender/editors/armature/poseUtils.c b/source/blender/editors/armature/pose_utils.c index f3c32399ad6..a5e51ccf32a 100644 --- a/source/blender/editors/armature/poseUtils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -23,25 +23,14 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/editors/armature/poseUtils.c +/** \file blender/editors/armature/pose_utils.c * \ingroup edarmature */ - -#include <stdlib.h> -#include <stdio.h> -#include <stddef.h> -#include <string.h> -#include <math.h> -#include <float.h> - #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_blenlib.h" -#include "BLI_dynstr.h" -#include "BLI_dlrbTree.h" -#include "BLI_utildefines.h" #include "DNA_anim_types.h" #include "DNA_armature_types.h" diff --git a/source/blender/editors/armature/poseobject.c b/source/blender/editors/armature/poseobject.c deleted file mode 100644 index c7d1e428355..00000000000 --- a/source/blender/editors/armature/poseobject.c +++ /dev/null @@ -1,2364 +0,0 @@ -/* - * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - * - * Contributor(s): Ton Roosendaal, Blender Foundation '05, full recode. - * Joshua Leung - * - * ***** END GPL LICENSE BLOCK ***** - * support for animation modes - Reevan McKay - */ - -/** \file blender/editors/armature/poseobject.c - * \ingroup edarmature - */ - - -#include <stdlib.h> -#include <stddef.h> -#include <string.h> - -#include "MEM_guardedalloc.h" - -#include "BLI_math.h" -#include "BLI_blenlib.h" -#include "BLI_dynstr.h" -#include "BLI_utildefines.h" - -#include "DNA_anim_types.h" -#include "DNA_armature_types.h" -#include "DNA_constraint_types.h" -#include "DNA_scene_types.h" -#include "DNA_object_types.h" - -#include "BKE_animsys.h" -#include "BKE_anim.h" -#include "BKE_idprop.h" -#include "BKE_action.h" -#include "BKE_armature.h" -#include "BKE_context.h" -#include "BKE_constraint.h" -#include "BKE_deform.h" -#include "BKE_depsgraph.h" -#include "BKE_fcurve.h" -#include "BKE_modifier.h" -#include "BKE_object.h" -#include "BKE_report.h" - - -#include "RNA_access.h" -#include "RNA_define.h" -#include "RNA_enum_types.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_armature.h" -#include "ED_keyframing.h" -#include "ED_mesh.h" -#include "ED_screen.h" -#include "ED_object.h" -#include "ED_util.h" /* clipboard */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "armature_intern.h" - -/* matches logic with ED_operator_posemode_context() */ -Object *ED_pose_object_from_context(bContext *C) -{ - ScrArea *sa = CTX_wm_area(C); - Object *ob; - - /* since this call may also be used from the buttons window, we need to check for where to get the object */ - if (sa && sa->spacetype == SPACE_BUTS) { - ob = ED_object_context(C); - } - else { - ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - } - - return ob; -} - -/* This function is used to process the necessary updates for */ -void ED_armature_enter_posemode(bContext *C, Base *base) -{ - ReportList *reports = CTX_wm_reports(C); - Object *ob = base->object; - - if (ob->id.lib) { - BKE_report(reports, RPT_WARNING, "Cannot pose libdata"); - return; - } - - switch (ob->type) { - case OB_ARMATURE: - ob->restore_mode = ob->mode; - ob->mode |= OB_MODE_POSE; - - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_POSE, NULL); - - break; - default: - return; - } - - /* XXX: disabled as this would otherwise cause a nasty loop... */ - //ED_object_toggle_modes(C, ob->mode); -} - -void ED_armature_exit_posemode(bContext *C, Base *base) -{ - if (base) { - Object *ob = base->object; - - ob->restore_mode = ob->mode; - ob->mode &= ~OB_MODE_POSE; - - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL); - } -} - -/* if a selected or active bone is protected, throw error (oonly if warn == 1) and return 1 */ -/* only_selected == 1: the active bone is allowed to be protected */ -#if 0 /* UNUSED 2.5 */ -static short pose_has_protected_selected(Object *ob, short warn) -{ - /* check protection */ - if (ob->proxy) { - bPoseChannel *pchan; - bArmature *arm = ob->data; - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone && (pchan->bone->layer & arm->layer)) { - if (pchan->bone->layer & arm->layer_protected) { - if (pchan->bone->flag & BONE_SELECTED) - break; - } - } - } - if (pchan) { - if (warn) error("Cannot change Proxy protected bones"); - return 1; - } - } - return 0; -} -#endif - -/* only for real IK, not for auto-IK */ -static int pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan, int level) -{ - bConstraint *con; - Bone *bone; - - /* No need to check if constraint is active (has influence), - * since all constraints with CONSTRAINT_IK_AUTO are active */ - for (con = pchan->constraints.first; con; con = con->next) { - if (con->type == CONSTRAINT_TYPE_KINEMATIC) { - bKinematicConstraint *data = con->data; - if (data->rootbone == 0 || data->rootbone > level) { - if ((data->flag & CONSTRAINT_IK_AUTO) == 0) - return 1; - } - } - } - for (bone = pchan->bone->childbase.first; bone; bone = bone->next) { - pchan = BKE_pose_channel_find_name(ob->pose, bone->name); - if (pchan && pose_channel_in_IK_chain(ob, pchan, level + 1)) - return 1; - } - return 0; -} - -int ED_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan) -{ - return pose_channel_in_IK_chain(ob, pchan, 0); -} - -/* ********************************************** */ -/* Motion Paths */ - -/* For the object with pose/action: update paths for those that have got them - * This should selectively update paths that exist... - * - * To be called from various tools that do incremental updates - */ -void ED_pose_recalculate_paths(Scene *scene, Object *ob) -{ - ListBase targets = {NULL, NULL}; - - /* set flag to force recalc, then grab the relevant bones to target */ - ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS; - animviz_get_object_motionpaths(ob, &targets); - - /* recalculate paths, then free */ - animviz_calc_motionpaths(scene, &targets); - BLI_freelistN(&targets); -} - - -/* show popup to determine settings */ -static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - /* set default settings from existing/stored settings */ - { - bAnimVizSettings *avs = &ob->pose->avs; - PointerRNA avs_ptr; - - RNA_int_set(op->ptr, "start_frame", avs->path_sf); - RNA_int_set(op->ptr, "end_frame", avs->path_ef); - - RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); - RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location")); - } - - /* show popup dialog to allow editing of range... */ - // FIXME: hardcoded dimensions here are just arbitrary - return WM_operator_props_dialog_popup(C, op, 10 * UI_UNIT_X, 10 * UI_UNIT_Y); -} - -/* For the object with pose/action: create path curves for selected bones - * This recalculates the WHOLE path within the pchan->pathsf and pchan->pathef range - */ -static int pose_calculate_paths_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - Scene *scene = CTX_data_scene(C); - - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - /* grab baking settings from operator settings */ - { - bAnimVizSettings *avs = &ob->pose->avs; - PointerRNA avs_ptr; - - avs->path_sf = RNA_int_get(op->ptr, "start_frame"); - avs->path_ef = RNA_int_get(op->ptr, "end_frame"); - - RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); - RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location")); - } - - /* set up path data for bones being calculated */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - /* verify makes sure that the selected bone has a bone with the appropriate settings */ - animviz_verify_motionpaths(op->reports, scene, ob, pchan); - } - CTX_DATA_END; - - /* calculate the bones that now have motionpaths... */ - /* TODO: only make for the selected bones? */ - ED_pose_recalculate_paths(scene, ob); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_paths_calculate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Calculate Bone Paths"; - ot->idname = "POSE_OT_paths_calculate"; - ot->description = "Calculate paths for the selected bones"; - - /* api callbacks */ - ot->invoke = pose_calculate_paths_invoke; - ot->exec = pose_calculate_paths_exec; - ot->poll = ED_operator_posemode_exclusive; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_int(ot->srna, "start_frame", 1, MINAFRAME, MAXFRAME, "Start", - "First frame to calculate bone paths on", MINFRAME, MAXFRAME / 2.0); - RNA_def_int(ot->srna, "end_frame", 250, MINAFRAME, MAXFRAME, "End", - "Last frame to calculate bone paths on", MINFRAME, MAXFRAME / 2.0); - - RNA_def_enum(ot->srna, "bake_location", motionpath_bake_location_items, 0, - "Bake Location", - "Which point on the bones is used when calculating paths"); -} - -/* --------- */ - -static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - Scene *scene = CTX_data_scene(C); - - if (ELEM(NULL, ob, scene)) - return OPERATOR_CANCELLED; - - /* calculate the bones that now have motionpaths... */ - /* TODO: only make for the selected bones? */ - ED_pose_recalculate_paths(scene, ob); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_paths_update(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Update Bone Paths"; - ot->idname = "POSE_OT_paths_update"; - ot->description = "Recalculate paths for bones that already have them"; - - /* api callbakcs */ - ot->exec = pose_update_paths_exec; - ot->poll = ED_operator_posemode_exclusive; /* TODO: this should probably check for active bone and/or existing paths */ - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* --------- */ - -/* for the object with pose/action: clear path curves for selected bones only */ -static void ED_pose_clear_paths(Object *ob) -{ - bPoseChannel *pchan; - short skipped = 0; - - if (ELEM(NULL, ob, ob->pose)) - return; - - /* free the motionpath blocks, but also take note of whether we skipped some... */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->mpath) { - if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) { - animviz_free_motionpath(pchan->mpath); - pchan->mpath = NULL; - } - else - skipped = 1; - } - } - - /* if we didn't skip any, we shouldn't have any paths left */ - if (skipped == 0) - ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS; -} - -/* operator callback for this */ -static int pose_clear_paths_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - - /* only continue if there's an object */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - /* use the backend function for this */ - ED_pose_clear_paths(ob); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_paths_clear(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Clear Bone Paths"; - ot->idname = "POSE_OT_paths_clear"; - ot->description = "Clear path caches for selected bones"; - - /* api callbacks */ - ot->exec = pose_clear_paths_exec; - ot->poll = ED_operator_posemode_exclusive; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ******************* Select Constraint Target Operator ************* */ - -static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (bArmature *)ob->data; - bConstraint *con; - int found = 0; - - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - if (pchan->bone->flag & BONE_SELECTED) { - for (con = pchan->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if ((ct->tar == ob) && (ct->subtarget[0])) { - bPoseChannel *pchanc = BKE_pose_channel_find_name(ob->pose, ct->subtarget); - if ((pchanc) && !(pchanc->bone->flag & BONE_UNSELECTABLE)) { - pchanc->bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; - found = 1; - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 1); - } - } - } - } - CTX_DATA_END; - - if (!found) - return OPERATOR_CANCELLED; - - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - return OPERATOR_FINISHED; -} - -void POSE_OT_select_constraint_target(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Constraint Target"; - ot->idname = "POSE_OT_select_constraint_target"; - ot->description = "Select bones used as targets for the currently selected bones"; - - /* api callbacks */ - ot->exec = pose_select_constraint_target_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ******************* select hierarchy operator ************* */ - -static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = ob->data; - Bone *curbone, *pabone, *chbone; - int direction = RNA_enum_get(op->ptr, "direction"); - int add_to_sel = RNA_boolean_get(op->ptr, "extend"); - int found = 0; - - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - curbone = pchan->bone; - - if ((curbone->flag & BONE_UNSELECTABLE) == 0) { - if (curbone == arm->act_bone) { - if (direction == BONE_SELECT_PARENT) { - if (pchan->parent == NULL) continue; - else pabone = pchan->parent->bone; - - if (PBONE_SELECTABLE(arm, pabone)) { - if (!add_to_sel) curbone->flag &= ~BONE_SELECTED; - pabone->flag |= BONE_SELECTED; - arm->act_bone = pabone; - - found = 1; - break; - } - } - else { /* direction == BONE_SELECT_CHILD */ - /* the child member is only assigned to connected bones, see [#30340] */ -#if 0 - if (pchan->child == NULL) continue; - else chbone = pchan->child->bone; -#else - /* instead. find _any_ visible child bone, using the first one is a little arbitrary - campbell */ - chbone = pchan->child ? pchan->child->bone : NULL; - if (chbone == NULL) { - bPoseChannel *pchan_child; - - for (pchan_child = ob->pose->chanbase.first; pchan_child; pchan_child = pchan_child->next) { - /* possible we have multiple children, some invisible */ - if (PBONE_SELECTABLE(arm, pchan_child->bone)) { - if (pchan_child->parent == pchan) { - chbone = pchan_child->bone; - break; - } - } - } - } - - if (chbone == NULL) continue; -#endif - - if (PBONE_SELECTABLE(arm, chbone)) { - if (!add_to_sel) curbone->flag &= ~BONE_SELECTED; - chbone->flag |= BONE_SELECTED; - arm->act_bone = chbone; - - found = 1; - break; - } - } - } - } - } - CTX_DATA_END; - - if (found == 0) - return OPERATOR_CANCELLED; - - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - return OPERATOR_FINISHED; -} - -void POSE_OT_select_hierarchy(wmOperatorType *ot) -{ - static EnumPropertyItem direction_items[] = { - {BONE_SELECT_PARENT, "PARENT", 0, "Select Parent", ""}, - {BONE_SELECT_CHILD, "CHILD", 0, "Select Child", ""}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Select Hierarchy"; - ot->idname = "POSE_OT_select_hierarchy"; - ot->description = "Select immediate parent/children of selected bones"; - - /* api callbacks */ - ot->exec = pose_select_hierarchy_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - ot->prop = RNA_def_enum(ot->srna, "direction", direction_items, BONE_SELECT_PARENT, "Direction", ""); - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); -} - -/* ******************* select grouped operator ************* */ - -static short pose_select_same_group(bContext *C, Object *ob, short extend) -{ - bArmature *arm = (ob) ? ob->data : NULL; - bPose *pose = (ob) ? ob->pose : NULL; - char *group_flags; - int numGroups = 0; - short changed = 0, tagged = 0; - - /* sanity checks */ - if (ELEM3(NULL, ob, pose, arm)) - return 0; - - /* count the number of groups */ - numGroups = BLI_countlist(&pose->agroups); - if (numGroups == 0) - return 0; - - /* alloc a small array to keep track of the groups to use - * - each cell stores on/off state for whether group should be used - * - size is (numGroups + 1), since (index = 0) is used for no-group - */ - group_flags = MEM_callocN(numGroups + 1, "pose_select_same_group"); - - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - /* keep track of group as group to use later? */ - if (pchan->bone->flag & BONE_SELECTED) { - group_flags[pchan->agrp_index] = 1; - tagged = 1; - } - - /* deselect all bones before selecting new ones? */ - if ((extend == 0) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) - pchan->bone->flag &= ~BONE_SELECTED; - } - CTX_DATA_END; - - /* small optimization: only loop through bones a second time if there are any groups tagged */ - if (tagged) { - /* only if group matches (and is not selected or current bone) */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - /* check if the group used by this bone is counted */ - if (group_flags[pchan->agrp_index]) { - pchan->bone->flag |= BONE_SELECTED; - changed = 1; - } - } - } - CTX_DATA_END; - } - - /* free temp info */ - MEM_freeN(group_flags); - - return changed; -} - -static short pose_select_same_layer(bContext *C, Object *ob, short extend) -{ - bPose *pose = (ob) ? ob->pose : NULL; - bArmature *arm = (ob) ? ob->data : NULL; - short changed = 0; - int layers = 0; - - if (ELEM3(NULL, ob, pose, arm)) - return 0; - - /* figure out what bones are selected */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - /* keep track of layers to use later? */ - if (pchan->bone->flag & BONE_SELECTED) - layers |= pchan->bone->layer; - - /* deselect all bones before selecting new ones? */ - if ((extend == 0) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) - pchan->bone->flag &= ~BONE_SELECTED; - } - CTX_DATA_END; - if (layers == 0) - return 0; - - /* select bones that are on same layers as layers flag */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - /* if bone is on a suitable layer, and the bone can have its selection changed, select it */ - if ((layers & pchan->bone->layer) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - pchan->bone->flag |= BONE_SELECTED; - changed = 1; - } - } - CTX_DATA_END; - - return changed; -} - -static int pose_select_same_keyingset(bContext *C, Object *ob, short extend) -{ - KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C)); - KS_Path *ksp; - - bArmature *arm = (ob) ? ob->data : NULL; - bPose *pose = (ob) ? ob->pose : NULL; - short changed = 0; - - /* sanity checks: validate Keying Set and object */ - if ((ks == NULL) || (ANIM_validate_keyingset(C, NULL, ks) != 0)) - return 0; - - if (ELEM3(NULL, ob, pose, arm)) - return 0; - - /* if not extending selection, deselect all selected first */ - if (extend == 0) { - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) - pchan->bone->flag &= ~BONE_SELECTED; - } - CTX_DATA_END; - } - - /* iterate over elements in the Keying Set, setting selection depending on whether - * that bone is visible or not... - */ - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { - /* only items related to this object will be relevant */ - if ((ksp->id == &ob->id) && (ksp->rna_path != NULL)) { - if (strstr(ksp->rna_path, "bones")) { - char *boneName = BLI_str_quoted_substrN(ksp->rna_path, "bones["); - - if (boneName) { - bPoseChannel *pchan = BKE_pose_channel_find_name(pose, boneName); - - if (pchan) { - /* select if bone is visible and can be affected */ - if (PBONE_SELECTABLE(arm, pchan->bone)) { - pchan->bone->flag |= BONE_SELECTED; - changed = 1; - } - } - - /* free temp memory */ - MEM_freeN(boneName); - } - } - } - } - - return changed; -} - -static int pose_select_grouped_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (bArmature *)ob->data; - short extend = RNA_boolean_get(op->ptr, "extend"); - short changed = 0; - - /* sanity check */ - if (ob->pose == NULL) - return OPERATOR_CANCELLED; - - /* selection types - * NOTE: for the order of these, see the enum in POSE_OT_select_grouped() - */ - switch (RNA_enum_get(op->ptr, "type")) { - case 1: /* group */ - changed = pose_select_same_group(C, ob, extend); - break; - case 2: /* Keying Set */ - changed = pose_select_same_keyingset(C, ob, extend); - break; - default: /* layer */ - changed = pose_select_same_layer(C, ob, extend); - break; - } - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - - /* report done status */ - if (changed) - return OPERATOR_FINISHED; - else - return OPERATOR_CANCELLED; -} - -void POSE_OT_select_grouped(wmOperatorType *ot) -{ - static EnumPropertyItem prop_select_grouped_types[] = { - {0, "LAYER", 0, "Layer", "Shared layers"}, - {1, "GROUP", 0, "Group", "Shared group"}, - {2, "KEYINGSET", 0, "Keying Set", "All bones affected by active Keying Set"}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Select Grouped"; - ot->description = "Select all visible bones grouped by similar properties"; - ot->idname = "POSE_OT_select_grouped"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = pose_select_grouped_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "extend", FALSE, "Extend", "Extend selection instead of deselecting everything first"); - ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", ""); -} - - -/* ********************************************** */ - -/* context active object, or weightpainted object with armature in posemode */ -static int pose_bone_flip_active_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob_act = CTX_data_active_object(C); - Object *ob = BKE_object_pose_armature_get(ob_act); - - if (ob && (ob->mode & OB_MODE_POSE)) { - bArmature *arm = ob->data; - - if (arm->act_bone) { - bPoseChannel *pchanf; - char name[MAXBONENAME]; - flip_side_name(name, arm->act_bone->name, TRUE); - - pchanf = BKE_pose_channel_find_name(ob->pose, name); - if (pchanf && pchanf->bone != arm->act_bone) { - arm->act_bone->flag &= ~BONE_SELECTED; - pchanf->bone->flag |= BONE_SELECTED; - - arm->act_bone = pchanf->bone; - - /* in weightpaint we select the associated vertex group too */ - if (ob_act->mode & OB_MODE_WEIGHT_PAINT) { - ED_vgroup_select_by_name(ob_act, name); - DAG_id_tag_update(&ob_act->id, OB_RECALC_DATA); - } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - return OPERATOR_FINISHED; - } - } - } - - return OPERATOR_CANCELLED; -} - -void POSE_OT_select_flip_active(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Flip Selected Active Bone"; - ot->idname = "POSE_OT_select_flip_active"; - ot->description = "Activate the bone with a flipped name"; - - /* api callbacks */ - ot->exec = pose_bone_flip_active_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - - -/* ********************************************** */ -#if 0 /* UNUSED 2.5 */ -static void pose_copy_menu(Scene *scene) -{ - Object *obedit = scene->obedit; // XXX context - Object *ob = OBACT; - bArmature *arm; - bPoseChannel *pchan, *pchanact; - short nr = 0; - int i = 0; - - /* paranoia checks */ - if (ELEM(NULL, ob, ob->pose)) return; - if ((ob == obedit) || (ob->mode & OB_MODE_POSE) == 0) return; - - pchan = BKE_pose_channel_active(ob); - - if (pchan == NULL) return; - pchanact = pchan; - arm = ob->data; - - /* if proxy-protected bones selected, some things (such as locks + displays) shouldn't be changeable, - * but for constraints (just add local constraints) - */ - if (pose_has_protected_selected(ob, 0)) { - i = BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */ - if (i < 25) - nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|Constraints... %x5"); - else - nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4"); - } - else { - i = BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */ - if (i < 25) - nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|Constraints... %x5|%l|Transform Locks %x6|IK Limits %x7|Bone Shape %x8"); - else - nr = pupmenu("Copy Pose Attributes %t|Local Location %x1|Local Rotation %x2|Local Size %x3|%l|Visual Location %x9|Visual Rotation %x10|Visual Size %x11|%l|Constraints (All) %x4|%l|Transform Locks %x6|IK Limits %x7|Bone Shape %x8"); - } - - if (nr <= 0) - return; - - if (nr != 5) { - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if ((arm->layer & pchan->bone->layer) && - (pchan->bone->flag & BONE_SELECTED) && - (pchan != pchanact) ) - { - switch (nr) { - case 1: /* Local Location */ - copy_v3_v3(pchan->loc, pchanact->loc); - break; - case 2: /* Local Rotation */ - copy_qt_qt(pchan->quat, pchanact->quat); - copy_v3_v3(pchan->eul, pchanact->eul); - break; - case 3: /* Local Size */ - copy_v3_v3(pchan->size, pchanact->size); - break; - case 4: /* All Constraints */ - { - ListBase tmp_constraints = {NULL, NULL}; - - /* copy constraints to tmpbase and apply 'local' tags before - * appending to list of constraints for this channel - */ - BKE_copy_constraints(&tmp_constraints, &pchanact->constraints, TRUE); - if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) { - bConstraint *con; - - /* add proxy-local tags */ - for (con = tmp_constraints.first; con; con = con->next) - con->flag |= CONSTRAINT_PROXY_LOCAL; - } - BLI_movelisttolist(&pchan->constraints, &tmp_constraints); - - /* update flags (need to add here, not just copy) */ - pchan->constflag |= pchanact->constflag; - - if (ob->pose) - ob->pose->flag |= POSE_RECALC; - } - break; - case 6: /* Transform Locks */ - pchan->protectflag = pchanact->protectflag; - break; - case 7: /* IK (DOF) settings */ - { - pchan->ikflag = pchanact->ikflag; - copy_v3_v3(pchan->limitmin, pchanact->limitmin); - copy_v3_v3(pchan->limitmax, pchanact->limitmax); - copy_v3_v3(pchan->stiffness, pchanact->stiffness); - pchan->ikstretch = pchanact->ikstretch; - pchan->ikrotweight = pchanact->ikrotweight; - pchan->iklinweight = pchanact->iklinweight; - } - break; - case 8: /* Custom Bone Shape */ - pchan->custom = pchanact->custom; - break; - case 9: /* Visual Location */ - BKE_armature_loc_pose_to_bone(pchan, pchanact->pose_mat[3], pchan->loc); - break; - case 10: /* Visual Rotation */ - { - float delta_mat[4][4]; - - BKE_armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat); - - if (pchan->rotmode == ROT_MODE_AXISANGLE) { - float tmp_quat[4]; - - /* need to convert to quat first (in temp var)... */ - mat4_to_quat(tmp_quat, delta_mat); - quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, tmp_quat); - } - else if (pchan->rotmode == ROT_MODE_QUAT) - mat4_to_quat(pchan->quat, delta_mat); - else - mat4_to_eulO(pchan->eul, pchan->rotmode, delta_mat); - } - break; - case 11: /* Visual Size */ - { - float delta_mat[4][4], size[4]; - - BKE_armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat); - mat4_to_size(size, delta_mat); - copy_v3_v3(pchan->size, size); - } - } - } - } - } - else { /* constraints, optional (note: max we can have is 24 constraints) */ - bConstraint *con, *con_back; - int const_toggle[24] = {0}; /* XXX, initialize as 0 to quiet errors */ - ListBase const_copy = {NULL, NULL}; - - BLI_duplicatelist(&const_copy, &(pchanact->constraints)); - - /* build the puplist of constraints */ - for (con = pchanact->constraints.first, i = 0; con; con = con->next, i++) { - const_toggle[i] = 1; -// add_numbut(i, TOG|INT, con->name, 0, 0, &(const_toggle[i]), ""); - } - -// if (!do_clever_numbuts("Select Constraints", i, REDRAW)) { -// BLI_freelistN(&const_copy); -// return; -// } - - /* now build a new listbase from the options selected */ - for (i = 0, con = const_copy.first; con; i++) { - /* if not selected, free/remove it from the list */ - if (!const_toggle[i]) { - con_back = con->next; - BLI_freelinkN(&const_copy, con); - con = con_back; - } - else - con = con->next; - } - - /* Copy the temo listbase to the selected posebones */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if ((arm->layer & pchan->bone->layer) && - (pchan->bone->flag & BONE_SELECTED) && - (pchan != pchanact) ) - { - ListBase tmp_constraints = {NULL, NULL}; - - /* copy constraints to tmpbase and apply 'local' tags before - * appending to list of constraints for this channel - */ - BKE_copy_constraints(&tmp_constraints, &const_copy, TRUE); - if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) { - /* add proxy-local tags */ - for (con = tmp_constraints.first; con; con = con->next) - con->flag |= CONSTRAINT_PROXY_LOCAL; - } - BLI_movelisttolist(&pchan->constraints, &tmp_constraints); - - /* update flags (need to add here, not just copy) */ - pchan->constflag |= pchanact->constflag; - } - } - BLI_freelistN(&const_copy); - BKE_pose_update_constraint_flags(ob->pose); /* we could work out the flags but its simpler to do this */ - - if (ob->pose) - ob->pose->flag |= POSE_RECALC; - } - - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); // and all its relations - - BIF_undo_push("Copy Pose Attributes"); - -} -#endif - -/* ******************** copy/paste pose ********************** */ - -/* Global copy/paste buffer for pose - cleared on start/end session + before every copy operation */ -static bPose *g_posebuf = NULL; - -void ED_clipboard_posebuf_free(void) -{ - if (g_posebuf) { - bPoseChannel *pchan; - - for (pchan = g_posebuf->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->prop) { - IDP_FreeProperty(pchan->prop); - MEM_freeN(pchan->prop); - } - } - - /* was copied without constraints */ - BLI_freelistN(&g_posebuf->chanbase); - MEM_freeN(g_posebuf); - } - - g_posebuf = NULL; -} - -/* This function is used to indicate that a bone is selected - * and needs to be included in copy buffer (used to be for inserting keys) - */ -static void set_pose_keys(Object *ob) -{ - bArmature *arm = ob->data; - bPoseChannel *chan; - - if (ob->pose) { - for (chan = ob->pose->chanbase.first; chan; chan = chan->next) { - Bone *bone = chan->bone; - if ((bone) && (bone->flag & BONE_SELECTED) && (arm->layer & bone->layer)) - chan->flag |= POSE_KEY; - else - chan->flag &= ~POSE_KEY; - } - } -} - -/* perform paste pose, for a single bone - * < ob: object where bone to paste to lives - * < chan: bone that pose to paste comes from - * < selOnly: only paste on selected bones - * < flip: flip on x-axis - * - * > returns: whether the bone that we pasted to if we succeeded - */ -static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, short selOnly, short flip) -{ - bPoseChannel *pchan; - char name[MAXBONENAME]; - short paste_ok; - - /* get the name - if flipping, we must flip this first */ - if (flip) - flip_side_name(name, chan->name, 0); /* 0 = don't strip off number extensions */ - else - BLI_strncpy(name, chan->name, sizeof(name)); - - /* only copy when: - * 1) channel exists - poses are not meant to add random channels to anymore - * 2) if selection-masking is on, channel is selected - only selected bones get pasted on, allowing making both sides symmetrical - */ - pchan = BKE_pose_channel_find_name(ob->pose, name); - - if (selOnly) - paste_ok = ((pchan) && (pchan->bone->flag & BONE_SELECTED)); - else - paste_ok = ((pchan != NULL)); - - /* continue? */ - if (paste_ok) { - /* only loc rot size - * - only copies transform info for the pose - */ - copy_v3_v3(pchan->loc, chan->loc); - copy_v3_v3(pchan->size, chan->size); - pchan->flag = chan->flag; - - /* check if rotation modes are compatible (i.e. do they need any conversions) */ - if (pchan->rotmode == chan->rotmode) { - /* copy the type of rotation in use */ - if (pchan->rotmode > 0) { - copy_v3_v3(pchan->eul, chan->eul); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - copy_v3_v3(pchan->rotAxis, chan->rotAxis); - pchan->rotAngle = chan->rotAngle; - } - else { - copy_qt_qt(pchan->quat, chan->quat); - } - } - else if (pchan->rotmode > 0) { - /* quat/axis-angle to euler */ - if (chan->rotmode == ROT_MODE_AXISANGLE) - axis_angle_to_eulO(pchan->eul, pchan->rotmode, chan->rotAxis, chan->rotAngle); - else - quat_to_eulO(pchan->eul, pchan->rotmode, chan->quat); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - /* quat/euler to axis angle */ - if (chan->rotmode > 0) - eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->eul, chan->rotmode); - else - quat_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, chan->quat); - } - else { - /* euler/axis-angle to quat */ - if (chan->rotmode > 0) - eulO_to_quat(pchan->quat, chan->eul, chan->rotmode); - else - axis_angle_to_quat(pchan->quat, chan->rotAxis, pchan->rotAngle); - } - - /* paste flipped pose? */ - if (flip) { - pchan->loc[0] *= -1; - - /* has to be done as eulers... */ - if (pchan->rotmode > 0) { - pchan->eul[1] *= -1; - pchan->eul[2] *= -1; - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - float eul[3]; - - axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, pchan->rotAxis, pchan->rotAngle); - eul[1] *= -1; - eul[2] *= -1; - eulO_to_axis_angle(pchan->rotAxis, &pchan->rotAngle, eul, EULER_ORDER_DEFAULT); - } - else { - float eul[3]; - - normalize_qt(pchan->quat); - quat_to_eul(eul, pchan->quat); - eul[1] *= -1; - eul[2] *= -1; - eul_to_quat(pchan->quat, eul); - } - } - - /* ID properties */ - if (chan->prop) { - if (pchan->prop) { - /* if we have existing properties on a bone, just copy over the values of - * matching properties (i.e. ones which will have some impact) on to the - * target instead of just blinding replacing all [ - */ - IDP_SyncGroupValues(pchan->prop, chan->prop); - } - else { - /* no existing properties, so assume that we want copies too? */ - pchan->prop = IDP_CopyProperty(chan->prop); - } - } - } - - /* return whether paste went ahead */ - return pchan; -} - -/* ---- */ - -static int pose_copy_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - - /* sanity checking */ - if (ELEM(NULL, ob, ob->pose)) { - BKE_report(op->reports, RPT_ERROR, "No pose to copy"); - return OPERATOR_CANCELLED; - } - - /* free existing pose buffer */ - ED_clipboard_posebuf_free(); - - /* sets chan->flag to POSE_KEY if bone selected, then copy those bones to the buffer */ - set_pose_keys(ob); - BKE_pose_copy_data(&g_posebuf, ob->pose, 0); - - - return OPERATOR_FINISHED; -} - -void POSE_OT_copy(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Copy Pose"; - ot->idname = "POSE_OT_copy"; - ot->description = "Copies the current pose of the selected bones to copy/paste buffer"; - - /* api callbacks */ - ot->exec = pose_copy_exec; - ot->poll = ED_operator_posemode; - - /* flag */ - ot->flag = OPTYPE_REGISTER; -} - -/* ---- */ - -static int pose_paste_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - Scene *scene = CTX_data_scene(C); - bPoseChannel *chan; - int flip = RNA_boolean_get(op->ptr, "flipped"); - int selOnly = RNA_boolean_get(op->ptr, "selected_mask"); - - /* get KeyingSet to use */ - KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOC_ROT_SCALE_ID); - - /* sanity checks */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - if (g_posebuf == NULL) { - BKE_report(op->reports, RPT_ERROR, "Copy buffer is empty"); - return OPERATOR_CANCELLED; - } - - /* if selOnly option is enabled, if user hasn't selected any bones, - * just go back to default behavior to be more in line with other pose tools - */ - if (selOnly) { - if (CTX_DATA_COUNT(C, selected_pose_bones) == 0) - selOnly = 0; - } - - /* Safely merge all of the channels in the buffer pose into any existing pose */ - for (chan = g_posebuf->chanbase.first; chan; chan = chan->next) { - if (chan->flag & POSE_KEY) { - /* try to perform paste on this bone */ - bPoseChannel *pchan = pose_bone_do_paste(ob, chan, selOnly, flip); - - if (pchan) { - /* keyframing tagging for successful paste */ - ED_autokeyframe_pchan(C, scene, ob, pchan, ks); - } - } - } - - /* Update event for pose and deformation children */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_paste(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Paste Pose"; - ot->idname = "POSE_OT_paste"; - ot->description = "Paste the stored pose on to the current pose"; - - /* api callbacks */ - ot->exec = pose_paste_exec; - ot->poll = ED_operator_posemode; - - /* flag */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_boolean(ot->srna, "flipped", FALSE, "Flipped on X-Axis", "Paste the stored pose flipped on to current pose"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - RNA_def_boolean(ot->srna, "selected_mask", FALSE, "On Selected Only", "Only paste the stored pose on to selected bones in the current pose"); -} - -/* ********************************************** */ -/* Bone Groups */ - -static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_pose_object_from_context(C); - - /* only continue if there's an object */ - if (ob == NULL) - return OPERATOR_CANCELLED; - - /* for now, just call the API function for this */ - BKE_pose_add_group(ob); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_group_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Bone Group"; - ot->idname = "POSE_OT_group_add"; - ot->description = "Add a new bone group"; - - /* api callbacks */ - ot->exec = pose_group_add_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - - -static int pose_group_remove_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_pose_object_from_context(C); - - /* only continue if there's an object */ - if (ob == NULL) - return OPERATOR_CANCELLED; - - /* for now, just call the API function for this */ - BKE_pose_remove_group(ob); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_group_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Bone Group"; - ot->idname = "POSE_OT_group_remove"; - ot->description = "Remove the active bone group"; - - /* api callbacks */ - ot->exec = pose_group_remove_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ------------ */ - -/* invoke callback which presents a list of bone-groups for the user to choose from */ -static int pose_groups_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt)) -{ - Object *ob = ED_pose_object_from_context(C); - bPose *pose; - - uiPopupMenu *pup; - uiLayout *layout; - bActionGroup *grp; - int i; - - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - pose = ob->pose; - - /* if there's no active group (or active is invalid), create a new menu to find it */ - if (pose->active_group <= 0) { - /* create a new menu, and start populating it with group names */ - pup = uiPupMenuBegin(C, op->type->name, ICON_NONE); - layout = uiPupMenuLayout(pup); - - /* special entry - allow to create new group, then use that - * (not to be used for removing though) - */ - if (strstr(op->idname, "assign")) { - uiItemIntO(layout, "New Group", ICON_NONE, op->idname, "type", 0); - uiItemS(layout); - } - - /* add entries for each group */ - for (grp = pose->agroups.first, i = 1; grp; grp = grp->next, i++) - uiItemIntO(layout, grp->name, ICON_NONE, op->idname, "type", i); - - /* finish building the menu, and process it (should result in calling self again) */ - uiPupMenuEnd(C, pup); - - return OPERATOR_CANCELLED; - } - else { - /* just use the active group index, and call the exec callback for the calling operator */ - RNA_int_set(op->ptr, "type", pose->active_group); - return op->type->exec(C, op); - } -} - -/* Assign selected pchans to the bone group that the user selects */ -static int pose_group_assign_exec(bContext *C, wmOperator *op) -{ - Object *ob = ED_pose_object_from_context(C); - bPose *pose; - short done = FALSE; - - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - pose = ob->pose; - - /* set the active group number to the one from operator props - * - if 0 after this, make a new group... - */ - pose->active_group = RNA_int_get(op->ptr, "type"); - if (pose->active_group == 0) - BKE_pose_add_group(ob); - - /* add selected bones to group then */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - pchan->agrp_index = pose->active_group; - done = TRUE; - } - CTX_DATA_END; - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - /* report done status */ - if (done) - return OPERATOR_FINISHED; - else - return OPERATOR_CANCELLED; -} - -void POSE_OT_group_assign(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Selected to Bone Group"; - ot->idname = "POSE_OT_group_assign"; - ot->description = "Add selected bones to the chosen bone group"; - - /* api callbacks */ - ot->invoke = pose_groups_menu_invoke; - ot->exec = pose_group_assign_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_int(ot->srna, "type", 0, 0, INT_MAX, "Bone Group Index", "", 0, 10); -} - - -static int pose_group_unassign_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_pose_object_from_context(C); - short done = FALSE; - - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - /* find selected bones to remove from all bone groups */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - if (pchan->agrp_index) { - pchan->agrp_index = 0; - done = TRUE; - } - } - CTX_DATA_END; - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - /* report done status */ - if (done) - return OPERATOR_FINISHED; - else - return OPERATOR_CANCELLED; -} - -void POSE_OT_group_unassign(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Selected from Bone Groups"; - ot->idname = "POSE_OT_group_unassign"; - ot->description = "Remove selected bones from all bone groups"; - - /* api callbacks */ - ot->exec = pose_group_unassign_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int group_move_exec(bContext *C, wmOperator *op) -{ - Object *ob = ED_pose_object_from_context(C); - bPose *pose = (ob) ? ob->pose : NULL; - bPoseChannel *pchan; - bActionGroup *grp; - int dir = RNA_enum_get(op->ptr, "direction"); - int grpIndexA, grpIndexB; - - if (ELEM(NULL, ob, pose)) - return OPERATOR_CANCELLED; - if (pose->active_group <= 0) - return OPERATOR_CANCELLED; - - /* get group to move */ - grp = BLI_findlink(&pose->agroups, pose->active_group - 1); - if (grp == NULL) - return OPERATOR_CANCELLED; - - /* move bone group */ - grpIndexA = pose->active_group; - if (dir == 1) { /* up */ - void *prev = grp->prev; - - if (prev == NULL) - return OPERATOR_FINISHED; - - BLI_remlink(&pose->agroups, grp); - BLI_insertlinkbefore(&pose->agroups, prev, grp); - - grpIndexB = grpIndexA - 1; - pose->active_group--; - } - else { /* down */ - void *next = grp->next; - - if (next == NULL) - return OPERATOR_FINISHED; - - BLI_remlink(&pose->agroups, grp); - BLI_insertlinkafter(&pose->agroups, next, grp); - - grpIndexB = grpIndexA + 1; - pose->active_group++; - } - - /* fix changed bone group indices in bones (swap grpIndexA with grpIndexB) */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->agrp_index == grpIndexB) - pchan->agrp_index = grpIndexA; - else if (pchan->agrp_index == grpIndexA) - pchan->agrp_index = grpIndexB; - } - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_group_move(wmOperatorType *ot) -{ - static EnumPropertyItem group_slot_move[] = { - {1, "UP", 0, "Up", ""}, - {-1, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Move Bone Group"; - ot->idname = "POSE_OT_group_move"; - ot->description = "Change position of active Bone Group in list of Bone Groups"; - - /* api callbacks */ - ot->exec = group_move_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "direction", group_slot_move, 0, "Direction", "Direction to move, UP or DOWN"); -} - -/* bone group sort element */ -typedef struct tSortActionGroup { - bActionGroup *agrp; - int index; -} tSortActionGroup; - -/* compare bone groups by name */ -static int compare_agroup(const void *sgrp_a_ptr, const void *sgrp_b_ptr) -{ - tSortActionGroup *sgrp_a = (tSortActionGroup *)sgrp_a_ptr; - tSortActionGroup *sgrp_b = (tSortActionGroup *)sgrp_b_ptr; - - return strcmp(sgrp_a->agrp->name, sgrp_b->agrp->name); -} - -static int group_sort_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_pose_object_from_context(C); - bPose *pose = (ob) ? ob->pose : NULL; - bPoseChannel *pchan; - tSortActionGroup *agrp_array; - bActionGroup *agrp; - int agrp_count; - int i; - - if (ELEM(NULL, ob, pose)) - return OPERATOR_CANCELLED; - if (pose->active_group <= 0) - return OPERATOR_CANCELLED; - - /* create temporary array with bone groups and indices */ - agrp_count = BLI_countlist(&pose->agroups); - agrp_array = MEM_mallocN(sizeof(tSortActionGroup) * agrp_count, "sort bone groups"); - for (agrp = pose->agroups.first, i = 0; agrp; agrp = agrp->next, i++) { - BLI_assert(i < agrp_count); - agrp_array[i].agrp = agrp; - agrp_array[i].index = i + 1; - } - - /* sort bone groups by name */ - qsort(agrp_array, agrp_count, sizeof(tSortActionGroup), compare_agroup); - - /* create sorted bone group list from sorted array */ - pose->agroups.first = pose->agroups.last = NULL; - for (i = 0; i < agrp_count; i++) { - BLI_addtail(&pose->agroups, agrp_array[i].agrp); - } - - /* fix changed bone group indizes in bones */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - for (i = 0; i < agrp_count; i++) { - if (pchan->agrp_index == agrp_array[i].index) { - pchan->agrp_index = i + 1; - break; - } - } - } - - /* free temp resources */ - MEM_freeN(agrp_array); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_group_sort(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Sort Bone Groups"; - ot->idname = "POSE_OT_group_sort"; - ot->description = "Sort Bone Groups by their names in ascending order"; - - /* api callbacks */ - ot->exec = group_sort_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static void pose_group_select(bContext *C, Object *ob, int select) -{ - bPose *pose = ob->pose; - - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - if (select) { - if (pchan->agrp_index == pose->active_group) - pchan->bone->flag |= BONE_SELECTED; - } - else { - if (pchan->agrp_index == pose->active_group) - pchan->bone->flag &= ~BONE_SELECTED; - } - } - } - CTX_DATA_END; -} - -static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_pose_object_from_context(C); - - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - pose_group_select(C, ob, 1); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_group_select(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Bones of Bone Group"; - ot->idname = "POSE_OT_group_select"; - ot->description = "Select bones in active Bone Group"; - - /* api callbacks */ - ot->exec = pose_group_select_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int pose_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_pose_object_from_context(C); - - /* only continue if there's an object, and a pose there too */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - - pose_group_select(C, ob, 0); - - /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_group_deselect(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Deselect Bone Group"; - ot->idname = "POSE_OT_group_deselect"; - ot->description = "Deselect bones of active Bone Group"; - - /* api callbacks */ - ot->exec = pose_group_deselect_exec; - ot->poll = ED_operator_posemode_context; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ********************************************** */ - -static int pose_flip_names_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm; - - /* paranoia checks */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - arm = ob->data; - - /* loop through selected bones, auto-naming them */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - char newname[MAXBONENAME]; - flip_side_name(newname, pchan->name, TRUE); - ED_armature_bone_rename(arm, pchan->name, newname); - } - CTX_DATA_END; - - /* since we renamed stuff... */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_flip_names(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Flip Names"; - ot->idname = "POSE_OT_flip_names"; - ot->description = "Flips (and corrects) the axis suffixes of the the names of selected bones"; - - /* api callbacks */ - ot->exec = pose_flip_names_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ------------------ */ - -static int pose_autoside_names_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm; - char newname[MAXBONENAME]; - short axis = RNA_enum_get(op->ptr, "axis"); - - /* paranoia checks */ - if (ELEM(NULL, ob, ob->pose)) - return OPERATOR_CANCELLED; - arm = ob->data; - - /* loop through selected bones, auto-naming them */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - BLI_strncpy(newname, pchan->name, sizeof(newname)); - if (bone_autoside_name(newname, 1, axis, pchan->bone->head[axis], pchan->bone->tail[axis])) - ED_armature_bone_rename(arm, pchan->name, newname); - } - CTX_DATA_END; - - /* since we renamed stuff... */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_autoside_names(wmOperatorType *ot) -{ - static EnumPropertyItem axis_items[] = { - {0, "XAXIS", 0, "X-Axis", "Left/Right"}, - {1, "YAXIS", 0, "Y-Axis", "Front/Back"}, - {2, "ZAXIS", 0, "Z-Axis", "Top/Bottom"}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "AutoName by Axis"; - ot->idname = "POSE_OT_autoside_names"; - ot->description = "Automatically renames the selected bones according to which side of the target axis they fall on"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = pose_autoside_names_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* settings */ - ot->prop = RNA_def_enum(ot->srna, "axis", axis_items, 0, "Axis", "Axis tag names with"); -} - -/* ********************************************** */ - -static int pose_bone_rotmode_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_active_object(C); - int mode = RNA_enum_get(op->ptr, "type"); - - /* set rotation mode of selected bones */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - pchan->rotmode = mode; - } - CTX_DATA_END; - - /* notifiers and updates */ - DAG_id_tag_update((ID *)ob, OB_RECALC_DATA); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_rotation_mode_set(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Set Rotation Mode"; - ot->idname = "POSE_OT_rotation_mode_set"; - ot->description = "Set the rotation representation used by selected bones"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = pose_bone_rotmode_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", posebone_rotmode_items, 0, "Rotation Mode", ""); -} - -/* ********************************************** */ - -/* Show all armature layers */ -static int pose_armature_layers_showall_poll(bContext *C) -{ - /* this single operator can be used in posemode OR editmode for armatures */ - return ED_operator_posemode(C) || ED_operator_editarmature(C); -} - -static int pose_armature_layers_showall_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (ob) ? ob->data : NULL; - PointerRNA ptr; - int maxLayers = (RNA_boolean_get(op->ptr, "all")) ? 32 : 16; - int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - int i; - - /* sanity checking */ - if (arm == NULL) - return OPERATOR_CANCELLED; - - /* use RNA to set the layers - * although it would be faster to just set directly using bitflags, we still - * need to setup a RNA pointer so that we get the "update" callbacks for free... - */ - RNA_id_pointer_create(&arm->id, &ptr); - - for (i = 0; i < maxLayers; i++) - layers[i] = 1; - - RNA_boolean_set_array(&ptr, "layers", layers); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - /* done */ - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_layers_show_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Show All Layers"; - ot->idname = "ARMATURE_OT_layers_show_all"; - ot->description = "Make all armature layers visible"; - - /* callbacks */ - ot->exec = pose_armature_layers_showall_exec; - ot->poll = pose_armature_layers_showall_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All Layers", "Enable all layers or just the first 16 (top row)"); -} - -/* ------------------- */ - -/* Present a popup to get the layers that should be used */ -static int pose_armature_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (ob) ? ob->data : NULL; - PointerRNA ptr; - int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - - /* sanity checking */ - if (arm == NULL) - return OPERATOR_CANCELLED; - - /* get RNA pointer to armature data to use that to retrieve the layers as ints to init the operator */ - RNA_id_pointer_create((ID *)arm, &ptr); - RNA_boolean_get_array(&ptr, "layers", layers); - RNA_boolean_set_array(op->ptr, "layers", layers); - - /* part to sync with other similar operators... */ - return WM_operator_props_popup(C, op, evt); -} - -/* Set the visible layers for the active armature (edit and pose modes) */ -static int pose_armature_layers_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - PointerRNA ptr; - int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - - if (ELEM(NULL, ob, ob->data)) { - return OPERATOR_CANCELLED; - } - - /* get the values set in the operator properties */ - RNA_boolean_get_array(op->ptr, "layers", layers); - - /* get pointer for armature, and write data there... */ - RNA_id_pointer_create((ID *)ob->data, &ptr); - RNA_boolean_set_array(&ptr, "layers", layers); - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - - -void POSE_OT_armature_layers(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Change Armature Layers"; - ot->idname = "POSE_OT_armature_layers"; - ot->description = "Change the visible armature layers"; - - /* callbacks */ - ot->invoke = pose_armature_layers_invoke; - ot->exec = pose_armature_layers_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible"); -} - -void ARMATURE_OT_armature_layers(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Change Armature Layers"; - ot->idname = "ARMATURE_OT_armature_layers"; - ot->description = "Change the visible armature layers"; - - /* callbacks */ - ot->invoke = pose_armature_layers_invoke; - ot->exec = pose_armature_layers_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers to make visible"); -} - -/* ------------------- */ - -/* Present a popup to get the layers that should be used */ -static int pose_bone_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt) -{ - int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - - /* get layers that are active already */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - short bit; - - /* loop over the bits for this pchan's layers, adding layers where they're needed */ - for (bit = 0; bit < 32; bit++) { - if (pchan->bone->layer & (1 << bit)) - layers[bit] = 1; - } - } - CTX_DATA_END; - - /* copy layers to operator */ - RNA_boolean_set_array(op->ptr, "layers", layers); - - /* part to sync with other similar operators... */ - return WM_operator_props_popup(C, op, evt); -} - -/* Set the visible layers for the active armature (edit and pose modes) */ -static int pose_bone_layers_exec(bContext *C, wmOperator *op) -{ - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - PointerRNA ptr; - int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - - if (ob == NULL || ob->data == NULL) { - return OPERATOR_CANCELLED; - } - - /* get the values set in the operator properties */ - RNA_boolean_get_array(op->ptr, "layers", layers); - - /* set layers of pchans based on the values set in the operator props */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - /* get pointer for pchan, and write flags this way */ - RNA_pointer_create((ID *)ob->data, &RNA_Bone, pchan->bone, &ptr); - RNA_boolean_set_array(&ptr, "layers", layers); - } - CTX_DATA_END; - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_bone_layers(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Change Bone Layers"; - ot->idname = "POSE_OT_bone_layers"; - ot->description = "Change the layers that the selected bones belong to"; - - /* callbacks */ - ot->invoke = pose_bone_layers_invoke; - ot->exec = pose_bone_layers_exec; - ot->poll = ED_operator_posemode_exclusive; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to"); -} - -/* ------------------- */ - -/* Present a popup to get the layers that should be used */ -static int armature_bone_layers_invoke(bContext *C, wmOperator *op, wmEvent *evt) -{ - int layers[32] = {0}; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - - /* get layers that are active already */ - CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) - { - short bit; - - /* loop over the bits for this pchan's layers, adding layers where they're needed */ - for (bit = 0; bit < 32; bit++) { - if (ebone->layer & (1 << bit)) - layers[bit] = 1; - } - } - CTX_DATA_END; - - /* copy layers to operator */ - RNA_boolean_set_array(op->ptr, "layers", layers); - - /* part to sync with other similar operators... */ - return WM_operator_props_popup(C, op, evt); -} - -/* Set the visible layers for the active armature (edit and pose modes) */ -static int armature_bone_layers_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_edit_object(C); - bArmature *arm = (ob) ? ob->data : NULL; - PointerRNA ptr; - int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ - - /* get the values set in the operator properties */ - RNA_boolean_get_array(op->ptr, "layers", layers); - - /* set layers of pchans based on the values set in the operator props */ - CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) - { - /* get pointer for pchan, and write flags this way */ - RNA_pointer_create((ID *)arm, &RNA_EditBone, ebone, &ptr); - RNA_boolean_set_array(&ptr, "layers", layers); - } - CTX_DATA_END; - - /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); - - return OPERATOR_FINISHED; -} - -void ARMATURE_OT_bone_layers(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Change Bone Layers"; - ot->idname = "ARMATURE_OT_bone_layers"; - ot->description = "Change the layers that the selected bones belong to"; - - /* callbacks */ - ot->invoke = armature_bone_layers_invoke; - ot->exec = armature_bone_layers_exec; - ot->poll = ED_operator_editarmature; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean_layer_member(ot->srna, "layers", 32, NULL, "Layer", "Armature layers that bone belongs to"); -} - -/* ********************************************** */ -/* Flip Quats */ - -static int pose_flip_quats_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID); - - /* loop through all selected pchans, flipping and keying (as needed) */ - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) - { - /* only if bone is using quaternion rotation */ - if (pchan->rotmode == ROT_MODE_QUAT) { - /* quaternions have 720 degree range */ - negate_v4(pchan->quat); - - ED_autokeyframe_pchan(C, scene, ob, pchan, ks); - } - } - CTX_DATA_END; - - /* notifiers and updates */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_quaternions_flip(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Flip Quats"; - ot->idname = "POSE_OT_quaternions_flip"; - ot->description = "Flip quaternion values to achieve desired rotations, while maintaining the same orientations"; - - /* callbacks */ - ot->exec = pose_flip_quats_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ********************************************** */ -/* Clear User Transforms */ - -static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - float cframe = (float)CFRA; - const short only_select = RNA_boolean_get(op->ptr, "only_selected"); - - if ((ob->adt) && (ob->adt->action)) { - /* XXX: this is just like this to avoid contaminating anything else; - * just pose values should change, so this should be fine - */ - bPose *dummyPose = NULL; - Object workob = {{0}}; - bPoseChannel *pchan; - - /* execute animation step for current frame using a dummy copy of the pose */ - BKE_pose_copy_data(&dummyPose, ob->pose, 0); - - BLI_strncpy(workob.id.name, "OB<ClearTfmWorkOb>", sizeof(workob.id.name)); - workob.type = OB_ARMATURE; - workob.data = ob->data; - workob.adt = ob->adt; - workob.pose = dummyPose; - - BKE_animsys_evaluate_animdata(scene, &workob.id, workob.adt, cframe, ADT_RECALC_ANIM); - - /* copy back values, but on selected bones only */ - for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { - pose_bone_do_paste(ob, pchan, only_select, 0); - } - - /* free temp data - free manually as was copied without constraints */ - for (pchan = dummyPose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->prop) { - IDP_FreeProperty(pchan->prop); - MEM_freeN(pchan->prop); - } - } - - /* was copied without constraints */ - BLI_freelistN(&dummyPose->chanbase); - MEM_freeN(dummyPose); - } - else { - /* no animation, so just reset whole pose to rest pose - * (cannot just restore for selected though) - */ - BKE_pose_rest(ob->pose); - } - - /* notifiers and updates */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); - - return OPERATOR_FINISHED; -} - -void POSE_OT_user_transforms_clear(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Clear User Transforms"; - ot->idname = "POSE_OT_user_transforms_clear"; - ot->description = "Reset pose on selected bones to keyframed state"; - - /* callbacks */ - ot->exec = pose_clear_user_transforms_exec; - ot->poll = ED_operator_posemode; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "only_selected", TRUE, "Only Selected", "Only visible/selected bones"); -} - diff --git a/source/blender/editors/armature/reeb.c b/source/blender/editors/armature/reeb.c index 1b5c550df11..454b0f0bceb 100644 --- a/source/blender/editors/armature/reeb.c +++ b/source/blender/editors/armature/reeb.c @@ -24,29 +24,14 @@ * \ingroup edarmature */ -#include <math.h> -#include <string.h> /* for memcpy */ -#include <stdio.h> -#include <stdlib.h> /* for qsort */ -#include <float.h> - -#include "DNA_scene_types.h" -#include "DNA_object_types.h" - #include "MEM_guardedalloc.h" #include "BKE_context.h" #include "BLI_blenlib.h" #include "BLI_math.h" -#include "BLI_utildefines.h" #include "BLI_edgehash.h" #include "BLI_ghash.h" -#include "BLI_heap.h" - -#include "BKE_mesh.h" - -#include "ONL_opennl.h" #include "reeb.h" diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt index 25df17d232a..c38ded49830 100644 --- a/source/blender/editors/curve/CMakeLists.txt +++ b/source/blender/editors/curve/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC ../include + ../../blenfont ../../blenkernel ../../blenlib ../../blenloader @@ -42,4 +43,8 @@ set(SRC curve_intern.h ) +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/curve/SConscript b/source/blender/editors/curve/SConscript index abefd3c6dd6..21c6a3732fa 100644 --- a/source/blender/editors/curve/SConscript +++ b/source/blender/editors/curve/SConscript @@ -29,9 +29,14 @@ Import ('env') sources = env.Glob('*.c') -incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' +defs = [] + +incs = '../include ../../blenfont ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' incs += ' ../../bmesh ../../gpu ../../blenloader' incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern' -env.BlenderLib ( 'bf_editors_curve', sources, Split(incs), [], libtype=['core'], priority=[45] ) +if env['WITH_BF_INTERNATIONAL']: + defs.append('WITH_INTERNATIONAL') + +env.BlenderLib ( 'bf_editors_curve', sources, Split(incs), defs, libtype=['core'], priority=[45] ) diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index b5aa55dbda9..d01e5c5d9bf 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -53,6 +53,8 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" +#include "BLF_translation.h" + #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" @@ -4925,7 +4927,7 @@ static int toggle_cyclic_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(eve for (nu = editnurb->first; nu; nu = nu->next) { if (nu->pntsu > 1 || nu->pntsv > 1) { if (nu->type == CU_NURBS) { - pup = uiPupMenuBegin(C, "Direction", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Direction"), ICON_NONE); layout = uiPupMenuLayout(pup); uiItemsEnumO(layout, op->type->idname, "direction"); uiPupMenuEnd(C, pup); @@ -6051,14 +6053,14 @@ static int delete_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) uiLayout *layout; if (obedit->type == OB_SURF) { - pup = uiPupMenuBegin(C, "Delete", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Delete"), ICON_NONE); layout = uiPupMenuLayout(pup); uiItemEnumO_ptr(layout, op->type, NULL, 0, "type", 0); uiItemEnumO_ptr(layout, op->type, NULL, 0, "type", 2); uiPupMenuEnd(C, pup); } else { - pup = uiPupMenuBegin(C, "Delete", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Delete"), ICON_NONE); layout = uiPupMenuLayout(pup); uiItemsEnumO(layout, op->type->idname, "type"); uiPupMenuEnd(C, pup); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 8cd2bd861bc..46815450bf2 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -931,7 +931,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* check that point segment of the boundbox of the eraser stroke */ if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) || - ((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1))) { + ((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1))) + { /* check if point segment of stroke had anything to do with * eraser region (either within stroke painted, or on its lines) * - this assumes that linewidth is irrelevant diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 4db79df033e..30ac360cab6 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -137,7 +137,7 @@ struct EditBone *ED_armature_edit_bone_add(struct bArmature *arm, const char *na void ED_armature_edit_bone_remove(struct bArmature *arm, EditBone *exBone); void transform_armature_mirror_update(struct Object *obedit); -void docenter_armature(struct Scene *scene, struct Object *ob, float cursor[3], int centermode, int around); +void ED_armature_origin_set(struct Scene *scene, struct Object *ob, float cursor[3], int centermode, int around); void ED_armature_apply_transform(struct Object *ob, float mat[4][4]); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 46f4515e0ca..d89d66dd62c 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -105,9 +105,10 @@ void ED_view3d_depth_tag_update(struct RegionView3D *rv3d); typedef enum { V3D_PROJ_RET_OK = 0, V3D_PROJ_RET_CLIP_NEAR = 1, /* can't avoid this when in perspective mode, (can't avoid) */ - V3D_PROJ_RET_CLIP_BB = 2, /* bounding box clip - RV3D_CLIPPING */ - V3D_PROJ_RET_CLIP_WIN = 3, /* outside window bounds */ - V3D_PROJ_RET_OVERFLOW = 4 /* outside range (mainly for short), (can't avoid) */ + V3D_PROJ_RET_CLIP_ZERO = 2, /* so close to zero we can't apply a perspective matrix usefully */ + V3D_PROJ_RET_CLIP_BB = 3, /* bounding box clip - RV3D_CLIPPING */ + V3D_PROJ_RET_CLIP_WIN = 4, /* outside window bounds */ + V3D_PROJ_RET_OVERFLOW = 5 /* outside range (mainly for short), (can't avoid) */ } eV3DProjStatus; /* some clipping tests are optional */ @@ -116,10 +117,13 @@ typedef enum { V3D_PROJ_TEST_CLIP_BB = (1 << 0), V3D_PROJ_TEST_CLIP_WIN = (1 << 1), V3D_PROJ_TEST_CLIP_NEAR = (1 << 2), + V3D_PROJ_TEST_CLIP_ZERO = (1 << 3) } eV3DProjTest; -#define V3D_PROJ_TEST_CLIP_DEFAULT (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) -#define V3D_PROJ_TEST_ALL (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) +#define V3D_PROJ_TEST_CLIP_DEFAULT \ + (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) +#define V3D_PROJ_TEST_ALL \ + (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_ZERO) /* view3d_iterators.c */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 78689c078c6..3c8a9a87fbe 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -183,6 +183,7 @@ typedef struct uiLayout uiLayout; /* scale fixed button widths by this to account for DPI */ #define UI_DPI_FAC ((U.pixelsize * (float)U.dpi) / 72.0f) +#define UI_DPI_WINDOW_FAC (((float)U.dpi) / 72.0f) /* 16 to copy ICON_DEFAULT_HEIGHT */ #define UI_DPI_ICON_SIZE ((float)16 * UI_DPI_FAC) diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 37249896e34..2c17a629ed4 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1097,34 +1097,29 @@ void uiDrawBlock(const bContext *C, uiBlock *block) /* ************* EVENTS ************* */ -static void ui_is_but_sel(uiBut *but, double *value) +int ui_is_but_push_ex(uiBut *but, double *value) { - short is_push = 0; /* (0 == UNSELECT), (1 == SELECT), (2 == DO-NOHING) */ - short is_true = TRUE; - - if (ELEM3(but->type, TOGN, ICONTOGN, OPTIONN)) { - is_true = FALSE; - } + int is_push = false; /* (0 == UNSELECT), (1 == SELECT), (-1 == DO-NOHING) */ if (but->bit) { + const bool state = ELEM3(but->type, TOGN, ICONTOGN, OPTIONN) ? false : true; int lvalue; UI_GET_BUT_VALUE_INIT(but, *value); lvalue = (int)*value; if (UI_BITBUT_TEST(lvalue, (but->bitnr))) { - is_push = is_true; + is_push = state; } else { - is_push = !is_true; + is_push = !state; } } else { switch (but->type) { case BUT: - is_push = 2; - break; case HOTKEYEVT: case KEYEVT: - is_push = 2; + case COLOR: + is_push = -1; break; case TOGBUT: case TOG: @@ -1134,42 +1129,48 @@ static void ui_is_but_sel(uiBut *but, double *value) case ICONTOG: case OPTION: UI_GET_BUT_VALUE_INIT(but, *value); - if (*value != (double)but->hardmin) is_push = 1; + if (*value != (double)but->hardmin) is_push = true; break; case ICONTOGN: case TOGN: case OPTIONN: UI_GET_BUT_VALUE_INIT(but, *value); - if (*value == 0.0) is_push = 1; + if (*value == 0.0) is_push = true; break; case ROW: case LISTROW: UI_GET_BUT_VALUE_INIT(but, *value); /* support for rna enum buts */ if (but->rnaprop && (RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG)) { - if ((int)*value & (int)but->hardmax) is_push = 1; + if ((int)*value & (int)but->hardmax) is_push = true; } else { - if (*value == (double)but->hardmax) is_push = 1; + if (*value == (double)but->hardmax) is_push = true; } break; - case COLOR: - is_push = 2; - break; default: - is_push = 2; + is_push = -1; break; } } - - if (is_push == 2) { - /* pass */ - } - else if (is_push == 1) { - but->flag |= UI_SELECT; - } - else { - but->flag &= ~UI_SELECT; + + return is_push; +} +int ui_is_but_push(uiBut *but) +{ + double value = UI_BUT_VALUE_UNSET; + return ui_is_but_push_ex(but, &value); +} + +static void ui_check_but_select(uiBut *but, double *value) +{ + switch (ui_is_but_push_ex(but, value)) { + case true: + but->flag |= UI_SELECT; + break; + case false: + but->flag &= ~UI_SELECT; + break; } } @@ -1620,8 +1621,7 @@ void ui_set_but_val(uiBut *but, double value) value = *((float *)but->poin) = (float)value; } - /* update select flag */ - ui_is_but_sel(but, &value); + ui_check_but_select(but, &value); } int ui_get_but_string_max_length(uiBut *but) @@ -2256,7 +2256,7 @@ void ui_check_but(uiBut *but) double value = UI_BUT_VALUE_UNSET; // float okwidth; // UNUSED - ui_is_but_sel(but, &value); + ui_check_but_select(but, &value); /* only update soft range while not editing */ if (but->rnaprop && !(but->editval || but->editstr || but->editvec)) { @@ -3929,15 +3929,20 @@ void uiButGetStrInfo(bContext *C, uiBut *but, ...) } } else if (type == BUT_GET_RNA_LABEL_CONTEXT) { + const char *_tmp = NULL; if (but->rnaprop) - tmp = BLI_strdup(RNA_property_translation_context(but->rnaprop)); + _tmp = RNA_property_translation_context(but->rnaprop); else if (but->optype) - tmp = BLI_strdup(RNA_struct_translation_context(but->optype->srna)); + _tmp = RNA_struct_translation_context(but->optype->srna); else if (ELEM(but->type, MENU, PULLDOWN)) { MenuType *mt = uiButGetMenuType(but); if (mt) - tmp = BLI_strdup(RNA_struct_translation_context(mt->ext.srna)); + _tmp = RNA_struct_translation_context(mt->ext.srna); + } + if (!_tmp) { /* _tmp == BLF_I18NCONTEXT_DEFAULT */ + _tmp = BLF_I18NCONTEXT_DEFAULT_BPY; } + tmp = BLI_strdup(_tmp); } else if (ELEM3(type, BUT_GET_RNAENUM_IDENTIFIER, BUT_GET_RNAENUM_LABEL, BUT_GET_RNAENUM_TIP)) { PointerRNA *ptr = NULL; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 5cff61f4239..e0d6c293be5 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -81,6 +81,8 @@ /* place the mouse at the scaled down location when un-grabbing */ #define USE_CONT_MOUSE_CORRECT +/* support dragging toggle buttons */ +#define USE_DRAG_TOGGLE /* proto */ static void ui_add_smart_controller(bContext *C, uiBut *from, uiBut *to); @@ -151,6 +153,7 @@ typedef struct uiHandleButtonData { int maxlen, selextend, selstartx; /* number editing / dragging */ + /* coords are Window/uiBlock relative (depends on the button) */ int draglastx, draglasty; int dragstartx, dragstarty; int dragchange, draglock, dragsel; @@ -406,9 +409,9 @@ static void ui_apply_but_func(bContext *C, uiBut *but) } } -static void ui_apply_autokey_undo(bContext *C, uiBut *but) +/* typically call ui_apply_undo(), ui_apply_autokey() */ +static void ui_apply_undo(uiBut *but) { - Scene *scene = CTX_data_scene(C); uiAfterFunc *after; if (but->flag & UI_BUT_UNDO) { @@ -430,6 +433,11 @@ static void ui_apply_autokey_undo(bContext *C, uiBut *but) BLI_strncpy(after->undostr, str, sizeof(after->undostr)); BLI_addtail(&UIAfterFuncs, after); } +} + +static void ui_apply_autokey(bContext *C, uiBut *but) +{ + Scene *scene = CTX_data_scene(C); /* try autokey */ ui_but_anim_autokey(C, but, scene, scene->r.cfra); @@ -732,6 +740,153 @@ static void ui_apply_but_CHARTAB(bContext *C, uiBut *but, uiHandleButtonData *da /* ****************** drag drop code *********************** */ +#ifdef USE_DRAG_TOGGLE + +typedef struct uiDragToggleHandle { + /* init */ + bool is_set; + float but_cent_start[2]; + eButType but_type_start; + + bool xy_lock[2]; + int xy_last[2]; +} uiDragToggleHandle; + +static bool ui_drag_toggle_set_xy_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start, + const int xy_src[2], const int xy_dst[2]) +{ + bool change = false; + uiBlock *block; + + for (block = ar->uiblocks.first; block; block = block->next) { + uiBut *but; + + float xy_a_block[2] = {UNPACK2(xy_src)}; + float xy_b_block[2] = {UNPACK2(xy_dst)}; + + ui_window_to_block_fl(ar, block, &xy_a_block[0], &xy_a_block[1]); + ui_window_to_block_fl(ar, block, &xy_b_block[0], &xy_b_block[1]); + + for (but = block->buttons.first; but; but = but->next) { + if (ui_is_but_interactive(but)) { + if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { + + /* execute the button */ + if (ui_is_but_bool(but) && but->type == but_type_start) { + /* is it pressed? */ + bool is_set_but = ui_is_but_push(but); + BLI_assert(ui_is_but_bool(but) == true); + if (is_set_but != is_set) { + uiButExecute(C, but); + change = true; + } + } + /* done */ + + } + } + } + } + + return change; +} + +static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const int xy_input[2]) +{ + ARegion *ar = CTX_wm_region(C); + bool do_draw = false; + int xy[2]; + + /** + * Initialize Locking: + * + * Check if we need to initialize the lock axis by finding if the first + * button we mouse over is X or Y aligned, then lock the mouse to that axis after. + */ + if (drag_info->xy_lock[0] == false && drag_info->xy_lock[1] == false) { + ARegion *ar = CTX_wm_region(C); + + /* first store the buttons original coords */ + uiBut *but = ui_but_find_mouse_over(ar, xy_input[0], xy_input[1]); + if (but) { + const float but_cent_new[2] = {BLI_rctf_cent_x(&but->rect), + BLI_rctf_cent_y(&but->rect)}; + + /* check if this is a different button, chances are high the button wont move about :) */ + if (len_manhattan_v2v2(drag_info->but_cent_start, but_cent_new) > 1.0f) { + if (fabsf(drag_info->but_cent_start[0] - but_cent_new[0]) < + fabsf(drag_info->but_cent_start[1] - but_cent_new[1])) + { + drag_info->xy_lock[0] = true; + } + else { + drag_info->xy_lock[1] = true; + } + } + } + } + /* done with axis locking */ + + + xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : drag_info->xy_last[0]; + xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : drag_info->xy_last[1]; + + + /* touch all buttons between last mouse coord and this one */ + do_draw = ui_drag_toggle_set_xy_xy(C, ar, drag_info->is_set, drag_info->but_type_start, drag_info->xy_last, xy); + + if (do_draw) { + ED_region_tag_redraw(ar); + } + + copy_v2_v2_int(drag_info->xy_last, xy); +} + +static void ui_handler_region_drag_toggle_remove(bContext *UNUSED(C), void *userdata) +{ + uiDragToggleHandle *drag_info = userdata; + MEM_freeN(drag_info); +} + +static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void *userdata) +{ + uiDragToggleHandle *drag_info = userdata; + bool done = false; + + switch (event->type) { + case LEFTMOUSE: + { + if (event->val != KM_PRESS) { + done = true; + } + break; + } + case MOUSEMOVE: + { + ui_drag_toggle_set(C, drag_info, &event->x); + break; + } + } + + if (done) { + wmWindow *win = CTX_wm_window(C); + WM_event_remove_ui_handler(&win->modalhandlers, + ui_handler_region_drag_toggle, + ui_handler_region_drag_toggle_remove, + drag_info, false); + ui_handler_region_drag_toggle_remove(C, drag_info); + + WM_event_add_mousemove(C); + return WM_UI_HANDLER_BREAK; + } + else { + return WM_UI_HANDLER_CONTINUE; + } +} + +#endif /* USE_DRAG_TOGGLE */ + + static int ui_but_mouse_inside_icon(uiBut *but, ARegion *ar, const wmEvent *event) { rcti rect; @@ -765,18 +920,24 @@ static int ui_but_start_drag(bContext *C, uiBut *but, uiHandleButtonData *data, button_activate_state(C, but, BUTTON_STATE_EXIT); data->cancel = TRUE; - +#ifdef USE_DRAG_TOGGLE if (ui_is_but_bool(but)) { - const bool is_set = (ui_get_but_val(but) != 0.0); - PointerRNA ptr; - WM_operator_properties_create(&ptr, "UI_OT_drag_toggle"); - RNA_boolean_set(&ptr, "state", !is_set); - RNA_int_set(&ptr, "last_x", data->dragstartx); - RNA_int_set(&ptr, "last_y", data->dragstarty); - WM_operator_name_call(C, "UI_OT_drag_toggle", WM_OP_INVOKE_DEFAULT, &ptr); - WM_operator_properties_free(&ptr); + uiDragToggleHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__); + + drag_info->is_set = ui_is_but_push(but); + drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect); + drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect); + drag_info->but_type_start = but->type; + copy_v2_v2_int(drag_info->xy_last, &event->x); + + WM_event_add_ui_handler(C, &data->window->modalhandlers, + ui_handler_region_drag_toggle, + ui_handler_region_drag_toggle_remove, + drag_info); } - else { + else +#endif + { wmDrag *drag; drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but)); @@ -2485,25 +2646,32 @@ static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHa static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { +#ifdef USE_DRAG_TOGGLE if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS && ui_is_but_bool(but)) { + data->togdual = event->ctrl; + data->togonly = !event->shift; + ui_apply_button(C, but->block, but, data, true); button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); data->dragstartx = event->x; data->dragstarty = event->y; - return WM_UI_HANDLER_CONTINUE; + return WM_UI_HANDLER_BREAK; } - + } + else if (data->state == BUTTON_STATE_WAIT_DRAG) { + /* note: the 'BUTTON_STATE_WAIT_DRAG' part of 'ui_do_but_EXIT' could be refactored into its own function */ + data->applied = false; + return ui_do_but_EXIT(C, but, data, event); + } +#endif + if (data->state == BUTTON_STATE_HIGHLIGHT) { if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { data->togdual = event->ctrl; data->togonly = !event->shift; button_activate_state(C, but, BUTTON_STATE_EXIT); - return WM_UI_HANDLER_CONTINUE; + return WM_UI_HANDLER_BREAK; } } - else if (data->state == BUTTON_STATE_WAIT_DRAG) { - /* note: the 'BUTTON_STATE_WAIT_DRAG' part of 'ui_do_but_EXIT' could be refactored into its own function */ - return ui_do_but_EXIT(C, but, data, event); - } return WM_UI_HANDLER_CONTINUE; } @@ -2524,13 +2692,15 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con return WM_UI_HANDLER_CONTINUE; } } +#ifdef USE_DRAG_TOGGLE if (event->type == LEFTMOUSE && ui_is_but_bool(but)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); data->dragstartx = event->x; data->dragstarty = event->y; return WM_UI_HANDLER_CONTINUE; } - +#endif + if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { int ret = WM_UI_HANDLER_BREAK; /* XXX (a bit ugly) Special case handling for filebrowser drag button */ @@ -3246,13 +3416,14 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co return WM_UI_HANDLER_BREAK; } } +#ifdef USE_DRAG_TOGGLE if (event->type == LEFTMOUSE && ui_is_but_bool(but)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); data->dragstartx = event->x; data->dragstarty = event->y; return WM_UI_HANDLER_BREAK; } - +#endif /* regular open menu */ if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); @@ -4253,7 +4424,7 @@ static int ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) { /* resize histogram widget itself */ - hist->height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my))/UI_DPI_FAC; + hist->height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my)) / UI_DPI_FAC; } else { /* scale histogram values (dy / 10 for better control) */ @@ -4337,7 +4508,7 @@ static int ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx, if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) { /* resize waveform widget itself */ - scopes->wavefrm_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my))/UI_DPI_FAC; + scopes->wavefrm_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my)) / UI_DPI_FAC; } else { /* scale waveform values */ @@ -4419,7 +4590,7 @@ static int ui_numedit_but_VECTORSCOPE(uiBut *but, uiHandleButtonData *data, int if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) { /* resize vectorscope widget itself */ - scopes->vecscope_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my))/UI_DPI_FAC; + scopes->vecscope_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my)) / UI_DPI_FAC; } data->draglastx = mx; @@ -4622,7 +4793,7 @@ static int ui_numedit_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonDa if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) { /* resize preview widget itself */ - scopes->track_preview_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my))/UI_DPI_FAC; + scopes->track_preview_height = (BLI_rctf_size_y(&but->rect) + (data->dragstarty - my)) / UI_DPI_FAC; } else { if (!scopes->track_locked) { @@ -5452,6 +5623,24 @@ static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y) return 1; } +/** + * Can we mouse over the button or is it hidden/disabled/layout. + */ +bool ui_is_but_interactive(uiBut *but) +{ + /* note, LABEL is included for highlights, this allows drags */ + if (but->type == LABEL && but->dragpoin == NULL) + return false; + if (ELEM3(but->type, ROUNDBOX, SEPR, LISTBOX)) + return false; + if (but->flag & UI_HIDDEN) + return false; + if (but->flag & UI_SCROLLED) + return false; + + return true; +} + uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y) { uiBlock *block; @@ -5469,17 +5658,11 @@ uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y) ui_window_to_block(ar, block, &mx, &my); for (but = block->buttons.first; but; but = but->next) { - /* note, LABEL is included for highlights, this allows drags */ - if (but->type == LABEL && but->dragpoin == NULL) - continue; - if (ELEM3(but->type, ROUNDBOX, SEPR, LISTBOX)) - continue; - if (but->flag & UI_HIDDEN) - continue; - if (but->flag & UI_SCROLLED) - continue; - if (ui_but_contains_pt(but, mx, my)) - butover = but; + if (ui_is_but_interactive(but)) { + if (ui_but_contains_pt(but, mx, my)) { + butover = but; + } + } } /* CLIP_EVENTS prevents the event from reaching other blocks */ @@ -5795,7 +5978,8 @@ static void button_activate_exit(bContext *C, uiBut *but, uiHandleButtonData *da if (!onfree && !data->cancel) { /* autokey & undo push */ - ui_apply_autokey_undo(C, but); + ui_apply_undo(but); + ui_apply_autokey(C, but); /* popup menu memory */ if (block->flag & UI_BLOCK_POPUP_MEMORY) @@ -5820,12 +6004,13 @@ static void button_activate_exit(bContext *C, uiBut *but, uiHandleButtonData *da /* redraw (data is but->active!) */ ED_region_tag_redraw(data->region); - + /* clean up button */ if (but->active) { MEM_freeN(but->active); but->active = NULL; } + but->flag &= ~(UI_ACTIVE | UI_SELECT); but->flag |= UI_BUT_LAST_ACTIVE; if (!onfree) @@ -6084,6 +6269,7 @@ void ui_button_execute_do(struct bContext *C, struct ARegion *ar, uiBut *but) data->region = ar; ui_apply_button(C, but->block, but, data, true); /* use onfree event so undo is handled by caller and apply is already done above */ + ui_apply_autokey(C, but); button_activate_exit((bContext *)C, but, data, false, true); but->active = active_back; } @@ -6107,14 +6293,12 @@ static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiBu static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) { - uiHandleButtonData *data; + uiHandleButtonData *data = but->active; + const uiButtonActivateType state_orig = data->state; uiBlock *block; ARegion *ar; - uiBut *postbut; - uiButtonActivateType posttype; int retval; - data = but->active; block = but->block; ar = data->region; @@ -6266,14 +6450,28 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) } if (data->state == BUTTON_STATE_EXIT) { - postbut = data->postbut; - posttype = data->posttype; + uiBut *post_but = data->postbut; + uiButtonActivateType post_type = data->posttype; - button_activate_exit(C, but, data, (postbut == NULL), false); + button_activate_exit(C, but, data, (post_but == NULL), false); /* for jumping to the next button with tab while text editing */ - if (postbut) - button_activate_init(C, ar, postbut, posttype); + if (post_but) { + button_activate_init(C, ar, post_but, post_type); + } + else { + /* XXX issue is because WM_event_add_mousemove(C) is a bad hack and not reliable, + *if that gets coded better this bypass can go away too. + * + * This is needed to make sure if a button was active, + * it stays active while the mouse is over it. + * This avoids adding mousemoves, see: [#33466] */ + if (ELEM(state_orig, BUTTON_ACTIVATE, BUTTON_ACTIVATE_OVER)) { + if (ui_but_find_mouse_over(ar, event->x, event->y) == but) { + button_activate_init(C, ar, but, state_orig); + } + } + } } return retval; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index f0e59f1f935..9093e8f8d30 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -171,8 +171,8 @@ struct uiBut { char *str; char strdata[UI_MAX_NAME_STR]; char drawstr[UI_MAX_DRAW_STR]; - - rctf rect; + + rctf rect; /* block relative coords */ char *poin; float hardmin, hardmax, softmin, softmax; @@ -225,7 +225,7 @@ struct uiBut { void *rename_orig; uiLink *link; - short linkto[2]; + short linkto[2]; /* region relative coords */ const char *tip, *lockstr; @@ -410,6 +410,11 @@ extern int ui_is_but_bool(uiBut *but); extern int ui_is_but_unit(uiBut *but); extern int ui_is_but_rna_valid(uiBut *but); extern int ui_is_but_utf8(uiBut *but); +extern bool ui_is_but_interactive(uiBut *but); + +extern int ui_is_but_push_ex(uiBut *but, double *value); +extern int ui_is_but_push(uiBut *but); + extern void ui_bounds_block(uiBlock *block); extern void ui_block_translate(uiBlock *block, int x, int y); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c69f53a53d2..1e95b4df762 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -2263,6 +2263,7 @@ uiLayout *uiLayoutRow(uiLayout *layout, int align) litem->enabled = 1; litem->context = layout->context; litem->space = (align) ? 0 : layout->root->style->buttonspacex; + litem->redalert = layout->redalert; litem->w = layout->w; BLI_addtail(&layout->items, litem); @@ -2283,6 +2284,7 @@ uiLayout *uiLayoutColumn(uiLayout *layout, int align) litem->enabled = 1; litem->context = layout->context; litem->space = (litem->align) ? 0 : layout->root->style->buttonspacey; + litem->redalert = layout->redalert; litem->w = layout->w; BLI_addtail(&layout->items, litem); @@ -2303,6 +2305,7 @@ uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, int align) flow->litem.enabled = 1; flow->litem.context = layout->context; flow->litem.space = (flow->litem.align) ? 0 : layout->root->style->columnspace; + flow->litem.redalert = layout->redalert; flow->litem.w = layout->w; flow->number = number; BLI_addtail(&layout->items, flow); @@ -2323,6 +2326,7 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type) box->litem.enabled = 1; box->litem.context = layout->context; box->litem.space = layout->root->style->columnspace; + box->litem.redalert = layout->redalert; box->litem.w = layout->w; BLI_addtail(&layout->items, box); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index d48d699d881..3298859ee0c 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1071,222 +1071,6 @@ static void UI_OT_reloadtranslation(wmOperatorType *ot) ot->exec = reloadtranslation_exec; } - -/* -------------------------------------------------------------------- */ -/* Toggle Drag Operator */ - -typedef struct DragOpInfo { - bool xy_lock[2]; - float but_cent_start[2]; - eButType but_type_start; -} DragOpInfo; - -typedef struct DragOpPlotData { - bContext *C; - ARegion *ar; - bool is_set; - eButType but_type_start; - bool do_draw; - const uiBut *but_prev; -} DragOpPlotData; - -static const uiBut *ui_but_set_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start, - const int xy[2], const uiBut *but_prev) -{ - uiBut *but = ui_but_find_mouse_over(ar, xy[0], xy[1]); - - if (but_prev == but) { - return but_prev; - } - - if (but && ui_is_but_bool(but) && but->type == but_type_start) { - /* is it pressed? */ - bool is_set_but = (ui_get_but_val(but) != 0.0); - BLI_assert(ui_is_but_bool(but) == true); - if (is_set_but != is_set) { - uiButExecute(C, but); - return but; - } - } - - return but_prev; -} - -static int ui_but_set_cb(int x, int y, void *data_v) -{ - DragOpPlotData *data = data_v; - int xy[2] = {x, y}; - data->but_prev = ui_but_set_xy(data->C, data->ar, data->is_set, data->but_type_start, xy, data->but_prev); - return 1; /* keep going */ -} - -/* operates on buttons between 2 mouse-points */ -static bool ui_but_set_xy_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start, - const int xy_src[2], const int xy_dst[2]) -{ - DragOpPlotData data; - data.C = C; - data.ar = ar; - data.is_set = is_set; - data.but_type_start = but_type_start; - data.do_draw = false; - data.but_prev = NULL; - - - /* prevent dragging too fast loosing buttons */ - plot_line_v2v2i(xy_src, xy_dst, ui_but_set_cb, &data); - - return data.do_draw; -} - -static void ui_drag_but_set(bContext *C, wmOperator *op, const int xy_input[2]) -{ - ARegion *ar = CTX_wm_region(C); - DragOpInfo *drag_info = op->customdata; - bool do_draw = false; - - const bool is_set = RNA_boolean_get(op->ptr, "state"); - const int xy_last[2] = {RNA_int_get(op->ptr, "last_x"), - RNA_int_get(op->ptr, "last_y")}; - - int xy[2]; - - /** - * Initialize Locking: - * - * Check if we need to initialize the lock axis by finding if the first - * button we mouse over is X or Y aligned, then lock the mouse to that axis after. - */ - if (drag_info->xy_lock[0] == false && drag_info->xy_lock[1] == false) { - ARegion *ar = CTX_wm_region(C); - - /* first store the buttons original coords */ - uiBut *but = ui_but_find_mouse_over(ar, xy_input[0], xy_input[1]); - if (but) { - const float but_cent_new[2] = {BLI_rctf_cent_x(&but->rect), - BLI_rctf_cent_y(&but->rect)}; - - /* check if this is a different button, chances are high the button wont move about :) */ - if (len_manhattan_v2v2(drag_info->but_cent_start, but_cent_new) > 1.0f) { - if (fabsf(drag_info->but_cent_start[0] - but_cent_new[0]) < - fabsf(drag_info->but_cent_start[1] - but_cent_new[1])) - { - drag_info->xy_lock[0] = true; - } - else { - drag_info->xy_lock[1] = true; - } - } - } - } - /* done with axis locking */ - - - xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : xy_last[0]; - xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : xy_last[1]; - - - /* touch all buttons between last mouse coord and this one */ - do_draw = ui_but_set_xy_xy(C, ar, is_set, drag_info->but_type_start, xy_last, xy); - - if (do_draw) { - ED_region_tag_redraw(ar); - } - - RNA_int_set(op->ptr, "last_x", xy[0]); - RNA_int_set(op->ptr, "last_y", xy[1]); -} - -static int ui_drag_toggle_invoke(bContext *C, wmOperator *op, wmEvent *event) -{ - int xy_last[2] = {RNA_int_get(op->ptr, "last_x"), - RNA_int_get(op->ptr, "last_y")}; - - float but_cent_start[2]; - eButType but_type_start; - DragOpInfo *drag_info; - - { - /* find the button where we started dragging */ - ARegion *ar = CTX_wm_region(C); - uiBut *but = ui_but_find_mouse_over(ar, xy_last[0], xy_last[1]); - if (but) { - but_cent_start[0] = BLI_rctf_cent_x(&but->rect); - but_cent_start[1] = BLI_rctf_cent_y(&but->rect); - but_type_start = but->type; - } - else { - return OPERATOR_CANCELLED; - } - } - - drag_info = op->customdata = MEM_callocN(sizeof(DragOpInfo), __func__); - copy_v2_v2(drag_info->but_cent_start, but_cent_start); - drag_info->but_type_start = but_type_start; - - /* set the initial button */ - ui_drag_but_set(C, op, xy_last); - ui_drag_but_set(C, op, &event->x); - - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; -} - -static int ui_drag_toggle_modal(bContext *C, wmOperator *op, wmEvent *event) -{ - bool done = false; - - switch (event->type) { - case LEFTMOUSE: - { - if (event->val != KM_PRESS) { - done = true; - } - break; - } - case MOUSEMOVE: - { - ui_drag_but_set(C, op, &event->x); - break; - } - } - - if (done) { - MEM_freeN(op->customdata); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_RUNNING_MODAL; - } -} - -static int ui_drag_toggle_cancel(bContext *UNUSED(C), wmOperator *op) -{ - MEM_freeN(op->customdata); - return OPERATOR_CANCELLED; -} - -static void UI_OT_drag_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Button Drag Toggle"; - ot->description = ""; - ot->idname = "UI_OT_drag_toggle"; - - /* api callbacks */ - ot->invoke = ui_drag_toggle_invoke; - ot->modal = ui_drag_toggle_modal; - ot->cancel = ui_drag_toggle_cancel; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; - - /* properties */ - RNA_def_boolean(ot->srna, "state", true, "State", ""); - RNA_def_int(ot->srna, "last_x", 0, 0, INT_MAX, "X", "", 0, INT_MAX); - RNA_def_int(ot->srna, "last_y", 0, 0, INT_MAX, "Y", "", 0, INT_MAX); -} - /* ********************************************************* */ /* Registration */ @@ -1304,6 +1088,4 @@ void UI_buttons_operatortypes(void) WM_operatortype_append(UI_OT_edittranslation_init); #endif WM_operatortype_append(UI_OT_reloadtranslation); - WM_operatortype_append(UI_OT_drag_toggle); } - diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 900ba7de559..83540cef9fc 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2594,7 +2594,8 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co /* if list length changes and active is out of view, scroll to it */ if ((ui_list->list_last_len != len) && - (activei < ui_list->list_scroll || activei >= ui_list->list_scroll + items)) { + (activei < ui_list->list_scroll || activei >= ui_list->list_scroll + items)) + { ui_list->list_scroll = activei; } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 68df8e29957..dd7b1d0ee06 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1855,7 +1855,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r widget_verts_to_quad_strip(&wtb, totvert, quad_strip); glVertexPointer(2, GL_FLOAT, 0, quad_strip); - glDrawArrays(GL_QUAD_STRIP, 0, totvert * 2 + 2); + glDrawArrays(GL_QUAD_STRIP, 0, totvert * 2); /* add + 2 for getting a complete soft rect. Now it skips top edge to allow transparent menus */ } glDisableClientState(GL_VERTEX_ARRAY); diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 7f1140501b5..daa78957231 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -1034,10 +1034,11 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event) } else { /* 'continuous' or 'dolly' */ - float fac, zoomfac = 0.001f * v2d->maxzoom; + float fac, zoomfac = 0.01f; /* some view2d's (graph) don't have min/max zoom, or extreme ones */ - CLAMP (zoomfac, 0.001f, 0.01f); + if (v2d->maxzoom > 0.0f) + zoomfac = CLAMPIS(0.001f * v2d->maxzoom, 0.001f, 0.01f); /* x-axis transform */ fac = zoomfac * (event->x - vzd->lastx); @@ -1116,7 +1117,7 @@ static void VIEW2D_OT_zoom(wmOperatorType *ot) ot->poll = view_zoom_poll; /* operator is repeatable */ - // ot->flag = OPTYPE_BLOCKING; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; /* rna - must keep these in sync with the other operators */ RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX); diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 7db23041c88..d4ae5b8b29b 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -47,4 +47,8 @@ if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + blender_add_lib(bf_editor_io "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/io/SConscript b/source/blender/editors/io/SConscript index cef73f33ddd..b7449ccad79 100644 --- a/source/blender/editors/io/SConscript +++ b/source/blender/editors/io/SConscript @@ -36,4 +36,7 @@ incs += '../../makesdna ../../makesrna ../../windowmanager ../../collada' if env['WITH_BF_COLLADA']: defs += ['WITH_COLLADA'] +if env['WITH_BF_INTERNATIONAL']: + defs += ['WITH_INTERNATIONAL'] + env.BlenderLib ( 'bf_editor_io', sources, Split(incs), defines=defs, libtype=['core','player'], priority=[330,210] ) diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index 7703a8638c9..4908c101a7c 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -92,6 +92,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) int use_texture_copies; int active_uv_only; + int triangulate; int use_object_instantiation; int sort_by_name; int second_life; @@ -118,6 +119,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) use_texture_copies = RNA_boolean_get(op->ptr, "use_texture_copies"); active_uv_only = RNA_boolean_get(op->ptr, "active_uv_only"); + triangulate = RNA_boolean_get(op->ptr, "triangulate"); use_object_instantiation = RNA_boolean_get(op->ptr, "use_object_instantiation"); sort_by_name = RNA_boolean_get(op->ptr, "sort_by_name"); second_life = RNA_boolean_get(op->ptr, "second_life"); @@ -140,9 +142,11 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) include_material_textures, use_texture_copies, + triangulate, use_object_instantiation, sort_by_name, - second_life)) { + second_life)) + { return OPERATOR_FINISHED; } else { @@ -217,6 +221,8 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr) uiItemL(row, IFACE_("Collada Options:"), ICON_MODIFIER); row = uiLayoutRow(box, FALSE); + uiItemR(row, imfptr, "triangulate", 0, NULL, ICON_NONE); + row = uiLayoutRow(box, FALSE); uiItemR(row, imfptr, "use_object_instantiation", 0, NULL, ICON_NONE); row = uiLayoutRow(box, FALSE); uiItemR(row, imfptr, "sort_by_name", 0, NULL, ICON_NONE); @@ -293,6 +299,9 @@ void WM_OT_collada_export(wmOperatorType *ot) "Copy textures to same folder where the .dae file is exported"); + RNA_def_boolean(ot->srna, "triangulate", 1, "Triangulate", + "Export Polygons (Quads & NGons) as Triangles"); + RNA_def_boolean(ot->srna, "use_object_instantiation", 1, "Use Object Instances", "Instantiate multiple Objects from same Data"); diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c index 7ac27e038a4..7ddf2b54a88 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.c @@ -181,11 +181,17 @@ void paintface_reveal(Object *ob) static void hash_add_face(EdgeHash *ehash, MPoly *mp, MLoop *mloop) { - MLoop *ml; - int i; + MLoop *ml, *ml_next; + int i = mp->totloop; + + ml_next = mloop; /* first loop */ + ml = &ml_next[i - 1]; /* last loop */ + + while (i-- != 0) { + BLI_edgehash_insert(ehash, ml->v, ml_next->v, NULL); - for (i = 0, ml = mloop; i < mp->totloop; i++, ml++) { - BLI_edgehash_insert(ehash, ml->v, ME_POLY_LOOP_NEXT(mloop, mp, i)->v, NULL); + ml = ml_next; + ml_next++; } } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 57f116693f8..51161970c0b 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -933,9 +933,13 @@ static int edbm_select_mode_exec(bContext *C, wmOperator *op) static int edbm_select_mode_invoke(bContext *C, wmOperator *op, wmEvent *event) { - // RNA_enum_set(op->ptr, "type"); /* type must be set already */ - RNA_boolean_set(op->ptr, "use_extend", event->shift); - RNA_boolean_set(op->ptr, "use_expand", event->ctrl); + /* detecting these options based on shift/ctrl here is weak, but it's done + * to make this work when clicking buttons or menus */ + if (!RNA_struct_property_is_set(op->ptr, "use_extend")) + RNA_boolean_set(op->ptr, "use_extend", event->shift); + if (!RNA_struct_property_is_set(op->ptr, "use_expand")) + RNA_boolean_set(op->ptr, "use_expand", event->ctrl); + return edbm_select_mode_exec(C, op); } @@ -1358,6 +1362,22 @@ static int edgetag_context_check(Scene *scene, BMesh *bm, BMEdge *e) return 0; } +static void edgetag_ensure_cd_flag(Scene *scene, Mesh *me) +{ + BMesh *bm = me->edit_btmesh->bm; + + switch (scene->toolsettings->edge_mode) { + case EDGE_MODE_TAG_CREASE: + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE); + break; + case EDGE_MODE_TAG_BEVEL: + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT); + break; + default: + break; + } +} + static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge *e_dst) { /* BM_ELEM_TAG flag is used to store visited edges */ @@ -1371,16 +1391,7 @@ static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge /* note, would pass BM_EDGE except we are looping over all edges anyway */ BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); - switch (scene->toolsettings->edge_mode) { - case EDGE_MODE_TAG_CREASE: - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(OBACT), ME_CDFLAG_EDGE_CREASE); - break; - case EDGE_MODE_TAG_BEVEL: - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(OBACT), ME_CDFLAG_EDGE_BWEIGHT); - break; - default: - break; - } + edgetag_ensure_cd_flag(scene, OBACT->data); BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) { if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == FALSE) { @@ -1470,7 +1481,7 @@ static int mouse_mesh_shortest_path_edge(ViewContext *vc) e_dst = EDBM_edge_find_nearest(vc, &dist); if (e_dst) { Mesh *me = vc->obedit->data; - int path = 0; + bool is_path = false; if (em->bm->selected.last) { BMEditSelection *ese = em->bm->selected.last; @@ -1481,13 +1492,14 @@ static int mouse_mesh_shortest_path_edge(ViewContext *vc) if (e_act != e_dst) { if (edgetag_shortest_path(vc->scene, em->bm, e_act, e_dst)) { BM_select_history_remove(em->bm, e_act); - path = 1; + is_path = true; } } } } - if (path == 0) { + if (is_path == false) { int act = (edgetag_context_check(vc->scene, em->bm, e_dst) == 0); + edgetag_ensure_cd_flag(vc->scene, vc->obedit->data); edgetag_context_set(em->bm, vc->scene, e_dst, act); /* switch the edge option */ } @@ -2963,7 +2975,7 @@ void MESH_OT_select_random(wmOperatorType *ot) /* props */ RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, "Percent", "Percentage of elements to select randomly", 0.f, 100.0f); - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } static int edbm_select_next_loop_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index dcb25423b80..92e1dbf433f 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3321,7 +3321,8 @@ static int edbm_fill_exec(bContext *C, wmOperator *op) if (!EDBM_op_init(em, &bmop, op, "triangle_fill edges=%he use_beauty=%b", - BM_ELEM_SELECT, use_beauty)) { + BM_ELEM_SELECT, use_beauty)) + { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index f983a43f573..378f6374336 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -149,7 +149,7 @@ int join_mesh_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* remove tessface to ensure we don't old references to invalid faces */ + /* remove tessface to ensure we don't hold references to invalid faces */ BKE_mesh_tessface_clear(me); /* new material indices and material array */ @@ -309,6 +309,10 @@ int join_mesh_exec(bContext *C, wmOperator *op) me = base->object->data; if (me->totvert) { + + /* merge customdata flag */ + ((Mesh *)ob->data)->cd_flag |= me->cd_flag; + /* standard data */ CustomData_merge(&me->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert); CustomData_copy_data(&me->vdata, &vdata, 0, vertofs, me->totvert); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index bc577f6ee29..997cbb71683 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -67,6 +67,7 @@ #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_group.h" +#include "BKE_image.h" #include "BKE_lamp.h" #include "BKE_lattice.h" #include "BKE_library.h" @@ -735,6 +736,83 @@ void OBJECT_OT_empty_add(wmOperatorType *ot) ED_object_add_generic_props(ot, FALSE); } +static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Base *base = NULL; + Image *ima = NULL; + Object *ob = NULL; + + /* check image input variables */ + if (RNA_struct_property_is_set(op->ptr, "filepath")) { + char path[FILE_MAX]; + + RNA_string_get(op->ptr, "filepath", path); + ima = BKE_image_load_exists(path); + } + else if (RNA_struct_property_is_set(op->ptr, "name")) { + char name[MAX_ID_NAME - 2]; + + RNA_string_get(op->ptr, "name", name); + ima = (Image *)BKE_libblock_find_name(ID_IM, name); + } + + if (ima == NULL) { + BKE_report(op->reports, RPT_ERROR, "Not an image"); + return OPERATOR_CANCELLED; + } + + base = ED_view3d_give_base_under_cursor(C, event->mval); + + /* if empty under cursor, then set object */ + if (base && base->object->type == OB_EMPTY) { + ob = base->object; + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, CTX_data_scene(C)); + } + else { + /* add new empty */ + unsigned int layer; + float rot[3]; + + if (!ED_object_add_generic_get_opts(C, op, NULL, rot, NULL, &layer, NULL)) + return OPERATOR_CANCELLED; + + ob = ED_object_add_type(C, OB_EMPTY, NULL, rot, FALSE, layer); + + /* add under the mouse */ + ED_object_location_from_view(C, ob->loc); + ED_view3d_cursor3d_position(C, ob->loc, event->mval); + } + + ob->empty_drawtype = OB_EMPTY_IMAGE; + ob->data = ima; + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_drop_named_image(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Add Empty Image/Drop Image To Empty"; + ot->description = "Add an empty image type to scene with data"; + ot->idname = "OBJECT_OT_drop_named_image"; + + /* api callbacks */ + ot->invoke = empty_drop_named_image_invoke; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_string(ot->srna, "filepath", "", FILE_MAX, "Filepath", "Path to image file"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Image name to assign"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ED_object_add_generic_props(ot, FALSE); +} + /********************* Add Lamp Operator ********************/ static const char *get_lamp_defname(int type) @@ -2034,12 +2112,17 @@ static int add_named_exec(bContext *C, wmOperator *op) } basen->lay = basen->object->lay = scene->lay; + basen->object->restrictflag &= ~OB_RESTRICT_VIEW; if (event) { + ARegion *ar = CTX_wm_region(C); + const int mval[2] = {event->x - ar->winrct.xmin, + event->y - ar->winrct.ymin}; ED_object_location_from_view(C, basen->object->loc); - ED_view3d_cursor3d_position(C, basen->object->loc, event->mval); + ED_view3d_cursor3d_position(C, basen->object->loc, mval); } + ED_base_object_select(basen, BA_SELECT); ED_base_object_activate(C, basen); copy_object_set_idnew(C, dupflag); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 84b989e2f59..1bed7d7dd11 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -40,6 +40,8 @@ #include "BLI_dynstr.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "DNA_anim_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" @@ -1873,7 +1875,7 @@ static int pose_ik_add_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt)) } /* prepare popup menu to choose targetting options */ - pup = uiPupMenuBegin(C, "Add IK", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Add IK"), ICON_NONE); layout = uiPupMenuLayout(pup); /* the type of targets we'll set determines the menu entries to show... */ @@ -1882,14 +1884,14 @@ static int pose_ik_add_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt)) * - the only thing that matters is that we want a target... */ if (tar_pchan) - uiItemBooleanO(layout, "To Active Bone", ICON_NONE, "POSE_OT_ik_add", "with_targets", 1); + uiItemBooleanO(layout, IFACE_("To Active Bone"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1); else - uiItemBooleanO(layout, "To Active Object", ICON_NONE, "POSE_OT_ik_add", "with_targets", 1); + uiItemBooleanO(layout, IFACE_("To Active Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1); } else { /* we have a choice of adding to a new empty, or not setting any target (targetless IK) */ - uiItemBooleanO(layout, "To New Empty Object", ICON_NONE, "POSE_OT_ik_add", "with_targets", 1); - uiItemBooleanO(layout, "Without Targets", ICON_NONE, "POSE_OT_ik_add", "with_targets", 0); + uiItemBooleanO(layout, IFACE_("To New Empty Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1); + uiItemBooleanO(layout, IFACE_("Without Targets"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 0); } /* finish building the menu, and process it (should result in calling self again) */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 4523ac098d6..a302aa65fd0 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -113,6 +113,7 @@ void OBJECT_OT_metaball_add(struct wmOperatorType *ot); void OBJECT_OT_text_add(struct wmOperatorType *ot); void OBJECT_OT_armature_add(struct wmOperatorType *ot); void OBJECT_OT_empty_add(struct wmOperatorType *ot); +void OBJECT_OT_drop_named_image(struct wmOperatorType *ot); void OBJECT_OT_lamp_add(struct wmOperatorType *ot); void OBJECT_OT_effector_add(struct wmOperatorType *ot); void OBJECT_OT_camera_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index bf408d6dbe4..e5aaaffade8 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -114,6 +114,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_text_add); WM_operatortype_append(OBJECT_OT_armature_add); WM_operatortype_append(OBJECT_OT_empty_add); + WM_operatortype_append(OBJECT_OT_drop_named_image); WM_operatortype_append(OBJECT_OT_lamp_add); WM_operatortype_append(OBJECT_OT_camera_add); WM_operatortype_append(OBJECT_OT_speaker_add); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index c5e8310e9fb..f9dde043607 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -304,7 +304,7 @@ static int make_proxy_invoke(bContext *C, wmOperator *op, wmEvent *evt) } else if (ob->id.lib) { - uiPopupMenu *pup = uiPupMenuBegin(C, "OK?", ICON_QUESTION); + uiPopupMenu *pup = uiPupMenuBegin(C, IFACE_("OK?"), ICON_QUESTION); uiLayout *layout = uiPupMenuLayout(pup); /* create operator menu item with relevant properties filled in */ diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index 775a2897513..baa0199baf7 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -50,6 +50,8 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "BKE_context.h" #include "BKE_group.h" #include "BKE_main.h" @@ -606,7 +608,7 @@ static short select_grouped_group(bContext *C, Object *ob) /* Select objects in } /* build the menu. */ - pup = uiPupMenuBegin(C, "Select Group", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Select Group"), ICON_NONE); layout = uiPupMenuLayout(pup); for (i = 0; i < group_count; i++) { diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 01dcac2d1b4..3fa6c301fcb 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -871,7 +871,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) /* Function to recenter armatures in editarmature.c * Bone + object locations are handled there. */ - docenter_armature(scene, ob, cursor, centermode, around); + ED_armature_origin_set(scene, ob, cursor, centermode, around); tot_change++; arm->id.flag |= LIB_DOIT; diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 87ff42ef4db..ef882b2486b 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -558,8 +558,10 @@ static int ed_vgroup_transfer_weight(Object *ob_dst, Object *ob_src, bDeformGrou } /* Loop through the vertices.*/ - for (i = 0, dv_src = dv_array_src, dv_dst = dv_array_dst; i < me_dst->totvert; - i++, dv_dst++, dv_src++, mv_src++, mv_dst++) { + for (i = 0, dv_src = dv_array_src, dv_dst = dv_array_dst; + i < me_dst->totvert; + i++, dv_dst++, dv_src++, mv_src++, mv_dst++) + { if (*dv_dst == NULL) { continue; @@ -2598,7 +2600,7 @@ static int vertex_group_poll(bContext *C) return (ob && !ob->id.lib && OB_TYPE_SUPPORT_VGROUP(ob->type) && data && !data->lib); } -static int UNUSED_FUNCTION(vertex_group_poll_edit) (bContext * C) +static int UNUSED_FUNCTION(vertex_group_poll_edit) (bContext *C) { Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; diff --git a/source/blender/editors/physics/physics_pointcache.c b/source/blender/editors/physics/physics_pointcache.c index bbce94b6215..b9742c9968f 100644 --- a/source/blender/editors/physics/physics_pointcache.c +++ b/source/blender/editors/physics/physics_pointcache.c @@ -90,6 +90,20 @@ static void bake_console_progress_end(void *UNUSED(arg)) printf("\rbake: done!\n"); } +static void ptcache_free_bake(PointCache *cache) +{ + if (cache->edit) { + if (!cache->edit->edited || 1) {// XXX okee("Lose changes done in particle mode?")) { + PE_free_ptcache_edit(cache->edit); + cache->edit = NULL; + cache->flag &= ~PTCACHE_BAKED; + } + } + else { + cache->flag &= ~PTCACHE_BAKED; + } +} + static int ptcache_bake_all_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -139,7 +153,7 @@ static int ptcache_free_bake_all_exec(bContext *C, wmOperator *UNUSED(op)) BKE_ptcache_ids_from_object(&pidlist, base->object, scene, MAX_DUPLI_RECUR); for (pid=pidlist.first; pid; pid=pid->next) { - pid->cache->flag &= ~PTCACHE_BAKED; + ptcache_free_bake(pid->cache); } BLI_freelistN(&pidlist); @@ -241,15 +255,7 @@ static int ptcache_free_bake_exec(bContext *C, wmOperator *UNUSED(op)) PointCache *cache= ptr.data; Object *ob= ptr.id.data; - if (cache->edit) { - if (!cache->edit->edited || 1) {// XXX okee("Lose changes done in particle mode?")) { - PE_free_ptcache_edit(cache->edit); - cache->edit = NULL; - cache->flag &= ~PTCACHE_BAKED; - } - } - else - cache->flag &= ~PTCACHE_BAKED; + ptcache_free_bake(cache); WM_event_add_notifier(C, NC_OBJECT|ND_POINTCACHE, ob); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 46f726e45c6..1b7cd4a6d20 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1586,14 +1586,14 @@ int ED_area_header_standardbuttons(const bContext *C, uiBlock *block, int yco) ICON_DISCLOSURE_TRI_RIGHT, xco, yco, U.widget_unit, U.widget_unit * 0.9f, &(sa->flag), 0, 0, 0, 0, - "Show pulldown menus"); + TIP_("Show pulldown menus")); } else { but = uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, 0, ICON_DISCLOSURE_TRI_DOWN, xco, yco, U.widget_unit, U.widget_unit * 0.9f, &(sa->flag), 0, 0, 0, 0, - "Hide pulldown menus"); + TIP_("Hide pulldown menus")); } uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */ @@ -1967,11 +1967,11 @@ void ED_region_visible_rect(ARegion *ar, rcti *rect) if (BLI_rcti_isect(rect, &arn->winrct, NULL)) { /* overlap left, also check 1 pixel offset (2 regions on one side) */ - if ( ABS(rect->xmin - arn->winrct.xmin) < 2) + if (ABS(rect->xmin - arn->winrct.xmin) < 2) rect->xmin = arn->winrct.xmax; /* overlap right */ - if ( ABS(rect->xmax - arn->winrct.xmax) < 2) + if (ABS(rect->xmax - arn->winrct.xmax) < 2) rect->xmax = arn->winrct.xmin; } } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 12a7a33c893..fcd0968d52f 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -206,7 +206,7 @@ int ED_operator_animview_active(bContext *C) return TRUE; } - CTX_wm_operator_poll_msg_set(C, "expected an timeline/animation area to be active"); + CTX_wm_operator_poll_msg_set(C, "expected a timeline/animation area to be active"); return 0; } @@ -3427,8 +3427,8 @@ static int userpref_show_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *ev rcti rect; int sizex, sizey; - sizex = 800; - sizey = 480; + sizex = 800 * UI_DPI_WINDOW_FAC; + sizey = 480 * UI_DPI_WINDOW_FAC; /* some magic to calculate postition */ /* pixelsize: mouse coords are in U.pixelsize units :/ */ @@ -3447,8 +3447,8 @@ static int userpref_show_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *ev static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) { /* identifiers */ - ot->name = "Show/Hide User Preferences"; - ot->description = "Show/hide user preferences"; + ot->name = "Show User Preferences"; + ot->description = "Show user preferences"; ot->idname = "SCREEN_OT_userpref_show"; /* api callbacks */ diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 1f7fee313b3..9cde62e8302 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -449,13 +449,21 @@ static void screenshot_endjob(void *sjv) static int screencast_exec(bContext *C, wmOperator *op) { + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); bScreen *screen = CTX_wm_screen(C); - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), screen, "Screencast", 0, WM_JOB_TYPE_SCREENCAST); - ScreenshotJob *sj = MEM_callocN(sizeof(ScreenshotJob), "screenshot job"); + wmJob *wm_job; + ScreenshotJob *sj; + /* if called again, stop the running job */ + if (WM_jobs_test(wm, screen, WM_JOB_TYPE_SCREENCAST)) + WM_jobs_stop(wm, screen, screenshot_startjob); + + wm_job = WM_jobs_get(wm, win, screen, "Screencast", 0, WM_JOB_TYPE_SCREENCAST); + sj = MEM_callocN(sizeof(ScreenshotJob), "screenshot job"); + /* setup sj */ if (RNA_boolean_get(op->ptr, "full")) { - wmWindow *win = CTX_wm_window(C); sj->x = 0; sj->y = 0; sj->dumpsx = WM_window_pixels_x(win); @@ -470,7 +478,7 @@ static int screencast_exec(bContext *C, wmOperator *op) } sj->bmain = CTX_data_main(C); sj->scene = CTX_data_scene(C); - sj->wm = CTX_wm_manager(C); + sj->wm = wm; BKE_reports_init(&sj->reports, RPT_PRINT); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index faa9ce00da8..e7c3ddd071b 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -151,12 +151,10 @@ void BRUSH_OT_curve_preset(struct wmOperatorType *ot); void PAINT_OT_face_select_linked(struct wmOperatorType *ot); void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot); void PAINT_OT_face_select_all(struct wmOperatorType *ot); -void PAINT_OT_face_select_inverse(struct wmOperatorType *ot); void PAINT_OT_face_select_hide(struct wmOperatorType *ot); void PAINT_OT_face_select_reveal(struct wmOperatorType *ot); void PAINT_OT_vert_select_all(struct wmOperatorType *ot); -void PAINT_OT_vert_select_inverse(struct wmOperatorType *ot); int vert_paint_poll(struct bContext *C); int mask_paint_poll(struct bContext *C); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 3cf67667f39..dffb8c39bf2 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -127,13 +127,14 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot) { static EnumPropertyItem mode_items[] = { - {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the \"value\" property"}, + {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the 'value' property"}, {PAINT_MASK_INVERT, "INVERT", 0, "Invert", "Invert the mask"}, {0}}; /* identifiers */ ot->name = "Mask Flood Fill"; ot->idname = "PAINT_OT_mask_flood_fill"; + ot->description = "Fill the whole mask with a given value, or invert its values"; /* api callbacks */ ot->exec = mask_flood_fill_exec; @@ -143,5 +144,6 @@ void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot) /* rna */ RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float(ot->srna, "value", 0, 0, 1, "Value", "Mask level to use when mode is \"Value\"; zero means no masking and one is fully masked", 0, 1); + RNA_def_float(ot->srna, "value", 0, 0, 1, "Value", + "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", 0, 1); } diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index ba33ad22f92..7385c2f0cf1 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -485,7 +485,6 @@ void ED_operatortypes_paint(void) /* vertex selection */ WM_operatortype_append(PAINT_OT_vert_select_all); - WM_operatortype_append(PAINT_OT_vert_select_inverse); /* vertex */ WM_operatortype_append(PAINT_OT_vertex_paint_toggle); @@ -496,7 +495,6 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_face_select_linked); WM_operatortype_append(PAINT_OT_face_select_linked_pick); WM_operatortype_append(PAINT_OT_face_select_all); - WM_operatortype_append(PAINT_OT_face_select_inverse); WM_operatortype_append(PAINT_OT_face_select_hide); WM_operatortype_append(PAINT_OT_face_select_reveal); @@ -748,7 +746,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf) keymap = WM_keymap_find(keyconf, "Weight Paint Vertex Selection", 0, 0); keymap->poll = vert_paint_poll; WM_keymap_add_item(keymap, "PAINT_OT_vert_select_all", AKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "PAINT_OT_vert_select_inverse", IKEY, KM_PRESS, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "PAINT_OT_vert_select_all", IKEY, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "action", SEL_INVERT); WM_keymap_add_item(keymap, "VIEW3D_OT_select_border", BKEY, KM_PRESS, 0, 0); kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "deselect", FALSE); @@ -777,7 +776,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf) keymap->poll = facemask_paint_poll; WM_keymap_add_item(keymap, "PAINT_OT_face_select_all", AKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "PAINT_OT_face_select_inverse", IKEY, KM_PRESS, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "PAINT_OT_face_select_all", IKEY, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "action", SEL_INVERT); kmi = WM_keymap_add_item(keymap, "PAINT_OT_face_select_hide", HKEY, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "unselected", FALSE); kmi = WM_keymap_add_item(keymap, "PAINT_OT_face_select_hide", HKEY, KM_PRESS, KM_SHIFT, 0); diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 4f156276aac..dc3f310b405 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -448,7 +448,7 @@ static int face_select_all_exec(bContext *C, wmOperator *op) void PAINT_OT_face_select_all(wmOperatorType *ot) { - ot->name = "Face Selection"; + ot->name = "(De)select All"; ot->description = "Change selection for all faces"; ot->idname = "PAINT_OT_face_select_all"; @@ -472,7 +472,7 @@ static int vert_select_all_exec(bContext *C, wmOperator *op) void PAINT_OT_vert_select_all(wmOperatorType *ot) { - ot->name = "Vertex Selection"; + ot->name = "(De)select All"; ot->description = "Change selection for all vertices"; ot->idname = "PAINT_OT_vert_select_all"; @@ -484,46 +484,6 @@ void PAINT_OT_vert_select_all(wmOperatorType *ot) WM_operator_properties_select_all(ot); } -static int vert_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - paintvert_deselect_all_visible(ob, SEL_INVERT, TRUE); - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; -} - -void PAINT_OT_vert_select_inverse(wmOperatorType *ot) -{ - ot->name = "Vertex Select Invert"; - ot->description = "Invert selection of vertices"; - ot->idname = "PAINT_OT_vert_select_inverse"; - - ot->exec = vert_select_inverse_exec; - ot->poll = vert_paint_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} -static int face_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - paintface_deselect_all_visible(ob, SEL_INVERT, TRUE); - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; -} - - -void PAINT_OT_face_select_inverse(wmOperatorType *ot) -{ - ot->name = "Face Select Invert"; - ot->description = "Invert selection of faces"; - ot->idname = "PAINT_OT_face_select_inverse"; - - ot->exec = face_select_inverse_exec; - ot->poll = facemask_paint_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - static int face_select_hide_exec(bContext *C, wmOperator *op) { const int unselected = RNA_boolean_get(op->ptr, "unselected"); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 8ff2a68013c..0277e1e11dc 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -913,7 +913,8 @@ static float calc_vp_alpha_dl(VPaint *vp, ViewContext *vc, BLI_INLINE float wval_blend(const float weight, const float paintval, const float alpha) { - return (paintval * alpha) + (weight * (1.0f - alpha)); + const float talpha = min_ff(alpha, 1.0f); /* blending with values over 1 doesn't make sense */ + return (paintval * talpha) + (weight * (1.0f - talpha)); } BLI_INLINE float wval_add(const float weight, const float paintval, const float alpha) { diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 342671698d2..90e7645032b 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2281,7 +2281,7 @@ static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int to unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); sculpt_brush_test_init(ss, &test); - if (ss->cache->original) { + if (ss->cache->original && unode->co) { BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_fast(&test, unode->co[vd.i])) { @@ -3742,7 +3742,8 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { /* not supported yet for multires or dynamic topology */ if (!ss->multires && !ss->bm && !ss->layer_co && - (brush->flag & BRUSH_PERSISTENT)) { + (brush->flag & BRUSH_PERSISTENT)) + { if (!ss->layer_co) ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert, "sculpt mesh vertices copy"); @@ -4784,6 +4785,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot) /* identifiers */ ot->name = "Symmetrize"; ot->idname = "SCULPT_OT_symmetrize"; + ot->description = "Symmetrize the topology modifications"; /* api callbacks */ ot->exec = sculpt_symmetrize_exec; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index c828e8c8651..406756f4197 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -567,6 +567,8 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, unode->totvert = totvert; } + else + maxgrid = 0; /* we will use this while sculpting, is mapalloc slow to access then? */ diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index 935b15f3cd8..16d194d0929 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -37,6 +37,8 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "DNA_brush_types.h" #include "DNA_ID.h" #include "DNA_lamp_types.h" @@ -397,7 +399,7 @@ void uiTemplateTextureUser(uiLayout *layout, bContext *C) user = ct->user; if (!user) { - uiItemL(layout, "No textures in context.", ICON_NONE); + uiItemL(layout, IFACE_("No textures in context"), ICON_NONE); return; } diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index b2d31ececa5..9409ce42d3a 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -407,7 +407,7 @@ void uiTemplateMarker(uiLayout *layout, PointerRNA *ptr, const char *propname, P if (track->flag & TRACK_LOCKED) { uiLayoutSetActive(layout, FALSE); block = uiLayoutAbsoluteBlock(layout); - uiDefBut(block, LABEL, 0, IFACE_("Track is locked"), 0, 0, UI_UNIT_X*15.0f, UI_UNIT_Y, NULL, 0, 0, 0, 0, ""); + uiDefBut(block, LABEL, 0, IFACE_("Track is locked"), 0, 0, UI_UNIT_X * 15.0f, UI_UNIT_Y, NULL, 0, 0, 0, 0, ""); return; } diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 77ed197c1d7..ac7ebd9ddac 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -2892,7 +2892,7 @@ void CLIP_OT_set_solver_keyframe(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe", "keyframe to set"); + RNA_def_enum(ot->srna, "keyframe", keyframe_items, 0, "Keyframe", "Keyframe to set"); } /********************** track copy color operator *********************/ diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 9349abb4d8b..79a578a86d1 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1178,11 +1178,10 @@ static void file_expand_directory(bContext *C) get_default_root(sfile->params->dir); } /* change "C:" --> "C:\", [#28102] */ - else if ( (isalpha(sfile->params->dir[0]) && - (sfile->params->dir[1] == ':')) && - (sfile->params->dir[2] == '\0') - - ) { + else if ((isalpha(sfile->params->dir[0]) && + (sfile->params->dir[1] == ':')) && + (sfile->params->dir[2] == '\0')) + { sfile->params->dir[2] = '\\'; sfile->params->dir[3] = '\0'; } diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h index 1b69eb09fce..eea7e0e3837 100644 --- a/source/blender/editors/space_file/fsmenu.h +++ b/source/blender/editors/space_file/fsmenu.h @@ -64,7 +64,7 @@ char *fsmenu_get_entry(struct FSMenu *fsmenu, FSMenuCategory category, int index /** Inserts a new fsmenu entry with the given \a path. * Duplicate entries are not added. - * \param sorted Should entry be inserted in sorted order? + * \param flag Options for inserting the entry. */ void fsmenu_insert_entry(struct FSMenu *fsmenu, FSMenuCategory category, const char *path, const FSMenuInsert flag); diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index ab16a9d55e6..f1501857b13 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -461,6 +461,7 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa /* Target ID */ row = uiLayoutRow(layout, FALSE); + uiLayoutSetRedAlert(row, ((dtar->flag & DTAR_FLAG_INVALID) && !dtar->id)); uiTemplateAnyID(row, &dtar_ptr, "id", "id_type", IFACE_("Prop:")); /* Target Property */ @@ -470,8 +471,9 @@ static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVa /* get pointer for resolving the property selected */ RNA_id_pointer_create(dtar->id, &root_ptr); - col = uiLayoutColumn(layout, TRUE); /* rna path */ + col = uiLayoutColumn(layout, TRUE); + uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); uiTemplatePathBuilder(col, &dtar_ptr, "data_path", &root_ptr, IFACE_("Path")); } } @@ -492,21 +494,23 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar * /* Bone 1 */ col = uiLayoutColumn(layout, TRUE); + uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Bone 1:")); - + if (dtar->id && ob1->pose) { PointerRNA tar_ptr; - + RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr); uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); } col = uiLayoutColumn(layout, TRUE); + uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Bone 2:")); if (dtar2->id && ob2->pose) { PointerRNA tar_ptr; - + RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr); uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); } @@ -515,7 +519,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar * /* settings for 'location difference' driver variable type */ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *dvar) { - DriverTarget *dtar = &dvar->targets[0]; + DriverTarget *dtar = &dvar->targets[0]; DriverTarget *dtar2 = &dvar->targets[1]; Object *ob1 = (Object *)dtar->id; Object *ob2 = (Object *)dtar2->id; @@ -523,32 +527,36 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar * uiLayout *col; /* initialize RNA pointer to the target */ - RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr); + RNA_pointer_create(id, &RNA_DriverTarget, dtar, &dtar_ptr); RNA_pointer_create(id, &RNA_DriverTarget, dtar2, &dtar2_ptr); /* Bone 1 */ col = uiLayoutColumn(layout, TRUE); + uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone 1:")); if (dtar->id && ob1->pose) { PointerRNA tar_ptr; - + RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr); uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); } - + + uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */ uiItemR(col, &dtar_ptr, "transform_space", 0, NULL, ICON_NONE); col = uiLayoutColumn(layout, TRUE); + uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Ob/Bone 2:")); if (dtar2->id && ob2->pose) { PointerRNA tar_ptr; - + RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr); uiItemPointerR(col, &dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); } + uiLayoutSetRedAlert(col, false); /* we can clear it again now - it's only needed when creating the ID/Bone fields */ uiItemR(col, &dtar2_ptr, "transform_space", 0, NULL, ICON_NONE); } @@ -565,11 +573,12 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar /* properties */ col = uiLayoutColumn(layout, TRUE); + uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone:")); if (dtar->id && ob->pose) { PointerRNA tar_ptr; - + RNA_pointer_create(dtar->id, &RNA_Pose, ob->pose, &tar_ptr); uiItemPointerR(col, &dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA); } @@ -623,7 +632,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) if (driver->type == DRIVER_TYPE_PYTHON) { /* expression */ uiItemR(col, &driver_ptr, "expression", 0, IFACE_("Expr"), ICON_NONE); - + /* errors? */ if (driver->flag & DRIVER_FLAG_INVALID) uiItemL(col, IFACE_("ERROR: Invalid Python expression"), ICON_ERROR); @@ -668,19 +677,19 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) box = uiLayoutBox(col); /* first row context info for driver */ RNA_pointer_create(ale->id, &RNA_DriverVariable, dvar, &dvar_ptr); - + row = uiLayoutRow(box, FALSE); block = uiLayoutGetBlock(row); /* variable name */ uiItemR(row, &dvar_ptr, "name", 0, "", ICON_NONE); - + /* remove button */ uiBlockSetEmboss(block, UI_EMBOSSN); but = uiDefIconBut(block, BUT, B_IPO_DEPCHANGE, ICON_X, 290, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0.0, 0.0, IFACE_("Delete target variable")); uiButSetFunc(but, driver_delete_var_cb, driver, dvar); uiBlockSetEmboss(block, UI_EMBOSS); - + /* variable type */ row = uiLayoutRow(box, FALSE); uiItemR(row, &dvar_ptr, "type", 0, "", ICON_NONE); @@ -721,7 +730,7 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) else { BLI_snprintf(valBuf, sizeof(valBuf), "%.3f", dvar->curval); } - + uiItemL(row, valBuf, ICON_NONE); } } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 01364fcacf0..6237958e723 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -82,36 +82,36 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf, if (ima == NULL) return; if (ibuf == NULL) { - ofs += sprintf(str, "%s", IFACE_("Can't Load Image")); + ofs += sprintf(str, IFACE_("Can't Load Image")); } else { if (ima->source == IMA_SRC_MOVIE) { - ofs += sprintf(str, "%s", IFACE_("Movie")); + ofs += sprintf(str, IFACE_("Movie")); if (ima->anim) - ofs += sprintf(str + ofs, IFACE_("%d frs"), IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN)); + ofs += sprintf(str + ofs, IFACE_(" %d frs"), IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN)); } else - ofs += sprintf(str, "%s", IFACE_("Image")); + ofs += sprintf(str, IFACE_("Image")); - ofs += sprintf(str + ofs, ": %s %d x %d,", IFACE_("size"), ibuf->x, ibuf->y); + ofs += sprintf(str + ofs, IFACE_(": size %d x %d,"), ibuf->x, ibuf->y); if (ibuf->rect_float) { if (ibuf->channels != 4) { - ofs += sprintf(str + ofs, "%d %s", ibuf->channels, IFACE_("float channel(s)")); + ofs += sprintf(str + ofs, IFACE_("%d float channel(s)"), ibuf->channels); } else if (ibuf->planes == R_IMF_PLANES_RGBA) - ofs += sprintf(str + ofs, "%s", IFACE_(" RGBA float")); + ofs += sprintf(str + ofs, IFACE_(" RGBA float")); else - ofs += sprintf(str + ofs, "%s", IFACE_(" RGB float")); + ofs += sprintf(str + ofs, IFACE_(" RGB float")); } else { if (ibuf->planes == R_IMF_PLANES_RGBA) - ofs += sprintf(str + ofs, "%s", IFACE_(" RGBA byte")); + ofs += sprintf(str + ofs, IFACE_(" RGBA byte")); else - ofs += sprintf(str + ofs, "%s", IFACE_(" RGB byte")); + ofs += sprintf(str + ofs, IFACE_(" RGB byte")); } if (ibuf->zbuf || ibuf->zbuf_float) - ofs += sprintf(str + ofs, "%s", IFACE_(" + Z")); + ofs += sprintf(str + ofs, IFACE_(" + Z")); if (ima->source == IMA_SRC_SEQUENCE) { char *file = BLI_last_slash(ibuf->name); diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index f86e59c41a8..5184b1e1a73 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -87,8 +87,6 @@ void IMAGE_OT_sample(struct wmOperatorType *ot); void IMAGE_OT_sample_line(struct wmOperatorType *ot); void IMAGE_OT_curves_point_set(struct wmOperatorType *ot); -void IMAGE_OT_record_composite(struct wmOperatorType *ot); - /* image_panels.c */ struct ImageUser *ntree_get_active_iuser(struct bNodeTree *ntree); void image_buttons_register(struct ARegionType *art); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index dc834d88323..b913b3528ac 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1959,9 +1959,10 @@ static int image_pack_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event) ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); if (!as_png && (ibuf && (ibuf->userflags & IB_BITMAPDIRTY))) { - pup = uiPupMenuBegin(C, "OK", ICON_QUESTION); + pup = uiPupMenuBegin(C, IFACE_("OK"), ICON_QUESTION); layout = uiPupMenuLayout(pup); - uiItemBooleanO(layout, "Can't pack edited image from disk. Pack as internal PNG?", ICON_NONE, op->idname, "as_png", 1); + uiItemBooleanO(layout, IFACE_("Can't pack edited image from disk, pack as internal PNG?"), ICON_NONE, + op->idname, "as_png", 1); uiPupMenuEnd(C, pup); BKE_image_release_ibuf(ima, ibuf, NULL); @@ -2438,6 +2439,7 @@ void IMAGE_OT_curves_point_set(wmOperatorType *ot) RNA_def_enum(ot->srna, "point", point_items, 0, "Point", "Set black point or white point for curves"); } +#if 0 /* Not ported to 2.5x yet */ /******************** record composite operator *********************/ typedef struct RecordCompositeData { @@ -2589,6 +2591,8 @@ void IMAGE_OT_record_composite(wmOperatorType *ot) ot->poll = space_image_buffer_exists_poll; } +#endif + /********************* cycle render slot operator *********************/ static int image_cycle_render_slot_poll(bContext *C) diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index ca8270ba0f5..d82a46e9578 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -253,8 +253,6 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_sample_line); WM_operatortype_append(IMAGE_OT_curves_point_set); - WM_operatortype_append(IMAGE_OT_record_composite); - WM_operatortype_append(IMAGE_OT_properties); WM_operatortype_append(IMAGE_OT_scopes); } diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 22668a3de3a..04f6a5152e6 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -40,9 +40,11 @@ #include "BLI_blenlib.h" #include "BLI_math.h" -#include "BKE_bpath.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + +#include "BKE_bpath.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" @@ -218,9 +220,9 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event) } if (count == 1) - strcpy(title, "Unpack 1 file"); + strcpy(title, IFACE_("Unpack 1 File")); else - BLI_snprintf(title, sizeof(title), "Unpack %d files", count); + BLI_snprintf(title, sizeof(title), IFACE_("Unpack %d Files"), count); pup = uiPupMenuBegin(C, title, ICON_NONE); layout = uiPupMenuLayout(pup); @@ -291,7 +293,7 @@ static int unpack_item_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event uiPopupMenu *pup; uiLayout *layout; - pup = uiPupMenuBegin(C, "Unpack", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Unpack"), ICON_NONE); layout = uiPupMenuLayout(pup); uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 5a8a77f7350..976769752f9 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -367,9 +367,9 @@ static void stats_string(Scene *scene) mmap_in_use = MEM_get_mapped_memory_in_use(); /* get memory statistics */ - s = memstr + sprintf(memstr, " | Mem:%.2fM", (double)((mem_in_use - mmap_in_use) >> 10) / 1024.0); + s = memstr + sprintf(memstr, IFACE_(" | Mem:%.2fM"), (double)((mem_in_use - mmap_in_use) >> 10) / 1024.0); if (mmap_in_use) - sprintf(s, " (%.2fM)", (double)((mmap_in_use) >> 10) / 1024.0); + sprintf(s, IFACE_(" (%.2fM)"), (double)((mmap_in_use) >> 10) / 1024.0); s = stats->infostr; @@ -377,32 +377,32 @@ static void stats_string(Scene *scene) if (scene->obedit) { if (BKE_keyblock_from_object(scene->obedit)) - s += sprintf(s, "(Key) "); + s += sprintf(s, IFACE_("(Key) ")); if (scene->obedit->type == OB_MESH) { - s += sprintf(s, "Verts:%d/%d | Edges:%d/%d | Faces:%d/%d | Tris:%d", + s += sprintf(s, IFACE_("Verts:%d/%d | Edges:%d/%d | Faces:%d/%d | Tris:%d"), stats->totvertsel, stats->totvert, stats->totedgesel, stats->totedge, stats->totfacesel, stats->totface, stats->tottri); } else if (scene->obedit->type == OB_ARMATURE) { - s += sprintf(s, "Verts:%d/%d | Bones:%d/%d", stats->totvertsel, stats->totvert, stats->totbonesel, + s += sprintf(s, IFACE_("Verts:%d/%d | Bones:%d/%d"), stats->totvertsel, stats->totvert, stats->totbonesel, stats->totbone); } else { - s += sprintf(s, "Verts:%d/%d", stats->totvertsel, stats->totvert); + s += sprintf(s, IFACE_("Verts:%d/%d"), stats->totvertsel, stats->totvert); } strcat(s, memstr); } else if (ob && (ob->mode & OB_MODE_POSE)) { - s += sprintf(s, "Bones:%d/%d %s", + s += sprintf(s, IFACE_("Bones:%d/%d %s"), stats->totbonesel, stats->totbone, memstr); } else if (stats_is_object_dynamic_topology_sculpt(ob)) { - s += sprintf(s, "Verts:%d | Tris:%d", stats->totvert, stats->tottri); + s += sprintf(s, IFACE_("Verts:%d | Tris:%d"), stats->totvert, stats->tottri); } else { - s += sprintf(s, "Verts:%d | Faces:%d | Tris:%d | Objects:%d/%d | Lamps:%d/%d%s", + s += sprintf(s, IFACE_("Verts:%d | Faces:%d | Tris:%d | Objects:%d/%d | Lamps:%d/%d%s"), stats->totvert, stats->totface, stats->tottri, stats->totobjsel, stats->totobj, stats->totlampsel, stats->totlamp, memstr); } diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index e2eb1f030b3..31c773e613e 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -311,7 +311,7 @@ static void nla_panel_properties(const bContext *C, Panel *pa) /* strip extents */ column = uiLayoutColumn(layout, TRUE); - uiItemL(column, "Strip Extents:", ICON_NONE); + uiItemL(column, IFACE_("Strip Extents:"), ICON_NONE); uiItemR(column, &strip_ptr, "frame_start", 0, NULL, ICON_NONE); uiItemR(column, &strip_ptr, "frame_end", 0, NULL, ICON_NONE); @@ -346,7 +346,7 @@ static void nla_panel_properties(const bContext *C, Panel *pa) /* settings */ column = uiLayoutColumn(layout, TRUE); uiLayoutSetActive(column, !(RNA_boolean_get(&strip_ptr, "use_animated_influence") || RNA_boolean_get(&strip_ptr, "use_animated_time"))); - uiItemL(column, "Playback Settings:", ICON_NONE); + uiItemL(column, IFACE_("Playback Settings:"), ICON_NONE); uiItemR(column, &strip_ptr, "mute", 0, NULL, ICON_NONE); uiItemR(column, &strip_ptr, "use_reverse", 0, NULL, ICON_NONE); } @@ -376,15 +376,15 @@ static void nla_panel_actclip(const bContext *C, Panel *pa) /* action extents */ // XXX custom names were used here (to avoid the prefixes)... probably not necessary in future? column = uiLayoutColumn(layout, TRUE); - uiItemL(column, "Action Extents:", ICON_NONE); - uiItemR(column, &strip_ptr, "action_frame_start", 0, "Start Frame", ICON_NONE); - uiItemR(column, &strip_ptr, "action_frame_end", 0, "End Frame", ICON_NONE); + uiItemL(column, IFACE_("Action Extents:"), ICON_NONE); + uiItemR(column, &strip_ptr, "action_frame_start", 0, IFACE_("Start Frame"), ICON_NONE); + uiItemR(column, &strip_ptr, "action_frame_end", 0, IFACE_("End Frame"), ICON_NONE); uiItemO(column, NULL, ICON_NONE, "NLA_OT_action_sync_length"); /* action usage */ column = uiLayoutColumn(layout, TRUE); uiLayoutSetActive(column, RNA_boolean_get(&strip_ptr, "use_animated_time") == FALSE); - uiItemL(column, "Playback Settings:", ICON_NONE); + uiItemL(column, IFACE_("Playback Settings:"), ICON_NONE); uiItemR(column, &strip_ptr, "scale", 0, NULL, ICON_NONE); uiItemR(column, &strip_ptr, "repeat", 0, NULL, ICON_NONE); } diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 3740c3fae5e..484eb47fa8e 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -44,6 +44,8 @@ #include "BLI_rand.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "BKE_action.h" #include "BKE_fcurve.h" #include "BKE_nla.h" @@ -1951,7 +1953,7 @@ static int nla_fmodifier_add_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent uiLayout *layout; int i; - pup = uiPupMenuBegin(C, "Add F-Modifier", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Add F-Modifier"), ICON_NONE); layout = uiPupMenuLayout(pup); /* start from 1 to skip the 'Invalid' modifier type */ diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index b211bca4c0a..8a2e03f2660 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1131,8 +1131,8 @@ static void node_draw_reroute(const bContext *C, ARegion *ar, SpaceNode *UNUSED( /* skip if out of view */ if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax || - node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) { - + node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) + { uiEndBlock(C, node->block); node->block = NULL; return; @@ -1608,12 +1608,15 @@ static void node_composit_buts_image_details(uiLayout *layout, bContext *C, Poin node_composit_buts_image(layout, C, ptr); + uiItemR(layout, ptr, "use_straight_alpha_output", 0, NULL, 0); + if (!node->id) return; imaptr = RNA_pointer_get(ptr, "image"); uiTemplateColorspaceSettings(layout, &imaptr, "colorspace_settings"); + uiItemR(layout, &imaptr, "alpha_mode", 0, NULL, 0); } static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, PointerRNA *ptr) diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.c index 22631568d03..4943bb45113 100644 --- a/source/blender/editors/space_node/node_add.c +++ b/source/blender/editors/space_node/node_add.c @@ -307,6 +307,7 @@ void NODE_OT_add_reroute(wmOperatorType *ot) ot->name = "Add Reroute"; ot->idname = "NODE_OT_add_reroute"; + ot->description = "Add a reroute node"; ot->invoke = WM_gesture_lines_invoke; ot->modal = WM_gesture_lines_modal; diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c index 943f12c4c54..7b7b98f132c 100644 --- a/source/blender/editors/space_node/node_group.c +++ b/source/blender/editors/space_node/node_group.c @@ -42,6 +42,8 @@ #include "BLI_rect.h" #include "BLI_math.h" +#include "BLF_translation.h" + #include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_context.h" @@ -789,7 +791,7 @@ static int node_group_separate_exec(bContext *C, wmOperator *op) static int node_group_separate_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event)) { - uiPopupMenu *pup = uiPupMenuBegin(C, "Separate", ICON_NONE); + uiPopupMenu *pup = uiPupMenuBegin(C, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE); uiLayout *layout = uiPupMenuLayout(pup); uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); @@ -1148,7 +1150,7 @@ static int node_group_make_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent * { SpaceNode *snode = CTX_wm_space_node(C); bNode *act = nodeGetActive(snode->edittree); - uiPopupMenu *pup = uiPupMenuBegin(C, "Make Group", ICON_NONE); + uiPopupMenu *pup = uiPupMenuBegin(C, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Make Group"), ICON_NONE); uiLayout *layout = uiPupMenuLayout(pup); uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 303782f3fbd..cd04c8c6bd1 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -389,7 +389,7 @@ static void group_linkobs2scene_cb(bContext *UNUSED(C), Scene *scene, TreeElemen /* link to scene */ base = MEM_callocN(sizeof(Base), "add_base"); BLI_addhead(&scene->base, base); - base->lay = (1 << 20) - 1; /*v3d->lay;*/ /* would be nice to use the 3d layer but the include's not here */ + base->lay = gob->ob->lay; gob->ob->flag |= SELECT; base->flag = gob->ob->flag; base->object = gob->ob; diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 29a6a1f6d50..78a76532487 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -126,23 +126,50 @@ static void sequencer_generic_invoke_path__internal(bContext *C, wmOperator *op, } } -static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, wmEvent *event, int flag) +static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type) { - View2D *v2d = UI_view2d_fromcontext(C); - - float mval_v2d[2]; + Sequence *tgt = NULL; + Sequence *seq; + Scene *scene = CTX_data_scene(C); + Editing *ed = BKE_sequencer_editing_get(scene, TRUE); + int cfra = (int) CFRA; + int proximity = INT_MAX; + + if (!ed || !ed->seqbasep) { + return 1; + } + + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if ((type == -1 || seq->type == type) && + (seq->enddisp < cfra) && + (cfra - seq->enddisp < proximity)) + { + tgt = seq; + proximity = cfra - seq->enddisp; + } + } - UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &mval_v2d[0], &mval_v2d[1]); + if (tgt) { + return tgt->machine; + } + return 1; +} +static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, int flag, int type) +{ + Scene *scene = CTX_data_scene(C); + + int cfra = (int) CFRA; + /* effect strips don't need a channel initialized from the mouse */ if (!(flag & SEQPROP_NOCHAN)) { - RNA_int_set(op->ptr, "channel", (int)mval_v2d[1] + 0.5f); + RNA_int_set(op->ptr, "channel", sequencer_generic_invoke_xy_guess_channel(C, type)); } - RNA_int_set(op->ptr, "frame_start", (int)mval_v2d[0]); + RNA_int_set(op->ptr, "frame_start", cfra); if ((flag & SEQPROP_ENDFRAME) && RNA_struct_property_is_set(op->ptr, "frame_end") == 0) - RNA_int_set(op->ptr, "frame_end", (int)mval_v2d[0] + 25); // XXX arbitary but ok for now. + RNA_int_set(op->ptr, "frame_end", cfra + 25); // XXX arbitary but ok for now. if (!(flag & SEQPROP_NOPATHS)) { sequencer_generic_invoke_path__internal(C, op, "filepath"); @@ -277,7 +304,7 @@ static int sequencer_add_scene_strip_invoke(bContext *C, wmOperator *op, wmEvent if (!RNA_struct_property_is_set(op->ptr, "scene")) return WM_enum_search_invoke(C, op, event); - sequencer_generic_invoke_xy__internal(C, op, event, 0); + sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_SCENE); return sequencer_add_scene_strip_exec(C, op); // needs a menu // return WM_menu_invoke(C, op, event); @@ -375,7 +402,7 @@ static int sequencer_add_movieclip_strip_invoke(bContext *C, wmOperator *op, wmE if (!RNA_struct_property_is_set(op->ptr, "clip")) return WM_enum_search_invoke(C, op, event); - sequencer_generic_invoke_xy__internal(C, op, event, 0); + sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MOVIECLIP); return sequencer_add_movieclip_strip_exec(C, op); // needs a menu // return WM_menu_invoke(C, op, event); @@ -472,7 +499,7 @@ static int sequencer_add_mask_strip_invoke(bContext *C, wmOperator *op, wmEvent if (!RNA_struct_property_is_set(op->ptr, "mask")) return WM_enum_search_invoke(C, op, event); - sequencer_generic_invoke_xy__internal(C, op, event, 0); + sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MASK); return sequencer_add_mask_strip_exec(C, op); // needs a menu // return WM_menu_invoke(C, op, event); @@ -575,7 +602,7 @@ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) } -static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, wmEvent *event) +static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { if (!ED_operator_sequencer_active(C)) { @@ -587,11 +614,11 @@ static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, wmEvent if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) || RNA_struct_property_is_set(op->ptr, "filepath")) { - sequencer_generic_invoke_xy__internal(C, op, event, SEQPROP_NOPATHS); + sequencer_generic_invoke_xy__internal(C, op, SEQPROP_NOPATHS, SEQ_TYPE_MOVIE); return sequencer_add_movie_strip_exec(C, op); } - sequencer_generic_invoke_xy__internal(C, op, event, 0); + sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MOVIE); WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; @@ -630,7 +657,7 @@ static int sequencer_add_sound_strip_exec(bContext *C, wmOperator *op) return sequencer_add_generic_strip_exec(C, op, BKE_sequencer_add_sound_strip); } -static int sequencer_add_sound_strip_invoke(bContext *C, wmOperator *op, wmEvent *event) +static int sequencer_add_sound_strip_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { if (!ED_operator_sequencer_active(C)) { @@ -642,11 +669,11 @@ static int sequencer_add_sound_strip_invoke(bContext *C, wmOperator *op, wmEvent if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) || RNA_struct_property_is_set(op->ptr, "filepath")) { - sequencer_generic_invoke_xy__internal(C, op, event, SEQPROP_NOPATHS); + sequencer_generic_invoke_xy__internal(C, op, SEQPROP_NOPATHS, SEQ_TYPE_SOUND_RAM); return sequencer_add_sound_strip_exec(C, op); } - sequencer_generic_invoke_xy__internal(C, op, event, 0); + sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_SOUND_RAM); WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; @@ -742,7 +769,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, wmEvent *event) +static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { if (!ED_operator_sequencer_active(C)) { @@ -753,11 +780,11 @@ static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, wmEvent /* drag drop has set the names */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { - sequencer_generic_invoke_xy__internal(C, op, event, SEQPROP_ENDFRAME | SEQPROP_NOPATHS); + sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME | SEQPROP_NOPATHS, SEQ_TYPE_IMAGE); return sequencer_add_image_strip_exec(C, op); } - sequencer_generic_invoke_xy__internal(C, op, event, SEQPROP_ENDFRAME); + sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME, SEQ_TYPE_IMAGE); WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; @@ -894,7 +921,7 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) /* add color */ -static int sequencer_add_effect_strip_invoke(bContext *C, wmOperator *op, wmEvent *event) +static int sequencer_add_effect_strip_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) { short is_type_set = RNA_struct_property_is_set(op->ptr, "type"); int type = -1; @@ -917,7 +944,7 @@ static int sequencer_add_effect_strip_invoke(bContext *C, wmOperator *op, wmEven } } - sequencer_generic_invoke_xy__internal(C, op, event, prop_flag); + sequencer_generic_invoke_xy__internal(C, op, prop_flag, type); return sequencer_add_effect_strip_exec(C, op); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 249ba986fd3..396878cbfeb 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1059,8 +1059,10 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ibuf->x, ibuf->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, display_buffer); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (sseq->flag & SEQ_USE_ALPHA) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } glBegin(GL_QUADS); @@ -1093,7 +1095,8 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq glEnd(); glBindTexture(GL_TEXTURE_2D, last_texid); glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); + if (sseq->flag & SEQ_USE_ALPHA) + glDisable(GL_BLEND); glDeleteTextures(1, &texid); if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index d541e1d6c07..4c6b909882c 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -120,7 +120,7 @@ static SpaceLink *sequencer_new(const bContext *C) sseq->chanshown = 0; sseq->view = SEQ_VIEW_SEQUENCE; sseq->mainb = SEQ_DRAW_IMG_IMBUF; - sseq->flag = SEQ_SHOW_GPENCIL; + sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA; /* header */ ar = MEM_callocN(sizeof(ARegion), "header for sequencer"); diff --git a/source/blender/editors/space_text/text_format.c b/source/blender/editors/space_text/text_format.c index a8c2de193c4..3c7897200ed 100644 --- a/source/blender/editors/space_text/text_format.c +++ b/source/blender/editors/space_text/text_format.c @@ -157,7 +157,7 @@ void text_format_fill(const char **str_p, char **fmt_p, const char type, const i *fmt++ = type; str += size; - i += size; + i += 1; } str--; diff --git a/source/blender/editors/space_text/text_header.c b/source/blender/editors/space_text/text_header.c index 52253b2d5d2..605a08e587a 100644 --- a/source/blender/editors/space_text/text_header.c +++ b/source/blender/editors/space_text/text_header.c @@ -42,6 +42,8 @@ #include "BLI_blenlib.h" +#include "BLF_translation.h" + #include "BKE_context.h" #include "BKE_screen.h" @@ -120,7 +122,7 @@ void TEXT_OT_properties(wmOperatorType *ot) uiPopupMenu *pup; if (text) { - pup = uiPupMenuBegin(C, "Text", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Text"), ICON_NONE); if (txt_has_sel(text)) { uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_cut"); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_copy"); @@ -134,7 +136,7 @@ void TEXT_OT_properties(wmOperatorType *ot) uiPupMenuEnd(C, pup); } else { - pup = uiPupMenuBegin(C, "File", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("File"), ICON_NONE); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_new"); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_open"); uiPupMenuEnd(C, pup); @@ -146,7 +148,7 @@ void TEXT_OT_properties(wmOperatorType *ot) uiPopupMenu *pup; - pup = uiPupMenuBegin(C, "Edit", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Edit"), ICON_NONE); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_cut"); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_copy"); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_paste"); @@ -159,7 +161,7 @@ void TEXT_OT_properties(wmOperatorType *ot) uiPopupMenu *pup; if (text) { - pup = uiPupMenuBegin(C, "Text", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Text"), ICON_NONE); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_new"); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_open"); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_save"); @@ -168,7 +170,7 @@ void TEXT_OT_properties(wmOperatorType *ot) uiPupMenuEnd(C, pup); } else { - pup = uiPupMenuBegin(C, "File", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("File"), ICON_NONE); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_new"); uiItemO(layout, NULL, ICON_NONE, "TEXT_OT_open"); uiPupMenuEnd(C, pup); @@ -180,11 +182,14 @@ void TEXT_OT_properties(wmOperatorType *ot) uiPopupMenu *pup; - pup = uiPupMenuBegin(C, "Text", ICON_NONE); - uiItemEnumO(layout, "TEXT_OT_move", "Top of File", 0, "type", FILE_TOP); - uiItemEnumO(layout, "TEXT_OT_move", "Bottom of File", 0, "type", FILE_BOTTOM); - uiItemEnumO(layout, "TEXT_OT_move", "Page Up", 0, "type", PREV_PAGE); - uiItemEnumO(layout, "TEXT_OT_move", "Page Down", 0, "type", NEXT_PAGE); + pup = uiPupMenuBegin(C, IFACE_("Text"), ICON_NONE); + uiItemEnumO(layout, "TEXT_OT_move", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Top of File"), + 0, "type", FILE_TOP); + uiItemEnumO(layout, "TEXT_OT_move", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Bottom of File"), + 0, "type", FILE_BOTTOM); + uiItemEnumO(layout, "TEXT_OT_move", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Page Up"), 0, "type", PREV_PAGE); + uiItemEnumO(layout, "TEXT_OT_move", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Page Down"), + 0, "type", NEXT_PAGE); uiPupMenuEnd(C, pup); } #endif diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 9b5d453d8a3..b45961bff11 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -3110,27 +3110,31 @@ static int text_resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *UN case 1: if (text->flags & TXT_ISDIRTY) { /* modified locally and externally, ahhh. offer more possibilites. */ - pup = uiPupMenuBegin(C, "File Modified Outside and Inside Blender", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("File Modified Outside and Inside Blender"), ICON_NONE); layout = uiPupMenuLayout(pup); - uiItemEnumO_ptr(layout, op->type, "Reload from disk (ignore local changes)", 0, "resolution", RESOLVE_RELOAD); - uiItemEnumO_ptr(layout, op->type, "Save to disk (ignore outside changes)", 0, "resolution", RESOLVE_SAVE); - uiItemEnumO_ptr(layout, op->type, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL); + uiItemEnumO_ptr(layout, op->type, IFACE_("Reload from disk (ignore local changes)"), + 0, "resolution", RESOLVE_RELOAD); + uiItemEnumO_ptr(layout, op->type, IFACE_("Save to disk (ignore outside changes)"), + 0, "resolution", RESOLVE_SAVE); + uiItemEnumO_ptr(layout, op->type, IFACE_("Make text internal (separate copy)"), + 0, "resolution", RESOLVE_MAKE_INTERNAL); uiPupMenuEnd(C, pup); } else { - pup = uiPupMenuBegin(C, "File Modified Outside Blender", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("File Modified Outside Blender"), ICON_NONE); layout = uiPupMenuLayout(pup); - uiItemEnumO_ptr(layout, op->type, "Reload from disk", 0, "resolution", RESOLVE_RELOAD); - uiItemEnumO_ptr(layout, op->type, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL); - uiItemEnumO_ptr(layout, op->type, "Ignore", 0, "resolution", RESOLVE_IGNORE); + uiItemEnumO_ptr(layout, op->type, IFACE_("Reload from disk"), 0, "resolution", RESOLVE_RELOAD); + uiItemEnumO_ptr(layout, op->type, IFACE_("Make text internal (separate copy)"), + 0, "resolution", RESOLVE_MAKE_INTERNAL); + uiItemEnumO_ptr(layout, op->type, IFACE_("Ignore"), 0, "resolution", RESOLVE_IGNORE); uiPupMenuEnd(C, pup); } break; case 2: - pup = uiPupMenuBegin(C, "File Deleted Outside Blender", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("File Deleted Outside Blender"), ICON_NONE); layout = uiPupMenuLayout(pup); - uiItemEnumO_ptr(layout, op->type, "Make text internal", 0, "resolution", RESOLVE_MAKE_INTERNAL); - uiItemEnumO_ptr(layout, op->type, "Recreate file", 0, "resolution", RESOLVE_SAVE); + uiItemEnumO_ptr(layout, op->type, IFACE_("Make text internal"), 0, "resolution", RESOLVE_MAKE_INTERNAL); + uiItemEnumO_ptr(layout, op->type, IFACE_("Recreate file"), 0, "resolution", RESOLVE_SAVE); uiPupMenuEnd(C, pup); break; } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index fc8d5d26455..32d961ab64c 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -623,16 +623,17 @@ static void draw_empty_image(Object *ob, const short dflag, const unsigned char if ((dflag & DRAW_CONSTCOLOR) == 0) { glColor3ubv(ob_wire_col); - - /* Calculate the outline vertex positions */ - glBegin(GL_LINE_LOOP); - glVertex2f(ofs_x, ofs_y); - glVertex2f(ofs_x + ima_x, ofs_y); - glVertex2f(ofs_x + ima_x, ofs_y + ima_y); - glVertex2f(ofs_x, ofs_y + ima_y); - glEnd(); } + /* Calculate the outline vertex positions */ + glBegin(GL_LINE_LOOP); + glVertex2f(ofs_x, ofs_y); + glVertex2f(ofs_x + ima_x, ofs_y); + glVertex2f(ofs_x + ima_x, ofs_y + ima_y); + glVertex2f(ofs_x, ofs_y + ima_y); + glEnd(); + + /* Reset GL settings */ glMatrixMode(GL_MODELVIEW); glPopMatrix(); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 658196a1bd4..57755231240 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -513,20 +513,34 @@ static int view3d_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, wmEvent *UNUS return 0; } - static int view3d_ima_bg_drop_poll(bContext *C, wmDrag *drag, wmEvent *event) { - if (ED_view3d_give_base_under_cursor(C, event->mval) ) { - return 0; + if (event->ctrl) + return false; + + if (!ED_view3d_give_base_under_cursor(C, event->mval)) { + return view3d_ima_drop_poll(C, drag, event); } - return view3d_ima_drop_poll(C, drag, event); + return 0; } -static int view3d_ima_ob_drop_poll(bContext *C, wmDrag *drag, wmEvent *event) +static int view3d_ima_empty_drop_poll(bContext *C, wmDrag *drag, wmEvent *event) { - if (ED_view3d_give_base_under_cursor(C, event->mval) ) { + Base *base = ED_view3d_give_base_under_cursor(C, event->mval); + + /* either holding and ctrl and no object, or dropping to empty */ + if ((event->ctrl && !base) || (base && base->object->type == OB_EMPTY)) + return view3d_ima_drop_poll(C, drag, event); + + return 0; +} + +static int view3d_ima_mesh_drop_poll(bContext *C, wmDrag *drag, wmEvent *event) +{ + Base *base = ED_view3d_give_base_under_cursor(C, event->mval); + + if (base && base->object->type == OB_MESH) return view3d_ima_drop_poll(C, drag, event); - } return 0; } @@ -570,7 +584,8 @@ static void view3d_dropboxes(void) WM_dropbox_add(lb, "OBJECT_OT_add_named", view3d_ob_drop_poll, view3d_ob_drop_copy); WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, view3d_id_drop_copy); - WM_dropbox_add(lb, "MESH_OT_drop_named_image", view3d_ima_ob_drop_poll, view3d_id_path_drop_copy); + WM_dropbox_add(lb, "MESH_OT_drop_named_image", view3d_ima_mesh_drop_poll, view3d_id_path_drop_copy); + WM_dropbox_add(lb, "OBJECT_OT_drop_named_image", view3d_ima_empty_drop_poll, view3d_id_path_drop_copy); WM_dropbox_add(lb, "VIEW3D_OT_background_image_add", view3d_ima_bg_drop_poll, view3d_id_path_drop_copy); WM_dropbox_add(lb, "OBJECT_OT_group_instance_add", view3d_group_drop_poll, view3d_group_drop_copy); } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 30ed7a57f9d..5f54f0dcfc8 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -996,12 +996,12 @@ void VIEW3D_OT_rotate(wmOperatorType *ot) /* NDOF utility functions * (should these functions live in this file?) */ -float ndof_to_axis_angle(struct wmNDOFMotionData *ndof, float axis[3]) +float ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3]) { return ndof->dt * normalize_v3_v3(axis, ndof->rvec); } -void ndof_to_quat(struct wmNDOFMotionData *ndof, float q[4]) +void ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) { float axis[3]; float angle; @@ -1010,6 +1010,84 @@ void ndof_to_quat(struct wmNDOFMotionData *ndof, float q[4]) axis_angle_to_quat(q, axis, angle); } +static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof, RegionView3D *rv3d, const float view_inv[4], + const float rot_sensitivity, const float dt, + /* optional, can be NULL*/ + ViewOpsData *vod) +{ + if (U.ndof_flag & NDOF_TURNTABLE) { + + /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */ + float angle, rot[4]; + float xvec[3] = {1, 0, 0}; + + /* Determine the direction of the x vector (for rotating up and down) */ + mul_qt_v3(view_inv, xvec); + + /* Perform the up/down rotation */ + angle = rot_sensitivity * dt * ndof->rx; + if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) + angle = -angle; + rot[0] = cosf(angle); + mul_v3_v3fl(rot + 1, xvec, sin(angle)); + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); + + /* Perform the orbital rotation */ + angle = rot_sensitivity * dt * ndof->ry; + if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) + angle = -angle; + + /* update the onscreen doo-dad */ + rv3d->rot_angle = angle; + rv3d->rot_axis[0] = 0; + rv3d->rot_axis[1] = 0; + rv3d->rot_axis[2] = 1; + + rot[0] = cosf(angle); + rot[1] = rot[2] = 0.0; + rot[3] = sinf(angle); + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); + + } + else { + float rot[4]; + float axis[3]; + float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis); + + if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS) axis[2] = -axis[2]; + if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) axis[0] = -axis[0]; + if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) axis[1] = -axis[1]; + + + /* transform rotation axis from view to world coordinates */ + mul_qt_v3(view_inv, axis); + + /* update the onscreen doo-dad */ + rv3d->rot_angle = angle; + copy_v3_v3(rv3d->rot_axis, axis); + + axis_angle_to_quat(rot, axis, angle); + + /* apply rotation */ + mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); + } + + /* rotate around custom center */ + if (vod && vod->use_dyn_ofs) { + float q1[4]; + + /* compute the post multiplication quat, to rotate the offset correctly */ + conjugate_qt_qt(q1, vod->oldquat); + mul_qt_qtqt(q1, q1, rv3d->viewquat); + + conjugate_qt(q1); /* conj == inv for unit quat */ + copy_v3_v3(rv3d->ofs, vod->ofs); + sub_v3_v3(rv3d->ofs, vod->dyn_ofs); + mul_qt_v3(q1, rv3d->ofs); + add_v3_v3(rv3d->ofs, vod->dyn_ofs); + } +} + /* -- "orbit" navigation (trackball/turntable) * -- zooming * -- panning in rotationally-locked views @@ -1065,86 +1143,8 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event) } if (has_rotation) { - rv3d->view = RV3D_VIEW_USER; - - if (U.ndof_flag & NDOF_TURNTABLE) { - - /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */ - float angle, rot[4]; - float xvec[3] = {1, 0, 0}; - - /* Determine the direction of the x vector (for rotating up and down) */ - mul_qt_v3(view_inv, xvec); - - /* Perform the up/down rotation */ - angle = rot_sensitivity * dt * ndof->rx; - if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) - angle = -angle; - rot[0] = cos(angle); - mul_v3_v3fl(rot + 1, xvec, sin(angle)); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - /* Perform the orbital rotation */ - angle = rot_sensitivity * dt * ndof->ry; - if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) - angle = -angle; - - /* update the onscreen doo-dad */ - rv3d->rot_angle = angle; - rv3d->rot_axis[0] = 0; - rv3d->rot_axis[1] = 0; - rv3d->rot_axis[2] = 1; - - rot[0] = cos(angle); - rot[1] = rot[2] = 0.0; - rot[3] = sin(angle); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - } - else { - float rot[4]; - float axis[3]; - float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis); - - if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS) - axis[2] = -axis[2]; - - if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) - axis[0] = -axis[0]; - - if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) - axis[1] = -axis[1]; - - - /* transform rotation axis from view to world coordinates */ - mul_qt_v3(view_inv, axis); - - /* update the onscreen doo-dad */ - rv3d->rot_angle = angle; - copy_v3_v3(rv3d->rot_axis, axis); - - axis_angle_to_quat(rot, axis, angle); - - /* apply rotation */ - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - } - - /* rotate around custom center */ - if (vod && vod->use_dyn_ofs) { - float q1[4]; - - /* compute the post multiplication quat, to rotate the offset correctly */ - conjugate_qt_qt(q1, vod->oldquat); - mul_qt_qtqt(q1, q1, rv3d->viewquat); - - conjugate_qt(q1); /* conj == inv for unit quat */ - copy_v3_v3(rv3d->ofs, vod->ofs); - sub_v3_v3(rv3d->ofs, vod->dyn_ofs); - mul_qt_v3(q1, rv3d->ofs); - add_v3_v3(rv3d->ofs, vod->dyn_ofs); - } + view3d_ndof_orbit(ndof, rv3d, view_inv, rot_sensitivity, dt, vod); } } @@ -1239,86 +1239,8 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event) } if (has_rotation) { - rv3d->view = RV3D_VIEW_USER; - - if (U.ndof_flag & NDOF_TURNTABLE) { - - /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */ - float angle, rot[4]; - float xvec[3] = {1, 0, 0}; - - /* Determine the direction of the x vector (for rotating up and down) */ - mul_qt_v3(view_inv, xvec); - - /* Perform the up/down rotation */ - angle = rot_sensitivity * dt * ndof->rx; - if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) - angle = -angle; - rot[0] = cos(angle); - mul_v3_v3fl(rot + 1, xvec, sin(angle)); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - /* Perform the orbital rotation */ - angle = rot_sensitivity * dt * ndof->ry; - if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) - angle = -angle; - - /* update the onscreen doo-dad */ - rv3d->rot_angle = angle; - rv3d->rot_axis[0] = 0; - rv3d->rot_axis[1] = 0; - rv3d->rot_axis[2] = 1; - - rot[0] = cos(angle); - rot[1] = rot[2] = 0.0; - rot[3] = sin(angle); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - } - else { - float rot[4]; - float axis[3]; - float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis); - - if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS) - axis[2] = -axis[2]; - - if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) - axis[0] = -axis[0]; - - if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) - axis[1] = -axis[1]; - - - /* transform rotation axis from view to world coordinates */ - mul_qt_v3(view_inv, axis); - - /* update the onscreen doo-dad */ - rv3d->rot_angle = angle; - copy_v3_v3(rv3d->rot_axis, axis); - - axis_angle_to_quat(rot, axis, angle); - - /* apply rotation */ - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - } - - /* rotate around custom center */ - if (vod && vod->use_dyn_ofs) { - float q1[4]; - - /* compute the post multiplication quat, to rotate the offset correctly */ - conjugate_qt_qt(q1, vod->oldquat); - mul_qt_qtqt(q1, q1, rv3d->viewquat); - - conjugate_qt(q1); /* conj == inv for unit quat */ - copy_v3_v3(rv3d->ofs, vod->ofs); - sub_v3_v3(rv3d->ofs, vod->dyn_ofs); - mul_qt_v3(q1, rv3d->ofs); - add_v3_v3(rv3d->ofs, vod->dyn_ofs); - } + view3d_ndof_orbit(ndof, rv3d, view_inv, rot_sensitivity, dt, vod); } } @@ -1502,84 +1424,8 @@ static int ndof_all_invoke(bContext *C, wmOperator *op, wmEvent *event) /* move center of view opposite of hand motion (this is camera mode, not object mode) */ sub_v3_v3(rv3d->ofs, pan_vec); - - if (U.ndof_flag & NDOF_TURNTABLE) { - /* turntable view code by John Aughey, adapted for 3D mouse by [mce] */ - float angle, rot[4]; - float xvec[3] = {1, 0, 0}; - - /* Determine the direction of the x vector (for rotating up and down) */ - mul_qt_v3(view_inv, xvec); - - /* Perform the up/down rotation */ - angle = rot_sensitivity * dt * ndof->rx; - if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) - angle = -angle; - rot[0] = cos(angle); - mul_v3_v3fl(rot + 1, xvec, sin(angle)); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - /* Perform the orbital rotation */ - angle = rot_sensitivity * dt * ndof->ry; - if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) - angle = -angle; - - /* update the onscreen doo-dad */ - rv3d->rot_angle = angle; - rv3d->rot_axis[0] = 0; - rv3d->rot_axis[1] = 0; - rv3d->rot_axis[2] = 1; - - rot[0] = cos(angle); - rot[1] = rot[2] = 0.0; - rot[3] = sin(angle); - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - } - else { - - float rot[4]; - float axis[3]; - float angle = rot_sensitivity * ndof_to_axis_angle(ndof, axis); - - if (U.ndof_flag & NDOF_ROLL_INVERT_AXIS) - axis[2] = -axis[2]; - - if (U.ndof_flag & NDOF_TILT_INVERT_AXIS) - axis[0] = -axis[0]; - - if (U.ndof_flag & NDOF_ROTATE_INVERT_AXIS) - axis[1] = -axis[1]; - - /* transform rotation axis from view to world coordinates */ - mul_qt_v3(view_inv, axis); - - /* update the onscreen doo-dad */ - rv3d->rot_angle = angle; - copy_v3_v3(rv3d->rot_axis, axis); - - axis_angle_to_quat(rot, axis, angle); - - /* apply rotation */ - mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot); - - } - - /* rotate around custom center */ - if (vod->use_dyn_ofs) { - float q1[4]; - - /* compute the post multiplication quat, to rotate the offset correctly */ - conjugate_qt_qt(q1, vod->oldquat); - mul_qt_qtqt(q1, q1, rv3d->viewquat); - - conjugate_qt(q1); /* conj == inv for unit quat */ - copy_v3_v3(rv3d->ofs, vod->ofs); - sub_v3_v3(rv3d->ofs, vod->dyn_ofs); - mul_qt_v3(q1, rv3d->ofs); - add_v3_v3(rv3d->ofs, vod->dyn_ofs); - } + view3d_ndof_orbit(ndof, rv3d, view_inv, rot_sensitivity, dt, vod); } viewops_data_free(C, op); @@ -3699,7 +3545,9 @@ static int background_image_add_invoke(bContext *C, wmOperator *op, wmEvent *UNU void VIEW3D_OT_background_image_add(wmOperatorType *ot) { /* identifiers */ - ot->name = "Add Background Image"; + /* note: having key shortcut here is bad practice, + * but for now keep because this displays when dragging an image over the 3D viewport */ + ot->name = "Add Background Image (Ctrl for Empty Object)"; ot->description = "Add a new background image"; ot->idname = "VIEW3D_OT_background_image_add"; diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 2b30e4e6b81..e078fa8eda1 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -400,13 +400,13 @@ void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C) block = uiLayoutGetBlock(row); uiDefIconButBitS(block, TOG, SCE_SELECT_VERTEX, B_SEL_VERT, ICON_VERTEXSEL, 0, 0, UI_UNIT_X, UI_UNIT_Y, &em->selectmode, 1.0, 0.0, 0, 0, - "Vertex select - Shift-Click for multiple modes"); + TIP_("Vertex select - Shift-Click for multiple modes")); uiDefIconButBitS(block, TOG, SCE_SELECT_EDGE, B_SEL_EDGE, ICON_EDGESEL, 0, 0, UI_UNIT_X, UI_UNIT_Y, &em->selectmode, 1.0, 0.0, 0, 0, - "Edge select - Shift-Click for multiple modes, Ctrl-Click expands selection"); + TIP_("Edge select - Shift-Click for multiple modes, Ctrl-Click expands selection")); uiDefIconButBitS(block, TOG, SCE_SELECT_FACE, B_SEL_FACE, ICON_FACESEL, 0, 0, UI_UNIT_X, UI_UNIT_Y, &em->selectmode, 1.0, 0.0, 0, 0, - "Face select - Shift-Click for multiple modes, Ctrl-Click expands selection"); + TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection")); } } @@ -453,6 +453,13 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) uiItemR(layout, &v3dptr, "viewport_shade", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); if (obedit == NULL && is_paint) { + + if (ob->mode & OB_MODE_WEIGHT_PAINT) { + /* Only for Weight Paint. makes no sense in other paint modes. */ + row = uiLayoutRow(layout, TRUE); + uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); + } + /* Manipulators aren't used in paint modes */ if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) { /* masks aren't used for sculpt and particle painting */ @@ -476,7 +483,9 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); /* pose/object only however we want to allow in weight paint mode too - * so don't be totally strict and just check not-editmode for now */ + * so don't be totally strict and just check not-editmode for now + * XXX We never get here when we are in Weight Paint mode + */ if (obedit == NULL) { uiItemR(row, &v3dptr, "use_pivot_point_align", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); } diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index c8f0fe44433..d4cfa9076a6 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -101,8 +101,8 @@ void VIEW3D_OT_clear_render_border(struct wmOperatorType *ot); void VIEW3D_OT_zoom_border(struct wmOperatorType *ot); void view3d_boxview_copy(ScrArea *sa, ARegion *ar); -void ndof_to_quat(struct wmNDOFMotionData *ndof, float q[4]); -float ndof_to_axis_angle(struct wmNDOFMotionData *ndof, float axis[3]); +void ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]); +float ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axis[3]); /* view3d_fly.c */ void view3d_keymap(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index c428cb02236..bc1656ff7c6 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -43,6 +43,7 @@ #include "ED_view3d.h" /* own include */ #define BL_NEAR_CLIP 0.001 +#define BL_ZERO_CLIP 0.001 /* Non Clipping Projection Functions * ********************************* */ @@ -134,18 +135,25 @@ static eV3DProjStatus ed_view3d_project__internal(ARegion *ar, vec4[3] = 1.0; mul_m4_v4(perspmat, vec4); - if (((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) || (vec4[3] > (float)BL_NEAR_CLIP)) { - const float scalar = (vec4[3] != 0.0f) ? (1.0f / vec4[3]): 0.0f; - const float fx = ((float)ar->winx / 2.0f) * (1.0f + (vec4[0] * scalar)); - if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fx > 0.0f && fx < (float)ar->winx)) { - const float fy = ((float)ar->winy / 2.0f) * (1.0f + (vec4[1] * scalar)); - if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fy > 0.0f && fy < (float)ar->winy)) { - r_co[0] = floorf(fx); - r_co[1] = floorf(fy); - - /* check if the point is behind the view, we need to flip in this case */ - if (UNLIKELY((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) && (vec4[3] < 0.0f)) { - negate_v2(r_co); + + + if (((flag & V3D_PROJ_TEST_CLIP_ZERO) == 0) || (fabsf(vec4[3]) > (float)BL_ZERO_CLIP)) { + if (((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) || (vec4[3] > (float)BL_NEAR_CLIP)) { + const float scalar = (vec4[3] != 0.0f) ? (1.0f / vec4[3]): 0.0f; + const float fx = ((float)ar->winx / 2.0f) * (1.0f + (vec4[0] * scalar)); + if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fx > 0.0f && fx < (float)ar->winx)) { + const float fy = ((float)ar->winy / 2.0f) * (1.0f + (vec4[1] * scalar)); + if (((flag & V3D_PROJ_TEST_CLIP_WIN) == 0) || (fy > 0.0f && fy < (float)ar->winy)) { + r_co[0] = floorf(fx); + r_co[1] = floorf(fy); + + /* check if the point is behind the view, we need to flip in this case */ + if (UNLIKELY((flag & V3D_PROJ_TEST_CLIP_NEAR) == 0) && (vec4[3] < 0.0f)) { + negate_v2(r_co); + } + } + else { + return V3D_PROJ_RET_CLIP_WIN; } } else { @@ -153,11 +161,11 @@ static eV3DProjStatus ed_view3d_project__internal(ARegion *ar, } } else { - return V3D_PROJ_RET_CLIP_WIN; + return V3D_PROJ_RET_CLIP_NEAR; } } else { - return V3D_PROJ_RET_CLIP_NEAR; + return V3D_PROJ_RET_CLIP_ZERO; } return V3D_PROJ_RET_OK; diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index dac887f7f13..24260898066 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -617,7 +617,8 @@ static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, const LassoSelectUserData *data = userData; if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) { + BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)) + { bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT); } } @@ -717,7 +718,8 @@ static void do_lasso_select_mball__doSelectElem(void *userData, struct MetaElem LassoSelectUserData *data = userData; if (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX)) { + BLI_lasso_is_point_inside(data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX)) + { if (data->select) ml->flag |= SELECT; else ml->flag &= ~SELECT; data->is_change = TRUE; diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index ea82ebb9234..c8874d13cac 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -239,11 +239,11 @@ void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy) } } -void projectIntView(TransInfo *t, const float vec[3], int adr[2]) +void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag) { if (t->spacetype == SPACE_VIEW3D) { if (t->ar->regiontype == RGN_TYPE_WINDOW) { - if (ED_view3d_project_int_global(t->ar, vec, adr, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) { + if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) { adr[0] = (int)2140000000.0f; /* this is what was done in 2.64, perhaps we can be smarter? */ adr[1] = (int)2140000000.0f; } @@ -361,15 +361,19 @@ void projectIntView(TransInfo *t, const float vec[3], int adr[2]) UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], adr, adr + 1); } } +void projectIntView(TransInfo *t, const float vec[3], int adr[2]) +{ + projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP); +} -void projectFloatView(TransInfo *t, const float vec[3], float adr[2]) +void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag) { switch (t->spacetype) { case SPACE_VIEW3D: { if (t->ar->regiontype == RGN_TYPE_WINDOW) { /* allow points behind the view [#33643] */ - if (ED_view3d_project_float_global(t->ar, vec, adr, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) { + if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) { /* XXX, 2.64 and prior did this, weak! */ adr[0] = t->ar->winx / 2.0f; adr[1] = t->ar->winy / 2.0f; @@ -393,6 +397,10 @@ void projectFloatView(TransInfo *t, const float vec[3], float adr[2]) zero_v2(adr); } +void projectFloatView(TransInfo *t, const float vec[3], float adr[2]) +{ + projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP); +} void applyAspectRatio(TransInfo *t, float vec[2]) { @@ -1543,7 +1551,7 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata) if (ob) mul_m4_v3(ob->obmat, vecrot); } - projectFloatView(t, vecrot, cent); // no overflow in extreme cases + projectFloatViewEx(t, vecrot, cent, V3D_PROJ_TEST_CLIP_ZERO); glPushMatrix(); @@ -5285,6 +5293,7 @@ static int createEdgeSlideVerts(TransInfo *t) TransDataEdgeSlideVert *sv; /* XXX, 'sv' will initialize multiple times, this is suspicious. see [#34024] */ + BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false); sv = sv_array + GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v)); sv->v = v; sv->origvert = *v; @@ -5307,6 +5316,7 @@ static int createEdgeSlideVerts(TransInfo *t) e1 = e; e = get_other_edge(v, e); if (!e) { + BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false); sv = sv_array + GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v)); sv->v = v; sv->origvert = *v; @@ -5331,6 +5341,12 @@ static int createEdgeSlideVerts(TransInfo *t) l1 = get_next_loop(v, l1, e1, e, vec); l2 = l2 ? get_next_loop(v, l2, e1, e, vec2) : NULL; + if (UNLIKELY(l1 == NULL && l2 != NULL)) { + l1 = l2; + l2 = NULL; + swap_v3_v3(vec, vec2); + } + BM_elem_flag_disable(v, BM_ELEM_TAG); BM_elem_flag_disable(v2, BM_ELEM_TAG); } while (e != v_first->e && l1); @@ -5372,6 +5388,7 @@ static int createEdgeSlideVerts(TransInfo *t) continue; } + BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false); j = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v)); if (sv_array[j].down) { diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 9c57c74a26d..dbf56f658ac 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -35,6 +35,7 @@ #include "ED_transform.h" #include "ED_numinput.h" +#include "ED_view3d.h" #include "DNA_listBase.h" @@ -481,7 +482,9 @@ int transformEnd(struct bContext *C, TransInfo *t); void setTransformViewMatrices(TransInfo *t); void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy); +void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag); void projectIntView(TransInfo *t, const float vec[3], int adr[2]); +void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag); void projectFloatView(TransInfo *t, const float vec[3], float adr[2]); void applyAspectRatio(TransInfo *t, float *vec); diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 5be06188e4e..57816e7546c 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -4560,7 +4560,7 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob) short constinv; short skip_invert = 0; - if (ob->rigidbody_object) { + if (t->mode != TFM_DUMMY && ob->rigidbody_object) { float rot[3][3], scale[3]; /* save original object transform */ diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index cd6035a232b..54fb567b8a2 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -841,37 +841,89 @@ static void manipulator_setcolor(View3D *v3d, char axis, int colcode, unsigned c glColor4ubv(col); } -/* viewmatrix should have been set OK, also no shademode! */ -static void draw_manipulator_axes(View3D *v3d, RegionView3D *rv3d, int colcode, int flagx, int flagy, int flagz) +static void axis_sort_v3(const float axis_values[3], int r_axis_order[3]) { + float v[3]; + copy_v3_v3(v, axis_values); - /* axes */ - if (flagx) { - manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0])); - if (flagx & MAN_SCALE_X) glLoadName(MAN_SCALE_X); - else if (flagx & MAN_TRANS_X) glLoadName(MAN_TRANS_X); - glBegin(GL_LINES); - glVertex3f(0.2f, 0.0f, 0.0f); - glVertex3f(1.0f, 0.0f, 0.0f); - glEnd(); +#define SWAP_AXIS(a, b) { \ + SWAP(float, v[a], v[b]); \ + SWAP(int, r_axis_order[a], r_axis_order[b]); \ +} (void)0 + + if (v[0] < v[1]) { + if (v[2] < v[0]) { SWAP_AXIS(0, 2); } + } + else { + if (v[1] < v[2]) { SWAP_AXIS(0, 1); } + else { SWAP_AXIS(0, 2); } } - if (flagy) { - if (flagy & MAN_SCALE_Y) glLoadName(MAN_SCALE_Y); - else if (flagy & MAN_TRANS_Y) glLoadName(MAN_TRANS_Y); - manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1])); - glBegin(GL_LINES); - glVertex3f(0.0f, 0.2f, 0.0f); - glVertex3f(0.0f, 1.0f, 0.0f); - glEnd(); + if (v[2] < v[1]) { SWAP_AXIS(1, 2); } + +#undef SWAP_AXIS +} +static void manipulator_axis_order(RegionView3D *rv3d, int r_axis_order[3]) +{ + float axis_values[3]; + float vec[3]; + + ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], vec); + + axis_values[0] = -dot_v3v3(rv3d->twmat[0], vec); + axis_values[1] = -dot_v3v3(rv3d->twmat[1], vec); + axis_values[2] = -dot_v3v3(rv3d->twmat[2], vec); + + axis_sort_v3(axis_values, r_axis_order); +} + +/* viewmatrix should have been set OK, also no shademode! */ +static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int colcode, + int flagx, int flagy, int flagz, int axis) +{ + switch (axis) { + case 0: + /* axes */ + if (flagx) { + manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0])); + if (flagx & MAN_SCALE_X) glLoadName(MAN_SCALE_X); + else if (flagx & MAN_TRANS_X) glLoadName(MAN_TRANS_X); + glBegin(GL_LINES); + glVertex3f(0.2f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glEnd(); + } + break; + case 1: + if (flagy) { + if (flagy & MAN_SCALE_Y) glLoadName(MAN_SCALE_Y); + else if (flagy & MAN_TRANS_Y) glLoadName(MAN_TRANS_Y); + manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1])); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glEnd(); + } + break; + case 2: + if (flagz) { + if (flagz & MAN_SCALE_Z) glLoadName(MAN_SCALE_Z); + else if (flagz & MAN_TRANS_Z) glLoadName(MAN_TRANS_Z); + manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2])); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.0f, 0.0f, 1.0f); + glEnd(); + } + break; } - if (flagz) { - if (flagz & MAN_SCALE_Z) glLoadName(MAN_SCALE_Z); - else if (flagz & MAN_TRANS_Z) glLoadName(MAN_TRANS_Z); - manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2])); - glBegin(GL_LINES); - glVertex3f(0.0f, 0.0f, 0.2f); - glVertex3f(0.0f, 0.0f, 1.0f); - glEnd(); +} +static void draw_manipulator_axes(View3D *v3d, RegionView3D *rv3d, int colcode, + int flagx, int flagy, int flagz, + const int axis_order[3]) +{ + int i; + for (i = 0; i < 3; i++) { + draw_manipulator_axes_single(v3d, rv3d, colcode, flagx, flagy, flagz, axis_order[i]); } } @@ -1214,10 +1266,14 @@ static void draw_manipulator_scale(View3D *v3d, RegionView3D *rv3d, int moving, { float cywid = 0.25f * 0.01f * (float)U.tw_handlesize; float cusize = cywid * 0.75f, dz; + int axis_order[3] = {2, 0, 1}; + int i; /* when called while moving in mixed mode, do not draw when... */ if ((drawflags & MAN_SCALE_C) == 0) return; + manipulator_axis_order(rv3d, axis_order); + glDisable(GL_DEPTH_TEST); /* not in combo mode */ @@ -1255,28 +1311,41 @@ static void draw_manipulator_scale(View3D *v3d, RegionView3D *rv3d, int moving, /* axis */ /* in combo mode, this is always drawn as first type */ - draw_manipulator_axes(v3d, rv3d, colcode, drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z); - - /* Z cube */ - glTranslatef(0.0, 0.0, dz); - if (drawflags & MAN_SCALE_Z) { - if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_Z); - manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2])); - drawsolidcube(cusize); - } - /* X cube */ - glTranslatef(dz, 0.0, -dz); - if (drawflags & MAN_SCALE_X) { - if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_X); - manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0])); - drawsolidcube(cusize); - } - /* Y cube */ - glTranslatef(-dz, dz, 0.0); - if (drawflags & MAN_SCALE_Y) { - if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_Y); - manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1])); - drawsolidcube(cusize); + draw_manipulator_axes(v3d, rv3d, colcode, + drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z, + axis_order); + + + for (i = 0; i < 3; i++) { + switch (axis_order[i]) { + case 0: /* X cube */ + if (drawflags & MAN_SCALE_X) { + glTranslatef(dz, 0.0, 0.0); + if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_X); + manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0])); + drawsolidcube(cusize); + glTranslatef(-dz, 0.0, 0.0); + } + break; + case 1: /* Y cube */ + if (drawflags & MAN_SCALE_Y) { + glTranslatef(0.0, dz, 0.0); + if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_Y); + manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1])); + drawsolidcube(cusize); + glTranslatef(0.0, -dz, 0.0); + } + break; + case 2: /* Z cube */ + if (drawflags & MAN_SCALE_Z) { + glTranslatef(0.0, 0.0, dz); + if (G.f & G_PICKSEL) glLoadName(MAN_SCALE_Z); + manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2])); + drawsolidcube(cusize); + glTranslatef(0.0, 0.0, -dz); + } + break; + } } /* if shiftkey, center point as last, for selectbuffer order */ @@ -1333,16 +1402,17 @@ static void draw_manipulator_translate(View3D *v3d, RegionView3D *rv3d, int UNUS float cywid = 0.25f * cylen, dz, size; float unitmat[4][4]; int shift = 0; // XXX + int axis_order[3] = {0, 1, 2}; + int i; /* when called while moving in mixed mode, do not draw when... */ if ((drawflags & MAN_TRANS_C) == 0) return; + manipulator_axis_order(rv3d, axis_order); + // XXX if (moving) glTranslatef(t->vec[0], t->vec[1], t->vec[2]); glDisable(GL_DEPTH_TEST); - qobj = gluNewQuadric(); - gluQuadricDrawStyle(qobj, GLU_FILL); - /* center circle, do not add to selection when shift is pressed (planar constraint) */ if ((G.f & G_PICKSEL) && shift == 0) glLoadName(MAN_TRANS_C); @@ -1360,8 +1430,11 @@ static void draw_manipulator_translate(View3D *v3d, RegionView3D *rv3d, int UNUS glLoadName(-1); // translate drawn as last, only axis when no combo with scale, or for ghosting - if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST) - draw_manipulator_axes(v3d, rv3d, colcode, drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z); + if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST) { + draw_manipulator_axes(v3d, rv3d, colcode, + drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z, + axis_order); + } /* offset in combo mode, for rotate a bit more */ @@ -1369,29 +1442,43 @@ static void draw_manipulator_translate(View3D *v3d, RegionView3D *rv3d, int UNUS else if (combo & (V3D_MANIP_SCALE)) dz = 1.0f + 0.5f * cylen; else dz = 1.0f; - /* Z Cone */ - glTranslatef(0.0, 0.0, dz); - if (drawflags & MAN_TRANS_Z) { - if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_Z); - manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2])); - draw_cone(qobj, cylen, cywid); - } - /* X Cone */ - glTranslatef(dz, 0.0, -dz); - if (drawflags & MAN_TRANS_X) { - if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_X); - glRotatef(90.0, 0.0, 1.0, 0.0); - manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0])); - draw_cone(qobj, cylen, cywid); - glRotatef(-90.0, 0.0, 1.0, 0.0); - } - /* Y Cone */ - glTranslatef(-dz, dz, 0.0); - if (drawflags & MAN_TRANS_Y) { - if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_Y); - glRotatef(-90.0, 1.0, 0.0, 0.0); - manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1])); - draw_cone(qobj, cylen, cywid); + qobj = gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); + + for (i = 0; i < 3; i++) { + switch (axis_order[i]) { + case 0: /* Z Cone */ + if (drawflags & MAN_TRANS_Z) { + glTranslatef(0.0, 0.0, dz); + if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_Z); + manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->twangle[2])); + draw_cone(qobj, cylen, cywid); + glTranslatef(0.0, 0.0, -dz); + } + break; + case 1: /* X Cone */ + if (drawflags & MAN_TRANS_X) { + glTranslatef(dz, 0.0, 0.0); + if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->twangle[0])); + draw_cone(qobj, cylen, cywid); + glRotatef(-90.0, 0.0, 1.0, 0.0); + glTranslatef(-dz, 0.0, 0.0); + } + break; + case 2: /* Y Cone */ + if (drawflags & MAN_TRANS_Y) { + glTranslatef(0.0, dz, 0.0); + if (G.f & G_PICKSEL) glLoadName(MAN_TRANS_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->twangle[1])); + draw_cone(qobj, cylen, cywid); + glRotatef(90.0, 1.0, 0.0, 0.0); + glTranslatef(0.0, -dz, 0.0); + } + break; + } } gluDeleteQuadric(qobj); @@ -1407,10 +1494,13 @@ static void draw_manipulator_rotate_cyl(View3D *v3d, RegionView3D *rv3d, int mov float size; float cylen = 0.01f * (float)U.tw_handlesize; float cywid = 0.25f * cylen; - + int axis_order[3] = {2, 0, 1}; + int i; /* when called while moving in mixed mode, do not draw when... */ if ((drawflags & MAN_ROT_C) == 0) return; + manipulator_axis_order(rv3d, axis_order); + /* prepare for screen aligned draw */ glPushMatrix(); size = screen_aligned(rv3d, rv3d->twmat); @@ -1461,36 +1551,50 @@ static void draw_manipulator_rotate_cyl(View3D *v3d, RegionView3D *rv3d, int mov if ((G.f & G_PICKSEL) == 0) { // only draw axis when combo didn't draw scale axes - if ((combo & V3D_MANIP_SCALE) == 0) - draw_manipulator_axes(v3d, rv3d, colcode, drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z); + if ((combo & V3D_MANIP_SCALE) == 0) { + draw_manipulator_axes(v3d, rv3d, colcode, + drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z, + axis_order); + } /* only has to be set when not in picking */ gluQuadricDrawStyle(qobj, GLU_FILL); } - /* Z cyl */ - glTranslatef(0.0, 0.0, 1.0); - if (drawflags & MAN_ROT_Z) { - if (G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); - manipulator_setcolor(v3d, 'Z', colcode, 255); - draw_cylinder(qobj, cylen, cywid); - } - /* X cyl */ - glTranslatef(1.0, 0.0, -1.0); - if (drawflags & MAN_ROT_X) { - if (G.f & G_PICKSEL) glLoadName(MAN_ROT_X); - glRotatef(90.0, 0.0, 1.0, 0.0); - manipulator_setcolor(v3d, 'X', colcode, 255); - draw_cylinder(qobj, cylen, cywid); - glRotatef(-90.0, 0.0, 1.0, 0.0); - } - /* Y cylinder */ - glTranslatef(-1.0, 1.0, 0.0); - if (drawflags & MAN_ROT_Y) { - if (G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); - glRotatef(-90.0, 1.0, 0.0, 0.0); - manipulator_setcolor(v3d, 'Y', colcode, 255); - draw_cylinder(qobj, cylen, cywid); + for (i = 0; i < 3; i++) { + switch (axis_order[i]) { + case 0: /* X cylinder */ + if (drawflags & MAN_ROT_X) { + glTranslatef(1.0, 0.0, 0.0); + if (G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor(v3d, 'X', colcode, 255); + draw_cylinder(qobj, cylen, cywid); + glRotatef(-90.0, 0.0, 1.0, 0.0); + glTranslatef(-1.0, 0.0, 0.0); + } + break; + case 1: /* Y cylinder */ + if (drawflags & MAN_ROT_Y) { + glTranslatef(0.0, 1.0, 0.0); + if (G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor(v3d, 'Y', colcode, 255); + draw_cylinder(qobj, cylen, cywid); + glRotatef(90.0, 1.0, 0.0, 0.0); + glTranslatef(0.0, -1.0, 0.0); + } + break; + case 2: /* Z cylinder */ + if (drawflags & MAN_ROT_Z) { + glTranslatef(0.0, 0.0, 1.0); + if (G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor(v3d, 'Z', colcode, 255); + draw_cylinder(qobj, cylen, cywid); + glTranslatef(0.0, 0.0, -1.0); + } + break; + } } /* restore */ diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 32dc31954f2..0392c0f47a2 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -37,6 +37,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "BKE_context.h" #include "BKE_global.h" #include "BKE_armature.h" @@ -169,7 +171,7 @@ static int select_orientation_invoke(bContext *C, wmOperator *UNUSED(op), wmEven uiPopupMenu *pup; uiLayout *layout; - pup = uiPupMenuBegin(C, "Orientation", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Orientation"), ICON_NONE); layout = uiPupMenuLayout(pup); uiItemsEnumO(layout, "TRANSFORM_OT_select_orientation", "orientation"); uiPupMenuEnd(C, pup); diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 23d6b0a075e..f0f31b1e793 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -42,6 +42,8 @@ #include "BLI_blenlib.h" +#include "BLF_translation.h" + #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" @@ -174,10 +176,10 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha char line[FILE_MAX + 100]; wmOperatorType *ot = WM_operatortype_find(opname, 1); - pup = uiPupMenuBegin(C, "Unpack file", ICON_NONE); + pup = uiPupMenuBegin(C, IFACE_("Unpack File"), ICON_NONE); layout = uiPupMenuLayout(pup); - strcpy(line, "Remove Pack"); + strcpy(line, IFACE_("Remove Pack")); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_REMOVE); RNA_string_set(&props_ptr, "id", id_name); @@ -191,14 +193,14 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha if (strcmp(abs_name, local_name) != 0) { switch (checkPackedFile(local_name, pf)) { case PF_NOFILE: - BLI_snprintf(line, sizeof(line), "Create %s", local_name); + BLI_snprintf(line, sizeof(line), IFACE_("Create %s"), local_name); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_WRITE_LOCAL); RNA_string_set(&props_ptr, "id", id_name); break; case PF_EQUAL: - BLI_snprintf(line, sizeof(line), "Use %s (identical)", local_name); + BLI_snprintf(line, sizeof(line), IFACE_("Use %s (identical)"), local_name); //uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_USE_LOCAL); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_USE_LOCAL); @@ -206,13 +208,13 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha break; case PF_DIFFERS: - BLI_snprintf(line, sizeof(line), "Use %s (differs)", local_name); + BLI_snprintf(line, sizeof(line), IFACE_("Use %s (differs)"), local_name); //uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_USE_LOCAL); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_USE_LOCAL); RNA_string_set(&props_ptr, "id", id_name); - BLI_snprintf(line, sizeof(line), "Overwrite %s", local_name); + BLI_snprintf(line, sizeof(line), IFACE_("Overwrite %s"), local_name); //uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_WRITE_LOCAL); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_WRITE_LOCAL); @@ -224,27 +226,27 @@ void unpack_menu(bContext *C, const char *opname, const char *id_name, const cha switch (checkPackedFile(abs_name, pf)) { case PF_NOFILE: - BLI_snprintf(line, sizeof(line), "Create %s", abs_name); + BLI_snprintf(line, sizeof(line), IFACE_("Create %s"), abs_name); //uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_WRITE_ORIGINAL); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_WRITE_ORIGINAL); RNA_string_set(&props_ptr, "id", id_name); break; case PF_EQUAL: - BLI_snprintf(line, sizeof(line), "Use %s (identical)", abs_name); + BLI_snprintf(line, sizeof(line), IFACE_("Use %s (identical)"), abs_name); //uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_USE_ORIGINAL); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_USE_ORIGINAL); RNA_string_set(&props_ptr, "id", id_name); break; case PF_DIFFERS: - BLI_snprintf(line, sizeof(line), "Use %s (differs)", abs_name); + BLI_snprintf(line, sizeof(line), IFACE_("Use %s (differs)"), abs_name); //uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_USE_ORIGINAL); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_USE_ORIGINAL); RNA_string_set(&props_ptr, "id", id_name); - BLI_snprintf(line, sizeof(line), "Overwrite %s", abs_name); + BLI_snprintf(line, sizeof(line), IFACE_("Overwrite %s"), abs_name); //uiItemEnumO_ptr(layout, ot, line, 0, "method", PF_WRITE_ORIGINAL); props_ptr = uiItemFullO_ptr(layout, ot, line, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_O_RETURN_PROPS); RNA_enum_set(&props_ptr, "method", PF_WRITE_ORIGINAL); diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index 8a0ef06ef12..ac2ee21d09d 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -83,8 +83,8 @@ void ED_undo_push(bContext *C, const char *str) Object *obact = CTX_data_active_object(C); if (G.debug & G_DEBUG) - printf("undo push %s\n", str); - + printf("%s: %s\n", __func__, str); + if (obedit) { if (U.undosteps == 0) return; diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index a17d3c20c1c..fbb6250c05b 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -113,7 +113,7 @@ static int ED_operator_uvedit_can_uv_sculpt(struct bContext *C) return ED_space_image_show_uvedit(sima, obedit) && !(toolsettings->use_uv_sculpt); } -static int UNUSED_FUNCTION(ED_operator_uvmap_mesh) (bContext * C) +static int UNUSED_FUNCTION(ED_operator_uvmap_mesh) (bContext *C) { Object *ob = CTX_data_active_object(C); @@ -1582,7 +1582,7 @@ static void UV_OT_align(wmOperatorType *ot) /* api callbacks */ ot->exec = align_exec; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; /* properties */ RNA_def_enum(ot->srna, "axis", axis_items, 'a', "Axis", "Axis to align UV locations on"); @@ -2258,7 +2258,7 @@ static void UV_OT_select(wmOperatorType *ot) /* api callbacks */ ot->exec = select_exec; ot->invoke = select_invoke; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; /* properties */ RNA_def_boolean(ot->srna, "extend", 0, @@ -2303,7 +2303,7 @@ static void UV_OT_select_loop(wmOperatorType *ot) /* api callbacks */ ot->exec = select_loop_exec; ot->invoke = select_loop_invoke; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; /* properties */ RNA_def_boolean(ot->srna, "extend", 0, @@ -2377,7 +2377,7 @@ static void UV_OT_select_linked(wmOperatorType *ot) /* api callbacks */ ot->exec = select_linked_exec; - ot->poll = ED_operator_image_active; /* requires space image */ + ot->poll = ED_operator_uvedit; /* requires space image */ /* properties */ RNA_def_boolean(ot->srna, "extend", 0, @@ -2405,7 +2405,7 @@ static void UV_OT_select_linked_pick(wmOperatorType *ot) /* api callbacks */ ot->invoke = select_linked_pick_invoke; ot->exec = select_linked_pick_exec; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; /* properties */ RNA_def_boolean(ot->srna, "extend", 0, @@ -2842,7 +2842,7 @@ static void UV_OT_select_border(wmOperatorType *ot) ot->invoke = WM_border_select_invoke; ot->exec = border_select_exec; ot->modal = WM_border_select_modal; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; ot->cancel = WM_border_select_cancel; /* flags */ @@ -2966,7 +2966,7 @@ static void UV_OT_circle_select(wmOperatorType *ot) ot->invoke = WM_gesture_circle_invoke; ot->modal = WM_gesture_circle_modal; ot->exec = circle_select_exec; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; ot->cancel = WM_gesture_circle_cancel; /* flags */ @@ -3095,7 +3095,7 @@ static void UV_OT_select_lasso(wmOperatorType *ot) ot->invoke = WM_gesture_lasso_invoke; ot->modal = WM_gesture_lasso_modal; ot->exec = uv_lasso_select_exec; - ot->poll = ED_operator_image_active; + ot->poll = ED_operator_uvedit; ot->cancel = WM_gesture_lasso_cancel; /* flags */ @@ -3170,7 +3170,7 @@ static void UV_OT_snap_cursor(wmOperatorType *ot) /* api callbacks */ ot->exec = snap_cursor_exec; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; /* properties */ RNA_def_enum(ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to"); @@ -3350,7 +3350,7 @@ static void UV_OT_snap_selected(wmOperatorType *ot) /* api callbacks */ ot->exec = snap_selection_exec; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; /* properties */ RNA_def_enum(ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to"); @@ -3769,7 +3769,7 @@ static void UV_OT_cursor_set(wmOperatorType *ot) /* api callbacks */ ot->exec = set_2d_cursor_exec; ot->invoke = set_2d_cursor_invoke; - ot->poll = ED_operator_image_active; /* requires space image */; + ot->poll = ED_operator_uvedit; /* requires space image */; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 6abc41759e7..faeb0f721f4 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -50,6 +50,7 @@ set(SRC intern/gpu_codegen.c intern/gpu_draw.c intern/gpu_extensions.c + intern/gpu_fixed_material.c intern/gpu_material.c GPU_buffers.h @@ -59,10 +60,12 @@ set(SRC intern/gpu_codegen.h ) +data_to_c_simple(shaders/gpu_shader_fixed_fragment.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fixed_vertex.glsl SRC) data_to_c_simple(shaders/gpu_shader_material.glsl SRC) -data_to_c_simple(shaders/gpu_shader_vertex.glsl SRC) data_to_c_simple(shaders/gpu_shader_sep_gaussian_blur_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_sep_gaussian_blur_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_vertex.glsl SRC) data_to_c_simple(shaders/gpu_shader_vsm_store_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_vsm_store_vert.glsl SRC) diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index 66a7c917a55..7492304bd40 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -32,6 +32,8 @@ #ifndef __GPU_EXTENSIONS_H__ #define __GPU_EXTENSIONS_H__ +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif @@ -160,17 +162,17 @@ void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels); * - only for fragment shaders now * - must call texture bind before setting a texture as uniform! */ -GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *libcode); /*GPUShader *lib);*/ -/*GPUShader *GPU_shader_create_lib(const char *code);*/ +GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *libcode, const char *defines); void GPU_shader_free(GPUShader *shader); void GPU_shader_bind(GPUShader *shader); -void GPU_shader_unbind(GPUShader *shader); +void GPU_shader_unbind(void); int GPU_shader_get_uniform(GPUShader *shader, const char *name); void GPU_shader_uniform_vector(GPUShader *shader, int location, int length, int arraysize, float *value); void GPU_shader_uniform_texture(GPUShader *shader, int location, GPUTexture *tex); +void GPU_shader_uniform_int(GPUShader *shader, int location, int value); int GPU_shader_get_attribute(GPUShader *shader, const char *name); @@ -199,6 +201,30 @@ typedef struct GPUVertexAttribs { int totlayer; } GPUVertexAttribs; +/* Fixed Function Materials */ + +typedef enum GPUFixedMaterialOption { + GPU_FIXED_COLOR_MATERIAL = (1<<0), /* replace diffuse with glcolor */ + GPU_FIXED_SOLID_LIGHTING = (1<<1), /* use solid lighting (only 3 directional lights) */ + GPU_FIXED_SCENE_LIGHTING = (1<<2), /* use scene lighting (up to 8 arbitrary lights) */ + GPU_FIXED_TWO_SIDED = (1<<3), /* flip normals towards viewer */ + GPU_FIXED_TEXTURE_2D = (1<<4), /* use 2D texture to replace diffuse color */ + + GPU_FIXED_OPTIONS_NUM = 5, + GPU_FIXED_OPTION_COMBINATIONS = (1<<GPU_FIXED_OPTIONS_NUM) +} GPUFixedMaterialOption; + +void GPU_fixed_materials_init(void); +void GPU_fixed_materials_exit(void); + +void GPU_fixed_material_shader_bind(int options); +void GPU_fixed_material_shader_unbind(void); + +void GPU_fixed_material_colors(const float diffuse[3], const float specular[3], + int shininess, float alpha); + +bool GPU_fixed_material_need_normals(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/SConscript b/source/blender/gpu/SConscript index f7db3dfeaa0..ccea40c0909 100644 --- a/source/blender/gpu/SConscript +++ b/source/blender/gpu/SConscript @@ -49,10 +49,12 @@ if env['WITH_BF_DDS']: # generated data files import os sources.extend(( + os.path.join(env['DATA_SOURCES'], "gpu_shader_fixed_fragment.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_fixed_vertex.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_material.glsl.c"), - os.path.join(env['DATA_SOURCES'], "gpu_shader_vertex.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_sep_gaussian_blur_frag.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_sep_gaussian_blur_vert.glsl.c"), + os.path.join(env['DATA_SOURCES'], "gpu_shader_vertex.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_vsm_store_frag.glsl.c"), os.path.join(env['DATA_SOURCES'], "gpu_shader_vsm_store_vert.glsl.c"), )) diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index b27a4be9f21..59953659a2c 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -237,8 +237,6 @@ GPUFunction *GPU_lookup_function(const char *name) if (!FUNCTION_HASH) { FUNCTION_HASH = BLI_ghash_str_new("GPU_lookup_function gh"); gpu_parse_functions_string(FUNCTION_HASH, glsl_material_library); - /*FUNCTION_PROTOTYPES = gpu_generate_function_prototyps(FUNCTION_HASH); - FUNCTION_LIB = GPU_shader_create_lib(datatoc_gpu_shader_material_glsl);*/ } return (GPUFunction*)BLI_ghash_lookup(FUNCTION_HASH, (void *)name); @@ -758,7 +756,7 @@ static void GPU_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) } } - GPU_shader_unbind(shader); + GPU_shader_unbind(); } void GPU_pass_bind(GPUPass *pass, double time, int mipmap) @@ -820,7 +818,7 @@ void GPU_pass_unbind(GPUPass *pass) input->tex = NULL; } - GPU_shader_unbind(shader); + GPU_shader_unbind(); } /* Node Link Functions */ @@ -1368,7 +1366,7 @@ GPUPass *GPU_generate_pass(ListBase *nodes, GPUNodeLink *outlink, GPUVertexAttri /* generate code and compile with opengl */ fragmentcode = code_generate_fragment(nodes, outlink->output, name); vertexcode = code_generate_vertex(nodes); - shader = GPU_shader_create(vertexcode, fragmentcode, glsl_material_library); /*FUNCTION_LIB);*/ + shader = GPU_shader_create(vertexcode, fragmentcode, glsl_material_library, NULL); /* failed? */ if (!shader) { diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index e8e47013159..56aa4b222cb 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -205,12 +205,15 @@ void GPU_extensions_init(void) #else GG.os = GPU_OS_UNIX; #endif + + GPU_fixed_materials_init(); } void GPU_extensions_exit(void) { gpu_extensions_init = 0; GPU_codegen_exit(); + GPU_fixed_materials_exit(); } int GPU_glsl_support(void) @@ -1006,7 +1009,7 @@ void GPU_framebuffer_blur(GPUFrameBuffer *fb, GPUTexture *tex, GPUFrameBuffer *b glTexCoord2d(0, 1); glVertex2f(1, -1); glEnd(); - GPU_shader_unbind(blur_shader); + GPU_shader_unbind(); } /* GPUOffScreen */ @@ -1124,13 +1127,11 @@ static void shader_print_errors(const char *task, char *log, const char *code) fprintf(stderr, "%s\n", log); } -GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, /*GPUShader *lib,*/ const char *libcode) +GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, const char *libcode, const char *defines) { GLint status; GLcharARB log[5000]; - const char *fragsource[2]; GLsizei length = 0; - GLint count; GPUShader *shader; if (!GLEW_ARB_vertex_shader || !GLEW_ARB_fragment_shader) @@ -1154,8 +1155,14 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, /*GPU } if (vertexcode) { + const char *source[2]; + int num_source = 0; + + if (defines) source[num_source++] = defines; + if (vertexcode) source[num_source++] = vertexcode; + glAttachObjectARB(shader->object, shader->vertex); - glShaderSourceARB(shader->vertex, 1, (const char**)&vertexcode, NULL); + glShaderSourceARB(shader->vertex, num_source, source, NULL); glCompileShaderARB(shader->vertex); glGetObjectParameterivARB(shader->vertex, GL_OBJECT_COMPILE_STATUS_ARB, &status); @@ -1170,12 +1177,15 @@ GPUShader *GPU_shader_create(const char *vertexcode, const char *fragcode, /*GPU } if (fragcode) { - count = 0; - if (libcode) fragsource[count++] = libcode; - if (fragcode) fragsource[count++] = fragcode; + const char *source[3]; + int num_source = 0; + + if (defines) source[num_source++] = defines; + if (libcode) source[num_source++] = libcode; + if (fragcode) source[num_source++] = fragcode; glAttachObjectARB(shader->object, shader->fragment); - glShaderSourceARB(shader->fragment, count, fragsource, NULL); + glShaderSourceARB(shader->fragment, num_source, source, NULL); glCompileShaderARB(shader->fragment); glGetObjectParameterivARB(shader->fragment, GL_OBJECT_COMPILE_STATUS_ARB, &status); @@ -1254,7 +1264,7 @@ void GPU_shader_bind(GPUShader *shader) GPU_print_error("Post Shader Bind"); } -void GPU_shader_unbind(GPUShader *UNUSED(shader)) +void GPU_shader_unbind() { GPU_print_error("Pre Shader Unbind"); glUseProgramObjectARB(0); @@ -1296,6 +1306,16 @@ void GPU_shader_uniform_vector(GPUShader *UNUSED(shader), int location, int leng GPU_print_error("Post Uniform Vector"); } +void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) +{ + if (location == -1) + return; + + GPU_print_error("Pre Uniform Int"); + glUniform1iARB(location, value); + GPU_print_error("Post Uniform Int"); +} + void GPU_shader_uniform_texture(GPUShader *UNUSED(shader), int location, GPUTexture *tex) { GLenum arbnumber; @@ -1344,12 +1364,12 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) switch (shader) { case GPU_SHADER_VSM_STORE: if (!GG.shaders.vsm_store) - GG.shaders.vsm_store = GPU_shader_create(datatoc_gpu_shader_vsm_store_vert_glsl, datatoc_gpu_shader_vsm_store_frag_glsl, NULL); + GG.shaders.vsm_store = GPU_shader_create(datatoc_gpu_shader_vsm_store_vert_glsl, datatoc_gpu_shader_vsm_store_frag_glsl, NULL, NULL); retval = GG.shaders.vsm_store; break; case GPU_SHADER_SEP_GAUSSIAN_BLUR: if (!GG.shaders.sep_gaussian_blur) - GG.shaders.sep_gaussian_blur = GPU_shader_create(datatoc_gpu_shader_sep_gaussian_blur_vert_glsl, datatoc_gpu_shader_sep_gaussian_blur_frag_glsl, NULL); + GG.shaders.sep_gaussian_blur = GPU_shader_create(datatoc_gpu_shader_sep_gaussian_blur_vert_glsl, datatoc_gpu_shader_sep_gaussian_blur_frag_glsl, NULL, NULL); retval = GG.shaders.sep_gaussian_blur; break; } diff --git a/source/blender/gpu/intern/gpu_fixed_material.c b/source/blender/gpu/intern/gpu_fixed_material.c new file mode 100644 index 00000000000..d5a04c88e89 --- /dev/null +++ b/source/blender/gpu/intern/gpu_fixed_material.c @@ -0,0 +1,191 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Brecht Van Lommel. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/gpu/intern/gpu_fixed_material.c + * \ingroup gpu + */ + +/* GLSL shaders to replace fixed function OpenGL materials and lighting. These + * are deprecated in newer OpenGL versions and missing in OpenGL ES 2.0. Also, + * two sided lighting is no longer natively supported on NVidia cards which + * results in slow software fallback. + * + * Todo: + * - Replace glLight and glMaterial functions entirely with GLSL uniforms, to + * make OpenGL ES 2.0 work. + * - Replace glTexCoord and glColor with generic attributes. + * - Optimize for case where fewer than 3 or 8 lights are used. + * - Optimize for case where specular is not used. + * - Optimize for case where no texture matrix is used. + */ + +#include "GL/glew.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" + +#include "GPU_extensions.h" + +/* Fixed function material types */ + +static struct { + GPUShader *cached_shaders[GPU_FIXED_OPTION_COMBINATIONS]; + bool failed_shaders[GPU_FIXED_OPTION_COMBINATIONS]; + + bool need_normals; +} GPU_MATERIAL_STATE; + +/* Init / exit */ + +void GPU_fixed_materials_init() +{ + memset(&GPU_MATERIAL_STATE, 0, sizeof(GPU_MATERIAL_STATE)); +} + +void GPU_fixed_materials_exit() +{ + int i; + + for (i = 0; i < GPU_FIXED_OPTION_COMBINATIONS; i++) + if (GPU_MATERIAL_STATE.cached_shaders[i]) + GPU_shader_free(GPU_MATERIAL_STATE.cached_shaders[i]); +} + +/* Shader lookup / create */ + +static GPUShader *gpu_fixed_material_shader(int options) +{ + /* glsl code */ + extern char datatoc_gpu_shader_fixed_vertex_glsl[]; + extern char datatoc_gpu_shader_fixed_fragment_glsl[]; + + /* cached shaders */ + GPUShader *shader = GPU_MATERIAL_STATE.cached_shaders[options]; + + if (!shader && !GPU_MATERIAL_STATE.failed_shaders[options]) { + /* create shader if it doesn't exist yet */ + char defines[64*GPU_FIXED_OPTIONS_NUM] = ""; + + if (options & GPU_FIXED_COLOR_MATERIAL) + strcat(defines, "#define USE_COLOR\n"); + if (options & GPU_FIXED_TWO_SIDED) + strcat(defines, "#define USE_TWO_SIDED\n"); + if (options & GPU_FIXED_SOLID_LIGHTING) + strcat(defines, "#define USE_SOLID_LIGHTING\n"); + if (options & GPU_FIXED_SCENE_LIGHTING) + strcat(defines, "#define USE_SCENE_LIGHTING\n"); + if (options & GPU_FIXED_TEXTURE_2D) + strcat(defines, "#define USE_TEXTURE\n"); + + shader = GPU_shader_create( + datatoc_gpu_shader_fixed_vertex_glsl, + datatoc_gpu_shader_fixed_fragment_glsl, + NULL, defines); + + if (shader) { + /* set texture map to first texture unit */ + if (options & GPU_FIXED_TEXTURE_2D) + glUniform1i(GPU_shader_get_uniform(shader, "texture_map"), 0); + + GPU_MATERIAL_STATE.cached_shaders[options] = shader; + } + else + GPU_MATERIAL_STATE.failed_shaders[options] = true; + } + + return shader; +} + +/* Bind / unbind */ + +void GPU_fixed_material_shader_bind(int options) +{ + if (GPU_glsl_support()) { + GPUShader *shader = gpu_fixed_material_shader(options); + + if (shader) + GPU_shader_bind(shader); + } + else { + if (options & (GPU_FIXED_SOLID_LIGHTING|GPU_FIXED_SCENE_LIGHTING)) + glEnable(GL_LIGHTING); + + if (options & GPU_FIXED_TWO_SIDED) + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); + + if (options & GPU_FIXED_COLOR_MATERIAL) { + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + } + + if (options & GPU_FIXED_TEXTURE_2D) + glEnable(GL_TEXTURE_2D); + } + + /* temporary hack, should be solved outside of this file */ + GPU_MATERIAL_STATE.need_normals = (options & (GPU_FIXED_SOLID_LIGHTING|GPU_FIXED_SCENE_LIGHTING)); +} + +void GPU_fixed_material_shader_unbind() +{ + if (GPU_glsl_support()) { + GPU_shader_unbind(); + } + else { + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_TEXTURE_2D); + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); + } +} + +/* Material Colors */ + +void GPU_fixed_material_colors(const float diffuse[3], const float specular[3], + int shininess, float alpha) +{ + float gl_diffuse[4], gl_specular[4]; + + copy_v3_v3(gl_diffuse, diffuse); + gl_diffuse[3] = alpha; + + copy_v3_v3(gl_specular, specular); + gl_specular[3] = 1.0f; + + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); + glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, CLAMPIS(shininess, 1, 128)); +} + +bool GPU_fixed_material_need_normals() +{ + return GPU_MATERIAL_STATE.need_normals; +} + diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index b5ef27a338d..d7ac6febfb7 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -1919,7 +1919,7 @@ void GPU_lamp_shadow_buffer_bind(GPULamp *lamp, float viewmat[4][4], int *winsiz void GPU_lamp_shadow_buffer_unbind(GPULamp *lamp) { if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) { - GPU_shader_unbind(GPU_shader_get_builtin_shader(GPU_SHADER_VSM_STORE)); + GPU_shader_unbind(); GPU_framebuffer_blur(lamp->fb, lamp->tex, lamp->blurfb, lamp->blurtex); } diff --git a/source/blender/gpu/shaders/gpu_shader_fixed_fragment.glsl b/source/blender/gpu/shaders/gpu_shader_fixed_fragment.glsl new file mode 100644 index 00000000000..9610e0cf5aa --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fixed_fragment.glsl @@ -0,0 +1,169 @@ + +/* Options: + * + * USE_COLOR: use glColor for diffuse colors + * USE_TEXTURE: use texture for diffuse colors + * USE_SCENE_LIGHTING: use lights (up to 8) + * USE_SOLID_LIGHTING: assume 3 directional lights for solid draw mode + * USE_TWO_SIDED: flip normal towards viewer + * NO_SPECULAR: use specular component + */ + +#define NUM_SOLID_LIGHTS 3 +#define NUM_SCENE_LIGHTS 8 + +#if defined(USE_SOLID_LIGHTING) || defined(USE_SCENE_LIGHTING) +varying vec3 varying_normal; + +#ifndef USE_SOLID_LIGHTING +varying vec3 varying_position; +#endif +#endif + +#ifdef USE_COLOR +varying vec4 varying_vertex_color; +#endif + +#ifdef USE_TEXTURE +varying vec2 varying_texture_coord; +uniform sampler2D texture_map; +#endif + +void main() +{ +#if defined(USE_SOLID_LIGHTING) || defined(USE_SCENE_LIGHTING) + /* compute normal */ + vec3 N = normalize(varying_normal); + +#ifdef USE_TWO_SIDED + if (!gl_FrontFacing) + N = -N; +#endif + + /* compute diffuse and specular lighting */ + vec3 L_diffuse = vec3(0.0); +#ifndef NO_SPECULAR + vec3 L_specular = vec3(0.0); +#endif + +#ifdef USE_SOLID_LIGHTING + /* assume 3 directional lights */ + for (int i = 0; i < NUM_SOLID_LIGHTS; i++) { + vec3 light_direction = gl_LightSource[i].position.xyz; + + /* diffuse light */ + vec3 light_diffuse = gl_LightSource[i].diffuse.rgb; + float diffuse_bsdf = max(dot(N, light_direction), 0.0); + L_diffuse += light_diffuse*diffuse_bsdf; + +#ifndef NO_SPECULAR + /* specular light */ + vec3 light_specular = gl_LightSource[i].specular.rgb; + vec3 H = gl_LightSource[i].halfVector.xyz; + + float specular_bsdf = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess); + L_specular += light_specular*specular_bsdf; +#endif + } +#else + /* all 8 lights, makes no assumptions, potentially slow */ + +#ifndef NO_SPECULAR + /* view vector computation, depends on orthographics or perspective */ + vec3 V = (gl_ProjectionMatrix[3][3] == 0.0)? normalize(varying_position): vec3(0.0, 0.0, -1.0); +#endif + + for (int i = 0; i < NUM_SCENE_LIGHTS; i++) { + /* todo: this is a slow check for disabled lights */ + if (gl_LightSource[i].specular.a == 0.0) + continue; + + float intensity = 1.0; + vec3 light_direction; + + if (gl_LightSource[i].position.w == 0.0) { + /* directional light */ + light_direction = gl_LightSource[i].position.xyz; + } + else { + /* point light */ + vec3 d = gl_LightSource[i].position.xyz - varying_position; + light_direction = normalize(d); + + /* spot light cone */ + if (gl_LightSource[i].spotCutoff < 90.0) { + float cosine = max(dot(light_direction, -gl_LightSource[i].spotDirection), 0.0); + intensity = pow(cosine, gl_LightSource[i].spotExponent); + intensity *= step(gl_LightSource[i].spotCosCutoff, cosine); + } + + /* falloff */ + float distance = length(d); + + intensity /= gl_LightSource[i].constantAttenuation + + gl_LightSource[i].linearAttenuation * distance + + gl_LightSource[i].quadraticAttenuation * distance * distance; + } + + /* diffuse light */ + vec3 light_diffuse = gl_LightSource[i].diffuse.rgb; + float diffuse_bsdf = max(dot(N, light_direction), 0.0); + L_diffuse += light_diffuse*diffuse_bsdf*intensity; + +#ifndef NO_SPECULAR + /* specular light */ + vec3 light_specular = gl_LightSource[i].specular.rgb; + vec3 H = normalize(light_direction - V); + + float specular_bsdf = pow(max(dot(N, H), 0.0), gl_FrontMaterial.shininess); + L_specular += light_specular*specular_bsdf*intensity; +#endif + } +#endif + + /* compute diffuse color, possibly from texture or vertex colors */ + float alpha; + +#if defined(USE_TEXTURE) && defined(USE_COLOR) + vec4 texture_color = texture2D(texture_map, varying_texture_coord); + + L_diffuse *= texture_color.rgb * varying_vertex_color.rgb; + alpha = texture_color.a * varying_vertex_color.a; +#elif defined(USE_TEXTURE) + vec4 texture_color = texture2D(texture_map, varying_texture_coord); + + L_diffuse *= texture_color.rgb; + alpha = texture_color.a; +#elif defined(USE_COLOR) + L_diffuse *= varying_vertex_color.rgb; + alpha = varying_vertex_color.a; +#else + L_diffuse *= gl_FrontMaterial.diffuse.rgb; + alpha = gl_FrontMaterial.diffuse.a; +#endif + + /* sum lighting */ + vec3 L = gl_FrontLightModelProduct.sceneColor.rgb + L_diffuse; + +#ifndef NO_SPECULAR + L += L_specular*gl_FrontMaterial.specular.rgb; +#endif + + /* write out fragment color */ + gl_FragColor = vec4(L, alpha); +#else + + /* no lighting */ +#if defined(USE_TEXTURE) && defined(USE_COLOR) + gl_FragColor = texture2D(texture_map, varying_texture_coord) * varying_vertex_color; +#elif defined(USE_TEXTURE) + gl_FragColor = texture2D(texture_map, varying_texture_coord); +#elif defined(USE_COLOR) + gl_FragColor = varying_vertex_color; +#else + gl_FragColor = gl_FrontMaterial.diffuse; +#endif + +#endif +} + diff --git a/source/blender/gpu/shaders/gpu_shader_fixed_vertex.glsl b/source/blender/gpu/shaders/gpu_shader_fixed_vertex.glsl new file mode 100644 index 00000000000..612f9cff6aa --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fixed_vertex.glsl @@ -0,0 +1,48 @@ + +#if defined(USE_SOLID_LIGHTING) || defined(USE_SCENE_LIGHTING) +varying vec3 varying_normal; + +#ifndef USE_SOLID_LIGHTING +varying vec3 varying_position; +#endif +#endif + +#ifdef USE_COLOR +varying vec4 varying_vertex_color; +#endif + +#ifdef USE_TEXTURE +varying vec2 varying_texture_coord; +#endif + +void main() +{ + vec4 co = gl_ModelViewMatrix * gl_Vertex; + +#if defined(USE_SOLID_LIGHTING) || defined(USE_SCENE_LIGHTING) + varying_normal = normalize(gl_NormalMatrix * gl_Normal); + +#ifndef USE_SOLID_LIGHTING + varying_position = co.xyz; +#endif +#endif + + gl_Position = gl_ProjectionMatrix * co; + +#ifdef __GLSL_CG_DATA_TYPES + // Setting gl_ClipVertex is necessary to get glClipPlane working on NVIDIA graphic cards. + // gl_ClipVertex works only on NVIDIA graphic cards so we have to check with + // __GLSL_CG_DATA_TYPES if a NVIDIA graphic card is used (Cg support). + // gl_ClipVerte is supported up to GLSL 1.20. + gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex; +#endif + +#ifdef USE_COLOR + varying_vertex_color = gl_Color; +#endif + +#ifdef USE_TEXTURE + varying_texture_coord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).st; +#endif +} + diff --git a/source/blender/gpu/shaders/gpu_shader_vertex.glsl b/source/blender/gpu/shaders/gpu_shader_vertex.glsl index 574455e42b3..9e0db44ed31 100644 --- a/source/blender/gpu/shaders/gpu_shader_vertex.glsl +++ b/source/blender/gpu/shaders/gpu_shader_vertex.glsl @@ -14,7 +14,7 @@ void main() // gl_ClipVertex works only on NVIDIA graphic cards so we have to check with // __GLSL_CG_DATA_TYPES if a NVIDIA graphic card is used (Cg support). // gl_ClipVerte is supported up to GLSL 1.20. - #ifdef __GLSL_CG_DATA_TYPES - gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex; - #endif +#ifdef __GLSL_CG_DATA_TYPES + gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex; +#endif diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index e1ef7d92bd0..96bdb6c9bdd 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -1519,7 +1519,8 @@ static IK_Scene *convert_tree(Scene *blscene, Object *ob, bPoseChannel *pchan, f if (!ret || !scene->addCache(ikscene->cache) || !scene->addSolver(ikscene->solver) || - !scene->initialize()) { + !scene->initialize()) + { delete ikscene; ikscene = NULL; } @@ -1566,7 +1567,8 @@ static int init_scene(Object *ob) if (ob->pose->ikdata) { for (scene = ((IK_Data *)ob->pose->ikdata)->first; scene != NULL; - scene = scene->next) { + scene = scene->next) + { if (fabs(scene->blScale - scale) > KDL::epsilon) return 1; scene->channels[0].pchan->flag |= POSE_IKTREE; diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index ff297d70cc3..cfeacff7f4a 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -1702,13 +1702,11 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, int save_as_render, int int do_colormanagement; int is_movie = BKE_imtype_is_movie(image_format_data->imtype); int requires_linear_float = BKE_imtype_requires_linear_float(image_format_data->imtype); + int do_alpha_under = image_format_data->planes != R_IMF_PLANES_RGBA; do_colormanagement = save_as_render && (is_movie || !requires_linear_float); - if (do_colormanagement) { - int make_byte = FALSE; - ImFileType *type; - + if (do_colormanagement || do_alpha_under) { if (allocate_result) { colormanaged_ibuf = IMB_dupImBuf(ibuf); } @@ -1727,6 +1725,41 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, int save_as_render, int ibuf->mall |= IB_rectfloat; } } + } + + /* If we're saving from RGBA to RGB buffer then it's not + * so much useful to just ignore alpha -- it leads to bad + * artifacts especially when saving byte images. + * + * What we do here is we're overing our image on top of + * background color (which is currently black). + * + * This is quite much the same as what Gimp does and it + * seems to be what artists expects from saving. + * + * Do a conversion here, so image format writers could + * happily assume all the alpha tricks were made already. + * helps keep things locally here, not spreading it to + * all possible image writers we've got. + */ + if (do_alpha_under) { + float color[3] = {0, 0, 0}; + + if (colormanaged_ibuf->rect_float && colormanaged_ibuf->channels == 4) { + IMB_alpha_under_color_float(colormanaged_ibuf->rect_float, colormanaged_ibuf->x, + colormanaged_ibuf->y, color); + } + + if (colormanaged_ibuf->rect) { + IMB_alpha_under_color_byte((unsigned char *)colormanaged_ibuf->rect, + colormanaged_ibuf->x, colormanaged_ibuf->y, + color); + } + } + + if (do_colormanagement) { + int make_byte = FALSE; + ImFileType *type; /* for proper check whether byte buffer is required by a format or not * should be pretty safe since this image buffer is supposed to be used for diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index 5c2dc0c7df9..37070c7e2bf 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -26,6 +26,9 @@ #include <stddef.h> + +#include "BLI_utildefines.h" + #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_filetype.h" @@ -56,7 +59,7 @@ static int imb_ftype_iris(ImFileType *type, ImBuf *ibuf) return (ibuf->ftype == IMAGIC); } #ifdef WITH_QUICKTIME -static int imb_ftype_quicktime(ImFileType *type, ImBuf *ibuf) +static int imb_ftype_quicktime(ImFileType *UNUSED(type), ImBuf *UNUSED(ibuf)) { return 0; /* XXX */ } diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 59282c9d207..26dd0f2977a 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -367,9 +367,9 @@ void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, float backcol else { int mul = 255 - cp[3]; - cp[0] += mul * backcol[0] / 255; - cp[1] += mul * backcol[1] / 255; - cp[2] += mul * backcol[2] / 255; + cp[0] = (cp[0] * cp[3] >> 8) + mul * backcol[0] / 255; + cp[1] = (cp[1] * cp[3] >> 8) + mul * backcol[1] / 255; + cp[2] = (cp[2] * cp[3] >> 8) + mul * backcol[2] / 255; } cp[3] = 255; diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index 3d04b8ee184..f4ac4322747 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -803,7 +803,7 @@ static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters) case 16: if (numcomps == 4) { - if (channels_in_float == 4){ + if (channels_in_float == 4) { PIXEL_LOOPER_BEGIN(rect_float) { premul_to_straight_v4_v4(from_straight, rect_float); diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 3f6e6c59137..392c92148b4 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -27,6 +27,10 @@ /** \file DNA_action_types.h * \ingroup DNA + * + * Define actions data-block for the animation system. + * A collection of animation curves and drivers to be assigned to data-blocks + * or sequenced in the non-linear-editor (NLA). */ #ifndef __DNA_ACTION_TYPES_H__ @@ -692,4 +696,4 @@ typedef enum ACHAN_FLAG { ACHAN_MOVED = (1 << 31) } ACHAN_FLAG; -#endif +#endif /* __DNA_ACTION_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_actuator_types.h b/source/blender/makesdna/DNA_actuator_types.h index 7c4772f24e8..1495ba1b1a5 100644 --- a/source/blender/makesdna/DNA_actuator_types.h +++ b/source/blender/makesdna/DNA_actuator_types.h @@ -27,6 +27,8 @@ /** \file DNA_actuator_types.h * \ingroup DNA + * + * #bActuator type is specifically for use by Object logic-bricks in the game-engine. */ #ifndef __DNA_ACTUATOR_TYPES_H__ @@ -535,6 +537,4 @@ typedef struct bActuator { #define ACT_STEERING_AUTOMATICFACING 4 #define ACT_STEERING_NORMALUP 8 -#endif - - +#endif /* __DNA_ACTUATOR_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 1ac6e6db94b..73e2a9f433b 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -284,15 +284,18 @@ typedef enum eDriverTarget_Flag { /* used for targets that use the pchan_name instead of RNA path * (i.e. rotation difference) */ - DTAR_FLAG_STRUCT_REF = (1<<0), + DTAR_FLAG_STRUCT_REF = (1 << 0), /* idtype can only be 'Object' */ - DTAR_FLAG_ID_OB_ONLY = (1<<1), + DTAR_FLAG_ID_OB_ONLY = (1 << 1), /* "localspace" flags */ /* base flag - basically "pre parent+constraints" */ - DTAR_FLAG_LOCALSPACE = (1<<2), + DTAR_FLAG_LOCALSPACE = (1 << 2), /* include constraints transformed to space including parents */ - DTAR_FLAG_LOCAL_CONSTS = (1<<3), + DTAR_FLAG_LOCAL_CONSTS = (1 << 3), + + /* error flags */ + DTAR_FLAG_INVALID = (1 << 4), } eDriverTarget_Flag; /* Transform Channels for Driver Targets */ diff --git a/source/blender/makesdna/DNA_controller_types.h b/source/blender/makesdna/DNA_controller_types.h index bdfedb5b4d1..0c1aaf5fd20 100644 --- a/source/blender/makesdna/DNA_controller_types.h +++ b/source/blender/makesdna/DNA_controller_types.h @@ -27,6 +27,8 @@ /** \file DNA_controller_types.h * \ingroup DNA + * + * #bController type is specifically for use by Object logic-bricks in the game-engine. */ #ifndef __DNA_CONTROLLER_TYPES_H__ @@ -89,5 +91,4 @@ typedef struct bController { #define CONT_PY_SCRIPT 0 #define CONT_PY_MODULE 1 -#endif - +#endif /* __DNA_CONTROLLER_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index e28b8780250..4a3debe756b 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -27,6 +27,8 @@ /** \file DNA_customdata_types.h * \ingroup DNA + * + * Used for custom mesh data types (stored per vert/edge/loop/face) */ #ifndef __DNA_CUSTOMDATA_TYPES_H__ @@ -177,4 +179,4 @@ typedef struct CustomData { } #endif -#endif +#endif /* __DNA_CUSTOMDATA_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_defs.h b/source/blender/makesdna/DNA_defs.h index 774fbcf081a..ffe4fc970b1 100644 --- a/source/blender/makesdna/DNA_defs.h +++ b/source/blender/makesdna/DNA_defs.h @@ -22,6 +22,8 @@ /** \file DNA_defs.h * \ingroup DNA + * + * Group generic defines for all DNA headers may use in this file. */ #ifndef __DNA_DEFS_H__ diff --git a/source/blender/makesdna/DNA_group_types.h b/source/blender/makesdna/DNA_group_types.h index a084bee1c2d..2740281b4c0 100644 --- a/source/blender/makesdna/DNA_group_types.h +++ b/source/blender/makesdna/DNA_group_types.h @@ -27,6 +27,8 @@ /** \file DNA_group_types.h * \ingroup DNA + * + * \brief Object groups, one object can be in many groups at once. */ #ifndef __DNA_GROUP_TYPES_H__ @@ -58,4 +60,4 @@ typedef struct Group { float dupli_ofs[3]; } Group; -#endif +#endif /* __DNA_GROUP_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 682f54481fc..54ec07c1855 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -101,10 +101,12 @@ typedef struct Image { float lastupdate; int lastused; short animspeed; + short pad2; /* for generated images */ - short gen_x, gen_y; + int gen_x, gen_y; char gen_type, gen_flag; + char gen_pad[2]; /* display aspect - for UV editing images resized for faster openGL display */ float aspx, aspy; diff --git a/source/blender/makesdna/DNA_key_types.h b/source/blender/makesdna/DNA_key_types.h index 6e5861043c1..4783247420c 100644 --- a/source/blender/makesdna/DNA_key_types.h +++ b/source/blender/makesdna/DNA_key_types.h @@ -29,6 +29,10 @@ /** \file DNA_key_types.h * \ingroup DNA + * + * This file defines structures for Shape-Keys (not animation keyframes), + * attached to Mesh, Curve and Lattice Data. Even though Key's are ID blocks they + * aren't intended to be shared between multiple data blocks as with other ID types. */ #include "DNA_defs.h" diff --git a/source/blender/makesdna/DNA_mask_types.h b/source/blender/makesdna/DNA_mask_types.h index 5b25d1a072c..1b6b802f2de 100644 --- a/source/blender/makesdna/DNA_mask_types.h +++ b/source/blender/makesdna/DNA_mask_types.h @@ -30,6 +30,9 @@ * \ingroup DNA * \since march-2012 * \author Sergey Sharybin + * + * Mask data-blocks are collections of 2D curves to be used + * for image masking in the compositor and sequencer. */ #ifndef __DNA_MASK_TYPES_H__ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 5aaf46a541f..62c997b72c6 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -854,4 +854,7 @@ typedef struct NodeShaderNormalMap { #define CMP_NODE_MASK_MBLUR_SAMPLES_MAX 64 +/* image */ +#define CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT 1 + #endif diff --git a/source/blender/makesdna/DNA_property_types.h b/source/blender/makesdna/DNA_property_types.h index c1b810cd42b..c8275a41404 100644 --- a/source/blender/makesdna/DNA_property_types.h +++ b/source/blender/makesdna/DNA_property_types.h @@ -31,6 +31,8 @@ * \author nzc * \attention Renderrecipe and scene decription. The fact that there is a * hierarchy here is a bit strange, and not desirable. + * + * #bProperty type is specifically for use by Objects game-logic. */ #ifndef __DNA_PROPERTY_TYPES_H__ @@ -60,5 +62,4 @@ typedef struct bProperty { #define MAX_PROPSTRING 128 -#endif - +#endif /* __DNA_PROPERTY_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h index c144bc4e588..4a96c324f04 100644 --- a/source/blender/makesdna/DNA_rigidbody_types.h +++ b/source/blender/makesdna/DNA_rigidbody_types.h @@ -80,9 +80,7 @@ typedef enum eRigidBodyWorld_Flag { /* sim data needs to be rebuilt */ RBW_FLAG_NEEDS_REBUILD = (1 << 1), /* usse split impulse when stepping the simulation */ - RBW_FLAG_USE_SPLIT_IMPULSE = (1 << 2), - /* need to step simulation after frame update */ - RBW_FLAG_FRAME_UPDATE = (1 << 3) + RBW_FLAG_USE_SPLIT_IMPULSE = (1 << 2) } eRigidBodyWorld_Flag; /* ******************************** */ diff --git a/source/blender/makesdna/DNA_sensor_types.h b/source/blender/makesdna/DNA_sensor_types.h index 05927e3a486..d8cf80d047b 100644 --- a/source/blender/makesdna/DNA_sensor_types.h +++ b/source/blender/makesdna/DNA_sensor_types.h @@ -29,6 +29,8 @@ * \ingroup DNA * \since mar-2001 * \author nzc + * + * #bSensor type is specifically for use by Object logic-bricks in the game-engine. */ #ifndef __DNA_SENSOR_TYPES_H__ @@ -324,5 +326,5 @@ typedef struct bJoystickSensor { #define SENS_DELAY_REPEAT 1 // should match JOYINDEX_MAX in SCA_JoystickDefines.h */ #define SENS_JOY_MAXINDEX 8 -#endif +#endif /* __DNA_SENSOR_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 0aa466f7245..455350d8310 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -28,6 +28,14 @@ * \ingroup DNA * \since mar-2001 * \author nzc + * + * Structs for use by the 'Sequencer' (Video Editor) + * + * Note on terminology + * - #Sequence: video/effect/audio data you can select and manipulate in the sequencer. + * - #Sequence.machine: Strange name for the channel. + * - #Strip: The data referenced by the #Sequence + * - Meta Strip (SEQ_TYPE_META): Support for nesting Sequences. */ #ifndef __DNA_SEQUENCE_TYPES_H__ @@ -441,4 +449,4 @@ enum { SEQUENCE_MASK_INPUT_ID = 1 }; -#endif +#endif /* __DNA_SEQUENCE_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 4c1d2c638b9..4587ed948cf 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -28,6 +28,8 @@ * \ingroup DNA * \since mar-2001 * \author nzc + * + * Structs for each of space type in the user interface. */ #ifndef __DNA_SPACE_TYPES_H__ @@ -498,6 +500,7 @@ typedef enum eSpaceSeq_Flag { SEQ_DRAW_SAFE_MARGINS = (1 << 3), SEQ_SHOW_GPENCIL = (1 << 4), SEQ_NO_DRAW_CFRANUM = (1 << 5), + SEQ_USE_ALPHA = (1 << 6), /* use RGBA display mode for preview */ } eSpaceSeq_Flag; /* sseq->view */ @@ -1004,8 +1007,8 @@ typedef struct SpaceUserPref { ListBase regionbase; /* storage of regions for inactive spaces */ int spacetype; - int pad; - + char pad[3]; + char filter_type; char filter[64]; /* search term for filtering in the UI */ } SpaceUserPref; @@ -1133,4 +1136,4 @@ typedef enum eSpace_Type { #define IMG_SIZE_FALLBACK 256 -#endif +#endif /* __DNA_SPACE_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_text_types.h b/source/blender/makesdna/DNA_text_types.h index 3194adba3a0..8b18ecd7253 100644 --- a/source/blender/makesdna/DNA_text_types.h +++ b/source/blender/makesdna/DNA_text_types.h @@ -28,6 +28,9 @@ * \ingroup DNA * \since mar-2001 * \author nzc + * + * Text blocks used for Python-Scripts, OpenShadingLanguage + * and arbitrary text data to store in blend files. */ #ifndef __DNA_TEXT_TYPES_H__ @@ -75,4 +78,4 @@ typedef struct Text { #define TXT_FOLLOW 0x0200 /* always follow cursor (console) */ #define TXT_TABSTOSPACES 0x0400 /* use space instead of tabs */ -#endif +#endif /* __DNA_TEXT_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h index 8789a17a7f7..73cda070fd2 100644 --- a/source/blender/makesdna/DNA_tracking_types.h +++ b/source/blender/makesdna/DNA_tracking_types.h @@ -30,6 +30,8 @@ * \ingroup DNA * \since may-2011 * \author Sergey Sharybin + * + * Structs used for camera tracking and the movie-clip editor. */ #ifndef __DNA_TRACKING_TYPES_H__ @@ -431,4 +433,4 @@ enum { TRACKING_COVERAGE_OK = 2 }; -#endif +#endif /* __DNA_TRACKING_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_vfont_types.h b/source/blender/makesdna/DNA_vfont_types.h index 7aaeaf23db2..416c5f594a3 100644 --- a/source/blender/makesdna/DNA_vfont_types.h +++ b/source/blender/makesdna/DNA_vfont_types.h @@ -29,6 +29,9 @@ * \ingroup DNA * \since mar-2001 * \author nzc + * + * Vector Fonts used for text in the 3D view-port + * (unrelated to text used to render the GUI). */ #ifndef __DNA_VFONT_TYPES_H__ @@ -63,5 +66,5 @@ typedef struct VFont { #define FO_SELCHANGE 10 #define FO_BUILTIN_NAME "<builtin>" -#endif +#endif /* __DNA_VFONT_TYPES_H__ */ diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 585e08ea706..44792951cad 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -290,6 +290,9 @@ add_executable(makesrna ${SRC} ${SRC_RNA_INC} ${SRC_DNA_INC}) target_link_libraries(makesrna bf_dna) target_link_libraries(makesrna bf_dna_blenlib) +# too many warnings with clang +remove_cc_flag("-Wmissing-prototypes") + # Output rna_*_gen.c # note (linux only): with crashes try add this after COMMAND: valgrind --leak-check=full --track-origins=yes add_custom_command( diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index adfb096b25f..6035326e0ae 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -3196,18 +3196,16 @@ static int rna_raw_access(ReportList *reports, PointerRNA *ptr, PropertyRNA *pro return 0; } + /* check item array */ + itemlen = RNA_property_array_length(&itemptr, itemprop); + /* dynamic array? need to get length per item */ if (itemprop->getlength) { itemprop = NULL; } /* try to access as raw array */ else if (RNA_property_collection_raw_array(ptr, prop, itemprop, &out)) { - int arraylen; - - /* check item array */ - itemlen = RNA_property_array_length(&itemptr, itemprop); - - arraylen = (itemlen == 0) ? 1 : itemlen; + int arraylen = (itemlen == 0) ? 1 : itemlen; if (in.len != arraylen * out.len) { BKE_reportf(reports, RPT_ERROR, "Array length mismatch (expected %d, got %d)", out.len * arraylen, in.len); @@ -4866,7 +4864,7 @@ static char *rna_pointer_as_string__bldata(PointerRNA *ptr) } } -char *RNA_pointer_as_string(bContext *C, PointerRNA *ptr, PropertyRNA *prop_ptr, PointerRNA *ptr_prop) +char *RNA_pointer_as_string(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *prop_ptr, PointerRNA *ptr_prop) { if (RNA_property_flag(prop_ptr) & PROP_IDPROPERTY) { return rna_pointer_as_string__idprop(C, ptr_prop); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 1fff567f25c..73fe5f3a48d 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2412,7 +2412,12 @@ static void def_cmp_image(StructRNA *srna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Image", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - + + prop = RNA_def_property(srna, "use_straight_alpha_output", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "custom1", CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT); + RNA_def_property_ui_text(prop, "Straight Alpha Output", "Put Node output buffer to straight alpha instead of premultiplied"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + /* NB: image user properties used in the UI are redefined in def_node_image_user, * to trigger correct updates of the node editor. RNA design problem that prevents * updates from nested structs ... diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index d6f3f594395..4bf5de03b05 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -2892,7 +2892,7 @@ static void rna_def_particle_settings(BlenderRNA *brna) RNA_def_property_float_funcs(prop, "rna_PartSetting_linelentail_get", "rna_PartSetting_linelentail_set", NULL); RNA_def_property_range(prop, 0.0f, 100000.0f); RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 3); - RNA_def_property_ui_text(prop, "Back", "Length of the line's tail"); + RNA_def_property_ui_text(prop, "Tail", "Length of the line's tail"); RNA_def_property_update(prop, 0, "rna_Particle_redo"); prop = RNA_def_property(srna, "line_length_head", PROP_FLOAT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 5931440b422..e2e373d8beb 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -299,19 +299,19 @@ static void rna_def_render_engine(BlenderRNA *brna) /* final render callbacks */ func = RNA_def_function(srna, "update", NULL); RNA_def_function_ui_description(func, "Export scene data for render"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); RNA_def_pointer(func, "data", "BlendData", "", ""); RNA_def_pointer(func, "scene", "Scene", "", ""); func = RNA_def_function(srna, "render", NULL); RNA_def_function_ui_description(func, "Render scene into an image"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); RNA_def_pointer(func, "scene", "Scene", "", ""); /* viewport render callbacks */ func = RNA_def_function(srna, "view_update", NULL); RNA_def_function_ui_description(func, "Update on data changes for viewport render"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); RNA_def_pointer(func, "context", "Context", "", ""); func = RNA_def_function(srna, "view_draw", NULL); diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c index f5cf247b5a1..171bc702bc5 100644 --- a/source/blender/makesrna/intern/rna_rigidbody.c +++ b/source/blender/makesrna/intern/rna_rigidbody.c @@ -234,6 +234,20 @@ static void rna_RigidBodyOb_collision_margin_set(PointerRNA *ptr, float value) #endif } +static void rna_RigidBodyOb_collision_groups_set(PointerRNA *ptr, const int *values) +{ + RigidBodyOb *rbo = (RigidBodyOb *)ptr->data; + int i; + + for (i = 0; i < 20; i++) { + if (values[i]) + rbo->col_groups |= (1 << i); + else + rbo->col_groups &= ~(1 << i); + } + rbo->flag |= RBO_FLAG_NEEDS_VALIDATE; +} + static void rna_RigidBodyOb_kinematic_state_set(PointerRNA *ptr, int value) { RigidBodyOb *rbo = (RigidBodyOb *)ptr->data; @@ -812,6 +826,7 @@ static void rna_def_rigidbody_object(BlenderRNA *brna) prop = RNA_def_property(srna, "collision_groups", PROP_BOOLEAN, PROP_LAYER_MEMBER); RNA_def_property_boolean_sdna(prop, NULL, "col_groups", 1); RNA_def_property_array(prop, 20); + RNA_def_property_boolean_funcs(prop, NULL, "rna_RigidBodyOb_collision_groups_set"); RNA_def_property_ui_text(prop, "Collision Groups", "Collision Groups Rigid Body belongs to"); RNA_def_property_update(prop, NC_OBJECT | ND_POINTCACHE, "rna_RigidBodyOb_reset"); RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); @@ -949,13 +964,13 @@ static void rna_def_rigidbody_constraint(BlenderRNA *brna) prop = RNA_def_property(srna, "use_motor_lin", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_MOTOR_LIN); RNA_def_property_boolean_funcs(prop, NULL, "rna_RigidBodyCon_use_motor_lin_set"); - RNA_def_property_ui_text(prop, "Linear Motor", "Enables linear motor"); + RNA_def_property_ui_text(prop, "Linear Motor", "Enable linear motor"); RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset"); prop = RNA_def_property(srna, "use_motor_ang", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", RBC_FLAG_USE_MOTOR_ANG); RNA_def_property_boolean_funcs(prop, NULL, "rna_RigidBodyCon_use_motor_ang_set"); - RNA_def_property_ui_text(prop, "Angular Motor", "Enables angular motor"); + RNA_def_property_ui_text(prop, "Angular Motor", "Enable angular motor"); RNA_def_property_update(prop, NC_OBJECT, "rna_RigidBodyOb_reset"); prop = RNA_def_property(srna, "limit_lin_x_lower", PROP_FLOAT, PROP_UNIT_LENGTH); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 20269005b3f..906c8853179 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -916,16 +916,13 @@ static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_codecType_itemf(bCon EnumPropertyItem tmp = {0, "", 0, "", ""}; QuicktimeCodecTypeDesc *codecTypeDesc; int i = 1, totitem = 0; - char id[5]; - + for (i = 0; i < quicktime_get_num_videocodecs(); i++) { codecTypeDesc = quicktime_get_videocodecType_desc(i); if (!codecTypeDesc) break; - + tmp.value = codecTypeDesc->rnatmpvalue; - *((int *)id) = codecTypeDesc->codecType; - id[4] = 0; - tmp.identifier = id; + tmp.identifier = codecTypeDesc->codecName; tmp.name = codecTypeDesc->codecName; RNA_enum_item_add(&item, &totitem, &tmp); } @@ -1586,8 +1583,8 @@ static void rna_def_tool_settings(BlenderRNA *brna) static EnumPropertyItem draw_groupuser_items[] = { {OB_DRAW_GROUPUSER_NONE, "NONE", 0, "None", ""}, - {OB_DRAW_GROUPUSER_ACTIVE, "ACTIVE", 0, "Active", "Show vertices with no weights in the actuve group"}, - {OB_DRAW_GROUPUSER_ALL, "ALL", 0, "All", "Show vertices with no weights in the any group"}, + {OB_DRAW_GROUPUSER_ACTIVE, "ACTIVE", 0, "Active", "Show vertices with no weights in the active group"}, + {OB_DRAW_GROUPUSER_ALL, "ALL", 0, "All", "Show vertices with no weights in any group"}, {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index b0df27b957b..b3619330e7a 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -111,6 +111,7 @@ static void rna_Scene_collada_export( int include_material_textures, int use_texture_copies, + int use_ngons, int use_object_instantiation, int sort_by_name, int second_life) @@ -118,7 +119,7 @@ static void rna_Scene_collada_export( collada_export(scene, filepath, apply_modifiers, export_mesh_type, selected, include_children, include_armatures, include_shapekeys, deform_bones_only, active_uv_only, include_uv_textures, include_material_textures, - use_texture_copies, use_object_instantiation, sort_by_name, second_life); + use_texture_copies, use_ngons, use_object_instantiation, sort_by_name, second_life); } #endif @@ -160,6 +161,7 @@ void RNA_api_scene(StructRNA *srna) parm = RNA_def_boolean(func, "include_material_textures", 0, "Include Material Textures", "Export textures assigned to the object Materials"); parm = RNA_def_boolean(func, "use_texture_copies", 0, "copy", "Copy textures to same folder where the .dae file is exported"); + parm = RNA_def_boolean(func, "use_ngons", 1, "Use NGons", "Keep NGons in Export"); parm = RNA_def_boolean(func, "use_object_instantiation", 1, "Use Object Instances", "Instantiate multiple Objects from same Data"); parm = RNA_def_boolean(func, "sort_by_name", 0, "Sort by Object name", "Sort exported data by Object name"); parm = RNA_def_boolean(func, "second_life", 0, "Export for Second Life", "Compatibility mode for Second Life"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 7dc2dbf3d03..f1f4c13731f 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2249,6 +2249,13 @@ static void rna_def_space_sequencer(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem preview_channels_items[] = { + {SEQ_USE_ALPHA, "COLOR_ALPHA", ICON_IMAGE_RGB_ALPHA, "Color and Alpha", + "Draw image with RGB colors and alpha transparency"}, + {0, "COLOR", ICON_IMAGE_RGB, "Color", "Draw image with RGB colors"}, + {0, NULL, 0, NULL, NULL} + }; + srna = RNA_def_struct(brna, "SpaceSequenceEditor", "Space"); RNA_def_struct_sdna(srna, "SpaceSeq"); RNA_def_struct_ui_text(srna, "Space Sequence Editor", "Sequence editor space data"); @@ -2266,7 +2273,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_enum_items(prop, display_mode_items); RNA_def_property_ui_text(prop, "Display Mode", "View mode to use for displaying sequencer output"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - + /* flags */ prop = RNA_def_property(srna, "show_frame_indicator", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_NO_DRAW_CFRANUM); @@ -2311,7 +2318,13 @@ static void rna_def_space_sequencer(BlenderRNA *brna) "The channel number shown in the image preview. 0 is the result of all strips combined"); RNA_def_property_range(prop, -5, MAXSEQ); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - + + prop = RNA_def_property(srna, "preview_channels", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, preview_channels_items); + RNA_def_property_ui_text(prop, "Draw Channels", "Channels of the preview to draw"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + prop = RNA_def_property(srna, "draw_overexposed", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "zebra"); RNA_def_property_ui_text(prop, "Show Overexposed", "Show overexposed areas with zebra stripes"); @@ -3042,13 +3055,24 @@ static void rna_def_space_info(BlenderRNA *brna) static void rna_def_space_userpref(BlenderRNA *brna) { + static EnumPropertyItem filter_type_items[] = { + {0, "NAME", 0, "Name", "Filter based on the operator name"}, + {1, "KEY", 0, "Key-Binding", "Filter based on key bindings"}, + {0, NULL, 0, NULL, NULL}}; + StructRNA *srna; PropertyRNA *prop; srna = RNA_def_struct(brna, "SpaceUserPreferences", "Space"); RNA_def_struct_sdna(srna, "SpaceUserPref"); RNA_def_struct_ui_text(srna, "Space User Preferences", "User preferences space data"); - + + prop = RNA_def_property(srna, "filter_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "filter_type"); + RNA_def_property_enum_items(prop, filter_type_items); + RNA_def_property_ui_text(prop, "Filter Type", "Filter method"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); + prop = RNA_def_property(srna, "filter_text", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "filter"); RNA_def_property_ui_text(prop, "Filter", "Search term for filtering in the UI"); diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index cd646f4849c..798395b9fef 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -579,6 +579,9 @@ static void rna_def_trackingSettings(BlenderRNA *brna) {REFINE_FOCAL_LENGTH | REFINE_PRINCIPAL_POINT, "FOCAL_LENGTH_PRINCIPAL_POINT", 0, "Focal Length, Optical Center", "Refine focal length and optical center"}, + {REFINE_RADIAL_DISTORTION_K1 | + REFINE_RADIAL_DISTORTION_K2, "RADIAL_K1_K2", 0, "K1, K2", + "Refine radial distortion K1 and K2"}, {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 2530a230e0a..d5620f91d6e 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -137,13 +137,13 @@ static void sphere_do( int i, defgrp_index; int has_radius = 0; short flag, type; - float fac, facm, len = 0.0f; + float len = 0.0f; + float fac = cmd->fac; + float facm = 1.0f - fac; + const float fac_orig = fac; float vec[3], center[3] = {0.0f, 0.0f, 0.0f}; float mat[4][4], imat[4][4]; - fac = cmd->fac; - facm = 1.0f - fac; - flag = cmd->flag; type = cmd->type; /* projection type: sphere or cylinder */ @@ -193,67 +193,6 @@ static void sphere_do( if (len == 0.0f) len = 10.0f; } - /* ready to apply the effect, one vertex at a time; - * tiny optimization: the code is separated (with parts repeated) - * in two possible cases: - * with or w/o a vgroup. With lots of if's in the code below, - * further optimization's are possible, if needed */ - if (dvert) { /* with a vgroup */ - MDeformVert *dv = dvert; - float fac_orig = fac; - for (i = 0; i < numVerts; i++, dv++) { - float tmp_co[3]; - float weight; - - copy_v3_v3(tmp_co, vertexCos[i]); - if (ctrl_ob) { - if (flag & MOD_CAST_USE_OB_TRANSFORM) { - mul_m4_v3(mat, tmp_co); - } - else { - sub_v3_v3(tmp_co, center); - } - } - - copy_v3_v3(vec, tmp_co); - - if (type == MOD_CAST_TYPE_CYLINDER) - vec[2] = 0.0f; - - if (has_radius) { - if (len_v3(vec) > cmd->radius) continue; - } - - weight = defvert_find_weight(dv, defgrp_index); - if (weight <= 0.0f) continue; - - fac = fac_orig * weight; - facm = 1.0f - fac; - - normalize_v3(vec); - - if (flag & MOD_CAST_X) - tmp_co[0] = fac * vec[0] * len + facm * tmp_co[0]; - if (flag & MOD_CAST_Y) - tmp_co[1] = fac * vec[1] * len + facm * tmp_co[1]; - if (flag & MOD_CAST_Z) - tmp_co[2] = fac * vec[2] * len + facm * tmp_co[2]; - - if (ctrl_ob) { - if (flag & MOD_CAST_USE_OB_TRANSFORM) { - mul_m4_v3(imat, tmp_co); - } - else { - add_v3_v3(tmp_co, center); - } - } - - copy_v3_v3(vertexCos[i], tmp_co); - } - return; - } - - /* no vgroup */ for (i = 0; i < numVerts; i++) { float tmp_co[3]; @@ -276,6 +215,16 @@ static void sphere_do( if (len_v3(vec) > cmd->radius) continue; } + if (dvert) { + const float weight = defvert_find_weight(&dvert[i], defgrp_index); + if (weight == 0.0f) { + continue; + } + + fac = fac_orig * weight; + facm = 1.0f - fac; + } + normalize_v3(vec); if (flag & MOD_CAST_X) @@ -308,14 +257,13 @@ static void cuboid_do( int i, defgrp_index; int has_radius = 0; short flag; - float fac, facm; + float fac = cmd->fac; + float facm = 1.0f - fac; + const float fac_orig = fac; float min[3], max[3], bb[8][3]; float center[3] = {0.0f, 0.0f, 0.0f}; float mat[4][4], imat[4][4]; - fac = cmd->fac; - facm = 1.0f - fac; - flag = cmd->flag; ctrl_ob = cmd->object; @@ -397,118 +345,10 @@ static void cuboid_do( bb[0][2] = bb[1][2] = bb[2][2] = bb[3][2] = min[2]; bb[4][2] = bb[5][2] = bb[6][2] = bb[7][2] = max[2]; - /* ready to apply the effect, one vertex at a time; - * tiny optimization: the code is separated (with parts repeated) - * in two possible cases: - * with or w/o a vgroup. With lots of if's in the code below, - * further optimization's are possible, if needed */ - if (dvert) { /* with a vgroup */ - float fac_orig = fac; - for (i = 0; i < numVerts; i++) { - MDeformWeight *dw = NULL; - int j, octant, coord; - float d[3], dmax, apex[3], fbb; - float tmp_co[3]; - - copy_v3_v3(tmp_co, vertexCos[i]); - if (ctrl_ob) { - if (flag & MOD_CAST_USE_OB_TRANSFORM) { - mul_m4_v3(mat, tmp_co); - } - else { - sub_v3_v3(tmp_co, center); - } - } - - if (has_radius) { - if (fabsf(tmp_co[0]) > cmd->radius || - fabsf(tmp_co[1]) > cmd->radius || - fabsf(tmp_co[2]) > cmd->radius) - { - continue; - } - } - - for (j = 0; j < dvert[i].totweight; ++j) { - if (dvert[i].dw[j].def_nr == defgrp_index) { - dw = &dvert[i].dw[j]; - break; - } - } - if (!dw) continue; - - fac = fac_orig * dw->weight; - facm = 1.0f - fac; - - /* The algo used to project the vertices to their - * bounding box (bb) is pretty simple: - * for each vertex v: - * 1) find in which octant v is in; - * 2) find which outer "wall" of that octant is closer to v; - * 3) calculate factor (var fbb) to project v to that wall; - * 4) project. */ - - /* find in which octant this vertex is in */ - octant = 0; - if (tmp_co[0] > 0.0f) octant += 1; - if (tmp_co[1] > 0.0f) octant += 2; - if (tmp_co[2] > 0.0f) octant += 4; - - /* apex is the bb's vertex at the chosen octant */ - copy_v3_v3(apex, bb[octant]); - - /* find which bb plane is closest to this vertex ... */ - d[0] = tmp_co[0] / apex[0]; - d[1] = tmp_co[1] / apex[1]; - d[2] = tmp_co[2] / apex[2]; - - /* ... (the closest has the higher (closer to 1) d value) */ - dmax = d[0]; - coord = 0; - if (d[1] > dmax) { - dmax = d[1]; - coord = 1; - } - if (d[2] > dmax) { - /* dmax = d[2]; */ /* commented, we don't need it */ - coord = 2; - } - - /* ok, now we know which coordinate of the vertex to use */ - - if (fabsf(tmp_co[coord]) < FLT_EPSILON) /* avoid division by zero */ - continue; - - /* finally, this is the factor we wanted, to project the vertex - * to its bounding box (bb) */ - fbb = apex[coord] / tmp_co[coord]; - - /* calculate the new vertex position */ - if (flag & MOD_CAST_X) - tmp_co[0] = facm * tmp_co[0] + fac * tmp_co[0] * fbb; - if (flag & MOD_CAST_Y) - tmp_co[1] = facm * tmp_co[1] + fac * tmp_co[1] * fbb; - if (flag & MOD_CAST_Z) - tmp_co[2] = facm * tmp_co[2] + fac * tmp_co[2] * fbb; - - if (ctrl_ob) { - if (flag & MOD_CAST_USE_OB_TRANSFORM) { - mul_m4_v3(imat, tmp_co); - } - else { - add_v3_v3(tmp_co, center); - } - } - - copy_v3_v3(vertexCos[i], tmp_co); - } - return; - } - - /* no vgroup (check previous case for comments about the code) */ + /* ready to apply the effect, one vertex at a time */ for (i = 0; i < numVerts; i++) { int octant, coord; - float d[3], dmax, fbb, apex[3]; + float d[3], dmax, apex[3], fbb; float tmp_co[3]; copy_v3_v3(tmp_co, vertexCos[i]); @@ -530,17 +370,39 @@ static void cuboid_do( } } + if (dvert) { + const float weight = defvert_find_weight(&dvert[i], defgrp_index); + if (weight == 0.0f) { + continue; + } + + fac = fac_orig * weight; + facm = 1.0f - fac; + } + + /* The algo used to project the vertices to their + * bounding box (bb) is pretty simple: + * for each vertex v: + * 1) find in which octant v is in; + * 2) find which outer "wall" of that octant is closer to v; + * 3) calculate factor (var fbb) to project v to that wall; + * 4) project. */ + + /* find in which octant this vertex is in */ octant = 0; if (tmp_co[0] > 0.0f) octant += 1; if (tmp_co[1] > 0.0f) octant += 2; if (tmp_co[2] > 0.0f) octant += 4; + /* apex is the bb's vertex at the chosen octant */ copy_v3_v3(apex, bb[octant]); + /* find which bb plane is closest to this vertex ... */ d[0] = tmp_co[0] / apex[0]; d[1] = tmp_co[1] / apex[1]; d[2] = tmp_co[2] / apex[2]; + /* ... (the closest has the higher (closer to 1) d value) */ dmax = d[0]; coord = 0; if (d[1] > dmax) { @@ -552,11 +414,16 @@ static void cuboid_do( coord = 2; } - if (fabsf(tmp_co[coord]) < FLT_EPSILON) + /* ok, now we know which coordinate of the vertex to use */ + + if (fabsf(tmp_co[coord]) < FLT_EPSILON) /* avoid division by zero */ continue; + /* finally, this is the factor we wanted, to project the vertex + * to its bounding box (bb) */ fbb = apex[coord] / tmp_co[coord]; + /* calculate the new vertex position */ if (flag & MOD_CAST_X) tmp_co[0] = facm * tmp_co[0] + fac * tmp_co[0] * fbb; if (flag & MOD_CAST_Y) diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 5e702a4eabc..be7bfae22fb 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -251,7 +251,7 @@ static void meshcache_do( /* -------------------------------------------------------------------- */ /* Apply the transformation matrix (if needed) */ if (UNLIKELY(err_str)) { - modifier_setError(&mcmd->modifier, err_str); + modifier_setError(&mcmd->modifier, "%s", err_str); } else if (ok) { bool use_matrix = false; diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 038fb4913ec..a198eaf8ca9 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -29,6 +29,7 @@ * \ingroup modifiers */ +#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "MEM_guardedalloc.h" @@ -598,6 +599,10 @@ static DerivedMesh *applyModifier( int *orig_ed; int j; + if (crease_rim || crease_outer || crease_inner) { + result->cd_flag |= ME_CDFLAG_EDGE_CREASE; + } + /* add faces & edges */ origindex_edge = result->getEdgeDataArray(result, CD_ORIGINDEX); ed = &medge[numEdges * 2]; diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index c0d46b14aa8..c48682b877e 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -107,6 +107,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, subsurf_flags |= SUBSURF_IN_EDIT_MODE; result = subsurf_make_derived_from_derived(derivedData, smd, NULL, subsurf_flags); + result->cd_flag = derivedData->cd_flag; if (useRenderParams || !isFinalCalc) { DerivedMesh *cddm = CDDM_copy(result); diff --git a/source/blender/nodes/intern/node_exec.c b/source/blender/nodes/intern/node_exec.c index 7a8b8c940c9..8cf7cc7a1ea 100644 --- a/source/blender/nodes/intern/node_exec.c +++ b/source/blender/nodes/intern/node_exec.c @@ -248,8 +248,9 @@ void ntree_exec_end(bNodeTreeExec *exec) MEM_freeN(exec->stack); for (n=0, nodeexec= exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) { - if (nodeexec->node->typeinfo->freeexecfunc) - nodeexec->node->typeinfo->freeexecfunc(nodeexec->node, nodeexec->data); + if (nodeexec->node->typeinfo) + if (nodeexec->node->typeinfo->freeexecfunc) + nodeexec->node->typeinfo->freeexecfunc(nodeexec->node, nodeexec->data); } if (exec->nodeexec) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 19ec35ae357..cef4e23242d 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -4257,11 +4257,12 @@ static PyObject *pyrna_prop_collection_find(BPy_PropertyRNA *self, PyObject *key return PyLong_FromLong(index); } -static void foreach_attr_type(BPy_PropertyRNA *self, const char *attr, +static bool foreach_attr_type(BPy_PropertyRNA *self, const char *attr, /* values to assign */ RawPropertyType *raw_type, int *attr_tot, bool *attr_signed) { PropertyRNA *prop; + bool attr_ok = true; *raw_type = PROP_RAW_UNSET; *attr_tot = 0; *attr_signed = false; @@ -4270,12 +4271,19 @@ static void foreach_attr_type(BPy_PropertyRNA *self, const char *attr, RNA_PROP_BEGIN (&self->ptr, itemptr, self->prop) { prop = RNA_struct_find_property(&itemptr, attr); - *raw_type = RNA_property_raw_type(prop); - *attr_tot = RNA_property_array_length(&itemptr, prop); - *attr_signed = (RNA_property_subtype(prop) == PROP_UNSIGNED) ? false : true; + if (prop) { + *raw_type = RNA_property_raw_type(prop); + *attr_tot = RNA_property_array_length(&itemptr, prop); + *attr_signed = (RNA_property_subtype(prop) != PROP_UNSIGNED); + } + else { + attr_ok = false; + } break; } RNA_PROP_END; + + return attr_ok; } /* pyrna_prop_collection_foreach_get/set both use this */ @@ -4295,15 +4303,26 @@ static int foreach_parse_args(BPy_PropertyRNA *self, PyObject *args, *attr_signed = false; *raw_type = PROP_RAW_UNSET; - if (!PyArg_ParseTuple(args, "sO", attr, seq) || (!PySequence_Check(*seq) && PyObject_CheckBuffer(*seq))) { - PyErr_SetString(PyExc_TypeError, "foreach_get(attr, sequence) expects a string and a sequence"); + if (!PyArg_ParseTuple(args, "sO:foreach_get/set", attr, seq)) { + return -1; + } + + if (!PySequence_Check(*seq) && PyObject_CheckBuffer(*seq)) { + PyErr_Format(PyExc_TypeError, + "foreach_get/set expected second argument to be a sequence or buffer, not a %.200s", + Py_TYPE(seq)->tp_name); return -1; } *tot = PySequence_Size(*seq); /* TODO - buffer may not be a sequence! array.array() is tho. */ if (*tot > 0) { - foreach_attr_type(self, *attr, raw_type, attr_tot, attr_signed); + if (!foreach_attr_type(self, *attr, raw_type, attr_tot, attr_signed)) { + PyErr_Format(PyExc_AttributeError, + "foreach_get/set '%.200s.%200s[...]' elements have no attribute '%.200s'", + RNA_struct_identifier(self->ptr.type), RNA_property_identifier(self->prop), *attr); + return -1; + } *size = RNA_raw_type_sizeof(*raw_type); #if 0 /* works fine but not strictly needed, we could allow RNA_property_collection_raw_* to do the checks */ diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index a19f8e2d8ed..b61ed55da06 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -156,7 +156,8 @@ static int pyrna_struct_keyframe_parse( /* note, parse_str MUST start with 's|ifsO!' */ if (!PyArg_ParseTupleAndKeywords(args, kw, parse_str, (char **)kwlist, &path, index, cfra, group_name, - &PySet_Type, &pyoptions)) { + &PySet_Type, &pyoptions)) + { return -1; } diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index effc564fdc9..12d36ccfdd2 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -166,7 +166,8 @@ if(WITH_GAMEENGINE) endif() if(APPLE) - if(CMAKE_OSX_ARCHITECTURES MATCHES "i386" OR CMAKE_OSX_ARCHITECTURES MATCHES "x86_64") + # SSE math is enabled by default on x86_64 + if(CMAKE_OSX_ARCHITECTURES MATCHES "i386") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mfpmath=sse") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mfpmath=sse") endif() diff --git a/source/blender/render/intern/raytrace/reorganize.h b/source/blender/render/intern/raytrace/reorganize.h index 9d9711eee56..5624df25267 100644 --- a/source/blender/render/intern/raytrace/reorganize.h +++ b/source/blender/render/intern/raytrace/reorganize.h @@ -80,7 +80,6 @@ void reorganize_find_fittest_parent(Node *tree, Node *node, std::pair<float, Nod } } -static int tot_moves = 0; template<class Node> void reorganize(Node *root) { @@ -109,8 +108,6 @@ void reorganize(Node *root) tmp->sibling = best.second->child; best.second->child = tmp; - - tot_moves++; } diff --git a/source/blender/render/intern/source/occlusion.c b/source/blender/render/intern/source/occlusion.c index 747362a7974..1c594ef7db2 100644 --- a/source/blender/render/intern/source/occlusion.c +++ b/source/blender/render/intern/source/occlusion.c @@ -763,414 +763,6 @@ static float occ_solid_angle(OccNode *node, const float v[3], float d2, float in return ((node->area * dotemit * dotreceive) / (d2 + node->area * INVPI)) * INVPI; } -static void vec_add_dir(float r[3], const float v1[3], const float v2[3], const float fac) -{ - r[0] = v1[0] + fac * (v2[0] - v1[0]); - r[1] = v1[1] + fac * (v2[1] - v1[1]); - r[2] = v1[2] + fac * (v2[2] - v1[2]); -} - -/* TODO: exact duplicate of ff_visible_quad() in math_geom.c - * why not de-duplicate? (also above helper func) */ -static int occ_visible_quad(const float p[3], const float n[3], - const float v0[3], const float v1[3], const float v2[3], - float q0[3], float q1[3], float q2[3], float q3[3]) -{ - static const float epsilon = 1e-6f; - float c, sd[3]; - - c = dot_v3v3(n, p); - - /* signed distances from the vertices to the plane. */ - sd[0] = dot_v3v3(n, v0) - c; - sd[1] = dot_v3v3(n, v1) - c; - sd[2] = dot_v3v3(n, v2) - c; - - if (fabsf(sd[0]) < epsilon) sd[0] = 0.0f; - if (fabsf(sd[1]) < epsilon) sd[1] = 0.0f; - if (fabsf(sd[2]) < epsilon) sd[2] = 0.0f; - - if (sd[0] > 0) { - if (sd[1] > 0) { - if (sd[2] > 0) { - /* +++ */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - else if (sd[2] < 0) { - /* ++- */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - vec_add_dir(q2, v1, v2, (sd[1] / (sd[1] - sd[2]))); - vec_add_dir(q3, v0, v2, (sd[0] / (sd[0] - sd[2]))); - } - else { - /* ++0 */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - } - else if (sd[1] < 0) { - if (sd[2] > 0) { - /* +-+ */ - copy_v3_v3(q0, v0); - vec_add_dir(q1, v0, v1, (sd[0] / (sd[0] - sd[1]))); - vec_add_dir(q2, v1, v2, (sd[1] / (sd[1] - sd[2]))); - copy_v3_v3(q3, v2); - } - else if (sd[2] < 0) { - /* +-- */ - copy_v3_v3(q0, v0); - vec_add_dir(q1, v0, v1, (sd[0] / (sd[0] - sd[1]))); - vec_add_dir(q2, v0, v2, (sd[0] / (sd[0] - sd[2]))); - copy_v3_v3(q3, q2); - } - else { - /* +-0 */ - copy_v3_v3(q0, v0); - vec_add_dir(q1, v0, v1, (sd[0] / (sd[0] - sd[1]))); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - } - else { - if (sd[2] > 0) { - /* +0+ */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - else if (sd[2] < 0) { - /* +0- */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - vec_add_dir(q2, v0, v2, (sd[0] / (sd[0] - sd[2]))); - copy_v3_v3(q3, q2); - } - else { - /* +00 */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - } - } - else if (sd[0] < 0) { - if (sd[1] > 0) { - if (sd[2] > 0) { - /* -++ */ - vec_add_dir(q0, v0, v1, (sd[0] / (sd[0] - sd[1]))); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - vec_add_dir(q3, v0, v2, (sd[0] / (sd[0] - sd[2]))); - } - else if (sd[2] < 0) { - /* -+- */ - vec_add_dir(q0, v0, v1, (sd[0] / (sd[0] - sd[1]))); - copy_v3_v3(q1, v1); - vec_add_dir(q2, v1, v2, (sd[1] / (sd[1] - sd[2]))); - copy_v3_v3(q3, q2); - } - else { - /* -+0 */ - vec_add_dir(q0, v0, v1, (sd[0] / (sd[0] - sd[1]))); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - } - else if (sd[1] < 0) { - if (sd[2] > 0) { - /* --+ */ - vec_add_dir(q0, v0, v2, (sd[0] / (sd[0] - sd[2]))); - vec_add_dir(q1, v1, v2, (sd[1] / (sd[1] - sd[2]))); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - else if (sd[2] < 0) { - /* --- */ - return 0; - } - else { - /* --0 */ - return 0; - } - } - else { - if (sd[2] > 0) { - /* -0+ */ - vec_add_dir(q0, v0, v2, (sd[0] / (sd[0] - sd[2]))); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - else if (sd[2] < 0) { - /* -0- */ - return 0; - } - else { - /* -00 */ - return 0; - } - } - } - else { - if (sd[1] > 0) { - if (sd[2] > 0) { - /* 0++ */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - else if (sd[2] < 0) { - /* 0+- */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - vec_add_dir(q2, v1, v2, (sd[1] / (sd[1] - sd[2]))); - copy_v3_v3(q3, q2); - } - else { - /* 0+0 */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - } - else if (sd[1] < 0) { - if (sd[2] > 0) { - /* 0-+ */ - copy_v3_v3(q0, v0); - vec_add_dir(q1, v1, v2, (sd[1] / (sd[1] - sd[2]))); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - else if (sd[2] < 0) { - /* 0-- */ - return 0; - } - else { - /* 0-0 */ - return 0; - } - } - else { - if (sd[2] > 0) { - /* 00+ */ - copy_v3_v3(q0, v0); - copy_v3_v3(q1, v1); - copy_v3_v3(q2, v2); - copy_v3_v3(q3, q2); - } - else if (sd[2] < 0) { - /* 00- */ - return 0; - } - else { - /* 000 */ - return 0; - } - } - } - - return 1; -} - -/* altivec optimization, this works, but is unused */ - -#if 0 -#include <Accelerate/Accelerate.h> - -typedef union { - vFloat v; - float f[4]; -} vFloatResult; - -static vFloat vec_splat_float(float val) -{ - return (vFloat) {val, val, val, val}; -} - -static float occ_quad_form_factor(float *p, float *n, float *q0, float *q1, float *q2, float *q3) -{ - vFloat vcos, rlen, vrx, vry, vrz, vsrx, vsry, vsrz, gx, gy, gz, vangle; - vUInt8 rotate = (vUInt8) {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3}; - vFloatResult vresult; - float result; - - /* compute r* */ - vrx = (vFloat) {q0[0], q1[0], q2[0], q3[0]} -vec_splat_float(p[0]); - vry = (vFloat) {q0[1], q1[1], q2[1], q3[1]} -vec_splat_float(p[1]); - vrz = (vFloat) {q0[2], q1[2], q2[2], q3[2]} -vec_splat_float(p[2]); - - /* normalize r* */ - rlen = vec_rsqrte(vrx * vrx + vry * vry + vrz * vrz + vec_splat_float(1e-16f)); - vrx = vrx * rlen; - vry = vry * rlen; - vrz = vrz * rlen; - - /* rotate r* for cross and dot */ - vsrx = vec_perm(vrx, vrx, rotate); - vsry = vec_perm(vry, vry, rotate); - vsrz = vec_perm(vrz, vrz, rotate); - - /* cross product */ - gx = vsry * vrz - vsrz * vry; - gy = vsrz * vrx - vsrx * vrz; - gz = vsrx * vry - vsry * vrx; - - /* normalize */ - rlen = vec_rsqrte(gx * gx + gy * gy + gz * gz + vec_splat_float(1e-16f)); - gx = gx * rlen; - gy = gy * rlen; - gz = gz * rlen; - - /* angle */ - vcos = vrx * vsrx + vry * vsry + vrz * vsrz; - vcos = vec_max(vec_min(vcos, vec_splat_float(1.0f)), vec_splat_float(-1.0f)); - vangle = vacosf(vcos); - - /* dot */ - vresult.v = (vec_splat_float(n[0]) * gx + - vec_splat_float(n[1]) * gy + - vec_splat_float(n[2]) * gz) * vangle; - - result = (vresult.f[0] + vresult.f[1] + vresult.f[2] + vresult.f[3]) * (0.5f / (float)M_PI); - result = MAX2(result, 0.0f); - - return result; -} - -#endif - -/* SSE optimization, acos code doesn't work */ - -#if 0 - -#include <xmmintrin.h> - -static __m128 sse_approx_acos(__m128 x) -{ - /* needs a better approximation than taylor expansion of acos, since that - * gives big erros for near 1.0 values, sqrt(2*x)*acos(1-x) should work - * better, see http://www.tom.womack.net/projects/sse-fast-arctrig.html */ - - return _mm_set_ps1(1.0f); -} - -static float occ_quad_form_factor(float *p, float *n, float *q0, float *q1, float *q2, float *q3) -{ - float r0[3], r1[3], r2[3], r3[3], g0[3], g1[3], g2[3], g3[3]; - float a1, a2, a3, a4, dot1, dot2, dot3, dot4, result; - float fresult[4] __attribute__((aligned(16))); - __m128 qx, qy, qz, rx, ry, rz, rlen, srx, sry, srz, gx, gy, gz, glen, rcos, angle, aresult; - - /* compute r */ - qx = _mm_set_ps(q3[0], q2[0], q1[0], q0[0]); - qy = _mm_set_ps(q3[1], q2[1], q1[1], q0[1]); - qz = _mm_set_ps(q3[2], q2[2], q1[2], q0[2]); - - rx = qx - _mm_set_ps1(p[0]); - ry = qy - _mm_set_ps1(p[1]); - rz = qz - _mm_set_ps1(p[2]); - - /* normalize r */ - rlen = _mm_rsqrt_ps(rx * rx + ry * ry + rz * rz + _mm_set_ps1(1e-16f)); - rx = rx * rlen; - ry = ry * rlen; - rz = rz * rlen; - - /* cross product */ - srx = _mm_shuffle_ps(rx, rx, _MM_SHUFFLE(0, 3, 2, 1)); - sry = _mm_shuffle_ps(ry, ry, _MM_SHUFFLE(0, 3, 2, 1)); - srz = _mm_shuffle_ps(rz, rz, _MM_SHUFFLE(0, 3, 2, 1)); - - gx = sry * rz - srz * ry; - gy = srz * rx - srx * rz; - gz = srx * ry - sry * rx; - - /* normalize g */ - glen = _mm_rsqrt_ps(gx * gx + gy * gy + gz * gz + _mm_set_ps1(1e-16f)); - gx = gx * glen; - gy = gy * glen; - gz = gz * glen; - - /* compute angle */ - rcos = rx * srx + ry * sry + rz * srz; - rcos = _mm_max_ps(_mm_min_ps(rcos, _mm_set_ps1(1.0f)), _mm_set_ps1(-1.0f)); - - angle = sse_approx_cos(rcos); - aresult = (_mm_set_ps1(n[0]) * gx + _mm_set_ps1(n[1]) * gy + _mm_set_ps1(n[2]) * gz) * angle; - - /* sum together */ - result = (fresult[0] + fresult[1] + fresult[2] + fresult[3]) * (0.5f / (float)M_PI); - result = MAX2(result, 0.0f); - - return result; -} - -#endif - -static void normalizef(float *n) -{ - float d; - - d = dot_v3v3(n, n); - - if (d > 1.0e-35F) { - d = 1.0f / sqrtf(d); - - n[0] *= d; - n[1] *= d; - n[2] *= d; - } -} - -/* TODO: exact duplicate of ff_quad_form_factor() in math_geom.c - * why not de-duplicate? (also above helper func) */ -static float occ_quad_form_factor(const float p[3], const float n[3], const float q0[3], const float q1[3], const float q2[3], const float q3[3]) -{ - float r0[3], r1[3], r2[3], r3[3], g0[3], g1[3], g2[3], g3[3]; - float a1, a2, a3, a4, dot1, dot2, dot3, dot4, result; - - sub_v3_v3v3(r0, q0, p); - sub_v3_v3v3(r1, q1, p); - sub_v3_v3v3(r2, q2, p); - sub_v3_v3v3(r3, q3, p); - - normalizef(r0); - normalizef(r1); - normalizef(r2); - normalizef(r3); - - cross_v3_v3v3(g0, r1, r0); normalizef(g0); - cross_v3_v3v3(g1, r2, r1); normalizef(g1); - cross_v3_v3v3(g2, r3, r2); normalizef(g2); - cross_v3_v3v3(g3, r0, r3); normalizef(g3); - - a1 = saacosf(dot_v3v3(r0, r1)); - a2 = saacosf(dot_v3v3(r1, r2)); - a3 = saacosf(dot_v3v3(r2, r3)); - a4 = saacosf(dot_v3v3(r3, r0)); - - dot1 = dot_v3v3(n, g0); - dot2 = dot_v3v3(n, g1); - dot3 = dot_v3v3(n, g2); - dot4 = dot_v3v3(n, g3); - - result = (a1 * dot1 + a2 * dot2 + a3 * dot3 + a4 * dot4) * 0.5f / (float)M_PI; - result = MAX2(result, 0.0f); - - return result; -} - static float occ_form_factor(OccFace *face, float *p, float *n) { ObjectInstanceRen *obi; @@ -1190,16 +782,16 @@ static float occ_form_factor(OccFace *face, float *p, float *n) mul_m4_v3(obi->mat, v3); } - if (occ_visible_quad(p, n, v1, v2, v3, q0, q1, q2, q3)) - contrib += occ_quad_form_factor(p, n, q0, q1, q2, q3); + if (form_factor_visible_quad(p, n, v1, v2, v3, q0, q1, q2, q3)) + contrib += form_factor_quad(p, n, q0, q1, q2, q3); if (vlr->v4) { copy_v3_v3(v4, vlr->v4->co); if (obi->flag & R_TRANSFORMED) mul_m4_v3(obi->mat, v4); - if (occ_visible_quad(p, n, v1, v3, v4, q0, q1, q2, q3)) - contrib += occ_quad_form_factor(p, n, q0, q1, q2, q3); + if (form_factor_visible_quad(p, n, v1, v3, v4, q0, q1, q2, q3)) + contrib += form_factor_quad(p, n, q0, q1, q2, q3); } return contrib; diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 076bdaeaea7..ce18723b8c0 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1457,7 +1457,7 @@ static void tag_scenes_for_render(Render *re) Scene *scene = (Scene*) node->id; if (scene->r.alphamode != R_ALPHAPREMUL) { - BKE_reportf(re->reports, RPT_WARNING, "Setting scene %s alpha mode to Premul\n", scene->id.name + 2); + BKE_reportf(re->reports, RPT_WARNING, "Setting scene %s alpha mode to Premul", scene->id.name + 2); /* also print, so feedback is immediate */ printf("2.66 versioning fix: setting scene %s alpha mode to Premul\n", scene->id.name + 2); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index a1437b70090..57fe518dd46 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -308,7 +308,7 @@ void WM_gestures_remove(struct bContext *C); void WM_event_add_fileselect(struct bContext *C, struct wmOperator *op); void WM_event_fileselect_event(struct bContext *C, void *ophandle, int eventval); #ifndef NDEBUG -void WM_event_print(struct wmEvent *event); +void WM_event_print(const struct wmEvent *event); #endif void WM_operator_region_active_win_set(struct bContext *C); diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 6044e3fb771..173a8237c02 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -796,15 +796,18 @@ void wm_draw_update(bContext *C) GPU_free_unused_buffers(); for (win = wm->windows.first; win; win = win->next) { - int state = GHOST_GetWindowState(win->ghostwin); - - if (state == GHOST_kWindowStateMinimized) { - /* do not update minimized windows, it gives issues on intel drivers (see [#33223]) - * anyway, it seems logical to skip update for invisible windows - */ - continue; +#ifdef WIN32 + if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { + GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin); + + if (state == GHOST_kWindowStateMinimized) { + /* do not update minimized windows, it gives issues on intel drivers (see [#33223]) + * anyway, it seems logical to skip update for invisible windows + */ + continue; + } } - +#endif if (win->drawmethod != U.wmdrawmethod) { wm_draw_window_clear(win); win->drawmethod = U.wmdrawmethod; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 206297d2c95..4e181ec930b 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -472,7 +472,7 @@ void WM_operator_region_active_win_set(bContext *C) /* for debugging only, getting inspecting events manually is tedious */ #ifndef NDEBUG -void WM_event_print(wmEvent *event) +void WM_event_print(const wmEvent *event) { if (event) { const char *unknown = "UNKNOWN"; diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 79257479529..678c77d0d88 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -110,6 +110,8 @@ static GHash *global_ops_hash = NULL; +#define UNDOCUMENTED_OPERATOR_TIP N_("(undocumented operator)") + /* ************ operator API, exported ********** */ @@ -163,7 +165,7 @@ void WM_operatortype_append(void (*opfunc)(wmOperatorType *)) } /* XXX All ops should have a description but for now allow them not to. */ - RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : N_("(undocumented operator)")); + RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); RNA_def_struct_identifier(ot->srna, ot->idname); BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); @@ -178,7 +180,7 @@ void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void * /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); opfunc(ot, userdata); - RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : N_("(undocumented operator)")); + RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); RNA_def_struct_identifier(ot->srna, ot->idname); BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); @@ -375,7 +377,7 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam ot->poll = NULL; if (!ot->description) /* XXX All ops should have a description but for now allow them not to. */ - ot->description = N_("(undocumented operator)"); + ot->description = UNDOCUMENTED_OPERATOR_TIP; RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); RNA_def_struct_identifier(ot->srna, ot->idname); @@ -401,7 +403,7 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *), ot->poll = NULL; if (!ot->description) - ot->description = N_("(undocumented operator)"); + ot->description = UNDOCUMENTED_OPERATOR_TIP; /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); @@ -1846,6 +1848,7 @@ static void WM_OT_call_menu(wmOperatorType *ot) { ot->name = "Call Menu"; ot->idname = "WM_OT_call_menu"; + ot->description = "Call (draw) a pre-defined menu"; ot->exec = wm_call_menu_exec; ot->poll = WM_operator_winactive; diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index fd44f4a7169..b510956dbc1 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -252,8 +252,15 @@ static void playanim_toscreen(PlayAnimPict *picture, struct ImBuf *ibuf, int fon glRasterPos2f(0.0f, 0.0f); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + fdrawcheckerboard(0.0f, 0.0f, ibuf->x, ibuf->y); + glDrawPixels(ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + glDisable(GL_BLEND); + pupdate_time(); if (picture && (g_WS.qual & (WS_QUAL_SHIFT | WS_QUAL_LMOUSE)) && (fontid != -1)) { |