Blender V4.5
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->userdata) {
360 /* the read file is a multilayered openexr file (userdata != 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
391 IMB_exr_close(ibuf->userdata);
392 ibuf->userdata = 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
451
453{
454 BLI_assert(sli->ibuf);
455 ImBuf *ibuf = sli->ibuf;
456 const size_t ibuf_pixel_count = IMB_get_pixel_count(ibuf);
457 float *gpu_matcap_3components = MEM_calloc_arrayN<float>(3 * ibuf_pixel_count, __func__);
458
459 const float(*offset4)[4] = (const float(*)[4])ibuf->float_buffer.data;
460 float(*offset3)[3] = (float(*)[3])gpu_matcap_3components;
461 for (size_t i = 0; i < ibuf_pixel_count; i++, offset4++, offset3++) {
462 copy_v3_v3(*offset3, *offset4);
463 }
464
466 "matcap", ibuf->x, ibuf->y, 1, GPU_R11F_G11F_B10F, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
467 GPU_texture_update(sli->gputexture, GPU_DATA_FLOAT, gpu_matcap_3components);
468
469 MEM_SAFE_FREE(gpu_matcap_3components);
470}
471
494
495static float4 studiolight_calculate_radiance(const ImBuf *ibuf, const float direction[3])
496{
497 float uv[2];
498 direction_to_equirect(uv, direction);
499 return blender::imbuf::interpolate_nearest_border_fl(ibuf, uv[0] * ibuf->x, uv[1] * ibuf->y);
500}
501
502/*
503 * Spherical Harmonics
504 */
505BLI_INLINE float area_element(float x, float y)
506{
507 return atan2(x * y, sqrtf(x * x + y * y + 1));
508}
509
510static float brdf_approx(float spec_color, float roughness, float NV)
511{
512 /* Very rough approximation. We don't need it to be correct, just fast.
513 * Just simulate fresnel effect with roughness attenuation. */
514 float fresnel = exp2(-8.35f * NV) * (1.0f - roughness);
515 return spec_color * (1.0f - fresnel) + fresnel;
516}
517
518/* NL need to be unclamped. w in [0..1] range. */
519static float wrapped_lighting(float NL, float w)
520{
521 float w_1 = w + 1.0f;
522 return max_ff((NL + w) / (w_1 * w_1), 0.0f);
523}
524
525static float blinn_specular(const float L[3],
526 const float I[3],
527 const float N[3],
528 const float R[3],
529 float NL,
530 float roughness,
531 float wrap)
532{
533 float half_dir[3];
534 float wrapped_NL = dot_v3v3(L, R);
535 add_v3_v3v3(half_dir, L, I);
536 normalize_v3(half_dir);
537 float spec_angle = max_ff(dot_v3v3(half_dir, N), 0.0f);
538
539 float gloss = 1.0f - roughness;
540 /* Reduce gloss for smooth light. (simulate bigger light) */
541 gloss *= 1.0f - wrap;
542 float shininess = exp2(10.0f * gloss + 1.0f);
543
544 /* Pi is already divided in the light power.
545 * normalization_factor = (shininess + 8.0) / (8.0 * M_PI) */
546 float normalization_factor = shininess * 0.125f + 1.0f;
547 float spec_light = powf(spec_angle, shininess) * max_ff(NL, 0.0f) * normalization_factor;
548
549 /* Simulate Env. light. */
550 float w = wrap * (1.0 - roughness) + roughness;
551 float spec_env = wrapped_lighting(wrapped_NL, w);
552
553 float w2 = wrap * wrap;
554
555 return spec_light * (1.0 - w2) + spec_env * w2;
556}
557
558/* Keep in sync with the GLSL shader function `get_world_lighting()`. */
559static void studiolight_lights_eval(StudioLight *sl, const float normal[3], float r_color[3])
560{
561 float R[3], I[3] = {0.0f, 0.0f, 1.0f}, N[3] = {normal[0], normal[2], -normal[1]};
562 const float roughness = 0.5f;
563 const float diffuse_color = 0.8f;
564 const float specular_color = brdf_approx(0.05f, roughness, N[2]);
565 float diff_light[3], spec_light[3];
566
567 /* Ambient lighting */
568 copy_v3_v3(diff_light, sl->light_ambient);
569 copy_v3_v3(spec_light, sl->light_ambient);
570
571 reflect_v3_v3v3(R, I, N);
572 for (int i = 0; i < STUDIOLIGHT_MAX_LIGHT; i++) {
573 SolidLight *light = &sl->light[i];
574 if (light->flag) {
575 /* Diffuse lighting */
576 float NL = dot_v3v3(light->vec, N);
577 float diff = wrapped_lighting(NL, light->smooth);
578 madd_v3_v3fl(diff_light, light->col, diff);
579 /* Specular lighting */
580 float spec = blinn_specular(light->vec, I, N, R, NL, roughness, light->smooth);
581 madd_v3_v3fl(spec_light, light->spec, spec);
582 }
583 }
584
585 /* Multiply result by surface colors. */
586 mul_v3_fl(diff_light, diffuse_color * (1.0 - specular_color));
587 mul_v3_fl(spec_light, specular_color);
588
589 add_v3_v3v3(r_color, diff_light, spec_light);
590}
591
592static StudioLight *studiolight_add_file(const char *filepath, int flag)
593{
594 char filename[FILE_MAXFILE];
595 BLI_path_split_file_part(filepath, filename, FILE_MAXFILE);
596
597 if ((((flag & STUDIOLIGHT_TYPE_STUDIO) != 0) && BLI_path_extension_check(filename, ".sl")) ||
599 {
601 STRNCPY(sl->name, filename);
602 STRNCPY(sl->filepath, filepath);
603
604 if ((flag & STUDIOLIGHT_TYPE_STUDIO) != 0) {
606 }
608 return sl;
609 }
610 return nullptr;
611}
612
613static void studiolight_add_files_from_datafolder(const int folder_id,
614 const char *subfolder,
615 int flag)
616{
617 const std::optional<std::string> folder = BKE_appdir_folder_id(folder_id, subfolder);
618 if (!folder) {
619 return;
620 }
621
622 direntry *dirs;
623 const uint dirs_num = BLI_filelist_dir_contents(folder->c_str(), &dirs);
624 int i;
625 for (i = 0; i < dirs_num; i++) {
626 if (dirs[i].type & S_IFREG) {
627 studiolight_add_file(dirs[i].path, flag);
628 }
629 }
630 BLI_filelist_free(dirs, dirs_num);
631 dirs = nullptr;
632}
633
635{
636 /* Internal studiolights before external studio lights */
638 return 1;
639 }
640 return 0;
641}
642
643static int studiolight_cmp(const void *a, const void *b)
644{
645 const StudioLight *sl1 = static_cast<const StudioLight *>(a);
646 const StudioLight *sl2 = static_cast<const StudioLight *>(b);
647
648 const int flagorder1 = studiolight_flag_cmp_order(sl1);
649 const int flagorder2 = studiolight_flag_cmp_order(sl2);
650
651 if (flagorder1 < flagorder2) {
652 return -1;
653 }
654 if (flagorder1 > flagorder2) {
655 return 1;
656 }
657
658 return BLI_strcasecmp(sl1->name, sl2->name);
659}
660
661/* icons */
662
663/* Takes normalized uvs as parameter (range from 0 to 1).
664 * inner_edge and outer_edge are distances (from the center)
665 * in uv space for the alpha mask falloff. */
666static uint alpha_circle_mask(float u, float v, float inner_edge, float outer_edge)
667{
668 /* Coords from center. */
669 const float co[2] = {u - 0.5f, v - 0.5f};
670 float dist = len_v2(co);
671 float alpha = 1.0f + (inner_edge - dist) / (outer_edge - inner_edge);
672 uint mask = uint(floorf(255.0f * min_ff(max_ff(alpha, 0.0f), 1.0f)));
673 return mask << 24;
674}
675
676/* Percentage of the icon that the preview sphere covers. */
677#define STUDIOLIGHT_DIAMETER 0.95f
678/* Rescale coord around (0.5, 0.5) by STUDIOLIGHT_DIAMETER. */
679#define RESCALE_COORD(x) (x / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f)
680
681/* Remaps normalized UV [0..1] to a sphere normal around (0.5, 0.5) */
682static void sphere_normal_from_uv(float normal[3], float u, float v)
683{
684 normal[0] = u * 2.0f - 1.0f;
685 normal[1] = v * 2.0f - 1.0f;
686 float dist = len_v2(normal);
687 normal[2] = sqrtf(1.0f - square_f(dist));
688}
689
690static void studiolight_radiance_preview(uint *icon_buffer, StudioLight *sl)
691{
693
695 float dy = RESCALE_COORD(y);
696 float dx = RESCALE_COORD(x);
697
698 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
699 if (alphamask != 0) {
700 float normal[3], direction[3];
701 const float incoming[3] = {0.0f, 0.0f, -1.0f};
702 sphere_normal_from_uv(normal, dx, dy);
703 reflect_v3_v3v3(direction, incoming, normal);
704 /* We want to see horizon not poles. */
705 std::swap(direction[1], direction[2]);
706 direction[1] = -direction[1];
707
709
710 *pixel = rgb_to_cpack(linearrgb_to_srgb(color[0]),
711 linearrgb_to_srgb(color[1]),
712 linearrgb_to_srgb(color[2])) |
713 alphamask;
714 }
715 else {
716 *pixel = 0x0;
717 }
718 }
720}
721
722static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool flipped)
723{
724 using namespace blender;
725
727
728 ImBuf *diffuse_buffer = sl->matcap_diffuse.ibuf;
729 ImBuf *specular_buffer = sl->matcap_specular.ibuf;
730
732 float dy = RESCALE_COORD(y);
733 float dx = RESCALE_COORD(x);
734 if (flipped) {
735 dx = 1.0f - dx;
736 }
737
738 float u = dx * diffuse_buffer->x - 1.0f;
739 float v = dy * diffuse_buffer->y - 1.0f;
741
742 if (specular_buffer) {
743 float4 specular = imbuf::interpolate_nearest_border_fl(specular_buffer, u, v);
744 add_v3_v3(color, specular);
745 }
746
747 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
748
752 alphamask;
753 }
755}
756
757static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl)
758{
760 float dy = RESCALE_COORD(y);
761 float dx = RESCALE_COORD(x);
762
763 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
764 if (alphamask != 0) {
765 float normal[3], color[3];
766 sphere_normal_from_uv(normal, dx, dy);
767 /* We want to see horizon not poles. */
768 std::swap(normal[1], normal[2]);
769 normal[1] = -normal[1];
770
771 studiolight_lights_eval(sl, normal, color);
772
776 alphamask;
777 }
778 else {
779 *pixel = 0x0;
780 }
781 }
783}
784
785void BKE_studiolight_default(SolidLight lights[4], float light_ambient[3])
786{
787 copy_v3_fl3(light_ambient, 0.0, 0.0, 0.0);
788
789 lights[0].flag = 1;
790 lights[0].smooth = 0.526620f;
791 lights[0].col[0] = 0.033103f;
792 lights[0].col[1] = 0.033103f;
793 lights[0].col[2] = 0.033103f;
794 lights[0].spec[0] = 0.266761f;
795 lights[0].spec[1] = 0.266761f;
796 lights[0].spec[2] = 0.266761f;
797 lights[0].vec[0] = -0.352546f;
798 lights[0].vec[1] = 0.170931f;
799 lights[0].vec[2] = -0.920051f;
800
801 lights[1].flag = 1;
802 lights[1].smooth = 0.000000f;
803 lights[1].col[0] = 0.521083f;
804 lights[1].col[1] = 0.538226f;
805 lights[1].col[2] = 0.538226f;
806 lights[1].spec[0] = 0.599030f;
807 lights[1].spec[1] = 0.599030f;
808 lights[1].spec[2] = 0.599030f;
809 lights[1].vec[0] = -0.408163f;
810 lights[1].vec[1] = 0.346939f;
811 lights[1].vec[2] = 0.844415f;
812
813 lights[2].flag = 1;
814 lights[2].smooth = 0.478261f;
815 lights[2].col[0] = 0.038403f;
816 lights[2].col[1] = 0.034357f;
817 lights[2].col[2] = 0.049530f;
818 lights[2].spec[0] = 0.106102f;
819 lights[2].spec[1] = 0.125981f;
820 lights[2].spec[2] = 0.158523f;
821 lights[2].vec[0] = 0.521739f;
822 lights[2].vec[1] = 0.826087f;
823 lights[2].vec[2] = 0.212999f;
824
825 lights[3].flag = 1;
826 lights[3].smooth = 0.200000f;
827 lights[3].col[0] = 0.090838f;
828 lights[3].col[1] = 0.082080f;
829 lights[3].col[2] = 0.072255f;
830 lights[3].spec[0] = 0.106535f;
831 lights[3].spec[1] = 0.084771f;
832 lights[3].spec[2] = 0.066080f;
833 lights[3].vec[0] = 0.624519f;
834 lights[3].vec[1] = -0.562067f;
835 lights[3].vec[2] = -0.542269f;
836}
837
839{
840 /* Add default studio light */
843 STRNCPY(sl->name, "Default");
844
846
847 /* Go over the preset folder and add a studio-light for every image with its path. */
848 /* Also reserve icon space for it. */
867
868 /* sort studio lights on filename. */
870
872}
873
875{
876 while (StudioLight *sl = static_cast<StudioLight *>(BLI_pophead(&studiolights))) {
878 }
879}
880
882{
883 const char *default_name = "";
884
886 default_name = STUDIOLIGHT_WORLD_DEFAULT;
887 }
888 else if (flag & STUDIOLIGHT_TYPE_MATCAP) {
889 default_name = STUDIOLIGHT_MATCAP_DEFAULT;
890 }
891
893 if ((sl->flag & flag) && STREQ(sl->name, default_name)) {
894 return sl;
895 }
896 }
897
899 if (sl->flag & flag) {
900 return sl;
901 }
902 }
903 return nullptr;
904}
905
906StudioLight *BKE_studiolight_find(const char *name, int flag)
907{
909 if (STREQLEN(sl->name, name, FILE_MAXFILE)) {
910 if (sl->flag & flag) {
911 return sl;
912 }
913
914 /* flags do not match, so use default */
916 }
917 }
918 /* When not found, use the default studio light */
920}
921
923{
925 if (sl->index == index) {
926 return sl;
927 }
928 }
929 /* When not found, use the default studio light */
931}
932
937
938void BKE_studiolight_preview(uint *icon_buffer, StudioLight *sl, int icon_id_type)
939{
940 switch (icon_id_type) {
942 default: {
943 studiolight_radiance_preview(icon_buffer, sl);
944 break;
945 }
947 studiolight_irradiance_preview(icon_buffer, sl);
948 break;
949 }
951 studiolight_matcap_preview(icon_buffer, sl, false);
952 break;
953 }
955 studiolight_matcap_preview(icon_buffer, sl, true);
956 break;
957 }
958 }
960}
961
981
982/*
983 * Python API Functions.
984 */
985
987{
988 if (sl->flag & STUDIOLIGHT_USER_DEFINED) {
991 }
992}
993
994StudioLight *BKE_studiolight_load(const char *filepath, int type)
995{
997 return sl;
998}
999
1001 const SolidLight light[4],
1002 const float light_ambient[3])
1003{
1007
1008 char filename[FILE_MAXFILE];
1009 BLI_path_split_file_part(filepath, filename, FILE_MAXFILE);
1010 STRNCPY(sl->filepath, filepath);
1011 STRNCPY(sl->name, filename);
1012
1013 memcpy(sl->light, light, sizeof(*light) * 4);
1014 memcpy(sl->light_ambient, light_ambient, sizeof(*light_ambient) * 3);
1015
1017
1019 return sl;
1020}
1021
1023{
1024 static StudioLight sl = {nullptr};
1026
1027 memcpy(sl.light, U.light_param, sizeof(*sl.light) * 4);
1028 memcpy(sl.light_ambient, U.light_ambient, sizeof(*sl.light_ambient) * 3);
1029
1030 return &sl;
1031}
1032
1038
1040 StudioLightFreeFunction *free_function,
1041 void *data)
1042{
1043 sl->free_function = free_function;
1045}
1046
1048{
1049 BLI_assert(sl != nullptr);
1050 if (sl->icon_id_radiance == icon_id) {
1051 sl->icon_id_radiance = 0;
1052 }
1053 if (sl->icon_id_irradiance == icon_id) {
1054 sl->icon_id_irradiance = 0;
1055 }
1056 if (sl->icon_id_matcap == icon_id) {
1057 sl->icon_id_matcap = 0;
1058 }
1059 if (sl->icon_id_matcap_flipped == icon_id) {
1060 sl->icon_id_matcap_flipped = 0;
1061 }
1062}
@ BLENDER_USER_DATAFILES
@ BLENDER_SYSTEM_DATAFILES
std::optional< std::string > BKE_appdir_folder_id(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:717
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:36
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:576
void BLI_file_free_lines(struct LinkNode *lines)
Definition storage.cc:622
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:688
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.
GPUTexture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
@ GPU_DATA_FLOAT
void GPU_texture_extend_mode(GPUTexture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_SAMPLER_EXTEND_MODE_REPEAT
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
@ GPU_RGBA16F
@ GPU_R11F_G11F_B10F
void GPU_texture_update(GPUTexture *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:204
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(void *handle)
void IMB_exr_multilayer_convert(void *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 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
#define NL
#define powf(x, y)
#define atan2f(x, y)
#define floorf(x)
#define acosf(x)
#define sqrtf(x)
#define str(s)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
#define exp2
#define MEM_SAFE_FREE(v)
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)
#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:71
#define I
void * userdata
ImBufFloatBuffer float_buffer
enum eImbFileType ftype
struct GPUTexture * gputexture
struct ImBuf * ibuf
StudioLightImage matcap_specular
SolidLight light[STUDIOLIGHT_MAX_LIGHT]
void * free_function_data
struct GPUTexture * equirect_radiance_gputexture
char filepath[FILE_MAX]
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:139