WIP: Sculpt: dynamic topology refactor #104613

Draft
Joseph Eagar wants to merge 331 commits from temp-sculpt-dyntopo into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
138 changed files with 34848 additions and 7510 deletions

View File

@ -118,6 +118,38 @@ class UnifiedPaintPanel:
return row
@staticmethod
def get_dyntopo_prop(context, brush, prop_name):
sculpt = context.tool_settings.sculpt
if not getattr(brush.dyntopo, "is_" + prop_name + "_overridden"):
return getattr(sculpt.dyntopo, prop_name)
else:
return getattr(brush.dyntopo, prop_name)
@staticmethod
def prop_unified_dyntopo(
layout, context, brush, prop_name, text=None, expand=False
):
sculpt = context.tool_settings.sculpt
override_name = "is_" + prop_name + "_overridden"
inherit = not getattr(brush.dyntopo, override_name)
if inherit:
final_dyntopo = sculpt.dyntopo
else:
final_dyntopo = brush.dyntopo
layout = layout.row(align=True)
#layout.enabled = inherit
layout.prop(final_dyntopo, prop_name, text=text, expand=expand)
if not inherit:
layout.prop(
brush.dyntopo, override_name, text="", icon='ERROR', emboss=False
)
@staticmethod
def prop_unified_color(parent, context, brush, prop_name, *, text=None):
ups = context.tool_settings.unified_paint_settings
@ -564,7 +596,11 @@ def brush_settings(layout, context, brush, popover=False):
row.prop(brush, "invert_hardness_pressure", text="")
row.prop(brush, "use_hardness_pressure", text="")
# auto_smooth_factor and use_inverse_smooth_pressure
layout.prop(brush.dyntopo, "disabled")
layout.separator()
layout.label(text="Smooth Settings")
if capabilities.has_auto_smooth:
UnifiedPaintPanel.prop_unified(
layout,
@ -575,12 +611,41 @@ def brush_settings(layout, context, brush, popover=False):
slider=True,
)
if capabilities.has_auto_smooth or brush.sculpt_tool == 'SMOOTH':
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
"auto_smooth_projection",
slider=True,
text="Preserve Volume",
)
is_smooth = capabilities.has_auto_smooth
is_smooth = is_smooth or brush.sculpt_tool in {'SMOOTH', 'SIMPLIFY'}
hard_edge = context.tool_settings.unified_paint_settings.hard_edge_mode
if is_smooth:
UnifiedPaintPanel.prop_unified(
layout,
context,
brush,
"hard_corner_pin",
slider=True,
unified_name="use_unified_hard_corner_pin",
text="Corner Pin",
)
if capabilities.has_auto_smooth or brush.sculpt_tool == 'SMOOTH':
layout.prop(brush, "use_weighted_smooth")
# topology_rake_factor
if (
capabilities.has_topology_rake and
context.sculpt_object.use_dynamic_topology_sculpting
):
layout.prop(brush, "topology_rake_factor", slider=True)
layout.prop(brush, "use_curvature_rake")
# normal_weight
if capabilities.has_normal_weight:
@ -954,6 +1019,8 @@ def brush_settings_advanced(layout, context, brush, popover=False):
use_accumulate = capabilities.has_accumulate
use_frontface = True
layout.prop(brush.dyntopo, "disabled")
col = layout.column(heading="Auto-Masking", align=True)
col.prop(brush, "use_automasking_topology", text="Topology")

View File

@ -1523,6 +1523,10 @@ class _defs_sculpt:
row = layout.row(align=True)
row.prop(props, "deform_axis")
layout.prop(props, "orientation", expand=False)
if props.type in {'SMOOTH', 'SURFACE_SMOOTH', 'ENHANCE_DETAILS', 'SHARPEN'}:
layout.prop(props, "hard_corner_pin", expand=False)
if props.type == 'SURFACE_SMOOTH':
layout.prop(props, "surface_smooth_shape_preservation", expand=False)
layout.prop(props, "surface_smooth_current_vertex", expand=False)

View File

@ -7344,6 +7344,7 @@ class VIEW3D_PT_overlay_edit_curve(Panel):
sub.prop(overlay, "normals_length", text="Normals")
class VIEW3D_PT_overlay_sculpt(Panel):
bl_space_type = 'VIEW_3D'
bl_context = ".sculpt_mode"
@ -7374,7 +7375,6 @@ class VIEW3D_PT_overlay_sculpt(Panel):
sub.active = overlay.show_sculpt_face_sets
row.prop(overlay, "sculpt_mode_face_sets_opacity", text="Face Sets")
class VIEW3D_PT_overlay_sculpt_curves(Panel):
bl_space_type = 'VIEW_3D'
bl_context = ".curves_sculpt"

