Blender V5.0
studiolight.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006-2007 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_studiolight.h"
10
11#include "BKE_appdir.hh"
12#include "BKE_icons.h"
13
14#include "BLI_dynstr.h"
15#include "BLI_fileops.h"
16#include "BLI_fileops_types.h"
17#include "BLI_linklist.h"
18#include "BLI_listbase.h"
19#include "BLI_math_color.h"
20#include "BLI_math_vector.h"
21#include "BLI_path_utils.hh"
22#include "BLI_string.h"
23
24#include "DNA_listBase.h"
25
26#include "IMB_imbuf.hh"
27#include "IMB_interp.hh"
28#include "IMB_openexr.hh"
29
30#include "GPU_texture.hh"
31
32#include "MEM_guardedalloc.h"
33
34#include <cstring>
35
36using blender::float4;
37
38/* Statics */
40static int last_studiolight_id = 0;
41#define STUDIOLIGHT_PASSNAME_DIFFUSE "diffuse"
42#define STUDIOLIGHT_PASSNAME_SPECULAR "specular"
43
44static const char *STUDIOLIGHT_LIGHTS_FOLDER = "studiolights" SEP_STR "studio" SEP_STR;
45static const char *STUDIOLIGHT_WORLD_FOLDER = "studiolights" SEP_STR "world" SEP_STR;
46static const char *STUDIOLIGHT_MATCAP_FOLDER = "studiolights" SEP_STR "matcap" SEP_STR;
47
48static const char *STUDIOLIGHT_WORLD_DEFAULT = "forest.exr";
49static const char *STUDIOLIGHT_MATCAP_DEFAULT = "basic_1.exr";
50
51/* ITER MACRO */
52
66#define ITER_PIXELS(type, src, channels, width, height) \
67 { \
68 float texel_size[2]; \
69 texel_size[0] = 1.0f / width; \
70 texel_size[1] = 1.0f / height; \
71 type(*pixel_)[channels] = (type(*)[channels])src; \
72 for (float y = 0.5 * texel_size[1]; y < 1.0; y += texel_size[1]) { \
73 for (float x = 0.5 * texel_size[0]; x < 1.0; x += texel_size[0], pixel_++) { \
74 type *pixel = *pixel_;
75
76#define ITER_PIXELS_END \
77 } \
78 } \
79 } \
80 ((void)0)
81
82/* FUNCTIONS */
83#define IMB_SAFE_FREE(p) \
84 do { \
85 if (p) { \
86 IMB_freeImBuf(p); \
87 p = nullptr; \
88 } \
89 } while (0)
90
91#define GPU_TEXTURE_SAFE_FREE(p) \
92 do { \
93 if (p) { \
94 GPU_texture_free(p); \
95 p = nullptr; \
96 } \
97 } while (0)
98
106
108{
109#define STUDIOLIGHT_DELETE_ICON(s) \
110 do { \
111 if (s != 0) { \
112 BKE_icon_delete(s); \
113 s = 0; \
114 } \
115 } while (0)
116
117 if (sl->free_function) {
119 }
124#undef STUDIOLIGHT_DELETE_ICON
125
127
131 MEM_SAFE_FREE(sl);
132}
133
140{
141 const bool is_used_in_viewport = bool(sl->flag & (STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE |
144 if (is_used_in_viewport) {
145 return;
146 }
148}
149
172
173#define STUDIOLIGHT_FILE_VERSION 1
174
175#define READ_VAL(type, parser, id, val, lines) \
176 do { \
177 for (LinkNode *line = lines; line; line = line->next) { \
178 char *val_str, *str = static_cast<char *>(line->link); \
179 if ((val_str = strstr(str, id " "))) { \
180 val_str += sizeof(id); /* Skip id + spacer. */ \
181 val = parser(val_str); \
182 } \
183 } \
184 } while (0)
185
186#define READ_FVAL(id, val, lines) READ_VAL(float, atof, id, val, lines)
187#define READ_IVAL(id, val, lines) READ_VAL(int, atoi, id, val, lines)
188
189#define READ_VEC3(id, val, lines) \
190 do { \
191 READ_FVAL(id ".x", val[0], lines); \
192 READ_FVAL(id ".y", val[1], lines); \
193 READ_FVAL(id ".z", val[2], lines); \
194 } while (0)
195
196#define READ_SOLIDLIGHT(sl, i, lines) \
197 do { \
198 READ_IVAL("light[" STRINGIFY(i) "].flag", sl[i].flag, lines); \
199 READ_FVAL("light[" STRINGIFY(i) "].smooth", sl[i].smooth, lines); \
200 READ_VEC3("light[" STRINGIFY(i) "].col", sl[i].col, lines); \
201 READ_VEC3("light[" STRINGIFY(i) "].spec", sl[i].spec, lines); \
202 READ_VEC3("light[" STRINGIFY(i) "].vec", sl[i].vec, lines); \
203 } while (0)
204
206{
208 if (lines) {
209 READ_VEC3("light_ambient", sl->light_ambient, lines);
210 READ_SOLIDLIGHT(sl->light, 0, lines);
211 READ_SOLIDLIGHT(sl->light, 1, lines);
212 READ_SOLIDLIGHT(sl->light, 2, lines);
213 READ_SOLIDLIGHT(sl->light, 3, lines);
214 }
215 BLI_file_free_lines(lines);
216}
217
218#undef READ_SOLIDLIGHT
219#undef READ_VEC3
220#undef READ_IVAL
221#undef READ_FVAL
222
223#define WRITE_FVAL(str, id, val) BLI_dynstr_appendf(str, id " %f\n", val)
224#define WRITE_IVAL(str, id, val) BLI_dynstr_appendf(str, id " %d\n", val)
225
226#define WRITE_VEC3(str, id, val) \
227 do { \
228 WRITE_FVAL(str, id ".x", val[0]); \
229 WRITE_FVAL(str, id ".y", val[1]); \
230 WRITE_FVAL(str, id ".z", val[2]); \
231 } while (0)
232
233#define WRITE_SOLIDLIGHT(str, sl, i) \
234 do { \
235 WRITE_IVAL(str, "light[" STRINGIFY(i) "].flag", sl[i].flag); \
236 WRITE_FVAL(str, "light[" STRINGIFY(i) "].smooth", sl[i].smooth); \
237 WRITE_VEC3(str, "light[" STRINGIFY(i) "].col", sl[i].col); \
238 WRITE_VEC3(str, "light[" STRINGIFY(i) "].spec", sl[i].spec); \
239 WRITE_VEC3(str, "light[" STRINGIFY(i) "].vec", sl[i].vec); \
240 } while (0)
241
243{
244 FILE *fp = BLI_fopen(sl->filepath, "wb");
245 if (fp) {
247
248 /* Very dumb ASCII format. One value per line separated by a space. */
250 WRITE_VEC3(str, "light_ambient", sl->light_ambient);
251 WRITE_SOLIDLIGHT(str, sl->light, 0);
252 WRITE_SOLIDLIGHT(str, sl->light, 1);
253 WRITE_SOLIDLIGHT(str, sl->light, 2);
254 WRITE_SOLIDLIGHT(str, sl->light, 3);
255
256 char *cstr = BLI_dynstr_get_cstring(str);
257
258 fwrite(cstr, BLI_dynstr_get_len(str), 1, fp);
259 fclose(fp);
260
261 MEM_freeN(cstr);
263 }
264}
265
266#undef WRITE_SOLIDLIGHT
267#undef WRITE_VEC3
268#undef WRITE_IVAL
269#undef WRITE_FVAL
270
271static void direction_to_equirect(float r[2], const float dir[3])
272{
273 r[0] = (atan2f(dir[1], dir[0]) - M_PI) / -(M_PI * 2);
274 r[1] = (acosf(dir[2] / 1.0) - M_PI) / -M_PI;
275}
276
277namespace {
278
279struct MultilayerConvertContext {
280 int num_diffuse_channels;
281 float *diffuse_pass;
282 int num_specular_channels;
283 float *specular_pass;
284};
285
286} // namespace
287
288static void *studiolight_multilayer_addview(void * /*base*/, const char * /*view_name*/)
289{
290 return nullptr;
291}
292static void *studiolight_multilayer_addlayer(void *base, const char * /*layer_name*/)
293{
294 return base;
295}
296
297/* Convert a multilayer pass to ImBuf channel 4 float buffer.
298 * NOTE: Parameter rect will become invalid. Do not use rect after calling this
299 * function */
301 float *rect,
302 const uint channels)
303{
304 if (channels == 4) {
305 return rect;
306 }
307
308 float *new_rect = MEM_calloc_arrayN<float>(4 * size_t(ibuf->x) * size_t(ibuf->y), __func__);
309
311 rect,
312 channels,
315 false,
316 ibuf->x,
317 ibuf->y,
318 ibuf->x,
319 ibuf->x);
320
321 MEM_freeN(rect);
322 return new_rect;
323}
324
325static void studiolight_multilayer_addpass(void *base,
326 void * /*lay*/,
327 const char *pass_name,
328 float *rect,
329 int num_channels,
330 const char * /*chan_id*/,
331 const char * /*view_name*/)
332{
333 MultilayerConvertContext *ctx = static_cast<MultilayerConvertContext *>(base);
334 /* NOTE: This function must free pass pixels data if it is not used, this
335 * is how IMB_exr_multilayer_convert() is working. */
336 /* If we've found a first combined pass, skip all the rest ones. */
337 if (STREQ(pass_name, STUDIOLIGHT_PASSNAME_DIFFUSE)) {
338 ctx->diffuse_pass = rect;
339 ctx->num_diffuse_channels = num_channels;
340 }
341 else if (STREQ(pass_name, STUDIOLIGHT_PASSNAME_SPECULAR)) {
342 ctx->specular_pass = rect;
343 ctx->num_specular_channels = num_channels;
344 }
345 else {
346 MEM_freeN(rect);
347 }
348}
349
351{
354 ImBuf *specular_ibuf = nullptr;
355 ImBuf *diffuse_ibuf = nullptr;
356 const bool failed = (ibuf == nullptr);
357
358 if (ibuf) {
359 if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->exrhandle) {
360 /* the read file is a multilayered openexr file (exrhandle != nullptr)
361 * This file is currently only supported for MATCAPS where
362 * the first found 'diffuse' pass will be used for diffuse lighting
363 * and the first found 'specular' pass will be used for specular lighting */
364 MultilayerConvertContext ctx = {0};
366 &ctx,
370
371 /* `ctx.diffuse_pass` and `ctx.specular_pass` can be freed inside
372 * `studiolight_multilayer_convert_pass` when conversion happens.
373 * When not converted we move the ownership of the buffer to the
374 * `converted_pass`. We only need to free `converted_pass` as it holds
375 * the unmodified allocation from the `ctx.*_pass` or the converted data.
376 */
377 if (ctx.diffuse_pass != nullptr) {
378 float *converted_pass = studiolight_multilayer_convert_pass(
379 ibuf, ctx.diffuse_pass, ctx.num_diffuse_channels);
380 diffuse_ibuf = IMB_allocFromBufferOwn(
381 nullptr, converted_pass, ibuf->x, ibuf->y, ctx.num_diffuse_channels);
382 }
383
384 if (ctx.specular_pass != nullptr) {
385 float *converted_pass = studiolight_multilayer_convert_pass(
386 ibuf, ctx.specular_pass, ctx.num_specular_channels);
387 specular_ibuf = IMB_allocFromBufferOwn(
388 nullptr, converted_pass, ibuf->x, ibuf->y, ctx.num_specular_channels);
389 }
390
392 ibuf->exrhandle = nullptr;
393 IMB_freeImBuf(ibuf);
394 ibuf = nullptr;
395 }
396 else {
397 /* read file is an single layer openexr file or the read file isn't
398 * an openexr file */
400 diffuse_ibuf = ibuf;
401 ibuf = nullptr;
402 }
403 }
404
405 if (diffuse_ibuf == nullptr) {
406 /* Create 1x1 diffuse buffer, in case image failed to load or if there was
407 * only a specular pass in the multilayer file or no passes were found. */
408 const float black[4] = {0.0f, 0.0f, 0.0f, 1.0f};
409 const float magenta[4] = {1.0f, 0.0f, 1.0f, 1.0f};
410 diffuse_ibuf = IMB_allocFromBuffer(
411 nullptr, (failed || (specular_ibuf == nullptr)) ? magenta : black, 1, 1, 4);
412 }
413
414 if (sl->flag & STUDIOLIGHT_TYPE_MATCAP) {
415 sl->matcap_diffuse.ibuf = diffuse_ibuf;
416 sl->matcap_specular.ibuf = specular_ibuf;
417 if (specular_ibuf != nullptr) {
419 }
420 }
421 else {
422 sl->equirect_radiance_buffer = diffuse_ibuf;
423 if (specular_ibuf != nullptr) {
424 IMB_freeImBuf(specular_ibuf);
425 }
426 }
427 }
428
430}
431
452
454{
455 BLI_assert(sli->ibuf);
456 ImBuf *ibuf = sli->ibuf;
457 const size_t ibuf_pixel_count = IMB_get_pixel_count(ibuf);
458 float *gpu_matcap_3components = MEM_calloc_arrayN<float>(3 * ibuf_pixel_count, __func__);
459
460 const float (*offset4)[4] = (const float (*)[4])ibuf->float_buffer.data;
461 float (*offset3)[3] = (float (*)[3])gpu_matcap_3components;
462 for (size_t i = 0; i < ibuf_pixel_count; i++, offset4++, offset3++) {
463 copy_v3_v3(*offset3, *offset4);
464 }
465
466 sli->gputexture = GPU_texture_create_2d("matcap",
467 ibuf->x,
468 ibuf->y,
469 1,
470 blender::gpu::TextureFormat::UFLOAT_11_11_10,
472 nullptr);
473 GPU_texture_update(sli->gputexture, GPU_DATA_FLOAT, gpu_matcap_3components);
474
475 MEM_SAFE_FREE(gpu_matcap_3components);
476}
477
500
501static float4 studiolight_calculate_radiance(const ImBuf *ibuf, const float direction[3])
502{
503 float uv[2];
504 direction_to_equirect(uv, direction);
505 return blender::imbuf::interpolate_nearest_border_fl(ibuf, uv[0] * ibuf->x, uv[1] * ibuf->y);
506}
507
508/*
509 * Spherical Harmonics
510 */
511BLI_INLINE float area_element(float x, float y)
512{
513 return atan2(x * y, sqrtf(x * x + y * y + 1));
514}
515
516static float brdf_approx(float spec_color, float roughness, float NV)
517{
518 /* Very rough approximation. We don't need it to be correct, just fast.
519 * Just simulate fresnel effect with roughness attenuation. */
520 float fresnel = exp2(-8.35f * NV) * (1.0f - roughness);
521 return spec_color * (1.0f - fresnel) + fresnel;
522}
523
524/* NL need to be unclamped. w in [0..1] range. */
525static float wrapped_lighting(float NL, float w)
526{
527 float w_1 = w + 1.0f;
528 return max_ff((NL + w) / (w_1 * w_1), 0.0f);
529}
530
531static float blinn_specular(const float L[3],
532 const float I[3],
533 const float N[3],
534 const float R[3],
535 float NL,
536 float roughness,
537 float wrap)
538{
539 float half_dir[3];
540 float wrapped_NL = dot_v3v3(L, R);
541 add_v3_v3v3(half_dir, L, I);
542 normalize_v3(half_dir);
543 float spec_angle = max_ff(dot_v3v3(half_dir, N), 0.0f);
544
545 float gloss = 1.0f - roughness;
546 /* Reduce gloss for smooth light. (simulate bigger light) */
547 gloss *= 1.0f - wrap;
548 float shininess = exp2(10.0f * gloss + 1.0f);
549
550 /* Pi is already divided in the light power.
551 * normalization_factor = (shininess + 8.0) / (8.0 * M_PI) */
552 float normalization_factor = shininess * 0.125f + 1.0f;
553 float spec_light = powf(spec_angle, shininess) * max_ff(NL, 0.0f) * normalization_factor;
554
555 /* Simulate Env. light. */
556 float w = wrap * (1.0 - roughness) + roughness;
557 float spec_env = wrapped_lighting(wrapped_NL, w);
558
559 float w2 = wrap * wrap;
560
561 return spec_light * (1.0 - w2) + spec_env * w2;
562}
563
564/* Keep in sync with the GLSL shader function `get_world_lighting()`. */
565static void studiolight_lights_eval(StudioLight *sl, const float normal[3], float r_color[3])
566{
567 float R[3], I[3] = {0.0f, 0.0f, 1.0f}, N[3] = {normal[0], normal[2], -normal[1]};
568 const float roughness = 0.5f;
569 const float diffuse_color = 0.8f;
570 const float specular_color = brdf_approx(0.05f, roughness, N[2]);
571 float diff_light[3], spec_light[3];
572
573 /* Ambient lighting */
574 copy_v3_v3(diff_light, sl->light_ambient);
575 copy_v3_v3(spec_light, sl->light_ambient);
576
577 reflect_v3_v3v3(R, I, N);
578 for (int i = 0; i < STUDIOLIGHT_MAX_LIGHT; i++) {
579 SolidLight *light = &sl->light[i];
580 if (light->flag) {
581 /* Diffuse lighting */
582 float NL = dot_v3v3(light->vec, N);
583 float diff = wrapped_lighting(NL, light->smooth);
584 madd_v3_v3fl(diff_light, light->col, diff);
585 /* Specular lighting */
586 float spec = blinn_specular(light->vec, I, N, R, NL, roughness, light->smooth);
587 madd_v3_v3fl(spec_light, light->spec, spec);
588 }
589 }
590
591 /* Multiply result by surface colors. */
592 mul_v3_fl(diff_light, diffuse_color * (1.0 - specular_color));
593 mul_v3_fl(spec_light, specular_color);
594
595 add_v3_v3v3(r_color, diff_light, spec_light);
596}
597
598static StudioLight *studiolight_add_file(const char *filepath, int flag)
599{
600 char filename[FILE_MAXFILE];
601 BLI_path_split_file_part(filepath, filename, FILE_MAXFILE);
602
603 if ((((flag & STUDIOLIGHT_TYPE_STUDIO) != 0) && BLI_path_extension_check(filename, ".sl")) ||
605 {
607 STRNCPY(sl->name, filename);
608 STRNCPY(sl->filepath, filepath);
609
610 if ((flag & STUDIOLIGHT_TYPE_STUDIO) != 0) {
612 }
614 return sl;
615 }
616 return nullptr;
617}
618
619static void studiolight_add_files_from_datafolder(const int folder_id,
620 const char *subfolder,
621 int flag)
622{
623 const std::optional<std::string> folder = BKE_appdir_folder_id(folder_id, subfolder);
624 if (!folder) {
625 return;
626 }
627
628 direntry *dirs;
629 const uint dirs_num = BLI_filelist_dir_contents(folder->c_str(), &dirs);
630 int i;
631 for (i = 0; i < dirs_num; i++) {
632 if (dirs[i].type & S_IFREG) {
633 studiolight_add_file(dirs[i].path, flag);
634 }
635 }
636 BLI_filelist_free(dirs, dirs_num);
637 dirs = nullptr;
638}
639
641{
642 /* Internal studiolights before external studio lights */
644 return 1;
645 }
646 return 0;
647}
648
649static int studiolight_cmp(const void *a, const void *b)
650{
651 const StudioLight *sl1 = static_cast<const StudioLight *>(a);
652 const StudioLight *sl2 = static_cast<const StudioLight *>(b);
653
654 const int flagorder1 = studiolight_flag_cmp_order(sl1);
655 const int flagorder2 = studiolight_flag_cmp_order(sl2);
656
657 if (flagorder1 < flagorder2) {
658 return -1;
659 }
660 if (flagorder1 > flagorder2) {
661 return 1;
662 }
663
664 return BLI_strcasecmp(sl1->name, sl2->name);
665}
666
667/* icons */
668
669/* Takes normalized uvs as parameter (range from 0 to 1).
670 * inner_edge and outer_edge are distances (from the center)
671 * in uv space for the alpha mask falloff. */
672static uint alpha_circle_mask(float u, float v, float inner_edge, float outer_edge)
673{
674 /* Coords from center. */
675 const float co[2] = {u - 0.5f, v - 0.5f};
676 float dist = len_v2(co);
677 float alpha = 1.0f + (inner_edge - dist) / (outer_edge - inner_edge);
678 uint mask = uint(floorf(255.0f * min_ff(max_ff(alpha, 0.0f), 1.0f)));
679 return mask << 24;
680}
681
682/* Percentage of the icon that the preview sphere covers. */
683#define STUDIOLIGHT_DIAMETER 0.95f
684/* Rescale coord around (0.5, 0.5) by STUDIOLIGHT_DIAMETER. */
685#define RESCALE_COORD(x) (x / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f)
686
687/* Remaps normalized UV [0..1] to a sphere normal around (0.5, 0.5) */
688static void sphere_normal_from_uv(float normal[3], float u, float v)
689{
690 normal[0] = u * 2.0f - 1.0f;
691 normal[1] = v * 2.0f - 1.0f;
692 float dist = len_v2(normal);
693 normal[2] = sqrtf(1.0f - square_f(dist));
694}
695
696static void studiolight_radiance_preview(uint *icon_buffer, StudioLight *sl)
697{
699
701 float dy = RESCALE_COORD(y);
702 float dx = RESCALE_COORD(x);
703
704 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
705 if (alphamask != 0) {
706 float normal[3], direction[3];
707 const float incoming[3] = {0.0f, 0.0f, -1.0f};
708 sphere_normal_from_uv(normal, dx, dy);
709 reflect_v3_v3v3(direction, incoming, normal);
710 /* We want to see horizon not poles. */
711 std::swap(direction[1], direction[2]);
712 direction[1] = -direction[1];
713
715
716 *pixel = rgb_to_cpack(linearrgb_to_srgb(color[0]),
717 linearrgb_to_srgb(color[1]),
718 linearrgb_to_srgb(color[2])) |
719 alphamask;
720 }
721 else {
722 *pixel = 0x0;
723 }
724 }
726}
727
728static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool flipped)
729{
730 using namespace blender;
731
733
734 ImBuf *diffuse_buffer = sl->matcap_diffuse.ibuf;
735 ImBuf *specular_buffer = sl->matcap_specular.ibuf;
736
738 float dy = RESCALE_COORD(y);
739 float dx = RESCALE_COORD(x);
740 if (flipped) {
741 dx = 1.0f - dx;
742 }
743
744 float u = dx * diffuse_buffer->x - 1.0f;
745 float v = dy * diffuse_buffer->y - 1.0f;
747
748 if (specular_buffer) {
749 float4 specular = imbuf::interpolate_nearest_border_fl(specular_buffer, u, v);
750 add_v3_v3(color, specular);
751 }
752
753 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
754
758 alphamask;
759 }
761}
762
763static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl)
764{
766 float dy = RESCALE_COORD(y);
767 float dx = RESCALE_COORD(x);
768
769 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
770 if (alphamask != 0) {
771 float normal[3], color[3];
772 sphere_normal_from_uv(normal, dx, dy);
773 /* We want to see horizon not poles. */
774 std::swap(normal[1], normal[2]);
775 normal[1] = -normal[1];
776
777 studiolight_lights_eval(sl, normal, color);
778
782 alphamask;
783 }
784 else {
785 *pixel = 0x0;
786 }
787 }
789}
790
791void BKE_studiolight_default(SolidLight lights[4], float light_ambient[3])
792{
793 copy_v3_fl3(light_ambient, 0.0, 0.0, 0.0);
794
795 lights[0].flag = 1;
796 lights[0].smooth = 0.526620f;
797 lights[0].col[0] = 0.033103f;
798 lights[0].col[1] = 0.033103f;
799 lights[0].col[2] = 0.033103f;
800 lights[0].spec[0] = 0.266761f;
801 lights[0].spec[1] = 0.266761f;
802 lights[0].spec[2] = 0.266761f;
803 lights[0].vec[0] = -0.352546f;
804 lights[0].vec[1] = 0.170931f;
805 lights[0].vec[2] = -0.920051f;
806
807 lights[1].flag = 1;
808 lights[1].smooth = 0.000000f;
809 lights[1].col[0] = 0.521083f;
810 lights[1].col[1] = 0.538226f;
811 lights[1].col[2] = 0.538226f;
812 lights[1].spec[0] = 0.599030f;
813 lights[1].spec[1] = 0.599030f;
814 lights[1].spec[2] = 0.599030f;
815 lights[1].vec[0] = -0.408163f;
816 lights[1].vec[1] = 0.346939f;
817 lights[1].vec[2] = 0.844415f;
818
819 lights[2].flag = 1;
820 lights[2].smooth = 0.478261f;
821 lights[2].col[0] = 0.038403f;
822 lights[2].col[1] = 0.034357f;
823 lights[2].col[2] = 0.049530f;
824 lights[2].spec[0] = 0.106102f;
825 lights[2].spec[1] = 0.125981f;
826 lights[2].spec[2] = 0.158523f;
827 lights[2].vec[0] = 0.521739f;
828 lights[2].vec[1] = 0.826087f;
829 lights[2].vec[2] = 0.212999f;
830
831 lights[3].flag = 1;
832 lights[3].smooth = 0.200000f;
833 lights[3].col[0] = 0.090838f;
834 lights[3].col[1] = 0.082080f;
835 lights[3].col[2] = 0.072255f;
836 lights[3].spec[0] = 0.106535f;
837 lights[3].spec[1] = 0.084771f;
838 lights[3].spec[2] = 0.066080f;
839 lights[3].vec[0] = 0.624519f;
840 lights[3].vec[1] = -0.562067f;
841 lights[3].vec[2] = -0.542269f;
842}
843
845{
846 /* Add default studio light */
849 STRNCPY(sl->name, "Default");
850
852
853 /* Go over the preset folder and add a studio-light for every image with its path. */
854 /* Also reserve icon space for it. */
873
874 /* sort studio lights on filename. */
876
878}
879
881{
882 while (StudioLight *sl = static_cast<StudioLight *>(BLI_pophead(&studiolights))) {
884 }
885}
886
888{
889 const char *default_name = "";
890
892 default_name = STUDIOLIGHT_WORLD_DEFAULT;
893 }
894 else if (flag & STUDIOLIGHT_TYPE_MATCAP) {
895 default_name = STUDIOLIGHT_MATCAP_DEFAULT;
896 }
897
899 if ((sl->flag & flag) && STREQ(sl->name, default_name)) {
900 return sl;
901 }
902 }
903
905 if (sl->flag & flag) {
906 return sl;
907 }
908 }
909 return nullptr;
910}
911
913{
915 if (STREQLEN(sl->name, name, FILE_MAXFILE)) {
916 if (sl->flag & flag) {
917 return sl;
918 }
919
920 /* flags do not match, so use default */
922 }
923 }
924 /* When not found, use the default studio light */
926}
927
929{
931 if (sl->index == index) {
932 return sl;
933 }
934 }
935 /* When not found, use the default studio light */
937}
938
943
944void BKE_studiolight_preview(uint *icon_buffer, StudioLight *sl, int icon_id_type)
945{
946 switch (icon_id_type) {
948 default: {
949 studiolight_radiance_preview(icon_buffer, sl);
950 break;
951 }
953 studiolight_irradiance_preview(icon_buffer, sl);
954 break;
955 }
957 studiolight_matcap_preview(icon_buffer, sl, false);
958 break;
959 }
961 studiolight_matcap_preview(icon_buffer, sl, true);
962 break;
963 }
964 }
966}
967
987
988/*
989 * Python API Functions.
990 */
991
993{
994 if (sl->flag & STUDIOLIGHT_USER_DEFINED) {
997 }
998}
999
1000StudioLight *BKE_studiolight_load(const char *filepath, int type)
1001{
1003 return sl;
1004}
1005
1007 const SolidLight light[4],
1008 const float light_ambient[3])
1009{
1013
1014 char filename[FILE_MAXFILE];
1015 BLI_path_split_file_part(filepath, filename, FILE_MAXFILE);
1016 STRNCPY(sl->filepath, filepath);
1017 STRNCPY(sl->name, filename);
1018
1019 memcpy(sl->light, light, sizeof(*light) * 4);
1020 memcpy(sl->light_ambient, light_ambient, sizeof(*light_ambient) * 3);
1021
1023
1025 return sl;
1026}
1027
1029{
1030 static StudioLight sl = {nullptr};
1032
1033 memcpy(sl.light, U.light_param, sizeof(*sl.light) * 4);
1034 memcpy(sl.light_ambient, U.light_ambient, sizeof(*sl.light_ambient) * 3);
1035
1036 return &sl;
1037}
1038
1044
1046 StudioLightFreeFunction *free_function,
1047 void *data)
1048{
1049 sl->free_function = free_function;
1051}
1052
1054{
1055 BLI_assert(sl != nullptr);
1056 if (sl->icon_id_radiance == icon_id) {
1057 sl->icon_id_radiance = 0;
1058 }
1059 if (sl->icon_id_irradiance == icon_id) {
1060 sl->icon_id_irradiance = 0;
1061 }
1062 if (sl->icon_id_matcap == icon_id) {
1063 sl->icon_id_matcap = 0;
1064 }
1065 if (sl->icon_id_matcap_flipped == icon_id) {
1066 sl->icon_id_matcap_flipped = 0;
1067 }
1068}
std::optional< std::string > BKE_appdir_folder_id(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:721
@ BLENDER_USER_DATAFILES
@ BLENDER_SYSTEM_DATAFILES
int BKE_icon_ensure_studio_light(struct StudioLight *sl, int id_type)
Definition icons.cc:572
#define STUDIOLIGHT_ICON_ID_TYPE_MATCAP
#define STUDIOLIGHT_ICON_ID_TYPE_MATCAP_FLIPPED
#define STUDIOLIGHT_ICON_ID_TYPE_RADIANCE
@ STUDIOLIGHT_EXTERNAL_IMAGE_LOADED
@ STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE
@ STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE
@ STUDIOLIGHT_USER_DEFINED
@ STUDIOLIGHT_INTERNAL
@ STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE
@ STUDIOLIGHT_TYPE_MATCAP
@ STUDIOLIGHT_TYPE_WORLD
@ STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS
@ STUDIOLIGHT_TYPE_STUDIO
@ STUDIOLIGHT_EXTERNAL_FILE
void StudioLightFreeFunction(struct StudioLight *, void *data)
#define STUDIOLIGHT_ICON_SIZE
#define STUDIOLIGHT_ICON_ID_TYPE_IRRADIANCE
#define STUDIOLIGHT_MAX_LIGHT
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_dynstr_get_len(const DynStr *ds) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.cc:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
struct LinkNode * BLI_file_read_as_lines(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:563
void BLI_file_free_lines(struct LinkNode *lines)
Definition storage.cc:609
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
Some types for dealing with directories.
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void void BLI_listbase_sort(ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:252
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float square_f(float a)
unsigned int rgb_to_cpack(float r, float g, float b)
float linearrgb_to_srgb(float c)
#define M_PI
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
void reflect_v3_v3v3(float out[3], const float v[3], const float normal[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void copy_v3_fl3(float v[3], float x, float y, float z)
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
#define FILE_MAXFILE
bool BLI_path_extension_check_array(const char *path, const char **ext_array) ATTR_NONNULL(1
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
bool BLI_path_extension_check(const char *path, const char *ext) ATTR_NONNULL(1
int char char int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
unsigned int uint
#define STREQLEN(a, b, n)
#define STREQ(a, b)
These structs are the foundation for all linked lists in the library system.
@ GPU_DATA_FLOAT
void GPU_texture_extend_mode(blender::gpu::Texture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_SAMPLER_EXTEND_MODE_REPEAT
blender::gpu::Texture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_filter_mode(blender::gpu::Texture *texture, bool use_filter)
void GPU_texture_update(blender::gpu::Texture *texture, eGPUDataFormat data_format, const void *data)
ImBuf * IMB_load_image_from_filepath(const char *filepath, const int flags, char r_colorspace[IM_MAX_SPACE]=nullptr)
Definition readimage.cc:189
ImBuf * IMB_allocFromBuffer(const uint8_t *byte_buffer, const float *float_buffer, unsigned int w, unsigned int h, unsigned int channels)
ImBuf * IMB_allocFromBufferOwn(uint8_t *byte_buffer, float *float_buffer, unsigned int w, unsigned int h, unsigned int channels)
void IMB_buffer_float_from_float(float *rect_to, const float *rect_from, int channels_from, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from)
void IMB_freeImBuf(ImBuf *ibuf)
size_t IMB_get_pixel_count(const ImBuf *ibuf)
Get the length of the data of the given image buffer in pixels.
void IMB_float_from_byte(ImBuf *ibuf)
@ IMB_FTYPE_OPENEXR
#define IB_PROFILE_LINEAR_RGB
@ IB_alphamode_ignore
@ IB_multilayer
const char * imb_ext_image[]
void IMB_exr_close(ExrHandle *handle)
void IMB_exr_multilayer_convert(ExrHandle *handle, void *base, void *(*addview)(void *base, const char *str), void *(*addlayer)(void *base, const char *str), void(*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id, const char *view))
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define U
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
nullptr float
#define NL
#define powf(x, y)
#define acosf(x)
#define str(s)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
#define exp2
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
ccl_device_inline float3 atan2(const float3 y, const float3 x)
#define N
#define R
#define L
float4 interpolate_nearest_border_fl(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:27
VecBase< float, 4 > float4
float wrap(float value, float max, float min)
Definition node_math.h:103
#define I
const char * name
#define floorf
#define sqrtf
#define atan2f
ImBufFloatBuffer float_buffer
enum eImbFileType ftype
ExrHandle * exrhandle
struct ImBuf * ibuf
blender::gpu::Texture * gputexture
StudioLightImage matcap_specular
SolidLight light[STUDIOLIGHT_MAX_LIGHT]
void * free_function_data
char filepath[FILE_MAX]
blender::gpu::Texture * equirect_radiance_gputexture
StudioLightFreeFunction * free_function
struct ImBuf * equirect_radiance_buffer
int icon_id_matcap_flipped
char name[FILE_MAXFILE]
StudioLightImage matcap_diffuse
float light_ambient[3]
static void studiolight_create_matcap_specular_gputexture(StudioLight *sl)
#define STUDIOLIGHT_FILE_VERSION
static const char * STUDIOLIGHT_LIGHTS_FOLDER
static void * studiolight_multilayer_addlayer(void *base, const char *)
static void studiolight_radiance_preview(uint *icon_buffer, StudioLight *sl)
#define WRITE_IVAL(str, id, val)
void BKE_studiolight_remove(StudioLight *sl)
#define STUDIOLIGHT_PASSNAME_DIFFUSE
static StudioLight * studiolight_add_file(const char *filepath, int flag)
static void studiolight_lights_eval(StudioLight *sl, const float normal[3], float r_color[3])
StudioLight * BKE_studiolight_studio_edit_get()
static float * studiolight_multilayer_convert_pass(const ImBuf *ibuf, float *rect, const uint channels)
static void studiolight_free_temp_resources(StudioLight *sl)
static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl)
static const char * STUDIOLIGHT_MATCAP_DEFAULT
static float4 studiolight_calculate_radiance(const ImBuf *ibuf, const float direction[3])
#define WRITE_SOLIDLIGHT(str, sl, i)
static int last_studiolight_id
static int studiolight_cmp(const void *a, const void *b)
#define ITER_PIXELS(type, src, channels, width, height)
static int studiolight_flag_cmp_order(const StudioLight *sl)
StudioLight * BKE_studiolight_load(const char *filepath, int type)
static StudioLight * studiolight_create(int flag)
static ListBase studiolights
static void studiolight_free(StudioLight *sl)
static void studiolight_load_equirect_image(StudioLight *sl)
void BKE_studiolight_unset_icon_id(StudioLight *sl, int icon_id)
static uint alpha_circle_mask(float u, float v, float inner_edge, float outer_edge)
StudioLight * BKE_studiolight_find_default(int flag)
static void studiolight_multilayer_addpass(void *base, void *, const char *pass_name, float *rect, int num_channels, const char *, const char *)
static void studiolight_create_matcap_gputexture(StudioLightImage *sli)
static void sphere_normal_from_uv(float normal[3], float u, float v)
void BKE_studiolight_set_free_function(StudioLight *sl, StudioLightFreeFunction *free_function, void *data)
StudioLight * BKE_studiolight_find(const char *name, int flag)
void BKE_studiolight_free()
static void studiolight_add_files_from_datafolder(const int folder_id, const char *subfolder, int flag)
#define WRITE_VEC3(str, id, val)
void BKE_studiolight_init()
BLI_INLINE float area_element(float x, float y)
void BKE_studiolight_default(SolidLight lights[4], float light_ambient[3])
StudioLight * BKE_studiolight_findindex(int index, int flag)
#define ITER_PIXELS_END
static void studiolight_load_solid_light(StudioLight *sl)
static float wrapped_lighting(float NL, float w)
#define STUDIOLIGHT_DELETE_ICON(s)
static void studiolight_free_image_buffers(StudioLight *sl)
#define STUDIOLIGHT_PASSNAME_SPECULAR
static const char * STUDIOLIGHT_WORLD_FOLDER
static void studiolight_create_matcap_diffuse_gputexture(StudioLight *sl)
#define READ_VEC3(id, val, lines)
void BKE_studiolight_ensure_flag(StudioLight *sl, int flag)
ListBase * BKE_studiolight_listbase()
StudioLight * BKE_studiolight_create(const char *filepath, const SolidLight light[4], const float light_ambient[3])
#define IMB_SAFE_FREE(p)
static const char * STUDIOLIGHT_MATCAP_FOLDER
#define RESCALE_COORD(x)
static void studiolight_create_equirect_radiance_gputexture(StudioLight *sl)
static float brdf_approx(float spec_color, float roughness, float NV)
#define GPU_TEXTURE_SAFE_FREE(p)
static void direction_to_equirect(float r[2], const float dir[3])
static void studiolight_write_solid_light(StudioLight *sl)
static const char * STUDIOLIGHT_WORLD_DEFAULT
static void * studiolight_multilayer_addview(void *, const char *)
#define READ_SOLIDLIGHT(sl, i, lines)
static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool flipped)
void BKE_studiolight_refresh()
static float blinn_specular(const float L[3], const float I[3], const float N[3], const float R[3], float NL, float roughness, float wrap)
void BKE_studiolight_preview(uint *icon_buffer, StudioLight *sl, int icon_id_type)
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39
uint8_t flag
Definition wm_window.cc:145