diff options
101 files changed, 4381 insertions, 376 deletions
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 82895acf25e..6b85ad3dde1 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -2601,7 +2601,6 @@ install_DEB() { fi # These libs should always be available in debian/ubuntu official repository... - OPENJPEG_DEV="libopenjpeg-dev" VORBIS_DEV="libvorbis-dev" OGG_DEV="libogg-dev" THEORA_DEV="libtheora-dev" @@ -2609,17 +2608,25 @@ install_DEB() { _packages="gawk cmake cmake-curses-gui build-essential libjpeg-dev libpng-dev libtiff-dev \ git libfreetype6-dev libx11-dev flex bison libtbb-dev libxxf86vm-dev \ libxcursor-dev libxi-dev wget libsqlite3-dev libxrandr-dev libxinerama-dev \ - libbz2-dev libncurses5-dev libssl-dev liblzma-dev libreadline-dev $OPENJPEG_DEV \ + libbz2-dev libncurses5-dev libssl-dev liblzma-dev libreadline-dev \ libopenal-dev libglew-dev yasm $THEORA_DEV $VORBIS_DEV $OGG_DEV \ libsdl1.2-dev libfftw3-dev patch bzip2 libxml2-dev libtinyxml-dev libjemalloc-dev" # libglewmx-dev (broken in deb testing currently...) - OPENJPEG_USE=true VORBIS_USE=true OGG_USE=true THEORA_USE=true PRINT "" + # New Ubuntu crap (17.04 and more) have no openjpeg lib! + OPENJPEG_DEV="libopenjpeg-dev" + check_package_DEB $OPENJPEG_DEV + if [ $? -eq 0 ]; then + _packages="$_packages $OPENJPEG_DEV" + OPENJPEG_USE=true + fi + + PRINT "" # Some not-so-old distro (ubuntu 12.4) do not have it, do not fail in this case, just warn. YAMLCPP_DEV="libyaml-cpp-dev" check_package_DEB $YAMLCPP_DEV diff --git a/build_files/buildbot/slave_compile.py b/build_files/buildbot/slave_compile.py index 4fb05c9f977..0e264a752d5 100644 --- a/build_files/buildbot/slave_compile.py +++ b/build_files/buildbot/slave_compile.py @@ -74,6 +74,9 @@ if 'cmake' in builder: cmake_extra_options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64') cmake_extra_options.append('-DWITH_CODEC_QUICKTIME=OFF') cmake_extra_options.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6') + cmake_extra_options.append('-DCUDA_HOST_COMPILER=/usr/local/cuda-hack/clang') + cmake_extra_options.append('-DCUDA_NVCC_EXECUTABLE=/usr/local/cuda-hack/nvcc') + elif builder.startswith('win'): @@ -177,7 +180,7 @@ if 'cmake' in builder: os.remove('CMakeCache.txt') retcode = subprocess.call(target_chroot_prefix + ['cmake', blender_dir] + target_cmake_options) if retcode != 0: - print('Condifuration FAILED!') + print('Configuration FAILED!') sys.exit(retcode) if 'win32' in builder or 'win64' in builder: diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index d05699236cc..2810ad15b9a 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -379,27 +379,16 @@ Object *BlenderSync::sync_object(BL::Object& b_parent, } } - /* random number */ - object->random_id = hash_string(object->name.c_str()); - - if(persistent_id) { - for(int i = 0; i < OBJECT_PERSISTENT_ID_SIZE; i++) - object->random_id = hash_int_2d(object->random_id, persistent_id[i]); - } - else - object->random_id = hash_int_2d(object->random_id, 0); - - if(b_parent.ptr.data != b_ob.ptr.data) - object->random_id ^= hash_int(hash_string(b_parent.name().c_str())); - - /* dupli texture coordinates */ + /* dupli texture coordinates and random_id */ if(b_dupli_ob) { object->dupli_generated = 0.5f*get_float3(b_dupli_ob.orco()) - make_float3(0.5f, 0.5f, 0.5f); object->dupli_uv = get_float2(b_dupli_ob.uv()); + object->random_id = b_dupli_ob.random_id(); } else { object->dupli_generated = make_float3(0.0f, 0.0f, 0.0f); object->dupli_uv = make_float2(0.0f, 0.0f); + object->random_id = hash_int_2d(hash_string(object->name.c_str()), 0); } object->tag_update(scene); diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 3f04f11aab4..bdbab1006c0 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -521,6 +521,19 @@ static ShaderNode *add_node(Scene *scene, } node = hair; } + else if(b_node.is_a(&RNA_ShaderNodeBsdfPrincipled)) { + BL::ShaderNodeBsdfPrincipled b_principled_node(b_node); + PrincipledBsdfNode *principled = new PrincipledBsdfNode(); + switch (b_principled_node.distribution()) { + case BL::ShaderNodeBsdfPrincipled::distribution_GGX: + principled->distribution = CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID; + break; + case BL::ShaderNodeBsdfPrincipled::distribution_MULTI_GGX: + principled->distribution = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID; + break; + } + node = principled; + } else if(b_node.is_a(&RNA_ShaderNodeBsdfTranslucent)) { node = new TranslucentBsdfNode(); } diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp index d7ce2603d7e..34ed2c67a44 100644 --- a/intern/cycles/device/opencl/opencl_util.cpp +++ b/intern/cycles/device/opencl/opencl_util.cpp @@ -902,7 +902,7 @@ bool OpenCLInfo::get_platform_name(cl_platform_id platform_id, string OpenCLInfo::get_platform_name(cl_platform_id platform_id) { string platform_name; - if (!get_platform_name(platform_id, &platform_name)) { + if(!get_platform_name(platform_id, &platform_name)) { return ""; } return platform_name; diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 63a6085e2c9..9783c0a1978 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -140,6 +140,8 @@ set(SRC_CLOSURE_HEADERS closure/bssrdf.h closure/emissive.h closure/volume.h + closure/bsdf_principled_diffuse.h + closure/bsdf_principled_sheen.h ) set(SRC_SVM_HEADERS diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index 4dea651ae4d..5f369cfc94d 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -27,6 +27,8 @@ #include "kernel/closure/bsdf_ashikhmin_shirley.h" #include "kernel/closure/bsdf_toon.h" #include "kernel/closure/bsdf_hair.h" +#include "kernel/closure/bsdf_principled_diffuse.h" +#include "kernel/closure/bsdf_principled_sheen.h" #ifdef __SUBSURFACE__ # include "kernel/closure/bssrdf.h" #endif @@ -86,16 +88,21 @@ ccl_device_forceinline int bsdf_sample(KernelGlobals *kg, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf); break; case CLOSURE_BSDF_MICROFACET_GGX_ID: + case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID: + case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID: case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID: + case CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: label = bsdf_microfacet_ggx_sample(kg, sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf); break; case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: label = bsdf_microfacet_multi_ggx_sample(kg, sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf, &sd->lcg_state); break; case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: label = bsdf_microfacet_multi_ggx_glass_sample(kg, sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf, &sd->lcg_state); break; @@ -130,6 +137,15 @@ ccl_device_forceinline int bsdf_sample(KernelGlobals *kg, label = bsdf_hair_transmission_sample(sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf); break; + case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: + case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: + label = bsdf_principled_diffuse_sample(sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, + eval, omega_in, &domega_in->dx, &domega_in->dy, pdf); + break; + case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: + label = bsdf_principled_sheen_sample(sc, sd->Ng, sd->I, sd->dI.dx, sd->dI.dy, randu, randv, + eval, omega_in, &domega_in->dx, &domega_in->dy, pdf); + break; #endif #ifdef __VOLUME__ case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: @@ -188,14 +204,19 @@ float3 bsdf_eval(KernelGlobals *kg, eval = bsdf_transparent_eval_reflect(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_MICROFACET_GGX_ID: + case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID: + case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID: case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID: + case CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: eval = bsdf_microfacet_ggx_eval_reflect(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: eval = bsdf_microfacet_multi_ggx_eval_reflect(sc, sd->I, omega_in, pdf, &sd->lcg_state); break; case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: eval = bsdf_microfacet_multi_ggx_glass_eval_reflect(sc, sd->I, omega_in, pdf, &sd->lcg_state); break; case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: @@ -222,6 +243,13 @@ float3 bsdf_eval(KernelGlobals *kg, case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: eval = bsdf_hair_transmission_eval_reflect(sc, sd->I, omega_in, pdf); break; + case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: + case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: + eval = bsdf_principled_diffuse_eval_reflect(sc, sd->I, omega_in, pdf); + break; + case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: + eval = bsdf_principled_sheen_eval_reflect(sc, sd->I, omega_in, pdf); + break; #endif #ifdef __VOLUME__ case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: @@ -256,14 +284,19 @@ float3 bsdf_eval(KernelGlobals *kg, eval = bsdf_transparent_eval_transmit(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_MICROFACET_GGX_ID: + case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID: + case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID: case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID: + case CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: eval = bsdf_microfacet_ggx_eval_transmit(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: eval = bsdf_microfacet_multi_ggx_eval_transmit(sc, sd->I, omega_in, pdf, &sd->lcg_state); break; case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: eval = bsdf_microfacet_multi_ggx_glass_eval_transmit(sc, sd->I, omega_in, pdf, &sd->lcg_state); break; case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: @@ -290,6 +323,13 @@ float3 bsdf_eval(KernelGlobals *kg, case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: eval = bsdf_hair_transmission_eval_transmit(sc, sd->I, omega_in, pdf); break; + case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: + case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: + eval = bsdf_principled_diffuse_eval_transmit(sc, sd->I, omega_in, pdf); + break; + case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: + eval = bsdf_principled_sheen_eval_transmit(sc, sd->I, omega_in, pdf); + break; #endif #ifdef __VOLUME__ case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: @@ -311,11 +351,16 @@ ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness) #ifdef __SVM__ switch(sc->type) { case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: bsdf_microfacet_multi_ggx_blur(sc, roughness); break; case CLOSURE_BSDF_MICROFACET_GGX_ID: + case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID: + case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID: case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID: + case CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: bsdf_microfacet_ggx_blur(sc, roughness); break; @@ -349,10 +394,15 @@ ccl_device bool bsdf_merge(ShaderClosure *a, ShaderClosure *b) case CLOSURE_BSDF_REFLECTION_ID: case CLOSURE_BSDF_REFRACTION_ID: case CLOSURE_BSDF_MICROFACET_GGX_ID: + case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID: + case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID: case CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID: + case CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: + case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID: diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index 5f17b377d97..1e1e20f024b 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -36,7 +36,8 @@ CCL_NAMESPACE_BEGIN typedef ccl_addr_space struct MicrofacetExtra { - float3 color; + float3 color, cspec0; + float clearcoat; } MicrofacetExtra; typedef ccl_addr_space struct MicrofacetBsdf { @@ -232,6 +233,36 @@ ccl_device_forceinline float3 microfacet_sample_stretched( return normalize(make_float3(-slope_x, -slope_y, 1.0f)); } +/* Calculate the reflection color + * + * If fresnel is used, the color is an interpolation of the F0 color and white + * with respect to the fresnel + * + * Else it is simply white + */ +ccl_device_forceinline float3 reflection_color(const MicrofacetBsdf *bsdf, float3 L, float3 H) { + float3 F = make_float3(1.0f, 1.0f, 1.0f); + bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID + || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID + || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID); + + if(use_fresnel) { + float F0 = fresnel_dielectric_cos(1.0f, bsdf->ior); + + F = interpolate_fresnel_color(L, H, bsdf->ior, F0, bsdf->extra->cspec0); + } + + return F; +} + +ccl_device_forceinline float D_GTR1(float NdotH, float alpha) +{ + if (alpha >= 1.0f) return M_1_PI_F; + float alpha2 = alpha*alpha; + float t = 1.0f + (alpha2 - 1.0f) * NdotH*NdotH; + return (alpha2 - 1.0f) / (M_PI_F * logf(alpha2) * t); +} + /* GGX microfacet with Smith shadow-masking from: * * Microfacet Models for Refraction through Rough Surfaces @@ -247,14 +278,44 @@ ccl_device_forceinline float3 microfacet_sample_stretched( ccl_device int bsdf_microfacet_ggx_setup(MicrofacetBsdf *bsdf) { + bsdf->extra = NULL; + bsdf->alpha_x = saturate(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; - + bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_ID; return SD_BSDF|SD_BSDF_HAS_EVAL; } +ccl_device int bsdf_microfacet_ggx_fresnel_setup(MicrofacetBsdf *bsdf) +{ + bsdf->extra->cspec0.x = saturate(bsdf->extra->cspec0.x); + bsdf->extra->cspec0.y = saturate(bsdf->extra->cspec0.y); + bsdf->extra->cspec0.z = saturate(bsdf->extra->cspec0.z); + + bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_y = bsdf->alpha_x; + + bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID; + + return SD_BSDF|SD_BSDF_HAS_EVAL; +} + +ccl_device int bsdf_microfacet_ggx_clearcoat_setup(MicrofacetBsdf *bsdf) +{ + bsdf->extra->cspec0.x = saturate(bsdf->extra->cspec0.x); + bsdf->extra->cspec0.y = saturate(bsdf->extra->cspec0.y); + bsdf->extra->cspec0.z = saturate(bsdf->extra->cspec0.z); + + bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_y = bsdf->alpha_x; + + bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID; + + return SD_BSDF|SD_BSDF_HAS_EVAL; +} + ccl_device bool bsdf_microfacet_merge(const ShaderClosure *a, const ShaderClosure *b) { const MicrofacetBsdf *bsdf_a = (const MicrofacetBsdf*)a; @@ -272,16 +333,34 @@ ccl_device bool bsdf_microfacet_merge(const ShaderClosure *a, const ShaderClosur ccl_device int bsdf_microfacet_ggx_aniso_setup(MicrofacetBsdf *bsdf) { + bsdf->extra = NULL; + bsdf->alpha_x = saturate(bsdf->alpha_x); bsdf->alpha_y = saturate(bsdf->alpha_y); - + bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID; return SD_BSDF|SD_BSDF_HAS_EVAL; } +ccl_device int bsdf_microfacet_ggx_aniso_fresnel_setup(MicrofacetBsdf *bsdf) +{ + bsdf->extra->cspec0.x = saturate(bsdf->extra->cspec0.x); + bsdf->extra->cspec0.y = saturate(bsdf->extra->cspec0.y); + bsdf->extra->cspec0.z = saturate(bsdf->extra->cspec0.z); + + bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_y = saturate(bsdf->alpha_y); + + bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID; + + return SD_BSDF|SD_BSDF_HAS_EVAL; +} + ccl_device int bsdf_microfacet_ggx_refraction_setup(MicrofacetBsdf *bsdf) { + bsdf->extra = NULL; + bsdf->alpha_x = saturate(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; @@ -318,6 +397,8 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, cons float alpha2 = alpha_x * alpha_y; float D, G1o, G1i; + bool is_principled_clearcoat = (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID); + if(alpha_x == alpha_y) { /* isotropic * eq. 20: (F*G*D)/(4*in*on) @@ -326,7 +407,18 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, cons float cosThetaM2 = cosThetaM * cosThetaM; float cosThetaM4 = cosThetaM2 * cosThetaM2; float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2; - D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2)); + + if(is_principled_clearcoat) { + /* use GTR1 for clearcoat */ + D = D_GTR1(cosThetaM, bsdf->alpha_x); + + /* the alpha value for clearcoat is a fixed 0.25 => alpha2 = 0.25 * 0.25 */ + alpha2 = 0.0625f; + } + else { + /* use GTR2 otherwise */ + D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2)); + } /* eq. 34: now calculate G1(i,m) and G1(o,m) */ G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO))); @@ -373,7 +465,13 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, cons /* eq. 20 */ float common = D * 0.25f / cosNO; - float out = G * common; + + float3 F = reflection_color(bsdf, omega_in, m); + if(is_principled_clearcoat) { + F *= 0.25f * bsdf->extra->clearcoat; + } + + float3 out = F * G * common; /* eq. 2 in distribution of visible normals sampling * pm = Dw = G1o * dot(m, I) * D / dot(N, I); */ @@ -383,7 +481,7 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, cons * pdf = pm * 0.25 / dot(m, I); */ *pdf = G1o * common; - return make_float3(out, out, out); + return out; } return make_float3(0.0f, 0.0f, 0.0f); @@ -488,6 +586,17 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, const ShaderClosure /* some high number for MIS */ *pdf = 1e6f; *eval = make_float3(1e6f, 1e6f, 1e6f); + + bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID + || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID + || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID); + + /* if fresnel is used, calculate the color with reflection_color(...) */ + if(use_fresnel) { + *pdf = 1.0f; + *eval = reflection_color(bsdf, *omega_in, m); + } + label = LABEL_REFLECT | LABEL_SINGULAR; } else { @@ -496,16 +605,32 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, const ShaderClosure float alpha2 = alpha_x * alpha_y; float D, G1i; + bool is_principled_clearcoat = (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID); + if(alpha_x == alpha_y) { /* isotropic */ float cosThetaM2 = cosThetaM * cosThetaM; float cosThetaM4 = cosThetaM2 * cosThetaM2; float tanThetaM2 = 1/(cosThetaM2) - 1; - D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2)); /* eval BRDF*cosNI */ float cosNI = dot(N, *omega_in); + if(is_principled_clearcoat) { + /* use GTR1 for clearcoat */ + D = D_GTR1(cosThetaM, bsdf->alpha_x); + + /* the alpha value for clearcoat is a fixed 0.25 => alpha2 = 0.25 * 0.25 */ + alpha2 = 0.0625f; + + /* recalculate G1o */ + G1o = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO))); + } + else { + /* use GTR2 otherwise */ + D = alpha2 / (M_PI_F * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2)); + } + /* eq. 34: now calculate G1(i,m) */ G1i = 2 / (1 + safe_sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI))); } @@ -537,10 +662,14 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, const ShaderClosure /* see eval function for derivation */ float common = (G1o * D) * 0.25f / cosNO; - float out = G1i * common; *pdf = common; - *eval = make_float3(out, out, out); + float3 F = reflection_color(bsdf, *omega_in, m); + if(is_principled_clearcoat) { + F *= 0.25f * bsdf->extra->clearcoat; + } + + *eval = G1i * common * F; } #ifdef __RAY_DIFFERENTIALS__ diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index 194503e6e87..30644946840 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -345,8 +345,9 @@ ccl_device int bsdf_microfacet_multi_ggx_common_setup(MicrofacetBsdf *bsdf) bsdf->extra->color.x = saturate(bsdf->extra->color.x); bsdf->extra->color.y = saturate(bsdf->extra->color.y); bsdf->extra->color.z = saturate(bsdf->extra->color.z); - - bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID; + bsdf->extra->cspec0.x = saturate(bsdf->extra->cspec0.x); + bsdf->extra->cspec0.y = saturate(bsdf->extra->cspec0.y); + bsdf->extra->cspec0.z = saturate(bsdf->extra->cspec0.z); return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_NEEDS_LCG; } @@ -356,6 +357,18 @@ ccl_device int bsdf_microfacet_multi_ggx_aniso_setup(MicrofacetBsdf *bsdf) if(is_zero(bsdf->T)) bsdf->T = make_float3(1.0f, 0.0f, 0.0f); + bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID; + + return bsdf_microfacet_multi_ggx_common_setup(bsdf); +} + +ccl_device int bsdf_microfacet_multi_ggx_aniso_fresnel_setup(MicrofacetBsdf *bsdf) +{ + if(is_zero(bsdf->T)) + bsdf->T = make_float3(1.0f, 0.0f, 0.0f); + + bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID; + return bsdf_microfacet_multi_ggx_common_setup(bsdf); } @@ -363,6 +376,26 @@ ccl_device int bsdf_microfacet_multi_ggx_setup(MicrofacetBsdf *bsdf) { bsdf->alpha_y = bsdf->alpha_x; + bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID; + + return bsdf_microfacet_multi_ggx_common_setup(bsdf); +} + +ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(MicrofacetBsdf *bsdf) +{ + bsdf->alpha_y = bsdf->alpha_x; + + bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID; + + return bsdf_microfacet_multi_ggx_common_setup(bsdf); +} + +ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(MicrofacetBsdf *bsdf) +{ + bsdf->alpha_y = bsdf->alpha_x; + + bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID; + return bsdf_microfacet_multi_ggx_common_setup(bsdf); } @@ -378,6 +411,8 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc return make_float3(0.0f, 0.0f, 0.0f); } + bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID); + bool is_aniso = (bsdf->alpha_x != bsdf->alpha_y); float3 X, Y, Z; Z = bsdf->N; @@ -393,7 +428,7 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc *pdf = mf_ggx_aniso_pdf(localI, localO, make_float2(bsdf->alpha_x, bsdf->alpha_y)); else *pdf = mf_ggx_pdf(localI, localO, bsdf->alpha_x); - return mf_eval_glossy(localI, localO, true, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, NULL, NULL); + return mf_eval_glossy(localI, localO, true, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, NULL, NULL, bsdf->ior, use_fresnel, bsdf->extra->cspec0); } ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf, ccl_addr_space uint *lcg_state) @@ -410,6 +445,8 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals *kg, const ShaderC return LABEL_REFLECT|LABEL_SINGULAR; } + bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID); + bool is_aniso = (bsdf->alpha_x != bsdf->alpha_y); if(is_aniso) make_orthonormals_tangent(Z, bsdf->T, &X, &Y); @@ -419,7 +456,7 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals *kg, const ShaderC float3 localI = make_float3(dot(I, X), dot(I, Y), dot(I, Z)); float3 localO; - *eval = mf_sample_glossy(localI, &localO, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, NULL, NULL); + *eval = mf_sample_glossy(localI, &localO, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, NULL, NULL, bsdf->ior, use_fresnel, bsdf->extra->cspec0); if(is_aniso) *pdf = mf_ggx_aniso_pdf(localI, localO, make_float2(bsdf->alpha_x, bsdf->alpha_y)); else @@ -450,6 +487,23 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_setup(MicrofacetBsdf *bsdf) return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_NEEDS_LCG; } +ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(MicrofacetBsdf *bsdf) +{ + bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); + bsdf->alpha_y = bsdf->alpha_x; + bsdf->ior = max(0.0f, bsdf->ior); + bsdf->extra->color.x = saturate(bsdf->extra->color.x); + bsdf->extra->color.y = saturate(bsdf->extra->color.y); + bsdf->extra->color.z = saturate(bsdf->extra->color.z); + bsdf->extra->cspec0.x = saturate(bsdf->extra->cspec0.x); + bsdf->extra->cspec0.y = saturate(bsdf->extra->cspec0.y); + bsdf->extra->cspec0.z = saturate(bsdf->extra->cspec0.z); + + bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID; + + return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSDF_NEEDS_LCG; +} + ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_transmit(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf, ccl_addr_space uint *lcg_state) { const MicrofacetBsdf *bsdf = (const MicrofacetBsdf*)sc; @@ -465,7 +519,7 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_transmit(const ShaderClos float3 localO = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z)); *pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior); - return mf_eval_glass(localI, localO, false, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior); + return mf_eval_glass(localI, localO, false, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior, false, bsdf->extra->color); } ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosure *sc, const float3 I, const float3 omega_in, float *pdf, ccl_addr_space uint *lcg_state) { @@ -475,6 +529,8 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosu return make_float3(0.0f, 0.0f, 0.0f); } + bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID); + float3 X, Y, Z; Z = bsdf->N; make_orthonormals(Z, &X, &Y); @@ -483,7 +539,7 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosu float3 localO = make_float3(dot(omega_in, X), dot(omega_in, Y), dot(omega_in, Z)); *pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior); - return mf_eval_glass(localI, localO, true, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior); + return mf_eval_glass(localI, localO, true, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior, use_fresnel, bsdf->extra->cspec0); } ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg, const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf, ccl_addr_space uint *lcg_state) @@ -525,12 +581,14 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg, const S } } + bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID); + make_orthonormals(Z, &X, &Y); float3 localI = make_float3(dot(I, X), dot(I, Y), dot(I, Z)); float3 localO; - *eval = mf_sample_glass(localI, &localO, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior); + *eval = mf_sample_glass(localI, &localO, bsdf->extra->color, bsdf->alpha_x, bsdf->alpha_y, lcg_state, bsdf->ior, use_fresnel, bsdf->extra->cspec0); *pdf = mf_glass_pdf(localI, localO, bsdf->alpha_x, bsdf->ior); *eval *= *pdf; diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h index 8054fa8e849..16d900088cb 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h @@ -35,8 +35,13 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)( ccl_addr_space uint *lcg_state #ifdef MF_MULTI_GLASS , const float eta + , bool use_fresnel + , const float3 cspec0 #elif defined(MF_MULTI_GLOSSY) - , float3 *n, float3 *k + , float3 *n, float3 *k + , const float eta + , bool use_fresnel + , const float3 cspec0 #endif ) { @@ -71,12 +76,20 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)( /* Analytically compute single scattering for lower noise. */ float3 eval; + float3 throughput = make_float3(1.0f, 1.0f, 1.0f); #ifdef MF_MULTI_GLASS eval = mf_eval_phase_glass(-wi, lambda_r, wo, wo_outside, alpha, eta); if(wo_outside) eval *= -lambda_r / (shadowing_lambda - lambda_r); else eval *= -lambda_r * beta(-lambda_r, shadowing_lambda+1.0f); + + float F0 = fresnel_dielectric_cos(1.0f, eta); + if(use_fresnel) { + throughput = interpolate_fresnel_color(wi, normalize(wi + wo), eta, F0, cspec0); + + eval *= throughput; + } #elif defined(MF_MULTI_DIFFUSE) /* Diffuse has no special closed form for the single scattering bounce */ eval = make_float3(0.0f, 0.0f, 0.0f); @@ -94,6 +107,13 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)( else { eval = make_float3(val, val, val); } + + float F0 = fresnel_dielectric_cos(1.0f, eta); + if(use_fresnel) { + throughput = interpolate_fresnel_color(wi, wh, eta, F0, cspec0); + + eval = throughput * val; + } #endif float3 wr = -wi; @@ -101,7 +121,6 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)( float C1_r = 1.0f; float G1_r = 0.0f; bool outside = true; - float3 throughput = make_float3(1.0f, 1.0f, 1.0f); for(int order = 0; order < 10; order++) { /* Sample microfacet height and normal */ @@ -117,6 +136,18 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)( eval += throughput * G2_G1 * mf_eval_phase_diffuse(wo, wm); } #endif +#ifdef MF_MULTI_GLASS + if(order == 0 && use_fresnel) { + /* Evaluate amount of scattering towards wo on this microfacet. */ + float3 phase; + if(outside) + phase = mf_eval_phase_glass(wr, lambda_r, wo, wo_outside, alpha, eta); + else + phase = mf_eval_phase_glass(wr, lambda_r, -wo, !wo_outside, alpha, 1.0f / eta); + + eval = throughput * phase * mf_G1(wo_outside ? wo : -wo, mf_C1((outside == wo_outside) ? hr : -hr), shadowing_lambda); + } +#endif if(order > 0) { /* Evaluate amount of scattering towards wo on this microfacet. */ float3 phase; @@ -136,23 +167,39 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)( /* Bounce from the microfacet. */ #ifdef MF_MULTI_GLASS bool next_outside; + float3 wi_prev = -wr; wr = mf_sample_phase_glass(-wr, outside? eta: 1.0f/eta, wm, lcg_step_float_addrspace(lcg_state), &next_outside); if(!next_outside) { outside = !outside; wr = -wr; hr = -hr; } + + if(use_fresnel && !next_outside) { + throughput *= color; + } + else if(use_fresnel && order > 0) { + throughput *= interpolate_fresnel_color(wi_prev, wm, eta, F0, cspec0); + } #elif defined(MF_MULTI_DIFFUSE) wr = mf_sample_phase_diffuse(wm, lcg_step_float_addrspace(lcg_state), lcg_step_float_addrspace(lcg_state)); #else /* MF_MULTI_GLOSSY */ + if(use_fresnel && order > 0) { + throughput *= interpolate_fresnel_color(-wr, wm, eta, F0, cspec0); + } wr = mf_sample_phase_glossy(-wr, n, k, &throughput, wm); #endif lambda_r = mf_lambda(wr, alpha); +#if defined(MF_MULTI_GLOSSY) || defined(MF_MULTI_GLASS) + if(!use_fresnel) + throughput *= color; +#else throughput *= color; +#endif C1_r = mf_C1(hr); G1_r = mf_G1(wr, C1_r, lambda_r); @@ -171,8 +218,13 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)( ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, float3 *wo, const float3 color, const float alpha_x, const float alpha_y, ccl_addr_space uint *lcg_state #ifdef MF_MULTI_GLASS , const float eta + , bool use_fresnel + , const float3 cspec0 #elif defined(MF_MULTI_GLOSSY) , float3 *n, float3 *k + , const float eta + , bool use_fresnel + , const float3 cspec0 #endif ) { @@ -185,6 +237,17 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, float3 float C1_r = 1.0f; float G1_r = 0.0f; bool outside = true; +#ifdef MF_MULTI_GLASS + float F0 = fresnel_dielectric_cos(1.0f, eta); + if(use_fresnel) { + throughput = interpolate_fresnel_color(wi, normalize(wi + wr), eta, F0, cspec0); + } +#elif defined(MF_MULTI_GLOSSY) + float F0 = fresnel_dielectric_cos(1.0f, eta); + if(use_fresnel) { + throughput = interpolate_fresnel_color(wi, normalize(wi + wr), eta, F0, cspec0); + } +#endif int order; for(order = 0; order < 10; order++) { @@ -199,23 +262,51 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, float3 lcg_step_float_addrspace(lcg_state))); /* First-bounce color is already accounted for in mix weight. */ +#if defined(MF_MULTI_GLASS) || defined(MF_MULTI_GLOSSY) + if(!use_fresnel && order > 0) + throughput *= color; +#else if(order > 0) throughput *= color; +#endif /* Bounce from the microfacet. */ #ifdef MF_MULTI_GLASS bool next_outside; + float3 wi_prev = -wr; wr = mf_sample_phase_glass(-wr, outside? eta: 1.0f/eta, wm, lcg_step_float_addrspace(lcg_state), &next_outside); if(!next_outside) { hr = -hr; wr = -wr; outside = !outside; } + + if(use_fresnel) { + if(!next_outside) { + throughput *= color; + } + else { + float3 t_color = interpolate_fresnel_color(wi_prev, wm, eta, F0, cspec0); + + if(order == 0) + throughput = t_color; + else + throughput *= t_color; + } + } #elif defined(MF_MULTI_DIFFUSE) wr = mf_sample_phase_diffuse(wm, lcg_step_float_addrspace(lcg_state), lcg_step_float_addrspace(lcg_state)); #else /* MF_MULTI_GLOSSY */ + if(use_fresnel) { + float3 t_color = interpolate_fresnel_color(-wr, wm, eta, F0, cspec0); + + if(order == 0) + throughput = t_color; + else + throughput *= t_color; + } wr = mf_sample_phase_glossy(-wr, n, k, &throughput, wm); #endif diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h new file mode 100644 index 00000000000..215c32e1ffb --- /dev/null +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -0,0 +1,119 @@ +/* + * Copyright 2011-2017 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BSDF_PRINCIPLED_DIFFUSE_H__ +#define __BSDF_PRINCIPLED_DIFFUSE_H__ + +/* DISNEY PRINCIPLED DIFFUSE BRDF + * + * Shading model by Brent Burley (Disney): "Physically Based Shading at Disney" (2012) + */ + +CCL_NAMESPACE_BEGIN + +typedef ccl_addr_space struct PrincipledDiffuseBsdf { + SHADER_CLOSURE_BASE; + + float roughness; +} PrincipledDiffuseBsdf; + +ccl_device float3 calculate_principled_diffuse_brdf(const PrincipledDiffuseBsdf *bsdf, + float3 N, float3 V, float3 L, float3 H, float *pdf) +{ + float NdotL = max(dot(N, L), 0.0f); + float NdotV = max(dot(N, V), 0.0f); + + if(NdotL < 0 || NdotV < 0) { + *pdf = 0.0f; + return make_float3(0.0f, 0.0f, 0.0f); + } + + float LdotH = dot(L, H); + + float FL = schlick_fresnel(NdotL), FV = schlick_fresnel(NdotV); + const float Fd90 = 0.5f + 2.0f * LdotH*LdotH * bsdf->roughness; + float Fd = (1.0f * (1.0f - FL) + Fd90 * FL) * (1.0f * (1.0f - FV) + Fd90 * FV); + + float value = M_1_PI_F * NdotL * Fd; + + return make_float3(value, value, value); +} + +ccl_device int bsdf_principled_diffuse_setup(PrincipledDiffuseBsdf *bsdf) +{ + bsdf->type = CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID; + return SD_BSDF|SD_BSDF_HAS_EVAL; +} + +ccl_device float3 bsdf_principled_diffuse_eval_reflect(const ShaderClosure *sc, const float3 I, + const float3 omega_in, float *pdf) +{ + const PrincipledDiffuseBsdf *bsdf = (const PrincipledDiffuseBsdf *)sc; + + float3 N = bsdf->N; + float3 V = I; // outgoing + float3 L = omega_in; // incoming + float3 H = normalize(L + V); + + if(dot(N, omega_in) > 0.0f) { + *pdf = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F; + return calculate_principled_diffuse_brdf(bsdf, N, V, L, H, pdf); + } + else { + *pdf = 0.0f; + return make_float3(0.0f, 0.0f, 0.0f); + } +} + +ccl_device float3 bsdf_principled_diffuse_eval_transmit(const ShaderClosure *sc, const float3 I, + const float3 omega_in, float *pdf) +{ + return make_float3(0.0f, 0.0f, 0.0f); +} + +ccl_device int bsdf_principled_diffuse_sample(const ShaderClosure *sc, + float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, + float3 *eval, float3 *omega_in, float3 *domega_in_dx, + float3 *domega_in_dy, float *pdf) +{ + const PrincipledDiffuseBsdf *bsdf = (const PrincipledDiffuseBsdf *)sc; + + float3 N = bsdf->N; + + sample_cos_hemisphere(N, randu, randv, omega_in, pdf); + + if(dot(Ng, *omega_in) > 0) { + float3 H = normalize(I + *omega_in); + + *eval = calculate_principled_diffuse_brdf(bsdf, N, I, *omega_in, H, pdf); + +#ifdef __RAY_DIFFERENTIALS__ + // TODO: find a better approximation for the diffuse bounce + *domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx); + *domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy); +#endif + } + else { + *pdf = 0.0f; + } + return LABEL_REFLECT|LABEL_DIFFUSE; +} + +CCL_NAMESPACE_END + +#endif /* __BSDF_PRINCIPLED_DIFFUSE_H__ */ + + diff --git a/intern/cycles/kernel/closure/bsdf_principled_sheen.h b/intern/cycles/kernel/closure/bsdf_principled_sheen.h new file mode 100644 index 00000000000..f4476bfecd0 --- /dev/null +++ b/intern/cycles/kernel/closure/bsdf_principled_sheen.h @@ -0,0 +1,113 @@ +/* + * Copyright 2011-2017 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BSDF_PRINCIPLED_SHEEN_H__ +#define __BSDF_PRINCIPLED_SHEEN_H__ + +/* DISNEY PRINCIPLED SHEEN BRDF + * + * Shading model by Brent Burley (Disney): "Physically Based Shading at Disney" (2012) + */ + +CCL_NAMESPACE_BEGIN + +typedef ccl_addr_space struct PrincipledSheenBsdf { + SHADER_CLOSURE_BASE; +} PrincipledSheenBsdf; + +ccl_device float3 calculate_principled_sheen_brdf(const PrincipledSheenBsdf *bsdf, + float3 N, float3 V, float3 L, float3 H, float *pdf) +{ + float NdotL = dot(N, L); + float NdotV = dot(N, V); + + if(NdotL < 0 || NdotV < 0) { + *pdf = 0.0f; + return make_float3(0.0f, 0.0f, 0.0f); + } + + float LdotH = dot(L, H); + + float value = schlick_fresnel(LdotH) * NdotL; + + return make_float3(value, value, value); +} + +ccl_device int bsdf_principled_sheen_setup(PrincipledSheenBsdf *bsdf) +{ + bsdf->type = CLOSURE_BSDF_PRINCIPLED_SHEEN_ID; + return SD_BSDF|SD_BSDF_HAS_EVAL; +} + +ccl_device float3 bsdf_principled_sheen_eval_reflect(const ShaderClosure *sc, const float3 I, + const float3 omega_in, float *pdf) +{ + const PrincipledSheenBsdf *bsdf = (const PrincipledSheenBsdf *)sc; + + float3 N = bsdf->N; + float3 V = I; // outgoing + float3 L = omega_in; // incoming + float3 H = normalize(L + V); + + if(dot(N, omega_in) > 0.0f) { + *pdf = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F; + return calculate_principled_sheen_brdf(bsdf, N, V, L, H, pdf); + } + else { + *pdf = 0.0f; + return make_float3(0.0f, 0.0f, 0.0f); + } +} + +ccl_device float3 bsdf_principled_sheen_eval_transmit(const ShaderClosure *sc, const float3 I, + const float3 omega_in, float *pdf) +{ + return make_float3(0.0f, 0.0f, 0.0f); +} + +ccl_device int bsdf_principled_sheen_sample(const ShaderClosure *sc, + float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, + float3 *eval, float3 *omega_in, float3 *domega_in_dx, + float3 *domega_in_dy, float *pdf) +{ + const PrincipledSheenBsdf *bsdf = (const PrincipledSheenBsdf *)sc; + + float3 N = bsdf->N; + + sample_cos_hemisphere(N, randu, randv, omega_in, pdf); + + if(dot(Ng, *omega_in) > 0) { + float3 H = normalize(I + *omega_in); + + *eval = calculate_principled_sheen_brdf(bsdf, N, I, *omega_in, H, pdf); + +#ifdef __RAY_DIFFERENTIALS__ + // TODO: find a better approximation for the diffuse bounce + *domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx); + *domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy); +#endif + } + else { + *pdf = 0.0f; + } + return LABEL_REFLECT|LABEL_DIFFUSE; +} + +CCL_NAMESPACE_END + +#endif /* __BSDF_PRINCIPLED_SHEEN_H__ */ + + diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index b0c5280b6cb..3dc15d5791c 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -124,6 +124,13 @@ ccl_device float3 fresnel_conductor(float cosi, const float3 eta, const float3 k return(Rparl2 + Rperp2) * 0.5f; } +ccl_device float schlick_fresnel(float u) +{ + float m = clamp(1.0f - u, 0.0f, 1.0f); + float m2 = m * m; + return m2 * m2 * m; // pow(m, 5) +} + ccl_device float smooth_step(float edge0, float edge1, float x) { float result; @@ -136,6 +143,19 @@ ccl_device float smooth_step(float edge0, float edge1, float x) return result; } +/* Calculate the fresnel color which is a blend between white and the F0 color (cspec0) */ +ccl_device_forceinline float3 interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, float3 cspec0) { + /* Calculate the fresnel interpolation factor + * The value from fresnel_dielectric_cos(...) has to be normalized because + * the cspec0 keeps the F0 color + */ + float F0_norm = 1.0f / (1.0f - F0); + float FH = (fresnel_dielectric_cos(dot(L, H), ior) - F0) * F0_norm; + + /* Blend between white and a specular color with respect to the fresnel */ + return cspec0 * (1.0f - FH) + make_float3(1.0f, 1.0f, 1.0f) * FH; +} + CCL_NAMESPACE_END #endif /* __BSDF_UTIL_H__ */ diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index 475a60c50b2..f354e47ce2a 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -27,6 +27,7 @@ typedef ccl_addr_space struct Bssrdf { float d; float texture_blur; float albedo; + float roughness; } Bssrdf; /* Planar Truncated Gaussian @@ -359,10 +360,29 @@ ccl_device int bssrdf_setup(Bssrdf *bssrdf, ClosureType type) { if(bssrdf->radius < BSSRDF_MIN_RADIUS) { /* revert to diffuse BSDF if radius too small */ - DiffuseBsdf *bsdf = (DiffuseBsdf*)bssrdf; - bsdf->N = bssrdf->N; - int flag = bsdf_diffuse_setup(bsdf); - bsdf->type = CLOSURE_BSDF_BSSRDF_ID; + int flag; + if(type == CLOSURE_BSSRDF_PRINCIPLED_ID) { + float roughness = bssrdf->roughness; + float3 N = bssrdf->N; + float3 weight = bssrdf->weight; + float sample_weight = bssrdf->sample_weight; + + PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf*)bssrdf; + + bsdf->N = N; + bsdf->roughness = roughness; + bsdf->weight = weight; + bsdf->sample_weight = sample_weight; + flag = bsdf_principled_diffuse_setup(bsdf); + bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; + } + else { + DiffuseBsdf *bsdf = (DiffuseBsdf*)bssrdf; + bsdf->N = bssrdf->N; + flag = bsdf_diffuse_setup(bsdf); + bsdf->type = CLOSURE_BSDF_BSSRDF_ID; + } + return flag; } else { @@ -370,7 +390,7 @@ ccl_device int bssrdf_setup(Bssrdf *bssrdf, ClosureType type) bssrdf->sharpness = saturate(bssrdf->sharpness); bssrdf->type = type; - if(type == CLOSURE_BSSRDF_BURLEY_ID) { + if(type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_PRINCIPLED_ID) { bssrdf_burley_setup(bssrdf); } @@ -384,7 +404,7 @@ ccl_device void bssrdf_sample(const ShaderClosure *sc, float xi, float *r, float bssrdf_cubic_sample(sc, xi, r, h); else if(sc->type == CLOSURE_BSSRDF_GAUSSIAN_ID) bssrdf_gaussian_sample(sc, xi, r, h); - else /*if(sc->type == CLOSURE_BSSRDF_BURLEY_ID)*/ + else /*if(sc->type == CLOSURE_BSSRDF_BURLEY_ID || sc->type == CLOSURE_BSSRDF_PRINCIPLED_ID)*/ bssrdf_burley_sample(sc, xi, r, h); } @@ -394,7 +414,7 @@ ccl_device_forceinline float bssrdf_pdf(const ShaderClosure *sc, float r) return bssrdf_cubic_pdf(sc, r); else if(sc->type == CLOSURE_BSSRDF_GAUSSIAN_ID) return bssrdf_gaussian_pdf(sc, r); - else /*if(sc->type == CLOSURE_BSSRDF_BURLEY_ID)*/ + else /*if(sc->type == CLOSURE_BSSRDF_BURLEY_ID || sc->type == CLOSURE_BSSRDF_PRINCIPLED_ID)*/ return bssrdf_burley_pdf(sc, r); } diff --git a/intern/cycles/kernel/closure/bssrdf.h.orig b/intern/cycles/kernel/closure/bssrdf.h.orig new file mode 100644 index 00000000000..67435a23391 --- /dev/null +++ b/intern/cycles/kernel/closure/bssrdf.h.orig @@ -0,0 +1,428 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __KERNEL_BSSRDF_H__ +#define __KERNEL_BSSRDF_H__ + +CCL_NAMESPACE_BEGIN + +typedef ccl_addr_space struct Bssrdf { + SHADER_CLOSURE_BASE; + + float radius; + float sharpness; + float d; + float texture_blur; + float albedo; +<<<<<<< HEAD +======= + float roughness; + float3 N; +>>>>>>> origin/master +} Bssrdf; + +/* Planar Truncated Gaussian + * + * Note how this is different from the typical gaussian, this one integrates + * to 1 over the plane (where you get an extra 2*pi*x factor). We are lucky + * that integrating x*exp(-x) gives a nice closed form solution. */ + +/* paper suggests 1/12.46 which is much too small, suspect it's *12.46 */ +#define GAUSS_TRUNCATE 12.46f + +ccl_device float bssrdf_gaussian_eval(const ShaderClosure *sc, float r) +{ + /* integrate (2*pi*r * exp(-r*r/(2*v)))/(2*pi*v)) from 0 to Rm + * = 1 - exp(-Rm*Rm/(2*v)) */ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float v = bssrdf->radius*bssrdf->radius*(0.25f*0.25f); + const float Rm = sqrtf(v*GAUSS_TRUNCATE); + + if(r >= Rm) + return 0.0f; + + return expf(-r*r/(2.0f*v))/(2.0f*M_PI_F*v); +} + +ccl_device float bssrdf_gaussian_pdf(const ShaderClosure *sc, float r) +{ + /* 1.0 - expf(-Rm*Rm/(2*v)) simplified */ + const float area_truncated = 1.0f - expf(-0.5f*GAUSS_TRUNCATE); + + return bssrdf_gaussian_eval(sc, r) * (1.0f/(area_truncated)); +} + +ccl_device void bssrdf_gaussian_sample(const ShaderClosure *sc, float xi, float *r, float *h) +{ + /* xi = integrate (2*pi*r * exp(-r*r/(2*v)))/(2*pi*v)) = -exp(-r^2/(2*v)) + * r = sqrt(-2*v*logf(xi)) */ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float v = bssrdf->radius*bssrdf->radius*(0.25f*0.25f); + const float Rm = sqrtf(v*GAUSS_TRUNCATE); + + /* 1.0 - expf(-Rm*Rm/(2*v)) simplified */ + const float area_truncated = 1.0f - expf(-0.5f*GAUSS_TRUNCATE); + + /* r(xi) */ + const float r_squared = -2.0f*v*logf(1.0f - xi*area_truncated); + *r = sqrtf(r_squared); + + /* h^2 + r^2 = Rm^2 */ + *h = safe_sqrtf(Rm*Rm - r_squared); +} + +/* Planar Cubic BSSRDF falloff + * + * This is basically (Rm - x)^3, with some factors to normalize it. For sampling + * we integrate 2*pi*x * (Rm - x)^3, which gives us a quintic equation that as + * far as I can tell has no closed form solution. So we get an iterative solution + * instead with newton-raphson. */ + +ccl_device float bssrdf_cubic_eval(const ShaderClosure *sc, float r) +{ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float sharpness = bssrdf->sharpness; + + if(sharpness == 0.0f) { + const float Rm = bssrdf->radius; + + if(r >= Rm) + return 0.0f; + + /* integrate (2*pi*r * 10*(R - r)^3)/(pi * R^5) from 0 to R = 1 */ + const float Rm5 = (Rm*Rm) * (Rm*Rm) * Rm; + const float f = Rm - r; + const float num = f*f*f; + + return (10.0f * num) / (Rm5 * M_PI_F); + + } + else { + float Rm = bssrdf->radius*(1.0f + sharpness); + + if(r >= Rm) + return 0.0f; + + /* custom variation with extra sharpness, to match the previous code */ + const float y = 1.0f/(1.0f + sharpness); + float Rmy, ry, ryinv; + + if(sharpness == 1.0f) { + Rmy = sqrtf(Rm); + ry = sqrtf(r); + ryinv = (ry > 0.0f)? 1.0f/ry: 0.0f; + } + else { + Rmy = powf(Rm, y); + ry = powf(r, y); + ryinv = (r > 0.0f)? powf(r, 2.0f*y - 2.0f): 0.0f; + } + + const float Rmy5 = (Rmy*Rmy) * (Rmy*Rmy) * Rmy; + const float f = Rmy - ry; + const float num = f*(f*f)*(y*ryinv); + + return (10.0f * num) / (Rmy5 * M_PI_F); + } +} + +ccl_device float bssrdf_cubic_pdf(const ShaderClosure *sc, float r) +{ + return bssrdf_cubic_eval(sc, r); +} + +/* solve 10x^2 - 20x^3 + 15x^4 - 4x^5 - xi == 0 */ +ccl_device_forceinline float bssrdf_cubic_quintic_root_find(float xi) +{ + /* newton-raphson iteration, usually succeeds in 2-4 iterations, except + * outside 0.02 ... 0.98 where it can go up to 10, so overall performance + * should not be too bad */ + const float tolerance = 1e-6f; + const int max_iteration_count = 10; + float x = 0.25f; + int i; + + for(i = 0; i < max_iteration_count; i++) { + float x2 = x*x; + float x3 = x2*x; + float nx = (1.0f - x); + + float f = 10.0f*x2 - 20.0f*x3 + 15.0f*x2*x2 - 4.0f*x2*x3 - xi; + float f_ = 20.0f*(x*nx)*(nx*nx); + + if(fabsf(f) < tolerance || f_ == 0.0f) + break; + + x = saturate(x - f/f_); + } + + return x; +} + +ccl_device void bssrdf_cubic_sample(const ShaderClosure *sc, float xi, float *r, float *h) +{ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float sharpness = bssrdf->sharpness; + float Rm = bssrdf->radius; + float r_ = bssrdf_cubic_quintic_root_find(xi); + + if(sharpness != 0.0f) { + r_ = powf(r_, 1.0f + sharpness); + Rm *= (1.0f + sharpness); + } + + r_ *= Rm; + *r = r_; + + /* h^2 + r^2 = Rm^2 */ + *h = safe_sqrtf(Rm*Rm - r_*r_); +} + +/* Approximate Reflectance Profiles + * http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf + */ + +/* This is a bit arbitrary, just need big enough radius so it matches + * the mean free length, but still not too big so sampling is still + * effective. Might need some further tweaks. + */ +#define BURLEY_TRUNCATE 16.0f +#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) + +ccl_device_inline float bssrdf_burley_fitting(float A) +{ + /* Diffuse surface transmission, equation (6). */ + return 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); +} + +/* Scale mean free path length so it gives similar looking result + * to Cubic and Gaussian models. + */ +ccl_device_inline float bssrdf_burley_compatible_mfp(float r) +{ + return 0.25f * M_1_PI_F * r; +} + +ccl_device void bssrdf_burley_setup(Bssrdf *bssrdf) +{ + /* Mean free path length. */ + const float l = bssrdf_burley_compatible_mfp(bssrdf->radius); + /* Surface albedo. */ + const float A = bssrdf->albedo; + const float s = bssrdf_burley_fitting(A); + const float d = l / s; + + bssrdf->d = d; +} + +ccl_device float bssrdf_burley_eval(const ShaderClosure *sc, float r) +{ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float d = bssrdf->d; + const float Rm = BURLEY_TRUNCATE * d; + + if(r >= Rm) + return 0.0f; + + /* Burley refletance profile, equation (3). + * + * NOTES: + * - Surface albedo is already included into sc->weight, no need to + * multiply by this term here. + * - This is normalized diffuse model, so the equation is mutliplied + * by 2*pi, which also matches cdf(). + */ + float exp_r_3_d = expf(-r / (3.0f * d)); + float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; + return (exp_r_d + exp_r_3_d) / (4.0f*d); +} + +ccl_device float bssrdf_burley_pdf(const ShaderClosure *sc, float r) +{ + return bssrdf_burley_eval(sc, r) * (1.0f/BURLEY_TRUNCATE_CDF); +} + +/* Find the radius for desired CDF value. + * Returns scaled radius, meaning the result is to be scaled up by d. + * Since there's no closed form solution we do Newton-Raphson method to find it. + */ +ccl_device_forceinline float bssrdf_burley_root_find(float xi) +{ + const float tolerance = 1e-6f; + const int max_iteration_count = 10; + /* Do initial guess based on manual curve fitting, this allows us to reduce + * number of iterations to maximum 4 across the [0..1] range. We keep maximum + * number of iteration higher just to be sure we didn't miss root in some + * corner case. + */ + float r; + if(xi <= 0.9f) { + r = expf(xi * xi * 2.4f) - 1.0f; + } + else { + /* TODO(sergey): Some nicer curve fit is possible here. */ + r = 15.0f; + } + /* Solve against scaled radius. */ + for(int i = 0; i < max_iteration_count; i++) { + float exp_r_3 = expf(-r / 3.0f); + float exp_r = exp_r_3 * exp_r_3 * exp_r_3; + float f = 1.0f - 0.25f * exp_r - 0.75f * exp_r_3 - xi; + float f_ = 0.25f * exp_r + 0.25f * exp_r_3; + + if(fabsf(f) < tolerance || f_ == 0.0f) { + break; + } + + r = r - f/f_; + if(r < 0.0f) { + r = 0.0f; + } + } + return r; +} + +ccl_device void bssrdf_burley_sample(const ShaderClosure *sc, + float xi, + float *r, + float *h) +{ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float d = bssrdf->d; + const float Rm = BURLEY_TRUNCATE * d; + const float r_ = bssrdf_burley_root_find(xi * BURLEY_TRUNCATE_CDF) * d; + + *r = r_; + + /* h^2 + r^2 = Rm^2 */ + *h = safe_sqrtf(Rm*Rm - r_*r_); +} + +/* None BSSRDF falloff + * + * Samples distributed over disk with no falloff, for reference. */ + +ccl_device float bssrdf_none_eval(const ShaderClosure *sc, float r) +{ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float Rm = bssrdf->radius; + return (r < Rm)? 1.0f: 0.0f; +} + +ccl_device float bssrdf_none_pdf(const ShaderClosure *sc, float r) +{ + /* integrate (2*pi*r)/(pi*Rm*Rm) from 0 to Rm = 1 */ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float Rm = bssrdf->radius; + const float area = (M_PI_F*Rm*Rm); + + return bssrdf_none_eval(sc, r) / area; +} + +ccl_device void bssrdf_none_sample(const ShaderClosure *sc, float xi, float *r, float *h) +{ + /* xi = integrate (2*pi*r)/(pi*Rm*Rm) = r^2/Rm^2 + * r = sqrt(xi)*Rm */ + const Bssrdf *bssrdf = (const Bssrdf*)sc; + const float Rm = bssrdf->radius; + const float r_ = sqrtf(xi)*Rm; + + *r = r_; + + /* h^2 + r^2 = Rm^2 */ + *h = safe_sqrtf(Rm*Rm - r_*r_); +} + +/* Generic */ + +ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) +{ + Bssrdf *bssrdf = (Bssrdf*)closure_alloc(sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight); + + if(!bssrdf) + return NULL; + + float sample_weight = fabsf(average(weight)); + bssrdf->sample_weight = sample_weight; + return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? bssrdf : NULL; +} + +ccl_device int bssrdf_setup(Bssrdf *bssrdf, ClosureType type) +{ + if(bssrdf->radius < BSSRDF_MIN_RADIUS) { + /* revert to diffuse BSDF if radius too small */ + int flag; + if(type == CLOSURE_BSSRDF_PRINCIPLED_ID) { + float roughness = bssrdf->roughness; + float3 N = bssrdf->N; + float3 weight = bssrdf->weight; + float sample_weight = bssrdf->sample_weight; + + PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf*)bssrdf; + + bsdf->N = N; + bsdf->roughness = roughness; + bsdf->weight = weight; + bsdf->sample_weight = sample_weight; + flag = bsdf_principled_diffuse_setup(bsdf); + bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; + } + else { + DiffuseBsdf *bsdf = (DiffuseBsdf*)bssrdf; + bsdf->N = bssrdf->N; + flag = bsdf_diffuse_setup(bsdf); + bsdf->type = CLOSURE_BSDF_BSSRDF_ID; + } + + return flag; + } + else { + bssrdf->texture_blur = saturate(bssrdf->texture_blur); + bssrdf->sharpness = saturate(bssrdf->sharpness); + bssrdf->type = type; + + if(type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_PRINCIPLED_ID) { + bssrdf_burley_setup(bssrdf); + } + + return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSSRDF; + } +} + +ccl_device void bssrdf_sample(const ShaderClosure *sc, float xi, float *r, float *h) +{ + if(sc->type == CLOSURE_BSSRDF_CUBIC_ID) + bssrdf_cubic_sample(sc, xi, r, h); + else if(sc->type == CLOSURE_BSSRDF_GAUSSIAN_ID) + bssrdf_gaussian_sample(sc, xi, r, h); + else /*if(sc->type == CLOSURE_BSSRDF_BURLEY_ID || sc->type == CLOSURE_BSSRDF_PRINCIPLED_ID)*/ + bssrdf_burley_sample(sc, xi, r, h); +} + +ccl_device_forceinline float bssrdf_pdf(const ShaderClosure *sc, float r) +{ + if(sc->type == CLOSURE_BSSRDF_CUBIC_ID) + return bssrdf_cubic_pdf(sc, r); + else if(sc->type == CLOSURE_BSSRDF_GAUSSIAN_ID) + return bssrdf_gaussian_pdf(sc, r); + else /*if(sc->type == CLOSURE_BSSRDF_BURLEY_ID || sc->type == CLOSURE_BSSRDF_PRINCIPLED_ID)*/ + return bssrdf_burley_pdf(sc, r); +} + +CCL_NAMESPACE_END + +#endif /* __KERNEL_BSSRDF_H__ */ + diff --git a/intern/cycles/kernel/kernel_shadow.h b/intern/cycles/kernel/kernel_shadow.h index db6f839d9ed..fab5946970d 100644 --- a/intern/cycles/kernel/kernel_shadow.h +++ b/intern/cycles/kernel/kernel_shadow.h @@ -379,7 +379,7 @@ ccl_device bool shadow_blocked_transparent_stepped( float3 *shadow) { bool blocked, is_transparent_isect; - if (skip_object == OBJECT_NONE) { + if(skip_object == OBJECT_NONE) { blocked = scene_intersect(kg, *ray, PATH_RAY_SHADOW_OPAQUE, diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h index f75e9337bdb..6c8b7cca4ce 100644 --- a/intern/cycles/kernel/kernel_subsurface.h +++ b/intern/cycles/kernel/kernel_subsurface.h @@ -140,7 +140,7 @@ ccl_device_inline float3 subsurface_scatter_eval(ShaderData *sd, } /* replace closures with a single diffuse bsdf closure after scatter step */ -ccl_device void subsurface_scatter_setup_diffuse_bsdf(ShaderData *sd, float3 weight, bool hit, float3 N) +ccl_device void subsurface_scatter_setup_diffuse_bsdf(ShaderData *sd, ShaderClosure *sc, float3 weight, bool hit, float3 N) { sd->flag &= ~SD_CLOSURE_FLAGS; sd->randb_closure = 0.0f; @@ -148,15 +148,31 @@ ccl_device void subsurface_scatter_setup_diffuse_bsdf(ShaderData *sd, float3 wei sd->num_closure_extra = 0; if(hit) { - DiffuseBsdf *bsdf = (DiffuseBsdf*)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight); + Bssrdf *bssrdf = (Bssrdf *)sc; + if(bssrdf->type == CLOSURE_BSSRDF_PRINCIPLED_ID) { + PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf*)bsdf_alloc(sd, sizeof(PrincipledDiffuseBsdf), weight); + + if(bsdf) { + bsdf->N = N; + bsdf->roughness = bssrdf->roughness; + sd->flag |= bsdf_principled_diffuse_setup(bsdf); + + /* replace CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID with this special ID so render passes + * can recognize it as not being a regular Disney principled diffuse closure */ + bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; + } + } + else if(CLOSURE_IS_BSSRDF(bssrdf->type)) { + DiffuseBsdf *bsdf = (DiffuseBsdf*)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight); - if(bsdf) { - bsdf->N = N; - sd->flag |= bsdf_diffuse_setup(bsdf); + if(bsdf) { + bsdf->N = N; + sd->flag |= bsdf_diffuse_setup(bsdf); - /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes - * can recognize it as not being a regular diffuse closure */ - bsdf->type = CLOSURE_BSDF_BSSRDF_ID; + /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes + * can recognize it as not being a regular diffuse closure */ + bsdf->type = CLOSURE_BSDF_BSSRDF_ID; + } } } } @@ -388,7 +404,7 @@ ccl_device_noinline void subsurface_scatter_multi_setup( subsurface_color_bump_blur(kg, sd, state, state_flag, &weight, &N); /* Setup diffuse BSDF. */ - subsurface_scatter_setup_diffuse_bsdf(sd, weight, true, N); + subsurface_scatter_setup_diffuse_bsdf(sd, sc, weight, true, N); } #ifndef __SPLIT_KERNEL__ @@ -479,7 +495,7 @@ ccl_device void subsurface_scatter_step(KernelGlobals *kg, ShaderData *sd, PathS subsurface_color_bump_blur(kg, sd, state, state_flag, &eval, &N); /* setup diffuse bsdf */ - subsurface_scatter_setup_diffuse_bsdf(sd, eval, (ss_isect.num_hits > 0), N); + subsurface_scatter_setup_diffuse_bsdf(sd, sc, eval, (ss_isect.num_hits > 0), N); } #endif /* ! __SPLIT_KERNEL__ */ diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp index 95beea01d25..188c3960a5f 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.cpp +++ b/intern/cycles/kernel/osl/osl_bssrdf.cpp @@ -39,7 +39,9 @@ #include "kernel/kernel_montecarlo.h" #include "kernel/closure/alloc.h" +#include "kernel/closure/bsdf_util.h" #include "kernel/closure/bsdf_diffuse.h" +#include "kernel/closure/bsdf_principled_diffuse.h" #include "kernel/closure/bssrdf.h" CCL_NAMESPACE_BEGIN @@ -78,6 +80,7 @@ public: bssrdf->albedo = albedo.x; bssrdf->sharpness = sharpness; bssrdf->N = params.N; + bssrdf->roughness = params.roughness; sd->flag |= bssrdf_setup(bssrdf, (ClosureType)type); } @@ -89,6 +92,7 @@ public: bssrdf->albedo = albedo.y; bssrdf->sharpness = sharpness; bssrdf->N = params.N; + bssrdf->roughness = params.roughness; sd->flag |= bssrdf_setup(bssrdf, (ClosureType)type); } @@ -100,6 +104,7 @@ public: bssrdf->albedo = albedo.z; bssrdf->sharpness = sharpness; bssrdf->N = params.N; + bssrdf->roughness = params.roughness; sd->flag |= bssrdf_setup(bssrdf, (ClosureType)type); } } @@ -180,5 +185,31 @@ ClosureParam *closure_bssrdf_burley_params() CCLOSURE_PREPARE(closure_bssrdf_burley_prepare, BurleyBSSRDFClosure) +/* Disney principled */ + +class PrincipledBSSRDFClosure : public CBSSRDFClosure { +public: + void setup(ShaderData *sd, int path_flag, float3 weight) + { + alloc(sd, path_flag, weight * albedo, CLOSURE_BSSRDF_PRINCIPLED_ID); + } +}; + +ClosureParam *closure_bssrdf_principled_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(PrincipledBSSRDFClosure, params.N), + CLOSURE_FLOAT3_PARAM(PrincipledBSSRDFClosure, radius), + CLOSURE_FLOAT_PARAM(PrincipledBSSRDFClosure, params.texture_blur), + CLOSURE_FLOAT3_PARAM(PrincipledBSSRDFClosure, albedo), + CLOSURE_FLOAT_PARAM(PrincipledBSSRDFClosure, params.roughness), + CLOSURE_STRING_KEYPARAM(PrincipledBSSRDFClosure, label, "label"), + CLOSURE_FINISH_PARAM(PrincipledBSSRDFClosure) + }; + return params; +} + +CCLOSURE_PREPARE(closure_bssrdf_principled_prepare, PrincipledBSSRDFClosure) + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index f44714c2150..5b66793a05d 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -60,6 +60,8 @@ #include "kernel/closure/bsdf_ashikhmin_shirley.h" #include "kernel/closure/bsdf_toon.h" #include "kernel/closure/bsdf_hair.h" +#include "kernel/closure/bsdf_principled_diffuse.h" +#include "kernel/closure/bsdf_principled_sheen.h" #include "kernel/closure/volume.h" CCL_NAMESPACE_BEGIN @@ -176,6 +178,63 @@ VOLUME_CLOSURE_CLASS_END(VolumeHenyeyGreenstein, henyey_greenstein) VOLUME_CLOSURE_CLASS_BEGIN(VolumeAbsorption, absorption, ShaderClosure, LABEL_SINGULAR) VOLUME_CLOSURE_CLASS_END(VolumeAbsorption, absorption) +BSDF_CLOSURE_CLASS_BEGIN(PrincipledDiffuse, principled_diffuse, PrincipledDiffuseBsdf, LABEL_DIFFUSE) + CLOSURE_FLOAT3_PARAM(PrincipledDiffuseClosure, params.N), + CLOSURE_FLOAT_PARAM(PrincipledDiffuseClosure, params.roughness), +BSDF_CLOSURE_CLASS_END(PrincipledDiffuse, principled_diffuse) + +BSDF_CLOSURE_CLASS_BEGIN(PrincipledSheen, principled_sheen, PrincipledSheenBsdf, LABEL_DIFFUSE) + CLOSURE_FLOAT3_PARAM(PrincipledSheenClosure, params.N), +BSDF_CLOSURE_CLASS_END(PrincipledSheen, principled_sheen) + +/* DISNEY PRINCIPLED CLEARCOAT */ +class PrincipledClearcoatClosure : public CBSDFClosure { +public: + MicrofacetBsdf params; + float clearcoat, clearcoat_gloss; + + MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + { + MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc_osl(sd, sizeof(MicrofacetBsdf), weight, ¶ms); + MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + if(bsdf && extra) { + bsdf->extra = extra; + + bsdf->ior = 1.5f; + + bsdf->alpha_x = 0.1f * (1.0f - clearcoat_gloss) + 0.001f * clearcoat_gloss; + bsdf->alpha_y = 0.1f * (1.0f - clearcoat_gloss) + 0.001f * clearcoat_gloss; + + bsdf->extra->cspec0 = make_float3(0.04f, 0.04f, 0.04f); + bsdf->extra->clearcoat = clearcoat; + + return bsdf; + } + + return NULL; + } + + void setup(ShaderData *sd, int path_flag, float3 weight) + { + MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); + sd->flag |= (bsdf) ? bsdf_microfacet_ggx_clearcoat_setup(bsdf) : 0; + } +}; + +ClosureParam *closure_bsdf_principled_clearcoat_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(PrincipledClearcoatClosure, params.N), + CLOSURE_FLOAT_PARAM(PrincipledClearcoatClosure, clearcoat), + CLOSURE_FLOAT_PARAM(PrincipledClearcoatClosure, clearcoat_gloss), + CLOSURE_STRING_KEYPARAM(PrincipledClearcoatClosure, label, "label"), + CLOSURE_FINISH_PARAM(PrincipledClearcoatClosure) + }; + return params; +} +CCLOSURE_PREPARE(closure_bsdf_principled_clearcoat_prepare, PrincipledClearcoatClosure) + + /* Registration */ static void register_closure(OSL::ShadingSystem *ss, const char *name, int id, OSL::ClosureParam *params, OSL::PrepareClosureFunc prepare) @@ -215,6 +274,16 @@ void OSLShader::register_closures(OSLShadingSystem *ss_) closure_bsdf_microfacet_multi_ggx_glass_params(), closure_bsdf_microfacet_multi_ggx_glass_prepare); register_closure(ss, "microfacet_multi_ggx_aniso", id++, closure_bsdf_microfacet_multi_ggx_aniso_params(), closure_bsdf_microfacet_multi_ggx_aniso_prepare); + register_closure(ss, "microfacet_ggx_fresnel", id++, + closure_bsdf_microfacet_ggx_fresnel_params(), closure_bsdf_microfacet_ggx_fresnel_prepare); + register_closure(ss, "microfacet_ggx_aniso_fresnel", id++, + closure_bsdf_microfacet_ggx_aniso_fresnel_params(), closure_bsdf_microfacet_ggx_aniso_fresnel_prepare); + register_closure(ss, "microfacet_multi_ggx_fresnel", id++, + closure_bsdf_microfacet_multi_ggx_fresnel_params(), closure_bsdf_microfacet_multi_ggx_fresnel_prepare); + register_closure(ss, "microfacet_multi_ggx_glass_fresnel", id++, + closure_bsdf_microfacet_multi_ggx_glass_fresnel_params(), closure_bsdf_microfacet_multi_ggx_glass_fresnel_prepare); + register_closure(ss, "microfacet_multi_ggx_aniso_fresnel", id++, + closure_bsdf_microfacet_multi_ggx_aniso_fresnel_params(), closure_bsdf_microfacet_multi_ggx_aniso_fresnel_prepare); register_closure(ss, "microfacet_beckmann", id++, bsdf_microfacet_beckmann_params(), bsdf_microfacet_beckmann_prepare); register_closure(ss, "microfacet_beckmann_aniso", id++, @@ -229,6 +298,12 @@ void OSLShader::register_closures(OSLShadingSystem *ss_) bsdf_diffuse_toon_params(), bsdf_diffuse_toon_prepare); register_closure(ss, "glossy_toon", id++, bsdf_glossy_toon_params(), bsdf_glossy_toon_prepare); + register_closure(ss, "principled_diffuse", id++, + bsdf_principled_diffuse_params(), bsdf_principled_diffuse_prepare); + register_closure(ss, "principled_sheen", id++, + bsdf_principled_sheen_params(), bsdf_principled_sheen_prepare); + register_closure(ss, "principled_clearcoat", id++, + closure_bsdf_principled_clearcoat_params(), closure_bsdf_principled_clearcoat_prepare); register_closure(ss, "emission", id++, closure_emission_params(), closure_emission_prepare); @@ -248,6 +323,8 @@ void OSLShader::register_closures(OSLShadingSystem *ss_) closure_bssrdf_gaussian_params(), closure_bssrdf_gaussian_prepare); register_closure(ss, "bssrdf_burley", id++, closure_bssrdf_burley_params(), closure_bssrdf_burley_prepare); + register_closure(ss, "bssrdf_principled", id++, + closure_bssrdf_principled_params(), closure_bssrdf_principled_prepare); register_closure(ss, "hair_reflection", id++, bsdf_hair_reflection_params(), bsdf_hair_reflection_prepare); @@ -278,6 +355,86 @@ bool CBSDFClosure::skip(const ShaderData *sd, int path_flag, int scattering) return false; } + +/* GGX closures with Fresnel */ + +class MicrofacetFresnelClosure : public CBSDFClosure { +public: + MicrofacetBsdf params; + float3 color; + float3 cspec0; + + MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + { + /* Technically, the MultiGGX Glass closure may also transmit. However, + * since this is set statically and only used for caustic flags, this + * is probably as good as it gets. */ + if(!skip(sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc_osl(sd, sizeof(MicrofacetBsdf), weight, ¶ms); + MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + if(bsdf && extra) { + bsdf->extra = extra; + bsdf->extra->color = color; + bsdf->extra->cspec0 = cspec0; + return bsdf; + } + } + + return NULL; + } +}; + +class MicrofacetGGXFresnelClosure : public MicrofacetFresnelClosure { +public: + void setup(ShaderData *sd, int path_flag, float3 weight) + { + MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); + sd->flag |= (bsdf) ? bsdf_microfacet_ggx_fresnel_setup(bsdf) : 0; + } +}; + +ClosureParam *closure_bsdf_microfacet_ggx_fresnel_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, params.N), + CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.alpha_x), + CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.ior), + CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, color), + CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, cspec0), + CLOSURE_STRING_KEYPARAM(MicrofacetGGXFresnelClosure, label, "label"), + CLOSURE_FINISH_PARAM(MicrofacetGGXFresnelClosure) + }; + return params; +} +CCLOSURE_PREPARE(closure_bsdf_microfacet_ggx_fresnel_prepare, MicrofacetGGXFresnelClosure); + +class MicrofacetGGXAnisoFresnelClosure : public MicrofacetFresnelClosure { +public: + void setup(ShaderData *sd, int path_flag, float3 weight) + { + MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); + sd->flag |= (bsdf) ? bsdf_microfacet_ggx_aniso_fresnel_setup(bsdf) : 0; + } +}; + +ClosureParam *closure_bsdf_microfacet_ggx_aniso_fresnel_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, params.N), + CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, params.T), + CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.alpha_x), + CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.alpha_y), + CLOSURE_FLOAT_PARAM(MicrofacetGGXFresnelClosure, params.ior), + CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, color), + CLOSURE_FLOAT3_PARAM(MicrofacetGGXFresnelClosure, cspec0), + CLOSURE_STRING_KEYPARAM(MicrofacetGGXFresnelClosure, label, "label"), + CLOSURE_FINISH_PARAM(MicrofacetGGXFresnelClosure) + }; + return params; +} +CCLOSURE_PREPARE(closure_bsdf_microfacet_ggx_aniso_fresnel_prepare, MicrofacetGGXAnisoFresnelClosure); + + /* Multiscattering GGX closures */ class MicrofacetMultiClosure : public CBSDFClosure { @@ -287,7 +444,7 @@ public: MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) { - /* Technically, the MultiGGX Glass closure may also transmit. However, + /* Technically, the MultiGGX closure may also transmit. However, * since this is set statically and only used for caustic flags, this * is probably as good as it gets. */ if(!skip(sd, path_flag, LABEL_GLOSSY|LABEL_REFLECT)) { @@ -375,5 +532,110 @@ ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_params() } CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_glass_prepare, MicrofacetMultiGGXGlassClosure); + +/* Multiscattering GGX closures with Fresnel */ + +class MicrofacetMultiFresnelClosure : public CBSDFClosure { +public: + MicrofacetBsdf params; + float3 color; + float3 cspec0; + + MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + { + /* Technically, the MultiGGX closure may also transmit. However, + * since this is set statically and only used for caustic flags, this + * is probably as good as it gets. */ + if(!skip(sd, path_flag, LABEL_GLOSSY | LABEL_REFLECT)) { + MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc_osl(sd, sizeof(MicrofacetBsdf), weight, ¶ms); + MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + if(bsdf && extra) { + bsdf->extra = extra; + bsdf->extra->color = color; + bsdf->extra->cspec0 = cspec0; + return bsdf; + } + } + + return NULL; + } +}; + +class MicrofacetMultiGGXFresnelClosure : public MicrofacetMultiFresnelClosure { +public: + void setup(ShaderData *sd, int path_flag, float3 weight) + { + MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); + sd->flag |= (bsdf) ? bsdf_microfacet_multi_ggx_fresnel_setup(bsdf) : 0; + } +}; + +ClosureParam *closure_bsdf_microfacet_multi_ggx_fresnel_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, params.N), + CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.alpha_x), + CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.ior), + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, color), + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, cspec0), + CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXFresnelClosure, label, "label"), + CLOSURE_FINISH_PARAM(MicrofacetMultiGGXFresnelClosure) + }; + return params; +} +CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_fresnel_prepare, MicrofacetMultiGGXFresnelClosure); + +class MicrofacetMultiGGXAnisoFresnelClosure : public MicrofacetMultiFresnelClosure { +public: + void setup(ShaderData *sd, int path_flag, float3 weight) + { + MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); + sd->flag |= (bsdf) ? bsdf_microfacet_multi_ggx_aniso_fresnel_setup(bsdf) : 0; + } +}; + +ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_fresnel_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, params.N), + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, params.T), + CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.alpha_x), + CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.alpha_y), + CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.ior), + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, color), + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, cspec0), + CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXFresnelClosure, label, "label"), + CLOSURE_FINISH_PARAM(MicrofacetMultiGGXFresnelClosure) + }; + return params; +} +CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_aniso_fresnel_prepare, MicrofacetMultiGGXAnisoFresnelClosure); + +class MicrofacetMultiGGXGlassFresnelClosure : public MicrofacetMultiFresnelClosure { +public: + MicrofacetMultiGGXGlassFresnelClosure() : MicrofacetMultiFresnelClosure() {} + + void setup(ShaderData *sd, int path_flag, float3 weight) + { + MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); + sd->flag |= (bsdf) ? bsdf_microfacet_multi_ggx_glass_fresnel_setup(bsdf) : 0; + } +}; + +ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_fresnel_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, params.N), + CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.alpha_x), + CLOSURE_FLOAT_PARAM(MicrofacetMultiGGXFresnelClosure, params.ior), + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, color), + CLOSURE_FLOAT3_PARAM(MicrofacetMultiGGXFresnelClosure, cspec0), + CLOSURE_STRING_KEYPARAM(MicrofacetMultiGGXFresnelClosure, label, "label"), + CLOSURE_FINISH_PARAM(MicrofacetMultiGGXFresnelClosure) + }; + return params; +} +CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_glass_fresnel_prepare, MicrofacetMultiGGXGlassFresnelClosure); + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h index 929cf00a7e6..ff5fd9cc905 100644 --- a/intern/cycles/kernel/osl/osl_closures.h +++ b/intern/cycles/kernel/osl/osl_closures.h @@ -51,10 +51,17 @@ OSL::ClosureParam *closure_bsdf_phong_ramp_params(); OSL::ClosureParam *closure_bssrdf_cubic_params(); OSL::ClosureParam *closure_bssrdf_gaussian_params(); OSL::ClosureParam *closure_bssrdf_burley_params(); +OSL::ClosureParam *closure_bssrdf_principled_params(); OSL::ClosureParam *closure_henyey_greenstein_volume_params(); OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_params(); OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_params(); OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_params(); +OSL::ClosureParam *closure_bsdf_microfacet_ggx_fresnel_params(); +OSL::ClosureParam *closure_bsdf_microfacet_ggx_aniso_fresnel_params(); +OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_fresnel_params(); +OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_fresnel_params(); +OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_fresnel_params(); +OSL::ClosureParam *closure_bsdf_principled_clearcoat_params(); void closure_emission_prepare(OSL::RendererServices *, int id, void *data); void closure_background_prepare(OSL::RendererServices *, int id, void *data); @@ -65,10 +72,17 @@ void closure_bsdf_phong_ramp_prepare(OSL::RendererServices *, int id, void *data void closure_bssrdf_cubic_prepare(OSL::RendererServices *, int id, void *data); void closure_bssrdf_gaussian_prepare(OSL::RendererServices *, int id, void *data); void closure_bssrdf_burley_prepare(OSL::RendererServices *, int id, void *data); +void closure_bssrdf_principled_prepare(OSL::RendererServices *, int id, void *data); void closure_henyey_greenstein_volume_prepare(OSL::RendererServices *, int id, void *data); void closure_bsdf_microfacet_multi_ggx_prepare(OSL::RendererServices *, int id, void *data); void closure_bsdf_microfacet_multi_ggx_glass_prepare(OSL::RendererServices *, int id, void *data); void closure_bsdf_microfacet_multi_ggx_aniso_prepare(OSL::RendererServices *, int id, void *data); +void closure_bsdf_microfacet_ggx_fresnel_prepare(OSL::RendererServices *, int id, void *data); +void closure_bsdf_microfacet_ggx_aniso_fresnel_prepare(OSL::RendererServices *, int id, void *data); +void closure_bsdf_microfacet_multi_ggx_fresnel_prepare(OSL::RendererServices *, int id, void *data); +void closure_bsdf_microfacet_multi_ggx_glass_fresnel_prepare(OSL::RendererServices *, int id, void *data); +void closure_bsdf_microfacet_multi_ggx_aniso_fresnel_prepare(OSL::RendererServices *, int id, void *data); +void closure_bsdf_principled_clearcoat_prepare(OSL::RendererServices *, int id, void *data); #define CCLOSURE_PREPARE(name, classname) \ void name(RendererServices *, int id, void *data) \ diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/shaders/CMakeLists.txt index b43f8402d42..1a8ed4c884a 100644 --- a/intern/cycles/kernel/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/shaders/CMakeLists.txt @@ -81,13 +81,15 @@ set(SRC_OSL node_wireframe.osl node_hair_bsdf.osl node_uv_map.osl + node_principled_bsdf.osl node_rgb_to_bw.osl ) set(SRC_OSL_HEADERS - node_texture.h node_color.h node_fresnel.h + node_ramp_util.h + node_texture.h stdosl.h oslutil.h ) diff --git a/intern/cycles/kernel/shaders/node_principled_bsdf.osl b/intern/cycles/kernel/shaders/node_principled_bsdf.osl new file mode 100644 index 00000000000..39c6c7e5fd9 --- /dev/null +++ b/intern/cycles/kernel/shaders/node_principled_bsdf.osl @@ -0,0 +1,120 @@ +/* + * Copyright 2011-2017 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdosl.h" +#include "node_fresnel.h" + +shader node_principled_bsdf( + string distribution = "Multiscatter GGX", + color BaseColor = color(0.8, 0.8, 0.8), + float Subsurface = 0.0, + vector SubsurfaceRadius = vector(1.0, 1.0, 1.0), + color SubsurfaceColor = color(0.7, 0.1, 0.1), + float Metallic = 0.0, + float Specular = 0.5, + float SpecularTint = 0.0, + float Roughness = 0.5, + float Anisotropic = 0.0, + float AnisotropicRotation = 0.0, + float Sheen = 0.0, + float SheenTint = 0.5, + float Clearcoat = 0.0, + float ClearcoatGloss = 1.0, + float IOR = 1.45, + float Transparency = 0.0, + float RefractionRoughness = 0.0, + normal Normal = N, + normal ClearcoatNormal = N, + normal Tangent = normalize(dPdu), + output closure color BSDF = 0) +{ + float f = max(IOR, 1e-5); + float diffuse_weight = (1.0 - clamp(Metallic, 0.0, 1.0)) * (1.0 - clamp(Transparency, 0.0, 1.0)); + float transp = clamp(Transparency, 0.0, 1.0) * (1.0 - clamp(Metallic, 0.0, 1.0)); + float specular_weight = (1.0 - transp); + + vector T = Tangent; + + float m_cdlum = luminance(BaseColor); + color m_ctint = m_cdlum > 0.0 ? BaseColor / m_cdlum : color(0.0, 0.0, 0.0); // normalize lum. to isolate hue+sat + + /* rotate tangent */ + if (AnisotropicRotation != 0.0) + T = rotate(T, AnisotropicRotation * M_2PI, point(0.0, 0.0, 0.0), Normal); + + if (diffuse_weight > 1e-5) { + if (Subsurface > 1e-5) { + color Albedo = SubsurfaceColor * Subsurface + BaseColor * (1.0 - Subsurface); + BSDF = bssrdf_principled(Normal, Subsurface * SubsurfaceRadius, 0.0, Albedo, Roughness); + } else { + BSDF = BaseColor * principled_diffuse(Normal, Roughness); + } + + if (Sheen > 1e-5) { + color sheen_color = color(1.0, 1.0, 1.0) * (1.0 - SheenTint) + m_ctint * SheenTint; + + BSDF = BSDF + sheen_color * Sheen * principled_sheen(Normal); + } + + BSDF = BSDF * diffuse_weight; + } + + if (specular_weight > 1e-5) { + float aspect = sqrt(1.0 - Anisotropic * 0.9); + float r2 = Roughness * Roughness; + + float alpha_x = max(0.001, r2 / aspect); + float alpha_y = max(0.001, r2 * aspect); + + color tmp_col = color(1.0, 1.0, 1.0) * (1.0 - SpecularTint) + m_ctint * SpecularTint; + + color Cspec0 = (Specular * 0.08 * tmp_col) * (1.0 - Metallic) + BaseColor * Metallic; + + if (distribution == "GGX" || Roughness <= 0.075) { + BSDF = BSDF + specular_weight * microfacet_ggx_aniso_fresnel(Normal, T, alpha_x, alpha_y, (2.0 / (1.0 - sqrt(0.08 * Specular))) - 1.0, BaseColor, Cspec0); + } else { + BSDF = BSDF + specular_weight * microfacet_multi_ggx_aniso_fresnel(Normal, T, alpha_x, alpha_y, (2.0 / (1.0 - sqrt(0.08 * Specular))) - 1.0, BaseColor, Cspec0); + } + } + + if (transp > 1e-5) { + color Cspec0 = BaseColor * SpecularTint + color(1.0, 1.0, 1.0) * (1.0 - SpecularTint); + float eta = backfacing() ? 1.0 / f : f; + + if (distribution == "GGX" || Roughness <= 5e-2) { + float cosNO = dot(Normal, I); + float Fr = fresnel_dielectric_cos(cosNO, eta); + + float refl_roughness = Roughness; + if (Roughness <= 1e-2) + refl_roughness = 0.0; + + float refraction_roughness = refl_roughness; + if (distribution == "GGX") + refraction_roughness = 1.0 - (1.0 - refl_roughness) * (1.0 - RefractionRoughness); + + BSDF = BSDF + transp * (Fr * microfacet_ggx_fresnel(Normal, refl_roughness * refl_roughness, eta, BaseColor, Cspec0) + + (1.0 - Fr) * BaseColor * microfacet_ggx_refraction(Normal, refraction_roughness * refraction_roughness, eta)); + } else { + BSDF = BSDF + transp * microfacet_multi_ggx_glass_fresnel(Normal, Roughness * Roughness, eta, BaseColor, Cspec0); + } + } + + if (Clearcoat > 1e-5) { + BSDF = BSDF + principled_clearcoat(ClearcoatNormal, Clearcoat, ClearcoatGloss); + } +} + diff --git a/intern/cycles/kernel/shaders/stdosl.h b/intern/cycles/kernel/shaders/stdosl.h index a8dda8a12c9..289d1091b0a 100644 --- a/intern/cycles/kernel/shaders/stdosl.h +++ b/intern/cycles/kernel/shaders/stdosl.h @@ -530,6 +530,11 @@ closure color microfacet_ggx_refraction(normal N, float ag, float eta) BUILTIN; closure color microfacet_multi_ggx(normal N, float ag, color C) BUILTIN; closure color microfacet_multi_ggx_aniso(normal N, vector T, float ax, float ay, color C) BUILTIN; closure color microfacet_multi_ggx_glass(normal N, float ag, float eta, color C) BUILTIN; +closure color microfacet_ggx_fresnel(normal N, float ag, float eta, color C, color Cspec0) BUILTIN; +closure color microfacet_ggx_aniso_fresnel(normal N, vector T, float ax, float ay, float eta, color C, color Cspec0) BUILTIN; +closure color microfacet_multi_ggx_fresnel(normal N, float ag, float eta, color C, color Cspec0) BUILTIN; +closure color microfacet_multi_ggx_aniso_fresnel(normal N, vector T, float ax, float ay, float eta, color C, color Cspec0) BUILTIN; +closure color microfacet_multi_ggx_glass_fresnel(normal N, float ag, float eta, color C, color Cspec0) BUILTIN; closure color microfacet_beckmann(normal N, float ab) BUILTIN; closure color microfacet_beckmann_aniso(normal N, vector T, float ax, float ay) BUILTIN; closure color microfacet_beckmann_refraction(normal N, float ab, float eta) BUILTIN; @@ -539,11 +544,15 @@ closure color emission() BUILTIN; closure color background() BUILTIN; closure color holdout() BUILTIN; closure color ambient_occlusion() BUILTIN; +closure color principled_diffuse(normal N, float roughness) BUILTIN; +closure color principled_sheen(normal N) BUILTIN; +closure color principled_clearcoat(normal N, float clearcoat, float clearcoat_gloss) BUILTIN; // BSSRDF closure color bssrdf_cubic(normal N, vector radius, float texture_blur, float sharpness) BUILTIN; closure color bssrdf_gaussian(normal N, vector radius, float texture_blur) BUILTIN; closure color bssrdf_burley(normal N, vector radius, float texture_blur, color albedo) BUILTIN; +closure color bssrdf_principled(normal N, vector radius, float texture_blur, color subsurface_color, float roughness) BUILTIN; // Hair closure color hair_reflection(normal N, float roughnessu, float roughnessv, vector T, float offset) BUILTIN; diff --git a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h b/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h index 8f0fb294870..13f8ea040fd 100644 --- a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h +++ b/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h @@ -122,7 +122,7 @@ ccl_device void kernel_holdout_emission_blurring_pathtermination_ao( #ifdef __SHADOW_TRICKS__ if((sd->object_flag & SD_OBJECT_SHADOW_CATCHER)) { - if (state->flag & PATH_RAY_CAMERA) { + if(state->flag & PATH_RAY_CAMERA) { state->flag |= (PATH_RAY_SHADOW_CATCHER | PATH_RAY_SHADOW_CATCHER_ONLY | PATH_RAY_STORE_SHADOW_INFO); state->catcher_object = sd->object; if(!kernel_data.background.transparent) { diff --git a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h.orig b/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h.orig new file mode 100644 index 00000000000..3a70068f4d7 --- /dev/null +++ b/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h.orig @@ -0,0 +1,297 @@ +/* + * Copyright 2011-2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CCL_NAMESPACE_BEGIN + +/* This kernel takes care of the logic to process "material of type holdout", + * indirect primitive emission, bsdf blurring, probabilistic path termination + * and AO. + * + * This kernels determines the rays for which a shadow_blocked() function + * associated with AO should be executed. Those rays for which a + * shadow_blocked() function for AO must be executed are marked with flag + * RAY_SHADOW_RAY_CAST_ao and enqueued into the queue + * QUEUE_SHADOW_RAY_CAST_AO_RAYS + * + * Ray state of rays that are terminated in this kernel are changed to RAY_UPDATE_BUFFER + * + * Note on Queues: + * This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS + * and processes only the rays of state RAY_ACTIVE. + * There are different points in this kernel where a ray may terminate and + * reach RAY_UPDATE_BUFFER state. These rays are enqueued into + * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will still be present + * in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has + * been changed to RAY_UPDATE_BUFFER, there is no problem. + * + * State of queues when this kernel is called: + * At entry, + * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and + * RAY_REGENERATED rays + * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with + * RAY_TO_REGENERATE rays. + * - QUEUE_SHADOW_RAY_CAST_AO_RAYS will be empty. + * At exit, + * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, + * RAY_REGENERATED and RAY_UPDATE_BUFFER rays. + * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with + * RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays. + * - QUEUE_SHADOW_RAY_CAST_AO_RAYS will be filled with rays marked with + * flag RAY_SHADOW_RAY_CAST_AO + */ +ccl_device void kernel_holdout_emission_blurring_pathtermination_ao( + KernelGlobals *kg, + ccl_local_param BackgroundAOLocals *locals) +{ + if(ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { + locals->queue_atomics_bg = 0; + locals->queue_atomics_ao = 0; + } + ccl_barrier(CCL_LOCAL_MEM_FENCE); + + char enqueue_flag = 0; + char enqueue_flag_AO_SHADOW_RAY_CAST = 0; + int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); + ray_index = get_ray_index(kg, ray_index, + QUEUE_ACTIVE_AND_REGENERATED_RAYS, + kernel_split_state.queue_data, + kernel_split_params.queue_size, + 0); + +#ifdef __COMPUTE_DEVICE_GPU__ + /* If we are executing on a GPU device, we exit all threads that are not + * required. + * + * If we are executing on a CPU device, then we need to keep all threads + * active since we have barrier() calls later in the kernel. CPU devices, + * expect all threads to execute barrier statement. + */ + if(ray_index == QUEUE_EMPTY_SLOT) { + return; + } +#endif /* __COMPUTE_DEVICE_GPU__ */ + +#ifndef __COMPUTE_DEVICE_GPU__ + if(ray_index != QUEUE_EMPTY_SLOT) { +#endif + + int stride = kernel_split_params.stride; + + unsigned int work_index; + unsigned int pixel_x; + unsigned int pixel_y; + + unsigned int tile_x; + unsigned int tile_y; + unsigned int sample; + + RNG rng = kernel_split_state.rng[ray_index]; + ccl_global PathState *state = 0x0; + float3 throughput; + + ccl_global char *ray_state = kernel_split_state.ray_state; + ShaderData *sd = &kernel_split_state.sd[ray_index]; + ccl_global float *buffer = kernel_split_params.buffer; + + if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { + + throughput = kernel_split_state.throughput[ray_index]; + state = &kernel_split_state.path_state[ray_index]; + + work_index = kernel_split_state.work_array[ray_index]; + sample = get_work_sample(kg, work_index, ray_index) + kernel_split_params.start_sample; + get_work_pixel_tile_position(kg, &pixel_x, &pixel_y, + &tile_x, &tile_y, + work_index, + ray_index); + + buffer += (kernel_split_params.offset + pixel_x + pixel_y * stride) * kernel_data.film.pass_stride; + +#ifdef __SHADOW_TRICKS__ + if((sd->object_flag & SD_OBJECT_SHADOW_CATCHER)) { +<<<<<<< HEAD + if (state->flag & PATH_RAY_CAMERA) { + state->flag |= (PATH_RAY_SHADOW_CATCHER | PATH_RAY_SHADOW_CATCHER_ONLY | PATH_RAY_STORE_SHADOW_INFO); +======= + if(state->flag & PATH_RAY_CAMERA) { + state->flag |= (PATH_RAY_SHADOW_CATCHER | PATH_RAY_SHADOW_CATCHER_ONLY); +>>>>>>> origin/master + state->catcher_object = sd->object; + if(!kernel_data.background.transparent) { + PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; + ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; + L->shadow_color = indirect_background(kg, &kernel_split_state.sd_DL_shadow[ray_index], state, ray); + } + } + } + else { + state->flag &= ~PATH_RAY_SHADOW_CATCHER_ONLY; + } +#endif /* __SHADOW_TRICKS__ */ + + /* holdout */ +#ifdef __HOLDOUT__ + if(((sd->flag & SD_HOLDOUT) || + (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && + (state->flag & PATH_RAY_CAMERA)) + { + if(kernel_data.background.transparent) { + float3 holdout_weight; + if(sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { + holdout_weight = make_float3(1.0f, 1.0f, 1.0f); + } + else { + holdout_weight = shader_holdout_eval(kg, sd); + } + /* any throughput is ok, should all be identical here */ + kernel_split_state.L_transparent[ray_index] += average(holdout_weight*throughput); + } + if(sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { + ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); + enqueue_flag = 1; + } + } +#endif /* __HOLDOUT__ */ + } + + if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { + PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; + /* Holdout mask objects do not write data passes. */ + kernel_write_data_passes(kg, + buffer, + L, + sd, + sample, + state, + throughput); + /* Blurring of bsdf after bounces, for rays that have a small likelihood + * of following this particular path (diffuse, rough glossy. + */ + if(kernel_data.integrator.filter_glossy != FLT_MAX) { + float blur_pdf = kernel_data.integrator.filter_glossy*state->min_ray_pdf; + if(blur_pdf < 1.0f) { + float blur_roughness = sqrtf(1.0f - blur_pdf)*0.5f; + shader_bsdf_blur(kg, sd, blur_roughness); + } + } + +#ifdef __EMISSION__ + /* emission */ + if(sd->flag & SD_EMISSION) { + /* TODO(sergey): is isect.t wrong here for transparent surfaces? */ + float3 emission = indirect_primitive_emission( + kg, + sd, + kernel_split_state.isect[ray_index].t, + state->flag, + state->ray_pdf); + path_radiance_accum_emission(L, throughput, emission, state->bounce); + } +#endif /* __EMISSION__ */ + + /* Path termination. this is a strange place to put the termination, it's + * mainly due to the mixed in MIS that we use. gives too many unneeded + * shader evaluations, only need emission if we are going to terminate. + */ + float probability = path_state_terminate_probability(kg, state, throughput); + + if(probability == 0.0f) { + ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); + enqueue_flag = 1; + } + + if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { + if(probability != 1.0f) { + float terminate = path_state_rng_1D_for_decision(kg, &rng, state, PRNG_TERMINATE); + if(terminate >= probability) { + ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); + enqueue_flag = 1; + } + else { + kernel_split_state.throughput[ray_index] = throughput/probability; + } + } + + kernel_update_denoising_features(kg, sd, state, L); + } + } + +#ifdef __AO__ + if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { + /* ambient occlusion */ + if(kernel_data.integrator.use_ambient_occlusion || + (sd->flag & SD_AO)) + { + /* todo: solve correlation */ + float bsdf_u, bsdf_v; + path_state_rng_2D(kg, &rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + + float ao_factor = kernel_data.background.ao_factor; + float3 ao_N; + kernel_split_state.ao_bsdf[ray_index] = shader_bsdf_ao(kg, sd, ao_factor, &ao_N); + kernel_split_state.ao_alpha[ray_index] = shader_bsdf_alpha(kg, sd); + + float3 ao_D; + float ao_pdf; + sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); + + if(dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) { + Ray _ray; + _ray.P = ray_offset(sd->P, sd->Ng); + _ray.D = ao_D; + _ray.t = kernel_data.background.ao_distance; +#ifdef __OBJECT_MOTION__ + _ray.time = sd->time; +#endif + _ray.dP = sd->dP; + _ray.dD = differential3_zero(); + kernel_split_state.ao_light_ray[ray_index] = _ray; + + ADD_RAY_FLAG(ray_state, ray_index, RAY_SHADOW_RAY_CAST_AO); + enqueue_flag_AO_SHADOW_RAY_CAST = 1; + } + } + } +#endif /* __AO__ */ + kernel_split_state.rng[ray_index] = rng; + + +#ifndef __COMPUTE_DEVICE_GPU__ + } +#endif + + /* Enqueue RAY_UPDATE_BUFFER rays. */ + enqueue_ray_index_local(ray_index, + QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, + enqueue_flag, + kernel_split_params.queue_size, + &locals->queue_atomics_bg, + kernel_split_state.queue_data, + kernel_split_params.queue_index); + +#ifdef __AO__ + /* Enqueue to-shadow-ray-cast rays. */ + enqueue_ray_index_local(ray_index, + QUEUE_SHADOW_RAY_CAST_AO_RAYS, + enqueue_flag_AO_SHADOW_RAY_CAST, + kernel_split_params.queue_size, + &locals->queue_atomics_ao, + kernel_split_state.queue_data, + kernel_split_params.queue_index); +#endif +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index b53e0243332..e4ce3a7b233 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -76,6 +76,338 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float * float param2 = (stack_valid(param2_offset))? stack_load_float(stack, param2_offset): __uint_as_float(node.w); switch(type) { + case CLOSURE_BSDF_PRINCIPLED_ID: { + uint specular_offset, roughness_offset, specular_tint_offset, anisotropic_offset, sheen_offset, + sheen_tint_offset, clearcoat_offset, clearcoat_gloss_offset, eta_offset, transparency_offset, + anisotropic_rotation_offset, refraction_roughness_offset; + uint4 data_node2 = read_node(kg, offset); + + float3 T = stack_load_float3(stack, data_node.y); + decode_node_uchar4(data_node.z, &specular_offset, &roughness_offset, &specular_tint_offset, &anisotropic_offset); + decode_node_uchar4(data_node.w, &sheen_offset, &sheen_tint_offset, &clearcoat_offset, &clearcoat_gloss_offset); + decode_node_uchar4(data_node2.x, &eta_offset, &transparency_offset, &anisotropic_rotation_offset, &refraction_roughness_offset); + + // get Disney principled parameters + float metallic = param1; + float subsurface = param2; + float specular = stack_load_float(stack, specular_offset); + float roughness = stack_load_float(stack, roughness_offset); + float specular_tint = stack_load_float(stack, specular_tint_offset); + float anisotropic = stack_load_float(stack, anisotropic_offset); + float sheen = stack_load_float(stack, sheen_offset); + float sheen_tint = stack_load_float(stack, sheen_tint_offset); + float clearcoat = stack_load_float(stack, clearcoat_offset); + float clearcoat_gloss = stack_load_float(stack, clearcoat_gloss_offset); + float transparency = stack_load_float(stack, transparency_offset); + float anisotropic_rotation = stack_load_float(stack, anisotropic_rotation_offset); + float refraction_roughness = stack_load_float(stack, refraction_roughness_offset); + float eta = fmaxf(stack_load_float(stack, eta_offset), 1e-5f); + + ClosureType distribution = stack_valid(data_node2.y) ? (ClosureType) data_node2.y : CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID; + + /* rotate tangent */ + if(anisotropic_rotation != 0.0f) + T = rotate_around_axis(T, N, anisotropic_rotation * M_2PI_F); + + /* calculate ior */ + float ior = (sd->flag & SD_BACKFACING) ? 1.0f / eta : eta; + + // calculate fresnel for refraction + float cosNO = dot(N, sd->I); + float fresnel = fresnel_dielectric_cos(cosNO, ior); + + // calculate weights of the diffuse and specular part + float diffuse_weight = (1.0f - saturate(metallic)) * (1.0f - saturate(transparency)); + + float transp = saturate(transparency) * (1.0f - saturate(metallic)); + float specular_weight = (1.0f - transp); + + // get the base color + uint4 data_base_color = read_node(kg, offset); + float3 base_color = stack_valid(data_base_color.x) ? stack_load_float3(stack, data_base_color.x) : + make_float3(__uint_as_float(data_base_color.y), __uint_as_float(data_base_color.z), __uint_as_float(data_base_color.w)); + + // get the additional clearcoat normal and subsurface scattering radius + uint4 data_cn_ssr = read_node(kg, offset); + float3 clearcoat_normal = stack_valid(data_cn_ssr.x) ? stack_load_float3(stack, data_cn_ssr.x) : sd->N; + float3 subsurface_radius = stack_valid(data_cn_ssr.y) ? stack_load_float3(stack, data_cn_ssr.y) : make_float3(1.0f, 1.0f, 1.0f); + + // get the subsurface color + uint4 data_subsurface_color = read_node(kg, offset); + float3 subsurface_color = stack_valid(data_subsurface_color.x) ? stack_load_float3(stack, data_subsurface_color.x) : + make_float3(__uint_as_float(data_subsurface_color.y), __uint_as_float(data_subsurface_color.z), __uint_as_float(data_subsurface_color.w)); + + float3 weight = sd->svm_closure_weight * mix_weight; + +#ifdef __SUBSURFACE__ + float3 albedo = subsurface_color * subsurface + base_color * (1.0f - subsurface); + float3 subsurf_weight = weight * albedo * diffuse_weight; + float subsurf_sample_weight = fabsf(average(subsurf_weight)); + + /* disable in case of diffuse ancestor, can't see it well then and + * adds considerably noise due to probabilities of continuing path + * getting lower and lower */ + if(path_flag & PATH_RAY_DIFFUSE_ANCESTOR) { + subsurface = 0.0f; + } + + /* diffuse */ + if(fabsf(average(base_color)) > CLOSURE_WEIGHT_CUTOFF) { + if(subsurface < CLOSURE_WEIGHT_CUTOFF && diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { + float3 diff_weight = weight * base_color * diffuse_weight; + + PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf*)bsdf_alloc(sd, sizeof(PrincipledDiffuseBsdf), diff_weight); + + if(bsdf) { + bsdf->N = N; + bsdf->roughness = roughness; + + /* setup bsdf */ + sd->flag |= bsdf_principled_diffuse_setup(bsdf); + } + } + else if(subsurface > CLOSURE_WEIGHT_CUTOFF && subsurf_sample_weight > CLOSURE_WEIGHT_CUTOFF) { + /* radius * scale */ + float3 radius = subsurface_radius * subsurface; + /* sharpness */ + float sharpness = 0.0f; + /* texture color blur */ + float texture_blur = 0.0f; + + /* create one closure per color channel */ + Bssrdf *bssrdf = bssrdf_alloc(sd, make_float3(subsurf_weight.x, 0.0f, 0.0f)); + if(bssrdf) { + bssrdf->sample_weight = subsurf_sample_weight; + bssrdf->radius = radius.x; + bssrdf->texture_blur = texture_blur; + bssrdf->albedo = albedo.x; + bssrdf->sharpness = sharpness; + bssrdf->N = N; + bssrdf->roughness = roughness; + + /* setup bsdf */ + sd->flag |= bssrdf_setup(bssrdf, (ClosureType)CLOSURE_BSSRDF_PRINCIPLED_ID); + } + + bssrdf = bssrdf_alloc(sd, make_float3(0.0f, subsurf_weight.y, 0.0f)); + if(bssrdf) { + bssrdf->sample_weight = subsurf_sample_weight; + bssrdf->radius = radius.y; + bssrdf->texture_blur = texture_blur; + bssrdf->albedo = albedo.y; + bssrdf->sharpness = sharpness; + bssrdf->N = N; + bssrdf->roughness = roughness; + + /* setup bsdf */ + sd->flag |= bssrdf_setup(bssrdf, (ClosureType)CLOSURE_BSSRDF_PRINCIPLED_ID); + } + + bssrdf = bssrdf_alloc(sd, make_float3(0.0f, 0.0f, subsurf_weight.z)); + if(bssrdf) { + bssrdf->sample_weight = subsurf_sample_weight; + bssrdf->radius = radius.z; + bssrdf->texture_blur = texture_blur; + bssrdf->albedo = albedo.z; + bssrdf->sharpness = sharpness; + bssrdf->N = N; + bssrdf->roughness = roughness; + + /* setup bsdf */ + sd->flag |= bssrdf_setup(bssrdf, (ClosureType)CLOSURE_BSSRDF_PRINCIPLED_ID); + } + } + } +#else + /* diffuse */ + if(diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { + float3 diff_weight = weight * base_color * diffuse_weight; + + PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf*)bsdf_alloc(sd, sizeof(PrincipledDiffuseBsdf), diff_weight); + + if(bsdf) { + bsdf->N = N; + bsdf->roughness = roughness; + + /* setup bsdf */ + sd->flag |= bsdf_principled_diffuse_setup(bsdf); + } + } +#endif + + /* sheen */ + if(diffuse_weight > CLOSURE_WEIGHT_CUTOFF && sheen > CLOSURE_WEIGHT_CUTOFF) { + float m_cdlum = linear_rgb_to_gray(base_color); + float3 m_ctint = m_cdlum > 0.0f ? base_color / m_cdlum : make_float3(1.0f, 1.0f, 1.0f); // normalize lum. to isolate hue+sat + + /* color of the sheen component */ + float3 sheen_color = make_float3(1.0f, 1.0f, 1.0f) * (1.0f - sheen_tint) + m_ctint * sheen_tint; + + float3 sheen_weight = weight * sheen * sheen_color * diffuse_weight; + + PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf*)bsdf_alloc(sd, sizeof(PrincipledSheenBsdf), sheen_weight); + + if(bsdf) { + bsdf->N = N; + + /* setup bsdf */ + sd->flag |= bsdf_principled_sheen_setup(bsdf); + } + } + + /* specular reflection */ +#ifdef __CAUSTICS_TRICKS__ + if(kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) { +#endif + if(specular_weight > CLOSURE_WEIGHT_CUTOFF && (specular > CLOSURE_WEIGHT_CUTOFF || metallic > CLOSURE_WEIGHT_CUTOFF)) { + float3 spec_weight = weight * specular_weight; + + MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), spec_weight); + MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + + if(bsdf && extra) { + bsdf->N = N; + bsdf->ior = (2.0f / (1.0f - safe_sqrtf(0.08f * specular))) - 1.0f; + bsdf->T = T; + bsdf->extra = extra; + + float aspect = safe_sqrtf(1.0f - anisotropic * 0.9f); + float r2 = roughness * roughness; + + bsdf->alpha_x = fmaxf(0.001f, r2 / aspect); + bsdf->alpha_y = fmaxf(0.001f, r2 * aspect); + + float m_cdlum = 0.3f * base_color.x + 0.6f * base_color.y + 0.1f * base_color.z; // luminance approx. + float3 m_ctint = m_cdlum > 0.0f ? base_color / m_cdlum : make_float3(0.0f, 0.0f, 0.0f); // normalize lum. to isolate hue+sat + float3 tmp_col = make_float3(1.0f, 1.0f, 1.0f) * (1.0f - specular_tint) + m_ctint * specular_tint; + + bsdf->extra->cspec0 = (specular * 0.08f * tmp_col) * (1.0f - metallic) + base_color * metallic; + bsdf->extra->color = base_color; + + /* setup bsdf */ + if(distribution == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID || roughness <= 0.075f) /* use single-scatter GGX */ + sd->flag |= bsdf_microfacet_ggx_aniso_fresnel_setup(bsdf); + else /* use multi-scatter GGX */ + sd->flag |= bsdf_microfacet_multi_ggx_aniso_fresnel_setup(bsdf); + } + } +#ifdef __CAUSTICS_TRICKS__ + } +#endif + + /* BSDF */ +#ifdef __CAUSTICS_TRICKS__ + if(kernel_data.integrator.caustics_reflective || kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) { +#endif + if(transp > CLOSURE_WEIGHT_CUTOFF) { + float3 glass_weight = weight * transp; + float3 cspec0 = base_color * specular_tint + make_float3(1.0f, 1.0f, 1.0f) * (1.0f - specular_tint); + + if(roughness <= 5e-2f || distribution == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID) { /* use single-scatter GGX */ + float refl_roughness = roughness; + + /* reflection */ +#ifdef __CAUSTICS_TRICKS__ + if(kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) +#endif + { + MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), glass_weight*fresnel); + MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + + if(bsdf && extra) { + bsdf->N = N; + bsdf->extra = extra; + + bsdf->alpha_x = refl_roughness * refl_roughness; + bsdf->alpha_y = refl_roughness * refl_roughness; + bsdf->ior = ior; + + bsdf->extra->color = base_color; + bsdf->extra->cspec0 = cspec0; + + /* setup bsdf */ + sd->flag |= bsdf_microfacet_ggx_fresnel_setup(bsdf); + } + } + + /* refraction */ +#ifdef __CAUSTICS_TRICKS__ + if(kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) +#endif + { + MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), base_color*glass_weight*(1.0f - fresnel)); + + if(bsdf) { + bsdf->N = N; + + if(distribution == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID) + refraction_roughness = 1.0f - (1.0f - refl_roughness) * (1.0f - refraction_roughness); + else + refraction_roughness = refl_roughness; + + bsdf->alpha_x = refraction_roughness * refraction_roughness; + bsdf->alpha_y = refraction_roughness * refraction_roughness; + bsdf->ior = ior; + + /* setup bsdf */ + sd->flag |= bsdf_microfacet_ggx_refraction_setup(bsdf); + } + } + } + else { /* use multi-scatter GGX */ + MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), glass_weight); + MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + + if(bsdf && extra) { + bsdf->N = N; + bsdf->extra = extra; + bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + + bsdf->alpha_x = roughness * roughness; + bsdf->alpha_y = roughness * roughness; + bsdf->ior = ior; + + bsdf->extra->color = base_color; + bsdf->extra->cspec0 = cspec0; + + /* setup bsdf */ + sd->flag |= bsdf_microfacet_multi_ggx_glass_fresnel_setup(bsdf); + } + } + } +#ifdef __CAUSTICS_TRICKS__ + } +#endif + + /* clearcoat */ +#ifdef __CAUSTICS_TRICKS__ + if(kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) { +#endif + if(clearcoat > CLOSURE_WEIGHT_CUTOFF) { + MicrofacetBsdf *bsdf = (MicrofacetBsdf*)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); + MicrofacetExtra *extra = (MicrofacetExtra*)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + + if(bsdf && extra) { + bsdf->N = clearcoat_normal; + bsdf->ior = 1.5f; + bsdf->extra = extra; + + bsdf->alpha_x = 0.1f * (1.0f - clearcoat_gloss) + 0.001f * clearcoat_gloss; + bsdf->alpha_y = 0.1f * (1.0f - clearcoat_gloss) + 0.001f * clearcoat_gloss; + + bsdf->extra->cspec0 = make_float3(0.04f, 0.04f, 0.04f); + bsdf->extra->clearcoat = clearcoat; + + /* setup bsdf */ + sd->flag |= bsdf_microfacet_ggx_clearcoat_setup(bsdf); + } + } +#ifdef __CAUSTICS_TRICKS__ + } +#endif + + break; + } case CLOSURE_BSDF_DIFFUSE_ID: { float3 weight = sd->svm_closure_weight * mix_weight; OrenNayarBsdf *bsdf = (OrenNayarBsdf*)bsdf_alloc(sd, sizeof(OrenNayarBsdf), weight); diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 6287e9b70a3..be99777dfb5 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -397,16 +397,23 @@ typedef enum ClosureType { CLOSURE_BSDF_DIFFUSE_ID, CLOSURE_BSDF_OREN_NAYAR_ID, CLOSURE_BSDF_DIFFUSE_RAMP_ID, + CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID, + CLOSURE_BSDF_PRINCIPLED_SHEEN_ID, CLOSURE_BSDF_DIFFUSE_TOON_ID, /* Glossy */ CLOSURE_BSDF_REFLECTION_ID, CLOSURE_BSDF_MICROFACET_GGX_ID, + CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID, + CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID, CLOSURE_BSDF_MICROFACET_BECKMANN_ID, CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID, CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID, CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID, + CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID, CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_FRESNEL_ID, CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID, CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID, CLOSURE_BSDF_ASHIKHMIN_VELVET_ID, @@ -422,16 +429,19 @@ typedef enum ClosureType { CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID, CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID, CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID, CLOSURE_BSDF_SHARP_GLASS_ID, CLOSURE_BSDF_HAIR_TRANSMISSION_ID, /* Special cases */ CLOSURE_BSDF_BSSRDF_ID, + CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID, CLOSURE_BSDF_TRANSPARENT_ID, /* BSSRDF */ CLOSURE_BSSRDF_CUBIC_ID, CLOSURE_BSSRDF_GAUSSIAN_ID, + CLOSURE_BSSRDF_PRINCIPLED_ID, CLOSURE_BSSRDF_BURLEY_ID, /* Other */ @@ -445,6 +455,8 @@ typedef enum ClosureType { CLOSURE_VOLUME_ABSORPTION_ID, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, + CLOSURE_BSDF_PRINCIPLED_ID, + NBUILTIN_CLOSURES } ClosureType; @@ -453,7 +465,7 @@ typedef enum ClosureType { #define CLOSURE_IS_BSDF_DIFFUSE(type) (type >= CLOSURE_BSDF_DIFFUSE_ID && type <= CLOSURE_BSDF_DIFFUSE_TOON_ID) #define CLOSURE_IS_BSDF_GLOSSY(type) (type >= CLOSURE_BSDF_REFLECTION_ID && type <= CLOSURE_BSDF_HAIR_REFLECTION_ID) #define CLOSURE_IS_BSDF_TRANSMISSION(type) (type >= CLOSURE_BSDF_TRANSLUCENT_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID) -#define CLOSURE_IS_BSDF_BSSRDF(type) (type == CLOSURE_BSDF_BSSRDF_ID) +#define CLOSURE_IS_BSDF_BSSRDF(type) (type == CLOSURE_BSDF_BSSRDF_ID || type == CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID) #define CLOSURE_IS_BSDF_TRANSPARENT(type) (type == CLOSURE_BSDF_TRANSPARENT_ID) #define CLOSURE_IS_BSDF_ANISOTROPIC(type) (type >= CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID) #define CLOSURE_IS_BSDF_MULTISCATTER(type) (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID ||\ @@ -470,6 +482,7 @@ typedef enum ClosureType { #define CLOSURE_IS_AMBIENT_OCCLUSION(type) (type == CLOSURE_AMBIENT_OCCLUSION_ID) #define CLOSURE_IS_PHASE(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) #define CLOSURE_IS_GLASS(type) (type >= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID && type <= CLOSURE_BSDF_SHARP_GLASS_ID) +#define CLOSURE_IS_PRINCIPLED(type) (type == CLOSURE_BSDF_PRINCIPLED_ID) #define CLOSURE_WEIGHT_CUTOFF 1e-5f diff --git a/intern/cycles/kernel/svm/svm_types.h.orig b/intern/cycles/kernel/svm/svm_types.h.orig new file mode 100644 index 00000000000..413651e10c0 --- /dev/null +++ b/intern/cycles/kernel/svm/svm_types.h.orig @@ -0,0 +1,506 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SVM_TYPES_H__ +#define __SVM_TYPES_H__ + +CCL_NAMESPACE_BEGIN + +/* Stack */ + +/* SVM stack has a fixed size */ +#define SVM_STACK_SIZE 255 +/* SVM stack offsets with this value indicate that it's not on the stack */ +#define SVM_STACK_INVALID 255 + +#define SVM_BUMP_EVAL_STATE_SIZE 9 + +/* Nodes */ + +/* Known frequencies of used nodes, used for selective nodes compilation + * in the kernel. Currently only affects split OpenCL kernel. + * + * Keep as defines so it's easy to check which nodes are to be compiled + * from preprocessor. + * + * Lower the number of group more often the node is used. + */ +#define NODE_GROUP_LEVEL_0 0 +#define NODE_GROUP_LEVEL_1 1 +#define NODE_GROUP_LEVEL_2 2 +#define NODE_GROUP_LEVEL_3 3 +#define NODE_GROUP_LEVEL_MAX NODE_GROUP_LEVEL_3 + +#define NODE_FEATURE_VOLUME (1 << 0) +#define NODE_FEATURE_HAIR (1 << 1) +#define NODE_FEATURE_BUMP (1 << 2) +#define NODE_FEATURE_BUMP_STATE (1 << 3) +/* TODO(sergey): Consider using something like ((uint)(-1)). + * Need to check carefully operand types around usage of this + * define first. + */ +#define NODE_FEATURE_ALL (NODE_FEATURE_VOLUME|NODE_FEATURE_HAIR|NODE_FEATURE_BUMP|NODE_FEATURE_BUMP_STATE) + +typedef enum ShaderNodeType { + NODE_END = 0, + NODE_CLOSURE_BSDF, + NODE_CLOSURE_EMISSION, + NODE_CLOSURE_BACKGROUND, + NODE_CLOSURE_SET_WEIGHT, + NODE_CLOSURE_WEIGHT, + NODE_MIX_CLOSURE, + NODE_JUMP_IF_ZERO, + NODE_JUMP_IF_ONE, + NODE_TEX_IMAGE, + NODE_TEX_IMAGE_BOX, + NODE_TEX_SKY, + NODE_GEOMETRY, + NODE_GEOMETRY_DUPLI, + NODE_LIGHT_PATH, + NODE_VALUE_F, + NODE_VALUE_V, + NODE_MIX, + NODE_ATTR, + NODE_CONVERT, + NODE_FRESNEL, + NODE_WIREFRAME, + NODE_WAVELENGTH, + NODE_BLACKBODY, + NODE_EMISSION_WEIGHT, + NODE_TEX_GRADIENT, + NODE_TEX_VORONOI, + NODE_TEX_MUSGRAVE, + NODE_TEX_WAVE, + NODE_TEX_MAGIC, + NODE_TEX_NOISE, + NODE_SHADER_JUMP, + NODE_SET_DISPLACEMENT, + NODE_GEOMETRY_BUMP_DX, + NODE_GEOMETRY_BUMP_DY, + NODE_SET_BUMP, + NODE_MATH, + NODE_VECTOR_MATH, + NODE_VECTOR_TRANSFORM, + NODE_MAPPING, + NODE_TEX_COORD, + NODE_TEX_COORD_BUMP_DX, + NODE_TEX_COORD_BUMP_DY, + NODE_ATTR_BUMP_DX, + NODE_ATTR_BUMP_DY, + NODE_TEX_ENVIRONMENT, + NODE_CLOSURE_HOLDOUT, + NODE_LAYER_WEIGHT, + NODE_CLOSURE_VOLUME, + NODE_SEPARATE_VECTOR, + NODE_COMBINE_VECTOR, + NODE_SEPARATE_HSV, + NODE_COMBINE_HSV, + NODE_HSV, + NODE_CAMERA, + NODE_INVERT, + NODE_NORMAL, + NODE_GAMMA, + NODE_TEX_CHECKER, + NODE_BRIGHTCONTRAST, + NODE_RGB_RAMP, + NODE_RGB_CURVES, + NODE_VECTOR_CURVES, + NODE_MIN_MAX, + NODE_LIGHT_FALLOFF, + NODE_OBJECT_INFO, + NODE_PARTICLE_INFO, + NODE_TEX_BRICK, + NODE_CLOSURE_SET_NORMAL, + NODE_CLOSURE_AMBIENT_OCCLUSION, + NODE_TANGENT, + NODE_NORMAL_MAP, + NODE_HAIR_INFO, + NODE_UVMAP, + NODE_TEX_VOXEL, + NODE_ENTER_BUMP_EVAL, + NODE_LEAVE_BUMP_EVAL, +} ShaderNodeType; + +typedef enum NodeAttributeType { + NODE_ATTR_FLOAT = 0, + NODE_ATTR_FLOAT3, + NODE_ATTR_MATRIX +} NodeAttributeType; + +typedef enum NodeGeometry { + NODE_GEOM_P = 0, + NODE_GEOM_N, + NODE_GEOM_T, + NODE_GEOM_I, + NODE_GEOM_Ng, + NODE_GEOM_uv +} NodeGeometry; + +typedef enum NodeObjectInfo { + NODE_INFO_OB_LOCATION, + NODE_INFO_OB_INDEX, + NODE_INFO_MAT_INDEX, + NODE_INFO_OB_RANDOM +} NodeObjectInfo; + +typedef enum NodeParticleInfo { + NODE_INFO_PAR_INDEX, + NODE_INFO_PAR_AGE, + NODE_INFO_PAR_LIFETIME, + NODE_INFO_PAR_LOCATION, + NODE_INFO_PAR_ROTATION, + NODE_INFO_PAR_SIZE, + NODE_INFO_PAR_VELOCITY, + NODE_INFO_PAR_ANGULAR_VELOCITY +} NodeParticleInfo; + +typedef enum NodeHairInfo { + NODE_INFO_CURVE_IS_STRAND, + NODE_INFO_CURVE_INTERCEPT, + NODE_INFO_CURVE_THICKNESS, + /*fade for minimum hair width transpency*/ + /*NODE_INFO_CURVE_FADE,*/ + NODE_INFO_CURVE_TANGENT_NORMAL +} NodeHairInfo; + +typedef enum NodeLightPath { + NODE_LP_camera = 0, + NODE_LP_shadow, + NODE_LP_diffuse, + NODE_LP_glossy, + NODE_LP_singular, + NODE_LP_reflection, + NODE_LP_transmission, + NODE_LP_volume_scatter, + NODE_LP_backfacing, + NODE_LP_ray_length, + NODE_LP_ray_depth, + NODE_LP_ray_diffuse, + NODE_LP_ray_glossy, + NODE_LP_ray_transparent, + NODE_LP_ray_transmission, +} NodeLightPath; + +typedef enum NodeLightFalloff { + NODE_LIGHT_FALLOFF_QUADRATIC, + NODE_LIGHT_FALLOFF_LINEAR, + NODE_LIGHT_FALLOFF_CONSTANT +} NodeLightFalloff; + +typedef enum NodeTexCoord { + NODE_TEXCO_NORMAL, + NODE_TEXCO_OBJECT, + NODE_TEXCO_CAMERA, + NODE_TEXCO_WINDOW, + NODE_TEXCO_REFLECTION, + NODE_TEXCO_DUPLI_GENERATED, + NODE_TEXCO_DUPLI_UV, + NODE_TEXCO_VOLUME_GENERATED +} NodeTexCoord; + +typedef enum NodeMix { + NODE_MIX_BLEND = 0, + NODE_MIX_ADD, + NODE_MIX_MUL, + NODE_MIX_SUB, + NODE_MIX_SCREEN, + NODE_MIX_DIV, + NODE_MIX_DIFF, + NODE_MIX_DARK, + NODE_MIX_LIGHT, + NODE_MIX_OVERLAY, + NODE_MIX_DODGE, + NODE_MIX_BURN, + NODE_MIX_HUE, + NODE_MIX_SAT, + NODE_MIX_VAL, + NODE_MIX_COLOR, + NODE_MIX_SOFT, + NODE_MIX_LINEAR, + NODE_MIX_CLAMP /* used for the clamp UI option */ +} NodeMix; + +typedef enum NodeMath { + NODE_MATH_ADD, + NODE_MATH_SUBTRACT, + NODE_MATH_MULTIPLY, + NODE_MATH_DIVIDE, + NODE_MATH_SINE, + NODE_MATH_COSINE, + NODE_MATH_TANGENT, + NODE_MATH_ARCSINE, + NODE_MATH_ARCCOSINE, + NODE_MATH_ARCTANGENT, + NODE_MATH_POWER, + NODE_MATH_LOGARITHM, + NODE_MATH_MINIMUM, + NODE_MATH_MAXIMUM, + NODE_MATH_ROUND, + NODE_MATH_LESS_THAN, + NODE_MATH_GREATER_THAN, + NODE_MATH_MODULO, + NODE_MATH_ABSOLUTE, + NODE_MATH_CLAMP /* used for the clamp UI option */ +} NodeMath; + +typedef enum NodeVectorMath { + NODE_VECTOR_MATH_ADD, + NODE_VECTOR_MATH_SUBTRACT, + NODE_VECTOR_MATH_AVERAGE, + NODE_VECTOR_MATH_DOT_PRODUCT, + NODE_VECTOR_MATH_CROSS_PRODUCT, + NODE_VECTOR_MATH_NORMALIZE +} NodeVectorMath; + +typedef enum NodeVectorTransformType { + NODE_VECTOR_TRANSFORM_TYPE_VECTOR, + NODE_VECTOR_TRANSFORM_TYPE_POINT, + NODE_VECTOR_TRANSFORM_TYPE_NORMAL +} NodeVectorTransformType; + +typedef enum NodeVectorTransformConvertSpace { + NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD, + NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT, + NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA +} NodeVectorTransformConvertSpace; + +typedef enum NodeConvert { + NODE_CONVERT_FV, + NODE_CONVERT_FI, + NODE_CONVERT_CF, + NODE_CONVERT_CI, + NODE_CONVERT_VF, + NODE_CONVERT_VI, + NODE_CONVERT_IF, + NODE_CONVERT_IV +} NodeConvert; + +typedef enum NodeMusgraveType { + NODE_MUSGRAVE_MULTIFRACTAL, + NODE_MUSGRAVE_FBM, + NODE_MUSGRAVE_HYBRID_MULTIFRACTAL, + NODE_MUSGRAVE_RIDGED_MULTIFRACTAL, + NODE_MUSGRAVE_HETERO_TERRAIN +} NodeMusgraveType; + +typedef enum NodeWaveType { + NODE_WAVE_BANDS, + NODE_WAVE_RINGS +} NodeWaveType; + +typedef enum NodeWaveProfiles { + NODE_WAVE_PROFILE_SIN, + NODE_WAVE_PROFILE_SAW, +} NodeWaveProfile; + +typedef enum NodeSkyType { + NODE_SKY_OLD, + NODE_SKY_NEW +} NodeSkyType; + +typedef enum NodeGradientType { + NODE_BLEND_LINEAR, + NODE_BLEND_QUADRATIC, + NODE_BLEND_EASING, + NODE_BLEND_DIAGONAL, + NODE_BLEND_RADIAL, + NODE_BLEND_QUADRATIC_SPHERE, + NODE_BLEND_SPHERICAL +} NodeGradientType; + +typedef enum NodeVoronoiColoring { + NODE_VORONOI_INTENSITY, + NODE_VORONOI_CELLS +} NodeVoronoiColoring; + +typedef enum NodeBlendWeightType { + NODE_LAYER_WEIGHT_FRESNEL, + NODE_LAYER_WEIGHT_FACING +} NodeBlendWeightType; + +typedef enum NodeTangentDirectionType { + NODE_TANGENT_RADIAL, + NODE_TANGENT_UVMAP +} NodeTangentDirectionType; + +typedef enum NodeTangentAxis { + NODE_TANGENT_AXIS_X, + NODE_TANGENT_AXIS_Y, + NODE_TANGENT_AXIS_Z +} NodeTangentAxis; + +typedef enum NodeNormalMapSpace { + NODE_NORMAL_MAP_TANGENT, + NODE_NORMAL_MAP_OBJECT, + NODE_NORMAL_MAP_WORLD, + NODE_NORMAL_MAP_BLENDER_OBJECT, + NODE_NORMAL_MAP_BLENDER_WORLD, +} NodeNormalMapSpace; + +typedef enum NodeImageColorSpace { + NODE_COLOR_SPACE_NONE = 0, + NODE_COLOR_SPACE_COLOR = 1, +} NodeImageColorSpace; + +typedef enum NodeImageProjection { + NODE_IMAGE_PROJ_FLAT = 0, + NODE_IMAGE_PROJ_BOX = 1, + NODE_IMAGE_PROJ_SPHERE = 2, + NODE_IMAGE_PROJ_TUBE = 3, +} NodeImageProjection; + +typedef enum NodeEnvironmentProjection { + NODE_ENVIRONMENT_EQUIRECTANGULAR = 0, + NODE_ENVIRONMENT_MIRROR_BALL = 1, +} NodeEnvironmentProjection; + +typedef enum NodeBumpOffset { + NODE_BUMP_OFFSET_CENTER, + NODE_BUMP_OFFSET_DX, + NODE_BUMP_OFFSET_DY, +} NodeBumpOffset; + +typedef enum NodeTexVoxelSpace { + NODE_TEX_VOXEL_SPACE_OBJECT = 0, + NODE_TEX_VOXEL_SPACE_WORLD = 1, +} NodeTexVoxelSpace; + +typedef enum ShaderType { + SHADER_TYPE_SURFACE, + SHADER_TYPE_VOLUME, + SHADER_TYPE_DISPLACEMENT, + SHADER_TYPE_BUMP, +} ShaderType; + +/* Closure */ + +typedef enum ClosureType { + /* Special type, flags generic node as a non-BSDF. */ + CLOSURE_NONE_ID, + + CLOSURE_BSDF_ID, + + /* Diffuse */ + CLOSURE_BSDF_DIFFUSE_ID, + CLOSURE_BSDF_OREN_NAYAR_ID, + CLOSURE_BSDF_DIFFUSE_RAMP_ID, + CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID, + CLOSURE_BSDF_PRINCIPLED_SHEEN_ID, + CLOSURE_BSDF_DIFFUSE_TOON_ID, + + /* Glossy */ + CLOSURE_BSDF_REFLECTION_ID, + CLOSURE_BSDF_MICROFACET_GGX_ID, + CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID, + CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID, + CLOSURE_BSDF_MICROFACET_BECKMANN_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID, + CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID, + CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID, + CLOSURE_BSDF_MICROFACET_GGX_ANISO_FRESNEL_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_FRESNEL_ID, + CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID, + CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID, + CLOSURE_BSDF_ASHIKHMIN_VELVET_ID, + CLOSURE_BSDF_PHONG_RAMP_ID, + CLOSURE_BSDF_GLOSSY_TOON_ID, + CLOSURE_BSDF_HAIR_REFLECTION_ID, + + /* Transmission */ + CLOSURE_BSDF_TRANSLUCENT_ID, + CLOSURE_BSDF_REFRACTION_ID, + CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID, + CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID, + CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID, + CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID, +<<<<<<< HEAD +======= + CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID, + CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID, +>>>>>>> origin/master + CLOSURE_BSDF_SHARP_GLASS_ID, + CLOSURE_BSDF_HAIR_TRANSMISSION_ID, + + /* Special cases */ + CLOSURE_BSDF_BSSRDF_ID, + CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID, + CLOSURE_BSDF_TRANSPARENT_ID, + + /* BSSRDF */ + CLOSURE_BSSRDF_CUBIC_ID, + CLOSURE_BSSRDF_GAUSSIAN_ID, + CLOSURE_BSSRDF_PRINCIPLED_ID, + CLOSURE_BSSRDF_BURLEY_ID, + + /* Other */ + CLOSURE_EMISSION_ID, + CLOSURE_BACKGROUND_ID, + CLOSURE_HOLDOUT_ID, + CLOSURE_AMBIENT_OCCLUSION_ID, + + /* Volume */ + CLOSURE_VOLUME_ID, + CLOSURE_VOLUME_ABSORPTION_ID, + CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, + + CLOSURE_BSDF_PRINCIPLED_ID, + + NBUILTIN_CLOSURES +} ClosureType; + +/* watch this, being lazy with memory usage */ +#define CLOSURE_IS_BSDF(type) (type <= CLOSURE_BSDF_TRANSPARENT_ID) +#define CLOSURE_IS_BSDF_DIFFUSE(type) (type >= CLOSURE_BSDF_DIFFUSE_ID && type <= CLOSURE_BSDF_DIFFUSE_TOON_ID) +<<<<<<< HEAD +#define CLOSURE_IS_BSDF_GLOSSY(type) (type >= CLOSURE_BSDF_REFLECTION_ID && type <= CLOSURE_BSDF_HAIR_REFLECTION_ID) +#define CLOSURE_IS_BSDF_TRANSMISSION(type) (type >= CLOSURE_BSDF_TRANSLUCENT_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID) +#define CLOSURE_IS_BSDF_BSSRDF(type) (type == CLOSURE_BSDF_BSSRDF_ID) +#define CLOSURE_IS_BSDF_TRANSPARENT(type) (type == CLOSURE_BSDF_TRANSPARENT_ID) +======= +#define CLOSURE_IS_BSDF_GLOSSY(type) (type >= CLOSURE_BSDF_GLOSSY_ID && type <= CLOSURE_BSDF_HAIR_REFLECTION_ID) +#define CLOSURE_IS_BSDF_TRANSMISSION(type) (type >= CLOSURE_BSDF_TRANSMISSION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID) +#define CLOSURE_IS_BSDF_BSSRDF(type) (type == CLOSURE_BSDF_BSSRDF_ID || type == CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID) +>>>>>>> origin/master +#define CLOSURE_IS_BSDF_ANISOTROPIC(type) (type >= CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID) +#define CLOSURE_IS_BSDF_MULTISCATTER(type) (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID ||\ + type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID || \ + type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID) +#define CLOSURE_IS_BSDF_MICROFACET(type) ((type >= CLOSURE_BSDF_REFLECTION_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID) ||\ + (type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)) +#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_BURLEY_ID) +#define CLOSURE_IS_BSSRDF(type) (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_BURLEY_ID) +#define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) +#define CLOSURE_IS_EMISSION(type) (type == CLOSURE_EMISSION_ID) +#define CLOSURE_IS_HOLDOUT(type) (type == CLOSURE_HOLDOUT_ID) +#define CLOSURE_IS_BACKGROUND(type) (type == CLOSURE_BACKGROUND_ID) +#define CLOSURE_IS_AMBIENT_OCCLUSION(type) (type == CLOSURE_AMBIENT_OCCLUSION_ID) +#define CLOSURE_IS_PHASE(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) +<<<<<<< HEAD +#define CLOSURE_IS_GLASS(type) (type >= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID && type <= CLOSURE_BSDF_SHARP_GLASS_ID) +======= +#define CLOSURE_IS_GLASS(type) (type >= CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID && type <= CLOSURE_BSDF_SHARP_GLASS_ID) +#define CLOSURE_IS_PRINCIPLED(type) (type == CLOSURE_BSDF_PRINCIPLED_ID) +>>>>>>> origin/master + +#define CLOSURE_WEIGHT_CUTOFF 1e-5f + +CCL_NAMESPACE_END + +#endif /* __SVM_TYPES_H__ */ + diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index 12fff8e5587..2d810ff664f 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -423,7 +423,8 @@ void ShaderGraph::copy_nodes(ShaderNodeSet& nodes, ShaderNodeMap& nnodemap) /* Graph simplification */ /* ******************** */ -/* Step 1: Remove proxy nodes. +/* Remove proxy nodes. + * * These only exists temporarily when exporting groups, and we must remove them * early so that node->attributes() and default links do not see them. */ @@ -493,7 +494,8 @@ void ShaderGraph::remove_proxy_nodes() } } -/* Step 2: Constant folding. +/* Constant folding. + * * Try to constant fold some nodes, and pipe result directly to * the input socket of connected nodes. */ @@ -554,7 +556,7 @@ void ShaderGraph::constant_fold() } } -/* Step 3: Simplification. */ +/* Simplification. */ void ShaderGraph::simplify_settings(Scene *scene) { foreach(ShaderNode *node, nodes) { @@ -562,7 +564,7 @@ void ShaderGraph::simplify_settings(Scene *scene) } } -/* Step 4: Deduplicate nodes with same settings. */ +/* Deduplicate nodes with same settings. */ void ShaderGraph::deduplicate_nodes() { /* NOTES: @@ -638,6 +640,48 @@ void ShaderGraph::deduplicate_nodes() } } +/* Check whether volume output has meaningful nodes, otherwise + * disconnect the output. + */ +void ShaderGraph::verify_volume_output() +{ + /* Check whether we can optimize the whole volume graph out. */ + ShaderInput *volume_in = output()->input("Volume"); + if(volume_in->link == NULL) { + return; + } + bool has_valid_volume = false; + ShaderNodeSet scheduled; + queue<ShaderNode*> traverse_queue; + /* Schedule volume output. */ + traverse_queue.push(volume_in->link->parent); + scheduled.insert(volume_in->link->parent); + /* Traverse down the tree. */ + while(!traverse_queue.empty()) { + ShaderNode *node = traverse_queue.front(); + traverse_queue.pop(); + /* Node is fully valid for volume, can't optimize anything out. */ + if(node->has_volume_support()) { + has_valid_volume = true; + break; + } + foreach(ShaderInput *input, node->inputs) { + if(input->link == NULL) { + continue; + } + if(scheduled.find(input->link->parent) != scheduled.end()) { + continue; + } + traverse_queue.push(input->link->parent); + scheduled.insert(input->link->parent); + } + } + if(!has_valid_volume) { + VLOG(1) << "Disconnect meaningless volume output."; + disconnect(volume_in->link); + } +} + void ShaderGraph::break_cycles(ShaderNode *node, vector<bool>& visited, vector<bool>& on_stack) { visited[node->id] = true; @@ -666,16 +710,11 @@ void ShaderGraph::clean(Scene *scene) { /* Graph simplification */ - /* 1: Remove proxy nodes was already done. */ - - /* 2: Constant folding. */ + /* NOTE: Remove proxy nodes was already done. */ constant_fold(); - - /* 3: Simplification. */ simplify_settings(scene); - - /* 4: De-duplication. */ deduplicate_nodes(); + verify_volume_output(); /* we do two things here: find cycles and break them, and remove unused * nodes that don't feed into the output. how cycles are broken is @@ -998,6 +1037,9 @@ int ShaderGraph::get_num_closures() else if(CLOSURE_IS_BSDF_MULTISCATTER(closure_type)) { num_closures += 2; } + else if(CLOSURE_IS_PRINCIPLED(closure_type)) { + num_closures += 8; + } else { ++num_closures; } diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h index 09932695d1f..72e391991a7 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/render/graph.h @@ -155,7 +155,7 @@ public: virtual bool has_spatial_varying() { return false; } virtual bool has_object_dependency() { return false; } virtual bool has_integrator_dependency() { return false; } - + virtual bool has_volume_support() { return false; } vector<ShaderInput*> inputs; vector<ShaderOutput*> outputs; @@ -284,6 +284,7 @@ protected: void constant_fold(); void simplify_settings(Scene *scene); void deduplicate_nodes(); + void verify_volume_output(); }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 1070e05a03b..3f56690d0c1 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -2286,6 +2286,150 @@ void DiffuseBsdfNode::compile(OSLCompiler& compiler) compiler.add(this, "node_diffuse_bsdf"); } +/* Disney principled BSDF Closure */ +NODE_DEFINE(PrincipledBsdfNode) +{ + NodeType* type = NodeType::add("principled_bsdf", create, NodeType::SHADER); + + static NodeEnum distribution_enum; + distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID); + distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); + SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); + SOCKET_IN_COLOR(base_color, "Base Color", make_float3(0.8f, 0.8f, 0.8f)); + SOCKET_IN_COLOR(subsurface_color, "Subsurface Color", make_float3(0.8f, 0.8f, 0.8f)); + SOCKET_IN_FLOAT(metallic, "Metallic", 0.0f); + SOCKET_IN_FLOAT(subsurface, "Subsurface", 0.0f); + SOCKET_IN_VECTOR(subsurface_radius, "Subsurface Radius", make_float3(0.1f, 0.1f, 0.1f)); + SOCKET_IN_FLOAT(specular, "Specular", 0.0f); + SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f); + SOCKET_IN_FLOAT(specular_tint, "Specular Tint", 0.0f); + SOCKET_IN_FLOAT(anisotropic, "Anisotropic", 0.0f); + SOCKET_IN_FLOAT(sheen, "Sheen", 0.0f); + SOCKET_IN_FLOAT(sheen_tint, "Sheen Tint", 0.0f); + SOCKET_IN_FLOAT(clearcoat, "Clearcoat", 0.0f); + SOCKET_IN_FLOAT(clearcoat_gloss, "Clearcoat Gloss", 0.0f); + SOCKET_IN_FLOAT(ior, "IOR", 0.0f); + SOCKET_IN_FLOAT(transparency, "Transparency", 0.0f); + SOCKET_IN_FLOAT(refraction_roughness, "Refraction Roughness", 0.0f); + SOCKET_IN_FLOAT(anisotropic_rotation, "Anisotropic Rotation", 0.0f); + SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); + SOCKET_IN_NORMAL(clearcoat_normal, "Clearcoat Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); + SOCKET_IN_NORMAL(tangent, "Tangent", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TANGENT); + SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); + + SOCKET_OUT_CLOSURE(BSDF, "BSDF"); + + return type; +} + +PrincipledBsdfNode::PrincipledBsdfNode() + : ShaderNode(node_type) +{ + special_type = SHADER_SPECIAL_TYPE_CLOSURE; + closure = CLOSURE_BSDF_PRINCIPLED_ID; + distribution = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID; + distribution_orig = NBUILTIN_CLOSURES; +} + +void PrincipledBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) +{ + if(shader->has_surface) { + ShaderInput *tangent_in = input("Tangent"); + + if(!tangent_in->link) + attributes->add(ATTR_STD_GENERATED); + } + + ShaderNode::attributes(shader, attributes); +} + +void PrincipledBsdfNode::compile(SVMCompiler& compiler, ShaderInput *p_metallic, ShaderInput *p_subsurface, ShaderInput *p_subsurface_radius, + ShaderInput *p_specular, ShaderInput *p_roughness, ShaderInput *p_specular_tint, ShaderInput *p_anisotropic, + ShaderInput *p_sheen, ShaderInput *p_sheen_tint, ShaderInput *p_clearcoat, ShaderInput *p_clearcoat_gloss, + ShaderInput *p_ior, ShaderInput *p_transparency, ShaderInput *p_anisotropic_rotation, ShaderInput *p_refraction_roughness) +{ + ShaderInput *base_color_in = input("Base Color"); + ShaderInput *subsurface_color_in = input("Subsurface Color"); + ShaderInput *normal_in = input("Normal"); + ShaderInput *clearcoat_normal_in = input("Clearcoat Normal"); + ShaderInput *tangent_in = input("Tangent"); + + float3 weight = make_float3(1.0f, 1.0f, 1.0f); + + compiler.add_node(NODE_CLOSURE_SET_WEIGHT, weight); + + int normal_offset = compiler.stack_assign_if_linked(normal_in); + int clearcoat_normal_offset = compiler.stack_assign_if_linked(clearcoat_normal_in); + int tangent_offset = compiler.stack_assign_if_linked(tangent_in); + int specular_offset = compiler.stack_assign(p_specular); + int roughness_offset = compiler.stack_assign(p_roughness); + int specular_tint_offset = compiler.stack_assign(p_specular_tint); + int anisotropic_offset = compiler.stack_assign(p_anisotropic); + int sheen_offset = compiler.stack_assign(p_sheen); + int sheen_tint_offset = compiler.stack_assign(p_sheen_tint); + int clearcoat_offset = compiler.stack_assign(p_clearcoat); + int clearcoat_gloss_offset = compiler.stack_assign(p_clearcoat_gloss); + int ior_offset = compiler.stack_assign(p_ior); + int transparency_offset = compiler.stack_assign(p_transparency); + int refraction_roughness_offset = compiler.stack_assign(p_refraction_roughness); + int anisotropic_rotation_offset = compiler.stack_assign(p_anisotropic_rotation); + int subsurface_radius_offset = compiler.stack_assign(p_subsurface_radius); + + compiler.add_node(NODE_CLOSURE_BSDF, + compiler.encode_uchar4(closure, + compiler.stack_assign(p_metallic), + compiler.stack_assign(p_subsurface), + compiler.closure_mix_weight_offset()), + __float_as_int((p_metallic) ? get_float(p_metallic->socket_type) : 0.0f), + __float_as_int((p_subsurface) ? get_float(p_subsurface->socket_type) : 0.0f)); + + compiler.add_node(normal_offset, tangent_offset, + compiler.encode_uchar4(specular_offset, roughness_offset, specular_tint_offset, anisotropic_offset), + compiler.encode_uchar4(sheen_offset, sheen_tint_offset, clearcoat_offset, clearcoat_gloss_offset)); + + compiler.add_node(compiler.encode_uchar4(ior_offset, transparency_offset, anisotropic_rotation_offset, refraction_roughness_offset), + distribution, SVM_STACK_INVALID, SVM_STACK_INVALID); + + float3 bc_default = get_float3(base_color_in->socket_type); + + compiler.add_node(((base_color_in->link) ? compiler.stack_assign(base_color_in) : SVM_STACK_INVALID), + __float_as_int(bc_default.x), __float_as_int(bc_default.y), __float_as_int(bc_default.z)); + + compiler.add_node(clearcoat_normal_offset, subsurface_radius_offset, SVM_STACK_INVALID, SVM_STACK_INVALID); + + float3 ss_default = get_float3(subsurface_color_in->socket_type); + + compiler.add_node(((subsurface_color_in->link) ? compiler.stack_assign(subsurface_color_in) : SVM_STACK_INVALID), + __float_as_int(ss_default.x), __float_as_int(ss_default.y), __float_as_int(ss_default.z)); +} + +bool PrincipledBsdfNode::has_integrator_dependency() +{ + ShaderInput *roughness_input = input("Roughness"); + return !roughness_input->link && roughness <= 1e-4f; +} + +void PrincipledBsdfNode::compile(SVMCompiler& compiler) +{ + compile(compiler, input("Metallic"), input("Subsurface"), input("Subsurface Radius"), input("Specular"), + input("Roughness"), input("Specular Tint"), input("Anisotropic"), input("Sheen"), input("Sheen Tint"), + input("Clearcoat"), input("Clearcoat Gloss"), input("IOR"), input("Transparency"), + input("Anisotropic Rotation"), input("Refraction Roughness")); +} + +void PrincipledBsdfNode::compile(OSLCompiler& compiler) +{ + compiler.parameter(this, "distribution"); + compiler.add(this, "node_principled_bsdf"); +} + +bool PrincipledBsdfNode::has_bssrdf_bump() +{ + /* detect if anything is plugged into the normal input besides the default */ + ShaderInput *normal_in = input("Normal"); + return (normal_in->link && normal_in->link->parent->special_type != SHADER_SPECIAL_TYPE_GEOMETRY); +} + /* Translucent BSDF Closure */ NODE_DEFINE(TranslucentBsdfNode) diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index a755b653a5b..bc488161e3b 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -361,6 +361,39 @@ public: float roughness; }; +/* Disney principled BRDF */ +class PrincipledBsdfNode : public ShaderNode { +public: + SHADER_NODE_CLASS(PrincipledBsdfNode) + + bool has_spatial_varying() { return true; } + bool has_surface_bssrdf() { return true; } + bool has_bssrdf_bump(); + void compile(SVMCompiler& compiler, ShaderInput *metallic, ShaderInput *subsurface, ShaderInput *subsurface_radius, + ShaderInput *specular, ShaderInput *roughness, ShaderInput *specular_tint, ShaderInput *anisotropic, + ShaderInput *sheen, ShaderInput *sheen_tint, ShaderInput *clearcoat, ShaderInput *clearcoat_gloss, + ShaderInput *ior, ShaderInput *transparency, ShaderInput *anisotropic_rotation, ShaderInput *refraction_roughness); + + float3 base_color; + float3 subsurface_color, subsurface_radius; + float metallic, subsurface, specular, roughness, specular_tint, anisotropic, + sheen, sheen_tint, clearcoat, clearcoat_gloss, ior, transparency, + anisotropic_rotation, refraction_roughness; + float3 normal, clearcoat_normal, tangent; + float surface_mix_weight; + ClosureType closure, distribution, distribution_orig; + + virtual bool equals(const ShaderNode * /*other*/) + { + /* TODO(sergey): With some care BSDF nodes can be de-duplicated. */ + return false; + } + + ClosureType get_closure_type() { return closure; } + bool has_integrator_dependency(); + void attributes(Shader *shader, AttributeRequestSet *attributes); +}; + class TranslucentBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(TranslucentBsdfNode) @@ -445,6 +478,7 @@ public: virtual ClosureType get_closure_type() { return CLOSURE_EMISSION_ID; } bool has_surface_emission() { return true; } + bool has_volume_support() { return true; } float3 color; float strength; @@ -496,6 +530,7 @@ public: return ShaderNode::get_feature() | NODE_FEATURE_VOLUME; } virtual ClosureType get_closure_type() { return closure; } + virtual bool has_volume_support() { return true; } float3 color; float density; diff --git a/release/scripts/freestyle/modules/freestyle/utils.py b/release/scripts/freestyle/modules/freestyle/utils.py index d3eba187f70..2d47995d474 100644 --- a/release/scripts/freestyle/modules/freestyle/utils.py +++ b/release/scripts/freestyle/modules/freestyle/utils.py @@ -318,7 +318,7 @@ class BoundingBox: class StrokeCollector(StrokeShader): - "Collects and Stores stroke objects" + """Collects and Stores stroke objects""" def __init__(self): StrokeShader.__init__(self) self.strokes = [] diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index c0d92c331b7..e50922593de 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -176,12 +176,11 @@ def draw(layout, context, context_member, property_type, use_edit=True): if not is_rna: props = row.operator("wm.properties_edit", text="Edit") assign_props(props, val_draw, key) + props = row.operator("wm.properties_remove", text="", icon='ZOOMOUT') + assign_props(props, val_draw, key) else: row.label(text="API Defined") - props = row.operator("wm.properties_remove", text="", icon='ZOOMOUT') - assign_props(props, val_draw, key) - class PropertyPanel: """ diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py index 78fcf0dd124..02fb05e29eb 100644 --- a/release/scripts/startup/bl_operators/anim.py +++ b/release/scripts/startup/bl_operators/anim.py @@ -36,7 +36,7 @@ from bpy.props import ( class ANIM_OT_keying_set_export(Operator): - "Export Keying Set to a python script" + """Export Keying Set to a python script""" bl_idname = "anim.keying_set_export" bl_label = "Export Keying Set..." @@ -102,15 +102,13 @@ class ANIM_OT_keying_set_export(Operator): if ksp.id in id_to_paths_cache: continue - """ - - idtype_list is used to get the list of id-datablocks from - bpy.data.* since this info isn't available elsewhere - - id.bl_rna.name gives a name suitable for UI, - with a capitalised first letter, but we need - the plural form that's all lower case - - special handling is needed for "nested" ID-blocks - (e.g. nodetree in Material) - """ + # - idtype_list is used to get the list of id-datablocks from + # bpy.data.* since this info isn't available elsewhere + # - id.bl_rna.name gives a name suitable for UI, + # with a capitalised first letter, but we need + # the plural form that's all lower case + # - special handling is needed for "nested" ID-blocks + # (e.g. nodetree in Material) if ksp.id.bl_rna.identifier.startswith("ShaderNodeTree"): # Find material or lamp using this node tree... id_bpy_path = "bpy.data.nodes[\"%s\"]" diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py index acec2d8fe91..18f91110053 100644 --- a/release/scripts/startup/bl_operators/view3d.py +++ b/release/scripts/startup/bl_operators/view3d.py @@ -24,7 +24,7 @@ from bpy.props import BoolProperty class VIEW3D_OT_edit_mesh_extrude_individual_move(Operator): - "Extrude individual elements and move" + """Extrude individual elements and move""" bl_label = "Extrude Individual and Move" bl_idname = "view3d.edit_mesh_extrude_individual_move" @@ -62,7 +62,7 @@ class VIEW3D_OT_edit_mesh_extrude_individual_move(Operator): class VIEW3D_OT_edit_mesh_extrude_move(Operator): - "Extrude and move along normals" + """Extrude and move along normals""" bl_label = "Extrude and Move on Normals" bl_idname = "view3d.edit_mesh_extrude_move_normal" @@ -111,7 +111,7 @@ class VIEW3D_OT_edit_mesh_extrude_move(Operator): class VIEW3D_OT_edit_mesh_extrude_shrink_fatten(Operator): - "Extrude and move along individual normals" + """Extrude and move along individual normals""" bl_label = "Extrude and Move on Individual Normals" bl_idname = "view3d.edit_mesh_extrude_move_shrink_fatten" @@ -128,7 +128,7 @@ class VIEW3D_OT_edit_mesh_extrude_shrink_fatten(Operator): class VIEW3D_OT_select_or_deselect_all(Operator): - "Select element under the mouse, deselect everything is there's nothing under the mouse" + """Select element under the mouse, deselect everything is there's nothing under the mouse""" bl_label = "Select or Deselect All" bl_idname = "view3d.select_or_deselect_all" bl_options = {'UNDO'} @@ -220,4 +220,4 @@ classes = ( VIEW3D_OT_edit_mesh_extrude_move, VIEW3D_OT_edit_mesh_extrude_shrink_fatten, VIEW3D_OT_select_or_deselect_all, -)
\ No newline at end of file +) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 86c58fffd73..20586b727d5 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -845,7 +845,7 @@ class WM_OT_context_modal_mouse(Operator): class WM_OT_url_open(Operator): - "Open a website in the web-browser" + """Open a website in the web-browser""" bl_idname = "wm.url_open" bl_label = "" bl_options = {'INTERNAL'} @@ -862,7 +862,7 @@ class WM_OT_url_open(Operator): class WM_OT_path_open(Operator): - "Open a path in a file browser" + """Open a path in a file browser""" bl_idname = "wm.path_open" bl_label = "" bl_options = {'INTERNAL'} @@ -1324,7 +1324,7 @@ class WM_OT_properties_add(Operator): class WM_OT_properties_context_change(Operator): - "Jump to a different tab inside the properties editor" + """Jump to a different tab inside the properties editor""" bl_idname = "wm.properties_context_change" bl_label = "" bl_options = {'INTERNAL'} @@ -1532,7 +1532,7 @@ class WM_OT_blenderplayer_start(Operator): class WM_OT_keyconfig_test(Operator): - "Test key-config for conflicts" + """Test key-config for conflicts""" bl_idname = "wm.keyconfig_test" bl_label = "Test Key Configuration for Conflicts" @@ -1549,7 +1549,7 @@ class WM_OT_keyconfig_test(Operator): class WM_OT_keyconfig_import(Operator): - "Import key configuration from a python script" + """Import key configuration from a python script""" bl_idname = "wm.keyconfig_import" bl_label = "Import Key Configuration..." @@ -1616,7 +1616,7 @@ class WM_OT_keyconfig_import(Operator): class WM_OT_keyconfig_export(Operator): - "Export key configuration to a python script" + """Export key configuration to a python script""" bl_idname = "wm.keyconfig_export" bl_label = "Export Key Configuration..." @@ -1666,7 +1666,7 @@ class WM_OT_keyconfig_export(Operator): class WM_OT_keymap_restore(Operator): - "Restore key map(s)" + """Restore key map(s)""" bl_idname = "wm.keymap_restore" bl_label = "Restore Key Map(s)" @@ -1689,7 +1689,7 @@ class WM_OT_keymap_restore(Operator): class WM_OT_keyitem_restore(Operator): - "Restore key map item" + """Restore key map item""" bl_idname = "wm.keyitem_restore" bl_label = "Restore Key Map Item" @@ -1714,7 +1714,7 @@ class WM_OT_keyitem_restore(Operator): class WM_OT_keyitem_add(Operator): - "Add key map item" + """Add key map item""" bl_idname = "wm.keyitem_add" bl_label = "Add Key Map Item" @@ -1736,7 +1736,7 @@ class WM_OT_keyitem_add(Operator): class WM_OT_keyitem_remove(Operator): - "Remove key map item" + """Remove key map item""" bl_idname = "wm.keyitem_remove" bl_label = "Remove Key Map Item" @@ -1757,7 +1757,7 @@ class WM_OT_keyitem_remove(Operator): class WM_OT_keyconfig_remove(Operator): - "Remove key config" + """Remove key config""" bl_idname = "wm.keyconfig_remove" bl_label = "Remove Key Config" @@ -1775,7 +1775,7 @@ class WM_OT_keyconfig_remove(Operator): class WM_OT_operator_cheat_sheet(Operator): - "List all the Operators in a text-block, useful for scripting" + """List all the Operators in a text-block, useful for scripting""" bl_idname = "wm.operator_cheat_sheet" bl_label = "Operator Cheat Sheet" @@ -1804,7 +1804,7 @@ class WM_OT_operator_cheat_sheet(Operator): # Add-on Operators class WM_OT_addon_enable(Operator): - "Enable an add-on" + """Enable an add-on""" bl_idname = "wm.addon_enable" bl_label = "Enable Add-on" @@ -1848,7 +1848,7 @@ class WM_OT_addon_enable(Operator): class WM_OT_addon_disable(Operator): - "Disable an add-on" + """Disable an add-on""" bl_idname = "wm.addon_disable" bl_label = "Disable Add-on" @@ -1877,7 +1877,7 @@ class WM_OT_addon_disable(Operator): class WM_OT_theme_install(Operator): - "Load and apply a Blender XML theme file" + """Load and apply a Blender XML theme file""" bl_idname = "wm.theme_install" bl_label = "Install Theme..." @@ -1939,7 +1939,7 @@ class WM_OT_theme_install(Operator): class WM_OT_addon_refresh(Operator): - "Scan add-on directories for new modules" + """Scan add-on directories for new modules""" bl_idname = "wm.addon_refresh" bl_label = "Refresh" @@ -1954,7 +1954,7 @@ class WM_OT_addon_refresh(Operator): # Note: shares some logic with WM_OT_app_template_install # but not enough to de-duplicate. Fixed here may apply to both. class WM_OT_addon_install(Operator): - "Install an add-on" + """Install an add-on""" bl_idname = "wm.addon_install" bl_label = "Install Add-on from File..." @@ -2108,7 +2108,7 @@ class WM_OT_addon_install(Operator): class WM_OT_addon_remove(Operator): - "Delete the add-on from the file system" + """Delete the add-on from the file system""" bl_idname = "wm.addon_remove" bl_label = "Remove Add-on" @@ -2167,7 +2167,7 @@ class WM_OT_addon_remove(Operator): class WM_OT_addon_expand(Operator): - "Display information and preferences for this add-on" + """Display information and preferences for this add-on""" bl_idname = "wm.addon_expand" bl_label = "" bl_options = {'INTERNAL'} @@ -2191,7 +2191,7 @@ class WM_OT_addon_expand(Operator): class WM_OT_addon_userpref_show(Operator): - "Show add-on user preferences" + """Show add-on user preferences""" bl_idname = "wm.addon_userpref_show" bl_label = "" bl_options = {'INTERNAL'} @@ -2223,7 +2223,7 @@ class WM_OT_addon_userpref_show(Operator): # Note: shares some logic with WM_OT_addon_install # but not enough to de-duplicate. Fixes here may apply to both. class WM_OT_app_template_install(Operator): - "Install an application-template" + """Install an application-template""" bl_idname = "wm.app_template_install" bl_label = "Install Template from File..." diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 3f05620fcf6..43679727992 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -1238,7 +1238,6 @@ class CLIP_MT_view(Menu): layout.prop(sc, "show_seconds") layout.prop(sc, "show_locked_time") - layout.separator() layout.separator() layout.operator("screen.area_dupli") diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index c748e71a0a2..f070161f3da 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -617,7 +617,6 @@ class IMAGE_PT_game_properties(Panel): ima = sima.image split = layout.split() - col = split.column() col.prop(ima, "use_animation") sub = col.column(align=True) @@ -626,17 +625,21 @@ class IMAGE_PT_game_properties(Panel): sub.prop(ima, "frame_end", text="End") sub.prop(ima, "fps", text="Speed") + col = split.column() col.prop(ima, "use_tiles") sub = col.column(align=True) sub.active = ima.use_tiles or ima.use_animation sub.prop(ima, "tiles_x", text="X") sub.prop(ima, "tiles_y", text="Y") + split = layout.split() col = split.column() col.label(text="Clamp:") col.prop(ima, "use_clamp_x", text="X") col.prop(ima, "use_clamp_y", text="Y") - col.separator() + + col = split.column() + col.label(text="Mapping:") col.prop(ima, "mapping", expand=True) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index f13c7095f67..9245def7473 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1699,7 +1699,7 @@ class VIEW3D_MT_brush(Menu): layout = self.layout settings = UnifiedPaintPanel.paint_settings(context) - brush = settings.brush + brush = getattr(settings, "brush", None) ups = context.tool_settings.unified_paint_settings layout.prop(ups, "use_unified_size", text="Unified Size") @@ -1708,6 +1708,11 @@ class VIEW3D_MT_brush(Menu): layout.prop(ups, "use_unified_color", text="Unified Color") layout.separator() + # skip if no active brush + if not brush: + layout.label(text="No Brushes currently available", icon="INFO") + return + # brush paint modes layout.menu("VIEW3D_MT_brush_paint_modes") @@ -1720,10 +1725,6 @@ class VIEW3D_MT_brush(Menu): elif context.vertex_paint_object or context.weight_paint_object: layout.prop_menu_enum(brush, "vertex_tool") - # skip if no active brush - if not brush: - return - # TODO: still missing a lot of brush options here # sculpt options diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index afc6e5455c4..bee6ae80590 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -157,6 +157,7 @@ shader_node_categories = [ NodeItem("ShaderNodeGeometry"), NodeItem("ShaderNodeExtendedMaterial"), NodeItem("ShaderNodeParticleInfo"), + NodeItem("ShaderNodeObjectInfo"), NodeItem("NodeGroupInput", poll=group_input_output_item_poll), ]), ShaderOldNodeCategory("SH_OUTPUT", "Output", items=[ @@ -225,6 +226,7 @@ shader_node_categories = [ NodeItem("ShaderNodeMixShader"), NodeItem("ShaderNodeAddShader"), NodeItem("ShaderNodeBsdfDiffuse", poll=object_shader_nodes_poll), + NodeItem("ShaderNodeBsdfPrincipled", poll=object_shader_nodes_poll), NodeItem("ShaderNodeBsdfGlossy", poll=object_shader_nodes_poll), NodeItem("ShaderNodeBsdfTransparent", poll=object_shader_nodes_poll), NodeItem("ShaderNodeBsdfRefraction", poll=object_shader_nodes_poll), diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h index e92d5f2d9f7..6b698e5ec66 100644 --- a/source/blender/alembic/ABC_alembic.h +++ b/source/blender/alembic/ABC_alembic.h @@ -53,41 +53,57 @@ struct AlembicExportParams { double shutter_open; double shutter_close; - /* bools */ - unsigned int selected_only : 1; - unsigned int uvs : 1; - unsigned int normals : 1; - unsigned int vcolors : 1; - unsigned int apply_subdiv : 1; - unsigned int flatten_hierarchy : 1; - unsigned int visible_layers_only : 1; - unsigned int renderable_only : 1; - unsigned int face_sets : 1; - unsigned int use_subdiv_schema : 1; - unsigned int packuv : 1; - unsigned int triangulate : 1; + bool selected_only; + bool uvs; + bool normals; + bool vcolors; + bool apply_subdiv; + bool flatten_hierarchy; + bool visible_layers_only; + bool renderable_only; + bool face_sets; + bool use_subdiv_schema; + bool packuv; + bool triangulate; + bool export_hair; + bool export_particles; unsigned int compression_type : 1; + /* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx + * in DNA_modifier_types.h */ int quad_method; int ngon_method; + float global_scale; }; -void ABC_export( +/* The ABC_export and ABC_import functions both take a as_background_job + * parameter, and return a boolean. + * + * When as_background_job=true, returns false immediately after scheduling + * a background job. + * + * When as_background_job=false, performs the export synchronously, and returns + * true when the export was ok, and false if there were any errors. + */ + +bool ABC_export( struct Scene *scene, struct bContext *C, const char *filepath, - const struct AlembicExportParams *params); + const struct AlembicExportParams *params, + bool as_background_job); -void ABC_import(struct bContext *C, +bool ABC_import(struct bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, - bool validate_meshes); + bool validate_meshes, + bool as_background_job); AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths); diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc index 0542255d84b..bf485c7ef61 100644 --- a/source/blender/alembic/intern/abc_curves.cc +++ b/source/blender/alembic/intern/abc_curves.cc @@ -49,19 +49,26 @@ using Alembic::Abc::Int32ArraySamplePtr; using Alembic::Abc::FloatArraySamplePtr; using Alembic::Abc::P3fArraySamplePtr; using Alembic::Abc::UcharArraySamplePtr; +using Alembic::Abc::PropertyHeader; +using Alembic::AbcGeom::ICompoundProperty; using Alembic::AbcGeom::ICurves; using Alembic::AbcGeom::ICurvesSchema; using Alembic::AbcGeom::IFloatGeomParam; +using Alembic::AbcGeom::IInt16Property; using Alembic::AbcGeom::ISampleSelector; using Alembic::AbcGeom::kWrapExisting; using Alembic::AbcGeom::CurvePeriodicity; +using Alembic::AbcGeom::OCompoundProperty; using Alembic::AbcGeom::OCurves; using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::OInt16Property; using Alembic::AbcGeom::ON3fGeomParam; using Alembic::AbcGeom::OV2fGeomParam; +#define ABC_CURVE_RESOLUTION_U_PROPNAME "blender:resolution" + /* ************************************************************************** */ AbcCurveWriter::AbcCurveWriter(Scene *scene, @@ -73,6 +80,11 @@ AbcCurveWriter::AbcCurveWriter(Scene *scene, { OCurves curves(parent->alembicXform(), m_name, m_time_sampling); m_schema = curves.getSchema(); + + Curve *cu = static_cast<Curve *>(m_object->data); + OCompoundProperty user_props = m_schema.getUserProperties(); + OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME); + user_prop_resolu.set(cu->resolu); } void AbcCurveWriter::do_write() @@ -95,7 +107,7 @@ void AbcCurveWriter::do_write() for (; nurbs; nurbs = nurbs->next) { if (nurbs->bp) { curve_basis = Alembic::AbcGeom::kNoBasis; - curve_type = Alembic::AbcGeom::kLinear; + curve_type = Alembic::AbcGeom::kVariableOrder; const int totpoint = nurbs->pntsu * nurbs->pntsv; @@ -160,7 +172,7 @@ void AbcCurveWriter::do_write() } } - orders.push_back(nurbs->orderu + 1); + orders.push_back(nurbs->orderu); vert_counts.push_back(verts.size()); } @@ -205,11 +217,23 @@ void AbcCurveReader::readObjectData(Main *bmain, float time) cu->flag |= CU_DEFORM_FILL | CU_3D; cu->actvert = CU_ACT_NONE; + cu->resolu = 1; + + const ISampleSelector sample_sel(time); + + ICompoundProperty user_props = m_curves_schema.getUserProperties(); + if (user_props) { + const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME); + if (header != NULL && header->isScalar() && IInt16Property::matches(*header)) { + IInt16Property resolu(user_props, header->getName()); + cu->resolu = resolu.getValue(sample_sel); + } + } m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str()); m_object->data = cu; - read_curve_sample(cu, m_curves_schema, time); + read_curve_sample(cu, m_curves_schema, sample_sel); if (has_animations(m_curves_schema, m_settings)) { addCacheModifier(); @@ -218,9 +242,8 @@ void AbcCurveReader::readObjectData(Main *bmain, float time) /* ************************************************************************** */ -void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time) +void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const ISampleSelector &sample_sel) { - const ISampleSelector sample_sel(time); ICurvesSchema::Sample smp = schema.getValue(sample_sel); const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices(); const P3fArraySamplePtr positions = smp.getPositions(); @@ -250,13 +273,18 @@ void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time) nu->pntsv = 1; nu->flag |= CU_SMOOTH; - nu->orderu = num_verts; - - if (smp.getType() == Alembic::AbcGeom::kCubic) { - nu->orderu = 3; - } - else if (orders && orders->size() > i) { - nu->orderu = static_cast<short>((*orders)[i] - 1); + switch (smp.getType()) { + case Alembic::AbcGeom::kCubic: + nu->orderu = 4; + break; + case Alembic::AbcGeom::kVariableOrder: + if (orders && orders->size() > i) { + nu->orderu = static_cast<short>((*orders)[i]); + break; + } + case Alembic::AbcGeom::kLinear: + default: + nu->orderu = 2; } if (periodicity == Alembic::AbcGeom::kNonPeriodic) { @@ -377,7 +405,7 @@ DerivedMesh *AbcCurveReader::read_derivedmesh(DerivedMesh * /*dm*/, const float if (curve_count != num_vertices->size()) { BKE_nurbList_free(&curve->nurb); - read_curve_sample(curve, m_curves_schema, time); + read_curve_sample(curve, m_curves_schema, sample_sel); } else { Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); diff --git a/source/blender/alembic/intern/abc_curves.h b/source/blender/alembic/intern/abc_curves.h index 2757d179a47..71b0d205820 100644 --- a/source/blender/alembic/intern/abc_curves.h +++ b/source/blender/alembic/intern/abc_curves.h @@ -61,6 +61,8 @@ public: /* ************************************************************************** */ -void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, const float time); +void read_curve_sample(Curve *cu, + const Alembic::AbcGeom::ICurvesSchema &schema, + const Alembic::Abc::ISampleSelector &sample_selector); #endif /* __ABC_CURVES_H__ */ diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc index a8d1587abdb..d70b9625f1d 100644 --- a/source/blender/alembic/intern/abc_exporter.cc +++ b/source/blender/alembic/intern/abc_exporter.cc @@ -83,6 +83,8 @@ ExportSettings::ExportSettings() , export_vcols(false) , export_face_sets(false) , export_vweigths(false) + , export_hair(true) + , export_particles(true) , apply_subdiv(false) , use_subdiv_schema(false) , export_child_hairs(true) @@ -290,13 +292,7 @@ void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled) OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_writer->archive(), m_trans_sampling_index); - if (m_settings.flatten_hierarchy) { - createTransformWritersFlat(); - } - else { - createTransformWritersHierarchy(bmain->eval_ctx); - } - + createTransformWritersHierarchy(bmain->eval_ctx); createShapeWriters(bmain->eval_ctx); /* Make a list of frames to export. */ @@ -383,24 +379,6 @@ void AbcExporter::createTransformWritersHierarchy(EvaluationContext *eval_ctx) } } -void AbcExporter::createTransformWritersFlat() -{ - Base *base = static_cast<Base *>(m_scene->base.first); - - while (base) { - Object *ob = base->object; - - if (export_object(&m_settings, ob, false) && object_is_shape(ob)) { - std::string name = get_id_name(ob); - m_xforms[name] = new AbcTransformWriter( - ob, m_writer->archive().getTop(), NULL, - m_trans_sampling_index, m_settings); - } - - base = base->next; - } -} - void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent) { /* If an object isn't exported itself, its duplilist shouldn't be @@ -440,12 +418,18 @@ void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Obje AbcTransformWriter * AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent) { - const std::string name = get_object_dag_path_name(ob, dupliObParent); - /* An object should not be its own parent, or we'll get infinite loops. */ BLI_assert(ob != parent); BLI_assert(ob != dupliObParent); + std::string name; + if (m_settings.flatten_hierarchy) { + name = get_id_name(ob); + } + else { + name = get_object_dag_path_name(ob, dupliObParent); + } + /* check if we have already created a transform writer for this object */ AbcTransformWriter *my_writer = getXForm(name); if (my_writer != NULL){ @@ -455,13 +439,23 @@ AbcTransformWriter * AbcExporter::createTransformWriter(Object *ob, Object *pare AbcTransformWriter *parent_writer = NULL; Alembic::Abc::OObject alembic_parent; - if (parent) { + if (m_settings.flatten_hierarchy || parent == NULL) { + /* Parentless objects still have the "top object" as parent + * in Alembic. */ + alembic_parent = m_writer->archive().getTop(); + } + else { /* Since there are so many different ways to find parents (as evident * in the number of conditions below), we can't really look up the * parent by name. We'll just call createTransformWriter(), which will * return the parent's AbcTransformWriter pointer. */ if (parent->parent) { - parent_writer = createTransformWriter(parent, parent->parent, dupliObParent); + if (parent == dupliObParent) { + parent_writer = createTransformWriter(parent, parent->parent, NULL); + } + else { + parent_writer = createTransformWriter(parent, parent->parent, dupliObParent); + } } else if (parent == dupliObParent) { if (dupliObParent->parent == NULL) { @@ -478,14 +472,15 @@ AbcTransformWriter * AbcExporter::createTransformWriter(Object *ob, Object *pare BLI_assert(parent_writer); alembic_parent = parent_writer->alembicXform(); } - else { - /* Parentless objects still have the "top object" as parent - * in Alembic. */ - alembic_parent = m_writer->archive().getTop(); - } my_writer = new AbcTransformWriter(ob, alembic_parent, parent_writer, m_trans_sampling_index, m_settings); + + /* When flattening, the matrix of the dupliobject has to be added. */ + if (m_settings.flatten_hierarchy && dupliObParent) { + my_writer->m_proxy_from = dupliObParent; + } + m_xforms[name] = my_writer; return my_writer; } @@ -532,6 +527,29 @@ void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Object *ob, Object free_object_duplilist(lb); } +void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform) +{ + if (!m_settings.export_hair && !m_settings.export_particles) { + return; + } + + ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first); + + for (; psys; psys = psys->next) { + if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { + continue; + } + + if (m_settings.export_hair && psys->part->type == PART_HAIR) { + m_settings.export_child_hairs = true; + m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys)); + } + else if (m_settings.export_particles && psys->part->type == PART_EMITTER) { + m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys)); + } + } +} + void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) { if (!object_is_shape(ob)) { @@ -554,21 +572,7 @@ void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) return; } - ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first); - - for (; psys; psys = psys->next) { - if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { - continue; - } - - if (psys->part->type == PART_HAIR) { - m_settings.export_child_hairs = true; - m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys)); - } - else if (psys->part->type == PART_EMITTER) { - m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys)); - } - } + createParticleSystemsWriters(ob, xform); switch (ob->type) { case OB_MESH: diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h index 0f08037fd17..797a2560d65 100644 --- a/source/blender/alembic/intern/abc_exporter.h +++ b/source/blender/alembic/intern/abc_exporter.h @@ -63,6 +63,8 @@ struct ExportSettings { bool export_vcols; bool export_face_sets; bool export_vweigths; + bool export_hair; + bool export_particles; bool apply_subdiv; bool use_subdiv_schema; @@ -109,12 +111,12 @@ private: void getFrameSet(double step, std::set<double> &frames); void createTransformWritersHierarchy(EvaluationContext *eval_ctx); - void createTransformWritersFlat(); AbcTransformWriter * createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); void exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent = NULL); void exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent); void createShapeWriters(EvaluationContext *eval_ctx); void createShapeWriter(Object *ob, Object *dupliObParent); + void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform); AbcTransformWriter *getXForm(const std::string &name); diff --git a/source/blender/alembic/intern/abc_transform.cc b/source/blender/alembic/intern/abc_transform.cc index 2e626edeb8b..6e218cac429 100644 --- a/source/blender/alembic/intern/abc_transform.cc +++ b/source/blender/alembic/intern/abc_transform.cc @@ -62,6 +62,7 @@ AbcTransformWriter::AbcTransformWriter(Object *ob, unsigned int time_sampling, ExportSettings &settings) : AbcObjectWriter(NULL, ob, time_sampling, settings, parent) + , m_proxy_from(NULL) { m_is_animated = hasAnimation(m_object); @@ -90,7 +91,8 @@ void AbcTransformWriter::do_write() float yup_mat[4][4]; create_transform_matrix(m_object, yup_mat, - m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD); + m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, + m_proxy_from); /* Only apply rotation to root camera, parenting will propagate it. */ if (m_object->type == OB_CAMERA && (!m_inherits_xform || !has_parent_camera(m_object))) { diff --git a/source/blender/alembic/intern/abc_transform.h b/source/blender/alembic/intern/abc_transform.h index 714adc299c1..59388e155dc 100644 --- a/source/blender/alembic/intern/abc_transform.h +++ b/source/blender/alembic/intern/abc_transform.h @@ -41,6 +41,9 @@ class AbcTransformWriter : public AbcObjectWriter { bool m_inherits_xform; public: + Object *m_proxy_from; + +public: AbcTransformWriter(Object *ob, const Alembic::AbcGeom::OObject &abc_parent, AbcTransformWriter *parent, diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc index 248ca4e64ae..67d2d3b1eb2 100644 --- a/source/blender/alembic/intern/abc_util.cc +++ b/source/blender/alembic/intern/abc_util.cc @@ -257,7 +257,8 @@ void convert_matrix(const Imath::M44d &xform, Object *ob, float r_mat[4][4]) /* Recompute transform matrix of object in new coordinate system * (from Z-Up to Y-Up). */ -void create_transform_matrix(Object *obj, float r_yup_mat[4][4], AbcMatrixMode mode) +void create_transform_matrix(Object *obj, float r_yup_mat[4][4], AbcMatrixMode mode, + Object *proxy_from) { float zup_mat[4][4]; @@ -267,11 +268,16 @@ void create_transform_matrix(Object *obj, float r_yup_mat[4][4], AbcMatrixMode m * constraints and modifiers as well as the obj->parentinv matrix. */ invert_m4_m4(obj->parent->imat, obj->parent->obmat); mul_m4_m4m4(zup_mat, obj->parent->imat, obj->obmat); - copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP); } else { - copy_m44_axis_swap(r_yup_mat, obj->obmat, ABC_YUP_FROM_ZUP); + copy_m4_m4(zup_mat, obj->obmat); } + + if (proxy_from) { + mul_m4_m4m4(zup_mat, proxy_from->obmat, zup_mat); + } + + copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP); } bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name) diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h index 7bde9ac6e15..74d0faf97d3 100644 --- a/source/blender/alembic/intern/abc_util.h +++ b/source/blender/alembic/intern/abc_util.h @@ -62,7 +62,8 @@ typedef enum { ABC_MATRIX_WORLD = 1, ABC_MATRIX_LOCAL = 2, } AbcMatrixMode; -void create_transform_matrix(Object *obj, float r_transform_mat[4][4], AbcMatrixMode mode); +void create_transform_matrix(Object *obj, float r_transform_mat[4][4], + AbcMatrixMode mode, Object *proxy_from); void split(const std::string &s, const char delim, std::vector<std::string> &tokens); diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc index 0de0d1a1ab6..104b19beaf0 100644 --- a/source/blender/alembic/intern/alembic_capi.cc +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -238,6 +238,7 @@ struct ExportJobData { float *progress; bool was_canceled; + bool export_ok; }; static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) @@ -271,6 +272,8 @@ static void export_startjob(void *customdata, short *stop, short *do_update, flo BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain, scene, scene->lay); } + + data->export_ok = !data->was_canceled; } catch (const std::exception &e) { ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; @@ -297,15 +300,17 @@ static void export_endjob(void *customdata) BKE_spacedata_draw_locks(false); } -void ABC_export( +bool ABC_export( Scene *scene, bContext *C, const char *filepath, - const struct AlembicExportParams *params) + const struct AlembicExportParams *params, + bool as_background_job) { ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData")); job->scene = scene; job->bmain = CTX_data_main(C); + job->export_ok = false; BLI_strncpy(job->filename, filepath, 1024); /* Alright, alright, alright.... @@ -336,6 +341,8 @@ void ABC_export( job->settings.export_normals = params->normals; job->settings.export_uvs = params->uvs; job->settings.export_vcols = params->vcolors; + job->settings.export_hair = params->export_hair; + job->settings.export_particles = params->export_particles; job->settings.apply_subdiv = params->apply_subdiv; job->settings.flatten_hierarchy = params->flatten_hierarchy; job->settings.visible_layers_only = params->visible_layers_only; @@ -352,19 +359,31 @@ void ABC_export( std::swap(job->settings.frame_start, job->settings.frame_end); } - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - job->scene, - "Alembic Export", - WM_JOB_PROGRESS, - WM_JOB_TYPE_ALEMBIC); + if (as_background_job) { + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "Alembic Export", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, MEM_freeN); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ + short stop = 0, do_update = 0; + float progress = 0.f; - /* setup job */ - WM_jobs_customdata_set(wm_job, job, MEM_freeN); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); + export_startjob(job, &stop, &do_update, &progress); + export_endjob(job); + } - WM_jobs_start(CTX_wm_manager(C), wm_job); + return job->export_ok; } /* ********************** Import file ********************** */ @@ -593,6 +612,7 @@ struct ImportJobData { char error_code; bool was_cancelled; + bool import_ok; }; ABC_INLINE bool is_mesh_and_strands(const IObject &object) @@ -801,6 +821,7 @@ static void import_endjob(void *user_data) switch (data->error_code) { default: case ABC_NO_ERROR: + data->import_ok = !data->was_cancelled; break; case ABC_ARCHIVE_FAIL: WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail."); @@ -816,13 +837,16 @@ static void import_freejob(void *user_data) delete data; } -void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes) +bool ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, + bool set_frame_range, int sequence_len, int offset, + bool validate_meshes, bool as_background_job) { /* Using new here since MEM_* funcs do not call ctor to properly initialize * data. */ ImportJobData *job = new ImportJobData(); job->bmain = CTX_data_main(C); job->scene = CTX_data_scene(C); + job->import_ok = false; BLI_strncpy(job->filename, filepath, 1024); job->settings.scale = scale; @@ -836,19 +860,31 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence G.is_break = false; - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - job->scene, - "Alembic Import", - WM_JOB_PROGRESS, - WM_JOB_TYPE_ALEMBIC); + if (as_background_job) { + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "Alembic Import", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); - /* setup job */ - WM_jobs_customdata_set(wm_job, job, import_freejob); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob); + /* setup job */ + WM_jobs_customdata_set(wm_job, job, import_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while importing. */ + short stop = 0, do_update = 0; + float progress = 0.f; + + import_startjob(job, &stop, &do_update, &progress); + import_endjob(job); + } - WM_jobs_start(CTX_wm_manager(C), wm_job); + return job->import_ok; } /* ************************************************************************** */ diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index aa7d539538b..bbbabfb8ba2 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -135,6 +135,7 @@ void blf_glyph_cache_clear(FontBLF *font) while ((gc = BLI_pophead(&font->cache))) { blf_glyph_cache_free(gc); } + font->glyph_cache = NULL; } void blf_glyph_cache_free(GlyphCacheBLF *gc) diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 78d6f6c7cb9..23bb526073b 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -177,6 +177,7 @@ void BKE_pose_eval_bone(struct EvaluationContext *eval_ctx, struct bPoseChannel *pchan); void BKE_pose_constraints_evaluate(struct EvaluationContext *eval_ctx, + struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 193f1154c54..7544e8f5b3e 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -785,6 +785,7 @@ struct ShadeResult; #define SH_NODE_OUTPUT_LINESTYLE 190 #define SH_NODE_UVALONGSTROKE 191 #define SH_NODE_TEX_POINTDENSITY 192 +#define SH_NODE_BSDF_PRINCIPLED 193 /* custom defines options for Material node */ #define SH_NODE_MAT_DIFF 1 diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 3bc81a69c86..df0bddede3b 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -628,10 +628,10 @@ void BKE_pose_eval_bone(EvaluationContext *UNUSED(eval_ctx), } void BKE_pose_constraints_evaluate(EvaluationContext *UNUSED(eval_ctx), + Scene *scene, Object *ob, bPoseChannel *pchan) { - Scene *scene = G.main->scene.first; DEBUG_PRINT("%s on %s pchan %s\n", __func__, ob->id.name, pchan->name); bArmature *arm = (bArmature *)ob->data; if (arm->flag & ARM_RESTPOS) { diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 318f6480aaf..8a01e120d6d 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -2216,8 +2216,10 @@ void BKE_imbuf_write_prepare(ImBuf *ibuf, const ImageFormatData *imf) ibuf->foptions.flag |= OPENEXR_HALF; ibuf->foptions.flag |= (imf->exr_codec & OPENEXR_COMPRESS); - if (!(imf->flag & R_IMF_FLAG_ZBUF)) - ibuf->zbuf_float = NULL; /* signal for exr saving */ + if (!(imf->flag & R_IMF_FLAG_ZBUF)) { + /* Signal for exr saving. */ + IMB_freezbuffloatImBuf(ibuf); + } } #endif diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index f3223e31b17..63da1b5f9e7 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3568,6 +3568,7 @@ static void registerShaderNodes(void) register_node_type_sh_background(); register_node_type_sh_bsdf_anisotropic(); register_node_type_sh_bsdf_diffuse(); + register_node_type_sh_bsdf_principled(); register_node_type_sh_bsdf_glossy(); register_node_type_sh_bsdf_glass(); register_node_type_sh_bsdf_translucent(); diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index e3b801b3193..eb7abc2f004 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -64,6 +64,7 @@ #include "BLI_strict_flags.h" +#include "BLI_hash.h" /* Dupli-Geometry */ @@ -180,6 +181,22 @@ static DupliObject *make_dupli(const DupliContext *ctx, if (ob->type == OB_MBALL) dob->no_draw = true; + /* random number */ + /* the logic here is designed to match Cycles */ + dob->random_id = BLI_hash_string(dob->ob->id.name + 2); + + if (dob->persistent_id[0] != INT_MAX) { + for(i = 0; i < MAX_DUPLI_RECUR*2; i++) + dob->random_id = BLI_hash_int_2d(dob->random_id, (unsigned int)dob->persistent_id[i]); + } + else { + dob->random_id = BLI_hash_int_2d(dob->random_id, 0); + } + + if (ctx->object != ob) { + dob->random_id ^= BLI_hash_int(BLI_hash_string(ctx->object->id.name + 2)); + } + return dob; } diff --git a/source/blender/blenlib/BLI_hash.h b/source/blender/blenlib/BLI_hash.h new file mode 100644 index 00000000000..50c7a7f9f0f --- /dev/null +++ b/source/blender/blenlib/BLI_hash.h @@ -0,0 +1,66 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BLI_HASH_H__ +#define __BLI_HASH_H__ + +/** \file BLI_hash.h + * \ingroup bli + */ + +BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky) +{ +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + + unsigned int a, b, c; + + a = b = c = 0xdeadbeef + (2 << 2) + 13; + a += kx; + b += ky; + + c ^= b; c -= rot(b,14); + a ^= c; a -= rot(c,11); + b ^= a; b -= rot(a,25); + c ^= b; c -= rot(b,16); + a ^= c; a -= rot(c,4); + b ^= a; b -= rot(a,14); + c ^= b; c -= rot(b,24); + + return c; + +#undef rot +} + +BLI_INLINE unsigned int BLI_hash_string(const char *str) +{ + unsigned int i = 0, c; + + while((c = *str++)) + i = i * 37 + c; + + return i; +} + +BLI_INLINE unsigned int BLI_hash_int(unsigned int k) +{ + return BLI_hash_int_2d(k, 0); +} + +#endif // __BLI_HASH_H__ diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index b6a55d34d14..b59e7f99d59 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -44,7 +44,7 @@ void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1); void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file); void BLI_make_exist(char *dir); -void BLI_make_existing_file(const char *name); +bool BLI_make_existing_file(const char *name); void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen); void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen); void BLI_split_file_part(const char *string, char *file, const size_t filelen); diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 3277519c66e..fcb02a86a84 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -152,6 +152,7 @@ set(SRC BLI_ghash.h BLI_graph.h BLI_gsqueue.h + BLI_hash.h BLI_hash_md5.h BLI_hash_mm2a.h BLI_heap.h diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 7b765cfa939..40b6aaa18c1 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1234,14 +1234,16 @@ void BLI_make_exist(char *dir) /** * Ensures that the parent directory of *name exists. + * + * \return true on success (i.e. given path now exists on FS), false otherwise. */ -void BLI_make_existing_file(const char *name) +bool BLI_make_existing_file(const char *name) { char di[FILE_MAX]; BLI_split_dir_part(name, di, sizeof(di)); /* make if the dir doesn't exist */ - BLI_dir_create_recursive(di); + return BLI_dir_create_recursive(di); } /** diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 10b586342dd..745f8283328 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -130,7 +130,7 @@ struct DepsgraphNodeBuilder { void build_object(Scene *scene, Base *base, Object *ob); void build_object_transform(Scene *scene, Object *ob); void build_object_constraints(Scene *scene, Object *ob); - void build_pose_constraints(Object *ob, bPoseChannel *pchan); + void build_pose_constraints(Scene *scene, Object *ob, bPoseChannel *pchan); void build_rigidbody(Scene *scene); void build_particles(Scene *scene, Object *ob); void build_cloth(Scene *scene, Object *object); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc index f2437ce1fac..e307909cb11 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc @@ -64,11 +64,12 @@ extern "C" { namespace DEG { -void DepsgraphNodeBuilder::build_pose_constraints(Object *ob, bPoseChannel *pchan) +void DepsgraphNodeBuilder::build_pose_constraints(Scene *scene, Object *ob, bPoseChannel *pchan) { /* create node for constraint stack */ add_operation_node(&ob->id, DEPSNODE_TYPE_BONE, pchan->name, - DEPSOP_TYPE_EXEC, function_bind(BKE_pose_constraints_evaluate, _1, ob, pchan), + DEPSOP_TYPE_EXEC, + function_bind(BKE_pose_constraints_evaluate, _1, scene, ob, pchan), DEG_OPCODE_BONE_CONSTRAINTS); } @@ -201,7 +202,7 @@ void DepsgraphNodeBuilder::build_rig(Scene *scene, Object *ob) /* constraints */ if (pchan->constraints.first != NULL) { - build_pose_constraints(ob, pchan); + build_pose_constraints(scene, ob, pchan); } /** diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 47f42ab5321..648fe930030 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -6047,6 +6047,9 @@ int join_curve_exec(bContext *C, wmOperator *op) cu = ob->data; BLI_movelisttolist(&cu->nurb, &tempbase); + /* Account for mixed 2D/3D curves when joining */ + BKE_curve_curve_dimension_update(cu); + DAG_relations_tag_update(bmain); // because we removed object(s), call before editmode! DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 5879306b06c..d6e40377e63 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -484,7 +484,8 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, bGPdata *gpd = p->gpd; bGPDbrush *brush = p->brush; tGPspoint *pt; - + ToolSettings *ts = p->scene->toolsettings; + /* check painting mode */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { /* straight lines only - i.e. only store start and end point in buffer */ @@ -636,7 +637,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, View3D *v3d = p->sa->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->ar); - ED_view3d_autodist_init(p->scene, p->ar, v3d, (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 1 : 0); + ED_view3d_autodist_init(p->scene, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); } /* convert screen-coordinates to appropriate coordinates (and store them) */ @@ -756,7 +757,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) int i, totelem; /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ - int depth_margin = (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 4 : 0; + int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; /* get total number of points to allocate space for * - drawing straight-lines only requires the endpoints @@ -910,7 +911,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) depth_arr[i] = 0.9999f; } else { - if (p->gpd->flag & GP_DATA_DEPTH_STROKE_ENDPOINTS) { + if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) { /* remove all info between the valid endpoints */ int first_valid = 0; int last_valid = 0; @@ -1794,6 +1795,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ static void gp_paint_strokeend(tGPsdata *p) { + ToolSettings *ts = p->scene->toolsettings; /* for surface sketching, need to set the right OpenGL context stuff so that * the conversions will project the values correctly... */ @@ -1802,7 +1804,7 @@ static void gp_paint_strokeend(tGPsdata *p) /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(p->win, p->ar); - ED_view3d_autodist_init(p->scene, p->ar, v3d, (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 1 : 0); + ED_view3d_autodist_init(p->scene, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); } /* check if doing eraser or not */ diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 3f9eb33e239..62c36552048 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -122,6 +122,8 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op) .renderable_only = RNA_boolean_get(op->ptr, "renderable_only"), .face_sets = RNA_boolean_get(op->ptr, "face_sets"), .use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"), + .export_hair = RNA_boolean_get(op->ptr, "export_hair"), + .export_particles = RNA_boolean_get(op->ptr, "export_particles"), .compression_type = RNA_enum_get(op->ptr, "compression_type"), .packuv = RNA_boolean_get(op->ptr, "packuv"), .triangulate = RNA_boolean_get(op->ptr, "triangulate"), @@ -131,15 +133,17 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op) .global_scale = RNA_float_get(op->ptr, "global_scale"), }; - ABC_export(CTX_data_scene(C), C, filename, ¶ms); + const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job"); + bool ok = ABC_export(CTX_data_scene(C), C, filename, ¶ms, as_background_job); - return OPERATOR_FINISHED; + return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) { uiLayout *box; uiLayout *row; + uiLayout *col; #ifdef WITH_ALEMBIC_HDF5 box = uiLayoutBox(layout); @@ -231,6 +235,15 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) row = uiLayoutRow(box, false); uiLayoutSetEnabled(row, triangulate); uiItemR(row, imfptr, "ngon_method", 0, NULL, ICON_NONE); + + /* Object Data */ + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Particle Systems:"), ICON_PARTICLE_DATA); + + col = uiLayoutColumn(box, true); + uiItemR(col, imfptr, "export_hair", 0, NULL, ICON_NONE); + uiItemR(col, imfptr, "export_particles", 0, NULL, ICON_NONE); } static void wm_alembic_export_draw(bContext *C, wmOperator *op) @@ -348,6 +361,12 @@ void WM_OT_alembic_export(wmOperatorType *ot) RNA_def_enum(ot->srna, "ngon_method", rna_enum_modifier_triangulate_quad_method_items, MOD_TRIANGULATE_NGON_BEAUTY, "Polygon Method", "Method for splitting the polygons into triangles"); + RNA_def_boolean(ot->srna, "export_hair", 1, "Export Hair", "Exports hair particle systems as animated curves"); + RNA_def_boolean(ot->srna, "export_particles", 1, "Export Particles", "Exports non-hair particle systems"); + + RNA_def_boolean(ot->srna, "as_background_job", true, "Run as Background Job", + "Enable this to run the import in the background, disable to block Blender while importing"); + /* This dummy prop is used to check whether we need to init the start and * end frame values to that of the scene's, otherwise they are reset at * every change, draw update. */ @@ -482,6 +501,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op) const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence"); const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range"); const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); + const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job"); int offset = 0; int sequence_len = 1; @@ -490,9 +510,11 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op) sequence_len = get_sequence_len(filename, &offset); } - ABC_import(C, filename, scale, is_sequence, set_frame_range, sequence_len, offset, validate_meshes); + bool ok = ABC_import(C, filename, scale, is_sequence, set_frame_range, + sequence_len, offset, validate_meshes, + as_background_job); - return OPERATOR_FINISHED; + return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void WM_OT_alembic_import(wmOperatorType *ot) @@ -523,6 +545,9 @@ void WM_OT_alembic_import(wmOperatorType *ot) RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence", "Set to true if the cache is split into separate files"); + + RNA_def_boolean(ot->srna, "as_background_job", true, "Run as Background Job", + "Enable this to run the export in the background, disable to block Blender while exporting"); } #endif diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index b5adf38527b..6460e83e2a0 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -631,71 +631,63 @@ static int fluid_validate_scene(ReportList *reports, Scene *scene, Object *fsDom #define FLUID_SUFFIX_CONFIG_TMP (FLUID_SUFFIX_CONFIG ".tmp") #define FLUID_SUFFIX_SURFACE "fluidsurface" -static int fluid_init_filepaths(Object *fsDomain, char *targetDir, char *targetFile, char *debugStrBuffer) +static bool fluid_init_filepaths( + ReportList *reports, FluidsimSettings *domainSettings, Object *fsDomain, + char *targetDir, char *targetFile) { - FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(fsDomain, eModifierType_Fluidsim); - FluidsimSettings *domainSettings= fluidmd->fss; - FILE *fileCfg; - int dirExist = 0; - char newSurfdataPath[FILE_MAX]; /* modified output settings */ const char *suffixConfigTmp = FLUID_SUFFIX_CONFIG_TMP; - int outStringsChanged = 0; /* prepare names... */ - const char *relbase= modifier_path_relbase(fsDomain); + const char *relbase = modifier_path_relbase(fsDomain); + + /* We do not accept empty paths, they can end in random places silently, see T51176. */ + if (domainSettings->surfdataPath[0] == '\0') { + modifier_path_init(domainSettings->surfdataPath, sizeof(domainSettings->surfdataPath), + OB_FLUIDSIM_SURF_DIR_DEFAULT); + BKE_reportf(reports, RPT_WARNING, "Fluidsim: empty cache path, reset to default '%s'", + domainSettings->surfdataPath); + } BLI_strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR); - BLI_strncpy(newSurfdataPath, domainSettings->surfdataPath, FILE_MAXDIR); /* if 0'd out below, this value is never used! */ - BLI_path_abs(targetDir, relbase); /* fixed #frame-no */ + BLI_path_abs(targetDir, relbase); /* .tmp: don't overwrite/delete original file */ BLI_join_dirfile(targetFile, FILE_MAX, targetDir, suffixConfigTmp); - // make sure all directories exist - // as the bobjs use the same dir, this only needs to be checked - // for the cfg output - BLI_make_existing_file(targetFile); - - // check selected directory - // simply try to open cfg file for writing to test validity of settings - fileCfg = BLI_fopen(targetFile, "w"); - if (fileCfg) { - dirExist = 1; fclose(fileCfg); - // remove cfg dummy from directory test - BLI_delete(targetFile, false, false); - } - - if (targetDir[0] == '\0' || (!dirExist)) { - char blendFile[FILE_MAX]; - - // invalid dir, reset to current/previous - BLI_split_file_part(G.main->name, blendFile, sizeof(blendFile)); - BLI_replace_extension(blendFile, FILE_MAX, ""); /* strip .blend */ - BLI_snprintf(newSurfdataPath, FILE_MAX, "//fluidsimdata/%s_%s_", blendFile, fsDomain->id.name); - - BLI_snprintf(debugStrBuffer, 256, "fluidsimBake::error - warning resetting output dir to '%s'\n", newSurfdataPath); - elbeemDebugOut(debugStrBuffer); - outStringsChanged=1; - } - - /* check if modified output dir is ok */ -#if 0 - if (outStringsChanged) { - char dispmsg[FILE_MAX+256]; - int selection=0; - BLI_strncpy(dispmsg, "Output settings set to: '", sizeof(dispmsg)); - strcat(dispmsg, newSurfdataPath); - strcat(dispmsg, "'%t|Continue with changed settings %x1|Discard and abort %x0"); - - /* ask user if thats what he/she wants... */ - selection = pupmenu(dispmsg); - if (selection < 1) return 0; /* 0 from menu, or -1 aborted */ - BLI_strncpy(targetDir, newSurfdataPath, sizeof(targetDir)); - strncpy(domainSettings->surfdataPath, newSurfdataPath, FILE_MAXDIR); - BLI_path_abs(targetDir, G.main->name); /* fixed #frame-no */ + /* Ensure whole path exists and is wirtable. */ + const bool dir_exists = BLI_dir_create_recursive(targetDir); + const bool is_writable = BLI_file_is_writable(targetFile); + + /* We change path to some presumably valid default value, but do not allow bake process to continue, + * this gives user chance to set manually another path. */ + if (!dir_exists || !is_writable) { + modifier_path_init(domainSettings->surfdataPath, sizeof(domainSettings->surfdataPath), + OB_FLUIDSIM_SURF_DIR_DEFAULT); + + if (!dir_exists) { + BKE_reportf(reports, RPT_ERROR, "Fluidsim: could not create cache directory '%s', reset to default '%s'", + targetDir, domainSettings->surfdataPath); + } + else { + BKE_reportf(reports, RPT_ERROR, "Fluidsim: cache directory '%s' is not writable, reset to default '%s'", + targetDir, domainSettings->surfdataPath); + } + + BLI_strncpy(targetDir, domainSettings->surfdataPath, FILE_MAXDIR); + BLI_path_abs(targetDir, relbase); + + /* .tmp: don't overwrite/delete original file */ + BLI_join_dirfile(targetFile, FILE_MAX, targetDir, suffixConfigTmp); + + /* Ensure whole path exists and is wirtable. */ + if (!BLI_dir_create_recursive(targetDir) || !BLI_file_is_writable(targetFile)) { + BKE_reportf(reports, RPT_ERROR, "Fluidsim: could not use default cache directory '%s', " + "please define a valid cache path manually", targetDir); + } + return false; } -#endif - return outStringsChanged; + + return true; } /* ******************************************************************************** */ @@ -857,7 +849,6 @@ static int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain, shor char targetDir[FILE_MAX]; // store & modify output settings char targetFile[FILE_MAX]; // temp. store filename from targetDir for access - int outStringsChanged = 0; // modified? copy back before baking float domainMat[4][4]; float invDomMat[4][4]; @@ -943,7 +934,11 @@ static int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain, shor /* ******** prepare output file paths ******** */ - outStringsChanged = fluid_init_filepaths(fsDomain, targetDir, targetFile, debugStrBuffer); + if (!fluid_init_filepaths(reports, domainSettings, fsDomain, targetDir, targetFile)) { + fluidbake_free_data(channels, fobjects, fsset, fb); + return false; + } + channels->length = scene->r.efra; // DG TODO: why using endframe and not "noFrames" here? .. because "noFrames" is buggy too? (not using sfra) channels->aniFrameTime = (double)((double)domainSettings->animEnd - (double)domainSettings->animStart) / (double)noFrames; @@ -968,11 +963,6 @@ static int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain, shor /* ******** start writing / exporting ******** */ // use .tmp, don't overwrite/delete original file BLI_join_dirfile(targetFile, sizeof(targetFile), targetDir, suffixConfigTmp); - - // make sure these directories exist as well - if (outStringsChanged) { - BLI_make_existing_file(targetFile); - } /* ******** export domain to elbeem ******** */ elbeemResetSettings(fsset); diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 93bac3f6660..216cbe9d7f4 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -566,7 +566,7 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo float rast_x = x + off_x * xzoom; float rast_y = y + off_y * yzoom; - GLfloat scissor[4]; + GLfloat viewport[4]; int draw_w, draw_h; /* Determine the smallest number of pixels we need to draw @@ -581,9 +581,9 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo * fails if we zoom in on one really huge pixel so that it * covers the entire screen). */ - glGetFloatv(GL_SCISSOR_BOX, scissor); - draw_w = min_ii(img_w - off_x, ceil((scissor[2] - rast_x) / xzoom)); - draw_h = min_ii(img_h - off_y, ceil((scissor[3] - rast_y) / yzoom)); + glGetFloatv(GL_VIEWPORT, viewport); + draw_w = min_ii(img_w - off_x, ceil((viewport[2] - rast_x) / xzoom)); + draw_h = min_ii(img_h - off_y, ceil((viewport[3] - rast_y) / yzoom)); if (draw_w > 0 && draw_h > 0) { diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index e4f5802a395..8dba0e39524 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1234,6 +1234,7 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_BSDF_GLOSSY: case SH_NODE_BSDF_GLASS: case SH_NODE_BSDF_REFRACTION: + case SH_NODE_BSDF_PRINCIPLED: ntype->draw_buttons = node_shader_buts_glossy; break; case SH_NODE_BSDF_ANISOTROPIC: diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 0d92d22a173..6db23686832 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -98,6 +98,7 @@ typedef enum GPUBuiltin { GPU_PARTICLE_ANG_VELOCITY = (1 << 12), GPU_LOC_TO_VIEW_MATRIX = (1 << 13), GPU_INVERSE_LOC_TO_VIEW_MATRIX = (1 << 14), + GPU_OBJECT_INFO = (1 << 15) } GPUBuiltin; typedef enum GPUOpenGLBuiltin { @@ -213,6 +214,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); void GPU_material_enable_alpha(GPUMaterial *material); +GPUBuiltin GPU_get_material_builtins(GPUMaterial *material); GPUBlendMode GPU_material_alpha_blend(GPUMaterial *material, float obcol[4]); /* High level functions to create and use GPU materials */ @@ -230,7 +232,7 @@ void GPU_material_bind( float viewmat[4][4], float viewinv[4][4], float cameraborder[4], bool scenelock); void GPU_material_bind_uniforms( GPUMaterial *material, float obmat[4][4], float viewmat[4][4], float obcol[4], - float autobumpscale, GPUParticleInfo *pi); + float autobumpscale, GPUParticleInfo *pi, float object_info[3]); void GPU_material_unbind(GPUMaterial *material); bool GPU_material_bound(GPUMaterial *material); struct Scene *GPU_material_scene(GPUMaterial *material); @@ -345,6 +347,7 @@ struct GPUParticleInfo float location[3]; float velocity[3]; float angular_velocity[3]; + int random_id; }; #ifdef WITH_OPENSUBDIV diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index c3896fbd659..b5512aa108d 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -410,6 +410,8 @@ const char *GPU_builtin_name(GPUBuiltin builtin) return "unfparticlevel"; else if (builtin == GPU_PARTICLE_ANG_VELOCITY) return "unfparticleangvel"; + else if (builtin == GPU_OBJECT_INFO) + return "unfobjectinfo"; else return ""; } diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c index 964c2b5051e..2f2a16f9e1d 100644 --- a/source/blender/gpu/intern/gpu_compositing.c +++ b/source/blender/gpu/intern/gpu_compositing.c @@ -798,7 +798,9 @@ bool GPU_fx_do_composite_pass( ssao_shader = GPU_shader_get_builtin_fx_shader(GPU_SHADER_FX_SSAO, is_persp); if (ssao_shader) { const GPUSSAOSettings *fx_ssao = fx->settings.ssao; - float ssao_params[4] = {fx_ssao->distance_max, fx_ssao->factor, fx_ssao->attenuation, 0.0f}; + /* adjust attenuation to be scale invariant */ + float attenuation = fx_ssao->attenuation / (fx_ssao->distance_max * fx_ssao->distance_max); + float ssao_params[4] = {fx_ssao->distance_max, fx_ssao->factor, attenuation, 0.0f}; float sample_params[3]; sample_params[0] = fx->ssao_sample_count_cache; diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 074fadf6003..79934190cbc 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -45,6 +45,7 @@ #include "BLI_math.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_hash.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" @@ -1899,6 +1900,21 @@ static int gpu_get_particle_info(GPUParticleInfo *pi) return 0; } +static void GPU_get_object_info(float oi[3], Material *mat) +{ + Object *ob = GMS.gob; + oi[0] = ob->index; + oi[1] = mat->index; + unsigned int random; + if (GMS.dob) { + random = GMS.dob->random_id; + } + else { + random = BLI_hash_int_2d(BLI_hash_string(GMS.gob->id.name + 2), 0); + } + oi[2] = random * (1.0f/(float)0xFFFFFFFF); +} + int GPU_object_material_bind(int nr, void *attribs) { GPUVertexAttribs *gattribs = attribs; @@ -1958,6 +1974,7 @@ int GPU_object_material_bind(int nr, void *attribs) /* bind glsl material and get attributes */ Material *mat = GMS.gmatbuf[nr]; GPUParticleInfo partile_info; + float object_info[3] = {0}; float auto_bump_scale; @@ -1967,13 +1984,17 @@ int GPU_object_material_bind(int nr, void *attribs) if (GMS.dob) { gpu_get_particle_info(&partile_info); } + + if (GPU_get_material_builtins(gpumat) & GPU_OBJECT_INFO) { + GPU_get_object_info(object_info, mat); + } GPU_material_bind( gpumat, GMS.gob->lay, GMS.glay, 1.0, !(GMS.gob->mode & OB_MODE_TEXTURE_PAINT), GMS.gviewmat, GMS.gviewinv, GMS.gviewcamtexcofac, GMS.gscenelock); auto_bump_scale = GMS.gob->derivedFinal != NULL ? GMS.gob->derivedFinal->auto_bump_scale : 1.0f; - GPU_material_bind_uniforms(gpumat, GMS.gob->obmat, GMS.gviewmat, GMS.gob->col, auto_bump_scale, &partile_info); + GPU_material_bind_uniforms(gpumat, GMS.gob->obmat, GMS.gviewmat, GMS.gob->col, auto_bump_scale, &partile_info, object_info); GMS.gboundmat = mat; /* for glsl use alpha blend mode, unless it's set to solid and diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index d3ea8e13691..1f3ae7f708a 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -122,6 +122,8 @@ struct GPUMaterial { int partvel; int partangvel; + int objectinfoloc; + ListBase lamps; bool bound; @@ -268,6 +270,8 @@ static int gpu_material_construct_end(GPUMaterial *material, const char *passnam material->partvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_VELOCITY)); if (material->builtins & GPU_PARTICLE_ANG_VELOCITY) material->partangvel = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_PARTICLE_ANG_VELOCITY)); + if (material->builtins & GPU_OBJECT_INFO) + material->objectinfoloc = GPU_shader_get_uniform(shader, GPU_builtin_name(GPU_OBJECT_INFO)); return 1; } else { @@ -398,9 +402,14 @@ void GPU_material_bind( } } +GPUBuiltin GPU_get_material_builtins(GPUMaterial *material) +{ + return material->builtins; +} + void GPU_material_bind_uniforms( GPUMaterial *material, float obmat[4][4], float viewmat[4][4], float obcol[4], - float autobumpscale, GPUParticleInfo *pi) + float autobumpscale, GPUParticleInfo *pi, float object_info[3]) { if (material->pass) { GPUShader *shader = GPU_pass_shader(material->pass); @@ -449,6 +458,9 @@ void GPU_material_bind_uniforms( if (material->builtins & GPU_PARTICLE_ANG_VELOCITY) { GPU_shader_uniform_vector(shader, material->partangvel, 3, 1, pi->angular_velocity); } + if (material->builtins & GPU_OBJECT_INFO) { + GPU_shader_uniform_vector(shader, material->objectinfoloc, 3, 1, object_info); + } } } diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c index 14f2764b009..b579f87698c 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader.c @@ -39,6 +39,7 @@ #include "GPU_glew.h" #include "GPU_shader.h" #include "GPU_texture.h" +#include "GPU_material.h" /* TODO(sergey): Find better default values for this constants. */ #define MAX_DEFINE_LENGTH 1024 diff --git a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl index 50c8e255162..f19ff4ec65a 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl @@ -75,7 +75,7 @@ float calculate_ssao_factor(float depth) float f = dot(dir, normal); /* use minor bias here to avoid self shadowing */ - if (f > 0.05 * len + 0.0001) + if (f > 0.05 * len) factor += f * 1.0 / (len * (1.0 + len * len * ssao_params.z)); } } diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 0f3ffa8244b..b88fba12f55 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2378,11 +2378,19 @@ void shade_alpha_obcolor(vec4 col, vec4 obcol, out vec4 outcol) /*********** NEW SHADER UTILITIES **************/ -float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) +float fresnel_dielectric_0(float eta) +{ + /* compute fresnel reflactance at normal incidence => cosi = 1.0 */ + float A = (eta - 1.0) / (eta + 1.0); + + return A * A; +} + +float fresnel_dielectric_cos(float cosi, float eta) { /* compute fresnel reflectance without explicitly computing * the refracted direction */ - float c = abs(dot(Incoming, Normal)); + float c = abs(cosi); float g = eta * eta - 1.0 + c * c; float result; @@ -2399,6 +2407,13 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) return result; } +float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta) +{ + /* compute fresnel reflectance without explicitly computing + * the refracted direction */ + return fresnel_dielectric_cos(dot(Incoming, Normal), eta); +} + float hypot(float x, float y) { return sqrt(x * x + y * y); @@ -2492,6 +2507,57 @@ float floorfrac(float x, out int i) return x - i; } + +/* Principled BSDF operations */ + +float sqr(float a) +{ + return a*a; +} + +float schlick_fresnel(float u) +{ + float m = clamp(1.0 - u, 0.0, 1.0); + float m2 = m * m; + return m2 * m2 * m; // pow(m,5) +} + +float GTR1(float NdotH, float a) +{ + if (a >= 1.0) return M_1_PI; + float a2 = a*a; + float t = 1.0 + (a2 - 1.0) * NdotH*NdotH; + return (a2 - 1.0) / (M_PI * log(a2) * t); +} + +float GTR2(float NdotH, float a) +{ + float a2 = a*a; + float t = 1.0 + (a2 - 1.0) * NdotH*NdotH; + return a2 / (M_PI * t*t); +} + +float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay) +{ + return 1.0 / (M_PI * ax*ay * sqr(sqr(HdotX / ax) + sqr(HdotY / ay) + NdotH*NdotH)); +} + +float smithG_GGX(float NdotV, float alphaG) +{ + float a = alphaG*alphaG; + float b = NdotV*NdotV; + return 1.0 / (NdotV + sqrt(a + b - a * b)); +} + +vec3 rotate_vector(vec3 p, vec3 n, float theta) { + return ( + p * cos(theta) + cross(n, p) * + sin(theta) + n * dot(p, n) * + (1.0 - cos(theta)) + ); +} + + /*********** NEW SHADER NODES ***************/ #define NUM_LIGHTS 3 @@ -2553,6 +2619,126 @@ void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out vec4 resu node_bsdf_diffuse(color, 0.0, N, result); } +void node_bsdf_principled(vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, float metallic, float specular, + float specular_tint, float roughness, float anisotropic, float anisotropic_rotation, float sheen, float sheen_tint, float clearcoat, + float clearcoat_gloss, float ior, float transparency, float refraction_roughness, vec3 N, vec3 CN, vec3 T, vec3 I, out vec4 result) +{ + /* ambient light */ + // TODO: set ambient light to an appropriate value + vec3 L = vec3(mix(0.1, 0.03, metallic)) * base_color.rgb; + + float eta = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0; + + /* set the viewing vector */ + vec3 V = -normalize(I); + + /* get the tangent */ + vec3 Tangent = T; + if (T == vec3(0.0)) { + // if no tangent is set, use a default tangent + Tangent = vec3(1.0, 0.0, 0.0); + if (N.x != 0.0 || N.y != 0.0) { + vec3 N_xz = normalize(vec3(N.x, 0.0, N.z)); + + vec3 axis = normalize(cross(vec3(0.0, 0.0, 1.0), N_xz)); + float angle = acos(dot(vec3(0.0, 0.0, 1.0), N_xz)); + + Tangent = normalize(rotate_vector(vec3(1.0, 0.0, 0.0), axis, angle)); + } + } + + /* rotate tangent */ + if (anisotropic_rotation != 0.0) { + Tangent = rotate_vector(Tangent, N, anisotropic_rotation * 2.0 * M_PI); + } + + /* calculate the tangent and bitangent */ + vec3 Y = normalize(cross(N, Tangent)); + vec3 X = cross(Y, N); + + /* fresnel normalization parameters */ + float F0 = fresnel_dielectric_0(eta); + float F0_norm = 1.0 / (1.0 - F0); + + /* directional lights */ + for (int i = 0; i < NUM_LIGHTS; i++) { + vec3 light_position_world = gl_LightSource[i].position.xyz; + vec3 light_position = normalize(gl_NormalMatrix * light_position_world); + + vec3 H = normalize(light_position + V); + + vec3 light_specular = gl_LightSource[i].specular.rgb; + + float NdotL = dot(N, light_position); + float NdotV = dot(N, V); + float LdotH = dot(light_position, H); + + vec3 diffuse_and_specular_bsdf = vec3(0.0); + if (NdotL >= 0.0 && NdotV >= 0.0) { + float NdotH = dot(N, H); + + float Cdlum = 0.3 * base_color.r + 0.6 * base_color.g + 0.1 * base_color.b; // luminance approx. + + vec3 Ctint = Cdlum > 0 ? base_color.rgb / Cdlum : vec3(1.0); // normalize lum. to isolate hue+sat + vec3 Cspec0 = mix(specular * 0.08 * mix(vec3(1.0), Ctint, specular_tint), base_color.rgb, metallic); + vec3 Csheen = mix(vec3(1.0), Ctint, sheen_tint); + + // Diffuse fresnel - go from 1 at normal incidence to .5 at grazing + // and mix in diffuse retro-reflection based on roughness + + float FL = schlick_fresnel(NdotL), FV = schlick_fresnel(NdotV); + float Fd90 = 0.5 + 2.0 * LdotH*LdotH * roughness; + float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV); + + // Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf + // 1.25 scale is used to (roughly) preserve albedo + // Fss90 used to "flatten" retroreflection based on roughness + float Fss90 = LdotH*LdotH * roughness; + float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV); + float ss = 1.25 * (Fss * (1.0 / (NdotL + NdotV) - 0.5) + 0.5); + + // specular + float aspect = sqrt(1.0 - anisotropic * 0.9); + float a = sqr(roughness); + float ax = max(0.001, a / aspect); + float ay = max(0.001, a * aspect); + float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay); //GTR2(NdotH, a); + float FH = (fresnel_dielectric_cos(LdotH, eta) - F0) * F0_norm; + vec3 Fs = mix(Cspec0, vec3(1.0), FH); + float roughg = sqr(roughness * 0.5 + 0.5); + float Gs = smithG_GGX(NdotL, roughg) * smithG_GGX(NdotV, roughg); + + // sheen + vec3 Fsheen = schlick_fresnel(LdotH) * sheen * Csheen; + + diffuse_and_specular_bsdf = (M_1_PI * mix(Fd, ss, subsurface) * base_color.rgb + Fsheen) + * (1.0 - metallic) + Gs * Fs * Ds; + } + diffuse_and_specular_bsdf *= max(NdotL, 0.0); + + float CNdotL = dot(CN, light_position); + float CNdotV = dot(CN, V); + + vec3 clearcoat_bsdf = vec3(0.0); + if (CNdotL >= 0.0 && CNdotV >= 0.0 && clearcoat > 0.0) { + float CNdotH = dot(CN, H); + //float FH = schlick_fresnel(LdotH); + + // clearcoat (ior = 1.5 -> F0 = 0.04) + float Dr = GTR1(CNdotH, mix(0.1, 0.001, clearcoat_gloss)); + float Fr = fresnel_dielectric_cos(LdotH, 1.5); //mix(0.04, 1.0, FH); + float Gr = smithG_GGX(CNdotL, 0.25) * smithG_GGX(CNdotV, 0.25); + + clearcoat_bsdf = clearcoat * Gr * Fr * Dr * vec3(0.25); + } + clearcoat_bsdf *= max(CNdotL, 0.0); + + L += light_specular * (diffuse_and_specular_bsdf + clearcoat_bsdf); + } + + result = vec4(L, 1.0); +} + void node_bsdf_translucent(vec4 color, vec3 N, out vec4 result) { node_bsdf_diffuse(color, 0.0, N, result); @@ -3563,12 +3749,12 @@ void node_light_falloff(float strength, float tsmooth, out float quadratic, out constant = strength; } -void node_object_info(out vec3 location, out float object_index, out float material_index, out float random) +void node_object_info(mat4 obmat, vec3 info, out vec3 location, out float object_index, out float material_index, out float random) { - location = vec3(0.0); - object_index = 0.0; - material_index = 0.0; - random = 0.0; + location = obmat[3].xyz; + object_index = info.x; + material_index = info.y; + random = info.z; } void node_normal_map(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal) diff --git a/source/blender/makesdna/DNA_object_fluidsim.h b/source/blender/makesdna/DNA_object_fluidsim.h index a714195dd5d..846d5788d63 100644 --- a/source/blender/makesdna/DNA_object_fluidsim.h +++ b/source/blender/makesdna/DNA_object_fluidsim.h @@ -179,6 +179,7 @@ typedef struct FluidsimSettings { #define OB_FLUIDSIM_ACTIVE (1 << 1) #define OB_FLUIDSIM_OVERRIDE_TIME (1 << 2) +#define OB_FLUIDSIM_SURF_DIR_DEFAULT "cache_fluid" #define OB_FLUIDSIM_SURF_PREVIEW_OBJ_FNAME "fluidsurface_preview_####.bobj.gz" #define OB_FLUIDSIM_SURF_FINAL_OBJ_FNAME "fluidsurface_final_####.bobj.gz" #define OB_FLUIDSIM_SURF_FINAL_VEL_FNAME "fluidsurface_final_####.bvel.gz" diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index d24c7faa9f5..6d79e6d49f8 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -335,6 +335,8 @@ typedef struct DupliObject { /* particle this dupli was generated from */ struct ParticleSystem *particle_system; + unsigned int random_id; + unsigned int pad; } DupliObject; /* **************** OBJECT ********************* */ diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 6ed2c55138c..a74758a4f71 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -342,7 +342,7 @@ static void rna_ID_user_clear(ID *id) static void rna_ID_user_remap(ID *id, Main *bmain, ID *new_id) { - if (GS(id->name) == GS(new_id->name)) { + if ((GS(id->name) == GS(new_id->name)) && (id != new_id)) { /* For now, do not allow remapping data in linked data from here... */ BKE_libblock_remap(bmain, id, new_id, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 8d42d746e1c..c3477644979 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2995,6 +2995,15 @@ static void rna_ShaderNodeScript_update(Main *bmain, Scene *scene, PointerRNA *p ED_node_tag_update_nodetree(bmain, ntree, node); } +static void rna_ShaderNodePrincipled_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + bNodeTree *ntree = (bNodeTree *)ptr->id.data; + bNode *node = (bNode *)ptr->data; + + nodeUpdate(ntree, node); + rna_Node_update(bmain, scene, ptr); +} + static void rna_ShaderNodeSubsurface_update(Main *bmain, Scene *scene, PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->id.data; @@ -3254,6 +3263,12 @@ static EnumPropertyItem node_script_mode_items[] = { {0, NULL, 0, NULL, NULL} }; +static EnumPropertyItem node_principled_distribution_items[] = { + { SHD_GLOSSY_GGX, "GGX", 0, "GGX", "" }, + { SHD_GLOSSY_MULTI_GGX, "MULTI_GGX", 0, "Multiscatter GGX", "" }, + { 0, NULL, 0, NULL, NULL } +}; + /* -- Common nodes ---------------------------------------------------------- */ static void def_group_input(StructRNA *srna) @@ -4188,6 +4203,17 @@ static void def_glass(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_principled(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, node_principled_distribution_items); + RNA_def_property_ui_text(prop, "Distribution", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNodePrincipled_update"); +} + static void def_refraction(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index b3c166a6810..ec6a03e713d 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -2897,6 +2897,10 @@ static void rna_def_dupli_object(BlenderRNA *brna) RNA_def_property_enum_items(prop, dupli_items); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE); RNA_def_property_ui_text(prop, "Dupli Type", "Duplicator type that generated this dupli object"); + + prop = RNA_def_property(srna, "random_id", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Dupli random id", "Random id for this dupli object"); } static void rna_def_object_base(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index c680abe71a4..9b9f0705bb4 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -321,49 +321,55 @@ static void rna_Object_ray_cast( float origin[3], float direction[3], float distance, int *r_success, float r_location[3], float r_normal[3], int *r_index) { - BVHTreeFromMesh treeData = {NULL}; - + bool success = false; + if (ob->derivedFinal == NULL) { BKE_reportf(reports, RPT_ERROR, "Object '%s' has no mesh data to be used for ray casting", ob->id.name + 2); return; } - /* no need to managing allocation or freeing of the BVH data. this is generated and freed as needed */ - bvhtree_from_mesh_looptri(&treeData, ob->derivedFinal, 0.0f, 4, 6); + /* Test BoundBox first (efficiency) */ + BoundBox *bb = BKE_object_boundbox_get(ob); + float distmin; + if (!bb || (isect_ray_aabb_v3_simple(origin, direction, bb->vec[0], bb->vec[6], &distmin, NULL) && distmin <= distance)) { - /* may fail if the mesh has no faces, in that case the ray-cast misses */ - if (treeData.tree != NULL) { - BVHTreeRayHit hit; + BVHTreeFromMesh treeData = {NULL}; - hit.index = -1; - hit.dist = distance; - - normalize_v3(direction); + /* no need to managing allocation or freeing of the BVH data. this is generated and freed as needed */ + bvhtree_from_mesh_looptri(&treeData, ob->derivedFinal, 0.0f, 4, 6); + + /* may fail if the mesh has no faces, in that case the ray-cast misses */ + if (treeData.tree != NULL) { + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = distance; - if (BLI_bvhtree_ray_cast(treeData.tree, origin, direction, 0.0f, &hit, - treeData.raycast_callback, &treeData) != -1) - { - if (hit.dist <= distance) { - *r_success = true; + normalize_v3(direction); - copy_v3_v3(r_location, hit.co); - copy_v3_v3(r_normal, hit.no); - *r_index = dm_looptri_to_poly_index(ob->derivedFinal, &treeData.looptri[hit.index]); - goto finally; + if (BLI_bvhtree_ray_cast(treeData.tree, origin, direction, 0.0f, &hit, + treeData.raycast_callback, &treeData) != -1) + { + if (hit.dist <= distance) { + *r_success = success = true; + + copy_v3_v3(r_location, hit.co); + copy_v3_v3(r_normal, hit.no); + *r_index = dm_looptri_to_poly_index(ob->derivedFinal, &treeData.looptri[hit.index]); + } } + + free_bvhtree_from_mesh(&treeData); } } + if (success == false) { + *r_success = false; - *r_success = false; - - zero_v3(r_location); - zero_v3(r_normal); - *r_index = -1; - -finally: - free_bvhtree_from_mesh(&treeData); + zero_v3(r_location); + zero_v3(r_normal); + *r_index = -1; + } } static void rna_Object_closest_point_on_mesh( diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 9b202120b82..d3487fca42e 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -208,6 +208,8 @@ static void rna_Scene_alembic_export( int renderable_only, int face_sets, int use_subdiv_schema, + int export_hair, + int export_particles, int compression_type, int packuv, float scale, @@ -241,6 +243,8 @@ static void rna_Scene_alembic_export( .renderable_only = renderable_only, .face_sets = face_sets, .use_subdiv_schema = use_subdiv_schema, + .export_hair = export_hair, + .export_particles = export_particles, .compression_type = compression_type, .packuv = packuv, .triangulate = triangulate, @@ -250,7 +254,7 @@ static void rna_Scene_alembic_export( .global_scale = scale, }; - ABC_export(scene, C, filepath, ¶ms); + ABC_export(scene, C, filepath, ¶ms, true); #ifdef WITH_PYTHON BPy_END_ALLOW_THREADS; @@ -435,8 +439,9 @@ void RNA_api_scene(StructRNA *srna) #endif #ifdef WITH_ALEMBIC + /* XXX Deprecated, will be removed in 2.8 in favour of calling the export operator. */ func = RNA_def_function(srna, "alembic_export", "rna_Scene_alembic_export"); - RNA_def_function_ui_description(func, "Export to Alembic file"); + RNA_def_function_ui_description(func, "Export to Alembic file (deprecated, use the Alembic export operator)"); parm = RNA_def_string(func, "filepath", NULL, FILE_MAX, "File Path", "File path to write Alembic file"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); @@ -458,6 +463,8 @@ void RNA_api_scene(StructRNA *srna) RNA_def_boolean(func, "renderable_only" , 0, "Renderable objects only", "Export only objects marked renderable in the outliner"); RNA_def_boolean(func, "face_sets" , 0, "Facesets", "Export face sets"); RNA_def_boolean(func, "subdiv_schema", 0, "Use Alembic subdivision Schema", "Use Alembic subdivision Schema"); + RNA_def_boolean(func, "export_hair", 1, "Export Hair", "Exports hair particle systems as animated curves"); + RNA_def_boolean(func, "export_particles", 1, "Export Particles", "Exports non-hair particle systems"); RNA_def_enum(func, "compression_type", rna_enum_abc_compression_items, 0, "Compression", ""); RNA_def_boolean(func, "packuv" , 0, "Export with packed UV islands", "Export with packed UV islands"); RNA_def_float(func, "scale", 1.0f, 0.0001f, 1000.0f, "Scale", "Value by which to enlarge or shrink the objects with respect to the world's origin", 0.0001f, 1000.0f); diff --git a/source/blender/modifiers/intern/MOD_fluidsim_util.c b/source/blender/modifiers/intern/MOD_fluidsim_util.c index ffbbb1b0745..2ecf06057db 100644 --- a/source/blender/modifiers/intern/MOD_fluidsim_util.c +++ b/source/blender/modifiers/intern/MOD_fluidsim_util.c @@ -78,8 +78,8 @@ void fluidsim_init(FluidsimModifierData *fluidmd) fss->resolutionxyz = 65; fss->previewresxyz = 45; fss->realsize = 0.5; - fss->guiDisplayMode = 2; // preview - fss->renderDisplayMode = 3; // render + fss->guiDisplayMode = OB_FSDOM_PREVIEW; + fss->renderDisplayMode = OB_FSDOM_FINAL; fss->viscosityValue = 1.0; fss->viscosityExponent = 6; @@ -98,7 +98,7 @@ void fluidsim_init(FluidsimModifierData *fluidmd) /* fluid/inflow settings * fss->iniVel --> automatically set to 0 */ - modifier_path_init(fss->surfdataPath, sizeof(fss->surfdataPath), "cache_fluid"); + modifier_path_init(fss->surfdataPath, sizeof(fss->surfdataPath), OB_FLUIDSIM_SURF_DIR_DEFAULT); /* first init of bounding box */ /* no bounding box needed */ @@ -423,8 +423,6 @@ static void fluidsim_read_vel_cache(FluidsimModifierData *fluidmd, DerivedMesh * static DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams) { - int displaymode = 0; - int curFrame = framenr /* - 1 */ /*scene->r.sfra*/; /* start with 0 at start frame */ /* why start with 0 as start frame?? Animations + time are frozen for frame 0 anyway. (See physics_fluid.c for that. - DG */ /* If we start with frame 0, we need to remap all animation channels, too, because they will all be 1 frame late if using frame-1! - DG */ @@ -435,25 +433,23 @@ static DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, MPoly *mpoly; MPoly mp_example = {0}; - if (!useRenderParams) { - displaymode = fss->guiDisplayMode; - } - else { - displaymode = fss->renderDisplayMode; - } + const int displaymode = useRenderParams ? fss->renderDisplayMode : fss->guiDisplayMode; switch (displaymode) { - case 1: + case OB_FSDOM_GEOM: /* just display original object */ return NULL; - case 2: + case OB_FSDOM_PREVIEW: /* use preview mesh */ BLI_join_dirfile(targetFile, sizeof(targetFile), fss->surfdataPath, OB_FLUIDSIM_SURF_PREVIEW_OBJ_FNAME); break; - default: /* 3 */ - /* 3. use final mesh */ + case OB_FSDOM_FINAL: + /* use final mesh */ BLI_join_dirfile(targetFile, sizeof(targetFile), fss->surfdataPath, OB_FLUIDSIM_SURF_FINAL_OBJ_FNAME); break; + default: + BLI_assert(!"Wrong fluidsim display type"); + return NULL; } /* offset baked frame */ @@ -494,7 +490,7 @@ static DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, /* load vertex velocities, if they exist... * TODO? use generate flag as loading flag as well? * warning, needs original .bobj.gz mesh loading filename */ - if (displaymode == 3) { + if (displaymode == OB_FSDOM_FINAL) { fluidsim_read_vel_cache(fluidmd, dm, targetFile); } else { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index d20881df150..3596a2aa12d 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -163,6 +163,7 @@ set(SRC shader/nodes/node_shader_background.c shader/nodes/node_shader_bsdf_anisotropic.c shader/nodes/node_shader_bsdf_diffuse.c + shader/nodes/node_shader_bsdf_principled.c shader/nodes/node_shader_bsdf_glass.c shader/nodes/node_shader_bsdf_glossy.c shader/nodes/node_shader_bsdf_toon.c diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 4c0047f1d58..804c1897a27 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -106,6 +106,7 @@ void register_node_type_sh_bsdf_transparent(void); void register_node_type_sh_bsdf_velvet(void); void register_node_type_sh_bsdf_toon(void); void register_node_type_sh_bsdf_anisotropic(void); +void register_node_type_sh_bsdf_principled(void); void register_node_type_sh_emission(void); void register_node_type_sh_holdout(void); void register_node_type_sh_volume_absorption(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index a9c0df7d7e8..e754d063397 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -80,6 +80,7 @@ DefNode( ShaderNode, SH_NODE_BACKGROUND, 0, "BA DefNode( ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "" ) DefNode( ShaderNode, SH_NODE_BSDF_ANISOTROPIC, def_anisotropic, "BSDF_ANISOTROPIC", BsdfAnisotropic, "Anisotropic BSDF", "" ) DefNode( ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "" ) +DefNode( ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "" ) DefNode( ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "" ) DefNode( ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "" ) DefNode( ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "" ) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c new file mode 100644 index 00000000000..8b0bfa36324 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c @@ -0,0 +1,114 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "../node_shader_util.h" + +/* **************** OUTPUT ******************** */ + +static bNodeSocketTemplate sh_node_bsdf_principled_in[] = { + { SOCK_RGBA, 1, N_("Base Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 1, N_("Subsurface"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_VECTOR, 1, N_("Subsurface Radius"), 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f}, + { SOCK_RGBA, 1, N_("Subsurface Color"), 0.7f, 0.1f, 0.1f, 1.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 1, N_("Metallic"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Specular"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Specular Tint"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Anisotropic"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Anisotropic Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Sheen"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Sheen Tint"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Clearcoat"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Clearcoat Gloss"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("IOR"), 1.45f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, + { SOCK_FLOAT, 1, N_("Transparency"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Refraction Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + { SOCK_VECTOR, 1, N_("Clearcoat Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + { SOCK_VECTOR, 1, N_("Tangent"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + { -1, 0, "" } +}; + +static bNodeSocketTemplate sh_node_bsdf_principled_out[] = { + { SOCK_SHADER, 0, N_("BSDF")}, + { -1, 0, "" } +}; + +static void node_shader_init_principled(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = SHD_GLOSSY_MULTI_GGX; +} + +static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) +{ + // normal + if (!in[17].link) + in[17].link = GPU_builtin(GPU_VIEW_NORMAL); + else + GPU_link(mat, "direction_transform_m4v3", in[17].link, GPU_builtin(GPU_VIEW_MATRIX), &in[17].link); + + // clearcoat normal + if (!in[18].link) + in[18].link = GPU_builtin(GPU_VIEW_NORMAL); + else + GPU_link(mat, "direction_transform_m4v3", in[18].link, GPU_builtin(GPU_VIEW_MATRIX), &in[18].link); + + return GPU_stack_link(mat, "node_bsdf_principled", in, out, GPU_builtin(GPU_VIEW_POSITION)); +} + +static void node_shader_update_principled(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock; + int distribution = node->custom1; + + for (sock = node->inputs.first; sock; sock = sock->next) { + if (STREQ(sock->name, "Refraction Roughness")) { + if (distribution == SHD_GLOSSY_GGX) + sock->flag &= ~SOCK_UNAVAIL; + else + sock->flag |= SOCK_UNAVAIL; + + } + } +} + +/* node type definition */ +void register_node_type_sh_bsdf_principled(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_BSDF_PRINCIPLED, "Principled BSDF", NODE_CLASS_SHADER, 0); + node_type_compatibility(&ntype, NODE_NEW_SHADING); + node_type_socket_templates(&ntype, sh_node_bsdf_principled_in, sh_node_bsdf_principled_out); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, node_shader_init_principled); + node_type_storage(&ntype, "", NULL, NULL); + node_type_gpu(&ntype, node_shader_gpu_bsdf_principled); + node_type_update(&ntype, node_shader_update_principled, NULL); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_object_info.c b/source/blender/nodes/shader/nodes/node_shader_object_info.c index d1905246fd4..165d565a5b4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_object_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_object_info.c @@ -39,7 +39,16 @@ static bNodeSocketTemplate sh_node_object_info_out[] = { static int node_shader_gpu_object_info(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - return GPU_stack_link(mat, "node_object_info", in, out); + return GPU_stack_link(mat, "node_object_info", in, out, GPU_builtin(GPU_OBJECT_MATRIX), GPU_builtin(GPU_OBJECT_INFO)); +} + +static void node_shader_exec_object_info(void *data, int UNUSED(thread), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), bNodeStack **UNUSED(in), bNodeStack **out) +{ + ShaderCallData *scd = (ShaderCallData *)data; + copy_v4_v4(out[0]->vec, RE_object_instance_get_matrix(scd->shi->obi, RE_OBJECT_INSTANCE_MATRIX_OB)[3]); + out[1]->vec[0] = RE_object_instance_get_object_pass_index(scd->shi->obi); + out[2]->vec[0] = scd->shi->mat->index; + out[3]->vec[0] = RE_object_instance_get_random_id(scd->shi->obi) * (1.0f/(float)0xFFFFFFFF);; } /* node type definition */ @@ -53,6 +62,7 @@ void register_node_type_sh_object_info(void) node_type_init(&ntype, NULL); node_type_storage(&ntype, "", NULL, NULL); node_type_gpu(&ntype, node_shader_gpu_object_info); + node_type_exec(&ntype, NULL, NULL, node_shader_exec_object_info); nodeRegisterType(&ntype); } diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index f535aa5aa71..eaa4cf2c69c 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -381,7 +381,7 @@ bool RE_allow_render_generic_object(struct Object *ob); /* RE_updateRenderInstances flag */ enum { RE_OBJECT_INSTANCES_UPDATE_VIEW = (1 << 0), - RE_OBJECT_INSTANCES_UPDATE_OBMAT = (1 << 1), + RE_OBJECT_INSTANCES_UPDATE_OBMAT = (1 << 1) }; void RE_updateRenderInstances(Render *re, int flag); diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index ae389fdfd2e..b64c0c8fc52 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -178,6 +178,7 @@ typedef struct ShadeInput { unsigned int lay; int layflag, passflag, combinedflag; + short object_pass_index; struct Group *light_override; struct Material *mat_override; @@ -241,6 +242,9 @@ enum { const float (*RE_object_instance_get_matrix(struct ObjectInstanceRen *obi, int matrix_id))[4]; +float RE_object_instance_get_object_pass_index(struct ObjectInstanceRen *obi); +float RE_object_instance_get_random_id(struct ObjectInstanceRen *obi); + enum { RE_VIEW_MATRIX, RE_VIEWINV_MATRIX, diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index b3a5ccdae17..f0323340899 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -382,6 +382,8 @@ typedef struct ObjectInstanceRen { float part_co[3]; float part_vel[3]; float part_avel[3]; + + unsigned int random_id; } ObjectInstanceRen; /* ------------------------------------------------------------------------- */ diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index 76e6ca8d467..199322795f3 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -66,6 +66,7 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_hash.h" #include "DNA_material_types.h" #include "DNA_meshdata_types.h" @@ -1458,6 +1459,14 @@ ObjectInstanceRen *RE_addRenderInstance( } } + /* Fill object info */ + if (dob) { + obi->random_id = dob->random_id; + } + else { + obi->random_id = BLI_hash_int_2d(BLI_hash_string(obi->ob->id.name + 2), 0); + } + RE_updateRenderInstance(re, obi, RE_OBJECT_INSTANCES_UPDATE_OBMAT | RE_OBJECT_INSTANCES_UPDATE_VIEW); if (mat) { diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c index a8fb72fb7f8..8dea0930b9e 100644 --- a/source/blender/render/intern/source/shadeoutput.c +++ b/source/blender/render/intern/source/shadeoutput.c @@ -2141,6 +2141,16 @@ const float (*RE_object_instance_get_matrix(struct ObjectInstanceRen *obi, int m return NULL; } +float RE_object_instance_get_object_pass_index(struct ObjectInstanceRen *obi) +{ + return obi->ob->index; +} + +float RE_object_instance_get_random_id(struct ObjectInstanceRen *obi) +{ + return obi->random_id; +} + const float (*RE_render_current_get_matrix(int matrix_id))[4] { switch (matrix_id) { diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index d9a4be8552d..c6d41c9612f 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -274,6 +274,8 @@ struct Object *RE_GetCamera(struct Render *re) RET_NULL float RE_lamp_get_data(struct ShadeInput *shi, struct Object *lamp_obj, float col[4], float lv[3], float *dist, float shadow[4]) RET_ZERO const float (*RE_object_instance_get_matrix(struct ObjectInstanceRen *obi, int matrix_id))[4] RET_NULL const float (*RE_render_current_get_matrix(int matrix_id))[4] RET_NULL +float RE_object_instance_get_object_pass_index(struct ObjectInstanceRen *obi) RET_ZERO +float RE_object_instance_get_random_id(struct ObjectInstanceRen *obi) RET_ZERO /* blenkernel */ bool BKE_paint_proj_mesh_data_check(struct Scene *scene, struct Object *ob, bool *uvs, bool *mat, bool *tex, bool *stencil) RET_ZERO diff --git a/source/gameengine/Ketsji/BL_BlenderShader.cpp b/source/gameengine/Ketsji/BL_BlenderShader.cpp index 95679b5d3a6..9cbd61590b6 100644 --- a/source/gameengine/Ketsji/BL_BlenderShader.cpp +++ b/source/gameengine/Ketsji/BL_BlenderShader.cpp @@ -169,7 +169,7 @@ void BL_BlenderShader::Update(const RAS_MeshSlot & ms, RAS_IRasterizer* rasty ) rasty->GetViewMatrix().getValue(&viewmat[0][0]); float auto_bump_scale = ms.m_pDerivedMesh!=0 ? ms.m_pDerivedMesh->auto_bump_scale : 1.0f; - GPU_material_bind_uniforms(gpumat, obmat, viewmat, obcol, auto_bump_scale, NULL); + GPU_material_bind_uniforms(gpumat, obmat, viewmat, obcol, auto_bump_scale, NULL, NULL); mAlphaBlend = GPU_material_alpha_blend(gpumat, obcol); } diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 393aa512f0c..30d593bd8d6 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -442,3 +442,29 @@ if(WITH_CYCLES) MESSAGE(STATUS "Disabling Cycles tests because tests folder does not exist") endif() endif() + +if(WITH_ALEMBIC) + add_test(script_alembic_import ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/bl_alembic_import_test.py + -- + --testdir "${TEST_SRC_DIR}/alembic" + ) + + if(MSVC) + add_test(NAME cycles_${subject}_test + COMMAND + "$<TARGET_FILE_DIR:blender>/${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}/python/bin/python$<$<CONFIG:Debug>:_d>" + ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py + --blender "${TEST_BLENDER_EXE_BARE}" + --testdir "${TEST_SRC_DIR}/alembic" + --alembic-root "${ALEMBIC_ROOT_DIR}" + ) + else() + add_test(alembic_tests + ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py + --blender "${TEST_BLENDER_EXE_BARE}" + --testdir "${TEST_SRC_DIR}/alembic" + --alembic-root "${ALEMBIC_ROOT_DIR}" + ) + endif() +endif() diff --git a/tests/python/alembic_tests.py b/tests/python/alembic_tests.py new file mode 100755 index 00000000000..1cdfd75426a --- /dev/null +++ b/tests/python/alembic_tests.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python3 +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import argparse +import functools +import shutil +import pathlib +import subprocess +import sys +import tempfile +import unittest + + +def with_tempdir(wrapped): + """Creates a temporary directory for the function, cleaning up after it returns normally. + + When the wrapped function raises an exception, the contents of the temporary directory + remain available for manual inspection. + + The wrapped function is called with an extra positional argument containing + the pathlib.Path() of the temporary directory. + """ + + @functools.wraps(wrapped) + def decorator(*args, **kwargs): + dirname = tempfile.mkdtemp(prefix='blender-alembic-test') + try: + retval = wrapped(*args, pathlib.Path(dirname), **kwargs) + except: + print('Exception in %s, not cleaning up temporary directory %s' % (wrapped, dirname)) + raise + else: + shutil.rmtree(dirname) + return retval + + return decorator + + +class AbcPropError(Exception): + """Raised when AbstractAlembicTest.abcprop() finds an error.""" + + +class AbstractAlembicTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + import re + + cls.blender = args.blender + cls.testdir = pathlib.Path(args.testdir) + cls.alembic_root = pathlib.Path(args.alembic_root) + + # 'abcls' outputs ANSI colour codes, even when stdout is not a terminal. + # See https://github.com/alembic/alembic/issues/120 + cls.ansi_remove_re = re.compile(rb'\x1b[^m]*m') + + # 'abcls' array notation, like "name[16]" + cls.abcls_array = re.compile(r'^(?P<name>[^\[]+)(\[(?P<arraysize>\d+)\])?$') + + def run_blender(self, filepath: str, python_script: str, timeout: int=300) -> str: + """Runs Blender by opening a blendfile and executing a script. + + Returns Blender's stdout + stderr combined into one string. + + :param filepath: taken relative to self.testdir. + :param timeout: in seconds + """ + + blendfile = self.testdir / filepath + + command = ( + self.blender, + '--background', + '-noaudio', + '--factory-startup', + '--enable-autoexec', + str(blendfile), + '-E', 'CYCLES', + '--python-exit-code', '47', + '--python-expr', python_script, + ) + + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + timeout=timeout) + output = proc.stdout.decode('utf8') + if proc.returncode: + self.fail('Error %d running Blender:\n%s' % (proc.returncode, output)) + + return output + + def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict: + """Uses abcls to obtain compound property values from an Alembic object. + + A dict of subproperties is returned, where the values are Python values. + + The Python bindings for Alembic are old, and only compatible with Python 2.x, + so that's why we can't use them here, and have to rely on other tooling. + """ + import collections + + abcls = self.alembic_root / 'bin' / 'abcls' + + command = (str(abcls), '-vl', '%s%s' % (filepath, proppath)) + proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + timeout=30) + + coloured_output = proc.stdout + output = self.ansi_remove_re.sub(b'', coloured_output).decode('utf8') + + if proc.returncode: + raise AbcPropError('Error %d running abcls:\n%s' % (proc.returncode, output)) + + # Mapping from value type to callable that can convert a string to Python values. + converters = { + 'bool_t': int, + 'uint8_t': int, + 'int16_t': int, + 'int32_t': int, + 'uint64_t': int, + 'float64_t': float, + 'float32_t': float, + } + + result = {} + + # Ideally we'd get abcls to output JSON, see https://github.com/alembic/alembic/issues/121 + lines = collections.deque(output.split('\n')) + while lines: + info = lines.popleft() + if not info: + continue + parts = info.split() + proptype = parts[0] + + if proptype == 'CompoundProperty': + # To read those, call self.abcprop() on it. + continue + valtype_and_arrsize, name_and_extent = parts[1:] + + # Parse name and extent + m = self.abcls_array.match(name_and_extent) + if not m: + self.fail('Unparsable name/extent from abcls: %s' % name_and_extent) + name, extent = m.group('name'), m.group('arraysize') + + if extent != '1': + self.fail('Unsupported extent %s for property %s/%s' % (extent, proppath, name)) + + # Parse type + m = self.abcls_array.match(valtype_and_arrsize) + if not m: + self.fail('Unparsable value type from abcls: %s' % valtype_and_arrsize) + valtype, scalarsize = m.group('name'), m.group('arraysize') + + # Convert values + try: + conv = converters[valtype] + except KeyError: + self.fail('Unsupported type %s for property %s/%s' % (valtype, proppath, name)) + + def convert_single_line(linevalue): + try: + if scalarsize is None: + return conv(linevalue) + else: + return [conv(v.strip()) for v in linevalue.split(',')] + except ValueError as ex: + return str(ex) + + if proptype == 'ScalarProperty': + value = lines.popleft() + result[name] = convert_single_line(value) + elif proptype == 'ArrayProperty': + arrayvalue = [] + # Arrays consist of a variable number of items, and end in a blank line. + while True: + linevalue = lines.popleft() + if not linevalue: + break + arrayvalue.append(convert_single_line(linevalue)) + result[name] = arrayvalue + else: + self.fail('Unsupported type %s for property %s/%s' % (proptype, proppath, name)) + + return result + + def assertAlmostEqualFloatArray(self, actual, expect, places=6, delta=None): + """Asserts that the arrays of floats are almost equal.""" + + self.assertEqual(len(actual), len(expect), + 'Actual array has %d items, expected %d' % (len(actual), len(expect))) + + for idx, (act, exp) in enumerate(zip(actual, expect)): + self.assertAlmostEqual(act, exp, places=places, delta=delta, + msg='%f != %f at index %d' % (act, exp, idx)) + + +class HierarchicalAndFlatExportTest(AbstractAlembicTest): + @with_tempdir + def test_hierarchical_export(self, tempdir: pathlib.Path): + abc = tempdir / 'cubes_hierarchical.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_layers_only=True, flatten=False)" % abc + self.run_blender('cubes-hierarchy.blend', script) + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Cube/Cube_002/Cube_012/.xform') + self.assertEqual(1, xform['.inherits']) + self.assertAlmostEqualFloatArray( + xform['.vals'], + [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 3.07484, -2.92265, 0.0586434, 1.0] + ) + + @with_tempdir + def test_flat_export(self, tempdir: pathlib.Path): + abc = tempdir / 'cubes_flat.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_layers_only=True, flatten=True)" % abc + self.run_blender('cubes-hierarchy.blend', script) + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Cube_012/.xform') + self.assertEqual(0, xform['.inherits']) + + self.assertAlmostEqualFloatArray( + xform['.vals'], + [0.343134, 0.485243, 0.804238, 0, + 0.0, 0.856222, -0.516608, 0, + -0.939287, 0.177266, 0.293799, 0, + 1, 3, 4, 1], + ) + + +class DupliGroupExportTest(AbstractAlembicTest): + @with_tempdir + def test_hierarchical_export(self, tempdir: pathlib.Path): + abc = tempdir / 'dupligroup_hierarchical.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_layers_only=True, flatten=False)" % abc + self.run_blender('dupligroup-scene.blend', script) + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder/Suzanne/.xform') + self.assertEqual(1, xform['.inherits']) + self.assertAlmostEqualFloatArray( + xform['.vals'], + [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 2.0, 0.0, 1.0] + ) + + @with_tempdir + def test_flat_export(self, tempdir: pathlib.Path): + abc = tempdir / 'dupligroup_hierarchical.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_layers_only=True, flatten=True)" % abc + self.run_blender('dupligroup-scene.blend', script) + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Suzanne/.xform') + self.assertEqual(0, xform['.inherits']) + + self.assertAlmostEqualFloatArray( + xform['.vals'], + [1.5, 0.0, 0.0, 0.0, + 0.0, 1.5, 0.0, 0.0, + 0.0, 0.0, 1.5, 0.0, + 2.0, 3.0, 0.0, 1.0] + ) + + +class CurveExportTest(AbstractAlembicTest): + @with_tempdir + def test_export_single_curve(self, tempdir: pathlib.Path): + abc = tempdir / 'single-curve.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_layers_only=True, flatten=False)" % abc + self.run_blender('single-curve.blend', script) + + # Now check the resulting Alembic file. + abcprop = self.abcprop(abc, '/NurbsCurve/NurbsCurveShape/.geom') + self.assertEqual(abcprop['.orders'], [4]) + + abcprop = self.abcprop(abc, '/NurbsCurve/NurbsCurveShape/.geom/.userProperties') + self.assertEqual(abcprop['blender:resolution'], 10) + + +class HairParticlesExportTest(AbstractAlembicTest): + """Tests exporting with/without hair/particles. + + Just a basic test to ensure that the enabling/disabling works, and that export + works at all. NOT testing the quality/contents of the exported file. + """ + + def _do_test(self, tempdir: pathlib.Path, export_hair: bool, export_particles: bool) -> pathlib.Path: + abc = tempdir / 'hair-particles.abc' + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \ + "renderable_only=True, visible_layers_only=True, flatten=False, " \ + "export_hair=%r, export_particles=%r, as_background_job=False)" \ + % (abc, export_hair, export_particles) + self.run_blender('hair-particles.blend', script) + return abc + + @with_tempdir + def test_with_both(self, tempdir: pathlib.Path): + abc = self._do_test(tempdir, True, True) + + abcprop = self.abcprop(abc, '/Suzanne/Hair system/.geom') + self.assertIn('nVertices', abcprop) + + abcprop = self.abcprop(abc, '/Suzanne/Non-hair particle system/.geom') + self.assertIn('.velocities', abcprop) + + abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom') + self.assertIn('.faceIndices', abcprop) + + @with_tempdir + def test_with_hair_only(self, tempdir: pathlib.Path): + abc = self._do_test(tempdir, True, False) + + abcprop = self.abcprop(abc, '/Suzanne/Hair system/.geom') + self.assertIn('nVertices', abcprop) + + self.assertRaises(AbcPropError, self.abcprop, abc, + '/Suzanne/Non-hair particle system/.geom') + + abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom') + self.assertIn('.faceIndices', abcprop) + + @with_tempdir + def test_with_particles_only(self, tempdir: pathlib.Path): + abc = self._do_test(tempdir, False, True) + + self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair system/.geom') + + abcprop = self.abcprop(abc, '/Suzanne/Non-hair particle system/.geom') + self.assertIn('.velocities', abcprop) + + abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom') + self.assertIn('.faceIndices', abcprop) + + @with_tempdir + def test_with_neither(self, tempdir: pathlib.Path): + abc = self._do_test(tempdir, False, False) + + self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair system/.geom') + self.assertRaises(AbcPropError, self.abcprop, abc, + '/Suzanne/Non-hair particle system/.geom') + + abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom') + self.assertIn('.faceIndices', abcprop) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--blender', required=True) + parser.add_argument('--testdir', required=True) + parser.add_argument('--alembic-root', required=True) + args, remaining = parser.parse_known_args() + + unittest.main(argv=sys.argv[0:1] + remaining) diff --git a/tests/python/bl_alembic_import_test.py b/tests/python/bl_alembic_import_test.py new file mode 100644 index 00000000000..fd43e404e82 --- /dev/null +++ b/tests/python/bl_alembic_import_test.py @@ -0,0 +1,92 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +""" +./blender.bin --background -noaudio --factory-startup --python tests/python/bl_alembic_import_test.py -- --testdir /path/to/lib/tests/alembic +""" + +import pathlib +import sys +import unittest + +import bpy + +args = None + + +class SimpleImportTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.testdir = args.testdir + + def setUp(self): + self.assertTrue(self.testdir.exists(), + 'Test dir %s should exist' % self.testdir) + + # Make sure we always start with a known-empty file. + bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend")) + + def test_import_cube_hierarchy(self): + res = bpy.ops.wm.alembic_import( + filepath=str(self.testdir / "cubes-hierarchy.abc"), + as_background_job=False) + self.assertEqual({'FINISHED'}, res) + + # The objects should be linked to scene_collection in Blender 2.8, + # and to scene in Blender 2.7x. + objects = bpy.context.scene.objects + self.assertEqual(13, len(objects)) + + # Test the hierarchy. + self.assertIsNone(objects['Cube'].parent) + self.assertEqual(objects['Cube'], objects['Cube_001'].parent) + self.assertEqual(objects['Cube'], objects['Cube_002'].parent) + self.assertEqual(objects['Cube'], objects['Cube_003'].parent) + self.assertEqual(objects['Cube_003'], objects['Cube_004'].parent) + self.assertEqual(objects['Cube_003'], objects['Cube_005'].parent) + self.assertEqual(objects['Cube_003'], objects['Cube_006'].parent) + + +def main(): + global args + import argparse + + if '--' in sys.argv: + argv = [sys.argv[0]] + sys.argv[sys.argv.index('--')+1:] + else: + argv = sys.argv + + parser = argparse.ArgumentParser() + parser.add_argument('--testdir', required=True, type=pathlib.Path) + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == "__main__": + import traceback + # So a python error exits Blender itself too + try: + main() + except SystemExit: + raise + except: + traceback.print_exc() + sys.exit(1) |