Blender V4.5
sculpt_detail.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8#include "sculpt_dyntopo.hh"
9
10#include "MEM_guardedalloc.h"
11
12#include "BLI_index_mask.hh"
13#include "BLI_math_geom.h"
14#include "BLI_math_matrix.h"
15#include "BLI_math_rotation.h"
16#include "BLI_math_vector.hh"
17#include "BLI_string.h"
18#include "BLI_time.h"
19
20#include "BLT_translation.hh"
21
22#include "DNA_brush_types.h"
23#include "DNA_mesh_types.h"
24
25#include "BKE_attribute.hh"
26#include "BKE_brush.hh"
27#include "BKE_context.hh"
28#include "BKE_layer.hh"
29#include "BKE_object.hh"
30#include "BKE_paint.hh"
31#include "BKE_paint_bvh.hh"
32#include "BKE_screen.hh"
33
34#include "GPU_immediate.hh"
35#include "GPU_immediate_util.hh"
36#include "GPU_matrix.hh"
37#include "GPU_state.hh"
38
39#include "WM_api.hh"
40#include "WM_types.hh"
41
42#include "ED_screen.hh"
43#include "ED_space_api.hh"
44#include "ED_view3d.hh"
45
46#include "DEG_depsgraph.hh"
47
48#include "sculpt_intern.hh"
49#include "sculpt_undo.hh"
50
51#include "RNA_access.hh"
52#include "RNA_define.hh"
53#include "RNA_prototypes.hh"
54
55#include "UI_interface.hh"
56
57#include "CLG_log.h"
58
59#include <cstdlib>
60
61#include "bmesh.hh"
62
64
65static CLG_LogRef LOG = {"sculpt.detail"};
66
67/* -------------------------------------------------------------------- */
70
79
88
90{
92
93 return SCULPT_mode_poll(C) && ob->sculpt->bm;
94}
95
97
98/* -------------------------------------------------------------------- */
101
103{
104 const Scene &scene = *CTX_data_scene(C);
105 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
108 SculptSession &ss = *ob.sculpt;
109
110 const View3D *v3d = CTX_wm_view3d(C);
111 const Base *base = CTX_data_active_base(C);
112 if (!BKE_base_is_visible(v3d, base)) {
113 return OPERATOR_CANCELLED;
114 }
115
118
119 IndexMaskMemory memory;
120 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
121
122 if (nodes.is_empty()) {
123 return OPERATOR_CANCELLED;
124 }
125
126 node_mask.foreach_index([&](const int i) { BKE_pbvh_node_mark_topology_update(nodes[i]); });
127
128 /* Get the bounding box, its center and size. */
130 const float3 center = math::midpoint(bounds.min, bounds.max);
131 const float3 dim = bounds.max - bounds.min;
132 const float size = math::reduce_max(dim);
133
134 /* Update topology size. */
135 const float max_edge_len = 1.0f /
136 (sd->constant_detail * mat4_to_scale(ob.object_to_world().ptr()));
137 const float min_edge_len = max_edge_len * detail_size::EDGE_LENGTH_MIN_FACTOR;
138
139 undo::push_begin(scene, ob, op);
141
142 const double start_time = BLI_time_now_seconds();
143
145 pbvh,
146 *ss.bm_log,
148 min_edge_len,
149 max_edge_len,
150 center,
151 std::nullopt,
152 size,
153 false,
154 false))
155 {
156 node_mask.foreach_index([&](const int i) { BKE_pbvh_node_mark_topology_update(nodes[i]); });
157 }
158
159 CLOG_INFO(&LOG, 2, "Detail flood fill took %f seconds.", BLI_time_now_seconds() - start_time);
160
161 undo::push_end(ob);
162
163 /* Force rebuild of bke::pbvh::Tree for better BB placement. */
166
167 /* Redraw. */
169
170 return OPERATOR_FINISHED;
171}
172
174{
175 /* Identifiers. */
176 ot->name = "Detail Flood Fill";
177 ot->idname = "SCULPT_OT_detail_flood_fill";
178 ot->description = "Flood fill the mesh with the selected detail setting";
179
180 /* API callbacks. */
183
185}
186
188
189/* -------------------------------------------------------------------- */
192
195 Voxel = 1,
196};
197
199 {int(SampleDetailModeType::Dyntopo), "DYNTOPO", 0, "Dyntopo", "Sample dyntopo detail"},
200 {int(SampleDetailModeType::Voxel), "VOXEL", 0, "Voxel", "Sample mesh voxel size"},
201 {0, nullptr, 0, nullptr, nullptr},
202};
203
204static bool sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2])
205{
207 Object &ob = *vc->obact;
208 SculptSession &ss = *ob.sculpt;
209 Mesh &mesh = *static_cast<Mesh *>(ob.data);
211 const OffsetIndices faces = mesh.faces();
212 const Span<int> corner_verts = mesh.corner_verts();
213 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
214 const bke::AttributeAccessor attributes = mesh.attributes();
215 const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
216
218
219 /* Update the active vertex. */
220 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
221 if (!cursor_geometry_info_update(C, &cgi, mval_fl, false)) {
222 return false;
223 }
225
226 /* Average the edge length of the connected edges to the active vertex. */
227 const int active_vert = std::get<int>(ss.active_vert());
228 const float3 active_vert_position = positions[active_vert];
229 float edge_length = 0.0f;
230 Vector<int> neighbors;
231 for (const int neighbor : vert_neighbors_get_mesh(
232 faces, corner_verts, vert_to_face_map, hide_poly, active_vert, neighbors))
233 {
234 edge_length += math::distance(active_vert_position, positions[neighbor]);
235 }
236 mesh.remesh_voxel_size = edge_length / float(neighbors.size());
237 return true;
238}
239
242 float *tmin)
243{
244 if (BKE_pbvh_node_get_tmin(&node) < *tmin) {
246 node, srd.ray_start, &srd.isect_precalc, &srd.depth, &srd.edge_length))
247 {
248 srd.hit = true;
249 *tmin = srd.depth;
250 }
251 }
252}
253
254static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2])
255{
257 Object &ob = *vc->obact;
258 const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
259
261
262 const float2 mval_fl = {float(mval[0]), float(mval[1])};
263 float3 ray_start;
264 float3 ray_end;
265 float3 ray_normal;
266 float depth = raycast_init(vc, mval_fl, ray_start, ray_end, ray_normal, false);
267
269 srd.hit = false;
270 srd.ray_start = ray_start;
271 srd.depth = depth;
272 srd.edge_length = 0.0f;
274
276
278 pbvh,
279 [&](bke::pbvh::Node &node, float *tmin) {
280 sculpt_raycast_detail_cb(static_cast<bke::pbvh::BMeshNode &>(node), srd, tmin);
281 },
282 ray_start,
283 ray_normal,
284 false);
285
286 if (srd.hit && srd.edge_length > 0.0f) {
287 /* Convert edge length to world space detail resolution. */
288 sd.constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob.object_to_world().ptr()));
289 }
290}
291
293 const int event_xy[2],
294 const SampleDetailModeType mode)
295{
296 /* Find 3D view to pick from. */
297 bScreen *screen = CTX_wm_screen(C);
298 ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, event_xy);
299 ARegion *region = (area) ? BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, event_xy) : nullptr;
300 if (region == nullptr) {
301 return OPERATOR_CANCELLED;
302 }
303
304 /* Set context to 3D view. */
305 ScrArea *prev_area = CTX_wm_area(C);
306 ARegion *prev_region = CTX_wm_region(C);
307 CTX_wm_area_set(C, area);
308 CTX_wm_region_set(C, region);
309
312
313 Object *ob = vc.obact;
314 if (ob == nullptr) {
315 return OPERATOR_CANCELLED;
316 }
317
319 if (!pbvh) {
320 return OPERATOR_CANCELLED;
321 }
322
323 const View3D *v3d = CTX_wm_view3d(C);
324 const Base *base = CTX_data_active_base(C);
325 if (!BKE_base_is_visible(v3d, base)) {
326 return OPERATOR_CANCELLED;
327 }
328
329 const int mval[2] = {
330 event_xy[0] - region->winrct.xmin,
331 event_xy[1] - region->winrct.ymin,
332 };
333
334 /* Pick sample detail. */
335 switch (mode) {
337 if (pbvh->type() != bke::pbvh::Type::BMesh) {
338 CTX_wm_area_set(C, prev_area);
339 CTX_wm_region_set(C, prev_region);
340 return OPERATOR_CANCELLED;
341 }
342 sample_detail_dyntopo(C, &vc, mval);
343 break;
345 if (pbvh->type() != bke::pbvh::Type::Mesh) {
346 CTX_wm_area_set(C, prev_area);
347 CTX_wm_region_set(C, prev_region);
348 return OPERATOR_CANCELLED;
349 }
350 if (!sample_detail_voxel(C, &vc, mval)) {
351 return OPERATOR_CANCELLED;
352 }
353 break;
354 }
355
356 /* Restore context. */
357 CTX_wm_area_set(C, prev_area);
358 CTX_wm_region_set(C, prev_region);
359
360 return OPERATOR_FINISHED;
361}
362
364{
365 int ss_co[2];
366 RNA_int_get_array(op->ptr, "location", ss_co);
368 return sample_detail(C, ss_co, mode);
369}
370
372 wmOperator *op,
373 const wmEvent * /*event*/)
374{
375 ED_workspace_status_text(C, IFACE_("Click on the mesh to set the detail"));
379}
380
382 wmOperator *op,
383 const wmEvent *event)
384{
385 switch (event->type) {
386 case LEFTMOUSE:
387 if (event->val == KM_PRESS) {
389 sample_detail(C, event->xy, mode);
390
391 RNA_int_set_array(op->ptr, "location", event->xy);
393 ED_workspace_status_text(C, nullptr);
395
396 return OPERATOR_FINISHED;
397 }
398 break;
399 case EVT_ESCKEY:
400 case RIGHTMOUSE: {
402 ED_workspace_status_text(C, nullptr);
403
404 return OPERATOR_CANCELLED;
405 }
406 default: {
407 break;
408 }
409 }
410
412}
413
415{
416 /* Identifiers. */
417 ot->name = "Sample Detail Size";
418 ot->idname = "SCULPT_OT_sample_detail_size";
419 ot->description = "Sample the mesh detail on clicked point";
420
421 /* API callbacks. */
425 ot->poll = SCULPT_mode_poll;
426
428
429 PropertyRNA *prop;
430 prop = RNA_def_int_array(ot->srna,
431 "location",
432 2,
433 nullptr,
434 0,
435 SHRT_MAX,
436 "Location",
437 "Screen coordinates of sampling",
438 0,
439 SHRT_MAX);
441 prop = RNA_def_enum(ot->srna,
442 "mode",
445 "Detail Mode",
446 "Target sculpting workflow that is going to use the sampled size");
448}
449
451
452/* -------------------------------------------------------------------- */
455
456/* Defines how much the mouse movement will modify the detail size value. */
457#define DETAIL_SIZE_DELTA_SPEED 0.08f
458#define DETAIL_SIZE_DELTA_ACCURATE_SPEED 0.004f
459
465
469
471
472 float init_mval[2];
474
475 float outline_col[4];
476
479
480 /* The values stored here vary based on the detailing mode. */
484
485 float radius;
486
489
492
493 float preview_tri[3][3];
494 float gizmo_mat[4][4];
495};
496
499 const float start_co[3],
500 const float end_co[3],
501 bool flip,
502 const float angle)
503{
504 float object_space_constant_detail;
505 if (cd->mode == DETAILING_MODE_RESOLUTION) {
506 object_space_constant_detail = detail_size::constant_to_detail_size(cd->current_value,
507 *cd->active_object);
508 }
509 else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
510 object_space_constant_detail = detail_size::brush_to_detail_size(cd->current_value,
511 cd->brush_radius);
512 }
513 else {
514 object_space_constant_detail = detail_size::relative_to_detail_size(
515 cd->current_value, cd->brush_radius, cd->pixel_radius, U.pixelsize);
516 }
517
518 /* The constant detail represents the maximum edge length allowed before subdividing it. If the
519 * triangle grid preview is created with this value it will represent an ideal mesh density where
520 * all edges have the exact maximum length, which never happens in practice. As the minimum edge
521 * length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the average
522 * between max and min edge length so the preview is more accurate. */
523 object_space_constant_detail *= 0.7f;
524
525 const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]);
526 const int tot_lines = int(total_len / object_space_constant_detail) + 1;
527 const float tot_lines_fl = total_len / object_space_constant_detail;
528 float spacing_disp[3];
529 sub_v3_v3v3(spacing_disp, end_co, start_co);
530 normalize_v3(spacing_disp);
531
532 float line_disp[3];
533 zero_v3(line_disp);
534 rotate_v2_v2fl(line_disp, spacing_disp, DEG2RAD(angle));
535 mul_v3_fl(spacing_disp, total_len / tot_lines_fl);
536
537 immBegin(GPU_PRIM_LINES, uint(tot_lines) * 2);
538 for (int i = 0; i < tot_lines; i++) {
539 float line_length;
540 if (flip) {
541 line_length = total_len * (float(i) / float(tot_lines_fl));
542 }
543 else {
544 line_length = total_len * (1.0f - (float(i) / float(tot_lines_fl)));
545 }
546 float line_start[3];
547 copy_v3_v3(line_start, start_co);
548 madd_v3_v3v3fl(line_start, line_start, spacing_disp, i);
549 float line_end[3];
550 madd_v3_v3v3fl(line_end, line_start, line_disp, line_length);
551 immVertex3fv(pos3d, line_start);
552 immVertex3fv(pos3d, line_end);
553 }
554 immEnd();
555}
556
557static void dyntopo_detail_size_edit_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
558{
561 GPU_line_smooth(true);
562
567
568 /* Draw Cursor */
570 GPU_line_width(3.0f);
571
572 imm_draw_circle_wire_3d(pos3d, 0, 0, cd->radius, 80);
573
574 /* Draw Triangle. */
575 immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f);
577 immVertex3fv(pos3d, cd->preview_tri[0]);
578 immVertex3fv(pos3d, cd->preview_tri[1]);
579
580 immVertex3fv(pos3d, cd->preview_tri[1]);
581 immVertex3fv(pos3d, cd->preview_tri[2]);
582
583 immVertex3fv(pos3d, cd->preview_tri[2]);
584 immVertex3fv(pos3d, cd->preview_tri[0]);
585 immEnd();
586
587 /* Draw Grid */
588 GPU_line_width(1.0f);
590 pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], false, 60.0f);
592 pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], true, 120.0f);
594 pos3d, cd, cd->preview_tri[0], cd->preview_tri[2], false, -60.0f);
595
599 GPU_line_smooth(false);
600}
601
603{
604 Object *active_object = CTX_data_active_object(C);
605 SculptSession &ss = *active_object->sculpt;
606 ARegion *region = CTX_wm_region(C);
608 op->customdata);
609 ED_region_draw_cb_exit(region->runtime->type, cd->draw_handle);
610 ss.draw_faded_cursor = false;
611 MEM_freeN(cd);
612 op->customdata = nullptr;
613 ED_workspace_status_text(C, nullptr);
614
615 ScrArea *area = CTX_wm_area(C);
616 ED_area_status_text(area, nullptr);
617}
618
620{
621 /* TODO: Get range from RNA for these values? */
622 if (cd->mode == DETAILING_MODE_RESOLUTION) {
623 cd->min_value = 1.0f;
624 cd->max_value = 500.0f;
625 }
626 else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
627 cd->min_value = 0.5f;
628 cd->max_value = 100.0f;
629 }
630 else {
631 cd->min_value = 0.5f;
632 cd->max_value = 40.0f;
633 }
634}
635
638{
639 SculptSession &ss = *ob.sculpt;
640 const ActiveVert active_vert = ss.active_vert();
641 if (std::holds_alternative<std::monostate>(active_vert)) {
642 return;
643 }
644 BMVert *active_vertex = std::get<BMVert *>(active_vert);
645
646 float len_accum = 0;
647 BMeshNeighborVerts neighbors;
648 for (BMVert *neighbor : vert_neighbors_get_bmesh(*active_vertex, neighbors)) {
649 len_accum += len_v3v3(active_vertex->co, neighbor->co);
650 }
651 const int num_neighbors = neighbors.size();
652
653 if (num_neighbors > 0) {
654 const float avg_edge_len = len_accum / num_neighbors;
655 /* Use 0.7 as the average of min and max dyntopo edge length. */
656 const float detail_size = 0.7f / (avg_edge_len *
657 mat4_to_scale(cd->active_object->object_to_world().ptr()));
658 float sampled_value;
659 if (cd->mode == DETAILING_MODE_RESOLUTION) {
660 sampled_value = detail_size;
661 }
662 else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
665 }
666 else {
668 detail_size, cd->brush_radius, cd->pixel_radius, U.pixelsize, *cd->active_object);
669 }
670 cd->current_value = clamp_f(sampled_value, cd->min_value, cd->max_value);
671 }
672}
673
675 const wmEvent *event)
676{
677 const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
678
679 float detail_size_delta;
680 float invert = cd->mode == DETAILING_MODE_RESOLUTION ? 1.0f : -1.0f;
681 if (cd->accurate_mode) {
682 detail_size_delta = mval[0] - cd->accurate_mval[0];
683 cd->current_value = cd->accurate_value +
684 detail_size_delta * DETAIL_SIZE_DELTA_ACCURATE_SPEED * invert;
685 }
686 else {
687 detail_size_delta = mval[0] - cd->init_mval[0];
688 cd->current_value = cd->init_value + detail_size_delta * DETAIL_SIZE_DELTA_SPEED * invert;
689 }
690
691 if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) {
692 cd->accurate_mode = true;
693 copy_v2_v2(cd->accurate_mval, mval);
695 }
696 if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) {
697 cd->accurate_mode = false;
698 cd->accurate_value = 0.0f;
699 }
700
702}
703
706{
707 Scene *scene = CTX_data_scene(C);
708
709 Sculpt *sd = scene->toolsettings->sculpt;
710 PointerRNA sculpt_ptr = RNA_pointer_create_discrete(&scene->id, &RNA_Sculpt, sd);
711
712 char msg[UI_MAX_DRAW_STR];
713 const char *format_string;
714 const char *property_name;
715 if (cd->mode == DETAILING_MODE_RESOLUTION) {
716 property_name = "constant_detail_resolution";
717 format_string = "%s: %0.4f";
718 }
719 else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
720 property_name = "detail_percent";
721 format_string = "%s: %3.1f%%";
722 }
723 else {
724 property_name = "detail_size";
725 format_string = "%s: %0.4f";
726 }
727 const PropertyRNA *prop = RNA_struct_find_property(&sculpt_ptr, property_name);
728 const char *ui_name = RNA_property_ui_name(prop);
729 SNPRINTF(msg, format_string, ui_name, cd->current_value);
730 ScrArea *area = CTX_wm_area(C);
731 ED_area_status_text(area, msg);
732
733 WorkspaceStatus status(C);
734 status.item(IFACE_("Confirm"), ICON_EVENT_RETURN, ICON_MOUSE_LMB);
735 status.item(IFACE_("Cancel"), ICON_EVENT_ESC, ICON_MOUSE_RMB);
736 status.item(IFACE_("Change Size"), ICON_MOUSE_MOVE);
737 status.item_bool(IFACE_("Sample Mode"), cd->sample_mode, ICON_EVENT_CTRL);
738 status.item_bool(IFACE_("Precision Mode"), cd->accurate_mode, ICON_EVENT_SHIFT);
739}
740
742 wmOperator *op,
743 const wmEvent *event)
744{
745 Object &active_object = *CTX_data_active_object(C);
746 SculptSession &ss = *active_object.sculpt;
747 ARegion *region = CTX_wm_region(C);
749 op->customdata);
751
752 /* Cancel modal operator */
753 if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) ||
754 (event->type == RIGHTMOUSE && event->val == KM_PRESS))
755 {
757 ED_region_tag_redraw(region);
758 return OPERATOR_FINISHED;
759 }
760
761 /* Finish modal operator */
762 if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
763 (event->type == EVT_RETKEY && event->val == KM_PRESS) ||
764 (event->type == EVT_PADENTER && event->val == KM_PRESS))
765 {
766 ED_region_draw_cb_exit(region->runtime->type, cd->draw_handle);
767 if (cd->mode == DETAILING_MODE_RESOLUTION) {
769 }
770 else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
772 }
773 else {
774 sd->detail_size = cd->current_value;
775 }
776
777 ss.draw_faded_cursor = false;
778 MEM_freeN(cd);
779 ED_region_tag_redraw(region);
780 ED_workspace_status_text(C, nullptr);
781
782 ScrArea *area = CTX_wm_area(C);
783 ED_area_status_text(area, nullptr);
784 return OPERATOR_FINISHED;
785 }
786
787 ED_region_tag_redraw(region);
788
790 if (event->val == KM_PRESS) {
791 cd->sample_mode = true;
792 }
793 else if (event->val == KM_RELEASE) {
794 cd->sample_mode = false;
795 }
796 }
797
798 /* Sample mode sets the detail size sampling the average edge length under the surface. */
799 if (cd->sample_mode) {
803 }
804 /* Regular mode, changes the detail size by moving the cursor. */
807
809}
810
812{
813 if (mode == DETAILING_MODE_RESOLUTION) {
814 return sd->constant_detail;
815 }
816 if (mode == DETAILING_MODE_BRUSH_PERCENT) {
817 return sd->detail_percent;
818 }
819 return sd->detail_size;
820}
821
823 wmOperator *op,
824 const wmEvent *event)
825{
826 const ToolSettings *tool_settings = CTX_data_tool_settings(C);
827 Sculpt *sd = tool_settings->sculpt;
828
829 ARegion *region = CTX_wm_region(C);
830 Object &active_object = *CTX_data_active_object(C);
831 Brush *brush = BKE_paint_brush(&sd->paint);
832
834
835 /* Initial operator Custom Data setup. */
838 cd->active_object = &active_object;
839 cd->init_mval[0] = event->mval[0];
840 cd->init_mval[1] = event->mval[1];
843 }
844 else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) {
846 }
847 else {
849 }
850
851 const float initial_detail_size = dyntopo_detail_size_initial_value(sd, cd->mode);
852 cd->current_value = initial_detail_size;
853 cd->init_value = initial_detail_size;
854 copy_v4_v4(cd->outline_col, brush->add_col);
855 op->customdata = cd;
856
857 SculptSession &ss = *active_object.sculpt;
859 cd->radius = ss.cursor_radius;
860
863
864 const Scene *scene = CTX_data_scene(C);
865 cd->brush_radius = object_space_radius_get(vc, *scene, *brush, ss.cursor_location);
866 cd->pixel_radius = BKE_brush_size_get(scene, brush);
867
868 /* Generates the matrix to position the gizmo in the surface of the mesh using the same
869 * location and orientation as the brush cursor. */
870 float cursor_trans[4][4], cursor_rot[4][4];
871 const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f};
872 float quat[4];
873 copy_m4_m4(cursor_trans, active_object.object_to_world().ptr());
874 translate_m4(cursor_trans, ss.cursor_location[0], ss.cursor_location[1], ss.cursor_location[2]);
875
876 float cursor_normal[3];
877 if (ss.cursor_sampled_normal) {
878 copy_v3_v3(cursor_normal, *ss.cursor_sampled_normal);
879 }
880 else {
881 copy_v3_v3(cursor_normal, ss.cursor_normal);
882 }
883
884 rotation_between_vecs_to_quat(quat, z_axis, cursor_normal);
885 quat_to_mat4(cursor_rot, quat);
886 copy_m4_m4(cd->gizmo_mat, cursor_trans);
887 mul_m4_m4_post(cd->gizmo_mat, cursor_rot);
888
889 /* Initialize the position of the triangle vertices. */
890 const float y_axis[3] = {0.0f, cd->radius, 0.0f};
891 for (int i = 0; i < 3; i++) {
892 zero_v3(cd->preview_tri[i]);
893 rotate_v2_v2fl(cd->preview_tri[i], y_axis, DEG2RAD(120.0f * i));
894 }
895
896 vert_random_access_ensure(active_object);
897
899 ED_region_tag_redraw(region);
900
901 ss.draw_faded_cursor = true;
902
903 const char *status_str = IFACE_(
904 "Move the mouse to change the dyntopo detail size. LMB: confirm size, ESC/RMB: cancel, "
905 "SHIFT: precision mode, CTRL: sample detail size");
906
907 ED_workspace_status_text(C, status_str);
909
911}
912
914{
915 /* identifiers */
916 ot->name = "Edit Dyntopo Detail Size";
917 ot->description = "Modify the detail size of dyntopo interactively";
918 ot->idname = "SCULPT_OT_dyntopo_detail_size_edit";
919
920 /* API callbacks. */
925
927}
928
929} // namespace blender::ed::sculpt_paint::dyntopo
930
932
933float constant_to_detail_size(const float constant_detail, const Object &ob)
934{
935 return 1.0f / (constant_detail * mat4_to_scale(ob.object_to_world().ptr()));
936}
937
938float brush_to_detail_size(const float brush_percent, const float brush_radius)
939{
940 return brush_radius * brush_percent / 100.0f;
941}
942
943float relative_to_detail_size(const float relative_detail,
944 const float brush_radius,
945 const float pixel_radius,
946 const float pixel_size)
947{
948 return (brush_radius / pixel_radius) * (relative_detail * pixel_size) / RELATIVE_SCALE_FACTOR;
949}
950
951float constant_to_brush_detail(const float constant_detail,
952 const float brush_radius,
953 const Object &ob)
954{
955 const float object_scale = mat4_to_scale(ob.object_to_world().ptr());
956
957 return 100.0f / (constant_detail * brush_radius * object_scale);
958}
959
960float constant_to_relative_detail(const float constant_detail,
961 const float brush_radius,
962 const float pixel_radius,
963 const float pixel_size,
964 const Object &ob)
965{
966 const float object_scale = mat4_to_scale(ob.object_to_world().ptr());
967
968 return (pixel_radius / brush_radius) * (RELATIVE_SCALE_FACTOR / pixel_size) *
969 (1.0f / (constant_detail * object_scale));
970}
971
972} // namespace blender::ed::sculpt_paint::dyntopo::detail_size
973
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1210
bScreen * CTX_wm_screen(const bContext *C)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Base * CTX_data_active_base(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
void CTX_wm_area_set(bContext *C, ScrArea *area)
void CTX_wm_region_set(bContext *C, ARegion *region)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
bool BKE_base_is_visible(const View3D *v3d, const Base *base)
General operations, lookup, etc. for blender objects.
std::variant< std::monostate, int, BMVert * > ActiveVert
Definition BKE_paint.hh:380
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:641
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2657
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2146
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
A BVH for high poly meshes.
void BKE_pbvh_node_mark_topology_update(blender::bke::pbvh::Node &node)
float BKE_pbvh_node_get_tmin(const blender::bke::pbvh::Node *node)
Definition pbvh.cc:773
@ PBVH_Collapse
@ PBVH_Subdivide
ARegion * BKE_area_find_region_xy(const ScrArea *area, int regiontype, const int xy[2]) ATTR_NONNULL(3)
Definition screen.cc:869
ScrArea ScrArea * BKE_screen_find_area_xy(const bScreen *screen, int spacetype, const int xy[2]) ATTR_NONNULL(1
MINLINE float clamp_f(float value, float min, float max)
#define DEG2RAD(_deg)
void isect_ray_tri_watertight_v3_precalc(struct IsectRayPrecalc *isect_precalc, const float ray_direction[3])
float mat4_to_scale(const float mat[4][4])
void translate_m4(float mat[4][4], float Tx, float Ty, float Tz)
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_m4_m4_post(float R[4][4], const float B[4][4])
void rotation_between_vecs_to_quat(float q[4], const float v1[3], const float v2[3])
void quat_to_mat4(float m[4][4], const float q[4])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void rotate_v2_v2fl(float r[2], const float p[2], float angle)
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v3(float n[3])
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:65
#define ELEM(...)
#define IFACE_(msgid)
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ SCULPT_DYNTOPO_DETAIL_MANUAL
@ SCULPT_DYNTOPO_DETAIL_CONSTANT
@ SCULPT_DYNTOPO_DETAIL_BRUSH
@ RGN_TYPE_WINDOW
@ SPACE_VIEW3D
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:872
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void immEnd()
void immUnbindProgram()
void immUniformColor4f(float r, float g, float b, float a)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void imm_draw_circle_wire_3d(uint pos, float x, float y, float radius, int nsegments)
void GPU_matrix_push()
#define GPU_matrix_mul(x)
void GPU_matrix_pop()
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:166
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
#define UI_MAX_DRAW_STR
#define ND_DRAW
Definition WM_types.hh:458
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
#define NC_SCENE
Definition WM_types.hh:375
#define ND_TOOLSETTINGS
Definition WM_types.hh:446
#define NC_OBJECT
Definition WM_types.hh:376
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define U
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:995
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
int64_t size() const
GAttributeReader lookup(const StringRef attribute_id) const
Span< NodeT > nodes() const
void foreach_index(Fn &&fn) const
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
Definition invert.h:11
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2912
void raycast(Tree &pbvh, FunctionRef< void(Node &node, float *tmin)> hit_fn, const float3 &ray_start, const float3 &ray_normal, bool original)
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2544
Bounds< float3 > bounds_get(const Tree &pbvh)
Definition pbvh.cc:1477
bool bmesh_update_topology(BMesh &bm, Tree &pbvh, BMLog &bm_log, PBVHTopologyUpdateMode mode, float min_edge_len, float max_edge_len, const float3 &center, const std::optional< float3 > &view_normal, float radius, bool use_frontface, bool use_projected)
bool raycast_node_detail_bmesh(const BMeshNode &node, const float3 &ray_start, const IsectRayPrecalc *isect_precalc, float *depth, float *r_edge_length)
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2416
float relative_to_detail_size(const float relative_detail, const float brush_radius, const float pixel_radius, const float pixel_size)
float constant_to_detail_size(const float constant_detail, const Object &ob)
float constant_to_relative_detail(const float constant_detail, const float brush_radius, const float pixel_radius, const float pixel_size, const Object &ob)
float constant_to_brush_detail(const float constant_detail, const float brush_radius, const Object &ob)
float brush_to_detail_size(const float brush_percent, const float brush_radius)
void SCULPT_OT_detail_flood_fill(wmOperatorType *ot)
static EnumPropertyItem prop_sculpt_sample_detail_mode_types[]
static void dyntopo_detail_size_edit_cancel(bContext *C, wmOperator *op)
static float dyntopo_detail_size_initial_value(const Sculpt *sd, const eDyntopoDetailingMode mode)
static wmOperatorStatus sculpt_sample_detail_size_exec(bContext *C, wmOperator *op)
static void dyntopo_detail_size_update_header(bContext *C, const DyntopoDetailSizeEditCustomData *cd)
static wmOperatorStatus dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
void SCULPT_OT_sample_detail_size(wmOperatorType *ot)
static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2])
static void dyntopo_detail_size_parallel_lines_draw(uint pos3d, DyntopoDetailSizeEditCustomData *cd, const float start_co[3], const float end_co[3], bool flip, const float angle)
static wmOperatorStatus dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void dyntopo_detail_size_update_from_mouse_delta(DyntopoDetailSizeEditCustomData *cd, const wmEvent *event)
static void dyntopo_detail_size_sample_from_surface(Object &ob, DyntopoDetailSizeEditCustomData *cd)
static void dyntopo_detail_size_edit_draw(const bContext *, ARegion *, void *arg)
static bool sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2])
void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot)
static wmOperatorStatus sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void dyntopo_detail_size_bounds(DyntopoDetailSizeEditCustomData *cd)
static wmOperatorStatus sample_detail(bContext *C, const int event_xy[2], const SampleDetailModeType mode)
static void sculpt_raycast_detail_cb(bke::pbvh::BMeshNode &node, SculptDetailRaycastData &srd, float *tmin)
static wmOperatorStatus sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *)
static bool sculpt_and_dynamic_topology_poll(bContext *C)
static bool sculpt_and_constant_or_manual_detail_poll(bContext *C)
static wmOperatorStatus sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op)
void push_node(const Depsgraph &depsgraph, const Object &object, const bke::pbvh::Node *node, const Type type)
void push_begin(const Scene &scene, Object &ob, const wmOperator *op)
float object_space_radius_get(const ViewContext &vc, const Scene &scene, const Brush &brush, const float3 &location, const float scale_factor)
Definition sculpt.cc:115
void vert_random_access_ensure(Object &object)
Definition sculpt.cc:142
Vector< BMVert *, 64 > BMeshNeighborVerts
float raycast_init(ViewContext *vc, const float2 &mval, float3 &r_ray_start, float3 &r_ray_end, float3 &r_ray_normal, bool original)
Definition sculpt.cc:4623
bool cursor_geometry_info_update(bContext *C, CursorGeometryInfo *out, const float2 &mval, const bool use_sampled_normal)
Definition sculpt.cc:4664
Span< BMVert * > vert_neighbors_get_bmesh(BMVert &vert, BMeshNeighborVerts &r_neighbors)
Definition sculpt.cc:388
Span< int > vert_neighbors_get_mesh(const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > vert_to_face, const Span< bool > hide_poly, const int vert, Vector< int > &r_neighbors)
Definition sculpt.cc:430
T reduce_max(const VecBase< T, Size > &a)
T distance(const T &a, const T &b)
T midpoint(const T &a, const T &b)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
const char * RNA_property_ui_name(const PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_int_array(StructOrFunctionRNA *cont_, const char *identifier, const int len, const int *default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void SCULPT_stroke_modifiers_check(const bContext *C, Object &ob, const Brush &brush)
Definition sculpt.cc:4447
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3660
#define DETAIL_SIZE_DELTA_ACCURATE_SPEED
#define DETAIL_SIZE_DELTA_SPEED
ARegionRuntimeHandle * runtime
float co[3]
float add_col[4]
struct SculptSession * sculpt
struct ToolSettings * toolsettings
BMLog * bm_log
Definition BKE_paint.hh:412
blender::float3 cursor_normal
Definition BKE_paint.hh:451
bool draw_faded_cursor
Definition BKE_paint.hh:448
float cursor_radius
Definition BKE_paint.hh:449
ActiveVert active_vert() const
Definition paint.cc:2227
blender::float3 cursor_location
Definition BKE_paint.hh:450
std::optional< blender::float3 > cursor_sampled_normal
Definition BKE_paint.hh:452
float detail_percent
float detail_size
float constant_detail
Object * obact
Definition ED_view3d.hh:75
int ymin
int xmin
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int xy[2]
Definition WM_types.hh:758
int mval[2]
Definition WM_types.hh:760
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_EYEDROPPER
Definition wm_cursors.hh:36
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ EVT_RIGHTCTRLKEY
@ EVT_LEFTCTRLKEY
@ EVT_PADENTER
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_LEFTSHIFTKEY
@ EVT_RETKEY
wmOperatorType * ot
Definition wm_files.cc:4226