Blender V5.0
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_utf8.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_paint.hh"
30#include "BKE_paint_bvh.hh"
31#include "BKE_screen.hh"
32
33#include "GPU_immediate.hh"
34#include "GPU_immediate_util.hh"
35#include "GPU_matrix.hh"
36#include "GPU_state.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "ED_screen.hh"
42#include "ED_space_api.hh"
43#include "ED_view3d.hh"
44
45#include "DEG_depsgraph.hh"
46
47#include "sculpt_intern.hh"
48#include "sculpt_undo.hh"
49
50#include "RNA_access.hh"
51#include "RNA_define.hh"
52#include "RNA_prototypes.hh"
53
54#include "UI_interface.hh"
55
56#include "CLG_log.h"
57
58#include <cstdlib>
59
60#include "bmesh.hh"
61
63
64static CLG_LogRef LOG = {"sculpt.detail"};
65
66/* -------------------------------------------------------------------- */
69
78
87
89{
91
92 return SCULPT_mode_poll(C) && ob->sculpt->bm;
93}
94
96
97/* -------------------------------------------------------------------- */
100
102{
103 const Scene &scene = *CTX_data_scene(C);
104 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
107 SculptSession &ss = *ob.sculpt;
108
109 const View3D *v3d = CTX_wm_view3d(C);
110 const Base *base = CTX_data_active_base(C);
111 if (!BKE_base_is_visible(v3d, base)) {
112 return OPERATOR_CANCELLED;
113 }
114
117
118 IndexMaskMemory memory;
119 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
120
121 if (nodes.is_empty()) {
122 return OPERATOR_CANCELLED;
123 }
124
125 node_mask.foreach_index([&](const int i) { BKE_pbvh_node_mark_topology_update(nodes[i]); });
126
127 /* Get the bounding box, its center and size. */
129 const float3 center = math::midpoint(bounds.min, bounds.max);
130 const float3 dim = bounds.max - bounds.min;
131 const float size = math::reduce_max(dim);
132
133 /* Update topology size. */
134 const float max_edge_len = 1.0f /
135 (sd->constant_detail * mat4_to_scale(ob.object_to_world().ptr()));
136 const float min_edge_len = max_edge_len * detail_size::EDGE_LENGTH_MIN_FACTOR;
137
138 undo::push_begin(scene, ob, op);
140
141 const double start_time = BLI_time_now_seconds();
142
144 pbvh,
145 *ss.bm_log,
147 min_edge_len,
148 max_edge_len,
149 center,
150 std::nullopt,
151 size,
152 false,
153 false))
154 {
155 node_mask.foreach_index([&](const int i) { BKE_pbvh_node_mark_topology_update(nodes[i]); });
156 }
157
158 CLOG_DEBUG(&LOG, "Detail flood fill took %f seconds.", BLI_time_now_seconds() - start_time);
159
160 undo::push_end(ob);
161
162 /* Force rebuild of bke::pbvh::Tree for better BB placement. */
165
166 /* Redraw. */
168
169 return OPERATOR_FINISHED;
170}
171
173{
174 /* Identifiers. */
175 ot->name = "Detail Flood Fill";
176 ot->idname = "SCULPT_OT_detail_flood_fill";
177 ot->description = "Flood fill the mesh with the selected detail setting";
178
179 /* API callbacks. */
182
184}
185
187
188/* -------------------------------------------------------------------- */
191
194 Voxel = 1,
195};
196
198 {int(SampleDetailModeType::Dyntopo), "DYNTOPO", 0, "Dyntopo", "Sample dyntopo detail"},
199 {int(SampleDetailModeType::Voxel), "VOXEL", 0, "Voxel", "Sample mesh voxel size"},
200 {0, nullptr, 0, nullptr, nullptr},
201};
202
203static bool sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2])
204{
206 Object &ob = *vc->obact;
207 SculptSession &ss = *ob.sculpt;
208 Mesh &mesh = *static_cast<Mesh *>(ob.data);
210 const OffsetIndices faces = mesh.faces();
211 const Span<int> corner_verts = mesh.corner_verts();
212 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
213 const bke::AttributeAccessor attributes = mesh.attributes();
214 const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
215
217
218 /* Update the active vertex. */
219 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
220 if (!cursor_geometry_info_update(C, &cgi, mval_fl, false)) {
221 return false;
222 }
224
225 /* Average the edge length of the connected edges to the active vertex. */
226 const int active_vert = std::get<int>(ss.active_vert());
227 const float3 active_vert_position = positions[active_vert];
228 float edge_length = 0.0f;
229 Vector<int> neighbors;
230 for (const int neighbor : vert_neighbors_get_mesh(
231 faces, corner_verts, vert_to_face_map, hide_poly, active_vert, neighbors))
232 {
233 edge_length += math::distance(active_vert_position, positions[neighbor]);
234 }
235 mesh.remesh_voxel_size = edge_length / float(neighbors.size());
236 return true;
237}
238
241 float *tmin)
242{
243 if (BKE_pbvh_node_get_tmin(&node) < *tmin) {
245 node, srd.ray_start, &srd.isect_precalc, &srd.depth, &srd.edge_length))
246 {
247 srd.hit = true;
248 *tmin = srd.depth;
249 }
250 }
251}
252
253static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2])
254{
256 Object &ob = *vc->obact;
257 const Brush &brush = *BKE_paint_brush_for_read(&sd.paint);
258
260
261 const float2 mval_fl = {float(mval[0]), float(mval[1])};
262 float3 ray_start;
263 float3 ray_end;
264 float3 ray_normal;
265 float depth = raycast_init(vc, mval_fl, ray_start, ray_end, ray_normal, false);
266
268 srd.hit = false;
269 srd.ray_start = ray_start;
270 srd.depth = depth;
271 srd.edge_length = 0.0f;
273
275
277 pbvh,
278 [&](bke::pbvh::Node &node, float *tmin) {
279 sculpt_raycast_detail_cb(static_cast<bke::pbvh::BMeshNode &>(node), srd, tmin);
280 },
281 ray_start,
282 ray_normal,
283 false);
284
285 if (srd.hit && srd.edge_length > 0.0f) {
286 /* Convert edge length to world space detail resolution. */
287 sd.constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob.object_to_world().ptr()));
288 }
289}
290
292 const int event_xy[2],
293 const SampleDetailModeType mode)
294{
295 /* Find 3D view to pick from. */
296 bScreen *screen = CTX_wm_screen(C);
297 ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, event_xy);
298 ARegion *region = (area) ? BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, event_xy) : nullptr;
299 if (region == nullptr) {
300 return OPERATOR_CANCELLED;
301 }
302
303 /* Set context to 3D view. */
304 ScrArea *prev_area = CTX_wm_area(C);
305 ARegion *prev_region = CTX_wm_region(C);
306 CTX_wm_area_set(C, area);
307 CTX_wm_region_set(C, region);
308
311
312 Object *ob = vc.obact;
313 if (ob == nullptr) {
314 return OPERATOR_CANCELLED;
315 }
316
318 if (!pbvh) {
319 return OPERATOR_CANCELLED;
320 }
321
322 const View3D *v3d = CTX_wm_view3d(C);
323 const Base *base = CTX_data_active_base(C);
324 if (!BKE_base_is_visible(v3d, base)) {
325 return OPERATOR_CANCELLED;
326 }
327
328 const int mval[2] = {
329 event_xy[0] - region->winrct.xmin,
330 event_xy[1] - region->winrct.ymin,
331 };
332
333 /* Pick sample detail. */
334 switch (mode) {
336 if (pbvh->type() != bke::pbvh::Type::BMesh) {
337 CTX_wm_area_set(C, prev_area);
338 CTX_wm_region_set(C, prev_region);
339 return OPERATOR_CANCELLED;
340 }
341 sample_detail_dyntopo(C, &vc, mval);
342 break;
344 if (pbvh->type() != bke::pbvh::Type::Mesh) {
345 CTX_wm_area_set(C, prev_area);
346 CTX_wm_region_set(C, prev_region);
347 return OPERATOR_CANCELLED;
348 }
349 if (!sample_detail_voxel(C, &vc, mval)) {
350 return OPERATOR_CANCELLED;
351 }
352 break;
353 }
354
355 /* Restore context. */
356 CTX_wm_area_set(C, prev_area);
357 CTX_wm_region_set(C, prev_region);
358
359 return OPERATOR_FINISHED;
360}
361
363{
364 int ss_co[2];
365 RNA_int_get_array(op->ptr, "location", ss_co);
367 return sample_detail(C, ss_co, mode);
368}
369
371 wmOperator *op,
372 const wmEvent * /*event*/)
373{
374 ED_workspace_status_text(C, IFACE_("Click on the mesh to set the detail"));
378}
379
381 wmOperator *op,
382 const wmEvent *event)
383{
384 switch (event->type) {
385 case LEFTMOUSE:
386 if (event->val == KM_PRESS) {
388 sample_detail(C, event->xy, mode);
389
390 RNA_int_set_array(op->ptr, "location", event->xy);
392 ED_workspace_status_text(C, nullptr);
394
395 return OPERATOR_FINISHED;
396 }
397 break;
398 case EVT_ESCKEY:
399 case RIGHTMOUSE: {
401 ED_workspace_status_text(C, nullptr);
402
403 return OPERATOR_CANCELLED;
404 }
405 default: {
406 break;
407 }
408 }
409
411}
412
414{
415 /* Identifiers. */
416 ot->name = "Sample Detail Size";
417 ot->idname = "SCULPT_OT_sample_detail_size";
418 ot->description = "Sample the mesh detail on clicked point";
419
420 /* API callbacks. */
424 ot->poll = SCULPT_mode_poll;
425
427
428 PropertyRNA *prop;
429 prop = RNA_def_int_array(ot->srna,
430 "location",
431 2,
432 nullptr,
433 0,
434 SHRT_MAX,
435 "Location",
436 "Screen coordinates of sampling",
437 0,
438 SHRT_MAX);
440 prop = RNA_def_enum(ot->srna,
441 "mode",
444 "Detail Mode",
445 "Target sculpting workflow that is going to use the sampled size");
447}
448
450
451/* -------------------------------------------------------------------- */
454
455/* Defines how much the mouse movement will modify the detail size value. */
456#define DETAIL_SIZE_DELTA_SPEED 0.08f
457#define DETAIL_SIZE_DELTA_ACCURATE_SPEED 0.004f
458
464
468
470
471 float init_mval[2];
473
474 float outline_col[4];
475
478
479 /* The values stored here vary based on the detailing mode. */
483
484 float radius;
485
488
491
492 float preview_tri[3][3];
493 float gizmo_mat[4][4];
494};
495
498 const float start_co[3],
499 const float end_co[3],
500 bool flip,
501 const float angle)
502{
503 float object_space_constant_detail;
504 if (cd->mode == DETAILING_MODE_RESOLUTION) {
505 object_space_constant_detail = detail_size::constant_to_detail_size(cd->current_value,
506 *cd->active_object);
507 }
508 else if (cd->mode == DETAILING_MODE_BRUSH_PERCENT) {
509 object_space_constant_detail = detail_size::brush_to_detail_size(cd->current_value,
510 cd->brush_radius);
511 }
512 else {
513 object_space_constant_detail = detail_size::relative_to_detail_size(
514 cd->current_value, cd->brush_radius, cd->pixel_radius, U.pixelsize);
515 }
516
517 /* The constant detail represents the maximum edge length allowed before subdividing it. If the
518 * triangle grid preview is created with this value it will represent an ideal mesh density where
519 * all edges have the exact maximum length, which never happens in practice. As the minimum edge
520 * length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the average
521 * between max and min edge length so the preview is more accurate. */
522 object_space_constant_detail *= 0.7f;
523
524 const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]);
525 const int tot_lines = int(total_len / object_space_constant_detail) + 1;
526 const float tot_lines_fl = total_len / object_space_constant_detail;
527 float spacing_disp[3];
528 sub_v3_v3v3(spacing_disp, end_co, start_co);
529 normalize_v3(spacing_disp);
530
531 float line_disp[3];
532 zero_v3(line_disp);
533 rotate_v2_v2fl(line_disp, spacing_disp, DEG2RAD(angle));
534 mul_v3_fl(spacing_disp, total_len / tot_lines_fl);
535
536 immBegin(GPU_PRIM_LINES, uint(tot_lines) * 2);
537 for (int i = 0; i < tot_lines; i++) {
538 float line_length;
539 if (flip) {
540 line_length = total_len * (float(i) / float(tot_lines_fl));
541 }
542 else {
543 line_length = total_len * (1.0f - (float(i) / float(tot_lines_fl)));
544 }
545 float line_start[3];
546 copy_v3_v3(line_start, start_co);
547 madd_v3_v3v3fl(line_start, line_start, spacing_disp, i);
548 float line_end[3];
549 madd_v3_v3v3fl(line_end, line_start, line_disp, line_length);
550 immVertex3fv(pos3d, line_start);
551 immVertex3fv(pos3d, line_end);
552 }
553 immEnd();
554}
555
556static void dyntopo_detail_size_edit_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
557{
560 GPU_line_smooth(true);
561
563 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
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_UTF8(msg, format_string, ui_name, cd->current_value);
730 ScrArea *area = CTX_wm_area(C);
731 ED_area_status_text(area, msg);
732
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
865 cd->pixel_radius = BKE_brush_radius_get(&sd->paint, brush);
866
867 /* Generates the matrix to position the gizmo in the surface of the mesh using the same
868 * location and orientation as the brush cursor. */
869 float cursor_trans[4][4], cursor_rot[4][4];
870 const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f};
871 float quat[4];
872 copy_m4_m4(cursor_trans, active_object.object_to_world().ptr());
873 translate_m4(cursor_trans, ss.cursor_location[0], ss.cursor_location[1], ss.cursor_location[2]);
874
875 float cursor_normal[3];
876 if (ss.cursor_sampled_normal) {
877 copy_v3_v3(cursor_normal, *ss.cursor_sampled_normal);
878 }
879 else {
880 copy_v3_v3(cursor_normal, ss.cursor_normal);
881 }
882
883 rotation_between_vecs_to_quat(quat, z_axis, cursor_normal);
884 quat_to_mat4(cursor_rot, quat);
885 copy_m4_m4(cd->gizmo_mat, cursor_trans);
886 mul_m4_m4_post(cd->gizmo_mat, cursor_rot);
887
888 /* Initialize the position of the triangle vertices. */
889 const float y_axis[3] = {0.0f, cd->radius, 0.0f};
890 for (int i = 0; i < 3; i++) {
891 zero_v3(cd->preview_tri[i]);
892 rotate_v2_v2fl(cd->preview_tri[i], y_axis, DEG2RAD(120.0f * i));
893 }
894
895 vert_random_access_ensure(active_object);
896
898 ED_region_tag_redraw(region);
899
900 ss.draw_faded_cursor = true;
901
902 const char *status_str = IFACE_(
903 "Move the mouse to change the dyntopo detail size. LMB: confirm size, ESC/RMB: cancel, "
904 "SHIFT: precision mode, CTRL: sample detail size");
905
906 ED_workspace_status_text(C, status_str);
908
910}
911
913{
914 /* identifiers */
915 ot->name = "Edit Dyntopo Detail Size";
916 ot->description = "Modify the detail size of dyntopo interactively";
917 ot->idname = "SCULPT_OT_dyntopo_detail_size_edit";
918
919 /* API callbacks. */
924
926}
927
928} // namespace blender::ed::sculpt_paint::dyntopo
929
931
932float constant_to_detail_size(const float constant_detail, const Object &ob)
933{
934 return 1.0f / (constant_detail * mat4_to_scale(ob.object_to_world().ptr()));
935}
936
937float brush_to_detail_size(const float brush_percent, const float brush_radius)
938{
939 return brush_radius * brush_percent / 100.0f;
940}
941
942float relative_to_detail_size(const float relative_detail,
943 const float brush_radius,
944 const float pixel_radius,
945 const float pixel_size)
946{
947 return (brush_radius / pixel_radius) * (relative_detail * pixel_size) / RELATIVE_SCALE_FACTOR;
948}
949
950float constant_to_brush_detail(const float constant_detail,
951 const float brush_radius,
952 const Object &ob)
953{
954 const float object_scale = mat4_to_scale(ob.object_to_world().ptr());
955
956 return 100.0f / (constant_detail * brush_radius * object_scale);
957}
958
959float constant_to_relative_detail(const float constant_detail,
960 const float brush_radius,
961 const float pixel_radius,
962 const float pixel_size,
963 const Object &ob)
964{
965 const float object_scale = mat4_to_scale(ob.object_to_world().ptr());
966
967 return (pixel_radius / brush_radius) * (RELATIVE_SCALE_FACTOR / pixel_size) *
968 (1.0f / (constant_detail * object_scale));
969}
970
971} // namespace blender::ed::sculpt_paint::dyntopo::detail_size
972
float BKE_brush_radius_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1272
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)
std::variant< std::monostate, int, BMVert * > ActiveVert
Definition BKE_paint.hh:360
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:650
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2797
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2286
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
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:849
@ 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:875
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_UTF8(dst, format,...)
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#define ELEM(...)
#define IFACE_(msgid)
#define CLOG_DEBUG(clg_ref,...)
Definition CLG_log.h:191
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ 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:851
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
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 immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immUniformColor4f(float r, float g, float b, float a)
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
void GPU_line_width(float width)
Definition gpu_state.cc:166
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
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:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#define UI_MAX_DRAW_STR
#define ND_DRAW
Definition WM_types.hh:461
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_TOOLSETTINGS
Definition WM_types.hh:449
#define NC_OBJECT
Definition WM_types.hh:379
#define U
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
GAttributeReader lookup(const StringRef attribute_id) const
Span< NodeT > nodes() const
void foreach_index(Fn &&fn) const
nullptr float
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:3052
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:2628
Bounds< float3 > bounds_get(const Tree &pbvh)
Definition pbvh.cc:1661
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:1040
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)
void vert_random_access_ensure(Object &object)
Definition sculpt.cc:141
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:4644
bool cursor_geometry_info_update(bContext *C, CursorGeometryInfo *out, const float2 &mval, const bool use_sampled_normal)
Definition sculpt.cc:4685
Span< BMVert * > vert_neighbors_get_bmesh(BMVert &vert, BMeshNeighborVerts &r_neighbors)
Definition sculpt.cc:387
float object_space_radius_get(const ViewContext &vc, const Paint &paint, const Brush &brush, const float3 &location, const float scale_factor)
Definition sculpt.cc:114
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:429
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
const int status
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, const PointerRNA *ptr)
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:4468
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3677
#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:392
blender::float3 cursor_normal
Definition BKE_paint.hh:431
bool draw_faded_cursor
Definition BKE_paint.hh:428
float cursor_radius
Definition BKE_paint.hh:429
ActiveVert active_vert() const
Definition paint.cc:2367
blender::float3 cursor_location
Definition BKE_paint.hh:430
std::optional< blender::float3 > cursor_sampled_normal
Definition BKE_paint.hh:432
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:757
short val
Definition WM_types.hh:759
int xy[2]
Definition WM_types.hh:761
int mval[2]
Definition WM_types.hh:763
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:4237