Merge branch 'master' into xr-dev
This commit is contained in:
commit
07b0b6e9b7
|
@ -1974,7 +1974,7 @@ if(FIRST_RUN)
|
|||
|
||||
set(_msg " - ${_setting}")
|
||||
string(LENGTH "${_msg}" _len)
|
||||
while("32" GREATER "${_len}")
|
||||
while("36" GREATER "${_len}")
|
||||
string(APPEND _msg " ")
|
||||
math(EXPR _len "${_len} + 1")
|
||||
endwhile()
|
||||
|
|
|
@ -106,8 +106,8 @@ if(WIN32)
|
|||
set(CPACK_WIX_LIGHT_EXTRA_FLAGS -dcl:medium)
|
||||
endif()
|
||||
|
||||
set(CPACK_PACKAGE_EXECUTABLES "blender-launcher" "blender")
|
||||
set(CPACK_CREATE_DESKTOP_LINKS "blender-launcher" "blender")
|
||||
set(CPACK_PACKAGE_EXECUTABLES "blender-launcher" "Blender")
|
||||
set(CPACK_CREATE_DESKTOP_LINKS "blender-launcher" "Blender")
|
||||
|
||||
include(CPack)
|
||||
|
||||
|
|
|
@ -345,10 +345,12 @@ endif()
|
|||
|
||||
windows_find_package(WebP)
|
||||
if(NOT WEBP_FOUND)
|
||||
set(WEBP_INCLUDE_DIRS ${LIBDIR}/webp/include)
|
||||
set(WEBP_ROOT_DIR ${LIBDIR}/webp)
|
||||
set(WEBP_LIBRARIES ${LIBDIR}/webp/lib/webp.lib ${LIBDIR}/webp/lib/webpdemux.lib ${LIBDIR}/webp/lib/webpmux.lib)
|
||||
set(WEBP_FOUND ON)
|
||||
if(EXISTS ${LIBDIR}/webp)
|
||||
set(WEBP_INCLUDE_DIRS ${LIBDIR}/webp/include)
|
||||
set(WEBP_ROOT_DIR ${LIBDIR}/webp)
|
||||
set(WEBP_LIBRARIES ${LIBDIR}/webp/lib/webp.lib ${LIBDIR}/webp/lib/webpdemux.lib ${LIBDIR}/webp/lib/webpmux.lib)
|
||||
set(WEBP_FOUND ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_OPENCOLLADA)
|
||||
|
|
|
@ -146,6 +146,8 @@ class CurvesGeometry : public ::CurvesGeometry {
|
|||
MutableSpan<int8_t> curve_types();
|
||||
|
||||
bool has_curve_with_type(const CurveType type) const;
|
||||
/** Return the number of curves with each type. */
|
||||
std::array<int, CURVE_TYPES_NUM> count_curve_types() const;
|
||||
|
||||
MutableSpan<float3> positions();
|
||||
Span<float3> positions() const;
|
||||
|
@ -264,6 +266,15 @@ class CurvesGeometry : public ::CurvesGeometry {
|
|||
|
||||
Span<float3> evaluated_positions() const;
|
||||
|
||||
/**
|
||||
* Evaluate a generic data to the standard evaluated points of a specific curve,
|
||||
* defined by the resolution attribute or other factors, depending on the curve type.
|
||||
*
|
||||
* \warning This function expects offsets to the evaluated points for each curve to be
|
||||
* calculated. That can be ensured with #ensure_evaluated_offsets.
|
||||
*/
|
||||
void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Make sure the basis weights for NURBS curve's evaluated points are calculated.
|
||||
|
@ -379,6 +390,13 @@ void calculate_evaluated_positions(Span<float3> positions,
|
|||
Span<int> evaluated_offsets,
|
||||
MutableSpan<float3> evaluated_positions);
|
||||
|
||||
/**
|
||||
* Evaluate generic data to the evaluated points, with counts for each segment described by
|
||||
* #evaluated_offsets. Unlike other curve types, for Bezier curves generic data and positions
|
||||
* are treated separately, since attribute values aren't stored for the handle control points.
|
||||
*/
|
||||
void interpolate_to_evaluated(GSpan src, Span<int> evaluated_offsets, GMutableSpan dst);
|
||||
|
||||
} // namespace bezier
|
||||
|
||||
namespace catmull_rom {
|
||||
|
|
|
@ -220,30 +220,78 @@ bool BKE_gpencil_stroke_sample(struct bGPdata *gpd,
|
|||
* \param gps: Stroke to smooth
|
||||
* \param i: Point index
|
||||
* \param inf: Amount of smoothing to apply
|
||||
* \param iterations: Radius of points to consider, equivalent to iterations
|
||||
* \param smooth_caps: Apply smooth to stroke extremes
|
||||
* \param keep_shape: Smooth out fine details first
|
||||
* \param r_gps: Stroke to put the result into
|
||||
*/
|
||||
bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps, int i, float inf, bool smooth_caps);
|
||||
bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps,
|
||||
int point_index,
|
||||
float influence,
|
||||
int iterations,
|
||||
bool smooth_caps,
|
||||
bool keep_shape,
|
||||
struct bGPDstroke *r_gps);
|
||||
/**
|
||||
* Apply smooth strength to stroke point.
|
||||
* \param gps: Stroke to smooth
|
||||
* \param point_index: Point index
|
||||
* \param influence: Amount of smoothing to apply
|
||||
* \param iterations: Radius of points to consider, equivalent to iterations
|
||||
* \param r_gps: Stroke to put the result into
|
||||
*/
|
||||
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence);
|
||||
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps,
|
||||
int point_index,
|
||||
float influence,
|
||||
int iterations,
|
||||
struct bGPDstroke *r_gps);
|
||||
/**
|
||||
* Apply smooth for thickness to stroke point (use pressure).
|
||||
* \param gps: Stroke to smooth
|
||||
* \param point_index: Point index
|
||||
* \param influence: Amount of smoothing to apply
|
||||
* \param iterations: Radius of points to consider, equivalent to iterations
|
||||
* \param r_gps: Stroke to put the result into
|
||||
*/
|
||||
bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence);
|
||||
bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps,
|
||||
int point_index,
|
||||
float influence,
|
||||
int iterations,
|
||||
struct bGPDstroke *r_gps);
|
||||
/**
|
||||
* Apply smooth for UV rotation to stroke point (use pressure).
|
||||
* Apply smooth for UV rotation/factor to stroke point.
|
||||
* \param gps: Stroke to smooth
|
||||
* \param point_index: Point index
|
||||
* \param influence: Amount of smoothing to apply
|
||||
* \param iterations: Radius of points to consider, equivalent to iterations
|
||||
* \param r_gps: Stroke to put the result into
|
||||
*/
|
||||
bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence);
|
||||
bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps,
|
||||
int point_index,
|
||||
float influence,
|
||||
int iterations,
|
||||
struct bGPDstroke *r_gps);
|
||||
/**
|
||||
* Apply smooth operation to the stroke.
|
||||
* \param gps: Stroke to smooth
|
||||
* \param influence: The interpolation factor for the smooth and the original stroke
|
||||
* \param iterations: Radius of points to consider, equivalent to iterations
|
||||
* \param smooth_position: Smooth point locations
|
||||
* \param smooth_strength: Smooth point strength
|
||||
* \param smooth_thickness: Smooth point thickness
|
||||
* \param smooth_uv: Smooth uv rotation/factor
|
||||
* \param keep_shape: Use different distribution for smooth locations to keep the shape
|
||||
* \param weights: per point weights to multiply influence with (optional, can be null)
|
||||
*/
|
||||
void BKE_gpencil_stroke_smooth(struct bGPDstroke *gps,
|
||||
const float influence,
|
||||
const int iterations,
|
||||
const bool smooth_position,
|
||||
const bool smooth_strength,
|
||||
const bool smooth_thickness,
|
||||
const bool smooth_uv,
|
||||
const bool keep_shape,
|
||||
const float *weights);
|
||||
/**
|
||||
* Close grease pencil stroke.
|
||||
* \param gps: Stroke to close
|
||||
|
|
|
@ -432,7 +432,8 @@ void BKE_collection_add_from_object(Main *bmain,
|
|||
bool is_instantiated = false;
|
||||
|
||||
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
|
||||
if (!ID_IS_LINKED(collection) && BKE_collection_has_object(collection, ob_src)) {
|
||||
if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDABLE_LIBRARY(collection) &&
|
||||
BKE_collection_has_object(collection, ob_src)) {
|
||||
collection_child_add(collection, collection_dst, 0, true);
|
||||
is_instantiated = true;
|
||||
}
|
||||
|
@ -454,7 +455,8 @@ void BKE_collection_add_from_collection(Main *bmain,
|
|||
bool is_instantiated = false;
|
||||
|
||||
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
|
||||
if (!ID_IS_LINKED(collection) && collection_find_child(collection, collection_src)) {
|
||||
if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDABLE_LIBRARY(collection) &&
|
||||
collection_find_child(collection, collection_src)) {
|
||||
collection_child_add(collection, collection_dst, 0, true);
|
||||
is_instantiated = true;
|
||||
}
|
||||
|
|
|
@ -134,6 +134,50 @@ void calculate_evaluated_positions(const Span<float3> positions,
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline void linear_interpolation(const T &a, const T &b, MutableSpan<T> dst)
|
||||
{
|
||||
dst.first() = a;
|
||||
const float step = 1.0f / dst.size();
|
||||
for (const int i : dst.index_range().drop_front(1)) {
|
||||
dst[i] = attribute_math::mix2(i * step, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void interpolate_to_evaluated(const Span<T> src,
|
||||
const Span<int> evaluated_offsets,
|
||||
MutableSpan<T> dst)
|
||||
{
|
||||
BLI_assert(!src.is_empty());
|
||||
BLI_assert(dst.size() == src.size());
|
||||
BLI_assert(evaluated_offsets.last() == dst.size());
|
||||
|
||||
linear_interpolation(src.first(), src[1], dst.take_front(evaluated_offsets.first()));
|
||||
|
||||
threading::parallel_for(
|
||||
src.index_range().drop_back(1).drop_front(1), 512, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
const IndexRange segment_points = offsets_to_range(evaluated_offsets, i - 1);
|
||||
linear_interpolation(src[i], src[i + 1], dst.slice(segment_points));
|
||||
}
|
||||
});
|
||||
|
||||
const IndexRange last_segment_points(evaluated_offsets.last(1),
|
||||
evaluated_offsets.last() - evaluated_offsets.last(1));
|
||||
linear_interpolation(src.last(), src.first(), dst.slice(last_segment_points));
|
||||
}
|
||||
|
||||
void interpolate_to_evaluated(const GSpan src, const Span<int> evaluated_offsets, GMutableSpan dst)
|
||||
{
|
||||
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
||||
interpolate_to_evaluated(src.typed<T>(), evaluated_offsets, dst.typed<T>());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::bke::curves::bezier
|
||||
|
|
|
@ -277,6 +277,40 @@ bool CurvesGeometry::has_curve_with_type(const CurveType type) const
|
|||
return false;
|
||||
}
|
||||
|
||||
std::array<int, CURVE_TYPES_NUM> CurvesGeometry::count_curve_types() const
|
||||
{
|
||||
using CountsType = std::array<int, CURVE_TYPES_NUM>;
|
||||
|
||||
CountsType identity;
|
||||
identity.fill(0);
|
||||
|
||||
const VArray<int8_t> types = this->curve_types();
|
||||
if (types.is_single()) {
|
||||
identity[types.get_internal_single()] = this->curves_num();
|
||||
return identity;
|
||||
}
|
||||
|
||||
Span<int8_t> types_span = types.get_internal_span();
|
||||
return threading::parallel_reduce(
|
||||
this->curves_range(),
|
||||
2048,
|
||||
identity,
|
||||
[&](const IndexRange curves_range, const CountsType &init) {
|
||||
CountsType result = init;
|
||||
for (const int curve_index : curves_range) {
|
||||
result[types_span[curve_index]]++;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[](const CountsType &a, const CountsType &b) {
|
||||
CountsType result = a;
|
||||
for (const int i : IndexRange(CURVE_TYPES_NUM)) {
|
||||
result[i] += b[i];
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
MutableSpan<float3> CurvesGeometry::positions()
|
||||
{
|
||||
this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named(
|
||||
|
@ -655,6 +689,38 @@ Span<float3> CurvesGeometry::evaluated_positions() const
|
|||
return this->runtime->evaluated_position_cache;
|
||||
}
|
||||
|
||||
void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
|
||||
const GSpan src,
|
||||
GMutableSpan dst) const
|
||||
{
|
||||
BLI_assert(!this->runtime->offsets_cache_dirty);
|
||||
BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
|
||||
const IndexRange points = this->points_for_curve(curve_index);
|
||||
BLI_assert(src.size() == points.size());
|
||||
BLI_assert(dst.size() == this->evaluated_points_for_curve(curve_index).size());
|
||||
switch (this->curve_types()[curve_index]) {
|
||||
case CURVE_TYPE_CATMULL_ROM:
|
||||
curves::catmull_rom::interpolate_to_evaluated(
|
||||
src, this->cyclic()[curve_index], this->resolution()[curve_index], dst);
|
||||
return;
|
||||
case CURVE_TYPE_POLY:
|
||||
dst.type().copy_assign_n(src.data(), dst.data(), src.size());
|
||||
return;
|
||||
case CURVE_TYPE_BEZIER:
|
||||
curves::bezier::interpolate_to_evaluated(
|
||||
src, this->runtime->bezier_evaluated_offsets.as_span().slice(points), dst);
|
||||
return;
|
||||
case CURVE_TYPE_NURBS:
|
||||
curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
|
||||
this->nurbs_orders()[curve_index],
|
||||
this->nurbs_weights().slice(points),
|
||||
src,
|
||||
dst);
|
||||
return;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -63,6 +63,28 @@ TEST(curves_geometry, Move)
|
|||
EXPECT_EQ(second_other.offsets().data(), offsets_data);
|
||||
}
|
||||
|
||||
TEST(curves_geometry, TypeCount)
|
||||
{
|
||||
CurvesGeometry curves = create_basic_curves(100, 10);
|
||||
curves.curve_types().copy_from({
|
||||
CURVE_TYPE_BEZIER,
|
||||
CURVE_TYPE_NURBS,
|
||||
CURVE_TYPE_NURBS,
|
||||
CURVE_TYPE_NURBS,
|
||||
CURVE_TYPE_CATMULL_ROM,
|
||||
CURVE_TYPE_CATMULL_ROM,
|
||||
CURVE_TYPE_CATMULL_ROM,
|
||||
CURVE_TYPE_POLY,
|
||||
CURVE_TYPE_POLY,
|
||||
CURVE_TYPE_POLY,
|
||||
});
|
||||
std::array<int, CURVE_TYPES_NUM> counts = curves.count_curve_types();
|
||||
EXPECT_EQ(counts[CURVE_TYPE_CATMULL_ROM], 3);
|
||||
EXPECT_EQ(counts[CURVE_TYPE_POLY], 3);
|
||||
EXPECT_EQ(counts[CURVE_TYPE_BEZIER], 1);
|
||||
EXPECT_EQ(counts[CURVE_TYPE_NURBS], 3);
|
||||
}
|
||||
|
||||
TEST(curves_geometry, CatmullRomEvaluation)
|
||||
{
|
||||
CurvesGeometry curves(4, 1);
|
||||
|
@ -383,4 +405,77 @@ TEST(curves_geometry, NURBSEvaluation)
|
|||
}
|
||||
}
|
||||
|
||||
TEST(curves_geometry, BezierGenericEvaluation)
|
||||
{
|
||||
CurvesGeometry curves(3, 1);
|
||||
curves.curve_types().fill(CURVE_TYPE_BEZIER);
|
||||
curves.resolution().fill(8);
|
||||
curves.offsets().last() = 3;
|
||||
|
||||
MutableSpan<float3> handles_left = curves.handle_positions_left();
|
||||
MutableSpan<float3> handles_right = curves.handle_positions_right();
|
||||
MutableSpan<float3> positions = curves.positions();
|
||||
positions.first() = {-1, 0, 0};
|
||||
handles_right.first() = {-1, 1, 0};
|
||||
handles_left[1] = {0, 0, 0};
|
||||
positions[1] = {1, 0, 0};
|
||||
handles_right[1] = {2, 0, 0};
|
||||
handles_left.last() = {1, 1, 0};
|
||||
positions.last() = {2, 1, 0};
|
||||
|
||||
/* Dangling handles shouldn't be used in a non-cyclic curve. */
|
||||
handles_left.first() = {100, 100, 100};
|
||||
handles_right.last() = {100, 100, 100};
|
||||
|
||||
Span<float3> evaluated_positions = curves.evaluated_positions();
|
||||
static const Array<float3> result_1{{
|
||||
{-1.0f, 0.0f, 0.0f},
|
||||
{-0.955078f, 0.287109f, 0.0f},
|
||||
{-0.828125f, 0.421875f, 0.0f},
|
||||
{-0.630859f, 0.439453f, 0.0f},
|
||||
{-0.375f, 0.375f, 0.0f},
|
||||
{-0.0722656f, 0.263672f, 0.0f},
|
||||
{0.265625f, 0.140625f, 0.0f},
|
||||
{0.626953f, 0.0410156f, 0.0f},
|
||||
{1.0f, 0.0f, 0.0f},
|
||||
{1.28906f, 0.0429688f, 0.0f},
|
||||
{1.4375f, 0.15625f, 0.0f},
|
||||
{1.49219f, 0.316406f, 0.0f},
|
||||
{1.5f, 0.5f, 0.0f},
|
||||
{1.50781f, 0.683594f, 0.0f},
|
||||
{1.5625f, 0.84375f, 0.0f},
|
||||
{1.71094f, 0.957031f, 0.0f},
|
||||
{2.0f, 1.0f, 0.0f},
|
||||
}};
|
||||
for (const int i : evaluated_positions.index_range()) {
|
||||
EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f);
|
||||
}
|
||||
|
||||
Array<float> radii{{0.0f, 1.0f, 2.0f}};
|
||||
Array<float> evaluated_radii(17);
|
||||
curves.interpolate_to_evaluated(0, radii.as_span(), evaluated_radii.as_mutable_span());
|
||||
static const Array<float> result_2{{
|
||||
0.0f,
|
||||
0.125f,
|
||||
0.25f,
|
||||
0.375f,
|
||||
0.5f,
|
||||
0.625f,
|
||||
0.75f,
|
||||
0.875f,
|
||||
1.0f,
|
||||
1.125f,
|
||||
1.25f,
|
||||
1.375f,
|
||||
1.5f,
|
||||
1.625f,
|
||||
1.75f,
|
||||
1.875f,
|
||||
2.0f,
|
||||
}};
|
||||
for (const int i : evaluated_radii.index_range()) {
|
||||
EXPECT_NEAR(evaluated_radii[i], result_2[i], 1e-6f);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::bke::tests
|
||||
|
|
|
@ -980,74 +980,116 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo
|
|||
/** \name Stroke Smooth Positions
|
||||
* \{ */
|
||||
|
||||
bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, int i, float inf, const bool smooth_caps)
|
||||
bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
|
||||
int i,
|
||||
float influence,
|
||||
int iterations,
|
||||
const bool smooth_caps,
|
||||
const bool keep_shape,
|
||||
bGPDstroke *r_gps)
|
||||
{
|
||||
bGPDspoint *pt = &gps->points[i];
|
||||
float sco[3] = {0.0f};
|
||||
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
|
||||
/* Do nothing if not enough points to smooth out */
|
||||
if (gps->totpoints <= 2) {
|
||||
/* If nothing to do, return early */
|
||||
if (gps->totpoints <= 2 || iterations <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Only affect endpoints by a fraction of the normal strength,
|
||||
* to prevent the stroke from shrinking too much
|
||||
/* Overview of the algorithm here and in the following smooth functions:
|
||||
* The smooth functions return the new attribute in question for a single point.
|
||||
* The result is stored in r_gps->points[i], while the data is read from gps.
|
||||
* To get a correct result, duplicate the stroke point data and read from the copy,
|
||||
* while writing to the real stroke. Not doing that will result in acceptable, but
|
||||
* asymmetric results.
|
||||
* This algorithm works as long as all points are being smoothed. If there is
|
||||
* points that should not get smoothed, use the old repeat smooth pattern with
|
||||
* the parameter "iterations" set to 1 or 2. (2 matches the old algorithm).
|
||||
*/
|
||||
if ((!smooth_caps) && (!is_cyclic && ELEM(i, 0, gps->totpoints - 1))) {
|
||||
inf *= 0.1f;
|
||||
|
||||
const bGPDspoint *pt = &gps->points[i];
|
||||
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
/* If smooth_caps is false, the caps will not be translated by smoothing. */
|
||||
if (!smooth_caps && !is_cyclic && ELEM(i, 0, gps->totpoints - 1)) {
|
||||
copy_v3_v3(&r_gps->points[i].x, &pt->x);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Compute smoothed coordinate by taking the ones nearby */
|
||||
/* XXX: This is potentially slow,
|
||||
* and suffers from accumulation error as earlier points are handled before later ones. */
|
||||
{
|
||||
/* XXX: this is hardcoded to look at 2 points on either side of the current one
|
||||
* (i.e. 5 items total). */
|
||||
const int steps = 2;
|
||||
const float average_fac = 1.0f / (float)(steps * 2 + 1);
|
||||
int step;
|
||||
/* This function uses a binomial kernel, which is the discrete version of gaussian blur.
|
||||
* The weight for a vertex at the relative index i is
|
||||
* w = nCr(n, j + n/2) / 2^n = (n/1 * (n-1)/2 * ... * (n-j-n/2)/(j+n/2)) / 2^n
|
||||
* All weights together sum up to 1
|
||||
* This is equivalent to doing multiple iterations of averaging neighbors,
|
||||
* where n = iterations * 2 and -n/2 <= j <= n/2
|
||||
*
|
||||
* Now the problem is that nCr(n, j + n/2) is very hard to compute for n > 500, since even
|
||||
* double precision isn't sufficient. A very good robust approximation for n > 20 is
|
||||
* nCr(n, j + n/2) / 2^n = sqrt(2/(pi*n)) * exp(-2*j*j/n)
|
||||
*
|
||||
* There is one more problem left: The old smooth algorithm was doing a more aggressive
|
||||
* smooth. To solve that problem, choose a different n/2, which does not match the range and
|
||||
* normalize the weights on finish. This may cause some artifacts at low values.
|
||||
*
|
||||
* keep_shape is a new option to stop the stroke from severly deforming.
|
||||
* It uses different partially negative weights.
|
||||
* w = 2 * (nCr(n, j + n/2) / 2^n) - (nCr(3*n, j + n) / 2^(3*n))
|
||||
* ~ 2 * sqrt(2/(pi*n)) * exp(-2*j*j/n) - sqrt(2/(pi*3*n)) * exp(-2*j*j/(3*n))
|
||||
* All weigths still sum up to 1.
|
||||
* Note these weights only work because the averaging is done in relative coordinates.
|
||||
*/
|
||||
float sco[3] = {0.0f, 0.0f, 0.0f};
|
||||
float tmp[3];
|
||||
const int n_half = keep_shape ? (iterations * iterations) / 8 + iterations :
|
||||
(iterations * iterations) / 4 + 2 * iterations + 12;
|
||||
double w = keep_shape ? 2.0 : 1.0;
|
||||
double w2 = keep_shape ?
|
||||
(1.0 / M_SQRT3) * exp((2 * iterations * iterations) / (double)(n_half * 3)) :
|
||||
0.0;
|
||||
double total_w = 0.0;
|
||||
for (int step = iterations; step > 0; step--) {
|
||||
int before = i - step;
|
||||
int after = i + step;
|
||||
float w_before = (float)(w - w2);
|
||||
float w_after = (float)(w - w2);
|
||||
|
||||
/* add the point itself */
|
||||
madd_v3_v3fl(sco, &pt->x, average_fac);
|
||||
|
||||
/* n-steps before/after current point */
|
||||
/* XXX: review how the endpoints are treated by this algorithm. */
|
||||
/* XXX: falloff measures should also introduce some weighting variations,
|
||||
* so that further-out points get less weight. */
|
||||
for (step = 1; step <= steps; step++) {
|
||||
bGPDspoint *pt1, *pt2;
|
||||
int before = i - step;
|
||||
int after = i + step;
|
||||
|
||||
if (is_cyclic) {
|
||||
if (before < 0) {
|
||||
/* Sub to end point (before is already negative). */
|
||||
before = gps->totpoints + before;
|
||||
CLAMP(before, 0, gps->totpoints - 1);
|
||||
}
|
||||
if (after > gps->totpoints - 1) {
|
||||
/* Add to start point. */
|
||||
after = after - gps->totpoints;
|
||||
CLAMP(after, 0, gps->totpoints - 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CLAMP_MIN(before, 0);
|
||||
CLAMP_MAX(after, gps->totpoints - 1);
|
||||
}
|
||||
|
||||
pt1 = &gps->points[before];
|
||||
pt2 = &gps->points[after];
|
||||
|
||||
/* add both these points to the average-sum (s += p[i]/n) */
|
||||
madd_v3_v3fl(sco, &pt1->x, average_fac);
|
||||
madd_v3_v3fl(sco, &pt2->x, average_fac);
|
||||
if (is_cyclic) {
|
||||
before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
|
||||
after = after % gps->totpoints;
|
||||
}
|
||||
else {
|
||||
if (before < 0) {
|
||||
if (!smooth_caps) {
|
||||
w_before *= -before / (float)i;
|
||||
}
|
||||
before = 0;
|
||||
}
|
||||
if (after > gps->totpoints - 1) {
|
||||
if (!smooth_caps) {
|
||||
w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - i);
|
||||
}
|
||||
after = gps->totpoints - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Based on influence factor, blend between original and optimal smoothed coordinate */
|
||||
interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
|
||||
/* Add both these points in relative coordinates to the weighted average sum. */
|
||||
sub_v3_v3v3(tmp, &gps->points[before].x, &pt->x);
|
||||
madd_v3_v3fl(sco, tmp, w_before);
|
||||
sub_v3_v3v3(tmp, &gps->points[after].x, &pt->x);
|
||||
madd_v3_v3fl(sco, tmp, w_after);
|
||||
|
||||
total_w += w_before;
|
||||
total_w += w_after;
|
||||
|
||||
w *= (n_half + step) / (double)(n_half + 1 - step);
|
||||
w2 *= (n_half * 3 + step) / (double)(n_half * 3 + 1 - step);
|
||||
}
|
||||
total_w += w - w2;
|
||||
/* The accumulated weight total_w should be
|
||||
* ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
|
||||
* here, but sometimes not quite. */
|
||||
mul_v3_fl(sco, (float)(1.0 / total_w));
|
||||
/* Shift back to global coordinates. */
|
||||
add_v3_v3(sco, &pt->x);
|
||||
|
||||
/* Based on influence factor, blend between original and optimal smoothed coordinate. */
|
||||
interp_v3_v3v3(&r_gps->points[i].x, &pt->x, sco, influence);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1058,74 +1100,54 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, int i, float inf, const bo
|
|||
/** \name Stroke Smooth Strength
|
||||
* \{ */
|
||||
|
||||
bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence)
|
||||
bool BKE_gpencil_stroke_smooth_strength(
|
||||
bGPDstroke *gps, int i, float influence, int iterations, bGPDstroke *r_gps)
|
||||
{
|
||||
bGPDspoint *ptb = &gps->points[point_index];
|
||||
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
|
||||
/* Do nothing if not enough points */
|
||||
if ((gps->totpoints <= 2) || (point_index < 1)) {
|
||||
/* If nothing to do, return early */
|
||||
if (gps->totpoints <= 2 || iterations <= 0) {
|
||||
return false;
|
||||
}
|
||||
/* Only affect endpoints by a fraction of the normal influence */
|
||||
float inf = influence;
|
||||
if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
|
||||
inf *= 0.01f;
|
||||
}
|
||||
/* Limit max influence to reduce pop effect. */
|
||||
CLAMP_MAX(inf, 0.98f);
|
||||
|
||||
float total = 0.0f;
|
||||
float max_strength = 0.0f;
|
||||
const int steps = 4;
|
||||
const float average_fac = 1.0f / (float)(steps * 2 + 1);
|
||||
int step;
|
||||
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
|
||||
|
||||
/* add the point itself */
|
||||
total += ptb->strength * average_fac;
|
||||
max_strength = ptb->strength;
|
||||
|
||||
/* n-steps before/after current point */
|
||||
for (step = 1; step <= steps; step++) {
|
||||
bGPDspoint *pt1, *pt2;
|
||||
int before = point_index - step;
|
||||
int after = point_index + step;
|
||||
const bGPDspoint *pt = &gps->points[i];
|
||||
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
float strength = 0.0f;
|
||||
const int n_half = (iterations * iterations) / 4 + iterations;
|
||||
double w = 1.0;
|
||||
double total_w = 0.0;
|
||||
for (int step = iterations; step > 0; step--) {
|
||||
int before = i - step;
|
||||
int after = i + step;
|
||||
float w_before = (float)w;
|
||||
float w_after = (float)w;
|
||||
|
||||
if (is_cyclic) {
|
||||
if (before < 0) {
|
||||
/* Sub to end point (before is already negative). */
|
||||
before = gps->totpoints + before;
|
||||
CLAMP(before, 0, gps->totpoints - 1);
|
||||
}
|
||||
if (after > gps->totpoints - 1) {
|
||||
/* Add to start point. */
|
||||
after = after - gps->totpoints;
|
||||
CLAMP(after, 0, gps->totpoints - 1);
|
||||
}
|
||||
before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
|
||||
after = after % gps->totpoints;
|
||||
}
|
||||
else {
|
||||
CLAMP_MIN(before, 0);
|
||||
CLAMP_MAX(after, gps->totpoints - 1);
|
||||
}
|
||||
pt1 = &gps->points[before];
|
||||
pt2 = &gps->points[after];
|
||||
|
||||
/* add both these points to the average-sum (s += p[i]/n) */
|
||||
total += pt1->strength * average_fac;
|
||||
total += pt2->strength * average_fac;
|
||||
/* Save max value. */
|
||||
if (max_strength < pt1->strength) {
|
||||
max_strength = pt1->strength;
|
||||
}
|
||||
if (max_strength < pt2->strength) {
|
||||
max_strength = pt2->strength;
|
||||
}
|
||||
/* Add both these points in relative coordinates to the weighted average sum. */
|
||||
strength += w_before * (gps->points[before].strength - pt->strength);
|
||||
strength += w_after * (gps->points[after].strength - pt->strength);
|
||||
|
||||
total_w += w_before;
|
||||
total_w += w_after;
|
||||
|
||||
w *= (n_half + step) / (double)(n_half + 1 - step);
|
||||
}
|
||||
total_w += w;
|
||||
/* The accumulated weight total_w should be
|
||||
* ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
|
||||
* here, but sometimes not quite. */
|
||||
strength /= total_w;
|
||||
|
||||
/* Based on influence factor, blend between original and optimal smoothed value. */
|
||||
ptb->strength = interpf(ptb->strength, total, inf);
|
||||
/* Clamp to maximum stroke strength to avoid weird results. */
|
||||
CLAMP_MAX(ptb->strength, max_strength);
|
||||
r_gps->points[i].strength = pt->strength + strength * influence;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1136,74 +1158,55 @@ bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float
|
|||
/** \name Stroke Smooth Thickness
|
||||
* \{ */
|
||||
|
||||
bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence)
|
||||
bool BKE_gpencil_stroke_smooth_thickness(
|
||||
bGPDstroke *gps, int i, float influence, int iterations, bGPDstroke *r_gps)
|
||||
{
|
||||
bGPDspoint *ptb = &gps->points[point_index];
|
||||
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
|
||||
/* Do nothing if not enough points */
|
||||
if ((gps->totpoints <= 2) || (point_index < 1)) {
|
||||
/* If nothing to do, return early */
|
||||
if (gps->totpoints <= 2 || iterations <= 0) {
|
||||
return false;
|
||||
}
|
||||
/* Only affect endpoints by a fraction of the normal influence */
|
||||
float inf = influence;
|
||||
if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
|
||||
inf *= 0.01f;
|
||||
}
|
||||
/* Limit max influence to reduce pop effect. */
|
||||
CLAMP_MAX(inf, 0.98f);
|
||||
|
||||
float total = 0.0f;
|
||||
float max_pressure = 0.0f;
|
||||
const int steps = 4;
|
||||
const float average_fac = 1.0f / (float)(steps * 2 + 1);
|
||||
int step;
|
||||
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
|
||||
|
||||
/* add the point itself */
|
||||
total += ptb->pressure * average_fac;
|
||||
max_pressure = ptb->pressure;
|
||||
|
||||
/* n-steps before/after current point */
|
||||
for (step = 1; step <= steps; step++) {
|
||||
bGPDspoint *pt1, *pt2;
|
||||
int before = point_index - step;
|
||||
int after = point_index + step;
|
||||
const bGPDspoint *pt = &gps->points[i];
|
||||
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
float pressure = 0.0f;
|
||||
const int n_half = (iterations * iterations) / 4 + iterations;
|
||||
double w = 1.0;
|
||||
double total_w = 0.0;
|
||||
for (int step = iterations; step > 0; step--) {
|
||||
int before = i - step;
|
||||
int after = i + step;
|
||||
float w_before = (float)w;
|
||||
float w_after = (float)w;
|
||||
|
||||
if (is_cyclic) {
|
||||
if (before < 0) {
|
||||
/* Sub to end point (before is already negative). */
|
||||
before = gps->totpoints + before;
|
||||
CLAMP(before, 0, gps->totpoints - 1);
|
||||
}
|
||||
if (after > gps->totpoints - 1) {
|
||||
/* Add to start point. */
|
||||
after = after - gps->totpoints;
|
||||
CLAMP(after, 0, gps->totpoints - 1);
|
||||
}
|
||||
before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
|
||||
after = after % gps->totpoints;
|
||||
}
|
||||
else {
|
||||
CLAMP_MIN(before, 0);
|
||||
CLAMP_MAX(after, gps->totpoints - 1);
|
||||
}
|
||||
pt1 = &gps->points[before];
|
||||
pt2 = &gps->points[after];
|
||||
|
||||
/* add both these points to the average-sum (s += p[i]/n) */
|
||||
total += pt1->pressure * average_fac;
|
||||
total += pt2->pressure * average_fac;
|
||||
/* Save max value. */
|
||||
if (max_pressure < pt1->pressure) {
|
||||
max_pressure = pt1->pressure;
|
||||
}
|
||||
if (max_pressure < pt2->pressure) {
|
||||
max_pressure = pt2->pressure;
|
||||
}
|
||||
/* Add both these points in relative coordinates to the weighted average sum. */
|
||||
pressure += w_before * (gps->points[before].pressure - pt->pressure);
|
||||
pressure += w_after * (gps->points[after].pressure - pt->pressure);
|
||||
|
||||
total_w += w_before;
|
||||
total_w += w_after;
|
||||
|
||||
w *= (n_half + step) / (double)(n_half + 1 - step);
|
||||
}
|
||||
total_w += w;
|
||||
/* The accumulated weight total_w should be
|
||||
* ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
|
||||
* here, but sometimes not quite. */
|
||||
pressure /= total_w;
|
||||
|
||||
/* Based on influence factor, blend between original and optimal smoothed value. */
|
||||
ptb->pressure = interpf(ptb->pressure, total, inf);
|
||||
/* Clamp to maximum stroke thickness to avoid weird results. */
|
||||
CLAMP_MAX(ptb->pressure, max_pressure);
|
||||
r_gps->points[i].pressure = pt->pressure + pressure * influence;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1213,57 +1216,127 @@ bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float
|
|||
/** \name Stroke Smooth UV
|
||||
* \{ */
|
||||
|
||||
bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence)
|
||||
bool BKE_gpencil_stroke_smooth_uv(
|
||||
struct bGPDstroke *gps, int i, float influence, int iterations, struct bGPDstroke *r_gps)
|
||||
{
|
||||
bGPDspoint *ptb = &gps->points[point_index];
|
||||
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
|
||||
/* Do nothing if not enough points */
|
||||
if (gps->totpoints <= 2) {
|
||||
/* If nothing to do, return early */
|
||||
if (gps->totpoints <= 2 || iterations <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Compute theoretical optimal value */
|
||||
bGPDspoint *pta, *ptc;
|
||||
int before = point_index - 1;
|
||||
int after = point_index + 1;
|
||||
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
|
||||
|
||||
if (is_cyclic) {
|
||||
if (before < 0) {
|
||||
/* Sub to end point (before is already negative). */
|
||||
before = gps->totpoints + before;
|
||||
CLAMP(before, 0, gps->totpoints - 1);
|
||||
const bGPDspoint *pt = &gps->points[i];
|
||||
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||
|
||||
/* If don't change the caps. */
|
||||
if (!is_cyclic && ELEM(i, 0, gps->totpoints - 1)) {
|
||||
r_gps->points[i].uv_rot = pt->uv_rot;
|
||||
r_gps->points[i].uv_fac = pt->uv_fac;
|
||||
return true;
|
||||
}
|
||||
|
||||
float uv_rot = 0.0f;
|
||||
float uv_fac = 0.0f;
|
||||
const int n_half = iterations * iterations + iterations;
|
||||
double w = 1.0;
|
||||
double total_w = 0.0;
|
||||
for (int step = iterations; step > 0; step--) {
|
||||
int before = i - step;
|
||||
int after = i + step;
|
||||
float w_before = (float)w;
|
||||
float w_after = (float)w;
|
||||
|
||||
if (is_cyclic) {
|
||||
before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
|
||||
after = after % gps->totpoints;
|
||||
}
|
||||
if (after > gps->totpoints - 1) {
|
||||
/* Add to start point. */
|
||||
after = after - gps->totpoints;
|
||||
CLAMP(after, 0, gps->totpoints - 1);
|
||||
else {
|
||||
if (before < 0) {
|
||||
w_before *= -before / (float)i;
|
||||
before = 0;
|
||||
}
|
||||
if (after > gps->totpoints - 1) {
|
||||
w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - i);
|
||||
after = gps->totpoints - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
CLAMP_MIN(before, 0);
|
||||
CLAMP_MAX(after, gps->totpoints - 1);
|
||||
}
|
||||
pta = &gps->points[before];
|
||||
ptc = &gps->points[after];
|
||||
|
||||
/* the optimal value is the corresponding to the interpolation of the pressure
|
||||
* at the distance of point b
|
||||
*/
|
||||
float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
|
||||
/* sometimes the factor can be wrong due stroke geometry, so use middle point */
|
||||
if ((fac < 0.0f) || (fac > 1.0f)) {
|
||||
fac = 0.5f;
|
||||
}
|
||||
float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac);
|
||||
/* Add both these points in relative coordinates to the weighted average sum. */
|
||||
uv_rot += w_before * (gps->points[before].uv_rot - pt->uv_rot);
|
||||
uv_rot += w_after * (gps->points[after].uv_rot - pt->uv_rot);
|
||||
uv_fac += w_before * (gps->points[before].uv_fac - pt->uv_fac);
|
||||
uv_fac += w_after * (gps->points[after].uv_fac - pt->uv_fac);
|
||||
|
||||
/* Based on influence factor, blend between original and optimal */
|
||||
ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence);
|
||||
CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2);
|
||||
total_w += w_before;
|
||||
total_w += w_after;
|
||||
|
||||
w *= (n_half + step) / (double)(n_half + 1 - step);
|
||||
}
|
||||
total_w += w;
|
||||
/* The accumulated weight total_w should be
|
||||
* ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
|
||||
* here, but sometimes not quite. */
|
||||
uv_rot /= total_w;
|
||||
uv_fac /= total_w;
|
||||
|
||||
/* Based on influence factor, blend between original and optimal smoothed value. */
|
||||
r_gps->points[i].uv_rot = pt->uv_rot + uv_rot * influence;
|
||||
r_gps->points[i].uv_fac = pt->uv_fac + uv_fac * influence;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BKE_gpencil_stroke_smooth(bGPDstroke *gps,
|
||||
const float influence,
|
||||
const int iterations,
|
||||
const bool smooth_position,
|
||||
const bool smooth_strength,
|
||||
const bool smooth_thickness,
|
||||
const bool smooth_uv,
|
||||
const bool keep_shape,
|
||||
const float *weights)
|
||||
{
|
||||
if (influence <= 0 || iterations <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make a copy of the point data to avoid directionality of the smooth operation. */
|
||||
bGPDstroke gps_old = *gps;
|
||||
gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
|
||||
|
||||
/* Smooth stroke. */
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
float val = influence;
|
||||
if (weights != NULL) {
|
||||
val *= weights[i];
|
||||
if (val <= 0.0f) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Currently the weights only control the influence, but is would be much better if they
|
||||
* would control the distribution used in smooth, similar to how the ends are handled. */
|
||||
|
||||
/* Perform smoothing. */
|
||||
if (smooth_position) {
|
||||
BKE_gpencil_stroke_smooth_point(&gps_old, i, val, iterations, false, keep_shape, gps);
|
||||
}
|
||||
if (smooth_strength) {
|
||||
BKE_gpencil_stroke_smooth_strength(&gps_old, i, val, iterations, gps);
|
||||
}
|
||||
if (smooth_thickness) {
|
||||
BKE_gpencil_stroke_smooth_thickness(&gps_old, i, val, iterations, gps);
|
||||
}
|
||||
if (smooth_uv) {
|
||||
BKE_gpencil_stroke_smooth_uv(&gps_old, i, val, iterations, gps);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the copied points array. */
|
||||
MEM_freeN(gps_old.points);
|
||||
}
|
||||
|
||||
void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
|
||||
int totpoints,
|
||||
float (*points2d)[2],
|
||||
|
@ -3443,7 +3516,7 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
|
|||
for (i = start; i < end; i++) {
|
||||
pt = &gps_a->points[i];
|
||||
pt->pressure += (avg_pressure - pt->pressure) * ratio;
|
||||
BKE_gpencil_stroke_smooth_point(gps_a, i, ratio * 0.6f, false);
|
||||
BKE_gpencil_stroke_smooth_point(gps_a, i, ratio * 0.6f, 2, false, true, gps_a);
|
||||
|
||||
ratio += step;
|
||||
/* In the center, reverse the ratio. */
|
||||
|
|
|
@ -910,18 +910,20 @@ static void curve_to_mesh_eval_ensure(Object &object)
|
|||
*
|
||||
* So we create temporary copy of the object which will use same data as the original bevel, but
|
||||
* will have no modifiers. */
|
||||
Object bevel_object = {{nullptr}};
|
||||
Object bevel_object;
|
||||
blender::dna::zero_memory(bevel_object);
|
||||
if (curve.bevobj != nullptr) {
|
||||
memcpy(&bevel_object, curve.bevobj, sizeof(bevel_object));
|
||||
blender::dna::copy_memory(bevel_object, *curve.bevobj);
|
||||
BLI_listbase_clear(&bevel_object.modifiers);
|
||||
BKE_object_runtime_reset(&bevel_object);
|
||||
curve.bevobj = &bevel_object;
|
||||
}
|
||||
|
||||
/* Same thing for taper. */
|
||||
Object taper_object = {{nullptr}};
|
||||
Object taper_object;
|
||||
blender::dna::zero_memory(taper_object);
|
||||
if (curve.taperobj != nullptr) {
|
||||
memcpy(&taper_object, curve.taperobj, sizeof(taper_object));
|
||||
blender::dna::copy_memory(taper_object, *curve.taperobj);
|
||||
BLI_listbase_clear(&taper_object.modifiers);
|
||||
BKE_object_runtime_reset(&taper_object);
|
||||
curve.taperobj = &taper_object;
|
||||
|
@ -1066,7 +1068,7 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph,
|
|||
}
|
||||
|
||||
Object object_for_eval;
|
||||
memcpy(&object_for_eval, object, sizeof(object_for_eval));
|
||||
blender::dna::zero_memory(object_for_eval);
|
||||
if (object_for_eval.runtime.data_orig != nullptr) {
|
||||
object_for_eval.data = object_for_eval.runtime.data_orig;
|
||||
}
|
||||
|
|
|
@ -1236,7 +1236,7 @@ IDTypeInfo IDType_ID_OB = {
|
|||
|
||||
void BKE_object_workob_clear(Object *workob)
|
||||
{
|
||||
memset(workob, 0, sizeof(Object));
|
||||
blender::dna::zero_memory(*workob);
|
||||
|
||||
workob->scale[0] = workob->scale[1] = workob->scale[2] = 1.0f;
|
||||
workob->dscale[0] = workob->dscale[1] = workob->dscale[2] = 1.0f;
|
||||
|
@ -3946,7 +3946,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
|
|||
/* pass */
|
||||
}
|
||||
else {
|
||||
Object temp_ob = *dob->ob;
|
||||
Object temp_ob = blender::dna::shallow_copy(*dob->ob);
|
||||
/* Do not modify the original boundbox. */
|
||||
temp_ob.runtime.bb = nullptr;
|
||||
BKE_object_replace_data_on_shallow_copy(&temp_ob, dob->ob_data);
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <type_traits>
|
||||
|
||||
#include "BLI_math_base_safe.h"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#ifdef WITH_GMP
|
||||
|
@ -21,6 +20,15 @@
|
|||
|
||||
namespace blender::math {
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_math_float_type = (std::is_floating_point_v<T>
|
||||
#ifdef WITH_GMP
|
||||
|| std::is_same_v<T, mpq_class>
|
||||
#endif
|
||||
);
|
||||
|
||||
template<typename T> inline constexpr bool is_math_integral_type = std::is_integral_v<T>;
|
||||
|
||||
template<typename T> inline bool is_zero(const T &a)
|
||||
{
|
||||
return a == T(0);
|
||||
|
@ -84,19 +92,23 @@ template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T ceil(const
|
|||
return std::ceil(a);
|
||||
}
|
||||
|
||||
template<typename T> inline T distance(const T &a, const T &b)
|
||||
{
|
||||
return std::abs(a - b);
|
||||
}
|
||||
|
||||
template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(const T &a)
|
||||
{
|
||||
return a - std::floor(a);
|
||||
}
|
||||
|
||||
template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
|
||||
inline T interpolate(const T &a, const T &b, const T &t)
|
||||
template<typename T, typename FactorT, BLI_ENABLE_IF((is_math_float_type<FactorT>))>
|
||||
inline T interpolate(const T &a, const T &b, const FactorT &t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
||||
template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))>
|
||||
inline T midpoint(const T &a, const T &b)
|
||||
template<typename T> inline T midpoint(const T &a, const T &b)
|
||||
{
|
||||
return (a + b) * T(0.5);
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size>
|
|||
BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] * b[i]);
|
||||
}
|
||||
|
||||
friend vec_base operator*(const vec_base &a, T b)
|
||||
template<typename FactorT> friend vec_base operator*(const vec_base &a, FactorT b)
|
||||
{
|
||||
BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] * b);
|
||||
}
|
||||
|
@ -579,13 +579,4 @@ using double2 = vec_base<double, 2>;
|
|||
using double3 = vec_base<double, 3>;
|
||||
using double4 = vec_base<double, 4>;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_math_float_type = (std::is_floating_point_v<T>
|
||||
#ifdef WITH_GMP
|
||||
|| std::is_same_v<T, mpq_class>
|
||||
#endif
|
||||
);
|
||||
|
||||
template<typename T> inline constexpr bool is_math_integral_type = std::is_integral_v<T>;
|
||||
|
||||
} // namespace blender
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
#include "BLI_math_base_safe.h"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
@ -339,10 +339,10 @@ inline vec_base<T, 3> cross_poly(Span<vec_base<T, 3>> poly)
|
|||
return n;
|
||||
}
|
||||
|
||||
template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))>
|
||||
template<typename T, typename FactorT, int Size, BLI_ENABLE_IF((is_math_float_type<FactorT>))>
|
||||
inline vec_base<T, Size> interpolate(const vec_base<T, Size> &a,
|
||||
const vec_base<T, Size> &b,
|
||||
const T &t)
|
||||
const FactorT &t)
|
||||
{
|
||||
return a * (1 - t) + b * t;
|
||||
}
|
||||
|
|
|
@ -151,4 +151,9 @@ TEST(math_base, Midpoint)
|
|||
EXPECT_NEAR(math::midpoint(100.0f, 200.0f), 150.0f, 1e-4f);
|
||||
}
|
||||
|
||||
TEST(math_base, InterpolateInt)
|
||||
{
|
||||
EXPECT_EQ(math::interpolate(100, 200, 0.4f), 140);
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
|
|
|
@ -85,4 +85,24 @@ TEST(math_vector, Clamp)
|
|||
EXPECT_EQ(result_2.z, -50);
|
||||
}
|
||||
|
||||
TEST(math_vector, InterpolateInt)
|
||||
{
|
||||
const int3 a(0, -100, 50);
|
||||
const int3 b(0, 100, 100);
|
||||
const int3 result = math::interpolate(a, b, 0.75);
|
||||
EXPECT_EQ(result.x, 0);
|
||||
EXPECT_EQ(result.y, 50);
|
||||
EXPECT_EQ(result.z, 87);
|
||||
}
|
||||
|
||||
TEST(math_vector, InterpolateFloat)
|
||||
{
|
||||
const float3 a(40.0f, -100.0f, 50.0f);
|
||||
const float3 b(20.0f, 100.0f, 100.0f);
|
||||
const float3 result = math::interpolate(a, b, 0.5);
|
||||
EXPECT_FLOAT_EQ(result.x, 30.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 75.0f);
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
|
|
|
@ -2415,6 +2415,24 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Change grease pencil smooth iterations to match old results with new algorithm. */
|
||||
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
|
||||
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
||||
if (md->type == eGpencilModifierType_Smooth) {
|
||||
SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
|
||||
if (gpmd->step == 1 && gpmd->factor <= 0.5f) {
|
||||
gpmd->factor *= 2.0f;
|
||||
}
|
||||
else {
|
||||
gpmd->step = 1 + (int)(gpmd->factor * max_ff(0.0f,
|
||||
min_ff(5.1f * sqrtf(gpmd->step) - 3.0f,
|
||||
gpmd->step + 2.0f)));
|
||||
gpmd->factor = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -167,7 +167,7 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data)
|
|||
/* Temporary object to evaluate. */
|
||||
Object *dupli_parent = data->dupli_parent;
|
||||
Object *temp_dupli_object = &data->temp_dupli_object;
|
||||
*temp_dupli_object = *dob->ob;
|
||||
*temp_dupli_object = blender::dna::shallow_copy(*dob->ob);
|
||||
temp_dupli_object->base_flag = dupli_parent->base_flag | BASE_FROM_DUPLI;
|
||||
temp_dupli_object->base_local_view_bits = dupli_parent->base_local_view_bits;
|
||||
temp_dupli_object->runtime.local_collections_bits =
|
||||
|
|
|
@ -3924,31 +3924,36 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
|
|||
|
||||
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
for (int r = 0; r < repeat; r++) {
|
||||
/* TODO use `BKE_gpencil_stroke_smooth` when the weights are better used. */
|
||||
bGPDstroke gps_old = *gps;
|
||||
gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
|
||||
/* Here the iteration needs to be done outside the smooth functions,
|
||||
* as there are points that don't get smoothed. */
|
||||
for (int n = 0; n < repeat; n++) {
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
bGPDspoint *pt = &gps->points[i];
|
||||
if ((only_selected) && ((pt->flag & GP_SPOINT_SELECT) == 0)) {
|
||||
if (only_selected && (gps->points[i].flag & GP_SPOINT_SELECT) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* perform smoothing */
|
||||
/* Perform smoothing. */
|
||||
if (smooth_position) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, i, factor, false);
|
||||
BKE_gpencil_stroke_smooth_point(&gps_old, i, factor, 1, false, false, gps);
|
||||
}
|
||||
if (smooth_strength) {
|
||||
BKE_gpencil_stroke_smooth_strength(gps, i, factor);
|
||||
BKE_gpencil_stroke_smooth_strength(&gps_old, i, factor, 1, gps);
|
||||
}
|
||||
if (smooth_thickness) {
|
||||
/* thickness need to repeat process several times */
|
||||
for (int r2 = 0; r2 < repeat * 2; r2++) {
|
||||
BKE_gpencil_stroke_smooth_thickness(gps, i, 1.0f - factor);
|
||||
}
|
||||
BKE_gpencil_stroke_smooth_thickness(&gps_old, i, 1.0f - factor, 1, gps);
|
||||
}
|
||||
if (smooth_uv) {
|
||||
BKE_gpencil_stroke_smooth_uv(gps, i, factor);
|
||||
BKE_gpencil_stroke_smooth_uv(&gps_old, i, factor, 1, gps);
|
||||
}
|
||||
}
|
||||
if (n < repeat - 1) {
|
||||
memcpy(gps_old.points, gps->points, sizeof(bGPDspoint) * gps->totpoints);
|
||||
}
|
||||
}
|
||||
MEM_freeN(gps_old.points);
|
||||
}
|
||||
}
|
||||
GP_EDITABLE_STROKES_END(gpstroke_iter);
|
||||
|
@ -4926,10 +4931,10 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
|
|||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
prop = RNA_def_int(ot->srna, "repeat", 1, 1, 50, "Repeat", "", 1, 20);
|
||||
prop = RNA_def_int(ot->srna, "repeat", 2, 1, 1000, "Repeat", "", 1, 1000);
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f);
|
||||
RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 2.0f, "Factor", "", 0.0f, 1.0f);
|
||||
RNA_def_boolean(ot->srna,
|
||||
"only_selected",
|
||||
true,
|
||||
|
|
|
@ -1638,14 +1638,9 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
|
|||
}
|
||||
}
|
||||
|
||||
/* smooth stroke */
|
||||
float reduce = 0.0f;
|
||||
float smoothfac = 1.0f;
|
||||
for (int r = 0; r < 1; r++) {
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, i, smoothfac - reduce, false);
|
||||
}
|
||||
reduce += 0.25f; /* reduce the factor */
|
||||
/* Smooth stroke. No copy of the stroke since there only a minor improvement here. */
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, i, 1.0f, 2, false, true, gps);
|
||||
}
|
||||
|
||||
/* if axis locked, reproject to plane locked */
|
||||
|
|
|
@ -310,23 +310,6 @@ static void gpencil_stroke_pair_table(bContext *C,
|
|||
}
|
||||
}
|
||||
|
||||
static void gpencil_interpolate_smooth_stroke(bGPDstroke *gps,
|
||||
float smooth_factor,
|
||||
int smooth_steps)
|
||||
{
|
||||
if (smooth_factor == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
float reduce = 0.0f;
|
||||
for (int r = 0; r < smooth_steps; r++) {
|
||||
for (int i = 0; i < gps->totpoints - 1; i++) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, i, smooth_factor - reduce, false);
|
||||
BKE_gpencil_stroke_smooth_strength(gps, i, smooth_factor);
|
||||
}
|
||||
reduce += 0.25f; /* reduce the factor */
|
||||
}
|
||||
}
|
||||
/* Perform interpolation */
|
||||
static void gpencil_interpolate_update_points(const bGPDstroke *gps_from,
|
||||
const bGPDstroke *gps_to,
|
||||
|
@ -553,7 +536,15 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
|
|||
|
||||
/* Update points position. */
|
||||
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
|
||||
gpencil_interpolate_smooth_stroke(new_stroke, tgpi->smooth_factor, tgpi->smooth_steps);
|
||||
BKE_gpencil_stroke_smooth(new_stroke,
|
||||
tgpi->smooth_factor,
|
||||
tgpi->smooth_steps,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
NULL);
|
||||
|
||||
/* Calc geometry data. */
|
||||
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
|
||||
|
@ -1385,7 +1376,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
|
|||
|
||||
/* Update points position. */
|
||||
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
|
||||
gpencil_interpolate_smooth_stroke(new_stroke, smooth_factor, smooth_steps);
|
||||
BKE_gpencil_stroke_smooth(
|
||||
new_stroke, smooth_factor, smooth_steps, true, true, false, false, true, NULL);
|
||||
|
||||
/* Calc geometry data. */
|
||||
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
|
||||
|
|
|
@ -1197,29 +1197,21 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
|
|||
gpencil_subdivide_stroke(gpd, gps, subdivide);
|
||||
}
|
||||
|
||||
/* Smooth stroke after subdiv - only if there's something to do for each iteration,
|
||||
* the factor is reduced to get a better smoothing
|
||||
* without changing too much the original stroke. */
|
||||
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
|
||||
(brush->gpencil_settings->draw_smoothfac > 0.0f)) {
|
||||
float reduce = 0.0f;
|
||||
for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) {
|
||||
for (i = 0; i < gps->totpoints - 1; i++) {
|
||||
BKE_gpencil_stroke_smooth_point(
|
||||
gps, i, brush->gpencil_settings->draw_smoothfac - reduce, false);
|
||||
BKE_gpencil_stroke_smooth_strength(gps, i, brush->gpencil_settings->draw_smoothfac);
|
||||
}
|
||||
reduce += 0.25f; /* reduce the factor */
|
||||
}
|
||||
/* Smooth stroke after subdiv - only if there's something to do for each iteration.
|
||||
* Keep the original stroke shape as much as possible. */
|
||||
const float smoothfac = brush->gpencil_settings->draw_smoothfac;
|
||||
const int iterations = brush->gpencil_settings->draw_smoothlvl;
|
||||
if (brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) {
|
||||
BKE_gpencil_stroke_smooth(gps, smoothfac, iterations, true, true, false, false, true, NULL);
|
||||
}
|
||||
/* If reproject the stroke using Stroke mode, need to apply a smooth because
|
||||
* the reprojection creates small jitter. */
|
||||
if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) {
|
||||
float ifac = (float)brush->gpencil_settings->input_samples / 10.0f;
|
||||
float sfac = interpf(1.0f, 0.2f, ifac);
|
||||
for (i = 0; i < gps->totpoints - 1; i++) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, i, sfac, false);
|
||||
BKE_gpencil_stroke_smooth_strength(gps, i, sfac);
|
||||
for (i = 0; i < gps->totpoints; i++) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, i, sfac, 2, false, true, gps);
|
||||
BKE_gpencil_stroke_smooth_strength(gps, i, sfac, 2, gps);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -329,16 +329,16 @@ static bool gpencil_brush_smooth_apply(tGP_BrushEditData *gso,
|
|||
|
||||
/* perform smoothing */
|
||||
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, pt_index, inf, false);
|
||||
BKE_gpencil_stroke_smooth_point(gps, pt_index, inf, 2, false, false, gps);
|
||||
}
|
||||
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
|
||||
BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf);
|
||||
BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf, 2, gps);
|
||||
}
|
||||
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
|
||||
BKE_gpencil_stroke_smooth_thickness(gps, pt_index, inf);
|
||||
BKE_gpencil_stroke_smooth_thickness(gps, pt_index, inf, 2, gps);
|
||||
}
|
||||
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
|
||||
BKE_gpencil_stroke_smooth_uv(gps, pt_index, inf);
|
||||
BKE_gpencil_stroke_smooth_uv(gps, pt_index, inf, 2, gps);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -201,6 +201,8 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
|
|||
ot->poll = WM_operator_winactive;
|
||||
ot->ui = wm_usd_export_draw;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER; /* No UNDO possible. */
|
||||
|
||||
WM_operator_properties_filesel(ot,
|
||||
FILE_TYPE_FOLDER | FILE_TYPE_USD,
|
||||
FILE_BLENDER,
|
||||
|
@ -459,6 +461,8 @@ void WM_OT_usd_import(struct wmOperatorType *ot)
|
|||
ot->poll = WM_operator_winactive;
|
||||
ot->ui = wm_usd_import_draw;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
WM_operator_properties_filesel(ot,
|
||||
FILE_TYPE_FOLDER | FILE_TYPE_USD,
|
||||
FILE_BLENDER,
|
||||
|
|
|
@ -1695,13 +1695,13 @@ static void object_apply_rotation(Object *ob, const float rmat[3][3])
|
|||
static void object_apply_location(Object *ob, const float loc[3])
|
||||
{
|
||||
/* quick but weak */
|
||||
Object ob_prev = *ob;
|
||||
Object ob_prev = blender::dna::shallow_copy(*ob);
|
||||
float mat[4][4];
|
||||
copy_m4_m4(mat, ob->obmat);
|
||||
copy_v3_v3(mat[3], loc);
|
||||
BKE_object_apply_mat4(ob, mat, true, true);
|
||||
copy_v3_v3(mat[3], ob->loc);
|
||||
*ob = ob_prev;
|
||||
*ob = blender::dna::shallow_copy(ob_prev);
|
||||
copy_v3_v3(ob->loc, mat[3]);
|
||||
}
|
||||
|
||||
|
|
|
@ -2790,8 +2790,7 @@ static int image_flip_exec(bContext *C, wmOperator *op)
|
|||
|
||||
ED_image_undo_push_end();
|
||||
|
||||
/* force GPU re-upload, all image is invalid. */
|
||||
BKE_image_free_gputextures(ima);
|
||||
BKE_image_partial_update_mark_full_update(ima);
|
||||
|
||||
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
|
||||
|
||||
|
@ -2910,8 +2909,7 @@ static int image_invert_exec(bContext *C, wmOperator *op)
|
|||
|
||||
ED_image_undo_push_end();
|
||||
|
||||
/* Force GPU re-upload, all image is invalid. */
|
||||
BKE_image_free_gputextures(ima);
|
||||
BKE_image_partial_update_mark_full_update(ima);
|
||||
|
||||
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
|
||||
|
||||
|
@ -3001,8 +2999,7 @@ static int image_scale_exec(bContext *C, wmOperator *op)
|
|||
|
||||
ED_image_undo_push_end();
|
||||
|
||||
/* Force GPU re-upload, all image is invalid. */
|
||||
BKE_image_free_gputextures(ima);
|
||||
BKE_image_partial_update_mark_full_update(ima);
|
||||
|
||||
DEG_id_tag_update(&ima->id, 0);
|
||||
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
|
||||
|
|
|
@ -150,7 +150,7 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, ListBase
|
|||
}
|
||||
else {
|
||||
ten = outliner_add_element(
|
||||
&space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
|
||||
&space_outliner_, &tenlib->subtree, lib, nullptr, TSE_ID_BASE, a);
|
||||
ten->directdata = lbarray[a];
|
||||
ten->name = outliner_idcode_to_plural(GS(id->name));
|
||||
}
|
||||
|
|
|
@ -148,6 +148,9 @@ static bool stroke_dash(const bGPDstroke *gps,
|
|||
|
||||
bGPDstroke *stroke = BKE_gpencil_stroke_new(
|
||||
ds->mat_nr < 0 ? gps->mat_nr : ds->mat_nr, size, gps->thickness);
|
||||
if (ds->flag & GP_DASH_USE_CYCLIC) {
|
||||
stroke->flag |= GP_STROKE_CYCLIC;
|
||||
}
|
||||
|
||||
for (int is = 0; is < size; is++) {
|
||||
bGPDspoint *p = &gps->points[new_stroke_offset + is];
|
||||
|
@ -337,6 +340,7 @@ static void panel_draw(const bContext *C, Panel *panel)
|
|||
uiItemR(sub, &ds_ptr, "radius", 0, NULL, ICON_NONE);
|
||||
uiItemR(sub, &ds_ptr, "opacity", 0, NULL, ICON_NONE);
|
||||
uiItemR(sub, &ds_ptr, "material_index", 0, NULL, ICON_NONE);
|
||||
uiItemR(sub, &ds_ptr, "use_cyclic", 0, NULL, ICON_NONE);
|
||||
}
|
||||
|
||||
gpencil_modifier_panel_end(layout, ptr);
|
||||
|
|
|
@ -105,15 +105,15 @@ static void deformStroke(GpencilModifierData *md,
|
|||
|
||||
/* Apply deformed coordinates. */
|
||||
pt = gps->points;
|
||||
bGPDstroke gps_old = *gps;
|
||||
gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
|
||||
for (i = 0; i < gps->totpoints; i++, pt++) {
|
||||
copy_v3_v3(&pt->x, vert_coords[i]);
|
||||
/* Smooth stroke. */
|
||||
if (mmd->smooth_factor > 0.0f) {
|
||||
for (int r = 0; r < mmd->smooth_step; r++) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, i, mmd->smooth_factor, true);
|
||||
}
|
||||
}
|
||||
BKE_gpencil_stroke_smooth_point(
|
||||
&gps_old, i, mmd->smooth_factor, mmd->smooth_step, true, false, gps);
|
||||
}
|
||||
MEM_freeN(gps_old.points);
|
||||
|
||||
MEM_freeN(vert_coords);
|
||||
|
||||
|
|
|
@ -34,10 +34,14 @@
|
|||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "MOD_gpencil_modifiertypes.h"
|
||||
#include "MOD_gpencil_ui_common.h"
|
||||
#include "MOD_gpencil_util.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
static void initData(GpencilModifierData *md)
|
||||
{
|
||||
SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
|
||||
|
@ -94,45 +98,40 @@ static void deformStroke(GpencilModifierData *md,
|
|||
return;
|
||||
}
|
||||
|
||||
/* smooth stroke */
|
||||
if (mmd->factor > 0.0f) {
|
||||
for (int r = 0; r < mmd->step; r++) {
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
|
||||
if (mmd->factor <= 0.0f || mmd->step <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify vertex group */
|
||||
float weight = get_modifier_point_weight(
|
||||
dvert, (mmd->flag & GP_SMOOTH_INVERT_VGROUP) != 0, def_nr);
|
||||
if (weight < 0.0f) {
|
||||
continue;
|
||||
}
|
||||
float *weights = NULL;
|
||||
if (def_nr != -1 || use_curve) {
|
||||
weights = MEM_malloc_arrayN(gps->totpoints, sizeof(*weights), __func__);
|
||||
/* Calculate weights. */
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
|
||||
|
||||
/* Custom curve to modulate value. */
|
||||
if (use_curve) {
|
||||
float value = (float)i / (gps->totpoints - 1);
|
||||
weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
|
||||
}
|
||||
/* Verify vertex group. */
|
||||
float weight = get_modifier_point_weight(
|
||||
dvert, (mmd->flag & GP_SMOOTH_INVERT_VGROUP) != 0, def_nr);
|
||||
|
||||
const float val = mmd->factor * weight;
|
||||
/* perform smoothing */
|
||||
if (mmd->flag & GP_SMOOTH_MOD_LOCATION) {
|
||||
BKE_gpencil_stroke_smooth_point(gps, i, val, false);
|
||||
}
|
||||
if (mmd->flag & GP_SMOOTH_MOD_STRENGTH) {
|
||||
BKE_gpencil_stroke_smooth_strength(gps, i, val);
|
||||
}
|
||||
if ((mmd->flag & GP_SMOOTH_MOD_THICKNESS) && (val > 0.0f)) {
|
||||
/* thickness need to repeat process several times */
|
||||
for (int r2 = 0; r2 < r * 10; r2++) {
|
||||
BKE_gpencil_stroke_smooth_thickness(gps, i, val);
|
||||
}
|
||||
}
|
||||
if (mmd->flag & GP_SMOOTH_MOD_UV) {
|
||||
BKE_gpencil_stroke_smooth_uv(gps, i, val);
|
||||
}
|
||||
/* Custom curve to modulate value. */
|
||||
if (use_curve && weight > 0.0f) {
|
||||
float value = (float)i / (gps->totpoints - 1);
|
||||
weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
|
||||
}
|
||||
|
||||
weights[i] = weight;
|
||||
}
|
||||
}
|
||||
BKE_gpencil_stroke_smooth(gps,
|
||||
mmd->factor,
|
||||
mmd->step,
|
||||
mmd->flag & GP_SMOOTH_MOD_LOCATION,
|
||||
mmd->flag & GP_SMOOTH_MOD_STRENGTH,
|
||||
mmd->flag & GP_SMOOTH_MOD_THICKNESS,
|
||||
mmd->flag & GP_SMOOTH_MOD_UV,
|
||||
mmd->flag & GP_SMOOTH_KEEP_SHAPE,
|
||||
weights);
|
||||
MEM_SAFE_FREE(weights);
|
||||
}
|
||||
|
||||
static void bakeModifier(struct Main *UNUSED(bmain),
|
||||
|
@ -161,7 +160,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
|
|||
|
||||
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||
{
|
||||
uiLayout *row;
|
||||
uiLayout *row, *col;
|
||||
uiLayout *layout = panel->layout;
|
||||
|
||||
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
|
||||
|
@ -177,6 +176,10 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
|||
uiItemR(layout, ptr, "factor", 0, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "step", 0, IFACE_("Repeat"), ICON_NONE);
|
||||
|
||||
col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_edit_position"));
|
||||
uiItemR(col, ptr, "keep_shape", 0, NULL, ICON_NONE);
|
||||
|
||||
gpencil_modifier_panel_end(layout, ptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -107,10 +107,10 @@ list(APPEND LIB
|
|||
blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
if(WIN32)
|
||||
set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /WHOLEARCHIVE:${USD_DEBUG_LIB}")
|
||||
set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /WHOLEARCHIVE:${USD_RELEASE_LIB}")
|
||||
set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /WHOLEARCHIVE:${USD_RELEASE_LIB}")
|
||||
set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /WHOLEARCHIVE:${USD_RELEASE_LIB}")
|
||||
set_property(TARGET bf_usd APPEND_STRING PROPERTY INTERFACE_LINK_OPTIONS "$<$<CONFIG:Debug>:/WHOLEARCHIVE:${USD_DEBUG_LIB}>")
|
||||
set_property(TARGET bf_usd APPEND_STRING PROPERTY INTERFACE_LINK_OPTIONS "$<$<CONFIG:Release>:/WHOLEARCHIVE:${USD_RELEASE_LIB}>")
|
||||
set_property(TARGET bf_usd APPEND_STRING PROPERTY INTERFACE_LINK_OPTIONS "$<$<CONFIG:RelWithDebInfo>:/WHOLEARCHIVE:${USD_RELEASE_LIB}>")
|
||||
set_property(TARGET bf_usd APPEND_STRING PROPERTY INTERFACE_LINK_OPTIONS "$<$<CONFIG:MinSizeRel>:/WHOLEARCHIVE:${USD_RELEASE_LIB}>")
|
||||
endif()
|
||||
|
||||
# Source: https://github.com/PixarAnimationStudios/USD/blob/master/BUILDING.md#linking-whole-archives
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "DNA_light_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include <pxr/usd/usdLux/light.h>
|
||||
|
||||
#include <pxr/usd/usdLux/diskLight.h>
|
||||
#include <pxr/usd/usdLux/distantLight.h>
|
||||
#include <pxr/usd/usdLux/rectLight.h>
|
||||
|
@ -40,14 +38,17 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
|
|||
if (!prim_) {
|
||||
return;
|
||||
}
|
||||
#if PXR_VERSION >= 2111
|
||||
pxr::UsdLuxLightAPI light_api(prim_);
|
||||
#else
|
||||
pxr::UsdLuxLight light_api(prim_);
|
||||
#endif
|
||||
|
||||
pxr::UsdLuxLight light_prim(prim_);
|
||||
|
||||
if (!light_prim) {
|
||||
if (!light_api) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::UsdLuxShapingAPI shaping_api(light_prim);
|
||||
pxr::UsdLuxShapingAPI shaping_api;
|
||||
|
||||
/* Set light type. */
|
||||
|
||||
|
@ -63,6 +64,8 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
|
|||
else if (prim_.IsA<pxr::UsdLuxSphereLight>()) {
|
||||
blight->type = LA_LOCAL;
|
||||
|
||||
shaping_api = pxr::UsdLuxShapingAPI(prim_);
|
||||
|
||||
if (shaping_api && shaping_api.GetShapingConeAngleAttr().IsAuthored()) {
|
||||
blight->type = LA_SPOT;
|
||||
}
|
||||
|
@ -73,7 +76,7 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
|
|||
|
||||
/* Set light values. */
|
||||
|
||||
if (pxr::UsdAttribute intensity_attr = light_prim.GetIntensityAttr()) {
|
||||
if (pxr::UsdAttribute intensity_attr = light_api.GetIntensityAttr()) {
|
||||
float intensity = 0.0f;
|
||||
if (intensity_attr.Get(&intensity, motionSampleTime)) {
|
||||
blight->energy = intensity * this->import_params_.light_intensity_scale;
|
||||
|
@ -92,14 +95,14 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
|
|||
light_prim.GetDiffuseAttr().Get(&diffuse, motionSampleTime);
|
||||
#endif
|
||||
|
||||
if (pxr::UsdAttribute spec_attr = light_prim.GetSpecularAttr()) {
|
||||
if (pxr::UsdAttribute spec_attr = light_api.GetSpecularAttr()) {
|
||||
float spec = 0.0f;
|
||||
if (spec_attr.Get(&spec, motionSampleTime)) {
|
||||
blight->spec_fac = spec;
|
||||
}
|
||||
}
|
||||
|
||||
if (pxr::UsdAttribute color_attr = light_prim.GetColorAttr()) {
|
||||
if (pxr::UsdAttribute color_attr = light_api.GetColorAttr()) {
|
||||
pxr::GfVec3f color;
|
||||
if (color_attr.Get(&color, motionSampleTime)) {
|
||||
blight->r = color[0];
|
||||
|
|
|
@ -18,7 +18,13 @@
|
|||
#include <pxr/usd/usdGeom/nurbsCurves.h>
|
||||
#include <pxr/usd/usdGeom/scope.h>
|
||||
#include <pxr/usd/usdGeom/xform.h>
|
||||
#include <pxr/usd/usdLux/light.h>
|
||||
|
||||
#if PXR_VERSION >= 2111
|
||||
# include <pxr/usd/usdLux/boundableLightBase.h>
|
||||
# include <pxr/usd/usdLux/nonboundableLightBase.h>
|
||||
#else
|
||||
# include <pxr/usd/usdLux/light.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
@ -55,7 +61,12 @@ USDPrimReader *USDStageReader::create_reader_if_allowed(const pxr::UsdPrim &prim
|
|||
if (params_.import_meshes && prim.IsA<pxr::UsdGeomMesh>()) {
|
||||
return new USDMeshReader(prim, params_, settings_);
|
||||
}
|
||||
#if PXR_VERSION >= 2111
|
||||
if (params_.import_lights && (prim.IsA<pxr::UsdLuxBoundableLightBase>() ||
|
||||
prim.IsA<pxr::UsdLuxNonboundableLightBase>())) {
|
||||
#else
|
||||
if (params_.import_lights && prim.IsA<pxr::UsdLuxLight>()) {
|
||||
#endif
|
||||
return new USDLightReader(prim, params_, settings_);
|
||||
}
|
||||
if (params_.import_volumes && prim.IsA<pxr::UsdVolVolume>()) {
|
||||
|
@ -82,7 +93,11 @@ USDPrimReader *USDStageReader::create_reader(const pxr::UsdPrim &prim)
|
|||
if (prim.IsA<pxr::UsdGeomMesh>()) {
|
||||
return new USDMeshReader(prim, params_, settings_);
|
||||
}
|
||||
#if PXR_VERSION >= 2111
|
||||
if (prim.IsA<pxr::UsdLuxBoundableLightBase>() || prim.IsA<pxr::UsdLuxNonboundableLightBase>()) {
|
||||
#else
|
||||
if (prim.IsA<pxr::UsdLuxLight>()) {
|
||||
#endif
|
||||
return new USDLightReader(prim, params_, settings_);
|
||||
}
|
||||
if (prim.IsA<pxr::UsdVolVolume>()) {
|
||||
|
|
|
@ -131,7 +131,7 @@ bool USDXformReader::is_root_xform_prim() const
|
|||
return false;
|
||||
}
|
||||
|
||||
if (prim_.IsInMaster()) {
|
||||
if (prim_.IsInPrototype()) {
|
||||
/* We don't consider prototypes to be root prims,
|
||||
* because we never want to apply global scaling
|
||||
* or rotations to the prototypes themselves. */
|
||||
|
|
|
@ -33,7 +33,12 @@ void USDLightWriter::do_write(HierarchyContext &context)
|
|||
pxr::UsdTimeCode timecode = get_export_time_code();
|
||||
|
||||
Light *light = static_cast<Light *>(context.object->data);
|
||||
pxr::UsdLuxLight usd_light;
|
||||
#if PXR_VERSION >= 2111
|
||||
pxr::UsdLuxLightAPI usd_light_api;
|
||||
#else
|
||||
pxr::UsdLuxLight usd_light_api;
|
||||
|
||||
#endif
|
||||
|
||||
switch (light->type) {
|
||||
case LA_AREA:
|
||||
|
@ -42,21 +47,33 @@ void USDLightWriter::do_write(HierarchyContext &context)
|
|||
case LA_AREA_ELLIPSE: { /* An ellipse light will deteriorate into a disk light. */
|
||||
pxr::UsdLuxDiskLight disk_light = pxr::UsdLuxDiskLight::Define(stage, usd_path);
|
||||
disk_light.CreateRadiusAttr().Set(light->area_size, timecode);
|
||||
usd_light = disk_light;
|
||||
#if PXR_VERSION >= 2111
|
||||
usd_light_api = disk_light.LightAPI();
|
||||
#else
|
||||
usd_light_api = disk_light;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case LA_AREA_RECT: {
|
||||
pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path);
|
||||
rect_light.CreateWidthAttr().Set(light->area_size, timecode);
|
||||
rect_light.CreateHeightAttr().Set(light->area_sizey, timecode);
|
||||
usd_light = rect_light;
|
||||
#if PXR_VERSION >= 2111
|
||||
usd_light_api = rect_light.LightAPI();
|
||||
#else
|
||||
usd_light_api = rect_light;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case LA_AREA_SQUARE: {
|
||||
pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path);
|
||||
rect_light.CreateWidthAttr().Set(light->area_size, timecode);
|
||||
rect_light.CreateHeightAttr().Set(light->area_size, timecode);
|
||||
usd_light = rect_light;
|
||||
#if PXR_VERSION >= 2111
|
||||
usd_light_api = rect_light.LightAPI();
|
||||
#else
|
||||
usd_light_api = rect_light;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -64,12 +81,23 @@ void USDLightWriter::do_write(HierarchyContext &context)
|
|||
case LA_LOCAL: {
|
||||
pxr::UsdLuxSphereLight sphere_light = pxr::UsdLuxSphereLight::Define(stage, usd_path);
|
||||
sphere_light.CreateRadiusAttr().Set(light->area_size, timecode);
|
||||
usd_light = sphere_light;
|
||||
#if PXR_VERSION >= 2111
|
||||
usd_light_api = sphere_light.LightAPI();
|
||||
#else
|
||||
usd_light_api = sphere_light;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case LA_SUN:
|
||||
usd_light = pxr::UsdLuxDistantLight::Define(stage, usd_path);
|
||||
case LA_SUN: {
|
||||
pxr::UsdLuxDistantLight distant_light = pxr::UsdLuxDistantLight::Define(stage, usd_path);
|
||||
/* TODO(makowalski): set angle attribute here. */
|
||||
#if PXR_VERSION >= 2111
|
||||
usd_light_api = distant_light.LightAPI();
|
||||
#else
|
||||
usd_light_api = distant_light;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_msg(0, "is_supported() returned true for unsupported light type");
|
||||
}
|
||||
|
@ -85,10 +113,10 @@ void USDLightWriter::do_write(HierarchyContext &context)
|
|||
else {
|
||||
usd_intensity = light->energy / 100.0f;
|
||||
}
|
||||
usd_light.CreateIntensityAttr().Set(usd_intensity, timecode);
|
||||
usd_light_api.CreateIntensityAttr().Set(usd_intensity, timecode);
|
||||
|
||||
usd_light.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode);
|
||||
usd_light.CreateSpecularAttr().Set(light->spec_fac, timecode);
|
||||
usd_light_api.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode);
|
||||
usd_light_api.CreateSpecularAttr().Set(light->spec_fac, timecode);
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
|
|
@ -163,7 +163,7 @@ void create_usd_preview_surface_material(const USDExporterContext &usd_export_co
|
|||
created_shader = create_usd_preview_shader(usd_export_context, usd_material, input_node);
|
||||
|
||||
preview_surface.CreateInput(input_spec.input_name, input_spec.input_type)
|
||||
.ConnectToSource(created_shader, input_spec.source_name);
|
||||
.ConnectToSource(created_shader.ConnectableAPI(), input_spec.source_name);
|
||||
}
|
||||
else if (input_spec.set_default_value) {
|
||||
/* Set hardcoded value. */
|
||||
|
@ -217,7 +217,7 @@ void create_usd_viewport_material(const USDExporterContext &usd_export_context,
|
|||
shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic);
|
||||
|
||||
/* Connect the shader and the material together. */
|
||||
usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface);
|
||||
usd_material.CreateSurfaceOutput().ConnectToSource(shader.ConnectableAPI(), usdtokens::surface);
|
||||
}
|
||||
|
||||
/* Return USD Preview Surface input map singleton. */
|
||||
|
@ -293,12 +293,12 @@ static void create_uvmap_shader(const USDExporterContext &usd_export_context,
|
|||
uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token)
|
||||
.Set(pxr::TfToken(uv_set));
|
||||
usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
|
||||
.ConnectToSource(uv_shader, usdtokens::result);
|
||||
.ConnectToSource(uv_shader.ConnectableAPI(), usdtokens::result);
|
||||
}
|
||||
else {
|
||||
uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token).Set(default_uv);
|
||||
usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
|
||||
.ConnectToSource(uv_shader, usdtokens::result);
|
||||
.ConnectToSource(uv_shader.ConnectableAPI(), usdtokens::result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +313,7 @@ static void create_uvmap_shader(const USDExporterContext &usd_export_context,
|
|||
if (uv_shader.GetPrim().IsValid()) {
|
||||
uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->Token).Set(default_uv);
|
||||
usd_tex_shader.CreateInput(usdtokens::st, pxr::SdfValueTypeNames->Float2)
|
||||
.ConnectToSource(uv_shader, usdtokens::result);
|
||||
.ConnectToSource(uv_shader.ConnectableAPI(), usdtokens::result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -488,7 +488,7 @@ static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &u
|
|||
case SH_NODE_BSDF_DIFFUSE:
|
||||
case SH_NODE_BSDF_PRINCIPLED: {
|
||||
shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface));
|
||||
material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface);
|
||||
material.CreateSurfaceOutput().ConnectToSource(shader.ConnectableAPI(), usdtokens::surface);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Obj
|
|||
{
|
||||
/* We need to copy the object because it may be in temporary space. */
|
||||
Object *obj_eval = DEG_get_evaluated_object(depsgraph, mesh_object);
|
||||
export_object_eval_ = *obj_eval;
|
||||
export_object_eval_ = dna::shallow_copy(*obj_eval);
|
||||
export_mesh_eval_ = export_params.apply_modifiers ?
|
||||
BKE_object_get_evaluated_mesh(&export_object_eval_) :
|
||||
BKE_object_get_pre_modified_mesh(&export_object_eval_);
|
||||
|
|
|
@ -28,6 +28,7 @@ typedef enum CurveType {
|
|||
CURVE_TYPE_BEZIER = 2,
|
||||
CURVE_TYPE_NURBS = 3,
|
||||
} CurveType;
|
||||
#define CURVE_TYPES_NUM 4
|
||||
|
||||
typedef enum HandleType {
|
||||
/** The handle can be moved anywhere, and doesn't influence the point's other handle. */
|
||||
|
|
|
@ -46,3 +46,100 @@
|
|||
|
||||
/* non-id name variables should use this length */
|
||||
#define MAX_NAME 64
|
||||
|
||||
/* #DNA_DEFINE_CXX_METHODS is used to define C++ methods which are needed for proper/safe resource
|
||||
* management, making unsafe (from an ownership perspective: i.e. pointers which sometimes needs to
|
||||
* be set to nullptr on copy, sometimes needs to be dupalloc-ed) operations explicit, and taking
|
||||
* care of compiler specific warnings when dealing with members marked with DNA_DEPRECATED.
|
||||
*
|
||||
* The `class_name` argument is to match the structure name the macro is used from.
|
||||
*
|
||||
* Typical usage example:
|
||||
*
|
||||
* typedef struct Object {
|
||||
* DNA_DEFINE_CXX_METHODS(Object)
|
||||
* } Object;
|
||||
*/
|
||||
#ifndef __cplusplus
|
||||
# define DNA_DEFINE_CXX_METHODS(class_name)
|
||||
#else
|
||||
|
||||
/* Forward-declared here since there is no simple header file to be pulled for this functionality.
|
||||
* Avoids pulling `string.h` from this header to get access to #memcpy. */
|
||||
extern "C" void _DNA_internal_memcpy(void *dst, const void *src, size_t size);
|
||||
extern "C" void _DNA_internal_memzero(void *dst, size_t size);
|
||||
|
||||
namespace blender::dna::internal {
|
||||
|
||||
template<class T> class ShallowDataConstRef {
|
||||
public:
|
||||
constexpr explicit ShallowDataConstRef(const T &ref) : ref_(ref)
|
||||
{
|
||||
}
|
||||
|
||||
inline const T *get_pointer() const
|
||||
{
|
||||
return &ref_;
|
||||
}
|
||||
|
||||
private:
|
||||
const T &ref_;
|
||||
};
|
||||
|
||||
} // namespace blender::dna::internal
|
||||
|
||||
# define DNA_DEFINE_CXX_METHODS(class_name) \
|
||||
class_name() = default; \
|
||||
~class_name() = default; \
|
||||
/* Delete copy and assignment, which are not safe for resource ownership. */ \
|
||||
class_name(const class_name &other) = delete; \
|
||||
class_name(class_name &&other) noexcept = delete; \
|
||||
class_name &operator=(const class_name &other) = delete; \
|
||||
class_name &operator=(class_name &&other) = delete; \
|
||||
/* Support for shallow copy. */ \
|
||||
/* NOTE: Calling the default constructor works-around deprecated warning generated by GCC. */ \
|
||||
class_name(const blender::dna::internal::ShallowDataConstRef<class_name> ref) : class_name() \
|
||||
{ \
|
||||
_DNA_internal_memcpy(this, ref.get_pointer(), sizeof(class_name)); \
|
||||
} \
|
||||
class_name &operator=(const blender::dna::internal::ShallowDataConstRef<class_name> ref) \
|
||||
{ \
|
||||
if (this != ref.get_pointer()) { \
|
||||
_DNA_internal_memcpy(this, ref.get_pointer(), sizeof(class_name)); \
|
||||
} \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
namespace blender::dna {
|
||||
|
||||
/* Creates shallow copy of the given object.
|
||||
* The entire object is copied as-is using memory copy.
|
||||
*
|
||||
* Typical usage:
|
||||
* Object temp_object = blender::dna::shallow_copy(*input_object);
|
||||
*
|
||||
* From the implementation detail go via copy constructor/assign operator defined in the structure.
|
||||
*/
|
||||
template<class T>
|
||||
[[nodiscard]] inline internal::ShallowDataConstRef<T> shallow_copy(const T &other)
|
||||
{
|
||||
return internal::ShallowDataConstRef(other);
|
||||
}
|
||||
|
||||
/* Fill underlying memory used by DNA object with zeroes. */
|
||||
template<class T> inline void zero_memory(T &object)
|
||||
{
|
||||
/* TODO(sergey): Consider adding static assert for T being a trivial type. */
|
||||
_DNA_internal_memzero(&object, sizeof(T));
|
||||
}
|
||||
|
||||
/* Copy memory from one DNA object to another. */
|
||||
template<class T> inline void copy_memory(T &dst, const T &src)
|
||||
{
|
||||
/* TODO(sergey): Consider adding static assert for T being a trivial type. */
|
||||
_DNA_internal_memcpy(&dst, &src, sizeof(T));
|
||||
}
|
||||
|
||||
} // namespace blender::dna
|
||||
|
||||
#endif
|
||||
|
|
|
@ -193,7 +193,7 @@
|
|||
.vgname = "", \
|
||||
.pass_index = 0, \
|
||||
.flag = GP_SMOOTH_MOD_LOCATION, \
|
||||
.factor = 0.5f, \
|
||||
.factor = 1.0f, \
|
||||
.step = 1, \
|
||||
.layer_pass = 0, \
|
||||
.curve_intensity = NULL, \
|
||||
|
|
|
@ -522,7 +522,7 @@ typedef struct DashGpencilModifierSegment {
|
|||
float radius;
|
||||
float opacity;
|
||||
int mat_nr;
|
||||
int _pad;
|
||||
int flag;
|
||||
} DashGpencilModifierSegment;
|
||||
|
||||
typedef struct DashGpencilModifierData {
|
||||
|
@ -546,6 +546,14 @@ typedef struct DashGpencilModifierData {
|
|||
|
||||
} DashGpencilModifierData;
|
||||
|
||||
typedef enum eDashGpencil_Flag {
|
||||
GP_DASH_INVERT_LAYER = (1 << 0),
|
||||
GP_DASH_INVERT_PASS = (1 << 1),
|
||||
GP_DASH_INVERT_LAYERPASS = (1 << 2),
|
||||
GP_DASH_INVERT_MATERIAL = (1 << 3),
|
||||
GP_DASH_USE_CYCLIC = (1 << 7),
|
||||
} eDashGpencil_Flag;
|
||||
|
||||
typedef struct MirrorGpencilModifierData {
|
||||
GpencilModifierData modifier;
|
||||
struct Object *object;
|
||||
|
@ -750,6 +758,7 @@ typedef enum eSmoothGpencil_Flag {
|
|||
GP_SMOOTH_INVERT_LAYERPASS = (1 << 7),
|
||||
GP_SMOOTH_INVERT_MATERIAL = (1 << 4),
|
||||
GP_SMOOTH_CUSTOM_CURVE = (1 << 8),
|
||||
GP_SMOOTH_KEEP_SHAPE = (1 << 9),
|
||||
} eSmoothGpencil_Flag;
|
||||
|
||||
typedef struct ArmatureGpencilModifierData {
|
||||
|
|
|
@ -233,6 +233,8 @@ enum eObjectLineArt_Flags {
|
|||
};
|
||||
|
||||
typedef struct Object {
|
||||
DNA_DEFINE_CXX_METHODS(Object)
|
||||
|
||||
ID id;
|
||||
/** Animation data (must be immediately after id for utilities to use it). */
|
||||
struct AnimData *adt;
|
||||
|
|
|
@ -308,3 +308,21 @@ const char *DNA_struct_rename_legacy_hack_alias_from_static(const char *name)
|
|||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal helpers for C++
|
||||
* \{ */
|
||||
|
||||
void _DNA_internal_memcpy(void *dst, const void *src, size_t size);
|
||||
void _DNA_internal_memcpy(void *dst, const void *src, const size_t size)
|
||||
{
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
|
||||
void _DNA_internal_memzero(void *dst, size_t size);
|
||||
void _DNA_internal_memzero(void *dst, const size_t size)
|
||||
{
|
||||
memset(dst, 0, size);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -620,6 +620,7 @@ static int preprocess_include(char *maindata, const int maindata_len)
|
|||
int newlen = 0;
|
||||
comment = 0;
|
||||
a = maindata_len;
|
||||
bool skip_until_closing_brace = false;
|
||||
while (a--) {
|
||||
|
||||
if (cp[0] == '/' && cp[1] == '*') {
|
||||
|
@ -646,6 +647,17 @@ static int preprocess_include(char *maindata, const int maindata_len)
|
|||
a -= 13;
|
||||
cp += 13;
|
||||
}
|
||||
else if (match_identifier(cp, "DNA_DEFINE_CXX_METHODS")) {
|
||||
/* single values are skipped already, so decrement 1 less */
|
||||
a -= 21;
|
||||
cp += 21;
|
||||
skip_until_closing_brace = true;
|
||||
}
|
||||
else if (skip_until_closing_brace) {
|
||||
if (cp[0] == ')') {
|
||||
skip_until_closing_brace = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
md[0] = cp[0];
|
||||
md++;
|
||||
|
|
|
@ -1355,7 +1355,8 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
|
|||
/* Smoothing factor for new strokes */
|
||||
prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac");
|
||||
RNA_def_property_range(prop, 0.0, 2.0f);
|
||||
RNA_def_property_range(prop, 0.0, 2.0);
|
||||
RNA_def_property_ui_range(prop, 0.0, 1.0, 10, 3);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Smooth",
|
||||
|
@ -1366,7 +1367,7 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
|
|||
/* Iterations of the Smoothing factor */
|
||||
prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl");
|
||||
RNA_def_property_range(prop, 1, 3);
|
||||
RNA_def_property_range(prop, 0, 100);
|
||||
RNA_def_property_ui_text(prop, "Iterations", "Number of times to smooth newly created strokes");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
|
|
@ -1026,11 +1026,16 @@ static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna)
|
|||
|
||||
prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "step");
|
||||
RNA_def_property_range(prop, 1, 10);
|
||||
RNA_def_property_range(prop, 1, 1000);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Step", "Number of times to apply smooth (high numbers can reduce fps)");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "keep_shape", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_KEEP_SHAPE);
|
||||
RNA_def_property_ui_text(prop, "Keep Shape", "Smooth the details, but keep the overall shape");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_INVERT_LAYER);
|
||||
RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
|
||||
|
@ -3738,6 +3743,11 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna)
|
|||
"Use this index on generated segment. -1 means using the existing material");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_cyclic", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_USE_CYCLIC);
|
||||
RNA_def_property_ui_text(prop, "Use Cyclic", "Enable cyclic on individual stroke dashes");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
srna = RNA_def_struct(brna, "DashGpencilModifierData", "GpencilModifier");
|
||||
RNA_def_struct_ui_text(srna, "Dash Modifier", "Create dot-dash effect for strokes");
|
||||
RNA_def_struct_sdna(srna, "DashGpencilModifierData");
|
||||
|
@ -3789,17 +3799,17 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna)
|
|||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYER);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_INVERT_LAYER);
|
||||
RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_MATERIAL);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_INVERT_MATERIAL);
|
||||
RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_PASS);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_INVERT_PASS);
|
||||
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
|
@ -3810,7 +3820,7 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna)
|
|||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYERPASS);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DASH_INVERT_LAYERPASS);
|
||||
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
|
|
Loading…
Reference in New Issue