From d3c4248946f65d287f18b3d6488cd450dfb6d81d Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 11 May 2018 16:55:14 +0200 Subject: Workbench: StudioLight HDRI's The Studio lights are now loaded from disk. The location is `datafiles/studiolights` they need to be JPG for now. JPG cannot store HDRI color range but they are clamped inside the Workbench engine for speed reason. I didn't select JP2K as it might not be enabled. Users can add upto 20 HDRI files. This limitation is inside the RNA_space.c Currently the icons are calculated when you first open the selection box for the HDRI's. We could add them to a background rendering later. I added 2 test files a sky texture rendered in Cycles and an HDRI from cloud.blender.org. --- source/blender/blenkernel/BKE_icons.h | 4 + source/blender/blenkernel/BKE_studiolight.h | 73 ++++++ source/blender/blenkernel/CMakeLists.txt | 2 + source/blender/blenkernel/intern/icons.c | 11 + source/blender/blenkernel/intern/studiolight.c | 297 +++++++++++++++++++++++++ 5 files changed, 387 insertions(+) create mode 100644 source/blender/blenkernel/BKE_studiolight.h create mode 100644 source/blender/blenkernel/intern/studiolight.c (limited to 'source/blender/blenkernel') diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index 766704b8f9c..e54531bdb0e 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -43,6 +43,7 @@ enum { ICON_DATA_PREVIEW, /** 2D triangles: obj is #Icon_Geom */ ICON_DATA_GEOM, + ICON_DATA_STUDIOLIGHT, }; struct Icon { @@ -77,6 +78,7 @@ typedef struct Icon Icon; struct ImBuf; struct PreviewImage; struct ID; +struct StudioLight; enum eIconSizes; @@ -158,6 +160,8 @@ struct ImBuf *BKE_icon_geom_rasterize( const struct Icon_Geom *geom, const unsigned int size_x, const unsigned int size_y); +int BKE_icon_ensure_studio_light(struct StudioLight *sl); + #define ICON_RENDER_DEFAULT_HEIGHT 32 #endif /* __BKE_ICONS_H__ */ diff --git a/source/blender/blenkernel/BKE_studiolight.h b/source/blender/blenkernel/BKE_studiolight.h new file mode 100644 index 00000000000..c65e9050157 --- /dev/null +++ b/source/blender/blenkernel/BKE_studiolight.h @@ -0,0 +1,73 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * The Original Code is Copyright (C) 2006-2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_STUDIOLIGHT_H__ +#define __BKE_STUDIOLIGHT_H__ + +/** \file BKE_studiolight.h + * \ingroup bke + * + * Studio lighting for the 3dview + */ + +#include "BLI_sys_types.h" + +#include "DNA_space_types.h" + +#define STUDIOLIGHT_X_POS 0 +#define STUDIOLIGHT_X_NEG 1 +#define STUDIOLIGHT_Y_POS 2 +#define STUDIOLIGHT_Y_NEG 3 +#define STUDIOLIGHT_Z_POS 4 +#define STUDIOLIGHT_Z_NEG 5 + +enum StudioLightFlag +{ + STUDIOLIGHT_DIFFUSE_LIGHT_CALCULATED = (1 << 0), + STUDIOLIGHT_EXTERNAL_FILE = (1 << 1), +} StudioLightFlag; + +typedef struct StudioLight +{ + struct StudioLight *next, *prev; + int flag; + char name[FILE_MAXFILE]; + char path[FILE_MAX]; + int icon_id; + int index; + float diffuse_light[6][3]; +} StudioLight; + +void BKE_studiolight_init(void); +void BKE_studiolight_free(void); +struct StudioLight *BKE_studiolight_find(const char* name); +struct StudioLight *BKE_studiolight_findindex(int index); +unsigned int *BKE_studiolight_preview(StudioLight *sl, int icon_size); +const struct ListBase *BKE_studiolight_listbase(void); +void BKE_studiolight_ensure_flag(StudioLight *sl, int flag); + +#endif /* __BKE_STUDIOLIGHT_H__ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 37e08cedb10..d5ce6f2f94d 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -185,6 +185,7 @@ set(SRC intern/softbody.c intern/sound.c intern/speaker.c + intern/studiolight.c intern/subsurf_ccg.c intern/suggestions.c intern/text.c @@ -303,6 +304,7 @@ set(SRC BKE_softbody.h BKE_sound.h BKE_speaker.h + BKE_studiolight.h BKE_subsurf.h BKE_suggestions.h BKE_text.h diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index f1f343faac8..61c05c2500d 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -56,6 +56,7 @@ #include "BKE_icons.h" #include "BKE_global.h" /* only for G.background test */ +#include "BKE_studiolight.h" #include "BLI_sys_types.h" // for intptr_t support @@ -802,3 +803,13 @@ struct Icon_Geom *BKE_icon_geom_from_file(const char *filename) } /** \} */ + +/** \name Studio Light Icon + * \{ */ +int BKE_icon_ensure_studio_light(struct StudioLight *sl) { + int icon_id = get_next_free_id(); + icon_create(icon_id, ICON_DATA_STUDIOLIGHT, sl); + return icon_id; +} +/** \} */ + diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c new file mode 100644 index 00000000000..9bc678a1820 --- /dev/null +++ b/source/blender/blenkernel/intern/studiolight.c @@ -0,0 +1,297 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * The Original Code is Copyright (C) 2006-2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/blenkernel/intern/studiolight.c + * \ingroup bke + */ + +#include "BKE_studiolight.h" + +#include "BKE_appdir.h" +#include "BKE_icons.h" + +#include "BLI_fileops.h" +#include "BLI_fileops_types.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "DNA_listBase.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "MEM_guardedalloc.h" + + +/* Statics */ +static ListBase studiolights; +static const char *STUDIO_LIGHT_FOLDER = "studiolights/"; + +/* FUNCTIONS */ +static void studiolight_free(struct StudioLight *sl) +{ + MEM_freeN(sl); +} + +static struct StudioLight *studiolight_create(void) +{ + struct StudioLight *sl = MEM_callocN(sizeof(*sl), __func__); + sl->path[0] = 0x00; + sl->name[0] = 0x00; + sl->flag = 0; + sl->index = BLI_listbase_count(&studiolights); + sl->icon_id = BKE_icon_ensure_studio_light(sl); + return sl; +} + +static void direction_to_equirectangular(float r[2], const float dir[3]) +{ + r[0] = (atan2f(dir[1], dir[0]) - M_PI) / -(M_PI * 2); + r[1] = (acosf(dir[2] / 1.0) - M_PI) / -M_PI; +} + +static void studiolight_calculate_directional_diffuse_light(ImBuf *ibuf, float color[4], const float start[3], const float v1[3], const float v2[3], int steps) +{ + float uv[2]; + float dir[3]; + float col[4]; + float v11[3]; + float v12[3]; + float totcol[4]; + + zero_v4(totcol); + for (int x = 0; x < steps ; x++) { + float xf = (float)x / (float)steps; + mul_v3_v3fl(v11, v1, xf); + for (int y = 0; y < steps; y++) { + float yf = (float)y / (float)steps; + /* start + x/steps*v1 + y/steps*v2 */ + mul_v3_v3fl(v12, v2, yf); + add_v3_v3v3(dir, start, v11); + add_v3_v3(dir, v12); + /* normalize */ + normalize_v3(dir); + + /* sample */ + direction_to_equirectangular(uv, dir); + nearest_interpolation_color_wrap(ibuf, NULL, col, uv[0] * ibuf->x, uv[1] * ibuf->y); + add_v3_v3(totcol, col); + } + } + mul_v3_v3fl(color, totcol, 1.0/(steps*steps)); +} + +static void studiolight_calculate_diffuse_light(StudioLight *sl) +{ + const int steps = 16; + float start[3]; + float v1[3]; + float v2[3]; + + /* init light to black */ + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_X_POS], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_X_NEG], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_Y_POS], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_Y_NEG], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_Z_POS], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_Z_NEG], 0.0f); + + if (sl->flag &= STUDIOLIGHT_EXTERNAL_FILE) { + ImBuf* ibuf = NULL; + ibuf = IMB_loadiffname(sl->path, 0, NULL); + if (ibuf) { + IMB_float_from_rect(ibuf); + + copy_v3_fl3(start, -1.0f, -1.0f, -1.0f); + copy_v3_fl3(v1, 0.0f, 2.0f, 0.0f); + copy_v3_fl3(v2, 0.0f, 0.0f, 2.0f); + studiolight_calculate_directional_diffuse_light(ibuf, sl->diffuse_light[STUDIOLIGHT_Z_NEG], start, v1, v2, steps); + copy_v3_fl3(start, 1.0f, -1.0f, -1.0f); + studiolight_calculate_directional_diffuse_light(ibuf, sl->diffuse_light[STUDIOLIGHT_Z_POS], start, v1, v2, steps); + + copy_v3_fl3(start, -1.0f, -1.0f, -1.0f); + copy_v3_fl3(v1, 2.0f, 0.0f, 0.0f); + copy_v3_fl3(v2, 0.0f, 0.0f, 2.0f); + studiolight_calculate_directional_diffuse_light(ibuf, sl->diffuse_light[STUDIOLIGHT_X_POS], start, v1, v2, steps); + copy_v3_fl3(start, -1.0f, 1.0f, -1.0f); + studiolight_calculate_directional_diffuse_light(ibuf, sl->diffuse_light[STUDIOLIGHT_X_NEG], start, v1, v2, steps); + + copy_v3_fl3(start, -1.0f, -1.0f, -1.0f); + copy_v3_fl3(v1, 2.0f, 0.0f, 0.0f); + copy_v3_fl3(v2, 0.0f, 2.0f, 0.0f); + studiolight_calculate_directional_diffuse_light(ibuf, sl->diffuse_light[STUDIOLIGHT_Y_NEG], start, v1, v2, steps); + copy_v3_fl3(start, -1.0f, -1.0f, 1.0f); + studiolight_calculate_directional_diffuse_light(ibuf, sl->diffuse_light[STUDIOLIGHT_Y_POS], start, v1, v2, steps); + + IMB_freeImBuf(ibuf); + } + } + sl->flag |= STUDIOLIGHT_DIFFUSE_LIGHT_CALCULATED; +} + + +/* 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(); + BLI_strncpy(sl->name, "INTERNAL_01\0", FILE_MAXFILE); + sl->flag = STUDIOLIGHT_DIFFUSE_LIGHT_CALCULATED; + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_X_POS], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_X_NEG], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_Y_POS], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_Y_NEG], 0.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_Z_POS], 1.0f); + copy_v3_fl(sl->diffuse_light[STUDIOLIGHT_Z_NEG], 0.0f); + BLI_addtail(&studiolights, sl); + + struct direntry *dir; + const char *folder = BKE_appdir_folder_id(BLENDER_DATAFILES, STUDIO_LIGHT_FOLDER); + unsigned int totfile = BLI_filelist_dir_contents(folder, &dir); + int i; + for (i = 0; i < totfile; i++) { + if ((dir[i].type & S_IFREG)) { + const char *filename = dir[i].relname; + const char *path = dir[i].path; + if (BLI_testextensie(filename, ".jpg")) { + sl = studiolight_create(); + sl->flag = STUDIOLIGHT_EXTERNAL_FILE; + BLI_strncpy(sl->name, filename, FILE_MAXFILE); + BLI_strncpy(sl->path, path, FILE_MAXFILE); + BLI_addtail(&studiolights, sl); + } + } + } + BLI_filelist_free(dir, totfile); + dir = NULL; +} + +void BKE_studiolight_free(void) +{ + struct StudioLight *sl; + while((sl = (StudioLight*)BLI_pophead(&studiolights))) + { + studiolight_free(sl); + } +} + +struct StudioLight *BKE_studiolight_find(const char* name) +{ + LISTBASE_FOREACH(StudioLight *, sl, &studiolights) { + if (STREQLEN(sl->name, name, FILE_MAXFILE)) { + return sl; + } + } + /* When not found, use the default studio light */ + return (StudioLight*)studiolights.first; +} + +struct StudioLight *BKE_studiolight_findindex(int index) +{ + LISTBASE_FOREACH(StudioLight *, sl, &studiolights) { + if (sl->index == index) { + return sl; + } + } + /* When not found, use the default studio light */ + return (StudioLight*)studiolights.first; +} + +const struct ListBase *BKE_studiolight_listbase(void) +{ + return &studiolights; +} + +unsigned int *BKE_studiolight_preview(StudioLight *sl, int icon_size) +{ + BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_DIFFUSE_LIGHT_CALCULATED); + + uint *rect = MEM_mallocN(icon_size * icon_size * sizeof(uint), __func__); + int icon_center = icon_size / 2; + float sphere_radius = icon_center * 0.9; + + int offset = 0; + for (int y = 0; y < icon_size; y++) { + float dy = y - icon_center; + for (int x = 0; x < icon_size; x++) { + float dx = x - icon_center; + /* calculate aliasing */ + float alias = 0; + const float alias_step = 0.333; + for (float ay = dy - 0.5; ay < dy + 0.5; ay += alias_step) { + for (float ax = dx - 0.5; ax < dx + 0.5; ax += alias_step) { + if (sqrt(ay * ay + ax * ax) < sphere_radius) { + alias += alias_step * alias_step; + } + } + } + uint pixelresult = 0x0; + uint alias_i = clamp_i(alias * 256, 0, 255); + if (alias_i != 0) { + /* calculate normal */ + uint alias_mask = alias_i << 24; + float normal[3]; + normal[0] = dx / sphere_radius; + normal[1] = dy / sphere_radius; + normal[2] = sqrt(-(normal[0] * normal[0]) - (normal[1] * normal[1]) + 1); + normalize_v3(normal); + + float color[3]; + mul_v3_v3fl(color, sl->diffuse_light[STUDIOLIGHT_X_POS], clamp_f(normal[0], 0.0, 1.0)); + interp_v3_v3v3(color, color, sl->diffuse_light[STUDIOLIGHT_X_NEG], clamp_f(-normal[0], 0.0, 1.0)); + interp_v3_v3v3(color, color, sl->diffuse_light[STUDIOLIGHT_Y_POS], clamp_f(normal[1], 0.0, 1.0)); + interp_v3_v3v3(color, color, sl->diffuse_light[STUDIOLIGHT_Y_NEG], clamp_f(-normal[1], 0.0, 1.0)); + interp_v3_v3v3(color, color, sl->diffuse_light[STUDIOLIGHT_Z_POS], clamp_f(normal[2], 0.0, 1.0)); + + pixelresult = rgb_to_cpack( + linearrgb_to_srgb(color[0]), + linearrgb_to_srgb(color[1]), + linearrgb_to_srgb(color[2])) | alias_mask; + } + rect[offset++] = pixelresult; + } + } + return rect; +} + +void BKE_studiolight_ensure_flag(StudioLight *sl, int flag) +{ + if (sl->flag & flag){ + return; + } + + if ((flag & STUDIOLIGHT_DIFFUSE_LIGHT_CALCULATED)) { + studiolight_calculate_diffuse_light(sl); + } +} -- cgit v1.2.3