WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 351 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
55 changed files with 1173 additions and 568 deletions
Showing only changes of commit 490f572ada - Show all commits

View File

@ -931,7 +931,8 @@ static void export_hair_curves_motion(Hair *hair,
}
/* Export motion keys. */
const int num_keys = hair->get_curve_keys().size();
const size_t num_keys = hair->num_keys();
const size_t num_curves = hair->num_curves();
float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
bool have_motion = false;
int num_motion_keys = 0;
@ -944,6 +945,9 @@ static void export_hair_curves_motion(Hair *hair,
for (const int i : points_by_curve.index_range()) {
const blender::IndexRange points = points_by_curve[i];
if (curve_index >= num_curves) {
break;
}
Hair::Curve curve = hair->get_curve(curve_index);
curve_index++;

View File

@ -661,9 +661,7 @@ void HIPDevice::tex_alloc(device_texture &mem)
address_mode = hipAddressModeClamp;
break;
case EXTENSION_CLIP:
/* TODO(@arya): setting this to Mode Clamp instead of Mode Border
* because it's unsupported in HIP. */
address_mode = hipAddressModeClamp;
address_mode = hipAddressModeBorder;
break;
case EXTENSION_MIRROR:
address_mode = hipAddressModeMirror;
@ -855,7 +853,11 @@ void HIPDevice::tex_alloc(device_texture &mem)
thread_scoped_lock lock(device_mem_map_mutex);
cmem = &device_mem_map[&mem];
hip_assert(hipTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL));
if (hipTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL) != hipSuccess) {
set_error(
"Failed to create texture. Maximum GPU texture size or available GPU memory was likely "
"exceeded.");
}
texture_info[slot].data = (uint64_t)cmem->texobject;
}

View File

@ -4588,7 +4588,7 @@ def km_grease_pencil_paint_mode(_params):
("paint.sample_color", {"type": 'X', "value": 'PRESS', "shift": True}, None),
# Isolate Layer
("grease_pencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
("grease_pencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
])
return keymap
@ -4663,8 +4663,8 @@ def km_grease_pencil_edit_mode(params):
("grease_pencil.reorder", {"type": 'DOWN_ARROW', "value": 'PRESS',
"ctrl": True, "shift": True}, {"properties": [("direction", "BOTTOM")]}),
# Isolate Layer
("grease_pencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
# Isolate Layer
("grease_pencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
])
return keymap

View File

@ -118,6 +118,7 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
col.prop(light, "use_shadow", text="Cast Shadow")
col.prop(light, "shadow_softness_factor", text="Shadow Softness")
col.prop(light, "shadow_filter_radius", text="Filtering Radius")
if light.type == 'SUN':
col.prop(light, "shadow_trace_distance", text="Trace Distance")

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 8
#define BLENDER_FILE_SUBVERSION 9
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -15,6 +15,8 @@ struct GreasePencilDrawing;
struct ListBase;
struct Main;
struct Object;
struct LineartGpencilModifierData;
struct GreasePencilLineartModifierData;
namespace blender::bke::greasepencil::convert {
@ -30,4 +32,9 @@ void layer_adjustments_to_modifiers(Main &bmain,
const bGPdata &src_object_data,
Object &dst_object);
void lineart_wrap_v3(const LineartGpencilModifierData *lmd_legacy,
GreasePencilLineartModifierData *lmd);
void lineart_unwrap_v3(LineartGpencilModifierData *lmd_legacy,
const GreasePencilLineartModifierData *lmd);
} // namespace blender::bke::greasepencil::convert

View File

@ -5319,6 +5319,45 @@ static void write_grid_paint_mask(BlendWriter *writer,
}
}
static void blend_write_layer_data(BlendWriter *writer,
const CustomDataLayer &layer,
const int count)
{
switch (layer.type) {
case CD_MDEFORMVERT:
BKE_defvert_blend_write(writer, count, static_cast<const MDeformVert *>(layer.data));
break;
case CD_MDISPS:
write_mdisps(
writer, count, static_cast<const MDisps *>(layer.data), layer.flag & CD_FLAG_EXTERNAL);
break;
case CD_PAINT_MASK:
BLO_write_raw(writer, sizeof(float) * count, static_cast<const float *>(layer.data));
break;
case CD_GRID_PAINT_MASK:
write_grid_paint_mask(writer, count, static_cast<const GridPaintMask *>(layer.data));
break;
case CD_PROP_BOOL:
BLO_write_raw(writer, sizeof(bool) * count, static_cast<const bool *>(layer.data));
break;
default: {
const char *structname;
int structnum;
CustomData_file_write_info(eCustomDataType(layer.type), &structname, &structnum);
if (structnum) {
int datasize = structnum * count;
BLO_write_struct_array_by_name(writer, structname, datasize, layer.data);
}
else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */
printf("%s error: layer '%s':%d - can't be written to file\n",
__func__,
structname,
layer.type);
}
}
}
}
void CustomData_blend_write(BlendWriter *writer,
CustomData *data,
Span<CustomDataLayer> layers_to_write,
@ -5335,39 +5374,7 @@ void CustomData_blend_write(BlendWriter *writer,
writer, CustomDataLayer, data->totlayer, data->layers, layers_to_write.data());
for (const CustomDataLayer &layer : layers_to_write) {
switch (layer.type) {
case CD_MDEFORMVERT:
BKE_defvert_blend_write(writer, count, static_cast<const MDeformVert *>(layer.data));
break;
case CD_MDISPS:
write_mdisps(
writer, count, static_cast<const MDisps *>(layer.data), layer.flag & CD_FLAG_EXTERNAL);
break;
case CD_PAINT_MASK:
BLO_write_raw(writer, sizeof(float) * count, static_cast<const float *>(layer.data));
break;
case CD_GRID_PAINT_MASK:
write_grid_paint_mask(writer, count, static_cast<const GridPaintMask *>(layer.data));
break;
case CD_PROP_BOOL:
BLO_write_raw(writer, sizeof(bool) * count, static_cast<const bool *>(layer.data));
break;
default: {
const char *structname;
int structnum;
CustomData_file_write_info(eCustomDataType(layer.type), &structname, &structnum);
if (structnum) {
int datasize = structnum * count;
BLO_write_struct_array_by_name(writer, structname, datasize, layer.data);
}
else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */
printf("%s error: layer '%s':%d - can't be written to file\n",
__func__,
structname,
layer.type);
}
}
}
blend_write_layer_data(writer, layer, count);
}
if (data->external) {
@ -5420,6 +5427,35 @@ static void blend_read_paint_mask(BlendDataReader *reader,
}
}
static void blend_read_layer_data(BlendDataReader *reader, CustomDataLayer &layer, const int count)
{
BLO_read_data_address(reader, &layer.data);
if (layer.data != nullptr) {
/* Make layer data shareable. */
layer.sharing_info = make_implicit_sharing_info_for_layer(
eCustomDataType(layer.type), 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 #84935.
* For a CD_MLOOPUV example, see #90620. */
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);
}
else if (layer.type == CD_GRID_PAINT_MASK) {
blend_read_paint_mask(reader, count, static_cast<GridPaintMask *>(layer.data));
}
else if (layer.type == CD_MDEFORMVERT) {
BKE_defvert_blend_read(reader, count, static_cast<MDeformVert *>(layer.data));
}
}
void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int count)
{
BLO_read_data_address(reader, &data->layers);
@ -5443,31 +5479,7 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int
layer->sharing_info = nullptr;
if (CustomData_verify_versions(data, i)) {
BLO_read_data_address(reader, &layer->data);
if (layer->data != nullptr) {
/* Make layer data shareable. */
layer->sharing_info = make_implicit_sharing_info_for_layer(
eCustomDataType(layer->type), 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 #84935.
* For a CD_MLOOPUV example, see #90620. */
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);
}
else if (layer->type == CD_GRID_PAINT_MASK) {
blend_read_paint_mask(reader, count, static_cast<GridPaintMask *>(layer->data));
}
else if (layer->type == CD_MDEFORMVERT) {
BKE_defvert_blend_read(reader, count, static_cast<MDeformVert *>(layer->data));
}
blend_read_layer_data(reader, *layer, count);
i++;
}
}

View File

@ -49,6 +49,7 @@
#include "DNA_ID.h"
#include "DNA_ID_enums.h"
#include "DNA_brush_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_modifier_types.h"
@ -2769,5 +2770,3 @@ static void write_layer_tree(GreasePencil &grease_pencil, BlendWriter *writer)
grease_pencil.root_group_ptr->wrap().prepare_for_dna_write();
write_layer_tree_group(writer, grease_pencil.root_group_ptr);
}
/** \} */

View File

