Workbench: Add back studio lighting presets
authorClément Foucault <foucault.clem@gmail.com>
Thu, 29 Nov 2018 18:54:23 +0000 (19:54 +0100)
committerClément Foucault <foucault.clem@gmail.com>
Thu, 29 Nov 2018 20:52:36 +0000 (21:52 +0100)
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

14 files changed:
release/scripts/startup/bl_operators/wm.py
release/scripts/startup/bl_ui/space_userpref.py
release/scripts/startup/bl_ui/space_view3d.py
source/blender/blenkernel/BKE_studiolight.h
source/blender/blenkernel/intern/studiolight.c
source/blender/blenloader/intern/versioning_280.c
source/blender/draw/engines/eevee/eevee_lookdev.c
source/blender/draw/engines/workbench/workbench_data.c
source/blender/draw/engines/workbench/workbench_private.h
source/blender/draw/engines/workbench/workbench_studiolight.c
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesdna/DNA_view3d_types.h
source/blender/makesrna/intern/rna_space.c
source/blender/makesrna/intern/rna_userdef.c

index 4206adcfb911329f3392ab7d700e835d8784490b..65ac0cb745b3e51fd7cc0c0046aba1f483cd1d63 100644 (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,
index 72b07d55f70392bc8cb0cf29463c8ce54d94279c..261eccc7c557ecfedb335804e180a6d2d783c85b 100644 (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,
index 0ba3b2217f2c7322f25ad2071279f1b1318f42e4..bc38214f10180518d911217517b29d49a65e7104 100644 (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")
index 6616af355ac78180898086e6a1eeb91b33882082..25931e2bbf54426649409800bb7732a8b4bdc790 100644 (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);
index 937c24c47e6cbf2cb71ef1f6e565ee3e373d7568..10d9dcfbfece4ec3af3ada169e818fb1686eb02f 100644 (file)
 #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();
index b2d9a385c6fde95d85820af277b071432554e2c9..5559982d820bf30cbf2d358f3c14c12849206005 100644 (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);
+                                               }
+                                       }
+                               }
+                       }
+               }
        }
 }
index 298a67b811bc406567a0b3625af740d3adf77f03..aa2f480597b0186a44f659c9ce8cd6e6acd23cbe 100644 (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();
index fd72848da493b55b172c49b07e633073abf31ffe..1cdf4c2b443fa6967853e063dca0c8c7bbd3f6e4 100644 (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);
 }
 
index 4ab0d39aa5590465ac7af3beb2015decad727fc6..b1f3a589d056bacce7e8d0a29daba6159856cbed 100644 (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);
index 901260d06609573ab0483554075c75b6507be256..183b285c69c35b314810e7a33c989883718b048a 100644 (file)
 #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])
index 5bb3523a458e0bd691adbad883c1c0e459643c87..e0bed7216e280ae6e1b82b3d00e631ea4232982d 100644 (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;
index b06f94db0647ab2051079aba8bca8240102f4f28..5c31f37744713380f514470fcd1b4d35324b0a28 100644 (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;
index fd6e7436351fcbd6fd02450fab83dc0645fc45bf..74fb09f808821e70f22115479ccf20aea71cf383 100644 (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:
index 89f777625a0e6072f811425bfce34a9f2ccde8fe..af6150704e0976a737514a85044beb591fdb7f97 100644 (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",