Eevee: LightCache: Initial Implementation

This separate probe rendering from viewport rendering, making possible to
run the baking in another thread (non blocking and faster).

The baked lighting is saved in the blend file. Nothing needs to be
recomputed on load.

There is a few missing bits / bugs:
- Cache cannot be saved to disk as a separate file, it is saved in the DNA
  for now making file larger and memory usage higher.
- Auto update only cubemaps does update the grids (bug).
- Probes cannot be updated individually (considered as dynamic).
- Light Cache cannot be (re)generated during render.
This commit is contained in:
Clément Foucault 2018-07-10 15:02:25 +02:00
parent 97f90d48a0
commit 1a43e08187
37 changed files with 2739 additions and 1365 deletions

View File

@ -712,15 +712,49 @@ class RENDER_PT_eevee_indirect_lighting(RenderButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
scene = context.scene
props = scene.eevee
col = layout.column()
col.operator("scene.light_cache_bake", text="Bake Indirect Lighting", icon='RENDER_STILL')
col.operator("scene.light_cache_bake", text="Bake Cubemap Only", icon='LIGHTPROBE_CUBEMAP').subset = "CUBEMAPS"
col.operator("scene.light_cache_free", text="Free Lighting Cache")
cache_info = scene.eevee.gi_cache_info
if cache_info:
col.label(text=cache_info)
col.prop(props, "gi_auto_bake")
col.prop(props, "gi_diffuse_bounces")
col.prop(props, "gi_cubemap_resolution")
col.prop(props, "gi_visibility_resolution", text="Diffuse Occlusion")
layout.use_property_split = False
row = layout.split(percentage=0.5)
row.alignment = 'RIGHT'
row.label("Cubemap Display")
sub = row.row(align=True)
sub.prop(props, "gi_cubemap_draw_size", text="Size")
if props.gi_show_cubemaps :
sub.prop(props, "gi_show_cubemaps", text="", toggle=True, icon='HIDE_OFF')
else:
sub.prop(props, "gi_show_cubemaps", text="", toggle=True, icon='HIDE_ON')
row = layout.split(percentage=0.5)
row.alignment = 'RIGHT'
row.label("Irradiance Display")
sub = row.row(align=True)
sub.prop(props, "gi_irradiance_draw_size", text="Size")
if props.gi_show_irradiance :
sub.prop(props, "gi_show_irradiance", text="", toggle=True, icon='HIDE_OFF')
else:
sub.prop(props, "gi_show_irradiance", text="", toggle=True, icon='HIDE_ON')
class RENDER_PT_eevee_film(RenderButtonsPanel, Panel):
bl_label = "Film"

View File

@ -102,6 +102,9 @@
#include "DEG_depsgraph_query.h"
#include "RE_engine.h"
#include "RE_engine.h"
#include "engines/eevee/eevee_lightcache.h"
#include "PIL_time.h"
@ -316,6 +319,9 @@ void BKE_scene_copy_data(Main *bmain, Scene *sce_dst, const Scene *sce_src, cons
else {
sce_dst->preview = NULL;
}
sce_dst->eevee.light_cache = NULL;
/* TODO Copy the cache. */
}
Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type)
@ -511,6 +517,11 @@ void BKE_scene_free_ex(Scene *sce, const bool do_id_user)
sce->master_collection = NULL;
}
if (sce->eevee.light_cache) {
EEVEE_lightcache_free(sce->eevee.light_cache);
sce->eevee.light_cache = NULL;
}
/* These are freed on doversion. */
BLI_assert(sce->layer_properties == NULL);
}
@ -814,6 +825,8 @@ void BKE_scene_init(Scene *sce)
sce->eevee.gi_diffuse_bounces = 3;
sce->eevee.gi_cubemap_resolution = 512;
sce->eevee.gi_visibility_resolution = 32;
sce->eevee.gi_cubemap_draw_size = 0.3f;
sce->eevee.gi_irradiance_draw_size = 0.1f;
sce->eevee.taa_samples = 16;
sce->eevee.taa_render_samples = 64;
@ -856,6 +869,8 @@ void BKE_scene_init(Scene *sce)
sce->eevee.shadow_cube_size = 512;
sce->eevee.shadow_cascade_size = 1024;
sce->eevee.light_cache = NULL;
sce->eevee.flag =
SCE_EEVEE_VOLUMETRIC_LIGHTS |
SCE_EEVEE_VOLUMETRIC_COLORED |

View File

@ -30,6 +30,7 @@ set(INC
../blenlib
../blentranslation
../depsgraph
../draw
../imbuf
../makesdna
../makesrna

View File

@ -393,6 +393,9 @@ BlendFileData *BLO_read_from_memfile(
/* makes lookup of existing images in old main */
blo_make_image_pointer_map(fd, oldmain);
/* makes lookup of existing light caches in old main */
blo_make_scene_pointer_map(fd, oldmain);
/* makes lookup of existing video clips in old main */
blo_make_movieclip_pointer_map(fd, oldmain);
@ -403,6 +406,9 @@ BlendFileData *BLO_read_from_memfile(
bfd = blo_read_file_internal(fd, filename);
/* ensures relinked light caches are not freed */
blo_end_scene_pointer_map(fd, oldmain);
/* ensures relinked images are not freed */
blo_end_image_pointer_map(fd, oldmain);

View File

@ -158,6 +158,8 @@
#include "BKE_colortools.h"
#include "BKE_workspace.h"
#include "DRW_engine.h"
#include "DEG_depsgraph.h"
#include "NOD_common.h"
@ -1339,6 +1341,8 @@ void blo_freefiledata(FileData *fd)
oldnewmap_free(fd->imamap);
if (fd->movieclipmap)
oldnewmap_free(fd->movieclipmap);
if (fd->scenemap)
oldnewmap_free(fd->scenemap);
if (fd->soundmap)
oldnewmap_free(fd->soundmap);
if (fd->packedmap)
@ -1526,6 +1530,13 @@ static void *newimaadr(FileData *fd, const void *adr) /* used to restore im
return NULL;
}
static void *newsceadr(FileData *fd, const void *adr) /* used to restore scene data after undo */
{
if (fd->scenemap && adr)
return oldnewmap_lookup_and_inc(fd->scenemap, adr, true);
return NULL;
}
static void *newmclipadr(FileData *fd, const void *adr) /* used to restore movie clip data after undo */
{
if (fd->movieclipmap && adr)
@ -1631,6 +1642,37 @@ void blo_clear_proxy_pointers_from_lib(Main *oldmain)
}
}
void blo_make_scene_pointer_map(FileData *fd, Main *oldmain)
{
Scene *sce = oldmain->scene.first;
fd->scenemap = oldnewmap_new();
for (; sce; sce = sce->id.next) {
if (sce->eevee.light_cache) {
struct LightCache *light_cache = sce->eevee.light_cache;
oldnewmap_insert(fd->scenemap, light_cache, light_cache, 0);
}
}
}
void blo_end_scene_pointer_map(FileData *fd, Main *oldmain)
{
OldNew *entry = fd->scenemap->entries;
Scene *sce = oldmain->scene.first;
int i;
/* used entries were restored, so we put them to zero */
for (i = 0; i < fd->scenemap->nentries; i++, entry++) {
if (entry->nr > 0)
entry->newp = NULL;
}
for (; sce; sce = sce->id.next) {
sce->eevee.light_cache = newsceadr(fd, sce->eevee.light_cache);
}
}
void blo_make_image_pointer_map(FileData *fd, Main *oldmain)
{
Image *ima = oldmain->image.first;
@ -2301,6 +2343,11 @@ static void direct_link_id(FileData *fd, ID *id)
id->override_static = newdataadr(fd, id->override_static);
link_list_ex(fd, &id->override_static->properties, direct_link_id_override_property_cb);
}
DrawDataList *drawdata = DRW_drawdatalist_from_id(id);
if (drawdata) {
BLI_listbase_clear((ListBase *)drawdata);
}
}
/* ************ READ CurveMapping *************** */
@ -5492,7 +5539,6 @@ static void direct_link_object(FileData *fd, Object *ob)
ob->derivedFinal = NULL;
BKE_object_runtime_reset(ob);
BLI_listbase_clear(&ob->gpulamp);
BLI_listbase_clear(&ob->drawdata);
link_list(fd, &ob->pc_ids);
/* Runtime curve data */
@ -5739,6 +5785,41 @@ static void lib_link_sequence_modifiers(FileData *fd, Scene *scene, ListBase *lb
}
}
static void direct_link_lightcache_texture(FileData *fd, LightCacheTexture *lctex)
{
lctex->tex = NULL;
if (lctex->data) {
lctex->data = newdataadr(fd, lctex->data);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
int data_size = lctex->components * lctex->tex_size[0] * lctex->tex_size[1] * lctex->tex_size[2];
if (lctex->data_type == LIGHTCACHETEX_FLOAT) {
BLI_endian_switch_float_array((float *)lctex->data, data_size * sizeof(float));
}
else if (lctex->data_type == LIGHTCACHETEX_UINT) {
BLI_endian_switch_uint32_array((unsigned int *)lctex->data, data_size * sizeof(unsigned int));
}
}
}
}
static void direct_link_lightcache(FileData *fd, LightCache *cache)
{
direct_link_lightcache_texture(fd, &cache->cube_tx);
direct_link_lightcache_texture(fd, &cache->grid_tx);
if (cache->cube_mips) {
cache->cube_mips = newdataadr(fd, cache->cube_mips);
for (int i = 0; i < cache->mips_len; ++i) {
direct_link_lightcache_texture(fd, &cache->cube_mips[i]);
}
}
cache->cube_data = newdataadr(fd, cache->cube_data);
cache->grid_data = newdataadr(fd, cache->grid_data);
}
/* check for cyclic set-scene,
* libs can cause this case which is normally prevented, see (T#####) */
#define USE_SETSCENE_CHECK
@ -6299,6 +6380,19 @@ static void direct_link_scene(FileData *fd, Scene *sce)
direct_link_view_layer(fd, view_layer);
}
if (fd->memfile) {
/* If it's undo try to recover the cache. */
if (fd->scenemap) sce->eevee.light_cache = newsceadr(fd, sce->eevee.light_cache);
else sce->eevee.light_cache = NULL;
}
else {
/* else read the cache from file. */
if (sce->eevee.light_cache) {
sce->eevee.light_cache = newdataadr(fd, sce->eevee.light_cache);
direct_link_lightcache(fd, sce->eevee.light_cache);
}
}
sce->layer_properties = newdataadr(fd, sce->layer_properties);
IDP_DirectLinkGroup_OrFree(&sce->layer_properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}