@ -1598,6 +1598,16 @@ static void legacy_object_modifier_weight_proximity(Object &object, GpencilModif
false);
}
static void legacy_object_modifier_weight_lineart(Object &object, GpencilModifierData &legacy_md)
{
ModifierData &md = legacy_object_modifier_common(
object, eModifierType_GreasePencilWeightAngle, legacy_md);
auto &md_lineart = reinterpret_cast<GreasePencilLineartModifierData &>(md);
auto &legacy_md_lineart = reinterpret_cast<LineartGpencilModifierData &>(legacy_md);
greasepencil::convert::lineart_wrap_v3(&legacy_md_lineart, &md_lineart);
}
static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
{
BLI_assert(BLI_listbase_is_empty(&object.modifiers));
@ -1671,6 +1681,8 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
case eGpencilModifierType_Simplify:
case eGpencilModifierType_Texture:
case eGpencilModifierType_Lineart:
legacy_object_modifier_weight_lineart(object, *gpd_md);
break;
case eGpencilModifierType_Shrinkwrap:
case eGpencilModifierType_Envelope:
case eGpencilModifierType_Outline:
@ -1707,4 +1719,98 @@ void legacy_gpencil_object(Main &bmain, Object &object)
BKE_object_free_derived_caches(&object);
}
void lineart_wrap_v3(const LineartGpencilModifierData *lmd_legacy,
GreasePencilLineartModifierData *lmd)
{
#define LMD_WRAP(var) lmd->var = lmd_legacy->var
LMD_WRAP(edge_types);
LMD_WRAP(source_type);
LMD_WRAP(use_multiple_levels);
LMD_WRAP(level_start);
LMD_WRAP(level_end);
LMD_WRAP(source_camera);
LMD_WRAP(light_contour_object);
LMD_WRAP(source_object);
LMD_WRAP(source_collection);
LMD_WRAP(target_material);
STRNCPY(lmd->source_vertex_group, lmd_legacy->source_vertex_group);
STRNCPY(lmd->vgname, lmd_legacy->vgname);
LMD_WRAP(overscan);
LMD_WRAP(shadow_camera_fov);
LMD_WRAP(shadow_camera_size);
LMD_WRAP(shadow_camera_near);
LMD_WRAP(shadow_camera_far);
LMD_WRAP(opacity);
lmd->thickness = lmd_legacy->thickness / 2;
LMD_WRAP(mask_switches);
LMD_WRAP(material_mask_bits);
LMD_WRAP(intersection_mask);
LMD_WRAP(shadow_selection);
LMD_WRAP(silhouette_selection);
LMD_WRAP(crease_threshold);
LMD_WRAP(angle_splitting_threshold);
LMD_WRAP(chain_smooth_tolerance);
LMD_WRAP(chaining_image_threshold);
LMD_WRAP(calculation_flags);
LMD_WRAP(flags);
LMD_WRAP(stroke_depth_offset);
LMD_WRAP(level_start_override);
LMD_WRAP(level_end_override);
LMD_WRAP(edge_types_override);
LMD_WRAP(shadow_selection_override);
LMD_WRAP(shadow_use_silhouette_override);
LMD_WRAP(cache);
LMD_WRAP(la_data_ptr);
#undef LMD_WRAP
}
void lineart_unwrap_v3(LineartGpencilModifierData *lmd_legacy,
const GreasePencilLineartModifierData *lmd)
{
#define LMD_UNWRAP(var) lmd_legacy->var = lmd->var
LMD_UNWRAP(edge_types);
LMD_UNWRAP(source_type);
LMD_UNWRAP(use_multiple_levels);
LMD_UNWRAP(level_start);
LMD_UNWRAP(level_end);
LMD_UNWRAP(source_camera);
LMD_UNWRAP(light_contour_object);
LMD_UNWRAP(source_object);
LMD_UNWRAP(source_collection);
LMD_UNWRAP(target_material);
STRNCPY(lmd_legacy->source_vertex_group, lmd->source_vertex_group);
STRNCPY(lmd_legacy->vgname, lmd->vgname);
LMD_UNWRAP(overscan);
LMD_UNWRAP(shadow_camera_fov);
LMD_UNWRAP(shadow_camera_size);
LMD_UNWRAP(shadow_camera_near);
LMD_UNWRAP(shadow_camera_far);
LMD_UNWRAP(opacity);
lmd_legacy->thickness = lmd->thickness * 2;
LMD_UNWRAP(mask_switches);
LMD_UNWRAP(material_mask_bits);
LMD_UNWRAP(intersection_mask);
LMD_UNWRAP(shadow_selection);
LMD_UNWRAP(silhouette_selection);
LMD_UNWRAP(crease_threshold);
LMD_UNWRAP(angle_splitting_threshold);
LMD_UNWRAP(chain_smooth_tolerance);
LMD_UNWRAP(chaining_image_threshold);
LMD_UNWRAP(calculation_flags);
LMD_UNWRAP(flags);
LMD_UNWRAP(stroke_depth_offset);
LMD_UNWRAP(level_start_override);
LMD_UNWRAP(level_end_override);
LMD_UNWRAP(edge_types_override);
LMD_UNWRAP(shadow_selection_override);
LMD_UNWRAP(shadow_use_silhouette_override);
LMD_UNWRAP(cache);
LMD_UNWRAP(la_data_ptr);
#undef LMD_UNWRAP
}
} // namespace blender::bke::greasepencil::convert

View File

@ -3032,7 +3032,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 7)) {
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 8)) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->shadow_filter_radius = 3.0f;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 9)) {
update_paint_modes_for_brush_assets(*bmain);
}

View File

@ -412,14 +412,20 @@ void MemoryBuffer::add_pixel(int x, int y, const float color[4])
}
}
static void read_ewa_elem(void *userdata, int x, int y, float result[4])
static void read_ewa_elem_checked(void *userdata, int x, int y, float result[4])
{
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
buffer->read_elem_checked(x, y, result);
}
static void read_ewa_elem_clamped(void *userdata, int x, int y, float result[4])
{
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
buffer->read_elem_clamped(x, y, result);
}
void MemoryBuffer::read_elem_filtered(
const float x, const float y, float dx[2], float dy[2], float *out) const
const float x, const float y, float dx[2], float dy[2], bool extend_boundary, float *out) const
{
BLI_assert(datatype_ == DataType::Color);
@ -441,7 +447,7 @@ void MemoryBuffer::read_elem_filtered(
uv_normal,
du_normal,
dv_normal,
read_ewa_elem,
extend_boundary ? read_ewa_elem_clamped : read_ewa_elem_checked,
const_cast<MemoryBuffer *>(this),
out);
}

View File

@ -263,7 +263,8 @@ class MemoryBuffer {
}
}
void read_elem_filtered(float x, float y, float dx[2], float dy[2], float *out) const;
void read_elem_filtered(
float x, float y, float dx[2], float dy[2], bool extend_boundary, float *out) const;
/**
* Get channel value at given coordinates.

View File

@ -5,6 +5,7 @@
#include "COM_CornerPinNode.h"
#include "COM_PlaneCornerPinOperation.h"
#include "COM_SMAAOperation.h"
namespace blender::compositor {
@ -13,7 +14,36 @@ CornerPinNode::CornerPinNode(bNode *editor_node) : Node(editor_node) {}
void CornerPinNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
NodeInput *input_image = this->get_input_socket(0);
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
converter.add_operation(plane_mask_operation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
converter.add_operation(warp_image_operation);
converter.map_input_socket(this->get_input_socket(0), warp_image_operation->get_input_socket(0));
/* NOTE: socket order differs between UI node and operations:
* bNode uses intuitive order following top-down layout:
* upper-left, upper-right, lower-left, lower-right
@ -21,23 +51,20 @@ void CornerPinNode::convert_to_operations(NodeConverter &converter,
* lower-left, lower-right, upper-right, upper-left
*/
const int node_corner_index[4] = {3, 4, 2, 1};
NodeOutput *output_warped_image = this->get_output_socket(0);
NodeOutput *output_plane = this->get_output_socket(1);
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
converter.add_operation(warp_image_operation);
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
converter.add_operation(plane_mask_operation);
converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0));
for (int i = 0; i < 4; i++) {
NodeInput *corner_input = get_input_socket(node_corner_index[i]);
converter.map_input_socket(corner_input, warp_image_operation->get_input_socket(i + 1));
converter.map_input_socket(corner_input, plane_mask_operation->get_input_socket(i));
}
converter.map_output_socket(output_warped_image, warp_image_operation->get_output_socket());
converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket());
SetAlphaMultiplyOperation *set_alpha_operation = new SetAlphaMultiplyOperation();
converter.add_operation(set_alpha_operation);
converter.add_link(warp_image_operation->get_output_socket(),
set_alpha_operation->get_input_socket(0));
converter.add_link(smaa_neighborhood->get_output_socket(),
set_alpha_operation->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(0),
set_alpha_operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -5,6 +5,7 @@
#include "COM_PlaneTrackDeformNode.h"
#include "COM_PlaneTrackOperation.h"
#include "COM_SMAAOperation.h"
namespace blender::compositor {
@ -22,9 +23,39 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
int frame_number = context.get_framenumber();
NodeInput *input_image = this->get_input_socket(0);
NodeOutput *output_warped_image = this->get_output_socket(0);
NodeOutput *output_plane = this->get_output_socket(1);
PlaneTrackMaskOperation *plane_mask_operation = new PlaneTrackMaskOperation();
plane_mask_operation->set_movie_clip(clip);
plane_mask_operation->set_tracking_object(data->tracking_object);
plane_mask_operation->set_plane_track_name(data->plane_track_name);
plane_mask_operation->set_framenumber(frame_number);
if (data->flag & CMP_NODE_PLANE_TRACK_DEFORM_FLAG_MOTION_BLUR) {
plane_mask_operation->set_motion_blur_samples(data->motion_blur_samples);
plane_mask_operation->set_motion_blur_shutter(data->motion_blur_shutter);
}
converter.add_operation(plane_mask_operation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation();
warp_image_operation->set_movie_clip(clip);
@ -37,21 +68,16 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
}
converter.add_operation(warp_image_operation);
converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0));
converter.map_output_socket(output_warped_image, warp_image_operation->get_output_socket());
converter.map_input_socket(this->get_input_socket(0), warp_image_operation->get_input_socket(0));
PlaneTrackMaskOperation *plane_mask_operation = new PlaneTrackMaskOperation();
plane_mask_operation->set_movie_clip(clip);
plane_mask_operation->set_tracking_object(data->tracking_object);
plane_mask_operation->set_plane_track_name(data->plane_track_name);
plane_mask_operation->set_framenumber(frame_number);
if (data->flag & CMP_NODE_PLANE_TRACK_DEFORM_FLAG_MOTION_BLUR) {
plane_mask_operation->set_motion_blur_samples(data->motion_blur_samples);
plane_mask_operation->set_motion_blur_shutter(data->motion_blur_shutter);
}
converter.add_operation(plane_mask_operation);
converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket());
SetAlphaMultiplyOperation *set_alpha_operation = new SetAlphaMultiplyOperation();
converter.add_operation(set_alpha_operation);
converter.add_link(warp_image_operation->get_output_socket(),
set_alpha_operation->get_input_socket(0));
converter.add_link(smaa_neighborhood->get_output_socket(),
set_alpha_operation->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(0),
set_alpha_operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -152,7 +152,7 @@ void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
else {
/* EWA filtering (without nearest it gets blurry with NO distortion). */
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], false, it.out);
}
}
}

View File

@ -144,7 +144,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output,
}
else {
/* EWA filtering. */
input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], false, it.out);
/* UV to alpha threshold. */
const float threshold = alpha_ * 0.05f;

View File

@ -99,7 +99,7 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *
if (motion_blur_samples_ == 1) {
for (; !it.is_end(); ++it) {
warp_coord(it.x, it.y, samples_[0].perspective_matrix, uv, deriv);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], true, it.out);
}
}
else {
@ -108,7 +108,7 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer *
for (const int sample : IndexRange(motion_blur_samples_)) {
float color[4];
warp_coord(it.x, it.y, samples_[sample].perspective_matrix, uv, deriv);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color);
input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], true, color);
add_v4_v4(it.out, color);
}
mul_v4_fl(it.out, 1.0f / float(motion_blur_samples_));
@ -175,14 +175,6 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx,
PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation()
{
add_output_socket(DataType::Value);
/* Currently hardcoded to 8 samples. */
osa_ = 8;
}
void PlaneDistortMaskOperation::init_execution()
{
BLI_jitter_init(jitter_, osa_);
}
void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
@ -190,37 +182,22 @@ void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *outpu
Span<MemoryBuffer *> /*inputs*/)
{
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
int inside_count = 0;
float accumulated_mask = 0.0f;
const float2 point = float2(it.x, it.y);
for (const int motion_sample : IndexRange(motion_blur_samples_)) {
MotionSample &sample = samples_[motion_sample];
inside_count += get_jitter_samples_inside_count(it.x, it.y, sample);
const bool is_inside_plane = isect_point_tri_v2(point,
sample.frame_space_corners[0],
sample.frame_space_corners[1],
sample.frame_space_corners[2]) ||
isect_point_tri_v2(point,
sample.frame_space_corners[0],
sample.frame_space_corners[2],
sample.frame_space_corners[3]);
accumulated_mask += is_inside_plane ? 1.0f : 0.0f;
}
*it.out = float(inside_count) / (osa_ * motion_blur_samples_);
*it.out = accumulated_mask / motion_blur_samples_;
}
}
int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x,
int y,
MotionSample &sample_data)
{
float point[2];
int inside_count = 0;
for (int sample = 0; sample < osa_; sample++) {
point[0] = x + jitter_[sample][0];
point[1] = y + jitter_[sample][1];
if (isect_point_tri_v2(point,
sample_data.frame_space_corners[0],
sample_data.frame_space_corners[1],
sample_data.frame_space_corners[2]) ||
isect_point_tri_v2(point,
sample_data.frame_space_corners[0],
sample_data.frame_space_corners[2],
sample_data.frame_space_corners[3]))
{
inside_count++;
}
}
return inside_count;
}
} // namespace blender::compositor

