Blender V4.3
MOD_cast.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_math_matrix.h"
10#include "BLI_math_vector.h"
11#include "BLI_utildefines.h"
12
13#include "BLT_translation.hh"
14
15#include "DNA_defaults.h"
16#include "DNA_meshdata_types.h"
17#include "DNA_object_types.h"
18#include "DNA_screen_types.h"
19
20#include "BKE_deform.hh"
21#include "BKE_lib_query.hh"
22#include "BKE_modifier.hh"
23#include "BKE_object_types.hh"
24
25#include "UI_interface.hh"
26#include "UI_resources.hh"
27
28#include "RNA_access.hh"
29#include "RNA_prototypes.hh"
30
31#include "MOD_ui_common.hh"
32#include "MOD_util.hh"
33
42
43static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
44{
46 short flag;
47
49
50 if ((cmd->fac == 0.0f) || flag == 0) {
51 return true;
52 }
53
54 return false;
55}
56
57static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
58{
60
61 /* Ask for vertex-groups if we need them. */
62 if (cmd->defgrp_name[0] != '\0') {
63 r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
64 }
65}
66
67static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
68{
70
71 walk(user_data, ob, (ID **)&cmd->object, IDWALK_CB_NOP);
72}
73
75{
77 if (cmd->object != nullptr) {
78 DEG_add_object_relation(ctx->node, cmd->object, DEG_OB_COMP_TRANSFORM, "Cast Modifier");
79 DEG_add_depends_on_transform_relation(ctx->node, "Cast Modifier");
80 }
81}
82
83static void sphere_do(CastModifierData *cmd,
84 const ModifierEvalContext * /*ctx*/,
85 Object *ob,
86 Mesh *mesh,
88{
89 const MDeformVert *dvert = nullptr;
90 const bool invert_vgroup = (cmd->flag & MOD_CAST_INVERT_VGROUP) != 0;
91
92 Object *ctrl_ob = nullptr;
93
94 int i, defgrp_index;
95 bool has_radius = false;
96 short flag, type;
97 float len = 0.0f;
98 float fac = cmd->fac;
99 float facm = 1.0f - fac;
100 const float fac_orig = fac;
101 float vec[3], center[3] = {0.0f, 0.0f, 0.0f};
102 float mat[4][4], imat[4][4];
103
104 flag = cmd->flag;
105 type = cmd->type; /* projection type: sphere or cylinder */
106
107 if (type == MOD_CAST_TYPE_CYLINDER) {
108 flag &= ~MOD_CAST_Z;
109 }
110
111 ctrl_ob = cmd->object;
112
113 /* The spheres center is {0, 0, 0} (the ob's own center in its local space),
114 * by default, but if the user defined a control object,
115 * we use its location, transformed to ob's local space. */
116 if (ctrl_ob) {
118 invert_m4_m4(imat, ctrl_ob->object_to_world().ptr());
119 mul_m4_m4m4(mat, imat, ob->object_to_world().ptr());
120 invert_m4_m4(imat, mat);
121 }
122
123 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
124 mul_v3_m4v3(center, ob->world_to_object().ptr(), ctrl_ob->object_to_world().location());
125 }
126
127 /* now we check which options the user wants */
128
129 /* 1) (flag was checked in the "if (ctrl_ob)" block above) */
130 /* 2) cmd->radius > 0.0f: only the vertices within this radius from
131 * the center of the effect should be deformed */
132 if (cmd->radius > FLT_EPSILON) {
133 has_radius = true;
134 }
135
136 /* 3) if we were given a vertex group name,
137 * only those vertices should be affected */
138 if (cmd->defgrp_name[0] != '\0') {
139 MOD_get_vgroup(ob, mesh, cmd->defgrp_name, &dvert, &defgrp_index);
140 }
141
143 len = cmd->radius;
144 }
145 else {
146 len = cmd->size;
147 }
148
149 if (len <= 0) {
150 for (i = 0; i < positions.size(); i++) {
151 len += len_v3v3(center, positions[i]);
152 }
153 len /= positions.size();
154
155 if (len == 0.0f) {
156 len = 10.0f;
157 }
158 }
159
160 for (i = 0; i < positions.size(); i++) {
161 float tmp_co[3];
162
163 copy_v3_v3(tmp_co, positions[i]);
164 if (ctrl_ob) {
166 mul_m4_v3(mat, tmp_co);
167 }
168 else {
169 sub_v3_v3(tmp_co, center);
170 }
171 }
172
173 copy_v3_v3(vec, tmp_co);
174
175 if (type == MOD_CAST_TYPE_CYLINDER) {
176 vec[2] = 0.0f;
177 }
178
179 if (has_radius) {
180 if (len_v3(vec) > cmd->radius) {
181 continue;
182 }
183 }
184
185 if (dvert) {
186 const float weight = invert_vgroup ?
187 1.0f - BKE_defvert_find_weight(&dvert[i], defgrp_index) :
188 BKE_defvert_find_weight(&dvert[i], defgrp_index);
189
190 if (weight == 0.0f) {
191 continue;
192 }
193
194 fac = fac_orig * weight;
195 facm = 1.0f - fac;
196 }
197
198 normalize_v3(vec);
199
200 if (flag & MOD_CAST_X) {
201 tmp_co[0] = fac * vec[0] * len + facm * tmp_co[0];
202 }
203 if (flag & MOD_CAST_Y) {
204 tmp_co[1] = fac * vec[1] * len + facm * tmp_co[1];
205 }
206 if (flag & MOD_CAST_Z) {
207 tmp_co[2] = fac * vec[2] * len + facm * tmp_co[2];
208 }
209
210 if (ctrl_ob) {
212 mul_m4_v3(imat, tmp_co);
213 }
214 else {
215 add_v3_v3(tmp_co, center);
216 }
217 }
218
219 copy_v3_v3(positions[i], tmp_co);
220 }
221}
222
224 const ModifierEvalContext * /*ctx*/,
225 Object *ob,
226 Mesh *mesh,
228{
229 const MDeformVert *dvert = nullptr;
230 int defgrp_index;
231 const bool invert_vgroup = (cmd->flag & MOD_CAST_INVERT_VGROUP) != 0;
232
233 Object *ctrl_ob = nullptr;
234
235 int i;
236 bool has_radius = false;
237 short flag;
238 float fac = cmd->fac;
239 float facm = 1.0f - fac;
240 const float fac_orig = fac;
241 float min[3], max[3], bb[8][3];
242 float center[3] = {0.0f, 0.0f, 0.0f};
243 float mat[4][4], imat[4][4];
244
245 flag = cmd->flag;
246
247 ctrl_ob = cmd->object;
248
249 /* now we check which options the user wants */
250
251 /* 1) (flag was checked in the "if (ctrl_ob)" block above) */
252 /* 2) cmd->radius > 0.0f: only the vertices within this radius from
253 * the center of the effect should be deformed */
254 if (cmd->radius > FLT_EPSILON) {
255 has_radius = true;
256 }
257
258 /* 3) if we were given a vertex group name,
259 * only those vertices should be affected */
260 if (cmd->defgrp_name[0] != '\0') {
261 MOD_get_vgroup(ob, mesh, cmd->defgrp_name, &dvert, &defgrp_index);
262 }
263
264 if (ctrl_ob) {
266 invert_m4_m4(imat, ctrl_ob->object_to_world().ptr());
267 mul_m4_m4m4(mat, imat, ob->object_to_world().ptr());
268 invert_m4_m4(imat, mat);
269 }
270
271 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
272 mul_v3_m4v3(center, ob->world_to_object().ptr(), ctrl_ob->object_to_world().location());
273 }
274
275 if ((flag & MOD_CAST_SIZE_FROM_RADIUS) && has_radius) {
276 for (i = 0; i < 3; i++) {
277 min[i] = -cmd->radius;
278 max[i] = cmd->radius;
279 }
280 }
281 else if (!(flag & MOD_CAST_SIZE_FROM_RADIUS) && cmd->size > 0) {
282 for (i = 0; i < 3; i++) {
283 min[i] = -cmd->size;
284 max[i] = cmd->size;
285 }
286 }
287 else {
288 /* get bound box */
289 /* We can't use the object's bound box because other modifiers
290 * may have changed the vertex data. */
291 INIT_MINMAX(min, max);
292
293 /* Cast's center is the ob's own center in its local space,
294 * by default, but if the user defined a control object, we use
295 * its location, transformed to ob's local space. */
296 if (ctrl_ob) {
297 float vec[3];
298
299 /* let the center of the ctrl_ob be part of the bound box: */
300 minmax_v3v3_v3(min, max, center);
301
302 for (i = 0; i < positions.size(); i++) {
303 sub_v3_v3v3(vec, positions[i], center);
304 minmax_v3v3_v3(min, max, vec);
305 }
306 }
307 else {
308 for (i = 0; i < positions.size(); i++) {
309 minmax_v3v3_v3(min, max, positions[i]);
310 }
311 }
312
313 /* we want a symmetric bound box around the origin */
314 if (fabsf(min[0]) > fabsf(max[0])) {
315 max[0] = fabsf(min[0]);
316 }
317 if (fabsf(min[1]) > fabsf(max[1])) {
318 max[1] = fabsf(min[1]);
319 }
320 if (fabsf(min[2]) > fabsf(max[2])) {
321 max[2] = fabsf(min[2]);
322 }
323 min[0] = -max[0];
324 min[1] = -max[1];
325 min[2] = -max[2];
326 }
327
328 /* building our custom bounding box */
329 bb[0][0] = bb[2][0] = bb[4][0] = bb[6][0] = min[0];
330 bb[1][0] = bb[3][0] = bb[5][0] = bb[7][0] = max[0];
331 bb[0][1] = bb[1][1] = bb[4][1] = bb[5][1] = min[1];
332 bb[2][1] = bb[3][1] = bb[6][1] = bb[7][1] = max[1];
333 bb[0][2] = bb[1][2] = bb[2][2] = bb[3][2] = min[2];
334 bb[4][2] = bb[5][2] = bb[6][2] = bb[7][2] = max[2];
335
336 /* ready to apply the effect, one vertex at a time */
337 for (i = 0; i < positions.size(); i++) {
338 int octant, coord;
339 float d[3], dmax, apex[3], fbb;
340 float tmp_co[3];
341
342 copy_v3_v3(tmp_co, positions[i]);
343 if (ctrl_ob) {
345 mul_m4_v3(mat, tmp_co);
346 }
347 else {
348 sub_v3_v3(tmp_co, center);
349 }
350 }
351
352 if (has_radius) {
353 if (fabsf(tmp_co[0]) > cmd->radius || fabsf(tmp_co[1]) > cmd->radius ||
354 fabsf(tmp_co[2]) > cmd->radius)
355 {
356 continue;
357 }
358 }
359
360 if (dvert) {
361 const float weight = invert_vgroup ?
362 1.0f - BKE_defvert_find_weight(&dvert[i], defgrp_index) :
363 BKE_defvert_find_weight(&dvert[i], defgrp_index);
364
365 if (weight == 0.0f) {
366 continue;
367 }
368
369 fac = fac_orig * weight;
370 facm = 1.0f - fac;
371 }
372
373 /* The algorithm used to project the vertices to their
374 * bounding box (bb) is pretty simple:
375 * for each vertex v:
376 * 1) find in which octant v is in;
377 * 2) find which outer "wall" of that octant is closer to v;
378 * 3) calculate factor (var fbb) to project v to that wall;
379 * 4) project. */
380
381 /* find in which octant this vertex is in */
382 octant = 0;
383 if (tmp_co[0] > 0.0f) {
384 octant += 1;
385 }
386 if (tmp_co[1] > 0.0f) {
387 octant += 2;
388 }
389 if (tmp_co[2] > 0.0f) {
390 octant += 4;
391 }
392
393 /* apex is the bb's vertex at the chosen octant */
394 copy_v3_v3(apex, bb[octant]);
395
396 /* find which bb plane is closest to this vertex ... */
397 d[0] = tmp_co[0] / apex[0];
398 d[1] = tmp_co[1] / apex[1];
399 d[2] = tmp_co[2] / apex[2];
400
401 /* ... (the closest has the higher (closer to 1) d value) */
402 dmax = d[0];
403 coord = 0;
404 if (d[1] > dmax) {
405 dmax = d[1];
406 coord = 1;
407 }
408 if (d[2] > dmax) {
409 // dmax = d[2]; /* commented, we don't need it */
410 coord = 2;
411 }
412
413 /* ok, now we know which coordinate of the vertex to use */
414
415 if (fabsf(tmp_co[coord]) < FLT_EPSILON) { /* avoid division by zero */
416 continue;
417 }
418
419 /* finally, this is the factor we wanted, to project the vertex
420 * to its bounding box (bb) */
421 fbb = apex[coord] / tmp_co[coord];
422
423 /* calculate the new vertex position */
424 if (flag & MOD_CAST_X) {
425 tmp_co[0] = facm * tmp_co[0] + fac * tmp_co[0] * fbb;
426 }
427 if (flag & MOD_CAST_Y) {
428 tmp_co[1] = facm * tmp_co[1] + fac * tmp_co[1] * fbb;
429 }
430 if (flag & MOD_CAST_Z) {
431 tmp_co[2] = facm * tmp_co[2] + fac * tmp_co[2] * fbb;
432 }
433
434 if (ctrl_ob) {
436 mul_m4_v3(imat, tmp_co);
437 }
438 else {
439 add_v3_v3(tmp_co, center);
440 }
441 }
442
443 copy_v3_v3(positions[i], tmp_co);
444 }
445}
446
448 const ModifierEvalContext *ctx,
449 Mesh *mesh,
451{
453
454 if (cmd->type == MOD_CAST_TYPE_CUBOID) {
455 cuboid_do(cmd, ctx, ctx->object, mesh, positions);
456 }
457 else { /* MOD_CAST_TYPE_SPHERE or MOD_CAST_TYPE_CYLINDER */
458 sphere_do(cmd, ctx, ctx->object, mesh, positions);
459 }
460}
461
462static void panel_draw(const bContext * /*C*/, Panel *panel)
463{
464 uiLayout *row;
465 uiLayout *layout = panel->layout;
467
468 PointerRNA ob_ptr;
470
471 PointerRNA cast_object_ptr = RNA_pointer_get(ptr, "object");
472
473 uiLayoutSetPropSep(layout, true);
474
475 uiItemR(layout, ptr, "cast_type", UI_ITEM_NONE, nullptr, ICON_NONE);
476
477 row = uiLayoutRowWithHeading(layout, true, IFACE_("Axis"));
478 uiItemR(row, ptr, "use_x", toggles_flag, nullptr, ICON_NONE);
479 uiItemR(row, ptr, "use_y", toggles_flag, nullptr, ICON_NONE);
480 uiItemR(row, ptr, "use_z", toggles_flag, nullptr, ICON_NONE);
481
482 uiItemR(layout, ptr, "factor", UI_ITEM_NONE, nullptr, ICON_NONE);
483 uiItemR(layout, ptr, "radius", UI_ITEM_NONE, nullptr, ICON_NONE);
484 uiItemR(layout, ptr, "size", UI_ITEM_NONE, nullptr, ICON_NONE);
485 uiItemR(layout, ptr, "use_radius_as_size", UI_ITEM_NONE, nullptr, ICON_NONE);
486
487 modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr);
488
489 uiItemR(layout, ptr, "object", UI_ITEM_NONE, nullptr, ICON_NONE);
490 if (!RNA_pointer_is_null(&cast_object_ptr)) {
491 uiItemR(layout, ptr, "use_transform", UI_ITEM_NONE, nullptr, ICON_NONE);
492 }
493
494 modifier_panel_end(layout, ptr);
495}
496
497static void panel_register(ARegionType *region_type)
498{
500}
501
503 /*idname*/ "Cast",
504 /*name*/ N_("Cast"),
505 /*struct_name*/ "CastModifierData",
506 /*struct_size*/ sizeof(CastModifierData),
507 /*srna*/ &RNA_CastModifier,
511 /*icon*/ ICON_MOD_CAST,
512
513 /*copy_data*/ BKE_modifier_copydata_generic,
514
515 /*deform_verts*/ deform_verts,
516 /*deform_matrices*/ nullptr,
517 /*deform_verts_EM*/ nullptr,
518 /*deform_matrices_EM*/ nullptr,
519 /*modify_mesh*/ nullptr,
520 /*modify_geometry_set*/ nullptr,
521
522 /*init_data*/ init_data,
523 /*required_data_mask*/ required_data_mask,
524 /*free_data*/ nullptr,
525 /*is_disabled*/ is_disabled,
526 /*update_depsgraph*/ update_depsgraph,
527 /*depends_on_time*/ nullptr,
528 /*depends_on_normals*/ nullptr,
529 /*foreach_ID_link*/ foreach_ID_link,
530 /*foreach_tex_link*/ nullptr,
531 /*free_runtime_data*/ nullptr,
532 /*panel_register*/ panel_register,
533 /*blend_write*/ nullptr,
534 /*blend_read*/ nullptr,
535 /*foreach_cache*/ nullptr,
536};
support for deformation groups and hooks.
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:770
@ IDWALK_CB_NOP
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_AcceptsCVs
@ eModifierTypeFlag_SupportsEditmode
@ eModifierTypeFlag_AcceptsVertexCosOnly
void(*)(void *user_data, Object *ob, ID **idpoin, int cb_flag) IDWalkFunc
#define BLI_assert(a)
Definition BLI_assert.h:50
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define INIT_MINMAX(min, max)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define IFACE_(msgid)
void DEG_add_depends_on_transform_relation(DepsNodeHandle *node_handle, const char *description)
void DEG_add_object_relation(DepsNodeHandle *node_handle, Object *object, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_TRANSFORM
#define CD_MASK_MDEFORMVERT
#define DNA_struct_default_get(struct_name)
struct CastModifierData CastModifierData
@ MOD_CAST_USE_OB_TRANSFORM
@ MOD_CAST_INVERT_VGROUP
@ MOD_CAST_Y
@ MOD_CAST_X
@ MOD_CAST_Z
@ MOD_CAST_SIZE_FROM_RADIUS
@ eModifierType_Cast
@ MOD_CAST_TYPE_CYLINDER
@ MOD_CAST_TYPE_CUBOID
Object is a sort of wrapper for general info.
static bool is_disabled
static void init_data(ModifierData *md)
Definition MOD_cast.cc:34
static void deform_verts(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh, blender::MutableSpan< blender::float3 > positions)
Definition MOD_cast.cc:447
static void panel_register(ARegionType *region_type)
Definition MOD_cast.cc:497
ModifierTypeInfo modifierType_Cast
Definition MOD_cast.cc:502
static void panel_draw(const bContext *, Panel *panel)
Definition MOD_cast.cc:462
static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
Definition MOD_cast.cc:57
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
Definition MOD_cast.cc:67
static void cuboid_do(CastModifierData *cmd, const ModifierEvalContext *, Object *ob, Mesh *mesh, blender::MutableSpan< blender::float3 > positions)
Definition MOD_cast.cc:223
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
Definition MOD_cast.cc:74
static void sphere_do(CastModifierData *cmd, const ModifierEvalContext *, Object *ob, Mesh *mesh, blender::MutableSpan< blender::float3 > positions)
Definition MOD_cast.cc:83
void modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_vgroup_ui(uiLayout *layout, PointerRNA *ptr, PointerRNA *ob_ptr, const char *vgroup_prop, const char *invert_vgroup_prop, const char *text)
void MOD_get_vgroup(const Object *ob, const Mesh *mesh, const char *name, const MDeformVert **dvert, int *defgrp_index)
Definition MOD_util.cc:159
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutRowWithHeading(uiLayout *layout, bool align, const char *heading)
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
eUI_Item_Flag
@ UI_ITEM_R_TOGGLE
@ UI_ITEM_R_FORCE_BLANK_DECORATE
#define fabsf(x)
int len
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
bool RNA_pointer_is_null(const PointerRNA *ptr)
#define min(a, b)
Definition sort.c:32
struct Object * object
Definition DNA_ID.h:413
ObjectRuntimeHandle * runtime
struct uiLayout * layout
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4126
uint8_t flag
Definition wm_window.cc:138