View File

@ -90,6 +90,7 @@ typedef struct FileData {
struct OldNewMap *libmap;
struct OldNewMap *imamap;
struct OldNewMap *movieclipmap;
struct OldNewMap *scenemap;
struct OldNewMap *soundmap;
struct OldNewMap *packedmap;
@ -140,6 +141,8 @@ FileData *blo_openblendermemfile(struct MemFile *memfile, struct ReportList *rep
void blo_clear_proxy_pointers_from_lib(Main *oldmain);
void blo_make_image_pointer_map(FileData *fd, Main *oldmain);
void blo_end_image_pointer_map(FileData *fd, Main *oldmain);
void blo_make_scene_pointer_map(FileData *fd, Main *oldmain);
void blo_end_scene_pointer_map(FileData *fd, Main *oldmain);
void blo_make_movieclip_pointer_map(FileData *fd, Main *oldmain);
void blo_end_movieclip_pointer_map(FileData *fd, Main *oldmain);
void blo_make_sound_pointer_map(FileData *fd, Main *oldmain);

View File

@ -1541,6 +1541,13 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "gi_cubemap_draw_size")) {
for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
scene->eevee.gi_irradiance_draw_size = 0.1f;
scene->eevee.gi_cubemap_draw_size = 0.3f;
}
}
for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
if (scene->toolsettings->manipulator_flag == 0) {
scene->toolsettings->manipulator_flag = SCE_MANIP_TRANSLATE | SCE_MANIP_ROTATE | SCE_MANIP_SCALE;

View File

@ -2479,6 +2479,36 @@ static void write_view_layer(WriteData *wd, ViewLayer *view_layer)
write_layer_collections(wd, &view_layer->layer_collections);
}
static void write_lightcache_texture(WriteData *wd, LightCacheTexture *tex)
{
if (tex->data) {
size_t data_size = tex->components * tex->tex_size[0] * tex->tex_size[1] * tex->tex_size[2];
if (tex->data_type == LIGHTCACHETEX_FLOAT) {
data_size *= sizeof(float);
}
else if (tex->data_type == LIGHTCACHETEX_UINT) {
data_size *= sizeof(unsigned int);
}
writedata(wd, DATA, data_size, tex->data);
}
}
static void write_lightcache(WriteData *wd, LightCache *cache)
{
write_lightcache_texture(wd, &cache->grid_tx);
write_lightcache_texture(wd, &cache->cube_tx);
if (cache->cube_mips) {
writestruct(wd, DATA, LightCacheTexture, cache->mips_len, cache->cube_mips);
for (int i = 0; i < cache->mips_len; ++i) {
write_lightcache_texture(wd, &cache->cube_mips[i]);
}
}
writestruct(wd, DATA, LightGridCache, cache->grid_len, cache->grid_data);
writestruct(wd, DATA, LightProbeCache, cache->cube_len, cache->cube_data);
}
static void write_scene(WriteData *wd, Scene *sce)
{
/* write LibData */
@ -2679,6 +2709,12 @@ static void write_scene(WriteData *wd, Scene *sce)
write_collection_nolib(wd, sce->master_collection);
}
/* Eevee Lightcache */
if (sce->eevee.light_cache && !wd->use_memfile) {
writestruct(wd, DATA, LightCache, 1, sce->eevee.light_cache);
write_lightcache(wd, sce->eevee.light_cache);
}
/* Freed on doversion. */
BLI_assert(sce->layer_properties == NULL);
}

View File

@ -555,6 +555,13 @@ void update_special_pointers(const Depsgraph *depsgraph,
update_particle_system_orig_pointers(object_orig, object_cow);
break;
}
case ID_SCE:
{
Scene *scene_cow = (Scene *)id_cow;
const Scene *scene_orig = (const Scene *)id_orig;
scene_cow->eevee.light_cache = scene_orig->eevee.light_cache;
break;
}
default:
break;
}
@ -926,6 +933,12 @@ void discard_mesh_edit_mode_pointers(ID *id_cow)
mesh_cow->edit_btmesh = NULL;
}
void discard_scene_pointers(ID *id_cow)
{
Scene *scene_cow = (Scene *)id_cow;
scene_cow->eevee.light_cache = NULL;
}
/* NULL-ify all edit mode pointers which points to data from
* original object.
*/
@ -948,6 +961,11 @@ void discard_edit_mode_pointers(ID *id_cow)
case ID_LT:
discard_lattice_edit_mode_pointers(id_cow);
break;
case ID_SCE:
/* Not really edit mode but still needs to run before
* BKE_libblock_free_datablock() */
discard_scene_pointers(id_cow);
break;
default:
break;
}

View File

@ -96,6 +96,7 @@ set(SRC
engines/eevee/eevee_depth_of_field.c
engines/eevee/eevee_effects.c
engines/eevee/eevee_engine.c
engines/eevee/eevee_lightcache.c
engines/eevee/eevee_lightprobes.c
engines/eevee/eevee_lights.c
engines/eevee/eevee_lookdev.c
@ -137,6 +138,7 @@ set(SRC
modes/edit_mesh_mode_intern.h
engines/basic/basic_engine.h
engines/eevee/eevee_engine.h
engines/eevee/eevee_lightcache.h
engines/eevee/eevee_lut.h
engines/eevee/eevee_private.h
engines/external/external_engine.h

View File

@ -28,6 +28,7 @@
#include "DRW_render.h"
#include "eevee_private.h"
#include "eevee_lightcache.h"
static void eevee_view_layer_data_free(void *storage)
{
@ -53,6 +54,11 @@ static void eevee_view_layer_data_free(void *storage)
MEM_SAFE_FREE(sldata->shcasters_buffers[1].shadow_casters);
MEM_SAFE_FREE(sldata->shcasters_buffers[1].flags);
if (sldata->fallback_lightcache) {
EEVEE_lightcache_free(sldata->fallback_lightcache);
sldata->fallback_lightcache = NULL;
}
/* Probes */
MEM_SAFE_FREE(sldata->probes);
DRW_UBO_FREE_SAFE(sldata->probe_ubo);
@ -60,15 +66,6 @@ static void eevee_view_layer_data_free(void *storage)
DRW_UBO_FREE_SAFE(sldata->planar_ubo);
DRW_UBO_FREE_SAFE(sldata->common_ubo);
DRW_UBO_FREE_SAFE(sldata->clip_ubo);
GPU_FRAMEBUFFER_FREE_SAFE(sldata->probe_filter_fb);
for (int i = 0; i < 6; ++i) {
GPU_FRAMEBUFFER_FREE_SAFE(sldata->probe_face_fb[i]);
}
DRW_TEXTURE_FREE_SAFE(sldata->probe_rt);
DRW_TEXTURE_FREE_SAFE(sldata->probe_depth_rt);
DRW_TEXTURE_FREE_SAFE(sldata->probe_pool);
DRW_TEXTURE_FREE_SAFE(sldata->irradiance_pool);
DRW_TEXTURE_FREE_SAFE(sldata->irradiance_rt);
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void)
@ -77,6 +74,18 @@ EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void)
&draw_engine_eevee_type);
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer)
{
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure_ex(
view_layer, &draw_engine_eevee_type, &eevee_view_layer_data_free);
if (*sldata == NULL) {
*sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData");
}
return *sldata;
}
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void)
{
EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure(

View File

@ -123,7 +123,7 @@ static void eevee_cache_init(void *vedata)
EEVEE_volumes_cache_init(sldata, vedata);
}
static void eevee_cache_populate(void *vedata, Object *ob)
void EEVEE_cache_populate(void *vedata, Object *ob)
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
@ -146,7 +146,7 @@ static void eevee_cache_populate(void *vedata, Object *ob)
/* TODO: Special case for dupli objects because we cannot save the object pointer. */
}
else {
EEVEE_lightprobes_cache_add(sldata, ob);
EEVEE_lightprobes_cache_add(sldata, vedata, ob);
}
}
else if (ob->type == OB_LAMP) {
@ -282,7 +282,9 @@ static void eevee_draw_background(void *vedata)
EEVEE_subsurface_compute(sldata, vedata);
EEVEE_reflection_compute(sldata, vedata);
EEVEE_occlusion_draw_debug(sldata, vedata);
DRW_draw_pass(psl->probe_display);
if (psl->probe_display) {
DRW_draw_pass(psl->probe_display);
}
EEVEE_refraction_compute(sldata, vedata);
/* Opaque refraction */
@ -367,7 +369,7 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
{
EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object);
if (ped != NULL && ped->dd.recalc != 0) {
ped->need_full_update = true;
ped->need_update = (ped->dd.recalc & (ID_RECALC_TRANSFORM | ID_RECALC_COPY_ON_WRITE)) != 0;
ped->dd.recalc = 0;
}
EEVEE_LampEngineData *led = EEVEE_lamp_data_get(object);
@ -385,10 +387,14 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object)
static void eevee_id_world_update(void *vedata, World *wo)
{
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
LightCache *lcache = stl->g_data->light_cache;
EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo);
if (wedata != NULL && wedata->dd.recalc != 0) {
if ((lcache->flag & (LIGHTCACHE_BAKED | LIGHTCACHE_BAKING)) == 0) {
lcache->flag |= LIGHTCACHE_UPDATE_WORLD;
}
wedata->dd.recalc = 0;
}
}
@ -446,7 +452,7 @@ DrawEngineType draw_engine_eevee_type = {
&eevee_engine_init,
&eevee_engine_free,
&eevee_cache_init,
&eevee_cache_populate,
&EEVEE_cache_populate,
&eevee_cache_finish,
&eevee_draw_background,
NULL, /* Everything is drawn in the background pass (see comment on function) */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
/*
* Copyright 2018, Blender Foundation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Blender Institute
*
*/
/** \file eevee_lightcache.h
* \ingroup eevee
*/
#ifndef __EEVEE_LIGHTCACHE_H__
#define __EEVEE_LIGHTCACHE_H__
#include "BLI_sys_types.h" /* for bool */
struct ViewLayer;
struct Scene;
struct SceneEEVEE;
struct LightCache;
struct EEVEE_ViewLayerData;
struct EEVEE_Data;
struct EEVEE_LightBake;
/* Light Bake */
struct wmJob *EEVEE_lightbake_job_create(
struct wmWindowManager *wm, struct wmWindow *win, struct Main *bmain,
struct ViewLayer *view_layer, struct Scene *scene, int delay);
void *EEVEE_lightbake_job_data_alloc(struct Main *bmain, struct ViewLayer *viewlayer, struct Scene *scene, bool run_as_job);
void EEVEE_lightbake_job_data_free(void *custom_data);
void EEVEE_lightbake_update(void *custom_data);
void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress);
void EEVEE_lightbake_update_world_quick(struct EEVEE_ViewLayerData *sldata, struct EEVEE_Data *vedata, const Scene *scene);
/* Light Cache */
struct LightCache *EEVEE_lightcache_create(
const int grid_len, const int cube_len,
const int cube_size, const int vis_size,
const int irr_size[3]);
void EEVEE_lightcache_free(struct LightCache *lcache);
void EEVEE_lightcache_load(struct LightCache *lcache);
void EEVEE_lightcache_info_update(struct SceneEEVEE *eevee);
#endif /* __EEVEE_LIGHTCACHE_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -148,7 +148,7 @@ void EEVEE_lights_init(EEVEE_ViewLayerData *sldata)
DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_blur);
/* Compute adequate size for the octahedral map. */
linfo->shadow_cube_store_size = (int)ceil(sqrt((sh_cube_size * sh_cube_size) * 6.0f));
linfo->shadow_cube_store_size = OCTAHEDRAL_SIZE_FROM_CUBESIZE(sh_cube_size);
CLAMP(linfo->shadow_cube_store_size, 1, 4096);
CLAMP(sh_cube_size, 1, 4096);

View File

@ -30,15 +30,31 @@
#include "DNA_screen_types.h"
#include "DNA_world_types.h"
#include "DEG_depsgraph_query.h"
#include "ED_screen.h"
#include "eevee_private.h"
#include "eevee_lightcache.h"
static void eevee_lookdev_lightcache_delete(EEVEE_Data *vedata)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
MEM_SAFE_FREE(stl->lookdev_lightcache);
MEM_SAFE_FREE(stl->lookdev_grid_data);
MEM_SAFE_FREE(stl->lookdev_cube_data);
DRW_TEXTURE_FREE_SAFE(txl->lookdev_grid_tx);
DRW_TEXTURE_FREE_SAFE(txl->lookdev_cube_tx);
}
void EEVEE_lookdev_cache_init(
EEVEE_Data *vedata, DRWShadingGroup **grp, GPUShader *shader, DRWPass *pass,
World *world, EEVEE_LightProbesInfo *pinfo)
{
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
if (LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d)) {
@ -47,6 +63,43 @@ void EEVEE_lookdev_cache_init(
struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get();
GPUTexture *tex = NULL;
/* If one of the component is missing we start from scratch. */
if ((stl->lookdev_grid_data == NULL) ||
(stl->lookdev_cube_data == NULL) ||
(txl->lookdev_grid_tx == NULL) ||
(txl->lookdev_cube_tx == NULL))
{
eevee_lookdev_lightcache_delete(vedata);
}
if (stl->lookdev_lightcache == NULL) {
const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph);
#if defined(IRRADIANCE_SH_L2)
int grid_res = 4;
#elif defined(IRRADIANCE_CUBEMAP)
int grid_res = 8;
#elif defined(IRRADIANCE_HL2)
int grid_res = 4;
#endif
int cube_res = OCTAHEDRAL_SIZE_FROM_CUBESIZE(scene_eval->eevee.gi_cubemap_resolution);
int vis_res = scene_eval->eevee.gi_visibility_resolution;
stl->lookdev_lightcache = EEVEE_lightcache_create(1, 1, cube_res, vis_res, (int[3]){grid_res, grid_res, 1});
/* We do this to use a special light cache for lookdev.
* This lightcache needs to be per viewport. But we need to
* have correct freeing when the viewport is closed. So we
* need to reference all textures to the txl and the memblocks
* to the stl. */
stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data;
stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data;
stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips;
txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex;
txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex;
}
stl->g_data->light_cache = stl->lookdev_lightcache;
*grp = DRW_shgroup_create(shader, pass);
axis_angle_to_mat3_single(stl->g_data->studiolight_matrix, 'Z', v3d->shading.studiolight_rot_z);
DRW_shgroup_uniform_mat3(*grp, "StudioLightMatrix", stl->g_data->studiolight_matrix);
@ -77,11 +130,9 @@ void EEVEE_lookdev_cache_init(
((pinfo->studiolight_index != sl->index) ||
(pinfo->studiolight_rot_z != v3d->shading.studiolight_rot_z)))
{
pinfo->update_world |= PROBE_UPDATE_ALL;
stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD;
pinfo->studiolight_index = sl->index;
pinfo->studiolight_rot_z = v3d->shading.studiolight_rot_z;
pinfo->prev_wo_sh_compiled = false;
pinfo->prev_world = NULL;
}
}
}

View File