View File

@ -60,21 +60,12 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation {
};
class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
protected:
int osa_;
float jitter_[32][2];
public:
PlaneDistortMaskOperation();
void init_execution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
private:
int get_jitter_samples_inside_count(int x, int y, MotionSample &sample_data);
};
} // namespace blender::compositor

View File

@ -191,7 +191,9 @@ set(GLSL_SRC
shaders/compositor_parallel_reduction.glsl
shaders/compositor_pixelate.glsl
shaders/compositor_plane_deform.glsl
shaders/compositor_plane_deform_mask.glsl
shaders/compositor_plane_deform_motion_blur.glsl
shaders/compositor_plane_deform_motion_blur_mask.glsl
shaders/compositor_premultiply_alpha.glsl
shaders/compositor_projector_lens_distortion.glsl
shaders/compositor_read_input.glsl
@ -315,7 +317,6 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/compositor_parallel_reduction_info.hh
shaders/infos/compositor_pixelate_info.hh
shaders/infos/compositor_plane_deform_info.hh
shaders/infos/compositor_plane_deform_motion_blur_info.hh
shaders/infos/compositor_premultiply_alpha_info.hh
shaders/infos/compositor_projector_lens_distortion_info.hh
shaders/infos/compositor_read_input_info.hh

View File

@ -8,8 +8,6 @@ void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
/* Add 0.5 to evaluate the input sampler at the center of the pixel and divide by the image size
* to get the coordinates into the sampler's expected [0, 1] range. */
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx));
vec3 transformed_coordinates = mat3(homography_matrix) * vec3(coordinates, 1.0);
@ -23,14 +21,8 @@ void main()
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
/* The plane mask is 1 if it is inside the plane and 0 otherwise. However, we use the alpha value
* of the sampled color for pixels outside of the plane to utilize the anti-aliasing effect of
* the anisotropic filtering. Therefore, the input_tx sampler should use anisotropic filtering
* and be clamped to zero border color. */
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
float mask_value = is_inside_plane ? 1.0 : sampled_color.a;
/* Premultiply the mask value as an alpha. */
vec4 plane_color = sampled_color * texture_load(mask_tx, texel).x;
imageStore(output_img, texel, sampled_color);
imageStore(mask_img, texel, vec4(mask_value));
imageStore(output_img, texel, plane_color);
}

View File

@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(mask_img));
vec3 transformed_coordinates = mat3(homography_matrix) * vec3(coordinates, 1.0);
vec2 projected_coordinates = transformed_coordinates.xy / transformed_coordinates.z;
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
float mask_value = is_inside_plane ? 1.0 : 0.0;
imageStore(mask_img, texel, vec4(mask_value));
}

View File

@ -2,17 +2,14 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
/* Add 0.5 to evaluate the sampler at the center of the pixel and divide by the size to get the
* coordinates into the sampler's expected [0, 1] range. We choose the maximum between both
* output sizes because one of the outputs might be a dummy 1x1 image. */
ivec2 output_size = max(imageSize(output_img), imageSize(mask_img));
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(output_size);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(output_img));
float accumulated_mask = 0.0;
vec4 accumulated_color = vec4(0.0);
for (int i = 0; i < number_of_motion_blur_samples; i++) {
mat3 homography_matrix = mat3(homography_matrices[i]);
@ -28,19 +25,12 @@ void main()
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
accumulated_color += sampled_color;
/* The plane mask is 1 if it is inside the plane and 0 otherwise. However, we use the alpha
* value of the sampled color for pixels outside of the plane to utilize the anti-aliasing
* effect of the anisotropic filtering. Therefore, the input_tx sampler should use anisotropic
* filtering and be clamped to zero border color. */
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
accumulated_mask += is_inside_plane ? 1.0 : sampled_color.a;
}
accumulated_mask /= number_of_motion_blur_samples;
accumulated_color /= number_of_motion_blur_samples;
imageStore(output_img, texel, accumulated_color);
imageStore(mask_img, texel, vec4(accumulated_mask));
/* Premultiply the mask value as an alpha. */
vec4 plane_color = accumulated_color * texture_load(mask_tx, texel).x;
imageStore(output_img, texel, plane_color);
}

View File

@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(mask_img));
float accumulated_mask = 0.0;
for (int i = 0; i < number_of_motion_blur_samples; i++) {
mat3 homography_matrix = mat3(homography_matrices[i]);
vec3 transformed_coordinates = homography_matrix * vec3(coordinates, 1.0);
vec2 projected_coordinates = transformed_coordinates.xy / transformed_coordinates.z;
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
all(lessThanEqual(projected_coordinates, vec2(1.0)));
accumulated_mask += is_inside_plane ? 1.0 : 0.0;
}
accumulated_mask /= number_of_motion_blur_samples;
imageStore(mask_img, texel, vec4(accumulated_mask));
}

View File

@ -4,11 +4,36 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_plane_deform_mask)
.local_group_size(16, 16)
.push_constant(Type::MAT4, "homography_matrix")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_mask.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform)
.local_group_size(16, 16)
.push_constant(Type::MAT4, "homography_matrix")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.sampler(1, ImageType::FLOAT_2D, "mask_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur_mask)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_motion_blur_mask.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.sampler(1, ImageType::FLOAT_2D, "mask_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.compute_source("compositor_plane_deform_motion_blur.glsl")
.do_static_compilation(true);

View File

@ -1,15 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_plane_deform_motion_blur)
.local_group_size(16, 16)
.push_constant(Type::INT, "number_of_motion_blur_samples")
.uniform_buf(0, "mat4", "homography_matrices[64]")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform_motion_blur.glsl")
.do_static_compilation(true);

View File

