Merge branch 'blender-v2.91-release' into master
[blender.git] / source / blender / editors / object / object_warp.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2013 by Blender Foundation
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edobj
22  */
23
24 #include "DNA_object_types.h"
25 #include "DNA_scene_types.h"
26 #include "DNA_view3d_types.h"
27
28 #include "BLI_math.h"
29
30 #include "BKE_context.h"
31
32 #include "RNA_access.h"
33 #include "RNA_define.h"
34
35 #include "WM_api.h"
36 #include "WM_types.h"
37
38 #include "ED_transverts.h"
39 #include "ED_view3d.h"
40
41 #include "object_intern.h"
42
43 static void object_warp_calc_view_matrix(float r_mat_view[4][4],
44                                          float r_center_view[3],
45                                          Object *obedit,
46                                          const float viewmat[4][4],
47                                          const float center[3],
48                                          const float offset_angle)
49 {
50   float mat_offset[4][4];
51   float viewmat_roll[4][4];
52
53   /* apply the rotation offset by rolling the view */
54   axis_angle_to_mat4_single(mat_offset, 'Z', offset_angle);
55   mul_m4_m4m4(viewmat_roll, mat_offset, viewmat);
56
57   /* apply the view and the object matrix */
58   mul_m4_m4m4(r_mat_view, viewmat_roll, obedit->obmat);
59
60   /* get the view-space cursor */
61   mul_v3_m4v3(r_center_view, viewmat_roll, center);
62 }
63
64 static void object_warp_transverts_minmax_x(TransVertStore *tvs,
65                                             const float mat_view[4][4],
66                                             const float center_view[3],
67                                             float *r_min,
68                                             float *r_max)
69 {
70   /* no need to apply translation and cursor offset for every vertex, delay this */
71   const float x_ofs = (mat_view[3][0] - center_view[0]);
72   float min = FLT_MAX, max = -FLT_MAX;
73
74   TransVert *tv = tvs->transverts;
75   for (int i = 0; i < tvs->transverts_tot; i++, tv++) {
76     float val;
77
78     /* convert objectspace->viewspace */
79     val = dot_m4_v3_row_x(mat_view, tv->loc);
80
81     min = min_ff(min, val);
82     max = max_ff(max, val);
83   }
84
85   *r_min = min + x_ofs;
86   *r_max = max + x_ofs;
87 }
88
89 static void object_warp_transverts(TransVertStore *tvs,
90                                    const float mat_view[4][4],
91                                    const float center_view[3],
92                                    const float angle_,
93                                    const float min,
94                                    const float max)
95 {
96   TransVert *tv;
97   const float angle = -angle_;
98   /* cache vars for tiny speedup */
99 #if 1
100   const float range = max - min;
101   const float range_inv = 1.0f / range;
102   const float min_ofs = min + (0.5f * range);
103 #endif
104
105   float dir_min[2], dir_max[2];
106   float imat_view[4][4];
107
108   invert_m4_m4(imat_view, mat_view);
109
110   /* calculate the direction vectors outside min/max range */
111   {
112     const float phi = angle * 0.5f;
113
114     dir_max[0] = cosf(phi);
115     dir_max[1] = sinf(phi);
116
117     dir_min[0] = -dir_max[0];
118     dir_min[1] = dir_max[1];
119   }
120
121   tv = tvs->transverts;
122   for (int i = 0; i < tvs->transverts_tot; i++, tv++) {
123     float co[3], co_add[2];
124     float val, phi;
125
126     /* convert objectspace->viewspace */
127     mul_v3_m4v3(co, mat_view, tv->loc);
128     sub_v2_v2(co, center_view);
129
130     val = co[0];
131     /* is overwritten later anyway */
132     // co[0] = 0.0f;
133
134     if (val < min) {
135       mul_v2_v2fl(co_add, dir_min, min - val);
136       val = min;
137     }
138     else if (val > max) {
139       mul_v2_v2fl(co_add, dir_max, val - max);
140       val = max;
141     }
142     else {
143       zero_v2(co_add);
144     }
145
146     /* map from x axis to (-0.5 - 0.5) */
147 #if 0
148     val = ((val - min) / (max - min)) - 0.5f;
149 #else
150     val = (val - min_ofs) * range_inv;
151 #endif
152
153     /* convert the x axis into a rotation */
154     phi = val * angle;
155
156     co[0] = -sinf(phi) * co[1];
157     co[1] = cosf(phi) * co[1];
158
159     add_v2_v2(co, co_add);
160
161     /* convert viewspace->objectspace */
162     add_v2_v2(co, center_view);
163     mul_v3_m4v3(tv->loc, imat_view, co);
164   }
165 }
166
167 static int object_warp_verts_exec(bContext *C, wmOperator *op)
168 {
169   const float warp_angle = RNA_float_get(op->ptr, "warp_angle");
170   const float offset_angle = RNA_float_get(op->ptr, "offset_angle");
171
172   TransVertStore tvs = {NULL};
173   Object *obedit = CTX_data_edit_object(C);
174
175   /* typically from 'rv3d' and 3d cursor */
176   float viewmat[4][4];
177   float center[3];
178
179   /* 'viewmat' relative vars */
180   float mat_view[4][4];
181   float center_view[3];
182
183   float min, max;
184
185   ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS | TM_SKIP_HANDLES);
186   if (tvs.transverts == NULL) {
187     return OPERATOR_CANCELLED;
188   }
189
190   /* get viewmatrix */
191   {
192     PropertyRNA *prop_viewmat = RNA_struct_find_property(op->ptr, "viewmat");
193     if (RNA_property_is_set(op->ptr, prop_viewmat)) {
194       RNA_property_float_get_array(op->ptr, prop_viewmat, (float *)viewmat);
195     }
196     else {
197       RegionView3D *rv3d = CTX_wm_region_view3d(C);
198
199       if (rv3d) {
200         copy_m4_m4(viewmat, rv3d->viewmat);
201       }
202       else {
203         unit_m4(viewmat);
204       }
205
206       RNA_property_float_set_array(op->ptr, prop_viewmat, (float *)viewmat);
207     }
208   }
209
210   /* get center */
211   {
212     PropertyRNA *prop_center = RNA_struct_find_property(op->ptr, "center");
213     if (RNA_property_is_set(op->ptr, prop_center)) {
214       RNA_property_float_get_array(op->ptr, prop_center, center);
215     }
216     else {
217       const Scene *scene = CTX_data_scene(C);
218       copy_v3_v3(center, scene->cursor.location);
219
220       RNA_property_float_set_array(op->ptr, prop_center, center);
221     }
222   }
223
224   object_warp_calc_view_matrix(mat_view, center_view, obedit, viewmat, center, offset_angle);
225
226   /* get minmax */
227   {
228     PropertyRNA *prop_min = RNA_struct_find_property(op->ptr, "min");
229     PropertyRNA *prop_max = RNA_struct_find_property(op->ptr, "max");
230
231     if (RNA_property_is_set(op->ptr, prop_min) || RNA_property_is_set(op->ptr, prop_max)) {
232       min = RNA_property_float_get(op->ptr, prop_min);
233       max = RNA_property_float_get(op->ptr, prop_max);
234     }
235     else {
236       /* handy to set the bounds of the mesh */
237       object_warp_transverts_minmax_x(&tvs, mat_view, center_view, &min, &max);
238
239       RNA_property_float_set(op->ptr, prop_min, min);
240       RNA_property_float_set(op->ptr, prop_max, max);
241     }
242
243     if (min > max) {
244       SWAP(float, min, max);
245     }
246   }
247
248   if (min != max) {
249     object_warp_transverts(&tvs, mat_view, center_view, warp_angle, min, max);
250   }
251
252   ED_transverts_update_obedit(&tvs, obedit);
253   ED_transverts_free(&tvs);
254
255   WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obedit);
256
257   return OPERATOR_FINISHED;
258 }
259
260 void TRANSFORM_OT_vertex_warp(struct wmOperatorType *ot)
261 {
262   PropertyRNA *prop;
263
264   /* identifiers */
265   ot->name = "Warp";
266   ot->description = "Warp vertices around the cursor";
267   ot->idname = "TRANSFORM_OT_vertex_warp";
268
269   /* api callbacks */
270   ot->exec = object_warp_verts_exec;
271   ot->poll = ED_transverts_poll;
272
273   /* flags */
274   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
275
276   /* props */
277   prop = RNA_def_float(ot->srna,
278                        "warp_angle",
279                        DEG2RADF(360.0f),
280                        -FLT_MAX,
281                        FLT_MAX,
282                        "Warp Angle",
283                        "Amount to warp about the cursor",
284                        DEG2RADF(-360.0f),
285                        DEG2RADF(360.0f));
286   RNA_def_property_subtype(prop, PROP_ANGLE);
287
288   prop = RNA_def_float(ot->srna,
289                        "offset_angle",
290                        DEG2RADF(0.0f),
291                        -FLT_MAX,
292                        FLT_MAX,
293                        "Offset Angle",
294                        "Angle to use as the basis for warping",
295                        DEG2RADF(-360.0f),
296                        DEG2RADF(360.0f));
297   RNA_def_property_subtype(prop, PROP_ANGLE);
298
299   prop = RNA_def_float(ot->srna, "min", -1.0f, -FLT_MAX, FLT_MAX, "Min", "", -100.0, 100.0);
300   prop = RNA_def_float(ot->srna, "max", 1.0f, -FLT_MAX, FLT_MAX, "Max", "", -100.0, 100.0);
301
302   /* hidden props */
303   prop = RNA_def_float_matrix(
304       ot->srna, "viewmat", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f);
305   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
306
307   prop = RNA_def_float_vector_xyz(
308       ot->srna, "center", 3, NULL, -FLT_MAX, FLT_MAX, "Center", "", -FLT_MAX, FLT_MAX);
309   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
310 }