View File

@ -975,7 +975,7 @@ class VIEW3D_PT_tools_brush_falloff_normal(View3DPaintPanel, Panel):
# TODO, move to space_view3d.py
class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
bl_label = "Dyntopo"
bl_label = "Dynamic Topology"
bl_options = {'DEFAULT_CLOSED'}
bl_ui_units_x = 12
@ -1007,23 +1007,119 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
col = layout.column()
col.active = context.sculpt_object.use_dynamic_topology_sculpting
col.prop(sculpt, "use_dyntopo")
sub = col.column()
sub.active = (brush and brush.sculpt_tool != 'MASK')
if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}:
sub.active = bool(brush)
detail_mode = UnifiedPaintPanel.get_dyntopo_prop(context, brush, "mode")
if detail_mode in {'CONSTANT', 'MANUAL'}:
row = sub.row(align=True)
row.prop(sculpt, "constant_detail_resolution")
UnifiedPaintPanel.prop_unified_dyntopo(
row,
context,
brush,
"constant_detail"
)
props = row.operator("sculpt.sample_detail_size", text="", icon='EYEDROPPER')
props.mode = 'DYNTOPO'
elif (sculpt.detail_type_method == 'BRUSH'):
sub.prop(sculpt, "detail_percent")
elif detail_mode == 'BRUSH':
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"detail_percent"
)
else:
sub.prop(sculpt, "detail_size")
sub.prop(sculpt, "detail_refine_method", text="Refine Method")
sub.prop(sculpt, "detail_type_method", text="Detailing")
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"detail_size"
)
if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}:
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"subdivide"
)
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"collapse"
)
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"cleanup"
)
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"mode",
expand=False
)
#NotForPR
if 0:
scene = context.scene
def do_prop(name, text=None):
if text is None: text = name
if name in scene:
sub.prop(scene, "[\"%s\"]" % name, text=text)
do_prop("dparam1", text="p0")
do_prop("dparam2", text="p1")
do_prop("dparam3", text="p2")
do_prop("dparam4", text="p3")
do_prop("dparam5", text="p4")
if UnifiedPaintPanel.get_dyntopo_prop(context, brush, "mode") in {'CONSTANT', 'MANUAL'}:
col.operator("sculpt.detail_flood_fill")
col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "interactive")
col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "developer")
if WindowManager.operator_properties_last("sculpt.detail_flood_fill").developer:
col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "emulate_brush")
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"spacing",
expand=True
)
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"radius_scale",
expand=True
)
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"repeat",
expand=True
)
UnifiedPaintPanel.prop_unified_dyntopo(
sub,
context,
brush,
"quality",
expand=True
)
class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel):
bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
@ -1074,12 +1170,58 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel):
tool_settings = context.tool_settings
sculpt = tool_settings.sculpt
brush = sculpt.brush
ups = tool_settings.unified_paint_settings
col = layout.column(heading="Display", align=True)
col.prop(sculpt, "show_low_resolution")
col.prop(sculpt, "use_sculpt_delay_updates")
col.prop(sculpt, "use_deform_only")
import bpy
if bpy.app.debug_value == 889:
col.prop(tool_settings, "show_origco")
col.prop(ups, "distort_correction_mode")
col.label(text="Smooth Boundaries")
col = layout.column(align=True)
col.prop(ups, "smooth_boundary_seam", text="Relax Marked Seams")
col.prop(ups, "smooth_boundary_uv", text="Relax UV Seams")
col.prop(ups, "smooth_boundary_face_set", text="Relax Face Sets")
col.separator();
row = col.row()
row.enabled = ups.smooth_boundary_face_set
row.prop(ups, "hard_edge_mode", text="Crease Face Sets")
UnifiedPaintPanel.prop_unified(
col,
context,
brush,
"hard_corner_pin",
slider=True,
unified_name = "use_unified_hard_corner_pin",
text="Corner Pin"
)
col.separator();
col.prop(ups, "smooth_boundary_mesh", text="Crease Boundaries")
col.prop(ups, "smooth_boundary_sharp_mark", text="Crease Marked Sharp")
col.prop(ups, "smooth_boundary_sharp_angle", text="Crease By Angle")
row = col.row()
row.enabled = ups.smooth_boundary_sharp_angle
UnifiedPaintPanel.prop_unified(
row,
context,
tool_settings.unified_paint_settings,
"sharp_angle_limit",
unified_name = "use_unified_sharp_angle_limit"
)
class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel):
bl_context = ".sculpt_mode" # dot on purpose (access from topbar)

View File

@ -22,6 +22,9 @@ struct MTex;
struct Scene;
struct ToolSettings;
struct UnifiedPaintSettings;
struct DynTopoSettings;
struct Sculpt;
struct CurveMapping;
// enum eCurveMappingPreset;
@ -56,6 +59,12 @@ Brush *BKE_brush_first_search(Main *bmain, eObjectMode ob_mode);
void BKE_brush_sculpt_reset(Brush *brush);
/* Which dyntopo settings are inherited by this brush from scene
* defaults. In most cases this is everything except for the
* local dyntopo disable flag.
*/
int BKE_brush_dyntopo_inherit_flags(Brush *brush);
/**
* Create a set of grease pencil Drawing presets.
*/
@ -158,7 +167,6 @@ void BKE_brush_input_samples_set(const Scene *scene, Brush *brush, int value);
bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush);
bool BKE_brush_use_alpha_pressure(const Brush *brush);
bool BKE_brush_use_size_pressure(const Brush *brush);
bool BKE_brush_sculpt_has_secondary_color(const Brush *brush);
/**
@ -175,6 +183,8 @@ void BKE_brush_scale_size(int *r_brush_size,
float new_unprojected_radius,
float old_unprojected_radius);
void BKE_brush_default_input_curves_set(Brush *brush);
/* Returns true if a brush requires a cube
* (often presented to the user as a square) tip inside a specific paint mode.
*/
@ -193,3 +203,13 @@ bool BKE_brush_has_cube_tip(const Brush *brush, PaintMode paint_mode);
/* debugging only */
void BKE_brush_debug_print_state(Brush *br);
void BKE_brush_get_dyntopo(Brush *brush, Sculpt *sd, DynTopoSettings *out);
bool BKE_brush_hard_edge_mode_get(const Scene *scene, const Brush *brush);
void BKE_brush_hard_edge_mode_set(Scene *scene, Brush *brush, bool val);
float BKE_brush_hard_corner_pin_get(const Scene *scene, const Brush *brush);
float BKE_brush_fset_slide_get(const Scene *scene, const Brush *brush);
float BKE_brush_curve_strength_ex(
int curve_preset, const CurveMapping *curve, float p, const float len, const bool invert);

View File

