WIP: Rewrite asset browser as separate editor #107576
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 = {});
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -199,7 +199,7 @@ void Instance::render_sync()
|
|||
**/
|
||||
void Instance::render_sample()
|
||||
{
|
||||
if (sampling.finished()) {
|
||||
if (sampling.finished_viewport()) {
|
||||
film.display();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 "";
|
||||
|
|
|
@ -29,8 +29,6 @@ enum eShaderType {
|
|||
FILM_FRAG = 0,
|
||||
FILM_COMP,
|
||||
|
||||
VELOCITY_RESOLVE,
|
||||
|
||||
MAX_SHADER_TYPE,
|
||||
};
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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_;
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(){};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -51,7 +51,6 @@ void ShaderBuilder::init()
|
|||
|
||||
void ShaderBuilder::exit()
|
||||
{
|
||||
GPU_backend_exit();
|
||||
GPU_exit();
|
||||
|
||||
GPU_context_discard(gpu_context_);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue