WIP: Rewrite asset browser as separate editor #107576

Draft
Julian Eisel wants to merge 95 commits from asset-browser-frontend-split into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
105 changed files with 1724 additions and 1064 deletions
Showing only changes of commit 15ad4334fc - Show all commits

View File

@ -11,7 +11,7 @@ CCL_NAMESPACE_BEGIN
* intersection we'll be comparing against the exact same distances. */
ccl_device_forceinline float intersection_t_offset(const float t)
{
/* This is a simplified version of nextafterf(t, FLT_MAX), only dealing with
/* This is a simplified version of `nextafterf(t, FLT_MAX)`, only dealing with
* non-negative and finite t. */
kernel_assert(t >= 0.0f && isfinite_safe(t));
const uint32_t bits = (t == 0.0f) ? 1 : __float_as_uint(t) + 1;

View File

@ -70,7 +70,7 @@ KERNEL_STRUCT_MEMBER(film, float4, rec709_to_r)
KERNEL_STRUCT_MEMBER(film, float4, rec709_to_g)
KERNEL_STRUCT_MEMBER(film, float4, rec709_to_b)
KERNEL_STRUCT_MEMBER(film, int, is_rec709)
/* Exposuse. */
/* Exposure. */
KERNEL_STRUCT_MEMBER(film, float, exposure)
/* Passed used. */
KERNEL_STRUCT_MEMBER(film, int, pass_flag)

View File

@ -1168,7 +1168,7 @@ typedef struct KernelData {
uint max_shaders;
uint volume_stack_size;
/* Always dynamic data mambers. */
/* Always dynamic data members. */
KernelCamera cam;
KernelBake bake;
KernelTables tables;

View File

@ -3852,11 +3852,15 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
input->relative_pointer = nullptr;
}
if (input->locked_pointer) {
/* Potentially add a motion event so the applicate has updated X/Y coordinates. */
int32_t xy_motion[2] = {0, 0};
bool xy_motion_create_event = false;
/* Request location to restore to. */
if (mode_current == GHOST_kGrabWrap) {
/* Since this call is initiated by Blender, we can be sure the window wasn't closed
* by logic outside this function - as the window was needed to make this call. */
int32_t xy_new[2] = {UNPACK2(input->pointer.xy)};
int32_t xy_next[2] = {UNPACK2(input->pointer.xy)};
GHOST_Rect bounds_scale;
@ -3865,21 +3869,18 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
bounds_scale.m_r = wl_fixed_from_int(wrap_bounds->m_r) / scale;
bounds_scale.m_b = wl_fixed_from_int(wrap_bounds->m_b) / scale;
bounds_scale.wrapPoint(UNPACK2(xy_new), 0, wrap_axis);
bounds_scale.wrapPoint(UNPACK2(xy_next), 0, wrap_axis);
/* Push an event so the new location is registered. */
if ((xy_new[0] != input->pointer.xy[0]) || (xy_new[1] != input->pointer.xy[1])) {
input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
GHOST_kEventCursorMove,
ghost_wl_surface_user_data(surface),
wl_fixed_to_int(scale * xy_new[0]),
wl_fixed_to_int(scale * xy_new[1]),
GHOST_TABLET_DATA_NONE));
if ((xy_next[0] != input->pointer.xy[0]) || (xy_next[1] != input->pointer.xy[1])) {
xy_motion[0] = xy_next[0];
xy_motion[1] = xy_next[1];
xy_motion_create_event = true;
}
input->pointer.xy[0] = xy_new[0];
input->pointer.xy[1] = xy_new[1];
input->pointer.xy[0] = xy_next[0];
input->pointer.xy[1] = xy_next[1];
zwp_locked_pointer_v1_set_cursor_position_hint(input->locked_pointer, UNPACK2(xy_new));
zwp_locked_pointer_v1_set_cursor_position_hint(input->locked_pointer, UNPACK2(xy_next));
wl_surface_commit(surface);
}
else if (mode_current == GHOST_kGrabHide) {
@ -3891,6 +3892,13 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
};
zwp_locked_pointer_v1_set_cursor_position_hint(input->locked_pointer, UNPACK2(xy_next));
wl_surface_commit(surface);
/* NOTE(@campbellbarton): The new cursor position is a hint,
* it's possible the hint is ignored. It doesn't seem like there is a good way to
* know if the hint will be used or not, at least not immediately. */
xy_motion[0] = xy_next[0];
xy_motion[1] = xy_next[1];
xy_motion_create_event = true;
}
}
#ifdef USE_GNOME_CONFINE_HACK
@ -3903,6 +3911,15 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
}
#endif
if (xy_motion_create_event) {
input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
GHOST_kEventCursorMove,
ghost_wl_surface_user_data(surface),
wl_fixed_to_int(scale * xy_motion[0]),
wl_fixed_to_int(scale * xy_motion[1]),
GHOST_TABLET_DATA_NONE));
}
zwp_locked_pointer_v1_destroy(input->locked_pointer);
input->locked_pointer = nullptr;
}

View File

@ -410,6 +410,9 @@ class RENDER_PT_eevee_next_sampling(RenderButtonsPanel, Panel):
col.prop(props, "taa_render_samples", text="Render")
col.prop(props, "taa_samples", text="Viewport")
col = layout.column()
col.prop(props, "use_taa_reprojection")
class RENDER_PT_eevee_indirect_lighting(RenderButtonsPanel, Panel):
bl_label = "Indirect Lighting"

View File

@ -95,8 +95,10 @@ class SPREADSHEET_HT_header(bpy.types.Header):
obj = root_context.object
if obj is None:
return False
if obj.type != 'MESH' or obj.mode != 'EDIT':
return False
if obj.type == 'MESH':
return obj.mode == 'EDIT'
if obj.type == 'CURVES':
return obj.mode == 'SCULPT_CURVES'
return True

View File

@ -604,7 +604,7 @@ class VIEW3D_HT_header(Header):
else:
if (object_mode not in {
'SCULPT', 'SCULPT_CURVES', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT',
'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL',
'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL',
}) or has_pose_mode:
show_snap = True
else:

View File

@ -537,10 +537,7 @@ class MutableAttributeAccessor : public AttributeAccessor {
* Get a writable attribute or none if it does not exist.
* Make sure to call #finish after changes are done.
*/
GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id)
{
return fn_->lookup_for_write(owner_, attribute_id);
}
GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id);
/**
* Get a writable attribute or non if it does not exist.
@ -677,8 +674,8 @@ struct AttributeTransferData {
* data-blocks of the same type.
*/
Vector<AttributeTransferData> retrieve_attributes_for_transfer(
const bke::AttributeAccessor &src_attributes,
bke::MutableAttributeAccessor &dst_attributes,
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes,
eAttrDomainMask domain_mask,
const Set<std::string> &skip = {});

View File

@ -968,6 +968,49 @@ void MutableAttributeAccessor::remove_anonymous()
}
}
/**
* Debug utility that checks whether the #finish function of an #AttributeWriter has been called.
*/
#ifdef DEBUG
struct FinishCallChecker {
std::string name;
bool finish_called = false;
std::function<void()> real_finish_fn;
~FinishCallChecker()
{
if (!this->finish_called) {
std::cerr << "Forgot to call `finish()` for '" << this->name << "'.\n";
}
}
};
#endif
GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef &attribute_id)
{
GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id);
/* Check that the #finish method is called in debug builds. */
#ifdef DEBUG
if (attribute) {
auto checker = std::make_shared<FinishCallChecker>();
if (attribute_id.is_named()) {
checker->name = attribute_id.name();
}
else {
checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id());
}
checker->real_finish_fn = attribute.tag_modified_fn;
attribute.tag_modified_fn = [checker]() {
if (checker->real_finish_fn) {
checker->real_finish_fn();
}
checker->finish_called = true;
};
}
#endif
return attribute;
}
GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
@ -1012,8 +1055,8 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span
}
Vector<AttributeTransferData> retrieve_attributes_for_transfer(
const bke::AttributeAccessor &src_attributes,
bke::MutableAttributeAccessor &dst_attributes,
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes,
const eAttrDomainMask domain_mask,
const Set<std::string> &skip)
{

View File

@ -1070,21 +1070,6 @@ bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
return true;
}
static void *ensure_customdata_layer(CustomData &custom_data,
const StringRefNull name,
const eCustomDataType data_type,
const int tot_elements)
{
for (const int other_layer_i : IndexRange(custom_data.totlayer)) {
CustomDataLayer &new_layer = custom_data.layers[other_layer_i];
if (name == StringRef(new_layer.name)) {
return new_layer.data;
}
}
return CustomData_add_layer_named(
&custom_data, data_type, CD_DEFAULT, nullptr, tot_elements, name.c_str());
}
static void copy_between_buffers(const CPPType &type,
const void *src_buffer,
void *dst_buffer,
@ -1180,56 +1165,37 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
[&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); },
/* Copy over point attributes. */
[&]() {
const CustomData &old_point_data = curves.point_data;
CustomData &new_point_data = new_curves.point_data;
for (const int layer_i : IndexRange(old_point_data.totlayer)) {
const CustomDataLayer &old_layer = old_point_data.layers[layer_i];
const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
void *dst_buffer = ensure_customdata_layer(
new_point_data, old_layer.name, data_type, new_point_count);
threading::parallel_for(
copy_point_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
for (const int range_i : ranges_range) {
const IndexRange src_range = copy_point_ranges[range_i];
copy_between_buffers(type,
old_layer.data,
dst_buffer,
src_range,
{copy_point_range_dst_offsets[range_i], src_range.size()});
}
});
for (auto &attribute : bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) {
threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) {
for (const int range_i : range) {
const IndexRange src_range = copy_point_ranges[range_i];
copy_between_buffers(attribute.src.type(),
attribute.src.data(),
attribute.dst.span.data(),
src_range,
{copy_point_range_dst_offsets[range_i], src_range.size()});
}
});
attribute.dst.finish();
}
},
/* Copy over curve attributes.
* In some cases points are just dissolved, so the the number of
* curves will be the same. That could be optimized in the future. */
[&]() {
const CustomData &old_curve_data = curves.curve_data;
CustomData &new_curve_data = new_curves.curve_data;
for (const int layer_i : IndexRange(old_curve_data.totlayer)) {
const CustomDataLayer &old_layer = old_curve_data.layers[layer_i];
const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
void *dst_buffer = ensure_customdata_layer(
new_curve_data, old_layer.name, data_type, new_curve_count);
for (auto &attribute : bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) {
if (new_curves.curves_num() == curves.curves_num()) {
type.copy_construct_n(old_layer.data, dst_buffer, new_curves.curves_num());
attribute.dst.span.copy_from(attribute.src);
}
else {
copy_with_map({type, old_layer.data, curves.curves_num()},
new_curve_orig_indices,
{type, dst_buffer, new_curves.curves_num()});
copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span);
}
attribute.dst.finish();
}
});
new_curves.update_curve_types();
return new_curves;
}
@ -1296,55 +1262,37 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
},
/* Copy over point attributes. */
[&]() {
const CustomData &old_point_data = curves.point_data;
CustomData &new_point_data = new_curves.point_data;
for (const int layer_i : IndexRange(old_point_data.totlayer)) {
const CustomDataLayer &old_layer = old_point_data.layers[layer_i];
const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
void *dst_buffer = ensure_customdata_layer(
new_point_data, old_layer.name, data_type, new_tot_points);
threading::parallel_for(
old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
for (const int range_i : ranges_range) {
copy_between_buffers(type,
old_layer.data,
dst_buffer,
old_point_ranges[range_i],
new_point_ranges[range_i]);
}
});
for (auto &attribute : bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) {
threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) {
for (const int range_i : range) {
copy_between_buffers(attribute.src.type(),
attribute.src.data(),
attribute.dst.span.data(),
old_point_ranges[range_i],
new_point_ranges[range_i]);
}
});
attribute.dst.finish();
}
},
/* Copy over curve attributes. */
[&]() {
const CustomData &old_curve_data = curves.curve_data;
CustomData &new_curve_data = new_curves.curve_data;
for (const int layer_i : IndexRange(old_curve_data.totlayer)) {
const CustomDataLayer &old_layer = old_curve_data.layers[layer_i];
const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
void *dst_buffer = ensure_customdata_layer(
new_curve_data, old_layer.name, data_type, new_tot_curves);
threading::parallel_for(
old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
for (const int range_i : ranges_range) {
copy_between_buffers(type,
old_layer.data,
dst_buffer,
old_curve_ranges[range_i],
new_curve_ranges[range_i]);
}
});
for (auto &attribute : bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) {
threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) {
for (const int range_i : range) {
copy_between_buffers(attribute.src.type(),
attribute.src.data(),
attribute.dst.span.data(),
old_curve_ranges[range_i],
new_curve_ranges[range_i]);
}
});
attribute.dst.finish();
}
});
new_curves.update_curve_types();
return new_curves;
}

View File