@ -122,6 +122,8 @@ bool CustomData_has_interp(const CustomData *data);
*/
bool CustomData_bmesh_has_free(const CustomData *data);
bool CustomData_layout_is_same(const struct CustomData *_a, const struct CustomData *_b);
/**
* Checks if any of the custom-data layers is referenced.
*/
@ -175,6 +177,12 @@ void CustomData_copy_layout(const CustomData *source,
/* BMESH_TODO, not really a public function but `readfile.cc` needs it. */
void CustomData_update_typemap(CustomData *data);
/* Copies all customdata layers without allocating data
* and without respect to type masks or CD_FLAG_NO_COPY
* or CD_FLAG_TEMPORARY flags.
*/
void CustomData_copy_all_layout(const struct CustomData *source, struct CustomData *dest);
/**
* Copies all layers from source to destination that don't exist there yet.
*/
@ -344,6 +352,16 @@ void CustomData_copy_data_layer(const CustomData *source,
int src_index,
int dst_index,
int count);
void CustomData_bmesh_poison(const struct CustomData *cdata, void *block);
void CustomData_bmesh_unpoison(const struct CustomData *cdata, void *block);
/* Swap attributes. Does not respect CD_FLAG_ELEM_NOCOPY. */
void CustomData_bmesh_swap_data(struct CustomData *source,
struct CustomData *dest,
void *src_block,
void **dest_block);
void CustomData_copy_data_named(
const CustomData *source, CustomData *dest, int source_index, int dest_index, int count);
void CustomData_copy_elements(eCustomDataType type,
@ -357,6 +375,12 @@ void CustomData_copy_elements(eCustomDataType type,
*/
void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_block);
/** XXX deprecated */
void CustomData_bmesh_copy_data(const CustomData *source,
CustomData *dest,
void *src_block,
void **dest_block);
/** Holds the minimal data necessary to copy data blocks from one custom data format to another. */
struct BMCustomDataCopyMap {
struct TrivialCopy {
@ -454,6 +478,13 @@ void CustomData_bmesh_interp(CustomData *data,
const float *sub_weights,
int count,
void *dst_block);
void CustomData_bmesh_interp_ex(struct CustomData *data,
const void **src_blocks,
const float *weights,
const float *sub_weights,
int count,
void *dst_block,
eCustomDataMask typemask);
/**
* Swap data inside each item, for all layers.
@ -803,6 +834,7 @@ void CustomData_blend_write(BlendWriter *writer,
void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count);
size_t CustomData_get_elem_size(const CustomDataLayer *layer);
void CustomData_regen_active_refs(CustomData *data);
#ifndef NDEBUG
struct DynStr;

View File

@ -0,0 +1,171 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* Dynamic remesher for PBVH
*/
#include "BKE_paint.hh"
#include "BKE_pbvh_api.hh"
#include "BLI_math_geom.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector_types.hh"
#define DYNTOPO_CD_INTERP
#define DYNTOPO_DYNAMIC_TESS
struct SculptSearchSphereData;
namespace blender::bke::dyntopo {
float dist_to_tri_sphere_simple(float p[3], float v1[3], float v2[3], float v3[3], float n[3]);
struct BrushTester {
bool is_sphere_or_tube;
virtual ~BrushTester() {}
virtual bool vert_in_range(BMVert * /*v*/)
{
return true;
}
virtual bool tri_in_range(BMVert * /*tri*/[3], float * /*no*/)
{
return true;
}
};
struct BrushSphere : public BrushTester {
BrushSphere(float3 center, float radius)
: center_(center), radius_(radius), radius_squared_(radius * radius)
{
is_sphere_or_tube = true;
}
bool vert_in_range(BMVert *v) override
{
return len_squared_v3v3(center_, v->co) <= radius_squared_;
}
bool tri_in_range(BMVert *tri[3], float *no) override
{
/* Check if triangle intersects the sphere */
float dis = dist_to_tri_sphere_simple((float *)center_,
(float *)tri[0]->co,
(float *)tri[1]->co,
(float *)tri[2]->co,
(float *)no);
return dis <= radius_squared_;
}
inline const float3 &center()
{
return center_;
}
inline float radius_squared()
{
return radius_squared_;
}
inline float radius()
{
return radius_;
}
protected:
float3 center_;
float radius_, radius_squared_;
};
struct BrushTube : public BrushSphere {
BrushTube(float3 center, float3 view_normal, float radius)
: BrushSphere(center, radius), view_normal_(view_normal)
{
project_plane_normalized_v3_v3v3(center_proj_, center_, view_normal_);
}
bool vert_in_range(BMVert *v) override
{
float c[3];
project_plane_normalized_v3_v3v3(c, v->co, view_normal_);
return len_squared_v3v3(center_proj_, c) <= radius_squared_;
}
bool tri_in_range(BMVert *tri[3], float * /*no*/) override
{
float c[3];
float tri_proj[3][3];
project_plane_normalized_v3_v3v3(tri_proj[0], tri[0]->co, view_normal_);
project_plane_normalized_v3_v3v3(tri_proj[1], tri[1]->co, view_normal_);
project_plane_normalized_v3_v3v3(tri_proj[2], tri[2]->co, view_normal_);
closest_on_tri_to_point_v3(c, center_proj_, tri_proj[0], tri_proj[1], tri_proj[2]);
/* Check if triangle intersects the sphere */
return len_squared_v3v3(center_proj_, c) <= radius_squared_;
}
private:
float3 center_proj_, view_normal_;
};
struct BrushNoRadius : public BrushTester {
BrushNoRadius()
{
is_sphere_or_tube = false;
}
};
typedef float (*DyntopoMaskCB)(PBVHVertRef vertex, void *userdata);
enum PBVHTopologyUpdateMode {
PBVH_None = 0,

Comment style.

Comment style.
PBVH_Subdivide = 1 << 0,
PBVH_Collapse = 1 << 1,
PBVH_Cleanup = 1 << 2, // dissolve verts surrounded by either 3 or 4 triangles then triangulate
PBVH_LocalSubdivide = 1 << 3,
PBVH_LocalCollapse = 1 << 4
};
ENUM_OPERATORS(PBVHTopologyUpdateMode, PBVH_LocalCollapse);
void detail_size_set(PBVH *pbvh, float detail_size, float detail_range);
bool remesh_topology(blender::bke::dyntopo::BrushTester *brush_tester,
struct Object *ob,
PBVH *pbvh,
PBVHTopologyUpdateMode mode,
bool use_frontface,
blender::float3 view_normal,
bool updatePBVH,
DyntopoMaskCB mask_cb,
void *mask_cb_data,
float quality);
bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *tester,
struct Object *ob,
PBVH *pbvh,
bool (*searchcb)(const PBVHNode &node,
const float3 &location,
const float radius_sq,
const bool original),
void (*undopush)(PBVHNode *node, void *data),
const blender::float3 &location,
const float radius_sq,
const bool original,
PBVHTopologyUpdateMode mode,
bool use_frontface,
blender::float3 view_normal,
bool updatePBVH,
DyntopoMaskCB mask_cb,
void *mask_cb_data,
float quality,
void *searchData);
void after_stroke(PBVH *pbvh, bool force_balance);
} // namespace blender::bke::dyntopo

View File

@ -0,0 +1,142 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bli
*
* A simple set class that's optimized for iteration.
* Elements are stored in both a blender::Map and a flat array.
*/
#include "BLI_compiler_attrs.h"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_vector.hh"
#include <utility>
#include <cstdio>
namespace blender::bke::dyntopo {
template<typename T> class DyntopoSet {
public:
DyntopoSet(int64_t reserve)
{
elem_to_index_.reserve(reserve);
index_to_elem_.reserve(reserve);
}
DyntopoSet() {}
DyntopoSet(const DyntopoSet &) = delete;
struct iterator {
iterator() : set_(nullptr), i_(-1) {}
iterator(DyntopoSet *set, int i) : set_(set), i_(i) {}
iterator(const iterator &b) : set_(b.set_), i_(b.i_) {}
iterator &operator=(const iterator &b)
{
set_ = b.set_;
i_ = b.i_;
return *this;
}
inline T *operator*()
{
return set_->index_to_elem_[i_];
}
inline iterator &operator++()
{
i_++;
while (i_ < set_->index_to_elem_.size() && set_->index_to_elem_[i_] == nullptr) {
i_++;
}
return *this;
}
inline bool operator==(const iterator &b)
{
return b.i_ == i_;
}
inline bool operator!=(const iterator &b)
{
return b.i_ != i_;
}
private:
DyntopoSet *set_;
int i_;
};
bool contains(T *key)
{
return elem_to_index_.contains(key);
}
void remove(T *key)
{
if (!elem_to_index_.contains(key)) {
return;
}
int i = elem_to_index_.pop(key);
index_to_elem_[i] = nullptr;
freelist_.append(i);
}
/* Add key, returns true if key was already in set. */
bool add(T *key)
{
int i;
if (freelist_.size() > 0) {
i = freelist_.last();
}
else {
i = index_to_elem_.size();
}
bool was_added = elem_to_index_.add(key, i);
if (was_added) {
if (i == index_to_elem_.size()) {
index_to_elem_.append(key);
}
else {
freelist_.pop_last();
index_to_elem_[i] = key;
}
}
return was_added;
}
int size()
{
return elem_to_index_.size();
}
iterator begin()
{
int i = 0;
while (i < index_to_elem_.size() && index_to_elem_[i] == nullptr) {
i++;
}
return iterator(this, i);
}
iterator end()
{
return iterator(this, index_to_elem_.size());
}
private:
blender::Map<T *, int> elem_to_index_;
blender::Vector<T *> index_to_elem_;
blender::Vector<int> freelist_;
};
} // namespace blender::bke::dyntopo

View File

@ -15,16 +15,26 @@
#include "BLI_offset_indices.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_set.hh"
#include "BLI_utildefines.h"
#include "DNA_brush_enums.h"
#include "DNA_brush_types.h"
#include "DNA_customdata_types.h"
#include "DNA_object_enums.h"
#include "DNA_scene_enums.h"
#include "BKE_pbvh.hh"
#include "bmesh.hh"
#include <type_traits>
struct SculptAttribute;
struct BMFace;
struct BMLog;
struct BMesh;
struct BMIdMap;
struct BMLog;
struct BlendDataReader;
struct BlendLibReader;
struct BlendWriter;
@ -232,7 +242,7 @@ bool BKE_paint_always_hide_test(const Object *ob);
*/
bool paint_is_grid_face_hidden(blender::BoundedBitSpan grid_hidden, int gridsize, int x, int y);
/**
* Return true if all vertices in the face are visible, false otherwise.
* Return true if face is visible.
*/
bool paint_is_bmesh_face_hidden(const BMFace *f);
@ -247,6 +257,7 @@ void BKE_paint_face_set_overlay_color_get(int face_set, int seed, uchar r_color[
bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups,
Brush *brush,
const float mouse_pos[2],
const float initial_mouse_pos[2],
PaintMode paint_mode,
bool stroke_has_started);
void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, float rotation);
@ -337,6 +348,13 @@ struct SculptBoundary {
* a distance of 0. */
float *distance;
float (*smoothco)[3];
float *boundary_dist; // distances from verts to boundary
float (*boundary_tangents)[3];
PBVHVertRef *boundary_closest;
int sculpt_totvert;
/* Data for drawing the preview. */
SculptBoundaryPreviewEdge *edges;
int edges_capacity;
@ -370,7 +388,7 @@ struct SculptBoundary {
/* Bend Deform type. */
struct {
float (*pivot_rotation_axis)[3];
float (*pivot_positions)[3];
float (*pivot_positions)[4];
} bend;
/* Slide Deform type. */
@ -383,6 +401,14 @@ struct SculptBoundary {
blender::float3 rotation_axis;
blender::float3 pivot_position;
} twist;
/* Cicrle Deform type. */
struct {
float (*origin)[3];
float *radius;
} circle;
int deform_target;
};
struct SculptFakeNeighbors {
@ -392,7 +418,7 @@ struct SculptFakeNeighbors {
float current_max_distance;
/* Indexed by vertex, stores the vertex index of its fake neighbor if available. */
int *fake_neighbor_index;
PBVHVertRef *fake_neighbor_index;
};
/* Session data (mode-specific) */
@ -408,6 +434,8 @@ struct SculptAttributeParams {
*/
int permanent : 1; /* Cannot be combined with simple_array. */
int stroke_only : 1; /* Release layer at end of struct */
int nointerp : 1;
int nocopy : 1;
};
struct SculptAttribute {
@ -440,6 +468,10 @@ struct SculptAttribute {
* inside of SculptSession.temp_attribute are used.
*/
bool used;
bool is_empty() const
{
return !used;
}
};
#define SCULPT_MAX_ATTRIBUTES 64
@ -457,23 +489,65 @@ struct SculptAttribute {
/* Convenience pointers for standard sculpt attributes. */
struct SculptAttributePointers {
SculptAttribute *face_set;
/* Persistent base. */
SculptAttribute *persistent_co;
SculptAttribute *persistent_no;
SculptAttribute *persistent_disp;
/* Layer brush. */
SculptAttribute *layer_displayment;
/* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
* initialized in #auto_mask::cache_init when needed. */
SculptAttribute *automasking_factor;
SculptAttribute *automasking_occlusion; /* CD_PROP_INT8. */
SculptAttribute *automasking_factor; /* Stroke only. */
SculptAttribute *automasking_occlusion; /* CD_PROP_INT8, stroke only */
SculptAttribute *automasking_stroke_id;
SculptAttribute *automasking_cavity;
SculptAttribute *automasking_cavity; /* Stroke only. */
SculptAttribute *topology_island_key; /* CD_PROP_INT8 */
/* BMesh */
SculptAttribute *dyntopo_node_id_vertex;
SculptAttribute *dyntopo_node_id_face;
SculptAttribute *rake_temp;
SculptAttribute *face_areas;
SculptAttribute *smooth_bdist;
SculptAttribute *smooth_vel;
/* Sculpt utility attributes. */
SculptAttribute *stroke_id;
SculptAttribute *boundary_flags; /* CD_PROP_INT32, vert */
SculptAttribute *edge_boundary_flags; /* CD_PROP_INT32, vert */
SculptAttribute *valence; /* CD_PROP_INT32, vert */
SculptAttribute *flags; /* CD_PROP_INT8, vert */
SculptAttribute *orig_co, *orig_no; /* CD_PROP_FLOAT3, vert */
SculptAttribute *orig_fsets; /* CD_PROP_INT32, face */
SculptAttribute *orig_color; /* CD_PROP_FLOAT4, vert */
SculptAttribute *orig_mask; /* CD_PROP_FLOAT vert */
SculptAttribute *curvature_dir; /* Curvature direction vectors, CD_PROP_FLOAT3 */
SculptAttribute *smear_previous;
SculptAttribute *hide_poly;
SculptAttribute *limit_surface;
SculptAttribute *layer_disp;
SculptAttribute *layer_id;
SculptAttribute *prefairing_co;
SculptAttribute *fairing_fade;
SculptAttribute *fairing_mask;
/* Stores the displacement produced by the laplacian step of HC smooth */
SculptAttribute *laplacian_disp;
/* Enhance Details */
SculptAttribute *detail_directions; /* Stroke only. */
};
struct SculptSession {
@ -489,8 +563,14 @@ struct SculptSession {
/* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */
blender::MutableSpan<blender::float3> vert_positions;
blender::Span<blender::int2> edges;
blender::OffsetIndices<int> faces;
blender::Span<int> corner_verts;
blender::Span<int> corner_edges;
const int *material_index;
CustomData *vdata, *edata, *ldata, *pdata;
/* These contain the vertex and poly counts of the final mesh. */
int totvert, faces_num;
@ -518,21 +598,43 @@ struct SculptSession {
/* Mesh Face Sets */
/* Total number of faces of the base mesh. */
int totfaces;
int totedges, totloops, totfaces;
/* The 0 ID is not used by the tools or the visibility system, it is just used when creating new
* geometry (the trim tool, for example) to detect which geometry was just added, so it can be
* assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set
* to 0. */
const int *face_sets;
* to 0.
*/
int *face_sets;
/**
* A reference to the ".hide_poly" attribute, to store whether (base) faces are hidden.
* May be null.
*/
const bool *hide_poly;
bool *hide_poly;
bool *select_poly;
/* BMesh for dynamic topology sculpting */
BMesh *bm;
BMIdMap *bm_idmap;
/* TODO: get rid of these cd_ members and use
* .attrs.XXX.bmesh_cd_offset directly.
*/
int cd_vert_node_offset;
int cd_face_node_offset;
int cd_vcol_offset;
int cd_vert_mask_offset;
int cd_faceset_offset;
int cd_face_areas;
float *vmask;
int totuv;
/* Reproject customdata during smooth. */
eAttrCorrectMode distort_correction_mode;
/* Undo/redo log for dynamic topology sculpting */
BMLog *bm_log;
@ -541,6 +643,11 @@ struct SculptSession {
/* PBVH acceleration structure */
PBVH *pbvh;
PBVH *last_pbvh;
/* Setting this to true allows a PBVH rebuild when evaluating the object even if the stroke or
* filter caches are active. */
bool needs_pbvh_rebuild;
/* Object is deformed with some modifiers. */
bool deform_modifiers_active;
@ -560,8 +667,8 @@ struct SculptSession {
/* Cursor data and active vertex for tools */
PBVHVertRef active_vertex;
PBVHFaceRef active_face;
int active_face_index;
int active_grid_index;
/* When active, the cursor draws with faded colors, indicating that there is an action
@ -578,6 +685,7 @@ struct SculptSession {
* when
* the gesture starts (intersection with the surface and if they ray hit the surface or not).
*/
blender::float3 gesture_initial_back_location;
blender::float3 gesture_initial_location;
blender::float3 gesture_initial_normal;
bool gesture_initial_hit;
@ -586,6 +694,14 @@ struct SculptSession {
RegionView3D *rv3d;
View3D *v3d;
Scene *scene;
int cd_origvcol_offset;
int cd_origco_offset;
int cd_origno_offset;
/* Face Sets by topology. */
int face_set_last_created;
PBVHFaceRef face_set_last_poly;
PBVHEdgeRef face_set_last_edge;
/* Dynamic mesh preview */
PBVHVertRef *preview_vert_list;
@ -659,7 +775,7 @@ struct SculptSession {
*/
bool sticky_shading_color;
uchar stroke_id;
ushort stroke_id;
/**
* Last used painting canvas key.
@ -667,18 +783,42 @@ struct SculptSession {
char *last_paint_canvas_key;
blender::float3 last_normal;
/* Used to derive initial tip rotation. */
float last_grab_delta[3];
blender::Span<blender::float3> poly_normals;
int last_automasking_settings_hash;
uchar last_automask_stroke_id;
bool *sharp_edge;
bool *seam_edge;
bool islands_valid; /* Is attrs.topology_island_key valid? */
bool hard_edge_mode;
DynTopoSettings cached_dyntopo;
float sharp_angle_limit;
eSculptBoundary smooth_boundary_flag;
};
void BKE_sculptsession_free(Object *ob);
void BKE_sculptsession_free_deformMats(SculptSession *ss);
void BKE_sculptsession_free_vwpaint_data(SculptSession *ss);
void BKE_sculptsession_bm_to_me(Object *ob, bool reorder);
void BKE_sculptsession_bm_to_me_for_render(Object *object);
int BKE_sculptsession_vertex_count(const SculptSession *ss);
void BKE_sculpt_ensure_idmap(Object *ob);
void BKE_sculpt_ensure_origco(Object *ob);
void BKE_sculpt_ensure_origmask(Object *ob);
void BKE_sculpt_ensure_origcolor(Object *ob);
void BKE_sculpt_ensure_origfset(Object *ob);
void BKE_sculpt_ensure_curvature_dir(Object *ob);
/* Ensures Sculpt_flags and sculpt_valence layers. */
void BKE_sculpt_ensure_sculpt_layers(Object *ob);
/* Ensure an attribute layer exists. */
SculptAttribute *BKE_sculpt_attribute_ensure(Object *ob,
blender::bke::AttrDomain domain,
@ -700,10 +840,34 @@ void BKE_sculpt_attribute_destroy_temporary_all(Object *ob);
/* Destroy attributes that were marked as stroke only in SculptAttributeParams. */
void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob);
/* Release a SculptAttribute ref without destroying the underlying attribute. */
void BKE_sculpt_attribute_release_ref(Object *ob, SculptAttribute *attr);
SculptAttribute BKE_sculpt_find_attribute(Object *ob, const char *name);
bool BKE_sculpt_init_flags_valence(Object *ob, PBVH *pbvh, int totvert, bool reset_flags);
BMesh *BKE_sculptsession_empty_bmesh_create(void);
void BKE_sculptsession_bmesh_attr_update_internal(Object *ob);
/* Ensures non-temporary attributes in me exist in the sculpt mesh, or vice
* versa if load_to_mesh is true.
*/
void BKE_sculptsession_sync_attributes(Object *ob, Mesh *me, bool load_to_mesh);
void BKE_sculptsession_bmesh_add_layers(Object *ob);
void BKE_sculptsession_update_attr_refs(Object *ob);
int BKE_sculptsession_get_totvert(const SculptSession *ss);
void BKE_sculpt_distort_correction_set(Object *ob, eAttrCorrectMode value);
void BKE_sculptsession_free_attribute_refs(Object *ob);
/**
* Create new color layer on object if it doesn't have one and if experimental feature set has
* sculpt vertex color enabled. Returns truth if new layer has been added, false otherwise.
*/
void BKE_sculpt_color_layer_create_if_needed(Object *object);
/**
@ -722,6 +886,7 @@ MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob)
* Update the pointer to the ".hide_poly" attribute. This is necessary because it is dynamically
* created, removed, and made mutable.
*/
bool *BKE_sculpt_hide_poly_ensure(Object *ob);
void BKE_sculpt_hide_poly_pointer_update(Object &object);
/**
@ -750,6 +915,23 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg)
*/
bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const RegionView3D *rv3d);
char BKE_get_fset_boundary_symflag(Object *object);
bool BKE_sculpt_has_persistent_base(SculptSession *ss);
/**
* Sets ob->sculpt->bm. The PBVH will be recreated if it exists
* (if it's of type PBVH_BMESH) as will ob->sculpt->bm_idmap.
*
* Note: BMLog (ob->sculpt->bm_log) doesn't need to be reallocated.
*/
void BKE_sculpt_set_bmesh(Object *ob, BMesh *bm, bool free_existing = true);
enum {
SCULPT_MASK_LAYER_CALC_VERT = (1 << 0),
SCULPT_MASK_LAYER_CALC_LOOP = (1 << 1),
};
/* paint_vertex.cc */
/**
@ -781,4 +963,237 @@ bool BKE_paint_canvas_image_get(PaintModeSettings *settings,
ImageUser **r_image_user);
int BKE_paint_canvas_uvmap_layer_index_get(const PaintModeSettings *settings, Object *ob);
void BKE_sculpt_check_cavity_curves(Sculpt *sd);
CurveMapping *BKE_sculpt_default_cavity_curve();
CurveMapping *BKE_sculpt_default_cavity_curve(void);
namespace blender::bke::paint {
/* Base implementation for vertex_attr_*** and face_attr_*** methods.
* Returns a pointer to the attribute data (as defined by attr) for elem.
*/
template<typename T, typename ElemRef = PBVHVertRef>
static T *elem_attr_ptr(const ElemRef elem, const SculptAttribute *attr)
{
void *ptr = nullptr;
if (attr->data) {
char *p = (char *)attr->data;
int idx = (int)elem.i;
if (attr->data_for_bmesh) {
BMElem *e = (BMElem *)elem.i;
idx = e->head.index;
}
ptr = p + attr->elem_size * (int)idx;
}
else {
BMElem *v = (BMElem *)elem.i;
ptr = BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset);
}
return static_cast<T *>(ptr);
}
/*
* Get a pointer to attribute data at vertex.
*
* Example: float *persistent_co = vertex_attr_ptr<float>(vertex, ss->attrs.persistent_co);
*/
template<typename T>
static T *vertex_attr_ptr(const PBVHVertRef vertex, const SculptAttribute *attr)
{
return elem_attr_ptr<T, PBVHVertRef>(vertex, attr);
}
/*
* Get attribute data at vertex.
*
* Example: float weight = vertex_attr_get<float>(vertex, ss->attrs.automasking_factor);
*/
template<typename T>
static T vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr)
{
return *vertex_attr_ptr<T>(vertex, attr);
}
/*
* Set attribute data at vertex.
*
* vertex_attr_set<float>(vertex, ss->attrs.automasking_factor, 1.0f);
*/
template<typename T>
static void vertex_attr_set(const PBVHVertRef vertex, const SculptAttribute *attr, T data)
{
*vertex_attr_ptr<T>(vertex, attr) = data;
}
/*
* Get a pointer to attribute data at vertex.
*
* Example: float *persistent_co = vertex_attr_ptr<float>(vertex, ss->attrs.persistent_co);
*/
template<typename T> static T *edge_attr_ptr(const PBVHEdgeRef edge, const SculptAttribute *attr)
{
return elem_attr_ptr<T, PBVHEdgeRef>(edge, attr);
}
/*
* Get attribute data at vertex.
*
* Example: float weight = vertex_attr_get<float>(vertex, ss->attrs.automasking_factor);
*/
template<typename T> static T edge_attr_get(const PBVHEdgeRef edge, const SculptAttribute *attr)
{
return *edge_attr_ptr<T>(edge, attr);
}
/*