@ -78,6 +78,8 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
this->power[LIGHT_SPECULAR] = la->spec_fac * shape_power;
this->power[LIGHT_VOLUME] = la->volume_fac * point_power;
this->pcf_radius = la->shadow_filter_radius;
eLightType new_type = to_light_type(la->type, la->area_shape, la->mode & LA_USE_SOFT_FALLOFF);
if (assign_if_different(this->type, new_type)) {
shadow_discard_safe(shadows);

View File

@ -807,8 +807,6 @@ struct LightData {
int tilemap_index;
/** Directional : Offset of the LOD min in LOD min tile units. */
int2 clipmap_base_offset;
/** Number of step for shadow map tracing. */
int shadow_ray_step_count;
/** Punctual: Other parts of the perspective matrix. */
float clip_side;
/** Punctual: Shift to apply to the light origin to get the shadow projection origin. */
@ -817,7 +815,9 @@ struct LightData {
float shadow_shape_scale_or_angle;
/** Trace distance for directional lights. */
float shadow_trace_distance;
float _pad2;
/* Radius in pixels for shadow filtering. */
float pcf_radius;
int _pad0;
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
@ -1024,7 +1024,7 @@ struct ShadowSceneData {
int step_count;
/* Bias the shading point by using the normal to avoid self intersection. */
float normal_bias;
int _pad2;
int _pad0;
};
BLI_STATIC_ASSERT_ALIGN(ShadowSceneData, 16)

View File

@ -1008,28 +1008,6 @@ void ShadowModule::end_sync()
{
Manager &manager = *inst_.manager;
{
/* Mark for update all shadow pages touching an updated shadow caster. */
PassSimple &pass = caster_update_ps_;
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_UPDATE));
pass.bind_ssbo("tilemaps_buf", tilemap_pool.tilemaps_data);
pass.bind_ssbo("tiles_buf", tilemap_pool.tiles_data);
/* Past caster transforms. */
if (past_casters_updated_.size() > 0) {
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.previous());
pass.bind_ssbo("resource_ids_buf", past_casters_updated_);
pass.dispatch(int3(past_casters_updated_.size(), 1, tilemap_pool.tilemaps_data.size()));
}
/* Current caster transforms. */
if (curr_casters_updated_.size() > 0) {
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
pass.bind_ssbo("resource_ids_buf", curr_casters_updated_);
pass.dispatch(int3(curr_casters_updated_.size(), 1, tilemap_pool.tilemaps_data.size()));
}
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
}
{
PassSimple &pass = tilemap_setup_ps_;
pass.init();
@ -1079,6 +1057,28 @@ void ShadowModule::end_sync()
}
}
{
/* Mark for update all shadow pages touching an updated shadow caster. */
PassSimple &pass = caster_update_ps_;
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_UPDATE));
pass.bind_ssbo("tilemaps_buf", tilemap_pool.tilemaps_data);
pass.bind_ssbo("tiles_buf", tilemap_pool.tiles_data);
/* Past caster transforms. */
if (past_casters_updated_.size() > 0) {
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.previous());
pass.bind_ssbo("resource_ids_buf", past_casters_updated_);
pass.dispatch(int3(past_casters_updated_.size(), 1, tilemap_pool.tilemaps_data.size()));
}
/* Current caster transforms. */
if (curr_casters_updated_.size() > 0) {
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
pass.bind_ssbo("resource_ids_buf", curr_casters_updated_);
pass.dispatch(int3(curr_casters_updated_.size(), 1, tilemap_pool.tilemaps_data.size()));
}
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
}
/* Non volume usage tagging happens between these two steps.
* (Setup at begin_sync) */
@ -1337,13 +1337,7 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
}
inst_.hiz_buffer.update();
/* Run caster update once and before the update loop.
* This is valid even before the view update since only the static tilemaps
* are concerned about this tagging. */
/* TODO(fclem): There is an optimization opportunity here where we can
* test casters only against the static tilemaps instead of all of them. */
inst_.manager->submit(caster_update_ps_, view);
bool update_casters = true;
do {
DRW_stats_group_start("Shadow");
@ -1351,6 +1345,12 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
GPU_uniformbuf_clear_to_zero(shadow_multi_view_.matrices_ubo_get());
inst_.manager->submit(tilemap_setup_ps_, view);
if (assign_if_different(update_casters, false)) {
/* Run caster update only once. */
/* TODO(fclem): There is an optimization opportunity here where we can
* test casters only against the static tilemaps instead of all of them. */
inst_.manager->submit(caster_update_ps_, view);
}
inst_.manager->submit(tilemap_usage_ps_, view);
inst_.manager->submit(tilemap_update_ps_, view);

View File

@ -14,20 +14,39 @@
# define SHADOW_ATLAS_TYPE usampler2DArray
#endif
float shadow_read_depth_at_tilemap_uv(SHADOW_ATLAS_TYPE atlas_tx,
usampler2D tilemaps_tx,
int tilemap_index,
vec2 tilemap_uv)
struct ShadowSampleParams {
vec3 lP;
vec3 uv;
int tilemap_index;
float z_range;
};
ShadowTileData shadow_tile_data_get(usampler2D tilemaps_tx, ShadowSampleParams params)
{
/* Prevent out of bound access. Assumes the input is already non negative. */
tilemap_uv = min(tilemap_uv, vec2(0.99999));
vec2 tilemap_uv = min(params.uv.xy, vec2(0.99999));
ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
/* Using bitwise ops is way faster than integer ops. */
const int page_shift = SHADOW_PAGE_LOD;
ivec2 tile_coord = texel_coord >> page_shift;
ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_coord, tilemap_index);
return shadow_tile_load(tilemaps_tx, tile_coord, params.tilemap_index);
}
float shadow_read_depth(SHADOW_ATLAS_TYPE atlas_tx,
usampler2D tilemaps_tx,
ShadowSampleParams params)
{
/* Prevent out of bound access. Assumes the input is already non negative. */
vec2 tilemap_uv = min(params.uv.xy, vec2(0.99999));
ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
/* Using bitwise ops is way faster than integer ops. */
const int page_shift = SHADOW_PAGE_LOD;
ivec2 tile_coord = texel_coord >> page_shift;
ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_coord, params.tilemap_index);
if (!tile.is_allocated) {
return -1.0;
@ -51,25 +70,36 @@ struct ShadowEvalResult {
/** \name Shadow Sampling Functions
* \{ */
/* TODO(fclem): Remove. Only here to avoid include order hell with common_math_lib. */
mat4x4 shadow_projection_perspective(
float left, float right, float bottom, float top, float near_clip, float far_clip)
mat4x4 shadow_projection_perspective(float side, float near_clip, float far_clip)
{
float x_delta = right - left;
float y_delta = top - bottom;
float z_delta = far_clip - near_clip;
mat4x4 mat = mat4x4(1.0);
if (x_delta != 0.0 && y_delta != 0.0 && z_delta != 0.0) {
mat[0][0] = near_clip * 2.0 / x_delta;
mat[1][1] = near_clip * 2.0 / y_delta;
mat[2][0] = (right + left) / x_delta; /* NOTE: negate Z. */
mat[2][1] = (top + bottom) / y_delta;
mat[2][2] = -(far_clip + near_clip) / z_delta;
mat[2][3] = -1.0;
mat[3][2] = (-2.0 * near_clip * far_clip) / z_delta;
mat[3][3] = 0.0;
}
mat[0][0] = near_clip / side;
mat[1][1] = near_clip / side;
mat[2][0] = 0.0;
mat[2][1] = 0.0;
mat[2][2] = -(far_clip + near_clip) / z_delta;
mat[2][3] = -1.0;
mat[3][2] = (-2.0 * near_clip * far_clip) / z_delta;
mat[3][3] = 0.0;
return mat;
}
mat4x4 shadow_projection_perspective_inverse(float side, float near_clip, float far_clip)
{
float z_delta = far_clip - near_clip;
float d = 2.0 * near_clip * far_clip;
mat4x4 mat = mat4x4(1.0);
mat[0][0] = side / near_clip;
mat[1][1] = side / near_clip;
mat[2][0] = 0.0;
mat[2][1] = 0.0;
mat[2][2] = 0.0;
mat[2][3] = (near_clip - far_clip) / d;
mat[3][2] = -1.0;
mat[3][3] = (near_clip + far_clip) / d;
return mat;
}
@ -92,33 +122,142 @@ float shadow_linear_occluder_distance(LightData light,
return receiver_z - occluder_z;
}
ShadowEvalResult shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
usampler2D tilemaps_tx,
LightData light,
vec3 P)
mat4 shadow_punctual_projection_perspective(LightData light)
{
/* Face Local (View) Space > Clip Space. */
float clip_far = intBitsToFloat(light.clip_far);
float clip_near = intBitsToFloat(light.clip_near);
float clip_side = light.clip_side;
return shadow_projection_perspective(clip_side, clip_near, clip_far);
}
mat4 shadow_punctual_projection_perspective_inverse(LightData light)
{
/* Face Local (View) Space > Clip Space. */
float clip_far = intBitsToFloat(light.clip_far);
float clip_near = intBitsToFloat(light.clip_near);
float clip_side = light.clip_side;
return shadow_projection_perspective_inverse(clip_side, clip_near, clip_far);
}
vec3 shadow_punctual_reconstruct_position(ShadowSampleParams params,
mat4 wininv,
LightData light,
vec3 uvw)
{
vec3 clip_P = uvw * 2.0 - 1.0;
vec3 lP = project_point(wininv, clip_P);
int face_id = params.tilemap_index - light.tilemap_index;
lP = shadow_punctual_face_local_to_local_position(face_id, lP);
return mat3(light.object_mat) * lP + light._position;
}
ShadowSampleParams shadow_punctual_sample_params_get(usampler2D tilemaps_tx,
LightData light,
vec3 P)
{
vec3 lP = (P - light._position) * mat3(light.object_mat);
int face_id = shadow_punctual_face_index_get(lP);
/* Local Light Space > Face Local (View) Space. */
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
/* Face Local (View) Space > Clip Space. */
float clip_far = intBitsToFloat(light.clip_far);
float clip_near = intBitsToFloat(light.clip_near);
float clip_side = light.clip_side;
/* TODO: Could be simplified since frustum is completely symmetrical. */
mat4 winmat = shadow_projection_perspective(
-clip_side, clip_side, -clip_side, clip_side, clip_near, clip_far);
mat4 winmat = shadow_punctual_projection_perspective(light);
vec3 clip_P = project_point(winmat, lP);
/* Clip Space > UV Space. */
vec3 uv_P = saturate(clip_P * 0.5 + 0.5);
float depth = shadow_read_depth_at_tilemap_uv(
atlas_tx, tilemaps_tx, light.tilemap_index + face_id, uv_P.xy);
ShadowSampleParams result;
result.lP = lP;
result.uv = uv_P;
result.tilemap_index = light.tilemap_index + face_id;
result.z_range = 1.0;
return result;
}
ShadowEvalResult shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
usampler2D tilemaps_tx,
LightData light,
vec3 P)
{
ShadowSampleParams params = shadow_punctual_sample_params_get(tilemaps_tx, light, P);
float depth = shadow_read_depth(atlas_tx, tilemaps_tx, params);
ShadowEvalResult result;
result.light_visibilty = float(uv_P.z < depth);
result.occluder_distance = shadow_linear_occluder_distance(light, false, lP, depth);
result.light_visibilty = float(params.uv.z < depth);
result.occluder_distance = shadow_linear_occluder_distance(light, false, params.lP, depth);
return result;
}
struct ShadowDirectionalSampleInfo {
float clip_near;
float clip_far;
int level_relative;
int lod_relative;
ivec2 clipmap_offset;
vec2 clipmap_origin;
};
ShadowDirectionalSampleInfo shadow_directional_sample_info_get(LightData light, vec3 lP)
{
ShadowDirectionalSampleInfo info;
info.clip_near = orderedIntBitsToFloat(light.clip_near);
info.clip_far = orderedIntBitsToFloat(light.clip_far);
int level = shadow_directional_level(light, lP - light._position);
/* This difference needs to be less than 32 for the later shift to be valid.
* This is ensured by ShadowDirectional::clipmap_level_range(). */
info.level_relative = level - light.clipmap_lod_min;
info.lod_relative = (light.type == LIGHT_SUN_ORTHO) ? light.clipmap_lod_min : level;
info.clipmap_offset = shadow_decompress_grid_offset(
light.type, light.clipmap_base_offset, info.level_relative);
info.clipmap_origin = vec2(light._clipmap_origin_x, light._clipmap_origin_y);
return info;
}
vec3 shadow_directional_reconstruct_position(ShadowSampleParams params, LightData light, vec3 uvw)
{
ShadowDirectionalSampleInfo info = shadow_directional_sample_info_get(light, params.lP);
vec2 tilemap_uv = uvw.xy;
tilemap_uv += vec2(info.clipmap_offset) / float(SHADOW_TILEMAP_RES);
vec2 clipmap_pos = (tilemap_uv - 0.5) / exp2(-float(info.lod_relative));
vec3 lP;
lP.xy = clipmap_pos + info.clipmap_origin;
lP.z = (params.uv.z + info.clip_near) * -1.0;
return mat3(light.object_mat) * lP;
}
ShadowSampleParams shadow_directional_sample_params_get(usampler2D tilemaps_tx,
LightData light,
vec3 P)
{
vec3 lP = P * mat3(light.object_mat);
ShadowDirectionalSampleInfo info = shadow_directional_sample_info_get(light, lP);
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
/* Assumed to be non-null. */
float z_range = info.clip_far - info.clip_near;
float dist_to_near_plane = -lP.z - info.clip_near;
vec2 clipmap_pos = lP.xy - info.clipmap_origin;
vec2 tilemap_uv = clipmap_pos * exp2(-float(info.lod_relative)) + 0.5;
/* Translate tilemap UVs to its origin. */
tilemap_uv -= vec2(info.clipmap_offset) / float(SHADOW_TILEMAP_RES);
/* Clamp to avoid out of tilemap access. */
tilemap_uv = saturate(tilemap_uv);
ShadowSampleParams result;
result.lP = lP;
result.uv = vec3(tilemap_uv, dist_to_near_plane);
result.tilemap_index = light.tilemap_index + info.level_relative;
result.z_range = z_range;
return result;
}
@ -127,40 +266,13 @@ ShadowEvalResult shadow_directional_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
LightData light,
vec3 P)
{
vec3 lP = P * mat3(light.object_mat);
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);
ShadowSampleParams params = shadow_directional_sample_params_get(tilemaps_tx, light, P);
float clip_near = orderedIntBitsToFloat(light.clip_near);
float clip_far = orderedIntBitsToFloat(light.clip_far);
/* Assumed to be non-null. */
float z_range = clip_far - clip_near;
float dist_to_near_plane = -lP.z - clip_near;
int level = shadow_directional_level(light, lP - light._position);
/* This difference needs to be less than 32 for the later shift to be valid.
* This is ensured by ShadowDirectional::clipmap_level_range(). */
int level_relative = level - light.clipmap_lod_min;
int lod_relative = (light.type == LIGHT_SUN_ORTHO) ? light.clipmap_lod_min : level;
vec2 clipmap_origin = vec2(light._clipmap_origin_x, light._clipmap_origin_y);
vec2 clipmap_pos = lP.xy - clipmap_origin;
vec2 tilemap_uv = clipmap_pos * exp2(-float(lod_relative)) + 0.5;
/* Compute offset in tile. */
ivec2 clipmap_offset = shadow_decompress_grid_offset(
light.type, light.clipmap_base_offset, level_relative);
/* Translate tilemap UVs to its origin. */
tilemap_uv -= vec2(clipmap_offset) / float(SHADOW_TILEMAP_RES);
/* Clamp to avoid out of tilemap access. */
tilemap_uv = saturate(tilemap_uv);
float depth = shadow_read_depth_at_tilemap_uv(
atlas_tx, tilemaps_tx, light.tilemap_index + level_relative, tilemap_uv);
float depth = shadow_read_depth(atlas_tx, tilemaps_tx, params);
ShadowEvalResult result;
result.light_visibilty = float(dist_to_near_plane < depth * z_range);
result.occluder_distance = shadow_linear_occluder_distance(light, true, lP, depth);
result.light_visibilty = float(params.uv.z < depth * params.z_range);
result.occluder_distance = shadow_linear_occluder_distance(light, true, params.lP, depth);
return result;
}

