WIP: Brush assets project #106303
|
@ -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++;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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".
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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), \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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, ¶ms, 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, ¶ms, 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")) {
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue