Workbench: Add back studio lighting presets

This changes a bit how the userprefs solid lights works. They are not
visible until enabling the "Edit Solid Light" checkbox. Once enabled the
current studiolight used for solid mode will be overwritten.

Once the lighting settings are tweaked, the user can click the
"Save as Studio light" button to save the current settings.
This makes it easy to create new lighting without messing the other
presets.

The studio lights are stored as ASCII files on the disk using a dead
simple custom format.

The UI/UX is not perfect and will be improved in other commits.

Also includes:
* Separate LookDev HDRI selection from Solid Lights
* Hide LookDev HDRIs from the Solid Lights selection list
This commit is contained in:
Clément Foucault 2018-11-29 19:54:23 +01:00
parent 8f4ab480bf
commit 844788a36c
14 changed files with 430 additions and 97 deletions

View File

@ -2462,7 +2462,7 @@ class WM_OT_studiolight_install(Operator):
for filepath in filepaths:
shutil.copy(str(filepath), str(path_studiolights))
userpref.studio_lights.new(str(path_studiolights.joinpath(filepath.name)), self.type)
userpref.studio_lights.load(str(path_studiolights.joinpath(filepath.name)), self.type)
# print message
msg = (
@ -2479,6 +2479,57 @@ class WM_OT_studiolight_install(Operator):
return {'RUNNING_MODAL'}
class WM_OT_studiolight_new(Operator):
bl_idname = 'wm.studiolight_new'
bl_label = "Create Studio Light from default light setup"
filename: StringProperty(
name="Filename",
default="StudioLight",
)
def execute(self, context):
import pathlib
userpref = context.user_preferences
path_studiolights = bpy.utils.user_resource('DATAFILES')
if not path_studiolights:
self.report({'ERROR'}, "Failed to get Studio Light path")
return {'CANCELLED'}
path_studiolights = pathlib.Path(path_studiolights, "studiolights", "studio")
if not path_studiolights.exists():
try:
path_studiolights.mkdir(parents=True, exist_ok=True)
except:
traceback.print_exc()
finalpath = str(path_studiolights.joinpath(self.filename));
if pathlib.Path(finalpath + ".sl").is_file():
self.report({'ERROR'}, "File already exists")
return {'CANCELLED'}
userpref.studio_lights.new(path=finalpath)
# print message
msg = (
tip_("StudioLight Installed %r into %r") %
(self.filename, str(path_studiolights))
)
print(msg)
self.report({'INFO'}, msg)
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.prop(self, "filename")
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=600)
class WM_OT_studiolight_uninstall(Operator):
bl_idname = 'wm.studiolight_uninstall'
bl_label = "Uninstall Studio Light"
@ -2765,6 +2816,7 @@ classes = (
WM_OT_owner_enable,
WM_OT_url_open,
WM_OT_studiolight_install,
WM_OT_studiolight_new,
WM_OT_studiolight_uninstall,
WM_OT_studiolight_userpref_show,
WM_OT_tool_set_by_name,

View File

@ -47,7 +47,9 @@ class USERPREF_HT_header(Header):
elif userpref.active_section == 'LIGHTS':
layout.operator('wm.studiolight_install', text="Add MatCap").type = 'MATCAP'
layout.operator('wm.studiolight_install', text="Add LookDev HDRI").type = 'WORLD'
layout.operator('wm.studiolight_install', text="Add Studio Light").type = 'STUDIO'
op = layout.operator('wm.studiolight_install', text="Add Studio Light")
op.type = 'STUDIO'
op.filter_glob = ".sl"
elif userpref.active_section == 'THEMES':
layout.operator("wm.theme_install", icon='FILEBROWSER')
layout.operator("ui.reset_default_theme", icon='LOOP_BACK')
@ -1498,6 +1500,10 @@ class StudioLightPanelMixin():
layout = self.layout
userpref = context.user_preferences
lights = self._get_lights(userpref)
self.draw_light_list(layout, lights)
def draw_light_list(self, layout, lights):
if lights:
flow = layout.column_flow(columns=4)
for studio_light in lights:
@ -1569,8 +1575,16 @@ class USERPREF_PT_studiolight_lights(Panel, StudioLightPanelMixin):
layout.separator()
layout.prop(system, "edit_solid_light")
layout.prop(system, "light_ambient")
layout.operator('wm.studiolight_new', text="Save as Studio light")
lights = self._get_lights(userpref)
self.draw_light_list(layout, lights)
classes = (
USERPREF_HT_header,
USERPREF_PT_navigation,

View File

@ -4190,9 +4190,9 @@ class VIEW3D_PT_shading_lighting(Panel):
if shading.light == 'STUDIO':
# Not implemented right now
# sub.template_icon_view(shading, "studio_light", scale=3)
sub.template_icon_view(shading, "studio_light", scale=3)
# if shading.selected_studio_light.orientation == 'WORLD':
# if shading.selected_studio_light.type == 'WORLD':
# col.prop(shading, "studiolight_rotate_z", text="Rotation")
col = split.column()
@ -4221,7 +4221,7 @@ class VIEW3D_PT_shading_lighting(Panel):
col = split.column()
col.operator('wm.studiolight_userpref_show', emboss=False, text="", icon='PREFERENCES')
if shading.selected_studio_light.orientation == 'WORLD':
if shading.selected_studio_light.type == 'WORLD':
split = layout.split(factor=0.9)
col = split.column()
col.prop(shading, "studiolight_rotate_z", text="Rotation")

View File

@ -39,6 +39,7 @@
#include "BLI_sys_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "IMB_imbuf_types.h"
@ -59,6 +60,8 @@
#define STUDIOLIGHT_ICON_ID_TYPE_MATCAP (1 << 2)
#define STUDIOLIGHT_ICON_ID_TYPE_MATCAP_FLIPPED (1 << 3)
#define STUDIOLIGHT_MAX_LIGHT 4
#define STUDIOLIGHT_ICON_SIZE 96
/* Only 1 - 5 is supported */
@ -97,7 +100,7 @@ enum StudioLightFlag {
#define STUDIOLIGHT_FLAG_ALL (STUDIOLIGHT_INTERNAL | STUDIOLIGHT_EXTERNAL_FILE)
#define STUDIOLIGHT_FLAG_ORIENTATIONS (STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_TYPE_WORLD | STUDIOLIGHT_TYPE_MATCAP)
#define STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE (STUDIOLIGHT_TYPE_WORLD)
#define STUDIOLIGHT_ORIENTATIONS_SOLID (STUDIOLIGHT_INTERNAL | STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_TYPE_WORLD)
#define STUDIOLIGHT_ORIENTATIONS_SOLID (STUDIOLIGHT_INTERNAL | STUDIOLIGHT_TYPE_STUDIO)
typedef void StudioLightFreeFunction(struct StudioLight *, void *data);
@ -121,6 +124,8 @@ typedef struct StudioLight {
ImBuf *radiance_cubemap_buffers[6];
struct GPUTexture *equirect_radiance_gputexture;
struct GPUTexture *equirect_irradiance_gputexture;
SolidLight light[STUDIOLIGHT_MAX_LIGHT];
float light_ambient[3];
/*
* Free function to clean up the running icons previews (wmJob) the usage is in
@ -140,7 +145,9 @@ void BKE_studiolight_preview(uint *icon_buffer, StudioLight *sl, int icon_id_typ
struct ListBase *BKE_studiolight_listbase(void);
void BKE_studiolight_ensure_flag(StudioLight *sl, int flag);
void BKE_studiolight_refresh(void);
StudioLight *BKE_studiolight_new(const char *path, int orientation);
StudioLight *BKE_studiolight_load(const char *path, int orientation);
StudioLight *BKE_studiolight_create(const char *path, const SolidLight light[4], const float light_ambient[3]);
StudioLight *BKE_studiolight_studio_edit_get(void);
void BKE_studiolight_remove(StudioLight *sl);
void BKE_studiolight_set_free_function(StudioLight *sl, StudioLightFreeFunction *free_function, void *data);
void BKE_studiolight_unset_icon_id(StudioLight *sl, int icon_id);

View File

@ -35,9 +35,11 @@
#include "BKE_appdir.h"
#include "BKE_icons.h"
#include "BLI_dynstr.h"
#include "BLI_fileops.h"
#include "BLI_fileops_types.h"
#include "BLI_listbase.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_math_color.h"
#include "BLI_path_util.h"
@ -82,7 +84,7 @@ static int last_studiolight_id = 0;
*/
#define STUDIOLIGHT_LOAD_CACHED_FILES
static const char *STUDIOLIGHT_LIGHTS_FOLDER = "studiolights/light/";
static const char *STUDIOLIGHT_LIGHTS_FOLDER = "studiolights/studio/";
static const char *STUDIOLIGHT_WORLD_FOLDER = "studiolights/world/";
static const char *STUDIOLIGHT_MATCAP_FOLDER = "studiolights/matcap/";
@ -168,13 +170,15 @@ static struct StudioLight *studiolight_create(int flag)
sl->free_function = NULL;
sl->flag = flag;
sl->index = ++last_studiolight_id;
if (flag & STUDIOLIGHT_TYPE_MATCAP) {
if (flag & STUDIOLIGHT_TYPE_STUDIO) {
sl->icon_id_irradiance = BKE_icon_ensure_studio_light(sl, STUDIOLIGHT_ICON_ID_TYPE_IRRADIANCE);
}
else if (flag & STUDIOLIGHT_TYPE_MATCAP) {
sl->icon_id_matcap = BKE_icon_ensure_studio_light(sl, STUDIOLIGHT_ICON_ID_TYPE_MATCAP);
sl->icon_id_matcap_flipped = BKE_icon_ensure_studio_light(sl, STUDIOLIGHT_ICON_ID_TYPE_MATCAP_FLIPPED);
}
else {
sl->icon_id_radiance = BKE_icon_ensure_studio_light(sl, STUDIOLIGHT_ICON_ID_TYPE_RADIANCE);
sl->icon_id_irradiance = BKE_icon_ensure_studio_light(sl, STUDIOLIGHT_ICON_ID_TYPE_IRRADIANCE);
}
for (int index = 0; index < 6; index++) {
@ -184,6 +188,99 @@ static struct StudioLight *studiolight_create(int flag)
return sl;
}
#define STUDIOLIGHT_FILE_VERSION 1
#define READ_VAL(type, parser, id, val, lines) do { \
for (LinkNode *line = lines; line; line = line->next) { \
char *val_str, *str = line->link; \
if ((val_str = strstr(str, id " "))) { \
val_str += sizeof(id); /* Skip id + spacer. */ \
val = parser(val_str); \
} \
} \
} while (0)
#define READ_FVAL(id, val, lines) READ_VAL(float, atof, id, val, lines)
#define READ_IVAL(id, val, lines) READ_VAL(int, atoi, id, val, lines)
#define READ_VEC3(id, val, lines) do { \
READ_FVAL(id ".x", val[0], lines); \
READ_FVAL(id ".y", val[1], lines); \
READ_FVAL(id ".z", val[2], lines); \
} while (0)
#define READ_SOLIDLIGHT(sl, i, lines) do { \
READ_IVAL("light[" STRINGIFY(i) "].flag", sl[i].flag, lines); \
READ_FVAL("light[" STRINGIFY(i) "].smooth", sl[i].smooth, lines); \
READ_VEC3("light[" STRINGIFY(i) "].col", sl[i].col, lines); \
READ_VEC3("light[" STRINGIFY(i) "].spec", sl[i].spec, lines); \
READ_VEC3("light[" STRINGIFY(i) "].vec", sl[i].vec, lines); \
} while (0)
static void studiolight_load_solid_light(StudioLight *sl)
{
LinkNode *lines = BLI_file_read_as_lines(sl->path);
if (lines) {
READ_VEC3("light_ambient", sl->light_ambient, lines);
READ_SOLIDLIGHT(sl->light, 0, lines);
READ_SOLIDLIGHT(sl->light, 1, lines);
READ_SOLIDLIGHT(sl->light, 2, lines);
READ_SOLIDLIGHT(sl->light, 3, lines);
}
BLI_file_free_lines(lines);
}
#undef READ_SOLIDLIGHT
#undef READ_VEC3
#undef READ_IVAL
#undef READ_FVAL
#define WRITE_FVAL(str, id, val) (BLI_dynstr_appendf(str, id " %f\n", val))
#define WRITE_IVAL(str, id, val) (BLI_dynstr_appendf(str, id " %d\n", val))
#define WRITE_VEC3(str, id, val) do { \
WRITE_FVAL(str, id ".x", val[0]); \
WRITE_FVAL(str, id ".y", val[1]); \
WRITE_FVAL(str, id ".z", val[2]); \
} while (0)
#define WRITE_SOLIDLIGHT(str, sl, i) do { \
WRITE_IVAL(str, "light[" STRINGIFY(i) "].flag", sl[i].flag); \
WRITE_FVAL(str, "light[" STRINGIFY(i) "].smooth", sl[i].smooth); \
WRITE_VEC3(str, "light[" STRINGIFY(i) "].col", sl[i].col); \
WRITE_VEC3(str, "light[" STRINGIFY(i) "].spec", sl[i].spec); \
WRITE_VEC3(str, "light[" STRINGIFY(i) "].vec", sl[i].vec); \
} while (0)
static void studiolight_write_solid_light(StudioLight *sl)
{
FILE *fp = BLI_fopen(sl->path, "wb");
if (fp) {
DynStr *str = BLI_dynstr_new();
/* Very dumb ascii format. One value per line separated by a space. */
WRITE_IVAL(str, "version", STUDIOLIGHT_FILE_VERSION);
WRITE_VEC3(str, "light_ambient", sl->light_ambient);
WRITE_SOLIDLIGHT(str, sl->light, 0);
WRITE_SOLIDLIGHT(str, sl->light, 1);
WRITE_SOLIDLIGHT(str, sl->light, 2);
WRITE_SOLIDLIGHT(str, sl->light, 3);
char *cstr = BLI_dynstr_get_cstring(str);
fwrite(cstr, BLI_dynstr_get_len(str), 1, fp);
fclose(fp);
MEM_freeN(cstr);
BLI_dynstr_free(str);
}
}
#undef WRITE_SOLIDLIGHT
#undef WRITE_VEC3
#undef WRITE_IVAL
#undef WRITE_FVAL
static void direction_to_equirect(float r[2], const float dir[3])
{
r[0] = (atan2f(dir[1], dir[0]) - M_PI) / -(M_PI * 2);
@ -732,6 +829,83 @@ static void studiolight_irradiance_eval(StudioLight *sl, float color[3], const f
}
#endif
static float brdf_approx(float spec_color, float roughness, float NV)
{
/* Very rough own approx. We don't need it to be correct, just fast.
* Just simulate fresnel effect with roughness attenuation. */
float fresnel = exp2(-8.35f * NV) * (1.0f - roughness);
return spec_color * (1.0f - fresnel) + fresnel;
}
/* NL need to be unclamped. w in [0..1] range. */
static float wrapped_lighting(float NL, float w)
{
float w_1 = w + 1.0f;
return max_ff((NL + w) / (w_1 * w_1), 0.0f);
}
static float blinn_specular(
const float L[3], const float I[3], const float N[3], float R[3], float NL, float roughness, float wrap)
{
float half_dir[3];
float wrapped_NL = dot_v3v3(L, R);
add_v3_v3v3(half_dir, L, I);
normalize_v3(half_dir);
float spec_angle = max_ff(dot_v3v3(half_dir, N), 0.0f);
float gloss = 1.0f - roughness;
/* Reduce gloss for smooth light. (simulate bigger light) */
gloss *= 1.0f - wrap;
float shininess = exp2(10.0f * gloss + 1.0f);
/* Pi is already divided in the lamp power.
* normalization_factor = (shininess + 8.0) / (8.0 * M_PI) */
float normalization_factor = shininess * 0.125f + 1.0f;
float spec_light = powf(spec_angle, shininess) * max_ff(NL, 0.0f) * normalization_factor;
/* Simulate Env. light. */
float w = wrap * (1.0 - roughness) + roughness;
float spec_env = wrapped_lighting(wrapped_NL, w);
float w2 = wrap * wrap;
return spec_light * (1.0 - w2) + spec_env * w2;
}
/* Keep in sync with the glsl shader function get_world_lighting() */
static void studiolight_lights_eval(StudioLight *sl, float color[3], const float normal[3])
{
float R[3], I[3] = {0.0f, 0.0f, 1.0f}, N[3] = {normal[0], normal[2], -normal[1]};
const float roughness = 0.5f;
const float diffuse_color = 0.8f;
const float specular_color = brdf_approx(0.05f, roughness, N[2]);
float diff_light[3], spec_light[3];
/* Ambient lighting */
copy_v3_v3(diff_light, sl->light_ambient);
copy_v3_v3(spec_light, sl->light_ambient);
reflect_v3_v3v3(R, I, N);
for (int i = 0; i < 3; ++i) {
SolidLight *light = &sl->light[i];
if (light->flag) {
/* Diffuse lighting */
float NL = dot_v3v3(light->vec, N);
float diff = wrapped_lighting(NL, light->smooth);
madd_v3_v3fl(diff_light, light->col, diff);
/* Specular lighting */
float spec = blinn_specular(light->vec, I, N, R, NL, roughness, light->smooth);
madd_v3_v3fl(spec_light, light->spec, spec);
}
}
/* Multiply result by surface colors. */
mul_v3_fl(diff_light, diffuse_color * (1.0 - specular_color));
mul_v3_fl(spec_light, specular_color);
add_v3_v3v3(color, diff_light, spec_light);
}
static bool studiolight_load_irradiance_equirect_image(StudioLight *sl)
{
#ifdef STUDIOLIGHT_LOAD_CACHED_FILES
@ -819,12 +993,21 @@ static StudioLight *studiolight_add_file(const char *path, int flag)
{
char filename[FILE_MAXFILE];
BLI_split_file_part(path, filename, FILE_MAXFILE);
if (BLI_path_extension_check_array(filename, imb_ext_image)) {
if ((((flag & STUDIOLIGHT_TYPE_STUDIO) != 0) && BLI_path_extension_check(filename, ".sl")) ||
BLI_path_extension_check_array(filename, imb_ext_image))
{
StudioLight *sl = studiolight_create(STUDIOLIGHT_EXTERNAL_FILE | flag);
BLI_strncpy(sl->name, filename, FILE_MAXFILE);
BLI_strncpy(sl->path, path, FILE_MAXFILE);
sl->path_irr_cache = BLI_string_joinN(path, ".irr");
sl->path_sh_cache = BLI_string_joinN(path, ".sh2");
if ((flag & STUDIOLIGHT_TYPE_STUDIO) != 0) {
studiolight_load_solid_light(sl);
}
else {
sl->path_irr_cache = BLI_string_joinN(path, ".irr");
sl->path_sh_cache = BLI_string_joinN(path, ".sh2");
}
BLI_addtail(&studiolights, sl);
return sl;
}
@ -971,8 +1154,6 @@ static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool
static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl)
{
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED);
ITER_PIXELS(uint, icon_buffer, 1,
STUDIOLIGHT_ICON_SIZE,
STUDIOLIGHT_ICON_SIZE)
@ -988,7 +1169,7 @@ static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl)
SWAP(float, normal[1], normal[2]);
normal[1] = -normal[1];
studiolight_spherical_harmonics_eval(sl, color, normal);
studiolight_lights_eval(sl, color, normal);
*pixel = rgb_to_cpack(
linearrgb_to_srgb(color[0]),
@ -1005,31 +1186,34 @@ static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl)
/* API */
void BKE_studiolight_init(void)
{
StudioLight *sl;
/* go over the preset folder and add a studiolight for every image with its path */
/* order studio lights by name */
/* Also reserve icon space for it. */
/* Add default studio light */
sl = studiolight_create(STUDIOLIGHT_INTERNAL | STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED | STUDIOLIGHT_TYPE_STUDIO);
StudioLight * sl = studiolight_create(STUDIOLIGHT_INTERNAL | STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED | STUDIOLIGHT_TYPE_STUDIO);
BLI_strncpy(sl->name, "Default", FILE_MAXFILE);
int i = 0;
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 1.03271556f, 1.07163882f, 1.11193657f);
#if STUDIOLIGHT_SH_BANDS > 1
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], -0.00480952f, 0.05290511f, 0.16394117f);
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], -0.29686999f, -0.27378261f, -0.24797194f);
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 0.47932500f, 0.48242140f, 0.47190312f);
#endif
#if STUDIOLIGHT_SH_BANDS > 2
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], -0.00576984f, 0.00504886f, 0.01640534f);
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 0.15500379f, 0.15415503f, 0.16244425f);
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], -0.02483751f, -0.02245096f, -0.00536885f);
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 0.11155496f, 0.11005443f, 0.10839636f);
copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 0.01363425f, 0.01278363f, -0.00159006f);
#endif
copy_v4_fl4(sl->light_ambient, 0.025000, 0.025000, 0.025000, 1.000000);
copy_v4_fl4(sl->light[0].vec, -0.580952, 0.228571, 0.781185, 0.0);
copy_v4_fl4(sl->light[0].col, 0.900000, 0.900000, 0.900000, 1.000000);
copy_v4_fl4(sl->light[0].spec, 0.318547, 0.318547, 0.318547, 1.000000);
sl->light[0].flag = 1;
sl->light[0].smooth = 0.1;
copy_v4_fl4(sl->light[1].vec, 0.788218, 0.593482, -0.162765, 0.0);
copy_v4_fl4(sl->light[1].col, 0.267115, 0.269928, 0.358840, 1.000000);
copy_v4_fl4(sl->light[1].spec, 0.090838, 0.090838, 0.090838, 1.000000);
sl->light[1].flag = 1;
sl->light[1].smooth = 0.25;
copy_v4_fl4(sl->light[2].vec, 0.696472, -0.696472, -0.172785, 0.0);
copy_v4_fl4(sl->light[2].col, 0.293216, 0.304662, 0.401968, 1.000000);
copy_v4_fl4(sl->light[2].spec, 0.069399, 0.020331, 0.020331, 1.000000);
sl->light[2].flag = 1;
sl->light[2].smooth = 0.5;
BLI_addtail(&studiolights, sl);
/* go over the preset folder and add a studiolight for every image with its path */
/* Also reserve icon space for it. */
studiolight_add_files_from_datafolder(BLENDER_SYSTEM_DATAFILES, STUDIOLIGHT_LIGHTS_FOLDER, STUDIOLIGHT_TYPE_STUDIO);
studiolight_add_files_from_datafolder(BLENDER_USER_DATAFILES, STUDIOLIGHT_LIGHTS_FOLDER, STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_USER_DEFINED);
studiolight_add_files_from_datafolder(BLENDER_SYSTEM_DATAFILES, STUDIOLIGHT_WORLD_FOLDER, STUDIOLIGHT_TYPE_WORLD);
@ -1161,12 +1345,42 @@ void BKE_studiolight_remove(StudioLight *sl)
}
}
StudioLight *BKE_studiolight_new(const char *path, int orientation)
StudioLight *BKE_studiolight_load(const char *path, int type)
{
StudioLight *sl = studiolight_add_file(path, orientation | STUDIOLIGHT_USER_DEFINED);
StudioLight *sl = studiolight_add_file(path, type | STUDIOLIGHT_USER_DEFINED);
return sl;
}
StudioLight *BKE_studiolight_create(const char *path, const SolidLight light[4], const float light_ambient[3])
{
StudioLight *sl = studiolight_create(STUDIOLIGHT_EXTERNAL_FILE | STUDIOLIGHT_USER_DEFINED | STUDIOLIGHT_TYPE_STUDIO);
char filename[FILE_MAXFILE];
BLI_split_file_part(path, filename, FILE_MAXFILE);
BLI_snprintf(sl->path, FILE_MAXFILE, "%s%s", path, ".sl");
BLI_snprintf(sl->name, FILE_MAXFILE, "%s%s", filename, ".sl");
memcpy(sl->light, light, sizeof(*light) * 3);
memcpy(sl->light_ambient, light_ambient, sizeof(*light_ambient) * 3);
studiolight_write_solid_light(sl);
BLI_addtail(&studiolights, sl);
return sl;
}
/* Only useful for workbench while editing the userprefs. */
StudioLight *BKE_studiolight_studio_edit_get(void)
{
static StudioLight sl = {0};
sl.flag = STUDIOLIGHT_TYPE_STUDIO;
memcpy(sl.light, U.light, sizeof(*sl.light) * 3);
memcpy(sl.light_ambient, U.light_ambient, sizeof(sl.light_ambient) * 3);
return &sl;
}
void BKE_studiolight_refresh(void)
{
BKE_studiolight_free();

View File

@ -2467,5 +2467,18 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
/* Move studio_light selection to lookdev_light. */
if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "char", "lookdev_light[256]")) {
for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
memcpy(v3d->shading.lookdev_light, v3d->shading.studio_light, sizeof(char) * 256);
}
}
}
}
}
}
}