View File

@ -205,6 +205,24 @@ vec3 shadow_punctual_local_position_to_face_local(int face_id, vec3 lL)
}
}
vec3 shadow_punctual_face_local_to_local_position(int face_id, vec3 fL)
{
switch (face_id) {
case 1:
return vec3(-fL.z, -fL.x, fL.y);
case 2:
return vec3(fL.z, fL.x, fL.y);
case 3:
return vec3(fL.x, -fL.z, fL.y);
case 4:
return vec3(-fL.x, fL.z, fL.y);
case 5:
return vec3(fL.x, -fL.y, -fL.z);
default:
return fL;
}
}
/* Turns local light coordinate into shadow region index. Matches eCubeFace order.
* \note lL does not need to be normalized. */
int shadow_punctual_face_index_get(vec3 lL)

View File

@ -12,6 +12,8 @@
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_bxdf_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(draw_math_geom_lib.glsl)
float shadow_read_depth_at_tilemap_uv(int tilemap_index, vec2 tilemap_uv)
{
@ -406,6 +408,67 @@ SHADOW_MAP_TRACE_FN(ShadowRayPunctual)
/** \name Shadow Evaluation
* \{ */
/* Compute the world space offset of the shading position required for
* stochastic percentage closer filtering of shadow-maps. */
vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng)
{
if (light.pcf_radius <= 0.001) {
/* Early return. */
return vec3(0.0);
}
ShadowSampleParams params;
if (is_directional) {
params = shadow_directional_sample_params_get(shadow_tilemaps_tx, light, P);
}
else {
params = shadow_punctual_sample_params_get(shadow_tilemaps_tx, light, P);
}
ShadowTileData tile = shadow_tile_data_get(shadow_tilemaps_tx, params);
if (!tile.is_allocated) {
return vec3(0.0);
}
/* Compute the shadow-map tangent-bitangent matrix. */
float uv_offset = 1.0 / float(SHADOW_MAP_MAX_RES);
vec3 TP, BP;
if (is_directional) {
TP = shadow_directional_reconstruct_position(
params, light, params.uv + vec3(uv_offset, 0.0, 0.0));
BP = shadow_directional_reconstruct_position(
params, light, params.uv + vec3(0.0, uv_offset, 0.0));
vec3 L = light._back;
/* Project the offset positions into the surface plane. */
TP = line_plane_intersect(TP, dot(L, TP) > 0.0 ? L : -L, P, Ng);
BP = line_plane_intersect(BP, dot(L, BP) > 0.0 ? L : -L, P, Ng);
}
else {
mat4 wininv = shadow_punctual_projection_perspective_inverse(light);
TP = shadow_punctual_reconstruct_position(
params, wininv, light, params.uv + vec3(uv_offset, 0.0, 0.0));
BP = shadow_punctual_reconstruct_position(
params, wininv, light, params.uv + vec3(0.0, uv_offset, 0.0));
/* Project the offset positions into the surface plane. */
TP = line_plane_intersect(light._position, normalize(TP - light._position), P, Ng);
BP = line_plane_intersect(light._position, normalize(BP - light._position), P, Ng);
}
mat2x3 TB = mat2x3(TP - P, BP - P);
/* Compute the actual offset. */
vec2 rand = vec2(0.0);
#ifdef EEVEE_SAMPLING_DATA
rand = sampling_rng_2D_get(SAMPLING_SHADOW_V);
#endif
vec2 pcf_offset = interlieved_gradient_noise(UTIL_TEXEL, vec2(0.0), rand);
pcf_offset = pcf_offset * 2.0 - 1.0;
pcf_offset *= light.pcf_radius;
return TB * pcf_offset;
}
/**
* Evaluate shadowing by casting rays toward the light direction.
*/
@ -433,6 +496,8 @@ ShadowEvalResult shadow_eval(LightData light,
float normal_offset = 0.02;
#endif
P += shadow_pcf_offset(light, is_directional, P, Ng);
/* Avoid self intersection. */
P = offset_ray(P, Ng);
/* The above offset isn't enough in most situation. Still add a bigger bias. */

View File

@ -44,7 +44,7 @@ static void add_values_to_text_cache(const GVArray &values,
const float3 position = math::transform_point(object_to_world, positions[i]);
const T &value = values_typed[i];
char numstr[32];
char numstr[64];
size_t numstr_len = 0;
if constexpr (std::is_same_v<T, bool>) {
numstr_len = SNPRINTF_RLEN(numstr, "%s", value ? "True" : "False");
@ -77,7 +77,8 @@ static void add_values_to_text_cache(const GVArray &values,
numstr, "(%.3f, %.3f, %.3f, %.3f)", value.r, value.g, value.b, value.a);
}
else if constexpr (std::is_same_v<T, math::Quaternion>) {
numstr_len = SNPRINTF_RLEN(numstr, "(%g, %g, %g, %g)", value.w, value.x, value.y, value.z);
numstr_len = SNPRINTF_RLEN(
numstr, "(%.3f, %.3f, %.3f, %.3f)", value.w, value.x, value.y, value.z);
}
else {
BLI_assert_unreachable();

View File

@ -129,7 +129,7 @@ void ED_undosys_type_free();
/* `memfile_undo.cc` */
MemFile *ED_undosys_stack_memfile_get_active(UndoStack *ustack);
MemFile *ED_undosys_stack_memfile_get_if_active(UndoStack *ustack);
/**
* If the last undo step is a memfile one, find the first #MemFileChunk matching given ID
* (using its session UUID), and tag it as "changed in the future".

View File

@ -1862,6 +1862,13 @@ static int object_mode_set_exec(bContext *C, wmOperator *op)
}
}
wmWindowManager *wm = CTX_wm_manager(C);
if (wm) {
if (WM_autosave_is_scheduled(wm)) {
WM_autosave_write(wm, CTX_data_main(C));
}
}
return OPERATOR_FINISHED;
}

View File

@ -220,6 +220,50 @@ static float update_overlay_strip_position_data(bContext *C, const int mval[2])
static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
{
if (g_drop_coords.in_use) {
if (!g_drop_coords.has_read_mouse_pos) {
/* We didn't read the mouse position, so we need to do it manually here. */
int xy[2];
wmWindow *win = CTX_wm_window(C);
xy[0] = win->eventstate->xy[0];
xy[1] = win->eventstate->xy[1];
ARegion *region = CTX_wm_region(C);
int mval[2];
/* Convert mouse coordinates to region local coordinates. */
mval[0] = xy[0] - region->winrct.xmin;
mval[1] = xy[1] - region->winrct.ymin;
update_overlay_strip_position_data(C, mval);
}
RNA_int_set(drop->ptr, "frame_start", g_drop_coords.start_frame);
RNA_int_set(drop->ptr, "channel", g_drop_coords.channel);
RNA_boolean_set(drop->ptr, "overlap_shuffle_override", true);
}
else {
/* We are dropped inside the preview region. Put the strip on top of the
* current displayed frame. */
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_ensure(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
SpaceSeq *sseq = CTX_wm_space_seq(C);
blender::VectorSet strips = SEQ_query_rendered_strips(
scene, channels, seqbase, scene->r.cfra, sseq->chanshown);
/* Get the top most strip channel that is in view. */
int max_channel = -1;
for (Sequence *seq : strips) {
max_channel = max_ii(seq->machine, max_channel);
}
if (max_channel != -1) {
RNA_int_set(drop->ptr, "channel", max_channel);
}
}
ID *id = WM_drag_get_local_ID_or_import_from_asset(C, drag, 0);
/* ID dropped. */
if (id != nullptr) {
@ -267,50 +311,6 @@ static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
RNA_string_set(&itemptr, "name", file);
}
}
if (g_drop_coords.in_use) {
if (!g_drop_coords.has_read_mouse_pos) {
/* We didn't read the mouse position, so we need to do it manually here. */
int xy[2];
wmWindow *win = CTX_wm_window(C);
xy[0] = win->eventstate->xy[0];
xy[1] = win->eventstate->xy[1];
ARegion *region = CTX_wm_region(C);
int mval[2];
/* Convert mouse coordinates to region local coordinates. */
mval[0] = xy[0] - region->winrct.xmin;
mval[1] = xy[1] - region->winrct.ymin;
update_overlay_strip_position_data(C, mval);
}
RNA_int_set(drop->ptr, "frame_start", g_drop_coords.start_frame);
RNA_int_set(drop->ptr, "channel", g_drop_coords.channel);
RNA_boolean_set(drop->ptr, "overlap_shuffle_override", true);
}
else {
/* We are dropped inside the preview region. Put the strip on top of the
* current displayed frame. */
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_ensure(scene);
ListBase *seqbase = SEQ_active_seqbase_get(ed);
ListBase *channels = SEQ_channels_displayed_get(ed);
SpaceSeq *sseq = CTX_wm_space_seq(C);
blender::VectorSet strips = SEQ_query_rendered_strips(
scene, channels, seqbase, scene->r.cfra, sseq->chanshown);
/* Get the top most strip channel that is in view. */
int max_channel = -1;
for (Sequence *seq : strips) {
max_channel = max_ii(seq->machine, max_channel);
}
if (max_channel != -1) {
RNA_int_set(drop->ptr, "channel", max_channel);
}
}
}
static void get_drag_path(const bContext *C, wmDrag *drag, char r_path[FILE_MAX])

View File

@ -405,7 +405,7 @@ static void gizmo_area_light_prop_matrix_set(const wmGizmo * /*gz*/,
la->area_size = len_v3(matrix[0]);
}
DEG_id_tag_update(&la->id, ID_RECALC_SYNC_TO_EVAL);
DEG_id_tag_update(&la->id, ID_RECALC_PARAMETERS);
WM_main_add_notifier(NC_LAMP | ND_LIGHTING_DRAW, la);
}