@ -4443,9 +4443,52 @@ bool CustomData_verify_versions(CustomData *data, int index)
return keeplayer;
}
static bool CustomData_layer_ensure_data_exists(CustomDataLayer *layer, size_t count)
{
BLI_assert(layer);
const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
BLI_assert(typeInfo);
if (layer->data || count == 0) {
return false;
}
switch (layer->type) {
/* When more instances of corrupt files are found, add them here. */
case CD_PROP_BOOL: /* See T84935. */
case CD_MLOOPUV: /* See T90620. */
layer->data = MEM_calloc_arrayN(count, typeInfo->size, layerType_getName(layer->type));
BLI_assert(layer->data);
if (typeInfo->set_default) {
typeInfo->set_default(layer->data, count);
}
return true;
break;
case CD_MTEXPOLY:
/* TODO: Investigate multiple test failures on cycles, e.g. cycles_shadow_catcher_cpu. */
break;
default:
/* Log an error so we can collect instances of bad files. */
CLOG_WARN(&LOG, "CustomDataLayer->data is NULL for type %d.", layer->type);
break;
}
return false;
}
bool CustomData_layer_validate(CustomDataLayer *layer, const uint totitems, const bool do_fixes)
{
BLI_assert(layer);
const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
BLI_assert(typeInfo);
if (do_fixes) {
CustomData_layer_ensure_data_exists(layer, totitems);
}
BLI_assert((totitems == 0) || layer->data);
BLI_assert(MEM_allocN_len(layer->data) >= totitems * typeInfo->size);
if (typeInfo->validate != nullptr) {
return typeInfo->validate(layer->data, totitems, do_fixes);
@ -5206,16 +5249,15 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
if (CustomData_verify_versions(data, i)) {
BLO_read_data_address(reader, &layer->data);
if (layer->data == nullptr && count > 0 && layer->type == CD_PROP_BOOL) {
/* Usually this should never happen, except when a custom data layer has not been written
* to a file correctly. */
CLOG_WARN(&LOG, "Reallocating custom data layer that was not saved correctly.");
const LayerTypeInfo *info = layerType_getInfo(layer->type);
layer->data = MEM_calloc_arrayN((size_t)count, info->size, layerType_getName(layer->type));
if (info->set_default) {
info->set_default(layer->data, count);
}
if (CustomData_layer_ensure_data_exists(layer, count)) {
/* Under normal operations, this shouldn't happen, but...
* For a CD_PROP_BOOL example, see T84935.
* For a CD_MLOOPUV example, see T90620. */
CLOG_WARN(&LOG,
"Allocated custom data layer that was not saved correctly for layer->type = %d.",
layer->type);
}
if (layer->type == CD_MDISPS) {
blend_read_mdisps(
reader, count, static_cast<MDisps *>(layer->data), layer->flag & CD_FLAG_EXTERNAL);

View File

@ -1038,8 +1038,6 @@ static void obstacles_from_mesh(Object *coll_ob,
/* Transform mesh vertices to domain grid space for fast lookups.
* This is valid because the mesh is copied above. */
BKE_mesh_vertex_normals_ensure(me);
float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(me);
for (i = 0; i < numverts; i++) {
float co[3];
@ -1047,11 +1045,6 @@ static void obstacles_from_mesh(Object *coll_ob,
mul_m4_v3(coll_ob->obmat, mvert[i].co);
manta_pos_to_cell(fds, mvert[i].co);
/* Vertex normal. */
mul_mat3_m4_v3(coll_ob->obmat, vert_normals[i]);
mul_mat3_m4_v3(fds->imat, vert_normals[i]);
normalize_v3(vert_normals[i]);
/* Vertex velocity. */
add_v3fl_v3fl_v3i(co, mvert[i].co, fds->shift);
if (has_velocity) {

View File

@ -73,18 +73,10 @@ static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data)
}
}
static void library_blend_write(struct BlendWriter *UNUSED(writer),
ID *id,
const void *UNUSED(id_address))
{
Library *lib = (Library *)id;
library_runtime_reset(lib);
}
static void library_blend_read_data(struct BlendDataReader *UNUSED(reader), ID *id)
{
Library *lib = (Library *)id;
library_runtime_reset(lib);
lib->runtime.name_map = NULL;
}
IDTypeInfo IDType_ID_LI = {
@ -107,7 +99,7 @@ IDTypeInfo IDType_ID_LI = {
.foreach_path = library_foreach_path,
.owner_get = NULL,
.blend_write = library_blend_write,
.blend_write = NULL,
.blend_read_data = library_blend_read_data,
.blend_read_lib = NULL,
.blend_read_expand = NULL,

View File

@ -9,7 +9,6 @@
#include "BLI_math_base.hh"
#include "BLI_math_color.hh"
#include "BLI_math_vector.hh"
#include "BLI_vector.hh"
namespace blender::length_parameterize {
@ -42,10 +41,10 @@ void accumulate_lengths(const Span<T> values, const bool cyclic, MutableSpan<flo
}
template<typename T>
inline void linear_interpolation(const Span<T> src,
const Span<int> indices,
const Span<float> factors,
MutableSpan<T> dst)
inline void interpolate(const Span<T> src,
const Span<int> indices,
const Span<float> factors,
MutableSpan<T> dst)
{
BLI_assert(indices.size() == factors.size());
BLI_assert(indices.size() == dst.size());
@ -75,6 +74,7 @@ struct SampleSegmentHint {
/**
* \param accumulated_segment_lengths: Lengths of individual segments added up.
* Each value describes the total length at the end of the segment following a point.
* \param sample_length: The position to sample at.
* \param r_segment_index: Returns the index of the segment that #sample_length is in.
* \param r_factor: Returns the position within the segment.
@ -82,7 +82,7 @@ struct SampleSegmentHint {
* \note #sample_length must not be outside of any segment.
*/
inline void sample_at_length(const Span<float> accumulated_segment_lengths,
float sample_length,
const float sample_length,
int &r_segment_index,
float &r_factor,
SampleSegmentHint *hint = nullptr)
@ -117,7 +117,7 @@ inline void sample_at_length(const Span<float> accumulated_segment_lengths,
const float segment_start = prev_point_index == 0 ? 0.0f : lengths[prev_point_index - 1];
const float segment_end = lengths[prev_point_index];
const float segment_length = segment_end - segment_start;
const float segment_length_inv = safe_divide(1.0f, segment_length);
const float segment_length_inv = math::safe_divide(1.0f, segment_length);
const float length_in_segment = sample_length - segment_start;
const float factor = length_in_segment * segment_length_inv;

View File

@ -404,7 +404,7 @@ GMutableVArraySpan::~GMutableVArraySpan()
if (varray_) {
if (show_not_saved_warning_) {
if (!save_has_been_called_) {
std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n";
std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
}
}
}

View File

@ -32,7 +32,7 @@ TEST(length_parameterize, FloatSimple)
Array<float> factors(4);
sample_uniform(lengths, true, indices, factors);
Array<float> results(4);
linear_interpolation<float>(values, indices, factors, results);
interpolate<float>(values, indices, factors, results);
Array<float> expected({
0.0f,
1.33333f,
@ -54,7 +54,7 @@ TEST(length_parameterize, Float)
Array<float> factors(20);
sample_uniform(lengths, true, indices, factors);
Array<float> results(20);
linear_interpolation<float>(values, indices, factors, results);
interpolate<float>(values, indices, factors, results);
Array<float> expected({
1.0f, 1.47368f, 1.94737f, 2.42105f, 2.89474f, 3.36842f, 3.84211f,
4.31579f, 4.78947f, 5.26316f, 5.73684f, 6.21053f, 6.68421f, 7.1579f,
@ -75,7 +75,7 @@ TEST(length_parameterize, Float2)
Array<float> factors(12);
sample_uniform(lengths, true, indices, factors);
Array<float2> results(12);
linear_interpolation<float2>(values, indices, factors, results);
interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.0f, 0.0f},
{0.272727f, 0.0f},
@ -105,7 +105,7 @@ TEST(length_parameterize, Float2Cyclic)
Array<float> factors(12);
sample_uniform(lengths, false, indices, factors);
Array<float2> results(12);
linear_interpolation<float2>(values, indices, factors, results);
interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.0f, 0.0f},
{0.333333f, 0.0f},
@ -135,7 +135,7 @@ TEST(length_parameterize, LineMany)
Array<float> factors(5007);
sample_uniform(lengths, true, indices, factors);
Array<float> results(5007);
linear_interpolation<float>(values, indices, factors, results);
interpolate<float>(values, indices, factors, results);
Array<float> expected({
1.9962f, 1.9964f, 1.9966f, 1.9968f, 1.997f, 1.9972f, 1.9974f, 1.9976f, 1.9978f, 1.998f,
1.9982f, 1.9984f, 1.9986f, 1.9988f, 1.999f, 1.9992f, 1.9994f, 1.9996f, 1.9998f, 2.0f,
@ -154,7 +154,7 @@ TEST(length_parameterize, CyclicMany)
Array<float> factors(5007);
sample_uniform(lengths, false, indices, factors);
Array<float2> results(5007);
linear_interpolation<float2>(values, indices, factors, results);
interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0, 0.0159776}, {0, 0.0151787}, {0, 0.0143797}, {0, 0.013581}, {0, 0.0127821},
{0, 0.0119832}, {0, 0.0111842}, {0, 0.0103855}, {0, 0.00958657}, {0, 0.00878763},
@ -178,7 +178,7 @@ TEST(length_parameterize, InterpolateColor)
Array<float> factors(10);
sample_uniform(lengths, false, indices, factors);
Array<ColorGeometry4f> results(10);
linear_interpolation<ColorGeometry4f>(colors, indices, factors, results);
interpolate<ColorGeometry4f>(colors, indices, factors, results);
Array<ColorGeometry4f> expected({
{0, 0, 0, 1},
{0.4, 0, 0, 1},
@ -209,8 +209,7 @@ TEST(length_parameterize, ArbitraryFloatSimple)
Array<float> factors(4);
sample_at_lengths(lengths, sample_lengths, indices, factors);
Array<float> results(4);
linear_interpolation<float>(values, indices, factors, results);
results.as_span().print_as_lines("results");
interpolate<float>(values, indices, factors, results);
Array<float> expected({
0.5f,
1.5f,
@ -233,8 +232,7 @@ TEST(length_parameterize, ArbitraryFloat2)
Array<float> factors(12);
sample_at_lengths(lengths, sample_lengths, indices, factors);
Array<float2> results(12);
linear_interpolation<float2>(values, indices, factors, results);
results.as_span().print_as_lines("results");
interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.5f, 0.0f},
{1.0f, 0.5f},

View File

@ -970,10 +970,15 @@ static void write_libraries(WriteData *wd, Main *main)
if (found_one) {
/* Not overridable. */
void *runtime_name_data = main->curlib->runtime.name_map;
main->curlib->runtime.name_map = NULL;
BlendWriter writer = {wd};
writestruct(wd, ID_LI, Library, 1, main->curlib);
BKE_id_blend_write(&writer, &main->curlib->id);
main->curlib->runtime.name_map = runtime_name_data;
if (main->curlib->packedfile) {
BKE_packedfile_blend_write(&writer, main->curlib->packedfile);
if (wd->use_memfile == false) {

View File

@ -67,6 +67,8 @@ set(SRC
intern/eval/deg_eval_runtime_backup_sound.cc
intern/eval/deg_eval_runtime_backup_volume.cc
intern/eval/deg_eval_stats.cc
intern/eval/deg_eval_visibility.cc
intern/eval/deg_eval_visibility.h
intern/node/deg_node.cc
intern/node/deg_node_component.cc
intern/node/deg_node_factory.cc

View File

@ -29,6 +29,7 @@
#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@ -71,10 +72,15 @@ bool DepsgraphBuilder::need_pull_base_into_graph(const Base *base)
if (base->flag & base_flag) {
return true;
}
/* More involved check: since we don't support dynamic changes in dependency graph topology and
* all visible objects are to be part of dependency graph, we pull all objects which has animated
* visibility. */
const Object *object = base->object;
return is_object_visibility_animated(base->object);
}
bool DepsgraphBuilder::is_object_visibility_animated(const Object *object)
{
AnimatedPropertyID property_id;
if (graph_->mode == DAG_EVAL_VIEWPORT) {
property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_viewport");
@ -127,84 +133,9 @@ bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, cons
/** \name Builder Finalizer.
* \{ */
namespace {
void deg_graph_build_flush_visibility(Depsgraph *graph)
{
enum {
DEG_NODE_VISITED = (1 << 0),
};
BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
for (IDNode *id_node : graph->id_nodes) {
for (ComponentNode *comp_node : id_node->components.values()) {
comp_node->affects_directly_visible |= id_node->is_directly_visible;
}
}
for (OperationNode *op_node : graph->operations) {
op_node->custom_flags = 0;
op_node->num_links_pending = 0;
for (Relation *rel : op_node->outlinks) {
if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
++op_node->num_links_pending;
}
}
if (op_node->num_links_pending == 0) {
BLI_stack_push(stack, &op_node);
op_node->custom_flags |= DEG_NODE_VISITED;
}
}
while (!BLI_stack_is_empty(stack)) {
OperationNode *op_node;
BLI_stack_pop(stack, &op_node);
/* Flush layers to parents. */
for (Relation *rel : op_node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
OperationNode *op_from = (OperationNode *)rel->from;
ComponentNode *comp_from = op_from->owner;
const bool target_directly_visible = op_node->owner->affects_directly_visible;
/* Visibility component forces all components of the current ID to be considered as
* affecting directly visible. */
if (comp_from->type == NodeType::VISIBILITY) {
if (target_directly_visible) {
IDNode *id_node_from = comp_from->owner;
for (ComponentNode *comp_node : id_node_from->components.values()) {
comp_node->affects_directly_visible |= target_directly_visible;
}
}
}
else {
comp_from->affects_directly_visible |= target_directly_visible;
}
}
}
/* Schedule parent nodes. */
for (Relation *rel : op_node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
OperationNode *op_from = (OperationNode *)rel->from;
if ((rel->flag & RELATION_FLAG_CYCLIC) == 0) {
BLI_assert(op_from->num_links_pending > 0);
--op_from->num_links_pending;
}
if ((op_from->num_links_pending == 0) && (op_from->custom_flags & DEG_NODE_VISITED) == 0) {
BLI_stack_push(stack, &op_from);
op_from->custom_flags |= DEG_NODE_VISITED;
}
}
}
}
BLI_stack_free(stack);
}
} // namespace
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
{
/* Make sure dependencies of visible ID data-blocks are visible. */
deg_graph_build_flush_visibility(graph);
deg_graph_flush_visibility_flags(graph);
deg_graph_remove_unused_noops(graph);
/* Re-tag IDs for update if it was tagged before the relations

View File

@ -24,6 +24,8 @@ class DepsgraphBuilder {
virtual bool need_pull_base_into_graph(const Base *base);
virtual bool is_object_visibility_animated(const Object *object);
virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const char *bone_name);

View File

@ -26,6 +26,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_curves_types.h"
#include "DNA_effect_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
@ -107,6 +108,7 @@
#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@ -179,11 +181,27 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id)
}
ComponentNode *visibility_component = id_node->add_component(NodeType::VISIBILITY);
OperationNode *visibility_operation = visibility_component->add_operation(
nullptr, OperationCode::VISIBILITY);
OperationNode *visibility_operation;
/* Optimization: currently only objects need a special visibility evaluation. For the rest ID
* types keep the node as a NO-OP so that relations can still be routed, but without penalty
* during the graph evaluation. */
if (id_type == ID_OB) {
visibility_operation = visibility_component->add_operation(
[id_node](::Depsgraph *depsgraph) {
deg_evaluate_object_node_visibility(depsgraph, id_node);
},
OperationCode::VISIBILITY);
}
else {
visibility_operation = visibility_component->add_operation(nullptr,
OperationCode::VISIBILITY);
}
/* Pin the node so that it and its relations are preserved by the unused nodes/relations
* deletion. This is mainly to make it easier to debug visibility. */
visibility_operation->flag |= OperationFlag::DEPSOP_FLAG_PINNED;
visibility_operation->flag |= (OperationFlag::DEPSOP_FLAG_PINNED |
OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
graph_->operations.append(visibility_operation);
}
return id_node;
@ -652,7 +670,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
IDNode *id_node;
if (built_map_.checkIsBuiltAndTag(collection)) {
id_node = find_id_node(&collection->id);
if (is_collection_visible && id_node->is_directly_visible == false &&
if (is_collection_visible && id_node->is_visible_on_build == false &&
id_node->is_collection_fully_expanded == true) {
/* Collection became visible, make sure nested collections and
* objects are poked with the new visibility flag, since they
@ -669,7 +687,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
else {
/* Collection itself. */
id_node = add_id_node(&collection->id);
id_node->is_directly_visible = is_collection_visible;
id_node->is_visible_on_build = is_collection_visible;
build_idproperties(collection->id.properties);
add_operation_node(&collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE);
@ -716,7 +734,7 @@ void DepsgraphNodeBuilder::build_object(int base_index,
build_object_flags(base_index, object, linked_state);
}
id_node->linked_state = max(id_node->linked_state, linked_state);
id_node->is_directly_visible |= is_visible;
id_node->is_visible_on_build |= is_visible;
id_node->has_base |= (base_index != -1);
/* There is no relation path which will connect current object with all the ones which come
@ -735,10 +753,10 @@ void DepsgraphNodeBuilder::build_object(int base_index,
* Probably need to assign that to something non-nullptr, but then the logic here will still be
* somewhat weird. */
if (scene_ != nullptr && object == scene_->camera) {
id_node->is_directly_visible = true;
id_node->is_visible_on_build = true;
}
else {
id_node->is_directly_visible = is_visible;
id_node->is_visible_on_build = is_visible;
}
id_node->has_base |= (base_index != -1);
/* Various flags, flushing from bases/collections. */
@ -1546,8 +1564,14 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
break;
}
case ID_CV: {
Curves *curves_id = reinterpret_cast<Curves *>(obdata);
op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
op_node->set_as_entry();
if (curves_id->surface != nullptr) {
build_object(-1, curves_id->surface, DEG_ID_LINKED_INDIRECTLY, false);
}
break;
}
case ID_PT: {

View File

@ -92,14 +92,20 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene,
int base_index = 0;
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
/* object itself */
if (need_pull_base_into_graph(base)) {
/* NOTE: We consider object visible even if it's currently
* restricted by the base/restriction flags. Otherwise its drivers
* will never be evaluated.
*
* TODO(sergey): Need to go more granular on visibility checks. */
build_object(base_index, base->object, linked_state, true);
base_index++;
if (!need_pull_base_into_graph(base)) {
continue;
}
/* NOTE: We consider object visible even if it's currently
* restricted by the base/restriction flags. Otherwise its drivers
* will never be evaluated.
*
* TODO(sergey): Need to go more granular on visibility checks. */
build_object(base_index, base->object, linked_state, true);
base_index++;
if (!graph_->has_animated_visibility) {
graph_->has_animated_visibility |= is_object_visibility_animated(base->object);
}
}
build_layer_collections(&view_layer->layer_collections);

View File

@ -27,6 +27,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_curves_types.h"
#include "DNA_effect_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
@ -810,6 +811,13 @@ void DepsgraphRelationBuilder::build_object(Object *object)
/* Parameters. */
build_parameters(&object->id);
/* Visibility.
* Evaluate visibility node after the object's base_flags has been updated to the current state
* of collections restrict and object's restrict flags. */
const ComponentKey object_from_layer_entry_key(&object->id, NodeType::OBJECT_FROM_LAYER);
const ComponentKey visibility_key(&object->id, NodeType::VISIBILITY);
add_relation(object_from_layer_entry_key, visibility_key, "Object Visibility");
}
/* NOTE: Implies that the object has base in the current view layer. */
@ -2190,8 +2198,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
* data mask to be used. We add relation here to ensure object is never
* evaluated prior to Scene's CoW is ready. */
OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL);
Relation *rel = add_relation(scene_key, obdata_ubereval_key, "CoW Relation");
rel->flag |= RELATION_FLAG_NO_FLUSH;
add_relation(scene_key, obdata_ubereval_key, "CoW Relation", RELATION_FLAG_NO_FLUSH);
/* Modifiers */
if (object->modifiers.first != nullptr) {
ModifierUpdateDepsgraphContext ctx = {};
@ -2419,8 +2426,16 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
}
break;
}
case ID_CV:
case ID_CV: {
Curves *curves_id = reinterpret_cast<Curves *>(obdata);
if (curves_id->surface != nullptr) {
build_object(curves_id->surface);
/* The relations between the surface and the curves are handled as part of the modifier
* stack building. */
}
break;
}
case ID_PT:
break;
case ID_VO: {

View File

@ -45,7 +45,9 @@ namespace blender::deg {
Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
: time_source(nullptr),
has_animated_visibility(false),
need_update_relations(true),
need_update_nodes_visibility(true),
need_tag_id_on_graph_visibility_update(true),
need_tag_id_on_graph_visibility_time_update(false),
bmain(bmain),

View File

@ -88,9 +88,15 @@ struct Depsgraph {
/* Top-level time source node. */
TimeSourceNode *time_source;
/* The graph contains data-blocks whose visibility depends on evaluation (driven or animated). */
bool has_animated_visibility;
/* Indicates whether relations needs to be updated. */
bool need_update_relations;
/* Indicates whether indirect effect of nodes on a directly visible ones needs to be updated. */
bool need_update_nodes_visibility;
/* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with
* an optional tag for their animation (time) update. */
bool need_tag_id_on_graph_visibility_update;

View File

@ -219,7 +219,9 @@ bool deg_iterator_objects_step(DEGObjectIterData *data)
for (; data->id_node_index < data->num_id_nodes; data->id_node_index++) {
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
if (!id_node->is_directly_visible) {
/* Use the build time visibility so that the ID is not appearing/disappearing throughout
* animation export. */
if (!id_node->is_visible_on_build) {
continue;
}
@ -338,10 +340,13 @@ static void DEG_iterator_ids_step(BLI_Iterator *iter, deg::IDNode *id_node, bool
{
ID *id_cow = id_node->id_cow;
if (!id_node->is_directly_visible) {
/* Use the build time visibility so that the ID is not appearing/disappearing throughout
* animation export. */
if (!id_node->is_visible_on_build) {
iter->skip = true;
return;
}
if (only_updated && !(id_cow->recalc & ID_RECALC_ALL)) {
/* Node-tree is considered part of the data-block. */
bNodeTree *ntree = ntreeFromID(id_cow);

View File

@ -37,6 +37,7 @@
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/eval/deg_eval_flush.h"
#include "intern/eval/deg_eval_stats.h"
#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@ -69,6 +70,9 @@ enum class EvaluationStage {
* involved. */
COPY_ON_WRITE,
/* Evaluate actual ID nodes visiblity based on the current state of animation and drivers. */
DYNAMIC_VISIBILITY,
/* Threaded evaluation of all possible operations. */
THREADED_EVALUATION,
@ -83,7 +87,8 @@ struct DepsgraphEvalState {
Depsgraph *graph;
bool do_stats;
EvaluationStage stage;
bool need_single_thread_pass;
bool need_update_pending_parents = true;
bool need_single_thread_pass = false;
};
void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_node)
@ -101,6 +106,12 @@ void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_nod
else {
operation_node->evaluate(depsgraph);
}
/* Clear the flag early on, allowing partial updates without re-evaluating the same node multiple
* times.
* This is a thread-safe modification as the node's flags are only read for a non-scheduled nodes
* and this node has been scheduled. */
operation_node->flag &= ~DEPSOP_FLAG_NEEDS_UPDATE;
}
void deg_task_run_func(TaskPool *pool, void *taskdata)
@ -116,24 +127,31 @@ void deg_task_run_func(TaskPool *pool, void *taskdata)
schedule_children(state, operation_node, schedule_node_to_pool, pool);
}
bool check_operation_node_visible(OperationNode *op_node)
bool check_operation_node_visible(const DepsgraphEvalState *state, OperationNode *op_node)
{
const ComponentNode *comp_node = op_node->owner;
/* Special exception, copy on write component is to be always evaluated,
* to keep copied "database" in a consistent state. */
/* Special case for copy on write component: it is to be always evaluated, to keep copied
* "database" in a consistent state. */
if (comp_node->type == NodeType::COPY_ON_WRITE) {
return true;
}
return comp_node->affects_directly_visible;
/* Special case for dynamic visiblity pass: the actual visibility is not yet known, so limit to
* only operations which affects visibility. */
if (state->stage == EvaluationStage::DYNAMIC_VISIBILITY) {
return op_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
}
return comp_node->affects_visible_id;
}
void calculate_pending_parents_for_node(OperationNode *node)
void calculate_pending_parents_for_node(const DepsgraphEvalState *state, OperationNode *node)
{
/* Update counters, applies for both visible and invisible IDs. */
node->num_links_pending = 0;
node->scheduled = false;
/* Invisible IDs requires no pending operations. */
if (!check_operation_node_visible(node)) {
if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to bother with anything if node is not tagged for update. */
@ -147,7 +165,7 @@ void calculate_pending_parents_for_node(OperationNode *node)
* calculation, but how is it possible that visible object depends
* on an invisible? This is something what is prohibited after
* deg_graph_build_flush_layers(). */
if (!check_operation_node_visible(from)) {
if (!check_operation_node_visible(state, from)) {
continue;
}
/* No need to wait for operation which is up to date. */
@ -159,20 +177,24 @@ void calculate_pending_parents_for_node(OperationNode *node)
}
}
void calculate_pending_parents(Depsgraph *graph)
void calculate_pending_parents_if_needed(DepsgraphEvalState *state)
{
for (OperationNode *node : graph->operations) {
calculate_pending_parents_for_node(node);
if (!state->need_update_pending_parents) {
return;
}
for (OperationNode *node : state->graph->operations) {
calculate_pending_parents_for_node(state, node);
}
state->need_update_pending_parents = false;
}
void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph)
{
const bool do_stats = state->do_stats;
calculate_pending_parents(graph);
/* Clear tags and other things which needs to be clear. */
for (OperationNode *node : graph->operations) {
if (do_stats) {
if (state->do_stats) {
for (OperationNode *node : graph->operations) {
node->stats.reset_current();
}
}
@ -197,11 +219,10 @@ bool need_evaluate_operation_at_stage(DepsgraphEvalState *state,
case EvaluationStage::COPY_ON_WRITE:
return (component_node->type == NodeType::COPY_ON_WRITE);
case EvaluationStage::DYNAMIC_VISIBILITY:
return operation_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
case EvaluationStage::THREADED_EVALUATION:
/* Sanity check: copy-on-write node should be evaluated already. This will be indicated by
* scheduled flag (we assume that scheduled operations have been actually handled by previous
* stage). */
BLI_assert(operation_node->scheduled || component_node->type != NodeType::COPY_ON_WRITE);
if (is_metaball_object_operation(operation_node)) {
state->need_single_thread_pass = true;
return false;
@ -227,7 +248,7 @@ void schedule_node(DepsgraphEvalState *state,
ScheduleFunctionArgs... schedule_function_args)
{
/* No need to schedule nodes of invisible ID. */
if (!check_operation_node_visible(node)) {
if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to schedule operations which are not tagged for update, they are
@ -311,6 +332,8 @@ void evaluate_graph_threaded_stage(DepsgraphEvalState *state,
{
state->stage = stage;
calculate_pending_parents_if_needed(state);
schedule_graph(state, schedule_node_to_pool, task_pool);
BLI_task_pool_work_and_wait(task_pool);
}
@ -322,6 +345,8 @@ void evaluate_graph_single_threaded_if_needed(DepsgraphEvalState *state)
return;
}
BLI_assert(!state->need_update_pending_parents);
state->stage = EvaluationStage::SINGLE_THREADED_WORKAROUND;
GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *));
@ -386,7 +411,6 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
DepsgraphEvalState state;
state.graph = graph;
state.do_stats = graph->debug.do_time_debug();
state.need_single_thread_pass = false;
/* Prepare all nodes for evaluation. */
initialize_execution(&state, graph);
@ -397,6 +421,10 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
* that if a dependency graph has a cycle evaluation functions will always "see" valid expanded
* datablock. It might not be evaluated yet, but at least the datablock will be valid.
*
* - If there is potentially dynamically changing visibility in the graph update the actual
* nodes visibilities, so that actual heavy data evaluation can benefit from knowledge that
* something heavy is not currently visible.
*
* - Multi-threaded evaluation of all possible nodes.
* Certain operations (and their subtrees) could be ignored. For example, meta-balls are not
* safe from threading point of view, so the threaded evaluation will stop at the metaball
@ -407,6 +435,24 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
TaskPool *task_pool = deg_evaluate_task_pool_create(&state);
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE);
if (graph->has_animated_visibility) {
/* Update pending parents including only the ones which are affecting operations which are
* affecting visibility. */
state.need_update_pending_parents = true;
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::DYNAMIC_VISIBILITY);
deg_graph_flush_visibility_flags_if_needed(graph);
/* Update parents to an updated visibility and evaluation stage.
*
* Need to do it regardless of whether visibility is actually changed or not: current state of
* the pending parents are all zeroes because it was previously calculated for only visibility
* related nodes and those are fully evaluated by now. */
state.need_update_pending_parents = true;
}
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::THREADED_EVALUATION);
BLI_task_pool_free(task_pool);

View File

@ -0,0 +1,147 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_visibility.h"
#include "DNA_layer_types.h"
#include "DNA_object_types.h"
#include "BLI_assert.h"
#include "BLI_stack.h"
#include "DEG_depsgraph.h"
#include "intern/depsgraph.h"
#include "intern/depsgraph_relation.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
#include "intern/node/deg_node_operation.h"
namespace blender::deg {
void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node)
{
BLI_assert(GS(id_node->id_cow->name) == ID_OB);
Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph);
const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id);
const int required_flags = (graph->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT :
BASE_ENABLED_RENDER;
const bool is_enabled = object->base_flag & required_flags;
if (id_node->is_enabled_on_eval != is_enabled) {
id_node->is_enabled_on_eval = is_enabled;
/* Tag dependency graph for changed visibility, so that it is updated on all dependencies prior
* to a pass of an actual evaluation. */
graph->need_update_nodes_visibility = true;
}
}
void deg_graph_flush_visibility_flags(Depsgraph *graph)
{
enum {
DEG_NODE_VISITED = (1 << 0),
};
for (IDNode *id_node : graph->id_nodes) {
for (ComponentNode *comp_node : id_node->components.values()) {
comp_node->possibly_affects_visible_id = id_node->is_visible_on_build;
comp_node->affects_visible_id = id_node->is_visible_on_build && id_node->is_enabled_on_eval;
}
}
BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
for (OperationNode *op_node : graph->operations) {
op_node->custom_flags = 0;
op_node->num_links_pending = 0;
for (Relation *rel : op_node->outlinks) {
if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
++op_node->num_links_pending;
}
}
if (op_node->num_links_pending == 0) {
BLI_stack_push(stack, &op_node);
op_node->custom_flags |= DEG_NODE_VISITED;
}
}
while (!BLI_stack_is_empty(stack)) {
OperationNode *op_node;
BLI_stack_pop(stack, &op_node);
/* Flush flags to parents. */
for (Relation *rel : op_node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
const OperationNode *op_to = reinterpret_cast<const OperationNode *>(rel->to);
const ComponentNode *comp_to = op_to->owner;
OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from);
ComponentNode *comp_from = op_from->owner;
const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id;
const bool target_affects_visible_id = comp_to->affects_visible_id;
op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
/* Visibility component forces all components of the current ID to be considered as
* affecting directly visible. */
if (comp_from->type == NodeType::VISIBILITY) {
const IDNode *id_node_from = comp_from->owner;
if (target_possibly_affects_visible_id) {
for (ComponentNode *comp_node : id_node_from->components.values()) {
comp_node->possibly_affects_visible_id |= target_possibly_affects_visible_id;
}
}
if (target_affects_visible_id) {
for (ComponentNode *comp_node : id_node_from->components.values()) {
comp_node->affects_visible_id |= target_affects_visible_id;
}
}
}
else {
comp_from->possibly_affects_visible_id |= target_possibly_affects_visible_id;
comp_from->affects_visible_id |= target_affects_visible_id;
}
}
}
/* Schedule parent nodes. */
for (Relation *rel : op_node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
OperationNode *op_from = (OperationNode *)rel->from;
if ((rel->flag & RELATION_FLAG_CYCLIC) == 0) {
BLI_assert(op_from->num_links_pending > 0);
--op_from->num_links_pending;
}
if ((op_from->num_links_pending == 0) && (op_from->custom_flags & DEG_NODE_VISITED) == 0) {
BLI_stack_push(stack, &op_from);
op_from->custom_flags |= DEG_NODE_VISITED;
}
}
}
}
BLI_stack_free(stack);
graph->need_update_nodes_visibility = false;
}
void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph)
{
if (!graph->need_update_nodes_visibility) {
return;
}
deg_graph_flush_visibility_flags(graph);
}
} // namespace blender::deg

View File

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
/** \file
* \ingroup depsgraph
*/
#pragma once
struct Depsgraph;
namespace blender::deg {
struct Depsgraph;
struct IDNode;
/* Evaluate actual node visibility flags based on the current state of object's visibility
* restriction flags. */
void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node);
/* Flush both static and dynamic visibility flags from leaves up to the roots, making it possible
* to know whether a node has affect on something (potentially) visible. */
void deg_graph_flush_visibility_flags(Depsgraph *graph);
void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph);
} // namespace blender::deg

View File

@ -67,7 +67,10 @@ uint64_t ComponentNode::OperationIDKey::hash() const
}
ComponentNode::ComponentNode()
: entry_operation(nullptr), exit_operation(nullptr), affects_directly_visible(false)
: entry_operation(nullptr),
exit_operation(nullptr),
possibly_affects_visible_id(false),
affects_visible_id(false)
{
operations_map = new Map<ComponentNode::OperationIDKey, OperationNode *>();
}
@ -90,7 +93,7 @@ string ComponentNode::identifier() const
const string idname = this->owner->name;
const string typebuf = "" + to_string(static_cast<int>(type)) + ")";
return typebuf + name + " : " + idname +
"( affects_directly_visible: " + (affects_directly_visible ? "true" : "false") + ")";
"( affects_visible_id: " + (affects_visible_id ? "true" : "false") + ")";
}
OperationNode *ComponentNode::find_operation(OperationIDKey key) const

View File

@ -24,7 +24,6 @@ struct bPoseChannel;
namespace blender::deg {
struct BoneComponentNode;
struct Depsgraph;
struct IDNode;
struct OperationNode;
@ -137,9 +136,17 @@ struct ComponentNode : public Node {
return true;
}
/* Denotes whether this component affects (possibly indirectly) on a
* directly visible object. */
bool affects_directly_visible;
/* The component has (possibly indirect) effect on a data-block whose node has
* is_visible_on_build set to true.
*
* This field is ensured to be up-to-date prior to `IDNode::finalize_build()`. */
bool possibly_affects_visible_id;
/* Denotes whether this component actually affects (possibly indirectly) on a directly visible
* object. Includes possibly run-time visibility update of ID nodes.
*
* NOTE: Is only reliable after `deg_graph_flush_visibility()`. */
bool affects_visible_id;
};
/* ---------------------------------------- */
@ -197,7 +204,7 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(Dupli);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Audio);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature);
DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock);
DEG_COMPONENT_NODE_DECLARE_NO_COW(Visibility);
DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(Visibility);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation);
DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput);
@ -214,7 +221,7 @@ struct SynchronizationComponentNode : public ComponentNode {
* The design is such that the synchronization is supposed to happen whenever any part of the
* ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag
* flushing and scheduling will handle the component in a generic manner. */
affects_directly_visible = true;
affects_visible_id = true;
}
DEG_COMPONENT_NODE_DECLARE;

View File

@ -69,7 +69,8 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata))
customdata_masks = DEGCustomDataMeshMasks();
previous_customdata_masks = DEGCustomDataMeshMasks();
linked_state = DEG_ID_LINKED_INDIRECTLY;
is_directly_visible = true;
is_visible_on_build = true;
is_enabled_on_eval = true;
is_collection_fully_expanded = false;
has_base = false;
is_user_modified = false;
@ -138,8 +139,8 @@ string IDNode::identifier() const
BLI_snprintf(orig_ptr, sizeof(orig_ptr), "%p", id_orig);
BLI_snprintf(cow_ptr, sizeof(cow_ptr), "%p", id_cow);
return string(nodeTypeAsString(type)) + " : " + name + " (orig: " + orig_ptr +
", eval: " + cow_ptr + ", is_directly_visible " +
(is_directly_visible ? "true" : "false") + ")";
", eval: " + cow_ptr + ", is_visible_on_build " +
(is_visible_on_build ? "true" : "false") + ")";
}
ComponentNode *IDNode::find_component(NodeType type, const char *name) const
@ -188,7 +189,7 @@ IDComponentsMask IDNode::get_visible_components_mask() const
{
IDComponentsMask result = 0;
for (ComponentNode *comp_node : components.values()) {
if (comp_node->affects_directly_visible) {
if (comp_node->possibly_affects_visible_id) {
const int component_type_as_int = static_cast<int>(comp_node->type);
BLI_assert(component_type_as_int < 64);
result |= (1ULL << component_type_as_int);

View File

@ -91,8 +91,21 @@ struct IDNode : public Node {
eDepsNode_LinkedState_Type linked_state;
/* Indicates the data-block is visible in the evaluated scene. */
bool is_directly_visible;
/* Indicates the data-block is to be considered visible in the evaluated scene.
*
* This flag is set during dependency graph build where check for an actual visibility might not
* be available yet due to driven or animated restriction flags. So it is more of an intent or,
* in other words, plausibility of the data-block to be visible. */
bool is_visible_on_build;
/* Evaluated state of whether evaluation considered this data-block "enabled".
*
* For objects this is derived from the base restriction flags, which might be animated or
* driven. It is set to `BASE_ENABLED_<VIEWPORT, RENDER>` (depending on the graph mode) after
* the object's flags from layer were evaluated.
*
* For other data-types is currently always true. */
bool is_enabled_on_eval;
/* For the collection type of ID, denotes whether collection was fully
* recursed into. */

View File

@ -214,6 +214,9 @@ enum OperationFlag {
* and not for connecting to an operation. */
DEPSOP_FLAG_PINNED = (1 << 3),
/* The operation directly or indirectly affects ID node visibility. */
DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4),
/* Set of flags which gets flushed along the relations. */
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
};

View File

@ -375,7 +375,6 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_surf_lib.glsl
engines/eevee_next/shaders/eevee_surf_world_frag.glsl
engines/eevee_next/shaders/eevee_velocity_lib.glsl
engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl
engines/eevee_next/eevee_defines.hh
engines/eevee_next/eevee_shader_shared.hh

View File

@ -29,10 +29,8 @@ namespace blender::eevee {
void Camera::init()
{
const Object *camera_eval = inst_.camera_eval_object;
synced_ = false;
data_.swap();
CameraData &data = data_.current();
CameraData &data = data_;
if (camera_eval) {
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
@ -78,9 +76,7 @@ void Camera::sync()
{
const Object *camera_eval = inst_.camera_eval_object;
data_.swap();
CameraData &data = data_.current();
CameraData &data = data_;
if (inst_.drw_view) {
DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false);
@ -142,14 +138,8 @@ void Camera::sync()
data.equirect_scale = float2(0.0f);
}
data_.current().push_update();
synced_ = true;
/* Detect changes in parameters. */
if (data_.current() != data_.previous()) {
inst_.sampling.reset();
}
data_.initialized = true;
data_.push_update();
}
/** \} */

View File

@ -83,9 +83,7 @@ class Camera {
Instance &inst_;
/** Double buffered to detect changes and have history for re-projection. */
SwapChain<CameraDataBuf, 2> data_;
/** Detects wrong usage. */
bool synced_ = false;
CameraDataBuf data_;
public:
Camera(Instance &inst) : inst_(inst){};
@ -99,28 +97,28 @@ class Camera {
**/
const CameraData &data_get() const
{
BLI_assert(synced_);
return data_.current();
BLI_assert(data_.initialized);
return data_;
}
const GPUUniformBuf *ubo_get() const
{
return data_.current();
return data_;
}
bool is_panoramic() const
{
return eevee::is_panoramic(data_.current().type);
return eevee::is_panoramic(data_.type);
}
bool is_orthographic() const
{
return data_.current().type == CAMERA_ORTHO;
return data_.type == CAMERA_ORTHO;
}
const float3 &position() const
{
return *reinterpret_cast<const float3 *>(data_.current().viewinv[3]);
return *reinterpret_cast<const float3 *>(data_.viewinv[3]);
}
const float3 &forward() const
{
return *reinterpret_cast<const float3 *>(data_.current().viewinv[2]);
return *reinterpret_cast<const float3 *>(data_.viewinv[2]);
}
};

View File

@ -163,11 +163,13 @@ inline bool operator!=(const FilmData &a, const FilmData &b)
void Film::init(const int2 &extent, const rcti *output_rect)
{
Sampling &sampling = inst_.sampling;
init_aovs();
{
/* Enable passes that need to be rendered. */
eViewLayerEEVEEPassType render_passes;
eViewLayerEEVEEPassType render_passes = eViewLayerEEVEEPassType(0);
if (inst_.is_viewport()) {
/* Viewport Case. */
@ -178,6 +180,8 @@ void Film::init(const int2 &extent, const rcti *output_rect)
* Using the render pass ensure we store the center depth. */
render_passes |= EEVEE_RENDER_PASS_Z;
}
/* TEST */
render_passes |= EEVEE_RENDER_PASS_VECTOR;
}
else {
/* Render Case. */
@ -211,7 +215,7 @@ void Film::init(const int2 &extent, const rcti *output_rect)
/* TODO(@fclem): Can't we rely on depsgraph update notification? */
if (assign_if_different(enabled_passes_, render_passes)) {
inst_.sampling.reset();
sampling.reset();
}
}
{
@ -224,14 +228,18 @@ void Film::init(const int2 &extent, const rcti *output_rect)
FilmData data = data_;
data.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
data.offset = int2(output_rect->xmin, output_rect->ymin);
data.filter_size = clamp_f(inst_.scene->r.gauss, 0.0f, 100.0f);
data.extent_inv = 1.0f / float2(data.extent);
/* Disable filtering if sample count is 1. */
data.filter_size = (sampling.sample_count() == 1) ?
0.0f :
clamp_f(inst_.scene->r.gauss, 0.0f, 100.0f);
/* TODO(fclem): parameter hidden in experimental.
* We need to figure out LOD bias first in order to preserve texture crispiness. */
data.scaling_factor = 1;
FilmData &data_prev_ = data_;
if (assign_if_different(data_prev_, data)) {
inst_.sampling.reset();
sampling.reset();
}
const eViewLayerEEVEEPassType data_passes = EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_NORMAL |
@ -325,7 +333,7 @@ void Film::init(const int2 &extent, const rcti *output_rect)
(data_.value_len > 0) ? data_.value_len : 1);
if (reset > 0) {
inst_.sampling.reset();
sampling.reset();
data_.use_history = 0;
data_.use_reprojection = 0;
@ -337,6 +345,8 @@ void Film::init(const int2 &extent, const rcti *output_rect)
depth_tx_.clear(float4(0.0f));
}
}
force_disable_reprojection_ = (inst_.scene->eevee.flag & SCE_EEVEE_TAA_REPROJECTION) == 0;
}
void Film::sync()
@ -349,12 +359,22 @@ void Film::sync()
/* TODO(fclem): Shader variation for panoramic & scaled resolution. */
RenderBuffers &rbuffers = inst_.render_buffers;
VelocityModule &velocity = inst_.velocity;
eGPUSamplerState filter = GPU_SAMPLER_FILTER;
/* For viewport, only previous motion is supported.
* Still bind previous step to avoid undefined behavior. */
eVelocityStep step_next = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT;
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
accumulate_ps_ = DRW_pass_create("Film.Accumulate", state);
GPUShader *sh = inst_.shaders.static_shader_get(shader);
DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_);
DRW_shgroup_uniform_block_ref(grp, "film_buf", &data_);
DRW_shgroup_uniform_block_ref(grp, "camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS]));
DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*velocity.camera_steps[STEP_CURRENT]));
DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*velocity.camera_steps[step_next]));
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &rbuffers.depth_tx);
DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &rbuffers.combined_tx);
DRW_shgroup_uniform_texture_ref(grp, "normal_tx", &rbuffers.normal_tx);
@ -375,7 +395,7 @@ void Film::sync()
* use image binding instead. */
DRW_shgroup_uniform_image_ref(grp, "in_weight_img", &weight_tx_.current());
DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_tx_.next());
DRW_shgroup_uniform_image_ref(grp, "in_combined_img", &combined_tx_.current());
DRW_shgroup_uniform_texture_ref_ex(grp, "in_combined_tx", &combined_tx_.current(), filter);
DRW_shgroup_uniform_image_ref(grp, "out_combined_img", &combined_tx_.next());
DRW_shgroup_uniform_image_ref(grp, "depth_img", &depth_tx_);
DRW_shgroup_uniform_image_ref(grp, "color_accum_img", &color_accum_tx_);
@ -395,13 +415,13 @@ void Film::sync()
void Film::end_sync()
{
if (inst_.sampling.is_reset()) {
data_.use_history = 0;
}
data_.use_reprojection = inst_.sampling.interactive_mode();
// if (camera.changed_type) {
// data_.use_reprojection = false;
// }
/* Just bypass the reprojection and reset the accumulation. */
if (force_disable_reprojection_ && inst_.sampling.is_reset()) {
data_.use_reprojection = false;
data_.use_history = false;
}
aovs_info.push_update();
@ -429,6 +449,11 @@ float2 Film::pixel_jitter_get() const
return jitter;
}
eViewLayerEEVEEPassType Film::enabled_passes_get() const
{
return enabled_passes_;
}
void Film::update_sample_table()
{
data_.subpixel_offset = pixel_jitter_get();
@ -528,7 +553,6 @@ void Film::accumulate(const DRWView *view)
/* Use history after first sample. */
if (data_.use_history == 0) {
data_.use_history = 1;
data_.use_reprojection = 1;
}
}

View File

@ -8,6 +8,12 @@
* The film class handles accumulation of samples with any distorted camera_type
* using a pixel filter. Inputs needs to be jittered so that the filter converges to the right
* result.
*
* In viewport, we switch between 2 accumulation mode depending on the scene state.
* - For static scene, we use a classic weighted accumulation.
* - For dynamic scene (if an update is detected), we use a more temporally stable accumulation
* following the Temporal Anti-Aliasing method (a.k.a. Temporal Super-Sampling). This does
* history reprojection and rectification to avoid most of the flickering.
*/
#pragma once
@ -43,6 +49,8 @@ class Film {
SwapChain<Texture, 2> weight_tx_;
/** Extent used by the render buffers when rendering the main views. */
int2 render_extent_ = int2(-1);
/** User setting to disable reprojection. Useful for debugging or have a more precise render. */
bool force_disable_reprojection_ = false;
DRWPass *accumulate_ps_ = nullptr;
@ -75,10 +83,7 @@ class Film {
float2 pixel_jitter_get() const;
eViewLayerEEVEEPassType enabled_passes_get() const
{
return enabled_passes_;
}
eViewLayerEEVEEPassType enabled_passes_get() const;
static bool pass_is_value(eViewLayerEEVEEPassType pass_type)
{

View File

@ -199,7 +199,7 @@ void Instance::render_sync()
**/
void Instance::render_sample()
{
if (sampling.finished()) {
if (sampling.finished_viewport()) {
film.display();
return;
}

View File

@ -56,8 +56,13 @@ void RenderBuffers::acquire(int2 extent, void *owner)
depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8, owner);
combined_tx.acquire(extent, color_format, owner);
bool do_vector_render_pass = inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR;
/* Only RG16F when only doing only reprojection or motion blur. */
eGPUTextureFormat vector_format = do_vector_render_pass ? GPU_RGBA16F : GPU_RG16F;
/* TODO(fclem): Make vector pass allocation optional if no TAA or motion blur is needed. */
vector_tx.acquire(extent, vector_format, owner);
normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format, owner);
vector_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VECTOR), color_format, owner);
diffuse_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_LIGHT), color_format, owner);
diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format, owner);
specular_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_LIGHT), color_format, owner);

View File

@ -57,22 +57,21 @@ void Sampling::end_sync()
{
if (reset_) {
viewport_sample_ = 0;
if (inst_.is_viewport()) {
interactive_mode_ = true;
}
}
if (interactive_mode_) {
int interactive_sample_count = min_ii(interactive_sample_max_, sample_count_);
if (inst_.is_viewport()) {
interactive_mode_ = viewport_sample_ < interactive_mode_threshold;
if (interactive_mode_) {
int interactive_sample_count = min_ii(interactive_sample_max_, sample_count_);
if (viewport_sample_ < interactive_sample_count) {
/* Loop over the same starting samples. */
sample_ = sample_ % interactive_sample_count;
}
else {
/* Break out of the loop and resume normal pattern. */
sample_ = interactive_sample_count;
interactive_mode_ = false;
if (viewport_sample_ < interactive_sample_count) {
/* Loop over the same starting samples. */
sample_ = sample_ % interactive_sample_count;
}
else {
/* Break out of the loop and resume normal pattern. */
sample_ = interactive_sample_count;
}
}
}
}
@ -138,8 +137,6 @@ void Sampling::step()
viewport_sample_++;
sample_++;
std::cout << sample_ << " " << viewport_sample_ << std::endl;
reset_ = false;
}
@ -218,7 +215,7 @@ void Sampling::dof_disk_sample_get(float *r_radius, float *r_theta) const
/** \} */
/* -------------------------------------------------------------------- */
/** \name Sampling patterns
/** \name Cumulative Distribution Function (CDF)
* \{ */
/* Creates a discrete cumulative distribution function table from a given curvemapping.

View File

@ -33,7 +33,7 @@ class Sampling {
/* During interactive rendering, loop over the first few samples. */
constexpr static uint64_t interactive_sample_max_ = 8;
/** 0 based current sample. */
/** 0 based current sample. Might not increase sequentially in viewport. */
uint64_t sample_ = 0;
/** Target sample count. */
uint64_t sample_count_ = 64;
@ -43,7 +43,7 @@ class Sampling {
uint64_t dof_sample_count_ = 1;
/** Motion blur steps. */
uint64_t motion_blur_steps_ = 1;
/** Increases if the view and the scene is static. */
/** Increases if the view and the scene is static. Does increase sequentially. */
int64_t viewport_sample_ = 0;
/** Tag to reset sampling for the next sample. */
bool reset_ = false;
@ -52,6 +52,12 @@ class Sampling {
* In interactive mode, image stability is prioritized over quality.
*/
bool interactive_mode_ = false;
/**
* Sample count after which we use the static accumulation.
* Interactive sampling from sample 0 to (interactive_mode_threshold - 1).
* Accumulation sampling from sample interactive_mode_threshold to sample_count_.
*/
static constexpr int interactive_mode_threshold = 3;
SamplingDataBuf data_;
@ -102,13 +108,24 @@ class Sampling {
/* Returns true if rendering has finished. */
bool finished() const
{
return (sample_ >= sample_count_ - 1);
return (sample_ >= sample_count_);
}
/* Returns true if viewport smoothing and sampling has finished. */
bool finished_viewport() const
{
return finished() && (viewport_sample_ >= interactive_sample_max_);
return (viewport_sample_ >= sample_count_) && !interactive_mode_;
}
/* Returns true if viewport renderer is in interactive mode and should use TAA. */
bool interactive_mode() const
{
return interactive_mode_;
}
uint64_t sample_count() const
{
return sample_count_;
}
/* Return true if we are starting a new motion blur step. We need to run sync again since

View File

@ -82,8 +82,6 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_film_frag";
case FILM_COMP:
return "eevee_film_comp";
case VELOCITY_RESOLVE:
return "eevee_velocity_resolve";
/* To avoid compiler warning about missing case. */
case MAX_SHADER_TYPE:
return "";

View File

@ -29,8 +29,6 @@ enum eShaderType {
FILM_FRAG = 0,
FILM_COMP,
VELOCITY_RESOLVE,
MAX_SHADER_TYPE,
};

View File

@ -124,7 +124,12 @@ struct CameraData {
float clip_far;
eCameraType type;
int _pad0;
bool initialized;
#ifdef __cplusplus
/* Small constructor to allow detecting new buffers. */
CameraData() : initialized(false){};
#endif
};
BLI_STATIC_ASSERT_ALIGN(CameraData, 16)
@ -156,6 +161,8 @@ struct FilmData {
* pixel if using scaled resolution rendering.
*/
float2 subpixel_offset;
/** Scaling factor to convert texel to uvs. */
float2 extent_inv;
/** Is true if history is valid and can be sampled. Bypass history to resets accumulation. */
bool1 use_history;
/** Is true if combined buffer is valid and can be re-projected to reduce variance. */
@ -165,7 +172,6 @@ struct FilmData {
/** Is true if accumulation of filtered passes is needed. */
bool1 any_render_pass_1;
bool1 any_render_pass_2;
int _pad0, _pad1;
/** Output counts per type. */
int color_len, value_len;
/** Index in color_accum_img or value_accum_img of each pass. -1 if pass is not enabled. */

View File

@ -9,10 +9,6 @@
* temporal re-projection or motion blur.
*
* It is the module that tracks the objects between frames updates.
*
* #VelocityModule contains all motion steps data and logic.
* #VelocityPass contains the resolve pass for static geometry.
* #VelocityView is a per view instance that contain the velocity buffer.
*/
#include "BKE_duplilist.h"
@ -36,8 +32,7 @@ namespace blender::eevee {
void VelocityModule::init()
{
#if 0 /* TODO renderpasses */
if (inst_.render && (inst_.render_passes.vector != nullptr)) {
if (inst_.render && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR)) {
/* No motion blur and the vector pass was requested. Do the step sync here. */
const Scene *scene = inst_.scene;
float initial_time = scene->r.cfra + scene->r.subframe;
@ -45,7 +40,6 @@ void VelocityModule::init()
step_sync(STEP_NEXT, initial_time + 1.0f);
inst_.set_time(initial_time);
}
#endif
}
static void step_object_sync_render(void *velocity,
@ -70,6 +64,11 @@ void VelocityModule::step_camera_sync()
{
inst_.camera.sync();
*camera_steps[step_] = inst_.camera.data_get();
/* Fix undefined camera steps when rendering is starting. */
if ((step_ == STEP_CURRENT) && (camera_steps[STEP_PREVIOUS]->initialized == false)) {
*camera_steps[STEP_PREVIOUS] = *static_cast<CameraData *>(camera_steps[step_]);
camera_steps[STEP_PREVIOUS]->initialized = true;
}
}
bool VelocityModule::step_object_sync(Object *ob,
@ -267,6 +266,10 @@ void VelocityModule::end_sync()
inst_.sampling.reset();
}
if (inst_.is_viewport() && camera_has_motion()) {
inst_.sampling.reset();
}
for (auto key : deleted_obj) {
velocity_map.remove(key);
}
@ -300,19 +303,6 @@ void VelocityModule::end_sync()
camera_steps[STEP_CURRENT]->push_update();
camera_steps[STEP_NEXT]->push_update();
indirection_buf.push_update();
{
resolve_ps_ = DRW_pass_create("Velocity.Resolve", (DRWState)0);
GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_RESOLVE);
DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
DRW_shgroup_uniform_image_ref(grp, "velocity_view_img", &velocity_view_tx_);
DRW_shgroup_uniform_image_ref(grp, "velocity_camera_img", &velocity_camera_tx_);
DRW_shgroup_uniform_block(grp, "camera_prev", *camera_steps[STEP_PREVIOUS]);
DRW_shgroup_uniform_block(grp, "camera_curr", *camera_steps[STEP_CURRENT]);
DRW_shgroup_uniform_block(grp, "camera_next", *camera_steps[STEP_NEXT]);
DRW_shgroup_call_compute_ref(grp, resolve_dispatch_size_);
}
}
bool VelocityModule::object_has_velocity(const Object *ob)
@ -359,60 +349,15 @@ void VelocityModule::bind_resources(DRWShadingGroup *grp)
DRW_shgroup_storage_block_ref(grp, "velocity_indirection_buf", &indirection_buf);
}
/* Resolve pass for static geometry and to camera space projection. */
void VelocityModule::resolve_camera_motion(GPUTexture *depth_tx,
GPUTexture *velocity_view_tx,
GPUTexture *velocity_camera_tx)
bool VelocityModule::camera_has_motion() const
{
input_depth_tx_ = depth_tx;
velocity_view_tx_ = velocity_view_tx;
velocity_camera_tx_ = velocity_camera_tx;
resolve_dispatch_size_.x = divide_ceil_u(GPU_texture_width(depth_tx), 8);
resolve_dispatch_size_.y = divide_ceil_u(GPU_texture_height(depth_tx), 8);
DRW_draw_pass(resolve_ps_);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Velocity View
* \{ */
void VelocityView::sync()
{
/* TODO: Remove. */
velocity_view_tx_.sync();
velocity_camera_tx_.sync();
}
void VelocityView::acquire(int2 extent)
{
/* WORKAROUND: View name should be unique and static.
* With this, we can reuse the same texture across views. */
DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
/* Only RG16F when only doing only reprojection or motion blur. */
eGPUTextureFormat format = inst_.is_viewport() ? GPU_RG16F : GPU_RGBA16F;
velocity_view_tx_.acquire(extent, format, owner);
if (false /* TODO(fclem): Panoramic camera. */) {
velocity_camera_tx_.acquire(extent, format, owner);
/* Only valid after sync. */
if (inst_.is_viewport()) {
/* Viewport has no next step. */
return *camera_steps[STEP_PREVIOUS] != *camera_steps[STEP_CURRENT];
}
else {
velocity_camera_tx_.acquire(int2(1), format, owner);
}
}
void VelocityView::resolve(GPUTexture *depth_tx)
{
inst_.velocity.resolve_camera_motion(depth_tx, velocity_view_tx_, velocity_camera_tx_);
}
void VelocityView::release()
{
velocity_view_tx_.release();
velocity_camera_tx_.release();
return *camera_steps[STEP_PREVIOUS] != *camera_steps[STEP_CURRENT] &&
*camera_steps[STEP_NEXT] != *camera_steps[STEP_CURRENT];
}
/** \} */

View File

@ -27,8 +27,6 @@ namespace blender::eevee {
/** Container for scene velocity data. */
class VelocityModule {
friend class VelocityView;
public:
struct VelocityObjectData : public VelocityIndex {
/** ID to retrieve the corresponding #VelocityGeometryData after copy. */
@ -69,15 +67,6 @@ class VelocityModule {
eVelocityStep step_ = STEP_CURRENT;
DRWPass *resolve_ps_ = nullptr;
/** Reference only. Not owned. */
GPUTexture *input_depth_tx_;
GPUTexture *velocity_view_tx_;
GPUTexture *velocity_camera_tx_;
int3 resolve_dispatch_size_ = int3(1, 1, 1);
public:
VelocityModule(Instance &inst) : inst_(inst)
{
@ -89,6 +78,7 @@ class VelocityModule {
}
for (CameraDataBuf *&step_buf : camera_steps) {
step_buf = new CameraDataBuf();
/* */
}
};
@ -121,56 +111,11 @@ class VelocityModule {
void bind_resources(DRWShadingGroup *grp);
bool camera_has_motion() const;
private:
bool object_has_velocity(const Object *ob);
bool object_is_deform(const Object *ob);
void resolve_camera_motion(GPUTexture *depth_tx,
GPUTexture *velocity_view_tx,
GPUTexture *velocity_camera_tx);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Velocity
*
* \{ */
/**
* Per view module.
*/
class VelocityView {
private:
Instance &inst_;
StringRefNull view_name_;
TextureFromPool velocity_camera_tx_ = {"velocity_camera_tx_"};
TextureFromPool velocity_view_tx_ = {"velocity_view_tx_"};
public:
VelocityView(Instance &inst, const char *name) : inst_(inst), view_name_(name){};
~VelocityView(){};
void sync();
void acquire(int2 extent);
void release();
void resolve(GPUTexture *depth_tx);
/**
* Getters
**/
GPUTexture *view_vectors_get() const
{
return velocity_view_tx_;
}
GPUTexture *camera_vectors_get() const
{
return (velocity_camera_tx_.is_valid()) ? velocity_camera_tx_ : velocity_view_tx_;
}
};
/** \} */

View File

@ -80,7 +80,6 @@ void ShadingView::sync()
// dof_.sync(winmat_p, extent_);
// mb_.sync(extent_);
velocity_.sync();
// rt_buffer_opaque_.sync(extent_);
// rt_buffer_refract_.sync(extent_);
// inst_.hiz_back.view_sync(extent_);
@ -103,18 +102,20 @@ void ShadingView::render()
RenderBuffers &rbufs = inst_.render_buffers;
rbufs.acquire(extent_, owner);
velocity_.acquire(extent_);
combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx));
prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get()));
GPU_ATTACHMENT_TEXTURE(rbufs.vector_tx));
update_view();
DRW_stats_group_start(name_);
DRW_view_set_active(render_view_);
float4 clear_velocity(VELOCITY_INVALID);
/* If camera has any motion, compute motion vector in the film pass. Otherwise, we avoid float
* precision issue by setting the motion of all static geometry to 0. */
float4 clear_velocity = float4(inst_.velocity.camera_has_motion() ? VELOCITY_INVALID : 0.0f);
GPU_framebuffer_bind(prepass_fb_);
GPU_framebuffer_clear_color(prepass_fb_, clear_velocity);
/* Alpha stores transmittance. So start at 1. */
@ -137,18 +138,14 @@ void ShadingView::render()
// inst_.lights.debug_draw(view_fb_);
// inst_.shadows.debug_draw(view_fb_);
velocity_.resolve(rbufs.depth_tx);
// GPUTexture *final_radiance_tx = render_post(combined_tx_);
inst_.film.accumulate(sub_view_);
rbufs.release();
postfx_tx_.release();
DRW_stats_group_end();
postfx_tx_.release();
velocity_.release();
}
GPUTexture *ShadingView::render_post(GPUTexture *input_tx)

View File

@ -44,7 +44,6 @@ class ShadingView {
/** Post-FX modules. */
// DepthOfField dof_;
// MotionBlur mb_;
VelocityView velocity_;
/** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */
// RaytraceBuffer rt_buffer_opaque_;
@ -69,7 +68,7 @@ class ShadingView {
public:
ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4])
: inst_(inst), name_(name), face_matrix_(face_matrix), velocity_(inst, name){};
: inst_(inst), name_(name), face_matrix_(face_matrix){};
~ShadingView(){};

View File

@ -143,24 +143,10 @@ vec2 camera_uv_from_view(CameraData cam, vec3 vV)
}
}
vec2 camera_uv_from_world(CameraData cam, vec3 V)
vec2 camera_uv_from_world(CameraData cam, vec3 P)
{
vec3 vV = transform_point(cam.viewmat, V);
switch (cam.type) {
default:
case CAMERA_ORTHO:
return camera_uv_from_view(cam.persmat, false, V);
case CAMERA_PERSP:
return camera_uv_from_view(cam.persmat, true, V);
case CAMERA_PANO_EQUIRECT:
return camera_equirectangular_from_direction(cam, vV);
case CAMERA_PANO_EQUISOLID:
/* ATTR_FALLTHROUGH; */
case CAMERA_PANO_EQUIDISTANT:
return camera_fisheye_from_direction(cam, vV);
case CAMERA_PANO_MIRROR:
return camera_mirror_ball_from_direction(cam, vV);
}
vec3 vV = transform_direction(cam.viewmat, normalize(P));
return camera_uv_from_view(cam, vV);
}
/** \} */

View File

@ -11,7 +11,7 @@ void main()
out_depth = imageLoad(depth_img, texel_film).r;
if (film_buf.display_id == -1) {
out_color = imageLoad(in_combined_img, texel_film);
out_color = texelFetch(in_combined_tx, texel_film, 0);
}
else if (film_buf.display_is_value) {
out_color.rgb = imageLoad(value_accum_img, ivec3(texel_film, film_buf.display_id)).rrr;

View File

@ -4,7 +4,9 @@
**/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
/* Return scene linear Z depth from the camera or radial depth for panoramic cameras. */
float film_depth_convert_to_scene(float depth)
@ -16,6 +18,54 @@ float film_depth_convert_to_scene(float depth)
return abs(get_view_z_from_depth(depth));
}
vec3 film_YCoCg_from_scene_linear(vec3 rgb_color)
{
const mat3 colorspace_tx = transpose(mat3(vec3(1, 2, 1), /* Y */
vec3(2, 0, -2), /* Co */
vec3(-1, 2, -1))); /* Cg */
return colorspace_tx * rgb_color;
}
vec4 film_YCoCg_from_scene_linear(vec4 rgba_color)
{
return vec4(film_YCoCg_from_scene_linear(rgba_color.rgb), rgba_color.a);
}
vec3 film_scene_linear_from_YCoCg(vec3 ycocg_color)
{
float Y = ycocg_color.x;
float Co = ycocg_color.y;
float Cg = ycocg_color.z;
vec3 rgb_color;
rgb_color.r = Y + Co - Cg;
rgb_color.g = Y + Cg;
rgb_color.b = Y - Co - Cg;
return rgb_color * 0.25;
}
/* Load a texture sample in a specific format. Combined pass needs to use this. */
vec4 film_texelfetch_as_YCoCg_opacity(sampler2D tx, ivec2 texel)
{
vec4 color = texelFetch(combined_tx, texel, 0);
/* Can we assume safe color from earlier pass? */
// color = safe_color(color);
/* Convert transmittance to opacity. */
color.a = saturate(1.0 - color.a);
/* Transform to YCoCg for accumulation. */
color.rgb = film_YCoCg_from_scene_linear(color.rgb);
return color;
}
/* Returns a weight based on Luma to reduce the flickering introduced by high energy pixels. */
float film_luma_weight(float luma)
{
/* Slide 20 of "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014. */
/* To preserve more details in dark areas, we use a bigger bias. */
/* TODO(fclem): exposure weighting. */
return 1.0 / (4.0 + luma);
}
/* -------------------------------------------------------------------- */
/** \name Filter
* \{ */
@ -116,18 +166,18 @@ void film_sample_accum_mist(FilmSample samp, inout float accum)
accum += mist * samp.weight;
}
void film_sample_accum_combined(FilmSample samp, inout vec4 accum)
void film_sample_accum_combined(FilmSample samp, inout vec4 accum, inout float weight_accum)
{
if (film_buf.combined_id == -1) {
return;
}
vec4 color = texelFetch(combined_tx, samp.texel, 0);
/* Convert transmittance to opacity. */
color.a = saturate(1.0 - color.a);
/* TODO(fclem) Pre-expose. */
color.rgb = log2(1.0 + color.rgb);
vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, samp.texel);
accum += color * samp.weight;
/* Weight by luma to remove fireflies. */
float weight = film_luma_weight(color.x) * samp.weight;
accum += color * weight;
weight_accum += weight;
}
/** \} */
@ -156,46 +206,286 @@ float film_weight_load(ivec2 texel)
/* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */
texel = texel % imageSize(in_weight_img).xy;
if (film_buf.use_history == false) {
if (!film_buf.use_history || film_buf.use_reprojection) {
return 0.0;
}
return imageLoad(in_weight_img, ivec3(texel, WEIGHT_lAYER_ACCUMULATION)).x;
}
/* Return the motion in pixels. */
void film_motion_load()
/* Returns motion in pixel space to retrieve the pixel history. */
vec2 film_pixel_history_motion_vector(ivec2 texel_sample)
{
// ivec2 texel_sample = film_sample_get(0, texel_film, distance_sample);
// vec4 vector = texelFetch(vector_tx, texel_sample);
/**
* Dilate velocity by using the nearest pixel in a cross pattern.
* "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 27)
*/
const ivec2 corners[4] = ivec2[4](ivec2(-2, -2), ivec2(2, -2), ivec2(-2, 2), ivec2(2, 2));
float min_depth = texelFetch(depth_tx, texel_sample, 0).x;
ivec2 nearest_texel = texel_sample;
for (int i = 0; i < 4; i++) {
ivec2 texel = clamp(texel_sample + corners[i], ivec2(0), textureSize(depth_tx, 0).xy);
float depth = texelFetch(depth_tx, texel, 0).x;
if (min_depth > depth) {
min_depth = depth;
nearest_texel = texel;
}
}
// vector.xy *= film_buf.extent;
vec4 vector = velocity_resolve(vector_tx, nearest_texel, min_depth);
/* Transform to pixel space. */
vector.xy *= vec2(film_buf.extent);
return vector.xy;
}
/* \a t is inter-pixel position. 0 means perfectly on a pixel center.
* Returns weights in both dimensions.
* Multiply each dimension weights to get final pixel weights. */
void film_get_catmull_rom_weights(vec2 t, out vec2 weights[4])
{
vec2 t2 = t * t;
vec2 t3 = t2 * t;
float fc = 0.5; /* Catmull-Rom. */
vec2 fct = t * fc;
vec2 fct2 = t2 * fc;
vec2 fct3 = t3 * fc;
weights[0] = (fct2 * 2.0 - fct3) - fct;
weights[1] = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0;
weights[2] = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct;
weights[3] = fct3 - fct2;
}
/* Load color using a special filter to avoid loosing detail.
* \a texel is sample position with subpixel accuracy. */
vec4 film_sample_catmull_rom(sampler2D color_tx, vec2 input_texel)
{
vec2 center_texel;
vec2 inter_texel = modf(input_texel, center_texel);
vec2 weights[4];
film_get_catmull_rom_weights(inter_texel, weights);
#if 0 /* Reference. 16 Taps. */
vec4 color = vec4(0.0);
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
ivec2 texel = ivec2(center_texel) + ivec2(x, y) - 1;
texel = clamp(texel, ivec2(0), textureSize(color_tx, 0).xy - 1);
color += texelFetch(color_tx, texel, 0) * weights[x].x * weights[y].y;
}
}
return color;
#elif 1 /* Optimize version. 5 Bilinear Taps. */
/**
* Use optimized version by leveraging bilinear filtering from hardware sampler and by removing
* corner taps.
* From "Filmic SMAA" by Jorge Jimenez at Siggraph 2016
* http://advances.realtimerendering.com/s2016/Filmic%20SMAA%20v7.pptx
*/
center_texel += 0.5;
/* Slide 92. */
vec2 weight_12 = weights[1] + weights[2];
vec2 uv_12 = (center_texel + weights[2] / weight_12) * film_buf.extent_inv;
vec2 uv_0 = (center_texel - 1.0) * film_buf.extent_inv;
vec2 uv_3 = (center_texel + 2.0) * film_buf.extent_inv;
vec4 color;
vec4 weight_cross = weight_12.xyyx * vec4(weights[0].yx, weights[3].xy);
float weight_center = weight_12.x * weight_12.y;
color = textureLod(color_tx, uv_12, 0.0) * weight_center;
color += textureLod(color_tx, vec2(uv_12.x, uv_0.y), 0.0) * weight_cross.x;
color += textureLod(color_tx, vec2(uv_0.x, uv_12.y), 0.0) * weight_cross.y;
color += textureLod(color_tx, vec2(uv_3.x, uv_12.y), 0.0) * weight_cross.z;
color += textureLod(color_tx, vec2(uv_12.x, uv_3.y), 0.0) * weight_cross.w;
/* Re-normalize for the removed corners. */
return color / (weight_center + sum(weight_cross));
#else /* Nearest interpolation for debugging. 1 Tap. */
ivec2 texel = ivec2(center_texel) + ivec2(greaterThan(inter_texel, vec2(0.5)));
texel = clamp(texel, ivec2(0), textureSize(color_tx, 0).xy - 1);
return texelFetch(color_tx, texel, 0);
#endif
}
/* Return history clipping bounding box in YCoCg color space. */
void film_combined_neighbor_boundbox(ivec2 texel, out vec4 min_c, out vec4 max_c)
{
/* Plus (+) shape offsets. */
const ivec2 plus_offsets[5] = ivec2[5](ivec2(0, 0), /* Center */
ivec2(-1, 0),
ivec2(0, -1),
ivec2(1, 0),
ivec2(0, 1));
#if 0
/**
* Compute Variance of neighborhood as described in:
* "An Excursion in Temporal Supersampling" by Marco Salvi at GDC 2016.
* and:
* "A Survey of Temporal Antialiasing Techniques" by Yang et al.
*/
/* First 2 moments. */
vec4 mu1 = vec4(0), mu2 = vec4(0);
for (int i = 0; i < 5; i++) {
vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + plus_offsets[i]);
mu1 += color;
mu2 += sqr(color);
}
mu1 *= (1.0 / 5.0);
mu2 *= (1.0 / 5.0);
/* Extent scaling. Range [0.75..1.25].
* Balance between more flickering (0.75) or more ghosting (1.25). */
const float gamma = 1.25;
/* Standard deviation. */
vec4 sigma = sqrt(abs(mu2 - sqr(mu1)));
/* eq. 6 in "A Survey of Temporal Antialiasing Techniques". */
min_c = mu1 - gamma * sigma;
max_c = mu1 + gamma * sigma;
#else
/**
* Simple bounding box calculation in YCoCg as described in:
* "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014
*/
min_c = vec4(1e16);
max_c = vec4(-1e16);
for (int i = 0; i < 5; i++) {
vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + plus_offsets[i]);
min_c = min(min_c, color);
max_c = max(max_c, color);
}
/* (Slide 32) Simple clamp to min/max of 8 neighbors results in 3x3 box artifacts.
* Round bbox shape by averaging 2 different min/max from 2 different neighborhood. */
vec4 min_c_3x3 = min_c;
vec4 max_c_3x3 = max_c;
const ivec2 corners[4] = ivec2[4](ivec2(-1, -1), ivec2(1, -1), ivec2(-1, 1), ivec2(1, 1));
for (int i = 0; i < 4; i++) {
vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + corners[i]);
min_c_3x3 = min(min_c_3x3, color);
max_c_3x3 = max(max_c_3x3, color);
}
min_c = (min_c + min_c_3x3) * 0.5;
max_c = (max_c + max_c_3x3) * 0.5;
#endif
}
/* 1D equivalent of line_aabb_clipping_dist(). */
float film_aabb_clipping_dist_alpha(float origin, float direction, float aabb_min, float aabb_max)
{
if (abs(direction) < 1e-5) {
return 0.0;
}
float nearest_plane = (direction > 0.0) ? aabb_min : aabb_max;
return (nearest_plane - origin) / direction;
}
/* Modulate the history color to avoid ghosting artifact. */
vec4 film_amend_combined_history(vec4 color_history, vec4 src_color, ivec2 src_texel)
{
/* Get local color bounding box of source neighboorhood. */
vec4 min_color, max_color;
film_combined_neighbor_boundbox(src_texel, min_color, max_color);
/* Clip instead of clamping to avoid color accumulating in the AABB corners. */
vec4 clip_dir = src_color - color_history;
float t = line_aabb_clipping_dist(color_history.rgb, clip_dir.rgb, min_color.rgb, max_color.rgb);
color_history.rgb += clip_dir.rgb * saturate(t);
/* Clip alpha on its own to avoid interference with other chanels. */
float t_a = film_aabb_clipping_dist_alpha(color_history.a, clip_dir.a, min_color.a, max_color.a);
color_history.a += clip_dir.a * saturate(t_a);
return color_history;
}
float film_history_blend_factor(float velocity,
vec2 texel,
float luma_incoming,
float luma_history)
{
/* 5% of incoming color by default. */
float blend = 0.05;
/* Blend less history if the pixel has substential velocity. */
blend = mix(blend, 0.20, saturate(velocity * 0.02));
/* Weight by luma. */
blend = max(blend, saturate(0.01 * luma_history / abs(luma_history - luma_incoming)));
/* Discard out of view history. */
if (any(lessThan(texel, vec2(0))) || any(greaterThanEqual(texel, film_buf.extent))) {
blend = 1.0;
}
/* Discard history if invalid. */
if (film_buf.use_history == false) {
blend = 1.0;
}
return blend;
}
/* Returns resolved final color. */
void film_store_combined(FilmSample dst, vec4 color, inout vec4 display)
void film_store_combined(
FilmSample dst, ivec2 src_texel, vec4 color, float color_weight, inout vec4 display)
{
if (film_buf.combined_id == -1) {
return;
}
/* Could we assume safe color from earlier pass? */
color = safe_color(color);
if (false) {
/* Re-projection using motion vectors. */
// ivec2 texel_combined = texel_film + film_motion_load(texel_film);
// float weight_combined = film_weight_load(texel_combined);
vec4 color_src, color_dst;
float weight_src, weight_dst;
/* Undo the weighting to get final spatialy-filtered color. */
color_src = color / color_weight;
if (film_buf.use_reprojection) {
/* Interactive accumulation. Do reprojection and Temporal Anti-Aliasing. */
/* Reproject by finding where this pixel was in the previous frame. */
vec2 motion = film_pixel_history_motion_vector(dst.texel);
vec2 history_texel = vec2(dst.texel) + motion;
float velocity = length(motion);
/* Load weight if it is not uniform accross the whole buffer (i.e: upsampling, panoramic). */
// dst.weight = film_weight_load(texel_combined);
color_dst = film_sample_catmull_rom(in_combined_tx, history_texel);
color_dst.rgb = film_YCoCg_from_scene_linear(color_dst.rgb);
float blend = film_history_blend_factor(velocity, history_texel, color_src.x, color_dst.x);
color_dst = film_amend_combined_history(color_dst, color_src, src_texel);
/* Luma weighted blend to avoid flickering. */
weight_dst = film_luma_weight(color_dst.x) * (1.0 - blend);
weight_src = film_luma_weight(color_src.x) * (blend);
}
#ifdef USE_NEIGHBORHOOD_CLAMPING
/* Only do that for combined pass as it has a non-negligeable performance impact. */
// color = clamp_bbox(color, min, max);
#endif
else {
/* Everything is static. Use render accumulation. */
color_dst = texelFetch(in_combined_tx, dst.texel, 0);
color_dst.rgb = film_YCoCg_from_scene_linear(color_dst.rgb);
vec4 dst_color = imageLoad(in_combined_img, dst.texel);
/* Luma weighted blend to avoid flickering. */
weight_dst = film_luma_weight(color_dst.x) * dst.weight;
weight_src = color_weight;
}
/* Weighted blend. */
color = color_dst * weight_dst + color_src * weight_src;
color /= weight_src + weight_dst;
color = (dst_color * dst.weight + color) * dst.weight_sum_inv;
color.rgb = film_scene_linear_from_YCoCg(color.rgb);
/* TODO(fclem) undo Pre-expose. */
// color.rgb = exp2(color.rgb) - 1.0;
/* Fix alpha not accumulating to 1 because of float imprecision. */
if (color.a > 0.995) {
color.a = 1.0;
}
/* Filter NaNs. */
if (any(isnan(color))) {
color = vec4(0.0, 0.0, 0.0, 1.0);
}
if (film_buf.display_id == -1) {
display = color;
@ -213,6 +503,11 @@ void film_store_color(FilmSample dst, int pass_id, vec4 color, inout vec4 displa
color = (data_film * dst.weight + color) * dst.weight_sum_inv;
/* Filter NaNs. */
if (any(isnan(color))) {
color = vec4(0.0, 0.0, 0.0, 1.0);
}
if (film_buf.display_id == pass_id) {
display = color;
}
@ -229,6 +524,11 @@ void film_store_value(FilmSample dst, int pass_id, float value, inout vec4 displ
value = (data_film * dst.weight + value) * dst.weight_sum_inv;
/* Filter NaNs. */
if (isnan(value)) {
value = 0.0;
}
if (film_buf.display_id == pass_id) {
display = vec4(value, value, value, 1.0);
}
@ -290,16 +590,28 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
/* NOTE: We split the accumulations into separate loops to avoid using too much registers and
* maximize occupancy. */
if (film_buf.combined_id != -1) {
/* NOTE: Do weight accumulation again since we use custom weights. */
float weight_accum = 0.0;
vec4 combined_accum = vec4(0.0);
for (int i = 0; i < film_buf.samples_len; i++) {
FilmSample src = film_sample_get(i, texel_film);
film_sample_accum_combined(src, combined_accum, weight_accum);
}
film_store_combined(dst, texel_film, combined_accum, weight_accum, out_color);
}
if (film_buf.has_data) {
float film_weight = film_distance_load(texel_film);
float film_distance = film_distance_load(texel_film);
/* Get sample closest to target texel. It is always sample 0. */
FilmSample film_sample = film_sample_get(0, texel_film);
if (film_sample.weight < film_weight) {
float depth = texelFetch(depth_tx, film_sample.texel, 0).x;
if (film_buf.use_reprojection || film_sample.weight < film_distance) {
vec4 normal = texelFetch(normal_tx, film_sample.texel, 0);
vec4 vector = texelFetch(vector_tx, film_sample.texel, 0);
float depth = texelFetch(depth_tx, film_sample.texel, 0).x;
vec4 vector = velocity_resolve(vector_tx, film_sample.texel, depth);
film_store_depth(texel_film, depth, out_depth);
film_store_data(texel_film, film_buf.normal_id, normal, out_color);
@ -311,16 +623,6 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
}
}
if (film_buf.combined_id != -1) {
vec4 combined_accum = vec4(0.0);
for (int i = 0; i < film_buf.samples_len; i++) {
FilmSample src = film_sample_get(i, texel_film);
film_sample_accum_combined(src, combined_accum);
}
film_store_combined(dst, combined_accum, out_color);
}
if (film_buf.any_render_pass_1) {
vec4 diffuse_light_accum = vec4(0.0);
vec4 specular_light_accum = vec4(0.0);

View File

@ -72,14 +72,7 @@ void main()
#endif
#ifdef MAT_VELOCITY
vec4 out_velocity_camera; /* TODO(fclem): Panoramic cameras. */
velocity_camera(interp.P + motion.prev,
interp.P,
interp.P - motion.next,
out_velocity_camera,
out_velocity_view);
/* For testing in viewport. */
out_velocity_view.zw = vec2(0.0);
out_velocity = velocity_surface(interp.P + motion.prev, interp.P, interp.P - motion.next);
out_velocity = velocity_pack(out_velocity);
#endif
}

View File

@ -4,21 +4,28 @@
#ifdef VELOCITY_CAMERA
vec4 velocity_pack(vec4 data)
{
return data * 0.01;
}
vec4 velocity_unpack(vec4 data)
{
return data * 100.0;
}
/**
* Given a triple of position, compute the previous and next motion vectors.
* Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy)
* Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy).
*/
vec4 velocity_view(vec3 P_prev, vec3 P, vec3 P_next)
vec4 velocity_surface(vec3 P_prv, vec3 P, vec3 P_nxt)
{
vec2 prev_uv, curr_uv, next_uv;
/* NOTE: We don't use the drw_view.persmat to avoid adding the TAA jitter to the velocity. */
vec2 prev_uv = project_point(camera_prev.persmat, P_prv).xy;
vec2 curr_uv = project_point(camera_curr.persmat, P).xy;
vec2 next_uv = project_point(camera_next.persmat, P_nxt).xy;
prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy;
curr_uv = transform_point(ViewProjectionMatrix, P).xy;
next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy;
vec4 motion;
motion.xy = prev_uv - curr_uv;
motion.zw = curr_uv - next_uv;
vec4 motion = vec4(prev_uv - curr_uv, curr_uv - next_uv);
/* Convert NDC velocity to UV velocity */
motion *= 0.5;
@ -26,37 +33,41 @@ vec4 velocity_view(vec3 P_prev, vec3 P, vec3 P_next)
}
/**
* Given a triple of position, compute the previous and next motion vectors.
* Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy)
* \a velocity_camera is the motion in film UV space after camera projection.
* \a velocity_view is the motion in ShadingView UV space. It is different
* from velocity_camera for multi-view rendering.
* Given a view space view vector \a vV, compute the previous and next motion vectors for
* background pixels.
* Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy).
*/
void velocity_camera(vec3 P_prev, vec3 P, vec3 P_next, out vec4 vel_camera, out vec4 vel_view)
vec4 velocity_background(vec3 vV)
{
vec2 prev_uv, curr_uv, next_uv;
prev_uv = camera_uv_from_world(camera_prev, P_prev);
curr_uv = camera_uv_from_world(camera_curr, P);
next_uv = camera_uv_from_world(camera_next, P_next);
/* Only transform direction to avoid loosing precision. */
vec3 V = transform_direction(camera_curr.viewinv, vV);
vel_camera.xy = prev_uv - curr_uv;
vel_camera.zw = curr_uv - next_uv;
return velocity_surface(V, V, V);
}
if (is_panoramic(camera_curr.type)) {
/* This path is only used if using using panoramic projections. Since the views always have
* the same 45° aperture angle, we can safely reuse the projection matrix. */
prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy;
curr_uv = transform_point(ViewProjectionMatrix, P).xy;
next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy;
/**
* Load and resolve correct velocity as some pixels might still not have correct
* motion data for performance reasons.
*/
vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth)
{
vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(vector_tx, 0).xy);
vec4 vector = texelFetch(vector_tx, texel, 0);
vel_view.xy = prev_uv - curr_uv;
vel_view.zw = curr_uv - next_uv;
/* Convert NDC velocity to UV velocity */
vel_view *= 0.5;
}
else {
vel_view = vel_camera;
if (vector.x == VELOCITY_INVALID) {
bool is_background = (depth == 1.0);
if (is_background) {
/* NOTE: Use viewCameraVec to avoid imprecision if camera is far from origin. */
vec3 vV = viewCameraVec(get_view_space_from_depth(uv, 1.0));
return velocity_background(vV);
}
else {
/* Static geometry. No translation in world space. */
vec3 P = get_world_space_from_depth(uv, depth);
return velocity_surface(P, P, P);
}
}
return velocity_unpack(vector);
}
#endif

View File

@ -1,58 +0,0 @@
/**
* Fullscreen pass that compute motion vector for static geometry.
* Animated geometry has already written correct motion vectors.
*/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
#define is_valid_output(img_) (imageSize(img_).x > 1)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec4 motion = imageLoad(velocity_view_img, texel);
bool pixel_has_valid_motion = (motion.x != VELOCITY_INVALID);
float depth = texelFetch(depth_tx, texel, 0).r;
bool is_background = (depth == 1.0f);
vec2 uv = vec2(texel) * drw_view.viewport_size_inverse;
vec3 P_next, P_prev, P_curr;
if (pixel_has_valid_motion) {
/* Animated geometry. View motion already computed during prepass. Convert only to camera. */
// P_prev = get_world_space_from_depth(uv + motion.xy, 0.5);
// P_curr = get_world_space_from_depth(uv, 0.5);
// P_next = get_world_space_from_depth(uv + motion.zw, 0.5);
return;
}
else if (is_background) {
/* NOTE: Use viewCameraVec to avoid imprecision if camera is far from origin. */
vec3 vV = viewCameraVec(get_view_space_from_depth(uv, 1.0));
vec3 V = transform_direction(ViewMatrixInverse, vV);
/* Background has no motion under camera translation. Translate view vector with the camera. */
/* WATCH(fclem): Might create precision issues. */
P_next = camera_next.viewinv[3].xyz + V;
P_curr = camera_curr.viewinv[3].xyz + V;
P_prev = camera_prev.viewinv[3].xyz + V;
}
else {
/* Static geometry. No translation in world space. */
P_curr = get_world_space_from_depth(uv, depth);
P_prev = P_curr;
P_next = P_curr;
}
vec4 vel_camera, vel_view;
velocity_camera(P_prev, P_curr, P_next, vel_camera, vel_view);
if (in_texture_range(texel, depth_tx)) {
imageStore(velocity_view_img, texel, vel_view);
if (is_valid_output(velocity_camera_img)) {
imageStore(velocity_camera_img, texel, vel_camera);
}
}
}

View File

@ -4,7 +4,7 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(eevee_film)
.uniform_buf(1, "FilmData", "film_buf")
.uniform_buf(4, "FilmData", "film_buf")
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
.sampler(1, ImageType::FLOAT_2D, "combined_tx")
.sampler(2, ImageType::FLOAT_2D, "normal_tx")
@ -20,15 +20,19 @@ GPU_SHADER_CREATE_INFO(eevee_film)
.sampler(12, ImageType::FLOAT_2D, "ambient_occlusion_tx")
.sampler(13, ImageType::FLOAT_2D_ARRAY, "aov_color_tx")
.sampler(14, ImageType::FLOAT_2D_ARRAY, "aov_value_tx")
/* Color History for TAA needs to be sampler to leverage bilinear sampling. */
.sampler(15, ImageType::FLOAT_2D, "in_combined_tx")
// .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx") /* TODO */
.image(0, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "in_weight_img")
.image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img")
.image(2, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_combined_img")
/* Color History for TAA needs to be sampler to leverage bilinear sampling. */
//.image(2, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_combined_img")
.image(3, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_combined_img")
.image(4, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "depth_img")
.image(5, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "color_accum_img")
.image(6, GPU_R16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "value_accum_img")
.additional_info("eevee_shared")
.additional_info("eevee_velocity_camera")
.additional_info("draw_view");
GPU_SHADER_CREATE_INFO(eevee_film_frag)

View File

@ -31,26 +31,7 @@ GPU_SHADER_CREATE_INFO(eevee_velocity_geom)
.storage_buf(
7, Qualifier::READ, "VelocityIndex", "velocity_indirection_buf[]", Frequency::PASS)
.vertex_out(eevee_velocity_surface_iface)
.fragment_out(0, Type::VEC4, "out_velocity_view")
.fragment_out(0, Type::VEC4, "out_velocity")
.additional_info("eevee_velocity_camera");
/** \} */
/* -------------------------------------------------------------------- */
/** \name Velocity Resolve
*
* Computes velocity for static objects.
* Also converts motion to camera space (as opposed to view space) if needed.
* \{ */
GPU_SHADER_CREATE_INFO(eevee_velocity_resolve)
.do_static_compilation(true)
.local_group_size(8, 8)
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
.image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_view_img")
.image(1, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "velocity_camera_img")
.additional_info("eevee_shared")
.compute_source("eevee_velocity_resolve_comp.glsl")
.additional_info("draw_view", "eevee_velocity_camera");
/** \} */

View File

@ -887,7 +887,6 @@ bool DRW_object_is_in_edit_mode(const struct Object *ob);
* we are rendering or drawing in the viewport.
*/
int DRW_object_visibility_in_active_context(const struct Object *ob);
bool DRW_object_is_flat_normal(const struct Object *ob);
bool DRW_object_use_hide_faces(const struct Object *ob);
bool DRW_object_is_visible_psys_in_active_context(const struct Object *object,

View File

@ -212,17 +212,6 @@ int DRW_object_visibility_in_active_context(const Object *ob)
return BKE_object_visibility(ob, mode);
}
bool DRW_object_is_flat_normal(const Object *ob)
{
if (ob->type == OB_MESH) {
const Mesh *me = ob->data;
if (me->mpoly && me->mpoly[0].flag & ME_SMOOTH) {
return false;
}
}
return true;
}
bool DRW_object_use_hide_faces(const struct Object *ob)
{
if (ob->type == OB_MESH) {

View File

@ -89,6 +89,10 @@ void DRW_volume_free(void)
static GPUTexture *grid_default_texture(eGPUDefaultValue default_value)
{
if (g_data.dummy_one == nullptr) {
drw_volume_globals_init();
}
switch (default_value) {
case GPU_DEFAULT_0:
return g_data.dummy_zero;

View File

@ -5,63 +5,71 @@
/** \name Math intersection & projection functions.
* \{ */
float point_plane_projection_dist(vec3 lineorigin, vec3 planeorigin, vec3 planenormal)
float point_plane_projection_dist(vec3 line_origin, vec3 plane_origin, vec3 plane_normal)
{
return dot(planenormal, planeorigin - lineorigin);
return dot(plane_normal, plane_origin - line_origin);
}
float line_plane_intersect_dist(vec3 lineorigin,
vec3 linedirection,
vec3 planeorigin,
vec3 planenormal)
float line_plane_intersect_dist(vec3 line_origin,
vec3 line_direction,
vec3 plane_origin,
vec3 plane_normal)
{
return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection);
return dot(plane_normal, plane_origin - line_origin) / dot(plane_normal, line_direction);
}
float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec4 plane)
float line_plane_intersect_dist(vec3 line_origin, vec3 line_direction, vec4 plane)
{
vec3 plane_co = plane.xyz * (-plane.w / len_squared(plane.xyz));
vec3 h = lineorigin - plane_co;
return -dot(plane.xyz, h) / dot(plane.xyz, linedirection);
vec3 h = line_origin - plane_co;
return -dot(plane.xyz, h) / dot(plane.xyz, line_direction);
}
vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
vec3 line_plane_intersect(vec3 line_origin,
vec3 line_direction,
vec3 plane_origin,
vec3 plane_normal)
{
float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal);
return lineorigin + linedirection * dist;
float dist = line_plane_intersect_dist(line_origin, line_direction, plane_origin, plane_normal);
return line_origin + line_direction * dist;
}
vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec4 plane)
vec3 line_plane_intersect(vec3 line_origin, vec3 line_direction, vec4 plane)
{
float dist = line_plane_intersect_dist(lineorigin, linedirection, plane);
return lineorigin + linedirection * dist;
float dist = line_plane_intersect_dist(line_origin, line_direction, plane);
return line_origin + line_direction * dist;
}
float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
float line_aligned_plane_intersect_dist(vec3 line_origin, vec3 line_direction, vec3 plane_origin)
{
/* aligned plane normal */
vec3 L = planeorigin - lineorigin;
float diskdist = length(L);
vec3 planenormal = -normalize(L);
return -diskdist / dot(planenormal, linedirection);
vec3 L = plane_origin - line_origin;
float disk_dist = length(L);
vec3 plane_normal = -normalize(L);
return -disk_dist / dot(plane_normal, line_direction);
}
vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
vec3 line_aligned_plane_intersect(vec3 line_origin, vec3 line_direction, vec3 plane_origin)
{
float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin);
float dist = line_aligned_plane_intersect_dist(line_origin, line_direction, plane_origin);
if (dist < 0) {
/* if intersection is behind we fake the intersection to be
* really far and (hopefully) not inside the radius of interest */
dist = 1e16;
}
return lineorigin + linedirection * dist;
return line_origin + line_direction * dist;
}
float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection)
/**
* Returns intersection distance between the unit sphere and the line
* with the assumption that \a line_origin is contained in the unit sphere.
* It will always returns the farthest intersection.
*/
float line_unit_sphere_intersect_dist(vec3 line_origin, vec3 line_direction)
{
float a = dot(linedirection, linedirection);
float b = dot(linedirection, lineorigin);
float c = dot(lineorigin, lineorigin) - 1;
float a = dot(line_direction, line_direction);
float b = dot(line_direction, line_origin);
float c = dot(line_origin, line_origin) - 1;
float dist = 1e15;
float determinant = b * b - a * c;
@ -72,22 +80,44 @@ float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection)
return dist;
}
float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection)
/**
* Returns minimum intersection distance between the unit box and the line
* with the assumption that \a line_origin is contained in the unit box.
* In other words, it will always returns the farthest intersection.
*/
float line_unit_box_intersect_dist(vec3 line_origin, vec3 line_direction)
{
/* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
*/
vec3 firstplane = (vec3(1.0) - lineorigin) / linedirection;
vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection;
vec3 furthestplane = max(firstplane, secondplane);
vec3 first_plane = (vec3(1.0) - line_origin) / line_direction;
vec3 second_plane = (vec3(-1.0) - line_origin) / line_direction;
vec3 farthest_plane = max(first_plane, second_plane);
return min_v3(furthestplane);
return min_v3(farthest_plane);
}
float line_unit_box_intersect_dist_safe(vec3 lineorigin, vec3 linedirection)
float line_unit_box_intersect_dist_safe(vec3 line_origin, vec3 line_direction)
{
vec3 safe_linedirection = max(vec3(1e-8), abs(linedirection)) *
select(vec3(1.0), -vec3(1.0), lessThan(linedirection, vec3(0.0)));
return line_unit_box_intersect_dist(lineorigin, safe_linedirection);
vec3 safe_line_direction = max(vec3(1e-8), abs(line_direction)) *
select(vec3(1.0), -vec3(1.0), lessThan(line_direction, vec3(0.0)));
return line_unit_box_intersect_dist(line_origin, safe_line_direction);
}
/**
* Returns clipping distance (intersection with the nearest plane) with the given axis-aligned
* bound box along \a line_direction.
* Safe even if \a line_direction is degenerate.
* It assumes that an intersection exists (i.e: that \a line_direction points towards the AABB).
*/
float line_aabb_clipping_dist(vec3 line_origin, vec3 line_direction, vec3 aabb_min, vec3 aabb_max)
{
vec3 safe_dir = select(line_direction, vec3(1e-5), lessThan(abs(line_direction), vec3(1e-5)));
vec3 dir_inv = 1.0 / safe_dir;
vec3 first_plane = (aabb_min - line_origin) * dir_inv;
vec3 second_plane = (aabb_max - line_origin) * dir_inv;
vec3 nearest_plane = min(first_plane, second_plane);
return max_v3(nearest_plane);
}
/** \} */
@ -98,8 +128,8 @@ float line_unit_box_intersect_dist_safe(vec3 lineorigin, vec3 linedirection)
void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
{
vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
T = normalize(cross(UpVector, N));
vec3 up_vector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
T = normalize(cross(up_vector, N));
B = cross(N, T);
}

View File

@ -1162,7 +1162,7 @@ static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
curve_draw_event_add_first(op, event);
}
}
else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
else if (ISMOUSE_MOTION(event->type)) {
if (cdd->state == CURVE_DRAW_PAINTING) {
const float mval_fl[2] = {UNPACK2(event->mval)};
if (len_squared_v2v2(mval_fl, cdd->prev.mval) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) {

View File

@ -1622,7 +1622,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (ISMOUSE_MOTION(event->type)) {
/* Check if dragging */
if (!cpd->dragging && WM_event_drag_test(event, event->prev_press_xy)) {
cpd->dragging = true;

View File

@ -2641,7 +2641,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
/* handle mode-specific events */
if (p->status == GP_STATUS_PAINTING) {
/* handle painting mouse-movements? */
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
annotation_add_missing_events(C, op, event, p);

View File

@ -3755,7 +3755,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* handle mode-specific events */
if (p->status == GP_STATUS_PAINTING) {
/* handle painting mouse-movements? */
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
bool is_speed_guide = ((guide->use_guide) &&
(p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));

View File

@ -10,8 +10,33 @@
extern "C" {
#endif
struct Curves;
void ED_operatortypes_sculpt_curves(void);
#ifdef __cplusplus
# include "BLI_index_mask.hh"
# include "BLI_vector.hh"
namespace blender::ed::sculpt_paint {
/**
* Find curves that have any point selected (a selection factor greater than zero),
* or curves that have their own selection factor greater than zero.
*/
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
/**
* Find points that are selected (a selection factor greater than zero),
* or points in curves with a selection factor greater than zero).
*/
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices);
} // namespace blender::ed::sculpt_paint
#endif
#ifdef __cplusplus
}
#endif

View File

@ -479,7 +479,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
}
else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
else if (ISMOUSE_MOTION(event->type)) {
if (eye->accum_start) {
/* button is pressed so keep sampling */
eyedropper_color_sample(C, eye, event->xy);

View File

@ -83,14 +83,14 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
if (but == NULL) {
return;
}
/* Get paths for src... */
/* Get paths for the source. */
PointerRNA *target_ptr = &but->rnapoin;
PropertyRNA *target_prop = but->rnaprop;
const int target_index = but->rnaindex;
char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
/* ... and destination */
/* Get paths for the destination. */
char *dst_path = RNA_path_from_ID_to_property(&ddr->ptr, ddr->prop);
/* Now create driver(s) */

View File

@ -4509,7 +4509,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
}
}
else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
if (event->type == EVT_UNKNOWNKEY) {
@ -4575,7 +4575,7 @@ static int ui_do_but_KEYEVT(bContext *C,
}
}
else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
@ -8097,7 +8097,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
#ifdef USE_DRAG_MULTINUM
data = but->active;
if (data) {
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) ||
if (ISMOUSE_MOTION(event->type) ||
/* if we started dragging, progress on any event */
(data->multi_data.init == BUTTON_MULTI_INIT_SETUP)) {
if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
@ -10096,8 +10096,7 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock
/* Pass, needed to click-exit outside of non-floating menus. */
ui_region_auto_open_clear(but->active->region);
}
else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) &&
ISMOUSE(event->type)) {
else if (ISMOUSE_BUTTON(event->type)) {
if (!ui_but_contains_point_px(but, but->active->region, event->xy)) {
but = NULL;
}

View File

@ -2318,7 +2318,7 @@ int ui_handler_panel_region(bContext *C,
const uiBut *active_but)
{
/* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}

View File

@ -168,7 +168,8 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C,
const wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region, win->eventstate->xy);
uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region,
win->eventstate->xy);
AbstractTreeViewItem *hovered_item = from_item_handle<AbstractTreeViewItem>(hovered_item_handle);
BLI_assert(hovered_item != nullptr);

View File

@ -8692,7 +8692,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
* Free the data here, then use #point_normals_ensure to add it back on demand. */
if (ret == OPERATOR_PASS_THROUGH) {
/* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (!ISMOUSE_MOTION(event->type)) {
point_normals_free(op);
}
}

View File

@ -2079,7 +2079,8 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op)
Object *surface_ob = CTX_data_active_object(C);
BLI_assert(surface_ob != nullptr);
Object *curves_ob = ED_object_add_type(C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits);
Object *curves_ob = ED_object_add_type(
C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits);
BKE_object_apply_mat4(curves_ob, surface_ob->obmat, false, false);
/* Set surface object. */

View File

@ -818,7 +818,7 @@ static bool modifier_apply_obdata(
/* Create a temporary geometry set and component. */
GeometrySet geometry_set;
geometry_set.get_component_for_write<CurveComponent>().replace(
&curves, GeometryOwnershipType::Editable);
&curves, GeometryOwnershipType::ReadOnly);
ModifierEvalContext mectx = {depsgraph, ob, (ModifierApplyFlag)0};
mti->modifyGeometrySet(md_eval, &mectx, &geometry_set);
@ -833,14 +833,11 @@ static bool modifier_apply_obdata(
.attributes_for_write()
.remove_anonymous();
/* If the modifier's output is a different curves data-block, copy the relevant information to
* the original. */
if (&curves_eval != &curves) {
blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move(
blender::bke::CurvesGeometry::wrap(curves_eval.geometry));
Main *bmain = DEG_get_bmain(depsgraph);
BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
}
/* Copy the relevant information to the original. */
blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move(
blender::bke::CurvesGeometry::wrap(curves_eval.geometry));
Main *bmain = DEG_get_bmain(depsgraph);
BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
}
else {
/* TODO: implement for point clouds and volumes. */
@ -1439,7 +1436,6 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
const bool do_report = RNA_boolean_get(op->ptr, "report");
const bool do_single_user = RNA_boolean_get(op->ptr, "single_user");
const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata");
@ -1448,6 +1444,8 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
return OPERATOR_CANCELLED;
}
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (do_single_user && ID_REAL_USERS(ob->data) > 1) {
ED_object_single_obdata_user(bmain, scene, ob);
BKE_main_id_newptr_and_tag_clear(bmain);

View File

@ -2222,7 +2222,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
bool is_finished = false;
if (ISMOUSE(xfd->init_event)) {
if (ISMOUSE_BUTTON(xfd->init_event)) {
if ((event->type == xfd->init_event) && (event->val == KM_RELEASE)) {
is_finished = true;
}

View File

@ -366,7 +366,7 @@ void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &n
length_parameterize::sample_at_lengths(orig_lengths, new_lengths, indices, factors);
Array<float3> new_positions(positions.size() - 1);
length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions);
length_parameterize::interpolate<float3>(positions, indices, factors, new_positions);
positions.drop_back(1).copy_from(new_positions);
positions.last() = new_last_position;
}

View File

@ -127,7 +127,7 @@ class ShrinkCurvesEffect : public CurvesEffect {
lp::sample_at_lengths(data.old_lengths, data.sample_lengths, data.indices, data.factors);
lp::linear_interpolation<float3>(data.old_positions, data.indices, data.factors, positions);
lp::interpolate<float3>(data.old_positions, data.indices, data.factors, positions);
}
};

View File

@ -14,6 +14,8 @@
#include "BKE_attribute.h"
#include "BKE_curves.hh"
#include "ED_curves_sculpt.h"
struct ARegion;
struct RegionView3D;
struct Depsgraph;
@ -98,12 +100,6 @@ VArray<float> get_curves_selection(const Curves &curves_id);
*/
VArray<float> get_point_selection(const Curves &curves_id);
/**
* Find curves that have any point selected (a selection factor greater than zero),
* or curves that have their own selection factor greater than zero.
*/
IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position);
class CurvesSculptCommonContext {

View File

@ -6,6 +6,8 @@
#include "curves_sculpt_intern.hh"
#include "ED_curves_sculpt.h"
namespace blender::ed::sculpt_paint {
static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain)
@ -62,7 +64,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
case ATTR_DOMAIN_POINT: {
const VArray<float> selection = curves.selection_point_float();
if (selection.is_single()) {
return selection.get_internal_single() == 0.0f ? IndexMask(0) :
return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
return index_mask_ops::find_indices_based_on_predicate(
@ -78,7 +80,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
case ATTR_DOMAIN_CURVE: {
const VArray<float> selection = curves.selection_curve_float();
if (selection.is_single()) {
return selection.get_internal_single() == 0.0f ? IndexMask(0) :
return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
return index_mask_ops::find_indices_based_on_predicate(
@ -102,4 +104,49 @@ IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_i
r_indices);
}
static IndexMask retrieve_selected_points(const CurvesGeometry &curves,
const eAttrDomain domain,
Vector<int64_t> &r_indices)
{
switch (domain) {
case ATTR_DOMAIN_POINT: {
const VArray<float> selection = curves.selection_point_float();
if (selection.is_single()) {
return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.points_num());
}
return index_mask_ops::find_indices_based_on_predicate(
curves.points_range(), 2048, r_indices, [&](const int i) {
return selection[i] > 0.0f;
});
}
case ATTR_DOMAIN_CURVE: {
const VArray<float> selection = curves.selection_curve_float();
if (selection.is_single()) {
return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.points_num());
}
const VArray<float> point_selection = curves.adapt_domain(
selection, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
return index_mask_ops::find_indices_based_on_predicate(
curves.points_range(), 2048, r_indices, [&](const int i) {
return point_selection[i] > 0.0f;
});
}
default:
BLI_assert_unreachable();
return {};
}
}
IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices)
{
if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) {
return CurvesGeometry::wrap(curves_id.geometry).points_range();
}
return retrieve_selected_points(CurvesGeometry::wrap(curves_id.geometry),
eAttrDomain(curves_id.selection_domain),
r_indices);
}
} // namespace blender::ed::sculpt_paint

View File

@ -123,7 +123,7 @@ typedef struct PaintStroke {
StrokeRedraw redraw;
StrokeDone done;
bool original; /* Raycast original mesh at start of stroke */
bool original; /* Ray-cast original mesh at start of stroke. */
} PaintStroke;
/*** Cursors ***/
@ -1550,8 +1550,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
paint_stroke_line_constrain(stroke, mouse);
if (stroke->stroke_started &&
(first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) {
if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) {
if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) ||
(br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position);
@ -1561,7 +1560,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
}
else if (first_modal ||
/* regular dabs */
(!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) ||
(!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) ||
/* airbrush */
((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
event->customdata == stroke->timer)) {

View File

@ -3362,7 +3362,7 @@ static void do_brush_action(Sculpt *sd,
if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) {
if (!ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
ss, 1.0f, 0.0f, 0.0f, false, true);
ob, 1.0f, 0.0f, 0.0f, false, true);
SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
}
SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);

View File

@ -611,13 +611,17 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph)
static ListBase *cloth_brush_collider_cache_create(Object *object, Depsgraph *depsgraph)
{
ListBase *cache = NULL;
DEG_OBJECT_ITER_BEGIN (depsgraph,
ob,
DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
DEG_ITER_OBJECT_FLAG_DUPLI) {
if (STREQ(object->id.name, ob->id.name)) {
continue;
}
CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type(
ob, eModifierType_Collision);
if (!cmd) {
@ -1029,13 +1033,14 @@ static void cloth_sim_initialize_default_node_state(SculptSession *ss,
MEM_SAFE_FREE(nodes);
}
SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
SculptClothSimulation *SCULPT_cloth_brush_simulation_create(Object *ob,
const float cloth_mass,
const float cloth_damping,
const float cloth_softbody_strength,
const bool use_collisions,
const bool needs_deform_coords)
{
SculptSession *ss = ob->sculpt;
const int totverts = SCULPT_vertex_count_get(ss);
SculptClothSimulation *cloth_sim;
@ -1073,7 +1078,7 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
cloth_sim->softbody_strength = cloth_softbody_strength;
if (use_collisions) {
cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph);
cloth_sim->collider_list = cloth_brush_collider_cache_create(ob, ss->depsgraph);
}
cloth_sim_initialize_default_node_state(ss, cloth_sim);
@ -1184,7 +1189,7 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* The simulation structure only needs to be created on the first symmetry pass. */
if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
ss,
ob,
brush->cloth_mass,
brush->cloth_damping,
brush->cloth_constraint_softbody_strength,
@ -1571,7 +1576,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions");
ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
ss,
ob,
cloth_mass,
cloth_damping,
0.0f,

View File

@ -1344,7 +1344,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim);
/* Public functions. */
struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct SculptSession *ss,
struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct Object *ob,
float cloth_mass,
float cloth_damping,
float cloth_softbody_strength,

View File

@ -31,8 +31,11 @@
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
#include "BLI_linklist.h"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@ -90,7 +93,9 @@ static CLG_LogRef LOG = {"ed.outliner.tools"};
using namespace blender::ed::outliner;
using blender::Map;
using blender::Set;
using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name ID/Library/Data Set/Un-link Utilities
@ -777,6 +782,25 @@ static void id_local_fn(bContext *C,
}
}
struct OutlinerLiboverrideDataIDRoot {
/** The linked ID that was selected for override. */
ID *id_root_reference;
/** The root of the override hierarchy to which the override of `id_root` belongs, once
* known/created. */
ID *id_hierarchy_root_override;
/** The ID that was detected as being a good candidate as instanciation hint for newly overridden
* objects, may be null.
*
* \note Typically currently only used when the root ID to override is a collection instanced by
* an emtpy object. */
ID *id_instance_hint;
/** If this override comes from an instancing object (which would be `id_instance_hint` then). */
bool is_override_instancing_object;
};
struct OutlinerLibOverrideData {
bool do_hierarchy;
@ -789,21 +813,44 @@ struct OutlinerLibOverrideData {
* solving broken overrides while not losing *all* of your overrides. */
bool do_resync_hierarchy_enforce;
/** The override hierarchy root, when known/created. */
ID *id_hierarchy_root_override;
/** A hash of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
/** A set of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
* their newly-created liboverrides in post-process step of override hierarchy creation. */
Set<uint> selected_id_uid;
/** A mapping from the found hierarchy roots to a linked list of IDs to override for each of
* these roots.
*
* \note the key may be either linked (in which case it will be replaced by the newly created
* override), or an actual already existing override. */
Map<ID *, Vector<OutlinerLiboverrideDataIDRoot>> id_hierarchy_roots;
/** All 'session_uuid' of all hierarchy root IDs used or created by the operation. */
Set<uint> id_hierarchy_roots_uid;
void id_root_add(ID *id_hierarchy_root_reference,
ID *id_root_reference,
ID *id_instance_hint,
const bool is_override_instancing_object)
{
OutlinerLiboverrideDataIDRoot id_root_data;
id_root_data.id_root_reference = id_root_reference;
id_root_data.id_hierarchy_root_override = nullptr;
id_root_data.id_instance_hint = id_instance_hint;
id_root_data.is_override_instancing_object = is_override_instancing_object;
Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
id_hierarchy_root_reference);
value.append(id_root_data);
}
};
/* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override
* hierarchy. */
static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED(C),
ReportList *UNUSED(reports),
static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
ReportList *reports,
Scene *UNUSED(scene),
TreeElement *UNUSED(te),
TreeStoreElem *UNUSED(tsep),
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data)
{
@ -829,28 +876,6 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
}
static void id_override_library_create_fn(bContext *C,
ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data)
{
BLI_assert(TSE_IS_REAL_ID(tselem));
/* We can only safely apply this operation on one item at a time, so only do it on the active
* one. */
if ((tselem->flag & TSE_ACTIVE) == 0) {
return;
}
ID *id_root_reference = tselem->id;
OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
const bool do_hierarchy = data->do_hierarchy;
bool success = false;
ID *id_instance_hint = nullptr;
bool is_override_instancing_object = false;
@ -866,15 +891,131 @@ static void id_override_library_create_fn(bContext *C,
}
}
if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) ||
(ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
Main *bmain = CTX_data_main(C);
if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) &&
!(ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
return;
}
id_root_reference->tag |= LIB_TAG_DOIT;
Main *bmain = CTX_data_main(C);
if (do_hierarchy) {
/* Tag all linked parents in tree hierarchy to be also overridden. */
ID *id_hierarchy_root_reference = id_root_reference;
while ((te = te->parent) != nullptr) {
if (!TSE_IS_REAL_ID(te->store_elem)) {
continue;
}
/* Tentative hierarchy root. */
ID *id_current_hierarchy_root = te->store_elem->id;
/* If the parent ID is from a different library than the reference root one, we are done
* with upwards tree processing in any case. */
if (id_current_hierarchy_root->lib != id_root_reference->lib) {
if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
/* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
* get an actual real override. */
continue;
}
/* If the parent ID is already an override, and is valid (i.e. local override), we can
* access its hierarchy root directly. */
if (!ID_IS_LINKED(id_current_hierarchy_root) &&
ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
id_current_hierarchy_root->override_library->reference->lib ==
id_root_reference->lib) {
id_hierarchy_root_reference =
id_current_hierarchy_root->override_library->hierarchy_root;
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
break;
}
if (ID_IS_LINKED(id_current_hierarchy_root)) {
/* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
* would most likely generate invisible/confusing/hard to use and manage overrides. */
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
"Invalid anchor ('%s') found, needed to create library override from "
"data-block '%s'",
id_current_hierarchy_root->name,
id_root_reference->name);
return;
}
/* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
* current `id_hierarchy_root_reference` is our best candidate. */
break;
}
/* If some element in the tree needs to be overridden, but its ID is not overridable,
* abort. */
if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
"Could not create library override from data-block '%s', one of its parents "
"is not overridable ('%s')",
id_root_reference->name,
id_current_hierarchy_root->name);
return;
}
id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
id_hierarchy_root_reference = id_current_hierarchy_root;
}
/* That case can happen when linked data is a complex mix involving several libraries and/or
* linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
* from another library. Do not try to support such cases for now. */
if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
(!ID_IS_LINKED(id_hierarchy_root_reference) &&
ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
id_hierarchy_root_reference->override_library->reference->lib ==
id_root_reference->lib))) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
"Invalid hierarchy root ('%s') found, needed to create library override from "
"data-block '%s'",
id_hierarchy_root_reference->name,
id_root_reference->name);
return;
}
data->id_root_add(id_hierarchy_root_reference,
id_root_reference,
id_instance_hint,
is_override_instancing_object);
}
else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
data->id_root_add(
id_root_reference, id_root_reference, id_instance_hint, is_override_instancing_object);
}
}
static void id_override_library_create_hierarchy(
Main &bmain,
Scene *scene,
ViewLayer *view_layer,
OutlinerLibOverrideData &data,
ID *id_hierarchy_root_reference,
Vector<OutlinerLiboverrideDataIDRoot> &data_idroots,
bool &r_aggregated_success)
{
BLI_assert(ID_IS_LINKED(id_hierarchy_root_reference) ||
ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
const bool do_hierarchy = data.do_hierarchy;
/* NOTE: This process is not the most efficient, but allows to re-use existing code.
* If this becomes a bottle-neck at some point, we need to implement a new
* `BKE_lib_override_library_hierarchy_create()` function able to process several roots inside of
* a same hierarchy in a single call. */
for (OutlinerLiboverrideDataIDRoot &data_idroot : data_idroots) {
/* For now, remap all local usages of linked ID to local override one here. */
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
FOREACH_MAIN_ID_BEGIN (&bmain, id_iter) {
if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) {
id_iter->tag &= ~LIB_TAG_DOIT;
}
@ -884,154 +1025,108 @@ static void id_override_library_create_fn(bContext *C,
}
FOREACH_MAIN_ID_END;
bool success = false;
if (do_hierarchy) {
/* Tag all linked parents in tree hierarchy to be also overridden. */
ID *id_hierarchy_root_reference = id_root_reference;
while ((te = te->parent) != nullptr) {
if (!TSE_IS_REAL_ID(te->store_elem)) {
continue;
}
/* Tentative hierarchy root. */
ID *id_current_hierarchy_root = te->store_elem->id;
/* If the parent ID is from a different library than the reference root one, we are done
* with upwards tree processing in any case. */
if (id_current_hierarchy_root->lib != id_root_reference->lib) {
if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
/* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
* get an actual real override. */
continue;
}
/* If the parent ID is already an override, and is valid (i.e. local override), we can
* access its hierarchy root directly. */
if (!ID_IS_LINKED(id_current_hierarchy_root) &&
ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
id_current_hierarchy_root->override_library->reference->lib ==
id_root_reference->lib) {
id_hierarchy_root_reference =
id_current_hierarchy_root->override_library->hierarchy_root;
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
break;
}
if (ID_IS_LINKED(id_current_hierarchy_root)) {
/* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
* would most likely generate invisible/confusing/hard to use and manage overrides. */
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
"Invalid anchor ('%s') found, needed to create library override from "
"data-block '%s'",
id_current_hierarchy_root->name,
id_root_reference->name);
return;
}
/* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
* current `id_hierarchy_root_reference` is our best candidate. */
break;
}
/* If some element in the tree needs to be overridden, but its ID is not overridable,
* abort. */
if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
"Could not create library override from data-block '%s', one of its parents "
"is not overridable ('%s')",
id_root_reference->name,
id_current_hierarchy_root->name);
return;
}
id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
id_hierarchy_root_reference = id_current_hierarchy_root;
}
/* That case can happen when linked data is a complex mix involving several libraries and/or
* linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
* from another library. Do not try to support such cases for now. */
if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
(!ID_IS_LINKED(id_hierarchy_root_reference) &&
ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
id_hierarchy_root_reference->override_library->reference->lib ==
id_root_reference->lib))) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
"Invalid hierarchy root ('%s') found, needed to create library override from "
"data-block '%s'",
id_hierarchy_root_reference->name,
id_root_reference->name);
return;
}
ID *id_root_override = nullptr;
success = BKE_lib_override_library_create(bmain,
CTX_data_scene(C),
CTX_data_view_layer(C),
success = BKE_lib_override_library_create(&bmain,
scene,
view_layer,
nullptr,
id_root_reference,
data_idroot.id_root_reference,
id_hierarchy_root_reference,
id_instance_hint,
data_idroot.id_instance_hint,
&id_root_override,
data->do_fully_editable);
data.do_fully_editable);
BLI_assert(id_root_override != nullptr);
BLI_assert(!ID_IS_LINKED(id_root_override));
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
if (ID_IS_LINKED(id_hierarchy_root_reference)) {
BLI_assert(
id_root_override->override_library->hierarchy_root->override_library->reference ==
id_hierarchy_root_reference);
data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
}
else {
BLI_assert(id_root_override->override_library->hierarchy_root ==
id_hierarchy_root_reference);
data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
if (success) {
BLI_assert(id_root_override != nullptr);
BLI_assert(!ID_IS_LINKED(id_root_override));
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_override));
if (ID_IS_LINKED(id_hierarchy_root_reference)) {
BLI_assert(id_hierarchy_root_override->override_library->reference ==
id_hierarchy_root_reference);
/* If the hierarchy root reference was a linked data, after the first iteration there is
* now a matching override, which shall be used for all further partial overrides with
* this same hierarchy. */
id_hierarchy_root_reference = id_hierarchy_root_override;
}
else {
BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference);
}
data_idroot.id_hierarchy_root_override = id_hierarchy_root_override;
data.id_hierarchy_roots_uid.add(id_hierarchy_root_override->session_uuid);
}
}
else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr;
else if (ID_IS_OVERRIDABLE_LIBRARY(data_idroot.id_root_reference)) {
ID *id_root_override = BKE_lib_override_library_create_from_id(
&bmain, data_idroot.id_root_reference, true);
success = id_root_override != nullptr;
if (success) {
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
id_root_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
}
/* Cleanup. */
BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_main_id_newptr_and_tag_clear(&bmain);
BKE_main_id_tag_all(&bmain, LIB_TAG_DOIT, false);
}
else {
BLI_assert_unreachable();
}
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
if (success && is_override_instancing_object) {
ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint);
if (success && data_idroot.is_override_instancing_object) {
BLI_assert(GS(data_idroot.id_instance_hint) == ID_OB);
ED_object_base_free_and_unlink(
&bmain, scene, reinterpret_cast<Object *>(data_idroot.id_instance_hint));
}
}
if (!success) {
BKE_reportf(reports,
RPT_WARNING,
"Could not create library override from data-block '%s'",
id_root_reference->name);
r_aggregated_success = r_aggregated_success && success;
}
}
/* Clear system override flag from newly created overrides which linked reference were previously
* selected in the Outliner tree. */
static void id_override_library_create_hierarchy_post_process(bContext *C,
OutlinerLibOverrideData *data)
static void id_override_library_create_hierarchy_process(bContext *C,
ReportList *reports,
OutlinerLibOverrideData &data)
{
Main *bmain = CTX_data_main(C);
ID *id_hierarchy_root_override = data->id_hierarchy_root_override;
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool do_hierarchy = data.do_hierarchy;
bool success = true;
for (auto &&[id_hierarchy_root_reference, data_idroots] : data.id_hierarchy_roots.items()) {
id_override_library_create_hierarchy(
*bmain, scene, view_layer, data, id_hierarchy_root_reference, data_idroots, success);
}
if (!success) {
BKE_reportf(reports,
RPT_WARNING,
"Could not create library override from one or more of the selected data-blocks");
}
if (!do_hierarchy) {
return;
}
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) ||
id_iter->override_library->hierarchy_root != id_hierarchy_root_override) {
if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
continue;
}
if (data->selected_id_uid.contains(id_iter->override_library->reference->session_uuid)) {
if (!data.id_hierarchy_roots_uid.contains(
id_iter->override_library->hierarchy_root->session_uuid)) {
continue;
}
if (data.selected_id_uid.contains(id_iter->override_library->reference->session_uuid) ||
data.selected_id_uid.contains(id_iter->session_uuid)) {
id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
}
}
@ -2254,8 +2349,18 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
OutlinerLibOverrideData override_data{};
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
override_data.do_hierarchy = false;
override_data.do_fully_editable = true;
outliner_do_libdata_operation(C,
op->reports,
scene,
space_outliner,
id_override_library_create_hierarchy_pre_process_fn,
&override_data);
id_override_library_create_hierarchy_process(C, op->reports, override_data);
ED_undo_push(C, "Overridden Data");
break;
}
@ -2263,15 +2368,15 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
override_data.do_fully_editable = U.experimental.use_override_new_fully_editable;
outliner_do_libdata_operation(C,
op->reports,
scene,
space_outliner,
id_override_library_create_hierarchy_pre_process_fn,
&override_data);
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
id_override_library_create_hierarchy_post_process(C, &override_data);
id_override_library_create_hierarchy_process(C, op->reports, override_data);
ED_undo_push(C, "Overridden Data Hierarchy");
break;

