Blender V4.3
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
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
100{
101 sl->flag &= ~STUDIOLIGHT_EXTERNAL_IMAGE_LOADED;
105}
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
283
284static void *studiolight_multilayer_addview(void * /*base*/, const char * /*view_name*/)
285{
286 return nullptr;
287}
288static void *studiolight_multilayer_addlayer(void *base, const char * /*layer_name*/)
289{
290 return base;
291}
292
293/* Convert a multilayer pass to ImBuf channel 4 float buffer.
294 * NOTE: Parameter rect will become invalid. Do not use rect after calling this
295 * function */
297 float *rect,
298 const uint channels)
299{
300 if (channels == 4) {
301 return rect;
302 }
303
304 float *new_rect = static_cast<float *>(
305 MEM_callocN(sizeof(float[4]) * ibuf->x * ibuf->y, __func__));
306
308 rect,
309 channels,
312 false,
313 ibuf->x,
314 ibuf->y,
315 ibuf->x,
316 ibuf->x);
317
318 MEM_freeN(rect);
319 return new_rect;
320}
321
322static void studiolight_multilayer_addpass(void *base,
323 void * /*lay*/,
324 const char *pass_name,
325 float *rect,
326 int num_channels,
327 const char * /*chan_id*/,
328 const char * /*view_name*/)
329{
330 MultilayerConvertContext *ctx = static_cast<MultilayerConvertContext *>(base);
331 /* NOTE: This function must free pass pixels data if it is not used, this
332 * is how IMB_exr_multilayer_convert() is working. */
333 /* If we've found a first combined pass, skip all the rest ones. */
334 if (STREQ(pass_name, STUDIOLIGHT_PASSNAME_DIFFUSE)) {
335 ctx->diffuse_pass = rect;
336 ctx->num_diffuse_channels = num_channels;
337 }
338 else if (STREQ(pass_name, STUDIOLIGHT_PASSNAME_SPECULAR)) {
339 ctx->specular_pass = rect;
340 ctx->num_specular_channels = num_channels;
341 }
342 else {
343 MEM_freeN(rect);
344 }
345}
346
348{
351 ImBuf *specular_ibuf = nullptr;
352 ImBuf *diffuse_ibuf = nullptr;
353 const bool failed = (ibuf == nullptr);
354
355 if (ibuf) {
356 if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) {
357 /* the read file is a multilayered openexr file (userdata != nullptr)
358 * This file is currently only supported for MATCAPS where
359 * the first found 'diffuse' pass will be used for diffuse lighting
360 * and the first found 'specular' pass will be used for specular lighting */
361 MultilayerConvertContext ctx = {0};
363 &ctx,
367
368 /* `ctx.diffuse_pass` and `ctx.specular_pass` can be freed inside
369 * `studiolight_multilayer_convert_pass` when conversion happens.
370 * When not converted we move the ownership of the buffer to the
371 * `converted_pass`. We only need to free `converted_pass` as it holds
372 * the unmodified allocation from the `ctx.*_pass` or the converted data.
373 */
374 if (ctx.diffuse_pass != nullptr) {
375 float *converted_pass = studiolight_multilayer_convert_pass(
376 ibuf, ctx.diffuse_pass, ctx.num_diffuse_channels);
377 diffuse_ibuf = IMB_allocFromBufferOwn(
378 nullptr, converted_pass, ibuf->x, ibuf->y, ctx.num_diffuse_channels);
379 }
380
381 if (ctx.specular_pass != nullptr) {
382 float *converted_pass = studiolight_multilayer_convert_pass(
383 ibuf, ctx.specular_pass, ctx.num_specular_channels);
384 specular_ibuf = IMB_allocFromBufferOwn(
385 nullptr, converted_pass, ibuf->x, ibuf->y, ctx.num_specular_channels);
386 }
387
388 IMB_exr_close(ibuf->userdata);
389 ibuf->userdata = nullptr;
390 IMB_freeImBuf(ibuf);
391 ibuf = nullptr;
392 }
393 else {
394 /* read file is an single layer openexr file or the read file isn't
395 * an openexr file */
397 diffuse_ibuf = ibuf;
398 ibuf = nullptr;
399 }
400 }
401
402 if (diffuse_ibuf == nullptr) {
403 /* Create 1x1 diffuse buffer, in case image failed to load or if there was
404 * only a specular pass in the multilayer file or no passes were found. */
405 const float black[4] = {0.0f, 0.0f, 0.0f, 1.0f};
406 const float magenta[4] = {1.0f, 0.0f, 1.0f, 1.0f};
407 diffuse_ibuf = IMB_allocFromBuffer(
408 nullptr, (failed || (specular_ibuf == nullptr)) ? magenta : black, 1, 1, 4);
409 }
410
411 if (sl->flag & STUDIOLIGHT_TYPE_MATCAP) {
412 sl->matcap_diffuse.ibuf = diffuse_ibuf;
413 sl->matcap_specular.ibuf = specular_ibuf;
414 if (specular_ibuf != nullptr) {
416 }
417 }
418 else {
419 sl->equirect_radiance_buffer = diffuse_ibuf;
420 if (specular_ibuf != nullptr) {
421 IMB_freeImBuf(specular_ibuf);
422 }
423 }
424 }
425
427}
428
448
450{
451 BLI_assert(sli->ibuf);
452 ImBuf *ibuf = sli->ibuf;
453 float *gpu_matcap_3components = static_cast<float *>(
454 MEM_callocN(sizeof(float[3]) * ibuf->x * ibuf->y, __func__));
455
456 const float(*offset4)[4] = (const float(*)[4])ibuf->float_buffer.data;
457 float(*offset3)[3] = (float(*)[3])gpu_matcap_3components;
458 for (int i = 0; i < ibuf->x * ibuf->y; i++, offset4++, offset3++) {
459 copy_v3_v3(*offset3, *offset4);
460 }
461
463 "matcap", ibuf->x, ibuf->y, 1, GPU_R11F_G11F_B10F, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
464 GPU_texture_update(sli->gputexture, GPU_DATA_FLOAT, gpu_matcap_3components);
465
466 MEM_SAFE_FREE(gpu_matcap_3components);
467}
468
491
492static float4 studiolight_calculate_radiance(const ImBuf *ibuf, const float direction[3])
493{
494 float uv[2];
495 direction_to_equirect(uv, direction);
496 return blender::imbuf::interpolate_nearest_border_fl(ibuf, uv[0] * ibuf->x, uv[1] * ibuf->y);
497}
498
499/*
500 * Spherical Harmonics
501 */
502BLI_INLINE float area_element(float x, float y)
503{
504 return atan2(x * y, sqrtf(x * x + y * y + 1));
505}
506
507static float brdf_approx(float spec_color, float roughness, float NV)
508{
509 /* Very rough approximation. We don't need it to be correct, just fast.
510 * Just simulate fresnel effect with roughness attenuation. */
511 float fresnel = exp2(-8.35f * NV) * (1.0f - roughness);
512 return spec_color * (1.0f - fresnel) + fresnel;
513}
514
515/* NL need to be unclamped. w in [0..1] range. */
516static float wrapped_lighting(float NL, float w)
517{
518 float w_1 = w + 1.0f;
519 return max_ff((NL + w) / (w_1 * w_1), 0.0f);
520}
521
522static float blinn_specular(const float L[3],
523 const float I[3],
524 const float N[3],
525 const float R[3],
526 float NL,
527 float roughness,
528 float wrap)
529{
530 float half_dir[3];
531 float wrapped_NL = dot_v3v3(L, R);
532 add_v3_v3v3(half_dir, L, I);
533 normalize_v3(half_dir);
534 float spec_angle = max_ff(dot_v3v3(half_dir, N), 0.0f);
535
536 float gloss = 1.0f - roughness;
537 /* Reduce gloss for smooth light. (simulate bigger light) */
538 gloss *= 1.0f - wrap;
539 float shininess = exp2(10.0f * gloss + 1.0f);
540
541 /* Pi is already divided in the light power.
542 * normalization_factor = (shininess + 8.0) / (8.0 * M_PI) */
543 float normalization_factor = shininess * 0.125f + 1.0f;
544 float spec_light = powf(spec_angle, shininess) * max_ff(NL, 0.0f) * normalization_factor;
545
546 /* Simulate Env. light. */
547 float w = wrap * (1.0 - roughness) + roughness;
548 float spec_env = wrapped_lighting(wrapped_NL, w);
549
550 float w2 = wrap * wrap;
551
552 return spec_light * (1.0 - w2) + spec_env * w2;
553}
554
555/* Keep in sync with the GLSL shader function `get_world_lighting()`. */
556static void studiolight_lights_eval(StudioLight *sl, const float normal[3], float r_color[3])
557{
558 float R[3], I[3] = {0.0f, 0.0f, 1.0f}, N[3] = {normal[0], normal[2], -normal[1]};
559 const float roughness = 0.5f;
560 const float diffuse_color = 0.8f;
561 const float specular_color = brdf_approx(0.05f, roughness, N[2]);
562 float diff_light[3], spec_light[3];
563
564 /* Ambient lighting */
565 copy_v3_v3(diff_light, sl->light_ambient);
566 copy_v3_v3(spec_light, sl->light_ambient);
567
568 reflect_v3_v3v3(R, I, N);
569 for (int i = 0; i < STUDIOLIGHT_MAX_LIGHT; i++) {
570 SolidLight *light = &sl->light[i];
571 if (light->flag) {
572 /* Diffuse lighting */
573 float NL = dot_v3v3(light->vec, N);
574 float diff = wrapped_lighting(NL, light->smooth);
575 madd_v3_v3fl(diff_light, light->col, diff);
576 /* Specular lighting */
577 float spec = blinn_specular(light->vec, I, N, R, NL, roughness, light->smooth);
578 madd_v3_v3fl(spec_light, light->spec, spec);
579 }
580 }
581
582 /* Multiply result by surface colors. */
583 mul_v3_fl(diff_light, diffuse_color * (1.0 - specular_color));
584 mul_v3_fl(spec_light, specular_color);
585
586 add_v3_v3v3(r_color, diff_light, spec_light);
587}
588
589static StudioLight *studiolight_add_file(const char *filepath, int flag)
590{
591 char filename[FILE_MAXFILE];
592 BLI_path_split_file_part(filepath, filename, FILE_MAXFILE);
593
594 if ((((flag & STUDIOLIGHT_TYPE_STUDIO) != 0) && BLI_path_extension_check(filename, ".sl")) ||
596 {
598 STRNCPY(sl->name, filename);
599 STRNCPY(sl->filepath, filepath);
600
601 if ((flag & STUDIOLIGHT_TYPE_STUDIO) != 0) {
603 }
605 return sl;
606 }
607 return nullptr;
608}
609
610static void studiolight_add_files_from_datafolder(const int folder_id,
611 const char *subfolder,
612 int flag)
613{
614 const std::optional<std::string> folder = BKE_appdir_folder_id(folder_id, subfolder);
615 if (!folder) {
616 return;
617 }
618
619 direntry *dirs;
620 const uint dirs_num = BLI_filelist_dir_contents(folder->c_str(), &dirs);
621 int i;
622 for (i = 0; i < dirs_num; i++) {
623 if (dirs[i].type & S_IFREG) {
624 studiolight_add_file(dirs[i].path, flag);
625 }
626 }
627 BLI_filelist_free(dirs, dirs_num);
628 dirs = nullptr;
629}
630
632{
633 /* Internal studiolights before external studio lights */
635 return 1;
636 }
637 return 0;
638}
639
640static int studiolight_cmp(const void *a, const void *b)
641{
642 const StudioLight *sl1 = static_cast<const StudioLight *>(a);
643 const StudioLight *sl2 = static_cast<const StudioLight *>(b);
644
645 const int flagorder1 = studiolight_flag_cmp_order(sl1);
646 const int flagorder2 = studiolight_flag_cmp_order(sl2);
647
648 if (flagorder1 < flagorder2) {
649 return -1;
650 }
651 if (flagorder1 > flagorder2) {
652 return 1;
653 }
654
655 return BLI_strcasecmp(sl1->name, sl2->name);
656}
657
658/* icons */
659
660/* Takes normalized uvs as parameter (range from 0 to 1).
661 * inner_edge and outer_edge are distances (from the center)
662 * in uv space for the alpha mask falloff. */
663static uint alpha_circle_mask(float u, float v, float inner_edge, float outer_edge)
664{
665 /* Coords from center. */
666 const float co[2] = {u - 0.5f, v - 0.5f};
667 float dist = len_v2(co);
668 float alpha = 1.0f + (inner_edge - dist) / (outer_edge - inner_edge);
669 uint mask = uint(floorf(255.0f * min_ff(max_ff(alpha, 0.0f), 1.0f)));
670 return mask << 24;
671}
672
673/* Percentage of the icon that the preview sphere covers. */
674#define STUDIOLIGHT_DIAMETER 0.95f
675/* Rescale coord around (0.5, 0.5) by STUDIOLIGHT_DIAMETER. */
676#define RESCALE_COORD(x) (x / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f)
677
678/* Remaps normalized UV [0..1] to a sphere normal around (0.5, 0.5) */
679static void sphere_normal_from_uv(float normal[3], float u, float v)
680{
681 normal[0] = u * 2.0f - 1.0f;
682 normal[1] = v * 2.0f - 1.0f;
683 float dist = len_v2(normal);
684 normal[2] = sqrtf(1.0f - square_f(dist));
685}
686
687static void studiolight_radiance_preview(uint *icon_buffer, StudioLight *sl)
688{
690
692 float dy = RESCALE_COORD(y);
693 float dx = RESCALE_COORD(x);
694
695 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
696 if (alphamask != 0) {
697 float normal[3], direction[3];
698 const float incoming[3] = {0.0f, 0.0f, -1.0f};
699 sphere_normal_from_uv(normal, dx, dy);
700 reflect_v3_v3v3(direction, incoming, normal);
701 /* We want to see horizon not poles. */
702 std::swap(direction[1], direction[2]);
703 direction[1] = -direction[1];
704
705 float4 color = studiolight_calculate_radiance(sl->equirect_radiance_buffer, direction);
706
707 *pixel = rgb_to_cpack(linearrgb_to_srgb(color[0]),
708 linearrgb_to_srgb(color[1]),
709 linearrgb_to_srgb(color[2])) |
710 alphamask;
711 }
712 else {
713 *pixel = 0x0;
714 }
715 }
717}
718
719static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool flipped)
720{
721 using namespace blender;
722
724
725 ImBuf *diffuse_buffer = sl->matcap_diffuse.ibuf;
726 ImBuf *specular_buffer = sl->matcap_specular.ibuf;
727
729 float dy = RESCALE_COORD(y);
730 float dx = RESCALE_COORD(x);
731 if (flipped) {
732 dx = 1.0f - dx;
733 }
734
735 float u = dx * diffuse_buffer->x - 1.0f;
736 float v = dy * diffuse_buffer->y - 1.0f;
737 float4 color = imbuf::interpolate_nearest_border_fl(diffuse_buffer, u, v);
738
739 if (specular_buffer) {
740 float4 specular = imbuf::interpolate_nearest_border_fl(specular_buffer, u, v);
741 add_v3_v3(color, specular);
742 }
743
744 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
745
746 *pixel = rgb_to_cpack(linearrgb_to_srgb(color[0]),
747 linearrgb_to_srgb(color[1]),
748 linearrgb_to_srgb(color[2])) |
749 alphamask;
750 }
752}
753
754static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl)
755{
757 float dy = RESCALE_COORD(y);
758 float dx = RESCALE_COORD(x);
759
760 uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
761 if (alphamask != 0) {
762 float normal[3], color[3];
763 sphere_normal_from_uv(normal, dx, dy);
764 /* We want to see horizon not poles. */
765 std::swap(normal[1], normal[2]);
766 normal[1] = -normal[1];
767
768 studiolight_lights_eval(sl, normal, color);
769
770 *pixel = rgb_to_cpack(linearrgb_to_srgb(color[0]),
771 linearrgb_to_srgb(color[1]),
772 linearrgb_to_srgb(color[2])) |
773 alphamask;
774 }
775 else {
776 *pixel = 0x0;
777 }
778 }
780}
781
782void BKE_studiolight_default(SolidLight lights[4], float light_ambient[3])
783{
784 copy_v3_fl3(light_ambient, 0.0, 0.0, 0.0);
785
786 lights[0].flag = 1;
787 lights[0].smooth = 0.526620f;
788 lights[0].col[0] = 0.033103f;
789 lights[0].col[1] = 0.033103f;
790 lights[0].col[2] = 0.033103f;
791 lights[0].spec[0] = 0.266761f;
792 lights[0].spec[1] = 0.266761f;
793 lights[0].spec[2] = 0.266761f;
794 lights[0].vec[0] = -0.352546f;
795 lights[0].vec[1] = 0.170931f;
796 lights[0].vec[2] = -0.920051f;
797
798 lights[1].flag = 1;
799 lights[1].smooth = 0.000000f;
800 lights[1].col[0] = 0.521083f;
801 lights[1].col[1] = 0.538226f;
802 lights[1].col[2] = 0.538226f;
803 lights[1].spec[0] = 0.599030f;
804 lights[1].spec[1] = 0.599030f;
805 lights[1].spec[2] = 0.599030f;
806 lights[1].vec[0] = -0.408163f;
807 lights[1].vec[1] = 0.346939f;
808 lights[1].vec[2] = 0.844415f;
809
810 lights[2].flag = 1;
811 lights[2].smooth = 0.478261f;
812 lights[2].col[0] = 0.038403f;
813 lights[2].col[1] = 0.034357f;
814 lights[2].col[2] = 0.049530f;
815 lights[2].spec[0] = 0.106102f;
816 lights[2].spec[1] = 0.125981f;
817 lights[2].spec[2] = 0.158523f;
818 lights[2].vec[0] = 0.521739f;
819 lights[2].vec[1] = 0.826087f;
820 lights[2].vec[2] = 0.212999f;
821
822 lights[3].flag = 1;
823 lights[3].smooth = 0.200000f;
824 lights[3].col[0] = 0.090838f;
825 lights[3].col[1] = 0.082080f;
826 lights[3].col[2] = 0.072255f;
827 lights[3].spec[0] = 0.106535f;
828 lights[3].spec[1] = 0.084771f;
829 lights[3].spec[2] = 0.066080f;
830 lights[3].vec[0] = 0.624519f;
831 lights[3].vec[1] = -0.562067f;
832 lights[3].vec[2] = -0.542269f;
833}
834
836{
837 /* Add default studio light */
840 STRNCPY(sl->name, "Default");
841
843
844 /* Go over the preset folder and add a studio-light for every image with its path. */
845 /* Also reserve icon space for it. */
864
865 /* sort studio lights on filename. */
867
869}
870
872{
873 while (StudioLight *sl = static_cast<StudioLight *>(BLI_pophead(&studiolights))) {
875 }
876}
877
879{
880 const char *default_name = "";
881
883 default_name = STUDIOLIGHT_WORLD_DEFAULT;
884 }
885 else if (flag & STUDIOLIGHT_TYPE_MATCAP) {
886 default_name = STUDIOLIGHT_MATCAP_DEFAULT;
887 }
888
890 if ((sl->flag & flag) && STREQ(sl->name, default_name)) {
891 return sl;
892 }
893 }
894
896 if (sl->flag & flag) {
897 return sl;
898 }
899 }
900 return nullptr;
901}
902
903StudioLight *BKE_studiolight_find(const char *name, int flag)
904{
906 if (STREQLEN(sl->name, name, FILE_MAXFILE)) {
907 if (sl->flag & flag) {
908 return sl;
909 }
910
911 /* flags do not match, so use default */
913 }
914 }
915 /* When not found, use the default studio light */
917}
918
920{
922 if (sl->index == index) {
923 return sl;
924 }
925 }
926 /* When not found, use the default studio light */
928}
929
934
935void BKE_studiolight_preview(uint *icon_buffer, StudioLight *sl, int icon_id_type)
936{
937 switch (icon_id_type) {
939 default: {
940 studiolight_radiance_preview(icon_buffer, sl);
941 break;
942 }
944 studiolight_irradiance_preview(icon_buffer, sl);
945 break;
946 }
948 studiolight_matcap_preview(icon_buffer, sl, false);
949 break;
950 }
952 studiolight_matcap_preview(icon_buffer, sl, true);
953 break;
954 }
955 }
957}
958
978
979/*
980 * Python API Functions.
981 */
982
984{
985 if (sl->flag & STUDIOLIGHT_USER_DEFINED) {
988 }
989}
990
991StudioLight *BKE_studiolight_load(const char *filepath, int type)
992{
994 return sl;
995}
996
998 const SolidLight light[4],
999 const float light_ambient[3])
1000{
1004
1005 char filename[FILE_MAXFILE];
1006 BLI_path_split_file_part(filepath, filename, FILE_MAXFILE);
1007 STRNCPY(sl->filepath, filepath);
1008 STRNCPY(sl->name, filename);
1009
1010 memcpy(sl->light, light, sizeof(*light) * 4);
1011 memcpy(sl->light_ambient, light_ambient, sizeof(*light_ambient) * 3);
1012
1014
1016 return sl;
1017}
1018
1020{
1021 static StudioLight sl = {nullptr};
1023
1024 memcpy(sl.light, U.light_param, sizeof(*sl.light) * 4);
1025 memcpy(sl.light_ambient, U.light_ambient, sizeof(*sl.light_ambient) * 3);
1026
1027 return &sl;
1028}
1029
1035
1037 StudioLightFreeFunction *free_function,
1038 void *data)
1039{
1040 sl->free_function = free_function;
1042}
1043
1045{
1046 BLI_assert(sl != nullptr);
1047 if (sl->icon_id_radiance == icon_id) {
1048 sl->icon_id_radiance = 0;
1049 }
1050 if (sl->icon_id_irradiance == icon_id) {
1051 sl->icon_id_irradiance = 0;
1052 }
1053 if (sl->icon_id_matcap == icon_id) {
1054 sl->icon_id_matcap = 0;
1055 }
1056 if (sl->icon_id_matcap_flipped == icon_id) {
1057 sl->icon_id_matcap_flipped = 0;
1058 }
1059}
@ 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:704
int BKE_icon_ensure_studio_light(struct StudioLight *sl, int id_type)
Definition icons.cc:586
#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:50
#define BLI_INLINE
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition BLI_dynstr.c:149
int BLI_dynstr_get_len(const DynStr *ds) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition BLI_dynstr.c:128
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.c:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
Definition BLI_dynstr.c:174
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:554
void BLI_file_free_lines(struct LinkNode *lines)
Definition storage.cc:600
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
Some types for dealing with directories.
#define LISTBASE_FOREACH(type, var, list)
void void BLI_listbase_sort(struct ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:251
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float square_f(float a)
#define M_PI
unsigned int rgb_to_cpack(float r, float g, float b)
float linearrgb_to_srgb(float c)
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
#define STRNCPY(dst, src)
Definition BLI_string.h:593
int char char int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
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)
void GPU_texture_update(GPUTexture *texture, eGPUDataFormat data_format, const void *data)
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)
Definition divers.cc:407
ImBuf * IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE])
Definition readimage.cc:146
void IMB_float_from_rect(ImBuf *ibuf)
Definition divers.cc:802
@ 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 MEM_SAFE_FREE(v)
ATTR_WARN_UNUSED_RESULT const BMVert * v
unsigned int U
Definition btGjkEpa3.h:78
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
local_group_size(16, 16) .push_constant(Type b
#define NL
#define powf(x, y)
#define atan2f(x, y)
#define floorf(x)
#define acosf(x)
#define sqrtf(x)
DOF_TILES_FLATTEN_GROUP_SIZE coc_tx GPU_R11F_G11F_B10F
draw_view in_light_buf[] float
#define str(s)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
struct ImBuf * IMB_allocFromBuffer(const uint8_t *, const float *, unsigned int, unsigned int, unsigned int)
void IMB_freeImBuf(ImBuf *)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
#define N
#define R
#define L
float4 interpolate_nearest_border_fl(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:27
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)
#define SEP_STR
Definition unit.cc:39
uint8_t flag
Definition wm_window.cc:138