@ -362,6 +362,8 @@ static void add_standard_uniforms(
DRWShadingGroup *shgrp, EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
int *ssr_id, float *refract_depth, bool use_ssrefraction, bool use_alpha_blend)
{
LightCache *lcache = vedata->stl->g_data->light_cache;
if (ssr_id == NULL) {
static int no_ssr = -1.0f;
ssr_id = &no_ssr;
@ -393,12 +395,12 @@ static void add_standard_uniforms(
/* TODO if diffuse bsdf */
if (true) {
DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &sldata->irradiance_pool);
DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex);
}
/* TODO if glossy bsdf */
if (true) {
DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &sldata->probe_pool);
DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &lcache->cube_tx.tex);
DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &vedata->txl->planar_pool);
DRW_shgroup_uniform_int(shgrp, "outputSsrId", ssr_id, 1);
}
@ -972,7 +974,6 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
DRW_shgroup_call_add(grp, geom, NULL);
break;
case GPU_MAT_QUEUED:
sldata->probes->all_materials_updated = false;
/* TODO Bypass probe compilation. */
col = compile_col;
break;
@ -1229,7 +1230,6 @@ static void material_opaque(
}
case GPU_MAT_QUEUED:
{
sldata->probes->all_materials_updated = false;
/* TODO Bypass probe compilation. */
color_p = compile_col;
metal_p = spec_p = rough_p = &half;
@ -1316,7 +1316,6 @@ static void material_transparent(
}
case GPU_MAT_QUEUED:
{
sldata->probes->all_materials_updated = false;
/* TODO Bypass probe compilation. */
color_p = compile_col;
metal_p = spec_p = rough_p = &half;
@ -1646,7 +1645,6 @@ void EEVEE_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata,
}
case GPU_MAT_QUEUED:
{
sldata->probes->all_materials_updated = false;
color_p = compile_col;
metal_p = spec_p = rough_p = &half;
break;

View File

@ -26,11 +26,14 @@
#ifndef __EEVEE_PRIVATE_H__
#define __EEVEE_PRIVATE_H__
#include "DNA_lightprobe_types.h"
struct Object;
struct EEVEE_BoundSphere;
struct EEVEE_ShadowCasterBuffer;
struct RenderLayer;
struct RenderResult;
struct GPUFrameBuffer;
extern struct DrawEngineType draw_engine_eevee_type;
@ -98,6 +101,10 @@ extern struct DrawEngineType draw_engine_eevee_type;
#define USE_SCENE_LIGHT(v3d) ((!v3d) || (!LOOK_DEV_MODE_ENABLED(v3d)) || ((LOOK_DEV_MODE_ENABLED(v3d) && (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS))))
#define LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d) (LOOK_DEV_MODE_ENABLED(v3d) && !(v3d->shading.flag & V3D_SHADING_SCENE_WORLD))
#define OCTAHEDRAL_SIZE_FROM_CUBESIZE(cube_size) ((int)ceilf(sqrtf((cube_size * cube_size) * 6.0f)))
#define MIN_CUBE_LOD_LEVEL 3
#define MAX_PLANAR_LOD_LEVEL 9
/* World shader variations */
enum {
VAR_WORLD_BACKGROUND = 0,
@ -132,6 +139,27 @@ enum {
VAR_MAT_SSSALBED = (1 << 17),
};
/* ************ PROBE UBO ************* */
/* They are the same struct as their Cache siblings.
* typedef'ing just to keep the naming consistent with
* other eevee types. */
typedef LightProbeCache EEVEE_LightProbe;
typedef LightGridCache EEVEE_LightGrid;
typedef struct EEVEE_PlanarReflection {
float plane_equation[4];
float clip_vec_x[3], attenuation_scale;
float clip_vec_y[3], attenuation_bias;
float clip_edge_x_pos, clip_edge_x_neg;
float clip_edge_y_pos, clip_edge_y_neg;
float facing_scale, facing_bias, clipsta, pad;
float reflectionmat[4][4]; /* Used for sampling the texture. */
float mtx[4][4]; /* Not used in shader. TODO move elsewhere. */
} EEVEE_PlanarReflection;
/* --------------------------------------- */
typedef struct EEVEE_BoundSphere {
float center[3], radius;
} EEVEE_BoundSphere;
@ -272,6 +300,9 @@ typedef struct EEVEE_TextureList {
struct GPUTexture *volume_scatter_history;
struct GPUTexture *volume_transmittance_history;
struct GPUTexture *lookdev_grid_tx;
struct GPUTexture *lookdev_cube_tx;
struct GPUTexture *planar_pool;
struct GPUTexture *planar_depth;
@ -288,6 +319,10 @@ typedef struct EEVEE_StorageList {
struct EEVEE_PrivateData *g_data;
struct LightCache *lookdev_lightcache;
EEVEE_LightProbe *lookdev_cube_data;
EEVEE_LightGrid *lookdev_grid_data;
LightCacheTexture *lookdev_cube_mips;
} EEVEE_StorageList;
/* ************ LIGHT UBO ************* */
@ -392,42 +427,11 @@ enum {
LIGHT_UPDATE_SHADOW_CUBE = (1 << 0),
};
/* ************ PROBE UBO ************* */
typedef struct EEVEE_LightProbe {
float position[3], parallax_type;
float attenuation_fac;
float attenuation_type;
float pad3[2];
float attenuationmat[4][4];
float parallaxmat[4][4];
} EEVEE_LightProbe;
typedef struct EEVEE_LightGrid {
float mat[4][4];
int resolution[3], offset;
float corner[3], attenuation_scale;
float increment_x[3], attenuation_bias; /* world space vector between 2 opposite cells */
float increment_y[3], level_bias;
float increment_z[3], pad4;
float visibility_bias, visibility_bleed, visibility_range, pad5;
} EEVEE_LightGrid;
typedef struct EEVEE_PlanarReflection {
float plane_equation[4];
float clip_vec_x[3], attenuation_scale;
float clip_vec_y[3], attenuation_bias;
float clip_edge_x_pos, clip_edge_x_neg;
float clip_edge_y_pos, clip_edge_y_neg;
float facing_scale, facing_bias, pad[2];
float reflectionmat[4][4];
} EEVEE_PlanarReflection;
/* ************ PROBE DATA ************* */
typedef struct EEVEE_LightProbeVisTest {
struct Collection *collection; /* Skip test if NULL */
bool invert;
bool cached; /* Reuse last test results */
struct Collection *collection; /* Skip test if NULL */
} EEVEE_LightProbeVisTest;
typedef struct EEVEE_LightProbesInfo {
@ -440,13 +444,9 @@ typedef struct EEVEE_LightProbesInfo {
int updated_bounce;
int num_bounce;
int cubemap_res;
int target_size;
int grid_initialized;
struct World *prev_world;
int update_world;
bool prev_wo_sh_compiled;
/* Update */
bool do_cube_update;
bool all_materials_updated;
bool do_grid_update;
/* For rendering probes */
float probemat[6][4][4];
int layer;
@ -465,15 +465,11 @@ typedef struct EEVEE_LightProbesInfo {
int shres;
int studiolight_index;
float studiolight_rot_z;
/* List of probes in the scene. */
/* XXX This is fragile, can get out of sync quickly. */
struct Object *probes_cube_ref[MAX_PROBE];
struct Object *probes_grid_ref[MAX_GRID];
struct Object *probes_planar_ref[MAX_PLANAR];
EEVEE_LightProbeVisTest planar_vis_tests[MAX_PLANAR];
/* UBO Storage : data used by UBO */
struct EEVEE_LightProbe probe_data[MAX_PROBE];
struct EEVEE_LightGrid grid_data[MAX_GRID];
struct EEVEE_PlanarReflection planar_data[MAX_PLANAR];
EEVEE_LightProbe probe_data[MAX_PROBE];
EEVEE_LightGrid grid_data[MAX_GRID];
EEVEE_PlanarReflection planar_data[MAX_PLANAR];
/* Probe Visibility Collection */
EEVEE_LightProbeVisTest vis_data;
} EEVEE_LightProbesInfo;
@ -673,24 +669,18 @@ typedef struct EEVEE_ViewLayerData {
struct GPUUniformBuffer *grid_ubo;
struct GPUUniformBuffer *planar_ubo;
struct GPUFrameBuffer *probe_filter_fb;
struct GPUFrameBuffer *probe_face_fb[6];
struct GPUTexture *probe_rt;
struct GPUTexture *probe_depth_rt;
struct GPUTexture *probe_pool;
struct GPUTexture *irradiance_pool;
struct GPUTexture *irradiance_rt;
/* Common Uniform Buffer */
struct EEVEE_CommonUniformBuffer common_data;
struct GPUUniformBuffer *common_ubo;
struct EEVEE_ClipPlanesUniformBuffer clip_data;
struct GPUUniformBuffer *clip_ubo;
struct LightCache *fallback_lightcache;
} EEVEE_ViewLayerData;
/* ************ OBJECT DATA ************ */
typedef struct EEVEE_LightData {
short light_id, shadow_id;
} EEVEE_LightData;
@ -726,29 +716,7 @@ typedef struct EEVEE_LampEngineData {
typedef struct EEVEE_LightProbeEngineData {
DrawData dd;
/* NOTE: need_full_update is set by dependency graph when the probe or it's
* object is updated. This triggers full probe update, including it's
* "progressive" GI refresh.
*
* need_update is always set to truth when need_full_update is tagged, but
* might also be forced to be kept truth during GI refresh stages.
*
* TODO(sergey): Is there a way to avoid two flags here, or at least make
* it more clear what's going on here?
*/
bool need_full_update;
bool need_update;
bool ready_to_shade;
int updated_cells;
int updated_lvl;
int num_cell;
int max_lvl;
int probe_id; /* Only used for display data */
float probe_size; /* Only used for display data */
DRWMatrixState mats; /* For planar probes */
float planer_eq_offset[4];
struct ListBase captured_object_list;
} EEVEE_LightProbeEngineData;
typedef struct EEVEE_ObjectEngineData {
@ -790,6 +758,8 @@ typedef struct EEVEE_PrivateData {
struct DRWShadingGroup *planar_display_shgrp;
struct GHash *material_hash;
float background_alpha; /* TODO find a better place for this. */
/* Chosen lightcache: can come from Lookdev or the viewlayer. */
struct LightCache *light_cache;
/* For planar probes */
float planar_texel_size[2];
/* For double buffering */
@ -807,6 +777,7 @@ typedef struct EEVEE_PrivateData {
/* eevee_data.c */
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void);
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob);
EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob);
@ -855,17 +826,37 @@ void EEVEE_lights_update(EEVEE_ViewLayerData *sldata);
void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl);
void EEVEE_lights_free(void);
/* eevee_lightprobes.c */
bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data);
bool EEVEE_lightprobes_all_probes_ready(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, Object *ob);
void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob);
void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_lightprobes_free(void);
void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, GPUTexture *rt_color, GPUTexture *rt_depth);
void EEVEE_lightbake_render_world(
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUFrameBuffer *face_fb[6]);
void EEVEE_lightbake_render_scene(
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUFrameBuffer *face_fb[6],
const float pos[3], float near_clip, float far_clip);
void EEVEE_lightbake_filter_glossy(
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUTexture *rt_color, struct GPUFrameBuffer *fb,
int probe_idx, float intensity, int maxlevel);
void EEVEE_lightbake_filter_diffuse(
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUTexture *rt_color, struct GPUFrameBuffer *fb,
int grid_offset, float intensity);
void EEVEE_lightbake_filter_visibility(
EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUTexture *rt_depth, struct GPUFrameBuffer *fb,
int grid_offset, float clipsta, float clipend, float vis_range, float vis_blur, int vis_size);
void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *prb_data, int *offset);
void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *prb_data);
void EEVEE_lightprobes_planar_data_from_object(Object *ob, EEVEE_PlanarReflection *eplanar, EEVEE_LightProbeVisTest *vis_test);
/* eevee_depth_of_field.c */
int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera);
void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
@ -954,6 +945,9 @@ void EEVEE_render_update_passes(struct RenderEngine *engine, struct Scene *scene
void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, DRWShadingGroup **grp, GPUShader *shader, DRWPass *pass, struct World *world, EEVEE_LightProbesInfo *pinfo);
void EEVEE_lookdev_draw_background(EEVEE_Data *vedata);
/** eevee_engine.c */
void EEVEE_cache_populate(void *vedata, Object *ob);
/* Shadow Matrix */
static const float texcomat[4][4] = { /* From NDC to TexCo */
{0.5f, 0.0f, 0.0f, 0.0f},

View File

@ -134,17 +134,31 @@ void EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph *
EEVEE_volumes_cache_init(sldata, vedata);
}
/* Used by light cache. in this case engine is NULL. */
void EEVEE_render_cache(
void *vedata, struct Object *ob,
struct RenderEngine *engine, struct Depsgraph *UNUSED(depsgraph))
{
EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure();
char info[42];
BLI_snprintf(info, sizeof(info), "Syncing %s", ob->id.name + 2);
RE_engine_update_stats(engine, NULL, info);
EEVEE_LightProbesInfo *pinfo = sldata->probes;
bool cast_shadow = false;
if (pinfo->vis_data.collection) {
/* Used for rendering probe with visibility groups. */
bool ob_vis = BKE_collection_has_object_recursive(pinfo->vis_data.collection, ob);
ob_vis = (pinfo->vis_data.invert) ? !ob_vis : ob_vis;
if (!ob_vis) {
return;
}
}
if (engine) {
char info[42];
BLI_snprintf(info, sizeof(info), "Syncing %s", ob->id.name + 2);
RE_engine_update_stats(engine, NULL, info);
}
if (ob->base_flag & BASE_VISIBLE) {
EEVEE_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
}
@ -154,7 +168,7 @@ void EEVEE_render_cache(
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_LIGHTPROBE) {
EEVEE_lightprobes_cache_add(sldata, ob);
EEVEE_lightprobes_cache_add(sldata, vedata, ob);
}
else if (ob->type == OB_LAMP) {
EEVEE_lights_cache_add(sldata, ob);
@ -477,14 +491,8 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl
DRW_viewport_matrix_override_set(g_data->viewinv, DRW_MAT_VIEWINV);
/* Refresh Probes */
while (EEVEE_lightprobes_all_probes_ready(sldata, vedata) == false) {
RE_engine_update_stats(engine, NULL, "Updating Probes");
EEVEE_lightprobes_refresh(sldata, vedata);
/* Refreshing probes can take some times, allow exit. */
if (RE_engine_test_break(engine)) {
return;
}
}
RE_engine_update_stats(engine, NULL, "Updating Probes");
EEVEE_lightprobes_refresh(sldata, vedata);
EEVEE_lightprobes_refresh_planar(sldata, vedata);
DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data);

View File

@ -187,6 +187,7 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
LightCache *lcache = stl->g_data->light_cache;
struct Gwn_Batch *quad = DRW_cache_fullscreen_quad_get();
@ -230,7 +231,7 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src);
DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input);
DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input);
DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &sldata->probe_pool);
DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex);
DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool);
DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth);
DRW_shgroup_uniform_texture_ref(grp, "hitBuffer", &effects->ssr_hit_output);