View File

@ -5,6 +5,7 @@
#include "BKE_attribute.hh"
#include "BKE_context.h"
#include "BKE_curves.hh"
#include "BKE_editmesh.h"
#include "BKE_geometry_fields.hh"
#include "BKE_global.h"
@ -22,6 +23,7 @@
#include "DEG_depsgraph_query.h"
#include "ED_curves_sculpt.h"
#include "ED_spreadsheet.h"
#include "NOD_geometry_nodes_eval_log.hh"
@ -232,23 +234,31 @@ int GeometryDataSource::tot_rows() const
return attributes.domain_size(domain_);
}
/**
* Only data sets corresponding to mesh objects in edit mode currently support selection filtering.
*/
bool GeometryDataSource::has_selection_filter() const
{
Object *object_orig = DEG_get_original_object(object_eval_);
if (object_orig->type != OB_MESH) {
return false;
switch (component_->type()) {
case GEO_COMPONENT_TYPE_MESH: {
if (object_orig->type != OB_MESH) {
return false;
}
if (object_orig->mode != OB_MODE_EDIT) {
return false;
}
return true;
}
case GEO_COMPONENT_TYPE_CURVE: {
if (object_orig->type != OB_CURVES) {
return false;
}
if (object_orig->mode != OB_MODE_SCULPT_CURVES) {
return false;
}
return true;
}
default:
return false;
}
if (object_orig->mode != OB_MODE_EDIT) {
return false;
}
if (component_->type() != GEO_COMPONENT_TYPE_MESH) {
return false;
}
return true;
}
IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) const
@ -256,50 +266,73 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c
std::lock_guard lock{mutex_};
const IndexMask full_range(this->tot_rows());
BLI_assert(object_eval_->mode == OB_MODE_EDIT);
BLI_assert(component_->type() == GEO_COMPONENT_TYPE_MESH);
Object *object_orig = DEG_get_original_object(object_eval_);
const MeshComponent *mesh_component = static_cast<const MeshComponent *>(component_);
const Mesh *mesh_eval = mesh_component->get_for_read();
Mesh *mesh_orig = (Mesh *)object_orig->data;
BMesh *bm = mesh_orig->edit_mesh->bm;
BM_mesh_elem_table_ensure(bm, BM_VERT);
switch (component_->type()) {
case GEO_COMPONENT_TYPE_MESH: {
BLI_assert(object_eval_->type == OB_MESH);
BLI_assert(object_eval_->mode == OB_MODE_EDIT);
Object *object_orig = DEG_get_original_object(object_eval_);
const Mesh *mesh_eval = geometry_set_.get_mesh_for_read();
const bke::AttributeAccessor attributes_eval = bke::mesh_attributes(*mesh_eval);
Mesh *mesh_orig = (Mesh *)object_orig->data;
BMesh *bm = mesh_orig->edit_mesh->bm;
BM_mesh_elem_table_ensure(bm, BM_VERT);
const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
if (orig_indices != nullptr) {
/* Use CD_ORIGINDEX layer if it exists. */
VArray<bool> selection = mesh_component->attributes()->adapt_domain<bool>(
VArray<bool>::ForFunc(mesh_eval->totvert,
[bm, orig_indices](int vertex_index) -> bool {
const int i_orig = orig_indices[vertex_index];
if (i_orig < 0) {
return false;
}
if (i_orig >= bm->totvert) {
return false;
}
BMVert *vert = bm->vtable[i_orig];
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
}),
ATTR_DOMAIN_POINT,
domain_);
return index_mask_ops::find_indices_from_virtual_array(full_range, selection, 1024, indices);
const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
if (orig_indices != nullptr) {
/* Use CD_ORIGINDEX layer if it exists. */
VArray<bool> selection = attributes_eval.adapt_domain<bool>(
VArray<bool>::ForFunc(mesh_eval->totvert,
[bm, orig_indices](int vertex_index) -> bool {
const int i_orig = orig_indices[vertex_index];
if (i_orig < 0) {
return false;
}
if (i_orig >= bm->totvert) {
return false;
}
const BMVert *vert = BM_vert_at_index(bm, i_orig);
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
}),
ATTR_DOMAIN_POINT,
domain_);
return index_mask_ops::find_indices_from_virtual_array(
full_range, selection, 1024, indices);
}
if (mesh_eval->totvert == bm->totvert) {
/* Use a simple heuristic to match original vertices to evaluated ones. */
VArray<bool> selection = attributes_eval.adapt_domain<bool>(
VArray<bool>::ForFunc(mesh_eval->totvert,
[bm](int vertex_index) -> bool {
const BMVert *vert = BM_vert_at_index(bm, vertex_index);
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
}),
ATTR_DOMAIN_POINT,
domain_);
return index_mask_ops::find_indices_from_virtual_array(
full_range, selection, 2048, indices);
}
return full_range;
}
case GEO_COMPONENT_TYPE_CURVE: {
BLI_assert(object_eval_->type == OB_CURVES);
BLI_assert(object_eval_->mode == OB_MODE_SCULPT_CURVES);
const CurveComponent &component = static_cast<const CurveComponent &>(*component_);
const Curves &curves_id = *component.get_for_read();
switch (domain_) {
case ATTR_DOMAIN_POINT:
return sculpt_paint::retrieve_selected_points(curves_id, indices);
case ATTR_DOMAIN_CURVE:
return sculpt_paint::retrieve_selected_curves(curves_id, indices);
default:
BLI_assert_unreachable();
}
return full_range;
}
default:
return full_range;
}
if (mesh_eval->totvert == bm->totvert) {
/* Use a simple heuristic to match original vertices to evaluated ones. */
VArray<bool> selection = mesh_component->attributes()->adapt_domain<bool>(
VArray<bool>::ForFunc(mesh_eval->totvert,
[bm](int vertex_index) -> bool {
BMVert *vert = bm->vtable[vertex_index];
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
}),
ATTR_DOMAIN_POINT,
domain_);
return index_mask_ops::find_indices_from_virtual_array(full_range, selection, 2048, indices);
}
return full_range;
}
void VolumeDataSource::foreach_default_column_ids(

View File

@ -68,10 +68,6 @@ class GeometryDataSource : public DataSource {
return object_eval_;
}
/**
* Only data sets corresponding to mesh objects in edit mode currently support selection
* filtering.
*/
bool has_selection_filter() const override;
IndexMask apply_selection_filter(Vector<int64_t> &indices) const;

View File

@ -659,7 +659,7 @@ static void walkEvent(WalkInfo *walk, const wmEvent *event)
if (event->type == TIMER && event->customdata == walk->timer) {
walk->redraw = true;
}
else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
else if (ISMOUSE_MOTION(event->type)) {
#ifdef USE_TABLET_SUPPORT
if ((walk->is_cursor_absolute == false) && event->tablet.is_motion_absolute) {

View File

@ -1327,7 +1327,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
if (t->redraw && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
if (t->redraw && !ISMOUSE_MOTION(event->type)) {
WM_window_status_area_tag_redraw(CTX_wm_window(t->context));
}

View File

@ -555,7 +555,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else {
/* Release confirms preference should not affect node editor (T69288, T70504). */
if (ISMOUSE(t->launch_event) &&
if (ISMOUSE_BUTTON(t->launch_event) &&
((U.flag & USER_RELEASECONFIRM) || (t->spacetype == SPACE_NODE))) {
/* Global "release confirm" on mouse bindings */
t->flag |= T_RELEASE_CONFIRM;

View File

@ -252,10 +252,10 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
if (curve_types[i_curve] == CURVE_TYPE_POLY) {
length_parameterize::linear_interpolation(src.slice(src_points),
sample_indices.as_span().slice(dst_points),
sample_factors.as_span().slice(dst_points),
dst.slice(dst_points));
length_parameterize::interpolate(src.slice(src_points),
sample_indices.as_span().slice(dst_points),
sample_factors.as_span().slice(dst_points),
dst.slice(dst_points));
}
else {
const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size();
@ -264,10 +264,10 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>();
src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated);
length_parameterize::linear_interpolation(evaluated.as_span(),
sample_indices.as_span().slice(dst_points),
sample_factors.as_span().slice(dst_points),
dst.slice(dst_points));
length_parameterize::interpolate(evaluated.as_span(),
sample_indices.as_span().slice(dst_points),
sample_factors.as_span().slice(dst_points),
dst.slice(dst_points));
}
}
});
@ -277,10 +277,10 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
for (const int i_curve : sliced_selection) {
const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
length_parameterize::linear_interpolation(evaluated_positions.slice(src_points),
sample_indices.as_span().slice(dst_points),
sample_factors.as_span().slice(dst_points),
dst_positions.slice(dst_points));
length_parameterize::interpolate(evaluated_positions.slice(src_points),
sample_indices.as_span().slice(dst_points),
sample_factors.as_span().slice(dst_points),
dst_positions.slice(dst_points));
}
/* Fill the default value for non-interpolating attributes that still must be copied. */

View File

@ -17,8 +17,8 @@
extern "C" {
#endif
/* GPU backends abstract the differences between different APIs. GPU_context_create
* automatically initializes the backend, and GPU_context_discard frees it when there
/* GPU back-ends abstract the differences between different APIs. #GPU_context_create
* automatically initializes the back-end, and #GPU_context_discard frees it when there
* are no more contexts. */
bool GPU_backend_supported(void);
eGPUBackendType GPU_backend_get_type(void);

View File

@ -51,7 +51,6 @@ void ShaderBuilder::init()
void ShaderBuilder::exit()
{
GPU_backend_exit();
GPU_exit();
GPU_context_discard(gpu_context_);

View File

@ -10,8 +10,8 @@
#include "mtl_backend.hh"
#include "mtl_context.hh"
#include "mtl_framebuffer.hh"
#include "mtl_uniform_buffer.hh"
#include "mtl_query.hh"
#include "mtl_uniform_buffer.hh"
#include "gpu_capabilities_private.hh"
#include "gpu_platform_private.hh"

View File

@ -68,7 +68,7 @@ gpu::MTLBuffer *MTLBufferPool::allocate_with_data(uint64_t size,
bool cpu_visible,
const void *data)
{
/* Allocate buffer with default HW-compatible alignemnt of 256 bytes.
/* Allocate buffer with default HW-compatible alignment of 256 bytes.
* See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */
return this->allocate_aligned_with_data(size, 256, cpu_visible, data);
}

View File

@ -104,8 +104,8 @@ void MTLQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values)
BLI_assert(ctx->get_inside_frame());
}
/* Wait for GPU operatiosn to complete and for query buffer contents
* to be synchronised back to host memory. */
/* Wait for GPU operations to complete and for query buffer contents
* to be synchronized back to host memory. */
GPU_finish();
/* Iterate through all possible visibility buffers and copy results into provided

View File

@ -25,7 +25,7 @@ class MTLUniformBuf : public UniformBuf {
* have yet been allocated. */
bool has_data_ = false;
/* Bindstate tracking. */
/* Bind-state tracking. */
int bind_slot_ = -1;
MTLContext *bound_ctx_ = nullptr;

View File

@ -107,7 +107,7 @@ void MTLUniformBuf::bind(int slot)
MEM_SAFE_FREE(data_);
}
/* Ensure there is atleast an empty dummy buffer. */
/* Ensure there is at least an empty dummy buffer. */
if (metal_buffer_ == nullptr) {
this->update(nullptr);
}

View File

@ -475,18 +475,19 @@ static int rna_Image_frame_duration_get(PointerRNA *ptr)
Image *ima = (Image *)ptr->owner_id;
int duration = 1;
if (!BKE_image_has_anim(ima)) {
/* Ensure image has been loaded into memory and frame duration is known. */
void *lock;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
BKE_image_release_ibuf(ima, ibuf, lock);
}
if (BKE_image_has_anim(ima)) {
struct anim *anim = ((ImageAnim *)ima->anims.first)->anim;
if (anim) {
duration = IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN);
}
}
else {
/* acquire ensures ima->anim is set, if possible! */
void *lock;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
BKE_image_release_ibuf(ima, ibuf, lock);
}
return duration;
}

View File

@ -1754,12 +1754,12 @@ static void rna_def_color_balance(BlenderRNA *brna)
prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN);
RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`");
RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
prop = RNA_def_property(srna, "invert_slope", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_SLOPE);
RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color`");
RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
prop = RNA_def_property(srna, "invert_offset", PROP_BOOLEAN, PROP_NONE);

Some files were not shown because too many files have changed in this diff Show More