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
Pablo Dobarro 4818ed1c76 Cleanup: Unindent if statements in sculpt tools code
This removes indentations from if statements by converting them to early
returns and continue.
Most of the code of brushes and tools has loops with a full indented
body inside of an if, which was also copied into some of the new tools.

Reviewed By: JacquesLucke

Differential Revision: https://developer.blender.org/D10333
2021-02-10 18:06:56 +01:00

384 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) {
continue;
}
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) {
continue;
}
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;
}