Versioning function to replace legacy instancing panel by geometry node modifier #105494

Open
Iliya Katushenock wants to merge 44 commits from mod_moder/blender:instances into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
25 changed files with 689 additions and 71 deletions
Showing only changes of commit 28ff78b39f - Show all commits

View File

@ -981,22 +981,8 @@ static ShaderNode *add_node(Scene *scene,
sky->set_sun_disc(b_sky_node.sun_disc());
sky->set_sun_size(b_sky_node.sun_size());
sky->set_sun_intensity(b_sky_node.sun_intensity());
/* Patch sun position to be able to animate daylight cycle while keeping the shading code
* simple. */
float sun_rotation = b_sky_node.sun_rotation();
/* Wrap into [-2PI..2PI] range. */
float sun_elevation = fmodf(b_sky_node.sun_elevation(), M_2PI_F);
/* Wrap into [-PI..PI] range. */
if (fabsf(sun_elevation) >= M_PI_F) {
sun_elevation -= copysignf(2.0f, sun_elevation) * M_PI_F;
}
/* Wrap into [-PI/2..PI/2] range while keeping the same absolute position. */
if (sun_elevation >= M_PI_2_F || sun_elevation <= -M_PI_2_F) {
sun_elevation = copysignf(M_PI_F, sun_elevation) - sun_elevation;
sun_rotation += M_PI_F;
}
sky->set_sun_elevation(sun_elevation);
sky->set_sun_rotation(sun_rotation);
sky->set_sun_elevation(b_sky_node.sun_elevation());
sky->set_sun_rotation(b_sky_node.sun_rotation());
sky->set_altitude(b_sky_node.altitude());
sky->set_air_density(b_sky_node.air_density());
sky->set_dust_density(b_sky_node.dust_density());

View File

@ -55,21 +55,29 @@ void WorkTileScheduler::reset_scheduler_state()
VLOG_WORK << "Will schedule tiles of size " << tile_size_;
if (VLOG_IS_ON(3)) {
/* The logging is based on multiple tiles scheduled, ignoring overhead of multi-tile scheduling
* and purely focusing on the number of used path states. */
const int num_path_states_in_tile = tile_size_.width * tile_size_.height *
tile_size_.num_samples;
const int num_tiles = max_num_path_states_ / num_path_states_in_tile;
VLOG_WORK << "Number of unused path states: "
<< max_num_path_states_ - num_tiles * num_path_states_in_tile;
const int num_path_states_in_tile = tile_size_.width * tile_size_.height *
tile_size_.num_samples;
if (num_path_states_in_tile == 0) {
num_tiles_x_ = 0;
num_tiles_y_ = 0;
num_tiles_per_sample_range_ = 0;
}
else {
if (VLOG_IS_ON(3)) {
/* The logging is based on multiple tiles scheduled, ignoring overhead of multi-tile
* scheduling and purely focusing on the number of used path states. */
const int num_tiles = max_num_path_states_ / num_path_states_in_tile;
VLOG_WORK << "Number of unused path states: "
<< max_num_path_states_ - num_tiles * num_path_states_in_tile;
}
num_tiles_x_ = divide_up(image_size_px_.x, tile_size_.width);
num_tiles_y_ = divide_up(image_size_px_.y, tile_size_.height);
num_tiles_per_sample_range_ = divide_up(samples_num_, tile_size_.num_samples);
}
num_tiles_x_ = divide_up(image_size_px_.x, tile_size_.width);
num_tiles_y_ = divide_up(image_size_px_.y, tile_size_.height);
total_tiles_num_ = num_tiles_x_ * num_tiles_y_;
num_tiles_per_sample_range_ = divide_up(samples_num_, tile_size_.num_samples);
next_work_index_ = 0;
total_work_size_ = total_tiles_num_ * num_tiles_per_sample_range_;

View File

@ -20,6 +20,7 @@ KERNEL_STRUCT_BEGIN(KernelBackground, background)
/* xyz store direction, w the angle. float4 instead of float3 is used
* to ensure consistent padding/alignment across devices. */
KERNEL_STRUCT_MEMBER(background, float4, sun)
KERNEL_STRUCT_MEMBER(background, int, use_sun_guiding)
/* Only shader index. */
KERNEL_STRUCT_MEMBER(background, int, surface_shader)
KERNEL_STRUCT_MEMBER(background, int, volume_shader)
@ -39,6 +40,10 @@ KERNEL_STRUCT_MEMBER(background, int, use_mis)
KERNEL_STRUCT_MEMBER(background, int, lightgroup)
/* Light Index. */
KERNEL_STRUCT_MEMBER(background, int, light_index)
/* Padding. */
KERNEL_STRUCT_MEMBER(background, int, pad1)
KERNEL_STRUCT_MEMBER(background, int, pad2)
KERNEL_STRUCT_MEMBER(background, int, pad3)
KERNEL_STRUCT_END(KernelBackground)
/* BVH: own BVH2 if no native device acceleration struct used. */

View File

@ -132,11 +132,11 @@ color sky_radiance_nishita(vector dir, float nishita_data[10], string filename)
/* definitions */
vector sun_dir = geographical_to_direction(sun_elevation, sun_rotation + M_PI_2);
float sun_dir_angle = precise_angle(dir, sun_dir);
float half_angular = angular_diameter / 2.0;
float half_angular = angular_diameter * 0.5;
float dir_elevation = M_PI_2 - direction[0];
/* if ray inside sun disc render it, otherwise render sky.
* alternatively, ignore the sun if we're evaluating the background texture. */
/* If the ray is inside the sun disc, render it, otherwise render the sky.
* Alternatively, ignore the sun if we're evaluating the background texture. */
if (sun_dir_angle < half_angular && sun_disc == 1 && raytype("importance_bake") != 1) {
/* get 2 pixels data */
color pixel_bottom = color(nishita_data[0], nishita_data[1], nishita_data[2]);

View File

@ -84,8 +84,8 @@ ccl_device_inline void sample_uniform_cone(const float3 N,
ccl_device_inline float pdf_uniform_cone(const float3 N, float3 D, float angle)
{
float zMin = cosf(angle);
float z = dot(N, D);
if (z > zMin) {
float z = precise_angle(N, D);
if (z < angle) {
return M_1_2PI_F / (1.0f - zMin);
}
return 0.0f;

View File

@ -138,12 +138,13 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals kg,
/* definitions */
float3 sun_dir = geographical_to_direction(sun_elevation, sun_rotation + M_PI_2_F);
float sun_dir_angle = precise_angle(dir, sun_dir);
float half_angular = angular_diameter / 2.0f;
float half_angular = angular_diameter * 0.5f;
float dir_elevation = M_PI_2_F - direction.x;
/* if ray inside sun disc render it, otherwise render sky.
* alternatively, ignore the sun if we're evaluating the background texture. */
if (sun_disc && sun_dir_angle < half_angular && !(path_flag & PATH_RAY_IMPORTANCE_BAKE)) {
/* If the ray is inside the sun disc, render it, otherwise render the sky.
* Alternatively, ignore the sun if we're evaluating the background texture. */
if (sun_disc && sun_dir_angle < half_angular &&
!((path_flag & PATH_RAY_IMPORTANCE_BAKE) && kernel_data.background.use_sun_guiding)) {
/* get 2 pixels data */
float y;

View File

@ -730,7 +730,7 @@ void LightManager::device_update_background(Device *device,
/* Determine sun direction from lat/long and texture mapping. */
float latitude = sky->get_sun_elevation();
float longitude = M_2PI_F - sky->get_sun_rotation() + M_PI_2_F;
float longitude = sky->get_sun_rotation() + M_PI_2_F;
float3 sun_direction = make_float3(
cosf(latitude) * cosf(longitude), cosf(latitude) * sinf(longitude), sinf(latitude));
Transform sky_transform = transform_inverse(sky->tex_mapping.compute_transform());
@ -752,7 +752,8 @@ void LightManager::device_update_background(Device *device,
}
/* If there's more than one sun, fall back to map sampling instead. */
if (num_suns != 1) {
kbackground->use_sun_guiding = (num_suns == 1);
if (!kbackground->use_sun_guiding) {
kbackground->sun_weight = 0.0f;
environment_res.x = max(environment_res.x, 4096);
environment_res.y = max(environment_res.y, 2048);

View File

@ -758,12 +758,7 @@ static void sky_texture_precompute_nishita(SunSky *sunsky,
float pixel_top[3];
SKY_nishita_skymodel_precompute_sun(
sun_elevation, sun_size, altitude, air_density, dust_density, pixel_bottom, pixel_top);
/* limit sun rotation between 0 and 360 degrees */
sun_rotation = fmodf(sun_rotation, M_2PI_F);
if (sun_rotation < 0.0f) {
sun_rotation += M_2PI_F;
}
sun_rotation = M_2PI_F - sun_rotation;
/* send data to svm_sky */
sunsky->nishita_data[0] = pixel_bottom[0];
sunsky->nishita_data[1] = pixel_bottom[1];
@ -873,6 +868,37 @@ NODE_DEFINE(SkyTextureNode)
SkyTextureNode::SkyTextureNode() : TextureNode(get_node_type()) {}
void SkyTextureNode::simplify_settings(Scene * /* scene */)
{
/* Patch sun position so users are able to animate the daylight cycle while keeping the shading
* code simple. */
float new_sun_elevation = sun_elevation;
float new_sun_rotation = sun_rotation;
/* Wrap `new_sun_elevation` into [-2PI..2PI] range. */
new_sun_elevation = fmodf(new_sun_elevation, M_2PI_F);
/* Wrap `new_sun_elevation` into [-PI..PI] range. */
if (fabsf(new_sun_elevation) >= M_PI_F) {
new_sun_elevation -= copysignf(2.0f, new_sun_elevation) * M_PI_F;
}
/* Wrap `new_sun_elevation` into [-PI/2..PI/2] range while keeping the same absolute position. */
if (new_sun_elevation >= M_PI_2_F || new_sun_elevation <= -M_PI_2_F) {
new_sun_elevation = copysignf(M_PI_F, new_sun_elevation) - new_sun_elevation;
new_sun_rotation += M_PI_F;
}
/* Wrap `new_sun_rotation` into [-2PI..2PI] range. */
new_sun_rotation = fmodf(new_sun_rotation, M_2PI_F);
/* Wrap `new_sun_rotation` into [0..2PI] range. */
if (new_sun_rotation < 0.0f) {
new_sun_rotation += M_2PI_F;
}
new_sun_rotation = M_2PI_F - new_sun_rotation;
sun_elevation = new_sun_elevation;
sun_rotation = new_sun_rotation;
}
void SkyTextureNode::compile(SVMCompiler &compiler)
{
ShaderInput *vector_in = input("Vector");

View File

@ -167,6 +167,8 @@ class SkyTextureNode : public TextureNode {
NODE_SOCKET_API(float3, vector)
ImageHandle handle;
void simplify_settings(Scene *scene);
float get_sun_size()
{
/* Clamping for numerical precision. */

View File

@ -1046,7 +1046,7 @@ class I18nMessages:
self.unescape()
def write(self, kind, dest):
self.writers[kind](self, dest)
return self.writers[kind](self, dest)
def write_messages_to_po(self, fname, compact=False):
"""
@ -1126,8 +1126,8 @@ class I18nMessages:
"-o",
fname,
)
ret = subprocess.call(cmd)
return
ret = subprocess.run(cmd, capture_output=True)
return ret
# XXX Code below is currently broken (generates corrupted mo files it seems :( )!
# Using http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html notation.
# Not generating hash table!
@ -1427,7 +1427,7 @@ class I18n:
self.unescape()
def write(self, kind, langs=set()):
self.writers[kind](self, langs)
return self.writers[kind](self, langs)
def write_to_po(self, langs=set()):
"""

View File

@ -96,7 +96,7 @@ class AddPresetBase:
# Reset preset name
wm = bpy.data.window_managers[0]
if name == wm.preset_name:
wm.preset_name = 'New Preset'
wm.preset_name = data_('New Preset')
filename = self.as_filename(name)

View File

@ -14,10 +14,48 @@ extern "C" {
struct LightProbe;
struct Main;
struct BlendWriter;
struct BlendDataReader;
struct LightProbeObjectCache;
struct LightProbeGridCacheFrame;
struct Object;
void BKE_lightprobe_type_set(struct LightProbe *probe, short lightprobe_type);
void *BKE_lightprobe_add(struct Main *bmain, const char *name);
void BKE_lightprobe_cache_blend_write(struct BlendWriter *writer,
struct LightProbeObjectCache *cache);
void BKE_lightprobe_cache_blend_read(struct BlendDataReader *reader,
struct LightProbeObjectCache *cache);
/**
* Create a single empty irradiance grid cache.
*/
struct LightProbeGridCacheFrame *BKE_lightprobe_grid_cache_frame_create(void);
/**
* Free a single grid cache.
*/
void BKE_lightprobe_grid_cache_frame_free(struct LightProbeGridCacheFrame *cache);
/**
* Create the grid cache list depending on the lightprobe baking settings.
* The list is left empty to be filled by the baking process.
*/
void BKE_lightprobe_cache_create(struct Object *object);
/**
* Free all irradiance grids allocated for the given object.
*/
void BKE_lightprobe_cache_free(struct Object *object);
/**
* Return the number of sample stored inside an irradiance cache.
* This depends on the light cache type.
*/
int64_t BKE_lightprobe_grid_cache_frame_sample_count(const struct LightProbeGridCacheFrame *cache);
#ifdef __cplusplus
}
#endif

View File

@ -170,7 +170,10 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
{
const int cb_flag = cb_data->cb_flag;
if (cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_EMBEDDED_NOT_OWNING)) {
/* NOTE: Support remapping of `IDWALK_CB_EMBEDDED_NON_OWNING` pointers, this is necessary in some
* complex low-level ID manipulation cases (e.g. in ID swapping, see #BKE_lib_id_swap & co).
*/
if (cb_flag & IDWALK_CB_EMBEDDED) {
return IDWALK_RET_NOP;
}
@ -890,8 +893,8 @@ static void libblock_relink_to_newid_prepare_data(Main *bmain,
static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data)
{
const int cb_flag = cb_data->cb_flag;
if (cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_EMBEDDED_NOT_OWNING |
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
/* NOTE: For now, support remapping `IDWALK_CB_EMBEDDED_NON_OWNING` pointers. */
if (cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
return IDWALK_RET_NOP;
}

View File

@ -12,6 +12,8 @@
#include "DNA_lightprobe_types.h"
#include "DNA_object_types.h"
#include "BLI_math_base.h"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
@ -129,3 +131,137 @@ void *BKE_lightprobe_add(Main *bmain, const char *name)
return probe;
}
static void lightprobe_grid_cache_frame_blend_write(BlendWriter *writer,
const LightProbeGridCacheFrame *cache)
{
BLO_write_struct_array(writer, LightProbeGridCacheFrame, cache->block_len, cache->block_infos);
int64_t sample_count = BKE_lightprobe_grid_cache_frame_sample_count(cache);
BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L0);
BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_a);
BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_b);
BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_c);
BLO_write_float_array(writer, sample_count, cache->visibility.L0);
BLO_write_float_array(writer, sample_count, cache->visibility.L1_a);
BLO_write_float_array(writer, sample_count, cache->visibility.L1_b);
BLO_write_float_array(writer, sample_count, cache->visibility.L1_c);
BLO_write_struct_array(
writer, LightProbeGridCacheFrame, sample_count, cache->connectivity.bitmask);
}
static void lightprobe_grid_cache_frame_blend_read(BlendDataReader *reader,
LightProbeGridCacheFrame *cache)
{
if (!ELEM(cache->data_layout,
LIGHTPROBE_CACHE_ADAPTIVE_RESOLUTION,
LIGHTPROBE_CACHE_UNIFORM_GRID)) {
/* Do not try to read data from incompatible layout. Clear all pointers. */
memset(cache, 0, sizeof(*cache));
return;
}
BLO_read_data_address(reader, &cache->block_infos);
int64_t sample_count = BKE_lightprobe_grid_cache_frame_sample_count(cache);
/* Baking data is not stored. */
cache->baking.L0 = nullptr;
cache->baking.L1_a = nullptr;
cache->baking.L1_b = nullptr;
cache->baking.L1_c = nullptr;
cache->surfels = nullptr;
cache->surfels_len = 0;
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L0);
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_a);
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_b);
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_c);
BLO_read_float_array(reader, sample_count, &cache->visibility.L0);
BLO_read_float_array(reader, sample_count, &cache->visibility.L1_a);
BLO_read_float_array(reader, sample_count, &cache->visibility.L1_b);
BLO_read_float_array(reader, sample_count, &cache->visibility.L1_c);
BLO_read_data_address(reader, &cache->connectivity.bitmask);
}
void BKE_lightprobe_cache_blend_write(BlendWriter *writer, LightProbeObjectCache *cache)
{
if (cache->grid_static_cache != nullptr) {
BLO_write_struct(writer, LightProbeGridCacheFrame, cache->grid_static_cache);
lightprobe_grid_cache_frame_blend_write(writer, cache->grid_static_cache);
}
}
void BKE_lightprobe_cache_blend_read(BlendDataReader *reader, LightProbeObjectCache *cache)
{
if (cache->grid_static_cache != nullptr) {
BLO_read_data_address(reader, &cache->grid_static_cache);
lightprobe_grid_cache_frame_blend_read(reader, cache->grid_static_cache);
}
}
template<typename T> static void spherical_harmonic_free(T &data)
{
MEM_SAFE_FREE(data.L0);
MEM_SAFE_FREE(data.L1_a);
MEM_SAFE_FREE(data.L1_b);
MEM_SAFE_FREE(data.L1_c);
}
LightProbeGridCacheFrame *BKE_lightprobe_grid_cache_frame_create()
{
LightProbeGridCacheFrame *cache = static_cast<LightProbeGridCacheFrame *>(
MEM_callocN(sizeof(LightProbeGridCacheFrame), "LightProbeGridCacheFrame"));
return cache;
}
void BKE_lightprobe_grid_cache_frame_free(LightProbeGridCacheFrame *cache)
{
MEM_SAFE_FREE(cache->block_infos);
spherical_harmonic_free(cache->baking);
spherical_harmonic_free(cache->irradiance);
spherical_harmonic_free(cache->visibility);
MEM_SAFE_FREE(cache->connectivity.bitmask);
MEM_SAFE_FREE(cache->surfels);
MEM_SAFE_FREE(cache);
}
void BKE_lightprobe_cache_create(Object *object)
{
BLI_assert(object->lightprobe_cache == nullptr);
object->lightprobe_cache = static_cast<LightProbeObjectCache *>(
MEM_callocN(sizeof(LightProbeObjectCache), "LightProbeObjectCache"));
}
void BKE_lightprobe_cache_free(Object *object)
{
if (object->lightprobe_cache == nullptr) {
return;
}
LightProbeObjectCache *cache = object->lightprobe_cache;
if (cache->shared == false) {
if (cache->grid_static_cache != nullptr) {
BKE_lightprobe_grid_cache_frame_free(cache->grid_static_cache);
}
}
MEM_SAFE_FREE(object->lightprobe_cache);
}
int64_t BKE_lightprobe_grid_cache_frame_sample_count(const LightProbeGridCacheFrame *cache)
{
if (cache->data_layout == LIGHTPROBE_CACHE_ADAPTIVE_RESOLUTION) {
return cache->block_len * cube_i(cache->block_size);
}
/* LIGHTPROBE_CACHE_UNIFORM_GRID */
return cache->size[0] * cache->size[1] * cache->size[2];
}

View File

@ -267,6 +267,18 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in
if (ob_src->lightgroup) {
ob_dst->lightgroup = (LightgroupMembership *)MEM_dupallocN(ob_src->lightgroup);
}
if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) != 0) {
if (ob_src->lightprobe_cache) {
/* Reference the original object data. */
ob_dst->lightprobe_cache = (LightProbeObjectCache *)MEM_dupallocN(ob_src->lightprobe_cache);
ob_dst->lightprobe_cache->shared = true;
}
}
else {
/* Do not copy lightprobe's cache. */
ob_dst->lightprobe_cache = nullptr;
}
}
static void object_free_data(ID *id)
@ -319,6 +331,8 @@ static void object_free_data(ID *id)
BKE_previewimg_free(&ob->preview);
MEM_SAFE_FREE(ob->lightgroup);
BKE_lightprobe_cache_free(ob);
}
static void library_foreach_modifiersForeachIDLink(void *user_data,
@ -597,6 +611,11 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre
if (ob->lightgroup) {
BLO_write_struct(writer, LightgroupMembership, ob->lightgroup);
}
if (ob->lightprobe_cache) {
BLO_write_struct(writer, LightProbeObjectCache, ob->lightprobe_cache);
BKE_lightprobe_cache_blend_write(writer, ob->lightprobe_cache);
}
}
/* XXX deprecated - old animation system */
@ -815,6 +834,11 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id)
BKE_previewimg_blend_read(reader, ob->preview);
BLO_read_data_address(reader, &ob->lightgroup);
BLO_read_data_address(reader, &ob->lightprobe_cache);
if (ob->lightprobe_cache) {
BKE_lightprobe_cache_blend_read(reader, ob->lightprobe_cache);
}
}
/* XXX deprecated - old animation system */

View File

@ -3104,6 +3104,7 @@ static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main,
* lib-linking to restore some data that should never be affected by undo, e.g. the 3D cursor of
* #Scene. */
id_old->orig_id = id;
id_old->tag |= LIB_TAG_UNDO_OLD_ID_REREAD_IN_PLACE;
BLI_addtail(new_lb, id_old);
BLI_addtail(old_lb, id);
@ -3122,18 +3123,21 @@ static bool read_libblock_undo_restore(
return true;
}
}
else if (id_type->flags & IDTYPE_FLAGS_NO_MEMFILE_UNDO) {
/* Skip reading any 'no undo' datablocks (typically UI-like ones), existing ones are kept.
* See `setup_app_data` for details. */
CLOG_INFO(&LOG_UNDO,
2,
"UNDO: skip restore datablock %s, 'NO_MEMFILE_UNDO' type of ID",
id->name);
return true;
}
else if (bhead->code == ID_LINK_PLACEHOLDER) {
/* Restore linked datablock. */
if (read_libblock_undo_restore_linked(fd, main, id, bhead)) {
return true;
}
}
else if (id_type->flags & IDTYPE_FLAGS_NO_MEMFILE_UNDO) {
/* Skip reading any UI datablocks, existing ones are kept. We don't
* support pointers from other datablocks to UI datablocks so those
* we also don't put UI datablocks in fd->libmap. */
return true;
}
/* Restore local datablocks. */
ID *id_old = nullptr;

View File

@ -41,6 +41,9 @@ void SCENE_OT_view_layer_remove_unused_lightgroups(struct wmOperatorType *ot);
void SCENE_OT_light_cache_bake(struct wmOperatorType *ot);
void SCENE_OT_light_cache_free(struct wmOperatorType *ot);
void OBJECT_OT_lightprobe_cache_bake(struct wmOperatorType *ot);
void OBJECT_OT_lightprobe_cache_free(struct wmOperatorType *ot);
void SCENE_OT_render_view_add(struct wmOperatorType *ot);
void SCENE_OT_render_view_remove(struct wmOperatorType *ot);

View File

@ -28,6 +28,9 @@ void ED_operatortypes_render()
WM_operatortype_append(OBJECT_OT_material_slot_move);
WM_operatortype_append(OBJECT_OT_material_slot_remove_unused);
WM_operatortype_append(OBJECT_OT_lightprobe_cache_bake);
WM_operatortype_append(OBJECT_OT_lightprobe_cache_free);
WM_operatortype_append(MATERIAL_OT_new);
WM_operatortype_append(TEXTURE_OT_new);
WM_operatortype_append(WORLD_OT_new);

View File

@ -37,6 +37,7 @@
#include "BKE_image.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lightprobe.h"
#include "BKE_linestyle.h"
#include "BKE_main.h"
#include "BKE_material.h"
@ -53,6 +54,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#ifdef WITH_FREESTYLE
# include "BKE_freestyle.h"
@ -1332,6 +1334,8 @@ enum {
LIGHTCACHE_SUBSET_ALL = 0,
LIGHTCACHE_SUBSET_DIRTY,
LIGHTCACHE_SUBSET_CUBE,
LIGHTCACHE_SUBSET_SELECTED,
LIGHTCACHE_SUBSET_ACTIVE,
};
static void light_cache_bake_tag_cache(Scene *scene, wmOperator *op)
@ -1348,6 +1352,9 @@ static void light_cache_bake_tag_cache(Scene *scene, wmOperator *op)
case LIGHTCACHE_SUBSET_DIRTY:
/* Leave tag untouched. */
break;
default:
BLI_assert_unreachable();
break;
}
}
}
@ -1492,6 +1499,169 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot)
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
}
/* NOTE: New version destined to replace the old lightcache bake operator. */
static void lightprobe_cache_bake_start(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
Scene *scene = CTX_data_scene(C);
auto is_irradiance_volume = [](Object *ob) -> bool {
return ob->type == OB_LIGHTPROBE &&
static_cast<LightProbe *>(ob->data)->type == LIGHTPROBE_TYPE_GRID;
};
auto irradiance_volume_setup = [](Object *ob) {
BKE_lightprobe_cache_free(ob);
BKE_lightprobe_cache_create(ob);
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
};
int subset = RNA_enum_get(op->ptr, "subset");
switch (subset) {
case LIGHTCACHE_SUBSET_ALL: {
FOREACH_OBJECT_BEGIN (scene, view_layer, ob) {
if (is_irradiance_volume(ob)) {
irradiance_volume_setup(ob);
}
}
FOREACH_OBJECT_END;
break;
}
case LIGHTCACHE_SUBSET_DIRTY: {
FOREACH_OBJECT_BEGIN (scene, view_layer, ob) {
if (is_irradiance_volume(ob) && ob->lightprobe_cache && ob->lightprobe_cache->dirty) {
irradiance_volume_setup(ob);
}
}
FOREACH_OBJECT_END;
break;
}
case LIGHTCACHE_SUBSET_SELECTED: {
uint objects_len = 0;
ObjectsInViewLayerParams parameters;
parameters.filter_fn = nullptr;
parameters.no_dup_data = true;
Object **objects = BKE_view_layer_array_selected_objects_params(
view_layer, nullptr, &objects_len, &parameters);
for (Object *ob : blender::MutableSpan<Object *>(objects, objects_len)) {
if (is_irradiance_volume(ob)) {
irradiance_volume_setup(ob);
}
}
MEM_freeN(objects);
break;
}
case LIGHTCACHE_SUBSET_ACTIVE: {
Object *active_ob = CTX_data_active_object(C);
if (is_irradiance_volume(active_ob)) {
irradiance_volume_setup(active_ob);
}
break;
}
default:
BLI_assert_unreachable();
break;
}
}
static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
Scene *scene = CTX_data_scene(C);
lightprobe_cache_bake_start(C, op);
WM_event_add_modal_handler(C, op);
/* store actual owner of job, so modal operator could check for it,
* the reason of this is that active scene could change when rendering
* several layers from compositor #31800. */
op->customdata = scene;
WM_cursor_wait(false);
return OPERATOR_RUNNING_MODAL;
}
static int lightprobe_cache_bake_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = (Scene *)op->customdata;
/* No running bake, remove handler and pass through. */
if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_LIGHT_BAKE)) {
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
}
/* Running bake. */
switch (event->type) {
case EVT_ESCKEY:
return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_PASS_THROUGH;
}
static void lightprobe_cache_bake_cancel(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
Scene *scene = (Scene *)op->customdata;
/* Kill on cancel, because job is using op->reports. */
WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_LIGHT_BAKE);
}
/* Executes blocking bake. */
static int lightprobe_cache_bake_exec(bContext *C, wmOperator *op)
{
lightprobe_cache_bake_start(C, op);
return OPERATOR_FINISHED;
}
void OBJECT_OT_lightprobe_cache_bake(wmOperatorType *ot)
{
static const EnumPropertyItem light_cache_subset_items[] = {
{LIGHTCACHE_SUBSET_ALL, "ALL", 0, "All Light Probes", "Bake all light probes"},
{LIGHTCACHE_SUBSET_DIRTY,
"DIRTY",
0,
"Dirty Only",
"Only bake light probes that are marked as dirty"},
{LIGHTCACHE_SUBSET_SELECTED,
"SELECTED",
0,
"Selected Only",
"Only bake selected light probes"},
{LIGHTCACHE_SUBSET_ACTIVE, "ACTIVE", 0, "Active Only", "Only bake the active light probe"},
{0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
ot->name = "Bake Light Cache";
ot->idname = "OBJECT_OT_lightprobe_cache_bake";
ot->description = "Bake the active view layer lighting";
/* api callbacks */
ot->invoke = lightprobe_cache_bake_invoke;
ot->modal = lightprobe_cache_bake_modal;
ot->cancel = lightprobe_cache_bake_cancel;
ot->exec = lightprobe_cache_bake_exec;
ot->prop = RNA_def_int(ot->srna,
"delay",
0,
0,
2000,
"Delay",
"Delay in millisecond before baking starts",
0,
2000);
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
ot->prop = RNA_def_enum(
ot->srna, "subset", light_cache_subset_items, 0, "Subset", "Subset of probes to update");
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1541,6 +1711,49 @@ void SCENE_OT_light_cache_free(wmOperatorType *ot)
ot->poll = light_cache_free_poll;
}
/* NOTE: New version destined to replace the old lightcache bake operator. */
static bool lightprobe_cache_free_poll(bContext *C)
{
Object *object = CTX_data_active_object(C);
return object && object->lightprobe_cache != nullptr;
}
static int lightprobe_cache_free_exec(bContext *C, wmOperator * /*op*/)
{
Scene *scene = CTX_data_scene(C);
Object *object = CTX_data_active_object(C);
/* Kill potential bake job first (see #57011). */
wmWindowManager *wm = CTX_wm_manager(C);
WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_LIGHT_BAKE);
if (object->lightprobe_cache == nullptr) {
return OPERATOR_CANCELLED;
}
BKE_lightprobe_cache_free(object);
DEG_id_tag_update(&object->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, scene);
return OPERATOR_FINISHED;
}
void OBJECT_OT_lightprobe_cache_free(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Light Cache";
ot->idname = "OBJECT_OT_lightprobe_cache_free";
ot->description = "Delete cached indirect lighting";
/* api callbacks */
ot->exec = lightprobe_cache_free_exec;
ot->poll = lightprobe_cache_free_poll;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -159,7 +159,7 @@ static void createTransMeshSkin(bContext *UNUSED(C), TransInfo *t)
if (mirror_data.vert_map) {
tc->data_mirror_len = mirror_data.mirror_elem_len;
tc->data_mirror = MEM_mallocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror),
tc->data_mirror = MEM_callocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror),
__func__);
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {

View File

@ -239,21 +239,39 @@ static void memfile_undosys_step_decode(struct bContext *C,
bmain, id, memfile_undosys_step_id_reused_cb, nullptr, IDWALK_READONLY);
}
/* NOTE: Tagging `ID_RECALC_COPY_ON_WRITE` here should not be needed in practice, since
* modified IDs should already have other depsgraph update tags anyway. However, for sakes of
* consistency, it's better to effectively use it, since content of that ID pointer does have
* been modified. */
unsigned int recalc_flags = id->recalc | ((id->tag & LIB_TAG_UNDO_OLD_ID_REREAD_IN_PLACE) ?
ID_RECALC_COPY_ON_WRITE :
0);
/* Tag depsgraph to update data-block for changes that happened between the
* current and the target state, see direct_link_id_restore_recalc(). */
if (id->recalc != 0) {
DEG_id_tag_update_ex(bmain, id, id->recalc);
if (recalc_flags != 0) {
DEG_id_tag_update_ex(bmain, id, recalc_flags);
}
bNodeTree *nodetree = ntreeFromID(id);
if (nodetree != nullptr && nodetree->id.recalc != 0) {
DEG_id_tag_update_ex(bmain, &nodetree->id, nodetree->id.recalc);
if (nodetree != nullptr) {
recalc_flags = nodetree->id.recalc;
if (id->tag & LIB_TAG_UNDO_OLD_ID_REREAD_IN_PLACE) {
recalc_flags |= ID_RECALC_COPY_ON_WRITE;
}
if (recalc_flags != 0) {
DEG_id_tag_update_ex(bmain, &nodetree->id, recalc_flags);
}
}
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
if (scene->master_collection != nullptr && scene->master_collection->id.recalc != 0) {
DEG_id_tag_update_ex(
bmain, &scene->master_collection->id, scene->master_collection->id.recalc);
if (scene->master_collection != nullptr) {
recalc_flags = scene->master_collection->id.recalc;
if (id->tag & LIB_TAG_UNDO_OLD_ID_REREAD_IN_PLACE) {
recalc_flags |= ID_RECALC_COPY_ON_WRITE;
}
if (recalc_flags != 0) {
DEG_id_tag_update_ex(bmain, &scene->master_collection->id, recalc_flags);
}
}
}
@ -264,7 +282,7 @@ static void memfile_undosys_step_decode(struct bContext *C,
FOREACH_MAIN_ID_BEGIN (bmain, id) {
/* Clear temporary tag. */
id->tag &= ~LIB_TAG_UNDO_OLD_ID_REUSED;
id->tag &= ~(LIB_TAG_UNDO_OLD_ID_REUSED | LIB_TAG_UNDO_OLD_ID_REREAD_IN_PLACE);
/* We only start accumulating from this point, any tags set up to here
* are already part of the current undo state. This is done in a second

View File

@ -485,6 +485,12 @@ void AbstractHierarchyIterator::context_update_for_graph_index(
{
/* Update the HierarchyContext so that it is consistent with the graph index. */
context->export_parent = graph_index.object;
/* If the parent type is such that it cannot be exported (at least not currently to USD or
* Alembic), always check the parent for animation. */
const short partype = context->object->partype & PARTYPE;
context->animation_check_include_parent |= ELEM(partype, PARBONE, PARVERT1, PARVERT3, PARSKEL);
if (context->export_parent != context->object->parent) {
/* The parent object in Blender is NOT used as the export parent. This means
* that the world transform of this object can be influenced by objects that

View File

@ -840,6 +840,13 @@ enum {
* RESET_AFTER_USE
*/
LIB_TAG_UNDO_OLD_ID_REUSED = 1 << 17,
/**
* ID has be re-read in-place, the ID address is the same as in the old BMain, but the content is
* different.
*
* RESET_AFTER_USE
*/
LIB_TAG_UNDO_OLD_ID_REREAD_IN_PLACE = 1 << 18,
/* ------------------------------------------------------------------------------------------- */
/**

View File

@ -61,9 +61,6 @@ typedef struct LightProbe {
struct Image *image;
/** Object visibility group, inclusive or exclusive. */
struct Collection *visibility_grp;
/* Runtime display data */
float distfalloff, distgridinf;
} LightProbe;
/* Probe->type */
@ -201,6 +198,137 @@ enum {
LIGHTCACHETEX_UINT = (1 << 2),
};
/* -------------------------------------------------------------------- */
/** \name Irradiance grid data storage
*
* Each spherical harmonic band is stored separately. This allow loading only a specific band.
* The layout of each array is set by the #LightProbeGridType.
* Any unavailable data is be set to nullptr.
* \{ */
/**
* Irradiance data (RGB) stored along visibility (A).
* This is the format used during baking and is used for visualizing the baking process.
*/
typedef struct LightProbeBakingData {
float (*L0)[4];
float (*L1_a)[4];
float (*L1_b)[4];
float (*L1_c)[4];
} LightProbeBakingData;
/**
* Irradiance stored as RGB triple using scene linear color space.
*/
typedef struct LightProbeIrradianceData {
float (*L0)[3];
float (*L1_a)[3];
float (*L1_b)[3];
float (*L1_c)[3];
} LightProbeIrradianceData;
/**
* Normalized visibility of distant light. Used for compositing grids together.
*/
typedef struct LightProbeVisibilityData {
float *L0;
float *L1_a;
float *L1_b;
float *L1_c;
} LightProbeVisibilityData;
/**
* Used to avoid light leaks. Validate visibility between each grid sample.
*/
typedef struct LightProbeConnectivityData {
/** Stores a bitmask of valid connections within a cell. */
uint8_t *bitmask;
} LightProbeConnectivityData;
/**
* Defines one block of data inside the grid cache data arrays.
* The block size if the same for all the blocks.
*/
typedef struct LightProbeBlockData {
/* Offset inside the level-of-detail this block starts. */
int offset[3];
/* Level-of-detail this block is from. */
int level;
} LightProbeBlockData;
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightProbeGridCacheFrame
*
* \{ */
/**
* A frame worth of baked lighting data.
*/
typedef struct LightProbeGridCacheFrame {
/** Number of samples in the highest level of detail. */
int size[3];
/** Spatial layout type of the data stored inside the data arrays. */
int data_layout;
/** Sparse or adaptive layout only: number of blocks inside data arrays. */
int block_len;
/** Sparse or adaptive layout only: size of a block in samples. All 3 dimensions are equal. */
int block_size;
/** Sparse or adaptive layout only: specify the blocks positions. */
LightProbeBlockData *block_infos;
/** In-progress baked data. Not stored in file. */
LightProbeBakingData baking;
/** Baked data. */
LightProbeIrradianceData irradiance;
LightProbeVisibilityData visibility;
LightProbeConnectivityData connectivity;
char _pad[4];
/** Number of debug surfels. */
int surfels_len;
/** Debug surfels used to visualize the baking process. Not stored in file. */
void *surfels;
} LightProbeGridCacheFrame;
/** #LightProbeGridCacheFrame.data_layout (int) */
enum {
/** Simple uniform grid. Raw output from GPU. Used during the baking process. */
LIGHTPROBE_CACHE_UNIFORM_GRID = 0,
/** Fills the space with different level of resolution. More efficient storage. */
LIGHTPROBE_CACHE_ADAPTIVE_RESOLUTION = 1,
};
/**
* Per object container of baked data.
* Should be called #LightProbeCache but name is already taken.
*/
typedef struct LightProbeObjectCache {
/** Allow correct versioning / different types of data for the same layout. */
int cache_type;
/** True if this cache references the original object's cache. */
char shared;
/** True if the cache has been tagged for automatic baking. */
char dirty;
char _pad0[2];
struct LightProbeGridCacheFrame *grid_static_cache;
} LightProbeObjectCache;
/** #LightProbeObjectCache.type (int) */
enum {
/** Light cache was just created and is not yet baked. Keep as 0 for default value. */
LIGHTPROBE_CACHE_TYPE_NONE = 0,
/** Light cache is baked for one specific frame and capture all indirect lighting. */
LIGHTPROBE_CACHE_TYPE_STATIC = 1,
};
/** \} */
#ifdef __cplusplus
}
#endif

View File

@ -34,6 +34,7 @@ struct FluidsimSettings;
struct GeometrySet;
struct Ipo;
struct LightgroupMembership;
struct LightProbeGridCacheFrame;
struct Material;
struct Mesh;
struct Object;
@ -449,6 +450,11 @@ typedef struct Object {
/** Lightgroup membership information. */
struct LightgroupMembership *lightgroup;
/** Irradiance caches baked for this object (light-probes only). */
struct LightProbeObjectCache *lightprobe_cache;
void *_pad9;
/** Runtime evaluation data (keep last). */
Object_Runtime runtime;
} Object;