Commit D14179: Revamp Vertex Paint With C++
- Verrtex paint mode has been refactored into C++ templates. It now works with both byte and float colors and point & corner attribute domains. - There is a new API for mixing colors (also based on C++ templates). Unlike the existing APIs byte and float colors are interpolated identically. Interpolation does happen in a squared rgb space, this may be changed in the future. - Vertex paint now uses the sculpt undo system. Reviewed By: Brecht Van Lommel. Differential Revision: https://developer.blender.org/D14179 Ref D14179
This commit is contained in:
Submodule release/datafiles/locale updated: 63699f9683...716dc02ec3
Submodule release/scripts/addons updated: baa581415c...787ea78f7f
@@ -615,9 +615,6 @@ typedef struct SculptSession {
|
||||
union {
|
||||
struct {
|
||||
struct SculptVertexPaintGeomMap gmap;
|
||||
|
||||
/* For non-airbrush painting to re-apply from the original (MLoop aligned). */
|
||||
unsigned int *previous_color;
|
||||
} vpaint;
|
||||
|
||||
struct {
|
||||
|
||||
@@ -1341,8 +1341,6 @@ void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss)
|
||||
struct SculptVertexPaintGeomMap *gmap = NULL;
|
||||
if (ss->mode_type == OB_MODE_VERTEX_PAINT) {
|
||||
gmap = &ss->mode.vpaint.gmap;
|
||||
|
||||
MEM_SAFE_FREE(ss->mode.vpaint.previous_color);
|
||||
}
|
||||
else if (ss->mode_type == OB_MODE_WEIGHT_PAINT) {
|
||||
gmap = &ss->mode.wpaint.gmap;
|
||||
|
||||
@@ -345,5 +345,7 @@ BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_l
|
||||
|
||||
using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>;
|
||||
using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>;
|
||||
using ColorPaint4f = ColorSceneLinear4f<eAlpha::Straight>;
|
||||
using ColorPaint4b = ColorSceneLinearByteEncoded4b<eAlpha::Straight>;
|
||||
|
||||
} // namespace blender
|
||||
|
||||
1053
source/blender/blenlib/BLI_color_mix.hh
Normal file
1053
source/blender/blenlib/BLI_color_mix.hh
Normal file
@@ -0,0 +1,1053 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
||||
|
||||
/** \file
|
||||
* \ingroup blenlib
|
||||
*
|
||||
* Contains color mixing utilities.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "IMB_colormanagement.h"
|
||||
#include "IMB_imbuf.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace blender::color {
|
||||
|
||||
struct ByteTraits {
|
||||
using ValueType = uchar;
|
||||
using BlendType = int;
|
||||
|
||||
inline static const uchar range = 255; /* Zero-based maximum value. */
|
||||
inline static const float frange = 255.0f; /* Convienent floating-point version of range. */
|
||||
inline static const int cmpRange = 254;
|
||||
inline static const int expandedRange = 256; /* One-based maxium value. */
|
||||
|
||||
inline static const int bytes = 1;
|
||||
inline static const float unit = 255.0f;
|
||||
|
||||
static inline BlendType divide_round(BlendType a, BlendType b)
|
||||
{
|
||||
return divide_round_i(a, b);
|
||||
}
|
||||
|
||||
static inline BlendType min(BlendType a, BlendType b)
|
||||
{
|
||||
return min_ii(a, b);
|
||||
}
|
||||
|
||||
static inline BlendType max(BlendType a, BlendType b)
|
||||
{
|
||||
return max_ii(a, b);
|
||||
}
|
||||
/* Discretizes in steps of 1.0 / range */
|
||||
static inline ValueType round(float f)
|
||||
{
|
||||
return round_fl_to_uchar(f);
|
||||
}
|
||||
};
|
||||
|
||||
struct FloatTraits {
|
||||
using ValueType = float;
|
||||
using BlendType = float;
|
||||
|
||||
inline const static float range = 1.0f;
|
||||
inline const static float frange = 1.0f;
|
||||
inline const static float cmpRange = 0.9999f;
|
||||
inline static const int expandedRange = 1.0f;
|
||||
|
||||
inline const static float unit = 1.0f;
|
||||
inline const static int bytes = 4;
|
||||
|
||||
static inline BlendType divide_round(BlendType a, BlendType b)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
|
||||
static inline BlendType min(BlendType a, BlendType b)
|
||||
{
|
||||
return min_ff(a, b);
|
||||
}
|
||||
|
||||
static inline BlendType max(BlendType a, BlendType b)
|
||||
{
|
||||
return min_ff(a, b);
|
||||
}
|
||||
|
||||
/* Discretizes in steps of 1.0 / range */
|
||||
static inline ValueType round(float f)
|
||||
{
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
static float get_luminance(ColorPaint4f c)
|
||||
{
|
||||
return IMB_colormanagement_get_luminance(&c.r);
|
||||
}
|
||||
|
||||
static int get_luminance(ColorPaint4b c)
|
||||
{
|
||||
return IMB_colormanagement_get_luminance_byte(&c.r);
|
||||
}
|
||||
|
||||
#define EPS_SATURATION 0.0005f
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Color Blending Modes
|
||||
* \{ */
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_blend(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
Blend mfac;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
if (fac >= Traits::range) {
|
||||
return col_dst;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = &col_src.r;
|
||||
cp_dst = &col_dst.r;
|
||||
cp_mix = &col_mix.r;
|
||||
|
||||
/* Updated to use the rgb squared color model which blends nicer. */
|
||||
Blend r1 = cp_src[0] * cp_src[0];
|
||||
Blend g1 = cp_src[1] * cp_src[1];
|
||||
Blend b1 = cp_src[2] * cp_src[2];
|
||||
Blend a1 = cp_src[3] * cp_src[3];
|
||||
|
||||
Blend r2 = cp_dst[0] * cp_dst[0];
|
||||
Blend g2 = cp_dst[1] * cp_dst[1];
|
||||
Blend b2 = cp_dst[2] * cp_dst[2];
|
||||
Blend a2 = cp_dst[3] * cp_dst[3];
|
||||
|
||||
cp_mix[0] = Traits::round(sqrtf(Traits::divide_round((mfac * r1 + fac * r2), Traits::range)));
|
||||
cp_mix[1] = Traits::round(sqrtf(Traits::divide_round((mfac * g1 + fac * g2), Traits::range)));
|
||||
cp_mix[2] = Traits::round(sqrtf(Traits::divide_round((mfac * b1 + fac * b2), Traits::range)));
|
||||
cp_mix[3] = Traits::round(sqrtf(Traits::divide_round((mfac * a1 + fac * a2), Traits::range)));
|
||||
return Color(col_mix[0], col_mix[1], col_mix[2], col_mix[3]);
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_add(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_src = (Value *)&col_src.r;
|
||||
cp_dst = (Value *)&col_dst.r;
|
||||
cp_mix = (Value *)&col_mix.r;
|
||||
|
||||
temp = cp_src[0] + Traits::divide_round((fac * cp_dst[0]), Traits::range);
|
||||
cp_mix[0] = (temp > Traits::cmpRange) ? Traits::range : temp;
|
||||
temp = cp_src[1] + Traits::divide_round((fac * cp_dst[1]), Traits::range);
|
||||
cp_mix[1] = (temp > Traits::cmpRange) ? Traits::range : temp;
|
||||
temp = cp_src[2] + Traits::divide_round((fac * cp_dst[2]), Traits::range);
|
||||
cp_mix[2] = (temp > Traits::cmpRange) ? Traits::range : temp;
|
||||
temp = cp_src[3] + Traits::divide_round((fac * cp_dst[3]), Traits::range);
|
||||
cp_mix[3] = (temp > Traits::cmpRange) ? Traits::range : temp;
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_sub(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
cp_src = (Value *)&col_src.r;
|
||||
cp_dst = (Value *)&col_dst.r;
|
||||
cp_mix = (Value *)&col_mix.r;
|
||||
|
||||
temp = cp_src[0] - Traits::divide_round((fac * cp_dst[0]), Traits::range);
|
||||
cp_mix[0] = (temp < 0) ? 0 : temp;
|
||||
temp = cp_src[1] - Traits::divide_round((fac * cp_dst[1]), Traits::range);
|
||||
cp_mix[1] = (temp < 0) ? 0 : temp;
|
||||
temp = cp_src[2] - Traits::divide_round((fac * cp_dst[2]), Traits::range);
|
||||
cp_mix[2] = (temp < 0) ? 0 : temp;
|
||||
temp = cp_src[3] - Traits::divide_round((fac * cp_dst[3]), Traits::range);
|
||||
cp_mix[3] = (temp < 0) ? 0 : temp;
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_mul(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
/* first mul, then blend the fac */
|
||||
cp_mix[0] = Traits::divide_round(mfac * cp_src[0] * Traits::range + fac * cp_dst[0] * cp_src[0],
|
||||
Traits::range * Traits::range);
|
||||
cp_mix[1] = Traits::divide_round(mfac * cp_src[1] * Traits::range + fac * cp_dst[1] * cp_src[1],
|
||||
Traits::range * Traits::range);
|
||||
cp_mix[2] = Traits::divide_round(mfac * cp_src[2] * Traits::range + fac * cp_dst[2] * cp_src[2],
|
||||
Traits::range * Traits::range);
|
||||
cp_mix[3] = Traits::divide_round(mfac * cp_src[3] * Traits::range + fac * cp_dst[3] * cp_src[3],
|
||||
Traits::range * Traits::range);
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_lighten(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
if (fac >= Traits::range) {
|
||||
return col_dst;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
/* See if we're lighter, if so mix, else don't do anything.
|
||||
* if the paint color is darker then the original, then ignore */
|
||||
if (get_luminance(cp_src) > get_luminance(cp_dst)) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_mix[0] = Traits::divide_round(mfac * cp_src[0] + fac * cp_dst[0], Traits::range);
|
||||
cp_mix[1] = Traits::divide_round(mfac * cp_src[1] + fac * cp_dst[1], Traits::range);
|
||||
cp_mix[2] = Traits::divide_round(mfac * cp_src[2] + fac * cp_dst[2], Traits::range);
|
||||
cp_mix[3] = Traits::divide_round(mfac * cp_src[3] + fac * cp_dst[3], Traits::range);
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_darken(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
if (fac >= Traits::range) {
|
||||
return col_dst;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
/* See if we're darker, if so mix, else don't do anything.
|
||||
* if the paint color is brighter then the original, then ignore */
|
||||
if (get_luminance(cp_src) < get_luminance(cp_dst)) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_mix[0] = Traits::divide_round((mfac * cp_src[0] + fac * cp_dst[0]), Traits::range);
|
||||
cp_mix[1] = Traits::divide_round((mfac * cp_src[1] + fac * cp_dst[1]), Traits::range);
|
||||
cp_mix[2] = Traits::divide_round((mfac * cp_src[2] + fac * cp_dst[2]), Traits::range);
|
||||
cp_mix[3] = Traits::divide_round((mfac * cp_src[3] + fac * cp_dst[3]), Traits::range);
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_colordodge(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac, temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
Blend dodgefac = (Blend)((float)Traits::range * 0.885f); /* ~225/255 */
|
||||
|
||||
temp = (cp_dst[0] == Traits::range) ?
|
||||
Traits::range :
|
||||
Traits::min((cp_src[0] * dodgefac) / (Traits::range - cp_dst[0]), Traits::range);
|
||||
cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range;
|
||||
temp = (cp_dst[1] == Traits::range) ?
|
||||
Traits::range :
|
||||
Traits::min((cp_src[1] * dodgefac) / (Traits::range - cp_dst[1]), Traits::range);
|
||||
cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range;
|
||||
temp = (cp_dst[2] == Traits::range) ?
|
||||
Traits::range :
|
||||
Traits::min((cp_src[2] * dodgefac) / (Traits::range - cp_dst[2]), Traits::range);
|
||||
cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range;
|
||||
temp = (cp_dst[3] == Traits::range) ?
|
||||
Traits::range :
|
||||
Traits::min((cp_src[3] * dodgefac) / (Traits::range - cp_dst[3]), Traits::range);
|
||||
cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_difference(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac, temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
temp = abs(cp_src[0] - cp_dst[0]);
|
||||
cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range;
|
||||
temp = abs(cp_src[1] - cp_dst[1]);
|
||||
cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range;
|
||||
temp = abs(cp_src[2] - cp_dst[2]);
|
||||
cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range;
|
||||
temp = abs(cp_src[3] - cp_dst[3]);
|
||||
cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_screen(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac, temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
temp = Traits::max(Traits::range - (((Traits::range - cp_src[0]) * (Traits::range - cp_dst[0])) /
|
||||
Traits::range),
|
||||
0);
|
||||
cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range;
|
||||
temp = Traits::max(Traits::range - (((Traits::range - cp_src[1]) * (Traits::range - cp_dst[1])) /
|
||||
Traits::range),
|
||||
0);
|
||||
cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range;
|
||||
temp = Traits::max(Traits::range - (((Traits::range - cp_src[2]) * (Traits::range - cp_dst[2])) /
|
||||
Traits::range),
|
||||
0);
|
||||
cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range;
|
||||
temp = Traits::max(Traits::range - (((Traits::range - cp_src[3]) * (Traits::range - cp_dst[3])) /
|
||||
Traits::range),
|
||||
0);
|
||||
cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_hardlight(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac, temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (cp_dst[i] > (Traits::range / 2)) {
|
||||
temp = Traits::range - ((Traits::range - 2 * (cp_dst[i] - (Traits::range / 2))) *
|
||||
(Traits::range - cp_src[i]) / Traits::range);
|
||||
}
|
||||
else {
|
||||
temp = (2 * cp_dst[i] * cp_src[i]) / Traits::expandedRange;
|
||||
}
|
||||
cp_mix[i] = Traits::min((mfac * cp_src[i] + temp * fac) / Traits::range, Traits::range);
|
||||
}
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_overlay(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac, temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (cp_src[i] > (Traits::range / 2)) {
|
||||
temp = Traits::range - ((Traits::range - 2 * (cp_src[i] - (Traits::range / 2))) *
|
||||
(Traits::range - cp_dst[i]) / Traits::range);
|
||||
}
|
||||
else {
|
||||
temp = (2 * cp_dst[i] * cp_src[i]) / Traits::expandedRange;
|
||||
}
|
||||
cp_mix[i] = Traits::min((mfac * cp_src[i] + temp * fac) / Traits::range, Traits::range);
|
||||
}
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_softlight(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac, temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
/* Use divide_round so we don't alter original byte equations. */
|
||||
const int add = Traits::divide_round(Traits::range, 4);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (cp_src[i] < (Traits::range / 2)) {
|
||||
temp = ((2 * ((cp_dst[i] / 2) + add)) * cp_src[i]) / Traits::range;
|
||||
}
|
||||
else {
|
||||
temp = Traits::range - (2 * (Traits::range - ((cp_dst[i] / 2) + add)) *
|
||||
(Traits::range - cp_src[i]) / Traits::range);
|
||||
}
|
||||
cp_mix[i] = (temp * fac + cp_src[i] * mfac) / Traits::range;
|
||||
}
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_exclusion(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac, temp;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
temp = (Traits::range / 2) -
|
||||
((2 * (cp_src[i] - (Traits::range / 2)) * (cp_dst[i] - (Traits::range / 2))) /
|
||||
Traits::range);
|
||||
cp_mix[i] = (temp * fac + cp_src[i] * mfac) / Traits::range;
|
||||
}
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_luminosity(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
float h1, s1, v1;
|
||||
float h2, s2, v2;
|
||||
float r, g, b;
|
||||
rgb_to_hsv(cp_src[0] / Traits::frange,
|
||||
cp_src[1] / Traits::frange,
|
||||
cp_src[2] / Traits::frange,
|
||||
&h1,
|
||||
&s1,
|
||||
&v1);
|
||||
rgb_to_hsv(cp_dst[0] / Traits::frange,
|
||||
cp_dst[1] / Traits::frange,
|
||||
cp_dst[2] / Traits::frange,
|
||||
&h2,
|
||||
&s2,
|
||||
&v2);
|
||||
|
||||
v1 = v2;
|
||||
|
||||
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
|
||||
|
||||
cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range;
|
||||
cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range;
|
||||
cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range;
|
||||
cp_mix[3] = ((Blend)(cp_dst[3]) * fac + mfac * cp_src[3]) / Traits::range;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_saturation(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
float h1, s1, v1;
|
||||
float h2, s2, v2;
|
||||
float r, g, b;
|
||||
rgb_to_hsv(cp_src[0] / Traits::frange,
|
||||
cp_src[1] / Traits::frange,
|
||||
cp_src[2] / Traits::frange,
|
||||
&h1,
|
||||
&s1,
|
||||
&v1);
|
||||
rgb_to_hsv(cp_dst[0] / Traits::frange,
|
||||
cp_dst[1] / Traits::frange,
|
||||
cp_dst[2] / Traits::frange,
|
||||
&h2,
|
||||
&s2,
|
||||
&v2);
|
||||
|
||||
if (s1 > EPS_SATURATION) {
|
||||
s1 = s2;
|
||||
}
|
||||
|
||||
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
|
||||
|
||||
cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range;
|
||||
cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range;
|
||||
cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_hue(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
float h1, s1, v1;
|
||||
float h2, s2, v2;
|
||||
float r, g, b;
|
||||
rgb_to_hsv(cp_src[0] / Traits::frange,
|
||||
cp_src[1] / Traits::frange,
|
||||
cp_src[2] / Traits::frange,
|
||||
&h1,
|
||||
&s1,
|
||||
&v1);
|
||||
rgb_to_hsv(cp_dst[0] / Traits::frange,
|
||||
cp_dst[1] / Traits::frange,
|
||||
cp_dst[2] / Traits::frange,
|
||||
&h2,
|
||||
&s2,
|
||||
&v2);
|
||||
|
||||
h1 = h2;
|
||||
|
||||
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
|
||||
|
||||
cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range;
|
||||
cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range;
|
||||
cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range;
|
||||
cp_mix[3] = ((Blend)(cp_dst[3]) * fac + mfac * cp_src[3]) / Traits::range;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_alpha_add(Color col_src, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_mix;
|
||||
Blend temp;
|
||||
Color col_mix = col_src;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
temp = cp_src[3] + fac;
|
||||
cp_mix[3] = (temp > Traits::cmpRange) ? Traits::range : temp;
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_alpha_sub(Color col_src, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_mix;
|
||||
Blend temp;
|
||||
Color col_mix = col_src;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
temp = cp_src[3] - fac;
|
||||
cp_mix[3] = temp < 0 ? 0 : temp;
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_pinlight(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
const Blend cmp = Traits::range / 2;
|
||||
|
||||
int i = 3;
|
||||
Blend temp;
|
||||
|
||||
while (i--) {
|
||||
if (cp_dst[i] > cmp) {
|
||||
temp = Traits::max(2 * (cp_dst[i] - cmp), cp_src[i]);
|
||||
}
|
||||
else {
|
||||
temp = Traits::min(2 * cp_dst[i], cp_src[i]);
|
||||
}
|
||||
cp_mix[i] = (Value)((Traits::min(temp, Traits::range) * fac + cp_src[i] * mfac) /
|
||||
Traits::range);
|
||||
}
|
||||
|
||||
col_mix.a = col_src.a;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_linearlight(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
const Blend cmp = Traits::range / 2;
|
||||
|
||||
int i = 3;
|
||||
while (i--) {
|
||||
Blend temp;
|
||||
|
||||
if (cp_dst[i] > cmp) {
|
||||
temp = Traits::min(cp_src[i] + 2 * (cp_dst[i] - cmp), Traits::range);
|
||||
}
|
||||
else {
|
||||
temp = Traits::max(cp_src[i] + 2 * cp_dst[i] - Traits::range, 0);
|
||||
}
|
||||
|
||||
cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range);
|
||||
}
|
||||
|
||||
col_mix.a = col_src.a;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_vividlight(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
const Blend cmp = Traits::range / 2;
|
||||
|
||||
int i = 3;
|
||||
|
||||
while (i--) {
|
||||
Blend temp;
|
||||
|
||||
if (cp_dst[i] == Traits::range) {
|
||||
temp = (cp_src[i] == 0) ? cmp : Traits::range;
|
||||
}
|
||||
else if (cp_dst[i] == 0) {
|
||||
temp = (cp_src[i] == Traits::range) ? cmp : 0;
|
||||
}
|
||||
else if (cp_dst[i] > cmp) {
|
||||
temp = Traits::min(((cp_src[i]) * Traits::range) / (2 * (Traits::range - cp_dst[i])),
|
||||
Traits::range);
|
||||
}
|
||||
else {
|
||||
temp = Traits::max(
|
||||
Traits::range - ((Traits::range - cp_src[i]) * Traits::range / (2 * cp_dst[i])), 0);
|
||||
}
|
||||
col_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range);
|
||||
}
|
||||
|
||||
col_mix.a = col_src.a;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_color(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
float h1, s1, v1;
|
||||
float h2, s2, v2;
|
||||
float r, g, b;
|
||||
|
||||
rgb_to_hsv(cp_src[0] / Traits::frange,
|
||||
cp_src[1] / Traits::frange,
|
||||
cp_src[2] / Traits::frange,
|
||||
&h1,
|
||||
&s1,
|
||||
&v1);
|
||||
rgb_to_hsv(cp_dst[0] / Traits::frange,
|
||||
cp_dst[1] / Traits::frange,
|
||||
cp_dst[2] / Traits::frange,
|
||||
&h2,
|
||||
&s2,
|
||||
&v2);
|
||||
|
||||
h1 = h2;
|
||||
s1 = s2;
|
||||
|
||||
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
|
||||
|
||||
cp_mix[0] = (Value)(((Blend)(r * Traits::frange) * fac + cp_src[0] * mfac) / Traits::range);
|
||||
cp_mix[1] = (Value)(((Blend)(g * Traits::frange) * fac + cp_src[1] * mfac) / Traits::range);
|
||||
cp_mix[2] = (Value)(((Blend)(b * Traits::frange) * fac + cp_src[2] * mfac) / Traits::range);
|
||||
|
||||
col_mix.a = col_src.a;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_colorburn(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
int i = 3;
|
||||
|
||||
while (i--) {
|
||||
const Blend temp =
|
||||
(cp_dst[i] == 0) ?
|
||||
0 :
|
||||
Traits::max(Traits::range - ((Traits::range - cp_src[i]) * Traits::range) / cp_dst[i],
|
||||
0);
|
||||
cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range);
|
||||
}
|
||||
|
||||
col_mix.a = col_src.a;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
static Color mix_linearburn(Color col_src, Color col_dst, typename Traits::BlendType fac)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
Value *cp_src, *cp_dst, *cp_mix;
|
||||
Blend mfac;
|
||||
Color col_mix(0, 0, 0, 0);
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = Traits::range - fac;
|
||||
|
||||
cp_src = (Value *)&col_src;
|
||||
cp_dst = (Value *)&col_dst;
|
||||
cp_mix = (Value *)&col_mix;
|
||||
|
||||
int i = 3;
|
||||
|
||||
while (i--) {
|
||||
const Blend temp = Traits::max(cp_src[i] + cp_dst[i] - Traits::range, 0);
|
||||
cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range);
|
||||
}
|
||||
|
||||
col_mix.a = col_src.a;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits>
|
||||
BLI_INLINE Color BLI_mix_colors(const IMB_BlendMode tool,
|
||||
const Color a,
|
||||
const Color b,
|
||||
const typename Traits::BlendType alpha)
|
||||
{
|
||||
switch ((IMB_BlendMode)tool) {
|
||||
case IMB_BLEND_MIX:
|
||||
return mix_blend<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_ADD:
|
||||
return mix_add<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_SUB:
|
||||
return mix_sub<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_MUL:
|
||||
return mix_mul<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_LIGHTEN:
|
||||
return mix_lighten<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_DARKEN:
|
||||
return mix_darken<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_COLORDODGE:
|
||||
return mix_colordodge<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_COLORBURN:
|
||||
return mix_colorburn<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_DIFFERENCE:
|
||||
return mix_difference<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_SCREEN:
|
||||
return mix_screen<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_HARDLIGHT:
|
||||
return mix_hardlight<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_OVERLAY:
|
||||
return mix_overlay<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_SOFTLIGHT:
|
||||
return mix_softlight<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_EXCLUSION:
|
||||
return mix_exclusion<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_LUMINOSITY:
|
||||
return mix_luminosity<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_SATURATION:
|
||||
return mix_saturation<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_HUE:
|
||||
return mix_hue<Color, Traits>(a, b, alpha);
|
||||
/* non-color */
|
||||
case IMB_BLEND_ERASE_ALPHA:
|
||||
return mix_alpha_sub<Color, Traits>(a, alpha);
|
||||
case IMB_BLEND_ADD_ALPHA:
|
||||
return mix_alpha_add<Color, Traits>(a, alpha);
|
||||
case IMB_BLEND_PINLIGHT:
|
||||
return mix_pinlight<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_LINEARLIGHT:
|
||||
return mix_linearlight<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_VIVIDLIGHT:
|
||||
return mix_vividlight<Color, Traits>(a, b, alpha);
|
||||
case IMB_BLEND_COLOR:
|
||||
return mix_color<Color, Traits>(a, b, alpha);
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return Color(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::color
|
||||
@@ -171,6 +171,7 @@ set(SRC
|
||||
BLI_boxpack_2d.h
|
||||
BLI_buffer.h
|
||||
BLI_color.hh
|
||||
BLI_color_mix.hh
|
||||
BLI_compiler_attrs.h
|
||||
BLI_compiler_compat.h
|
||||
BLI_compiler_typecheck.h
|
||||
|
||||
@@ -2448,6 +2448,54 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
/* Rebuild active/render color attribute references. */
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 302, 6)) {
|
||||
LISTBASE_FOREACH (Brush *, br, &bmain->brushes) {
|
||||
/* buggy code in wm_toolsystem broke smear in old files,
|
||||
reset to defaults */
|
||||
if (br->sculpt_tool == SCULPT_TOOL_SMEAR) {
|
||||
br->alpha = 1.0f;
|
||||
br->spacing = 5;
|
||||
br->flag &= ~BRUSH_ALPHA_PRESSURE;
|
||||
br->flag &= ~BRUSH_SPACE_ATTEN;
|
||||
br->curve_preset = BRUSH_CURVE_SPHERE;
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) {
|
||||
for (int step = 0; step < 2; step++) {
|
||||
CustomDataLayer *actlayer = NULL;
|
||||
|
||||
int vact1, vact2;
|
||||
|
||||
if (step) {
|
||||
vact1 = CustomData_get_render_layer_index(&me->vdata, CD_PROP_COLOR);
|
||||
vact2 = CustomData_get_render_layer_index(&me->ldata, CD_PROP_BYTE_COLOR);
|
||||
}
|
||||
else {
|
||||
vact1 = CustomData_get_active_layer_index(&me->vdata, CD_PROP_COLOR);
|
||||
vact2 = CustomData_get_active_layer_index(&me->ldata, CD_PROP_BYTE_COLOR);
|
||||
}
|
||||
|
||||
if (vact1 != -1) {
|
||||
actlayer = me->vdata.layers + vact1;
|
||||
}
|
||||
else if (vact2 != -1) {
|
||||
actlayer = me->ldata.layers + vact2;
|
||||
}
|
||||
|
||||
if (actlayer) {
|
||||
if (step) {
|
||||
BKE_id_attributes_render_color_set(&me->id, actlayer);
|
||||
}
|
||||
else {
|
||||
BKE_id_attributes_active_color_set(&me->id, actlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 302, 7)) {
|
||||
/* Generate 'system' liboverrides IDs.
|
||||
* NOTE: This is a fairly rough process, based on very basic heuristics. Should be enough for a
|
||||
|
||||
@@ -271,6 +271,14 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
|
||||
BKE_pbvh_is_drawing_set(ob->sculpt->pbvh, is_sculpt_pbvh);
|
||||
}
|
||||
|
||||
const CustomData *cd_vdata = workbench_mesh_get_vert_custom_data(me);
|
||||
const CustomData *cd_ldata = workbench_mesh_get_loop_custom_data(me);
|
||||
|
||||
bool has_color = (CustomData_has_layer(cd_vdata, CD_PROP_COLOR) ||
|
||||
CustomData_has_layer(cd_vdata, CD_PROP_BYTE_COLOR) ||
|
||||
CustomData_has_layer(cd_ldata, CD_PROP_COLOR) ||
|
||||
CustomData_has_layer(cd_ldata, CD_PROP_BYTE_COLOR));
|
||||
|
||||
if (color_type == V3D_SHADING_TEXTURE_COLOR) {
|
||||
if (ob->dt < OB_TEXTURE) {
|
||||
color_type = V3D_SHADING_MATERIAL_COLOR;
|
||||
@@ -285,14 +293,6 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
|
||||
color_type = V3D_SHADING_OBJECT_COLOR;
|
||||
}
|
||||
else {
|
||||
const CustomData *cd_vdata = workbench_mesh_get_vert_custom_data(me);
|
||||
const CustomData *cd_ldata = workbench_mesh_get_loop_custom_data(me);
|
||||
|
||||
bool has_color = (CustomData_has_layer(cd_vdata, CD_PROP_COLOR) ||
|
||||
CustomData_has_layer(cd_vdata, CD_PROP_BYTE_COLOR) ||
|
||||
CustomData_has_layer(cd_ldata, CD_PROP_COLOR) ||
|
||||
CustomData_has_layer(cd_ldata, CD_PROP_BYTE_COLOR));
|
||||
|
||||
if (!has_color) {
|
||||
color_type = V3D_SHADING_OBJECT_COLOR;
|
||||
}
|
||||
@@ -314,7 +314,7 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
|
||||
*r_texpaint_mode = true;
|
||||
}
|
||||
}
|
||||
else if (is_vertpaint_mode && me && CustomData_has_layer(ldata, CD_PROP_BYTE_COLOR)) {
|
||||
else if (is_vertpaint_mode && me && has_color) {
|
||||
color_type = V3D_SHADING_VERTEX_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,19 +428,19 @@ int ED_mesh_color_add(
|
||||
bool ED_mesh_color_ensure(struct Mesh *me, const char *name)
|
||||
{
|
||||
BLI_assert(me->edit_mesh == NULL);
|
||||
CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
|
||||
|
||||
if (!me->mloopcol && me->totloop) {
|
||||
CustomData_add_layer_named(
|
||||
&me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name);
|
||||
int layer_i = CustomData_get_layer_index(&me->ldata, CD_PROP_BYTE_COLOR);
|
||||
if (!layer) {
|
||||
CustomData_add_layer_named(&me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name);
|
||||
layer = me->ldata.layers + CustomData_get_layer_index(&me->ldata, CD_PROP_BYTE_COLOR);
|
||||
|
||||
BKE_id_attributes_active_color_set(&me->id, me->ldata.layers + layer_i);
|
||||
BKE_id_attributes_active_color_set(&me->id, layer);
|
||||
BKE_mesh_update_customdata_pointers(me, true);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&me->id, 0);
|
||||
|
||||
return (me->mloopcol != NULL);
|
||||
return (layer != NULL);
|
||||
}
|
||||
|
||||
bool ED_mesh_color_remove_index(Mesh *me, const int n)
|
||||
|
||||
@@ -48,7 +48,7 @@ set(SRC
|
||||
paint_ops.c
|
||||
paint_stroke.c
|
||||
paint_utils.c
|
||||
paint_vertex.c
|
||||
paint_vertex.cc
|
||||
paint_vertex_color_ops.c
|
||||
paint_vertex_color_utils.c
|
||||
paint_vertex_proj.c
|
||||
|
||||
@@ -133,7 +133,9 @@ void PAINT_OT_weight_gradient(struct wmOperatorType *ot);
|
||||
void PAINT_OT_vertex_paint_toggle(struct wmOperatorType *ot);
|
||||
void PAINT_OT_vertex_paint(struct wmOperatorType *ot);
|
||||
|
||||
unsigned int vpaint_get_current_col(struct Scene *scene, struct VPaint *vp, bool secondary);
|
||||
unsigned int vpaint_get_current_color(struct Scene *scene,
|
||||
struct VPaint *vp,
|
||||
bool secondary);
|
||||
|
||||
/* paint_vertex_color_utils.c */
|
||||
|
||||
|
||||
@@ -13,11 +13,14 @@
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array_utils.h"
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_color_mix.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
@@ -26,6 +29,7 @@
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_brush.h"
|
||||
@@ -66,14 +70,59 @@
|
||||
#include "paint_intern.h" /* own include */
|
||||
#include "sculpt_intern.h"
|
||||
|
||||
using blender::IndexRange;
|
||||
using namespace blender;
|
||||
using namespace blender::color;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Utilities
|
||||
* \{ */
|
||||
|
||||
static ColorPaint4b uint2color(uint c)
|
||||
{
|
||||
uchar *rgba = (uchar *)&c;
|
||||
return ColorPaint4b(rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
}
|
||||
|
||||
static uint color2uint(ColorPaint4b c)
|
||||
{
|
||||
return *(reinterpret_cast<uint *>(&c));
|
||||
}
|
||||
|
||||
static bool isZero(ColorPaint4f c)
|
||||
{
|
||||
return c.r == 0.0f && c.g == 0.0f && c.b == 0.0f && c.a == 0.0f;
|
||||
}
|
||||
|
||||
static bool isZero(ColorPaint4b c)
|
||||
{
|
||||
return c.r == 0 && c.g == 0 && c.b == 0 && c.a == 0;
|
||||
}
|
||||
|
||||
template<typename Color = ColorPaint4f> static ColorPaint4f toFloat(const Color &c)
|
||||
{
|
||||
if constexpr (std::is_same_v<Color, ColorPaint4b>) {
|
||||
return c.decode();
|
||||
}
|
||||
else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Color = ColorPaint4f> static Color fromFloat(const ColorPaint4f &c)
|
||||
{
|
||||
if constexpr (std::is_same_v<Color, ColorPaint4b>) {
|
||||
return c.encode();
|
||||
}
|
||||
else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use for 'blur' brush, align with PBVH nodes, created and freed on each update. */
|
||||
struct VPaintAverageAccum {
|
||||
uint len;
|
||||
uint value[3];
|
||||
template<typename BlendType> struct VPaintAverageAccum {
|
||||
BlendType len;
|
||||
BlendType value[3];
|
||||
};
|
||||
|
||||
struct WPaintAverageAccum {
|
||||
@@ -93,6 +142,26 @@ struct NormalAnglePrecalc {
|
||||
float angle_range;
|
||||
};
|
||||
|
||||
/* Returns number of elements. */
|
||||
static int get_vcol_elements(Mesh *me, size_t *r_elem_size)
|
||||
{
|
||||
CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
|
||||
AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer);
|
||||
|
||||
if (r_elem_size) {
|
||||
*r_elem_size = layer->type == CD_PROP_COLOR ? sizeof(float) * 4ULL : 4ULL;
|
||||
}
|
||||
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return me->totvert;
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return me->totloop;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void view_angle_limits_init(struct NormalAnglePrecalc *a, float angle, bool do_mask_normal)
|
||||
{
|
||||
angle = RAD2DEGF(angle);
|
||||
@@ -196,9 +265,8 @@ bool vertex_paint_mode_poll(bContext *C)
|
||||
}
|
||||
|
||||
CustomDataLayer *layer = BKE_id_attributes_active_color_get((ID *)ob->data);
|
||||
AttributeDomain domain = BKE_id_attribute_domain((ID *)ob->data, layer);
|
||||
|
||||
return layer && layer->type == CD_PROP_BYTE_COLOR && domain == ATTR_DOMAIN_CORNER;
|
||||
return layer != nullptr;
|
||||
}
|
||||
|
||||
static bool vertex_paint_poll_ex(bContext *C, bool check_tool)
|
||||
@@ -262,41 +330,54 @@ bool weight_paint_poll_ignore_tool(bContext *C)
|
||||
return weight_paint_poll_ex(C, false);
|
||||
}
|
||||
|
||||
uint vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary)
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static Color vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary)
|
||||
{
|
||||
Brush *brush = BKE_paint_brush(&vp->paint);
|
||||
uchar col[4];
|
||||
rgb_float_to_uchar(col,
|
||||
secondary ? BKE_brush_secondary_color_get(scene, brush) :
|
||||
BKE_brush_color_get(scene, brush));
|
||||
col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */
|
||||
return *(uint *)col;
|
||||
float color[4];
|
||||
const float *brush_color = secondary ? BKE_brush_secondary_color_get(scene, brush) :
|
||||
BKE_brush_color_get(scene, brush);
|
||||
copy_v3_v3(color, brush_color);
|
||||
color[3] = 1.0f; /* alpha isn't used, could even be removed to speedup paint a little */
|
||||
|
||||
return fromFloat<Color>(ColorPaint4f(color));
|
||||
}
|
||||
|
||||
uint vpaint_get_current_color(Scene *scene, VPaint *vp, bool secondary)
|
||||
{
|
||||
ColorPaint4b col = vpaint_get_current_col<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(
|
||||
scene, vp, secondary);
|
||||
|
||||
return color2uint(col);
|
||||
}
|
||||
|
||||
/* wpaint has 'wpaint_blend' */
|
||||
static uint vpaint_blend(const VPaint *vp,
|
||||
uint color_curr,
|
||||
uint color_orig,
|
||||
uint color_paint,
|
||||
const int alpha_i,
|
||||
/* pre scaled from [0-1] --> [0-255] */
|
||||
const int brush_alpha_value_i)
|
||||
template<typename Color, typename Traits>
|
||||
static Color vpaint_blend(const VPaint *vp,
|
||||
Color color_curr,
|
||||
Color color_orig,
|
||||
Color color_paint,
|
||||
const typename Traits::ValueType alpha,
|
||||
const typename Traits::BlendType brush_alpha_value)
|
||||
{
|
||||
const Brush *brush = vp->paint.brush;
|
||||
const IMB_BlendMode blend = brush->blend;
|
||||
using Value = typename Traits::ValueType;
|
||||
|
||||
uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i);
|
||||
const Brush *brush = vp->paint.brush;
|
||||
const IMB_BlendMode blend = (IMB_BlendMode)brush->blend;
|
||||
|
||||
Color color_blend = BLI_mix_colors<Color, Traits>(blend, color_curr, color_paint, alpha);
|
||||
|
||||
/* If no accumulate, clip color adding with `color_orig` & `color_test`. */
|
||||
if (!brush_use_accumulate(vp)) {
|
||||
uint color_test, a;
|
||||
char *cp, *ct, *co;
|
||||
uint a;
|
||||
Color color_test;
|
||||
Value *cp, *ct, *co;
|
||||
|
||||
color_test = ED_vpaint_blend_tool(blend, color_orig, color_paint, brush_alpha_value_i);
|
||||
color_test = BLI_mix_colors<Color, Traits>(blend, color_orig, color_paint, brush_alpha_value);
|
||||
|
||||
cp = (char *)&color_blend;
|
||||
ct = (char *)&color_test;
|
||||
co = (char *)&color_orig;
|
||||
cp = (Value *)&color_blend;
|
||||
ct = (Value *)&color_test;
|
||||
co = (Value *)&color_orig;
|
||||
|
||||
for (a = 0; a < 4; a++) {
|
||||
if (ct[a] < co[a]) {
|
||||
@@ -320,36 +401,15 @@ static uint vpaint_blend(const VPaint *vp,
|
||||
|
||||
if ((brush->flag & BRUSH_LOCK_ALPHA) &&
|
||||
!ELEM(blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA)) {
|
||||
char *cp, *cc;
|
||||
cp = (char *)&color_blend;
|
||||
cc = (char *)&color_curr;
|
||||
Value *cp, *cc;
|
||||
cp = (Value *)&color_blend;
|
||||
cc = (Value *)&color_curr;
|
||||
cp[3] = cc[3];
|
||||
}
|
||||
|
||||
return color_blend;
|
||||
}
|
||||
|
||||
static void tex_color_alpha(VPaint *vp, const ViewContext *vc, const float co[3], float r_rgba[4])
|
||||
{
|
||||
const Brush *brush = BKE_paint_brush(&vp->paint);
|
||||
BLI_assert(brush->mtex.tex != NULL);
|
||||
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) {
|
||||
BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, NULL);
|
||||
}
|
||||
else {
|
||||
float co_ss[2]; /* screenspace */
|
||||
if (ED_view3d_project_float_object(
|
||||
vc->region, co, co_ss, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) ==
|
||||
V3D_PROJ_RET_OK) {
|
||||
const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */
|
||||
BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, NULL);
|
||||
}
|
||||
else {
|
||||
zero_v4(r_rgba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* vpaint has 'vpaint_blend' */
|
||||
static float wpaint_blend(const VPaint *wp,
|
||||
float weight,
|
||||
@@ -359,7 +419,7 @@ static float wpaint_blend(const VPaint *wp,
|
||||
const short do_flip)
|
||||
{
|
||||
const Brush *brush = wp->paint.brush;
|
||||
IMB_BlendMode blend = brush->blend;
|
||||
IMB_BlendMode blend = (IMB_BlendMode)brush->blend;
|
||||
|
||||
if (do_flip) {
|
||||
switch (blend) {
|
||||
@@ -390,6 +450,32 @@ static float wpaint_blend(const VPaint *wp,
|
||||
return weight;
|
||||
}
|
||||
|
||||
static void paint_and_tex_color_alpha_intern(VPaint *vp,
|
||||
const ViewContext *vc,
|
||||
const float co[3],
|
||||
float r_rgba[4])
|
||||
{
|
||||
const Brush *brush = BKE_paint_brush(&vp->paint);
|
||||
BLI_assert(brush->mtex.tex != NULL);
|
||||
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) {
|
||||
BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, NULL);
|
||||
}
|
||||
else {
|
||||
float co_ss[2]; /* screenspace */
|
||||
if (ED_view3d_project_float_object(
|
||||
vc->region,
|
||||
co,
|
||||
co_ss,
|
||||
(eV3DProjTest)(V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR)) == V3D_PROJ_RET_OK) {
|
||||
const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */
|
||||
BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, NULL);
|
||||
}
|
||||
else {
|
||||
zero_v4(r_rgba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float wpaint_clamp_monotonic(float oldval, float curval, float newval)
|
||||
{
|
||||
if (newval < oldval) {
|
||||
@@ -746,7 +832,7 @@ static void do_weight_paint_vertex_single(
|
||||
float alpha,
|
||||
float paintweight)
|
||||
{
|
||||
Mesh *me = ob->data;
|
||||
Mesh *me = (Mesh *)ob->data;
|
||||
MDeformVert *dv = &me->dvert[index];
|
||||
bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
||||
|
||||
@@ -959,7 +1045,7 @@ static void do_weight_paint_vertex_multi(
|
||||
float alpha,
|
||||
float paintweight)
|
||||
{
|
||||
Mesh *me = ob->data;
|
||||
Mesh *me = (Mesh *)ob->data;
|
||||
MDeformVert *dv = &me->dvert[index];
|
||||
bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
||||
|
||||
@@ -1106,14 +1192,39 @@ static void vertex_paint_init_session(Depsgraph *depsgraph,
|
||||
BKE_sculpt_toolsettings_data_ensure(scene);
|
||||
|
||||
BLI_assert(ob->sculpt == NULL);
|
||||
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
|
||||
ob->sculpt = (SculptSession *)MEM_callocN(sizeof(SculptSession), "sculpt session");
|
||||
ob->sculpt->mode_type = object_mode;
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
|
||||
}
|
||||
|
||||
static void vertex_paint_init_stroke(Depsgraph *depsgraph, Object *ob)
|
||||
static void vertex_paint_init_stroke(Scene *scene, Depsgraph *depsgraph, Object *ob)
|
||||
{
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
/* Ensure ss->cache is allocated. It will mostly be initialized in
|
||||
* vwpaint_update_cache_invariants and vwpaint_update_cache_variants.
|
||||
*/
|
||||
if (!ss->cache) {
|
||||
ss->cache = (StrokeCache *)MEM_callocN(sizeof(StrokeCache), "stroke cache");
|
||||
}
|
||||
|
||||
/* Allocate scratch array for previous colors if needed. */
|
||||
if (!brush_use_accumulate(ts->vpaint)) {
|
||||
if (!ob->sculpt->cache->prev_colors_vpaint) {
|
||||
Mesh *me = BKE_object_get_original_mesh(ob);
|
||||
size_t elem_size;
|
||||
int elem_num;
|
||||
|
||||
elem_num = get_vcol_elements(me, &elem_size);
|
||||
|
||||
ob->sculpt->cache->prev_colors_vpaint = (uint *)MEM_callocN(elem_num * elem_size, __func__);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MEM_SAFE_FREE(ob->sculpt->cache->prev_colors_vpaint);
|
||||
}
|
||||
}
|
||||
|
||||
static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
|
||||
@@ -1129,12 +1240,12 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
|
||||
BLI_assert(ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT);
|
||||
}
|
||||
else {
|
||||
ob->sculpt->mode_type = 0;
|
||||
ob->sculpt->mode_type = (eObjectMode)0;
|
||||
BLI_assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh *me = ob->data;
|
||||
Mesh *me = (Mesh *)ob->data;
|
||||
|
||||
if (gmap->vert_to_loop == NULL) {
|
||||
gmap->vert_map_mem = NULL;
|
||||
@@ -1158,24 +1269,15 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
|
||||
}
|
||||
|
||||
/* Create average brush arrays */
|
||||
if (ob->mode == OB_MODE_VERTEX_PAINT) {
|
||||
if (!brush_use_accumulate(ts->vpaint)) {
|
||||
if (ob->sculpt->mode.vpaint.previous_color == NULL) {
|
||||
ob->sculpt->mode.vpaint.previous_color = MEM_callocN(me->totloop * sizeof(uint), __func__);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color);
|
||||
}
|
||||
}
|
||||
else if (ob->mode == OB_MODE_WEIGHT_PAINT) {
|
||||
if (ob->mode == OB_MODE_WEIGHT_PAINT) {
|
||||
if (!brush_use_accumulate(ts->wpaint)) {
|
||||
if (ob->sculpt->mode.wpaint.alpha_weight == NULL) {
|
||||
ob->sculpt->mode.wpaint.alpha_weight = MEM_callocN(me->totvert * sizeof(float), __func__);
|
||||
ob->sculpt->mode.wpaint.alpha_weight = (float *)MEM_callocN(me->totvert * sizeof(float),
|
||||
__func__);
|
||||
}
|
||||
if (ob->sculpt->mode.wpaint.dvert_prev == NULL) {
|
||||
ob->sculpt->mode.wpaint.dvert_prev = MEM_callocN(me->totvert * sizeof(MDeformVert),
|
||||
__func__);
|
||||
ob->sculpt->mode.wpaint.dvert_prev = (MDeformVert *)MEM_callocN(
|
||||
me->totvert * sizeof(MDeformVert), __func__);
|
||||
MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev;
|
||||
for (int i = 0; i < me->totvert; i++, dv++) {
|
||||
/* Use to show this isn't initialized, never apply to the mesh data. */
|
||||
@@ -1368,7 +1470,7 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
if (!is_mode_set) {
|
||||
if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
|
||||
if (!ED_object_mode_compat_set(C, ob, (eObjectMode)mode_flag, op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
@@ -1412,7 +1514,7 @@ static bool paint_mode_toggle_poll_test(bContext *C)
|
||||
if (ob == NULL || ob->type != OB_MESH) {
|
||||
return false;
|
||||
}
|
||||
if (!ob->data || ID_IS_LINKED(ob->data) || ID_IS_OVERRIDE_LIBRARY(ob->data)) {
|
||||
if (!ob->data || ID_IS_LINKED(ob->data)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1507,7 +1609,7 @@ static void vwpaint_update_cache_invariants(
|
||||
StrokeCache *cache;
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
|
||||
ViewContext *vc = paint_stroke_view_context(op->customdata);
|
||||
ViewContext *vc = paint_stroke_view_context((PaintStroke *)op->customdata);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
float mat[3][3];
|
||||
float view_dir[3] = {0.0f, 0.0f, 1.0f};
|
||||
@@ -1515,7 +1617,7 @@ static void vwpaint_update_cache_invariants(
|
||||
|
||||
/* VW paint needs to allocate stroke cache before update is called. */
|
||||
if (!ss->cache) {
|
||||
cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
|
||||
cache = (StrokeCache *)MEM_callocN(sizeof(StrokeCache), "stroke cache");
|
||||
ss->cache = cache;
|
||||
}
|
||||
else {
|
||||
@@ -1618,7 +1720,7 @@ static void vwpaint_update_cache_variants(bContext *C, VPaint *vp, Object *ob, P
|
||||
static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
struct PaintStroke *stroke = op->customdata;
|
||||
struct PaintStroke *stroke = (PaintStroke *)op->customdata;
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Mesh *me = BKE_mesh_from_object(ob);
|
||||
@@ -1638,13 +1740,13 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
||||
/* check if we are attempting to paint onto a locked vertex group,
|
||||
* and other options disallow it from doing anything useful */
|
||||
bDeformGroup *dg;
|
||||
dg = BLI_findlink(&me->vertex_group_names, vgroup_index.active);
|
||||
dg = (bDeformGroup *)BLI_findlink(&me->vertex_group_names, vgroup_index.active);
|
||||
if (dg->flag & DG_LOCK_WEIGHT) {
|
||||
BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting");
|
||||
return false;
|
||||
}
|
||||
if (vgroup_index.mirror != -1) {
|
||||
dg = BLI_findlink(&me->vertex_group_names, vgroup_index.mirror);
|
||||
dg = (bDeformGroup *)BLI_findlink(&me->vertex_group_names, vgroup_index.mirror);
|
||||
if (dg->flag & DG_LOCK_WEIGHT) {
|
||||
BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting");
|
||||
return false;
|
||||
@@ -1667,7 +1769,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
||||
|
||||
for (i = 0; i < defbase_tot; i++) {
|
||||
if (defbase_sel[i]) {
|
||||
dg = BLI_findlink(&me->vertex_group_names, i);
|
||||
dg = (bDeformGroup *)BLI_findlink(&me->vertex_group_names, i);
|
||||
if (dg->flag & DG_LOCK_WEIGHT) {
|
||||
BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting");
|
||||
MEM_freeN(defbase_sel);
|
||||
@@ -1679,7 +1781,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
||||
|
||||
/* ALLOCATIONS! no return after this line */
|
||||
/* make mode data storage */
|
||||
wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData");
|
||||
wpd = (WPaintData *)MEM_callocN(sizeof(struct WPaintData), "WPaintData");
|
||||
paint_stroke_set_mode_data(stroke, wpd);
|
||||
ED_view3d_viewcontext_init(C, &wpd->vc, depsgraph);
|
||||
view_angle_limits_init(&wpd->normal_angle_precalc,
|
||||
@@ -1712,10 +1814,10 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
||||
}
|
||||
|
||||
if (wpd->do_lock_relative || (ts->auto_normalize && wpd->lock_flags && !wpd->do_multipaint)) {
|
||||
bool *unlocked = MEM_dupallocN(wpd->vgroup_validmap);
|
||||
bool *unlocked = (bool *)MEM_dupallocN(wpd->vgroup_validmap);
|
||||
|
||||
if (wpd->lock_flags) {
|
||||
bool *locked = MEM_mallocN(sizeof(bool) * wpd->defbase_tot, __func__);
|
||||
bool *locked = (bool *)MEM_mallocN(sizeof(bool) * wpd->defbase_tot, __func__);
|
||||
BKE_object_defgroup_split_locked_validmap(
|
||||
wpd->defbase_tot, wpd->lock_flags, wpd->vgroup_validmap, locked, unlocked);
|
||||
wpd->vgroup_locked = locked;
|
||||
@@ -1726,7 +1828,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
||||
|
||||
if (wpd->do_multipaint && ts->auto_normalize) {
|
||||
bool *tmpflags;
|
||||
tmpflags = MEM_mallocN(sizeof(bool) * defbase_tot, __func__);
|
||||
tmpflags = (bool *)MEM_mallocN(sizeof(bool) * defbase_tot, __func__);
|
||||
if (wpd->lock_flags) {
|
||||
BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot);
|
||||
}
|
||||
@@ -1738,24 +1840,24 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
||||
else if (ts->auto_normalize) {
|
||||
bool *tmpflags;
|
||||
|
||||
tmpflags = wpd->lock_flags ? MEM_dupallocN(wpd->lock_flags) :
|
||||
MEM_callocN(sizeof(bool) * defbase_tot, __func__);
|
||||
tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) :
|
||||
(bool *)MEM_callocN(sizeof(bool) * defbase_tot, __func__);
|
||||
tmpflags[wpd->active.index] = true;
|
||||
wpd->active.lock = tmpflags;
|
||||
|
||||
tmpflags = wpd->lock_flags ? MEM_dupallocN(wpd->lock_flags) :
|
||||
MEM_callocN(sizeof(bool) * defbase_tot, __func__);
|
||||
tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) :
|
||||
(bool *)MEM_callocN(sizeof(bool) * defbase_tot, __func__);
|
||||
tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true;
|
||||
wpd->mirror.lock = tmpflags;
|
||||
}
|
||||
|
||||
/* If not previously created, create vertex/weight paint mode session data */
|
||||
vertex_paint_init_stroke(depsgraph, ob);
|
||||
vertex_paint_init_stroke(scene, depsgraph, ob);
|
||||
vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
|
||||
vertex_paint_init_session_data(ts, ob);
|
||||
|
||||
if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) {
|
||||
wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__);
|
||||
wpd->precomputed_weight = (float *)MEM_mallocN(sizeof(float) * me->totvert, __func__);
|
||||
}
|
||||
|
||||
if (ob->sculpt->mode.wpaint.dvert_prev != NULL) {
|
||||
@@ -1807,7 +1909,7 @@ static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
|
||||
const MDeformVert *dv = &data->me->dvert[n];
|
||||
|
||||
data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi);
|
||||
@@ -1821,13 +1923,12 @@ static void precompute_weight_values(
|
||||
}
|
||||
|
||||
/* threaded loop over vertices */
|
||||
SculptThreadedTaskData data = {
|
||||
.C = C,
|
||||
.ob = ob,
|
||||
.wpd = wpd,
|
||||
.wpi = wpi,
|
||||
.me = me,
|
||||
};
|
||||
SculptThreadedTaskData data;
|
||||
data.C = C;
|
||||
data.ob = ob;
|
||||
data.wpd = wpd;
|
||||
data.wpi = wpi;
|
||||
data.me = me;
|
||||
|
||||
TaskParallelSettings settings;
|
||||
BLI_parallel_range_settings_defaults(&settings);
|
||||
@@ -1840,7 +1941,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
@@ -1931,7 +2032,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
@@ -2041,7 +2142,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
@@ -2112,7 +2213,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata,
|
||||
static void do_wpaint_brush_calc_average_weight_cb_ex(
|
||||
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
StrokeCache *cache = ss->cache;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
@@ -2143,7 +2244,6 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(
|
||||
BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) {
|
||||
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
// const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
|
||||
const char v_flag = data->me->mvert[v_index].flag;
|
||||
|
||||
/* If the vertex is selected. */
|
||||
@@ -2162,7 +2262,8 @@ static void calculate_average_weight(SculptThreadedTaskData *data,
|
||||
PBVHNode **UNUSED(nodes),
|
||||
int totnode)
|
||||
{
|
||||
struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__);
|
||||
struct WPaintAverageAccum *accum = (WPaintAverageAccum *)MEM_mallocN(sizeof(*accum) * totnode,
|
||||
__func__);
|
||||
data->custom_data = accum;
|
||||
|
||||
TaskParallelSettings settings;
|
||||
@@ -2197,17 +2298,16 @@ static void wpaint_paint_leaves(bContext *C,
|
||||
const Brush *brush = ob->sculpt->cache->brush;
|
||||
|
||||
/* threaded loop over nodes */
|
||||
SculptThreadedTaskData data = {
|
||||
.C = C,
|
||||
.sd = sd,
|
||||
.ob = ob,
|
||||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
.vp = vp,
|
||||
.wpd = wpd,
|
||||
.wpi = wpi,
|
||||
.me = me,
|
||||
};
|
||||
SculptThreadedTaskData data = {0};
|
||||
data.C = C;
|
||||
data.sd = sd;
|
||||
data.ob = ob;
|
||||
data.brush = brush;
|
||||
data.nodes = nodes;
|
||||
data.vp = vp;
|
||||
data.wpd = wpd;
|
||||
data.wpi = wpi;
|
||||
data.me = me;
|
||||
|
||||
/* Use this so average can modify its weight without touching the brush. */
|
||||
data.strength = BKE_brush_weight_get(scene, brush);
|
||||
@@ -2243,12 +2343,12 @@ static PBVHNode **vwpaint_pbvh_gather_generic(
|
||||
|
||||
/* Build a list of all nodes that are potentially within the brush's area of influence */
|
||||
if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
|
||||
SculptSearchSphereData data = {
|
||||
.ss = ss,
|
||||
.sd = sd,
|
||||
.radius_squared = ss->cache->radius_squared,
|
||||
.original = true,
|
||||
};
|
||||
SculptSearchSphereData data = {0};
|
||||
data.ss = ss;
|
||||
data.sd = sd;
|
||||
data.radius_squared = ss->cache->radius_squared;
|
||||
data.original = true;
|
||||
|
||||
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
|
||||
if (use_normal) {
|
||||
SCULPT_pbvh_calc_area_normal(
|
||||
@@ -2262,13 +2362,13 @@ static PBVHNode **vwpaint_pbvh_gather_generic(
|
||||
struct DistRayAABB_Precalc dist_ray_to_aabb_precalc;
|
||||
dist_squared_ray_to_aabb_v3_precalc(
|
||||
&dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal);
|
||||
SculptSearchCircleData data = {
|
||||
.ss = ss,
|
||||
.sd = sd,
|
||||
.radius_squared = ss->cache->radius_squared,
|
||||
.original = true,
|
||||
.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc,
|
||||
};
|
||||
SculptSearchCircleData data = {0};
|
||||
data.ss = ss;
|
||||
data.sd = sd;
|
||||
data.radius_squared = ss->cache->radius_squared;
|
||||
data.original = true;
|
||||
data.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc;
|
||||
|
||||
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode);
|
||||
if (use_normal) {
|
||||
copy_v3_v3(ss->cache->sculpt_normal_symm, ss->cache->view_normal);
|
||||
@@ -2330,7 +2430,7 @@ static void wpaint_do_symmetrical_brush_actions(
|
||||
bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi)
|
||||
{
|
||||
Brush *brush = BKE_paint_brush(&wp->paint);
|
||||
Mesh *me = ob->data;
|
||||
Mesh *me = (Mesh *)ob->data;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
StrokeCache *cache = ss->cache;
|
||||
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
|
||||
@@ -2387,7 +2487,7 @@ static void wpaint_stroke_update_step(bContext *C,
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
VPaint *wp = ts->wpaint;
|
||||
Brush *brush = BKE_paint_brush(&wp->paint);
|
||||
struct WPaintData *wpd = paint_stroke_mode_data(stroke);
|
||||
struct WPaintData *wpd = (WPaintData *)paint_stroke_mode_data(stroke);
|
||||
ViewContext *vc;
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
||||
@@ -2442,7 +2542,7 @@ static void wpaint_stroke_update_step(bContext *C,
|
||||
/* *** done setting up WeightPaintInfo *** */
|
||||
|
||||
if (wpd->precomputed_weight) {
|
||||
precompute_weight_values(C, ob, brush, wpd, &wpi, ob->data);
|
||||
precompute_weight_values(C, ob, brush, wpd, &wpi, (Mesh *)ob->data);
|
||||
}
|
||||
|
||||
wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi);
|
||||
@@ -2455,9 +2555,9 @@ static void wpaint_stroke_update_step(bContext *C,
|
||||
mul_v3_m4v3(loc_world, ob->obmat, ss->cache->true_location);
|
||||
paint_last_stroke_update(scene, loc_world);
|
||||
|
||||
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
|
||||
DEG_id_tag_update(ob->data, 0);
|
||||
DEG_id_tag_update((ID *)ob->data, 0);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
swap_m4m4(wpd->vc.rv3d->persmat, mat);
|
||||
|
||||
@@ -2486,7 +2586,7 @@ static void wpaint_stroke_update_step(bContext *C,
|
||||
static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
struct WPaintData *wpd = paint_stroke_mode_data(stroke);
|
||||
struct WPaintData *wpd = (WPaintData *)paint_stroke_mode_data(stroke);
|
||||
|
||||
if (wpd) {
|
||||
if (wpd->defbase_sel) {
|
||||
@@ -2530,7 +2630,7 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
ParticleSystem *psys;
|
||||
int i;
|
||||
|
||||
for (psys = ob->particlesystem.first; psys; psys = psys->next) {
|
||||
for (psys = (ParticleSystem *)ob->particlesystem.first; psys; psys = psys->next) {
|
||||
for (i = 0; i < PSYS_TOT_VG; i++) {
|
||||
if (psys->vgroup[i] == BKE_object_defgroup_active_index_get(ob)) {
|
||||
psys->recalc |= ID_RECALC_PSYS_RESET;
|
||||
@@ -2540,7 +2640,7 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
}
|
||||
}
|
||||
|
||||
DEG_id_tag_update(ob->data, 0);
|
||||
DEG_id_tag_update((ID *)ob->data, 0);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
|
||||
@@ -2562,7 +2662,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
event->type);
|
||||
|
||||
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
||||
paint_stroke_free(C, op, op->customdata);
|
||||
paint_stroke_free(C, op, (PaintStroke *)op->customdata);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
/* add modal handler */
|
||||
@@ -2586,7 +2686,7 @@ static int wpaint_exec(bContext *C, wmOperator *op)
|
||||
0);
|
||||
|
||||
/* frees op->customdata */
|
||||
paint_stroke_exec(C, op, op->customdata);
|
||||
paint_stroke_exec(C, op, (PaintStroke *)op->customdata);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
@@ -2599,7 +2699,7 @@ static void wpaint_cancel(bContext *C, wmOperator *op)
|
||||
ob->sculpt->cache = NULL;
|
||||
}
|
||||
|
||||
paint_stroke_cancel(C, op, op->customdata);
|
||||
paint_stroke_cancel(C, op, (PaintStroke *)op->customdata);
|
||||
}
|
||||
|
||||
static int wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
@@ -2647,7 +2747,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
if (!is_mode_set) {
|
||||
if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
|
||||
if (!ED_object_mode_compat_set(C, ob, (eObjectMode)mode_flag, op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
@@ -2667,13 +2767,12 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
||||
BKE_paint_toolslots_brush_validate(bmain, &ts->vpaint->paint);
|
||||
}
|
||||
|
||||
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
|
||||
/* update modifier stack for mapping requirements */
|
||||
DEG_id_tag_update(&me->id, 0);
|
||||
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene);
|
||||
|
||||
WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
|
||||
|
||||
WM_toolsystem_update_from_context_view3d(C);
|
||||
@@ -2720,11 +2819,17 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
|
||||
* - revise whether op->customdata should be added in object, in set_vpaint.
|
||||
*/
|
||||
|
||||
struct VPaintData {
|
||||
struct VPaintDataBase {
|
||||
ViewContext vc;
|
||||
AttributeDomain domain;
|
||||
CustomDataType type;
|
||||
};
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
struct VPaintData : public VPaintDataBase {
|
||||
struct NormalAnglePrecalc normal_angle_precalc;
|
||||
|
||||
uint paintcol;
|
||||
Color paintcol;
|
||||
|
||||
struct VertProjHandle *vp_handle;
|
||||
struct CoNo *vertexcosnos;
|
||||
@@ -2743,19 +2848,87 @@ struct VPaintData {
|
||||
|
||||
/* Special storage for smear brush, avoid feedback loop - update each step. */
|
||||
struct {
|
||||
uint *color_prev;
|
||||
uint *color_curr;
|
||||
void *color_prev;
|
||||
void *color_curr;
|
||||
} smear;
|
||||
};
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void *vpaint_init_vpaint(bContext *C,
|
||||
struct wmOperator *op,
|
||||
Scene *scene,
|
||||
Depsgraph *depsgraph,
|
||||
VPaint *vp,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
const Brush *brush)
|
||||
{
|
||||
VPaintData<Color, Traits, domain> *vpd;
|
||||
|
||||
size_t elem_size;
|
||||
int elem_num = get_vcol_elements(me, &elem_size);
|
||||
/* make mode data storage */
|
||||
vpd = MEM_new<VPaintData<Color, Traits, domain>>("VPaintData");
|
||||
|
||||
if constexpr (std::is_same_v<Color, ColorPaint4f>) {
|
||||
vpd->type = CD_PROP_COLOR;
|
||||
}
|
||||
else {
|
||||
vpd->type = CD_PROP_BYTE_COLOR;
|
||||
}
|
||||
|
||||
vpd->domain = domain;
|
||||
|
||||
ED_view3d_viewcontext_init(C, &vpd->vc, depsgraph);
|
||||
view_angle_limits_init(&vpd->normal_angle_precalc,
|
||||
vp->paint.brush->falloff_angle,
|
||||
(vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0);
|
||||
|
||||
vpd->paintcol = vpaint_get_current_col<Color, Traits, domain>(
|
||||
scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT));
|
||||
|
||||
vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex;
|
||||
|
||||
/* are we painting onto a modified mesh?,
|
||||
* if not we can skip face map trickiness */
|
||||
if (vertex_paint_use_fast_update_check(ob)) {
|
||||
vpd->use_fast_update = true;
|
||||
}
|
||||
else {
|
||||
vpd->use_fast_update = false;
|
||||
}
|
||||
|
||||
/* to keep tracked of modified loops for shared vertex color blending */
|
||||
if (brush->vertexpaint_tool == VPAINT_TOOL_BLUR) {
|
||||
vpd->mlooptag = (bool *)MEM_mallocN(sizeof(bool) * elem_num, "VPaintData mlooptag");
|
||||
}
|
||||
|
||||
if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) {
|
||||
CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
|
||||
|
||||
vpd->smear.color_prev = MEM_malloc_arrayN(elem_num, elem_size, __func__);
|
||||
memcpy(vpd->smear.color_prev, layer->data, elem_size * elem_num);
|
||||
|
||||
vpd->smear.color_curr = (uint *)MEM_dupallocN(vpd->smear.color_prev);
|
||||
}
|
||||
|
||||
/* Create projection handle */
|
||||
if (vpd->is_texbrush) {
|
||||
ob->sculpt->building_vp_handle = true;
|
||||
vpd->vp_handle = ED_vpaint_proj_handle_create(depsgraph, scene, ob, &vpd->vertexcosnos);
|
||||
ob->sculpt->building_vp_handle = false;
|
||||
}
|
||||
|
||||
return static_cast<void *>(vpd);
|
||||
}
|
||||
|
||||
static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2])
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
struct PaintStroke *stroke = op->customdata;
|
||||
struct PaintStroke *stroke = (PaintStroke *)op->customdata;
|
||||
VPaint *vp = ts->vpaint;
|
||||
Brush *brush = BKE_paint_brush(&vp->paint);
|
||||
struct VPaintData *vpd;
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Mesh *me;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
@@ -2768,500 +2941,594 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f
|
||||
}
|
||||
|
||||
ED_mesh_color_ensure(me, NULL);
|
||||
if (me->mloopcol == NULL) {
|
||||
|
||||
CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
|
||||
if (!layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* make mode data storage */
|
||||
vpd = MEM_callocN(sizeof(*vpd), "VPaintData");
|
||||
AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer);
|
||||
void *vpd = nullptr;
|
||||
|
||||
if (domain == ATTR_DOMAIN_POINT) {
|
||||
if (layer->type == CD_PROP_COLOR) {
|
||||
vpd = vpaint_init_vpaint<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(
|
||||
C, op, scene, depsgraph, vp, ob, me, brush);
|
||||
}
|
||||
else if (layer->type == CD_PROP_BYTE_COLOR) {
|
||||
vpd = vpaint_init_vpaint<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(
|
||||
C, op, scene, depsgraph, vp, ob, me, brush);
|
||||
}
|
||||
}
|
||||
else if (domain == ATTR_DOMAIN_CORNER) {
|
||||
if (layer->type == CD_PROP_COLOR) {
|
||||
vpd = vpaint_init_vpaint<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(
|
||||
C, op, scene, depsgraph, vp, ob, me, brush);
|
||||
}
|
||||
else if (layer->type == CD_PROP_BYTE_COLOR) {
|
||||
vpd = vpaint_init_vpaint<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(
|
||||
C, op, scene, depsgraph, vp, ob, me, brush);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(vpd != nullptr);
|
||||
|
||||
paint_stroke_set_mode_data(stroke, vpd);
|
||||
ED_view3d_viewcontext_init(C, &vpd->vc, depsgraph);
|
||||
view_angle_limits_init(&vpd->normal_angle_precalc,
|
||||
vp->paint.brush->falloff_angle,
|
||||
(vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0);
|
||||
|
||||
vpd->paintcol = vpaint_get_current_col(
|
||||
scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT));
|
||||
|
||||
vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex;
|
||||
|
||||
/* are we painting onto a modified mesh?,
|
||||
* if not we can skip face map trickiness */
|
||||
if (vertex_paint_use_fast_update_check(ob)) {
|
||||
vpd->use_fast_update = true;
|
||||
// printf("Fast update!\n");
|
||||
}
|
||||
else {
|
||||
vpd->use_fast_update = false;
|
||||
// printf("No fast update!\n");
|
||||
}
|
||||
|
||||
/* to keep tracked of modified loops for shared vertex color blending */
|
||||
if (brush->vertexpaint_tool == VPAINT_TOOL_BLUR) {
|
||||
vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
|
||||
}
|
||||
|
||||
if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) {
|
||||
vpd->smear.color_prev = MEM_mallocN(sizeof(uint) * me->totloop, __func__);
|
||||
memcpy(vpd->smear.color_prev, me->mloopcol, sizeof(uint) * me->totloop);
|
||||
vpd->smear.color_curr = MEM_dupallocN(vpd->smear.color_prev);
|
||||
}
|
||||
|
||||
/* Create projection handle */
|
||||
if (vpd->is_texbrush) {
|
||||
ob->sculpt->building_vp_handle = true;
|
||||
vpd->vp_handle = ED_vpaint_proj_handle_create(depsgraph, scene, ob, &vpd->vertexcosnos);
|
||||
ob->sculpt->building_vp_handle = false;
|
||||
}
|
||||
|
||||
/* If not previously created, create vertex/weight paint mode session data */
|
||||
vertex_paint_init_stroke(depsgraph, ob);
|
||||
vertex_paint_init_stroke(scene, depsgraph, ob);
|
||||
vwpaint_update_cache_invariants(C, vp, ss, op, mouse);
|
||||
vertex_paint_init_session_data(ts, ob);
|
||||
|
||||
if (ob->sculpt->mode.vpaint.previous_color != NULL) {
|
||||
memset(ob->sculpt->mode.vpaint.previous_color, 0, sizeof(uint) * me->totloop);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void do_vpaint_brush_calc_average_color_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
template<class Color = ColorPaint4b, typename Traits = ByteTraits>
|
||||
static void do_vpaint_brush_blur_loops(bContext *C,
|
||||
Sculpt *sd,
|
||||
VPaint *vp,
|
||||
VPaintData<Color, Traits, ATTR_DOMAIN_CORNER> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
PBVHNode **nodes,
|
||||
int totnode,
|
||||
Color *lcol)
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
StrokeCache *cache = ss->cache;
|
||||
uint *lcol = data->lcol;
|
||||
char *col;
|
||||
const bool use_vert_sel = (data->me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n;
|
||||
accum->len = 0;
|
||||
memset(accum->value, 0, sizeof(accum->value));
|
||||
const Brush *brush = ob->sculpt->cache->brush;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, data->brush->falloff_shape);
|
||||
Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint);
|
||||
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
if (BKE_brush_curve_strength(data->brush, 0.0, cache->radius) > 0.0) {
|
||||
/* If the vertex is selected for painting. */
|
||||
const MVert *mv = &data->me->mvert[v_index];
|
||||
if (!use_vert_sel || mv->flag & SELECT) {
|
||||
accum->len += gmap->vert_to_loop[v_index].count;
|
||||
/* if a vertex is within the brush region, then add its color to the blend. */
|
||||
for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) {
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
col = (char *)(&lcol[l_index]);
|
||||
/* Color is squared to compensate the sqrt color encoding. */
|
||||
accum->value[0] += col[0] * col[0];
|
||||
accum->value[1] += col[1] * col[1];
|
||||
accum->value[2] += col[2] * col[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) {
|
||||
for (int n : range) {
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
|
||||
static float tex_color_alpha_ubyte(SculptThreadedTaskData *data,
|
||||
const float v_co[3],
|
||||
uint *r_color)
|
||||
{
|
||||
float rgba[4];
|
||||
float rgba_br[3];
|
||||
tex_color_alpha(data->vp, &data->vpd->vc, v_co, rgba);
|
||||
rgb_uchar_to_float(rgba_br, (const uchar *)&data->vpd->paintcol);
|
||||
mul_v3_v3(rgba_br, rgba);
|
||||
rgb_float_to_uchar((uchar *)r_color, rgba_br);
|
||||
return rgba[3];
|
||||
}
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
const StrokeCache *cache = ss->cache;
|
||||
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
||||
get_brush_alpha_data(
|
||||
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
||||
const bool use_normal = vwpaint_use_normal(vp);
|
||||
const bool use_vert_sel = (me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
|
||||
static void do_vpaint_brush_draw_task_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, brush->falloff_shape);
|
||||
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
|
||||
ss, brush->falloff_shape);
|
||||
|
||||
const Brush *brush = data->brush;
|
||||
const StrokeCache *cache = ss->cache;
|
||||
uint *lcol = data->lcol;
|
||||
const Scene *scene = CTX_data_scene(data->C);
|
||||
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
||||
get_brush_alpha_data(
|
||||
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
||||
const bool use_normal = vwpaint_use_normal(data->vp);
|
||||
const bool use_vert_sel = (data->me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
|
||||
* Otherwise, take the current vert. */
|
||||
const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
|
||||
const MVert *mv = &me->mvert[v_index];
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, data->brush->falloff_shape);
|
||||
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
|
||||
ss, data->brush->falloff_shape);
|
||||
/* If the vertex is selected for painting. */
|
||||
if (!use_vert_sel || mv->flag & SELECT) {
|
||||
float brush_strength = cache->bstrength;
|
||||
const float angle_cos = (use_normal && vd.no) ?
|
||||
dot_v3v3(sculpt_normal_frontface, vd.no) :
|
||||
1.0f;
|
||||
if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) &&
|
||||
((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 ||
|
||||
view_angle_limits_apply_falloff(
|
||||
&vpd->normal_angle_precalc, angle_cos, &brush_strength))) {
|
||||
const float brush_fade = BKE_brush_curve_strength(
|
||||
brush, sqrtf(test.dist), cache->radius);
|
||||
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
/* NOTE: Grids are 1:1 with corners (aka loops).
|
||||
* For grid based pbvh, take the vert whose loop corresponds to the current grid.
|
||||
* Otherwise, take the current vert. */
|
||||
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
|
||||
const MVert *mv = &data->me->mvert[v_index];
|
||||
/* Get the average poly color */
|
||||
Color color_final(0, 0, 0, 0);
|
||||
|
||||
/* If the vertex is selected for painting. */
|
||||
if (!use_vert_sel || mv->flag & SELECT) {
|
||||
/* Calc the dot prod. between ray norm on surf and current vert
|
||||
* (ie splash prevention factor), and only paint front facing verts. */
|
||||
float brush_strength = cache->bstrength;
|
||||
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
|
||||
1.0f;
|
||||
if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) &&
|
||||
((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 ||
|
||||
view_angle_limits_apply_falloff(
|
||||
&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) {
|
||||
const float brush_fade = BKE_brush_curve_strength(
|
||||
brush, sqrtf(test.dist), cache->radius);
|
||||
uint color_final = data->vpd->paintcol;
|
||||
int total_hit_loops = 0;
|
||||
Blend blend[4] = {0};
|
||||
|
||||
/* If we're painting with a texture, sample the texture color and alpha. */
|
||||
float tex_alpha = 1.0;
|
||||
if (data->vpd->is_texbrush) {
|
||||
/* NOTE: we may want to paint alpha as vertex color alpha. */
|
||||
tex_alpha = tex_color_alpha_ubyte(
|
||||
data, data->vpd->vertexcosnos[v_index].co, &color_final);
|
||||
}
|
||||
/* For each poly owning this vert, paint each loop belonging to this vert. */
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
BLI_assert(data->me->mloop[l_index].v == v_index);
|
||||
const MPoly *mp = &data->me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
uint color_orig = 0; /* unused when array is NULL */
|
||||
if (ss->mode.vpaint.previous_color != NULL) {
|
||||
/* Get the previous loop color */
|
||||
if (ss->mode.vpaint.previous_color[l_index] == 0) {
|
||||
ss->mode.vpaint.previous_color[l_index] = lcol[l_index];
|
||||
}
|
||||
color_orig = ss->mode.vpaint.previous_color[l_index];
|
||||
}
|
||||
const float final_alpha = 255 * brush_fade * brush_strength * tex_alpha *
|
||||
brush_alpha_pressure * grid_alpha;
|
||||
|
||||
/* Mix the new color with the original based on final_alpha. */
|
||||
lcol[l_index] = vpaint_blend(data->vp,
|
||||
lcol[l_index],
|
||||
color_orig,
|
||||
color_final,
|
||||
final_alpha,
|
||||
255 * brush_strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
static void do_vpaint_brush_blur_task_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
|
||||
Scene *scene = CTX_data_scene(data->C);
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
const Brush *brush = data->brush;
|
||||
const StrokeCache *cache = ss->cache;
|
||||
uint *lcol = data->lcol;
|
||||
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
||||
get_brush_alpha_data(
|
||||
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
||||
const bool use_normal = vwpaint_use_normal(data->vp);
|
||||
const bool use_vert_sel = (data->me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, data->brush->falloff_shape);
|
||||
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
|
||||
ss, data->brush->falloff_shape);
|
||||
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
|
||||
* Otherwise, take the current vert. */
|
||||
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
|
||||
const MVert *mv = &data->me->mvert[v_index];
|
||||
|
||||
/* If the vertex is selected for painting. */
|
||||
if (!use_vert_sel || mv->flag & SELECT) {
|
||||
float brush_strength = cache->bstrength;
|
||||
const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) :
|
||||
1.0f;
|
||||
if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) &&
|
||||
((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 ||
|
||||
view_angle_limits_apply_falloff(
|
||||
&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) {
|
||||
const float brush_fade = BKE_brush_curve_strength(
|
||||
brush, sqrtf(test.dist), cache->radius);
|
||||
|
||||
/* Get the average poly color */
|
||||
uint color_final = 0;
|
||||
int total_hit_loops = 0;
|
||||
uint blend[4] = {0};
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const MPoly *mp = &data->me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
total_hit_loops += mp->totloop;
|
||||
for (int k = 0; k < mp->totloop; k++) {
|
||||
const uint l_index = mp->loopstart + k;
|
||||
const char *col = (const char *)(&lcol[l_index]);
|
||||
/* Color is squared to compensate the sqrt color encoding. */
|
||||
blend[0] += (uint)col[0] * (uint)col[0];
|
||||
blend[1] += (uint)col[1] * (uint)col[1];
|
||||
blend[2] += (uint)col[2] * (uint)col[2];
|
||||
blend[3] += (uint)col[3] * (uint)col[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (total_hit_loops != 0) {
|
||||
/* Use rgb^2 color averaging. */
|
||||
char *col = (char *)(&color_final);
|
||||
col[0] = round_fl_to_uchar(sqrtf(divide_round_i(blend[0], total_hit_loops)));
|
||||
col[1] = round_fl_to_uchar(sqrtf(divide_round_i(blend[1], total_hit_loops)));
|
||||
col[2] = round_fl_to_uchar(sqrtf(divide_round_i(blend[2], total_hit_loops)));
|
||||
col[3] = round_fl_to_uchar(sqrtf(divide_round_i(blend[3], total_hit_loops)));
|
||||
|
||||
/* For each poly owning this vert,
|
||||
* paint each loop belonging to this vert. */
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
BLI_assert(data->me->mloop[l_index].v == v_index);
|
||||
const MPoly *mp = &data->me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
uint color_orig = 0; /* unused when array is NULL */
|
||||
if (ss->mode.vpaint.previous_color != NULL) {
|
||||
/* Get the previous loop color */
|
||||
if (ss->mode.vpaint.previous_color[l_index] == 0) {
|
||||
ss->mode.vpaint.previous_color[l_index] = lcol[l_index];
|
||||
}
|
||||
color_orig = ss->mode.vpaint.previous_color[l_index];
|
||||
}
|
||||
const float final_alpha = 255 * brush_fade * brush_strength *
|
||||
brush_alpha_pressure * grid_alpha;
|
||||
/* Mix the new color with the original
|
||||
* based on the brush strength and the curve. */
|
||||
lcol[l_index] = vpaint_blend(data->vp,
|
||||
lcol[l_index],
|
||||
color_orig,
|
||||
*((uint *)col),
|
||||
final_alpha,
|
||||
255 * brush_strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
|
||||
static void do_vpaint_brush_smear_task_cb_ex(void *__restrict userdata,
|
||||
const int n,
|
||||
const TaskParallelTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
SculptThreadedTaskData *data = userdata;
|
||||
SculptSession *ss = data->ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
|
||||
Scene *scene = CTX_data_scene(data->C);
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
const Brush *brush = data->brush;
|
||||
const StrokeCache *cache = ss->cache;
|
||||
uint *lcol = data->lcol;
|
||||
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
||||
get_brush_alpha_data(
|
||||
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
||||
float brush_dir[3];
|
||||
const bool use_normal = vwpaint_use_normal(data->vp);
|
||||
const bool use_vert_sel = (data->me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
|
||||
sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
|
||||
project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal);
|
||||
|
||||
if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) {
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, data->brush->falloff_shape);
|
||||
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
|
||||
ss, data->brush->falloff_shape);
|
||||
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
|
||||
* Otherwise, take the current vert. */
|
||||
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
|
||||
const MVert *mv_curr = &data->me->mvert[v_index];
|
||||
|
||||
/* if the vertex is selected for painting. */
|
||||
if (!use_vert_sel || mv_curr->flag & SELECT) {
|
||||
/* Calc the dot prod. between ray norm on surf and current vert
|
||||
* (ie splash prevention factor), and only paint front facing verts. */
|
||||
float brush_strength = cache->bstrength;
|
||||
const float angle_cos = (use_normal && vd.no) ?
|
||||
dot_v3v3(sculpt_normal_frontface, vd.no) :
|
||||
1.0f;
|
||||
if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) &&
|
||||
((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 ||
|
||||
view_angle_limits_apply_falloff(
|
||||
&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) {
|
||||
const float brush_fade = BKE_brush_curve_strength(
|
||||
brush, sqrtf(test.dist), cache->radius);
|
||||
|
||||
bool do_color = false;
|
||||
/* Minimum dot product between brush direction and current
|
||||
* to neighbor direction is 0.0, meaning orthogonal. */
|
||||
float stroke_dot_max = 0.0f;
|
||||
|
||||
/* Get the color of the loop in the opposite
|
||||
* direction of the brush movement */
|
||||
uint color_final = 0;
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
BLI_assert(data->me->mloop[l_index].v == v_index);
|
||||
UNUSED_VARS_NDEBUG(l_index);
|
||||
const MPoly *mp = &data->me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
const MLoop *ml_other = &data->me->mloop[mp->loopstart];
|
||||
for (int k = 0; k < mp->totloop; k++, ml_other++) {
|
||||
const uint v_other_index = ml_other->v;
|
||||
if (v_other_index != v_index) {
|
||||
const MVert *mv_other = &data->me->mvert[v_other_index];
|
||||
|
||||
/* Get the direction from the
|
||||
* selected vert to the neighbor. */
|
||||
float other_dir[3];
|
||||
sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co);
|
||||
project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal);
|
||||
|
||||
normalize_v3(other_dir);
|
||||
|
||||
const float stroke_dot = dot_v3v3(other_dir, brush_dir);
|
||||
|
||||
if (stroke_dot > stroke_dot_max) {
|
||||
stroke_dot_max = stroke_dot;
|
||||
color_final = data->vpd->smear.color_prev[mp->loopstart + k];
|
||||
do_color = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (do_color) {
|
||||
const float final_alpha = 255 * brush_fade * brush_strength * brush_alpha_pressure *
|
||||
grid_alpha;
|
||||
|
||||
/* For each poly owning this vert,
|
||||
* paint each loop belonging to this vert. */
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
BLI_assert(data->me->mloop[l_index].v == v_index);
|
||||
const MPoly *mp = &data->me->mpoly[p_index];
|
||||
int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const MPoly *mp = &me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
/* Get the previous loop color */
|
||||
uint color_orig = 0; /* unused when array is NULL */
|
||||
if (ss->mode.vpaint.previous_color != NULL) {
|
||||
/* Get the previous loop color */
|
||||
if (ss->mode.vpaint.previous_color[l_index] == 0) {
|
||||
ss->mode.vpaint.previous_color[l_index] = lcol[l_index];
|
||||
}
|
||||
color_orig = ss->mode.vpaint.previous_color[l_index];
|
||||
}
|
||||
/* Mix the new color with the original
|
||||
* based on the brush strength and the curve. */
|
||||
lcol[l_index] = vpaint_blend(data->vp,
|
||||
lcol[l_index],
|
||||
color_orig,
|
||||
color_final,
|
||||
final_alpha,
|
||||
255 * brush_strength);
|
||||
total_hit_loops += mp->totloop;
|
||||
for (int k = 0; k < mp->totloop; k++) {
|
||||
const uint l_index = mp->loopstart + k;
|
||||
Color *col = lcol + l_index;
|
||||
|
||||
data->vpd->smear.color_curr[l_index] = lcol[l_index];
|
||||
/* Color is squared to compensate the sqrt color encoding. */
|
||||
blend[0] += (Blend)col->r * (Blend)col->r;
|
||||
blend[1] += (Blend)col->g * (Blend)col->g;
|
||||
blend[2] += (Blend)col->b * (Blend)col->b;
|
||||
blend[3] += (Blend)col->a * (Blend)col->a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (total_hit_loops != 0) {
|
||||
/* Use rgb^2 color averaging. */
|
||||
Color *col = &color_final;
|
||||
|
||||
color_final.r = Traits::round(
|
||||
sqrtf(Traits::divide_round(blend[0], total_hit_loops)));
|
||||
color_final.g = Traits::round(
|
||||
sqrtf(Traits::divide_round(blend[1], total_hit_loops)));
|
||||
color_final.b = Traits::round(
|
||||
sqrtf(Traits::divide_round(blend[2], total_hit_loops)));
|
||||
color_final.a = Traits::round(
|
||||
sqrtf(Traits::divide_round(blend[3], total_hit_loops)));
|
||||
|
||||
/* For each poly owning this vert,
|
||||
* paint each loop belonging to this vert. */
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
BLI_assert(me->mloop[l_index].v == v_index);
|
||||
const MPoly *mp = &me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
Color color_orig(0, 0, 0, 0); /* unused when array is NULL */
|
||||
|
||||
if (previous_color != NULL) {
|
||||
/* Get the previous loop color */
|
||||
if (isZero(previous_color[l_index])) {
|
||||
previous_color[l_index] = lcol[l_index];
|
||||
}
|
||||
color_orig = previous_color[l_index];
|
||||
}
|
||||
const float final_alpha = Traits::range * brush_fade * brush_strength *
|
||||
brush_alpha_pressure * grid_alpha;
|
||||
/* Mix the new color with the original
|
||||
* based on the brush strength and the curve. */
|
||||
lcol[l_index] = vpaint_blend<Color, Traits>(vp,
|
||||
lcol[l_index],
|
||||
color_orig,
|
||||
*col,
|
||||
final_alpha,
|
||||
Traits::range * brush_strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static void calculate_average_color(SculptThreadedTaskData *data,
|
||||
PBVHNode **UNUSED(nodes),
|
||||
template<class Color = ColorPaint4b, typename Traits = ByteTraits>
|
||||
static void do_vpaint_brush_blur_verts(bContext *C,
|
||||
Sculpt *sd,
|
||||
VPaint *vp,
|
||||
VPaintData<Color, Traits, ATTR_DOMAIN_POINT> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
PBVHNode **nodes,
|
||||
int totnode,
|
||||
Color *lcol)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
const Brush *brush = ob->sculpt->cache->brush;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
||||
Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint);
|
||||
|
||||
blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) {
|
||||
for (int n : range) {
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
const StrokeCache *cache = ss->cache;
|
||||
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
||||
get_brush_alpha_data(
|
||||
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
||||
const bool use_normal = vwpaint_use_normal(vp);
|
||||
const bool use_vert_sel = (me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, brush->falloff_shape);
|
||||
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
|
||||
ss, brush->falloff_shape);
|
||||
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
|
||||
* Otherwise, take the current vert. */
|
||||
const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
|
||||
const MVert *mv = &me->mvert[v_index];
|
||||
|
||||
/* If the vertex is selected for painting. */
|
||||
if (!use_vert_sel || mv->flag & SELECT) {
|
||||
float brush_strength = cache->bstrength;
|
||||
const float angle_cos = (use_normal && vd.no) ?
|
||||
dot_v3v3(sculpt_normal_frontface, vd.no) :
|
||||
1.0f;
|
||||
if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) &&
|
||||
((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 ||
|
||||
view_angle_limits_apply_falloff(
|
||||
&vpd->normal_angle_precalc, angle_cos, &brush_strength))) {
|
||||
const float brush_fade = BKE_brush_curve_strength(
|
||||
brush, sqrtf(test.dist), cache->radius);
|
||||
|
||||
/* Get the average poly color */
|
||||
Color color_final(0, 0, 0, 0);
|
||||
|
||||
int total_hit_loops = 0;
|
||||
Blend blend[4] = {0};
|
||||
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const MPoly *mp = &me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
total_hit_loops += mp->totloop;
|
||||
for (int k = 0; k < mp->totloop; k++) {
|
||||
const uint l_index = mp->loopstart + k;
|
||||
const uint v_index = me->mloop[l_index].v;
|
||||
|
||||
Color *col = lcol + v_index;
|
||||
|
||||
/* Color is squared to compensate the sqrt color encoding. */
|
||||
blend[0] += (Blend)col->r * (Blend)col->r;
|
||||
blend[1] += (Blend)col->g * (Blend)col->g;
|
||||
blend[2] += (Blend)col->b * (Blend)col->b;
|
||||
blend[3] += (Blend)col->a * (Blend)col->a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (total_hit_loops != 0) {
|
||||
/* Use rgb^2 color averaging. */
|
||||
Color *col = &color_final;
|
||||
|
||||
color_final.r = Traits::round(
|
||||
sqrtf(Traits::divide_round(blend[0], total_hit_loops)));
|
||||
color_final.g = Traits::round(
|
||||
sqrtf(Traits::divide_round(blend[1], total_hit_loops)));
|
||||
color_final.b = Traits::round(
|
||||
sqrtf(Traits::divide_round(blend[2], total_hit_loops)));
|
||||
color_final.a = Traits::round(
|
||||
sqrtf(Traits::divide_round(blend[3], total_hit_loops)));
|
||||
|
||||
/* For each poly owning this vert,
|
||||
* paint each loop belonging to this vert. */
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
|
||||
BLI_assert(me->mloop[l_index].v == v_index);
|
||||
const MPoly *mp = &me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
Color color_orig(0, 0, 0, 0); /* unused when array is NULL */
|
||||
|
||||
if (previous_color != NULL) {
|
||||
/* Get the previous loop color */
|
||||
if (isZero(previous_color[v_index])) {
|
||||
previous_color[v_index] = lcol[v_index];
|
||||
}
|
||||
color_orig = previous_color[v_index];
|
||||
}
|
||||
const float final_alpha = Traits::range * brush_fade * brush_strength *
|
||||
brush_alpha_pressure * grid_alpha;
|
||||
/* Mix the new color with the original
|
||||
* based on the brush strength and the curve. */
|
||||
lcol[v_index] = vpaint_blend<Color, Traits>(vp,
|
||||
lcol[v_index],
|
||||
color_orig,
|
||||
*col,
|
||||
final_alpha,
|
||||
Traits::range * brush_strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Color = ColorPaint4b, typename Traits, AttributeDomain domain>
|
||||
static void do_vpaint_brush_smear(bContext *C,
|
||||
Sculpt *sd,
|
||||
VPaint *vp,
|
||||
VPaintData<Color, Traits, domain> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
PBVHNode **nodes,
|
||||
int totnode,
|
||||
Color *lcol)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
using Blend = typename Traits::BlendType;
|
||||
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
const StrokeCache *cache = ss->cache;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
|
||||
const Brush *brush = ob->sculpt->cache->brush;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
Color *color_curr = static_cast<Color *>(vpd->smear.color_curr);
|
||||
Color *color_prev_smear = static_cast<Color *>(vpd->smear.color_prev);
|
||||
Color *color_prev = reinterpret_cast<Color *>(ss->cache->prev_colors_vpaint);
|
||||
|
||||
blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) {
|
||||
for (int n : range) {
|
||||
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
||||
|
||||
get_brush_alpha_data(
|
||||
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
||||
float brush_dir[3];
|
||||
const bool use_normal = vwpaint_use_normal(vp);
|
||||
const bool use_vert_sel = (me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
|
||||
sub_v3_v3v3(brush_dir, cache->location, cache->last_location);
|
||||
project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal);
|
||||
|
||||
if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) {
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, brush->falloff_shape);
|
||||
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
|
||||
ss, brush->falloff_shape);
|
||||
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
|
||||
* Otherwise, take the current vert. */
|
||||
const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
|
||||
const MVert *mv_curr = &me->mvert[v_index];
|
||||
|
||||
/* if the vertex is selected for painting. */
|
||||
if (!use_vert_sel || mv_curr->flag & SELECT) {
|
||||
/* Calc the dot prod. between ray norm on surf and current vert
|
||||
* (ie splash prevention factor), and only paint front facing verts. */
|
||||
float brush_strength = cache->bstrength;
|
||||
const float angle_cos = (use_normal && vd.no) ?
|
||||
dot_v3v3(sculpt_normal_frontface, vd.no) :
|
||||
1.0f;
|
||||
if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) &&
|
||||
((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 ||
|
||||
view_angle_limits_apply_falloff(
|
||||
&vpd->normal_angle_precalc, angle_cos, &brush_strength))) {
|
||||
const float brush_fade = BKE_brush_curve_strength(
|
||||
brush, sqrtf(test.dist), cache->radius);
|
||||
|
||||
bool do_color = false;
|
||||
/* Minimum dot product between brush direction and current
|
||||
* to neighbor direction is 0.0, meaning orthogonal. */
|
||||
float stroke_dot_max = 0.0f;
|
||||
|
||||
/* Get the color of the loop in the opposite
|
||||
* direction of the brush movement */
|
||||
Color color_final(0, 0, 0, 0);
|
||||
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
BLI_assert(me->mloop[l_index].v == v_index);
|
||||
UNUSED_VARS_NDEBUG(l_index);
|
||||
const MPoly *mp = &me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
const MLoop *ml_other = &me->mloop[mp->loopstart];
|
||||
for (int k = 0; k < mp->totloop; k++, ml_other++) {
|
||||
const uint v_other_index = ml_other->v;
|
||||
if (v_other_index != v_index) {
|
||||
const MVert *mv_other = &me->mvert[v_other_index];
|
||||
|
||||
/* Get the direction from the
|
||||
* selected vert to the neighbor. */
|
||||
float other_dir[3];
|
||||
sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co);
|
||||
project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal);
|
||||
|
||||
normalize_v3(other_dir);
|
||||
|
||||
const float stroke_dot = dot_v3v3(other_dir, brush_dir);
|
||||
int elem_index;
|
||||
|
||||
if constexpr (domain == ATTR_DOMAIN_POINT) {
|
||||
elem_index = ml_other->v;
|
||||
}
|
||||
else {
|
||||
elem_index = mp->loopstart + k;
|
||||
}
|
||||
|
||||
if (stroke_dot > stroke_dot_max) {
|
||||
stroke_dot_max = stroke_dot;
|
||||
color_final = color_prev_smear[elem_index];
|
||||
do_color = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (do_color) {
|
||||
const float final_alpha = Traits::range * brush_fade * brush_strength *
|
||||
brush_alpha_pressure * grid_alpha;
|
||||
|
||||
/* For each poly owning this vert,
|
||||
* paint each loop belonging to this vert. */
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
|
||||
int elem_index;
|
||||
if constexpr (domain == ATTR_DOMAIN_POINT) {
|
||||
elem_index = v_index;
|
||||
}
|
||||
else {
|
||||
elem_index = l_index;
|
||||
}
|
||||
|
||||
BLI_assert(me->mloop[l_index].v == v_index);
|
||||
|
||||
const MPoly *mp = &me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
/* Get the previous element color */
|
||||
Color color_orig(0, 0, 0, 0); /* unused when array is NULL */
|
||||
|
||||
if (color_prev != NULL) {
|
||||
/* Get the previous element color */
|
||||
if (isZero(color_prev[elem_index])) {
|
||||
color_prev[elem_index] = lcol[elem_index];
|
||||
}
|
||||
color_orig = color_prev[elem_index];
|
||||
}
|
||||
/* Mix the new color with the original
|
||||
* based on the brush strength and the curve. */
|
||||
lcol[elem_index] = vpaint_blend<Color, Traits>(vp,
|
||||
lcol[elem_index],
|
||||
color_orig,
|
||||
color_final,
|
||||
final_alpha,
|
||||
Traits::range *
|
||||
brush_strength);
|
||||
|
||||
color_curr[elem_index] = lcol[elem_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
const Brush *brush,
|
||||
Color *lcol,
|
||||
PBVHNode **nodes,
|
||||
int totnode)
|
||||
{
|
||||
struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__);
|
||||
data->custom_data = accum;
|
||||
using Blend = typename Traits::BlendType;
|
||||
using Value = typename Traits::ValueType;
|
||||
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
|
||||
BLI_task_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings);
|
||||
struct VPaintAverageAccum<Blend> *accum = (VPaintAverageAccum<Blend> *)MEM_mallocN(
|
||||
sizeof(*accum) * totnode, __func__);
|
||||
blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) {
|
||||
for (int n : range) {
|
||||
SculptSession *ss = ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
|
||||
StrokeCache *cache = ss->cache;
|
||||
const bool use_vert_sel = (me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
|
||||
struct VPaintAverageAccum<Blend> *accum2 = accum + n;
|
||||
accum2->len = 0;
|
||||
memset(accum2->value, 0, sizeof(accum2->value));
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, brush->falloff_shape);
|
||||
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
if (BKE_brush_curve_strength(brush, 0.0, cache->radius) > 0.0) {
|
||||
/* If the vertex is selected for painting. */
|
||||
const MVert *mv = &me->mvert[v_index];
|
||||
if (!use_vert_sel || mv->flag & SELECT) {
|
||||
accum2->len += gmap->vert_to_loop[v_index].count;
|
||||
/* if a vertex is within the brush region, then add its color to the blend. */
|
||||
for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) {
|
||||
int elem_index;
|
||||
|
||||
if constexpr (domain == ATTR_DOMAIN_CORNER) {
|
||||
elem_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
}
|
||||
else {
|
||||
elem_index = v_index;
|
||||
}
|
||||
|
||||
Color *col = lcol + elem_index;
|
||||
|
||||
/* Color is squared to compensate the sqrt color encoding. */
|
||||
accum2->value[0] += col->r * col->r;
|
||||
accum2->value[1] += col->g * col->g;
|
||||
accum2->value[2] += col->b * col->b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
});
|
||||
|
||||
Blend accum_len = 0;
|
||||
Blend accum_value[3] = {0};
|
||||
Color blend(0, 0, 0, 0);
|
||||
|
||||
uint accum_len = 0;
|
||||
uint accum_value[3] = {0};
|
||||
uchar blend[4] = {0};
|
||||
for (int i = 0; i < totnode; i++) {
|
||||
accum_len += accum[i].len;
|
||||
accum_value[0] += accum[i].value[0];
|
||||
@@ -3269,61 +3536,236 @@ static void calculate_average_color(SculptThreadedTaskData *data,
|
||||
accum_value[2] += accum[i].value[2];
|
||||
}
|
||||
if (accum_len != 0) {
|
||||
blend[0] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[0], accum_len)));
|
||||
blend[1] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[1], accum_len)));
|
||||
blend[2] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[2], accum_len)));
|
||||
blend[3] = 255;
|
||||
data->vpd->paintcol = *((uint *)blend);
|
||||
}
|
||||
blend.r = Traits::round(sqrtf(Traits::divide_round(accum_value[0], accum_len)));
|
||||
blend.g = Traits::round(sqrtf(Traits::divide_round(accum_value[1], accum_len)));
|
||||
blend.b = Traits::round(sqrtf(Traits::divide_round(accum_value[2], accum_len)));
|
||||
blend.a = Traits::range;
|
||||
|
||||
MEM_SAFE_FREE(data->custom_data); /* 'accum' */
|
||||
vpd->paintcol = blend;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static float paint_and_tex_color_alpha(VPaint *vp,
|
||||
VPaintData<Color, Traits, domain> *vpd,
|
||||
const float v_co[3],
|
||||
Color *r_color)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
|
||||
ColorPaint4f rgba;
|
||||
ColorPaint4f rgba_br = toFloat(*r_color);
|
||||
|
||||
paint_and_tex_color_alpha_intern(vp, &vpd->vc, v_co, &rgba.r);
|
||||
|
||||
rgb_uchar_to_float(&rgba_br.r, (const uchar *)&vpd->paintcol);
|
||||
mul_v3_v3(rgba_br, rgba);
|
||||
|
||||
*r_color = fromFloat<Color>(rgba_br);
|
||||
return rgba[3];
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void vpaint_do_draw(bContext *C,
|
||||
Sculpt *sd,
|
||||
VPaint *vp,
|
||||
VPaintData<Color, Traits, domain> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
PBVHNode **nodes,
|
||||
int totnode,
|
||||
Color *lcol)
|
||||
{
|
||||
using Value = typename Traits::ValueType;
|
||||
|
||||
SculptSession *ss = ob->sculpt;
|
||||
const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh);
|
||||
|
||||
const Brush *brush = ob->sculpt->cache->brush;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
||||
Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint);
|
||||
|
||||
blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) {
|
||||
for (int n : range) {
|
||||
const bool has_grids = (pbvh_type == PBVH_GRIDS);
|
||||
const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap;
|
||||
|
||||
const StrokeCache *cache = ss->cache;
|
||||
float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
|
||||
get_brush_alpha_data(
|
||||
scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
|
||||
const bool use_normal = vwpaint_use_normal(vp);
|
||||
const bool use_vert_sel = (me->editflag &
|
||||
(ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
|
||||
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
|
||||
SculptBrushTest test;
|
||||
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, &test, brush->falloff_shape);
|
||||
const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
|
||||
ss, brush->falloff_shape);
|
||||
|
||||
Color paintcol = vpd->paintcol;
|
||||
|
||||
/* For each vertex */
|
||||
PBVHVertexIter vd;
|
||||
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
|
||||
/* Test to see if the vertex coordinates are within the spherical brush region. */
|
||||
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
|
||||
/* NOTE: Grids are 1:1 with corners (aka loops).
|
||||
* For grid based pbvh, take the vert whose loop corresponds to the current grid.
|
||||
* Otherwise, take the current vert. */
|
||||
const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
|
||||
vd.vert_indices[vd.i];
|
||||
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
|
||||
const MVert *mv = &me->mvert[v_index];
|
||||
|
||||
/* If the vertex is selected for painting. */
|
||||
if (!use_vert_sel || mv->flag & SELECT) {
|
||||
/* Calc the dot prod. between ray norm on surf and current vert
|
||||
* (ie splash prevention factor), and only paint front facing verts. */
|
||||
float brush_strength = cache->bstrength;
|
||||
const float angle_cos = (use_normal && vd.no) ?
|
||||
dot_v3v3(sculpt_normal_frontface, vd.no) :
|
||||
1.0f;
|
||||
if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) &&
|
||||
((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 ||
|
||||
view_angle_limits_apply_falloff(
|
||||
&vpd->normal_angle_precalc, angle_cos, &brush_strength))) {
|
||||
const float brush_fade = BKE_brush_curve_strength(
|
||||
brush, sqrtf(test.dist), cache->radius);
|
||||
|
||||
Color color_final = paintcol;
|
||||
|
||||
/* If we're painting with a texture, sample the texture color and alpha. */
|
||||
float tex_alpha = 1.0;
|
||||
if (vpd->is_texbrush) {
|
||||
/* NOTE: we may want to paint alpha as vertex color alpha. */
|
||||
tex_alpha = paint_and_tex_color_alpha<Color, Traits, domain>(
|
||||
vp, vpd, vpd->vertexcosnos[v_index].co, &color_final);
|
||||
}
|
||||
|
||||
Color color_orig(0, 0, 0, 0);
|
||||
|
||||
if constexpr (domain == ATTR_DOMAIN_POINT) {
|
||||
int v_index = vd.index;
|
||||
|
||||
if (previous_color != NULL) {
|
||||
/* Get the previous loop color */
|
||||
if (isZero(previous_color[v_index])) {
|
||||
previous_color[v_index] = lcol[v_index];
|
||||
}
|
||||
color_orig = previous_color[v_index];
|
||||
}
|
||||
const float final_alpha = Traits::frange * brush_fade * brush_strength *
|
||||
tex_alpha * brush_alpha_pressure * grid_alpha;
|
||||
|
||||
lcol[v_index] = vpaint_blend<Color, Traits>(vp,
|
||||
lcol[v_index],
|
||||
color_orig,
|
||||
color_final,
|
||||
final_alpha,
|
||||
Traits::range * brush_strength);
|
||||
}
|
||||
else {
|
||||
/* For each poly owning this vert, paint each loop belonging to this vert. */
|
||||
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
|
||||
const int p_index = gmap->vert_to_poly[v_index].indices[j];
|
||||
const int l_index = gmap->vert_to_loop[v_index].indices[j];
|
||||
BLI_assert(me->mloop[l_index].v == v_index);
|
||||
const MPoly *mp = &me->mpoly[p_index];
|
||||
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
|
||||
Color color_orig = Color(0, 0, 0, 0); /* unused when array is NULL */
|
||||
|
||||
if (previous_color != NULL) {
|
||||
/* Get the previous loop color */
|
||||
if (isZero(previous_color[l_index])) {
|
||||
previous_color[l_index] = lcol[l_index];
|
||||
}
|
||||
color_orig = previous_color[l_index];
|
||||
}
|
||||
const float final_alpha = Traits::frange * brush_fade * brush_strength *
|
||||
tex_alpha * brush_alpha_pressure * grid_alpha;
|
||||
|
||||
/* Mix the new color with the original based on final_alpha. */
|
||||
lcol[l_index] = vpaint_blend<Color, Traits>(vp,
|
||||
lcol[l_index],
|
||||
color_orig,
|
||||
color_final,
|
||||
final_alpha,
|
||||
Traits::range * brush_strength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_pbvh_vertex_iter_end;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void vpaint_do_blur(bContext *C,
|
||||
Sculpt *sd,
|
||||
VPaint *vp,
|
||||
VPaintData<Color, Traits, domain> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
PBVHNode **nodes,
|
||||
int totnode,
|
||||
Color *lcol)
|
||||
{
|
||||
if constexpr (domain == ATTR_DOMAIN_POINT) {
|
||||
do_vpaint_brush_blur_verts<Color, Traits>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol);
|
||||
}
|
||||
else {
|
||||
do_vpaint_brush_blur_loops<Color, Traits>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void vpaint_paint_leaves(bContext *C,
|
||||
Sculpt *sd,
|
||||
VPaint *vp,
|
||||
struct VPaintData *vpd,
|
||||
VPaintData<Color, Traits, domain> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
Color *lcol,
|
||||
PBVHNode **nodes,
|
||||
int totnode)
|
||||
{
|
||||
|
||||
for (int i : IndexRange(totnode)) {
|
||||
SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COLOR);
|
||||
}
|
||||
|
||||
const Brush *brush = ob->sculpt->cache->brush;
|
||||
|
||||
SculptThreadedTaskData data = {
|
||||
.C = C,
|
||||
.sd = sd,
|
||||
.ob = ob,
|
||||
.brush = brush,
|
||||
.nodes = nodes,
|
||||
.vp = vp,
|
||||
.vpd = vpd,
|
||||
.lcol = (uint *)me->mloopcol,
|
||||
.me = me,
|
||||
};
|
||||
TaskParallelSettings settings;
|
||||
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
|
||||
switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) {
|
||||
case VPAINT_TOOL_AVERAGE:
|
||||
calculate_average_color(&data, nodes, totnode);
|
||||
BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings);
|
||||
calculate_average_color<Color, Traits, domain>(vpd, ob, me, brush, lcol, nodes, totnode);
|
||||
case VPAINT_TOOL_DRAW:
|
||||
vpaint_do_draw<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol);
|
||||
break;
|
||||
case VPAINT_TOOL_BLUR:
|
||||
BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_blur_task_cb_ex, &settings);
|
||||
vpaint_do_blur<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol);
|
||||
break;
|
||||
case VPAINT_TOOL_SMEAR:
|
||||
BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_smear_task_cb_ex, &settings);
|
||||
do_vpaint_brush_smear<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol);
|
||||
break;
|
||||
case VPAINT_TOOL_DRAW:
|
||||
BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void vpaint_do_paint(bContext *C,
|
||||
Sculpt *sd,
|
||||
VPaint *vp,
|
||||
struct VPaintData *vpd,
|
||||
VPaintData<Color, Traits, domain> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
Brush *brush,
|
||||
@@ -3339,18 +3781,22 @@ static void vpaint_do_paint(bContext *C,
|
||||
int totnode;
|
||||
PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode);
|
||||
|
||||
CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
|
||||
Color *color_data = static_cast<Color *>(layer->data);
|
||||
|
||||
/* Paint those leaves. */
|
||||
vpaint_paint_leaves(C, sd, vp, vpd, ob, me, nodes, totnode);
|
||||
vpaint_paint_leaves<Color, Traits, domain>(C, sd, vp, vpd, ob, me, color_data, nodes, totnode);
|
||||
|
||||
if (nodes) {
|
||||
MEM_freeN(nodes);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void vpaint_do_radial_symmetry(bContext *C,
|
||||
Sculpt *sd,
|
||||
VPaint *vp,
|
||||
struct VPaintData *vpd,
|
||||
VPaintData<Color, Traits, domain> *vpd,
|
||||
Object *ob,
|
||||
Mesh *me,
|
||||
Brush *brush,
|
||||
@@ -3359,17 +3805,18 @@ static void vpaint_do_radial_symmetry(bContext *C,
|
||||
{
|
||||
for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) {
|
||||
const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X'];
|
||||
vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle);
|
||||
vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle);
|
||||
}
|
||||
}
|
||||
|
||||
/* near duplicate of: sculpt.c's,
|
||||
* 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void vpaint_do_symmetrical_brush_actions(
|
||||
bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob)
|
||||
bContext *C, Sculpt *sd, VPaint *vp, VPaintData<Color, Traits, domain> *vpd, Object *ob)
|
||||
{
|
||||
Brush *brush = BKE_paint_brush(&vp->paint);
|
||||
Mesh *me = ob->data;
|
||||
Mesh *me = (Mesh *)ob->data;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
StrokeCache *cache = ss->cache;
|
||||
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
|
||||
@@ -3377,10 +3824,10 @@ static void vpaint_do_symmetrical_brush_actions(
|
||||
|
||||
/* initial stroke */
|
||||
cache->mirror_symmetry_pass = 0;
|
||||
vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0);
|
||||
vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X');
|
||||
vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y');
|
||||
vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z');
|
||||
vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0);
|
||||
vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'X');
|
||||
vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Y');
|
||||
vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Z');
|
||||
|
||||
cache->symmetry = symm;
|
||||
|
||||
@@ -3393,16 +3840,16 @@ static void vpaint_do_symmetrical_brush_actions(
|
||||
SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0);
|
||||
|
||||
if (i & (1 << 0)) {
|
||||
vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0);
|
||||
vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X');
|
||||
vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0);
|
||||
vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'X');
|
||||
}
|
||||
if (i & (1 << 1)) {
|
||||
vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0);
|
||||
vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y');
|
||||
vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0);
|
||||
vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Y');
|
||||
}
|
||||
if (i & (1 << 2)) {
|
||||
vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0);
|
||||
vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z');
|
||||
vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0);
|
||||
vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Z');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3411,14 +3858,15 @@ static void vpaint_do_symmetrical_brush_actions(
|
||||
cache->is_last_valid = true;
|
||||
}
|
||||
|
||||
static void vpaint_stroke_update_step(bContext *C,
|
||||
wmOperator *UNUSED(op),
|
||||
struct PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void vpaint_stroke_update_step_intern(bContext *C,
|
||||
struct PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
struct VPaintData *vpd = paint_stroke_mode_data(stroke);
|
||||
VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>(
|
||||
paint_stroke_mode_data(stroke));
|
||||
VPaint *vp = ts->vpaint;
|
||||
ViewContext *vc = &vpd->vc;
|
||||
Object *ob = vc->obact;
|
||||
@@ -3436,15 +3884,20 @@ static void vpaint_stroke_update_step(bContext *C,
|
||||
|
||||
swap_m4m4(vc->rv3d->persmat, mat);
|
||||
|
||||
vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob);
|
||||
vpaint_do_symmetrical_brush_actions<Color, Traits, domain>(C, sd, vp, vpd, ob);
|
||||
|
||||
swap_m4m4(vc->rv3d->persmat, mat);
|
||||
|
||||
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
|
||||
if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) {
|
||||
memcpy(
|
||||
vpd->smear.color_prev, vpd->smear.color_curr, sizeof(uint) * ((Mesh *)ob->data)->totloop);
|
||||
Mesh *me = BKE_object_get_original_mesh(ob);
|
||||
|
||||
size_t elem_size;
|
||||
int elem_num;
|
||||
|
||||
elem_num = get_vcol_elements(me, &elem_size);
|
||||
memcpy(vpd->smear.color_prev, vpd->smear.color_curr, elem_size * elem_num);
|
||||
}
|
||||
|
||||
/* Calculate pivot for rotation around selection if needed.
|
||||
@@ -3458,19 +3911,47 @@ static void vpaint_stroke_update_step(bContext *C,
|
||||
if (vpd->use_fast_update == false) {
|
||||
/* recalculate modifier stack to get new colors, slow,
|
||||
* avoid this if we can! */
|
||||
DEG_id_tag_update(ob->data, 0);
|
||||
DEG_id_tag_update((ID *)ob->data, 0);
|
||||
}
|
||||
else {
|
||||
/* Flush changes through DEG. */
|
||||
DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE);
|
||||
DEG_id_tag_update((ID *)ob->data, ID_RECALC_COPY_ON_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
static void vpaint_stroke_update_step(bContext *C,
|
||||
wmOperator *UNUSED(op),
|
||||
struct PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
struct VPaintData *vpd = paint_stroke_mode_data(stroke);
|
||||
ViewContext *vc = &vpd->vc;
|
||||
Object *ob = vc->obact;
|
||||
VPaintDataBase *vpd = static_cast<VPaintDataBase *>(paint_stroke_mode_data(stroke));
|
||||
|
||||
if (vpd->domain == ATTR_DOMAIN_POINT) {
|
||||
if (vpd->type == CD_PROP_COLOR) {
|
||||
vpaint_stroke_update_step_intern<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(
|
||||
C, stroke, itemptr);
|
||||
}
|
||||
else if (vpd->type == CD_PROP_BYTE_COLOR) {
|
||||
vpaint_stroke_update_step_intern<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(
|
||||
C, stroke, itemptr);
|
||||
}
|
||||
}
|
||||
else if (vpd->domain == ATTR_DOMAIN_CORNER) {
|
||||
if (vpd->type == CD_PROP_COLOR) {
|
||||
vpaint_stroke_update_step_intern<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(
|
||||
C, stroke, itemptr);
|
||||
}
|
||||
else if (vpd->type == CD_PROP_BYTE_COLOR) {
|
||||
vpaint_stroke_update_step_intern<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(
|
||||
C, stroke, itemptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static void vpaint_free_vpaintdata(Object *ob, void *_vpd)
|
||||
{
|
||||
VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>(_vpd);
|
||||
|
||||
if (vpd->is_texbrush) {
|
||||
ED_vpaint_proj_handle_free(vpd->vp_handle);
|
||||
@@ -3486,6 +3967,41 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
MEM_freeN(vpd->smear.color_curr);
|
||||
}
|
||||
|
||||
MEM_delete<VPaintData<Color, Traits, domain>>(vpd);
|
||||
}
|
||||
|
||||
static ViewContext *vpaint_get_viewcontext(Object *ob, void *vpd_ptr)
|
||||
{
|
||||
VPaintDataBase *vpd = static_cast<VPaintDataBase *>(vpd_ptr);
|
||||
|
||||
return &vpd->vc;
|
||||
}
|
||||
|
||||
static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
{
|
||||
void *vpd_ptr = paint_stroke_mode_data(stroke);
|
||||
VPaintDataBase *vpd = static_cast<VPaintDataBase *>(vpd_ptr);
|
||||
|
||||
ViewContext *vc = &vpd->vc;
|
||||
Object *ob = vc->obact;
|
||||
|
||||
if (vpd->domain == ATTR_DOMAIN_POINT) {
|
||||
if (vpd->type == CD_PROP_COLOR) {
|
||||
vpaint_free_vpaintdata<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(ob, vpd);
|
||||
}
|
||||
else if (vpd->type == CD_PROP_BYTE_COLOR) {
|
||||
vpaint_free_vpaintdata<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(ob, vpd);
|
||||
}
|
||||
}
|
||||
else if (vpd->domain == ATTR_DOMAIN_CORNER) {
|
||||
if (vpd->type == CD_PROP_COLOR) {
|
||||
vpaint_free_vpaintdata<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(ob, vpd);
|
||||
}
|
||||
else if (vpd->type == CD_PROP_BYTE_COLOR) {
|
||||
vpaint_free_vpaintdata<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(ob, vpd);
|
||||
}
|
||||
}
|
||||
|
||||
SculptSession *ss = ob->sculpt;
|
||||
|
||||
if (ss->cache->alt_smooth) {
|
||||
@@ -3496,7 +4012,7 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
|
||||
MEM_freeN(vpd);
|
||||
SCULPT_undo_push_end(ob);
|
||||
|
||||
SCULPT_cache_free(ob->sculpt->cache);
|
||||
ob->sculpt->cache = NULL;
|
||||
@@ -3515,8 +4031,16 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
vpaint_stroke_done,
|
||||
event->type);
|
||||
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
||||
if (SCULPT_has_loop_colors(ob) && ob->sculpt->pbvh) {
|
||||
BKE_pbvh_ensure_node_loops(ob->sculpt->pbvh);
|
||||
}
|
||||
|
||||
SCULPT_undo_push_begin(ob, "Vertex Paint");
|
||||
|
||||
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
||||
paint_stroke_free(C, op, op->customdata);
|
||||
paint_stroke_free(C, op, (PaintStroke *)op->customdata);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
@@ -3541,7 +4065,7 @@ static int vpaint_exec(bContext *C, wmOperator *op)
|
||||
0);
|
||||
|
||||
/* frees op->customdata */
|
||||
paint_stroke_exec(C, op, op->customdata);
|
||||
paint_stroke_exec(C, op, (PaintStroke *)op->customdata);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
@@ -3554,7 +4078,7 @@ static void vpaint_cancel(bContext *C, wmOperator *op)
|
||||
ob->sculpt->cache = NULL;
|
||||
}
|
||||
|
||||
paint_stroke_cancel(C, op, op->customdata);
|
||||
paint_stroke_cancel(C, op, (PaintStroke *)op->customdata);
|
||||
}
|
||||
|
||||
static int vpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
@@ -3582,4 +4106,112 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot)
|
||||
paint_stroke_operator_properties(ot);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Set Vertex Colors Operator
|
||||
* \{ */
|
||||
|
||||
template<typename Color, typename Traits, AttributeDomain domain>
|
||||
static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_layer)
|
||||
{
|
||||
Mesh *me;
|
||||
if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Color paintcol = fromFloat<Color>(paintcol_in);
|
||||
|
||||
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
||||
|
||||
const MPoly *mp = me->mpoly;
|
||||
for (int i = 0; i < me->totpoly; i++, mp++) {
|
||||
if (use_face_sel && !(mp->flag & ME_FACE_SEL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
do {
|
||||
uint vidx = me->mloop[mp->loopstart + j].v;
|
||||
|
||||
if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) {
|
||||
if constexpr (domain == ATTR_DOMAIN_CORNER) {
|
||||
color_layer[mp->loopstart + j] = paintcol;
|
||||
}
|
||||
else {
|
||||
color_layer[vidx] = paintcol;
|
||||
}
|
||||
}
|
||||
j++;
|
||||
} while (j < mp->totloop);
|
||||
}
|
||||
|
||||
/* remove stale me->mcol, will be added later */
|
||||
BKE_mesh_tessface_clear(me);
|
||||
|
||||
DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE);
|
||||
|
||||
/* NOTE: Original mesh is used for display, so tag it directly here. */
|
||||
BKE_mesh_batch_cache_dirty_tag(me, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *obact = CTX_data_active_object(C);
|
||||
Mesh *me = BKE_object_get_original_mesh(obact);
|
||||
|
||||
// uint paintcol = vpaint_get_current_color(scene, scene->toolsettings->vpaint, false);
|
||||
ColorPaint4f paintcol = vpaint_get_current_col<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(
|
||||
scene, scene->toolsettings->vpaint, false);
|
||||
|
||||
bool ok = false;
|
||||
|
||||
CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id);
|
||||
AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer);
|
||||
|
||||
if (domain == ATTR_DOMAIN_POINT) {
|
||||
if (layer->type == CD_PROP_COLOR) {
|
||||
ok = vertex_color_set<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(
|
||||
obact, paintcol, static_cast<ColorPaint4f *>(layer->data));
|
||||
}
|
||||
else if (layer->type == CD_PROP_BYTE_COLOR) {
|
||||
ok = vertex_color_set<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(
|
||||
obact, paintcol, static_cast<ColorPaint4b *>(layer->data));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (layer->type == CD_PROP_COLOR) {
|
||||
ok = vertex_color_set<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(
|
||||
obact, paintcol, static_cast<ColorPaint4f *>(layer->data));
|
||||
}
|
||||
else if (layer->type == CD_PROP_BYTE_COLOR) {
|
||||
ok = vertex_color_set<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(
|
||||
obact, paintcol, static_cast<ColorPaint4b *>(layer->data));
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void PAINT_OT_vertex_color_set(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Set Vertex Colors";
|
||||
ot->idname = "PAINT_OT_vertex_color_set";
|
||||
ot->description = "Fill the active vertex color layer with the current paint color";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = vertex_color_set_exec;
|
||||
ot->poll = vertex_paint_mode_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -47,75 +47,6 @@ static void tag_object_after_update(Object *object)
|
||||
BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Set Vertex Colors Operator
|
||||
* \{ */
|
||||
|
||||
static bool vertex_color_set(Object *ob, uint paintcol)
|
||||
{
|
||||
Mesh *me;
|
||||
if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
|
||||
|
||||
const MPoly *mp = me->mpoly;
|
||||
for (int i = 0; i < me->totpoly; i++, mp++) {
|
||||
MLoopCol *lcol = me->mloopcol + mp->loopstart;
|
||||
|
||||
if (use_face_sel && !(mp->flag & ME_FACE_SEL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
do {
|
||||
uint vidx = me->mloop[mp->loopstart + j].v;
|
||||
if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) {
|
||||
*(int *)lcol = paintcol;
|
||||
}
|
||||
lcol++;
|
||||
j++;
|
||||
} while (j < mp->totloop);
|
||||
}
|
||||
|
||||
/* remove stale me->mcol, will be added later */
|
||||
BKE_mesh_tessface_clear(me);
|
||||
|
||||
tag_object_after_update(ob);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *obact = CTX_data_active_object(C);
|
||||
uint paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint, false);
|
||||
|
||||
if (vertex_color_set(obact, paintcol)) {
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void PAINT_OT_vertex_color_set(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Set Vertex Colors";
|
||||
ot->idname = "PAINT_OT_vertex_color_set";
|
||||
ot->description = "Fill the active vertex color layer with the current paint color";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = vertex_color_set_exec;
|
||||
ot->poll = vertex_paint_mode_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -73,570 +73,3 @@ bool ED_vpaint_color_transform(struct Object *ob,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Color Blending Modes
|
||||
* \{ */
|
||||
|
||||
BLI_INLINE uint mcol_blend(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
if (fac >= 255) {
|
||||
return col_dst;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
/* Updated to use the rgb squared color model which blends nicer. */
|
||||
int r1 = cp_src[0] * cp_src[0];
|
||||
int g1 = cp_src[1] * cp_src[1];
|
||||
int b1 = cp_src[2] * cp_src[2];
|
||||
int a1 = cp_src[3] * cp_src[3];
|
||||
|
||||
int r2 = cp_dst[0] * cp_dst[0];
|
||||
int g2 = cp_dst[1] * cp_dst[1];
|
||||
int b2 = cp_dst[2] * cp_dst[2];
|
||||
int a2 = cp_dst[3] * cp_dst[3];
|
||||
|
||||
cp_mix[0] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * r1 + fac * r2), 255)));
|
||||
cp_mix[1] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * g1 + fac * g2), 255)));
|
||||
cp_mix[2] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * b1 + fac * b2), 255)));
|
||||
cp_mix[3] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * a1 + fac * a2), 255)));
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_add(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
temp = cp_src[0] + divide_round_i((fac * cp_dst[0]), 255);
|
||||
cp_mix[0] = (temp > 254) ? 255 : temp;
|
||||
temp = cp_src[1] + divide_round_i((fac * cp_dst[1]), 255);
|
||||
cp_mix[1] = (temp > 254) ? 255 : temp;
|
||||
temp = cp_src[2] + divide_round_i((fac * cp_dst[2]), 255);
|
||||
cp_mix[2] = (temp > 254) ? 255 : temp;
|
||||
temp = cp_src[3] + divide_round_i((fac * cp_dst[3]), 255);
|
||||
cp_mix[3] = (temp > 254) ? 255 : temp;
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_sub(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
temp = cp_src[0] - divide_round_i((fac * cp_dst[0]), 255);
|
||||
cp_mix[0] = (temp < 0) ? 0 : temp;
|
||||
temp = cp_src[1] - divide_round_i((fac * cp_dst[1]), 255);
|
||||
cp_mix[1] = (temp < 0) ? 0 : temp;
|
||||
temp = cp_src[2] - divide_round_i((fac * cp_dst[2]), 255);
|
||||
cp_mix[2] = (temp < 0) ? 0 : temp;
|
||||
temp = cp_src[3] - divide_round_i((fac * cp_dst[3]), 255);
|
||||
cp_mix[3] = (temp < 0) ? 0 : temp;
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_mul(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
/* first mul, then blend the fac */
|
||||
cp_mix[0] = divide_round_i(mfac * cp_src[0] * 255 + fac * cp_dst[0] * cp_src[0], 255 * 255);
|
||||
cp_mix[1] = divide_round_i(mfac * cp_src[1] * 255 + fac * cp_dst[1] * cp_src[1], 255 * 255);
|
||||
cp_mix[2] = divide_round_i(mfac * cp_src[2] * 255 + fac * cp_dst[2] * cp_src[2], 255 * 255);
|
||||
cp_mix[3] = divide_round_i(mfac * cp_src[3] * 255 + fac * cp_dst[3] * cp_src[3], 255 * 255);
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_lighten(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
if (fac >= 255) {
|
||||
return col_dst;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
/* See if we're lighter, if so mix, else don't do anything.
|
||||
* if the paint color is darker then the original, then ignore */
|
||||
if (IMB_colormanagement_get_luminance_byte(cp_src) >
|
||||
IMB_colormanagement_get_luminance_byte(cp_dst)) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_mix[0] = divide_round_i(mfac * cp_src[0] + fac * cp_dst[0], 255);
|
||||
cp_mix[1] = divide_round_i(mfac * cp_src[1] + fac * cp_dst[1], 255);
|
||||
cp_mix[2] = divide_round_i(mfac * cp_src[2] + fac * cp_dst[2], 255);
|
||||
cp_mix[3] = divide_round_i(mfac * cp_src[3] + fac * cp_dst[3], 255);
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_darken(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
if (fac >= 255) {
|
||||
return col_dst;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
/* See if we're darker, if so mix, else don't do anything.
|
||||
* if the paint color is brighter then the original, then ignore */
|
||||
if (IMB_colormanagement_get_luminance_byte(cp_src) <
|
||||
IMB_colormanagement_get_luminance_byte(cp_dst)) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_mix[0] = divide_round_i((mfac * cp_src[0] + fac * cp_dst[0]), 255);
|
||||
cp_mix[1] = divide_round_i((mfac * cp_src[1] + fac * cp_dst[1]), 255);
|
||||
cp_mix[2] = divide_round_i((mfac * cp_src[2] + fac * cp_dst[2]), 255);
|
||||
cp_mix[3] = divide_round_i((mfac * cp_src[3] + fac * cp_dst[3]), 255);
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_colordodge(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac, temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
temp = (cp_dst[0] == 255) ? 255 : min_ii((cp_src[0] * 225) / (255 - cp_dst[0]), 255);
|
||||
cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255;
|
||||
temp = (cp_dst[1] == 255) ? 255 : min_ii((cp_src[1] * 225) / (255 - cp_dst[1]), 255);
|
||||
cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255;
|
||||
temp = (cp_dst[2] == 255) ? 255 : min_ii((cp_src[2] * 225) / (255 - cp_dst[2]), 255);
|
||||
cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255;
|
||||
temp = (cp_dst[3] == 255) ? 255 : min_ii((cp_src[3] * 225) / (255 - cp_dst[3]), 255);
|
||||
cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_difference(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac, temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
temp = abs(cp_src[0] - cp_dst[0]);
|
||||
cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255;
|
||||
temp = abs(cp_src[1] - cp_dst[1]);
|
||||
cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255;
|
||||
temp = abs(cp_src[2] - cp_dst[2]);
|
||||
cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255;
|
||||
temp = abs(cp_src[3] - cp_dst[3]);
|
||||
cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_screen(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac, temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
temp = max_ii(255 - (((255 - cp_src[0]) * (255 - cp_dst[0])) / 255), 0);
|
||||
cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255;
|
||||
temp = max_ii(255 - (((255 - cp_src[1]) * (255 - cp_dst[1])) / 255), 0);
|
||||
cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255;
|
||||
temp = max_ii(255 - (((255 - cp_src[2]) * (255 - cp_dst[2])) / 255), 0);
|
||||
cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255;
|
||||
temp = max_ii(255 - (((255 - cp_src[3]) * (255 - cp_dst[3])) / 255), 0);
|
||||
cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_hardlight(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac, temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (cp_dst[i] > 127) {
|
||||
temp = 255 - ((255 - 2 * (cp_dst[i] - 127)) * (255 - cp_src[i]) / 255);
|
||||
}
|
||||
else {
|
||||
temp = (2 * cp_dst[i] * cp_src[i]) >> 8;
|
||||
}
|
||||
cp_mix[i] = min_ii((mfac * cp_src[i] + temp * fac) / 255, 255);
|
||||
}
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_overlay(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac, temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (cp_src[i] > 127) {
|
||||
temp = 255 - ((255 - 2 * (cp_src[i] - 127)) * (255 - cp_dst[i]) / 255);
|
||||
}
|
||||
else {
|
||||
temp = (2 * cp_dst[i] * cp_src[i]) >> 8;
|
||||
}
|
||||
cp_mix[i] = min_ii((mfac * cp_src[i] + temp * fac) / 255, 255);
|
||||
}
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_softlight(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac, temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (cp_src[i] < 127) {
|
||||
temp = ((2 * ((cp_dst[i] / 2) + 64)) * cp_src[i]) / 255;
|
||||
}
|
||||
else {
|
||||
temp = 255 - (2 * (255 - ((cp_dst[i] / 2) + 64)) * (255 - cp_src[i]) / 255);
|
||||
}
|
||||
cp_mix[i] = (temp * fac + cp_src[i] * mfac) / 255;
|
||||
}
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_exclusion(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac, temp;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
temp = 127 - ((2 * (cp_src[i] - 127) * (cp_dst[i] - 127)) / 255);
|
||||
cp_mix[i] = (temp * fac + cp_src[i] * mfac) / 255;
|
||||
}
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_luminosity(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
float h1, s1, v1;
|
||||
float h2, s2, v2;
|
||||
float r, g, b;
|
||||
rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1);
|
||||
rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2);
|
||||
|
||||
v1 = v2;
|
||||
|
||||
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
|
||||
|
||||
cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255;
|
||||
cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255;
|
||||
cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255;
|
||||
cp_mix[3] = ((int)(cp_dst[3]) * fac + mfac * cp_src[3]) / 255;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_saturation(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
float h1, s1, v1;
|
||||
float h2, s2, v2;
|
||||
float r, g, b;
|
||||
rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1);
|
||||
rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2);
|
||||
|
||||
if (s1 > EPS_SATURATION) {
|
||||
s1 = s2;
|
||||
}
|
||||
|
||||
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
|
||||
|
||||
cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255;
|
||||
cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255;
|
||||
cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_hue(uint col_src, uint col_dst, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_dst, *cp_mix;
|
||||
int mfac;
|
||||
uint col_mix = 0;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
mfac = 255 - fac;
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_dst = (uchar *)&col_dst;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
float h1, s1, v1;
|
||||
float h2, s2, v2;
|
||||
float r, g, b;
|
||||
rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1);
|
||||
rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2);
|
||||
|
||||
h1 = h2;
|
||||
|
||||
hsv_to_rgb(h1, s1, v1, &r, &g, &b);
|
||||
|
||||
cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255;
|
||||
cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255;
|
||||
cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255;
|
||||
cp_mix[3] = ((int)(cp_dst[3]) * fac + mfac * cp_src[3]) / 255;
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_alpha_add(uint col_src, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_mix;
|
||||
int temp;
|
||||
uint col_mix = col_src;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
temp = cp_src[3] + fac;
|
||||
cp_mix[3] = (temp > 254) ? 255 : temp;
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
BLI_INLINE uint mcol_alpha_sub(uint col_src, int fac)
|
||||
{
|
||||
uchar *cp_src, *cp_mix;
|
||||
int temp;
|
||||
uint col_mix = col_src;
|
||||
|
||||
if (fac == 0) {
|
||||
return col_src;
|
||||
}
|
||||
|
||||
cp_src = (uchar *)&col_src;
|
||||
cp_mix = (uchar *)&col_mix;
|
||||
|
||||
temp = cp_src[3] - fac;
|
||||
cp_mix[3] = temp < 0 ? 0 : temp;
|
||||
|
||||
return col_mix;
|
||||
}
|
||||
|
||||
uint ED_vpaint_blend_tool(const int tool, const uint col, const uint paintcol, const int alpha_i)
|
||||
{
|
||||
switch ((IMB_BlendMode)tool) {
|
||||
case IMB_BLEND_MIX:
|
||||
return mcol_blend(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_ADD:
|
||||
return mcol_add(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_SUB:
|
||||
return mcol_sub(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_MUL:
|
||||
return mcol_mul(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_LIGHTEN:
|
||||
return mcol_lighten(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_DARKEN:
|
||||
return mcol_darken(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_COLORDODGE:
|
||||
return mcol_colordodge(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_DIFFERENCE:
|
||||
return mcol_difference(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_SCREEN:
|
||||
return mcol_screen(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_HARDLIGHT:
|
||||
return mcol_hardlight(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_OVERLAY:
|
||||
return mcol_overlay(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_SOFTLIGHT:
|
||||
return mcol_softlight(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_EXCLUSION:
|
||||
return mcol_exclusion(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_LUMINOSITY:
|
||||
return mcol_luminosity(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_SATURATION:
|
||||
return mcol_saturation(col, paintcol, alpha_i);
|
||||
case IMB_BLEND_HUE:
|
||||
return mcol_hue(col, paintcol, alpha_i);
|
||||
/* non-color */
|
||||
case IMB_BLEND_ERASE_ALPHA:
|
||||
return mcol_alpha_sub(col, alpha_i);
|
||||
case IMB_BLEND_ADD_ALPHA:
|
||||
return mcol_alpha_add(col, alpha_i);
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -4025,6 +4025,7 @@ void SCULPT_cache_free(StrokeCache *cache)
|
||||
MEM_SAFE_FREE(cache->detail_directions);
|
||||
MEM_SAFE_FREE(cache->prev_displacement);
|
||||
MEM_SAFE_FREE(cache->limit_surface_co);
|
||||
MEM_SAFE_FREE(cache->prev_colors_vpaint);
|
||||
|
||||
if (cache->pose_ik_chain) {
|
||||
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
|
||||
|
||||
@@ -219,7 +219,6 @@ typedef struct SculptThreadedTaskData {
|
||||
int totnode;
|
||||
|
||||
struct VPaint *vp;
|
||||
struct VPaintData *vpd;
|
||||
struct WPaintData *wpd;
|
||||
struct WeightPaintInfo *wpi;
|
||||
unsigned int *lcol;
|
||||
@@ -493,6 +492,7 @@ typedef struct StrokeCache {
|
||||
float mouse_event[2];
|
||||
|
||||
float (*prev_colors)[4];
|
||||
void *prev_colors_vpaint;
|
||||
|
||||
/* Multires Displacement Smear. */
|
||||
float (*prev_displacement)[3];
|
||||
|
||||
Submodule source/tools updated: 4c1e01e3e3...1e658ca996
Reference in New Issue
Block a user