Merge branch 'blender-v3.6-release' into goo-engine-v3.6-release

This commit is contained in:
2023-06-20 16:23:08 +01:00
155 changed files with 1973 additions and 3221 deletions
+1 -1
View File
@@ -506,7 +506,7 @@ if(NOT APPLE)
mark_as_advanced(WITH_CYCLES_DEVICE_CUDA)
option(WITH_CYCLES_CUDA_BINARIES "Build Cycles NVIDIA CUDA binaries" OFF)
set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 sm_89 compute_75 CACHE STRING "CUDA architectures to build binaries for")
set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 sm_89 compute_89 CACHE STRING "CUDA architectures to build binaries for")
option(WITH_CYCLES_CUDA_BUILD_SERIAL "Build cubins one after another (useful on machines with limited RAM)" OFF)
option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime (for developers, makes cuda-gdb work)" ON)
@@ -106,7 +106,9 @@ ExternalProject_Add(external_openimageio
CMAKE_GENERATOR ${PLATFORM_ALT_GENERATOR}
PREFIX ${BUILD_DIR}/openimageio
PATCH_COMMAND ${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/openimageio/src/external_openimageio/ < ${PATCH_DIR}/openimageio.diff &&
${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/openimageio/src/external_openimageio/ < ${PATCH_DIR}/oiio_3832.diff
${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/openimageio/src/external_openimageio/ < ${PATCH_DIR}/oiio_3832.diff &&
${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/openimageio/src/external_openimageio/ < ${PATCH_DIR}/oiio_deadlock.diff &&
${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/openimageio/src/external_openimageio/ < ${PATCH_DIR}/oiio_psd_8da473e254.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openimageio ${DEFAULT_CMAKE_FLAGS} ${OPENIMAGEIO_EXTRA_ARGS}
INSTALL_DIR ${LIBDIR}/openimageio
)
@@ -0,0 +1,62 @@
diff -Naur orig/src/idiff/idiff.cpp external_openimageio/src/idiff/idiff.cpp
--- orig/src/idiff/idiff.cpp 2023-06-07 07:47:42 -0600
+++ external_openimageio/src/idiff/idiff.cpp 2023-06-07 09:46:47 -0600
@@ -399,5 +399,6 @@
imagecache->invalidate_all(true);
ImageCache::destroy(imagecache);
+ default_thread_pool()->resize(0);
return ret;
}
diff -Naur orig/src/libutil/thread.cpp external_openimageio/src/libutil/thread.cpp
--- orig/src/libutil/thread.cpp 2023-06-07 07:47:42 -0600
+++ external_openimageio/src/libutil/thread.cpp 2023-06-07 09:45:39 -0600
@@ -151,9 +151,10 @@
this->set_thread(i);
}
} else { // the number of threads is decreased
+ std::vector<std::unique_ptr<std::thread>> terminating_threads;
for (int i = oldNThreads - 1; i >= nThreads; --i) {
*this->flags[i] = true; // this thread will finish
- this->terminating_threads.push_back(
+ terminating_threads.push_back(
std::move(this->threads[i]));
this->threads.erase(this->threads.begin() + i);
}
@@ -162,6 +163,11 @@
std::unique_lock<std::mutex> lock(this->mutex);
this->cv.notify_all();
}
+ // wait for the terminated threads to finish
+ for (auto& thread : terminating_threads) {
+ if (thread->joinable())
+ thread->join();
+ }
this->threads.resize(
nThreads); // safe to delete because the threads are detached
this->flags.resize(
@@ -238,16 +244,10 @@
if (thread->joinable())
thread->join();
}
- // wait for the terminated threads to finish
- for (auto& thread : this->terminating_threads) {
- if (thread->joinable())
- thread->join();
- }
// if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads
// therefore delete them here
this->clear_queue();
this->threads.clear();
- this->terminating_threads.clear();
this->flags.clear();
}
@@ -349,7 +349,6 @@
}
std::vector<std::unique_ptr<std::thread>> threads;
- std::vector<std::unique_ptr<std::thread>> terminating_threads;
std::vector<std::shared_ptr<std::atomic<bool>>> flags;
mutable pvt::ThreadsafeQueue<std::function<void(int id)>*> q;
std::atomic<bool> isDone;
@@ -0,0 +1,34 @@
diff --git a/src/psd.imageio/psdinput.cpp b/src/psd.imageio/psdinput.cpp
index 9dc240281..05b008e0a 100644
--- a/src/psd.imageio/psdinput.cpp
+++ b/src/psd.imageio/psdinput.cpp
@@ -1344,9 +1344,27 @@ PSDInput::load_resource_thumbnail(uint32_t length, bool isBGR)
if (!ioread(&jpeg_data[0], jpeg_length))
return false;
+ // Create an IOMemReader that references the thumbnail JPEG blob and read
+ // it with an ImageInput, into the memory owned by an ImageBuf.
Filesystem::IOMemReader thumbblob(jpeg_data.data(), jpeg_length);
- m_thumbnail = ImageBuf("thumbnail.jpg", 0, 0, nullptr, nullptr, &thumbblob);
- m_thumbnail.read(0, 0, true);
+ m_thumbnail.clear();
+ auto imgin = ImageInput::open("thumbnail.jpg", nullptr, &thumbblob);
+ if (imgin) {
+ ImageSpec spec = imgin->spec(0);
+ m_thumbnail.reset(spec, InitializePixels::No);
+ ok = imgin->read_image(0, 0, 0, m_thumbnail.spec().nchannels,
+ m_thumbnail.spec().format,
+ m_thumbnail.localpixels());
+ imgin.reset();
+ } else {
+ errorfmt("Failed to open thumbnail");
+ return false;
+ }
+ if (!ok) {
+ errorfmt("Failed to read thumbnail: {}", m_thumbnail.geterror());
+ m_thumbnail.clear();
+ return false;
+ }
// Set these attributes for the merged composite only (subimage 0)
composite_attribute("thumbnail_width", (int)m_thumbnail.spec().width);
+2
View File
@@ -2,6 +2,8 @@
#include "config_mac.h"
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include "config_freebsd.h"
#elif defined(__NetBSD__)
#include "config_netbsd.h"
#elif defined(__OpenBSD__)
#include "config_openbsd.h"
#elif defined(__MINGW32__)
+192
View File
@@ -0,0 +1,192 @@
/* define if glog doesn't use RTTI */
/* #undef DISABLE_RTTI */
/* Namespace for Google classes */
#define GOOGLE_NAMESPACE google
/* Define if you have the `dladdr' function */
/* #undef HAVE_DLADDR */
/* Define if you have the `snprintf' function */
#define HAVE_SNPRINTF
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H
/* Define to 1 if you have the <execinfo.h> header file. */
/* #undef HAVE_EXECINFO_H */
/* Define if you have the `fcntl' function */
#define HAVE_FCNTL
/* Define to 1 if you have the <glob.h> header file. */
#define HAVE_GLOB_H
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `pthread' library (-lpthread). */
#define HAVE_LIBPTHREAD
/* Define to 1 if you have the <libunwind.h> header file. */
/* #undef HAVE_LIBUNWIND_H */
/* define if you have google gflags library */
#define HAVE_LIB_GFLAGS
/* define if you have google gmock library */
/* #undef HAVE_LIB_GMOCK */
/* define if you have google gtest library */
/* #undef HAVE_LIB_GTEST */
/* define if you have libunwind */
/* #undef HAVE_LIB_UNWIND */
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H
/* define to disable multithreading support. */
/* #undef NO_THREADS */
/* define if the compiler implements namespaces */
#define HAVE_NAMESPACES
/* Define if you have the 'pread' function */
#define HAVE_PREAD
/* Define if you have POSIX threads libraries and header files. */
#define HAVE_PTHREAD
/* Define to 1 if you have the <pwd.h> header file. */
#define HAVE_PWD_H
/* Define if you have the 'pwrite' function */
#define HAVE_PWRITE
/* define if the compiler implements pthread_rwlock_* */
#define HAVE_RWLOCK 1
/* Define if you have the 'sigaction' function */
#define HAVE_SIGACTION
/* Define if you have the `sigaltstack' function */
#define HAVE_SIGALTSTACK 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H
/* Define to 1 if you have the <syscall.h> header file. */
/* #undef HAVE_SYSCALL_H */
/* Define to 1 if you have the <syslog.h> header file. */
#define HAVE_SYSLOG_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/syscall.h> header file. */
#define HAVE_SYS_SYSCALL_H
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/ucontext.h> header file. */
#define HAVE_SYS_UCONTEXT_H
/* Define to 1 if you have the <sys/utsname.h> header file. */
#define HAVE_SYS_UTSNAME_H
/* Define to 1 if you have the <ucontext.h> header file. */
#define HAVE_UCONTEXT_H
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the <unwind.h> header file. */
#define HAVE_UNWIND_H 1
/* define if the compiler supports using expression for operator */
#define HAVE_USING_OPERATOR
/* define if your compiler has __attribute__ */
#define HAVE___ATTRIBUTE__
/* define if your compiler has __builtin_expect */
#define HAVE___BUILTIN_EXPECT 1
/* define if your compiler has __sync_val_compare_and_swap */
#define HAVE___SYNC_VAL_COMPARE_AND_SWAP
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
/* #undef LT_OBJDIR */
/* Name of package */
/* #undef PACKAGE */
/* Define to the address where bug reports for this package should be sent. */
/* #undef PACKAGE_BUGREPORT */
/* Define to the full name of this package. */
/* #undef PACKAGE_NAME */
/* Define to the full name and version of this package. */
/* #undef PACKAGE_STRING */
/* Define to the one symbol short name of this package. */
/* #undef PACKAGE_TARNAME */
/* Define to the home page for this package. */
/* #undef PACKAGE_URL */
/* Define to the version of this package. */
/* #undef PACKAGE_VERSION */
/* How to access the PC from a struct ucontext */
/* #undef PC_FROM_UCONTEXT */
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
/* The size of `void *', as computed by sizeof. */
#define SIZEOF_VOID_P 8
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* the namespace where STL code like vector<> is defined */
#define STL_NAMESPACE std
/* location of source code */
#define TEST_SRC_DIR "."
/* Version number of package */
/* #undef VERSION */
/* Stops putting the code inside the Google namespace */
#define _END_GOOGLE_NAMESPACE_ }
/* Puts following code inside the Google namespace */
#define _START_GOOGLE_NAMESPACE_ namespace google {
#define GOOGLE_GLOG_DLL_DECL
/* isn't getting defined by configure script when clang compilers are used
and cuases compilation errors in stactrace/unwind modules */
#ifdef __clang__
# define NO_FRAME_POINTER
#endif
+5 -1
View File
@@ -146,7 +146,11 @@ void BlenderSync::sync_light(BL::Object &b_parent,
light->set_is_shadow_catcher(b_ob_info.real_object.is_shadow_catcher());
/* lightgroup */
light->set_lightgroup(ustring(b_ob_info.real_object.lightgroup()));
string lightgroup = b_ob_info.real_object.lightgroup();
if (lightgroup.empty()) {
lightgroup = b_parent.lightgroup();
}
light->set_lightgroup(ustring(lightgroup));
/* tag */
light->tag_update(scene);
+5 -1
View File
@@ -349,7 +349,11 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
}
/* lightgroup */
object->set_lightgroup(ustring(b_ob.lightgroup()));
string lightgroup = b_ob.lightgroup();
if (lightgroup.empty()) {
lightgroup = b_parent.lightgroup();
}
object->set_lightgroup(ustring(lightgroup));
object->tag_update(scene);
}
+16 -16
View File
@@ -94,7 +94,7 @@ endif()
# oneAPI
###########################################################################
if(WITH_CYCLES_DEVICE_ONEAPI)
if(WITH_CYCLES_DEVICE_ONEAPI OR EMBREE_SYCL_SUPPORT)
find_package(SYCL)
find_package(LevelZero)
set_and_warn_library_found("oneAPI" SYCL_FOUND WITH_CYCLES_DEVICE_ONEAPI)
@@ -102,23 +102,23 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
if(SYCL_FOUND AND SYCL_VERSION VERSION_GREATER_EQUAL 6.0 AND LEVEL_ZERO_FOUND)
message(STATUS "Found Level Zero: ${LEVEL_ZERO_LIBRARY}")
if(WITH_CYCLES_ONEAPI_BINARIES)
if(NOT OCLOC_INSTALL_DIR)
get_filename_component(_sycl_compiler_root ${SYCL_COMPILER} DIRECTORY)
get_filename_component(OCLOC_INSTALL_DIR "${_sycl_compiler_root}/../lib/ocloc" ABSOLUTE)
unset(_sycl_compiler_root)
endif()
if(NOT EXISTS ${OCLOC_INSTALL_DIR})
set(OCLOC_FOUND OFF)
message(STATUS "oneAPI ocloc not found in ${OCLOC_INSTALL_DIR}."
" A different ocloc directory can be set using OCLOC_INSTALL_DIR cmake variable.")
set_and_warn_library_found("ocloc" OCLOC_FOUND WITH_CYCLES_ONEAPI_BINARIES)
endif()
endif()
else()
message(STATUS "SYCL 6.0+ or Level Zero not found, disabling WITH_CYCLES_DEVICE_ONEAPI")
set(WITH_CYCLES_DEVICE_ONEAPI OFF)
endif()
endif()
if(WITH_CYCLES_DEVICE_ONEAPI AND WITH_CYCLES_ONEAPI_BINARIES)
if(NOT OCLOC_INSTALL_DIR)
get_filename_component(_sycl_compiler_root ${SYCL_COMPILER} DIRECTORY)
get_filename_component(OCLOC_INSTALL_DIR "${_sycl_compiler_root}/../lib/ocloc" ABSOLUTE)
unset(_sycl_compiler_root)
endif()
if(NOT EXISTS ${OCLOC_INSTALL_DIR})
set(OCLOC_FOUND OFF)
message(STATUS "oneAPI ocloc not found in ${OCLOC_INSTALL_DIR}."
" A different ocloc directory can be set using OCLOC_INSTALL_DIR cmake variable.")
set_and_warn_library_found("ocloc" OCLOC_FOUND WITH_CYCLES_ONEAPI_BINARIES)
endif()
endif()
+2
View File
@@ -359,6 +359,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
info.has_nanovdb = true;
info.has_light_tree = true;
info.has_mnee = true;
info.has_osl = true;
info.has_guiding = true;
info.has_profiling = true;
@@ -408,6 +409,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
/* Accumulate device info. */
info.has_nanovdb &= device.has_nanovdb;
info.has_light_tree &= device.has_light_tree;
info.has_mnee &= device.has_mnee;
info.has_osl &= device.has_osl;
info.has_guiding &= device.has_guiding;
info.has_profiling &= device.has_profiling;
+2
View File
@@ -75,6 +75,7 @@ class DeviceInfo {
bool display_device; /* GPU is used as a display device. */
bool has_nanovdb; /* Support NanoVDB volumes. */
bool has_light_tree; /* Support light tree. */
bool has_mnee; /* Support MNEE. */
bool has_osl; /* Support Open Shading Language. */
bool has_guiding; /* Support path guiding. */
bool has_profiling; /* Supports runtime collection of profiling info. */
@@ -97,6 +98,7 @@ class DeviceInfo {
display_device = false;
has_nanovdb = false;
has_light_tree = true;
has_mnee = true;
has_osl = false;
has_guiding = false;
has_profiling = false;
+1
View File
@@ -155,6 +155,7 @@ void device_hip_info(vector<DeviceInfo> &devices)
info.has_nanovdb = true;
info.has_light_tree = true;
info.has_mnee = true;
info.denoisers = 0;
info.has_gpu_queue = true;
+1
View File
@@ -59,6 +59,7 @@ void device_metal_info(vector<DeviceInfo> &devices)
info.has_nanovdb = vendor == METAL_GPU_APPLE;
info.has_light_tree = vendor != METAL_GPU_AMD;
info.has_mnee = vendor != METAL_GPU_AMD;
info.use_hardware_raytracing = vendor != METAL_GPU_INTEL;
if (info.use_hardware_raytracing) {
+14 -3
View File
@@ -367,7 +367,12 @@ ccl_device_inline void bsdf_roughness_eta(const KernelGlobals kg,
case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID: {
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
*roughness = make_float2(bsdf->alpha_x, bsdf->alpha_y);
*eta = CLOSURE_IS_REFRACTIVE(bsdf->type) ? 1.0f / bsdf->ior : bsdf->ior;
if (CLOSURE_IS_REFRACTION(bsdf->type) || CLOSURE_IS_GLASS(bsdf->type)) {
*eta = 1.0f / bsdf->ior;
}
else {
*eta = bsdf->ior;
}
break;
}
case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID:
@@ -672,7 +677,9 @@ ccl_device void bsdf_blur(KernelGlobals kg, ccl_private ShaderClosure *sc, float
}
ccl_device_inline Spectrum bsdf_albedo(ccl_private const ShaderData *sd,
ccl_private const ShaderClosure *sc)
ccl_private const ShaderClosure *sc,
const bool reflection,
const bool transmission)
{
Spectrum albedo = sc->weight;
/* Some closures include additional components such as Fresnel terms that cause their albedo to
@@ -686,12 +693,16 @@ ccl_device_inline Spectrum bsdf_albedo(ccl_private const ShaderData *sd,
* extra overhead though. */
#if defined(__SVM__) || defined(__OSL__)
if (CLOSURE_IS_BSDF_MICROFACET(sc->type)) {
albedo *= bsdf_microfacet_estimate_fresnel(sd, (ccl_private const MicrofacetBsdf *)sc);
albedo *= bsdf_microfacet_estimate_fresnel(
sd, (ccl_private const MicrofacetBsdf *)sc, reflection, transmission);
}
else if (sc->type == CLOSURE_BSDF_PRINCIPLED_SHEEN_ID) {
kernel_assert(reflection);
albedo *= ((ccl_private const PrincipledSheenBsdf *)sc)->avg_value;
}
else if (sc->type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) {
/* TODO(lukas): Principled Hair could also be split into a glossy and a transmission component,
* similar to Glass BSDFs. */
albedo *= bsdf_principled_hair_albedo(sd, sc);
}
#endif
+52 -28
View File
@@ -221,12 +221,20 @@ ccl_device_forceinline Spectrum microfacet_fresnel(ccl_private const MicrofacetB
const bool refraction)
{
if (bsdf->fresnel_type == MicrofacetFresnel::PRINCIPLED_V1) {
/* For Principled v1 Glass, cspec0 only provides the specular tint, the main Fresnel term
* is implicit in the phase function of the multi-scattering code.
* Therefore, for Glass, include it here on top of the Principled-specific tints. */
float F = 1.0f;
if (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID) {
F = fresnel_dielectric_cos(dot(wi, H), bsdf->ior);
}
ccl_private FresnelPrincipledV1 *fresnel = (ccl_private FresnelPrincipledV1 *)bsdf->fresnel;
if (refraction) {
return fresnel->color;
return (1.0f - F) * fresnel->color;
}
else {
return interpolate_fresnel_color(wi, H, bsdf->ior, fresnel->cspec0);
return F * interpolate_fresnel_color(wi, H, bsdf->ior, fresnel->cspec0);
}
}
else if (bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC) {
@@ -270,8 +278,15 @@ ccl_device_forceinline Spectrum microfacet_fresnel(ccl_private const MicrofacetB
else if (bsdf->fresnel_type == MicrofacetFresnel::CONSTANT) {
/* CONSTANT is only used my MultiGGX, which doesn't call this function.
* Therefore, this case only happens when determining the albedo of a MultiGGX closure.
* In that case, return 1.0 since the constant color is already baked into the weight. */
return one_spectrum();
* In that case, the constant color is already baked into the weight.
* So, just return the main dielectric Fresnel term for Glass and 1.0 for Glossy. */
if (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID) {
const float F = fresnel_dielectric_cos(dot(wi, H), bsdf->ior);
return make_spectrum(refraction ? (1.0f - F) : F);
}
else {
return refraction ? zero_spectrum() : one_spectrum();
}
}
else {
return one_spectrum();
@@ -285,17 +300,20 @@ ccl_device_forceinline Spectrum microfacet_fresnel(ccl_private const MicrofacetB
* This is used to adjust the sample weight, as well as for the Diff/Gloss/Trans Color pass
* and the Denoising Albedo pass. */
ccl_device Spectrum bsdf_microfacet_estimate_fresnel(ccl_private const ShaderData *sd,
ccl_private const MicrofacetBsdf *bsdf)
ccl_private const MicrofacetBsdf *bsdf,
const bool reflection,
const bool transmission)
{
const bool is_glass = CLOSURE_IS_GLASS(bsdf->type);
const bool is_refractive = CLOSURE_IS_REFRACTIVE(bsdf->type);
const bool m_refraction = CLOSURE_IS_REFRACTION(bsdf->type);
const bool m_glass = CLOSURE_IS_GLASS(bsdf->type);
const bool m_reflection = !(m_refraction || m_glass);
Spectrum albedo = zero_spectrum();
if (!is_refractive || is_glass) {
if (reflection && (m_reflection || m_glass)) {
/* BSDF has a reflective lobe. */
albedo += microfacet_fresnel(bsdf, sd->wi, bsdf->N, false);
}
if (is_refractive) {
if (transmission && (m_refraction || m_glass)) {
/* BSDF has a refractive lobe (unless there's TIR). */
albedo += microfacet_fresnel(bsdf, sd->wi, bsdf->N, true);
}
@@ -402,8 +420,12 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
}
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
const bool m_refractive = CLOSURE_IS_REFRACTIVE(bsdf->type);
/* Refraction: Only consider BTDF
* Glass: Consider both BRDF and BTDF, mix based on Fresnel
* Reflection: Only consider BRDF */
const bool m_refraction = CLOSURE_IS_REFRACTION(bsdf->type);
const bool m_glass = CLOSURE_IS_GLASS(bsdf->type);
const bool m_reflection = !(m_refraction || m_glass);
const float3 N = bsdf->N;
const float cos_NI = dot(N, wi);
@@ -413,7 +435,7 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
const float alpha_x = bsdf->alpha_x;
const float alpha_y = bsdf->alpha_y;
const bool is_refraction = (cos_NO < 0.0f);
const bool is_transmission = (cos_NO < 0.0f);
/* Check whether the pair of directions is valid for evaluation:
* - Incoming direction has to be in the upper hemisphere (Cycles convention)
@@ -422,15 +444,15 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
* - Purely reflective closures can't have refraction.
* - Purely refractive closures can't have reflection.
*/
if ((cos_NI <= 0) || (alpha_x * alpha_y <= 5e-7f) || ((cos_NgO < 0.0f) != is_refraction) ||
(is_refraction && !m_refractive) || (!is_refraction && m_refractive && !m_glass))
if ((cos_NI <= 0) || (alpha_x * alpha_y <= 5e-7f) || ((cos_NgO < 0.0f) != is_transmission) ||
(is_transmission && m_reflection) || (!is_transmission && m_refraction))
{
*pdf = 0.0f;
return zero_spectrum();
}
/* Compute half vector. */
float3 H = is_refraction ? -(bsdf->ior * wo + wi) : (wi + wo);
float3 H = is_transmission ? -(bsdf->ior * wo + wi) : (wi + wo);
const float inv_len_H = 1.0f / len(H);
H *= inv_len_H;
@@ -438,7 +460,7 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
float D, lambdaI, lambdaO;
/* TODO: add support for anisotropic transmission. */
if (alpha_x == alpha_y || is_refraction) { /* Isotropic. */
if (alpha_x == alpha_y || is_transmission) { /* Isotropic. */
float alpha2 = alpha_x * alpha_y;
if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) {
@@ -470,19 +492,19 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc,
}
float common = D / cos_NI *
(is_refraction ? sqr(bsdf->ior * inv_len_H) * fabsf(dot(H, wi) * dot(H, wo)) :
0.25f);
(is_transmission ? sqr(bsdf->ior * inv_len_H) * fabsf(dot(H, wi) * dot(H, wo)) :
0.25f);
float lobe_pdf = 1.0f;
if (m_glass) {
float fresnel = fresnel_dielectric_cos(dot(H, wi), bsdf->ior);
float reflect_pdf = (fresnel == 1.0f) ? 1.0f : clamp(fresnel, 0.125f, 0.875f);
lobe_pdf = is_refraction ? (1.0f - reflect_pdf) : reflect_pdf;
lobe_pdf = is_transmission ? (1.0f - reflect_pdf) : reflect_pdf;
}
*pdf = common * lobe_pdf / (1.0f + lambdaI);
const Spectrum F = microfacet_fresnel(bsdf, wi, H, is_refraction);
const Spectrum F = microfacet_fresnel(bsdf, wi, H, is_transmission);
return F * common / (1.0f + lambdaO + lambdaI);
}
@@ -503,7 +525,9 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc;
const float m_eta = bsdf->ior;
const bool m_refractive = CLOSURE_IS_REFRACTIVE(bsdf->type);
const bool m_refraction = CLOSURE_IS_REFRACTION(bsdf->type);
const bool m_glass = CLOSURE_IS_GLASS(bsdf->type);
const bool m_reflection = !(m_refraction || m_glass);
const float alpha_x = bsdf->alpha_x;
const float alpha_y = bsdf->alpha_y;
bool m_singular = (m_type == MicrofacetType::SHARP) || (alpha_x * alpha_y <= 5e-7f);
@@ -513,7 +537,7 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
if (cos_NI <= 0) {
*eval = zero_spectrum();
*pdf = 0.0f;
return (m_refractive ? LABEL_TRANSMIT : LABEL_REFLECT) |
return (m_reflection ? LABEL_REFLECT : LABEL_TRANSMIT) |
(m_singular ? LABEL_SINGULAR : LABEL_GLOSSY);
}
@@ -552,13 +576,13 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc,
bool valid;
bool do_refract;
float lobe_pdf;
if (m_refractive) {
if (m_refraction || m_glass) {
bool inside;
float fresnel = fresnel_dielectric(m_eta, H, wi, wo, &inside);
valid = !inside;
/* For glass closures, we decide between reflection and refraction here. */
if (CLOSURE_IS_GLASS(bsdf->type)) {
if (m_glass) {
if (fresnel == 1.0f) {
/* TIR, reflection is the only option. */
do_refract = false;
@@ -675,7 +699,7 @@ ccl_device void bsdf_microfacet_setup_fresnel_principledv1(
bsdf->fresnel_type = MicrofacetFresnel::PRINCIPLED_V1;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
}
ccl_device void bsdf_microfacet_setup_fresnel_conductor(ccl_private MicrofacetBsdf *bsdf,
@@ -684,7 +708,7 @@ ccl_device void bsdf_microfacet_setup_fresnel_conductor(ccl_private MicrofacetBs
{
bsdf->fresnel_type = MicrofacetFresnel::CONDUCTOR;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
}
ccl_device void bsdf_microfacet_setup_fresnel_dielectric_tint(
@@ -694,7 +718,7 @@ ccl_device void bsdf_microfacet_setup_fresnel_dielectric_tint(
{
bsdf->fresnel_type = MicrofacetFresnel::DIELECTRIC_TINT;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
}
ccl_device void bsdf_microfacet_setup_fresnel_generalized_schlick(
@@ -704,7 +728,7 @@ ccl_device void bsdf_microfacet_setup_fresnel_generalized_schlick(
{
bsdf->fresnel_type = MicrofacetFresnel::GENERALIZED_SCHLICK;
bsdf->fresnel = fresnel;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
}
/* GGX microfacet with Smith shadow-masking from:
@@ -739,7 +763,7 @@ ccl_device int bsdf_microfacet_ggx_clearcoat_setup(ccl_private MicrofacetBsdf *b
bsdf->fresnel_type = MicrofacetFresnel::DIELECTRIC;
bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
return SD_BSDF | SD_BSDF_HAS_EVAL;
}
@@ -407,7 +407,7 @@ ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(ccl_private MicrofacetBsd
bsdf->fresnel_type = MicrofacetFresnel::PRINCIPLED_V1;
bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
return bsdf_microfacet_multi_ggx_common_setup(bsdf);
}
@@ -618,7 +618,7 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(ccl_private Microfa
bsdf->fresnel_type = MicrofacetFresnel::PRINCIPLED_V1;
bsdf->type = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID;
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf));
bsdf->sample_weight *= average(bsdf_microfacet_estimate_fresnel(sd, bsdf, true, true));
return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG;
}
+1 -1
View File
@@ -67,7 +67,7 @@ ccl_device_forceinline void film_write_denoising_features_surface(KernelGlobals
}
}
Spectrum closure_albedo = bsdf_albedo(sd, sc);
Spectrum closure_albedo = bsdf_albedo(sd, sc, true, true);
if (bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) {
diffuse_albedo += closure_albedo;
sum_nonspecular_weight += sc->sample_weight;
+31 -6
View File
@@ -20,36 +20,61 @@ CCL_NAMESPACE_BEGIN
* them separately. */
ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval,
const ClosureType closure_type,
ccl_private const ShaderClosure *sc,
const float3 wo,
Spectrum value)
{
eval->diffuse = zero_spectrum();
eval->glossy = zero_spectrum();
if (CLOSURE_IS_BSDF_DIFFUSE(closure_type)) {
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
eval->diffuse = value;
}
else if (CLOSURE_IS_BSDF_GLOSSY(closure_type)) {
else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) {
eval->glossy = value;
}
else if (CLOSURE_IS_GLASS(sc->type)) {
/* Glass can count as glossy or transmission, depending on which side we end up on. */
if (dot(sc->N, wo) > 0.0f) {
eval->glossy = value;
}
}
eval->sum = value;
}
ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval, Spectrum value)
{
eval->diffuse = zero_spectrum();
eval->glossy = zero_spectrum();
eval->sum = value;
}
ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval,
const ClosureType closure_type,
ccl_private const ShaderClosure *sc,
const float3 wo,
Spectrum value)
{
if (CLOSURE_IS_BSDF_DIFFUSE(closure_type)) {
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) {
eval->diffuse += value;
}
else if (CLOSURE_IS_BSDF_GLOSSY(closure_type)) {
else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) {
eval->glossy += value;
}
else if (CLOSURE_IS_GLASS(sc->type)) {
if (dot(sc->N, wo) > 0.0f) {
eval->glossy += value;
}
}
eval->sum += value;
}
ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval, Spectrum value)
{
eval->sum += value;
}
ccl_device_inline bool bsdf_eval_is_zero(ccl_private BsdfEval *eval)
{
return is_zero(eval->sum);
+1 -1
View File
@@ -981,7 +981,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
bool found_refractive_microfacet_bsdf = false;
for (int ci = 0; ci < sd_mnee->num_closure; ci++) {
ccl_private ShaderClosure *bsdf = &sd_mnee->closure[ci];
if (CLOSURE_IS_REFRACTIVE(bsdf->type)) {
if (CLOSURE_IS_REFRACTION(bsdf->type) || CLOSURE_IS_GLASS(bsdf->type)) {
/* Note that Glass closures are treated as refractive further below. */
found_refractive_microfacet_bsdf = true;
@@ -90,31 +90,33 @@ ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
{
/* Filter out closures. */
if (kernel_data.integrator.filter_closures) {
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_EMISSION) {
const int filter_closures = kernel_data.integrator.filter_closures;
if (filter_closures & FILTER_CLOSURE_EMISSION) {
sd->closure_emission_background = zero_spectrum();
}
if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) {
sd->flag &= ~SD_BSDF_HAS_EVAL;
}
if (path_flag & PATH_RAY_CAMERA) {
if (filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) {
sd->flag &= ~SD_BSDF_HAS_EVAL;
}
for (int i = 0; i < sd->num_closure; i++) {
ccl_private ShaderClosure *sc = &sd->closure[i];
if ((CLOSURE_IS_BSDF_DIFFUSE(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIFFUSE)) ||
(CLOSURE_IS_BSDF_GLOSSY(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_GLOSSY)) ||
(CLOSURE_IS_BSDF_TRANSMISSION(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSMISSION)))
const bool filter_diffuse = (filter_closures & FILTER_CLOSURE_DIFFUSE);
const bool filter_glossy = (filter_closures & FILTER_CLOSURE_GLOSSY);
const bool filter_transmission = (filter_closures & FILTER_CLOSURE_TRANSMISSION);
const bool filter_glass = filter_glossy && filter_transmission;
if ((CLOSURE_IS_BSDF_DIFFUSE(sc->type) && filter_diffuse) ||
(CLOSURE_IS_BSDF_GLOSSY(sc->type) && filter_glossy) ||
(CLOSURE_IS_BSDF_TRANSMISSION(sc->type) && filter_transmission) ||
(CLOSURE_IS_GLASS(sc->type) && filter_glass))
{
sc->type = CLOSURE_NONE_ID;
sc->sample_weight = 0.0f;
}
else if ((CLOSURE_IS_BSDF_TRANSPARENT(sc->type) &&
(kernel_data.integrator.filter_closures & FILTER_CLOSURE_TRANSPARENT)))
{
(filter_closures & FILTER_CLOSURE_TRANSPARENT))) {
sc->type = CLOSURE_HOLDOUT_ID;
sc->sample_weight = 0.0f;
sd->flag |= SD_HOLDOUT;
@@ -218,6 +220,13 @@ ccl_device_forceinline bool _surface_shader_exclude(ClosureType type, uint light
return true;
}
}
/* Glass closures are both glossy and transmissive, so only exclude them if both are filtered. */
const uint exclude_glass = SHADER_EXCLUDE_TRANSMIT | SHADER_EXCLUDE_GLOSSY;
if ((light_shader_flags & exclude_glass) == exclude_glass) {
if (CLOSURE_IS_GLASS(type)) {
return true;
}
}
return false;
}
@@ -245,7 +254,7 @@ ccl_device_inline float _surface_shader_bsdf_eval_mis(KernelGlobals kg,
Spectrum eval = bsdf_eval(kg, sd, sc, wo, &bsdf_pdf);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
bsdf_eval_accum(result_eval, sc, wo, eval * sc->weight);
sum_pdf += bsdf_pdf * sc->sample_weight;
}
}
@@ -268,7 +277,7 @@ ccl_device_inline float surface_shader_bsdf_eval_pdfs(const KernelGlobals kg,
* factors drop out when using balance heuristic weighting. */
float sum_pdf = 0.0f;
float sum_sample_weight = 0.0f;
bsdf_eval_init(result_eval, CLOSURE_NONE_ID, zero_spectrum());
bsdf_eval_init(result_eval, zero_spectrum());
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
@@ -278,7 +287,7 @@ ccl_device_inline float surface_shader_bsdf_eval_pdfs(const KernelGlobals kg,
Spectrum eval = bsdf_eval(kg, sd, sc, wo, &bsdf_pdf);
kernel_assert(bsdf_pdf >= 0.0f);
if (bsdf_pdf != 0.0f) {
bsdf_eval_accum(result_eval, sc->type, eval * sc->weight);
bsdf_eval_accum(result_eval, sc, wo, eval * sc->weight);
sum_pdf += bsdf_pdf * sc->sample_weight;
kernel_assert(bsdf_pdf * sc->sample_weight >= 0.0f);
pdfs[i] = bsdf_pdf * sc->sample_weight;
@@ -319,7 +328,7 @@ ccl_device_inline
ccl_private BsdfEval *bsdf_eval,
const uint light_shader_flags)
{
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_spectrum());
bsdf_eval_init(bsdf_eval, zero_spectrum());
float pdf = _surface_shader_bsdf_eval_mis(
kg, sd, wo, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags);
@@ -441,7 +450,7 @@ ccl_device int surface_shader_bsdf_guided_sample_closure(KernelGlobals kg,
/* Initialize to zero. */
int label = LABEL_NONE;
Spectrum eval = zero_spectrum();
bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, eval);
bsdf_eval_init(bsdf_eval, eval);
*unguided_bsdf_pdf = 0.0f;
float guide_pdf = 0.0f;
@@ -505,7 +514,7 @@ ccl_device int surface_shader_bsdf_guided_sample_closure(KernelGlobals kg,
# endif
if (*unguided_bsdf_pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
bsdf_eval_init(bsdf_eval, sc, *wo, eval * sc->weight);
kernel_assert(reduce_min(bsdf_eval_sum(bsdf_eval)) >= 0.0f);
@@ -554,7 +563,7 @@ ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
label = bsdf_sample(kg, sd, sc, path_flag, rand_bsdf, &eval, wo, pdf, sampled_roughness, eta);
if (*pdf != 0.0f) {
bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight);
bsdf_eval_init(bsdf_eval, sc, *wo, eval * sc->weight);
if (sd->num_closure > 1) {
float sweight = sc->sample_weight;
@@ -562,7 +571,7 @@ ccl_device int surface_shader_bsdf_sample_closure(KernelGlobals kg,
}
}
else {
bsdf_eval_init(bsdf_eval, sc->type, zero_spectrum());
bsdf_eval_init(bsdf_eval, zero_spectrum());
}
return label;
@@ -634,7 +643,7 @@ ccl_device Spectrum surface_shader_diffuse(KernelGlobals kg, ccl_private const S
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type))
eval += bsdf_albedo(sd, sc);
eval += bsdf_albedo(sd, sc, true, true);
}
return eval;
@@ -647,8 +656,8 @@ ccl_device Spectrum surface_shader_glossy(KernelGlobals kg, ccl_private const Sh
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_GLOSSY(sc->type))
eval += bsdf_albedo(sd, sc);
if (CLOSURE_IS_BSDF_GLOSSY(sc->type) || CLOSURE_IS_GLASS(sc->type))
eval += bsdf_albedo(sd, sc, true, false);
}
return eval;
@@ -661,8 +670,8 @@ ccl_device Spectrum surface_shader_transmission(KernelGlobals kg, ccl_private co
for (int i = 0; i < sd->num_closure; i++) {
ccl_private const ShaderClosure *sc = &sd->closure[i];
if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type))
eval += bsdf_albedo(sd, sc);
if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type) || CLOSURE_IS_GLASS(sc->type))
eval += bsdf_albedo(sd, sc, false, true);
}
return eval;
@@ -217,7 +217,7 @@ ccl_device_inline float _volume_shader_phase_eval_mis(ccl_private const ShaderDa
Spectrum eval = volume_phase_eval(sd, svc, wo, &phase_pdf);
if (phase_pdf != 0.0f) {
bsdf_eval_accum(result_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_accum(result_eval, eval);
sum_pdf += phase_pdf * svc->sample_weight;
}
@@ -237,7 +237,7 @@ ccl_device float volume_shader_phase_eval(KernelGlobals kg,
Spectrum eval = volume_phase_eval(sd, svc, wo, &phase_pdf);
if (phase_pdf != 0.0f) {
bsdf_eval_accum(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_accum(phase_eval, eval);
}
return phase_pdf;
@@ -250,7 +250,7 @@ ccl_device float volume_shader_phase_eval(KernelGlobals kg,
const float3 wo,
ccl_private BsdfEval *phase_eval)
{
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
bsdf_eval_init(phase_eval, zero_spectrum());
float pdf = _volume_shader_phase_eval_mis(sd, phases, wo, -1, phase_eval, 0.0f, 0.0f);
@@ -300,7 +300,7 @@ ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
float guide_pdf = 0.0f;
*sampled_roughness = 1.0f - fabsf(svc->g);
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
bsdf_eval_init(phase_eval, zero_spectrum());
if (sample_guiding) {
/* Sample guiding distribution. */
@@ -321,7 +321,7 @@ ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
sd, svc, rand_phase.x, rand_phase.y, &eval, wo, unguided_phase_pdf);
if (*unguided_phase_pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_init(phase_eval, eval);
*phase_pdf = *unguided_phase_pdf;
if (use_volume_guiding) {
@@ -333,7 +333,7 @@ ccl_device int volume_shader_phase_guided_sample(KernelGlobals kg,
kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
}
else {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum());
bsdf_eval_init(phase_eval, zero_spectrum());
}
kernel_assert(reduce_min(bsdf_eval_sum(phase_eval)) >= 0.0f);
@@ -360,7 +360,7 @@ ccl_device int volume_shader_phase_sample(KernelGlobals kg,
int label = volume_phase_sample(sd, svc, rand_phase.x, rand_phase.y, &eval, wo, pdf);
if (*pdf != 0.0f) {
bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval);
bsdf_eval_init(phase_eval, eval);
}
return label;
+1 -1
View File
@@ -316,7 +316,7 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
return;
}
point_to_centroid = -bcone.axis;
cos_theta_u = fast_cosf(bcone.theta_o);
cos_theta_u = fast_cosf(bcone.theta_o + bcone.theta_e);
distance = 1.0f;
}
else {
+10 -8
View File
@@ -434,12 +434,14 @@ typedef enum ClosureType {
CLOSURE_BSDF_REFRACTION_ID,
CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID,
CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID,
CLOSURE_BSDF_HAIR_PRINCIPLED_ID,
CLOSURE_BSDF_HAIR_TRANSMISSION_ID,
/* Glass */
CLOSURE_BSDF_SHARP_GLASS_ID,
CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID,
CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID,
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID,
CLOSURE_BSDF_HAIR_PRINCIPLED_ID,
CLOSURE_BSDF_HAIR_TRANSMISSION_ID,
/* Special cases */
CLOSURE_BSDF_TRANSPARENT_ID,
@@ -473,15 +475,15 @@ typedef enum ClosureType {
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID)
#define CLOSURE_IS_BSDF_SINGULAR(type) \
(type == CLOSURE_BSDF_REFLECTION_ID || type == CLOSURE_BSDF_REFRACTION_ID || \
type == CLOSURE_BSDF_TRANSPARENT_ID)
type == CLOSURE_BSDF_TRANSPARENT_ID || type == CLOSURE_BSDF_SHARP_GLASS_ID)
#define CLOSURE_IS_BSDF_TRANSPARENT(type) (type == CLOSURE_BSDF_TRANSPARENT_ID)
#define CLOSURE_IS_BSDF_MULTISCATTER(type) \
(type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID || \
type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_BSDF_MICROFACET(type) \
((type >= CLOSURE_BSDF_MICROFACET_GGX_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID) || \
(type >= CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID && \
type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID))
((type >= CLOSURE_BSDF_REFLECTION_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID) || \
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID) || \
(type >= CLOSURE_BSDF_SHARP_GLASS_ID && type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID))
#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID)
#define CLOSURE_IS_BSSRDF(type) \
(type >= CLOSURE_BSSRDF_BURLEY_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID)
@@ -491,8 +493,8 @@ typedef enum ClosureType {
#define CLOSURE_IS_VOLUME_ABSORPTION(type) (type == CLOSURE_VOLUME_ABSORPTION_ID)
#define CLOSURE_IS_HOLDOUT(type) (type == CLOSURE_HOLDOUT_ID)
#define CLOSURE_IS_PHASE(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID)
#define CLOSURE_IS_REFRACTIVE(type) \
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_REFRACTION(type) \
(type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID)
#define CLOSURE_IS_GLASS(type) \
(type >= CLOSURE_BSDF_SHARP_GLASS_ID && type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_PRINCIPLED(type) (type == CLOSURE_BSDF_PRINCIPLED_ID)
+7 -5
View File
@@ -79,11 +79,6 @@ CCL_NAMESPACE_BEGIN
#define __VISIBILITY_FLAG__
#define __VOLUME__
/* TODO: solve internal compiler perf issue and enable light tree on Metal/AMD. */
#if defined(__KERNEL_METAL_AMD__)
# undef __LIGHT_TREE__
#endif
/* Device specific features */
#ifdef WITH_OSL
# define __OSL__
@@ -105,6 +100,13 @@ CCL_NAMESPACE_BEGIN
# define __MNEE__
#endif
#if defined(__KERNEL_METAL_AMD__)
/* Disabled due to internal compiler perf issue and enable light tree on Metal/AMD. */
# undef __LIGHT_TREE__
/* Disabled due to compiler crash on Metal/AMD. */
# undef __MNEE__
#endif
/* Scene-based selective features compilation. */
#ifdef __KERNEL_FEATURES__
# if !(__KERNEL_FEATURES & KERNEL_FEATURE_OBJECT_MOTION)
+35
View File
@@ -611,6 +611,41 @@ void AlembicObject::load_data_in_cache(CachedData &cached_data,
data_loaded = true;
}
void AlembicObject::load_data_in_cache(CachedData &cached_data,
AlembicProcedural *proc,
const IPointsSchema &schema,
Progress &progress)
{
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
cached_data.clear();
PointsSchemaData data;
data.positions = schema.getPositionsProperty();
data.radiuses = schema.getWidthsParam();
data.velocities = schema.getVelocitiesProperty();
data.time_sampling = schema.getTimeSampling();
data.num_samples = schema.getNumSamples();
data.default_radius = proc->get_default_radius();
data.radius_scale = get_radius_scale();
read_geometry_data(proc, cached_data, data, progress);
if (progress.get_cancel()) {
return;
}
/* Use the schema as the base compound property to also be able to look for top level properties.
*/
read_attributes(proc, cached_data, schema, {}, get_requested_attributes(), progress);
cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
}
void AlembicObject::setup_transform_cache(CachedData &cached_data, float scale)
{
cached_data.transforms.clear();
+4
View File
@@ -398,6 +398,10 @@ class AlembicObject : public Node {
AlembicProcedural *proc,
const Alembic::AbcGeom::ICurvesSchema &schema,
Progress &progress);
void load_data_in_cache(CachedData &cached_data,
AlembicProcedural *proc,
const Alembic::AbcGeom::IPointsSchema &schema,
Progress &progress);
bool has_data_loaded() const;
+1 -1
View File
@@ -663,8 +663,8 @@ static void read_points_data(CachedData &cached_data, const PointsSchemaData &da
if (do_radius) {
radius = (*radiuses)[offset + i];
a_radius.push_back_slow(radius);
}
a_radius.push_back_slow(radius * data.radius_scale);
a_shader.push_back_slow((int)0);
}
+1 -1
View File
@@ -110,7 +110,7 @@ void read_geometry_data(AlembicProcedural *proc,
const CurvesSchemaData &data,
Progress &progress);
/* Data of a ICurvesSchema that we need to read. */
/* Data of a IPointsSchema that we need to read. */
struct PointsSchemaData {
Alembic::AbcGeom::TimeSamplingPtr time_sampling;
size_t num_samples;
+1 -1
View File
@@ -25,7 +25,7 @@
# define HAVE_MALLOC_STATS
#elif defined(__FreeBSD__)
# include <malloc_np.h>
#elif defined(__OpenBSD__)
#elif defined(__NetBSD__) || defined(__OpenBSD__)
# undef USE_MALLOC_USABLE_SIZE
#elif defined(__APPLE__)
# include <malloc/malloc.h>
Binary file not shown.
+51 -21
View File
@@ -22,11 +22,10 @@ PERFORMANCE OF THIS SOFTWARE.
** Audaspace; version 1.3.0 -- https://audaspace.github.io/
** Cuda Wrangler; version cbf465b -- https://github.com/CudaWrangler/cuew
** Draco; version 1.3.6 -- https://google.github.io/draco/
** Embree; version 3.13.4 -- https://github.com/embree/embree
** Intel(R) oneAPI DPC++ compiler; version 20221019 --
** Embree; version 4.1.0 -- https://github.com/embree/embree
** Intel(R) oneAPI DPC++ compiler; version 2022-12 --
https://github.com/intel/llvm#oneapi-dpc-compiler
** Intel® Open Path Guiding Library; version v0.4.1-beta --
http://www.openpgl.org/
** Intel® Open Path Guiding Library; version 0.5.0 -- http://www.openpgl.org/
** Mantaflow; version 0.13 -- http://mantaflow.com/
** materialX; version 1.38.6 --
https://github.com/AcademySoftwareFoundation/MaterialX
@@ -40,6 +39,8 @@ https://software.intel.com/en-us/oneapi/onetbb
** SDL Extension Wrangler; version 15edf8e --
https://github.com/SDLWrangler/sdlew
** ShaderC; version 2022.3 -- https://github.com/google/shaderc
** SYCL Unified Runtime ; version fd711c920acc4434cb52ff18b078c082d9d7f44d --
https://github.com/oneapi-src/unified-runtime
** Vulkan Loader; version 1.2.198 --
https://github.com/KhronosGroup/Vulkan-Loader
@@ -279,6 +280,8 @@ limitations under the License.
Copyright 2014 Blender Foundation
* For ShaderC see also this required NOTICE:
Copyright 2015 The Shaderc Authors. All rights reserved.
* For SYCL Unified Runtime see also this required NOTICE:
Copyright (C) 2022-2023 Intel Corporation
* For Vulkan Loader see also this required NOTICE:
Copyright (c) 2019 The Khronos Group Inc.
Copyright (c) 2019 Valve Corporation
@@ -378,7 +381,7 @@ All rights reserved.
** Google Logging; version 4.4.0 -- https://github.com/google/glog
Copyright (c) 2006, Google Inc.
All rights reserved.
** Imath; version 3.1.5 -- https://github.com/AcademySoftwareFoundation/Imath
** Imath; version 3.1.7 -- https://github.com/AcademySoftwareFoundation/Imath
Copyright Contributors to the OpenEXR Project. All rights reserved.
** ISPC; version 1.17.0 -- https://github.com/ispc/ispc
Copyright Intel Corporation
@@ -395,10 +398,10 @@ Copyright Contributors to the Open Shading Language project.
** OpenColorIO; version 2.2.0 --
https://github.com/AcademySoftwareFoundation/OpenColorIO
Copyright Contributors to the OpenColorIO Project.
** OpenEXR; version 3.1.5 --
** OpenEXR; version 3.1.7 --
https://github.com/AcademySoftwareFoundation/openexr
Copyright Contributors to the OpenEXR Project. All rights reserved.
** OpenImageIO; version 2.4.9.0 -- http://www.openimageio.org
** OpenImageIO; version 2.4.11.0 -- http://www.openimageio.org
Copyright (c) 2008-present by Contributors to the OpenImageIO project. All
Rights Reserved.
** Pystring; version 1.1.3 -- https://github.com/imageworks/pystring
@@ -571,7 +574,7 @@ effect of CC0 on those rights.
------
** FLAC; version 1.3.4 -- https://xiph.org/flac/
** FLAC; version 1.4.2 -- https://xiph.org/flac/
Copyright (C) 2001-2009 Josh Coalson
Copyright (C) 2011-2016 Xiph.Org Foundation
** Potrace; version 1.16 -- http://potrace.sourceforge.net/
@@ -1242,7 +1245,7 @@ Copyright (C) 2003-2021 x264 project
** miniLZO; version 2.08 -- http://www.oberhumer.com/opensource/lzo/
LZO and miniLZO are Copyright (C) 1996-2014 Markus Franz Xaver Oberhumer
All Rights Reserved.
** The FreeType Project; version 2.12.1 --
** The FreeType Project; version 2.13.0 --
https://sourceforge.net/projects/freetype
Copyright (C) 1996-2020 by David Turner, Robert Wilhelm, and Werner Lemberg.
** X Drag and Drop; version 2000-08-08 --
@@ -2796,7 +2799,7 @@ That's all there is to it!
------
** FFmpeg; version 5.1.2 -- http://ffmpeg.org/
** FFmpeg; version 6.0 -- http://ffmpeg.org/
Copyright: The FFmpeg contributors
https://github.com/FFmpeg/FFmpeg/blob/master/CREDITS
** Libsndfile; version 1.1.0 -- http://libsndfile.github.io/libsndfile/
@@ -3425,7 +3428,35 @@ December 9, 2010
------
** {fmt}; version 8.0.0 -- https://github.com/fmtlib/fmt
** vcintrinsics; version 782fbf7301dc73acaa049a4324c976ad94f587f7 --
https://github.com/intel/vc-intrinsics
Copyright (c) 2019 Intel Corporation
MIT License
Copyright (c) 2019 Intel Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------
** {fmt}; version 9.1.0 -- https://github.com/fmtlib/fmt
Copyright (c) 2012 - present, Victor Zverovich
** Brotli; version 1.0.9 -- https://github.com/google/brotli
Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.
@@ -3454,11 +3485,11 @@ Copyright (c) 2006, 2008 Junio C Hamano
Copyright © 2017-2018 Red Hat Inc.
Copyright © 2012 Collabora, Ltd.
Copyright © 2008 Kristian Høgsberg
** Libxml2; version 2.10.3 -- http://xmlsoft.org/
** Libxml2; version 2.10.4 -- http://xmlsoft.org/
Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved.
** Mesa 3D; version 21.1.5 -- https://www.mesa3d.org/
Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
** oneAPI Level Zero; version v1.8.5 --
** oneAPI Level Zero; version v1.8.8 --
https://github.com/oneapi-src/level-zero
Copyright (C) 2019-2021 Intel Corporation
** OPENCollada; version 1.6.68 -- https://github.com/KhronosGroup/OpenCOLLADA
@@ -3470,12 +3501,11 @@ Copyright (c) 2018 Jingwei Huang, Yichao Zhou, Matthias Niessner,
Jonathan Shewchuk and Leonidas Guibas. All rights reserved.
** robin-map; version 0.6.2 -- https://github.com/Tessil/robin-map
Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
** sse2neon; version fe5ff00bb8d19b327714a3c290f3e2ce81ba3525 --
https://github.com/DLTcollab/sse2neon
** sse2neon; version 1.6.0 -- https://github.com/DLTcollab/sse2neon
Copyright sse2neon contributors
** TinyGLTF; version 2.5.0 -- https://github.com/syoyo/tinygltf
Copyright (c) 2017 Syoyo Fujita, Aurélien Chatelain and many contributors
** Wayland protocols; version 1.21 --
** Wayland protocols; version 1.31 --
https://gitlab.freedesktop.org/wayland/wayland-protocols
Copyright © 2008-2013 Kristian Høgsberg
Copyright © 2010-2013 Intel Corporation
@@ -3973,7 +4003,7 @@ the following restrictions:
------
** LibTIFF; version 4.4.0 -- http://www.libtiff.org/
** LibTIFF; version 4.5.1 -- http://www.libtiff.org/
Copyright (c) 1988-1997 Sam Leffler
Copyright (c) 1991-1997 Silicon Graphics, Inc.
@@ -4029,7 +4059,7 @@ Software.
** OpenSubdiv; version 3.5.0 -- http://graphics.pixar.com/opensubdiv
Copyright 2013 Pixar
** Universal Scene Description; version 22.11 -- http://www.openusd.org/
** Universal Scene Description; version 23.05 -- http://www.openusd.org/
Copyright 2016 Pixar
Licensed under the Apache License, Version 2.0 (the "Apache License") with the
@@ -4215,7 +4245,7 @@ disclaims all warranties with regard to this software.
------
** Wayland; version 1.21.0 -- https://gitlab.freedesktop.org/wayland/wayland
** Wayland; version 1.22.0 -- https://gitlab.freedesktop.org/wayland/wayland
Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
Copyright © 2011 Kristian Høgsberg
Copyright © 2011 Benjamin Franzke
@@ -4240,7 +4270,7 @@ MIT Expat
------
** OpenSSL; version 1.1.1q -- https://www.openssl.org/
** OpenSSL; version 3.1.1 -- https://www.openssl.org/
Copyright (c) 1998-2021 The OpenSSL Project
Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson
@@ -4363,7 +4393,7 @@ OpenSSL License
------
** Python; version 3.10.9 -- https://www.python.org
** Python; version 3.10.12 -- https://www.python.org
Copyright (c) 2001-2021 Python Software Foundation. All rights reserved.
A. HISTORY OF THE SOFTWARE
+41 -1
View File
@@ -178,6 +178,8 @@ class Object(bpy_types.ID):
def children(self):
"""All the children of this object.
:type: tuple of `Object`
.. note:: Takes ``O(len(bpy.data.objects))`` time."""
import bpy
return tuple(child for child in bpy.data.objects
@@ -187,6 +189,8 @@ class Object(bpy_types.ID):
def children_recursive(self):
"""A list of all children from this object.
:type: tuple of `Object`
.. note:: Takes ``O(len(bpy.data.objects))`` time."""
import bpy
parent_child_map = {}
@@ -209,6 +213,8 @@ class Object(bpy_types.ID):
"""
The collections this object is in.
:type: tuple of `Collection`
.. note:: Takes ``O(len(bpy.data.collections) + len(bpy.data.scenes))`` time."""
import bpy
return (
@@ -225,6 +231,8 @@ class Object(bpy_types.ID):
def users_scene(self):
"""The scenes this object is in.
:type: tuple of `Scene`
.. note:: Takes ``O(len(bpy.data.scenes) * len(bpy.data.objects))`` time."""
import bpy
return tuple(scene for scene in bpy.data.scenes
@@ -526,7 +534,7 @@ def ord_ind(i1, i2):
class Mesh(bpy_types.ID):
__slots__ = ()
def from_pydata(self, vertices, edges, faces):
def from_pydata(self, vertices, edges, faces, shade_flat=True):
"""
Make a mesh from a list of vertices/edges/faces
Until we have a nicer way to make geometry, use this.
@@ -583,6 +591,9 @@ class Mesh(bpy_types.ID):
self.polygons.foreach_set("loop_start", loop_starts)
self.polygons.foreach_set("vertices", vertex_indices)
if shade_flat:
self.shade_flat()
if edges_len or faces_len:
self.update(
# Needed to either:
@@ -597,6 +608,35 @@ class Mesh(bpy_types.ID):
def edge_keys(self):
return [ed.key for ed in self.edges]
def shade_flat(self):
"""
Render and display faces uniform, using face normals,
setting the "sharp_face" attribute true for every face
"""
attr = self.attributes.get("sharp_face")
if attr is None:
pass
elif attr.data_type == 'BOOLEAN' and attr.domain == 'FACE':
pass
else:
self.attributes.remove(attr)
attr = None
if attr is None:
attr = self.attributes.new("sharp_face", 'BOOLEAN', 'FACE')
for value in attr.data:
value.value = True
def shade_smooth(self):
"""
Render and display faces smooth, using interpolated vertex normals,
removing the "sharp_face" attribute
"""
attr = self.attributes.get("sharp_face")
if attr is not None:
self.attributes.remove(attr)
class MeshEdge(StructRNA):
__slots__ = ()
@@ -243,6 +243,7 @@ class AddTorus(Operator, object_utils.AddObjectHelper):
mesh.vertices.foreach_set("co", verts_loc)
mesh.polygons.foreach_set("loop_start", range(0, nbr_loops, 4))
mesh.loops.foreach_set("vertex_index", faces)
mesh.shade_flat()
if self.generate_uvs:
add_uvs(mesh, self.minor_segments, self.major_segments)
+2 -2
View File
@@ -29,7 +29,7 @@ def draw_ui_list(
context,
class_name="UI_UL_list",
*,
unique_id="",
unique_id,
list_path,
active_index_path,
insertion_operators=True,
@@ -46,7 +46,7 @@ def draw_ui_list(
:type context: :class:`Context`
:arg class_name: Name of the UIList class to draw. The default is the UIList class that ships with Blender.
:type class_name: str
:arg unique_id: Optional identifier, in case wanting to draw multiple unique copies of a list.
:arg unique_id: Unique identifier to differentiate this from other UI lists.
:type unique_id: str
:arg list_path: Data path of the list relative to context, eg. "object.vertex_groups".
:type list_path: str
+1 -1
View File
@@ -1876,7 +1876,7 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel):
split.label(text="Channel")
split.prop(strip, "channel", text="")
if strip.type == 'SOUND':
if not is_effect:
split = layout.split(factor=0.5 + max_factor)
split.alignment = 'RIGHT'
split.label(text="Speed Factor")
@@ -2529,18 +2529,6 @@ class _defs_sequencer_generic:
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def retime():
return dict(
idname="builtin.retime",
label="Retime",
icon="ops.sequencer.retime",
widget="SEQUENCER_GGT_gizmo_retime",
operator=None,
keymap=None,
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def sample():
return dict(
@@ -3222,7 +3210,6 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
'SEQUENCER': [
*_tools_select,
_defs_sequencer_generic.blade,
_defs_sequencer_generic.retime,
],
'SEQUENCER_PREVIEW': [
*_tools_select,
+3 -2
View File
@@ -18,8 +18,9 @@ class MyPanel(bpy.types.Panel):
draw_ui_list(
layout,
context,
list_context_path="scene.my_list",
active_index_context_path="scene.my_list_active_index"
list_path="scene.my_list",
active_index_path="scene.my_list_active_index",
unique_id="my_list_id",
)
@@ -48,6 +48,7 @@ void BKE_mesh_legacy_edge_crease_from_layers(struct Mesh *mesh);
* Copy edge creases from edges to a separate layer.
*/
void BKE_mesh_legacy_edge_crease_to_layers(struct Mesh *mesh);
void BKE_mesh_crease_layers_from_future(struct Mesh *mesh);
/**
* Copy bevel weights from separate layers into vertices and edges.
+1 -1
View File
@@ -865,7 +865,7 @@ void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Obj
*/
struct MultiresModifierData *BKE_sculpt_multires_active(const struct Scene *scene,
struct Object *ob);
int *BKE_sculpt_face_sets_ensure(struct Mesh *mesh);
int *BKE_sculpt_face_sets_ensure(struct Object *ob);
/**
* Create the attribute used to store face visibility and retrieve its data.
* Note that changes to the face visibility have to be propagated to other domains
@@ -149,6 +149,9 @@ struct StatesAroundFrame {
class ModifierSimulationCache {
private:
mutable std::mutex states_at_frames_mutex_;
/**
* All simulation states, sorted by frame.
*/
Vector<std::unique_ptr<ModifierSimulationStateAtFrame>> states_at_frames_;
/**
* Used for baking to deduplicate arrays when writing and writing from storage. Sharing info
@@ -184,4 +187,12 @@ class ModifierSimulationCache {
void clear_prev_states();
};
/**
* Wrap simulation cache in `std::shared_ptr` so that it can be owned by evaluated modifier even if
* the original modifier has been deleted.
*/
struct ModifierSimulationCachePtr {
std::shared_ptr<ModifierSimulationCache> ptr;
};
} // namespace blender::bke::sim
-7
View File
@@ -154,13 +154,6 @@ void BKE_sound_set_scene_sound_volume(void *handle, float volume, char animated)
void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated);
void BKE_sound_set_scene_sound_pitch_at_frame(void *handle, int frame, float pitch, char animated);
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle,
int frame_start,
int frame_end,
float pitch);
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated);
void BKE_sound_update_sequencer(struct Main *main, struct bSound *sound);
+2 -2
View File
@@ -38,7 +38,7 @@ typedef struct bMovieHandle {
void (*end_movie)(void *context_v);
/* Optional function. */
void (*get_movie_path)(char *filepath,
void (*get_movie_path)(char filepath[/*FILE_MAX*/ 1024],
const struct RenderData *rd,
bool preview,
const char *suffix);
@@ -52,7 +52,7 @@ bMovieHandle *BKE_movie_handle_get(char imtype);
/**
* \note Similar to #BKE_image_path_from_imformat()
*/
void BKE_movie_filepath_get(char *filepath,
void BKE_movie_filepath_get(char filepath[/*FILE_MAX*/ 1024],
const struct RenderData *rd,
bool preview,
const char *suffix);
+1 -1
View File
@@ -64,7 +64,7 @@ int BKE_ffmpeg_append(void *context_v,
int recty,
const char *suffix,
struct ReportList *reports);
void BKE_ffmpeg_filepath_get(char *filepath,
void BKE_ffmpeg_filepath_get(char filepath[/*FILE_MAX*/ 1024],
const struct RenderData *rd,
bool preview,
const char *suffix);
+46 -91
View File
@@ -664,6 +664,23 @@ static void bvhtree_from_editmesh_setup_data(BVHTree *tree,
}
}
static BVHTree *bvhtree_new_commom(
float epsilon, int tree_type, int axis, int elems_num, int &elems_num_active)
{
if (elems_num_active != -1) {
BLI_assert(IN_RANGE_INCL(elems_num_active, 0, elems_num));
}
else {
elems_num_active = elems_num;
}
if (elems_num_active == 0) {
return nullptr;
}
return BLI_bvhtree_new(elems_num_active, epsilon, tree_type, axis);
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -677,20 +694,15 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree(float epsilon,
const BitSpan verts_mask,
int verts_num_active)
{
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
const int verts_num = em->bm->totvert;
if (!verts_mask.is_empty()) {
BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num));
}
else {
verts_num_active = verts_num;
}
BVHTree *tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis);
BVHTree *tree = bvhtree_new_commom(epsilon, tree_type, axis, verts_num, verts_num_active);
if (!tree) {
return nullptr;
}
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
for (int i = 0; i < verts_num; i++) {
if (!verts_mask.is_empty() && !verts_mask[i]) {
continue;
@@ -711,17 +723,7 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(float epsilon,
const BitSpan verts_mask,
int verts_num_active)
{
if (!verts_mask.is_empty()) {
BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num));
}
else {
verts_num_active = verts_num;
}
if (verts_num_active == 0) {
return nullptr;
}
BVHTree *tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis);
BVHTree *tree = bvhtree_new_commom(epsilon, tree_type, axis, verts_num, verts_num_active);
if (!tree) {
return nullptr;
}
@@ -799,21 +801,15 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
const BitSpan edges_mask,
int edges_num_active)
{
BM_mesh_elem_table_ensure(em->bm, BM_EDGE);
const int edges_num = em->bm->totedge;
if (!edges_mask.is_empty()) {
BLI_assert(IN_RANGE_INCL(edges_num_active, 0, edges_num));
}
else {
edges_num_active = edges_num;
}
BVHTree *tree = BLI_bvhtree_new(edges_num_active, epsilon, tree_type, axis);
BVHTree *tree = bvhtree_new_commom(epsilon, tree_type, axis, edges_num, edges_num_active);
if (!tree) {
return nullptr;
}
BM_mesh_elem_table_ensure(em->bm, BM_EDGE);
int i;
BMIter iter;
BMEdge *eed;
@@ -840,18 +836,7 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
int tree_type,
int axis)
{
if (!edges_mask.is_empty()) {
BLI_assert(IN_RANGE_INCL(edges_num_active, 0, edges.size()));
}
else {
edges_num_active = edges.size();
}
if (edges_num_active == 0) {
return nullptr;
}
/* Create a BVH-tree of the given target */
BVHTree *tree = BLI_bvhtree_new(edges_num_active, epsilon, tree_type, axis);
BVHTree *tree = bvhtree_new_commom(epsilon, tree_type, axis, edges.size(), edges_num_active);
if (!tree) {
return nullptr;
}
@@ -935,20 +920,7 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon,
const BitSpan faces_mask,
int faces_num_active)
{
if (faces_num == 0) {
return nullptr;
}
if (!faces_mask.is_empty()) {
BLI_assert(IN_RANGE_INCL(faces_num_active, 0, faces_num));
}
else {
faces_num_active = faces_num;
}
/* Create a BVH-tree of the given target. */
// printf("%s: building BVH, total=%d\n", __func__, numFaces);
BVHTree *tree = BLI_bvhtree_new(faces_num_active, epsilon, tree_type, axis);
BVHTree *tree = bvhtree_new_commom(epsilon, tree_type, axis, faces_num, faces_num_active);
if (!tree) {
return nullptr;
}
@@ -989,20 +961,8 @@ static BVHTree *bvhtree_from_editmesh_looptri_create_tree(float epsilon,
int looptri_num_active)
{
const int looptri_num = em->tottri;
if (looptri_num == 0) {
return nullptr;
}
if (!looptri_mask.is_empty()) {
BLI_assert(IN_RANGE_INCL(looptri_num_active, 0, looptri_num));
}
else {
looptri_num_active = looptri_num;
}
/* Create a BVH-tree of the given target */
// printf("%s: building BVH, total=%d\n", __func__, numFaces);
BVHTree *tree = BLI_bvhtree_new(looptri_num_active, epsilon, tree_type, axis);
BVHTree *tree = bvhtree_new_commom(epsilon, tree_type, axis, looptri_num, looptri_num_active);
if (!tree) {
return nullptr;
}
@@ -1041,37 +1001,30 @@ static BVHTree *bvhtree_from_mesh_looptri_create_tree(float epsilon,
const BitSpan looptri_mask,
int looptri_num_active)
{
if (!looptri_mask.is_empty()) {
BLI_assert(IN_RANGE_INCL(looptri_num_active, 0, looptris.size()));
}
else {
looptri_num_active = looptris.size();
}
if (looptri_num_active == 0) {
if (positions == nullptr) {
return nullptr;
}
/* Create a BVH-tree of the given target */
// printf("%s: building BVH, total=%d\n", __func__, numFaces);
BVHTree *tree = BLI_bvhtree_new(looptri_num_active, epsilon, tree_type, axis);
BVHTree *tree = bvhtree_new_commom(
epsilon, tree_type, axis, looptris.size(), looptri_num_active);
if (!tree) {
return nullptr;
}
if (positions && !looptris.is_empty()) {
for (const int i : looptris.index_range()) {
float co[3][3];
if (!looptri_mask.is_empty() && !looptri_mask[i]) {
continue;
}
copy_v3_v3(co[0], positions[corner_verts[looptris[i].tri[0]]]);
copy_v3_v3(co[1], positions[corner_verts[looptris[i].tri[1]]]);
copy_v3_v3(co[2], positions[corner_verts[looptris[i].tri[2]]]);
BLI_bvhtree_insert(tree, i, co[0], 3);
for (const int i : looptris.index_range()) {
float co[3][3];
if (!looptri_mask.is_empty() && !looptri_mask[i]) {
continue;
}
copy_v3_v3(co[0], positions[corner_verts[looptris[i].tri[0]]]);
copy_v3_v3(co[1], positions[corner_verts[looptris[i].tri[1]]]);
copy_v3_v3(co[2], positions[corner_verts[looptris[i].tri[2]]]);
BLI_bvhtree_insert(tree, i, co[0], 3);
}
BLI_assert(BLI_bvhtree_get_len(tree) == looptri_num_active);
return tree;
@@ -1401,7 +1354,8 @@ BVHTree *BKE_bvhtree_from_pointcloud_get(BVHTreeFromPointCloud *data,
const PointCloud *pointcloud,
const int tree_type)
{
BVHTree *tree = BLI_bvhtree_new(pointcloud->totpoint, 0.0f, tree_type, 6);
int tot_point = pointcloud->totpoint;
BVHTree *tree = bvhtree_new_commom(0.0f, tree_type, 6, tot_point, tot_point);
if (!tree) {
return nullptr;
}
@@ -1410,7 +1364,8 @@ BVHTree *BKE_bvhtree_from_pointcloud_get(BVHTreeFromPointCloud *data,
for (const int i : positions.index_range()) {
BLI_bvhtree_insert(tree, i, positions[i], 1);
}
BLI_assert(BLI_bvhtree_get_len(tree) == pointcloud->totpoint);
BLI_assert(BLI_bvhtree_get_len(tree) == tot_point);
bvhtree_balance(tree, false);
data->coords = (const float(*)[3])positions.data();
+2 -1
View File
@@ -188,7 +188,8 @@ void BKE_cachefile_reader_open(CacheFile *cache_file,
case CACHEFILE_TYPE_ALEMBIC:
# ifdef WITH_ALEMBIC
/* Open Alembic cache reader. */
*reader = CacheReader_open_alembic_object(cache_file->handle, *reader, object, object_path);
*reader = CacheReader_open_alembic_object(
cache_file->handle, *reader, object, object_path, cache_file->is_sequence);
# endif
break;
case CACHEFILE_TYPE_USD:
@@ -9,6 +9,7 @@
#include "DNA_object_types.h"
#include "BLI_map.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_task.hh"
#include "BLI_threads.h"
#include "BLI_timeit.hh"
@@ -19,43 +20,14 @@
namespace blender::bke::calc_edges {
/** This is used to uniquely identify edges in a hash map. */
struct OrderedEdge {
int v_low, v_high;
OrderedEdge(const int v1, const int v2)
{
if (v1 < v2) {
v_low = v1;
v_high = v2;
}
else {
v_low = v2;
v_high = v1;
}
}
OrderedEdge(const uint v1, const uint v2) : OrderedEdge(int(v1), int(v2)) {}
uint64_t hash() const
{
return (this->v_low << 8) ^ this->v_high;
}
/** Return a hash value that is likely to be different in the low bits from the normal `hash()`
* function. This is necessary to avoid collisions in #BKE_mesh_calc_edges. */
uint64_t hash2() const
{
return this->v_low;
}
friend bool operator==(const OrderedEdge &e1, const OrderedEdge &e2)
{
BLI_assert(e1.v_low < e1.v_high);
BLI_assert(e2.v_low < e2.v_high);
return e1.v_low == e2.v_low && e1.v_high == e2.v_high;
}
};
/**
* Return a hash value that is likely to be different in the low bits from the normal `hash()`
* function. This is necessary to avoid collisions in #BKE_mesh_calc_edges.
*/
static uint64_t edge_hash_2(const OrderedEdge &edge)
{
return edge.v_low;
}
/* The map first contains an edge pointer and later an index. */
union OrigEdgeOrIndex {
@@ -84,7 +56,7 @@ static void add_existing_edges_to_hash_maps(Mesh *mesh,
for (const int2 &edge : edges) {
OrderedEdge ordered_edge{edge[0], edge[1]};
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & ordered_edge.hash2())) {
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.add_new(ordered_edge, {&edge});
}
}
@@ -107,7 +79,7 @@ static void add_polygon_edges_to_hash_maps(Mesh *mesh,
if (vert_prev != vert) {
OrderedEdge ordered_edge{vert_prev, vert};
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & ordered_edge.hash2())) {
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.lookup_or_add(ordered_edge, {nullptr});
}
}
@@ -169,7 +141,7 @@ static void update_edge_indices_in_poly_loops(const OffsetIndices<int> polys,
if (vert_prev != vert) {
OrderedEdge ordered_edge{vert_prev, vert};
/* Double lookup: First find the map that contains the edge, then lookup the edge. */
const EdgeMap &edge_map = edge_maps[parallel_mask & ordered_edge.hash2()];
const EdgeMap &edge_map = edge_maps[parallel_mask & edge_hash_2(ordered_edge)];
edge_index = edge_map.lookup(ordered_edge).index;
}
else {
@@ -1449,7 +1449,7 @@ void BKE_mesh_legacy_bevel_weight_to_layers(Mesh *mesh)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edge Crease Conversion
/** \name Crease Conversion
* \{ */
void BKE_mesh_legacy_edge_crease_from_layers(Mesh *mesh)
@@ -1488,6 +1488,40 @@ void BKE_mesh_legacy_edge_crease_to_layers(Mesh *mesh)
}
}
void BKE_mesh_crease_layers_from_future(Mesh *mesh)
{
using namespace blender;
using namespace blender::bke;
if (const std::optional<AttributeMetaData> meta_data = mesh->attributes().lookup_meta_data(
"crease_vert"))
{
if (meta_data->domain == ATTR_DOMAIN_POINT && meta_data->data_type == CD_PROP_FLOAT) {
if (const void *data = CustomData_get_layer_named(
&mesh->vdata, CD_PROP_FLOAT, "crease_vert")) {
if (void *new_data = CustomData_add_layer(
&mesh->vdata, CD_CREASE, CD_CONSTRUCT, mesh->totvert)) {
memcpy(new_data, data, sizeof(float) * mesh->totvert);
CustomData_free_layer_named(&mesh->vdata, "crease_vert", mesh->totvert);
}
}
}
}
if (const std::optional<AttributeMetaData> meta_data = mesh->attributes().lookup_meta_data(
"crease_edge"))
{
if (meta_data->domain == ATTR_DOMAIN_EDGE && meta_data->data_type == CD_PROP_FLOAT) {
if (const void *data = CustomData_get_layer_named(
&mesh->edata, CD_PROP_FLOAT, "crease_edge")) {
if (void *new_data = CustomData_add_layer(
&mesh->edata, CD_CREASE, CD_CONSTRUCT, mesh->totedge)) {
memcpy(new_data, data, sizeof(float) * mesh->totedge);
CustomData_free_layer_named(&mesh->edata, "crease_edge", mesh->totedge);
}
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
+11 -2
View File
@@ -1978,8 +1978,11 @@ void BKE_sculpt_update_object_for_edit(
sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, is_paint_tool);
}
int *BKE_sculpt_face_sets_ensure(Mesh *mesh)
int *BKE_sculpt_face_sets_ensure(Object *ob)
{
SculptSession *ss = ob->sculpt;
Mesh *mesh = static_cast<Mesh *>(ob->data);
using namespace blender;
using namespace blender::bke;
MutableAttributeAccessor attributes = mesh->attributes_for_write();
@@ -1991,8 +1994,14 @@ int *BKE_sculpt_face_sets_ensure(Mesh *mesh)
face_sets.finish();
}
return static_cast<int *>(CustomData_get_layer_named_for_write(
int *face_sets = static_cast<int *>(CustomData_get_layer_named_for_write(
&mesh->pdata, CD_PROP_INT32, ".sculpt_face_set", mesh->totpoly));
if (ss->pbvh && ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_GRIDS)) {
BKE_pbvh_face_sets_set(ss->pbvh, face_sets);
}
return face_sets;
}
bool *BKE_sculpt_hide_poly_ensure(Mesh *mesh)
@@ -9,6 +9,7 @@
#include "DNA_node_types.h"
#include "DNA_pointcloud_types.h"
#include "BLI_binary_search.hh"
#include "BLI_fileops.hh"
#include "BLI_hash_md5.h"
#include "BLI_path_util.h"
@@ -86,22 +87,40 @@ void ModifierSimulationCache::try_discover_bake(const StringRefNull absolute_bak
new_state_at_frame->state.owner_ = this;
states_at_frames_.append(std::move(new_state_at_frame));
}
bdata_sharing_ = std::make_unique<BDataSharing>();
cache_state_ = CacheState::Baked;
}
}
bdata_sharing_ = std::make_unique<BDataSharing>();
static int64_t find_state_at_frame(
const Span<std::unique_ptr<ModifierSimulationStateAtFrame>> states, const SubFrame &frame)
{
const int64_t i = binary_search::find_predicate_begin(
states, [&](const auto &item) { return item->frame >= frame; });
if (i == states.size()) {
return -1;
}
return i;
}
cache_state_ = CacheState::Baked;
static int64_t find_state_at_frame_exact(
const Span<std::unique_ptr<ModifierSimulationStateAtFrame>> states, const SubFrame &frame)
{
const int64_t i = find_state_at_frame(states, frame);
if (i == -1) {
return -1;
}
if (states[i]->frame != frame) {
return -1;
}
return i;
}
bool ModifierSimulationCache::has_state_at_frame(const SubFrame &frame) const
{
std::lock_guard lock(states_at_frames_mutex_);
for (const auto &item : states_at_frames_) {
if (item->frame == frame) {
return true;
}
}
return false;
return find_state_at_frame_exact(states_at_frames_, frame) != -1;
}
bool ModifierSimulationCache::has_states() const
@@ -114,23 +133,26 @@ const ModifierSimulationState *ModifierSimulationCache::get_state_at_exact_frame
const SubFrame &frame) const
{
std::lock_guard lock(states_at_frames_mutex_);
for (const auto &item : states_at_frames_) {
if (item->frame == frame) {
return &item->state;
}
const int64_t i = find_state_at_frame_exact(states_at_frames_, frame);
if (i == -1) {
return nullptr;
}
return nullptr;
return &states_at_frames_[i]->state;
}
ModifierSimulationState &ModifierSimulationCache::get_state_at_frame_for_write(
const SubFrame &frame)
{
std::lock_guard lock(states_at_frames_mutex_);
for (const auto &item : states_at_frames_) {
if (item->frame == frame) {
return item->state;
}
const int64_t i = find_state_at_frame_exact(states_at_frames_, frame);
if (i != -1) {
return states_at_frames_[i]->state;
}
if (!states_at_frames_.is_empty()) {
BLI_assert(frame > states_at_frames_.last()->frame);
}
states_at_frames_.append(std::make_unique<ModifierSimulationStateAtFrame>());
states_at_frames_.last()->frame = frame;
states_at_frames_.last()->state.owner_ = this;
@@ -140,23 +162,22 @@ ModifierSimulationState &ModifierSimulationCache::get_state_at_frame_for_write(
StatesAroundFrame ModifierSimulationCache::get_states_around_frame(const SubFrame &frame) const
{
std::lock_guard lock(states_at_frames_mutex_);
StatesAroundFrame states_around_frame;
for (const auto &item : states_at_frames_) {
if (item->frame < frame) {
if (states_around_frame.prev == nullptr || item->frame > states_around_frame.prev->frame) {
states_around_frame.prev = item.get();
}
}
if (item->frame == frame) {
if (states_around_frame.current == nullptr) {
states_around_frame.current = item.get();
}
}
if (item->frame > frame) {
if (states_around_frame.next == nullptr || item->frame < states_around_frame.next->frame) {
states_around_frame.next = item.get();
}
const int64_t i = find_state_at_frame(states_at_frames_, frame);
StatesAroundFrame states_around_frame{};
if (i == -1) {
if (!states_at_frames_.is_empty() && states_at_frames_.last()->frame < frame) {
states_around_frame.prev = states_at_frames_.last().get();
}
return states_around_frame;
}
if (states_at_frames_[i]->frame == frame) {
states_around_frame.current = states_at_frames_[i].get();
}
if (i > 0) {
states_around_frame.prev = states_at_frames_[i - 1].get();
}
if (i < states_at_frames_.size() - 2) {
states_around_frame.next = states_at_frames_[i + 1].get();
}
return states_around_frame;
}
-26
View File
@@ -819,20 +819,6 @@ void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated)
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, sound_cfra, &pitch, animated);
}
void BKE_sound_set_scene_sound_pitch_at_frame(void *handle, int frame, float pitch, char animated)
{
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, frame, &pitch, animated);
}
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle,
int frame_start,
int frame_end,
float pitch)
{
AUD_SequenceEntry_setConstantRangeAnimationData(
handle, AUD_AP_PITCH, frame_start, frame_end, &pitch);
}
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated)
{
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PANNING, sound_cfra, &pan, animated);
@@ -1384,18 +1370,6 @@ void BKE_sound_set_scene_sound_pitch(void *UNUSED(handle),
char UNUSED(animated))
{
}
void BKE_sound_set_scene_sound_pitch_at_frame(void *UNUSED(handle),
int UNUSED(frame),
float UNUSED(pitch),
char UNUSED(animated))
{
}
void BKE_sound_set_scene_sound_pitch_constant_range(void *UNUSED(handle),
int UNUSED(frame_start),
int UNUSED(frame_end),
float UNUSED(pitch))
{
}
float BKE_sound_get_length(struct Main *UNUSED(bmain), bSound *UNUSED(sound))
{
return 0;
+17 -8
View File
@@ -82,7 +82,10 @@ static int append_avi(void *context_v,
int recty,
const char *suffix,
ReportList *reports);
static void filepath_avi(char *string, const RenderData *rd, bool preview, const char *suffix);
static void filepath_avi(char filepath[FILE_MAX],
const RenderData *rd,
bool preview,
const char *suffix);
static void *context_create_avi(void);
static void context_free_avi(void *context_v);
#endif /* WITH_AVI */
@@ -140,7 +143,10 @@ bMovieHandle *BKE_movie_handle_get(const char imtype)
#ifdef WITH_AVI
static void filepath_avi(char *filepath, const RenderData *rd, bool preview, const char *suffix)
static void filepath_avi(char filepath[FILE_MAX],
const RenderData *rd,
bool preview,
const char *suffix)
{
int sfra, efra;
@@ -157,7 +163,7 @@ static void filepath_avi(char *filepath, const RenderData *rd, bool preview, con
efra = rd->efra;
}
strcpy(filepath, rd->pic);
BLI_strncpy(filepath, rd->pic, FILE_MAX);
BLI_path_abs(filepath, BKE_main_blendfile_path_from_global());
BLI_file_ensure_parent_dir_exists(filepath);
@@ -187,13 +193,13 @@ static int start_avi(void *context_v,
const char *suffix)
{
int x, y;
char name[256];
char filepath[FILE_MAX];
AviFormat format;
int quality;
double framerate;
AviMovie *avi = context_v;
filepath_avi(name, rd, preview, suffix);
filepath_avi(filepath, rd, preview, suffix);
x = rectx;
y = recty;
@@ -208,7 +214,7 @@ static int start_avi(void *context_v,
format = AVI_FORMAT_MJPEG;
}
if (AVI_open_compress(name, avi, 1, format) != AVI_ERROR_NONE) {
if (AVI_open_compress(filepath, avi, 1, format) != AVI_ERROR_NONE) {
BKE_report(reports, RPT_ERROR, "Cannot open or start AVI movie file");
return 0;
}
@@ -221,7 +227,7 @@ static int start_avi(void *context_v,
avi->interlace = 0;
avi->odd_fields = 0;
printf("Created avi: %s\n", name);
printf("Created avi: %s\n", filepath);
return 1;
}
@@ -297,7 +303,10 @@ static void context_free_avi(void *context_v)
#endif /* WITH_AVI */
void BKE_movie_filepath_get(char *filepath, const RenderData *rd, bool preview, const char *suffix)
void BKE_movie_filepath_get(char filepath[/*FILE_MAX*/ 1024],
const RenderData *rd,
bool preview,
const char *suffix)
{
bMovieHandle *mh = BKE_movie_handle_get(rd->im_format.imtype);
if (mh && mh->get_movie_path) {
@@ -1426,7 +1426,7 @@ static void ffmpeg_filepath_get(FFMpegContext *context,
BLI_path_suffix(string, FILE_MAX, suffix, "");
}
void BKE_ffmpeg_filepath_get(char *filepath,
void BKE_ffmpeg_filepath_get(char filepath[/*FILE_MAX*/ 1024],
const RenderData *rd,
bool preview,
const char *suffix)
@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bli
*/
#include <algorithm>
#include "BLI_utildefines.h"
namespace blender::binary_search {
/**
* Find the index of the first element where the predicate is true. The predicate must also be
* true for all following elements. If the predicate is false for all elements, the size of the
* range is returned.
*/
template<typename Iterator, typename Predicate>
int64_t find_predicate_begin(Iterator begin, Iterator end, Predicate &&predicate)
{
return std::lower_bound(begin,
end,
nullptr,
[&](const auto &value, void * /*dummy*/) { return !predicate(value); }) -
begin;
}
template<typename Range, typename Predicate>
int64_t find_predicate_begin(const Range &range, Predicate &&predicate)
{
return find_predicate_begin(range.begin(), range.end(), predicate);
}
} // namespace blender::binary_search
@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_assert.h"
#include "BLI_math_vector_types.hh"
namespace blender {
/**
* A version of `int2` used as a key for hash-maps, agnostic of the arbitrary order of the two
* vertices in a mesh edge.
*/
struct OrderedEdge {
int v_low;
int v_high;
OrderedEdge(const int v1, const int v2)
{
if (v1 < v2) {
v_low = v1;
v_high = v2;
}
else {
v_low = v2;
v_high = v1;
}
}
OrderedEdge(const int2 edge) : OrderedEdge(edge[0], edge[1]) {}
OrderedEdge(const uint v1, const uint v2) : OrderedEdge(int(v1), int(v2)) {}
uint64_t hash() const
{
return (this->v_low << 8) ^ this->v_high;
}
friend bool operator==(const OrderedEdge &e1, const OrderedEdge &e2)
{
BLI_assert(e1.v_low < e1.v_high);
BLI_assert(e2.v_low < e2.v_high);
return e1.v_low == e2.v_low && e1.v_high == e2.v_high;
}
};
} // namespace blender
+2
View File
@@ -182,6 +182,7 @@ set(SRC
BLI_assert.h
BLI_astar.h
BLI_atomic_disjoint_set.hh
BLI_binary_search.hh
BLI_bit_group_vector.hh
BLI_bit_ref.hh
BLI_bit_span.hh
@@ -318,6 +319,7 @@ set(SRC
BLI_noise.h
BLI_noise.hh
BLI_offset_indices.hh
BLI_ordered_edge.hh
BLI_parameter_pack_utils.hh
BLI_path_util.h
BLI_polyfill_2d.h
+1 -1
View File
@@ -116,7 +116,7 @@ int BLI_path_sequence_decode(const char *path,
if (head) {
/* Name_end points to last character of head,
* make it +1 so null-terminator is nicely placed. */
BLI_strncpy(head, path, name_end + 1);
BLI_strncpy(head, path, MIN2(head_maxncpy, name_end + 1));
}
if (r_digits_len) {
*r_digits_len = 0;
+2
View File
@@ -181,6 +181,8 @@ void BLO_blendfiledata_free(BlendFileData *bfd);
typedef struct BLODataBlockInfo {
char name[64]; /* MAX_NAME */
struct AssetMetaData *asset_data;
/** Ownership over #asset_data above can be "stolen out" of this struct, for more permanent
* storage. In that case, set this to false to avoid double freeing of the stolen data. */
bool free_asset_data;
/* Optimization: Tag data-blocks for which we know there is no preview.
* Knowing this can be used to skip the (potentially expensive) preview loading process. If this
@@ -82,7 +82,6 @@
#include "SEQ_channels.h"
#include "SEQ_effects.h"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@@ -697,25 +696,6 @@ static bool seq_speed_factor_set(Sequence *seq, void *user_data)
return true;
}
static bool do_versions_sequencer_init_retiming_tool_data(Sequence *seq, void *user_data)
{
const Scene *scene = static_cast<const Scene *>(user_data);
if (seq->speed_factor == 1 || !SEQ_retiming_is_allowed(seq)) {
return true;
}
const int content_length = SEQ_time_strip_length_get(scene, seq);
SEQ_retiming_data_ensure(seq);
SeqRetimingHandle *handle = &seq->retiming_handles[seq->retiming_handle_num - 1];
handle->strip_frame_index = round_fl_to_int(content_length / seq->speed_factor);
seq->speed_factor = 1.0f;
return true;
}
static void version_geometry_nodes_replace_transfer_attribute_node(bNodeTree *ntree)
{
using namespace blender;
@@ -1362,18 +1342,6 @@ void do_versions_after_linking_300(FileData * /*fd*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_ATLEAST(bmain, 306, 6)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Editing *ed = SEQ_editing_get(scene);
if (ed == nullptr) {
continue;
}
SEQ_for_each_callback(
&scene->ed->seqbase, do_versions_sequencer_init_retiming_tool_data, scene);
}
}
/**
* Versioning code until next subversion bump goes here.
*
@@ -1800,7 +1768,6 @@ static bool version_seq_fix_broken_sound_strips(Sequence *seq, void * /*user_dat
}
seq->speed_factor = 1.0f;
SEQ_retiming_data_clear(seq);
seq->startofs = 0.0f;
return true;
}
@@ -43,6 +43,7 @@ static void version_mesh_legacy_to_struct_of_array_format(Mesh &mesh)
BKE_mesh_legacy_convert_polys_to_offsets(&mesh);
BKE_mesh_legacy_convert_edges_to_generic(&mesh);
BKE_mesh_bevel_weight_layers_from_future(&mesh);
BKE_mesh_crease_layers_from_future(&mesh);
}
static void version_motion_tracking_legacy_camera_object(MovieClip &movieclip)
@@ -20,6 +20,7 @@
#include "BLI_utildefines.h"
#include "BKE_action.h"
#include "BKE_collection.h"
#include "RNA_prototypes.h"
@@ -182,6 +183,14 @@ void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
flag |= ID_RECALC_NTREE_OUTPUT;
}
}
else {
/* Collection content might have changed (children collection might have been added or
* removed from the graph based on their inclusion and visibility flags). */
const ID_Type id_type = GS(id_node->id_cow->name);
if (id_type == ID_GR) {
BKE_collection_object_cache_free(reinterpret_cast<Collection *>(id_node->id_cow));
}
}
/* Restore recalc flags from original ID, which could possibly contain recalc flags set by
* an operator and then were carried on by the undo system. */
flag |= id_orig->recalc;
@@ -914,9 +914,8 @@ void DepsgraphNodeBuilder::build_object_modifiers(Object *object)
return;
}
if (modifier_node->flag & DEPSOP_FLAG_USER_MODIFIED) {
if (nmd->simulation_cache &&
nmd->simulation_cache->cache_state() == bke::sim::CacheState::Valid) {
nmd->simulation_cache->invalidate();
if (nmd->simulation_cache->ptr->cache_state() == bke::sim::CacheState::Valid) {
nmd->simulation_cache->ptr->invalidate();
}
}
};
+1 -1
View File
@@ -265,7 +265,7 @@ static bool need_extra_redraw_after_scrubbing_ends(bContext *C)
return true;
}
Scene *scene = CTX_data_scene(C);
if (scene->eevee.flag & SCE_EEVEE_TAA_REPROJECTION) {
if (scene->eevee.taa_samples != 1) {
return true;
}
wmWindowManager *wm = CTX_wm_manager(C);
@@ -872,7 +872,6 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.sculpt.mask_by_color
ops.sculpt.mesh_filter
ops.sequencer.blade
ops.sequencer.retime
ops.transform.bone_envelope
ops.transform.bone_size
ops.transform.edge_slide
@@ -346,72 +346,19 @@ static bool *gpencil_vgroup_bone_deformed_map_get(Object *ob, const int defbase_
* for points in vertex groups deformed by bones.
* The logic is copied from `editors/sculpt_paint/paint_vertex.cc`. */
/**
* Normalize weights of a stroke point deformed by bones.
*/
static void do_weight_paint_normalize_all(MDeformVert *dvert,
const int defbase_tot,
const bool *vgroup_bone_deformed)
{
float sum = 0.0f, fac;
uint tot = 0;
MDeformWeight *dw;
dw = dvert->dw;
for (int i = dvert->totweight; i != 0; i--, dw++) {
if (dw->def_nr < defbase_tot && vgroup_bone_deformed[dw->def_nr]) {
tot++;
sum += dw->weight;
}
}
if ((tot == 0) || (sum == 1.0f)) {
return;
}
if (sum != 0.0f) {
fac = 1.0f / sum;
dw = dvert->dw;
for (int i = dvert->totweight; i != 0; i--, dw++) {
if (dw->def_nr < defbase_tot && vgroup_bone_deformed[dw->def_nr]) {
dw->weight *= fac;
}
}
}
else {
fac = 1.0f / tot;
dw = dvert->dw;
for (int i = dvert->totweight; i != 0; i--, dw++) {
if (dw->def_nr < defbase_tot && vgroup_bone_deformed[dw->def_nr]) {
dw->weight = fac;
}
}
}
}
/**
* A version of #do_weight_paint_normalize_all that only changes unlocked weights.
*/
static bool do_weight_paint_normalize_all_unlocked(MDeformVert *dvert,
const int defbase_tot,
const bool *vgroup_bone_deformed,
const bool *vgroup_locked,
const bool lock_active_vgroup,
const int active_vgroup)
static bool do_weight_paint_normalize(MDeformVert *dvert,
const int defbase_tot,
const bool *vgroup_bone_deformed,
const bool *vgroup_locked,
const bool lock_active_vgroup,
const int active_vgroup)
{
float sum = 0.0f, fac;
float sum_unlock = 0.0f;
float sum_lock = 0.0f;
uint tot = 0;
uint lock_tot = 0, unlock_tot = 0;
MDeformWeight *dw;
if (vgroup_locked == NULL) {
do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_bone_deformed);
return true;
}
if (dvert->totweight <= 1) {
return true;
}
@@ -423,10 +370,11 @@ static bool do_weight_paint_normalize_all_unlocked(MDeformVert *dvert,
sum += dw->weight;
if (vgroup_locked[dw->def_nr] || (lock_active_vgroup && active_vgroup == dw->def_nr)) {
lock_tot++;
sum_lock += dw->weight;
}
else {
tot++;
unlock_tot++;
sum_unlock += dw->weight;
}
}
@@ -436,8 +384,10 @@ static bool do_weight_paint_normalize_all_unlocked(MDeformVert *dvert,
return true;
}
if (tot == 0) {
return false;
if (unlock_tot == 0) {
/* There are no unlocked vertex groups to normalize. We don't need
* a second pass when there is only one locked group (the active group). */
return (lock_tot == 1);
}
if (sum_lock >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
@@ -445,7 +395,6 @@ static bool do_weight_paint_normalize_all_unlocked(MDeformVert *dvert,
* zero out what we can and return false. */
dw = dvert->dw;
for (int i = dvert->totweight; i != 0; i--, dw++) {
*dw = dvert->dw[i];
if (dw->def_nr < defbase_tot && vgroup_bone_deformed[dw->def_nr]) {
if ((vgroup_locked[dw->def_nr] == false) &&
!(lock_active_vgroup && active_vgroup == dw->def_nr)) {
@@ -472,7 +421,7 @@ static bool do_weight_paint_normalize_all_unlocked(MDeformVert *dvert,
}
}
else {
fac = (1.0f - sum_lock) / tot;
fac = (1.0f - sum_lock) / unlock_tot;
CLAMP(fac, 0.0f, 1.0f);
dw = dvert->dw;
@@ -491,18 +440,18 @@ static bool do_weight_paint_normalize_all_unlocked(MDeformVert *dvert,
}
/**
* A version of #do_weight_paint_normalize_all that only changes unlocked weights
* A version of #do_weight_paint_normalize that only changes unlocked weights
* and does a second pass without the active vertex group locked when the first pass fails.
*/
static void do_weight_paint_normalize_all_try(MDeformVert *dvert, tGP_BrushWeightpaintData *gso)
static void do_weight_paint_normalize_try(MDeformVert *dvert, tGP_BrushWeightpaintData *gso)
{
/* First pass with both active and explicitly locked vertex groups restricted from change. */
bool succes = do_weight_paint_normalize_all_unlocked(
bool succes = do_weight_paint_normalize(
dvert, gso->vgroup_tot, gso->vgroup_bone_deformed, gso->vgroup_locked, true, gso->vrgroup);
if (!succes) {
/* Second pass with active vertex group unlocked. */
do_weight_paint_normalize_all_unlocked(
do_weight_paint_normalize(
dvert, gso->vgroup_tot, gso->vgroup_bone_deformed, gso->vgroup_locked, false, -1);
}
}
@@ -584,7 +533,7 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso,
/* Perform auto-normalize. */
if (gso->auto_normalize) {
do_weight_paint_normalize_all_try(dvert, gso);
do_weight_paint_normalize_try(dvert, gso);
}
return true;
@@ -614,7 +563,7 @@ static bool brush_average_apply(tGP_BrushWeightpaintData *gso,
/* Perform auto-normalize. */
if (gso->auto_normalize) {
do_weight_paint_normalize_all_try(dvert, gso);
do_weight_paint_normalize_try(dvert, gso);
}
return true;
@@ -666,7 +615,7 @@ static bool brush_blur_apply(tGP_BrushWeightpaintData *gso,
/* Perform auto-normalize. */
if (gso->auto_normalize) {
do_weight_paint_normalize_all_try(dvert, gso);
do_weight_paint_normalize_try(dvert, gso);
}
return true;
@@ -748,7 +697,7 @@ static bool brush_smear_apply(tGP_BrushWeightpaintData *gso,
/* Perform auto-normalize. */
if (gso->auto_normalize) {
do_weight_paint_normalize_all_try(dvert, gso);
do_weight_paint_normalize_try(dvert, gso);
}
return true;
@@ -127,10 +127,13 @@ void ED_file_indexer_entries_clear(FileIndexerEntries *indexer_entries);
* Adds all entries from the given `datablock_infos` to the `indexer_entries`.
* The datablock_infos must only contain data for a single IDType. The specific IDType must be
* passed in the `idcode` parameter.
*
* \note This can "steal" data contained in \a datablock_infos, to avoid expensive copies, which is
* supported by the #BLODataBlockInfo type.
*/
void ED_file_indexer_entries_extend_from_datablock_infos(
FileIndexerEntries *indexer_entries,
const LinkNode * /*BLODataBlockInfo*/ datablock_infos,
struct LinkNode * /*BLODataBlockInfo*/ datablock_infos,
int idcode);
#ifdef __cplusplus
@@ -40,6 +40,7 @@
#include "BKE_action.h"
#include "BKE_blendfile.h"
#include "BKE_cachefile.h"
#include "BKE_colorband.h"
#include "BKE_colortools.h"
#include "BKE_constraint.h"
@@ -65,6 +66,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "ED_fileselect.h"
#include "ED_object.h"
@@ -6804,8 +6806,16 @@ void uiTemplateCacheFileProcedural(uiLayout *layout, const bContext *C, PointerR
Scene *scene = CTX_data_scene(C);
const bool engine_supports_procedural = RE_engine_supports_alembic_procedural(engine_type,
scene);
CacheFile *cache_file = static_cast<CacheFile *>(fileptr->data);
CacheFile *cache_file_eval = reinterpret_cast<CacheFile *>(
DEG_get_evaluated_id(CTX_data_depsgraph_pointer(C), &cache_file->id));
bool is_alembic = cache_file_eval->type == CACHEFILE_TYPE_ALEMBIC;
if (!engine_supports_procedural) {
if (!is_alembic) {
row = uiLayoutRow(layout, false);
uiItemL(row, TIP_("Only Alembic Procedurals supported"), ICON_INFO);
}
else if (!engine_supports_procedural) {
row = uiLayoutRow(layout, false);
/* For Cycles, verify that experimental features are enabled. */
if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) {
@@ -6822,7 +6832,7 @@ void uiTemplateCacheFileProcedural(uiLayout *layout, const bContext *C, PointerR
}
row = uiLayoutRow(layout, false);
uiLayoutSetActive(row, engine_supports_procedural);
uiLayoutSetActive(row, is_alembic && engine_supports_procedural);
uiItemR(row, fileptr, "use_render_procedural", 0, nullptr, ICON_NONE);
const bool use_render_procedural = RNA_boolean_get(fileptr, "use_render_procedural");
+7 -6
View File
@@ -3728,12 +3728,13 @@ static bool knife_snap_angle_relative(KnifeTool_OpData *kcd)
return false;
}
/* Re-calculate current ray in object space. */
knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, curr_origin, curr_origin_ofs);
sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin);
normalize_v3_v3(curr_ray_normal, curr_ray);
/* Use normal global direction. */
float no_global[3];
copy_v3_v3(no_global, fprev->no);
mul_transposed_mat3_m4_v3(kcd->curr.ob->world_to_object, no_global);
normalize_v3(no_global);
plane_from_point_normal_v3(plane, kcd->prev.cage, fprev->no);
plane_from_point_normal_v3(plane, kcd->prev.cage, no_global);
if (isect_ray_plane_v3(curr_origin, curr_ray_normal, plane, &lambda, false)) {
madd_v3_v3v3fl(ray_hit, curr_origin, curr_ray_normal, lambda);
@@ -3755,7 +3756,7 @@ static bool knife_snap_angle_relative(KnifeTool_OpData *kcd)
/* Maybe check for vectors being zero here? */
sub_v3_v3v3(v1, ray_hit, kcd->prev.cage);
copy_v3_v3(v2, refv);
kcd->angle = snap_v3_angle_plane(rotated_vec, v1, v2, fprev->no, snap_step);
kcd->angle = snap_v3_angle_plane(rotated_vec, v1, v2, no_global, snap_step);
add_v3_v3(rotated_vec, kcd->prev.cage);
knife_project_v2(kcd, rotated_vec, kcd->curr.mval);
@@ -49,6 +49,10 @@
#include "object_intern.h"
#include "WM_api.h"
#include "UI_interface.h"
namespace blender::ed::object::bake_simulation {
static bool calculate_to_frame_poll(bContext *C)
@@ -89,9 +93,7 @@ static void calculate_simulation_job_startjob(void *customdata,
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->simulation_cache != nullptr) {
nmd->simulation_cache->reset();
}
nmd->simulation_cache->ptr->reset();
}
}
objects_to_calc.append(object);
@@ -251,13 +253,7 @@ static void bake_simulation_job_startjob(void *customdata,
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->simulation_cache != nullptr) {
nmd->simulation_cache->reset();
}
if (StringRef(nmd->simulation_bake_directory).is_empty()) {
nmd->simulation_bake_directory = BLI_strdup(
bke::sim::get_default_modifier_bake_directory(*job.bmain, *object, *md).c_str());
}
nmd->simulation_cache->ptr->reset();
char absolute_bake_dir[FILE_MAX];
STRNCPY(absolute_bake_dir, nmd->simulation_bake_directory);
BLI_path_abs(absolute_bake_dir, base_path);
@@ -299,7 +295,7 @@ static void bake_simulation_job_startjob(void *customdata,
if (nmd.simulation_cache == nullptr) {
continue;
}
ModifierSimulationCache &sim_cache = *nmd.simulation_cache;
ModifierSimulationCache &sim_cache = *nmd.simulation_cache->ptr;
const ModifierSimulationState *sim_state = sim_cache.get_state_at_exact_frame(frame);
if (sim_state == nullptr || sim_state->zone_states_.is_empty()) {
continue;
@@ -343,7 +339,7 @@ static void bake_simulation_job_startjob(void *customdata,
NodesModifierData &nmd = *modifier_bake_data.nmd;
if (nmd.simulation_cache) {
/* Tag the caches as being baked so that they are not changed anymore. */
nmd.simulation_cache->cache_state_ = CacheState::Baked;
nmd.simulation_cache->ptr->cache_state_ = CacheState::Baked;
}
}
DEG_id_tag_update(&object_bake_data.object->id, ID_RECALC_GEOMETRY);
@@ -364,7 +360,7 @@ static void bake_simulation_job_endjob(void *customdata)
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr);
}
static int bake_simulation_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
static int bake_simulation_execute(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
Scene *scene = CTX_data_scene(C);
@@ -407,6 +403,160 @@ static int bake_simulation_invoke(bContext *C, wmOperator *op, const wmEvent * /
return OPERATOR_RUNNING_MODAL;
}
struct PathStringHash {
uint64_t operator()(const StringRef s) const
{
/* Normalize the paths so we can compare them. */
DynamicStackBuffer<256> norm_buf(s.size() + 1, 8);
memcpy(norm_buf.buffer(), s.data(), s.size() + 1);
char *norm = static_cast<char *>(norm_buf.buffer());
BLI_path_slash_native(norm);
/* Strip ending slash. */
BLI_path_slash_rstrip(norm);
BLI_path_normalize(norm);
return get_default_hash(norm);
}
};
struct PathStringEquality {
bool operator()(const StringRef a, const StringRef b) const
{
return BLI_path_cmp_normalized(a.data(), b.data()) == 0;
}
};
static bool bake_directory_has_data(const StringRefNull absolute_bake_dir)
{
char meta_dir[FILE_MAX];
BLI_path_join(meta_dir, sizeof(meta_dir), absolute_bake_dir.c_str(), "meta");
char bdata_dir[FILE_MAX];
BLI_path_join(bdata_dir, sizeof(bdata_dir), absolute_bake_dir.c_str(), "bdata");
if (!BLI_is_dir(meta_dir) || !BLI_is_dir(bdata_dir)) {
return false;
}
return true;
}
static void bake_simulation_validate_paths(bContext *C,
wmOperator *op,
const Span<Object *> objects)
{
Main *bmain = CTX_data_main(C);
for (Object *object : objects) {
if (!BKE_id_is_editable(bmain, &object->id)) {
continue;
}
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type != eModifierType_Nodes) {
continue;
}
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (StringRef(nmd->simulation_bake_directory).is_empty()) {
BKE_reportf(op->reports,
RPT_INFO,
"Bake directory of object %s, modifier %s is empty, setting default path",
object->id.name + 2,
md->name);
nmd->simulation_bake_directory = BLI_strdup(
bke::sim::get_default_modifier_bake_directory(*bmain, *object, *md).c_str());
}
}
}
}
/* Map for counting path references. */
using PathUsersMap = Map<std::string,
int,
default_inline_buffer_capacity(sizeof(std::string)),
DefaultProbingStrategy,
PathStringHash,
PathStringEquality>;
static PathUsersMap bake_simulation_get_path_users(bContext *C, const Span<Object *> objects)
{
Main *bmain = CTX_data_main(C);
PathUsersMap path_users;
for (const Object *object : objects) {
const char *base_path = ID_BLEND_PATH(bmain, &object->id);
LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
if (md->type != eModifierType_Nodes) {
continue;
}
const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
if (StringRef(nmd->simulation_bake_directory).is_empty()) {
continue;
}
char absolute_bake_dir[FILE_MAX];
STRNCPY(absolute_bake_dir, nmd->simulation_bake_directory);
BLI_path_abs(absolute_bake_dir, base_path);
path_users.add_or_modify(
absolute_bake_dir, [](int *value) { *value = 1; }, [](int *value) { ++(*value); });
}
}
return path_users;
}
static int bake_simulation_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
Vector<Object *> objects;
if (RNA_boolean_get(op->ptr, "selected")) {
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
objects.append(object);
}
CTX_DATA_END;
}
else {
if (Object *object = CTX_data_active_object(C)) {
objects.append(object);
}
}
/* Set empty paths to default. */
bake_simulation_validate_paths(C, op, objects);
PathUsersMap path_users = bake_simulation_get_path_users(C, objects);
bool has_path_conflict = false;
bool has_existing_bake_data = false;
for (const auto &item : path_users.items()) {
/* Check if multiple caches are writing to the same bake directory. */
if (item.value > 1) {
BKE_reportf(op->reports,
RPT_ERROR,
"Path conflict: %d caches set to path %s",
item.value,
item.key.data());
has_path_conflict = true;
}
/* Check if path exists and contains bake data already. */
if (bake_directory_has_data(item.key.data())) {
has_existing_bake_data = true;
}
}
if (has_path_conflict) {
UI_popup_menu_reports(C, op->reports);
return OPERATOR_CANCELLED;
}
if (has_existing_bake_data) {
return WM_operator_confirm_message(C, op, "Overwrite existing bake data");
}
return bake_simulation_execute(C, op);
}
static int bake_simulation_modal(bContext *C, wmOperator * /*op*/, const wmEvent * /*event*/)
{
if (!WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_BAKE_SIMULATION_NODES)) {
@@ -441,9 +591,7 @@ static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (nmd->simulation_cache != nullptr) {
nmd->simulation_cache->reset();
}
nmd->simulation_cache->ptr->reset();
if (StringRef(nmd->simulation_bake_directory).is_empty()) {
continue;
}
@@ -497,6 +645,7 @@ void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot)
ot->description = "Bake simulations in geometry nodes modifiers";
ot->idname = __func__;
ot->exec = bake_simulation_execute;
ot->invoke = bake_simulation_invoke;
ot->modal = bake_simulation_modal;
ot->poll = bake_simulation_poll;
@@ -722,7 +722,7 @@ static const EnumPropertyItem WT_vertex_group_select_item[] = {
const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *C,
PointerRNA * /*ptr*/,
PropertyRNA *prop,
PropertyRNA * /*prop*/,
bool *r_free,
const uint selection_mask)
{
@@ -760,12 +760,6 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *
RNA_enum_items_add_value(&item, &totitem, WT_vertex_group_select_item, WT_VGROUP_ALL);
}
/* Set `Deform Bone` as default selection if armature is present. */
if (ob) {
RNA_def_property_enum_default(
prop, BKE_modifiers_is_deformed_by_armature(ob) ? WT_VGROUP_BONE_DEFORM : WT_VGROUP_ALL);
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
@@ -2907,6 +2901,12 @@ void OBJECT_OT_vertex_group_normalize(wmOperatorType *ot)
static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
/* If armature is present, default to `Deform Bones` otherwise `All Groups`. */
RNA_enum_set(op->ptr,
"group_select_mode",
BKE_modifiers_is_deformed_by_armature(ob) ? WT_VGROUP_BONE_DEFORM : WT_VGROUP_ALL);
bool lock_active = RNA_boolean_get(op->ptr, "lock_active");
eVGroupSelect subset_type = static_cast<eVGroupSelect>(
RNA_enum_get(op->ptr, "group_select_mode"));
@@ -6640,7 +6640,8 @@ static void default_paint_slot_color_get(int layer_type, Material *ma, float col
bNode *in_node = nullptr;
if (ma && ma->nodetree) {
ma->nodetree->ensure_topology_cache();
const blender::Span<bNode *> nodes = ma->nodetree->nodes_by_type("ShaderNodeBsdfPrincipled");
const blender::Span<bNode *> nodes = ma->nodetree->nodes_by_type(
"ShaderNodeBsdfPrincipled");
in_node = nodes.is_empty() ? nullptr : nodes.first();
}
if (!in_node) {
@@ -784,7 +784,7 @@ static void sculpt_gesture_init_face_set_properties(SculptGestureContext *sgcont
sgcontext->operation = reinterpret_cast<SculptGestureOperation *>(
MEM_cnew<SculptGestureFaceSetOperation>(__func__));
sgcontext->ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
sgcontext->ss->face_sets = BKE_sculpt_face_sets_ensure(sgcontext->vc.obact);
SculptGestureFaceSetOperation *face_set_operation = (SculptGestureFaceSetOperation *)
sgcontext->operation;
@@ -1404,8 +1404,7 @@ static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgconte
{
Object *object = sgcontext->vc.obact;
SculptSession *ss = object->sculpt;
Mesh *mesh = (Mesh *)object->data;
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
ss->face_sets = BKE_sculpt_face_sets_ensure(object);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
sculpt_gesture_trim_calculate_depth(sgcontext);
@@ -389,8 +389,7 @@ static int palette_color_add_exec(bContext *C, wmOperator * /*op*/)
PAINT_MODE_TEXTURE_3D,
PAINT_MODE_TEXTURE_2D,
PAINT_MODE_VERTEX,
PAINT_MODE_SCULPT))
{
PAINT_MODE_SCULPT)) {
copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush));
color->value = 0.0;
}
@@ -5889,8 +5889,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent
BKE_sculpt_mask_layers_ensure(CTX_data_depsgraph_pointer(C), CTX_data_main(C), ob, mmd);
}
if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) {
Mesh *mesh = BKE_object_get_original_mesh(ob);
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
ss->face_sets = BKE_sculpt_face_sets_ensure(ob);
}
stroke = paint_stroke_new(C,
@@ -2199,7 +2199,7 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
Mesh *mesh = static_cast<Mesh *>(ob->data);
if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
ss->face_sets = BKE_sculpt_face_sets_ensure(ob);
}
/* Face Set operations are not supported in dyntopo. */
@@ -344,8 +344,8 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
ss->face_sets = BKE_sculpt_face_sets_ensure(ob);
Mesh *mesh = static_cast<Mesh *>(ob->data);
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
@@ -651,7 +651,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
const float threshold = RNA_float_get(op->ptr, "threshold");
Mesh *mesh = static_cast<Mesh *>(ob->data);
ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
ss->face_sets = BKE_sculpt_face_sets_ensure(ob);
const bke::AttributeAccessor attributes = mesh->attributes();
switch (mode) {
@@ -1410,7 +1410,7 @@ static bool sculpt_face_set_edit_init(bContext *C, wmOperator *op)
return false;
}
ss->face_sets = BKE_sculpt_face_sets_ensure(BKE_mesh_from_object(ob));
ss->face_sets = BKE_sculpt_face_sets_ensure(ob);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
return true;
@@ -618,10 +618,9 @@ static bool sculpt_undo_restore_face_sets(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
BKE_view_layer_synced_ensure(scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
Mesh *me = BKE_object_get_original_mesh(ob);
SculptSession *ss = ob->sculpt;
ss->face_sets = BKE_sculpt_face_sets_ensure(me);
ss->face_sets = BKE_sculpt_face_sets_ensure(ob);
BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets);
bool modified = false;
@@ -774,7 +774,7 @@ void timeline_draw_cache(const SpaceAction *saction, const Object *ob, const Sce
continue;
}
timeline_cache_draw_simulation_nodes(
*scene, *nmd->simulation_cache, y_offset, cache_draw_height, pos_id);
*scene, *nmd->simulation_cache->ptr, y_offset, cache_draw_height, pos_id);
y_offset += cache_draw_height;
}
}
@@ -40,12 +40,14 @@ constexpr FileIndexerType default_indexer()
}
static FileIndexerEntry *file_indexer_entry_create_from_datablock_info(
const BLODataBlockInfo *datablock_info, const int idcode)
BLODataBlockInfo *datablock_info, const int idcode)
{
FileIndexerEntry *entry = static_cast<FileIndexerEntry *>(
MEM_mallocN(sizeof(FileIndexerEntry), __func__));
entry->datablock_info = *datablock_info;
entry->idcode = idcode;
/* Shallow copy data-block info and mark original as having its asset data ownership stolen. */
entry->datablock_info = *datablock_info;
datablock_info->free_asset_data = false;
return entry;
}
@@ -55,11 +57,11 @@ extern "C" {
void ED_file_indexer_entries_extend_from_datablock_infos(
FileIndexerEntries *indexer_entries,
const LinkNode * /*BLODataBlockInfo*/ datablock_infos,
LinkNode * /*BLODataBlockInfo*/ datablock_infos,
const int idcode)
{
for (const LinkNode *ln = datablock_infos; ln; ln = ln->next) {
const BLODataBlockInfo *datablock_info = static_cast<const BLODataBlockInfo *>(ln->link);
for (LinkNode *ln = datablock_infos; ln; ln = ln->next) {
BLODataBlockInfo *datablock_info = static_cast<BLODataBlockInfo *>(ln->link);
FileIndexerEntry *file_indexer_entry =
blender::ed::file::indexer::file_indexer_entry_create_from_datablock_info(datablock_info,
idcode);
@@ -245,6 +245,13 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
{
bNode *new_node = bke::node_copy_with_mapping(
&tree, node, LIB_ID_COPY_DEFAULT, true, socket_map);
/* Reset socket shape in case a node is copied to a different tree type. */
LISTBASE_FOREACH (bNodeSocket *, socket, &new_node->inputs) {
socket->display_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
}
LISTBASE_FOREACH (bNodeSocket *, socket, &new_node->outputs) {
socket->display_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
}
node_map.add_new(&node, new_node);
}
else {
@@ -269,6 +276,8 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
for (bNode *new_node : node_map.values()) {
nodeSetSelected(new_node, true);
new_node->flag &= ~NODE_ACTIVE;
/* The parent pointer must be redirected to new node. */
if (new_node->parent) {
if (node_map.contains(new_node->parent)) {
@@ -3327,15 +3327,13 @@ static bool realtime_compositor_is_in_use(const bContext &context)
return false;
}
const Main *main = CTX_data_main(&context);
LISTBASE_FOREACH (const bScreen *, screen, &main->screens) {
wmWindowManager *wm = CTX_wm_manager(&context);
LISTBASE_FOREACH (const wmWindow *, win, &wm->windows) {
const bScreen *screen = WM_window_get_active_screen(win);
LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (const SpaceLink *, space, &area->spacedata) {
if (space->spacetype != SPACE_VIEW3D) {
continue;
}
const View3D &view_3d = *reinterpret_cast<const View3D *>(space);
const SpaceLink &space = *static_cast<const SpaceLink *>(area->spacedata.first);
if (space.spacetype == SPACE_VIEW3D) {
const View3D &view_3d = reinterpret_cast<const View3D &>(space);
if (view_3d.shading.use_compositor == V3D_SHADING_USE_COMPOSITOR_DISABLED) {
continue;
+16 -10
View File
@@ -913,16 +913,19 @@ static void node_group_make_insert_selected(const bContext &C,
links_to_remove.add(link);
continue;
}
if (link->fromnode == gnode) {
links_to_remove.add(link);
continue;
}
if (nodes_to_move.contains(link->fromnode)) {
internal_links_to_move.add(link);
continue;
}
else {
InputSocketInfo &info = input_links.lookup_or_add_default(link->fromsock);
info.from_node = link->fromnode;
info.links.append(link);
if (!info.interface_socket) {
info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock);
}
InputSocketInfo &info = input_links.lookup_or_add_default(link->fromsock);
info.from_node = link->fromnode;
info.links.append(link);
if (!info.interface_socket) {
info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock);
}
}
}
@@ -932,12 +935,15 @@ static void node_group_make_insert_selected(const bContext &C,
links_to_remove.add(link);
continue;
}
if (link->tonode == gnode) {
links_to_remove.add(link);
continue;
}
if (nodes_to_move.contains(link->tonode)) {
internal_links_to_move.add(link);
continue;
}
else {
output_links.append({link, add_interface_from_socket(ntree, group, *link->fromsock)});
}
output_links.append({link, add_interface_from_socket(ntree, group, *link->fromsock)});
}
}
}
@@ -35,13 +35,10 @@ set(SRC
sequencer_drag_drop.c
sequencer_draw.c
sequencer_edit.c
sequencer_gizmo_retime.cc
sequencer_gizmo_retime_type.cc
sequencer_modifier.c
sequencer_ops.c
sequencer_preview.c
sequencer_proxy.c
sequencer_retiming.cc
sequencer_scopes.c
sequencer_select.c
sequencer_thumbnails.c
@@ -414,6 +414,7 @@ static void draw_seq_waveform_overlay(
const float frames_per_pixel = BLI_rctf_size_x(&region->v2d.cur) / region->winx;
const float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
float samples_per_pixel = samples_per_frame * frames_per_pixel;
/* Align strip start with nearest pixel to prevent waveform flickering. */
const float x1_aligned = align_frame_with_pixel(x1, frames_per_pixel);
@@ -439,17 +440,15 @@ static void draw_seq_waveform_overlay(
size_t wave_data_len = 0;
/* Offset must be also aligned, otherwise waveform flickers when moving left handle. */
float start_frame = SEQ_time_left_handle_frame_get(scene, seq);
const float strip_offset = align_frame_with_pixel(seq->startofs + seq->anim_startofs,
frames_per_pixel);
float start_sample = strip_offset * samples_per_frame;
start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
/* Add off-screen part of strip to offset. */
start_frame += (frame_start - x1_aligned);
start_frame += seq->sound->offset_time / FPS;
start_sample += (frame_start - x1_aligned) * samples_per_frame;
for (int i = 0; i < pixels_to_draw; i++) {
float timeline_frame = start_frame + i * frames_per_pixel;
/* TODO: Use linear interpolation between frames to avoid bad drawing quality. */
float frame_index = SEQ_give_frame_index(scene, seq, timeline_frame);
float sample = frame_index * samples_per_frame;
float sample = start_sample + i * samples_per_pixel;
int sample_index = round_fl_to_int(sample);
if (sample_index < 0) {
@@ -470,8 +469,6 @@ static void draw_seq_waveform_overlay(
value_min = (1.0f - f) * value_min + f * waveform->data[sample_index * 3 + 3];
value_max = (1.0f - f) * value_max + f * waveform->data[sample_index * 3 + 4];
rms = (1.0f - f) * rms + f * waveform->data[sample_index * 3 + 5];
float samples_per_pixel = samples_per_frame * frames_per_pixel;
if (samples_per_pixel > 1.0f) {
/* We need to sum up the values we skip over until the next step. */
float next_pos = sample + samples_per_pixel;
@@ -1,106 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "BLI_span.hh"
#include "RNA_access.h"
#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_gizmo_library.h"
#include "ED_gizmo_utils.h"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
/* Own include. */
#include "sequencer_intern.h"
typedef struct GizmoGroup_retime {
wmGizmo *add_handle_gizmo;
wmGizmo *move_handle_gizmo;
wmGizmo *remove_handle_gizmo;
} GizmoGroup_retime;
static bool gizmogroup_retime_poll(const bContext *C, wmGizmoGroupType *gzgt)
{
/* Needed to prevent drawing gizmos when retiming tool is not activated. */
if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
return false;
}
if ((U.gizmo_flag & USER_GIZMO_DRAW) == 0) {
return false;
}
ScrArea *area = CTX_wm_area(C);
if (area == nullptr && area->spacetype != SPACE_SEQ) {
return false;
}
const SpaceSeq *sseq = (SpaceSeq *)area->spacedata.first;
if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_TOOL)) {
return false;
}
Editing *ed = SEQ_editing_get(CTX_data_scene(C));
Sequence *seq = ed->act_seq;
if (ed == nullptr || seq == nullptr || !SEQ_retiming_is_allowed(seq)) {
return false;
}
return true;
}
static void gizmogroup_retime_setup(const bContext * /* C */, wmGizmoGroup *gzgroup)
{
GizmoGroup_retime *ggd = (GizmoGroup_retime *)MEM_callocN(sizeof(GizmoGroup_retime), __func__);
/* Assign gizmos. */
const wmGizmoType *gzt_add_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_add", true);
ggd->add_handle_gizmo = WM_gizmo_new_ptr(gzt_add_handle, gzgroup, nullptr);
const wmGizmoType *gzt_remove_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_remove", true);
ggd->remove_handle_gizmo = WM_gizmo_new_ptr(gzt_remove_handle, gzgroup, nullptr);
const wmGizmoType *gzt_move_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_move", true);
ggd->move_handle_gizmo = WM_gizmo_new_ptr(gzt_move_handle, gzgroup, nullptr);
gzgroup->customdata = ggd;
/* Assign operators. */
wmOperatorType *ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_move", true);
WM_gizmo_operator_set(ggd->move_handle_gizmo, 0, ot, nullptr);
ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_add", true);
WM_gizmo_operator_set(ggd->add_handle_gizmo, 0, ot, nullptr);
ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_remove", true);
WM_gizmo_operator_set(ggd->remove_handle_gizmo, 0, ot, nullptr);
}
void SEQUENCER_GGT_gizmo_retime(wmGizmoGroupType *gzgt)
{
gzgt->name = "Sequencer Transform Gizmo Retime";
gzgt->idname = "SEQUENCER_GGT_gizmo_retime";
gzgt->flag = WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL;
gzgt->gzmap_params.spaceid = SPACE_SEQ;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
gzgt->poll = gizmogroup_retime_poll;
gzgt->setup = gizmogroup_retime_setup;
}
@@ -1,621 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_span.hh"
#include "DNA_anim_types.h"
#include "DNA_sequence_types.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_scene.h"
#include "BLF_api.h"
#include "GPU_batch.h"
#include "GPU_batch_utils.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_select.h"
#include "GPU_state.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_gizmo_library.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "UI_interface.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
#include "UI_view2d.h"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
/* Own include. */
#include "sequencer_intern.h"
using blender::MutableSpan;
/** Size in pixels. */
#define RETIME_HANDLE_MOUSEOVER_THRESHOLD (16.0f * UI_SCALE_FAC)
/** Factor based on icon size. */
#define RETIME_BUTTON_SIZE 0.6f
static float remove_gizmo_height_get(const View2D *v2d)
{
const float max_size = (SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM) * UI_view2d_scale_get_y(v2d);
return min_ff(14.0f * UI_SCALE_FAC, max_size * 0.4f);
}
static float strip_y_rescale(const Sequence *seq, const float y_value)
{
const float y_range = SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM;
return (y_value * y_range) + seq->machine + SEQ_STRIP_OFSBOTTOM;
}
static float handle_x_get(const Scene *scene, const Sequence *seq, const SeqRetimingHandle *handle)
{
const SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
const bool is_last_handle = (handle == last_handle);
return SEQ_retiming_handle_timeline_frame_get(scene, seq, handle) + (is_last_handle ? 1 : 0);
}
static const SeqRetimingHandle *mouse_over_handle_get(const Scene *scene,
const Sequence *seq,
const View2D *v2d,
const int mval[2])
{
int best_distance = INT_MAX;
const SeqRetimingHandle *best_handle = nullptr;
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (const SeqRetimingHandle &handle : handles) {
int distance = round_fl_to_int(
fabsf(UI_view2d_view_to_region_x(v2d, handle_x_get(scene, seq, &handle)) - mval[0]));
if (distance < RETIME_HANDLE_MOUSEOVER_THRESHOLD && distance < best_distance) {
best_distance = distance;
best_handle = &handle;
}
}
return best_handle;
}
static float pixels_to_view_width(const bContext *C, const float width)
{
const View2D *v2d = UI_view2d_fromcontext(C);
float scale_x = UI_view2d_view_to_region_x(v2d, 1) - UI_view2d_view_to_region_x(v2d, 0.0f);
return width / scale_x;
}
static float pixels_to_view_height(const bContext *C, const float height)
{
const View2D *v2d = UI_view2d_fromcontext(C);
float scale_y = UI_view2d_view_to_region_y(v2d, 1) - UI_view2d_view_to_region_y(v2d, 0.0f);
return height / scale_y;
}
static float strip_start_screenspace_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_left_handle_frame_get(scene, seq));
}
static float strip_end_screenspace_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_right_handle_frame_get(scene, seq));
}
static Sequence *active_seq_from_context(const bContext *C)
{
const Editing *ed = SEQ_editing_get(CTX_data_scene(C));
return ed->act_seq;
}
static rctf strip_box_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect;
rect.xmin = strip_start_screenspace_get(C, seq);
rect.xmax = strip_end_screenspace_get(C, seq);
rect.ymin = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0));
rect.ymax = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1));
return rect;
}
static rctf remove_box_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect = strip_box_get(C, seq);
rect.ymax = rect.ymin + remove_gizmo_height_get(v2d);
return rect;
}
static bool mouse_is_inside_box(const rctf *box, const int mval[2])
{
return mval[0] >= box->xmin && mval[0] <= box->xmax && mval[1] >= box->ymin &&
mval[1] <= box->ymax;
}
/* -------------------------------------------------------------------- */
/** \name Retiming Add Handle Gizmo
* \{ */
typedef struct RetimeButtonGizmo {
wmGizmo gizmo;
int icon_id;
const Sequence *seq_under_mouse;
bool is_mouse_over_gizmo;
} RetimeButtonGizmo;
typedef struct ButtonDimensions {
float height;
float width;
float x;
float y;
} ButtonDimensions;
static ButtonDimensions button_dimensions_get(const bContext *C, const RetimeButtonGizmo *gizmo)
{
const Scene *scene = CTX_data_scene(C);
const View2D *v2d = UI_view2d_fromcontext(C);
const Sequence *seq = active_seq_from_context(C);
const float icon_height = UI_icon_get_height(gizmo->icon_id) * UI_SCALE_FAC;
const float icon_width = UI_icon_get_width(gizmo->icon_id) * UI_SCALE_FAC;
const float icon_x = UI_view2d_view_to_region_x(v2d, BKE_scene_frame_get(scene)) +
icon_width / 2;
const float icon_y = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.5)) -
icon_height / 2;
const ButtonDimensions dimensions = {icon_height, icon_width, icon_x, icon_y};
return dimensions;
}
static rctf button_box_get(const bContext *C, const RetimeButtonGizmo *gizmo)
{
ButtonDimensions button_dimensions = button_dimensions_get(C, gizmo);
float x_range = button_dimensions.width;
float y_range = button_dimensions.height;
rctf rect;
rect.xmin = button_dimensions.x;
rect.xmax = button_dimensions.x + x_range;
rect.ymin = button_dimensions.y;
rect.ymax = button_dimensions.y + y_range;
return rect;
}
static void gizmo_retime_handle_add_draw(const bContext *C, wmGizmo *gz)
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
return;
}
const Scene *scene = CTX_data_scene(C);
const Sequence *seq = active_seq_from_context(C);
const int frame_index = BKE_scene_frame_get(scene) - SEQ_time_start_frame_get(seq);
const SeqRetimingHandle *handle = SEQ_retiming_find_segment_start_handle(seq, frame_index);
if (handle != nullptr && SEQ_retiming_handle_is_transition_type(handle)) {
return;
}
const ButtonDimensions button = button_dimensions_get(C, gizmo);
const rctf strip_box = strip_box_get(C, active_seq_from_context(C));
if (!BLI_rctf_isect_pt(&strip_box, button.x, button.y)) {
return;
}
wmOrtho2_region_pixelspace(CTX_wm_region(C));
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
const float alpha = gizmo->is_mouse_over_gizmo ? 1.0f : 0.6f;
immUniformColor4f(0.2f, 0.2f, 0.2f, alpha);
imm_draw_circle_fill_2d(pos,
button.x + button.width / 2,
button.y + button.height / 2,
button.width * RETIME_BUTTON_SIZE,
32);
immUnbindProgram();
GPU_polygon_smooth(false);
UI_icon_draw_alpha(button.x, button.y, gizmo->icon_id, alpha);
GPU_polygon_smooth(true);
GPU_blend(GPU_BLEND_NONE);
}
static int gizmo_retime_handle_add_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
Sequence *seq = active_seq_from_context(C);
Sequence *mouse_over_seq = nullptr;
gizmo->is_mouse_over_gizmo = false;
/* Store strip under mouse cursor. */
const rctf strip_box = strip_box_get(C, seq);
if (mouse_is_inside_box(&strip_box, mval)) {
mouse_over_seq = seq;
}
if (gizmo->seq_under_mouse != mouse_over_seq) {
gizmo->seq_under_mouse = mouse_over_seq;
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, CTX_data_scene(C));
}
if (gizmo->seq_under_mouse == nullptr) {
return -1;
}
const rctf button_box = button_box_get(C, gizmo);
if (!mouse_is_inside_box(&button_box, mval)) {
return -1;
}
gizmo->is_mouse_over_gizmo = true;
const Scene *scene = CTX_data_scene(C);
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "timeline_frame", BKE_scene_frame_get(scene));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, CTX_data_scene(C));
return 0;
}
static void gizmo_retime_handle_add_setup(wmGizmo *gz)
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
gizmo->icon_id = ICON_ADD;
}
void GIZMO_GT_retime_handle_add(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_add";
/* Api callbacks. */
gzt->setup = gizmo_retime_handle_add_setup;
gzt->draw = gizmo_retime_handle_add_draw;
gzt->test_select = gizmo_retime_handle_add_test_select;
gzt->struct_size = sizeof(RetimeButtonGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Move Handle Gizmo
* \{ */
typedef struct RetimeHandleMoveGizmo {
wmGizmo gizmo;
const Sequence *mouse_over_seq;
int mouse_over_handle_x;
bool create_transition_operation;
} RetimeHandleMoveGizmo;
static void retime_handle_draw(const bContext *C,
const RetimeHandleMoveGizmo *gizmo,
uint pos,
const Sequence *seq,
const SeqRetimingHandle *handle)
{
const Scene *scene = CTX_data_scene(C);
const float handle_x = handle_x_get(scene, seq, handle);
if (handle_x == SEQ_time_left_handle_frame_get(scene, seq)) {
return;
}
if (handle_x == SEQ_time_right_handle_frame_get(scene, seq)) {
return;
}
const View2D *v2d = UI_view2d_fromcontext(C);
const rctf strip_box = strip_box_get(C, seq);
if (!BLI_rctf_isect_x(&strip_box, UI_view2d_view_to_region_x(v2d, handle_x))) {
return; /* Handle out of strip bounds. */
}
const int ui_triangle_size = remove_gizmo_height_get(v2d);
const float bottom = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.0f)) + 2;
const float top = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1.0f)) - 2;
const float handle_position = UI_view2d_view_to_region_x(v2d, handle_x);
float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
if (seq == gizmo->mouse_over_seq && handle_x == gizmo->mouse_over_handle_x) {
if (gizmo->create_transition_operation) {
bool handle_is_transition = SEQ_retiming_handle_is_transition_type(handle);
bool prev_handle_is_transition = SEQ_retiming_handle_is_transition_type(handle - 1);
if (!(handle_is_transition || prev_handle_is_transition)) {
col[0] = 0.5f;
col[2] = 0.4f;
}
}
}
else {
mul_v3_fl(col, 0.65f);
}
immUniformColor4fv(col);
immBegin(GPU_PRIM_TRI_FAN, 3);
immVertex2f(pos, handle_position - ui_triangle_size / 2, bottom);
immVertex2f(pos, handle_position + ui_triangle_size / 2, bottom);
immVertex2f(pos, handle_position, bottom + ui_triangle_size);
immEnd();
immBegin(GPU_PRIM_LINES, 2);
immVertex2f(pos, handle_position, bottom);
immVertex2f(pos, handle_position, top);
immEnd();
}
static void retime_speed_text_draw(const bContext *C,
const Sequence *seq,
const SeqRetimingHandle *handle)
{
SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
if (handle == last_handle) {
return;
}
const Scene *scene = CTX_data_scene(C);
const int start_frame = SEQ_time_left_handle_frame_get(scene, seq);
const int end_frame = SEQ_time_right_handle_frame_get(scene, seq);
int next_handle_index = SEQ_retiming_handle_index_get(seq, handle) + 1;
const SeqRetimingHandle *next_handle = &SEQ_retiming_handles_get(seq)[next_handle_index];
if (handle_x_get(scene, seq, next_handle) < start_frame ||
handle_x_get(scene, seq, handle) > end_frame)
{
return; /* Label out of strip bounds. */
}
char label_str[40];
size_t label_len;
if (SEQ_retiming_handle_is_transition_type(handle)) {
const float prev_speed = SEQ_retiming_handle_speed_get(seq, handle - 1);
const float next_speed = SEQ_retiming_handle_speed_get(seq, next_handle + 1);
label_len = SNPRINTF_RLEN(label_str,
"%d%% - %d%%",
round_fl_to_int(prev_speed * 100.0f),
round_fl_to_int(next_speed * 100.0f));
}
else {
const float speed = SEQ_retiming_handle_speed_get(seq, next_handle);
label_len = SNPRINTF_RLEN(label_str, "%d%%", round_fl_to_int(speed * 100.0f));
}
const float width = pixels_to_view_width(C, BLF_width(BLF_default(), label_str, label_len));
const float xmin = max_ff(SEQ_time_left_handle_frame_get(scene, seq),
handle_x_get(scene, seq, handle));
const float xmax = min_ff(SEQ_time_right_handle_frame_get(scene, seq),
handle_x_get(scene, seq, next_handle));
const float text_x = (xmin + xmax - width) / 2;
const float text_y = strip_y_rescale(seq, 0) + pixels_to_view_height(C, 5);
if (width > xmax - xmin) {
return; /* Not enough space to draw label. */
}
const uchar col[4] = {255, 255, 255, 255};
UI_view2d_text_cache_add(UI_view2d_fromcontext(C), text_x, text_y, label_str, label_len, col);
}
static void gizmo_retime_handle_draw(const bContext *C, wmGizmo *gz)
{
RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz;
const View2D *v2d = UI_view2d_fromcontext(C);
/* TODO: This is hard-coded behavior, same as pre-select gizmos in 3D view.
* Better solution would be to check operator keymap and display this information in status bar
* and tool-tip. */
wmEvent *event = CTX_wm_window(C)->eventstate;
gizmo->create_transition_operation = (event->modifier & KM_SHIFT) != 0;
wmOrtho2_region_pixelspace(CTX_wm_region(C));
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(seq);
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (const SeqRetimingHandle &handle : handles) {
retime_speed_text_draw(C, seq, &handle);
if (&handle == handles.begin()) {
continue; /* Ignore first handle. */
}
retime_handle_draw(C, gizmo, pos, seq, &handle);
}
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
UI_view2d_text_cache_draw(CTX_wm_region(C));
UI_view2d_view_ortho(v2d); /* 'UI_view2d_text_cache_draw()' messes up current view. */
}
static int gizmo_retime_handle_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz;
Scene *scene = CTX_data_scene(C);
gizmo->mouse_over_seq = nullptr;
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(seq);
const SeqRetimingHandle *handle = mouse_over_handle_get(
scene, seq, UI_view2d_fromcontext(C), mval);
const int handle_index = SEQ_retiming_handle_index_get(seq, handle);
if (handle == nullptr) {
return -1;
}
if (handle_x_get(scene, seq, handle) == SEQ_time_left_handle_frame_get(scene, seq) ||
handle_index == 0)
{
return -1;
}
const View2D *v2d = UI_view2d_fromcontext(C);
rctf strip_box = strip_box_get(C, seq);
BLI_rctf_resize_x(&strip_box, BLI_rctf_size_x(&strip_box) + 2 * remove_gizmo_height_get(v2d));
if (!mouse_is_inside_box(&strip_box, mval)) {
return -1;
}
gizmo->mouse_over_seq = seq;
gizmo->mouse_over_handle_x = handle_x_get(scene, seq, handle);
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "handle_index", handle_index);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return 0;
}
static int gizmo_retime_handle_cursor_get(wmGizmo *gz)
{
if (RNA_boolean_get(gz->ptr, "show_drag")) {
return WM_CURSOR_EW_SCROLL;
}
return WM_CURSOR_DEFAULT;
}
static void gizmo_retime_handle_setup(wmGizmo *gz)
{
gz->flag = WM_GIZMO_DRAW_MODAL;
}
void GIZMO_GT_retime_handle(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_move";
/* Api callbacks. */
gzt->setup = gizmo_retime_handle_setup;
gzt->draw = gizmo_retime_handle_draw;
gzt->test_select = gizmo_retime_handle_test_select;
gzt->cursor_get = gizmo_retime_handle_cursor_get;
gzt->struct_size = sizeof(RetimeHandleMoveGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Remove Handle Gizmo
* \{ */
static void gizmo_retime_remove_draw(const bContext * /* C */, wmGizmo * /* gz */)
{
/* Pass. */
}
static int gizmo_retime_remove_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
Scene *scene = CTX_data_scene(C);
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(seq);
const SeqRetimingHandle *handle = mouse_over_handle_get(
scene, seq, UI_view2d_fromcontext(C), mval);
const int handle_index = SEQ_retiming_handle_index_get(seq, handle);
if (handle == nullptr) {
return -1;
}
if (handle_x_get(scene, seq, handle) == SEQ_time_left_handle_frame_get(scene, seq) ||
handle_index == 0)
{
return -1; /* Ignore first handle. */
}
SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
if (handle == last_handle) {
return -1; /* Last handle can not be removed. */
}
const View2D *v2d = UI_view2d_fromcontext(C);
rctf box = remove_box_get(C, seq);
BLI_rctf_resize_x(&box, BLI_rctf_size_x(&box) + 2 * remove_gizmo_height_get(v2d));
if (!mouse_is_inside_box(&box, mval)) {
return -1;
}
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "handle_index", handle_index);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return 0;
}
static int gizmo_retime_remove_cursor_get(wmGizmo *gz)
{
if (RNA_boolean_get(gz->ptr, "show_drag")) {
return WM_CURSOR_ERASER;
}
return WM_CURSOR_DEFAULT;
}
void GIZMO_GT_retime_remove(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_remove";
/* Api callbacks. */
gzt->draw = gizmo_retime_remove_draw;
gzt->test_select = gizmo_retime_remove_test_select;
gzt->cursor_get = gizmo_retime_remove_cursor_get;
gzt->struct_size = sizeof(wmGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */
@@ -10,17 +10,11 @@
#include "DNA_sequence_types.h"
#include "RNA_access.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Internal exports only. */
struct ARegion;
struct ARegionType;
struct Depsgraph;
struct wmGizmoGroupType;
struct wmGizmoType;
struct Main;
struct Scene;
struct SeqCollection;
@@ -305,21 +299,3 @@ int sequencer_image_seq_get_minmax_frame(struct wmOperator *op,
int *r_numdigits);
void sequencer_image_seq_reserve_frames(
struct wmOperator *op, struct StripElem *se, int len, int minframe, int numdigits);
/* sequencer_retiming.c */
void SEQUENCER_OT_retiming_reset(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_move(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_add(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_remove(struct wmOperatorType *ot);
/* sequencer_gizmo_retime.c */
void SEQUENCER_GGT_gizmo_retime(struct wmGizmoGroupType *gzgt);
/* sequencer_gizmo_retime_type.c */
void GIZMO_GT_retime_handle_add(struct wmGizmoType *gzt);
void GIZMO_GT_retime_handle(struct wmGizmoType *gzt);
void GIZMO_GT_retime_remove(struct wmGizmoType *gzt);
#ifdef __cplusplus
}
#endif
@@ -68,12 +68,6 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_cursor_set);
WM_operatortype_append(SEQUENCER_OT_scene_frame_range_update);
/* sequencer_retiming.c */
WM_operatortype_append(SEQUENCER_OT_retiming_reset);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_move);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_add);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_remove);
/* sequencer_select.c */
WM_operatortype_append(SEQUENCER_OT_select_all);
WM_operatortype_append(SEQUENCER_OT_select);
@@ -1,442 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "DNA_anim_types.h"
#include "DNA_scene_types.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "SEQ_transform.h"
#include "WM_api.h"
#include "RNA_define.h"
#include "UI_interface.h"
#include "UI_view2d.h"
#include "DEG_depsgraph.h"
/* Own include. */
#include "sequencer_intern.h"
using blender::MutableSpan;
static bool retiming_poll(bContext *C)
{
const Editing *ed = SEQ_editing_get(CTX_data_scene(C));
if (ed == nullptr) {
return false;
}
Sequence *seq = ed->act_seq;
if (seq == nullptr) {
return false;
}
if (!SEQ_retiming_is_allowed(seq)) {
CTX_wm_operator_poll_msg_set(C, "This strip type can not be retimed");
return false;
}
return true;
}
static void retiming_handle_overlap(Scene *scene, Sequence *seq)
{
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
SeqCollection *strips = SEQ_collection_create(__func__);
SEQ_collection_append_strip(seq, strips);
SeqCollection *dependant = SEQ_collection_create(__func__);
SEQ_collection_expand(scene, seqbase, strips, SEQ_query_strip_effect_chain);
SEQ_collection_remove_strip(seq, dependant);
SEQ_transform_handle_overlap(scene, seqbase, strips, dependant, true);
SEQ_collection_free(strips);
SEQ_collection_free(dependant);
}
/*-------------------------------------------------------------------- */
/** \name Retiming Reset
* \{ */
static int sequencer_retiming_reset_exec(bContext *C, wmOperator * /* op */)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
SEQ_retiming_data_clear(seq);
retiming_handle_overlap(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
void SEQUENCER_OT_retiming_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset Retiming";
ot->description = "Reset strip retiming";
ot->idname = "SEQUENCER_OT_retiming_reset";
/* api callbacks */
ot->exec = sequencer_retiming_reset_exec;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Move Handle
* \{ */
static SeqRetimingHandle *closest_retiming_handle_get(const bContext *C,
const Sequence *seq,
const float mouse_x)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
int best_distance = INT_MAX;
SeqRetimingHandle *closest_handle = nullptr;
const float distance_threshold = UI_view2d_region_to_view_x(v2d, 10);
const float mouse_x_view = UI_view2d_region_to_view_x(v2d, mouse_x);
for (int i = 0; i < SEQ_retiming_handles_count(seq); i++) {
SeqRetimingHandle *handle = seq->retiming_handles + i;
const int distance = round_fl_to_int(
fabsf(SEQ_retiming_handle_timeline_frame_get(scene, seq, handle) - mouse_x_view));
if (distance < distance_threshold && distance < best_distance) {
best_distance = distance;
closest_handle = handle;
}
}
return closest_handle;
}
static int sequencer_retiming_handle_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
int handle_index = 0;
if (RNA_struct_property_is_set(op->ptr, "handle_index")) {
handle_index = RNA_int_get(op->ptr, "handle_index");
}
/* Ensure retiming handle at left handle position. This way user gets more predictable result
* when strips have offsets. */
const int left_handle_frame = SEQ_time_left_handle_frame_get(scene, seq);
if (SEQ_retiming_add_handle(scene, seq, left_handle_frame) != nullptr) {
handle_index++; /* Advance index, because new handle was created. */
}
MutableSpan handles = SEQ_retiming_handles_get(seq);
SeqRetimingHandle *handle = nullptr;
if (RNA_struct_property_is_set(op->ptr, "handle_index")) {
BLI_assert(handle_index < handles.size());
handle = &handles[handle_index];
}
else {
handle = closest_retiming_handle_get(C, seq, event->mval[0]);
}
if (handle == nullptr) {
BKE_report(op->reports, RPT_ERROR, "No handle available");
return OPERATOR_CANCELLED;
}
RNA_int_set(op->ptr, "handle_index", SEQ_retiming_handle_index_get(seq, handle));
if ((event->modifier & KM_SHIFT) != 0) {
op->customdata = (void *)1;
}
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int sequencer_retiming_handle_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
const ARegion *region = CTX_wm_region(C);
const View2D *v2d = &region->v2d;
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
int handle_index = RNA_int_get(op->ptr, "handle_index");
SeqRetimingHandle *handle = &SEQ_retiming_handles_get(seq)[handle_index];
switch (event->type) {
case MOUSEMOVE: {
float mouse_x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
int offset = 0;
offset = mouse_x - SEQ_retiming_handle_timeline_frame_get(scene, seq, handle);
if (offset == 0) {
return OPERATOR_RUNNING_MODAL;
}
/* Add retiming gradient and move handle. */
if (op->customdata) {
SeqRetimingHandle *transition_handle = SEQ_retiming_add_transition(
scene, seq, handle, abs(offset));
/* New gradient handle was created - update operator properties. */
if (transition_handle != nullptr) {
if (offset < 0) {
handle = transition_handle;
}
else {
handle = transition_handle + 1;
}
RNA_int_set(op->ptr, "handle_index", SEQ_retiming_handle_index_get(seq, handle));
}
}
const bool handle_is_transition = SEQ_retiming_handle_is_transition_type(handle);
const bool prev_handle_is_transition = SEQ_retiming_handle_is_transition_type(handle - 1);
/* When working with transition, change handles when moving past pivot point. */
if (handle_is_transition || prev_handle_is_transition) {
SeqRetimingHandle *transition_start, *transition_end;
if (handle_is_transition) {
transition_start = handle;
transition_end = handle + 1;
}
else {
transition_start = handle - 1;
transition_end = handle;
}
const int offset_l = mouse_x -
SEQ_retiming_handle_timeline_frame_get(scene, seq, transition_start);
const int offset_r = mouse_x -
SEQ_retiming_handle_timeline_frame_get(scene, seq, transition_end);
if (prev_handle_is_transition && offset_l < 0) {
RNA_int_set(
op->ptr, "handle_index", SEQ_retiming_handle_index_get(seq, transition_start));
}
if (handle_is_transition && offset_r > 0) {
RNA_int_set(op->ptr, "handle_index", SEQ_retiming_handle_index_get(seq, transition_end));
}
}
SEQ_retiming_offset_handle(scene, seq, handle, offset);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_RUNNING_MODAL;
}
case LEFTMOUSE:
case EVT_RETKEY:
case EVT_SPACEKEY: {
retiming_handle_overlap(scene, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
case EVT_ESCKEY:
case RIGHTMOUSE: {
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_CANCELLED;
}
}
return OPERATOR_RUNNING_MODAL;
}
void SEQUENCER_OT_retiming_handle_move(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Move Retiming Handle";
ot->description = "Move retiming handle";
ot->idname = "SEQUENCER_OT_retiming_handle_move";
/* api callbacks */
ot->invoke = sequencer_retiming_handle_move_invoke;
ot->modal = sequencer_retiming_handle_move_modal;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
PropertyRNA *prop = RNA_def_int(ot->srna,
"handle_index",
0,
0,
INT_MAX,
"Handle Index",
"Index of handle to be moved",
0,
INT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Add Handle
* \{ */
static int sequesequencer_retiming_handle_add_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
SEQ_retiming_data_ensure(seq);
float timeline_frame;
if (RNA_struct_property_is_set(op->ptr, "timeline_frame")) {
timeline_frame = RNA_int_get(op->ptr, "timeline_frame");
}
else {
timeline_frame = BKE_scene_frame_get(scene);
}
const int frame_index = BKE_scene_frame_get(scene) - SEQ_time_start_frame_get(seq);
const SeqRetimingHandle *handle = SEQ_retiming_find_segment_start_handle(seq, frame_index);
if (SEQ_retiming_handle_is_transition_type(handle)) {
BKE_report(op->reports, RPT_ERROR, "Can not create handle inside of speed transition");
return OPERATOR_CANCELLED;
}
bool inserted = false;
const float end_frame = seq->start + SEQ_time_strip_length_get(scene, seq);
if (seq->start < timeline_frame && end_frame > timeline_frame) {
SEQ_retiming_add_handle(scene, seq, timeline_frame);
inserted = true;
}
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return inserted ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH;
}
void SEQUENCER_OT_retiming_handle_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Retiming Handle";
ot->description = "Add retiming Handle";
ot->idname = "SEQUENCER_OT_retiming_handle_add";
/* api callbacks */
ot->exec = sequesequencer_retiming_handle_add_exec;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_int(ot->srna,
"timeline_frame",
0,
0,
INT_MAX,
"Timeline Frame",
"Frame where handle will be added",
0,
INT_MAX);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Remove Handle
* \{ */
static int sequencer_retiming_handle_remove_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
SeqRetimingHandle *handle = (SeqRetimingHandle *)op->customdata;
SEQ_retiming_remove_handle(scene, seq, handle);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
static int sequencer_retiming_handle_remove_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
const Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
const Sequence *seq = ed->act_seq;
if (seq == nullptr) {
return OPERATOR_CANCELLED;
}
MutableSpan handles = SEQ_retiming_handles_get(seq);
SeqRetimingHandle *handle = nullptr;
if (RNA_struct_property_is_set(op->ptr, "handle_index")) {
const int handle_index = RNA_int_get(op->ptr, "handle_index");
BLI_assert(handle_index < handles.size());
handle = &handles[handle_index];
}
else {
handle = closest_retiming_handle_get(C, seq, event->mval[0]);
}
if (handle == nullptr) {
BKE_report(op->reports, RPT_ERROR, "No handle available");
return OPERATOR_CANCELLED;
}
op->customdata = handle;
return sequencer_retiming_handle_remove_exec(C, op);
}
void SEQUENCER_OT_retiming_handle_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Retiming Handle";
ot->description = "Remove retiming handle";
ot->idname = "SEQUENCER_OT_retiming_handle_remove";
/* api callbacks */
ot->invoke = sequencer_retiming_handle_remove_invoke;
ot->exec = sequencer_retiming_handle_remove_exec;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
PropertyRNA *prop = RNA_def_int(ot->srna,
"handle_index",
0,
0,
INT_MAX,
"Handle Index",
"Index of handle to be removed",
0,
INT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
/** \} */
@@ -426,15 +426,10 @@ static void sequencer_gizmos(void)
wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(
&(const struct wmGizmoMapType_Params){SPACE_SEQ, RGN_TYPE_PREVIEW});
WM_gizmotype_append(GIZMO_GT_retime_handle_add);
WM_gizmotype_append(GIZMO_GT_retime_handle);
WM_gizmotype_append(GIZMO_GT_retime_remove);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_translate);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_resize);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_rotate);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo_retime);
WM_gizmogrouptype_append_and_link(gzmap_type, SEQUENCER_GGT_navigate);
}
@@ -589,7 +584,6 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
case ND_SEQUENCER:
case ND_RENDER_RESULT:
ED_region_tag_redraw(region);
WM_gizmomap_tag_refresh(region->gizmo_map);
break;
}
break;
@@ -603,7 +597,6 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
case NC_SPACE:
if (wmn->data == ND_SPACE_SEQUENCER) {
ED_region_tag_redraw(region);
WM_gizmomap_tag_refresh(region->gizmo_map);
}
break;
case NC_ID:
@@ -614,7 +607,6 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
case NC_SCREEN:
if (ELEM(wmn->data, ND_ANIMPLAY)) {
ED_region_tag_redraw(region);
WM_gizmomap_tag_refresh(region->gizmo_map);
}
break;
}
@@ -1031,6 +1023,7 @@ void ED_spacetype_sequencer(void)
art->on_view2d_changed = sequencer_main_region_view2d_changed;
art->listener = sequencer_main_region_listener;
art->message_subscribe = sequencer_main_region_message_subscribe;
/* NOTE: inclusion of #ED_KEYMAP_GIZMO is currently for scripts and isn't used by default. */
art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES |
ED_KEYMAP_ANIMATION;
BLI_addhead(&st->regiontypes, art);
@@ -97,6 +97,8 @@ static eV3D_OpEvent view3d_navigate_event(ViewOpsData *vod, const wmEvent *event
{
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case VIEW_MODAL_CANCEL:
return VIEW_CANCEL;
case VIEW_MODAL_CONFIRM:
return VIEW_CONFIRM;
case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
@@ -32,6 +32,7 @@
#include "BLT_translation.h"
#include "UI_resources.h"
#include "UI_view2d.h"
#include "transform.h"
#include "transform_gizmo.h"
@@ -896,13 +897,12 @@ void drawPropCircle(const struct bContext *C, TransInfo *t)
}
else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION)) {
/* only scale y */
rcti *mask = &t->region->v2d.mask;
rctf *datamask = &t->region->v2d.cur;
float xsize = BLI_rctf_size_x(datamask);
float ysize = BLI_rctf_size_y(datamask);
float xmask = BLI_rcti_size_x(mask);
float ymask = BLI_rcti_size_y(mask);
GPU_matrix_scale_2f(1.0f, (ysize / xsize) * (xmask / ymask));
float xscale, yscale;
UI_view2d_scale_get(&t->region->v2d, &xscale, &yscale);
const float fac_scale = xscale / yscale;
GPU_matrix_scale_2f(1.0f, fac_scale);
GPU_matrix_translate_2f(0.0f, (t->center_global[1] / fac_scale) - t->center_global[1]);
}
eGPUDepthTest depth_test_enabled = GPU_depth_test_get();
@@ -588,6 +588,9 @@ static void handle_armature_parent_orientation(Object *ob, float r_mat[3][3])
if (active_pchan && active_pchan->parent) {
/* For child, show parent local regardless if "local location" is set for parent bone. */
transform_orientations_create_from_axis(r_mat, UNPACK3(active_pchan->parent->pose_mat));
float ob_orientations_mat[3][3];
transform_orientations_create_from_axis(ob_orientations_mat, UNPACK3(ob->object_to_world));
mul_m3_m3_pre(r_mat, ob_orientations_mat);
return;
}
@@ -236,9 +236,7 @@ static void snap_editmesh_minmax(SnapObjectContext *sctx,
}
}
static void snap_object_data_mesh_get(SnapObjectContext *sctx,
Object *ob_eval,
const Mesh *me_eval,
static void snap_object_data_mesh_get(const Mesh *me_eval,
bool use_hide,
BVHTreeFromMesh *r_treedata)
{
@@ -246,11 +244,6 @@ static void snap_object_data_mesh_get(SnapObjectContext *sctx,
const blender::OffsetIndices polys = me_eval->polys();
const Span<int> corner_verts = me_eval->corner_verts();
if (ob_eval->type == OB_MESH) {
/* Any existing #SnapData_EditMesh is now invalid. */
sctx->editmesh_caches.remove(BKE_editmesh_from_object(ob_eval));
}
/* The BVHTree from looptris is always required. */
BKE_bvhtree_from_mesh_get(
r_treedata, me_eval, use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI, 4);
@@ -678,8 +671,7 @@ static void editmesh_looptri_raycast_backface_culling_cb(void *userdata,
}
}
static bool raycastMesh(SnapObjectContext *sctx,
const SnapObjectParams *params,
static bool raycastMesh(const SnapObjectParams *params,
const float ray_start[3],
const float ray_dir[3],
Object *ob_eval,
@@ -747,7 +739,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
}
BVHTreeFromMesh treedata;
snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide, &treedata);
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
const blender::Span<int> looptri_polys = me_eval->looptri_polys();
@@ -1034,8 +1026,7 @@ static eSnapMode raycast_obj_fn(SnapObjectContext *sctx,
}
else {
const Mesh *me_eval = (const Mesh *)ob_data;
retval = raycastMesh(sctx,
params,
retval = raycastMesh(params,
dt->ray_start,
dt->ray_dir,
ob_eval,
@@ -1214,7 +1205,6 @@ static bool nearest_world_tree(SnapObjectContext * /*sctx*/,
static bool nearest_world_mesh(SnapObjectContext *sctx,
const struct SnapObjectParams *params,
Object *ob_eval,
const Mesh *me_eval,
const float (*obmat)[4],
bool use_hide,
@@ -1226,7 +1216,7 @@ static bool nearest_world_mesh(SnapObjectContext *sctx,
int *r_index)
{
BVHTreeFromMesh treedata;
snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide, &treedata);
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
if (treedata.tree == nullptr) {
return false;
}
@@ -1325,7 +1315,6 @@ static eSnapMode nearest_world_object_fn(SnapObjectContext *sctx,
const Mesh *me_eval = (const Mesh *)ob_data;
retval = nearest_world_mesh(sctx,
params,
ob_eval,
me_eval,
obmat,
use_hide,
@@ -2569,7 +2558,7 @@ static eSnapMode snapMesh(SnapObjectContext *sctx,
}
BVHTreeFromMesh treedata, treedata_dummy;
snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide, &treedata);
snap_object_data_mesh_get(me_eval, use_hide, &treedata);
BVHTree *bvhtree[2] = {nullptr};
bvhtree[0] = BKE_bvhtree_from_mesh_get(&treedata_dummy, me_eval, BVHTREE_FROM_LOOSEEDGES, 2);
@@ -1511,7 +1511,13 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
}
pack_island_params.scale_to_fit = RNA_boolean_get(op->ptr, "scale");
pack_island_params.merge_overlap = RNA_boolean_get(op->ptr, "merge_overlap");
pack_island_params.pin_method = eUVPackIsland_PinMethod(RNA_enum_get(op->ptr, "pin"));
if (RNA_boolean_get(op->ptr, "pin")) {
pack_island_params.pin_method = eUVPackIsland_PinMethod(RNA_enum_get(op->ptr, "pin_method"));
}
else {
pack_island_params.pin_method = ED_UVPACK_PIN_NONE;
}
pack_island_params.margin_method = eUVPackIsland_MarginMethod(
RNA_enum_get(op->ptr, "margin_method"));
@@ -1591,17 +1597,21 @@ static const EnumPropertyItem pack_shape_method_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
/**
* \note #ED_UVPACK_PIN_NONE is exposed as a boolean "pin".
* \note #ED_UVPACK_PIN_IGNORE is intentionally not exposed as it is confusing from the UI level
* (users can simply not select these islands).
* The option is kept kept internally because it's used for live unwrap.
*/
static const EnumPropertyItem pinned_islands_method_items[] = {
{ED_UVPACK_PIN_PACK, "PACK", 0, "Pack", "Pinned islands are packed normally"},
{ED_UVPACK_PIN_LOCK_SCALE, "SCALE", 0, "Lock Scale", "Pinned islands won't rescale"},
{ED_UVPACK_PIN_LOCK_ROTATION, "ROTATION", 0, "Lock Rotation", "Pinned islands won't rotate"},
{ED_UVPACK_PIN_LOCK_SCALE, "SCALE", 0, "Scale", "Pinned islands won't rescale"},
{ED_UVPACK_PIN_LOCK_ROTATION, "ROTATION", 0, "Rotation", "Pinned islands won't rotate"},
{ED_UVPACK_PIN_LOCK_ROTATION_SCALE,
"ROTATION_SCALE",
0,
"Lock Rotation and Scale",
"Rotation and Scale",
"Pinned islands will translate only"},
{ED_UVPACK_PIN_LOCK_ALL, "LOCKED", 0, "Lock in Place", "Pinned islands are locked in place"},
{ED_UVPACK_PIN_IGNORE, "IGNORE", 0, "Ignore", "Pinned islands are not packed"},
{ED_UVPACK_PIN_LOCK_ALL, "LOCKED", 0, "All", "Pinned islands are locked in place"},
{0, nullptr, 0, nullptr, nullptr},
};
@@ -1612,15 +1622,23 @@ static void uv_pack_islands_ui(bContext * /*C*/, wmOperator *op)
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, op->ptr, "shape_method", 0, nullptr, ICON_NONE);
uiItemR(layout, op->ptr, "scale", 0, nullptr, ICON_NONE);
uiItemR(layout, op->ptr, "rotate", 0, nullptr, ICON_NONE);
uiLayout *sub = uiLayoutRow(layout, true);
uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "rotate"));
uiItemR(sub, op->ptr, "rotate_method", 0, nullptr, ICON_NONE);
uiItemS(layout);
{
uiItemR(layout, op->ptr, "rotate", 0, nullptr, ICON_NONE);
uiLayout *sub = uiLayoutRow(layout, true);
uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "rotate"));
uiItemR(sub, op->ptr, "rotate_method", 0, nullptr, ICON_NONE);
uiItemS(layout);
}
uiItemR(layout, op->ptr, "margin_method", 0, nullptr, ICON_NONE);
uiItemR(layout, op->ptr, "margin", 0, nullptr, ICON_NONE);
uiItemS(layout);
uiItemR(layout, op->ptr, "pin", 0, nullptr, ICON_NONE);
{
uiItemR(layout, op->ptr, "pin", 0, nullptr, ICON_NONE);
uiLayout *sub = uiLayoutRow(layout, true);
uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "pin"));
uiItemR(sub, op->ptr, "pin_method", 0, IFACE_("Lock Method"), ICON_NONE);
uiItemS(layout);
}
uiItemR(layout, op->ptr, "merge_overlap", 0, nullptr, ICON_NONE);
uiItemR(layout, op->ptr, "udim_source", 0, nullptr, ICON_NONE);
uiItemS(layout);
@@ -1686,8 +1704,17 @@ void UV_OT_pack_islands(wmOperatorType *ot)
"");
RNA_def_float_factor(
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
RNA_def_enum(
ot->srna, "pin", pinned_islands_method_items, ED_UVPACK_PIN_PACK, "Pinned Islands", "");
RNA_def_boolean(ot->srna,
"pin",
false,
"Lock Pinned Islands",
"Constrain islands containing any pinned UV's");
RNA_def_enum(ot->srna,
"pin_method",
pinned_islands_method_items,
ED_UVPACK_PIN_LOCK_ALL,
"Pin Method",
"");
RNA_def_enum(ot->srna,
"shape_method",
pack_shape_method_items,
+9 -3
View File
@@ -47,12 +47,18 @@ enum eUVPackIsland_ShapeMethod {
};
enum eUVPackIsland_PinMethod {
ED_UVPACK_PIN_IGNORE = 0,
ED_UVPACK_PIN_PACK,
/** Pin has no impact on packing. */
ED_UVPACK_PIN_NONE = 0,
/**
* Ignore islands containing any pinned UV's.
* \note Not exposed in the UI, used only for live-unwrap.
*/
ED_UVPACK_PIN_IGNORE,
ED_UVPACK_PIN_LOCK_ROTATION,
ED_UVPACK_PIN_LOCK_ROTATION_SCALE,
ED_UVPACK_PIN_LOCK_SCALE,
ED_UVPACK_PIN_LOCK_ALL, /* Lock translation, rotation and scale. */
/** Lock the island in-place (translation, rotation and scale). */
ED_UVPACK_PIN_LOCK_ALL,
};
namespace blender::geometry {
@@ -1455,6 +1455,8 @@ static void customdata_weld(
int src_i, dest_i;
int j;
int vs_flag = 0;
/* interpolates a layer at a time */
dest_i = 0;
for (src_i = 0; src_i < source->totlayer; src_i++) {
@@ -1475,7 +1477,21 @@ static void customdata_weld(
/* if we found a matching layer, add the data */
if (dest->layers[dest_i].type == type) {
void *src_data = source->layers[src_i].data;
if (CustomData_layer_has_interp(dest, dest_i)) {
if (type == CD_MVERT_SKIN) {
/* The `typeInfo->interp` of #CD_MVERT_SKIN does not include the flags, so #MVERT_SKIN_ROOT
* and #MVERT_SKIN_LOOSE are lost after the interpolation.
*
* This behavior is not incorrect. Ideally, islands should be checked to avoid repeated
* roots.
*
* However, for now, to prevent the loss of flags, they are simply re-added if any of the
* merged vertices have them. */
for (j = 0; j < count; j++) {
MVertSkin *vs = &((MVertSkin *)src_data)[src_indices[j]];
vs_flag |= vs->flag;
}
}
else if (CustomData_layer_has_interp(dest, dest_i)) {
/* Already calculated.
* TODO: Optimize by exposing `typeInfo->interp`. */
}
@@ -1505,7 +1521,11 @@ static void customdata_weld(
for (dest_i = 0; dest_i < dest->totlayer; dest_i++) {
CustomDataLayer *layer_dst = &dest->layers[dest_i];
const eCustomDataType type = eCustomDataType(layer_dst->type);
if (CustomData_layer_has_interp(dest, dest_i)) {
if (type == CD_MVERT_SKIN) {
MVertSkin *vs = &((MVertSkin *)layer_dst->data)[dest_index];
vs->flag = vs_flag;
}
else if (CustomData_layer_has_interp(dest, dest_i)) {
/* Already calculated. */
}
else if (CustomData_layer_has_math(dest, dest_i)) {
@@ -2,6 +2,9 @@
#include "BLI_array_utils.hh"
#include "BLI_index_mask.hh"
#include "BLI_index_mask_ops.hh"
#include "BLI_ordered_edge.hh"
#include "BLI_vector_set.hh"
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
@@ -12,12 +15,6 @@
namespace blender::geometry {
/* Naively checks if the first vertices and the second vertices are the same. */
static inline bool naive_edges_equal(const int2 &edge1, const int2 &edge2)
{
return edge1 == edge2;
}
static void add_new_vertices(Mesh &mesh, const Span<int> new_to_old_verts_map)
{
/* These types aren't supported for interpolation below. */
@@ -153,70 +150,6 @@ static void add_new_edges(Mesh &mesh,
}
}
/**
* Merge the new_edge into the original edge.
*
* NOTE: This function is very specific to the situation and makes a lot of assumptions.
*/
static void merge_edges(const int orig_edge_i,
const int new_edge_i,
MutableSpan<int> new_corner_edges,
Vector<Vector<int>> &edge_to_loop_map,
Vector<int2> &new_edges,
Vector<int> &new_to_old_edges_map)
{
/* Merge back into the original edge by undoing the topology changes. */
BLI_assert(edge_to_loop_map[new_edge_i].size() == 1);
const int loop_i = edge_to_loop_map[new_edge_i][0];
new_corner_edges[loop_i] = orig_edge_i;
/* We are putting the last edge in the location of new_edge in all the maps, to remove
* new_edge efficiently. We have to update the topology information for this last edge
* though. Essentially we are replacing every instance of last_edge_i with new_edge_i. */
const int last_edge_i = new_edges.size() - 1;
if (last_edge_i != new_edge_i) {
BLI_assert(edge_to_loop_map[last_edge_i].size() == 1);
const int last_edge_loop_i = edge_to_loop_map[last_edge_i][0];
new_corner_edges[last_edge_loop_i] = new_edge_i;
}
/* We can now safely swap-remove. */
new_edges.remove_and_reorder(new_edge_i);
edge_to_loop_map.remove_and_reorder(new_edge_i);
new_to_old_edges_map.remove_and_reorder(new_edge_i);
}
/**
* Replace the vertex of an edge with a new one, and update the connected loops.
*
* NOTE: This only updates the loops containing the edge and the old vertex. It should therefore
* also be called on the adjacent edge.
*/
static void swap_vertex_of_edge(int2 &edge,
const int old_vert,
const int new_vert,
MutableSpan<int> corner_verts,
const Span<int> connected_loops)
{
if (edge[0] == old_vert) {
edge[0] = new_vert;
}
else if (edge[1] == old_vert) {
edge[1] = new_vert;
}
else {
BLI_assert_unreachable();
}
for (const int loop_i : connected_loops) {
if (corner_verts[loop_i] == old_vert) {
corner_verts[loop_i] = new_vert;
}
/* The old vertex is on the loop containing the adjacent edge. Since this function is also
* called on the adjacent edge, we don't replace it here. */
}
}
/** Split the vertex into duplicates so that each fan has a different vertex. */
static void split_vertex_per_fan(const int vertex,
const int start_offset,
@@ -224,7 +157,6 @@ static void split_vertex_per_fan(const int vertex,
const Span<int> fans,
const Span<int> fan_sizes,
const Span<Vector<int>> edge_to_loop_map,
MutableSpan<int2> new_edges,
MutableSpan<int> corner_verts,
MutableSpan<int> new_to_old_verts_map)
{
@@ -236,8 +168,13 @@ static void split_vertex_per_fan(const int vertex,
new_to_old_verts_map[new_vert_i - orig_verts_num] = vertex;
for (const int edge_i : fans.slice(fan_start, fan_sizes[i])) {
swap_vertex_of_edge(
new_edges[edge_i], vertex, new_vert_i, corner_verts, edge_to_loop_map[edge_i]);
for (const int loop_i : edge_to_loop_map[edge_i]) {
if (corner_verts[loop_i] == vertex) {
corner_verts[loop_i] = new_vert_i;
}
/* The old vertex is on the loop containing the adjacent edge. Since this function is also
* called on the adjacent edge, we don't replace it here. */
}
}
fan_start += fan_sizes[i];
}
@@ -266,7 +203,7 @@ static int adjacent_edge(const Span<int> corner_verts,
* be used to retrieve the fans from connected_edges.
*/
static void calc_vertex_fans(const int vertex,
const Span<int> new_corner_verts,
const Span<int> corner_verts,
const Span<int> new_corner_edges,
const OffsetIndices<int> polys,
const Span<Vector<int>> edge_to_loop_map,
@@ -297,7 +234,7 @@ static void calc_vertex_fans(const int vertex,
/* Add adjacent edges to search stack. */
for (const int loop_i : edge_to_loop_map[curr_edge_i]) {
const int adjacent_edge_i = adjacent_edge(
new_corner_verts, new_corner_edges, loop_i, polys[loop_to_poly_map[loop_i]], vertex);
corner_verts, new_corner_edges, loop_i, polys[loop_to_poly_map[loop_i]], vertex);
/* Find out if this edge was visited already. */
int i = curr_i + 1;
@@ -339,18 +276,13 @@ static void calc_vertex_fans(const int vertex,
static void split_edge_per_poly(const int edge_i,
const int new_edge_start,
MutableSpan<Vector<int>> edge_to_loop_map,
MutableSpan<int> corner_edges,
MutableSpan<int2> new_edges,
MutableSpan<int> new_to_old_edges_map)
MutableSpan<int> corner_edges)
{
if (edge_to_loop_map[edge_i].size() <= 1) {
return;
}
int new_edge_index = new_edge_start;
for (const int loop_i : edge_to_loop_map[edge_i].as_span().drop_front(1)) {
const int2 &new_edge(new_edges[edge_i]);
new_edges[new_edge_index] = new_edge;
new_to_old_edges_map[new_edge_index] = edge_i;
edge_to_loop_map[new_edge_index].append({loop_i});
corner_edges[loop_i] = new_edge_index;
new_edge_index++;
@@ -393,30 +325,35 @@ void split_edges(Mesh &mesh,
}
const OffsetIndices polys = mesh.polys();
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
MutableSpan<int> corner_edges = mesh.corner_edges_for_write();
Vector<int2> new_edges(new_edges_size);
new_edges.as_mutable_span().take_front(edges.size()).copy_from(edges);
const Array<int> orig_corner_edges = mesh.corner_edges();
Vector<int64_t> memory;
const bke::LooseEdgeCache loose_edges_cache = mesh.loose_edges();
IndexMask loose_edges;
if (loose_edges_cache.count > 0) {
loose_edges = index_mask_ops::find_indices_based_on_predicate(
edges.index_range(), 4096, memory, [&](const int64_t i) {
return loose_edges_cache.is_loose_bits[i];
});
}
edge_to_loop_map.resize(new_edges_size);
/* Used for transferring attributes. */
Vector<int> new_to_old_edges_map(IndexRange(new_edges.size()).as_span());
MutableSpan<int> corner_edges = mesh.corner_edges_for_write();
/* Step 1: Split the edges. */
threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
for (const int mask_i : range) {
const int edge_i = mask[mask_i];
split_edge_per_poly(edge_i,
edge_offsets[edge_i],
edge_to_loop_map,
corner_edges,
new_edges,
new_to_old_edges_map);
split_edge_per_poly(edge_i, edge_offsets[edge_i], edge_to_loop_map, corner_edges);
}
});
/* Step 1: Split the edges. */
mask.foreach_index([&](const int edge_i) {
split_edge_per_poly(edge_i, edge_offsets[edge_i], edge_to_loop_map, corner_edges);
});
/* Step 1.5: Update topology information (can't parallelize). */
for (const int edge_i : mask) {
const int2 &edge = edges[edge_i];
@@ -426,6 +363,8 @@ void split_edges(Mesh &mesh,
}
}
MutableSpan<int> corner_verts = mesh.corner_verts_for_write();
/* Step 2: Calculate vertex fans. */
Array<Vector<int>> vertex_fan_sizes(mesh.totvert);
threading::parallel_for(IndexRange(mesh.totvert), 512, [&](IndexRange range) {
@@ -471,39 +410,43 @@ void split_edges(Mesh &mesh,
vert_to_edge_map[vert],
vertex_fan_sizes[vert],
edge_to_loop_map,
new_edges,
corner_verts,
new_to_old_verts_map);
}
});
/* Step 4: Deduplicate edges. We loop backwards so we can use remove_and_reorder. Although this
* does look bad (3 nested loops), in practice the inner loops are very small. For most meshes,
* there are at most 2 polygons connected to each edge, and hence you'll only get at most 1
* duplicate per edge. */
for (int mask_i = mask.size() - 1; mask_i >= 0; mask_i--) {
const int edge = mask[mask_i];
int start_of_duplicates = edge_offsets[edge];
int end_of_duplicates = start_of_duplicates + num_edge_duplicates[edge] - 1;
for (int duplicate = end_of_duplicates; duplicate >= start_of_duplicates; duplicate--) {
if (naive_edges_equal(new_edges[edge], new_edges[duplicate])) {
merge_edges(
edge, duplicate, corner_edges, edge_to_loop_map, new_edges, new_to_old_edges_map);
break;
}
for (int other = start_of_duplicates; other < duplicate; other++) {
if (naive_edges_equal(new_edges[other], new_edges[duplicate])) {
merge_edges(
other, duplicate, corner_edges, edge_to_loop_map, new_edges, new_to_old_edges_map);
break;
}
}
VectorSet<OrderedEdge> new_edges;
new_edges.reserve(new_edges_size + loose_edges.size());
for (const int i : polys.index_range()) {
const IndexRange poly = polys[i];
for (const int corner : poly) {
const int vert_1 = corner_verts[corner];
const int vert_2 = corner_verts[bke::mesh::poly_corner_next(poly, corner)];
corner_edges[corner] = new_edges.index_of_or_add_as(OrderedEdge(vert_1, vert_2));
}
}
loose_edges.foreach_index([&](const int64_t i) { new_edges.add(OrderedEdge(edges[i])); });
Array<int> new_to_old_edges_map(new_edges.size());
auto index_mask_to_indices = [&](const IndexMask &mask, MutableSpan<int> indices) {
for (const int i : mask.index_range()) {
indices[i] = mask[i];
}
};
index_mask_to_indices(loose_edges,
new_to_old_edges_map.as_mutable_span().take_back(loose_edges.size()));
for (const int i : polys.index_range()) {
const IndexRange poly = polys[i];
for (const int corner : poly) {
const int new_edge_i = corner_edges[corner];
const int old_edge_i = orig_corner_edges[corner];
new_to_old_edges_map[new_edge_i] = old_edge_i;
}
}
/* Step 5: Resize the mesh to add the new vertices and rebuild the edges. */
add_new_vertices(mesh, new_to_old_verts_map);
add_new_edges(mesh, new_edges, new_to_old_edges_map, propagation_info);
add_new_edges(mesh, new_edges.as_span().cast<int2>(), new_to_old_edges_map, propagation_info);
BKE_mesh_tag_edges_split(&mesh);
}
+1 -1
View File
@@ -337,7 +337,7 @@ UVPackIsland_Params::UVPackIsland_Params()
only_selected_faces = false;
use_seams = false;
correct_aspect = false;
pin_method = ED_UVPACK_PIN_PACK;
pin_method = ED_UVPACK_PIN_NONE;
pin_unselected = false;
merge_overlap = false;
margin = 0.001f;
@@ -751,7 +751,8 @@ inline size_t to_bytesize(eGPUTextureFormat tex_format, eGPUDataFormat data_form
* Standard component len calculation does not apply, as the texture formats contain multiple
* channels, but associated data format contains several compacted components. */
if ((tex_format == GPU_R11F_G11F_B10F && data_format == GPU_DATA_10_11_11_REV) ||
(tex_format == GPU_RGB10_A2 && data_format == GPU_DATA_2_10_10_10_REV))
((tex_format == GPU_RGB10_A2 || tex_format == GPU_RGB10_A2UI) &&
data_format == GPU_DATA_2_10_10_10_REV))
{
return 4;
}
+21 -2
View File
@@ -107,6 +107,11 @@ class MTLUniformBuf;
/* MTLBuffer allocation wrapper. */
class MTLBuffer {
public:
/* NOTE: ListBase API is not used due to cutsom destructor operation required to release
* Metal objective C buffer resource. */
gpu::MTLBuffer *next, *prev;
private:
/* Metal resource. */
id<MTLBuffer> metal_buffer_;
@@ -177,6 +182,8 @@ class MTLBuffer {
/* Safety check to ensure buffers are not used after free. */
void debug_ensure_used();
MEM_CXX_CLASS_ALLOC_FUNCS("MTLBuffer");
};
/* View into part of an MTLBuffer. */
@@ -333,6 +340,8 @@ class MTLSafeFreeList {
}
}
}
MEM_CXX_CLASS_ALLOC_FUNCS("MTLSafeFreeList");
};
/* MTLBuffer pools. */
@@ -362,7 +371,7 @@ class MTLBufferPool {
#endif
/* Metal resources. */
bool ensure_initialised_ = false;
bool initialized_ = false;
id<MTLDevice> device_ = nil;
/* The buffer selection aims to pick a buffer which meets the minimum size requirements.
@@ -389,7 +398,10 @@ class MTLBufferPool {
std::mutex buffer_pool_lock_;
blender::Map<MTLBufferResourceOptions, MTLBufferPoolOrderedList *> buffer_pools_;
blender::Vector<gpu::MTLBuffer *> allocations_;
/* Linked list to track all existing allocations. Prioritizing fast insert/deletion. */
gpu::MTLBuffer *allocations_list_base_;
uint allocations_list_size_;
/* Maintain a queue of all MTLSafeFreeList's that have been released
* by the GPU and are ready to have their buffers re-inserted into the
@@ -432,6 +444,11 @@ class MTLBufferPool {
void ensure_buffer_pool(MTLResourceOptions options);
void insert_buffer_into_pool(MTLResourceOptions options, gpu::MTLBuffer *buffer);
void free();
/* Allocations list. */
void allocations_list_insert(gpu::MTLBuffer *buffer);
void allocations_list_delete(gpu::MTLBuffer *buffer);
void allocations_list_delete_all();
};
/* Scratch buffers are circular-buffers used for temporary data within the current frame.
@@ -492,6 +509,8 @@ class MTLScratchBufferManager {
* This call will perform a partial flush of the buffer starting from
* the last offset the data was flushed from, to the current offset. */
void flush_active_scratch_buffer();
MEM_CXX_CLASS_ALLOC_FUNCS("MTLBufferPool");
};
/** \} */

Some files were not shown because too many files have changed in this diff Show More