diff --git a/source/blender/blenkernel/BKE_lightprobe.h b/source/blender/blenkernel/BKE_lightprobe.h index d7f9ad9a5bd..9ccc9f0f4fd 100644 --- a/source/blender/blenkernel/BKE_lightprobe.h +++ b/source/blender/blenkernel/BKE_lightprobe.h @@ -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 diff --git a/source/blender/blenkernel/intern/lightprobe.cc b/source/blender/blenkernel/intern/lightprobe.cc index 4ad560d8245..48e8449dd7d 100644 --- a/source/blender/blenkernel/intern/lightprobe.cc +++ b/source/blender/blenkernel/intern/lightprobe.cc @@ -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 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( + 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( + 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]; +} diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index ace45ee4e95..c05722ceda9 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -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 */ diff --git a/source/blender/editors/render/render_intern.hh b/source/blender/editors/render/render_intern.hh index e9adf760ee7..2497c59ab4c 100644 --- a/source/blender/editors/render/render_intern.hh +++ b/source/blender/editors/render/render_intern.hh @@ -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); diff --git a/source/blender/editors/render/render_ops.cc b/source/blender/editors/render/render_ops.cc index 933a4d0cc88..84ed29fade7 100644 --- a/source/blender/editors/render/render_ops.cc +++ b/source/blender/editors/render/render_ops.cc @@ -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); diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index d26fceb9814..f075ffc2e54 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -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(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, ¶meters); + for (Object *ob : blender::MutableSpan(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; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index 5b5bc4c7392..487a954429c 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -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 diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 107130ecc88..f940c0e862c 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -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;