View File

@ -349,6 +349,7 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
EEVEE_StorageList *stl = vedata->stl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_EffectsInfo *effects = stl->effects;
LightCache *lcache = stl->g_data->light_cache;
EEVEE_CommonUniformBuffer *common_data = &sldata->common_data;
if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) {
@ -417,7 +418,7 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
psl->volumetric_scatter_ps = DRW_pass_create("Volumetric Scattering", DRW_STATE_WRITE_COLOR);
grp = DRW_shgroup_empty_tri_batch_create(scatter_sh, psl->volumetric_scatter_ps,
common_data->vol_tex_size[2]);
DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &sldata->irradiance_pool);
DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex);
DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool);
DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_prop_scattering);

View File

@ -1,15 +1,18 @@
flat in int pid;
in vec3 worldNormal;
in vec3 worldPosition;
in vec2 quadCoord;
out vec4 FragColor;
void main()
{
vec3 V = (ProjectionMatrix[3][3] == 0.0) /* if perspective */
? normalize(cameraPos - worldPosition)
: cameraForward;
vec3 N = normalize(worldNormal);
FragColor = vec4(textureLod_octahedron(probeCubes, vec4(reflect(-V, N), pid), 0.0, prbLodCubeMax).rgb, 1.0);
float dist_sqr = dot(quadCoord, quadCoord);
/* Discard outside the circle. */
if (dist_sqr > 1.0)
discard;
vec3 view_nor = vec3(quadCoord, sqrt(max(0.0, 1.0 - dist_sqr)));
vec3 world_ref = mat3(ViewMatrixInverse) * reflect(vec3(0.0, 0.0, -1.0), view_nor);
FragColor = vec4(textureLod_octahedron(probeCubes, vec4(world_ref, pid), 0.0, prbLodCubeMax).rgb, 1.0);
}

View File

@ -1,26 +1,43 @@
in vec3 pos;
/* XXX TODO fix code duplication */
struct CubeData {
vec4 position_type;
vec4 attenuation_fac_type;
mat4 influencemat;
mat4 parallaxmat;
};
/* Instance attrib */
in int probe_id;
in vec3 probe_location;
in float sphere_size;
layout(std140) uniform probe_block {
CubeData probes_data[MAX_PROBE];
};
uniform float sphere_size;
uniform vec3 screen_vecs[2];
flat out int pid;
out vec3 worldNormal;
out vec3 worldPosition;
out vec2 quadCoord;
const vec2 pos[6] = vec2[6](
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2(-1.0, 1.0),
vec2( 1.0, -1.0),
vec2( 1.0, 1.0),
vec2(-1.0, 1.0)
);
void main()
{
pid = probe_id;
pid = 1 + (gl_VertexID / 6); /* +1 for the world */
int vert_id = gl_VertexID % 6;
/* While this is not performant, we do this to
* match the object mode engine instancing shader. */
mat4 offsetmat = mat4(1.0); /* Identity */
offsetmat[3].xyz = probe_location;
quadCoord = pos[vert_id];
vec4 wpos = offsetmat * vec4(pos * sphere_size, 1.0);
worldPosition = wpos.xyz;
gl_Position = ViewProjectionMatrix * wpos;
worldNormal = normalize(pos);
vec3 ws_location = probes_data[pid].position_type.xyz;
vec3 screen_pos = screen_vecs[0] * quadCoord.x + screen_vecs[1] * quadCoord.y;
ws_location += screen_pos * sphere_size;
gl_Position = ViewProjectionMatrix * vec4(ws_location, 1.0);
gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting */
}

View File

@ -1,11 +1,19 @@
flat in int cellOffset;
in vec3 worldNormal;
in vec2 quadCoord;
out vec4 FragColor;
void main()
{
IrradianceData ir_data = load_irradiance_cell(cellOffset, worldNormal);
FragColor = vec4(compute_irradiance(worldNormal, ir_data), 1.0);
float dist_sqr = dot(quadCoord, quadCoord);
/* Discard outside the circle. */
if (dist_sqr > 1.0)
discard;
vec3 view_nor = vec3(quadCoord, sqrt(max(0.0, 1.0 - dist_sqr)));
vec3 world_nor = mat3(ViewMatrixInverse) * view_nor;
IrradianceData ir_data = load_irradiance_cell(cellOffset, world_nor);
FragColor = vec4(compute_irradiance(world_nor, ir_data), 1.0);
}

View File

@ -1,6 +1,4 @@
in vec3 pos;
uniform float sphere_size;
uniform int offset;
uniform ivec3 grid_resolution;
@ -8,25 +6,44 @@ uniform vec3 corner;
uniform vec3 increment_x;
uniform vec3 increment_y;
uniform vec3 increment_z;
uniform vec3 screen_vecs[2];
flat out int cellOffset;
out vec3 worldNormal;
out vec2 quadCoord;
const vec2 pos[6] = vec2[6](
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2(-1.0, 1.0),
vec2( 1.0, -1.0),
vec2( 1.0, 1.0),
vec2(-1.0, 1.0)
);
void main()
{
int cell_id = gl_VertexID / 6;
int vert_id = gl_VertexID % 6;
vec3 ls_cell_location;
/* Keep in sync with update_irradiance_probe */
ls_cell_location.z = float(gl_InstanceID % grid_resolution.z);
ls_cell_location.y = float((gl_InstanceID / grid_resolution.z) % grid_resolution.y);
ls_cell_location.x = float(gl_InstanceID / (grid_resolution.z * grid_resolution.y));
ls_cell_location.z = float(cell_id % grid_resolution.z);
ls_cell_location.y = float((cell_id / grid_resolution.z) % grid_resolution.y);
ls_cell_location.x = float(cell_id / (grid_resolution.z * grid_resolution.y));
cellOffset = offset + gl_InstanceID;
cellOffset = offset + cell_id;
vec3 ws_cell_location = corner +
(increment_x * ls_cell_location.x +
increment_y * ls_cell_location.y +
increment_z * ls_cell_location.z);
gl_Position = ViewProjectionMatrix * vec4(pos * 0.02 * sphere_size + ws_cell_location, 1.0);
worldNormal = normalize(pos);
quadCoord = pos[vert_id];
vec3 screen_pos = screen_vecs[0] * quadCoord.x + screen_vecs[1] * quadCoord.y;
ws_cell_location += screen_pos * sphere_size;
gl_Position = ViewProjectionMatrix * vec4(ws_cell_location , 1.0);
gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting */
}

