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
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
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
706
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;
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
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
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)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a producing a negative Combine Generate a color from its and blue channels(Deprecated)") DefNode(ShaderNode
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v
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
ccl_device_inline float4 mask(const int4 mask, const float4 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)
#define SEP_STR
Definition unit.cc:39
uint8_t flag
Definition wm_window.cc:138