View File

@ -355,13 +355,15 @@ static MemFile *ed_undosys_step_get_memfile(UndoStep *us_p)
return &us->data->memfile;
}
MemFile *ED_undosys_stack_memfile_get_active(UndoStack *ustack)
MemFile *ED_undosys_stack_memfile_get_if_active(UndoStack *ustack)
{
UndoStep *us = BKE_undosys_stack_active_with_type(ustack, BKE_UNDOSYS_TYPE_MEMFILE);
if (us) {
return ed_undosys_step_get_memfile(us);
if (!ustack->step_active) {
return nullptr;
}
return nullptr;
if (ustack->step_active->type != BKE_UNDOSYS_TYPE_MEMFILE) {
return nullptr;
}
return ed_undosys_step_get_memfile(ustack->step_active);
}
void ED_undosys_stack_memfile_id_changed_tag(UndoStack *ustack, ID *id)

View File

@ -25,6 +25,10 @@ struct ResampleCurvesOutputAttributeIDs {
*
* \note The values provided by the #count_field are clamped to 1 or greater.
*/
CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
const IndexMask &selection,
const VArray<int> &counts,
const ResampleCurvesOutputAttributeIDs &output_ids = {});
CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &selection_field,
@ -36,6 +40,10 @@ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
* #segment_length field input, rounded to make the length of each segment the same.
* The accuracy will depend on the curve's resolution parameter.
*/
CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
const IndexMask &selection,
const VArray<float> &sample_lengths,
const ResampleCurvesOutputAttributeIDs &output_ids = {});
CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &selection_field,
@ -45,6 +53,9 @@ CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
/**
* Evaluate each selected curve to its implicit evaluated points.
*/
CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
const IndexMask &selection,
const ResampleCurvesOutputAttributeIDs &output_ids = {});
CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &selection_field,

View File