View File

@ -27,6 +27,7 @@ struct PlanarData {
vec4 clip_edges;
vec4 facing_scale_bias;
mat4 reflectionmat; /* transform world space into reflection texture space */
mat4 unused;
};
#define pl_plane_eq plane_equation

View File

@ -256,7 +256,7 @@ void CLOSURE_NAME(
PlanarData pd = planars_data[i];
/* Fade on geometric normal. */
float fade = probe_attenuation_planar(pd, worldPosition, worldNormal, roughness);
float fade = probe_attenuation_planar(pd, worldPosition, (gl_FrontFacing) ? worldNormal : -worldNormal, roughness);
if (fade > 0.0) {
if (!(ssrToggle && ssr_id == outputSsrId)) {
@ -404,7 +404,7 @@ void CLOSURE_NAME(
spec_occlu = 1.0;
}
out_spec += spec_accum.rgb * ssr_spec * spec_occlu * float(specToggle);
out_spec += spec_accum.rgb * ssr_spec * spec_occlu;
#endif
#ifdef CLOSURE_REFRACTION
@ -419,7 +419,12 @@ void CLOSURE_NAME(
vec2 C_brdf_lut = texture(utilTex, vec3(C_uv, 1.0)).rg;
vec3 C_fresnel = F_ibl(vec3(0.04), brdf_lut) * specular_occlusion(NV, final_ao, C_roughness);
out_spec += C_spec_accum.rgb * C_fresnel * float(specToggle) * C_intensity;
out_spec += C_spec_accum.rgb * C_fresnel * C_intensity;
#endif
#ifdef CLOSURE_GLOSSY
/* Global toggle for lightprobe baking. */
out_spec *= float(specToggle);
#endif
/* ---------------------------------------------------------------- */

View File

@ -56,6 +56,9 @@ void MATERIAL_OT_paste(struct wmOperatorType *ot);
void SCENE_OT_view_layer_add(struct wmOperatorType *ot);
void SCENE_OT_view_layer_remove(struct wmOperatorType *ot);
void SCENE_OT_light_cache_bake(struct wmOperatorType *ot);
void SCENE_OT_light_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

@ -62,6 +62,9 @@ void ED_operatortypes_render(void)
WM_operatortype_append(SCENE_OT_render_view_add);
WM_operatortype_append(SCENE_OT_render_view_remove);
WM_operatortype_append(SCENE_OT_light_cache_bake);
WM_operatortype_append(SCENE_OT_light_cache_free);
#ifdef WITH_FREESTYLE
WM_operatortype_append(SCENE_OT_freestyle_module_add);
WM_operatortype_append(SCENE_OT_freestyle_module_remove);

View File

@ -33,6 +33,7 @@
#include "DNA_curve_types.h"
#include "DNA_lamp_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
@ -94,6 +95,8 @@
#include "RE_pipeline.h"
#include "engines/eevee/eevee_lightcache.h"
#include "render_intern.h" // own include
/********************** material slot operators *********************/
@ -673,6 +676,184 @@ void SCENE_OT_view_layer_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/********************** light cache operators *********************/
enum {
LIGHTCACHE_SUBSET_ALL = 0,
LIGHTCACHE_SUBSET_DIRTY,
LIGHTCACHE_SUBSET_CUBE,
};
static void light_cache_bake_tag_cache(Scene *scene, wmOperator *op)
{
if (scene->eevee.light_cache != NULL) {
int subset = RNA_enum_get(op->ptr, "subset");
switch (subset) {
case LIGHTCACHE_SUBSET_ALL:
scene->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_GRID | LIGHTCACHE_UPDATE_CUBE;
break;
case LIGHTCACHE_SUBSET_CUBE:
scene->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_CUBE;
break;
case LIGHTCACHE_SUBSET_DIRTY:
/* Leave tag untouched. */
break;
}
}
}
/* catch esc */
static int light_cache_bake_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = (Scene *) op->customdata;
/* no running blender, remove handler and pass through */
if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_RENDER)) {
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
}
/* running render */
switch (event->type) {
case ESCKEY:
return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_PASS_THROUGH;
}
static void light_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_RENDER);
}
/* executes blocking render */
static int light_cache_bake_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
G.is_break = false;
/* TODO abort if selected engine is not eevee. */
void *rj = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, false);
light_cache_bake_tag_cache(scene, op);
short stop = 0, do_update; float progress; /* Not actually used. */
EEVEE_lightbake_job(rj, &stop, &do_update, &progress);
EEVEE_lightbake_job_data_free(rj);
// no redraw needed, we leave state as we entered it
ED_update_for_newframe(bmain, CTX_data_depsgraph(C));
WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene);
return OPERATOR_FINISHED;
}
static int light_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
int delay = RNA_int_get(op->ptr, "delay");
wmJob *wm_job = EEVEE_lightbake_job_create(wm, win, bmain, view_layer, scene, delay);
if (!wm_job) {
return OPERATOR_CANCELLED;
}
/* add modal handler for ESC */
WM_event_add_modal_handler(C, op);
light_cache_bake_tag_cache(scene, 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_jobs_start(wm, wm_job);
WM_cursor_wait(0);
return OPERATOR_RUNNING_MODAL;
}
void SCENE_OT_light_cache_bake(wmOperatorType *ot)
{
static const EnumPropertyItem light_cache_subset_items[] = {
{LIGHTCACHE_SUBSET_ALL, "ALL", 0, "All LightProbes", "Bake both irradiance grids and reflection cubemaps"},
{LIGHTCACHE_SUBSET_DIRTY, "DIRTY", 0, "Dirty Only", "Only bake lightprobes that are marked as dirty"},
{LIGHTCACHE_SUBSET_CUBE, "CUBEMAPS", 0, "Cubemaps Only", "Try to only bake reflection cubemaps if irradiance "
"grids are up to date"},
{0, NULL, 0, NULL, NULL}
};
/* identifiers */
ot->name = "Bake Light Cache";
ot->idname = "SCENE_OT_light_cache_bake";
ot->description = "Bake the active view layer lighting";
/* api callbacks */
ot->invoke = light_cache_bake_invoke;
ot->modal = light_cache_bake_modal;
ot->cancel = light_cache_bake_cancel;
ot->exec = light_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);
}
static bool light_cache_free_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
return scene->eevee.light_cache;
}
static int light_cache_free_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
if (!scene->eevee.light_cache) {
return OPERATOR_CANCELLED;
}
EEVEE_lightcache_free(scene->eevee.light_cache);
scene->eevee.light_cache = NULL;
EEVEE_lightcache_info_update(&scene->eevee);
DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
return OPERATOR_FINISHED;
}
void SCENE_OT_light_cache_free(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Free Light Cache";
ot->idname = "SCENE_OT_light_cache_free";
ot->description = "Free cached indirect lighting";
/* api callbacks */
ot->exec = light_cache_free_exec;
ot->poll = light_cache_free_poll;
}
/********************** render view operators *********************/
static bool render_view_remove_poll(bContext *C)