View File

@ -62,7 +62,7 @@ void EEVEE_lookdev_cache_init(
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
if (LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d)) {
StudioLight *sl = BKE_studiolight_find(v3d->shading.studio_light, STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
StudioLight *sl = BKE_studiolight_find(v3d->shading.lookdev_light, STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
if (sl && (sl->flag & STUDIOLIGHT_TYPE_WORLD)) {
GPUShader *shader = EEVEE_shaders_default_studiolight_sh_get();
struct GPUBatch *geom = DRW_cache_fullscreen_quad_get();

View File

@ -40,13 +40,13 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd)
}
else {
wpd->studio_light = BKE_studiolight_find(
wpd->shading.studio_light, STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_TYPE_WORLD);
wpd->shading.studio_light, STUDIOLIGHT_TYPE_STUDIO);
}
/* If matcaps are missing, use this as fallback. */
if (UNLIKELY(wpd->studio_light == NULL)) {
wpd->studio_light = BKE_studiolight_find(
wpd->shading.studio_light, STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_TYPE_WORLD);
wpd->shading.studio_light, STUDIOLIGHT_TYPE_STUDIO);
}
wpd->shadow_multiplier = 1.0 - wpd->shading.shadow_intensity;
@ -79,7 +79,7 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd)
zero_v3(wd->background_color_high);
}
studiolight_update_world(wpd->studio_light, wd);
studiolight_update_world(wpd, wpd->studio_light, wd);
copy_v3_v3(wd->object_outline_color, wpd->shading.object_outline_color);
wd->object_outline_color[3] = 1.0f;
@ -157,9 +157,8 @@ void workbench_private_data_get_light_direction(WORKBENCH_PrivateData *wpd, floa
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
WORKBENCH_UBO_World *wd = &wpd->world_data;
float view_matrix[4][4], view_inv[4][4], rot_matrix[4][4];
float view_matrix[4][4];
DRW_viewport_matrix_get(view_matrix, DRW_MAT_VIEW);
DRW_viewport_matrix_get(view_inv, DRW_MAT_VIEWINV);
copy_v3_v3(r_light_direction, scene->display.light_direction);
negate_v3(r_light_direction);
@ -167,46 +166,6 @@ void workbench_private_data_get_light_direction(WORKBENCH_PrivateData *wpd, floa
/* Shadow direction. */
mul_v3_mat3_m4v3(wd->shadow_direction_vs, view_matrix, r_light_direction);
/* TODO enable when we support studiolight presets. */
if (STUDIOLIGHT_TYPE_WORLD_ENABLED(wpd) && false) {
axis_angle_to_mat4_single(rot_matrix, 'Y', -wpd->shading.studiolight_rot_z);
mul_m4_m4m4(rot_matrix, rot_matrix, view_matrix);
swap_v3_v3(rot_matrix[2], rot_matrix[1]);
negate_v3(rot_matrix[2]);
}
else {
unit_m4(rot_matrix);
}
/* Studio Lights. */
for (int i = 0; i < 4; i++) {
WORKBENCH_UBO_Light *light = &wd->lights[i];
/* TODO use 4 lights in studiolights prefs. */
if (i > 2) {
copy_v3_fl3(light->light_direction, 1.0f, 0.0f, 0.0f);
copy_v3_fl(light->specular_color, 0.0f);
copy_v3_fl(light->diffuse_color, 0.0f);
continue;
}
SolidLight *sl = &U.light[i];
if (sl->flag) {
copy_v3_v3(light->light_direction, sl->vec);
mul_mat3_m4_v3(rot_matrix, light->light_direction);
/* We should predivide the power by PI but that makes the lights really dim. */
copy_v3_v3(light->specular_color, sl->spec);
copy_v3_v3(light->diffuse_color, sl->col);
light->wrapped = sl->smooth;
}
else {
copy_v3_fl3(light->light_direction, 1.0f, 0.0f, 0.0f);
copy_v3_fl(light->specular_color, 0.0f);
copy_v3_fl(light->diffuse_color, 0.0f);
}
}
copy_v4_v4(wd->ambient_color, U.light_ambient);
DRW_uniformbuffer_update(wpd->world_ubo, wd);
}

View File

@ -299,7 +299,7 @@ void workbench_material_shgroup_uniform(
void workbench_material_copy(WORKBENCH_MaterialData *dest_material, const WORKBENCH_MaterialData *source_material);
/* workbench_studiolight.c */
void studiolight_update_world(StudioLight *sl, WORKBENCH_UBO_World *wd);
void studiolight_update_world(WORKBENCH_PrivateData *wpd, StudioLight *sl, WORKBENCH_UBO_World *wd);
void studiolight_update_light(WORKBENCH_PrivateData *wpd, const float light_direction[3]);
bool studiolight_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed);
float studiolight_object_shadow_distance(WORKBENCH_PrivateData *wpd, Object *ob, WORKBENCH_ObjectData *oed);

View File

@ -32,8 +32,56 @@
#include "BLI_math.h"
#include "BKE_global.h"
void studiolight_update_world(StudioLight *sl, WORKBENCH_UBO_World *wd)
void studiolight_update_world(WORKBENCH_PrivateData *wpd, StudioLight *studiolight, WORKBENCH_UBO_World *wd)
{
float view_matrix[4][4], rot_matrix[4][4];
DRW_viewport_matrix_get(view_matrix, DRW_MAT_VIEW);
/* TODO enable when we support studiolight presets. */
if (STUDIOLIGHT_TYPE_WORLD_ENABLED(wpd) && false) {
axis_angle_to_mat4_single(rot_matrix, 'Y', -wpd->shading.studiolight_rot_z);
mul_m4_m4m4(rot_matrix, rot_matrix, view_matrix);
swap_v3_v3(rot_matrix[2], rot_matrix[1]);
negate_v3(rot_matrix[2]);
}
else {
unit_m4(rot_matrix);
}
if (U.edit_solid_light) {
studiolight = BKE_studiolight_studio_edit_get();
}
/* Studio Lights. */
for (int i = 0; i < 4; i++) {
WORKBENCH_UBO_Light *light = &wd->lights[i];
/* TODO use 4 lights in studiolights prefs. */
if (i > 2) {
copy_v3_fl3(light->light_direction, 1.0f, 0.0f, 0.0f);
copy_v3_fl(light->specular_color, 0.0f);
copy_v3_fl(light->diffuse_color, 0.0f);
continue;
}
SolidLight *sl = &studiolight->light[i];
if (sl->flag) {
copy_v3_v3(light->light_direction, sl->vec);
mul_mat3_m4_v3(rot_matrix, light->light_direction);
/* We should predivide the power by PI but that makes the lights really dim. */
copy_v3_v3(light->specular_color, sl->spec);
copy_v3_v3(light->diffuse_color, sl->col);
light->wrapped = sl->smooth;
}
else {
copy_v3_fl3(light->light_direction, 1.0f, 0.0f, 0.0f);
copy_v3_fl(light->specular_color, 0.0f);
copy_v3_fl(light->diffuse_color, 0.0f);
}
}
copy_v3_v3(wd->ambient_color, studiolight->light_ambient);
#if 0
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED);
#if STUDIOLIGHT_SH_BANDS == 2
@ -87,6 +135,7 @@ void studiolight_update_world(StudioLight *sl, WORKBENCH_UBO_World *wd)
copy_v3_v3(wd->spherical_harmonics_coefs[i], sl->spherical_harmonics_coefs[i]);
}
#endif
#endif
}
static void compute_parallel_lines_nor_and_dist(const float v1[2], const float v2[2], const float v3[2], float r_line[2])