@ -245,37 +245,21 @@ static void normalize_curve_point_data(const IndexMaskSegment curve_selection,
}
}
static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &selection_field,
const fn::Field<int> &count_field,
const ResampleCurvesOutputAttributeIDs &output_ids)
static void resample_to_uniform(const CurvesGeometry &src_curves,
const IndexMask &selection,
const ResampleCurvesOutputAttributeIDs &output_ids,
CurvesGeometry &dst_curves)
{
if (src_curves.curves_range().is_empty()) {
return {};
return;
}
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const OffsetIndices evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
const VArray<bool> curves_cyclic = src_curves.cyclic();
const VArray<int8_t> curve_types = src_curves.curve_types();
const Span<float3> evaluated_positions = src_curves.evaluated_positions();
CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(count_field, dst_offsets.drop_back(1));
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
IndexMaskMemory memory;
const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
/* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
offset_indices::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
/* All resampled curves are poly curves. */
dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
@ -385,11 +369,75 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
}
});
IndexMaskMemory memory;
const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
copy_or_defaults_for_unselected_curves(src_curves, unselected, attributes, dst_curves);
for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
attribute.finish();
}
}
static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &selection_field,
const fn::Field<int> &count_field,
const ResampleCurvesOutputAttributeIDs &output_ids)
{
if (src_curves.curves_range().is_empty()) {
return {};
}
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(count_field, dst_offsets.drop_back(1));
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
IndexMaskMemory memory;
const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
/* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
offset_indices::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
resample_to_uniform(src_curves, selection, output_ids, dst_curves);
return dst_curves;
}
CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
const IndexMask &selection,
const VArray<int> &counts,
const ResampleCurvesOutputAttributeIDs &output_ids)
{
if (src_curves.curves_range().is_empty()) {
return {};
}
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
array_utils::copy(counts, selection, dst_offsets);
IndexMaskMemory memory;
const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
/* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
/* We assume the counts are at least 1. */
BLI_assert(std::all_of(dst_offsets.begin(),
dst_offsets.drop_back(1).end(),
[&](const int count) { return count > 0; }));
offset_indices::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
resample_to_uniform(src_curves, selection, output_ids, dst_curves);
return dst_curves;
}
@ -407,6 +455,40 @@ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
output_ids);
}
CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
const IndexMask &selection,
const VArray<float> &sample_lengths,
const ResampleCurvesOutputAttributeIDs &output_ids)
{
if (src_curves.curves_range().is_empty()) {
return {};
}
const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
const VArray<bool> curves_cyclic = src_curves.cyclic();
CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
src_curves.ensure_evaluated_lengths();
selection.foreach_index(GrainSize(1024), [&](const int curve_i) {
const float curve_length = src_curves.evaluated_length_total_for_curve(curve_i,
curves_cyclic[curve_i]);
dst_offsets[curve_i] = int(curve_length / sample_lengths[curve_i]) + 1;
});
IndexMaskMemory memory;
const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
/* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
offset_indices::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
resample_to_uniform(src_curves, selection, output_ids, dst_curves);
return dst_curves;
}
CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &selection_field,
@ -421,8 +503,7 @@ CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
}
CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &selection_field,
const IndexMask &selection,
const ResampleCurvesOutputAttributeIDs &output_ids)
{
if (src_curves.curves_range().is_empty()) {
@ -432,10 +513,6 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
const OffsetIndices src_evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
const Span<float3> evaluated_positions = src_curves.evaluated_positions();
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
IndexMaskMemory memory;
const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
@ -511,4 +588,19 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
return dst_curves;
}
CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
const fn::FieldContext &field_context,
const fn::Field<bool> &selection_field,
const ResampleCurvesOutputAttributeIDs &output_ids)
{
if (src_curves.curves_range().is_empty()) {
return {};
}
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
return resample_to_evaluated(
src_curves, evaluator.get_evaluated_selection_as_mask(), output_ids);
}
} // namespace blender::geometry

View File

@ -879,11 +879,6 @@ struct GreasePencilLineartModifierData;
struct LineartData;
struct Scene;
void MOD_lineart_wrap_modifier_v3(const LineartGpencilModifierData *lmd_legacy,
GreasePencilLineartModifierData *lmd);
void MOD_lineart_unwrap_modifier_v3(LineartGpencilModifierData *lmd_legacy,
const GreasePencilLineartModifierData *lmd);
void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd_legacy);
void MOD_lineart_destroy_render_data_v3(struct GreasePencilLineartModifierData *lmd);

View File

@ -36,6 +36,7 @@
#include "BKE_gpencil_legacy.h"
#include "BKE_gpencil_modifier_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_grease_pencil_legacy_convert.hh"
#include "BKE_lib_id.hh"
#include "BKE_material.h"
#include "BKE_mesh.hh"
@ -3558,100 +3559,6 @@ static void lineart_destroy_render_data(LineartData *ld)
lineart_mem_destroy(&ld->render_data_pool);
}
void MOD_lineart_wrap_modifier_v3(const LineartGpencilModifierData *lmd_legacy,
GreasePencilLineartModifierData *lmd)
{
#define LMD_WRAP(var) lmd->var = lmd_legacy->var
LMD_WRAP(edge_types);
LMD_WRAP(source_type);
LMD_WRAP(use_multiple_levels);
LMD_WRAP(level_start);
LMD_WRAP(level_end);
LMD_WRAP(source_camera);
LMD_WRAP(light_contour_object);
LMD_WRAP(source_object);
LMD_WRAP(source_collection);
LMD_WRAP(target_material);
STRNCPY(lmd->source_vertex_group, lmd_legacy->source_vertex_group);
STRNCPY(lmd->vgname, lmd_legacy->vgname);
LMD_WRAP(overscan);
LMD_WRAP(shadow_camera_fov);
LMD_WRAP(shadow_camera_size);
LMD_WRAP(shadow_camera_near);
LMD_WRAP(shadow_camera_far);
LMD_WRAP(opacity);
LMD_WRAP(thickness);
LMD_WRAP(mask_switches);
LMD_WRAP(material_mask_bits);
LMD_WRAP(intersection_mask);
LMD_WRAP(shadow_selection);
LMD_WRAP(silhouette_selection);
LMD_WRAP(crease_threshold);
LMD_WRAP(angle_splitting_threshold);
LMD_WRAP(chain_smooth_tolerance);
LMD_WRAP(chaining_image_threshold);
LMD_WRAP(calculation_flags);
LMD_WRAP(flags);
LMD_WRAP(stroke_depth_offset);
LMD_WRAP(level_start_override);
LMD_WRAP(level_end_override);
LMD_WRAP(edge_types_override);
LMD_WRAP(shadow_selection_override);
LMD_WRAP(shadow_use_silhouette_override);
LMD_WRAP(cache);
LMD_WRAP(la_data_ptr);
#undef LMD_WRAP
}
void MOD_lineart_unwrap_modifier_v3(LineartGpencilModifierData *lmd_legacy,
const GreasePencilLineartModifierData *lmd)
{
#define LMD_UNWRAP(var) lmd_legacy->var = lmd->var
LMD_UNWRAP(edge_types);
LMD_UNWRAP(source_type);
LMD_UNWRAP(use_multiple_levels);
LMD_UNWRAP(level_start);
LMD_UNWRAP(level_end);
LMD_UNWRAP(source_camera);
LMD_UNWRAP(light_contour_object);
LMD_UNWRAP(source_object);
LMD_UNWRAP(source_collection);
LMD_UNWRAP(target_material);
STRNCPY(lmd_legacy->source_vertex_group, lmd->source_vertex_group);
STRNCPY(lmd_legacy->vgname, lmd->vgname);
LMD_UNWRAP(overscan);
LMD_UNWRAP(shadow_camera_fov);
LMD_UNWRAP(shadow_camera_size);
LMD_UNWRAP(shadow_camera_near);
LMD_UNWRAP(shadow_camera_far);
LMD_UNWRAP(opacity);
LMD_UNWRAP(thickness);
LMD_UNWRAP(mask_switches);
LMD_UNWRAP(material_mask_bits);
LMD_UNWRAP(intersection_mask);
LMD_UNWRAP(shadow_selection);
LMD_UNWRAP(silhouette_selection);
LMD_UNWRAP(crease_threshold);
LMD_UNWRAP(angle_splitting_threshold);
LMD_UNWRAP(chain_smooth_tolerance);
LMD_UNWRAP(chaining_image_threshold);
LMD_UNWRAP(calculation_flags);
LMD_UNWRAP(flags);
LMD_UNWRAP(stroke_depth_offset);
LMD_UNWRAP(level_start_override);
LMD_UNWRAP(level_end_override);
LMD_UNWRAP(edge_types_override);
LMD_UNWRAP(shadow_selection_override);
LMD_UNWRAP(shadow_use_silhouette_override);
LMD_UNWRAP(cache);
LMD_UNWRAP(la_data_ptr);
#undef LMD_UNWRAP
}
void MOD_lineart_destroy_render_data_v3(GreasePencilLineartModifierData *lmd)
{
LineartData *ld = lmd->la_data_ptr;
@ -3671,9 +3578,9 @@ void MOD_lineart_destroy_render_data_v3(GreasePencilLineartModifierData *lmd)
void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd_legacy)
{
GreasePencilLineartModifierData lmd;
MOD_lineart_wrap_modifier_v3(lmd_legacy, &lmd);
greasepencil::convert::lineart_wrap_v3(lmd_legacy, &lmd);
MOD_lineart_destroy_render_data_v3(&lmd);
MOD_lineart_unwrap_modifier_v3(lmd_legacy, &lmd);
greasepencil::convert::lineart_unwrap_v3(lmd_legacy, &lmd);
}
LineartCache *MOD_lineart_init_cache()
@ -5325,10 +5232,10 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
{
bool ret = false;
GreasePencilLineartModifierData lmd;
MOD_lineart_wrap_modifier_v3(lmd_legacy, &lmd);
greasepencil::convert::lineart_wrap_v3(lmd_legacy, &lmd);
ret = MOD_lineart_compute_feature_lines_v3(
depsgraph, lmd, cached_result, enable_stroke_depth_offset);
MOD_lineart_unwrap_modifier_v3(lmd_legacy, &lmd);
greasepencil::convert::lineart_unwrap_v3(lmd_legacy, &lmd);
return ret;
}

View File

@ -13,6 +13,8 @@
#include "BKE_global.hh"
#include "BKE_gpencil_modifier_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_grease_pencil_legacy_convert.hh"
#include "BKE_object.hh"
#include "BLI_math_matrix.h"
@ -1300,7 +1302,7 @@ bool lineart_main_try_generate_shadow(Depsgraph *depsgraph,
{
bool ret = false;
GreasePencilLineartModifierData lmd;
MOD_lineart_wrap_modifier_v3(lmd_legacy, &lmd);
blender::bke::greasepencil::convert::lineart_wrap_v3(lmd_legacy, &lmd);
ret = lineart_main_try_generate_shadow_v3(depsgraph,
scene,
original_ld,
@ -1310,7 +1312,7 @@ bool lineart_main_try_generate_shadow(Depsgraph *depsgraph,
r_eeln,
r_calculated_edges_eln_list,
r_shadow_ld_if_reproject);
MOD_lineart_unwrap_modifier_v3(lmd_legacy, &lmd);
blender::bke::greasepencil::convert::lineart_unwrap_v3(lmd_legacy, &lmd);
return ret;
}

View File

@ -1429,13 +1429,33 @@ static int imb_exr_split_token(const char *str, const char *end, const char **to
return int(end - *token);
}
static void imb_exr_pass_name_from_channel(char *passname,
const ExrChannel *echan,
const char *channelname,
const bool has_xyz_channels)
{
const int passname_maxncpy = EXR_TOT_MAXNAME;
if (echan->chan_id == 'Z' && (!has_xyz_channels || BLI_strcaseeq(channelname, "depth"))) {
BLI_strncpy(passname, "Depth", passname_maxncpy);
}
else if (echan->chan_id == 'Y' && !has_xyz_channels) {
BLI_strncpy(passname, channelname, passname_maxncpy);
}
else if (ELEM(echan->chan_id, 'R', 'G', 'B', 'A', 'V', 'X', 'Y', 'Z')) {
BLI_strncpy(passname, "Combined", passname_maxncpy);
}
else {
BLI_strncpy(passname, channelname, passname_maxncpy);
}
}
static int imb_exr_split_channel_name(ExrChannel *echan,
char *layname,
char *passname,
bool has_xyz_channels)
{
const int layname_maxncpy = EXR_TOT_MAXNAME;
const int passname_maxncpy = EXR_TOT_MAXNAME;
const char *name = echan->m->name.c_str();
const char *end = name + strlen(name);
const char *token;
@ -1450,20 +1470,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan,
* versions of the listed channels. */
echan->chan_id = BLI_toupper_ascii(name[0]);
layname[0] = '\0';
if (echan->chan_id == 'Z' && !has_xyz_channels) {
BLI_strncpy(passname, "Depth", passname_maxncpy);
}
else if (echan->chan_id == 'Y' && !has_xyz_channels) {
BLI_strncpy(passname, name, passname_maxncpy);
}
else if (ELEM(echan->chan_id, 'R', 'G', 'B', 'A', 'V', 'X', 'Y', 'Z')) {
BLI_strncpy(passname, "Combined", passname_maxncpy);
}
else {
BLI_strncpy(passname, name, passname_maxncpy);
}
imb_exr_pass_name_from_channel(passname, echan, name, has_xyz_channels);
return 1;
}
@ -1519,14 +1526,20 @@ static int imb_exr_split_channel_name(ExrChannel *echan,
}
end -= len + 1; /* +1 to skip '.' separator */
/* second token is pass name */
len = imb_exr_split_token(name, end, &token);
if (len == 0) {
printf("multilayer read: bad channel name: %s\n", name);
return 0;
if (end > name) {
/* second token is pass name */
len = imb_exr_split_token(name, end, &token);
if (len == 0) {
printf("multilayer read: bad channel name: %s\n", name);
return 0;
}
BLI_strncpy(passname, token, len + 1);
end -= len + 1; /* +1 to skip '.' separator */
}
else {
/* Single token, determine pass name from channel name. */
imb_exr_pass_name_from_channel(passname, echan, channelname, has_xyz_channels);
}
BLI_strncpy(passname, token, len + 1);
end -= len + 1; /* +1 to skip '.' separator */
/* all preceding tokens combined as layer name */
if (end > name) {
@ -1592,10 +1605,65 @@ static bool exr_has_xyz_channels(ExrHandle *exr_handle)
return x_found && y_found && z_found;
}
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
/* Replacement for OpenEXR GetChannelsInMultiPartFile, that also handles the
* case where parts are used for passes instead of multiview. */
static std::vector<MultiViewChannelName> exr_channels_in_multi_part_file(
const MultiPartInputFile &file)
{
std::vector<MultiViewChannelName> channels;
GetChannelsInMultiPartFile(*data->ifile, channels);
/* Detect if file has multiview. */
StringVector multiview;
bool has_multiview = false;
if (file.parts() == 1) {
if (hasMultiView(file.header(0))) {
multiview = multiView(file.header(0));
has_multiview = true;
}
}
/* Get channels from each part. */
for (int p = 0; p < file.parts(); p++) {
const ChannelList &c = file.header(p).channels();
std::string part_view = "";
if (file.header(p).hasView()) {
part_view = file.header(p).view();
}
std::string part_name = "";
if (file.header(p).hasName()) {
part_name = file.header(p).name();
}
for (ChannelList::ConstIterator i = c.begin(); i != c.end(); i++) {
MultiViewChannelName m;
m.name = std::string(i.name());
m.internal_name = m.name;
if (has_multiview) {
m.view = viewFromChannelName(m.name, multiview);
m.name = removeViewName(m.internal_name, m.view);
}
else {
m.view = part_view;
}
/* Prepend part name as potential layer or pass name. */
if (!part_name.empty()) {
m.name = part_name + "." + m.name;
}
m.part_number = p;
channels.push_back(m);
}
}
return channels;
}
static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
{
std::vector<MultiViewChannelName> channels = exr_channels_in_multi_part_file(*data->ifile);
imb_exr_get_views(*data->ifile, *data->multiView);

View File

@ -44,6 +44,7 @@
.volume_fac = 1.0f, \
.shadow_softness_factor = 1.0f, \
.shadow_trace_distance = 10.0f, \
.shadow_filter_radius = 3.0f, \
.att_dist = 40.0f, \
.sun_angle = DEG2RADF(0.526f), \
.area_spread = DEG2RADF(180.0f), \

View File

@ -77,7 +77,7 @@ typedef struct Light {
float spec_fac, att_dist;
float shadow_softness_factor;
float shadow_trace_distance;
float _pad3;
float shadow_filter_radius;
/* Preview */
struct PreviewImage *preview;

View File

@ -3066,7 +3066,7 @@ typedef struct GreasePencilLineartModifierData {
*
* Do not change any of the data below since the layout of these
* data is currently shared with the old line art modifier.
* See `MOD_lineart_wrap_modifier_v3` for how it works. */
* See `BKE_grease_pencil_lineart_wrap_v3` for how it works. */
uint16_t edge_types; /* line type enable flags, bits in eLineartEdgeFlag */

View File

@ -1929,8 +1929,8 @@ typedef struct SceneEEVEE {
int shadow_ray_count;
int shadow_step_count;
float shadow_normal_bias;
float _pad0;
char _pad[4];
int ray_tracing_method;
struct RaytraceEEVEE ray_tracing_options;

View File

@ -210,6 +210,9 @@ typedef struct wmWindowManager {
ListBase timers;
/** Timer for auto save. */
struct wmTimer *autosavetimer;
/** Auto-save timer was up, but it wasn't possible to auto-save in the current mode. */
char autosave_scheduled;
char _pad2[7];
/** All undo history (runtime only). */
struct UndoStack *undo_stack;

View File

@ -296,6 +296,14 @@ static void rna_def_light_shadow(StructRNA *srna, bool sun)
prop, "Shadow Softness Factor", "Scale light shape for smaller penumbra");
RNA_def_property_update(prop, 0, "rna_Light_update");
prop = RNA_def_property(srna, "shadow_filter_radius", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 5.0f, 1.0f, 2);
RNA_def_property_ui_text(
prop, "Shadow Filter Radius", "Blur shadow aliasing using Percentage Closer Filtering");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_Light_update");
if (sun) {
prop = RNA_def_property(srna, "shadow_cascade_max_distance", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, nullptr, "cascade_max_dist");

View File

@ -15,6 +15,7 @@
#include "BKE_tracking.h"
#include "COM_algorithm_smaa.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
@ -75,31 +76,71 @@ class CornerPinOperation : public NodeOperation {
return;
}
Result plane_mask = compute_plane_mask(homography_matrix);
Result anti_aliased_plane_mask = context().create_temporary_result(ResultType::Float);
smaa(context(), plane_mask, anti_aliased_plane_mask);
plane_mask.release();
if (output_image.should_compute()) {
compute_plane(homography_matrix, anti_aliased_plane_mask);
}
if (output_mask.should_compute()) {
output_mask.steal_data(anti_aliased_plane_mask);
}
else {
anti_aliased_plane_mask.release();
}
}
void compute_plane(const float3x3 &homography_matrix, Result &plane_mask)
{
GPUShader *shader = context().get_shader("compositor_plane_deform");
GPU_shader_bind(shader);
GPU_shader_uniform_mat3_as_mat4(shader, "homography_matrix", homography_matrix.ptr());
Result &input_image = get_input("Image");
GPU_texture_mipmap_mode(input_image.texture(), true, true);
GPU_texture_anisotropic_filter(input_image.texture(), true);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_EXTEND);
input_image.bind_as_texture(shader, "input_tx");
plane_mask.bind_as_texture(shader, "mask_tx");
const Domain domain = compute_domain();
Result &output_image = get_result("Image");
output_image.allocate_texture(domain);
output_image.bind_as_image(shader, "output_img");
output_mask.allocate_texture(domain);
output_mask.bind_as_image(shader, "mask_img");
compute_dispatch_threads_at_least(shader, domain.size);
input_image.unbind_as_texture();
plane_mask.unbind_as_texture();
output_image.unbind_as_image();
output_mask.unbind_as_image();
GPU_shader_unbind();
}
Result compute_plane_mask(const float3x3 &homography_matrix)
{
GPUShader *shader = context().get_shader("compositor_plane_deform_mask");
GPU_shader_bind(shader);
GPU_shader_uniform_mat3_as_mat4(shader, "homography_matrix", homography_matrix.ptr());
const Domain domain = compute_domain();
Result plane_mask = context().create_temporary_result(ResultType::Float);
plane_mask.allocate_texture(domain);
plane_mask.bind_as_image(shader, "mask_img");
compute_dispatch_threads_at_least(shader, domain.size);
plane_mask.unbind_as_image();
GPU_shader_unbind();
return plane_mask;
}
float3x3 compute_homography_matrix()
{
float2 lower_left = get_input("Lower Left").get_vector_value_default(float4(0.0f)).xy();

View File

@ -30,6 +30,7 @@
#include "GPU_texture.h"
#include "GPU_uniform_buffer.h"
#include "COM_algorithm_smaa.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
@ -144,40 +145,87 @@ class PlaneTrackDeformOperation : public NodeOperation {
}
const Array<float4x4> homography_matrices = compute_homography_matrices(plane_track);
GPUUniformBuf *homography_matrices_buffer = GPU_uniformbuf_create_ex(
homography_matrices.size() * sizeof(float4x4),
homography_matrices.data(),
"Plane Track Deform Homography Matrices");
Result plane_mask = compute_plane_mask(homography_matrices, homography_matrices_buffer);
Result anti_aliased_plane_mask = context().create_temporary_result(ResultType::Float);
smaa(context(), plane_mask, anti_aliased_plane_mask);
plane_mask.release();
if (output_image.should_compute()) {
compute_plane(homography_matrices, homography_matrices_buffer, anti_aliased_plane_mask);
}
if (output_mask.should_compute()) {
output_mask.steal_data(anti_aliased_plane_mask);
}
else {
anti_aliased_plane_mask.release();
}
GPU_uniformbuf_free(homography_matrices_buffer);
}
void compute_plane(const Array<float4x4> &homography_matrices,
GPUUniformBuf *homography_matrices_buffer,
Result &plane_mask)
{
GPUShader *shader = context().get_shader("compositor_plane_deform_motion_blur");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "number_of_motion_blur_samples", homography_matrices.size());
GPUUniformBuf *matrices_buffer = GPU_uniformbuf_create_ex(
homography_matrices.size() * sizeof(float4x4),
homography_matrices.data(),
"Plane Track Deform Homography Matrices");
const int ubo_location = GPU_shader_get_ubo_binding(shader, "homography_matrices");
GPU_uniformbuf_bind(matrices_buffer, ubo_location);
GPU_uniformbuf_bind(homography_matrices_buffer, ubo_location);
Result &input_image = get_input("Image");
GPU_texture_mipmap_mode(input_image.texture(), true, true);
GPU_texture_anisotropic_filter(input_image.texture(), true);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode(input_image.texture(), GPU_SAMPLER_EXTEND_MODE_EXTEND);
input_image.bind_as_texture(shader, "input_tx");
plane_mask.bind_as_texture(shader, "mask_tx");
const Domain domain = compute_domain();
Result &output_image = get_result("Image");
output_image.allocate_texture(domain);
output_image.bind_as_image(shader, "output_img");
output_mask.allocate_texture(domain);
output_mask.bind_as_image(shader, "mask_img");
compute_dispatch_threads_at_least(shader, domain.size);
input_image.unbind_as_texture();
plane_mask.unbind_as_texture();
output_image.unbind_as_image();
output_mask.unbind_as_image();
GPU_uniformbuf_unbind(homography_matrices_buffer);
GPU_shader_unbind();
}
Result compute_plane_mask(const Array<float4x4> &homography_matrices,
GPUUniformBuf *homography_matrices_buffer)
{
GPUShader *shader = context().get_shader("compositor_plane_deform_motion_blur_mask");
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "number_of_motion_blur_samples", homography_matrices.size());
const int ubo_location = GPU_shader_get_ubo_binding(shader, "homography_matrices");
GPU_uniformbuf_bind(homography_matrices_buffer, ubo_location);
const Domain domain = compute_domain();
Result plane_mask = context().create_temporary_result(ResultType::Float);
plane_mask.allocate_texture(domain);
plane_mask.bind_as_image(shader, "mask_img");
compute_dispatch_threads_at_least(shader, domain.size);
plane_mask.unbind_as_image();
GPU_uniformbuf_unbind(homography_matrices_buffer);
GPU_shader_unbind();
GPU_uniformbuf_unbind(matrices_buffer);
GPU_uniformbuf_free(matrices_buffer);
return plane_mask;
}
Domain compute_domain() override

View File

@ -1720,6 +1720,10 @@ void WM_main_playanim(int argc, const char **argv);
*/
bool write_crash_blend();
bool WM_autosave_is_scheduled(wmWindowManager *wm);
/** Flushes all changes from edit modes and stores the auto-save file. */
void WM_autosave_write(wmWindowManager *wm, Main *bmain);
/**
* Lock the interface for any communication.
*/

View File

@ -2109,33 +2109,48 @@ static void wm_autosave_location(char filepath[FILE_MAX])
BLI_path_join(filepath, FILE_MAX, tempdir_base, filename);
}
static void wm_autosave_write(Main *bmain, wmWindowManager *wm)
static bool wm_autosave_write_try(Main *bmain, wmWindowManager *wm)
{
char filepath[FILE_MAX];
wm_autosave_location(filepath);
/* Fast save of last undo-buffer, now with UI. */
const bool use_memfile = (U.uiflag & USER_GLOBALUNDO) != 0;
MemFile *memfile = use_memfile ? ED_undosys_stack_memfile_get_active(wm->undo_stack) : nullptr;
if (memfile != nullptr) {
if (MemFile *memfile = ED_undosys_stack_memfile_get_if_active(wm->undo_stack)) {
/* Fast save of last undo-buffer, now with UI. */
BLO_memfile_write_file(memfile, filepath);
return true;
}
else {
if (use_memfile) {
/* This is very unlikely, alert developers of this unexpected case. */
CLOG_WARN(&LOG, "undo-data not found for writing, fallback to regular file write!");
}
/* Save as regular blend file with recovery information. */
const int fileflags = (G.fileflags & ~G_FILE_COMPRESS) | G_FILE_RECOVER_WRITE;
ED_editors_flush_edits(bmain);
/* Error reporting into console. */
BlendFileWriteParams params{};
BLO_write_file(bmain, filepath, fileflags, &params, nullptr);
if ((U.uiflag & USER_GLOBALUNDO) == 0) {
WM_autosave_write(wm, bmain);
return true;
}
/* Can't auto-save with MemFile right now, try again later. */
return false;
}
bool WM_autosave_is_scheduled(wmWindowManager *wm)
{
return wm->autosave_scheduled;
}
void WM_autosave_write(wmWindowManager *wm, Main *bmain)
{
ED_editors_flush_edits(bmain);
char filepath[FILE_MAX];
wm_autosave_location(filepath);
/* Save as regular blend file with recovery information. */
const int fileflags = (G.fileflags & ~G_FILE_COMPRESS) | G_FILE_RECOVER_WRITE;
/* Error reporting into console. */
BlendFileWriteParams params{};
BLO_write_file(bmain, filepath, fileflags, &params, nullptr);
/* Restart auto-save timer. */
wm_autosave_timer_end(wm);
wm_autosave_timer_begin(wm);
wm->autosave_scheduled = false;
}
static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
@ -2183,8 +2198,10 @@ void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer * /*wt*/)
}
}
wm_autosave_write(bmain, wm);
wm->autosave_scheduled = false;
if (!wm_autosave_write_try(bmain, wm)) {
wm->autosave_scheduled = true;
}
/* Restart the timer after file write, just in case file write takes a long time. */
wm_autosave_timer_begin(wm);
}
@ -3410,6 +3427,7 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
* often saving manually. */
wm_autosave_timer_end(wm);
wm_autosave_timer_begin(wm);
wm->autosave_scheduled = false;
}
if (!is_save_as && RNA_boolean_get(op->ptr, "exit")) {

View File

@ -473,29 +473,26 @@ void WM_exit_ex(bContext *C, const bool do_python_exit, const bool do_user_exit_
/* NOTE: same code copied in `wm_files.cc`. */
if (C && wm) {
if (do_user_exit_actions) {
/* save the undo state as quit.blend */
Main *bmain = CTX_data_main(C);
char filepath[FILE_MAX];
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
MemFile *undo_memfile = wm->undo_stack ?
ED_undosys_stack_memfile_get_active(wm->undo_stack) :
ED_undosys_stack_memfile_get_if_active(wm->undo_stack) :
nullptr;
if (undo_memfile != nullptr) {
/* save the undo state as quit.blend */
Main *bmain = CTX_data_main(C);
char filepath[FILE_MAX];
const int fileflags = G.fileflags & ~G_FILE_COMPRESS;
/* When true, the `undo_memfile` doesn't contain all information necessary
* for writing and up to date blend file. */
const bool is_memfile_outdated = ED_editors_flush_edits(bmain);
BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
/* When true, the `undo_memfile` doesn't contain all information necessary
* for writing and up to date blend file. */
const bool is_memfile_outdated = ED_editors_flush_edits(bmain);
BlendFileWriteParams blend_file_write_params{};
if (is_memfile_outdated ?
BLO_write_file(bmain, filepath, fileflags, &blend_file_write_params, nullptr) :
BLO_memfile_write_file(undo_memfile, filepath))
{
printf("Saved session recovery to \"%s\"\n", filepath);
}
if (undo_memfile && !is_memfile_outdated) {
BLO_memfile_write_file(undo_memfile, filepath);
}
else {
const int fileflags = G.fileflags & ~G_FILE_COMPRESS;
BlendFileWriteParams blend_file_write_params{};
BLO_write_file(bmain, filepath, fileflags, &blend_file_write_params, nullptr);
}
printf("Saved session recovery to \"%s\"\n", filepath);
}
WM_jobs_kill_all(wm);

@ -1 +1 @@
Subproject commit 3a0fc9f3bae1f1a838ed9e2aac0648fdad291817
Subproject commit 15c20a4b5d40dfbb1f64c65d898ea22ab00b1f1a