View File

@ -683,6 +683,18 @@ static void view3d_id_path_drop_copy(wmDrag *drag, wmDropBox *drop)
}
}
static void view3d_lightcache_update(bContext *C)
{
PointerRNA op_ptr;
WM_operator_properties_create(&op_ptr, "SCENE_OT_light_cache_bake");
RNA_int_set(&op_ptr, "delay", 200);
RNA_enum_set_identifier(C, &op_ptr, "subset", "DIRTY");
WM_operator_name_call(C, "SCENE_OT_light_cache_bake", WM_OP_INVOKE_DEFAULT, &op_ptr);
WM_operator_properties_free(&op_ptr);
}
/* region dropbox definition */
static void view3d_dropboxes(void)
@ -980,6 +992,9 @@ static void view3d_main_region_listener(
break;
}
break;
case NC_LIGHTPROBE:
ED_area_tag_refresh(sa);
break;
case NC_IMAGE:
/* this could be more fine grained checks if we had
* more context than just the region */
@ -1409,6 +1424,13 @@ static void space_view3d_listener(
}
}
static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(sa))
{
/* This is only used by the auto lightprobe refresh for the moment.
* So we don't need to check anything to know what to do. */
view3d_lightcache_update((bContext *)C);
}
const char *view3d_context_dir[] = {
"active_base", "active_object", NULL
};
@ -1509,6 +1531,7 @@ void ED_spacetype_view3d(void)
st->free = view3d_free;
st->init = view3d_init;
st->listener = space_view3d_listener;
st->refresh = space_view3d_refresh;
st->duplicate = view3d_duplicate;
st->operatortypes = view3d_operatortypes;
st->keymap = view3d_keymap;

View File

@ -106,6 +106,78 @@ enum {
LIGHTPROBE_SHAPE_BOX = 1,
};
/* ------- Eevee LightProbes ------- */
/* Needs to be there because written to file
* with the lightcache. */
typedef struct LightProbeCache {
float position[3], parallax_type;
float attenuation_fac;
float attenuation_type;
float pad3[2];
float attenuationmat[4][4];
float parallaxmat[4][4];
} LightProbeCache;
typedef struct LightGridCache {
float mat[4][4];
int resolution[3], offset; /* offset to the first irradiance sample in the pool. */
float corner[3], attenuation_scale;
float increment_x[3], attenuation_bias; /* world space vector between 2 opposite cells */
float increment_y[3], level_bias;
float increment_z[3], pad4;
float visibility_bias, visibility_bleed, visibility_range, pad5;
} LightGridCache;
/* ------ Eevee Lightcache ------- */
typedef struct LightCacheTexture {
struct GPUTexture *tex;
/* Copy of GPU datas to create GPUTextures on file read. */
char *data;
int tex_size[3];
char data_type;
char components;
char pad[2];
} LightCacheTexture;
typedef struct LightCache {
int flag;
/* only a single cache for now */
int cube_len, grid_len; /* Number of probes to use for rendering. */
int mips_len; /* Number of mipmap level to use. */
int vis_res, ref_res; /* Size of a visibility/reflection sample. */
int pad[2];
/* In the future, we could create a bigger texture containing
* multiple caches (for animation) and interpolate between the
* caches overtime to another texture. */
LightCacheTexture grid_tx;
LightCacheTexture cube_tx; /* Contains data for mipmap level 0. */
LightCacheTexture *cube_mips; /* Does not contains valid GPUTexture, only data. */
/* All lightprobes data contained in the cache. */
LightProbeCache *cube_data;
LightGridCache *grid_data;
} LightCache;
/* LightCache->flag */
enum {
LIGHTCACHE_BAKED = (1 << 0),
LIGHTCACHE_BAKING = (1 << 1),
LIGHTCACHE_CUBE_READY = (1 << 2),
LIGHTCACHE_GRID_READY = (1 << 3),
/* Update tagging */
LIGHTCACHE_UPDATE_CUBE = (1 << 4),
LIGHTCACHE_UPDATE_GRID = (1 << 5),
LIGHTCACHE_UPDATE_WORLD = (1 << 6),
};
/* EEVEE_LightCacheTexture->data_type */
enum {
LIGHTCACHETEX_BYTE = (1 << 0),
LIGHTCACHETEX_FLOAT = (1 << 1),
LIGHTCACHETEX_UINT = (1 << 2),
};
#ifdef __cplusplus
}
#endif

View File

@ -1389,6 +1389,9 @@ typedef struct SceneEEVEE {
int gi_cubemap_resolution;
int gi_visibility_resolution;
float gi_cubemap_draw_size;
float gi_irradiance_draw_size;
int taa_samples;
int taa_render_samples;
int sss_samples;
@ -1428,6 +1431,9 @@ typedef struct SceneEEVEE {
int shadow_method;
int shadow_cube_size;
int shadow_cascade_size;
struct LightCache *light_cache;
char light_cache_info[64];
} SceneEEVEE;
/* *************************************************************** */
@ -2101,6 +2107,9 @@ enum {
SCE_EEVEE_SSR_ENABLED = (1 << 14),
SCE_EEVEE_SSR_REFRACTION = (1 << 15),
SCE_EEVEE_SSR_HALF_RESOLUTION = (1 << 16),
SCE_EEVEE_SHOW_IRRADIANCE = (1 << 17),
SCE_EEVEE_SHOW_CUBEMAPS = (1 << 18),
SCE_EEVEE_GI_AUTOBAKE = (1 << 19),
};
/* SceneEEVEE->shadow_method */

View File

@ -5815,6 +5815,38 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
"Size of the shadow map applied to each irradiance sample");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
prop = RNA_def_property(srna, "gi_show_irradiance", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SHOW_IRRADIANCE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_ui_text(prop, "Show Irradiance Cache", "Display irradiance samples in the viewport");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
prop = RNA_def_property(srna, "gi_show_cubemaps", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SHOW_CUBEMAPS);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_ui_text(prop, "Show Cubemap Cache", "Display captured cubemaps in the viewport");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
prop = RNA_def_property(srna, "gi_irradiance_draw_size", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.05f, 10.0f);
RNA_def_property_float_default(prop, 0.1f);
RNA_def_property_ui_text(prop, "Irradiance Draw Size", "Size of the irradiance sample spheres to debug captured light");
prop = RNA_def_property(srna, "gi_cubemap_draw_size", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.05f, 10.0f);
RNA_def_property_float_default(prop, 0.3f);
RNA_def_property_ui_text(prop, "Cubemap Draw Size", "Size of the cubemap spheres to debug captured light");
prop = RNA_def_property(srna, "gi_auto_bake", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_GI_AUTOBAKE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_ui_text(prop, "Auto Bake", "Auto bake indirect lighting when editing probes");
prop = RNA_def_property(srna, "gi_cache_info", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "light_cache_info");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Light Cache Info", "Info on current cache status");
/* Temporal Anti-Aliasing (super sampling) */
prop = RNA_def_property(srna, "taa_samples", PROP_INT, PROP_NONE);
RNA_def_property_int_default(prop, 16);

View File

@ -524,6 +524,7 @@ enum {
WM_JOB_TYPE_ALEMBIC,
WM_JOB_TYPE_SHADER_COMPILATION,
WM_JOB_TYPE_STUDIOLIGHT,
WM_JOB_TYPE_LIGHT_BAKE,
/* add as needed, screencast, seq proxy build
* if having hard coded values is a problem */
};

View File

@ -257,6 +257,7 @@ typedef struct wmNotifier {
#define NC_GPENCIL (22<<24)
#define NC_LINESTYLE (23<<24)
#define NC_CAMERA (24<<24)
#define NC_LIGHTPROBE (25<<24)
/* data type, 256 entries is enough, it can overlap */
#define NOTE_DATA 0x00FF0000