View File

@ -589,7 +589,8 @@ typedef struct UserDef {
struct SolidLight light[3];
float light_ambient[3], pad7;
short gizmo_flag, gizmo_size;
short pad6[3];
short edit_solid_light;
short pad6[2];
short textimeout, texcollectrate;
short dragthreshold;
int memcachelimit;

View File

@ -148,6 +148,7 @@ typedef struct View3DShading {
char pad[7];
char studio_light[256]; /* FILE_MAXFILE */
char lookdev_light[256]; /* FILE_MAXFILE */
char matcap[256]; /* FILE_MAXFILE */
float shadow_intensity;

View File

@ -754,9 +754,12 @@ static PointerRNA rna_View3DShading_selected_studio_light_get(PointerRNA *ptr)
if (shading->type == OB_SOLID && shading->light == V3D_LIGHTING_MATCAP) {
sl = BKE_studiolight_find(shading->matcap, STUDIOLIGHT_FLAG_ALL);
}
else {
else if (shading->type == OB_SOLID && shading->light == V3D_LIGHTING_STUDIO) {
sl = BKE_studiolight_find(shading->studio_light, STUDIOLIGHT_FLAG_ALL);
}
else {
sl = BKE_studiolight_find(shading->lookdev_light, STUDIOLIGHT_FLAG_ALL);
}
return rna_pointer_inherit_refine(ptr, &RNA_StudioLight, sl);
}
@ -805,13 +808,14 @@ static int rna_View3DShading_studio_light_get(PointerRNA *ptr)
View3DShading *shading = (View3DShading *)ptr->data;
char *dna_storage = shading->studio_light;
int flag = STUDIOLIGHT_ORIENTATIONS_SOLID;
int flag = STUDIOLIGHT_TYPE_STUDIO;
if (shading->type == OB_SOLID && shading->light == V3D_LIGHTING_MATCAP) {
flag = STUDIOLIGHT_TYPE_MATCAP;
dna_storage = shading->matcap;
}
else if (shading->type == OB_MATERIAL) {
flag = STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE;
flag = STUDIOLIGHT_TYPE_WORLD;
dna_storage = shading->lookdev_light;
}
StudioLight *sl = BKE_studiolight_find(dna_storage, flag);
if (sl) {
@ -828,13 +832,14 @@ static void rna_View3DShading_studio_light_set(PointerRNA *ptr, int value)
View3DShading *shading = (View3DShading *)ptr->data;
char *dna_storage = shading->studio_light;
int flag = STUDIOLIGHT_ORIENTATIONS_SOLID;
int flag = STUDIOLIGHT_TYPE_STUDIO;
if (shading->type == OB_SOLID && shading->light == V3D_LIGHTING_MATCAP) {
flag = STUDIOLIGHT_TYPE_MATCAP;
dna_storage = shading->matcap;
}
else if (shading->type == OB_MATERIAL) {
flag = STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE;
flag = STUDIOLIGHT_TYPE_WORLD;
dna_storage = shading->lookdev_light;
}
StudioLight *sl = BKE_studiolight_findindex(value, flag);
if (sl) {
@ -876,8 +881,7 @@ static const EnumPropertyItem *rna_View3DShading_studio_light_itemf(
switch (shading->type) {
case OB_SOLID:
case OB_TEXTURE:
show_studiolight = (
(sl->flag & (STUDIOLIGHT_TYPE_WORLD | STUDIOLIGHT_TYPE_STUDIO)) != 0);
show_studiolight = ((sl->flag & STUDIOLIGHT_TYPE_STUDIO) != 0);
break;
case OB_MATERIAL:

View File

@ -628,9 +628,15 @@ static void rna_StudioLights_remove(UserDef *UNUSED(userdef), StudioLight *studi
BKE_studiolight_remove(studio_light);
}
static StudioLight *rna_StudioLights_new(UserDef *UNUSED(userdef), const char *path, int type)
static StudioLight *rna_StudioLights_load(UserDef *UNUSED(userdef), const char *path, int type)
{
return BKE_studiolight_new(path, type);
return BKE_studiolight_load(path, type);
}
/* TODO: Make it accept arguments. */
static StudioLight *rna_StudioLights_new(UserDef *userdef, const char *name)
{
return BKE_studiolight_create(name, userdef->light, userdef->light_ambient);
}
/* StudioLight.name */
@ -3315,8 +3321,8 @@ static void rna_def_userdef_studiolights(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "UserDef");
RNA_def_struct_ui_text(srna, "Studio Lights", "Collection of studio lights");
func = RNA_def_function(srna, "new", "rna_StudioLights_new");
RNA_def_function_ui_description(func, "Create a new studiolight");
func = RNA_def_function(srna, "load", "rna_StudioLights_load");
RNA_def_function_ui_description(func, "Load studiolight from file");
parm = RNA_def_string(func, "path", NULL, 0, "File Path", "File path where the studio light file can be found");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_enum(func, "type", rna_enum_studio_light_type_items, STUDIOLIGHT_TYPE_WORLD, "Type", "The type for the new studio light");
@ -3324,6 +3330,13 @@ static void rna_def_userdef_studiolights(BlenderRNA *brna)
parm = RNA_def_pointer(func, "studio_light", "StudioLight", "", "Newly created StudioLight");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "new", "rna_StudioLights_new");
RNA_def_function_ui_description(func, "Create studiolight from default lighting");
parm = RNA_def_string(func, "path", NULL, 0, "Path", "Path to the file that will contain the lighing info (without extension)");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_pointer(func, "studio_light", "StudioLight", "", "Newly created StudioLight");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_StudioLights_remove");
RNA_def_function_ui_description(func, "Remove a studio light");
parm = RNA_def_pointer(func, "studio_light", "StudioLight", "", "The studio light to remove");
@ -4297,6 +4310,12 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Ambient Color", "Color of the ambient light that uniformly lit the scene");
RNA_def_property_update(prop, 0, "rna_UserDef_viewport_lights_update");
prop = RNA_def_property(srna, "edit_solid_light", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "edit_solid_light", 1);
RNA_def_property_ui_text(prop, "Edit Solid Light",
"View the result of the solid lights in the viewport");
RNA_def_property_update(prop, 0, "rna_UserDef_viewport_lights_update");
prop = RNA_def_property(srna, "use_weight_color_range", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_CUSTOM_RANGE);
RNA_def_property_ui_text(prop, "Use Weight Color Range",