This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/sculpt_paint/sculpt_automasking.c
2020-10-16 15:33:00 +02:00

382 lines
11 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup edsculpt
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_task.h"
#include "DNA_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "DEG_depsgraph.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
#include "WM_types.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "bmesh.h"
#include <math.h>
#include <stdlib.h>
AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss)
{
if (ss->cache) {
return ss->cache->automasking;
}
if (ss->filter_cache) {
return ss->filter_cache->automasking;
}
return NULL;
}
bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
const Brush *br,
const eAutomasking_flag mode)
{
if (br) {
return br->automasking_flags & mode || sd->automasking_flags & mode;
}
return sd->automasking_flags & mode;
}
bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
{
if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
return false;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {
return true;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_FACE_SETS)) {
return true;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
return true;
}
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
return true;
}
return false;
}
static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush)
{
if (brush) {
return sculpt->automasking_flags | brush->automasking_flags;
}
return sculpt->automasking_flags;
}
static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush)
{
const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush);
if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
return true;
}
if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
return brush && brush->automasking_boundary_edges_propagation_steps != 1;
}
if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
return brush && brush->automasking_boundary_edges_propagation_steps != 1;
}
return false;
}
float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert)
{
if (!automasking) {
return 1.0f;
}
/* If the cache is initialized with valid info, use the cache. This is used when the
* automasking information can't be computed in real time per vertex and needs to be
* initialized for the whole mesh when the stroke starts. */
if (automasking->factor) {
return automasking->factor[vert];
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
if (!SCULPT_vertex_has_face_set(ss, vert, automasking->settings.initial_face_set)) {
return 0.0f;
}
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
if (SCULPT_vertex_is_boundary(ss, vert)) {
return 0.0f;
}
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
if (!SCULPT_vertex_has_unique_face_set(ss, vert)) {
return 0.0f;
}
}
return 1.0f;
}
void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
{
if (!automasking) {
return;
}
MEM_SAFE_FREE(automasking->factor);
MEM_SAFE_FREE(automasking);
}
static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
{
/* 2D falloff is not constrained by radius. */
if (br->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
return false;
}
if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)) {
return true;
}
return false;
}
typedef struct AutomaskFloodFillData {
float *automask_factor;
float radius;
bool use_radius;
float location[3];
char symm;
} AutomaskFloodFillData;
static bool automask_floodfill_cb(
SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
{
AutomaskFloodFillData *data = userdata;
data->automask_factor[to_v] = 1.0f;
data->automask_factor[from_v] = 1.0f;
return (!data->use_radius ||
SCULPT_is_vertex_inside_brush_radius_symm(
SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm));
}
static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert(!"Topology masking: pmap missing");
return NULL;
}
const int totvert = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totvert; i++) {
automask_factor[i] = 0.0f;
}
/* Flood fill automask to connected vertices. Limited to vertices inside
* the brush radius if the tool requires it. */
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
const float radius = ss->cache ? ss->cache->radius : FLT_MAX;
SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
AutomaskFloodFillData fdata = {
.automask_factor = automask_factor,
.radius = radius,
.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush),
.symm = SCULPT_mesh_symmetry_xyz_get(ob),
};
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
return automask_factor;
}
static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
return NULL;
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert(!"Face Sets automasking: pmap missing");
return NULL;
}
int tot_vert = SCULPT_vertex_count_get(ss);
int active_face_set = SCULPT_active_face_set_get(ss);
for (int i = 0; i < tot_vert; i++) {
if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
automask_factor[i] *= 0.0f;
}
}
return automask_factor;
}
#define EDGE_DISTANCE_INF -1
float *SCULPT_boundary_automasking_init(Object *ob,
eBoundaryAutomaskMode mode,
int propagation_steps,
float *automask_factor)
{
SculptSession *ss = ob->sculpt;
if (!ss->pmap) {
BLI_assert(!"Boundary Edges masking: pmap missing");
return NULL;
}
const int totvert = SCULPT_vertex_count_get(ss);
int *edge_distance = MEM_callocN(sizeof(int) * totvert, "automask_factor");
for (int i = 0; i < totvert; i++) {
edge_distance[i] = EDGE_DISTANCE_INF;
switch (mode) {
case AUTOMASK_INIT_BOUNDARY_EDGES:
if (SCULPT_vertex_is_boundary(ss, i)) {
edge_distance[i] = 0;
}
break;
case AUTOMASK_INIT_BOUNDARY_FACE_SETS:
if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
edge_distance[i] = 0;
}
break;
}
}
for (int propagation_it = 0; propagation_it < propagation_steps; propagation_it++) {
for (int i = 0; i < totvert; i++) {
if (edge_distance[i] == EDGE_DISTANCE_INF) {
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
if (edge_distance[ni.index] == propagation_it) {
edge_distance[i] = propagation_it + 1;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
}
}
}
for (int i = 0; i < totvert; i++) {
if (edge_distance[i] != EDGE_DISTANCE_INF) {
const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps);
const float edge_boundary_automask = pow2f(p);
automask_factor[i] *= (1.0f - edge_boundary_automask);
}
}
MEM_SAFE_FREE(edge_distance);
return automask_factor;
}
static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
SculptSession *ss,
Sculpt *sd,
Brush *brush)
{
automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss);
}
AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
return NULL;
}
AutomaskingCache *automasking = MEM_callocN(sizeof(AutomaskingCache), "automasking cache");
SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush);
SCULPT_boundary_info_ensure(ob);
if (!SCULPT_automasking_needs_factors_cache(sd, brush)) {
return automasking;
}
automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
for (int i = 0; i < totvert; i++) {
automasking->factor[i] = 1.0f;
}
const int boundary_propagation_steps = brush ?
brush->automasking_boundary_edges_propagation_steps :
1;
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_topology_automasking_init(sd, ob, automasking->factor);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(
ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(
ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
}
return automasking;
}