Manipulator: Add b-bone spline editing
[blender.git] / source / blender / editors / space_view3d / view3d_manipulator_armature.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/space_view3d/view3d_manipulator_armature.c
22  *  \ingroup spview3d
23  */
24
25 #include "BLI_blenlib.h"
26 #include "BLI_math.h"
27 #include "BLI_utildefines.h"
28
29 #include "BKE_armature.h"
30 #include "BKE_action.h"
31 #include "BKE_context.h"
32 #include "BKE_object.h"
33
34 #include "DNA_object_types.h"
35 #include "DNA_armature_types.h"
36
37 #include "ED_armature.h"
38 #include "ED_screen.h"
39 #include "ED_manipulator_library.h"
40
41 #include "UI_resources.h"
42
43 #include "MEM_guardedalloc.h"
44
45 #include "RNA_access.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "view3d_intern.h"  /* own include */
51
52
53 /* -------------------------------------------------------------------- */
54
55 /** \name Armature Spline Manipulator
56  *
57  * \{ */
58
59 /*
60  * TODO(campbell): Current conversion is a approximation (usable not correct),
61  * we'll need to take the next/previous bones into account to get the tangent directions.
62  * First last matrices from 'b_bone_spline_setup' are close but also not quite accurate
63  * since they're not at either end-points on the curve.
64  *
65  * Likely we'll need a function especially to get the first/last orientations.
66  */
67
68 #define BBONE_SCALE_Y 3.0f
69
70 struct BoneSplineHandle {
71         wmManipulator *manipulator;
72         bPoseChannel *pchan;
73         /* We could remove, keep since at the moment for checking the conversion. */
74         float co[3];
75         int index;
76 };
77
78 struct BoneSplineWidgetGroup {
79         struct BoneSplineHandle handles[2];
80 };
81
82 static void manipulator_bbone_offset_get(
83         const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop,
84         void *value_p)
85 {
86         struct BoneSplineHandle *bh = mpr_prop->custom_func.user_data;
87         bPoseChannel *pchan = bh->pchan;
88
89         float *value = value_p;
90         BLI_assert(mpr_prop->type->array_length == 3);
91
92         if (bh->index == 0) {
93                 bh->co[1] = pchan->bone->ease1 / BBONE_SCALE_Y;
94                 bh->co[0] = pchan->curveInX;
95                 bh->co[2] = pchan->curveInY;
96         }
97         else {
98                 bh->co[1] = -pchan->bone->ease2 / BBONE_SCALE_Y;
99                 bh->co[0] = pchan->curveOutX;
100                 bh->co[2] = pchan->curveOutY;
101         }
102         copy_v3_v3(value, bh->co);
103 }
104
105 static void manipulator_bbone_offset_set(
106         const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop,
107         const void *value_p)
108 {
109         struct BoneSplineHandle *bh = mpr_prop->custom_func.user_data;
110         bPoseChannel *pchan = bh->pchan;
111
112         const float *value = value_p;
113
114         BLI_assert(mpr_prop->type->array_length == 3);
115         copy_v3_v3(bh->co, value);
116
117         if (bh->index == 0) {
118                 pchan->bone->ease1 = max_ff(0.0f, bh->co[1] * BBONE_SCALE_Y);
119                 pchan->bone->curveInX = bh->co[0];
120                 pchan->bone->curveInY = bh->co[2];
121         }
122         else {
123                 pchan->bone->ease2 = max_ff(0.0f, -bh->co[1] * BBONE_SCALE_Y);
124                 pchan->bone->curveOutX = bh->co[0];
125                 pchan->bone->curveOutY = bh->co[2];
126         }
127
128 }
129
130 static bool WIDGETGROUP_armature_spline_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
131 {
132         Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
133         if (ob != NULL) {
134                 const bArmature *arm = ob->data;
135                 if (arm->drawtype == ARM_B_BONE) {
136                         if (arm->act_bone && arm->act_bone->segments > 1) {
137                                 return true;
138                         }
139                 }
140         }
141         return false;
142 }
143
144
145 static void WIDGETGROUP_armature_spline_setup(const bContext *C, wmManipulatorGroup *mgroup)
146 {
147         Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
148         bPoseChannel *pchan = BKE_pose_channel_active(ob);
149
150         const wmManipulatorType *wt_grab = WM_manipulatortype_find("MANIPULATOR_WT_grab_3d", true);
151
152         struct BoneSplineWidgetGroup *bspline_group = MEM_callocN(sizeof(struct BoneSplineWidgetGroup), __func__);
153         mgroup->customdata = bspline_group;
154
155         /* Handles */
156         for (int i = 0; i < ARRAY_SIZE(bspline_group->handles); i++) {
157                 wmManipulator *mpr;
158                 mpr = bspline_group->handles[i].manipulator = WM_manipulator_new_ptr(wt_grab, mgroup, NULL);
159                 RNA_enum_set(mpr->ptr, "draw_style", ED_MANIPULATOR_GRAB_STYLE_RING_2D);
160                 RNA_enum_set(mpr->ptr, "draw_options",
161                              ED_MANIPULATOR_GRAB_DRAW_FLAG_FILL | ED_MANIPULATOR_GRAB_DRAW_FLAG_ALIGN_VIEW);
162                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_DRAW_VALUE, true);
163
164                 UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, mpr->color);
165                 UI_GetThemeColor3fv(TH_MANIPULATOR_HI, mpr->color_hi);
166
167                 mpr->scale_basis = 0.06f;
168
169                 if (i == 0) {
170                         copy_v3_v3(mpr->matrix_basis[3], pchan->loc);
171                 }
172         }
173 }
174
175 static void WIDGETGROUP_armature_spline_refresh(const bContext *C, wmManipulatorGroup *mgroup)
176 {
177         Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
178
179         if (!mgroup->customdata)
180                 return;
181
182         struct BoneSplineWidgetGroup *bspline_group = mgroup->customdata;
183         bPoseChannel *pchan = BKE_pose_channel_active(ob);
184
185         /* Handles */
186         for (int i = 0; i < ARRAY_SIZE(bspline_group->handles); i++) {
187                 wmManipulator *mpr = bspline_group->handles[i].manipulator;
188                 bspline_group->handles[i].pchan = pchan;
189                 bspline_group->handles[i].index = i;
190
191                 float mat[4][4];
192                 mul_m4_m4m4(mat, ob->obmat, (i == 0) ? pchan->disp_mat : pchan->disp_tail_mat);
193                 copy_m4_m4(mpr->matrix_space, mat);
194
195                 /* need to set property here for undo. TODO would prefer to do this in _init */
196                 WM_manipulator_target_property_def_func(
197                         mpr, "offset",
198                         &(const struct wmManipulatorPropertyFnParams) {
199                             .value_get_fn = manipulator_bbone_offset_get,
200                             .value_set_fn = manipulator_bbone_offset_set,
201                             .range_get_fn = NULL,
202                             .user_data = &bspline_group->handles[i],
203                         });
204         }
205 }
206
207 void VIEW3D_WGT_armature_spline(wmManipulatorGroupType *wgt)
208 {
209         wgt->name = "Armature Spline Widgets";
210         wgt->idname = "VIEW3D_WGT_armature_spline";
211
212         wgt->flag = (WM_MANIPULATORGROUPTYPE_PERSISTENT |
213                      WM_MANIPULATORGROUPTYPE_3D);
214
215         wgt->poll = WIDGETGROUP_armature_spline_poll;
216         wgt->setup = WIDGETGROUP_armature_spline_setup;
217         wgt->refresh = WIDGETGROUP_armature_spline_refresh;
218 }
219
220 /** \} */